mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-20 14:12:59 +03:00
fix(anthropic): strip top_p when temperature is set to avoid 400 (#3691)
Integrated into release/v3.8.23
This commit is contained in:
@@ -1986,12 +1986,13 @@ export async function handleChatCore({
|
||||
// Use credentials.connectionId as a fallback so that requests without an
|
||||
// explicit session-level connectionId still register in the pendingRequests map.
|
||||
const pendingConnId = connectionId || credentials?.connectionId || null;
|
||||
const pendingRequestId = trackPendingRequest(model, provider, pendingConnId, true, {
|
||||
clientEndpoint: clientRawRequest?.endpoint || "/v1/chat/completions",
|
||||
clientRequest: clientRawRequest?.body ?? body,
|
||||
providerRequest: initialProviderRequest,
|
||||
stage: "registered",
|
||||
}) || generateRequestId();
|
||||
const pendingRequestId =
|
||||
trackPendingRequest(model, provider, pendingConnId, true, {
|
||||
clientEndpoint: clientRawRequest?.endpoint || "/v1/chat/completions",
|
||||
clientRequest: clientRawRequest?.body ?? body,
|
||||
providerRequest: initialProviderRequest,
|
||||
stage: "registered",
|
||||
}) || generateRequestId();
|
||||
|
||||
// Initialize rate limit settings from persisted DB (once, lazy)
|
||||
await initializeRateLimits();
|
||||
@@ -2769,7 +2770,10 @@ export async function handleChatCore({
|
||||
comboTargetLimits,
|
||||
});
|
||||
contextLimit = resolved.limit;
|
||||
log?.info?.("CONTEXT", `Combo context limit: ${resolved.limit} (source=${resolved.source})`);
|
||||
log?.info?.(
|
||||
"CONTEXT",
|
||||
`Combo context limit: ${resolved.limit} (source=${resolved.source})`
|
||||
);
|
||||
} catch (err) {
|
||||
log?.warn?.("CONTEXT", "Failed to resolve combo limits for compression: " + err);
|
||||
}
|
||||
@@ -3104,6 +3108,12 @@ export async function handleChatCore({
|
||||
translatedBody.messages,
|
||||
DEFAULT_THINKING_CLAUDE_SIGNATURE
|
||||
) as typeof translatedBody.messages;
|
||||
|
||||
// Anthropic API rejects requests with both temperature and top_p.
|
||||
// VS Code Claude extension and similar clients send both; strip top_p.
|
||||
if (translatedBody.temperature !== undefined && translatedBody.top_p !== undefined) {
|
||||
delete translatedBody.top_p;
|
||||
}
|
||||
}
|
||||
|
||||
// Fix #2468: always extract role:"system" → top-level system.
|
||||
@@ -5442,17 +5452,14 @@ export async function handleChatCore({
|
||||
}
|
||||
|
||||
const responseHeaders: Record<string, string> = {
|
||||
...buildStreamingResponseHeaders(
|
||||
providerResponse.headers,
|
||||
{
|
||||
provider,
|
||||
model,
|
||||
cacheHit: false,
|
||||
latencyMs: 0,
|
||||
usage: null,
|
||||
costUsd: 0,
|
||||
}
|
||||
),
|
||||
...buildStreamingResponseHeaders(providerResponse.headers, {
|
||||
provider,
|
||||
model,
|
||||
cacheHit: false,
|
||||
latencyMs: 0,
|
||||
usage: null,
|
||||
costUsd: 0,
|
||||
}),
|
||||
"x-omniroute-request-id": pendingRequestId,
|
||||
};
|
||||
|
||||
@@ -5560,7 +5567,9 @@ export async function handleChatCore({
|
||||
});
|
||||
} catch (e) {
|
||||
// Best-effort — don't break the stream completion path if this fails
|
||||
try { console.warn("finalizeMostRecentPendingRequest failed:", e && (e.message || e)); } catch {}
|
||||
try {
|
||||
console.warn("finalizeMostRecentPendingRequest failed:", e && (e.message || e));
|
||||
} catch {}
|
||||
}
|
||||
|
||||
if (apiKeyInfo?.id && streamUsage) {
|
||||
|
||||
@@ -212,7 +212,7 @@ export function openaiToClaudeRequest(model, body, stream) {
|
||||
if (body.temperature !== undefined) {
|
||||
result.temperature = body.temperature;
|
||||
}
|
||||
if (body.top_p !== undefined) {
|
||||
if (body.temperature === undefined && body.top_p !== undefined) {
|
||||
result.top_p = body.top_p;
|
||||
}
|
||||
if (body.stop !== undefined) {
|
||||
|
||||
@@ -84,7 +84,8 @@ test("OpenAI -> Claude maps system messages, parameters and assistant cache mark
|
||||
assert.equal(result.stream, true);
|
||||
assert.equal(result.max_tokens, 33);
|
||||
assert.equal(result.temperature, 0.25);
|
||||
assert.equal(result.top_p, 0.8);
|
||||
// top_p is stripped when temperature is also present (Anthropic rejects both).
|
||||
assert.equal(result.top_p, undefined);
|
||||
assert.deepEqual(result.stop_sequences, ["DONE"]);
|
||||
assert.equal(result.system[0].text, "Rule A\nRule B\nRule C");
|
||||
assert.equal(result.messages[0].role, "user");
|
||||
@@ -94,6 +95,21 @@ test("OpenAI -> Claude maps system messages, parameters and assistant cache mark
|
||||
assert.deepEqual(result.messages[1].content[0].cache_control, { type: "ephemeral" });
|
||||
});
|
||||
|
||||
test("OpenAI -> Claude strips top_p when temperature is also present", () => {
|
||||
const result = openaiToClaudeRequest(
|
||||
"claude-4-sonnet",
|
||||
{
|
||||
messages: [{ role: "user", content: "Hello" }],
|
||||
temperature: 0.25,
|
||||
top_p: 0.8,
|
||||
},
|
||||
false
|
||||
);
|
||||
|
||||
assert.equal(result.temperature, 0.25);
|
||||
assert.equal(result.top_p, undefined);
|
||||
});
|
||||
|
||||
test("OpenAI -> Claude converts multimodal content, tool declarations, tool calls and tool results", () => {
|
||||
const result = openaiToClaudeRequest(
|
||||
"claude-4-sonnet",
|
||||
|
||||
Reference in New Issue
Block a user