feat(proxy): improve quota fallback and proxy parity handling

This commit is contained in:
Draculabo committed 2026-04-11 16:47:57 +08:00
1 parent 388fbcb2a9
commit 4fa0a3c63d
9 files changed
+350 -66

No files matched your search

+40
View File
@@ -12,6 +12,7 @@ const mockTokenManager = {
recordParityError: vi.fn(),
getModelOutputLimitForAccount: vi.fn(),
getModelThinkingBudgetForAccount: vi.fn(),
resolveDynamicModelForAccount: vi.fn((_token: unknown, model: string) => model),
};
const mockGeminiClient = { streamGenerateInternal: vi.fn(), generateInternal: vi.fn() };
@@ -651,4 +652,43 @@ describe('ProxyService Protocol Parity Fixtures', () => {
expect(output).toContain('"content":"final answer"');
expect(output).toContain('data: [DONE]');
});
it('propagates OpenAI-compatible upstream stream errors instead of completing with [DONE]', async () => {
const service = new TestableProxyService();
const stream = new EventEmitter();
const observable = (service as any).processStreamResponse(stream, 'gpt-4o-mini');
const chunks: string[] = [];
const streamResult = await new Promise<{ error?: Error; completed: boolean }>((resolve) => {
observable.subscribe({
next: (chunk: string) => {
chunks.push(chunk);
},
error: (error: unknown) =>
resolve({
completed: false,
error: error instanceof Error ? error : new Error(String(error)),
}),
complete: () => resolve({ completed: true }),
});
const payload = JSON.stringify({
candidates: [
{
content: {
parts: [{ text: 'partial output' }],
},
},
],
});
stream.emit('data', Buffer.from(`data: ${payload}\n`));
stream.emit('error', new Error('socket hang up'));
});
expect(streamResult.completed).not.toBe(true);
expect(streamResult.error?.message).toContain('socket hang up');
expect(chunks.join('')).toContain('"content":"partial output"');
expect(chunks.join('')).not.toContain('data: [DONE]');
});
});