fix: add normalization for Claude passthrough bodies
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
import { detectFormat, getTargetFormat } from "../services/provider.js";
|
import { detectFormat, getTargetFormat } from "../services/provider.js";
|
||||||
import { translateRequest } from "../translator/index.js";
|
import { translateRequest } from "../translator/index.js";
|
||||||
import { FORMATS } from "../translator/formats.js";
|
import { FORMATS } from "../translator/formats.js";
|
||||||
|
import { normalizeClaudePassthrough } from "../translator/helpers/claudeHelper.js";
|
||||||
import { COLORS } from "../utils/stream.js";
|
import { COLORS } from "../utils/stream.js";
|
||||||
import { createStreamController } from "../utils/streamHandler.js";
|
import { createStreamController } from "../utils/streamHandler.js";
|
||||||
import { refreshWithRetry } from "../services/tokenRefresh.js";
|
import { refreshWithRetry } from "../services/tokenRefresh.js";
|
||||||
@@ -91,6 +92,8 @@ export async function handleChatCore({ body, modelInfo, credentials, log, onCred
|
|||||||
if (passthrough) {
|
if (passthrough) {
|
||||||
log?.debug?.("PASSTHROUGH", `${clientTool} → ${provider} | native lossless`);
|
log?.debug?.("PASSTHROUGH", `${clientTool} → ${provider} | native lossless`);
|
||||||
translatedBody = { ...body, model: upstreamModel };
|
translatedBody = { ...body, model: upstreamModel };
|
||||||
|
// Normalize newer Cowork/CC beta shapes (adaptive thinking, mid-conversation system) the API rejects
|
||||||
|
if (clientTool === "claude") normalizeClaudePassthrough(translatedBody, upstreamModel);
|
||||||
} else {
|
} else {
|
||||||
translatedBody = translateRequest(sourceFormat, targetFormat, upstreamModel, body, stream, credentials, provider, reqLogger, stripList, connectionId, clientTool);
|
translatedBody = translateRequest(sourceFormat, targetFormat, upstreamModel, body, stream, credentials, provider, reqLogger, stripList, connectionId, clientTool);
|
||||||
if (!translatedBody) {
|
if (!translatedBody) {
|
||||||
|
|||||||
@@ -76,6 +76,52 @@ export function fixToolUseOrdering(messages) {
|
|||||||
return merged;
|
return merged;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Models that reject thinking.type "adaptive" (only Sonnet/Opus support it)
|
||||||
|
const ADAPTIVE_THINKING_UNSUPPORTED = /haiku/i;
|
||||||
|
|
||||||
|
// Normalize a native Claude passthrough body to match Anthropic Messages API spec.
|
||||||
|
// Newer Cowork/Claude Code clients emit beta-only shapes that OAuth endpoints reject:
|
||||||
|
// 1. thinking.type "adaptive" → unsupported on Haiku
|
||||||
|
// 2. role "system" messages (mid-conversation-system beta) → only top-level system is allowed
|
||||||
|
export function normalizeClaudePassthrough(body, model = "") {
|
||||||
|
if (!body || typeof body !== "object") return body;
|
||||||
|
|
||||||
|
// 1. Downgrade adaptive thinking for models that don't support it
|
||||||
|
if (body.thinking?.type === "adaptive" && ADAPTIVE_THINKING_UNSUPPORTED.test(model)) {
|
||||||
|
body.thinking = { type: "enabled", budget_tokens: 10000 };
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Hoist mid-conversation system messages into the top-level system field
|
||||||
|
if (Array.isArray(body.messages)) {
|
||||||
|
const systemBlocks = [];
|
||||||
|
const messages = [];
|
||||||
|
for (const msg of body.messages) {
|
||||||
|
if (msg.role === "system") {
|
||||||
|
const text = typeof msg.content === "string"
|
||||||
|
? msg.content
|
||||||
|
: Array.isArray(msg.content)
|
||||||
|
? msg.content.map(b => (typeof b === "string" ? b : b?.text || "")).join("\n")
|
||||||
|
: "";
|
||||||
|
if (text.trim()) systemBlocks.push({ type: "text", text });
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
messages.push(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (systemBlocks.length > 0) {
|
||||||
|
const existing = Array.isArray(body.system)
|
||||||
|
? body.system
|
||||||
|
: typeof body.system === "string" && body.system.trim()
|
||||||
|
? [{ type: "text", text: body.system }]
|
||||||
|
: [];
|
||||||
|
body.system = [...existing, ...systemBlocks];
|
||||||
|
body.messages = messages;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return body;
|
||||||
|
}
|
||||||
|
|
||||||
const CLAUDE_FORMAT_PROVIDERS_WITHOUT_OUTPUT_CONFIG = new Set(["minimax", "minimax-cn"]);
|
const CLAUDE_FORMAT_PROVIDERS_WITHOUT_OUTPUT_CONFIG = new Set(["minimax", "minimax-cn"]);
|
||||||
|
|
||||||
// Prepare request for Claude format endpoints
|
// Prepare request for Claude format endpoints
|
||||||
|
|||||||
Reference in New Issue
Block a user