Spaces:
Sleeping
Sleeping
| """Renders a job's trace events into an interactive HTML timeline. | |
| Events are simple dicts (see local_runner.py for the exact shapes emitted): | |
| {"ts": "...", "kind": "tool_call" | "tool_result" | "note" | "task_update" | "status", | |
| "label": "short one-liner", "detail": "optional longer text", "ok": true/false/None} | |
| Rendered with plain <details>/<summary> so expand/collapse works with no JS, | |
| which keeps it safe to drop straight into a Gradio gr.HTML component. | |
| """ | |
| from __future__ import annotations | |
| import html as _html | |
| _ICONS = { | |
| "tool_call": "\U0001F527", # wrench | |
| "tool_result_ok": "✅", | |
| "tool_result_fail": "❌", | |
| "note": "\U0001F4AC", | |
| "task_update": "\U0001F4CB", | |
| "status_start": "▶️", | |
| "status_end": "\U0001F3C1", | |
| } | |
| _STYLE = """ | |
| <style> | |
| .pr-trace { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; } | |
| .pr-trace .pr-empty { | |
| color: var(--body-text-color-subdued, #888); font-style: italic; padding: 22px; | |
| text-align: center; border: 1px dashed var(--border-color-primary, #ddd); border-radius: 12px; | |
| } | |
| .pr-trace ol { list-style: none; margin: 0; padding: 4px 0 0; border-left: 2px solid var(--border-color-primary, #d0d5dd); max-height: 480px; overflow-y: auto; } | |
| .pr-trace li { position: relative; padding: 6px 0 6px 18px; margin-left: 4px; } | |
| .pr-trace li::before { | |
| content: ""; position: absolute; left: -7px; top: 14px; | |
| width: 10px; height: 10px; border-radius: 50%; | |
| background: #9aa4b2; border: 2px solid var(--background-fill-secondary, #fff); | |
| } | |
| .pr-trace li.ok::before { background: #0d9488; } | |
| .pr-trace li.fail::before { background: #b91c1c; } | |
| .pr-trace summary { cursor: pointer; display: flex; gap: 8px; align-items: baseline; padding: 2px 4px; border-radius: 6px; } | |
| .pr-trace summary:hover { background: rgba(13,148,136,0.08); } | |
| .pr-trace summary .pr-icon { flex: none; } | |
| .pr-trace summary .pr-label { font-weight: 600; } | |
| .pr-trace summary .pr-ts { margin-left: auto; font-size: 0.78em; color: var(--body-text-color-subdued, #888); white-space: nowrap; } | |
| .pr-trace .pr-detail { | |
| margin: 6px 0 0 22px; padding: 8px 10px; border-radius: 6px; | |
| background: rgba(127,127,127,0.08); font-size: 0.85em; white-space: pre-wrap; | |
| font-family: ui-monospace, 'Fira Code', monospace; max-height: 260px; overflow: auto; | |
| } | |
| .pr-trace .pr-live { | |
| color: #0d9488; font-weight: 600; margin-bottom: 8px; padding: 6px 10px; | |
| background: rgba(13,148,136,0.1); border-radius: 8px; display: inline-block; | |
| } | |
| </style> | |
| """ | |
| def _icon_for(event: dict) -> str: | |
| kind = event.get("kind") | |
| if kind == "tool_result": | |
| return _ICONS["tool_result_ok"] if event.get("ok") else _ICONS["tool_result_fail"] | |
| if kind == "status": | |
| return _ICONS["status_end"] if event.get("ok") is not None else _ICONS["status_start"] | |
| return _ICONS.get(kind, "•") | |
| def _row_class(event: dict) -> str: | |
| if event.get("ok") is True: | |
| return "ok" | |
| if event.get("ok") is False: | |
| return "fail" | |
| return "" | |
| def render_trace(events: list[dict], running: bool = False) -> str: | |
| if not events: | |
| empty_msg = "Waiting for the local runner to pick this job up..." if running else "No trace recorded yet." | |
| return f'{_STYLE}<div class="pr-trace"><div class="pr-empty">{_html.escape(empty_msg)}</div></div>' | |
| items = [] | |
| for event in events: | |
| icon = _icon_for(event) | |
| label = _html.escape(str(event.get("label", ""))) | |
| ts = _html.escape(str(event.get("ts", ""))) | |
| detail = event.get("detail") | |
| row_class = _row_class(event) | |
| detail_html = "" | |
| if detail: | |
| detail_html = f'<div class="pr-detail">{_html.escape(str(detail))}</div>' | |
| items.append( | |
| f'<li class="{row_class}"><details{" open" if detail and row_class == "fail" else ""}>' | |
| f'<summary><span class="pr-icon">{icon}</span>' | |
| f'<span class="pr-label">{label}</span>' | |
| f'<span class="pr-ts">{ts}</span></summary>' | |
| f"{detail_html}</details></li>" | |
| ) | |
| live_banner = '<div class="pr-live">⏳ Run in progress — refreshing automatically...</div>' if running else "" | |
| return f'{_STYLE}<div class="pr-trace">{live_banner}<ol>{"".join(items)}</ol></div>' | |