fix(ui): honor model view scope

Two bugs in the models tab, both still present on `main`:

1. `teamIdForQuery` was derived from the team picker alone, so "All Proxy
   Models" still sent `teamId` and the tab showed the current team's models
   under a heading that promises every model the user can reach.

2. `handleViewModeChange` was the only handler that changes the result set
   without passing `page: null`. Switching view while past page 1 kept the old
   page number against a different, usually shorter, list -- so the user saw an
   empty table or skipped earlier rows until they paged back manually.

Rebuilt on `main`: this PR was opened against the since-deleted
`litellm_internal_staging`. `main` has since moved this component onto
`useQueryStates`/`setTableState`, so the pagination fix is now one `page: null`
in the same shape as its siblings (`handleTeamChange`, the search handler, the
access-group handler all already pass it) rather than the `resetToFirstPage()`
call the original patch used. The test is rewritten against `main`'s
`renderWithProviders` + `onUrlUpdate` idiom, and the view label is
"All Proxy Models" here rather than "All Available Models".

NOT VERIFIED LOCALLY: the dashboard's deps will not install in this
environment -- `npm ci` refuses with `Required: {"node":">=24.14.1"}` against
node v22.22.3 -- so `vitest` could not be run on the new case. The source
change is four lines and matches the surrounding idiom; CI's own run is the
real check here, and I will fix whatever it reports.
This commit is contained in:
lzhan011 2026-10-05 05:07:52 -05:00
parent 1238cfe90f
commit bf0d20af1c
2 changed files with 42 additions and 2 deletions

View file

@ -250,6 +250,39 @@ describe("AllModelsTab", () => {
expect(lastModelsInfoCall().page).toBe(1);
});
it("drops the team filter and the page when switching to all proxy models", async () => {
// "All Proxy Models" promises every model the user can reach, so the team
// must stop narrowing the query -- and the page has to go back to 1 like it
// does for every other control that changes the result set.
const user = userEvent.setup();
setModelsInfo([makeRow()], 200);
const onUrlUpdate = vi.fn<OnUrlUpdateFunction>();
renderWithProviders(<AllModelsTab {...defaultProps} />, {
searchParams: { page: "3", filter_team: "team-1" },
onUrlUpdate,
});
await waitFor(() => {
expect(lastModelsInfoCall().teamId).toBe("team-1");
});
expect(lastModelsInfoCall().page).toBe(3);
await user.click(screen.getByTestId("models-view-select"));
await user.click(await screen.findByRole("option", { name: "All Proxy Models" }));
await waitFor(() => {
expect(lastModelsInfoCall().teamId).toBeUndefined();
});
expect(lastUrlParams(onUrlUpdate)?.get("page")).toBeNull();
await user.click(screen.getByTestId("models-view-select"));
await user.click(await screen.findByRole("option", { name: "Current Team Models" }));
await waitFor(() => {
expect(lastModelsInfoCall().teamId).toBe("team-1");
});
});
it("debounces the model name search into the server query", async () => {
renderWithProviders(<AllModelsTab {...defaultProps} />);

View file

@ -101,7 +101,11 @@ const AllModelsTab = ({
const [deleteLoading, setDeleteLoading] = useState(false);
const [pausingModelId, setPausingModelId] = useState<string | null>(null);
const teamIdForQuery = selectedTeamValue === PERSONAL_TEAM_VALUE ? undefined : selectedTeamValue;
// Scoped by the view mode, not just by the team picker: in "all available
// models" the team must not narrow the query, or the tab shows the current
// team's models under a heading that promises every model the user can reach.
const teamIdForQuery =
modelViewMode === "current_team" && selectedTeamValue !== PERSONAL_TEAM_VALUE ? selectedTeamValue : undefined;
const isConcreteModelGroup =
Boolean(selectedModelGroup) &&
selectedModelGroup !== ALL_MODEL_GROUPS_VALUE &&
@ -210,7 +214,10 @@ const AllModelsTab = ({
};
const handleViewModeChange = (value: ModelViewMode) => {
void setTableState({ view_mode: value });
// `page: null` like every other handler that changes the result set --
// without it, switching view while past page 1 keeps the old page number
// against a different, usually shorter, list.
void setTableState({ view_mode: value, page: null });
};
const resetFilters = () => {