From d994c090c70c72b92dfece901f8a185e3920640e Mon Sep 17 00:00:00 2001 From: Tin Chi Lo Date: Tue, 1 Sep 2026 19:54:10 -0700 Subject: [PATCH] fix(ui): hoist inline payload objects and sync generated API types --- .../add_model/add_auto_router_tab.tsx | 15 +- ...capability_router_tab.integration.test.tsx | 42 +++-- .../capability_router_config.test.ts | 5 +- .../edit_auto_router_modal.tsx | 22 +-- ui/litellm-dashboard/src/lib/http/schema.d.ts | 170 +++++++++++++++++- 5 files changed, 209 insertions(+), 45 deletions(-) diff --git a/ui/litellm-dashboard/src/components/add_model/add_auto_router_tab.tsx b/ui/litellm-dashboard/src/components/add_model/add_auto_router_tab.tsx index d3edae663a8..2ef47d300dd 100644 --- a/ui/litellm-dashboard/src/components/add_model/add_auto_router_tab.tsx +++ b/ui/litellm-dashboard/src/components/add_model/add_auto_router_tab.tsx @@ -473,14 +473,15 @@ const AddAutoRouterTab: React.FC = ({ toast.fromError(dryRunError); return; } + const capabilityRouterPayload: AddAutoRouterValues = { + auto_router_name: name, + ...teamScopePayload(requiresTeamScope, form.getValues("team_id")), + model_type: "capability_router", + capability_router_config: capabilityRouterConfig, + model_access_group: form.getValues("model_access_group"), + }; await handleAddAutoRouterSubmit( - { - auto_router_name: name, - ...teamScopePayload(requiresTeamScope, form.getValues("team_id")), - model_type: "capability_router", - capability_router_config: capabilityRouterConfig, - model_access_group: form.getValues("model_access_group"), - }, + capabilityRouterPayload, accessToken, () => form.reset(EMPTY_FORM_VALUES), handleOk, diff --git a/ui/litellm-dashboard/src/components/add_model/add_capability_router_tab.integration.test.tsx b/ui/litellm-dashboard/src/components/add_model/add_capability_router_tab.integration.test.tsx index aff26d0b01a..ccf64b5c2a4 100644 --- a/ui/litellm-dashboard/src/components/add_model/add_capability_router_tab.integration.test.tsx +++ b/ui/litellm-dashboard/src/components/add_model/add_capability_router_tab.integration.test.tsx @@ -26,28 +26,26 @@ vi.mock("@/app/(dashboard)/hooks/models/useModels", async (importOriginal) => { return { ...actual, fetchAllModelDeployments: mockFetchAllModelDeployments }; }); vi.mock("./handle_add_auto_router_submit", () => ({ handleAddAutoRouterSubmit: vi.fn() })); -vi.mock("./CapabilityRouterConfig", () => ({ - default: ({ onChange }: { onChange: (value: unknown) => void }) => ( - - ), -})); +vi.mock("./CapabilityRouterConfig", () => { + const filledConfig = { + candidates: [ + { model: "small", description: "Bounded extraction" }, + { model: "frontier", description: "Ambiguous multi-step work" }, + ], + classifier: { model: "classifier", timeout_ms: 3000, max_output_tokens: 1024 }, + probability_threshold: 0.75, + fallback_model: "frontier", + estimated_output_tokens: 1000, + cache_ttl_seconds: 3600, + }; + return { + default: ({ onChange }: { onChange: (value: unknown) => void }) => ( + + ), + }; +}); describe("AddAutoRouterTab capability flow", () => { beforeEach(() => { diff --git a/ui/litellm-dashboard/src/components/add_model/capability_router_config.test.ts b/ui/litellm-dashboard/src/components/add_model/capability_router_config.test.ts index b9508b1a855..450d4da290b 100644 --- a/ui/litellm-dashboard/src/components/add_model/capability_router_config.test.ts +++ b/ui/litellm-dashboard/src/components/add_model/capability_router_config.test.ts @@ -38,12 +38,13 @@ describe("capability router config", () => { }); it("hydrates stored values while supplying newly introduced defaults", () => { - const hydrated = hydrateCapabilityRouterConfig({ + const storedConfig = { candidates: validConfig().candidates, classifier: { model: "classifier" }, fallback_model: "frontier", probability_threshold: 0.8, - }); + }; + const hydrated = hydrateCapabilityRouterConfig(storedConfig); expect(hydrated.probability_threshold).toBe(0.8); expect(hydrated.classifier.timeout_ms).toBe(3000); diff --git a/ui/litellm-dashboard/src/components/edit_auto_router/edit_auto_router_modal.tsx b/ui/litellm-dashboard/src/components/edit_auto_router/edit_auto_router_modal.tsx index 37474abc957..eb091a26ef5 100644 --- a/ui/litellm-dashboard/src/components/edit_auto_router/edit_auto_router_modal.tsx +++ b/ui/litellm-dashboard/src/components/edit_auto_router/edit_auto_router_modal.tsx @@ -581,11 +581,12 @@ const EditAutoRouterModal: React.FC = ({ ...modelData.model_info, access_groups: values.model_access_group || [], }; - await modelPatchUpdateCall( - accessToken, - { model_name: values.auto_router_name, litellm_params: updatedLitellmParams, model_info: updatedModelInfo }, - modelData.model_info.id, - ); + const patchPayload = { + model_name: values.auto_router_name, + litellm_params: updatedLitellmParams, + model_info: updatedModelInfo, + }; + await modelPatchUpdateCall(accessToken, patchPayload, modelData.model_info.id); toast.success("Auto router configuration updated successfully"); onSuccess({ ...modelData, @@ -673,11 +674,12 @@ const EditAutoRouterModal: React.FC = ({ access_groups: values.model_access_group || [], }; - await modelPatchUpdateCall( - accessToken, - { model_name: values.auto_router_name, litellm_params: updatedLitellmParams, model_info: updatedModelInfo }, - modelData.model_info.id, - ); + const patchPayload = { + model_name: values.auto_router_name, + litellm_params: updatedLitellmParams, + model_info: updatedModelInfo, + }; + await modelPatchUpdateCall(accessToken, patchPayload, modelData.model_info.id); toast.success("Auto router configuration updated successfully"); onSuccess({ diff --git a/ui/litellm-dashboard/src/lib/http/schema.d.ts b/ui/litellm-dashboard/src/lib/http/schema.d.ts index 6f044fec3f3..aaa42e0f3c3 100644 --- a/ui/litellm-dashboard/src/lib/http/schema.d.ts +++ b/ui/litellm-dashboard/src/lib/http/schema.d.ts @@ -1382,6 +1382,26 @@ export interface paths { patch?: never; trace?: never; }; + "/auto_router/validate_capability_router_config": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** + * Validate Capability Router Config + * @description Validate a capability-router config without saving it. + */ + post: operations["validate_capability_router_config_auto_router_validate_capability_router_config_post"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; "/auto_router/validate_complexity_router_config": { parameters: { query?: never; @@ -23355,8 +23375,10 @@ export interface components { * validating them against one surface's schema would reject the others. */ AutoRouterRoutingTestRequest: { - /** @description The complexity router config to route against, in the shape /model/new accepts */ - complexity_router_config: components["schemas"]["RequestComplexityRouterConfig"]; + /** @description The capability router config to route against */ + capability_router_config?: components["schemas"]["CapabilityRouterConfig"] | null; + /** @description The complexity router config to route against */ + complexity_router_config?: components["schemas"]["RequestComplexityRouterConfig"] | null; /** * Default Model * @description Model to route to when no tier resolves, i.e. complexity_router_default_model @@ -24560,6 +24582,89 @@ export interface components { */ status: "cancelled"; }; + /** + * CapabilityClassifierConfig + * @description The model used to estimate each candidate's probability of success. + */ + CapabilityClassifierConfig: { + /** + * Max Message Chars + * @default 2000 + */ + max_message_chars: number; + /** + * Max Output Tokens + * @default 1024 + */ + max_output_tokens: number; + /** Model */ + model: string; + /** + * Timeout Ms + * @default 3000 + */ + timeout_ms: number; + }; + /** + * CapabilityRouterCandidate + * @description A model group and the operator's description of when it succeeds. + */ + CapabilityRouterCandidate: { + /** Description */ + description: string; + /** Model */ + model: string; + }; + /** + * CapabilityRouterConfig + * @description Operator configuration for cheapest-qualified model selection. + */ + CapabilityRouterConfig: { + /** + * Cache Ttl Seconds + * @default 3600 + */ + cache_ttl_seconds: number; + /** Candidates */ + candidates: components["schemas"]["CapabilityRouterCandidate"][]; + classifier: components["schemas"]["CapabilityClassifierConfig"]; + /** + * Estimated Output Tokens + * @default 1000 + */ + estimated_output_tokens: number; + /** Fallback Model */ + fallback_model: string; + /** + * Probability Threshold + * @default 0.7 + */ + probability_threshold: number; + /** + * Threshold Step + * @default 0.1 + */ + threshold_step: number; + }; + /** + * CapabilityRouterConfigValidationRequest + * @description A capability-router config to validate without saving. + */ + CapabilityRouterConfigValidationRequest: { + /** Capability Router Config */ + capability_router_config: { + [key: string]: unknown; + }; + /** Team Id */ + team_id?: string | null; + }; + /** CapabilityRouterConfigValidationResponse */ + CapabilityRouterConfigValidationResponse: { + /** Error */ + error?: string | null; + /** Valid */ + valid: boolean; + }; /** ChatCompletionAnnotation */ ChatCompletionAnnotation: { /** @@ -29043,6 +29148,10 @@ export interface components { cache_read_input_token_cost_priority?: number | null; /** Cache Read Input Token Cost Ultrafast */ cache_read_input_token_cost_ultrafast?: number | null; + /** Capability Router Config */ + capability_router_config?: { + [key: string]: unknown; + } | null; /** Citation Cost Per Token */ citation_cost_per_token?: number | null; /** Complexity Router Config */ @@ -35720,11 +35829,21 @@ export interface components { * @description Per-request provenance for a pre-routing strategy (auto-router) decision. */ StandardLoggingRoutingDecision: { + /** Cached */ + cached?: boolean; + /** Candidate Costs */ + candidate_costs?: { + [key: string]: number; + }; + /** Candidate Probabilities */ + candidate_probabilities?: { + [key: string]: number; + }; /** * Cause * @enum {string} */ - cause?: "heuristic_scorer" | "reasoning_override" | "llm_classifier" | "heuristic_first_short_circuit" | "classifier_plugin" | "classifier_fallback" | "default_model_fallback" | "literal_keyword_match" | "semantic_keyword_match" | "plan_mode" | "housekeeping" | "modality_escalation" | "session_affinity_pin" | "session_affinity_escalation" | "user_turn_continuation" | "default_fallback" | "keyword" | "quality_tier" | "bandit"; + cause?: "heuristic_scorer" | "reasoning_override" | "llm_classifier" | "heuristic_first_short_circuit" | "classifier_plugin" | "classifier_fallback" | "default_model_fallback" | "literal_keyword_match" | "semantic_keyword_match" | "plan_mode" | "housekeeping" | "modality_escalation" | "session_affinity_pin" | "session_affinity_escalation" | "user_turn_continuation" | "default_fallback" | "keyword" | "quality_tier" | "bandit" | "capability_classifier" | "capability_cache" | "capability_fallback"; /** Classifier Cost */ classifier_cost?: number; /** Classifier Model */ @@ -35739,8 +35858,14 @@ export interface components { escalated?: boolean; /** Escalation Keyword */ escalation_keyword?: string; + /** Fallback Reason */ + fallback_reason?: string | null; /** Matched Keyword */ matched_keyword?: string; + /** Probability Threshold */ + probability_threshold?: number; + /** Qualified Models */ + qualified_models?: string[]; /** Reasoning Override Min Score */ reasoning_override_min_score?: number; /** Request Type */ @@ -35753,7 +35878,7 @@ export interface components { * Router Type * @enum {string} */ - router_type?: "complexity" | "adaptive" | "quality"; + router_type?: "complexity" | "adaptive" | "quality" | "capability"; /** Savings Baseline Deployment Id */ savings_baseline_deployment_id?: string; /** Savings Baseline Model */ @@ -38872,6 +38997,10 @@ export interface components { cache_read_input_token_cost_priority?: number | null; /** Cache Read Input Token Cost Ultrafast */ cache_read_input_token_cost_ultrafast?: number | null; + /** Capability Router Config */ + capability_router_config?: { + [key: string]: unknown; + } | null; /** Citation Cost Per Token */ citation_cost_per_token?: number | null; /** Complexity Router Config */ @@ -40981,6 +41110,39 @@ export interface operations { }; }; }; + validate_capability_router_config_auto_router_validate_capability_router_config_post: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["CapabilityRouterConfigValidationRequest"]; + }; + }; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["CapabilityRouterConfigValidationResponse"]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; validate_complexity_router_config_auto_router_validate_complexity_router_config_post: { parameters: { query?: never;