From 46196339d27113ea029e564fd5efe13ef029d1c9 Mon Sep 17 00:00:00 2001 From: ishaan-berri <155045088+ishaan-berri@users.noreply.github.com> Date: Thu, 1 Oct 2026 15:45:31 -0700 Subject: [PATCH] feat(ui): add test trace, tracing key and otel endpoints to tracing setup (#44090) * feat(ui): add a call that posts an OTLP export to the proxy * feat(ui): build a sample agent run as an OTLP export * feat(ui): add a pulsing dot for active tracing * feat(ui): render a crisp sample run preview from real trace components * chore(ui): remove the pixelated agent traces preview image * feat(ui): add test trace, tracing key, otel endpoints and more frameworks to tracing setup * test(ui): cover tracing setup test trace, key masking, endpoints and frameworks * feat(ui): open the received test trace and mark tracing as active * test(ui): mock the new tracing setup network calls * fix(ui): let tracing setup use the full page width * fix(ui): send OTLP/JSON sample trace ids as hex per the spec * test(ui): cover the sample trace export shape and hex ids --- .../public/assets/agent-traces-preview.png | Bin 113074 -> 0 bytes .../src/app/(dashboard)/lens/page.tsx | 7 +- .../src/components/networking.tsx | 3 + .../view_logs/TraceView/ActiveDot.tsx | 8 + .../view_logs/TraceView/AgentTracesPage.tsx | 14 +- .../TraceView/AgentTracesSection.test.tsx | 3 +- .../TraceView/AgentTracesSection.tsx | 30 +- .../view_logs/TraceView/TracePreview.tsx | 70 ++ .../TraceView/TracingSetupCard.test.tsx | 196 +++++- .../view_logs/TraceView/TracingSetupCard.tsx | 662 ++++++++++++++---- .../view_logs/TraceView/previewTrace.json | 98 +++ .../view_logs/TraceView/sampleTrace.test.ts | 48 ++ .../view_logs/TraceView/sampleTrace.ts | 95 +++ 13 files changed, 1061 insertions(+), 173 deletions(-) delete mode 100644 ui/litellm-dashboard/public/assets/agent-traces-preview.png create mode 100644 ui/litellm-dashboard/src/components/view_logs/TraceView/ActiveDot.tsx create mode 100644 ui/litellm-dashboard/src/components/view_logs/TraceView/TracePreview.tsx create mode 100644 ui/litellm-dashboard/src/components/view_logs/TraceView/previewTrace.json create mode 100644 ui/litellm-dashboard/src/components/view_logs/TraceView/sampleTrace.test.ts create mode 100644 ui/litellm-dashboard/src/components/view_logs/TraceView/sampleTrace.ts diff --git a/ui/litellm-dashboard/public/assets/agent-traces-preview.png b/ui/litellm-dashboard/public/assets/agent-traces-preview.png deleted file mode 100644 index 34569e263315acaf94bd75a51ff44e3acd3d52cd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 113074 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 B#*Y91 diff --git a/ui/litellm-dashboard/src/app/(dashboard)/lens/page.tsx b/ui/litellm-dashboard/src/app/(dashboard)/lens/page.tsx index 282262b6322..26726691a48 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/lens/page.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/lens/page.tsx @@ -37,7 +37,12 @@ export default function LensPage() { - + {isProxyAdminTierRole(userRole ?? "") ? ( diff --git a/ui/litellm-dashboard/src/components/networking.tsx b/ui/litellm-dashboard/src/components/networking.tsx index 97d1782b08c..da902d4a9e2 100644 --- a/ui/litellm-dashboard/src/components/networking.tsx +++ b/ui/litellm-dashboard/src/components/networking.tsx @@ -2122,6 +2122,9 @@ export const agentTraceListCall = async ({ return apiClient.get(`/v1/traces`, { accessToken, query }); }; +export const sendOtlpTraceCall = async (accessToken: string, exportRequest: object): Promise => + apiClient.post(`/v1/traces`, { accessToken, body: exportRequest }); + export const agentTraceCall = async (accessToken: string, traceId: string, traceRef?: string): Promise => apiClient.get(`/v1/traces/${encodeURIComponent(traceId)}`, { accessToken, diff --git a/ui/litellm-dashboard/src/components/view_logs/TraceView/ActiveDot.tsx b/ui/litellm-dashboard/src/components/view_logs/TraceView/ActiveDot.tsx new file mode 100644 index 00000000000..b3d4cf55482 --- /dev/null +++ b/ui/litellm-dashboard/src/components/view_logs/TraceView/ActiveDot.tsx @@ -0,0 +1,8 @@ +export function ActiveDot() { + return ( +

@@ -156,7 +174,7 @@ export function AgentTracesSection({ > ← Back to traces - +
); } @@ -183,7 +201,7 @@ export function AgentTracesSection({ onStatusChange={setStatus} > {timeControls && ( diff --git a/ui/litellm-dashboard/src/components/view_logs/TraceView/TracePreview.tsx b/ui/litellm-dashboard/src/components/view_logs/TraceView/TracePreview.tsx new file mode 100644 index 00000000000..c4fd04602ac --- /dev/null +++ b/ui/litellm-dashboard/src/components/view_logs/TraceView/TracePreview.tsx @@ -0,0 +1,70 @@ +"use client"; + +import { cn } from "@/lib/cva.config"; + +import previewTrace from "./previewTrace.json"; +import { SpanIcon } from "./SpanIcon"; +import type { SpanType } from "./traceTypes"; +import { fmtMs } from "./traceUtils"; + +interface PreviewRow { + id: string; + name: string; + type: SpanType; + model: string | null; + depth: number; + start_offset_ms: number; + duration_ms: number; + error: boolean; +} + +const preview = previewTrace as { + name: string; + input_preview: string; + span_count: number; + duration_ms: number; + rows: PreviewRow[]; +}; + +export function TracePreview() { + const total = preview.duration_ms; + return ( +
+
+ + {preview.name} + {preview.input_preview} + + {preview.span_count} steps · {fmtMs(total)} + +
+
+ {preview.rows.map((row) => ( +
+
+ + {row.name} +
+
+
+
+ + {fmtMs(row.duration_ms)} + +
+ ))} +
+
+
+ ); +} diff --git a/ui/litellm-dashboard/src/components/view_logs/TraceView/TracingSetupCard.test.tsx b/ui/litellm-dashboard/src/components/view_logs/TraceView/TracingSetupCard.test.tsx index 4f42180d409..027870ab1ae 100644 --- a/ui/litellm-dashboard/src/components/view_logs/TraceView/TracingSetupCard.test.tsx +++ b/ui/litellm-dashboard/src/components/view_logs/TraceView/TracingSetupCard.test.tsx @@ -1,39 +1,98 @@ -import { render, screen } from "@testing-library/react"; +import { screen } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; -import { describe, expect, it, vi } from "vitest"; -import { chooseSelectOption } from "@/../tests/test-utils"; +import { beforeEach, describe, expect, it, vi } from "vitest"; +import { chooseSelectOption, renderWithProviders } from "@/../tests/test-utils"; import { copyToClipboard } from "@/utils/dataUtils"; -import { codingAgentPrompt, tracingEnvSnippet, TracingSetupCard } from "./TracingSetupCard"; +import { agentTraceCall, apiClient, sendOtlpTraceCall } from "../../networking"; +import { + codingAgentCommand, + codingAgentPrompt, + maskSecret, + TRACING_KEY_REQUEST, + tracingEnvSnippet, + TracingSetupCard, +} from "./TracingSetupCard"; +import type { Trace } from "./traceTypes"; -vi.mock("../../networking", () => ({ getProxyBaseUrl: () => "http://proxy.test/" })); +vi.mock("../../networking", () => ({ + getProxyBaseUrl: () => "http://proxy.test/", + sendOtlpTraceCall: vi.fn(), + agentTraceCall: vi.fn(), + apiClient: { post: vi.fn() }, +})); vi.mock("@/utils/dataUtils", () => ({ copyToClipboard: vi.fn().mockResolvedValue(true) })); +const SECRET = "sk-abcdefghijklmnopWXYZ"; + +const renderCard = ( + props: { + detail?: string | null; + connected?: boolean; + onCheck?: () => void; + readOnly?: boolean; + canMintTracingKey?: boolean; + } = {}, +) => { + const onOpenTrace = vi.fn(); + renderWithProviders( + , + ); + return { onOpenTrace, card: screen.getByTestId("tracing-setup-card") }; +}; + +beforeEach(() => vi.clearAllMocks()); + describe("TracingSetupCard", () => { - it("shows agent connection guidance once tracing is enabled without presenting example data as live", async () => { + it("shows agent connection guidance and labels the example run as sample data", async () => { const user = userEvent.setup(); - render(); + const { card } = renderCard(); expect(screen.getByRole("heading", { name: "Connect your agent" })).toBeVisible(); expect(screen.getByText("Tracing enabled")).toBeVisible(); expect(screen.getByText("Waiting for your first trace")).toBeVisible(); - expect(screen.getByRole("img", { hidden: true, name: /Example agent trace/ })).not.toBeVisible(); - expect(screen.getByTestId("tracing-setup-card")).not.toHaveTextContent("store: clickhouse"); + expect(screen.getByTestId("trace-preview")).toBeVisible(); + expect(card).toHaveTextContent("sample data, not your runs"); + expect(card).not.toHaveTextContent("store: clickhouse"); await user.click(screen.getByText("Set up manually")); - expect(screen.getByText(/OTEL_EXPORTER_OTLP_ENDPOINT=http:\/\/proxy.test/)).toBeVisible(); - expect(screen.getByTestId("tracing-setup-card")).not.toHaveTextContent(/langsmith/i); + expect(screen.getByText(/^export OTEL_EXPORTER_OTLP_ENDPOINT=http:\/\/proxy.test/)).toBeVisible(); + expect(card).not.toHaveTextContent(/langsmith/i); }); - it("copies instructions for the selected framework and preserves both manual installers", async () => { + it("lists the OTEL endpoints before the framework picker, each copyable", async () => { const user = userEvent.setup(); - render(); + const { card } = renderCard(); + const text = card.textContent ?? ""; + expect(text.indexOf("OpenTelemetry (OTEL) endpoints")).toBeLessThan(text.indexOf("Your agent framework")); + await user.click(screen.getByRole("button", { name: "Copy http://proxy.test/v1/traces" })); + expect(copyToClipboard).toHaveBeenLastCalledWith("http://proxy.test/v1/traces"); + }); + + it("hides the sample preview once traces are arriving", () => { + renderCard({ connected: true }); + expect(screen.getByRole("heading", { name: "Connect another agent" })).toBeVisible(); + expect(screen.queryByTestId("trace-preview")).not.toBeInTheDocument(); + }); + + it("builds the coding agent command for the selected framework and keeps both manual installers", async () => { + const user = userEvent.setup(); + renderCard(); await chooseSelectOption(user, screen.getByRole("combobox", { name: "Your agent framework" }), "CrewAI"); - await user.click(screen.getByRole("button", { name: "Copy setup prompt" })); - expect(copyToClipboard).toHaveBeenLastCalledWith( - codingAgentPrompt("http://proxy.test", { - label: "CrewAI", - packages: "crewai openinference-instrumentation-crewai", - }), - ); - expect(screen.getByRole("button", { name: "Prompt copied" })).toBeVisible(); + const prompt = codingAgentPrompt("http://proxy.test", { + label: "CrewAI", + packages: "crewai openinference-instrumentation-crewai", + }); + const commandText = () => screen.getByText(/^claude |^codex /).textContent; + expect(commandText()).toBe(codingAgentCommand("Claude Code", prompt)); + await user.click(screen.getByRole("tab", { name: "Codex" })); + expect(commandText()).toBe(codingAgentCommand("Codex", prompt)); + await user.click(screen.getByText("Set up manually")); expect(screen.getByText(/pip install -U opentelemetry-distro/)).toHaveTextContent( "crewai openinference-instrumentation-crewai", @@ -44,13 +103,88 @@ describe("TracingSetupCard", () => { ); }); + it("uses npm and a CommonJS-safe TypeScript entrypoint for the Vercel AI SDK", async () => { + const user = userEvent.setup(); + const { card } = renderCard(); + await chooseSelectOption(user, screen.getByRole("combobox", { name: "Your agent framework" }), "Vercel AI SDK"); + await user.click(screen.getByText("Set up manually")); + expect(card).toHaveTextContent("npm install ai @ai-sdk/openai-compatible @vercel/otel"); + expect(card).toHaveTextContent("my_agent.ts"); + expect(card).not.toHaveTextContent("opentelemetry-instrument python"); + const quickstart = screen.getByText(/registerOTel\(\{/).textContent ?? ""; + expect(quickstart).toContain("async function main()"); + expect(quickstart.split("\n").filter((line) => /^(const|let) .*= await /.test(line))).toEqual([]); + }); + + it("hides the actions a read-only viewer cannot perform", () => { + const { card } = renderCard({ readOnly: true }); + expect(screen.queryByRole("button", { name: "Send a test trace" })).not.toBeInTheDocument(); + expect(screen.queryByRole("button", { name: "Generate tracing key" })).not.toBeInTheDocument(); + expect(card).toHaveTextContent("ask a proxy admin for one"); + expect(card).toHaveTextContent("OpenTelemetry (OTEL) endpoints"); + }); + + it("offers a scoped tracing key only to callers allowed to set key routes", () => { + const { card } = renderCard({ canMintTracingKey: false }); + expect(screen.queryByRole("button", { name: "Generate tracing key" })).not.toBeInTheDocument(); + expect(screen.getByRole("button", { name: "Send a test trace" })).toBeVisible(); + expect(card).toHaveTextContent("Use any LiteLLM virtual key you already have"); + }); + + it("generates a tracing key that stays masked on screen but copies in full", async () => { + const user = userEvent.setup(); + vi.mocked(apiClient.post).mockResolvedValue({ key: SECRET }); + const { card } = renderCard(); + + await user.click(screen.getByRole("button", { name: "Generate tracing key" })); + + expect(await screen.findByText("Your tracing key")).toBeVisible(); + expect(apiClient.post).toHaveBeenCalledWith("/key/generate", { + accessToken: "sk-admin", + body: TRACING_KEY_REQUEST, + }); + expect(TRACING_KEY_REQUEST.allowed_routes).toEqual(["/v1/traces"]); + expect(card).not.toHaveTextContent(SECRET); + expect(card).toHaveTextContent(maskSecret(SECRET)); + await user.click(screen.getAllByRole("button", { name: "Copy" })[0]); + expect(copyToClipboard).toHaveBeenLastCalledWith(SECRET); + }); + + it("sends a test trace, waits for it to land, then opens it", async () => { + const user = userEvent.setup(); + const summary = { trace_id: "abc", name: "weather_agent" } as Trace["summary"]; + vi.mocked(sendOtlpTraceCall).mockResolvedValue(undefined); + vi.mocked(agentTraceCall).mockResolvedValue({ summary, agents: [], spans: [] } as unknown as Trace); + const { onOpenTrace } = renderCard(); + + await user.click(screen.getByRole("button", { name: "Send a test trace" })); + await user.click(await screen.findByRole("button", { name: /View trace/ })); + + expect(sendOtlpTraceCall).toHaveBeenCalledOnce(); + expect(vi.mocked(agentTraceCall).mock.calls[0][1]).toMatch(/^[0-9a-f]{32}$/); + expect(onOpenTrace).toHaveBeenCalledWith(summary); + }); + + it("reports a failed send instead of claiming success", async () => { + const user = userEvent.setup(); + vi.mocked(sendOtlpTraceCall).mockRejectedValue(new Error("boom")); + renderCard(); + + await user.click(screen.getByRole("button", { name: "Send a test trace" })); + + expect(await screen.findByText("Could not send the test trace.")).toBeVisible(); + expect(agentTraceCall).not.toHaveBeenCalled(); + expect(screen.queryByRole("button", { name: /View trace/ })).not.toBeInTheDocument(); + }); + it("guides proxy setup before agent setup and allows checking readiness", async () => { const user = userEvent.setup(); const onCheck = vi.fn(); - render(); + const { card } = renderCard({ detail: "Agent tracing is not enabled", onCheck }); expect(screen.getByRole("heading", { name: "Enable tracing" })).toBeVisible(); - expect(screen.getByTestId("tracing-setup-card")).toHaveTextContent("store: clickhouse"); + expect(card).toHaveTextContent("store: clickhouse"); expect(screen.queryByRole("combobox", { name: "Your agent framework" })).not.toBeInTheDocument(); + expect(screen.queryByRole("button", { name: "Send a test trace" })).not.toBeInTheDocument(); await user.click(screen.getByRole("button", { name: "Check setup" })); expect(onCheck).toHaveBeenCalledOnce(); expect(screen.getByText(/Tracing is still unavailable/)).toBeVisible(); @@ -63,7 +197,13 @@ describe("setup snippets", () => { expect(env).toContain("OTEL_EXPORTER_OTLP_ENDPOINT=http://proxy.test\n"); expect(env).toContain("OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf"); expect(env).not.toContain("/v1/traces"); + expect(env).not.toContain("export LITELLM_API_KEY="); expect(env).toContain("Bearer $LITELLM_API_KEY"); + const withKey = tracingEnvSnippet("http://proxy.test", SECRET); + expect(withKey).toContain(`export LITELLM_TRACING_KEY=${SECRET}\n`); + expect(withKey).toContain('OTEL_EXPORTER_OTLP_HEADERS="Authorization=Bearer $LITELLM_TRACING_KEY"'); + expect(withKey).not.toContain("LITELLM_API_KEY"); + const prompt = codingAgentPrompt("http://proxy.test", { label: "LangChain", packages: "langchain" }); expect(prompt).toContain("base_url=http://proxy.test/v1"); expect(prompt).toContain("opentelemetry-distro opentelemetry-exporter-otlp-proto-http langchain"); @@ -71,4 +211,14 @@ describe("setup snippets", () => { expect(prompt).toContain("Lens > Traces"); expect(prompt).not.toMatch(/langsmith/i); }); + + it("builds a shell-safe command for each coding agent", () => { + expect(codingAgentCommand("Claude Code", "it's")).toBe("claude 'it'\\''s'"); + expect(codingAgentCommand("Codex", "go")).toBe("codex 'go'"); + }); + + it("masks secrets but keeps a recognisable prefix and suffix", () => { + expect(maskSecret(SECRET)).toBe(`sk-ab${"•".repeat(16)}WXYZ`); + expect(maskSecret("short")).toBe("•••••"); + }); }); diff --git a/ui/litellm-dashboard/src/components/view_logs/TraceView/TracingSetupCard.tsx b/ui/litellm-dashboard/src/components/view_logs/TraceView/TracingSetupCard.tsx index 183dd24cb5e..5ac5c988efe 100644 --- a/ui/litellm-dashboard/src/components/view_logs/TraceView/TracingSetupCard.tsx +++ b/ui/litellm-dashboard/src/components/view_logs/TraceView/TracingSetupCard.tsx @@ -1,6 +1,6 @@ "use client"; -import { ArrowUpRight, Check, Copy, Loader2 } from "lucide-react"; +import { ArrowRight, ArrowUpRight, Check, Copy, KeyRound, Loader2, Send } from "lucide-react"; import { useState } from "react"; import { cn } from "@/lib/cva.config"; @@ -8,22 +8,42 @@ import { Button } from "@/components/ui/button"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { copyToClipboard } from "@/utils/dataUtils"; -import previewImg from "../../../../public/assets/agent-traces-preview.png"; +import anthropicLogo from "../../../../public/assets/logos/anthropic.svg"; import crewaiLogo from "../../../../public/assets/logos/crewai-color.svg"; import langchainLogo from "../../../../public/assets/logos/langchain.svg"; import langgraphLogo from "../../../../public/assets/logos/langgraph-color.svg"; import llamaindexLogo from "../../../../public/assets/logos/llamaindex-color.svg"; import openaiAgentsLogo from "../../../../public/assets/logos/openai-agents.svg"; +import openaiLogo from "../../../../public/assets/logos/openai_small.svg"; import otelLogo from "../../../../public/assets/logos/opentelemetry.svg"; import pydanticAiLogo from "../../../../public/assets/logos/pydantic-ai-color.svg"; -import { getProxyBaseUrl } from "../../networking"; +import vercelLogo from "../../../../public/assets/logos/vercel.svg"; +import { agentTraceCall, apiClient, getProxyBaseUrl, sendOtlpTraceCall } from "../../networking"; +import { ActiveDot } from "./ActiveDot"; +import { sampleTraceExport } from "./sampleTrace"; +import { TracePreview } from "./TracePreview"; +import type { TraceSummary } from "./traceTypes"; const COPIED_RESET_MS = 1500; const DOCS_URL = "https://docs.litellm.ai/docs/proxy/lens"; const OTEL_BASE_PACKAGES = "opentelemetry-distro opentelemetry-exporter-otlp-proto-http"; -const RUN_SNIPPET = "opentelemetry-instrument python my_agent.py"; +const PY_RUN_SNIPPET = "opentelemetry-instrument python my_agent.py"; +const TS_RUN_SNIPPET = "npx tsx my_agent.ts"; +const SAMPLE_TRACE_POLL_MS = 1000; +export const TRACING_KEY_REQUEST = { + key_alias: "Agent tracing", + allowed_routes: ["/v1/traces"], + metadata: { purpose: "agent_tracing" }, +} as const; +const SAMPLE_TRACE_POLL_ATTEMPTS = 15; type Installer = "pip" | "uv"; +type CodingAgent = "Claude Code" | "Codex"; + +const PY_INSTALL: Record string> = { + pip: (packages) => `pip install -U ${packages}`, + uv: (packages) => `uv add ${packages}`, +}; interface FrameworkGuide { id: string; @@ -31,12 +51,50 @@ interface FrameworkGuide { logo: string; packages: string; quickstart: string; + typescript?: boolean; } const FRAMEWORKS: readonly FrameworkGuide[] = [ + { + id: "deep-agents", + label: "Deep Agents", + logo: langgraphLogo.src, + packages: "deepagents langchain-openai openinference-instrumentation-langchain", + quickstart: `from deepagents import create_deep_agent +from langchain_openai import ChatOpenAI + +llm = ChatOpenAI(model="claude-sonnet-4-5", base_url="{PROXY}/v1", api_key=os.environ["LITELLM_API_KEY"]) +agent = create_deep_agent(model=llm, tools=[], system_prompt="You are a careful researcher.") +agent.invoke({"messages": [{"role": "user", "content": "What is LiteLLM?"}]})`, + }, + { + id: "vercel-ai-sdk", + label: "Vercel AI SDK", + logo: vercelLogo.src, + typescript: true, + packages: "ai @ai-sdk/openai-compatible @vercel/otel @opentelemetry/api", + quickstart: `import { createOpenAICompatible } from "@ai-sdk/openai-compatible"; +import { registerOTel } from "@vercel/otel"; +import { generateText } from "ai"; + +registerOTel({ serviceName: process.env.OTEL_SERVICE_NAME ?? "my-agent" }); + +const litellm = createOpenAICompatible({ name: "litellm", baseURL: "{PROXY}/v1", apiKey: process.env.LITELLM_API_KEY }); + +async function main() { + const { text } = await generateText({ + model: litellm("claude-sonnet-4-5"), + prompt: "What is LiteLLM?", + experimental_telemetry: { isEnabled: true, functionId: "my_agent" }, + }); + console.log(text); +} + +main();`, + }, { id: "langgraph", - label: "LangGraph / Deep Agents", + label: "LangGraph", logo: langgraphLogo.src, packages: "langgraph langchain-openai openinference-instrumentation-langchain", quickstart: `from langchain.agents import create_agent @@ -119,19 +177,23 @@ with tracer.start_as_current_span("my_agent", attributes=attrs): }, ]; -const installPackages = (guide: Pick): string => - [OTEL_BASE_PACKAGES, guide.packages].filter(Boolean).join(" "); +const installPackages = (guide: Pick): string => + guide.typescript ? guide.packages : [OTEL_BASE_PACKAGES, guide.packages].filter(Boolean).join(" "); /** The endpoint is the proxy base URL: OTLP exporters append /v1/traces themselves. */ -export const tracingEnvSnippet = (proxyUrl: string): string => +export const tracingEnvSnippet = (proxyUrl: string, tracingKey: string | null = null): string => [ + ...(tracingKey ? [`export LITELLM_TRACING_KEY=${tracingKey}`] : []), `export OTEL_EXPORTER_OTLP_ENDPOINT=${proxyUrl}`, "export OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf", - 'export OTEL_EXPORTER_OTLP_HEADERS="Authorization=Bearer $LITELLM_API_KEY"', + `export OTEL_EXPORTER_OTLP_HEADERS="Authorization=Bearer $${tracingKey ? "LITELLM_TRACING_KEY" : "LITELLM_API_KEY"}"`, "export OTEL_SERVICE_NAME=my-agent", ].join("\n"); -export const codingAgentPrompt = (proxyUrl: string, guide: Pick): string => +export const codingAgentPrompt = ( + proxyUrl: string, + guide: Pick, +): string => [ `Send this ${guide.label} project's OpenTelemetry traces to LiteLLM.`, "", @@ -141,7 +203,9 @@ export const codingAgentPrompt = (proxyUrl: string, guide: Pick", - "3. Start the app through OTEL auto-instrumentation: opentelemetry-instrument .", + guide.typescript + ? "3. Call registerOTel() from @vercel/otel at startup and pass experimental_telemetry: { isEnabled: true } to every AI SDK call." + : "3. Start the app through OTEL auto-instrumentation: opentelemetry-instrument .", `4. Point every LLM client at LiteLLM: base_url=${proxyUrl}/v1, api key from LITELLM_API_KEY.`, "5. Give each agent and subagent a name so runs are easy to read.", "6. Run the agent once and confirm the run shows up in the LiteLLM UI under Lens > Traces.", @@ -149,6 +213,21 @@ export const codingAgentPrompt = (proxyUrl: string, guide: Pick `'${value.replaceAll("'", "'\\''")}'`; + +export const codingAgentCommand = (agent: CodingAgent, prompt: string): string => + `${agent === "Claude Code" ? "claude" : "codex"} ${shellQuote(prompt)}`; + +export const maskSecret = (secret: string): string => + secret.length > 10 ? `${secret.slice(0, 5)}${"•".repeat(16)}${secret.slice(-4)}` : "•".repeat(secret.length); + +export const otlpEndpoints = (proxyUrl: string): readonly (readonly [string, string, boolean])[] => [ + ["Traces endpoint (POST)", `${proxyUrl}/v1/traces`, true], + ["OTEL_EXPORTER_OTLP_ENDPOINT", proxyUrl, true], + ["Auth header", "Authorization: Bearer ", true], + ["Protocol", "OTLP/HTTP, protobuf or JSON (gRPC not supported)", false], +]; + export const PROXY_CONFIG_SNIPPET = [ "general_settings:", " tracing:", @@ -157,7 +236,17 @@ export const PROXY_CONFIG_SNIPPET = [ "# env: CLICKHOUSE_URL (writer) and CLICKHOUSE_READER_URL (read-only user)", ].join("\n"); -function CodeBlock({ code, tabs, wrap = false }: { code: string; tabs?: React.ReactNode; wrap?: boolean }) { +function CodeBlock({ + code, + display = code, + tabs, + wrap = false, +}: { + code: string; + display?: string; + tabs?: React.ReactNode; + wrap?: boolean; +}) { const [copied, setCopied] = useState(false); const copy = async () => { if (await copyToClipboard(code)) { @@ -184,7 +273,7 @@ function CodeBlock({ code, tabs, wrap = false }: { code: string; tabs?: React.Re wrap ? "whitespace-pre-wrap" : "overflow-x-auto", )} > - {code} + {display}
); @@ -198,10 +287,12 @@ function LineTabs({ value, options, onChange, + logos, }: { value: T; options: T[]; onChange: (v: T) => void; + logos?: Partial>; }) { return (
@@ -213,12 +304,13 @@ function LineTabs({ aria-selected={value === option} onClick={() => onChange(option)} className={cn( - "-mb-px h-9 border-b-2 text-[12.5px]", + "-mb-px inline-flex h-9 items-center gap-1.5 border-b-2 text-[12.5px]", value === option ? "border-foreground text-foreground" : "border-transparent text-muted-foreground hover:text-foreground", )} > + {logos?.[option] && } {option} ))} @@ -226,7 +318,7 @@ function LineTabs({ ); } -function Step({ title, children }: { title: string; children: React.ReactNode }) { +function Step({ title, children }: { title: React.ReactNode; children: React.ReactNode }) { return (

{title}

@@ -235,6 +327,86 @@ function Step({ title, children }: { title: string; children: React.ReactNode }) ); } +type SendState = + | { kind: "idle" } + | { kind: "sending" } + | { kind: "waiting" } + | { kind: "ready"; trace: TraceSummary } + | { kind: "failed"; message: string }; + +const SEND_LABEL: Record, string> = { + idle: "Send a test trace", + sending: "Sending…", + waiting: "Waiting for it to arrive…", + failed: "Send a test trace", +}; + +const delay = (ms: number) => new Promise((resolve) => window.setTimeout(resolve, ms)); + +async function waitForTrace(accessToken: string, traceId: string): Promise { + for (let attempt = 0; attempt < SAMPLE_TRACE_POLL_ATTEMPTS; attempt++) { + try { + return (await agentTraceCall(accessToken, traceId)).summary; + } catch { + await delay(SAMPLE_TRACE_POLL_MS); + } + } + return null; +} + +function SendTestTrace({ + accessToken, + onOpenTrace, +}: { + accessToken: string; + onOpenTrace: (trace: TraceSummary) => void; +}) { + const [state, setState] = useState({ kind: "idle" }); + const send = async () => { + setState({ kind: "sending" }); + const sample = sampleTraceExport(Date.now()); + try { + await sendOtlpTraceCall(accessToken, sample.body); + } catch { + setState({ kind: "failed", message: "Could not send the test trace." }); + return; + } + setState({ kind: "waiting" }); + const trace = await waitForTrace(accessToken, sample.traceId); + setState( + trace + ? { kind: "ready", trace } + : { kind: "failed", message: "Sent, but it has not shown up yet. Check again in a moment." }, + ); + }; + if (state.kind === "ready") { + return ( +
+ + + +
+ ); + } + const busy = state.kind === "sending" || state.kind === "waiting"; + return ( +
+ + {state.kind === "failed" &&

{state.message}

} +
+ ); +} + function TraceReceipt({ connected, checked, @@ -273,159 +445,367 @@ function TraceReceipt({ ); } +function TracingKey({ + accessToken, + tracingKey, + onCreated, +}: { + accessToken: string; + tracingKey: string | null; + onCreated: (key: string) => void; +}) { + const [creating, setCreating] = useState(false); + const [error, setError] = useState(""); + const create = async () => { + setCreating(true); + setError(""); + try { + const result = await apiClient.post<{ key?: string }>("/key/generate", { + accessToken, + body: TRACING_KEY_REQUEST, + }); + if (!result.key) throw new Error("The proxy did not return the new key"); + onCreated(result.key); + } catch (cause) { + setError(cause instanceof Error ? cause.message : "Could not create a key"); + } finally { + setCreating(false); + } + }; + if (tracingKey) { + return ( +
+ Your tracing key} /> +

+ Hidden for safety. Copy copies the full key, and the environment step below includes it. This key can only + send traces, so your agent still needs its own key for model calls. Manage it under Virtual Keys as + "Agent tracing". +

+
+ ); + } + return ( +
+ + Or use any existing LiteLLM virtual key. + {error &&

{error}

} +
+ ); +} + +function EndpointValue({ value }: { value: string }) { + const [copied, setCopied] = useState(false); + const copy = async () => { + if (await copyToClipboard(value)) { + setCopied(true); + window.setTimeout(() => setCopied(false), COPIED_RESET_MS); + } + }; + return ( + + ); +} + +function Endpoints({ proxyUrl }: { proxyUrl: string }) { + return ( +
+

+ + OpenTelemetry (OTEL) endpoints +

+

+ Point any OpenTelemetry exporter here. The framework guides below set these for you. +

+
+ {otlpEndpoints(proxyUrl).map(([label, value, copyable]) => ( +
+
{label}
+
+ {copyable ? : {value}} +
+
+ ))} +
+
+ ); +} + +const CODING_AGENT_LOGOS: Record = { + "Claude Code": anthropicLogo.src, + Codex: openaiLogo.src, +}; + function setupTitle(enabled: boolean, connected: boolean) { if (!enabled) return "Enable tracing"; return connected ? "Connect another agent" : "Connect your agent"; } -export function TracingSetupCard({ - detail, - connected = false, +interface ConnectAgentProps { + accessToken: string; + onOpenTrace: (trace: TraceSummary) => void; + connected: boolean; + checked: boolean; + checking: boolean; + onCheck: () => void; + readOnly: boolean; + canMintTracingKey: boolean; +} + +function EnableTracing({ checked, checking, onCheck }: { checked: boolean; checking: boolean; onCheck: () => void }) { + return ( + <> + +

+ Set your ClickHouse writer and read-only reader URLs, add this to config.yaml, then restart the proxy. Ask + your proxy administrator if you don’t manage this deployment. +

+ config.yaml} /> + + ClickHouse and proxy setup +
+ {checked && !checking && ( +

+ Tracing is still unavailable. Check that the configuration was applied to this proxy and it has restarted. +

+ )} + + + ); +} + +function ConnectAgent({ + accessToken, + onOpenTrace, + connected, + checked, + checking, onCheck, - checking = false, -}: { - detail: string | null; - connected?: boolean; - onCheck?: () => void; - checking?: boolean; -}) { + readOnly, + canMintTracingKey, +}: ConnectAgentProps) { const proxyUrl = getProxyBaseUrl().replace(/\/$/, ""); const [framework, setFramework] = useState(FRAMEWORKS[0].id); const [installer, setInstaller] = useState("pip"); - const [copied, setCopied] = useState(false); - const [checked, setChecked] = useState(false); + const [codingAgent, setCodingAgent] = useState("Claude Code"); + const [tracingKey, setTracingKey] = useState(null); const guide = FRAMEWORKS.find((f) => f.id === framework) ?? FRAMEWORKS[0]; const packages = installPackages(guide); - const install = installer === "pip" ? `pip install -U ${packages}` : `uv add ${packages}`; + const install = guide.typescript ? `npm install ${packages}` : PY_INSTALL[installer](packages); + const quickstart = guide.quickstart.replace("{PROXY}", proxyUrl); + return ( + <> + {!readOnly && ( + +

+ Send a small sample run (an agent, an LLM call and a tool call) to confirm tracing works end to end. +

+ +
+ )} + + + +
+ + +
+ + + {canMintTracingKey && !readOnly ? ( + + ) : ( +

+ Use any LiteLLM virtual key you already have, or ask a proxy admin for one. +

+ )} +
+ + + Let Claude Code or + Codex connect it + + } + > +

+ Run this in your agent’s project. It starts your coding agent with the setup task and reads the key from + LITELLM_API_KEY. +

+ + } + /> +
+ +
+ Set up manually + + npm + ) : ( + + ) + } + /> + + + Shell} + /> + + +

+ Replace the example model with a model configured on your proxy. +

+ {guide.typescript ? "my_agent.ts" : "my_agent.py"}} + /> +
+ Shell} /> +
+
+
+ + + + {!connected && ( +
+

+ Example run (sample data, not your runs) +

+ +
+ )} + + ); +} + +export function TracingSetupCard({ + detail, + accessToken, + onOpenTrace, + connected = false, + onCheck, + checking = false, + readOnly = false, + canMintTracingKey = false, +}: { + detail: string | null; + accessToken: string; + onOpenTrace: (trace: TraceSummary) => void; + connected?: boolean; + onCheck?: () => void; + checking?: boolean; + readOnly?: boolean; + canMintTracingKey?: boolean; +}) { + const [checked, setChecked] = useState(false); const enabled = detail === null; - const copyPrompt = async () => { - if (await copyToClipboard(codingAgentPrompt(proxyUrl, guide))) { - setCopied(true); - window.setTimeout(() => setCopied(false), COPIED_RESET_MS); - } - }; const check = () => { setChecked(true); onCheck?.(); }; return ( -
-

{setupTitle(enabled, connected)}

+
+
+

{setupTitle(enabled, connected)}

+ + Docs +

{enabled ? "Send your agent’s runs to LiteLLM to see its inputs, outputs, and tool calls." : "Tracing needs ClickHouse and a small update to your LiteLLM proxy configuration."}

- {enabled &&

- - {!enabled ? ( - <> - -

- Set your ClickHouse writer and read-only reader URLs, add this to config.yaml, then restart the proxy. Ask - your proxy administrator if you don’t manage this deployment. -

- config.yaml} /> - - ClickHouse and proxy setup -
- {checked && !checking && ( -

- Tracing is still unavailable. Check that the configuration was applied to this proxy and it has restarted. -

- )} - - + {enabled ? ( + ) : ( - <> -
- - -
- -

- Paste the setup prompt into Claude Code or Codex in your agent’s project. It uses your existing LiteLLM - key from the environment. -

- -
-
- Set up manually - - } - /> - - - Shell} /> - - -

- Replace the example model with a model configured on your proxy. -

- my_agent.py} - /> -
- Shell} /> -
-
-
- -
- See an example trace -

Example only. These are not your agent’s runs.

- Example agent trace with tool calls, inputs, and outputs -
- + )}
); diff --git a/ui/litellm-dashboard/src/components/view_logs/TraceView/previewTrace.json b/ui/litellm-dashboard/src/components/view_logs/TraceView/previewTrace.json new file mode 100644 index 00000000000..7da2a5b454d --- /dev/null +++ b/ui/litellm-dashboard/src/components/view_logs/TraceView/previewTrace.json @@ -0,0 +1,98 @@ +{ + "name": "deep_research_agent", + "input_preview": "Should we store OTEL agent spans in ClickHouse or Postgres at 50k spans/sec?", + "span_count": 126, + "duration_ms": 51385, + "rows": [ + { + "id": "5e79f3b5b504985e", + "name": "deep_research_agent", + "type": "agent", + "model": null, + "depth": 0, + "start_offset_ms": 0, + "duration_ms": 51385.4, + "error": false + }, + { + "id": "83451f3235847f6c", + "name": "model", + "type": "chain", + "model": null, + "depth": 1, + "start_offset_ms": 1, + "duration_ms": 9518.2, + "error": false + }, + { + "id": "8a6a1c31940d07af", + "name": "ChatOpenAI", + "type": "llm", + "model": "claude-sonnet-4-5", + "depth": 2, + "start_offset_ms": 6, + "duration_ms": 9510.8, + "error": false + }, + { + "id": "f6fdd164d528fee5", + "name": "tools", + "type": "chain", + "model": null, + "depth": 1, + "start_offset_ms": 9520, + "duration_ms": 2.7, + "error": false + }, + { + "id": "1526d46d48d29a09", + "name": "write_file", + "type": "tool", + "model": null, + "depth": 2, + "start_offset_ms": 9521, + "duration_ms": 0.6, + "error": false + }, + { + "id": "2697122e295d91b6", + "name": "tools", + "type": "chain", + "model": null, + "depth": 1, + "start_offset_ms": 9523, + "duration_ms": 35177.7, + "error": false + }, + { + "id": "b2fb3a8f5a2fce01", + "name": "task", + "type": "tool", + "model": null, + "depth": 2, + "start_offset_ms": 9524, + "duration_ms": 35176.1, + "error": false + }, + { + "id": "81499b492fd93f85", + "name": "researcher", + "type": "agent", + "model": null, + "depth": 3, + "start_offset_ms": 9524, + "duration_ms": 35175.3, + "error": false + }, + { + "id": "64a2c760897f3310", + "name": "model", + "type": "chain", + "model": null, + "depth": 4, + "start_offset_ms": 9526, + "duration_ms": 6071.3, + "error": false + } + ] +} diff --git a/ui/litellm-dashboard/src/components/view_logs/TraceView/sampleTrace.test.ts b/ui/litellm-dashboard/src/components/view_logs/TraceView/sampleTrace.test.ts new file mode 100644 index 00000000000..56ac75a5ea7 --- /dev/null +++ b/ui/litellm-dashboard/src/components/view_logs/TraceView/sampleTrace.test.ts @@ -0,0 +1,48 @@ +import { describe, expect, it } from "vitest"; + +import { SAMPLE_TRACE_SERVICE, sampleTraceExport } from "./sampleTrace"; + +interface ExportedSpan { + traceId: string; + spanId: string; + parentSpanId?: string; + startTimeUnixNano: string; + endTimeUnixNano: string; + attributes: { key: string; value: { stringValue: string } }[]; +} + +const spansOf = (body: object): ExportedSpan[] => + (body as { resourceSpans: { scopeSpans: { spans: ExportedSpan[] }[] }[] }).resourceSpans[0].scopeSpans[0].spans; + +const operation = (span: ExportedSpan) => + span.attributes.find((a) => a.key === "gen_ai.operation.name")?.value.stringValue; + +describe("sampleTraceExport", () => { + it("uses OTLP/JSON hex ids and returns the trace id the spans carry", () => { + const { traceId, body } = sampleTraceExport(Date.now()); + const spans = spansOf(body); + expect(traceId).toMatch(/^[0-9a-f]{32}$/); + for (const span of spans) { + expect(span.traceId).toBe(traceId); + expect(span.spanId).toMatch(/^[0-9a-f]{16}$/); + } + }); + + it("nests an LLM call and a tool call under one root agent", () => { + const spans = spansOf(sampleTraceExport(Date.now()).body); + const roots = spans.filter((s) => !s.parentSpanId); + expect(roots).toHaveLength(1); + expect(operation(roots[0])).toBe("invoke_agent"); + const children = spans.filter((s) => s.parentSpanId === roots[0].spanId); + expect(children.map(operation).sort()).toEqual(["chat", "execute_tool"]); + }); + + it("ends at the given time and tags the sample service", () => { + const now = 1_790_000_000_000; + const { body } = sampleTraceExport(now); + const root = spansOf(body).find((s) => !s.parentSpanId)!; + expect(BigInt(root.endTimeUnixNano)).toBe(BigInt(now) * BigInt(1_000_000)); + expect(BigInt(root.startTimeUnixNano)).toBeLessThan(BigInt(root.endTimeUnixNano)); + expect(JSON.stringify(body)).toContain(SAMPLE_TRACE_SERVICE); + }); +}); diff --git a/ui/litellm-dashboard/src/components/view_logs/TraceView/sampleTrace.ts b/ui/litellm-dashboard/src/components/view_logs/TraceView/sampleTrace.ts new file mode 100644 index 00000000000..6dc6f3262b7 --- /dev/null +++ b/ui/litellm-dashboard/src/components/view_logs/TraceView/sampleTrace.ts @@ -0,0 +1,95 @@ +const NS_PER_MS = BigInt(1_000_000); + +export const SAMPLE_TRACE_SERVICE = "litellm-sample-agent"; + +const randomHexId = (bytes: number): string => + Array.from(crypto.getRandomValues(new Uint8Array(bytes)), (b) => b.toString(16).padStart(2, "0")).join(""); + +const str = (key: string, value: string) => ({ key, value: { stringValue: value } }); + +const messages = (role: string, content: string): string => JSON.stringify([{ role, content }]); + +interface SampleSpan { + name: string; + parent: string | null; + startMs: number; + endMs: number; + attributes: ReturnType[]; +} + +export function sampleTraceExport(nowMs: number): { traceId: string; body: object } { + const traceId = randomHexId(16); + const agentId = randomHexId(8); + const question = "What is the weather in San Francisco?"; + const answer = "It is 18°C and sunny in San Francisco."; + const spans: readonly (SampleSpan & { id: string })[] = [ + { + id: agentId, + name: "weather_agent", + parent: null, + startMs: 0, + endMs: 2400, + attributes: [ + str("gen_ai.operation.name", "invoke_agent"), + str("gen_ai.agent.name", "weather_agent"), + str("gen_ai.input.messages", messages("user", question)), + str("gen_ai.output.messages", messages("assistant", answer)), + ], + }, + { + id: randomHexId(8), + name: "chat sample-model", + parent: agentId, + startMs: 100, + endMs: 1500, + attributes: [ + str("gen_ai.operation.name", "chat"), + str("gen_ai.agent.name", "weather_agent"), + str("gen_ai.request.model", "sample-model"), + str("gen_ai.input.messages", messages("user", question)), + str("gen_ai.output.messages", messages("assistant", "Calling get_weather(city=San Francisco)")), + ], + }, + { + id: randomHexId(8), + name: "get_weather", + parent: agentId, + startMs: 1600, + endMs: 2300, + attributes: [ + str("gen_ai.operation.name", "execute_tool"), + str("gen_ai.agent.name", "weather_agent"), + str("gen_ai.tool.call.arguments", JSON.stringify({ city: "San Francisco" })), + str("gen_ai.tool.call.result", JSON.stringify({ temp_c: 18, sky: "sunny" })), + ], + }, + ]; + const startNs = BigInt(nowMs - 2400) * NS_PER_MS; + const toNs = (ms: number): string => String(startNs + BigInt(ms) * NS_PER_MS); + return { + traceId, + body: { + resourceSpans: [ + { + resource: { attributes: [str("service.name", SAMPLE_TRACE_SERVICE)] }, + scopeSpans: [ + { + scope: { name: "litellm-ui-sample" }, + spans: spans.map((s) => ({ + traceId, + spanId: s.id, + ...(s.parent ? { parentSpanId: s.parent } : {}), + name: s.name, + kind: 1, + startTimeUnixNano: toNs(s.startMs), + endTimeUnixNano: toNs(s.endMs), + attributes: s.attributes, + status: { code: 1 }, + })), + }, + ], + }, + ], + }, + }; +}