forked from boos/BoosAPI
Tighten alias and header edge-cases called out in review
The latest review pass found a few more concrete edge-cases that were still cheap to close: invalid /responses aliases could still rewrite typos, websocket session headers should prefer proxy-safe dashed forms, contributor rendering needed an input guard, and several upstream request/response helpers still had small contract mismatches around Accept/beta handling and synthesized tool ids. These fixes keep the release branch behavior explicit while the PR remains open. Constraint: Keep changes surgical and well-covered so the review cleanup does not destabilize the already-green release branch Rejected: Leave these for post-merge cleanup | they were still active review threads with straightforward fixes Confidence: high Scope-risk: narrow Directive: When alias routes exist only for ergonomics, reject unknown subpaths loudly instead of silently rewriting them Tested: Focused vitest for contributor updater, route aliases, upstream request builder, openai responses response bridge; typecheck; git diff --check Not-tested: Full npm test; the remaining Gemini review thread that likely needs a broader canonical-tool-result contract decision
This commit is contained in:
@@ -292,6 +292,41 @@ describe('openai responses response bridge', () => {
|
||||
}
|
||||
});
|
||||
|
||||
it('uses fc_* item ids while keeping call_* ids for synthesized function_call outputs', () => {
|
||||
const payload = buildNormalizedFinalToOpenAiResponsesPayload({
|
||||
upstreamPayload: {
|
||||
id: 'opaque_tool_call_ids',
|
||||
model: 'gpt-5',
|
||||
},
|
||||
normalized: {
|
||||
id: 'opaque_tool_call_ids',
|
||||
model: 'gpt-5',
|
||||
created: 1700000000,
|
||||
content: '',
|
||||
reasoningContent: '',
|
||||
finishReason: 'tool_calls',
|
||||
toolCalls: [{
|
||||
id: 'call_weather_1',
|
||||
name: 'lookup_weather',
|
||||
arguments: '{"city":"Paris"}',
|
||||
}],
|
||||
},
|
||||
usage: {
|
||||
promptTokens: 1,
|
||||
completionTokens: 1,
|
||||
totalTokens: 2,
|
||||
},
|
||||
});
|
||||
|
||||
expect(payload.output).toEqual([
|
||||
expect.objectContaining({
|
||||
id: 'fc_weather_1',
|
||||
type: 'function_call',
|
||||
call_id: 'call_weather_1',
|
||||
}),
|
||||
]);
|
||||
});
|
||||
|
||||
it('keeps the outbound facade pointed at the response bridge object', () => {
|
||||
expect(openAiResponsesOutbound).toBe(openAiResponsesResponseBridge);
|
||||
});
|
||||
|
||||
@@ -20,6 +20,15 @@ function createSyntheticId(prefix: 'resp' | 'msg' | 'call'): string {
|
||||
return `${prefix}_${Date.now()}_${syntheticIdCounter}`;
|
||||
}
|
||||
|
||||
function toFunctionCallItemId(callId: string): string {
|
||||
const trimmed = callId.trim();
|
||||
if (!trimmed) {
|
||||
const syntheticCallId = createSyntheticId('call');
|
||||
return `fc_${syntheticCallId.slice('call_'.length)}`;
|
||||
}
|
||||
return trimmed.startsWith('call_') ? `fc_${trimmed.slice('call_'.length)}` : `fc_${trimmed}`;
|
||||
}
|
||||
|
||||
function cloneJson<T>(value: T): T {
|
||||
if (Array.isArray(value)) {
|
||||
return value.map((item) => cloneJson(item)) as T;
|
||||
@@ -413,7 +422,7 @@ export function buildNormalizedFinalToOpenAiResponsesPayload(input: {
|
||||
}
|
||||
|
||||
output.push({
|
||||
id: toolCall.id,
|
||||
id: toFunctionCallItemId(toolCall.id),
|
||||
type: 'function_call',
|
||||
status: 'completed',
|
||||
call_id: toolCall.id,
|
||||
|
||||
Reference in New Issue
Block a user