Spaces:
Sleeping
Sleeping
| """Pure rendering of agent events to HTML, plus the session run-cap check.""" | |
| import html as _html | |
| import json | |
| from agent import config | |
| TIMELINE_CSS = """ | |
| :root { --bg:#0b0f14; --card:#121821; --line:#1e2733; --fg:#cfd8e3; | |
| --muted:#7d8a99; --cyan:#39d3ee; --green:#37e0a6; --amber:#f0b35e; } | |
| .gradio-container { background: var(--bg) !important; } | |
| #oa-timeline { font-family: ui-monospace, "JetBrains Mono", Menlo, Consolas, monospace; | |
| display:flex; flex-direction:column; gap:10px; } | |
| .oa-card { background:var(--card); border:1px solid var(--line); border-left:3px solid var(--line); | |
| border-radius:8px; padding:10px 14px; color:var(--fg); } | |
| .oa-card .oa-head { font-size:12px; letter-spacing:.04em; color:var(--muted); margin-bottom:4px; } | |
| .oa-thought { border-left-color: var(--cyan); } | |
| .oa-revision { border-left-color: var(--amber); background:#1a1710; } | |
| .oa-tool_call { border-left-color: var(--green); } | |
| .oa-observation { border-left-color:#2b3a4a; } | |
| .oa-observation.oa-weak { border-left-color: var(--amber); opacity:.92; } | |
| .oa-final { border-left-color: var(--green); background:#0f1a16; font-size:15px; } | |
| .oa-status { color:var(--muted); font-style:italic; } | |
| .oa-args, .oa-obs { white-space:pre-wrap; word-break:break-word; margin-top:4px; } | |
| #oa-stats { color:var(--muted); font-family: ui-monospace, monospace; margin-top:6px; } | |
| """ | |
| _ICON = {"thought": "🧠", "tool_call": "🔧", "observation": "📄", "final": "✅", "limit": "⛔"} | |
| def _esc(text) -> str: | |
| return _html.escape(str(text)) | |
| def render_event(event) -> str: | |
| kind = event["kind"] | |
| step = event.get("step", "") | |
| if kind == "thought": | |
| revision = event.get("revision", False) | |
| icon = "↻" if revision else _ICON["thought"] | |
| label = "revision" if revision else "thought" | |
| cls = "oa-thought oa-revision" if revision else "oa-thought" | |
| return (f'<div class="oa-card {cls}"><div class="oa-head">{icon} {label} · step {step}</div>' | |
| f'<div>{_esc(event["text"])}</div></div>') | |
| if kind == "tool_call": | |
| args = json.dumps(event.get("args", {}), ensure_ascii=False) | |
| return (f'<div class="oa-card oa-tool_call"><div class="oa-head">🔧 tool call · step {step}</div>' | |
| f'<div>{_esc(event["tool"])}</div><div class="oa-args">{_esc(args)}</div></div>') | |
| if kind == "observation": | |
| weak = " oa-weak" if event.get("weak") else "" | |
| tag = " (weak — reconsidering)" if event.get("weak") else "" | |
| return (f'<div class="oa-card oa-observation{weak}"><div class="oa-head">📄 observation · ' | |
| f'{_esc(event["tool"])}{tag}</div><div class="oa-obs">{_esc(event["text"])}</div></div>') | |
| if kind == "final": | |
| return (f'<div class="oa-card oa-final"><div class="oa-head">✅ final answer</div>' | |
| f'<div>{_esc(event["text"])}</div></div>') | |
| if kind == "limit": | |
| return (f'<div class="oa-card"><div class="oa-head">⛔ stopped</div>' | |
| f'<div>{_esc(event["text"])}</div></div>') | |
| return "" | |
| def render_timeline(events, status=None) -> str: | |
| cards = [render_event(e) for e in events if e.get("kind") != "status"] | |
| if status: | |
| cards.append(f'<div class="oa-card oa-status">{_esc(status)}</div>') | |
| return f'<div id="oa-timeline">{"".join(cards)}</div>' | |
| def render_stats(*, steps: int, tool_calls: int, seconds: float) -> str: | |
| return f"{steps} steps · {tool_calls} tool calls · {seconds:.1f}s" | |
| def check_run_allowed(runs_used: int, cap: int = config.RUNS_PER_SESSION): | |
| if runs_used >= cap: | |
| return False, f"You've reached this session's run limit ({cap}). Refresh to start a new session." | |
| return True, "" | |