1
0
forked from boos/BoosAPI

Settle the last concrete review regressions on the release branch

A few review threads still pointed at real behavior mismatches: Anthropic tool
JSON deltas were still trimming whitespace, Responses conversion still raised
low token caps to the floor, and the bare /responses GET alias returned upgrade
messaging that did not match the actual websocket entrypoint. This commit makes
those behaviors explicit and locks them with focused tests.

Constraint: Keep the release branch green while fixing only the review findings that have a clear, testable contract
Rejected: Leave these for a follow-up cleanup PR | they were low-diff and directly blocked comment resolution on the current release PR
Confidence: high
Scope-risk: narrow
Directive: Preserve raw stream delta text unless a downstream protocol explicitly requires normalization; whitespace can be semantic in incremental payloads
Tested: Focused vitest for anthropic stream bridge, responses conversion, route aliases; typecheck; git diff --check
Not-tested: Full npm test; the remaining review threads that still need broader product decisions
This commit is contained in:
cita-777
2026-04-18 13:28:50 +08:00
parent 02520255db
commit e795f22367
6 changed files with 30 additions and 9 deletions
+1 -1
View File
@@ -34,7 +34,7 @@ export async function responsesProxyRoute(app: FastifyInstance) {
app.get('/responses', async (_request: FastifyRequest, reply: FastifyReply) =>
reply.code(426).send({
error: {
message: 'WebSocket upgrade required for GET /responses',
message: 'WebSocket upgrade required for GET /v1/responses',
type: 'invalid_request_error',
},
}));
+1 -1
View File
@@ -89,7 +89,7 @@ describe('proxy route aliases', () => {
expect(handleOpenAiResponsesSurfaceRequestMock).not.toHaveBeenCalled();
expect(response.json()).toEqual({
error: {
message: 'WebSocket upgrade required for GET /responses',
message: 'WebSocket upgrade required for GET /v1/responses',
type: 'invalid_request_error',
},
});
@@ -109,6 +109,30 @@ describe('anthropic messages stream bridge', () => {
expect(thinkingResult.lines.join('')).toContain(' padded thinking ');
});
it('preserves partial_json spacing for streaming tool-call deltas', () => {
const streamContext = anthropicMessagesStream.createContext('claude-test');
const downstreamContext = anthropicMessagesStream.createDownstreamContext();
const result = consumeAnthropicSseEvent(
{
event: 'content_block_delta',
data: JSON.stringify({
type: 'content_block_delta',
index: 0,
delta: {
type: 'input_json_delta',
partial_json: '{ "city": "Paris" }',
},
}),
},
streamContext,
downstreamContext,
'claude-test',
);
expect(result.lines.join('')).toContain('partial_json":"{ \\"city\\": \\"Paris\\" }"');
});
it('preserves responses output-item ordering when streaming anthropic fallback blocks', () => {
const streamContext = anthropicMessagesStream.createContext('claude-test');
const downstreamContext = anthropicMessagesStream.createDownstreamContext();
@@ -867,7 +867,7 @@ function normalizeAnthropicRawEvent(
return {
toolCallDeltas: [{
index,
argumentsDelta: asTrimmedString(delta.partial_json),
argumentsDelta: asUntouchedString(delta.partial_json),
}],
};
}
@@ -719,7 +719,7 @@ describe('convertOpenAiBodyToResponsesBody', () => {
expect(result.max_output_tokens).toBeUndefined();
});
it('clamps explicit chat token limits to the sub2api floor when converting to Responses bodies', () => {
it('preserves explicit chat token limits when converting to Responses bodies', () => {
const result = convertOpenAiBodyToResponsesBody(
{
model: 'gpt-5',
@@ -730,7 +730,7 @@ describe('convertOpenAiBodyToResponsesBody', () => {
false,
);
expect(result.max_output_tokens).toBe(128);
expect(result.max_output_tokens).toBe(64);
});
it('uses sub2api-compatible placeholders for empty tool arguments and empty tool outputs', () => {
@@ -618,10 +618,7 @@ export function convertOpenAiBodyToResponsesBody(
};
if (requestedMaxOutputTokens !== null && requestedMaxOutputTokens > 0) {
body.max_output_tokens = Math.max(
MIN_RESPONSES_MAX_OUTPUT_TOKENS,
Math.trunc(requestedMaxOutputTokens),
);
body.max_output_tokens = Math.trunc(requestedMaxOutputTokens);
}
if (systemContents.length > 0) {