From cedae29cc7e72f3f9f9ff8517f8a42129077c088 Mon Sep 17 00:00:00 2001 From: yuneng-jiang Date: Tue, 27 Oct 2026 16:26:01 -0700 Subject: [PATCH] fix(ui): keep vendor names and agent instructions in AI wording under SI terminology (#48670) XecGuard AI and Airia AI join the third-party names the SI swap leaves alone, so their Guardrail Garden cards stop reading "XecGuard SI" and "Airia SI" The copyable agent instructions go to Claude Code and Codex, so they keep AI the same way the compliance prompts do --- .../shared/CreatedKeyDisplay.test.tsx | 30 ++++++++++++++----- .../components/shared/CreatedKeyDisplay.tsx | 9 ++---- .../src/lib/terminology.test.ts | 2 ++ ui/litellm-dashboard/src/lib/terminology.ts | 2 ++ 4 files changed, 29 insertions(+), 14 deletions(-) diff --git a/ui/litellm-dashboard/src/components/shared/CreatedKeyDisplay.test.tsx b/ui/litellm-dashboard/src/components/shared/CreatedKeyDisplay.test.tsx index e35fd788759..4646f075b1d 100644 --- a/ui/litellm-dashboard/src/components/shared/CreatedKeyDisplay.test.tsx +++ b/ui/litellm-dashboard/src/components/shared/CreatedKeyDisplay.test.tsx @@ -2,7 +2,7 @@ import { cloneElement, type MouseEventHandler, type ReactElement, type ReactNode import { describe, it, expect, vi, beforeEach, afterEach } from "vitest"; import { render, screen, act } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; -import { toSiTerminology } from "@/lib/terminology"; +import { TerminologyProvider, useTerminology } from "@/contexts/TerminologyContext"; import CreatedKeyDisplay, { buildAgentInstructions } from "./CreatedKeyDisplay"; import { toast } from "@/lib/toast"; @@ -13,6 +13,10 @@ vi.mock("@/components/networking", () => ({ getProxyBaseUrl: vi.fn(() => "http://gw.test:4000/"), })); +vi.mock("@/app/(dashboard)/hooks/uiConfig/useUIConfig", () => ({ + useUIConfig: () => ({ data: { use_si_terminology: true } }), +})); + vi.mock("react-copy-to-clipboard", () => ({ CopyToClipboard: ({ text, @@ -159,6 +163,23 @@ describe("CreatedKeyDisplay", () => { expect(screen.getByRole("button", { name: "Copied!" })).toBeInTheDocument(); expect(toast.success).toHaveBeenCalledWith("Instructions copied"); }); + + it("copies agent instructions in AI wording even when the UI uses SI terminology", async () => { + const user = userEvent.setup({ advanceTimers: vi.advanceTimersByTime }); + const GatewayLabel = () => {useTerminology()("AI Gateway")}; + render( + + + + , + ); + + expect(screen.getByText("SI Gateway")).toBeInTheDocument(); + await user.click(screen.getByRole("button", { name: "Copy instructions" })); + + expect(mockCopy).toHaveBeenLastCalledWith(buildAgentInstructions("http://gw.test:4000", "sk-test-123")); + expect(mockCopy.mock.calls.at(-1)?.[0]).toContain("Use this LiteLLM AI gateway for model inference."); + }); }); describe("buildAgentInstructions", () => { @@ -183,11 +204,4 @@ describe("buildAgentInstructions", () => { ].join("\n"), ); }); - - it("rewrites only its own copy under SI terminology, never the gateway URL or the key", () => { - const lines = buildAgentInstructions("http://AI.example.com/", "sk-AI-1", toSiTerminology).split("\n"); - expect(lines[0]).toBe("Use this LiteLLM SI gateway for model inference."); - expect(lines).toContain("Gateway URL: http://AI.example.com"); - expect(lines).toContain("Virtual key: sk-AI-1"); - }); }); diff --git a/ui/litellm-dashboard/src/components/shared/CreatedKeyDisplay.tsx b/ui/litellm-dashboard/src/components/shared/CreatedKeyDisplay.tsx index e483d2546bb..d58a1864575 100644 --- a/ui/litellm-dashboard/src/components/shared/CreatedKeyDisplay.tsx +++ b/ui/litellm-dashboard/src/components/shared/CreatedKeyDisplay.tsx @@ -1,5 +1,3 @@ -import { useTerminology } from "@/contexts/TerminologyContext"; -import { keepTerminology, type Term } from "@/lib/terminology"; import React, { useState } from "react"; import { CopyToClipboard } from "react-copy-to-clipboard"; import { ChevronDown, ChevronUp } from "lucide-react"; @@ -13,14 +11,14 @@ interface CreatedKeyDisplayProps { apiKey: string; } -export const buildAgentInstructions = (baseUrl: string, apiKey: string, term: Term = keepTerminology): string => { +export const buildAgentInstructions = (baseUrl: string, apiKey: string): string => { const normalizedBaseUrl = baseUrl.replace(/\/+$/, ""); const endpointIntroduction = "LiteLLM is a gateway that exposes OpenAI and Anthropic compatible APIs. " + `Send the virtual key as a Bearer token (Authorization: Bearer ${apiKey}) to:`; return [ - term("Use this LiteLLM AI gateway for model inference."), + "Use this LiteLLM AI gateway for model inference.", "", `Gateway URL: ${normalizedBaseUrl}`, `Virtual key: ${apiKey}`, @@ -43,12 +41,11 @@ export const buildAgentInstructions = (baseUrl: string, apiKey: string, term: Te * Used on the Virtual Keys page and in the Add Agent wizard. */ const CreatedKeyDisplay: React.FC = ({ apiKey }) => { - const term = useTerminology(); const [copied, setCopied] = useState(false); const [instructionsCopied, setInstructionsCopied] = useState(false); const [showInstructions, setShowInstructions] = useState(false); const baseUrl = getProxyBaseUrl().replace(/\/+$/, ""); - const instructions = buildAgentInstructions(baseUrl, apiKey, term); + const instructions = buildAgentInstructions(baseUrl, apiKey); const handleCopy = () => { setCopied(true); diff --git a/ui/litellm-dashboard/src/lib/terminology.test.ts b/ui/litellm-dashboard/src/lib/terminology.test.ts index 9e5d6af036e..7fa24cb4f83 100644 --- a/ui/litellm-dashboard/src/lib/terminology.test.ts +++ b/ui/litellm-dashboard/src/lib/terminology.test.ts @@ -39,6 +39,8 @@ describe("toSiTerminology", () => { ["Cisco AI Defense Inspection API for AI apps", "Cisco AI Defense Inspection API for SI apps"], ["DeepKeep AI Firewall for AI security", "DeepKeep AI Firewall for SI security"], ["Defend AI Agentic Guardrails", "Defend AI Agentic Guardrails"], + ["CyCraft XecGuard AI security gateway for AI apps", "CyCraft XecGuard AI security gateway for SI apps"], + ["Airia AI security platform for responsible AI", "Airia AI security platform for responsible SI"], ])("keeps third-party names inside %j", (input, expected) => { expect(toSiTerminology(input)).toBe(expected); }); diff --git a/ui/litellm-dashboard/src/lib/terminology.ts b/ui/litellm-dashboard/src/lib/terminology.ts index 0f18ae95fa9..e056ae59f55 100644 --- a/ui/litellm-dashboard/src/lib/terminology.ts +++ b/ui/litellm-dashboard/src/lib/terminology.ts @@ -9,6 +9,8 @@ const THIRD_PARTY_NAMES: readonly string[] = [ "DeepKeep AI Firewall", "Defend AI", "Prisma AI Runtime Security", + "XecGuard AI", + "Airia AI", ]; const THIRD_PARTY_NAME_PATTERN = new RegExp(`(${THIRD_PARTY_NAMES.join("|")})`);