From e2de21fb5bc191ff1313f082125a4412199ba0ee Mon Sep 17 00:00:00 2001 From: moe-berri Date: Tue, 6 Oct 2026 16:29:59 -0700 Subject: [PATCH] fix(lens): retain unassigned actor labels in the thread view --- .../detail/conversation/ConversationParts.tsx | 1 + .../TraceThread.integration.test.tsx | 28 +++++++++++++++++++ .../detail/conversation/TraceThread.tsx | 1 + 3 files changed, 30 insertions(+) diff --git a/ui/litellm-dashboard/src/components/lens/traces/detail/conversation/ConversationParts.tsx b/ui/litellm-dashboard/src/components/lens/traces/detail/conversation/ConversationParts.tsx index 2621568b397..2d5a587ba4a 100644 --- a/ui/litellm-dashboard/src/components/lens/traces/detail/conversation/ConversationParts.tsx +++ b/ui/litellm-dashboard/src/components/lens/traces/detail/conversation/ConversationParts.tsx @@ -40,6 +40,7 @@ export function ConversationSteps({ export function ConversationStep({ item, onOpenStep }: { item: ConversationItem; onOpenStep: (id: string) => void }) { return (
+ {item.span.actor_unassigned &&

Unassigned

} {item.toolResult === undefined && (
diff --git a/ui/litellm-dashboard/src/components/lens/traces/detail/conversation/TraceThread.integration.test.tsx b/ui/litellm-dashboard/src/components/lens/traces/detail/conversation/TraceThread.integration.test.tsx index c73eeb8a46c..c15debe7d69 100644 --- a/ui/litellm-dashboard/src/components/lens/traces/detail/conversation/TraceThread.integration.test.tsx +++ b/ui/litellm-dashboard/src/components/lens/traces/detail/conversation/TraceThread.integration.test.tsx @@ -74,6 +74,34 @@ describe("TraceThread", () => { expect(screen.getByRole("treeitem", { selected: true })).toHaveAttribute("data-row-id", "tool"); }); + it.each(["reply", "tool"])("labels an unassigned %s without attributing it to the root actor", async (kind) => { + const user = userEvent.setup(); + const foreign = { + ...(kind === "reply" ? llm : tool), + span_id: "foreign", + actor_id: null, + actor_unassigned: true, + agent: "", + }; + vi.mocked(agentTraceCall).mockResolvedValue({ ...trace, spans: [root, foreign] } as Trace); + vi.mocked(agentTraceSpanCall).mockImplementation(async (_token, _trace, id) => + id === "root" + ? { ...details.root, output: "" } + : { span_id: id, input: "", output: "Content from an unknown actor", attributes: {} }, + ); + renderWithProviders( + , + ); + await user.click(await screen.findByRole("tab", { name: "Thread" })); + const thread = await screen.findByRole("region", { name: "Trace thread" }); + if (kind === "tool") { + await user.click(await within(thread).findByRole("button", { name: /^Worked/ })); + await user.click(within(thread).getByRole("button", { name: "Expand read_file tool call" })); + } + expect(await within(thread).findByText("Content from an unknown actor")).toBeVisible(); + expect(within(thread).getByText("Unassigned", { exact: true })).toBeVisible(); + }); + it("continues native child branches and merges actor coverage across trace pages", async () => { const user = userEvent.setup(); const spans = [ diff --git a/ui/litellm-dashboard/src/components/lens/traces/detail/conversation/TraceThread.tsx b/ui/litellm-dashboard/src/components/lens/traces/detail/conversation/TraceThread.tsx index dfc2fc3e715..358413b6d93 100644 --- a/ui/litellm-dashboard/src/components/lens/traces/detail/conversation/TraceThread.tsx +++ b/ui/litellm-dashboard/src/components/lens/traces/detail/conversation/TraceThread.tsx @@ -243,6 +243,7 @@ function ReplyMeta({ turn, onOpenStep }: { turn: ThreadTurn; onOpenStep: (id: st const tokens = item.span.input_tokens + item.span.output_tokens; return (
+ {item.span.actor_unassigned && Unassigned} {item.model && {item.model}} {fmtMs(item.span.duration_ms)} {tokens > 0 && {tokens.toLocaleString()} tok}