Skip to content

Commit 3281f0f

Browse files
committed
Revert "feat: capture all provider response headers and return x-request-id to caller"
This reverts commit c92b3b8.
1 parent c92b3b8 commit 3281f0f

12 files changed

Lines changed: 43 additions & 195 deletions

File tree

packages/backend/src/routes/inference/__tests__/images-routes.test.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ type FakeRequest = {
2929
};
3030

3131
type FakeReply = {
32-
header: (name: string, value: string) => FakeReply;
3332
code: (statusCode: number) => FakeReply;
3433
send: (payload: unknown) => unknown;
3534
};
@@ -101,7 +100,6 @@ describe('Images route telemetry', () => {
101100

102101
const replyState: { statusCode?: number; payload?: unknown } = {};
103102
const reply: FakeReply = {
104-
header: vi.fn(() => reply),
105103
code: vi.fn((statusCode: number) => {
106104
replyState.statusCode = statusCode;
107105
return reply;
@@ -216,7 +214,6 @@ describe('Images route telemetry', () => {
216214

217215
const replyState: { statusCode?: number; payload?: unknown } = {};
218216
const reply: FakeReply = {
219-
header: vi.fn(() => reply),
220217
code: vi.fn((statusCode: number) => {
221218
replyState.statusCode = statusCode;
222219
return reply;

packages/backend/src/routes/inference/chat.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,6 @@ export async function registerChatRoute(
137137
DebugManager.getInstance().flush(requestId);
138138

139139
logger.error('Error processing OpenAI request', e);
140-
reply.header('x-request-id', requestId);
141140
const statusCode = e.routingContext?.statusCode || 500;
142141
const errorType =
143142
statusCode === 401

packages/backend/src/routes/inference/embeddings.ts

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ export async function registerEmbeddingsRoute(
9494
DebugManager.getInstance().addTransformedResponse(requestId, formattedResponse);
9595
DebugManager.getInstance().flush(requestId);
9696

97-
return reply.header('x-request-id', requestId).send(formattedResponse);
97+
return reply.send(formattedResponse);
9898
} catch (e: any) {
9999
usageRecord.responseStatus = 'error';
100100
usageRecord.durationMs = Date.now() - startTime;
@@ -111,12 +111,9 @@ export async function registerEmbeddingsRoute(
111111
DebugManager.getInstance().flush(requestId);
112112
logger.error('Error processing embeddings request', e);
113113

114-
return reply
115-
.header('x-request-id', requestId)
116-
.code(e.routingContext?.statusCode || 500)
117-
.send({
118-
error: { message: e.message, type: 'api_error' },
119-
});
114+
return reply.code(e.routingContext?.statusCode || 500).send({
115+
error: { message: e.message, type: 'api_error' },
116+
});
120117
}
121118
});
122119
}

packages/backend/src/routes/inference/gemini.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,6 @@ export async function registerGeminiRoute(
148148
logger.error('Error processing Gemini request', e);
149149
const statusCode = e.routingContext?.statusCode || 500;
150150
const errorReason = e.routingContext?.code;
151-
reply.header('x-request-id', requestId);
152151
const status =
153152
statusCode === 401
154153
? 'UNAUTHENTICATED'

packages/backend/src/routes/inference/images.ts

Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ export async function registerImagesRoute(
117117
delete (unifiedResponse as any).plexus;
118118
}
119119

120-
return reply.header('x-request-id', requestId).send(unifiedResponse);
120+
return reply.send(unifiedResponse);
121121
} catch (e: any) {
122122
usageRecord.responseStatus = 'error';
123123
usageRecord.durationMs = Date.now() - startTime;
@@ -134,12 +134,9 @@ export async function registerImagesRoute(
134134
DebugManager.getInstance().flush(requestId);
135135
logger.error('Error processing image generation request', e);
136136

137-
return reply
138-
.header('x-request-id', requestId)
139-
.code(e.routingContext?.statusCode || 500)
140-
.send({
141-
error: { message: e.message, type: 'api_error' },
142-
});
137+
return reply.code(e.routingContext?.statusCode || 500).send({
138+
error: { message: e.message, type: 'api_error' },
139+
});
143140
}
144141
});
145142

@@ -288,7 +285,7 @@ export async function registerImagesRoute(
288285
delete (unifiedResponse as any).plexus;
289286
}
290287

291-
return reply.header('x-request-id', requestId).send(unifiedResponse);
288+
return reply.send(unifiedResponse);
292289
} catch (e: any) {
293290
usageRecord.responseStatus = 'error';
294291
usageRecord.durationMs = Date.now() - startTime;
@@ -305,12 +302,9 @@ export async function registerImagesRoute(
305302
DebugManager.getInstance().flush(requestId);
306303
logger.error('Error processing image edit request', e);
307304

308-
return reply
309-
.header('x-request-id', requestId)
310-
.code(e.routingContext?.statusCode || 500)
311-
.send({
312-
error: { message: e.message, type: 'api_error' },
313-
});
305+
return reply.code(e.routingContext?.statusCode || 500).send({
306+
error: { message: e.message, type: 'api_error' },
307+
});
314308
}
315309
});
316310
}

packages/backend/src/routes/inference/messages.ts

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -144,17 +144,14 @@ export async function registerMessagesRoute(
144144
? 'invalid_request_error'
145145
: 'api_error';
146146
const errorCode = e.routingContext?.code;
147-
return reply
148-
.header('x-request-id', requestId)
149-
.code(statusCode)
150-
.send({
151-
type: 'error',
152-
error: {
153-
type: errorType,
154-
message: e.message,
155-
...(errorCode && { code: errorCode }),
156-
},
157-
});
147+
return reply.code(statusCode).send({
148+
type: 'error',
149+
error: {
150+
type: errorType,
151+
message: e.message,
152+
...(errorCode && { code: errorCode }),
153+
},
154+
});
158155
}
159156
});
160157
}

packages/backend/src/routes/inference/responses.ts

Lines changed: 14 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -237,23 +237,20 @@ export async function registerResponsesRoute(
237237

238238
const statusCode = e.routingContext?.statusCode || 500;
239239
const errorCode = e.routingContext?.code;
240-
return reply
241-
.header('x-request-id', requestId)
242-
.code(statusCode)
243-
.send({
244-
error: {
245-
message: e.message || 'Internal server error',
246-
type: statusCode >= 500 ? 'server_error' : 'invalid_request_error',
247-
...(errorCode && { code: errorCode }),
248-
...(e.routingContext && {
249-
routing_context: {
250-
provider: e.routingContext.provider,
251-
target_model: e.routingContext.targetModel,
252-
target_api_type: e.routingContext.targetApiType,
253-
},
254-
}),
255-
},
256-
});
240+
return reply.code(statusCode).send({
241+
error: {
242+
message: e.message || 'Internal server error',
243+
type: statusCode >= 500 ? 'server_error' : 'invalid_request_error',
244+
...(errorCode && { code: errorCode }),
245+
...(e.routingContext && {
246+
routing_context: {
247+
provider: e.routingContext.provider,
248+
target_model: e.routingContext.targetModel,
249+
target_api_type: e.routingContext.targetApiType,
250+
},
251+
}),
252+
},
253+
});
257254
}
258255
};
259256

packages/backend/src/routes/inference/speech.ts

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -134,10 +134,10 @@ export async function registerSpeechRoute(
134134

135135
if (unifiedResponse.stream) {
136136
usageRecord.isStreamed = true;
137-
return reply.header('x-request-id', requestId).send(unifiedResponse.stream);
137+
return reply.send(unifiedResponse.stream);
138138
}
139139

140-
return reply.header('x-request-id', requestId).send(unifiedResponse.audio);
140+
return reply.send(unifiedResponse.audio);
141141
} catch (e: any) {
142142
usageRecord.responseStatus = 'error';
143143
usageRecord.durationMs = Date.now() - startTime;
@@ -154,12 +154,9 @@ export async function registerSpeechRoute(
154154
DebugManager.getInstance().flush(requestId);
155155
logger.error('Error processing speech request', e);
156156

157-
return reply
158-
.header('x-request-id', requestId)
159-
.code(e.routingContext?.statusCode || 500)
160-
.send({
161-
error: { message: e.message, type: 'api_error' },
162-
});
157+
return reply.code(e.routingContext?.statusCode || 500).send({
158+
error: { message: e.message, type: 'api_error' },
159+
});
163160
}
164161
});
165162
}

