Repository navigation
feat(ui): agent traces open in a side drawer with a chat-style run view - #43972
Conversation
Tree with connector lines, typed icon tiles and provider logos, hover cards with timing, and Input/Output sections rendered as message cards.
…alizers per convention OpenAI responses-style content (reasoning + text blocks) rendered as raw JSON in the trace view. Keep the text blocks and drop opaque reasoning. Move each convention into litellm/tracing/normalizers with an ordered registry so new frameworks plug in without touching OTLP decoding.
…ction_call blocks
…-style tree Clicking a run opens it in a drawer over the list instead of a full page. j/k and the header arrows switch runs, Esc closes. The tree gets dashed connectors, per-span waterfall bars, mono tool names and real provider logos. AI messages with reasoning/function_call blocks render as text.
|
|
Codecov Report❌ Patch coverage is 📢 Thoughts on this report? Let us know! |
|
PR overviewAll previously flagged issues have been addressed. No open security concerns remain on this pull request. Security reviewNo open security issues remain on this pull request. Fixed/addressed: 1 · PR risk: 0/10 |
…ced motion, narrow screens
|
@greptileai fixed JSON trimming, drawer shortcuts, reduced motion, narrow screens, tree guides and expandable tool output. Please re-review |
GET /v1/traces/{trace_id}/spans/{span_id} now also returns input_ui and
output_ui, a tagged union of messages, fields or text built server side by
litellm/tracing/ui_format.py. The trace UI renders from those fields and only
falls back to client-side parsing when talking to an older proxy. The raw
input and output strings are unchanged, and so is storage
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…still exceed the size limit
|
@greptileai added the standard LiteLLM UI span format and fixed the oversized-fields truncation case. Please re-review |
| return has_role and ("content" in message.model_fields_set or bool(message.tool_calls)) | ||
|
|
||
|
|
||
| def _arguments_text(arguments: JsonValue) -> str: |
| return "assistant" if has_tool_calls else "user" | ||
|
|
||
|
|
||
| def _ui_message(message: _RawMessage) -> UIMessage: |
…ep failed-tool styling
|
@greptileai kept both messages when tool_calls are oversized and preserved failed-tool styling. Please re-review |
| role_only: Final = MappingProxyType({"role": message.get("role", "user")}) | ||
| attempts: Final = ( | ||
| _cut_content(message, text, budget, 1), | ||
| _cut_content(role_only, text, budget, 1), | ||
| _cut_content(role_only, text, budget, _MAX_JSON_ESCAPE_BYTES), |
There was a problem hiding this comment.
| """One message cut to `budget` bytes, as valid JSON. | ||
|
|
||
| Shortens `content` first; if other fields (e.g. huge tool_calls) still don't fit, keeps only role + content. | ||
| """ |
There was a problem hiding this comment.
Unnecessary source comment This docstring explains straightforward fallback code, violating the repository's limits on source comments. Remove it before merging
| """One message cut to `budget` bytes, as valid JSON. | |
| Shortens `content` first; if other fields (e.g. huge tool_calls) still don't fit, keeps only role + content. | |
| """ |
Context Used: AGENTS.md (source)
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
| elif operation == "execute_tool": | ||
| row["ObservationType"] = "tool" | ||
| row["AgentName"] = attributes.get("gen_ai.agent.name", "") | ||
| row["Model"] = attributes.get("gen_ai.request.model") or attributes.get("gen_ai.response.model", "") |
There was a problem hiding this comment.
Normalizer mutates span rows These assignments violate the repository's immutability instruction for new code. Return normalized values or justify unavoidable mutation before merging
Context Used: AGENTS.md (source)
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
…ns_ui # Conflicts: # litellm/tracing/decode.py
TLDR
Problem this solves:
How it solves it:
litellm/tracing/normalizers/, so adding a new framework is one fileinput_ui/output_uiasmessages,fieldsortext, and the UI renders those instead of re-parsing raw payloads (falls back to the old parsing against an older proxy)Intentional product change: the run view is a side drawer now, not a full page. The list stays visible behind it
User Flow
Before: a developer opens a multi-turn agent trace and can't read it
After: the same trace reads like a chat, and switching runs is one key
Pre-Submission checklist
Please complete all items before asking a LiteLLM maintainer to review your PR
uv run pytest tests/unit/<your_test_file>.py -v. Leave the suites (make test-unit-*,make test-unit) to CI: it finishes in ~15 minutes where a laptop takes an hour or more@greptileaito re-request a review after pushing changes)Delays in PR merge?
If you're seeing a delay in your PR being merged, ping the LiteLLM Team on Slack (#pr-review).
Screenshots / Proof of Fix
Traces come from real Deep Agents runs on gpt-6.1-sol, exported over OTLP to a local proxy with
tracing: store: clickhouseBefore (ae60fd1)
travel-turn-7After (0f8c043)
Type
🆕 New Feature
🐛 Bug Fix
Caveats (if any)
Low
Final Attestation