mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-04 02:31:27 +00:00
fix(ui): revalidate the selected preset's models at submit time
handlePresetChange only ever applies a preset that was verified available at selection time, but that guarantee could go stale by submit time if the caller's model access narrowed in between (a token change re-keys the model query without clearing the selection, since clearing it would erase in-progress Custom edits too). Re-run the same presetAvailability check at the submit boundary instead of trusting state gathered earlier, so a stale preset can no longer create a router referencing models the current caller doesn't have.
This commit is contained in:
parent
e185362f8f
commit
aa4843cb02
2 changed files with 43 additions and 0 deletions
|
|
@ -306,6 +306,35 @@ describe("AddAutoRouterTab", () => {
|
|||
expect(isOptionDisabled(optionByLabel("OpenAI Family")!)).toBe(true);
|
||||
});
|
||||
|
||||
// handlePresetChange only ever applies an available preset, but that guarantee can go stale by
|
||||
// submit time: select under a caller with the full family, then switch to a caller missing one
|
||||
// of its models. Nothing clears the selection (that would erase in-progress Custom edits too),
|
||||
// so submit itself must re-verify against the current caller's list before creating the router.
|
||||
it("blocks submit when the selected preset's models are no longer available for the current caller", async () => {
|
||||
const user = userEvent.setup();
|
||||
mockFetchAvailableModels
|
||||
.mockResolvedValueOnce(ALL_FAMILY_MODELS)
|
||||
.mockResolvedValueOnce(ALL_FAMILY_MODELS.filter((m) => m.model_group !== "o3"));
|
||||
|
||||
const { rerender } = renderWithProviders(
|
||||
<AddAutoRouterTab handleOk={vi.fn()} accessToken="caller-a" userRole="Admin" />,
|
||||
);
|
||||
openTemplateDropdown();
|
||||
await waitFor(() => expect(isOptionDisabled(optionByLabel("OpenAI Family")!)).toBe(false));
|
||||
fireEvent.click(optionByLabel("OpenAI Family")!);
|
||||
|
||||
rerender(<AddAutoRouterTab handleOk={vi.fn()} accessToken="caller-b" userRole="Admin" />);
|
||||
await waitFor(() => expect(mockFetchAvailableModels).toHaveBeenCalledTimes(2));
|
||||
|
||||
await user.type(screen.getByPlaceholderText(/smart_router/i), "stale-preset-router");
|
||||
await user.click(screen.getByRole("button", { name: /add auto router/i }));
|
||||
|
||||
expect(NotificationManager.fromBackend).toHaveBeenCalledWith(
|
||||
"This template's models are no longer available. Please reselect a template or switch to Custom.",
|
||||
);
|
||||
expect(mockHandleAddAutoRouterSubmit).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
// Prefill must preserve a preset's deliberately-falsy fields (a 0 match threshold, an empty
|
||||
// escalation list). Using `||` instead of `??` would swap the 0 for the create-form default and
|
||||
// re-enable escalation the preset meant to turn off, so this asserts the exact submitted values.
|
||||
|
|
|
|||
|
|
@ -189,6 +189,20 @@ const AddAutoRouterTab: React.FC<AddAutoRouterTabProps> = ({
|
|||
return;
|
||||
}
|
||||
|
||||
// handlePresetChange only ever applies a preset that was available at selection time; that
|
||||
// guarantee can go stale by submit time (e.g. the caller's model access narrowed since), so
|
||||
// re-verify here rather than trust state gathered earlier.
|
||||
if (selectedPreset !== "custom") {
|
||||
const preset = getPresetByKey(selectedPreset);
|
||||
if (!preset || presetAvailability(preset).kind !== "available") {
|
||||
setShowValidationErrors(true);
|
||||
NotificationManager.fromBackend(
|
||||
"This template's models are no longer available. Please reselect a template or switch to Custom.",
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const {
|
||||
tiers,
|
||||
classifier_type: classifierType,
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue