From d197ca20fa1dcf98511d85898f9c691b42621b83 Mon Sep 17 00:00:00 2001 From: ryan Date: Fri, 18 Sep 2026 16:13:28 +0000 Subject: [PATCH] fix(ui): type transformModelData output as ModelData so the dashboard build typechecks Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- .../components/AllModelsTable.test.tsx | 14 +++--- .../utils/modelDataTransformer.ts | 43 +++++++++---------- .../src/components/ModelInfoEditForm.tsx | 2 +- .../src/components/model_dashboard/types.ts | 9 ++-- .../src/components/model_info_view.tsx | 2 +- .../models/ModelPricingSummary.test.tsx | 12 +++--- 6 files changed, 40 insertions(+), 42 deletions(-) diff --git a/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/components/AllModelsTable.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/components/AllModelsTable.test.tsx index 1f18d9cb4e6..3c054831d99 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/components/AllModelsTable.test.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/components/AllModelsTable.test.tsx @@ -11,8 +11,8 @@ const makeModel = (overrides: Partial = {}): ModelData => model_name: "gpt-4-public", litellm_model_name: "openai/gpt-4", provider: "openai", - input_cost: 30 as unknown as number, - output_cost: 60 as unknown as number, + input_cost: "30", + output_cost: "60", max_tokens: 8192, max_input_tokens: 8192, litellm_params: { model: "openai/gpt-4" }, @@ -178,7 +178,7 @@ describe("AllModelsTable", () => { rerender( , ); expect(screen.queryByText(/^\$/)).not.toBeInTheDocument(); @@ -190,8 +190,8 @@ describe("AllModelsTable", () => { {...baseProps} data={[ makeModel({ - input_cost: "0.00" as unknown as number, - output_cost: "0.00" as unknown as number, + input_cost: "0.00", + output_cost: "0.00", output_cost_per_second: 0.4, }), ]} @@ -205,8 +205,8 @@ describe("AllModelsTable", () => { {...baseProps} data={[ makeModel({ - input_cost: "0.60" as unknown as number, - output_cost: "0.00" as unknown as number, + input_cost: "0.60", + output_cost: "0.00", output_cost_per_second: 0.015, }), ]} diff --git a/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/utils/modelDataTransformer.ts b/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/utils/modelDataTransformer.ts index ef76a506a18..d33b9f91e2f 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/utils/modelDataTransformer.ts +++ b/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/utils/modelDataTransformer.ts @@ -1,4 +1,4 @@ -import { PerSecondCostTier } from "@/components/model_dashboard/types"; +import { LiteLLMParams, ModelData, ModelInfo, PerSecondCostTier } from "@/components/model_dashboard/types"; const PER_SECOND_TIER_KEY = /^output_cost_per_second_(.+)$/; @@ -8,15 +8,11 @@ export const perSecondCostTiers = (modelInfo: Record | null | u return resolution !== undefined && typeof value === "number" ? [{ resolution, cost: value }] : []; }); -interface RawLitellmParams { - model?: string; - custom_llm_provider?: string; - api_base?: string; +interface RawLitellmParams extends LiteLLMParams { output_cost_per_second?: number; - [key: string]: unknown; } -interface RawModelInfo { +interface RawModelInfo extends ModelInfo { input_cost_per_token?: number | null; output_cost_per_token?: number | null; output_cost_per_second?: number; @@ -25,14 +21,15 @@ interface RawModelInfo { [key: string]: unknown; } -interface RawModel { - litellm_params?: RawLitellmParams; - model_info?: RawModelInfo; +export interface RawModel { + model_name: string; + litellm_params: RawLitellmParams; + model_info: RawModelInfo; [key: string]: unknown; } -const costPerMillionTokens = (costPerToken: number | null | undefined) => - costPerToken == null ? costPerToken : (Number(costPerToken) * 1000000).toFixed(2); +const costPerMillionTokens = (costPerToken: number | null | undefined): string | null => + costPerToken == null ? null : (Number(costPerToken) * 1000000).toFixed(2); const resolveProvider = ( litellmModelName: string | null | undefined, @@ -45,24 +42,24 @@ const resolveProvider = ( return splitModel.length === 1 ? getProviderFromModel(litellmModelName) : splitModel[0]; }; -const transformModel = (rawModel: RawModel, getProviderFromModel: (model: string) => string) => { +const transformModel = (rawModel: RawModel, getProviderFromModel: (model: string) => string): ModelData => { const model: RawModel = JSON.parse(JSON.stringify(rawModel)); const litellmParams = model.litellm_params; const modelInfo = model.model_info; return { ...model, - provider: resolveProvider(litellmParams?.model, litellmParams?.custom_llm_provider, getProviderFromModel), - input_cost: modelInfo ? costPerMillionTokens(modelInfo.input_cost_per_token) : null, - output_cost: modelInfo ? costPerMillionTokens(modelInfo.output_cost_per_token) : null, - output_cost_per_second: litellmParams?.output_cost_per_second ?? modelInfo?.output_cost_per_second ?? null, + provider: resolveProvider(litellmParams.model, litellmParams.custom_llm_provider, getProviderFromModel), + input_cost: costPerMillionTokens(modelInfo?.input_cost_per_token), + output_cost: costPerMillionTokens(modelInfo?.output_cost_per_token), + output_cost_per_second: litellmParams.output_cost_per_second ?? modelInfo?.output_cost_per_second ?? null, output_cost_per_second_tiers: perSecondCostTiers(modelInfo), - litellm_model_name: litellmParams?.model, - max_tokens: modelInfo ? modelInfo.max_tokens : "Undefined", - max_input_tokens: modelInfo ? modelInfo.max_input_tokens : "Undefined", - api_base: litellmParams?.api_base, + litellm_model_name: litellmParams.model, + max_tokens: modelInfo?.max_tokens, + max_input_tokens: modelInfo?.max_input_tokens, + api_base: litellmParams.api_base, cleanedLitellmParams: Object.fromEntries( - Object.entries(litellmParams ?? {}).filter(([key]) => key !== "model" && key !== "api_base"), + Object.entries(litellmParams).filter(([key]) => key !== "model" && key !== "api_base"), ), }; }; @@ -70,7 +67,7 @@ const transformModel = (rawModel: RawModel, getProviderFromModel: (model: string export const transformModelData = ( rawModelData: { data?: RawModel[] | null } | null | undefined, getProviderFromModel: (model: string) => string, -) => { +): { data: ModelData[] } => { if (!rawModelData?.data) return { data: [] }; return { data: rawModelData.data.map((rawModel) => transformModel(rawModel, getProviderFromModel)) }; diff --git a/ui/litellm-dashboard/src/components/ModelInfoEditForm.tsx b/ui/litellm-dashboard/src/components/ModelInfoEditForm.tsx index fb4d90b5ea2..4f0af75ef75 100644 --- a/ui/litellm-dashboard/src/components/ModelInfoEditForm.tsx +++ b/ui/litellm-dashboard/src/components/ModelInfoEditForm.tsx @@ -270,7 +270,7 @@ const displayCost = (localModelData: any, field: TouchedPricingField): string => interface ModelInfoEditFormProps { localModelData: any; - modelData: { model_info: { team_id?: string | null } & Record }; + modelData: { model_info: { team_id?: string | null } }; teamAlias: string | null; accessToken: string | null; isEditing: boolean; diff --git a/ui/litellm-dashboard/src/components/model_dashboard/types.ts b/ui/litellm-dashboard/src/components/model_dashboard/types.ts index dd9a5b36058..f580e31a933 100644 --- a/ui/litellm-dashboard/src/components/model_dashboard/types.ts +++ b/ui/litellm-dashboard/src/components/model_dashboard/types.ts @@ -13,6 +13,7 @@ export interface ModelInfo { access_groups: string[] | null; blocked?: boolean; team_public_model_name?: string; + key?: string; } export interface LiteLLMParams { @@ -30,12 +31,12 @@ export interface ModelData { model_name: string; provider: string; litellm_model_name: string; - input_cost: number; - output_cost: number; + input_cost: string | null; + output_cost: string | null; output_cost_per_second?: number | null; output_cost_per_second_tiers?: PerSecondCostTier[]; - max_tokens: number; - max_input_tokens: number; + max_tokens?: number; + max_input_tokens?: number; api_base?: string; litellm_params: LiteLLMParams; cleanedLitellmParams: Record; diff --git a/ui/litellm-dashboard/src/components/model_info_view.tsx b/ui/litellm-dashboard/src/components/model_info_view.tsx index f4b6744b42e..ee3ac2ecbcb 100644 --- a/ui/litellm-dashboard/src/components/model_info_view.tsx +++ b/ui/litellm-dashboard/src/components/model_info_view.tsx @@ -365,7 +365,7 @@ export default function ModelInfoView({ // Parse the model_info from the form values let updatedModelInfo; try { - updatedModelInfo = values.model_info ? JSON.parse(values.model_info) : modelData.model_info; + updatedModelInfo = values.model_info ? JSON.parse(values.model_info) : modelData?.model_info; // Update access_groups from the form if (values.model_access_group) { updatedModelInfo = { diff --git a/ui/litellm-dashboard/src/components/molecules/models/ModelPricingSummary.test.tsx b/ui/litellm-dashboard/src/components/molecules/models/ModelPricingSummary.test.tsx index 5832f4cd17b..7cccea2a540 100644 --- a/ui/litellm-dashboard/src/components/molecules/models/ModelPricingSummary.test.tsx +++ b/ui/litellm-dashboard/src/components/molecules/models/ModelPricingSummary.test.tsx @@ -3,7 +3,7 @@ import { describe, it, expect } from "vitest"; import { render, screen } from "@testing-library/react"; import { ModelPricingSummary } from "./ModelPricingSummary"; -const tokenPriced = { input_cost: "1.50" as unknown as number, output_cost: "2.00" as unknown as number }; +const tokenPriced = { input_cost: "1.50", output_cost: "2.00" }; describe("ModelPricingSummary", () => { it("shows per-million-token rates for a token priced model", () => { @@ -16,8 +16,8 @@ describe("ModelPricingSummary", () => { render( { render( , @@ -49,7 +49,7 @@ describe("ModelPricingSummary", () => { it("renders a dash when the model has no pricing at all", () => { render( - , + , ); expect(screen.getByText("-")).toBeInTheDocument(); expect(screen.queryByText(/\$/)).not.toBeInTheDocument();