fix: improve streaming error propagation and forwarded response logging (#5317)
* Fix: Improve streaming error handling and forwarded response logging * Fix: fix ESLint error Strings must use singlequote quotes * fix: preserve and log forwarded stream errors * chore: narrow forwarded stream error fix scope * fix: make forwardFetchResponse awaitable and forward upstream error text * Restore original happy path handling * Remove redundant checks in forwardFetchResponse function * Don't send anything on parsing error end --------- Co-authored-by: Cohee <18619528+Cohee1207@users.noreply.github.com>
This commit is contained in:
+61
-2
@@ -1,6 +1,31 @@
|
||||
import { describe, test, expect } from '@jest/globals';
|
||||
import { afterEach, describe, test, expect, jest } from '@jest/globals';
|
||||
import { once } from 'node:events';
|
||||
import { PassThrough } from 'node:stream';
|
||||
import { Response } from 'node-fetch';
|
||||
import { CHAT_COMPLETION_SOURCES } from '../src/constants';
|
||||
import { flattenSchema } from '../src/util';
|
||||
import { flattenSchema, forwardFetchResponse } from '../src/util';
|
||||
|
||||
function createMockExpressResponse() {
|
||||
const response = new PassThrough();
|
||||
response.statusCode = 200;
|
||||
response.statusMessage = '';
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
async function collectResponseBody(response) {
|
||||
const chunks = [];
|
||||
|
||||
response.on('data', chunk => chunks.push(Buffer.from(chunk)));
|
||||
|
||||
await once(response, 'finish');
|
||||
|
||||
return Buffer.concat(chunks).toString('utf8');
|
||||
}
|
||||
|
||||
afterEach(() => {
|
||||
jest.restoreAllMocks();
|
||||
});
|
||||
|
||||
describe('flattenSchema', () => {
|
||||
test('should return the schema if it is not an object', () => {
|
||||
@@ -105,3 +130,37 @@ describe('flattenSchema', () => {
|
||||
expect(flattenSchema(schema, 'some-other-api')).toEqual(expected);
|
||||
});
|
||||
});
|
||||
|
||||
describe('forwardFetchResponse', () => {
|
||||
test('should log JSON error bodies and return the original body for non-2xx streaming responses', async () => {
|
||||
const warnSpy = jest.spyOn(console, 'warn').mockImplementation(() => undefined);
|
||||
const body = JSON.stringify({ error: { message: 'Forbidden by upstream policy' }, detail: 'policy_denied' });
|
||||
const response = createMockExpressResponse();
|
||||
const bodyPromise = collectResponseBody(response);
|
||||
|
||||
await forwardFetchResponse(new Response(body, {
|
||||
status: 403,
|
||||
statusText: 'Forbidden',
|
||||
}), response);
|
||||
|
||||
expect(await bodyPromise).toBe(body);
|
||||
expect(response.statusCode).toBe(403);
|
||||
expect(warnSpy).toHaveBeenCalledWith(`Streaming request failed with status 403 Forbidden: ${body}`);
|
||||
});
|
||||
|
||||
test('should log plain text error bodies and return the original body for non-2xx streaming responses', async () => {
|
||||
const warnSpy = jest.spyOn(console, 'warn').mockImplementation(() => undefined);
|
||||
const body = 'Plain text upstream failure';
|
||||
const response = createMockExpressResponse();
|
||||
const bodyPromise = collectResponseBody(response);
|
||||
|
||||
await forwardFetchResponse(new Response(body, {
|
||||
status: 502,
|
||||
statusText: 'Bad Gateway',
|
||||
}), response);
|
||||
|
||||
expect(await bodyPromise).toBe(body);
|
||||
expect(response.statusCode).toBe(502);
|
||||
expect(warnSpy).toHaveBeenCalledWith(`Streaming request failed with status 502 Bad Gateway: ${body}`);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user