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:
cita-777
2026-04-18 13:38:46 +08:00
parent e795f22367
commit 20f06d391c
9 changed files with 174 additions and 14 deletions
@@ -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,