diff --git a/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/RealtimePrettyView.test.tsx b/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/RealtimePrettyView.test.tsx index ea908552395..4a0adecc967 100644 --- a/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/RealtimePrettyView.test.tsx +++ b/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/RealtimePrettyView.test.tsx @@ -187,6 +187,49 @@ describe("RealtimePrettyView", () => { expect(screen.getByText("text")).toBeInTheDocument(); }); + it("should display the turn count in session header", () => { + render(); + expect(screen.getByText("2 turns")).toBeInTheDocument(); + }); + + it("should display singular 'turn' for a single response event", () => { + const singleTurnResponse = { + results: [ + { + type: "session.created", + session: { + id: "sess_1", + model: "gpt-4o-mini-realtime-preview", + voice: "alloy", + modalities: ["audio"], + }, + }, + { + type: "response.done", + response: { + id: "r1", + status: "completed", + output: [ + { + id: "item1", + role: "assistant", + type: "message", + content: [{ type: "audio", transcript: "Hi!" }], + }, + ], + }, + }, + ], + }; + render(); + expect(screen.getByText("1 turn")).toBeInTheDocument(); + }); + + it("should display the turn count in the output section header", () => { + render(); + expect(screen.getByText("Turns: 2")).toBeInTheDocument(); + }); + it("should display the Output section header", () => { render(); expect(screen.getByText("Output")).toBeInTheDocument(); diff --git a/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/RealtimePrettyView.tsx b/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/RealtimePrettyView.tsx index af048fe5042..94593c21fed 100644 --- a/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/RealtimePrettyView.tsx +++ b/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/RealtimePrettyView.tsx @@ -107,7 +107,7 @@ export function RealtimePrettyView({ response, metrics }: RealtimePrettyViewProp
{/* Session Configuration Card */} {sessionEvent?.session && ( - + )} {/* Conversation Turns */} @@ -138,7 +138,7 @@ export function RealtimePrettyView({ response, metrics }: RealtimePrettyViewProp ); } -function SessionCard({ session }: { session: RealtimeSession }) { +function SessionCard({ session, turnCount }: { session: RealtimeSession; turnCount: number }) { const [isCollapsed, setIsCollapsed] = useState(true); return ( @@ -184,6 +184,14 @@ function SessionCard({ session }: { session: RealtimeSession }) { {session.model} + {turnCount > 0 && ( + + {turnCount} {turnCount === 1 ? 'turn' : 'turns'} + + )} {session.voice && ( {session.voice} @@ -298,6 +306,7 @@ function ConversationCard({ const [isCollapsed, setIsCollapsed] = useState(false); const totalTokens = totalUsage?.total_tokens; + const turnCount = responses.length; const handleCopy = () => { const transcripts = responses .flatMap((r) => @@ -326,6 +335,7 @@ function ConversationCard({ onCopy={handleCopy} isCollapsed={isCollapsed} onToggleCollapse={() => setIsCollapsed(!isCollapsed)} + turnCount={turnCount} />
void; isCollapsed?: boolean; onToggleCollapse?: () => void; + turnCount?: number; } -export function SectionHeader({ type, tokens, cost, onCopy, isCollapsed, onToggleCollapse }: SectionHeaderProps) { +export function SectionHeader({ type, tokens, cost, onCopy, isCollapsed, onToggleCollapse, turnCount }: SectionHeaderProps) { return (
)} + + {/* Turn count */} + {turnCount !== undefined && turnCount > 0 && ( + + Turns: {turnCount} + + )}
{/* Copy Button */}