#!/usr/bin/env python3 """Generate a dry-run adaptation report (no platform side effects). Reads config/candidates.example.json (or DRY_RUN_CANDIDATES path). Writes reports/dry-run-.md """ from __future__ import annotations import json import os import sys from datetime import datetime from pathlib import Path from zoneinfo import ZoneInfo ROOT = Path(__file__).resolve().parents[1] TZ = ZoneInfo("Asia/Shanghai") def _load_candidates(path: Path) -> list[dict]: data = json.loads(path.read_text(encoding="utf-8")) if isinstance(data, list): return data if isinstance(data, dict) and "candidates" in data: return list(data["candidates"]) raise SystemExit(f"unexpected candidates schema in {path}") def _render(candidates: list[dict], ts: str, src: Path) -> str: lines = [ f"# Dry-run 适配建议报告 — {ts} (Asia/Shanghai)", "", "> **模式:dry-run** — 本报告不产生平台侧真实提交。", "> **需 Victor 人工提交**:请登录模盒控制台按草稿确认后手工提交。", "> **积分:待平台核实**(发布智能体本身不得分;成功适配按账本计)。", "", "## 闸门状态", "", f"- ALLOW_LIVE_SUBMIT is 1: {os.environ.get('ALLOW_LIVE_SUBMIT', '0').strip() == '1'}", f"- LIVE_CONFIRM set: {'yes' if os.environ.get('LIVE_CONFIRM', '').strip() else 'no'}", f"- STRATEGY_ID present: {'yes' if os.environ.get('STRATEGY_ID', '').strip() else 'no'}", "", "## 输入", "", f"- 候选文件:`{src}`", f"- 候选条数:{len(candidates)}", "", "## 建议草稿(字段示意,非真实 POST)", "", ] for i, c in enumerate(candidates, 1): model = c.get("modelAddress") or c.get("model_id") or "(missing model_id)" lines.append(f"### {i}. `{model}`") lines.append("") lines.append("```json") draft = { "modelAddress": model, "taskType": c.get("taskType", "text-generation"), "targetGpu": c.get("targetGpu", "(unset)"), "framework": c.get("framework", "(unset)"), "strategyId": "", "configParams": "", "notes": c.get("notes", ""), } lines.append(json.dumps(draft, ensure_ascii=False, indent=2)) lines.append("```") lines.append("") lines.append("- 风险:Phase1 未做唯一性/硬门预检;提交前请对照 do-not-resubmit 与队列空位。") lines.append("- **需 Victor 人工提交**(或经双重闸门的受控 live;本骨架默认拒绝 live)。") lines.append("- **积分:待平台核实**") lines.append("") lines.extend( [ "## 后续对接(Phase2)", "", "- **待对接 pending-submit**:尚未读取 `/workspace/modelhub-adapt/pending-submit.md`,未验证 A→B、PASS、硬门与 do-not-resubmit;本报告仅保留输入顺序。", "- 适配 W1 优先;同一 100 队列,≥95 全体停交;空位先 A 再 B,人工核验后决定,不凭示例报告提交。", "- 禁止 Makefile / 脚本调用 `/api/adapt/task/add` 或 `/agent_platform/strategies*` 写接口。", "", ] ) return "\n".join(lines) def main() -> None: src = Path( os.environ.get( "DRY_RUN_CANDIDATES", str(ROOT / "config" / "candidates.example.json"), ) ) if not src.is_file(): raise SystemExit(f"candidates file not found: {src}") candidates = _load_candidates(src) now = datetime.now(TZ) ts = now.strftime("%Y%m%d-%H%M%S") out_dir = ROOT / "reports" out_dir.mkdir(parents=True, exist_ok=True) out_path = out_dir / f"dry-run-{ts}.md" out_path.write_text(_render(candidates, now.strftime("%Y-%m-%d %H:%M:%S"), src), encoding="utf-8") print(f"Wrote {out_path}") if __name__ == "__main__": main()