diff --git a/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/components/AllModelsTab.tsx b/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/components/AllModelsTab.tsx
index a5553fa5f5..1df476a8a0 100644
--- a/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/components/AllModelsTab.tsx
+++ b/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/components/AllModelsTab.tsx
@@ -7,13 +7,14 @@ import { columns } from "@/components/molecules/models/columns";
import { getDisplayModelName } from "@/components/view_model/model_name_display";
import { InfoCircleOutlined } from "@ant-design/icons";
import { PaginationState, SortingState } from "@tanstack/react-table";
-import { Grid, Select, SelectItem, TabPanel, Text } from "@tremor/react";
-import { Skeleton, Spin } from "antd";
+import { Grid, TabPanel } from "@tremor/react";
+import { Badge, Select, Skeleton, Space, Typography } from "antd";
import debounce from "lodash/debounce";
import { useEffect, useMemo, useState } from "react";
import { useModelsInfo } from "../../hooks/models/useModels";
import { transformModelData } from "../utils/modelDataTransformer";
type ModelViewMode = "all" | "current_team";
+const { Text } = Typography;
interface AllModelsTabProps {
selectedModelGroup: string | null;
@@ -197,88 +198,95 @@ const AllModelsTab = ({
Current Team:
- {isLoading ? (
-
- ) : (
-
-
View:
- {isLoading ? (
-
- ) : (
-
setModelViewMode(value as "current_team" | "all")}
- >
-
-
-
-
Current Team Models
-
-
-
-
-
-
All Available Models
-
-
-
- )}
+
+ {isLoading ? (
+
+ ) : (
+ setModelViewMode(value as "current_team" | "all")}
+ options={[
+ {
+ value: "current_team",
+ label: (
+
+
+ Current Team Models
+
+ ),
+ },
+ {
+ value: "all",
+ label: (
+
+
+ All Available Models
+
+ ),
+ },
+ ]}
+ />
+ )}
+
@@ -382,34 +390,38 @@ const AllModelsTab = ({
{/* Model Name Filter */}
setSelectedModelGroup(value === "all" ? "all" : value)}
+ onChange={(value) => setSelectedModelGroup(value === "all" ? "all" : value)}
placeholder="Filter by Public Model Name"
- >
- All Models
- Wildcard Models (*)
- {availableModelGroups.map((group, idx) => (
-
- {group}
-
- ))}
-
+ showSearch
+ options={[
+ { value: "all", label: "All Models" },
+ { value: "wildcard", label: "Wildcard Models (*)" },
+ ...availableModelGroups.map((group, idx) => ({
+ value: group,
+ label: group,
+ })),
+ ]}
+ />
{/* Model Access Group Filter */}
setSelectedModelAccessGroupFilter(value === "all" ? null : value)}
+ onChange={(value) => setSelectedModelAccessGroupFilter(value === "all" ? null : value)}
placeholder="Filter by Model Access Group"
- >
- All Model Access Groups
- {availableModelAccessGroups.map((accessGroup, idx) => (
-
- {accessGroup}
-
- ))}
-
+ showSearch
+ options={[
+ { value: "all", label: "All Model Access Groups" },
+ ...availableModelAccessGroups.map((accessGroup, idx) => ({
+ value: accessGroup,
+ label: accessGroup,
+ })),
+ ]}
+ />
)}
diff --git a/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/utils/modelDataTransformer.test.ts b/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/utils/modelDataTransformer.test.ts
index eb7aecaa67..42b7672692 100644
--- a/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/utils/modelDataTransformer.test.ts
+++ b/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/utils/modelDataTransformer.test.ts
@@ -50,4 +50,73 @@ describe("transformModelData", () => {
const result = transformModelData(null, mockGetProviderFromModel);
expect(result).toEqual({ data: [] });
});
+
+ it("should handle zero cost models correctly", () => {
+ const rawData = {
+ data: [
+ {
+ model_name: "gemini-2.5-flash",
+ litellm_params: {
+ model: "vertex_ai/gemini-2.5-flash",
+ },
+ model_info: {
+ input_cost_per_token: 0.0,
+ output_cost_per_token: 0.0,
+ max_tokens: 65535,
+ max_input_tokens: 1048576,
+ },
+ },
+ ],
+ };
+
+ const result = transformModelData(rawData, mockGetProviderFromModel);
+
+ // Zero costs should be converted to "0.00" per 1M tokens, not left as 0 or null
+ expect(result.data[0]).toHaveProperty("input_cost", "0.00");
+ expect(result.data[0]).toHaveProperty("output_cost", "0.00");
+ });
+
+ it("should handle null cost fields in model_info", () => {
+ const rawData = {
+ data: [
+ {
+ model_name: "some-model",
+ litellm_params: {
+ model: "openai/some-model",
+ },
+ model_info: {
+ input_cost_per_token: null,
+ output_cost_per_token: null,
+ max_tokens: 4096,
+ max_input_tokens: 8192,
+ },
+ },
+ ],
+ };
+
+ const result = transformModelData(rawData, mockGetProviderFromModel);
+
+ // Null costs should remain null (displayed as "-" in the UI)
+ expect(result.data[0].input_cost).toBeNull();
+ expect(result.data[0].output_cost).toBeNull();
+ });
+
+ it("should handle missing model_info", () => {
+ const rawData = {
+ data: [
+ {
+ model_name: "some-model",
+ litellm_params: {
+ model: "openai/some-model",
+ },
+ },
+ ],
+ };
+
+ const result = transformModelData(rawData, mockGetProviderFromModel);
+
+ // Missing model_info should result in null costs
+ expect(result.data[0].input_cost).toBeNull();
+ expect(result.data[0].output_cost).toBeNull();
+ });
});
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 3ebf9ddd72..963fba5750 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
@@ -15,8 +15,8 @@ export const transformModelData = (rawModelData: any, getProviderFromModel: (mod
let model_info = curr_model?.model_info;
let provider = "";
- let input_cost = "Undefined";
- let output_cost = "Undefined";
+ let input_cost: any = null;
+ let output_cost: any = null;
let max_tokens = "Undefined";
let max_input_tokens = "Undefined";
let cleanedLitellmParams = {};
@@ -58,11 +58,11 @@ export const transformModelData = (rawModelData: any, getProviderFromModel: (mod
transformedData[i].litellm_model_name = litellm_model_name;
// Convert Cost in terms of Cost per 1M tokens
- if (transformedData[i].input_cost) {
+ if (transformedData[i].input_cost != null) {
transformedData[i].input_cost = (Number(transformedData[i].input_cost) * 1000000).toFixed(2);
}
- if (transformedData[i].output_cost) {
+ if (transformedData[i].output_cost != null) {
transformedData[i].output_cost = (Number(transformedData[i].output_cost) * 1000000).toFixed(2);
}
diff --git a/ui/litellm-dashboard/src/components/molecules/models/columns.tsx b/ui/litellm-dashboard/src/components/molecules/models/columns.tsx
index 958b6ac86a..8bb28e68b7 100644
--- a/ui/litellm-dashboard/src/components/molecules/models/columns.tsx
+++ b/ui/litellm-dashboard/src/components/molecules/models/columns.tsx
@@ -211,7 +211,7 @@ export const columns = (
const outputCost = model.output_cost;
// If both costs are missing or undefined, show "-"
- if (!inputCost && !outputCost) {
+ if (inputCost == null && outputCost == null) {
return (
-
@@ -223,9 +223,9 @@ export const columns = (
{/* Input Cost - Primary */}
- {inputCost &&
In: ${inputCost}
}
+ {inputCost != null &&
In: ${inputCost}
}
{/* Output Cost - Secondary */}
- {outputCost &&
Out: ${outputCost}
}
+ {outputCost != null &&
Out: ${outputCost}
}
);