mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-03 05:45:04 +03:00
fix(translator): strip Claude output_config before MiniMax (#4448)
Integrated into release/v3.8.32
This commit is contained in:
committed by
GitHub
parent
0403f29805
commit
9708feddc9
@@ -19,6 +19,7 @@ _In development — bullets added per PR; finalized at release._
|
||||
- **fix(usage):** reuse Gemini CLI project ID for quota checks (avoid re-discovery). (thanks @Delcado19)
|
||||
- **fix(dashboard):** surface manual config CTA when Claude CLI detection fails (remote deployments). (thanks @anuragg-saxenaa)
|
||||
- **fix(executors):** granular reasoning_effort handling for Claude models on GitHub Copilot. (thanks @baslr)
|
||||
- **fix(translator):** strip Claude output_config before MiniMax (rejected upstream). (thanks @hiepau1231)
|
||||
- **fix(combo): round-robin members fail over faster under concurrency saturation via a configurable queue depth** — when a round-robin combo member was saturated, requests sat in the per-model semaphore's **unbounded** queue and only failed over to the next member after the full `queueTimeoutMs` (default 30s) elapsed — so a burst of agentic requests deep-queued one hot member instead of spilling to healthy ones. The per-model semaphore now accepts a bounded queue depth and emits `SEMAPHORE_QUEUE_FULL` once it is full (the round-robin loop already cascades on that code), so a configured low depth fails over immediately. A new `queueDepth` combo-config knob (global default / provider override / per-combo, default **20** for backward compatibility; **0** = never queue → fail over now) is exposed in Settings → Combo Defaults. ([#3872](https://github.com/diegosouzapw/OmniRoute/issues/3872) — thanks @KooshaPari)
|
||||
- **fix(pricing): align Claude Code (`cc`) pricing with current Anthropic per-MTok rates** — the `cc` provider block in the default pricing table had stale numbers across every Claude 4.x family entry — most visibly, `claude-opus-4-5-20251101` was billed at the deprecated Opus 4.1 rate (`input $15` / `output $75`), and `claude-haiku-4-5-20251001` was at half the current Haiku 4.5 rate. The `cached` (cache hit) and `cache_creation` (5-minute cache write) multipliers were also off across Opus 4.6/4.7/4.8, Sonnet 4.5/4.6, Haiku 4.5, and Fable 5. All eight entries now match the rates Anthropic publishes (input, 5m cache write at 1.25x input, cache hit at 0.1x input, output; reasoning billed at the output rate), so cost accounting on the dashboard and per-request usage events stop under- or over-reporting Claude Code spend. (thanks @chulanpro5)
|
||||
|
||||
|
||||
@@ -2,6 +2,17 @@
|
||||
import { DEFAULT_THINKING_CLAUDE_SIGNATURE } from "../../config/defaultThinkingSignature.ts";
|
||||
import { lookupReasoning, recordReplay } from "../../services/reasoningCache.ts";
|
||||
|
||||
// MiniMax exposes a Claude-compatible endpoint but rejects Anthropic's extended
|
||||
// `output_config` parameter (used to steer reasoning effort and structured output)
|
||||
// with a generic 400 "invalid params" response. Strip the entire field before
|
||||
// dispatching Claude-shape requests to these providers. Anthropic Claude and
|
||||
// other Claude-compatible upstreams that do accept it are unaffected.
|
||||
// Ported from upstream decolua/9router#820 by @hiepau1231.
|
||||
const CLAUDE_FORMAT_PROVIDERS_WITHOUT_OUTPUT_CONFIG = new Set<string>([
|
||||
"minimax",
|
||||
"minimax-cn",
|
||||
]);
|
||||
|
||||
// Placeholder thinking text used as last-resort fallback when:
|
||||
// - Target upstream is a non-Anthropic Claude-shape provider
|
||||
// (kimi-coding, glmt, zai, …) that rejects redacted_thinking blobs
|
||||
@@ -211,6 +222,13 @@ export function prepareClaudeRequest(
|
||||
provider: string | null = null,
|
||||
preserveCacheControl = false
|
||||
): ClaudeRequestBody {
|
||||
// 0. Strip Anthropic `output_config` for providers that reject it on their
|
||||
// Claude-compatible endpoints (MiniMax). Must run before any downstream
|
||||
// processing so the field never reaches translateRequest/the executor.
|
||||
if (provider && CLAUDE_FORMAT_PROVIDERS_WITHOUT_OUTPUT_CONFIG.has(provider)) {
|
||||
delete body.output_config;
|
||||
}
|
||||
|
||||
// 1. System: remove all cache_control, add only to last block with ttl 1h
|
||||
// In passthrough mode, preserve existing cache_control markers
|
||||
const supportsPromptCaching =
|
||||
|
||||
53
tests/unit/claude-helper-minimax-output-config.test.ts
Normal file
53
tests/unit/claude-helper-minimax-output-config.test.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
// Port of upstream decolua/9router#820 by @hiepau1231.
|
||||
// MiniMax exposes a Claude-compatible endpoint but rejects Anthropic's
|
||||
// extended `output_config` parameter (used to steer reasoning effort and
|
||||
// structured output) with a generic 400 "invalid params" response.
|
||||
// `prepareClaudeRequest()` must strip the entire `output_config` for
|
||||
// MiniMax providers, while preserving it verbatim for Anthropic Claude
|
||||
// and other Claude-compatible upstreams that already accept it.
|
||||
import { describe, test } from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import { prepareClaudeRequest } from "../../open-sse/translator/helpers/claudeHelper.ts";
|
||||
|
||||
describe("prepareClaudeRequest output_config stripping for MiniMax", () => {
|
||||
const buildBody = () => ({
|
||||
model: "MiniMax-M2.7",
|
||||
system: [{ type: "text", text: "You are helpful." }],
|
||||
messages: [{ role: "user", content: [{ type: "text", text: "continue" }] }],
|
||||
max_tokens: 1024,
|
||||
output_config: {
|
||||
effort: "medium",
|
||||
format: {
|
||||
type: "json_schema",
|
||||
schema: {
|
||||
type: "object",
|
||||
properties: { title: { type: "string" } },
|
||||
required: ["title"],
|
||||
additionalProperties: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
test("strips output_config (effort + format) for minimax", () => {
|
||||
const body = buildBody();
|
||||
const result = prepareClaudeRequest(body as any, "minimax");
|
||||
assert.equal(result.output_config, undefined);
|
||||
// Sanity: rest of the request must still be intact.
|
||||
assert.equal(result.messages?.[0]?.content?.[0]?.text, "continue");
|
||||
assert.equal(result.max_tokens, 1024);
|
||||
});
|
||||
|
||||
test("strips output_config (effort + format) for minimax-cn", () => {
|
||||
const body = buildBody();
|
||||
const result = prepareClaudeRequest(body as any, "minimax-cn");
|
||||
assert.equal(result.output_config, undefined);
|
||||
});
|
||||
|
||||
test("preserves output_config for Anthropic Claude", () => {
|
||||
const body = buildBody();
|
||||
const original = JSON.parse(JSON.stringify(body.output_config));
|
||||
const result = prepareClaudeRequest(body as any, "claude");
|
||||
assert.deepEqual(result.output_config, original);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user