From 1a5b87796d4445499f490188e2530701989121ef Mon Sep 17 00:00:00 2001 From: kerrylu-berri <339337838+kerrylu-berri@users.noreply.github.com> Date: Mon, 12 Oct 2026 22:05:17 +0000 Subject: [PATCH] fix(ui): show the legacy evaluation mode as decisions in the Model Hub and guardrail picker Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- .../decision_model/DecisionModelFields.tsx | 2 +- .../src/components/AIHub/ModelHubTable.tsx | 4 ++-- .../AIHub/ModelHubTableColumns.test.tsx | 6 ++++++ .../components/AIHub/ModelHubTableColumns.tsx | 3 ++- .../src/components/model_filters.tsx | 6 ++++-- .../src/lib/decisionModels.test.ts | 18 +++++++++++++++++- ui/litellm-dashboard/src/lib/decisionModels.ts | 5 +++++ 7 files changed, 37 insertions(+), 7 deletions(-) diff --git a/ui/litellm-dashboard/src/app/(dashboard)/guardrails/_components/decision_model/DecisionModelFields.tsx b/ui/litellm-dashboard/src/app/(dashboard)/guardrails/_components/decision_model/DecisionModelFields.tsx index 34e721bfae5..379995ad37c 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/guardrails/_components/decision_model/DecisionModelFields.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/guardrails/_components/decision_model/DecisionModelFields.tsx @@ -53,7 +53,7 @@ const NoDecisionModels: React.FC<{ providerFilter: string | null }> = ({ provide No decision models yet

- Add a decision model{source} to use this guardrail. A model shows up here once its mode is evaluation.{" "} + Add a decision model{source} to use this guardrail. A model shows up here once its mode is decisions.{" "} Add a model diff --git a/ui/litellm-dashboard/src/components/AIHub/ModelHubTable.tsx b/ui/litellm-dashboard/src/components/AIHub/ModelHubTable.tsx index 41b57a78eef..2007020e117 100644 --- a/ui/litellm-dashboard/src/components/AIHub/ModelHubTable.tsx +++ b/ui/litellm-dashboard/src/components/AIHub/ModelHubTable.tsx @@ -44,7 +44,7 @@ import { checkTokenValidity } from "@/utils/jwtUtils"; import { getCookie } from "@/utils/cookieUtils"; import { getLoginUrl } from "@/utils/returnUrlUtils"; import { uiHref } from "@/utils/uiHref"; -import { DECISIONS_DOCS_URL, SYSTEM_ONE_PLAYGROUND_ROUTE, isDecisionMode } from "@/lib/decisionModels"; +import { DECISIONS_DOCS_URL, SYSTEM_ONE_PLAYGROUND_ROUTE, displayMode, isDecisionMode } from "@/lib/decisionModels"; interface ModelHubTableProps { accessToken: string | null; @@ -672,7 +672,7 @@ const ModelHubTable: React.FC = ({

Mode:

-

{selectedModel.mode || "Not specified"}

+

{displayMode(selectedModel.mode) ?? "Not specified"}

Providers:

diff --git a/ui/litellm-dashboard/src/components/AIHub/ModelHubTableColumns.test.tsx b/ui/litellm-dashboard/src/components/AIHub/ModelHubTableColumns.test.tsx index 678b811921c..dbe0371241a 100644 --- a/ui/litellm-dashboard/src/components/AIHub/ModelHubTableColumns.test.tsx +++ b/ui/litellm-dashboard/src/components/AIHub/ModelHubTableColumns.test.tsx @@ -37,6 +37,12 @@ describe("getModelHubTableColumns", () => { expect(screen.getByText("gpt-4o")).toBeInTheDocument(); }); + it("shows the legacy evaluation mode as decisions", () => { + renderTable([{ ...mockModel, mode: "evaluation" }]); + expect(screen.getByText("decisions")).toBeInTheDocument(); + expect(screen.queryByText("evaluation")).not.toBeInTheDocument(); + }); + it("shows the first two providers and '+1' for overflow", () => { renderTable([mockModel]); expect(screen.getByText("openai")).toBeInTheDocument(); diff --git a/ui/litellm-dashboard/src/components/AIHub/ModelHubTableColumns.tsx b/ui/litellm-dashboard/src/components/AIHub/ModelHubTableColumns.tsx index 3d6423f2d44..559c18930c7 100644 --- a/ui/litellm-dashboard/src/components/AIHub/ModelHubTableColumns.tsx +++ b/ui/litellm-dashboard/src/components/AIHub/ModelHubTableColumns.tsx @@ -14,6 +14,7 @@ import { DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; import { cn } from "@/lib/cva.config"; +import { displayMode } from "@/lib/decisionModels"; import { copyToClipboard } from "@/utils/dataUtils"; export interface ModelHubData { @@ -136,7 +137,7 @@ export const getModelHubTableColumns = ({ onModelClick }: ModelHubTableColumnsDe sortingFn: "alphanumeric", cell: ({ row }) => row.original.mode ? ( - {row.original.mode} + {displayMode(row.original.mode)} ) : ( - ), diff --git a/ui/litellm-dashboard/src/components/model_filters.tsx b/ui/litellm-dashboard/src/components/model_filters.tsx index 0ce82d6d050..1eef5083f16 100644 --- a/ui/litellm-dashboard/src/components/model_filters.tsx +++ b/ui/litellm-dashboard/src/components/model_filters.tsx @@ -1,5 +1,6 @@ import React, { useState, useEffect, useMemo, useRef } from "react"; import { Card } from "@/components/ui/card"; +import { displayMode } from "@/lib/decisionModels"; import { matchesSearchTerm } from "@/utils/searchUtils"; interface ModelGroupInfo { @@ -51,7 +52,8 @@ const ModelFilters: React.FC = ({ const getUniqueModes = (data: ModelGroupInfo[]) => { const modes = new Set(); data.forEach((model) => { - if (model.mode) modes.add(model.mode); + const mode = displayMode(model.mode); + if (mode) modes.add(mode); }); return Array.from(modes); }; @@ -79,7 +81,7 @@ const ModelFilters: React.FC = ({ modelHubData?.filter((model) => { const matchesSearch = matchesSearchTerm(searchTerm, [model.model_group]); const matchesProvider = selectedProvider === "" || model.providers.includes(selectedProvider); - const matchesMode = selectedMode === "" || model.mode === selectedMode; + const matchesMode = selectedMode === "" || displayMode(model.mode) === selectedMode; // Check if model has the selected feature const matchesFeature = diff --git a/ui/litellm-dashboard/src/lib/decisionModels.test.ts b/ui/litellm-dashboard/src/lib/decisionModels.test.ts index 7552cc0cfd8..d5f3aff3fe1 100644 --- a/ui/litellm-dashboard/src/lib/decisionModels.test.ts +++ b/ui/litellm-dashboard/src/lib/decisionModels.test.ts @@ -1,5 +1,11 @@ import { describe, expect, it } from "vitest"; -import { EMPTY_DECISION_CATALOG, buildDecisionCatalog, isDecisionMode, isDecisionSelection } from "./decisionModels"; +import { + EMPTY_DECISION_CATALOG, + buildDecisionCatalog, + displayMode, + isDecisionMode, + isDecisionSelection, +} from "./decisionModels"; const COST_MAP = { "typesafe/jev-latest": { litellm_provider: "typesafe", mode: "evaluation" }, @@ -26,6 +32,16 @@ describe("isDecisionMode", () => { }); }); +describe("displayMode", () => { + it("shows the legacy evaluation mode as decisions and leaves other modes alone", () => { + expect(displayMode("evaluation")).toBe("decisions"); + expect(displayMode("decisions")).toBe("decisions"); + expect(displayMode("chat")).toBe("chat"); + expect(displayMode(null)).toBeUndefined(); + expect(displayMode(undefined)).toBeUndefined(); + }); +}); + describe("buildDecisionCatalog", () => { const catalog = buildDecisionCatalog(COST_MAP); diff --git a/ui/litellm-dashboard/src/lib/decisionModels.ts b/ui/litellm-dashboard/src/lib/decisionModels.ts index a14ce1948a9..e21befab616 100644 --- a/ui/litellm-dashboard/src/lib/decisionModels.ts +++ b/ui/litellm-dashboard/src/lib/decisionModels.ts @@ -28,6 +28,11 @@ export const EMPTY_DECISION_CATALOG: DecisionCatalog = { models: new Set(), prov export const isDecisionMode = (mode: string | null | undefined): boolean => mode === "decisions" || mode === "evaluation"; +export const DECISIONS_MODE = "decisions"; + +export const displayMode = (mode: string | null | undefined): string | undefined => + isDecisionMode(mode) ? DECISIONS_MODE : (mode ?? undefined); + const isDecisionEntry = (entry: CostMapEntry): boolean => isDecisionMode(entry.mode) || (entry.supported_endpoints ?? []).some((endpoint) => DECISION_ENDPOINTS.has(endpoint));