Skip to content

Commit b8c7ae1

Browse files
committed
fix(transport): tighten SSE buffer guards
1 parent 672a38f commit b8c7ae1

2 files changed

Lines changed: 12 additions & 5 deletions

File tree

src/agents/provider-transport-fetch.test.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1133,7 +1133,8 @@ describe("buildGuardedModelFetch", () => {
11331133
it("handles a large transport chunk containing many valid small SSE events", async () => {
11341134
// Regression: one TCP read can deliver >64 KiB of already-delimited SSE
11351135
// events; the cap must apply only to the unterminated tail, not the full chunk.
1136-
const manyEvents = `data: ${JSON.stringify({ ok: true })}\n\n`.repeat(500);
1136+
const eventCount = 5_000;
1137+
const manyEvents = `data: ${JSON.stringify({ ok: true })}\n\n`.repeat(eventCount);
11371138
fetchWithSsrFGuardMock.mockResolvedValue({
11381139
response: new Response(manyEvents, {
11391140
headers: { "content-type": "text/event-stream" },
@@ -1156,7 +1157,7 @@ describe("buildGuardedModelFetch", () => {
11561157
for await (const item of Stream.fromSSEResponse(response, new AbortController())) {
11571158
items.push(item);
11581159
}
1159-
expect(items.length).toBe(500);
1160+
expect(items.length).toBe(eventCount);
11601161
expect(items[0]).toEqual({ ok: true });
11611162
});
11621163

src/agents/provider-transport-fetch.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -132,13 +132,14 @@ function sanitizeOpenAISdkSseResponse(
132132
controller.close();
133133
return;
134134
}
135-
buffer += decoder.decode(chunk.value, { stream: true });
136-
totalBytes += chunk.value.byteLength;
137-
if (totalBytes > SSE_SYNTHESIZE_JSON_MAX_BYTES) {
135+
const nextTotalBytes = totalBytes + chunk.value.byteLength;
136+
if (nextTotalBytes > SSE_SYNTHESIZE_JSON_MAX_BYTES) {
138137
throw new Error(
139138
`Streaming JSON body exceeded ${SSE_SYNTHESIZE_JSON_MAX_BYTES} bytes while synthesizing SSE frames`,
140139
);
141140
}
141+
totalBytes = nextTotalBytes;
142+
buffer += decoder.decode(chunk.value, { stream: true });
142143
}
143144
} catch (error) {
144145
await reader?.cancel(error).catch(() => {});
@@ -191,6 +192,7 @@ function sanitizeOpenAISdkSseResponse(
191192
if (hasReadableSseData(block)) {
192193
controller.enqueue(encoder.encode(`${block}${separator}`));
193194
enqueued += 1;
195+
return enqueued;
194196
}
195197
}
196198
};
@@ -202,6 +204,10 @@ function sanitizeOpenAISdkSseResponse(
202204
async pull(controller) {
203205
try {
204206
for (;;) {
207+
const pending = enqueueSanitized(controller, "");
208+
if (pending > 0) {
209+
return;
210+
}
205211
const chunk = await reader?.read();
206212
if (!chunk || chunk.done) {
207213
const tail = decoder.decode();

0 commit comments

Comments
 (0)