AutoDataLab2.0 / training /scripts /kaggle_context_results_from_evidence.py
uchihamadara1816's picture
Upload 172 files
d02bacd verified
Raw
History Blame Contribute Delete
14.5 kB
#!/usr/bin/env python3
"""
Build full contextual text results from saved training evidence.
This script does NOT load LoRA adapters. That is intentional: it works even
when a run such as GRPO+RLVR has no `adapter_config.json` or adapter weights in
the exported folder. It reads `evidence.json`, replays the recorded CoS action
sequence in AutoDataLab++, and prints/saves the full expert reports plus CEO
brief for SFT, DPO, SFT+DPO, and GRPO+RLVR.
Kaggle example:
!python3 training/kaggle_context_results_from_evidence.py \\
--roots /kaggle/working /kaggle/input/results-filtered \\
--tasks expert_brief,risk_brief,crisis_brief \\
--rag-modes false,true
"""
from __future__ import annotations
import argparse
import json
import sys
from pathlib import Path
from typing import Any
REPO = Path(__file__).resolve().parents[2]
SCRIPT_DIR = Path(__file__).resolve().parent
if str(REPO) not in sys.path:
sys.path.insert(0, str(REPO))
if str(SCRIPT_DIR) not in sys.path:
sys.path.insert(0, str(SCRIPT_DIR))
from ceo_brief_env.environment import CEOBriefEnvironment, required_experts_for_task
from ceo_brief_env.models import CoSAction
from kaggle_agent_answers import format_episode_answers
DEFAULT_RUN_PATTERNS: dict[str, list[str]] = {
"sft": [
"training/evidence/sft/evidence.json",
"**/training/evidence/sft/evidence.json",
"**/qwen15b_sft_all/eval/evidence.json",
"**/qwen15b_sft_v1/eval/evidence.json",
],
"dpo": [
"training/evidence/dpo/evidence.json",
"**/training/evidence/dpo/evidence.json",
"**/qwen15b_dpo_all/eval/evidence.json",
"**/qwen15b_dpo_v1/eval/evidence.json",
],
"sft_dpo": [
"training/evidence/sft_dpo/evidence.json",
"**/training/evidence/sft_dpo/evidence.json",
"**/qwen15b_sft_then_dpo_all/eval/evidence.json",
"**/qwen15b_sft_then_dpo_v1/eval/evidence.json",
],
"grpo_rlvr": [
"training/evidence/grpo_rlvr/evidence.json",
"**/training/evidence/grpo_rlvr/evidence.json",
"**/qwen15b_grpo_rlvr_safe_all/eval/evidence.json",
"**/qwen15b_grpo_rlvr_safe_all/eval_after/evidence.json",
"**/qwen15b_grpo_rlvr_safe_all/eval_before/evidence.json",
"**/qwen15b_grpo_rlvr_safe_v1/eval/evidence.json",
"**/qwen15b_grpo_rlvr_safe_v1/eval_after/evidence.json",
"**/qwen15b_grpo_rlvr_safe_v1/eval_before/evidence.json",
"**/qwen15b_grpo_rlvr*/eval/evidence.json",
"**/qwen15b_grpo_rlvr*/eval_after/evidence.json",
"**/qwen15b_grpo_rlvr*/eval_before/evidence.json",
],
}
def parse_bool_text(text: str) -> bool:
return text.strip().lower() in {"1", "true", "yes", "y", "rag"}
def action_from_label(label: str) -> CoSAction:
label = (label or "").strip()
if ":" in label:
action_type, expert_id = label.split(":", 1)
expert_id = expert_id.strip() or None
if expert_id in {"none", "null"}:
expert_id = None
return CoSAction(action_type=action_type.strip(), expert_id=expert_id)
return CoSAction(action_type=label)
def action_from_any(item: Any) -> CoSAction:
if isinstance(item, dict):
if "action" in item and isinstance(item["action"], dict):
return CoSAction.model_validate(item["action"])
return CoSAction.model_validate(item)
return action_from_label(str(item))
def action_label(action: CoSAction) -> str:
if action.action_type in {"consult", "ask"}:
return f"{action.action_type}:{action.expert_id or 'null'}"
return action.action_type
def discover_evidence(roots: list[Path]) -> dict[str, Path]:
found: dict[str, Path] = {}
for label, patterns in DEFAULT_RUN_PATTERNS.items():
for root in roots:
if not root.exists():
continue
for pattern in patterns:
matches = sorted(root.glob(pattern), key=lambda p: (len(str(p)), str(p)))
if matches:
found[label] = matches[0]
break
if label in found:
break
return found
def load_evidence(path: Path) -> list[dict[str, Any]]:
data = json.loads(path.read_text(encoding="utf-8"))
if isinstance(data, dict) and "rows" in data:
data = data["rows"]
if not isinstance(data, list):
raise ValueError(f"Expected list evidence in {path}")
return [x for x in data if isinstance(x, dict)]
def deterministic_finish(obs, task: str) -> CoSAction:
for expert in required_experts_for_task(task):
if expert not in obs.consulted_experts:
return CoSAction(action_type="consult", expert_id=expert)
if obs.current_brief is None:
return CoSAction(action_type="summarize")
return CoSAction(action_type="submit")
def replay_row(method: str, row: dict[str, Any], complete_if_needed: bool) -> dict[str, Any]:
task = str(row.get("task") or "expert_brief")
use_rag = bool(row.get("rag", row.get("use_rag", False)))
env = CEOBriefEnvironment(shaping="strict", auto_fill_required=False)
obs = env.reset(task=task, use_rag=use_rag)
model_actions = [action_from_any(x) for x in (row.get("action_sequence") or [])]
fallback_actions = [action_from_any(x) for x in (row.get("fallback") or [])]
all_actions = model_actions + fallback_actions
trace: list[dict[str, Any]] = []
rewards: list[float] = []
for action in all_actions:
if obs.done:
break
obs = env.step(action)
rewards.append(float(obs.reward))
trace.append(
{
"step": obs.step_count,
"action": action.model_dump(exclude_none=True),
"action_label": action_label(action),
"reward": round(float(obs.reward), 4),
"done": bool(obs.done),
"consulted_experts": list(obs.consulted_experts),
"source": "model" if len(trace) < len(model_actions) else "fallback",
}
)
auto_finish: list[str] = []
while complete_if_needed and not obs.done and obs.step_count < obs.max_steps:
action = deterministic_finish(obs, task)
auto_finish.append(action_label(action))
obs = env.step(action)
rewards.append(float(obs.reward))
trace.append(
{
"step": obs.step_count,
"action": action.model_dump(exclude_none=True),
"action_label": action_label(action),
"reward": round(float(obs.reward), 4),
"done": bool(obs.done),
"consulted_experts": list(obs.consulted_experts),
"source": "auto_finish",
}
)
score = max(0.001, min(0.999, float(obs.terminal_grader_score or 0.001)))
return {
"task": task,
"policy_label": method,
"use_rag": use_rag,
"success": score >= 0.5,
"steps": obs.step_count,
"terminal_score": round(score, 4),
"cumulative_reward": round(sum(rewards), 4),
"step_rewards": [round(x, 4) for x in rewards],
"trace": trace,
"error": None,
"final_instruction": obs.instruction,
"task_difficulty": obs.task_difficulty,
"max_steps": obs.max_steps,
"consulted_experts": list(obs.consulted_experts),
"current_brief": obs.current_brief.model_dump() if obs.current_brief is not None else None,
"expert_reports": {k: v.model_dump() for k, v in obs.expert_reports.items()},
"evidence": {
"recorded_model_actions": [action_label(a) for a in model_actions],
"recorded_fallback_actions": [action_label(a) for a in fallback_actions],
"auto_finish_actions": auto_finish,
"recorded_policy_reward": row.get("policy_reward"),
"recorded_terminal_score": row.get("terminal_score"),
"needed_fallback": row.get("needed_fallback"),
"model_routed_required": row.get("model_routed_required") or [],
"required_experts": row.get("required_experts") or required_experts_for_task(task),
"trace_completion_previews": [
t.get("completion_preview")
for t in (row.get("trace") or [])
if isinstance(t, dict) and t.get("completion_preview")
],
},
}
def evidence_header(data: dict[str, Any]) -> str:
ev = data.get("evidence") or {}
lines = [
"TRAINING EVIDENCE CONTEXT",
f"Method: {data.get('policy_label')}",
f"Task: {data.get('task')} | RAG: {data.get('use_rag')}",
f"Recorded model route: {' -> '.join(ev.get('recorded_model_actions') or []) or '-'}",
f"Recorded fallback: {' -> '.join(ev.get('recorded_fallback_actions') or []) or '-'}",
f"Auto-finish used by this report: {' -> '.join(ev.get('auto_finish_actions') or []) or '-'}",
f"Required routed by model: {', '.join(ev.get('model_routed_required') or []) or '-'}",
f"Recorded policy reward: {ev.get('recorded_policy_reward')} | recorded terminal: {ev.get('recorded_terminal_score')}",
f"Replay terminal: {data.get('terminal_score')} | replay cumulative: {data.get('cumulative_reward')}",
]
previews = ev.get("trace_completion_previews") or []
if previews:
lines.append("\nRaw action completions / previews:")
lines.extend(f" {i + 1}. {p}" for i, p in enumerate(previews[:8]))
return "\n".join(lines)
def safe_name(text: str) -> str:
return "".join(ch if ch.isalnum() or ch in {"-", "_", "."} else "_" for ch in text).strip("_")
def main() -> int:
ap = argparse.ArgumentParser()
ap.add_argument(
"--roots",
nargs="*",
type=Path,
default=[
Path("/kaggle/working"),
Path("/kaggle/input"),
Path.cwd(),
REPO / "results",
],
help="Folders to search recursively for evidence.json files.",
)
ap.add_argument(
"--evidence",
action="append",
default=[],
help="Manual mapping: label=/path/to/evidence.json. Can be repeated.",
)
ap.add_argument("--tasks", default="expert_brief,risk_brief,crisis_brief")
ap.add_argument("--rag-modes", default="false,true")
ap.add_argument("--complete-if-needed", action="store_true", default=True)
ap.add_argument("--no-complete", dest="complete_if_needed", action="store_false")
ap.add_argument(
"--out-dir",
type=Path,
default=Path("/kaggle/working/context_results_all_methods")
if Path("/kaggle/working").is_dir()
else Path("context_results_all_methods"),
)
args = ap.parse_args()
args.out_dir.mkdir(parents=True, exist_ok=True)
evidence_map = discover_evidence(args.roots)
for spec in args.evidence:
if "=" not in spec:
raise ValueError("--evidence must be label=/path/to/evidence.json")
label, path = spec.split("=", 1)
evidence_map[label.strip()] = Path(path.strip())
wanted_tasks = {x.strip() for x in args.tasks.split(",") if x.strip()}
wanted_rag = {parse_bool_text(x) for x in args.rag_modes.split(",") if x.strip()}
if not evidence_map:
print("[error] No evidence.json files found. Pass --evidence label=/path/to/evidence.json", file=sys.stderr)
return 2
summary_rows: list[dict[str, Any]] = []
print("# AutoDataLab++ Context Results From Evidence\n")
print("Evidence files:")
for label, path in evidence_map.items():
print(f"- {label}: {path}")
print()
for method in ["sft", "dpo", "sft_dpo", "grpo_rlvr"]:
path = evidence_map.get(method)
if not path:
print(f"[skip] {method}: evidence.json not found")
continue
rows = load_evidence(path)
for row in rows:
task = str(row.get("task") or "")
rag = bool(row.get("rag", row.get("use_rag", False)))
if task not in wanted_tasks or rag not in wanted_rag:
continue
data = replay_row(method, row, complete_if_needed=bool(args.complete_if_needed))
text = evidence_header(data) + "\n\n" + format_episode_answers(data, show_scores=True)
stem = safe_name(f"{method}__{task}__rag_{rag}")
(args.out_dir / f"{stem}.txt").write_text(text, encoding="utf-8")
(args.out_dir / f"{stem}.json").write_text(json.dumps(data, indent=2, default=str), encoding="utf-8")
print("\n" + "#" * 96)
print(f"RESULT: {method} | task={task} | rag={rag}")
print("#" * 96)
print(text)
summary_rows.append(
{
"method": method,
"task": task,
"rag": rag,
"model_route": " -> ".join(data["evidence"]["recorded_model_actions"]),
"fallback": " -> ".join(data["evidence"]["recorded_fallback_actions"]),
"auto_finish": " -> ".join(data["evidence"]["auto_finish_actions"]),
"recorded_policy_reward": data["evidence"]["recorded_policy_reward"],
"recorded_terminal": data["evidence"]["recorded_terminal_score"],
"replay_terminal": data["terminal_score"],
"consulted": ", ".join(data["consulted_experts"]),
}
)
md = [
"# AutoDataLab++ Method Context Summary",
"",
"| Method | Task | RAG | Model route | Fallback | Auto-finish | Policy reward | Terminal | Consulted |",
"|---|---|---:|---|---|---|---:|---:|---|",
]
for row in summary_rows:
md.append(
f"| {row['method']} | {row['task']} | {row['rag']} | `{row['model_route']}` | "
f"`{row['fallback'] or '-'}` | `{row['auto_finish'] or '-'}` | "
f"{row['recorded_policy_reward']} | {row['replay_terminal']} | {row['consulted']} |"
)
summary = "\n".join(md)
(args.out_dir / "summary.md").write_text(summary, encoding="utf-8")
(args.out_dir / "summary.json").write_text(json.dumps(summary_rows, indent=2), encoding="utf-8")
print("\n" + summary)
print(f"\n[saved] {args.out_dir}")
print("[note] No adapters were loaded, so missing GRPO+RLVR adapter_config.json is not a problem.")
return 0
if __name__ == "__main__":
raise SystemExit(main())