packages/backend/src/routes/inference/transcriptions.ts

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ export async function registerTranscriptionsRoute(
186186
reply.type('application/json');
187187
}
188188

189-
return reply.header('x-request-id', requestId).send(formattedResponse);
189+
return reply.send(formattedResponse);
190190
} catch (e: any) {
191191
usageRecord.responseStatus = 'error';
192192
usageRecord.durationMs = Date.now() - startTime;
@@ -203,12 +203,9 @@ export async function registerTranscriptionsRoute(
203203
DebugManager.getInstance().flush(requestId);
204204
logger.error('Error processing transcription request', e);
205205

206-
return reply
207-
.header('x-request-id', requestId)
208-
.code(e.routingContext?.statusCode || 500)
209-
.send({
210-
error: { message: e.message, type: 'api_error' },
211-
});
206+
return reply.code(e.routingContext?.statusCode || 500).send({
207+
error: { message: e.message, type: 'api_error' },
208+
});
212209
}
213210
});
214211
}

packages/backend/src/services/__tests__/dispatcher-failover.test.ts

Lines changed: 0 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -560,84 +560,4 @@ describe('Dispatcher Failover', () => {
560560
expect(meta?.attemptCount).toBe(1);
561561
expect(meta?.finalAttemptProvider).toBe('p2');
562562
});
563-
564-
test('provider error captures all upstream response headers in routing context', async () => {
565-
setConfigForTesting(makeConfig({ targetCount: 1 }));
566-
fetchMock.mockImplementation(
567-
async () =>
568-
new Response(JSON.stringify({ error: { message: 'boom' } }), {
569-
status: 500,
570-
headers: {
571-
'Content-Type': 'application/json',
572-
'x-request-id': 'provider-req-123',
573-
'X-Custom-Header': 'custom-value',
574-
},
575-
})
576-
);
577-
578-
const dispatcher = new Dispatcher();
579-
580-
try {
581-
await dispatcher.dispatch({ ...makeChatRequest(), requestId: 'req-headers-test' });
582-
throw new Error('expected dispatch to fail');
583-
} catch (error: any) {
584-
expect(error.routingContext?.providerResponseHeaders).toBeDefined();
585-
expect(error.routingContext?.providerResponseHeaders['x-request-id']).toBe('provider-req-123');
586-
expect(error.routingContext?.providerResponseHeaders['x-custom-header']).toBe('custom-value');
587-
expect(error.routingContext?.providerResponseHeaders['content-type']).toBe('application/json');
588-
}
589-
});
590-
591-
test('retry history includes provider response headers on failed attempts', async () => {
592-
setConfigForTesting(makeConfig({ targetCount: 2 }));
593-
fetchMock
594-
.mockImplementationOnce(
595-
async () =>
596-
new Response(JSON.stringify({ error: { message: 'first boom' } }), {
597-
status: 500,
598-
headers: { 'x-request-id': 'first-req-id' },
599-
})
600-
)
601-
.mockImplementationOnce(async () => successChatResponse('model-2'));
602-
603-
const dispatcher = new Dispatcher();
604-
const response = await dispatcher.dispatch({ ...makeChatRequest(), requestId: 'req-retry-hist' });
605-
const meta = (response as any).plexus;
606-
const retryHistory = JSON.parse(meta?.retryHistory || '[]');
607-
608-
expect(retryHistory).toHaveLength(2);
609-
expect(retryHistory[0]?.status).toBe('failed');
610-
expect(retryHistory[0]?.providerResponseHeaders?.['x-request-id']).toBe('first-req-id');
611-
expect(retryHistory[1]?.status).toBe('success');
612-
});
613-
614-
test('intermediate failures are saved during failover', async () => {
615-
setConfigForTesting(makeConfig({ targetCount: 2 }));
616-
fetchMock
617-
.mockImplementationOnce(async () => errorResponse(500, 'first failed'))
618-
.mockImplementationOnce(async () => successChatResponse('model-2'));
619-
620-
const saveErrorSpy = vi.fn();
621-
const dispatcher = new Dispatcher();
622-
dispatcher.setUsageStorage({
623-
saveError: saveErrorSpy,
624-
recordFailedAttempt: vi.fn(),
625-
recordSuccessfulAttempt: vi.fn(),
626-
} as any);
627-
628-
const response = await dispatcher.dispatch({
629-
...makeChatRequest(),
630-
requestId: 'req-intermediate',
631-
});
632-
const meta = (response as any).plexus;
633-
634-
expect(meta?.attemptCount).toBe(2);
635-
// One call for the intermediate failure, one in the route handler doesn't happen
636-
// here because dispatch succeeded overall.
637-
expect(saveErrorSpy).toHaveBeenCalledTimes(1);
638-
const [savedRequestId, savedError, savedDetails] = saveErrorSpy.mock.calls[0] as any[];
639-
expect(savedRequestId).toBe('req-intermediate');
640-
expect(savedError?.routingContext?.statusCode).toBe(500);
641-
expect(savedDetails?.apiType).toBe('chat');
642-
});
643563
});

0 commit comments

Comments
 (0)