From b50790aaaddd079f77bff36dc61cba28ca231fee Mon Sep 17 00:00:00 2001 From: yuneng-jiang Date: Thu, 20 Nov 2025 20:40:08 -0800 Subject: [PATCH] Revert "Expose new model provider map endpoint and use in add model workflow" This reverts commit 8a4fefc5655185d80f6a9fb70d685960102e138c. --- .../public_endpoints/public_endpoints.py | 51 ------ .../public_endpoints/test_public_endpoints.py | 44 ----- .../ModelsAndEndpointsView.test.tsx | 155 ------------------ .../ModelsAndEndpointsView.tsx | 16 +- .../src/components/networking.tsx | 27 +-- 5 files changed, 8 insertions(+), 285 deletions(-) delete mode 100644 ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/ModelsAndEndpointsView.test.tsx diff --git a/litellm/proxy/public_endpoints/public_endpoints.py b/litellm/proxy/public_endpoints/public_endpoints.py index 2c5a800a90b..159d357c2a6 100644 --- a/litellm/proxy/public_endpoints/public_endpoints.py +++ b/litellm/proxy/public_endpoints/public_endpoints.py @@ -116,54 +116,3 @@ async def get_provider_fields() -> List[ProviderCreateInfo]: """ return get_provider_create_metadata() - - -@router.get( - "/public/model_provider_map", - tags=["public", "model management"], -) -async def get_model_provider_map(): - """ - Return a mapping of model names to their litellm_provider and mode. - This is a public endpoint that provides the same structure as /get/litellm_model_cost_map - but without cost information, making it accessible to non-admin users. - - Returns: - dict: A dictionary mapping model names to their provider information: - { - "model_name": { - "litellm_provider": "provider_name", - "mode": "chat" | "completion" | "embedding" | "image_generation" | "audio_transcription" | ... - }, - ... - } - """ - import litellm - - try: - _model_cost_map = litellm.model_cost - if not _model_cost_map: - return {} - - # Extract the litellm_provider and mode fields from each model entry - model_provider_map = {} - for model_name, model_info in _model_cost_map.items(): - if isinstance(model_info, dict) and "litellm_provider" in model_info: - litellm_provider = model_info["litellm_provider"] - # Only include if litellm_provider is not None/empty - if litellm_provider: - model_entry = { - "litellm_provider": litellm_provider - } - # Include mode if it exists - if "mode" in model_info and model_info["mode"]: - model_entry["mode"] = model_info["mode"] - - model_provider_map[model_name] = model_entry - - return model_provider_map - except Exception as e: - raise HTTPException( - status_code=500, - detail=f"Internal Server Error ({str(e)})", - ) diff --git a/tests/test_litellm/proxy/public_endpoints/test_public_endpoints.py b/tests/test_litellm/proxy/public_endpoints/test_public_endpoints.py index 8c76e64509b..8456cf55389 100644 --- a/tests/test_litellm/proxy/public_endpoints/test_public_endpoints.py +++ b/tests/test_litellm/proxy/public_endpoints/test_public_endpoints.py @@ -64,47 +64,3 @@ def test_get_provider_fields_returns_metadata(): } assert {"api_base", "api_key"}.issubset(runway_credential_keys) - -def test_get_model_provider_map_returns_correct_structure(): - app = FastAPI() - app.include_router(router) - client = TestClient(app) - - response = client.get("/public/model_provider_map") - - assert response.status_code == 200 - payload = response.json() - assert isinstance(payload, dict) - - # Verify structure: each entry should have litellm_provider, optionally mode - for model_name, model_info in payload.items(): - assert isinstance(model_name, str) - assert isinstance(model_info, dict) - assert "litellm_provider" in model_info - assert isinstance(model_info["litellm_provider"], str) - assert len(model_info["litellm_provider"]) > 0 - - # If mode exists, it should be a valid string - if "mode" in model_info: - assert isinstance(model_info["mode"], str) - assert len(model_info["mode"]) > 0 - - # Verify some common models exist (if model_cost is populated) - if len(payload) > 0: - # Check for at least one OpenAI model - openai_models = [ - model for model, info in payload.items() - if info.get("litellm_provider") == "openai" - ] - # If OpenAI models exist, verify structure - if openai_models: - sample_model = openai_models[0] - assert "litellm_provider" in payload[sample_model] - assert payload[sample_model]["litellm_provider"] == "openai" - # Most OpenAI models should have mode="chat" - if "mode" in payload[sample_model]: - assert payload[sample_model]["mode"] in [ - "chat", "completion", "embedding", - "image_generation", "audio_transcription" - ] - diff --git a/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/ModelsAndEndpointsView.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/ModelsAndEndpointsView.test.tsx deleted file mode 100644 index 50279076534..00000000000 --- a/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/ModelsAndEndpointsView.test.tsx +++ /dev/null @@ -1,155 +0,0 @@ -import { render, waitFor, screen } from "@testing-library/react"; -import { describe, it, expect, vi, beforeEach, beforeAll } from "vitest"; -import ModelsAndEndpointsView from "./ModelsAndEndpointsView"; -import * as useAuthorizedModule from "@/app/(dashboard)/hooks/useAuthorized"; -import * as useTeamsModule from "@/app/(dashboard)/hooks/useTeams"; - -global.ResizeObserver = vi.fn().mockImplementation(() => ({ - observe: vi.fn(), - unobserve: vi.fn(), - disconnect: vi.fn(), -})); - -const mockUseAuthorized = { - token: "mock-token", - accessToken: "mock-access-token", - userId: "user-123", - userEmail: "test@example.com", - userRole: "Admin", - premiumUser: true, - disabledPersonalKeyCreation: false, - showSSOBanner: false, -}; - -beforeAll(() => { - vi.spyOn(useAuthorizedModule, "default").mockReturnValue(mockUseAuthorized); - vi.spyOn(useTeamsModule, "default").mockReturnValue({ - teams: [], - setTeams: vi.fn(), - }); -}); - -vi.mock("@/components/networking", async (importOriginal) => { - const actual = await importOriginal(); - return { - ...actual, - modelInfoCall: vi.fn().mockResolvedValue({ - data: [ - { - model_name: "gpt-4", - litellm_params: { - model: "gpt-4", - custom_llm_provider: "openai", - }, - model_info: { - id: "model-1", - access_groups: [], - }, - }, - ], - }), - modelProviderMap: vi.fn().mockResolvedValue({ - "gpt-4": { - litellm_provider: "openai", - }, - }), - modelSettingsCall: vi.fn().mockResolvedValue([]), - credentialListCall: vi.fn().mockResolvedValue({ - credentials: [], - }), - modelMetricsCall: vi.fn().mockResolvedValue({ - data: [], - all_api_bases: [], - }), - streamingModelMetricsCall: vi.fn().mockResolvedValue({ - data: [], - all_api_bases: [], - }), - modelExceptionsCall: vi.fn().mockResolvedValue({ - data: [], - exception_types: [], - }), - modelMetricsSlowResponsesCall: vi.fn().mockResolvedValue([]), - getCallbacksCall: vi.fn().mockResolvedValue({ - router_settings: { - model_group_retry_policy: {}, - retry_policy: {}, - num_retries: 0, - model_group_alias: {}, - }, - }), - setCallbacksCall: vi.fn().mockResolvedValue({}), - adminGlobalActivityExceptions: vi.fn().mockResolvedValue({ - sum_num_rate_limit_exceptions: 0, - daily_data: [], - }), - adminGlobalActivityExceptionsPerDeployment: vi.fn().mockResolvedValue([]), - allEndUsersCall: vi.fn().mockResolvedValue([]), - modelAvailableCall: vi.fn().mockResolvedValue({ - data: [], - }), - getPassThroughEndpointsCall: vi.fn().mockResolvedValue({ - data: [], - }), - }; -}); - -describe("ModelsAndEndpointsView", () => { - const defaultProps = { - accessToken: "test-access-token", - token: "test-token", - userRole: "Admin", - userID: "test-user-id", - modelData: { - data: [ - { - model_name: "gpt-4", - litellm_params: { - model: "gpt-4", - custom_llm_provider: "openai", - }, - model_info: { - id: "model-1", - access_groups: [], - }, - }, - ], - }, - keys: [], - setModelData: vi.fn(), - premiumUser: true, - teams: [], - }; - - beforeEach(() => { - vi.clearAllMocks(); - }); - - it("should render the component successfully", async () => { - const { container } = render(); - - await waitFor(() => { - expect(container).toBeTruthy(); - }); - - expect(screen.getByText("Model Management")).toBeInTheDocument(); - }); - - it("should render tabs", async () => { - render(); - await waitFor(() => { - expect(screen.getByText("Model Management")).toBeInTheDocument(); - }); - - const allModelsTabs = screen.getAllByRole("tab", { name: /All Models/i }); - expect(allModelsTabs.length).toBeGreaterThan(0); - - const addModelTabs = screen.getAllByRole("tab", { name: /Add Model/i }); - expect(addModelTabs.length).toBeGreaterThan(0); - - expect(screen.getByRole("tab", { name: /LLM Credentials/i })).toBeInTheDocument(); - expect(screen.getByRole("tab", { name: /Pass-Through Endpoints/i })).toBeInTheDocument(); - expect(screen.getByRole("tab", { name: /Health Status/i })).toBeInTheDocument(); - expect(screen.getByRole("tab", { name: /Model Analytics/i })).toBeInTheDocument(); - }); -}); diff --git a/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/ModelsAndEndpointsView.tsx b/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/ModelsAndEndpointsView.tsx index 6967576a1a9..8d65c0e1702 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/ModelsAndEndpointsView.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/ModelsAndEndpointsView.tsx @@ -10,7 +10,7 @@ import { TabPanel, TabPanels, TabGroup, TabList, Tab, Icon } from "@tremor/react import { DateRangePickerValue } from "@tremor/react"; import { modelInfoCall, - modelProviderMap, + modelCostMap, modelMetricsCall, streamingModelMetricsCall, modelExceptionsCall, @@ -418,17 +418,13 @@ const ModelsAndEndpointsView: React.FC = ({ fetchData(); } - const fetchModelProviderMap = async () => { - try { - const data = await modelProviderMap(); - console.log(`received model provider map data: ${Object.keys(data).length} models`); - setModelMap(data); - } catch (error) { - console.error("Failed to fetch model provider map:", error); - } + const fetchModelMap = async () => { + const data = await modelCostMap(accessToken); + console.log(`received model cost map data: ${Object.keys(data)}`); + setModelMap(data); }; if (modelMap == null) { - fetchModelProviderMap(); + fetchModelMap(); } handleRefreshClick(); diff --git a/ui/litellm-dashboard/src/components/networking.tsx b/ui/litellm-dashboard/src/components/networking.tsx index 05416975f90..ddb95294e92 100644 --- a/ui/litellm-dashboard/src/components/networking.tsx +++ b/ui/litellm-dashboard/src/components/networking.tsx @@ -306,31 +306,6 @@ export const getOpenAPISchema = async () => { return jsonData; }; -export const modelProviderMap = async () => { - try { - const url = proxyBaseUrl ? `${proxyBaseUrl}/public/model_provider_map` : `/public/model_provider_map`; - const response = await fetch(url, { - method: "GET", - headers: { - "Content-Type": "application/json", - }, - }); - - if (!response.ok) { - const errorText = await response.text(); - console.error("Failed to fetch model provider map:", response.status, errorText); - throw new Error("Failed to load model provider mapping"); - } - - const jsonData = await response.json(); - console.log(`received model provider map data: ${Object.keys(jsonData).length} models`); - return jsonData; - } catch (error) { - console.error("Failed to get model provider map:", error); - throw error; - } -}; - export const modelCostMap = async (accessToken: string) => { try { const url = proxyBaseUrl ? `${proxyBaseUrl}/get/litellm_model_cost_map` : `/get/litellm_model_cost_map`; @@ -6702,6 +6677,7 @@ export const getGuardrailProviderSpecificParams = async (accessToken: string) => } }; + export const getAgentsList = async (accessToken: string) => { try { const url = proxyBaseUrl ? `${proxyBaseUrl}/v1/agents` : `/v1/agents`; @@ -6819,6 +6795,7 @@ export const patchAgentCall = async ( } }; + export const updateGuardrailCall = async ( accessToken: string, guardrailId: string,