mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-07 08:26:10 +00:00
fix(ui): accept any routing group name the backend accepts
The create form rejected names with slashes or spaces even though the proxy stores and routes any non-empty string. Drop the client-only character pattern and trim the name before the required check so a whitespace-only name is still refused Claude-Session: https://claude.ai/code/session_01HkaXiD6gssHnx3kqu1rR8C
This commit is contained in:
parent
300d335255
commit
6f18a4d81e
3 changed files with 24 additions and 7 deletions
|
|
@ -19,6 +19,13 @@ const EXPECTED_STORED_PAYLOAD: RoutingGroup = {
|
|||
|
||||
const SEEDED_CREATE: RoutingGroup = { group_name: "", models: ["gemini-pro"], routing_strategy: "simple-shuffle" };
|
||||
|
||||
const EXPECTED_SLASH_AND_SPACE_PAYLOAD: RoutingGroup = {
|
||||
group_name: "team a/fast chat",
|
||||
models: ["gemini-pro"],
|
||||
routing_strategy: "simple-shuffle",
|
||||
routing_strategy_args: null,
|
||||
};
|
||||
|
||||
const STORED_GROUP: RoutingGroup = {
|
||||
group_name: "already-taken",
|
||||
models: ["gpt-4o", "claude-sonnet"],
|
||||
|
|
@ -211,16 +218,28 @@ describe("RoutingGroupModal", () => {
|
|||
expect(onSubmit).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("rejects a name with characters outside the allowed set", async () => {
|
||||
it("accepts a name with slashes and spaces, since the backend does", async () => {
|
||||
const user = userEvent.setup();
|
||||
const { onSubmit } = renderModal({
|
||||
initialValue: { group_name: "", models: ["gemini-pro"], routing_strategy: "simple-shuffle" },
|
||||
});
|
||||
|
||||
await typeName(user, "bad name");
|
||||
await typeName(user, "team a/fast chat");
|
||||
await save(user, "Create Group");
|
||||
|
||||
expect(await screen.findByText("Only letters, numbers, dot, underscore, and dash are allowed")).toBeInTheDocument();
|
||||
expect(onSubmit).toHaveBeenCalledWith(EXPECTED_SLASH_AND_SPACE_PAYLOAD);
|
||||
});
|
||||
|
||||
it("rejects a whitespace-only name as missing", async () => {
|
||||
const user = userEvent.setup();
|
||||
const { onSubmit } = renderModal({
|
||||
initialValue: { group_name: "", models: ["gemini-pro"], routing_strategy: "simple-shuffle" },
|
||||
});
|
||||
|
||||
await typeName(user, " ");
|
||||
await save(user, "Create Group");
|
||||
|
||||
expect(await screen.findByText("Group name is required")).toBeInTheDocument();
|
||||
expect(onSubmit).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -23,7 +23,6 @@ import { Textarea } from "@/components/ui/textarea";
|
|||
import { useZodForm } from "@/lib/forms/useZodForm";
|
||||
import {
|
||||
GROUP_NAME_MAX_LENGTH,
|
||||
GROUP_NAME_PATTERN,
|
||||
STRATEGIES_WITH_ARGS,
|
||||
argsForStrategy,
|
||||
buildRoutingGroupPayload,
|
||||
|
|
@ -74,10 +73,10 @@ const RoutingGroupModal: React.FC<RoutingGroupModalProps> = ({
|
|||
const shape = {
|
||||
group_name: z
|
||||
.string()
|
||||
.trim()
|
||||
.min(1, "Group name is required")
|
||||
.max(GROUP_NAME_MAX_LENGTH, `Must be ${GROUP_NAME_MAX_LENGTH} characters or fewer`)
|
||||
.regex(GROUP_NAME_PATTERN, "Only letters, numbers, dot, underscore, and dash are allowed")
|
||||
.refine((value) => !reservedNames.has(value.trim().toLowerCase()), "A group with this name already exists"),
|
||||
.refine((value) => !reservedNames.has(value.toLowerCase()), "A group with this name already exists"),
|
||||
models: z.array(z.string()).min(1, "Select at least one model"),
|
||||
routing_strategy: z.string().min(1, "Strategy is required"),
|
||||
routing_strategy_args: z.string(),
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ import type { RoutingGroup } from "./types";
|
|||
|
||||
export const STRATEGIES_WITH_ARGS = new Set<string>(["latency-based-routing", "usage-based-routing"]);
|
||||
|
||||
export const GROUP_NAME_PATTERN = /^[A-Za-z0-9._-]+$/;
|
||||
export const GROUP_NAME_MAX_LENGTH = 64;
|
||||
|
||||
export interface RoutingGroupFormValues {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue