"""扫最近的 task,定位「bad arguments to run_python: code or script_path must be
provided」到底什么时候真正触发。

两条线:
  A. 直接在 tool-result 消息里搜这句错误 —— 这是运行时真的报了的铁证。
  B. 看产生它的那条 assistant run_python 调用,arguments 到底长啥样。
排除 `_compacted`(那是入库后上下文压缩留下的历史占位,运行时是有 code 的,不算)。
"""
import json
import os
from collections import Counter
from pathlib import Path

env = Path(__file__).resolve().parent.parent / ".env"
for line in env.read_text(encoding="utf-8").splitlines():
    if line.strip().startswith("ZCBOT_DB_URL="):
        os.environ["ZCBOT_DB_URL"] = line.split("=", 1)[1].strip()

from sqlalchemy import create_engine, text  # noqa: E402

engine = create_engine(os.environ["ZCBOT_DB_URL"])
ERR = "bad arguments to run_python: code or script_path must be provided"

with engine.connect() as conn:
    tasks = conn.execute(
        text("select task_id, created_at from tasks order by created_at desc limit 60")
    ).fetchall()

    per_task = Counter()
    shapes = Counter()
    samples = []
    for tid, created in tasks:
        msgs = conn.execute(
            text("select idx, payload from messages where task_id=:t order by idx"),
            {"t": tid},
        ).fetchall()
        # 建 tool_call_id -> arguments 映射(看错误对应的调用 args)
        call_args = {}
        for idx, payload in msgs:
            if payload.get("role") == "assistant":
                for tc in payload.get("tool_calls") or []:
                    call_args[tc.get("id")] = (tc.get("function") or {}).get("arguments")
        for idx, payload in msgs:
            if payload.get("role") != "tool":
                continue
            content = payload.get("content") or ""
            if isinstance(content, list):
                content = json.dumps(content, ensure_ascii=False)
            if ERR not in content:
                continue
            per_task[(str(tid)[:8], str(created)[:16])] += 1
            raw = call_args.get(payload.get("tool_call_id"))
            # 归类 args 形态
            try:
                args = json.loads(raw) if raw else {}
            except Exception:
                shape = "MANGLED(非法JSON)"
            else:
                if args == {}:
                    shape = "空 {}"
                elif "_compacted" in args:
                    shape = "_compacted(历史占位)"
                else:
                    shape = "其他: " + repr(raw)[:80]
            shapes[shape] += 1
            if len(samples) < 25:
                samples.append((str(tid)[:8], idx, shape, repr(raw)[:140]))

print(f"扫了最近 {len(tasks)} 个 task")
print(f"真正触发该错误的 tool-result 条数: {sum(per_task.values())}\n")
print("=== 按 task 分布(task / 创建时间 / 次数)===")
for (t, c), n in per_task.most_common():
    print(f"  {t}  {c}  ->  {n} 次")
print("\n=== 触发时 run_python 的 arguments 形态 ===")
for s, n in shapes.most_common():
    print(f"  {n:>3}x  {s}")
print("\n=== 样本 ===")
for t, idx, shape, raw in samples:
    print(f"  [{t} #{idx}] {shape}: {raw}")
