|
|
import json |
|
|
from typing import Any, Dict, List, Optional |
|
|
|
|
|
|
|
|
def _short(x: Any, n: int = 160) -> str: |
|
|
try: |
|
|
s = json.dumps(x, ensure_ascii=False) |
|
|
except Exception: |
|
|
s = str(x) |
|
|
if len(s) > n: |
|
|
return s[:n] + "…" |
|
|
return s |
|
|
|
|
|
|
|
|
def _fmt_reward(x: Optional[float]) -> str: |
|
|
if x is None: |
|
|
return "n/a" |
|
|
|
|
|
return f"{x:.6g}" |
|
|
|
|
|
|
|
|
def render_report_markdown(diff: Dict[str, Any]) -> str: |
|
|
s = diff.get("summary", {}) |
|
|
counts = diff.get("class_counts", {}) |
|
|
first = s.get("first_divergence_index") |
|
|
|
|
|
|
|
|
line_bits: List[str] = [] |
|
|
if first is None: |
|
|
line_bits.append("Timelines identical") |
|
|
else: |
|
|
line_bits.append(f"Identical until index {first}") |
|
|
line_bits.append(f"{s.get('diff_event_count', 0)} events differ") |
|
|
if s.get("missing_event_count", 0): |
|
|
line_bits.append(f"{s.get('missing_event_count')} missing") |
|
|
if s.get("final_reward_delta") is not None: |
|
|
line_bits.append(f"Final reward delta: {s.get('final_reward_delta'):.6g}") |
|
|
one_liner = " · ".join(line_bits) |
|
|
|
|
|
lines: List[str] = [] |
|
|
lines.append("# DRP Differential Report") |
|
|
lines.append("") |
|
|
lines.append(f"**{one_liner}**") |
|
|
lines.append("") |
|
|
lines.append("## Run identity") |
|
|
lines.append(f"- Run A: `{s.get('run_a')}`") |
|
|
lines.append(f"- Run B: `{s.get('run_b')}`") |
|
|
lines.append(f"- Framework A/B: `{s.get('framework_a')}` / `{s.get('framework_b')}`") |
|
|
lines.append(f"- Model A/B: `{s.get('model_a')}` / `{s.get('model_b')}`") |
|
|
lines.append(f"- Events A/B: `{s.get('events_a')}` / `{s.get('events_b')}`") |
|
|
lines.append("") |
|
|
|
|
|
if s.get("run_link_a") or s.get("run_link_b"): |
|
|
lines.append("## Viewer links (if provided)") |
|
|
if s.get("run_link_a"): |
|
|
lines.append(f"- Run A viewer: {s.get('run_link_a')}") |
|
|
if s.get("run_link_b"): |
|
|
lines.append(f"- Run B viewer: {s.get('run_link_b')}") |
|
|
lines.append("") |
|
|
|
|
|
lines.append("## Summary stats") |
|
|
lines.append(f"- First divergence index: `{s.get('first_divergence_index')}`") |
|
|
lines.append(f"- Diff events: `{s.get('diff_event_count')}`") |
|
|
lines.append(f"- Missing events: `{s.get('missing_event_count')}`") |
|
|
lines.append(f"- Final reward A/B/Δ: `{_fmt_reward(s.get('final_reward_a'))}` / `{_fmt_reward(s.get('final_reward_b'))}` / `{_fmt_reward(s.get('final_reward_delta'))}`") |
|
|
lines.append("") |
|
|
|
|
|
lines.append("## Divergence classes") |
|
|
if counts: |
|
|
for k in sorted(counts.keys()): |
|
|
lines.append(f"- **{k}**: {counts[k]}") |
|
|
else: |
|
|
lines.append("- (no diffs)") |
|
|
lines.append("") |
|
|
|
|
|
|
|
|
lines.append("## First divergence detail") |
|
|
if first is None: |
|
|
lines.append("- No divergence found.") |
|
|
return "\n".join(lines) |
|
|
|
|
|
first_item = None |
|
|
for item in diff.get("differences", []): |
|
|
if item.get("i") == first: |
|
|
first_item = item |
|
|
break |
|
|
|
|
|
if not first_item: |
|
|
|
|
|
lines.append("- Divergence is a length mismatch or occurs where one side is missing.") |
|
|
return "\n".join(lines) |
|
|
|
|
|
lines.append(f"- Index: `{first_item.get('i')}`") |
|
|
lines.append(f"- Class: `{first_item.get('class')}`") |
|
|
lines.append(f"- Kind A/B: `{first_item.get('kind_a')}` / `{first_item.get('kind_b')}`") |
|
|
lines.append(f"- Step A/B: `{first_item.get('step_a')}` / `{first_item.get('step_b')}`") |
|
|
if first_item.get("link_a") or first_item.get("link_b"): |
|
|
lines.append("") |
|
|
lines.append("### Jump to replay (if provided)") |
|
|
if first_item.get("link_a"): |
|
|
lines.append(f"- Open A @ i={first}: {first_item.get('link_a')}") |
|
|
if first_item.get("link_b"): |
|
|
lines.append(f"- Open B @ i={first}: {first_item.get('link_b')}") |
|
|
lines.append("") |
|
|
lines.append("### Key diffs (first 25)") |
|
|
for d in (first_item.get("diffs") or [])[:25]: |
|
|
lines.append(f"- `{d.get('path')}` ({d.get('kind')}): A={_short(d.get('a'))} | B={_short(d.get('b'))}") |
|
|
|
|
|
if "text_unified_diff" in first_item: |
|
|
lines.append("") |
|
|
lines.append("### Text unified diff (truncated)") |
|
|
lines.append("```diff") |
|
|
lines.append(first_item["text_unified_diff"][:8000]) |
|
|
lines.append("```") |
|
|
|
|
|
return "\n".join(lines) |