diff --git a/changelog.d/fixes/12379-groq-compound-allam-no-reasoning.md b/changelog.d/fixes/12379-groq-compound-allam-no-reasoning.md new file mode 100644 index 0000000000..d9db32cc6b --- /dev/null +++ b/changelog.d/fixes/12379-groq-compound-allam-no-reasoning.md @@ -0,0 +1 @@ +- **fix(providers):** declare `groq/compound` and `allam-2-7b` as non-reasoning models in the curated Groq registry so `reasoning_effort` / `output_config.effort` / `thinking` from Claude Code are stripped instead of forwarded, which Groq rejected with HTTP 400 ([#12134](https://github.com/diegosouzapw/OmniRoute/issues/12134)) diff --git a/open-sse/config/providers/registry/groq/index.ts b/open-sse/config/providers/registry/groq/index.ts index 07fa17d666..974e24e710 100644 --- a/open-sse/config/providers/registry/groq/index.ts +++ b/open-sse/config/providers/registry/groq/index.ts @@ -16,6 +16,10 @@ export const groqProvider: RegistryEntry = { supportsReasoning: false, }, { id: "llama-3.3-70b-versatile", name: "Llama 3.3 70B", supportsReasoning: false }, + // Same class (#12134): compound and ALLaM are not reasoning models on Groq either, so + // declare it here — undeclared models default to reasoning-capable via the heuristic. + { id: "groq/compound", name: "Groq Compound", supportsReasoning: false }, + { id: "allam-2-7b", name: "ALLaM 2 7B", supportsReasoning: false }, { id: "openai/gpt-oss-120b", name: "GPT-OSS 120B" }, { id: "openai/gpt-oss-20b", name: "GPT-OSS 20B" }, { id: "qwen/qwen3-32b", name: "Qwen3 32B" }, diff --git a/tests/unit/thinking-budget-groq-12134.test.ts b/tests/unit/thinking-budget-groq-12134.test.ts new file mode 100644 index 0000000000..c9b6aec646 --- /dev/null +++ b/tests/unit/thinking-budget-groq-12134.test.ts @@ -0,0 +1,61 @@ +import test from "node:test"; +import assert from "node:assert/strict"; + +const { applyThinkingBudget, setThinkingBudgetConfig, ThinkingMode, DEFAULT_THINKING_CONFIG } = + await import("../../open-sse/services/thinkingBudget.ts"); + +// Regression coverage for #12134 (same class as #3258): Claude Code → Groq failed with +// `reasoning_effort` HTTP 400 for `groq/compound` and `allam-2-7b`. Neither model was in the +// curated Groq registry, so the capability heuristic defaulted them to reasoning-capable and +// `reasoning_effort` (derived from Claude Code's `output_config.effort`) was forwarded verbatim. +// Both must now be declared `supportsReasoning: false` so the field is stripped, while reasoning +// models (gpt-oss) keep it. + +test("#12134 groq/groq/compound strips reasoning_effort", () => { + setThinkingBudgetConfig({ mode: ThinkingMode.PASSTHROUGH }); + const out = applyThinkingBudget({ + model: "groq/groq/compound", + messages: [{ role: "user", content: "hi" }], + reasoning_effort: "medium", + }) as Record; + assert.equal(out.reasoning_effort, undefined, "reasoning_effort must be stripped for compound"); + setThinkingBudgetConfig(DEFAULT_THINKING_CONFIG); +}); + +test("#12134 groq/groq/compound strips output_config.effort and thinking", () => { + setThinkingBudgetConfig({ mode: ThinkingMode.PASSTHROUGH }); + const out = applyThinkingBudget({ + model: "groq/groq/compound", + messages: [{ role: "user", content: "hi" }], + output_config: { effort: "high" }, + thinking: { type: "enabled", budget_tokens: 10240 }, + }) as Record; + assert.equal(out.thinking, undefined, "thinking must be stripped"); + assert.ok( + !out.output_config || out.output_config.effort === undefined, + "output_config.effort must be stripped (else claude→openai re-injects reasoning_effort)" + ); + setThinkingBudgetConfig(DEFAULT_THINKING_CONFIG); +}); + +test("#12134 groq/allam-2-7b strips reasoning_effort", () => { + setThinkingBudgetConfig({ mode: ThinkingMode.PASSTHROUGH }); + const out = applyThinkingBudget({ + model: "groq/allam-2-7b", + messages: [{ role: "user", content: "hi" }], + reasoning_effort: "low", + }) as Record; + assert.equal(out.reasoning_effort, undefined, "reasoning_effort must be stripped for allam"); + setThinkingBudgetConfig(DEFAULT_THINKING_CONFIG); +}); + +test("#12134 groq/openai/gpt-oss-20b KEEPS reasoning_effort (reasoning model — no regression)", () => { + setThinkingBudgetConfig({ mode: ThinkingMode.PASSTHROUGH }); + const out = applyThinkingBudget({ + model: "groq/openai/gpt-oss-20b", + messages: [{ role: "user", content: "hi" }], + reasoning_effort: "high", + }) as Record; + assert.equal(out.reasoning_effort, "high", "gpt-oss is a reasoning model — must keep the field"); + setThinkingBudgetConfig(DEFAULT_THINKING_CONFIG); +});