diff --git a/ui/litellm-dashboard/e2e_tests/tests/proxy-admin/keys.spec.ts b/ui/litellm-dashboard/e2e_tests/tests/proxy-admin/keys.spec.ts index 1e44d9a25a0..d0f81eac7b3 100644 --- a/ui/litellm-dashboard/e2e_tests/tests/proxy-admin/keys.spec.ts +++ b/ui/litellm-dashboard/e2e_tests/tests/proxy-admin/keys.spec.ts @@ -41,8 +41,8 @@ test.describe("Proxy Admin - Keys", () => { // Submit await page.getByRole("button", { name: "Create Key", exact: true }).click(); - // Success shows "Save your Key" in a second dialog - await expect(page.getByText("Save your Key")).toBeVisible({ timeout: 10_000 }); + // Success shows "API Key Created" in a second dialog + await expect(page.getByText("API Key Created")).toBeVisible({ timeout: 10_000 }); await page.keyboard.press("Escape"); // Verify the new key appears in the table @@ -150,7 +150,7 @@ test.describe("Proxy Admin - Keys", () => { await page.getByRole("button", { name: "Create Key", exact: true }).click(); - await expect(page.getByText("Save your Key")).toBeVisible({ timeout: 10_000 }); + await expect(page.getByText("API Key Created")).toBeVisible({ timeout: 10_000 }); await page.keyboard.press("Escape"); await expect(page.getByText(keyName)).toBeVisible({ timeout: 10_000 }); @@ -182,7 +182,7 @@ test.describe("Proxy Admin - Keys", () => { await page.getByRole("button", { name: "Create Key", exact: true }).click(); - await expect(page.getByText("Save your Key")).toBeVisible({ timeout: 10_000 }); + await expect(page.getByText("API Key Created")).toBeVisible({ timeout: 10_000 }); // Grab the new key from the success modal (rendered inside a
) and
// verify it can call /chat/completions for the model it was scoped to.
diff --git a/ui/litellm-dashboard/e2e_tests/tests/team-admin/teamAdmin.spec.ts b/ui/litellm-dashboard/e2e_tests/tests/team-admin/teamAdmin.spec.ts
index 1612e6929bd..aa33ce75654 100644
--- a/ui/litellm-dashboard/e2e_tests/tests/team-admin/teamAdmin.spec.ts
+++ b/ui/litellm-dashboard/e2e_tests/tests/team-admin/teamAdmin.spec.ts
@@ -109,7 +109,7 @@ test.describe("Team Admin", () => {
await page.getByRole("button", { name: "Create Key", exact: true }).click();
- await expect(page.getByText("Save your Key")).toBeVisible({ timeout: 10_000 });
+ await expect(page.getByText("API Key Created")).toBeVisible({ timeout: 10_000 });
await page.keyboard.press("Escape");
await expect(page.getByText(keyName)).toBeVisible({ timeout: 10_000 });
diff --git a/ui/litellm-dashboard/src/components/organisms/create_key_button.test.tsx b/ui/litellm-dashboard/src/components/organisms/create_key_button.test.tsx
index bd139df4833..27436a27b67 100644
--- a/ui/litellm-dashboard/src/components/organisms/create_key_button.test.tsx
+++ b/ui/litellm-dashboard/src/components/organisms/create_key_button.test.tsx
@@ -52,6 +52,8 @@ vi.mock("@/app/(dashboard)/hooks/keys/useKeys", () => ({
vi.mock("@ant-design/icons", () => ({
InfoCircleOutlined: () => null,
+ CopyOutlined: () => null,
+ WarningOutlined: () => null,
}));
vi.mock("react-copy-to-clipboard", () => ({
diff --git a/ui/litellm-dashboard/src/components/organisms/create_key_button.tsx b/ui/litellm-dashboard/src/components/organisms/create_key_button.tsx
index b590a7dc043..97b2f7e8ed9 100644
--- a/ui/litellm-dashboard/src/components/organisms/create_key_button.tsx
+++ b/ui/litellm-dashboard/src/components/organisms/create_key_button.tsx
@@ -1657,17 +1657,12 @@ const CreateKey: React.FC = ({ team, teams, data, addKey, autoOp
)}
{apiKey && (
-
-
- Save your Key
-
- {apiKey != null ? (
-
- ) : (
- Key being created, this might take 30s
- )}
-
-
+
+ {apiKey != null ? (
+
+ ) : (
+ Key being created, this might take 30s
+ )}
)}
diff --git a/ui/litellm-dashboard/src/components/shared/CreatedKeyDisplay.test.tsx b/ui/litellm-dashboard/src/components/shared/CreatedKeyDisplay.test.tsx
index eb6fe5f50cd..ed418221bfd 100644
--- a/ui/litellm-dashboard/src/components/shared/CreatedKeyDisplay.test.tsx
+++ b/ui/litellm-dashboard/src/components/shared/CreatedKeyDisplay.test.tsx
@@ -7,51 +7,74 @@ vi.mock("@/components/molecules/message_manager", () => ({
default: { success: vi.fn(), error: vi.fn(), warning: vi.fn(), info: vi.fn(), loading: vi.fn(), destroy: vi.fn() },
}));
+vi.mock("@/components/networking", () => ({
+ proxyBaseUrl: "https://litellm.example.com",
+}));
+
import MessageManager from "@/components/molecules/message_manager";
describe("CreatedKeyDisplay", () => {
beforeEach(() => {
vi.useFakeTimers({ shouldAdvanceTime: true });
+ vi.mocked(MessageManager.success).mockClear();
});
afterEach(() => {
vi.useRealTimers();
});
- it("should render", () => {
+ it("renders the API key value", () => {
render( );
- expect(screen.getByText("sk-test-123")).toBeInTheDocument();
+ expect(screen.getByTestId("created-key-value")).toHaveTextContent("sk-test-123");
});
- it("should display the security warning", () => {
+ it("displays the security warning and modal heading", () => {
render( );
- expect(screen.getByText(/you will not be able to view it again/i)).toBeInTheDocument();
+ expect(screen.getByRole("heading", { name: /api key created/i })).toBeInTheDocument();
+ expect(
+ screen.getByText(/make sure to copy your api key now/i),
+ ).toBeInTheDocument();
+ expect(screen.getByText(/you won't be able to see it again/i)).toBeInTheDocument();
});
- it("should show the copy button with initial label", () => {
+ it("builds a coding agent prompt that embeds the key, base URL, and docs links", () => {
render( );
- expect(screen.getByRole("button", { name: /copy virtual key/i })).toBeInTheDocument();
+ const prompt = screen.getByTestId("coding-agent-prompt").textContent ?? "";
+
+ expect(prompt).toMatch(/Base URL: https:\/\/litellm\.example\.com/);
+ expect(prompt).toMatch(/API key: sk-test-123/);
+ expect(prompt).toContain("Authorization: Bearer sk-test-123");
+ expect(prompt).toContain("https://litellm.example.com/v1/models");
+ expect(prompt).toContain("https://docs.litellm.ai/llms.txt");
+ expect(prompt).toContain("https://docs.litellm.ai/llms-full.txt");
});
- it("should change button text to Copied after clicking copy", async () => {
- const user = userEvent.setup({ advanceTimers: vi.advanceTimersByTime });
+ it("does not produce a trailing slash in the base URL when proxyBaseUrl is provided", () => {
render( );
-
- await user.click(screen.getByRole("button", { name: /copy virtual key/i }));
-
- expect(screen.getByRole("button", { name: /copied/i })).toBeInTheDocument();
+ const prompt = screen.getByTestId("coding-agent-prompt").textContent ?? "";
+ expect(prompt).not.toMatch(/Base URL: https:\/\/litellm\.example\.com\//);
});
- it("should show a success message when the key is copied", async () => {
+ it("copies the API key to clipboard when the primary button is clicked", async () => {
const user = userEvent.setup({ advanceTimers: vi.advanceTimersByTime });
render( );
await user.click(screen.getByRole("button", { name: /copy virtual key/i }));
expect(MessageManager.success).toHaveBeenCalledWith("Key copied to clipboard");
+ expect(screen.getByRole("button", { name: /copied/i })).toBeInTheDocument();
});
- it("should revert button text back after 2 seconds", async () => {
+ it("copies the coding agent prompt to clipboard when its copy button is clicked", async () => {
+ const user = userEvent.setup({ advanceTimers: vi.advanceTimersByTime });
+ render( );
+
+ await user.click(screen.getByRole("button", { name: /copy prompt for coding agents/i }));
+
+ expect(MessageManager.success).toHaveBeenCalledWith("Prompt copied to clipboard");
+ });
+
+ it("reverts the primary button label after 2 seconds", async () => {
const user = userEvent.setup({ advanceTimers: vi.advanceTimersByTime });
render( );
diff --git a/ui/litellm-dashboard/src/components/shared/CreatedKeyDisplay.tsx b/ui/litellm-dashboard/src/components/shared/CreatedKeyDisplay.tsx
index 00ffb8c6e20..6defe40d959 100644
--- a/ui/litellm-dashboard/src/components/shared/CreatedKeyDisplay.tsx
+++ b/ui/litellm-dashboard/src/components/shared/CreatedKeyDisplay.tsx
@@ -1,50 +1,150 @@
-import React, { useState } from "react";
+import React, { useMemo, useState } from "react";
import { CopyToClipboard } from "react-copy-to-clipboard";
-import { Button } from "antd";
+import { Button, Tooltip } from "antd";
+import { CopyOutlined, WarningOutlined } from "@ant-design/icons";
import MessageManager from "@/components/molecules/message_manager";
+import { proxyBaseUrl } from "@/components/networking";
interface CreatedKeyDisplayProps {
apiKey: string;
}
-/**
- * Shared component for displaying a newly-created virtual key.
- * Used on the Virtual Keys page and in the Add Agent wizard.
- */
-const CreatedKeyDisplay: React.FC = ({ apiKey }) => {
- const [copied, setCopied] = useState(false);
+const buildCodingAgentPrompt = (apiKey: string, baseUrl: string): string => {
+ return `You have access to LiteLLM, an OpenAI-compatible AI gateway that lets you call 100+ LLMs (OpenAI, Anthropic, Gemini, Bedrock, etc.) through a single API.
- const handleCopy = () => {
- setCopied(true);
+Use these credentials:
+- Base URL: ${baseUrl}
+- API key: ${apiKey}
+
+LiteLLM is a drop-in replacement for the OpenAI SDK. Point the SDK's \`base_url\` at the URL above and use the key as your \`OPENAI_API_KEY\`. To list the models available to this key, GET ${baseUrl}/v1/models with header \`Authorization: Bearer ${apiKey}\`.
+
+LiteLLM docs:
+- llms.txt (overview + all doc links): https://docs.litellm.ai/llms.txt
+- llms-full.txt (complete reference with inline code examples): https://docs.litellm.ai/llms-full.txt`;
+};
+
+const resolveProxyBaseUrl = (): string => {
+ if (proxyBaseUrl) return proxyBaseUrl.replace(/\/$/, "");
+ if (typeof window !== "undefined" && window.location?.origin) {
+ return window.location.origin;
+ }
+ return "https://your-litellm-proxy";
+};
+
+const CreatedKeyDisplay: React.FC = ({ apiKey }) => {
+ const [copiedKey, setCopiedKey] = useState(false);
+ const [copiedPrompt, setCopiedPrompt] = useState(false);
+
+ const codingAgentPrompt = useMemo(
+ () => buildCodingAgentPrompt(apiKey, resolveProxyBaseUrl()),
+ [apiKey],
+ );
+
+ const handleCopyKey = () => {
+ setCopiedKey(true);
MessageManager.success("Key copied to clipboard");
- setTimeout(() => setCopied(false), 2000);
+ setTimeout(() => setCopiedKey(false), 2000);
+ };
+
+ const handleCopyPrompt = () => {
+ setCopiedPrompt(true);
+ MessageManager.success("Prompt copied to clipboard");
+ setTimeout(() => setCopiedPrompt(false), 2000);
};
return (
-
-
- Please save this secret key somewhere safe and accessible. For security reasons,{" "}
- you will not be able to view it again through your LiteLLM account. If you
- lose this secret key, you will need to generate a new one.
-
-
- Virtual Key:
-
-
- {apiKey}
-
+
+
+ API Key Created
+
+ Paste this prompt into any coding agent to start using LiteLLM.
+
-
-