fix(proxy): keep stream guards active through sse piping

This commit is contained in:
Tam Nhu Tran committed 2026-04-15 02:52:29 -04:00
1 parent a3407093d7
commit 841eeb497c
4 files changed
+36 -16

No files matched your search

@@ -140,7 +140,7 @@ describe('openai proxy message edge cases', () => {
type: 'error',
error: {
type: 'api_error',
message: 'Failed to translate Cursor JSON response',
message: 'Failed to translate OpenAI-compatible JSON response',
},
});
});
@@ -202,6 +202,28 @@ describe('openai proxy message edge cases', () => {
});
});
it('emits an SSE error if the upstream stalls after response headers are sent', async () => {
process.env.CCS_OPENAI_PROXY_REQUEST_TIMEOUT_MS = '50';
await startProxyWithHandler((_req, res) => {
res.writeHead(200, { 'Content-Type': 'text/event-stream' });
res.write(
'data: {"id":"chatcmpl_1","model":"hf-model","choices":[{"index":0,"delta":{"role":"assistant","content":"partial"}}]}\n\n'
);
});
const response = await requestProxy({
model: 'hf-model',
stream: true,
messages: [{ role: 'user', content: 'hello' }],
});
expect(response.status).toBe(200);
const body = await response.text();
expect(body).toContain('event: message_start');
expect(body).toContain('event: error');
expect(body).toContain('"message":"Failed to translate OpenAI-compatible SSE response"');
});
it('aborts the upstream request when the client disconnects mid-flight', async () => {
await startProxyWithHandler(() => {});
@@ -281,7 +281,7 @@ describe('createAnthropicProxyResponse', () => {
};
expect(body.type).toBe('error');
expect(body.error?.type).toBe('api_error');
expect(body.error?.message).toBe('Failed to translate Cursor JSON response');
expect(body.error?.message).toBe('Failed to translate OpenAI-compatible JSON response');
});
it('returns 502 when Cursor response is missing choices', async () => {
@@ -305,7 +305,7 @@ describe('createAnthropicProxyResponse', () => {
};
expect(body.type).toBe('error');
expect(body.error?.type).toBe('api_error');
expect(body.error?.message).toBe('Failed to translate Cursor JSON response');
expect(body.error?.message).toBe('Failed to translate OpenAI-compatible JSON response');
});
it('returns 502 when Cursor response has empty choices', async () => {
@@ -330,7 +330,7 @@ describe('createAnthropicProxyResponse', () => {
};
expect(body.type).toBe('error');
expect(body.error?.type).toBe('api_error');
expect(body.error?.message).toBe('Failed to translate Cursor JSON response');
expect(body.error?.message).toBe('Failed to translate OpenAI-compatible JSON response');
});
it('returns Anthropic error envelopes for non-OK upstream JSON errors', async () => {
@@ -382,7 +382,7 @@ describe('createAnthropicProxyResponse', () => {
};
expect(body.type).toBe('error');
expect(body.error?.type).toBe('api_error');
expect(body.error?.message).toBe('Failed to translate Cursor JSON response');
expect(body.error?.message).toBe('Failed to translate OpenAI-compatible JSON response');
});
it('converts OpenAI SSE chunks into Anthropic SSE events', async () => {
@@ -420,7 +420,7 @@ describe('createAnthropicProxyResponse', () => {
expect(body).toContain('event: error');
expect(body).toContain('"type":"error"');
expect(body).toContain('"error":{"type":"api_error"');
expect(body).toContain('Failed to translate Cursor SSE response');
expect(body).toContain('Failed to translate OpenAI-compatible SSE response');
});
it('emits Anthropic-style error events when SSE JSON is malformed', async () => {
@@ -435,6 +435,6 @@ describe('createAnthropicProxyResponse', () => {
expect(body).toContain('event: error');
expect(body).toContain('"type":"error"');
expect(body).toContain('"error":{"type":"api_error"');
expect(body).toContain('Failed to translate Cursor SSE response');
expect(body).toContain('Failed to translate OpenAI-compatible SSE response');
});
});