From df87a49f8b9de9607bff25e95b029b15f1517534 Mon Sep 17 00:00:00 2001 From: ryan Date: Mon, 14 Sep 2026 21:35:55 +0000 Subject: [PATCH] fix(ui): offer no models when an org team's ceiling never arrives Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- .../ModelSelect/ModelSelect.test.tsx | 46 +++++++++++-------- .../components/ModelSelect/ModelSelect.tsx | 14 ++++-- 2 files changed, 38 insertions(+), 22 deletions(-) diff --git a/ui/litellm-dashboard/src/components/ModelSelect/ModelSelect.test.tsx b/ui/litellm-dashboard/src/components/ModelSelect/ModelSelect.test.tsx index 3ea42f5adc0..93a43d5e533 100644 --- a/ui/litellm-dashboard/src/components/ModelSelect/ModelSelect.test.tsx +++ b/ui/litellm-dashboard/src/components/ModelSelect/ModelSelect.test.tsx @@ -517,27 +517,35 @@ describe("ModelSelect", () => { expectOffered("All Proxy Models"); }); - it("should keep hiding All Proxy Models when neither the team nor the org reports a ceiling", async () => { - const user = userEvent.setup(); - mockUseOrganization.mockReturnValue({ data: undefined, isLoading: false } as any); - mockUseTeam.mockReturnValue({ - data: { team_id: "team-1", organization_models: null }, - isLoading: false, - } as any); + it("should offer no models for an org team when neither the team nor the org reports a ceiling", async () => { + const testCases = [ + { name: "/team/info withheld the ceiling", team: { team_id: "team-1", organization_models: null } }, + { name: "/team/info failed after the list seeded the team", team: { team_id: "team-1", models: [] } }, + ]; - renderWithProviders( - , - ); + for (const testCase of testCases) { + const user = userEvent.setup(); + mockUseOrganization.mockReturnValue({ data: undefined, isLoading: false } as any); + mockUseTeam.mockReturnValue({ data: testCase.team, isLoading: false, isFetching: false } as any); - await openModelList(user); - expectNotOffered("All Proxy Models"); - expectOffered("No Default Models"); + const { unmount } = renderWithProviders( + , + ); + + await openModelList(user); + expectNotOffered("All Proxy Models"); + expectOffered("No Default Models"); + expectNotOffered("gpt-4"); + expectNotOffered("claude-3"); + + unmount(); + } }); it("should use custom dataTestId when provided", async () => { diff --git a/ui/litellm-dashboard/src/components/ModelSelect/ModelSelect.tsx b/ui/litellm-dashboard/src/components/ModelSelect/ModelSelect.tsx index ca449be81e3..c29eba9d997 100644 --- a/ui/litellm-dashboard/src/components/ModelSelect/ModelSelect.tsx +++ b/ui/litellm-dashboard/src/components/ModelSelect/ModelSelect.tsx @@ -69,6 +69,7 @@ type ModelOptionGroup = { type FilterContextArgs = { allProxyModels: string[]; + organizationID?: string; organizationModels?: string[]; userModels?: string[]; options?: ModelSelectProps["options"]; @@ -88,8 +89,9 @@ const contextFilters: Record { - if (!organizationModels || isUncappedModelCeiling(organizationModels)) return allProxyModels; + team: ({ allProxyModels, organizationID, organizationModels }) => { + if (organizationModels === undefined) return organizationID ? [] : allProxyModels; + if (isUncappedModelCeiling(organizationModels)) return allProxyModels; return allProxyModels.filter((model) => organizationModels.includes(model)); }, @@ -115,7 +117,13 @@ const filterModels = ( const filterFn = contextFilters[ctx.context]; if (!filterFn) return []; - return filterFn({ allProxyModels: deduplicatedProxyModels, ...extra, options: ctx.options }); + const filterArgs: FilterContextArgs = { + allProxyModels: deduplicatedProxyModels, + organizationID: ctx.organizationID, + ...extra, + options: ctx.options, + }; + return filterFn(filterArgs); }; export const ModelSelect = (props: ModelSelectProps) => {