feat: add trace visualization to display_sample_record (#396) #438
Open
feat: add trace visualization to display_sample_record (#396) #438
Conversation
Render LLM conversation traces (produced by `with_trace != TraceType.NONE`) as readable conversation flows in `display_sample_record()`. Two backends: Rich terminal panels (styled by role) and Jupyter HTML block diagrams. - New module `trace_renderer.py` with `TraceMessage` TypedDict and `TraceRenderer` class (`render_rich`, `render_notebook_html`) - `include_traces` parameter on both mixin and standalone function (defaults to True, opt out with `include_traces=False`) - Traces shown after Generated Columns table, before images - Unit tests for various trace shapes and integration tests Made-with: Cursor
Extract is_notebook_environment() utility to replace scattered get_ipython() try/except blocks. Improve Rich trace readability with better colors, separators, text folding, and dedented content. Match HTML trace font to Rich monospace output. Move index label to top of display and reduce inter-table spacing. Made-with: Cursor
Contributor
Greptile SummaryThis PR adds LLM conversation trace visualization to Key issues found:
|
| Filename | Overview |
|---|---|
| packages/data-designer-config/src/data_designer/config/utils/trace_renderer.py | New module providing Rich terminal and Jupyter HTML trace rendering; contains a logic bug where parallel tool calls in a single turn are miscounted as multiple turns in the summary label. |
| packages/data-designer-config/src/data_designer/config/utils/visualization.py | Integrates trace rendering into display_sample_record; introduces a regression where the record index is not written to saved files when running in a notebook environment. |
| packages/data-designer-config/src/data_designer/config/utils/misc.py | Centralises notebook detection into is_notebook_environment(); the Google Colab check uses class.name instead of class.module, causing Colab to be misidentified as a non-notebook environment. |
| packages/data-designer-config/tests/config/utils/test_trace_renderer.py | Comprehensive test suite covering Rich rendering, HTML rendering, edge cases, and display_sample_record integration; success path of render_notebook_html (with IPython mocked in) is not directly exercised. |
Flowchart
%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[display_sample_record called] --> B[is_notebook_environment]
B -->|True| C[Display record index via IPython HTML]
B -->|False| D[Append index Text to render_list]
C --> E[Build render_list: seed/LLM/image tables]
D --> E
E --> F{include_traces?}
F -->|Yes| G[TraceRenderer instantiated\nIterate LLM column side_effect_columns]
G --> H{in_notebook?}
H -->|No - terminal| I[render_rich → append Rich Panel\nto render_list]
H -->|Yes - notebook| J[Append to traces_to_display_later]
I --> K[Console.print render_list]
J --> K
F -->|No| K
K --> L{save_path set?}
L -->|Yes| M[Record Console → save HTML/SVG]
L -->|No| N[Display Console print]
M --> O{in_notebook and\ntraces_to_display_later?}
N --> O
O -->|Yes| P[render_notebook_html → IPython display HTML]
O -->|No| Q[Display images via IPython if in notebook]
P --> Q
Prompt To Fix All With AI
This is a comment left during a code review.
Path: packages/data-designer-config/src/data_designer/config/utils/trace_renderer.py
Line: 157-160
Comment:
**`turn_count` overcounts turns for parallel tool calls**
`turn_ids` is populated with one entry per tool call ID, so `len(turn_ids)` equals the total number of unique tool calls — not the number of conversational turns. When multiple tool calls are issued in a single assistant message (parallel calls), the summary will incorrectly say "N tool calls in N turns" instead of "N tool calls in 1 turn".
For example, the `multi_turn_tool_trace` fixture has two parallel calls (`call_a`, `call_b`) in one assistant message. `turn_ids = {"call_a", "call_b"}` → `turn_count = 2`, producing "2 tool calls in 2 turns" when the correct value is "2 tool calls in 1 turn".
The fix is to track the number of assistant messages that contain tool calls, not the number of unique IDs:
```python
# Replace turn_ids: set[str] = set() with:
turn_count = 0
# Inside the `if tool_calls:` block, increment turn count once per assistant message:
if tool_calls:
turn_count += 1
for tc in tool_calls:
...
continue
# Then at the summary:
turn_count_val = turn_count if tool_call_count > 0 else 0
turn_word = "turn" if turn_count_val == 1 else "turns"
summary = f"{tool_call_count} tool {call_word} in {turn_count_val} {turn_word}" if tool_call_count > 0 else ""
```
How can I resolve this? If you propose a fix, please make it concise.
---
This is a comment left during a code review.
Path: packages/data-designer-config/src/data_designer/config/utils/visualization.py
Line: 379-394
Comment:
**Record index omitted from saved files in notebook environments**
When `in_notebook` is `True`, the record index is emitted via IPython's `display()` and is never added to `render_list`. This means that when a caller also passes `save_path`, the saved HTML/SVG file will not contain the record index — a regression from the previous behaviour where the index was always appended to `render_list` and therefore always appeared in the saved output.
Consider always adding the index to `render_list` (for saved output) in addition to the HTML display call in notebook mode:
```python
if record_index is not None:
if in_notebook:
try:
from IPython.display import HTML, display
display(
HTML(
f"<div style='text-align:center;font-family:Menlo,Monaco,Consolas,monospace;"
f"font-size:12px;color:#666;margin:8px 0;'>[index: {record_index}]</div>"
)
)
except ImportError:
pass
# Always include in render_list so it appears in saved files
render_list.append(Text(f"[index: {record_index}]", justify="center"))
```
How can I resolve this? If you propose a fix, please make it concise.
---
This is a comment left during a code review.
Path: packages/data-designer-config/src/data_designer/config/utils/misc.py
Line: 46
Comment:
**Google Colab detection always returns `False`**
`shell.__class__.__name__` returns the bare class name (e.g. `"Shell"`), not the fully-qualified module path. In Google Colab the interactive shell class is `google.colab._shell.Shell`, so `__class__.__name__` is `"Shell"` — which is not in the tuple `("ZMQInteractiveShell", "google.colab._shell")`. As a result, `is_notebook_environment()` will return `False` in Colab even though HTML rendering works fine there, and users will get plain-text Rich output instead of the new HTML trace visualization.
```suggestion
return shell is not None and (
shell.__class__.__name__ == "ZMQInteractiveShell"
or shell.__class__.__module__ == "google.colab._shell"
)
```
How can I resolve this? If you propose a fix, please make it concise.Last reviewed commit: "fix: address review ..."
packages/data-designer-config/src/data_designer/config/utils/trace_renderer.py
Outdated
Show resolved
Hide resolved
packages/data-designer-config/src/data_designer/config/utils/visualization.py
Show resolved
Hide resolved
packages/data-designer-config/src/data_designer/config/utils/misc.py
Outdated
Show resolved
Hide resolved
- Remove double html.escape() on func_name in render_notebook_html; _build_html_block already escapes the title - Guard notebook trace display with include_traces to prevent potential NameError if trace_renderer is not instantiated - Improve is_notebook_environment() to check shell class name (ZMQInteractiveShell / google.colab._shell) instead of just get_ipython() existence, avoiding false positives in IPython terminals Made-with: Cursor
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
📋 Summary
Adds LLM conversation trace visualization to
display_sample_record, allowing users to inspect the full chain-of-thought, tool calls, and tool results forLLM-generated columns. Supports both terminal (Rich) and Jupyter notebook (HTML) rendering.
🔄 Changes
✨ Added
TraceRendererclass in [trace_renderer.py](https://github.com/NVIDIA-NeMo/DataDesigner/blob/nmulepati/feat/396-trace-visualization/packages/data-designer-config/src/data_designer/config/utils/trace_renderer.py) with Rich terminal and Jupyter HTML rendering
is_notebook_environment()helper inmisc.pyfor centralized notebook detectioninclude_tracesparameter ondisplay_sample_recordandWithRecordSamplerMixin(defaultTrue)test_trace_renderer.py](https://github.com/NVIDIA-NeMo/DataDesigner/blob/nmulepati/feat/396-trace-visualization/packages/data-designer-config/tests/config/utils/test_trace_renderer.py) (338 lines, covering Rich rendering, HTML rendering, edge cases, and integration)
🔧 Changed
visualization.py: consolidated notebook detection to use sharedis_notebook_environment(), added trace rendering for all LLM column types, moved record indexdisplay to top, normalized padding defaults
🔍 Attention Areas
trace_renderer.py](https://github.com/NVIDIA-NeMo/DataDesigner/blob/nmulepati/feat/396-trace-visualization/packages/data-designer-config/src/data_designer/config/utils/trace_renderer.py) — New module: core rendering logic for both Rich and HTML output, typed trace message structures
visualization.py](https://github.com/NVIDIA-NeMo/DataDesigner/blob/nmulepati/feat/396-trace-visualization/packages/data-designer-config/src/data_designer/config/utils/visualization.py) — Integration point: trace column discovery, rendering order, and padding changes
In ipynb notebook
In console
🤖 Generated with AI