From e406ebf0dd3f30cdf678087be50b7aed45f9f1a0 Mon Sep 17 00:00:00 2001 From: yuneng-jiang Date: Wed, 21 Jan 2026 18:14:45 -0800 Subject: [PATCH 1/3] Model Select for Create Team --- .../app/(dashboard)/hooks/models/useModels.ts | 2 +- .../components/modals/CreateTeamModal.tsx | 2 +- .../components/ModelSelect/ModelSelect.tsx | 92 ++++++++++--------- .../src/components/OldTeams.tsx | 32 ++++--- .../src/components/networking.tsx | 4 + 5 files changed, 70 insertions(+), 62 deletions(-) diff --git a/ui/litellm-dashboard/src/app/(dashboard)/hooks/models/useModels.ts b/ui/litellm-dashboard/src/app/(dashboard)/hooks/models/useModels.ts index b9f67c1cac..1dbd79eacf 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/hooks/models/useModels.ts +++ b/ui/litellm-dashboard/src/app/(dashboard)/hooks/models/useModels.ts @@ -56,7 +56,7 @@ export const useAllProxyModels = () => { const { accessToken, userId, userRole } = useAuthorized(); return useQuery({ queryKey: allProxyModelsKeys.list({}), - queryFn: async () => await modelAvailableCall(accessToken!, userId!, userRole!, true), + queryFn: async () => await modelAvailableCall(accessToken!, userId!, userRole!, true, null, true, false, "expand"), enabled: Boolean(accessToken && userId && userRole), }); }; diff --git a/ui/litellm-dashboard/src/app/(dashboard)/teams/components/modals/CreateTeamModal.tsx b/ui/litellm-dashboard/src/app/(dashboard)/teams/components/modals/CreateTeamModal.tsx index df6d8d3ea8..972c39d49d 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/teams/components/modals/CreateTeamModal.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/teams/components/modals/CreateTeamModal.tsx @@ -526,7 +526,7 @@ const CreateTeamModal = ({ valuePropName="checked" help="Bypass global guardrails for this team" > - diff --git a/ui/litellm-dashboard/src/components/ModelSelect/ModelSelect.tsx b/ui/litellm-dashboard/src/components/ModelSelect/ModelSelect.tsx index a3ddeff322..b388135044 100644 --- a/ui/litellm-dashboard/src/components/ModelSelect/ModelSelect.tsx +++ b/ui/litellm-dashboard/src/components/ModelSelect/ModelSelect.tsx @@ -53,14 +53,13 @@ const contextFilters: Record { if (selectedOrganization) { - if (selectedOrganization.models.includes(MODEL_SELECT_ALL_PROXY_MODELS_SPECIAL_VALUE.value)) { + if (selectedOrganization.models.includes(MODEL_SELECT_ALL_PROXY_MODELS_SPECIAL_VALUE.value) || selectedOrganization.models.length === 0) { return allProxyModels; } - // Return organization's models (filtered from allProxyModels) return allProxyModels.filter((model) => selectedOrganization.models.includes(model)); } - return userModels ?? []; + return allProxyModels ?? []; }, organization: ({ allProxyModels, selectedOrganization, options }) => { @@ -102,9 +101,12 @@ export const ModelSelect = (props: ModelSelectProps) => { const isSpecialOption = (value: string) => MODEL_SELECT_SPECIAL_VALUES_ARRAY.some((sv) => sv.value === value); const hasSpecialOptionSelected = value.some(isSpecialOption); const isLoading = isLoadingAllProxyModels || isLoadingTeam || isLoadingOrganization || isCurrentUserLoading; + const organizationHasAllProxyModels = organization?.models.includes(MODEL_SELECT_ALL_PROXY_MODELS_SPECIAL_VALUE.value) || organization?.models.length === 0; + console.log("organization:", organization); + console.log("organizationHasAllProxyModels:", organizationHasAllProxyModels); const shouldShowAllProxyModels = showAllProxyModelsOverride || - (organization?.models.includes(MODEL_SELECT_ALL_PROXY_MODELS_SPECIAL_VALUE.value) && includeSpecialOptions); + (organizationHasAllProxyModels && includeSpecialOptions); if (isLoading) { return ; @@ -143,51 +145,51 @@ export const ModelSelect = (props: ModelSelectProps) => { options={[ includeSpecialOptions ? { - label: Special Options, - title: "Special Options", - options: [ - ...(shouldShowAllProxyModels - ? [ - { - label: All Proxy Models, - value: MODEL_SELECT_ALL_PROXY_MODELS_SPECIAL_VALUE.value, - disabled: - value.length > 0 && - value.some( - (v) => isSpecialOption(v) && v !== MODEL_SELECT_ALL_PROXY_MODELS_SPECIAL_VALUE.value, - ), - key: MODEL_SELECT_ALL_PROXY_MODELS_SPECIAL_VALUE.value, - }, - ] - : []), - { - label: No Default Models, - value: MODEL_SELECT_NO_DEFAULT_MODELS_SPECIAL_VALUE.value, - disabled: - value.length > 0 && - value.some((v) => isSpecialOption(v) && v !== MODEL_SELECT_NO_DEFAULT_MODELS_SPECIAL_VALUE.value), - key: MODEL_SELECT_NO_DEFAULT_MODELS_SPECIAL_VALUE.value, - }, - ], - } + label: Special Options, + title: "Special Options", + options: [ + ...(shouldShowAllProxyModels + ? [ + { + label: All Proxy Models, + value: MODEL_SELECT_ALL_PROXY_MODELS_SPECIAL_VALUE.value, + disabled: + value.length > 0 && + value.some( + (v) => isSpecialOption(v) && v !== MODEL_SELECT_ALL_PROXY_MODELS_SPECIAL_VALUE.value, + ), + key: MODEL_SELECT_ALL_PROXY_MODELS_SPECIAL_VALUE.value, + }, + ] + : []), + { + label: No Default Models, + value: MODEL_SELECT_NO_DEFAULT_MODELS_SPECIAL_VALUE.value, + disabled: + value.length > 0 && + value.some((v) => isSpecialOption(v) && v !== MODEL_SELECT_NO_DEFAULT_MODELS_SPECIAL_VALUE.value), + key: MODEL_SELECT_NO_DEFAULT_MODELS_SPECIAL_VALUE.value, + }, + ], + } : [], ...(wildcard.length > 0 ? [ - { - label: Wildcard Options, - title: "Wildcard Options", - options: wildcard.map((model) => { - const provider = model.replace("/*", ""); - const capitalizedProvider = provider.charAt(0).toUpperCase() + provider.slice(1); + { + label: Wildcard Options, + title: "Wildcard Options", + options: wildcard.map((model) => { + const provider = model.replace("/*", ""); + const capitalizedProvider = provider.charAt(0).toUpperCase() + provider.slice(1); - return { - label: {`All ${capitalizedProvider} models`}, - value: model, - disabled: hasSpecialOptionSelected, - }; - }), - }, - ] + return { + label: {`All ${capitalizedProvider} models`}, + value: model, + disabled: hasSpecialOptionSelected, + }; + }), + }, + ] : []), { label: Models, diff --git a/ui/litellm-dashboard/src/components/OldTeams.tsx b/ui/litellm-dashboard/src/components/OldTeams.tsx index 5679788d30..1202cc9169 100644 --- a/ui/litellm-dashboard/src/components/OldTeams.tsx +++ b/ui/litellm-dashboard/src/components/OldTeams.tsx @@ -85,6 +85,7 @@ import { updateExistingKeys } from "@/utils/dataUtils"; import DeleteResourceModal from "./common_components/DeleteResourceModal"; import TableIconActionButton from "./common_components/IconActionButton/TableIconActionButtons/TableIconActionButton"; import { Member, teamCreateCall, v2TeamListCall } from "./networking"; +import { ModelSelect } from "./ModelSelect/ModelSelect"; interface TeamInfo { members_with_roles: Member[]; @@ -1064,11 +1065,11 @@ const Teams: React.FC = ({ rules={ isOrgAdmin ? [ - { - required: true, - message: "Please select an organization", - }, - ] + { + required: true, + message: "Please select an organization", + }, + ] : [] } help={ @@ -1135,16 +1136,17 @@ const Teams: React.FC = ({ ]} name="models" > - - - No Default Models - - {modelsToPick.map((model) => ( - - {getModelDisplayName(model)} - - ))} - + form.setFieldValue("models", values)} + organizationID={form.getFieldValue("organization_id")} + options={{ + includeSpecialOptions: true, + showAllProxyModelsOverride: !form.getFieldValue("organization_id"), + }} + context="team" + dataTestId="create-team-models-select" + /> diff --git a/ui/litellm-dashboard/src/components/networking.tsx b/ui/litellm-dashboard/src/components/networking.tsx index 46561bc9e1..a4f2dfce3b 100644 --- a/ui/litellm-dashboard/src/components/networking.tsx +++ b/ui/litellm-dashboard/src/components/networking.tsx @@ -2457,6 +2457,7 @@ export const modelAvailableCall = async ( teamID: string | null = null, include_model_access_groups: boolean = false, only_model_access_groups: boolean = false, + scope?: string ) => { /** * Get all the models user has access to @@ -2475,6 +2476,9 @@ export const modelAvailableCall = async ( if (teamID) { params.append("team_id", teamID.toString()); } + if (scope) { + params.append("scope", scope); + } if (params.toString()) { url += `?${params.toString()}`; } From f737429ad94b3a1f96a0f368f0ad717a4f77ccd8 Mon Sep 17 00:00:00 2001 From: yuneng-jiang Date: Wed, 21 Jan 2026 18:21:42 -0800 Subject: [PATCH 2/3] create team model select --- .../hooks/models/useModels.test.ts | 6 +++++- .../src/components/OldTeams.test.tsx | 21 +++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/ui/litellm-dashboard/src/app/(dashboard)/hooks/models/useModels.test.ts b/ui/litellm-dashboard/src/app/(dashboard)/hooks/models/useModels.test.ts index a97c309ca9..c6629e5396 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/hooks/models/useModels.test.ts +++ b/ui/litellm-dashboard/src/app/(dashboard)/hooks/models/useModels.test.ts @@ -379,7 +379,11 @@ describe("useAllProxyModels", () => { "test-access-token", "test-user-id", "Admin", - true + true, + null, + true, + false, + "expand" ); expect(modelAvailableCall).toHaveBeenCalledTimes(1); }); diff --git a/ui/litellm-dashboard/src/components/OldTeams.test.tsx b/ui/litellm-dashboard/src/components/OldTeams.test.tsx index 76fc26a884..6b556638fb 100644 --- a/ui/litellm-dashboard/src/components/OldTeams.test.tsx +++ b/ui/litellm-dashboard/src/components/OldTeams.test.tsx @@ -60,6 +60,27 @@ vi.mock("@/components/team/team_info", () => ({ }, })); +vi.mock("./ModelSelect/ModelSelect", () => ({ + ModelSelect: React.forwardRef(({ value, onChange, dataTestId, id }: any, ref: any) => { + return ( + { + // Mock onChange - in real usage this would be handled by Ant Design Select + if (onChange) { + onChange(value || []); + } + }} + readOnly + /> + ); + }), +})); + vi.mock("@/app/(dashboard)/hooks/organizations/useOrganizations", () => ({ useOrganizations: () => mockUseOrganizations(), })); From d1172562bc8baf85eb504e9b98dd15100b85340d Mon Sep 17 00:00:00 2001 From: yuneng-jiang Date: Wed, 21 Jan 2026 18:29:08 -0800 Subject: [PATCH 3/3] fixing build --- .../src/components/OldTeams.test.tsx | 22 +++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/ui/litellm-dashboard/src/components/OldTeams.test.tsx b/ui/litellm-dashboard/src/components/OldTeams.test.tsx index 6b556638fb..91b428c8c9 100644 --- a/ui/litellm-dashboard/src/components/OldTeams.test.tsx +++ b/ui/litellm-dashboard/src/components/OldTeams.test.tsx @@ -60,8 +60,8 @@ vi.mock("@/components/team/team_info", () => ({ }, })); -vi.mock("./ModelSelect/ModelSelect", () => ({ - ModelSelect: React.forwardRef(({ value, onChange, dataTestId, id }: any, ref: any) => { +vi.mock("./ModelSelect/ModelSelect", () => { + const ModelSelect = React.forwardRef(({ value, onChange, dataTestId, id }: any, ref: any) => { return ( ({ readOnly /> ); - }), -})); + }); + ModelSelect.displayName = "ModelSelect"; + return { + ModelSelect, + }; +}); vi.mock("@/app/(dashboard)/hooks/organizations/useOrganizations", () => ({ useOrganizations: () => mockUseOrganizations(), @@ -334,6 +338,7 @@ describe("OldTeams - handleCreate organization handling", () => { created_at: new Date().toISOString(), keys: [], members_with_roles: [], + spend: 0, }, ]} searchParams={{}} @@ -408,6 +413,7 @@ describe("OldTeams - empty state", () => { created_at: new Date().toISOString(), keys: [], members_with_roles: [], + spend: 0, }, ]} searchParams={{}} @@ -576,6 +582,7 @@ describe("OldTeams - premium props", () => { created_at: new Date().toISOString(), keys: [], members_with_roles: [], + spend: 0, }, ]} searchParams={{}} @@ -624,6 +631,7 @@ describe("OldTeams - Default Team Settings tab visibility", () => { created_at: new Date().toISOString(), keys: [], members_with_roles: [], + spend: 0, }, ]} searchParams={{}} @@ -654,6 +662,7 @@ describe("OldTeams - Default Team Settings tab visibility", () => { created_at: new Date().toISOString(), keys: [], members_with_roles: [], + spend: 0, }, ]} searchParams={{}} @@ -684,6 +693,7 @@ describe("OldTeams - Default Team Settings tab visibility", () => { created_at: new Date().toISOString(), keys: [], members_with_roles: [], + spend: 0, }, ]} searchParams={{}} @@ -714,6 +724,7 @@ describe("OldTeams - Default Team Settings tab visibility", () => { created_at: new Date().toISOString(), keys: [], members_with_roles: [], + spend: 0, }, ]} searchParams={{}} @@ -812,6 +823,7 @@ describe("OldTeams - organization alias display", () => { created_at: new Date().toISOString(), keys: [], members_with_roles: [], + spend: 0, }, ]} searchParams={{}} @@ -845,6 +857,7 @@ describe("OldTeams - organization alias display", () => { created_at: new Date().toISOString(), keys: [], members_with_roles: [], + spend: 0, }, ]} searchParams={{}} @@ -877,6 +890,7 @@ describe("OldTeams - organization alias display", () => { created_at: new Date().toISOString(), keys: [], members_with_roles: [], + spend: 0, }, ]} searchParams={{}}