mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-28 01:32:17 +00:00
Merge 0fb5aeb5e5 into 5028f9ec59
This commit is contained in:
commit
50fe35ea3e
4 changed files with 117 additions and 16 deletions
|
|
@ -3,6 +3,7 @@ import {
|
|||
filterSensitiveMetadata,
|
||||
extractLoggingSettings,
|
||||
formatMetadataForDisplay,
|
||||
mergeKeyTags,
|
||||
stripTagsFromMetadata,
|
||||
} from "./key_info_utils";
|
||||
|
||||
|
|
@ -57,3 +58,30 @@ describe("stripTagsFromMetadata", () => {
|
|||
expect(input).toEqual(originalCopy);
|
||||
});
|
||||
});
|
||||
|
||||
describe("mergeKeyTags", () => {
|
||||
it("returns tags from the dedicated field", () => {
|
||||
expect(mergeKeyTags(["a", "b"], {})).toEqual(["a", "b"]);
|
||||
});
|
||||
|
||||
it("returns tags from metadata when the dedicated field is undefined", () => {
|
||||
expect(mergeKeyTags(undefined, { tags: ["a", "b"] })).toEqual(["a", "b"]);
|
||||
});
|
||||
|
||||
it("deduplicates overlapping tags with dedicated field order first", () => {
|
||||
expect(mergeKeyTags(["a", "b"], { tags: ["b", "c"] })).toEqual(["a", "b", "c"]);
|
||||
});
|
||||
|
||||
it("drops non-string metadata tags", () => {
|
||||
expect(mergeKeyTags(undefined, { tags: ["a", 1, null, false, "b"] })).toEqual(["a", "b"]);
|
||||
});
|
||||
|
||||
it("returns field tags when metadata is null or a string", () => {
|
||||
expect(mergeKeyTags(["a"], null)).toEqual(["a"]);
|
||||
expect(mergeKeyTags(["b"], "metadata")).toEqual(["b"]);
|
||||
});
|
||||
|
||||
it("returns field tags when metadata has no tags key", () => {
|
||||
expect(mergeKeyTags(["a"], { keep: "x" })).toEqual(["a"]);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -61,3 +61,12 @@ export const stripTagsFromMetadata = (metadata: any) => {
|
|||
const { tags, ...rest } = metadata as Record<string, any>;
|
||||
return rest;
|
||||
};
|
||||
|
||||
export const mergeKeyTags = (fieldTags: unknown, metadata: unknown): string[] => {
|
||||
const isString = (value: unknown): value is string => typeof value === "string";
|
||||
const fromField = Array.isArray(fieldTags) ? fieldTags.filter(isString) : [];
|
||||
const metadataTags =
|
||||
metadata && typeof metadata === "object" ? (metadata as Record<string, unknown>).tags : undefined;
|
||||
const fromMetadata = Array.isArray(metadataTags) ? metadataTags.filter(isString) : [];
|
||||
return Array.from(new Set([...fromField, ...fromMetadata]));
|
||||
};
|
||||
|
|
|
|||
|
|
@ -49,11 +49,14 @@ vi.mock("@/utils/dataUtils", () => ({
|
|||
copyToClipboard: async () => true,
|
||||
formatNumberWithCommas: (n: any) => String(n),
|
||||
}));
|
||||
vi.mock("../key_info_utils", () => ({
|
||||
extractLoggingSettings: () => ({}),
|
||||
formatMetadataForDisplay: (m: any) => JSON.stringify(m, null, 2),
|
||||
stripTagsFromMetadata: (m: any) => m,
|
||||
}));
|
||||
vi.mock("../key_info_utils", async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import("../key_info_utils")>();
|
||||
return {
|
||||
...actual,
|
||||
extractLoggingSettings: () => ({}),
|
||||
formatMetadataForDisplay: (m: any) => JSON.stringify(m, null, 2),
|
||||
};
|
||||
});
|
||||
vi.mock("../callback_info_helpers", () => ({
|
||||
callback_map: {},
|
||||
mapInternalToDisplayNames: (x: any) => x,
|
||||
|
|
@ -344,6 +347,66 @@ describe("KeyInfoView handleKeyUpdate guardrails guard", () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe("KeyInfoView handleKeyUpdate metadata tags", () => {
|
||||
it("merges tags from the field and metadata JSON while preserving metadata", async () => {
|
||||
renderView(true);
|
||||
|
||||
fireEvent.click(screen.getByText("Settings"));
|
||||
fireEvent.click(screen.getByText("Edit Settings"));
|
||||
(globalThis as any).__TEST_FORM_VALUES = {
|
||||
token: "tok_123",
|
||||
tags: ["ui-tag"],
|
||||
metadata: JSON.stringify({ tags: ["headroom-pilot"], keep: "x" }),
|
||||
};
|
||||
|
||||
fireEvent.click(screen.getByText("Mock Submit"));
|
||||
|
||||
await waitFor(() => expect(keyUpdateCallMock).toHaveBeenCalled());
|
||||
|
||||
const [, sentPayload] = keyUpdateCallMock.mock.calls[0];
|
||||
expect(sentPayload.metadata.tags).toEqual(["ui-tag", "headroom-pilot"]);
|
||||
expect(sentPayload.metadata.keep).toBe("x");
|
||||
expect("tags" in sentPayload).toBe(false);
|
||||
});
|
||||
|
||||
it("preserves metadata tags when the dedicated field is absent", async () => {
|
||||
renderView(true);
|
||||
|
||||
fireEvent.click(screen.getByText("Settings"));
|
||||
fireEvent.click(screen.getByText("Edit Settings"));
|
||||
(globalThis as any).__TEST_FORM_VALUES = {
|
||||
token: "tok_123",
|
||||
metadata: JSON.stringify({ tags: ["headroom-pilot"] }),
|
||||
};
|
||||
|
||||
fireEvent.click(screen.getByText("Mock Submit"));
|
||||
|
||||
await waitFor(() => expect(keyUpdateCallMock).toHaveBeenCalled());
|
||||
|
||||
const [, sentPayload] = keyUpdateCallMock.mock.calls[0];
|
||||
expect(sentPayload.metadata.tags).toEqual(["headroom-pilot"]);
|
||||
});
|
||||
|
||||
it("merges and deduplicates tags from object metadata", async () => {
|
||||
renderView(true);
|
||||
|
||||
fireEvent.click(screen.getByText("Settings"));
|
||||
fireEvent.click(screen.getByText("Edit Settings"));
|
||||
(globalThis as any).__TEST_FORM_VALUES = {
|
||||
token: "tok_123",
|
||||
tags: ["ui-tag", "headroom-pilot"],
|
||||
metadata: { tags: ["headroom-pilot"] },
|
||||
};
|
||||
|
||||
fireEvent.click(screen.getByText("Mock Submit"));
|
||||
|
||||
await waitFor(() => expect(keyUpdateCallMock).toHaveBeenCalled());
|
||||
|
||||
const [, sentPayload] = keyUpdateCallMock.mock.calls[0];
|
||||
expect(sentPayload.metadata.tags).toEqual(["ui-tag", "headroom-pilot"]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("KeyInfoView handleKeyUpdate mcp_toolsets", () => {
|
||||
it("should forward the toolsets the edit form supplies into object_permission", async () => {
|
||||
renderView(true);
|
||||
|
|
|
|||
|
|
@ -31,7 +31,12 @@ import AutoRotationView from "../common_components/AutoRotationView";
|
|||
import DeleteResourceModal from "../common_components/DeleteResourceModal";
|
||||
import RouterSettingsSummary from "../common_components/RouterSettingsSummary";
|
||||
import { hasRouterSettings } from "../common_components/routerSettingsPayload";
|
||||
import { extractLoggingSettings, formatMetadataForDisplay, stripTagsFromMetadata } from "../key_info_utils";
|
||||
import {
|
||||
extractLoggingSettings,
|
||||
formatMetadataForDisplay,
|
||||
mergeKeyTags,
|
||||
stripTagsFromMetadata,
|
||||
} from "../key_info_utils";
|
||||
import { KeyResponse } from "../key_team_helpers/key_list";
|
||||
import LoggingSettingsView from "../logging_settings_view";
|
||||
import { toast } from "@/lib/toast";
|
||||
|
|
@ -297,13 +302,10 @@ export default function KeyInfoView({
|
|||
if (formValues.metadata && typeof formValues.metadata === "string") {
|
||||
try {
|
||||
const parsedMetadata = JSON.parse(formValues.metadata);
|
||||
// Ensure tags are controlled via dedicated field, not in metadata textarea
|
||||
if ("tags" in parsedMetadata) {
|
||||
delete parsedMetadata["tags"];
|
||||
}
|
||||
const tags = mergeKeyTags(formValues.tags, parsedMetadata);
|
||||
formValues.metadata = {
|
||||
...parsedMetadata,
|
||||
...(Array.isArray(formValues.tags) && formValues.tags.length > 0 ? { tags: formValues.tags } : {}),
|
||||
...stripTagsFromMetadata(parsedMetadata),
|
||||
...(tags.length > 0 ? { tags } : {}),
|
||||
...(formValues.guardrails?.length > 0 ? { guardrails: formValues.guardrails } : {}),
|
||||
...(Array.isArray(formValues.logging_settings) && formValues.logging_settings.length > 0
|
||||
? { logging: formValues.logging_settings }
|
||||
|
|
@ -321,10 +323,10 @@ export default function KeyInfoView({
|
|||
}
|
||||
} else {
|
||||
const baseMetadata = formValues.metadata || {};
|
||||
const { tags: _omitTags, ...rest } = baseMetadata;
|
||||
const tags = mergeKeyTags(formValues.tags, baseMetadata);
|
||||
formValues.metadata = {
|
||||
...rest,
|
||||
...(Array.isArray(formValues.tags) && formValues.tags.length > 0 ? { tags: formValues.tags } : {}),
|
||||
...stripTagsFromMetadata(baseMetadata),
|
||||
...(tags.length > 0 ? { tags } : {}),
|
||||
...(formValues.guardrails?.length > 0 ? { guardrails: formValues.guardrails } : {}),
|
||||
...(Array.isArray(formValues.logging_settings) && formValues.logging_settings.length > 0
|
||||
? { logging: formValues.logging_settings }
|
||||
|
|
@ -337,7 +339,6 @@ export default function KeyInfoView({
|
|||
};
|
||||
}
|
||||
|
||||
// tags are merged into metadata; do not send as top-level field
|
||||
if ("tags" in formValues) {
|
||||
delete formValues.tags;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue