Spaces:
Sleeping
Sleeping
| #!/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()) | |