fix: Claude passthrough (#1359), kimi-k2 reasoning (#1360), thinking leak (#1361), Ollama redirect (#1381)

- Eliminate lossy Claude→OpenAI→Claude round-trip for Claude-format providers
- Expand isReasoner to include kimi-k2 and opencode-go provider models
- Block thinking param leak to non-Claude antigravity models (gemini, gpt-oss)
- Allow redirects for Ollama Cloud /v1/models endpoint (301)
This commit is contained in:
diegosouzapw
2026-04-18 04:33:50 -03:00
parent 15abfe61ec
commit 4a930e7966
4 changed files with 17 additions and 44 deletions

View File

@@ -1164,52 +1164,16 @@ export async function handleChatCore({
preserveCacheControl,
});
log?.debug?.("FORMAT", "claude-code-compatible bridge enabled");
} else if (isClaudePassthrough && preserveCacheControl) {
// Pure passthrough: when preserveCacheControl is true, forward the body
// as-is without prior normalization. The OpenAI round-trip would strip
// cache_control markers; even prepareClaudeRequest can alter structure.
// Claude Code sends well-formed Messages API payloads — trust it.
} else if (isClaudePassthrough) {
// Pure passthrough: forward the body as-is without OpenAI round-trip.
// The Claude→OpenAI→Claude double translation was lossy and corrupted
// payloads at high context (150+ msgs, 100+ tools). Fix: #1359.
// Claude Code sends well-formed Messages API payloads — trust them
// regardless of combo strategy or cache_control settings.
translatedBody = { ...body };
translatedBody._disableToolPrefix = true;
log?.debug?.("FORMAT", "claude passthrough with cache_control preservation");
} else if (isClaudePassthrough) {
// Claude OAuth expects the same Claude Code prompt + structural normalization
// as the OpenAI-compatible chat path. Round-trip through OpenAI to reuse the
// working Claude translator instead of forwarding raw Messages payloads.
const normalizeToolCallId = getModelNormalizeToolCallId(
provider || "",
model || "",
sourceFormat
);
const preserveDeveloperRole = getModelPreserveOpenAIDeveloperRole(
provider || "",
model || "",
sourceFormat
);
translatedBody = translateRequest(
FORMATS.CLAUDE,
FORMATS.OPENAI,
model,
{ ...body },
stream,
credentials,
provider,
reqLogger,
{ normalizeToolCallId, preserveDeveloperRole, preserveCacheControl }
);
translatedBody = translateRequest(
FORMATS.OPENAI,
FORMATS.CLAUDE,
model,
{ ...translatedBody, _disableToolPrefix: true },
stream,
credentials,
provider,
reqLogger,
{ normalizeToolCallId, preserveDeveloperRole, preserveCacheControl }
);
log?.debug?.("FORMAT", "claude->openai->claude normalized passthrough");
log?.debug?.("FORMAT", `claude passthrough (preserveCache=${preserveCacheControl})`);
} else {
translatedBody = { ...body };

View File

@@ -207,7 +207,9 @@ export function translateRequest(
// Inject reasoning_content = "" for DeepSeek/Reasoning models assistant messages with tool_calls
// if omitted by the client, to avoid upstream 400 errors (e.g. "Messages with role 'assistant' that contain tool_calls must also include reasoning_content")
const isReasoner =
provider === "deepseek" || (typeof model === "string" && /r1|reason/i.test(model));
provider === "deepseek" ||
provider === "opencode-go" ||
(typeof model === "string" && /r1|reason|kimi-k2/i.test(model));
if (isReasoner && result.messages && Array.isArray(result.messages)) {
for (const msg of result.messages) {
if (

View File

@@ -892,6 +892,8 @@ export async function GET(
...SAFE_OUTBOUND_FETCH_PRESETS.modelsPagination,
guard: getProviderOutboundGuard(),
proxyConfig: proxy,
// Ollama Cloud /v1/models returns 301 redirects (#1381)
...(provider === "ollama-cloud" ? { allowRedirect: true } : {}),
...fetchOptions,
});

View File

@@ -11,6 +11,11 @@ const REASONING_UNSUPPORTED_PATTERNS = [
"antigravity/claude-sonnet-4-6",
"antigravity/claude-sonnet-4-5",
"antigravity/claude-sonnet-4",
// Non-Claude antigravity models don't support thinking params (#1361)
"antigravity/gemini-",
"antigravity/gpt-oss-",
"antigravity/gemini-3",
"antigravity/tab_",
];
type CapabilityInput =