This commit is contained in:
Animesh Kumar 2026-09-12 13:30:18 +00:00 committed by GitHub
commit 47dedc01c4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 167 additions and 41 deletions

View file

@ -1,6 +1,10 @@
import { describe, expect, it } from "vitest";
import { getModelDisplayName } from "./fetch_available_models_team_key";
import {
collapseModelSentinelSelection,
getModelDisplayName,
hasModelSentinel,
} from "./fetch_available_models_team_key";
describe("getModelDisplayName", () => {
it("should return display label for all proxy models", () => {
@ -11,3 +15,31 @@ describe("getModelDisplayName", () => {
expect(getModelDisplayName("openai/*")).toBe("All openai models");
});
});
describe("hasModelSentinel", () => {
it.each(["all-proxy-models", "all-team-models", "no-default-models"])(
"treats %s as exclusive, so the concrete models alongside it are unpickable",
(sentinel) => {
expect(hasModelSentinel([sentinel])).toBe(true);
},
);
it("leaves a plain selection pickable", () => {
expect(hasModelSentinel(["gpt-4o", "claude-sonnet-4-5"])).toBe(false);
expect(hasModelSentinel([])).toBe(false);
});
});
describe("collapseModelSentinelSelection", () => {
it.each(["all-team-models", "all-proxy-models", "no-default-models"])(
"drops the concrete models already picked when %s is chosen",
(sentinel) => {
expect(collapseModelSentinelSelection(["gpt-4o", sentinel])).toEqual([sentinel]);
},
);
it("leaves a selection of real models alone", () => {
expect(collapseModelSentinelSelection(["gpt-4o", "claude-sonnet-4-5"])).toEqual(["gpt-4o", "claude-sonnet-4-5"]);
expect(collapseModelSentinelSelection([])).toEqual([]);
});
});

View file

@ -38,8 +38,15 @@ export const fetchAvailableModelsForTeamOrKey = async (
export const excludeProxyWideSentinel = (models: string[]): string[] =>
models.filter((model) => model !== "all-proxy-models");
export const hasAllModelsSentinel = (models: string[]): boolean =>
models.includes("all-proxy-models") || models.includes("all-team-models");
const MODEL_SENTINELS = ["all-team-models", "all-proxy-models", "no-default-models"] as const;
export const hasModelSentinel = (models: string[]): boolean =>
MODEL_SENTINELS.some((sentinel) => models.includes(sentinel));
export const collapseModelSentinelSelection = (models: string[]): string[] => {
const sentinel = MODEL_SENTINELS.find((candidate) => models.includes(candidate));
return sentinel === undefined ? models : [sentinel];
};
export const getModelDisplayName = (model: string) => {
if (model === "all-proxy-models") {

View file

@ -776,6 +776,44 @@ describe("CreateKey", () => {
expect(await screen.findByRole("option", { name: "All Team Models" })).toBeInTheDocument();
expect(screen.queryByRole("option", { name: "All Proxy Models" })).not.toBeInTheDocument();
});
it("offers No Default Models whether or not a team is selected", async () => {
await openModal();
await userEvent.click(await screen.findByLabelText("Models"));
expect(await screen.findByRole("option", { name: "No Default Models" })).toBeInTheDocument();
});
it("sends the no-default-models sentinel rather than an empty list", async () => {
await openModal();
await nameTheKey();
await userEvent.click(await screen.findByLabelText("Models"));
await userEvent.click(await screen.findByRole("option", { name: "No Default Models" }));
await userEvent.keyboard("{Escape}");
await submit();
expect((await createdPayload()).models).toStrictEqual(["no-default-models"]);
});
it("drops a model already picked when No Default Models is chosen after it", async () => {
state.teams = [{ team_id: "team-1", team_alias: "Team One", models: ["team-model-1"] }];
await openModal({ teams: state.teams as unknown as Team[] });
await nameTheKey();
await userEvent.click(await screen.findByLabelText("Team"));
await userEvent.click(await screen.findByRole("option", { name: /Team One/ }));
await userEvent.click(await screen.findByLabelText("Models"));
await userEvent.click(await screen.findByRole("option", { name: "team-model-1" }));
await userEvent.click(await screen.findByRole("option", { name: "No Default Models" }));
await userEvent.keyboard("{Escape}");
await submit();
expect((await createdPayload()).models).toStrictEqual(["no-default-models"]);
});
});
describe("organization dropdown", () => {

View file

@ -58,7 +58,8 @@ import { TagRateLimitEditor, TagRateLimitEntry } from "../key_team_helpers/TagRa
import {
excludeProxyWideSentinel,
getModelDisplayName,
hasAllModelsSentinel,
collapseModelSentinelSelection,
hasModelSentinel,
} from "../key_team_helpers/fetch_available_models_team_key";
import { Team } from "../key_team_helpers/key_list";
import MCPServerSelector from "../mcp_server_management/MCPServerSelector";
@ -626,18 +627,25 @@ const CreateKey: React.FC<CreateKeyProps> = ({ team, teams, data, addKey, autoOp
setSelectedProjectId(projectId);
};
const sentinelOptions: MultiSelectOption[] =
selectedProjectId === null
? [
selectedCreateKeyTeam
? { value: "all-team-models", label: "All Team Models" }
: { value: "all-proxy-models", label: "All Proxy Models" },
{ value: "no-default-models", label: "No Default Models" },
]
: [];
const sentinelValues = new Set(sentinelOptions.map((option) => option.value));
const modelOptions: MultiSelectOption[] = [
...(selectedProjectId === null && selectedCreateKeyTeam
? [{ value: "all-team-models", label: "All Team Models" }]
: []),
...(selectedProjectId === null && !selectedCreateKeyTeam
? [{ value: "all-proxy-models", label: "All Proxy Models" }]
: []),
...modelsToPick.map((model) => ({
value: model,
label: getModelDisplayName(model),
disabled: hasAllModelsSentinel(selectedModels),
})),
...sentinelOptions,
...modelsToPick
.filter((model) => !sentinelValues.has(model))
.map((model) => ({
value: model,
label: getModelDisplayName(model),
disabled: hasModelSentinel(selectedModels),
})),
];
const changeKeyType = (write: FieldWrite) => (value: string) => {
@ -908,14 +916,7 @@ const CreateKey: React.FC<CreateKeyProps> = ({ team, teams, data, addKey, autoOp
value={(control.value as string[] | undefined) ?? []}
placeholder="Select models"
disabled={keyType === "management" || keyType === "read_only"}
onValueChange={(values) => {
control.onChange(values);
if (values.includes("all-team-models")) {
form.setValue("models", ["all-team-models"]);
} else if (values.includes("all-proxy-models")) {
form.setValue("models", ["all-proxy-models"]);
}
}}
onValueChange={(values) => control.onChange(collapseModelSentinelSelection(values))}
/>
)}
</MountedFormField>

View file

@ -0,0 +1,23 @@
import { describe, expect, it } from "vitest";
import { modelSentinelOptions } from "./keyEditFieldNormalizers";
describe("modelSentinelOptions", () => {
it("offers No Default Models on a key with no team, so an access-group-only key is selectable", () => {
expect(modelSentinelOptions(null, false)).toEqual([
{ value: "all-proxy-models", label: "All Proxy Models" },
{ value: "no-default-models", label: "No Default Models" },
]);
});
it("offers No Default Models on a team key once the team has loaded", () => {
expect(modelSentinelOptions("team-1", true)).toEqual([
{ value: "all-team-models", label: "All Team Models" },
{ value: "no-default-models", label: "No Default Models" },
]);
});
it("offers nothing while a team key is still loading its team", () => {
expect(modelSentinelOptions("team-1", false)).toEqual([]);
});
});

View file

@ -30,8 +30,9 @@ export const modelSentinelOptions = (
keyTeamId: string | null | undefined,
teamLoaded: boolean,
): { value: string; label: string }[] => {
if (keyTeamId == null) return [{ value: "all-proxy-models", label: "All Proxy Models" }];
return teamLoaded ? [{ value: "all-team-models", label: "All Team Models" }] : [];
const noDefaultModels = { value: "no-default-models", label: "No Default Models" };
if (keyTeamId == null) return [{ value: "all-proxy-models", label: "All Proxy Models" }, noDefaultModels];
return teamLoaded ? [{ value: "all-team-models", label: "All Team Models" }, noDefaultModels] : [];
};
export const currentValuePlaceholder = (

View file

@ -326,6 +326,30 @@ describe("KeyEditView", () => {
});
});
it("lists No Default Models once when the available models already carry the sentinel", async () => {
vi.mocked(modelAvailableCall).mockResolvedValue({
data: [{ id: "gpt-4" }, { id: "no-default-models" }],
});
renderWithProviders(
<KeyEditView
keyData={MOCK_KEY_DATA}
onCancel={() => {}}
onSubmit={async () => {}}
accessToken={"test-token"}
userID={"user-1"}
userRole={"Admin"}
premiumUser={false}
/>,
);
await userEvent.click(await screen.findByLabelText("Models"));
expect(await screen.findByRole("option", { name: "gpt-4" })).toBeInTheDocument();
expect(screen.getAllByRole("option", { name: "No Default Models" })).toHaveLength(1);
expect(screen.queryByRole("option", { name: "no-default-models" })).not.toBeInTheDocument();
});
it("should render tags", async () => {
renderWithProviders(
<KeyEditView

View file

@ -49,7 +49,11 @@ import {
tagLimitsToRows,
tagRowsToLimits,
} from "../key_team_helpers/TagRateLimitEditor";
import { excludeProxyWideSentinel, hasAllModelsSentinel } from "../key_team_helpers/fetch_available_models_team_key";
import {
collapseModelSentinelSelection,
excludeProxyWideSentinel,
hasModelSentinel,
} from "../key_team_helpers/fetch_available_models_team_key";
import { KeyResponse } from "../key_team_helpers/key_list";
import MCPServerSelector from "../mcp_server_management/MCPServerSelector";
import MCPToolPermissions from "../mcp_server_management/MCPToolPermissions";
@ -325,13 +329,17 @@ export function KeyEditView({
form.setValue("disabled_callbacks", internalValues);
};
const sentinelOptions = modelSentinelOptions(keyData.team_id, team != null);
const sentinelValues = new Set(sentinelOptions.map((option) => option.value));
const modelOptions = [
...modelSentinelOptions(keyData.team_id, team != null),
...availableModels.map((model) => ({
value: model,
label: model,
disabled: hasAllModelsSentinel(selectedModels),
})),
...sentinelOptions,
...availableModels
.filter((model) => !sentinelValues.has(model))
.map((model) => ({
value: model,
label: model,
disabled: hasModelSentinel(selectedModels),
})),
];
const visibleTeams = selectedOrganizationId
@ -361,15 +369,7 @@ export function KeyEditView({
id={id}
options={modelOptions}
value={isModelsDisabled ? [] : (value as string[] | undefined) ?? []}
onValueChange={(next) => {
if (next.includes("all-team-models")) {
onChange(["all-team-models"]);
} else if (next.includes("all-proxy-models")) {
onChange(["all-proxy-models"]);
} else {
onChange(next);
}
}}
onValueChange={(next) => onChange(collapseModelSentinelSelection(next))}
disabled={isModelsDisabled}
placeholder="Select models"
/>