fix(translator): shape Anthropic non-stream responses from forced-streaming providers
The non-streaming Anthropic path leaked OpenAI chat.completion bodies to Claude clients when the upstream provider forces streaming (forced SSE->JSON path): parseSSEToOpenAIResponse yields choices[] which Claude Code cannot parse. Convert to Claude Messages shape for sourceFormat CLAUDE in sseToJsonHandler. Shared toClaudeMessageShape/openAICompletionToClaudeMessage moved to translator/concerns/claudeShape.js to avoid circular import between nonStreamingHandler and sseToJsonHandler. 5 new tests: sse-to-json reassembly + Claude guard, plus existing anthropic-nonstream-shape updated to import from the concern.
This commit is contained in:
1 parent
b239b2a8a9
commit
3de32a10f3
5 files changed
+139
-67
No files matched your search
@@ -3,6 +3,7 @@ import { restoreToolNames } from "../../utils/opencodeFingerprint.js";
|
||||
import { createErrorResult } from "../../utils/error.js";
|
||||
import { HTTP_STATUS } from "../../config/runtimeConfig.js";
|
||||
import { FORMATS } from "../../translator/formats.js";
|
||||
import { toClaudeMessageShape } from "../../translator/concerns/claudeShape.js";
|
||||
import { PROVIDERS } from "../../config/providers.js";
|
||||
import { buildRequestDetail, extractRequestConfig, saveUsageStats, formatDoneLine } from "./requestDetail.js";
|
||||
import { ROLE, RESPONSES_ITEM } from "../../translator/schema/index.js";
|
||||
@@ -356,10 +357,17 @@ export async function handleForcedSSEToJson({ providerResponse, sourceFormat, ta
|
||||
// lost on the non-streaming return path. Inlined (not imported from
|
||||
// nonStreamingHandler.js) to avoid a circular import: nonStreamingHandler
|
||||
// already imports parseSSEToOpenAIResponse from this module.
|
||||
const finalBody = sourceFormat === FORMATS.OPENAI_RESPONSES
|
||||
let finalBody = sourceFormat === FORMATS.OPENAI_RESPONSES
|
||||
? chatCompletionToResponses(parsed, customToolNames)
|
||||
: parsed;
|
||||
|
||||
// Anthropic client behind a forced-streaming chat provider: the SSE was
|
||||
// OpenAI-shaped, so hand the client the Claude Messages shape (type:
|
||||
// "message", content[] blocks, stop_reason) instead of leaking choices[].
|
||||
if (sourceFormat === FORMATS.CLAUDE) {
|
||||
finalBody = toClaudeMessageShape(finalBody);
|
||||
}
|
||||
|
||||
return { success: true, response: new Response(JSON.stringify(restoreToolNames(finalBody, toolNameMap)), { headers: { "Content-Type": "application/json", "Access-Control-Allow-Origin": "*" } }) };
|
||||
} catch (err) {
|
||||
console.error("[ChatCore] Chat Completions SSE→JSON failed:", err);
|
||||
|
||||
Reference in new issue
Block a user