mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-10 03:28:53 +00:00
feat(ui): run the Anthropic Family preset's reasoning tier on Opus 5 at high thinking (#38490)
The preset put Fable 5 in REASONING, sitting above Opus in a Haiku to Sonnet to Opus ladder even though Fable is the lighter model. Run Opus 5 there instead, at high thinking, so the tier above COMPLEX is the same model thinking harder rather than a different and lighter one. This is the first bundled preset to carry tier_model_configs. The round trip was already built and unit tested, but nothing between the bundled JSON and the create payload asserted on it, so add that coverage here.
This commit is contained in:
parent
490face7de
commit
0fba05800d
3 changed files with 57 additions and 2 deletions
|
|
@ -1,13 +1,16 @@
|
|||
{
|
||||
"anthropic_family": {
|
||||
"label": "Anthropic Family",
|
||||
"description": "Routes across the Claude model family: Haiku for simple queries, Sonnet for medium, Opus for complex and reasoning-heavy requests.",
|
||||
"description": "Routes across the Claude model family: Haiku for simple queries, Sonnet for medium, Opus for complex, Opus at high thinking for reasoning.",
|
||||
"complexity_router_config": {
|
||||
"tiers": {
|
||||
"SIMPLE": ["claude-haiku-4-5"],
|
||||
"MEDIUM": ["claude-sonnet-5"],
|
||||
"COMPLEX": ["claude-opus-5"],
|
||||
"REASONING": ["claude-fable-5"]
|
||||
"REASONING": ["claude-opus-5"]
|
||||
},
|
||||
"tier_model_configs": {
|
||||
"REASONING": [{ "model_name": "claude-opus-5", "litellm_params": { "reasoning_effort": "high" } }]
|
||||
},
|
||||
"classifier_type": "heuristic",
|
||||
"escalation_keywords": ["LITELLM ESCALATE"],
|
||||
|
|
|
|||
|
|
@ -592,6 +592,28 @@ describe("AddAutoRouterTab", () => {
|
|||
});
|
||||
});
|
||||
|
||||
// Every step between the bundled JSON and the payload drops these params silently.
|
||||
it("carries a preset's per-tier reasoning effort through to the create payload", async () => {
|
||||
const user = userEvent.setup();
|
||||
mockFetchAvailableModels.mockResolvedValue(ALL_FAMILY_MODELS);
|
||||
|
||||
renderWithProviders(<Harness />);
|
||||
await waitForPresetEnabled("Anthropic Family");
|
||||
await selectTemplate("Anthropic Family");
|
||||
|
||||
await user.type(screen.getByPlaceholderText(/smart_router/i), "anthropic-router");
|
||||
await user.click(screen.getByRole("button", { name: /add auto router/i }));
|
||||
|
||||
await waitFor(() => expect(handleAddAutoRouterSubmit).toHaveBeenCalled());
|
||||
expect(vi.mocked(handleAddAutoRouterSubmit).mock.calls.at(-1)?.[0]).toMatchObject({
|
||||
complexity_router_config: {
|
||||
tier_model_configs: {
|
||||
REASONING: [{ model_name: "claude-opus-5", litellm_params: { reasoning_effort: "high" } }],
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
// Bugbot-found bug: submitBlockedReason disables the button for this, but Form's onFinish
|
||||
// (wired to the same handler as the button) fires whenever the form itself is submitted,
|
||||
// independent of the button's own disabled state. Without submitRecommendedRouter re-checking
|
||||
|
|
|
|||
|
|
@ -99,6 +99,36 @@ describe("autorouter_presets", () => {
|
|||
);
|
||||
});
|
||||
|
||||
// Opus serves both tiers, so the effort is all that separates them and losing it fails silently.
|
||||
it("pins the anthropic preset's reasoning tier to Opus at high thinking", () => {
|
||||
const config = getPresetByKey("anthropic_family")!.complexity_router_config;
|
||||
expect(config.tiers.COMPLEX).toEqual(["claude-opus-5"]);
|
||||
expect(config.tiers.REASONING).toEqual(["claude-opus-5"]);
|
||||
expect(config.tier_model_configs).toEqual({
|
||||
REASONING: [{ model_name: "claude-opus-5", litellm_params: { reasoning_effort: "high" } }],
|
||||
});
|
||||
});
|
||||
|
||||
// serializeTierModelConfigs filters on the tier's models, so a stray name drops silently.
|
||||
it("never names a model in tier_model_configs that its own tier does not hold", () => {
|
||||
for (const preset of getAllPresets()) {
|
||||
const { tiers, tier_model_configs: configs } = preset.complexity_router_config;
|
||||
for (const [tier, entries] of Object.entries(configs ?? {})) {
|
||||
for (const entry of entries) {
|
||||
expect(tiers[tier as keyof typeof tiers] ?? [], `${preset.key}.${tier}`).toContain(entry.model_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it("prefills the anthropic preset's effort through to tier_model_params", () => {
|
||||
const preset = getPresetByKey("anthropic_family")!;
|
||||
const prefill = buildPresetPrefill(preset.complexity_router_config, groupsOnly(getRequiredModelsInPreset(preset)));
|
||||
expect(prefill.complexityRouterConfig.tier_model_params).toEqual({
|
||||
REASONING: { "claude-opus-5": { reasoning_effort: "high" } },
|
||||
});
|
||||
});
|
||||
|
||||
it("pins the gemini preset to concrete model ids, never Google's hot-swapping -latest aliases", () => {
|
||||
const gemini = getPresetByKey("gemini_family")!;
|
||||
const config = gemini.complexity_router_config;
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue