v0.12: stabilize search routing and profile-driven phase4 pipeline
This commit is contained in:
@@ -0,0 +1,129 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Apply a model profile to agent definition files.
|
||||
|
||||
Supports:
|
||||
- OpenCode YAML frontmatter agents in .opencode/agents/*.md
|
||||
- Codex TOML agents in codex_adapter_templates/codex/agents/*.toml
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import re
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
REPO_ROOT = Path(__file__).resolve().parent.parent
|
||||
if str(REPO_ROOT) not in sys.path:
|
||||
sys.path.insert(0, str(REPO_ROOT))
|
||||
|
||||
from scripts.lib.model_config import (
|
||||
ModelConfigError,
|
||||
parse_model_overrides,
|
||||
resolve_model_profile,
|
||||
)
|
||||
|
||||
OPENCODE_ROLE_TO_FILE = {
|
||||
"dr_plan": ".opencode/agents/dr-plan.md",
|
||||
"dr_pm": ".opencode/agents/dr-pm.md",
|
||||
"dr_searcher": ".opencode/agents/dr-searcher.md",
|
||||
"dr_analyst": ".opencode/agents/dr-analyst.md",
|
||||
"dr_verifier": ".opencode/agents/dr-verifier.md",
|
||||
"dr_chief_editor": ".opencode/agents/dr-chief-editor.md",
|
||||
"dr_editor_in_chief": ".opencode/agents/dr-editor-in-chief.md",
|
||||
"dr_reporter": ".opencode/agents/dr-reporter.md",
|
||||
}
|
||||
|
||||
CODEX_ROLE_TO_FILE = {
|
||||
"dr_plan": "codex_adapter_templates/codex/agents/dr-plan.toml",
|
||||
"dr_pm": "codex_adapter_templates/codex/agents/dr-pm.toml",
|
||||
"dr_searcher": "codex_adapter_templates/codex/agents/dr-searcher.toml",
|
||||
"dr_analyst": "codex_adapter_templates/codex/agents/dr-analyst.toml",
|
||||
"dr_verifier": "codex_adapter_templates/codex/agents/dr-verifier.toml",
|
||||
"dr_chief_editor": "codex_adapter_templates/codex/agents/dr-chief-editor.toml",
|
||||
"dr_editor_in_chief": "codex_adapter_templates/codex/agents/dr-editor-in-chief.toml",
|
||||
"dr_reporter": "codex_adapter_templates/codex/agents/dr-reporter.toml",
|
||||
}
|
||||
|
||||
|
||||
def replace_opencode_model(path: Path, model: str) -> bool:
|
||||
text = path.read_text(encoding="utf-8")
|
||||
new_text, count = re.subn(r"(?m)^model:\s*.+$", f"model: {model}", text, count=1)
|
||||
if count == 0:
|
||||
raise SystemExit(f"failed to locate model field: {path}")
|
||||
if new_text == text:
|
||||
return False
|
||||
path.write_text(new_text, encoding="utf-8")
|
||||
return True
|
||||
|
||||
|
||||
def replace_codex_model(path: Path, model: str) -> bool:
|
||||
text = path.read_text(encoding="utf-8")
|
||||
new_text, count = re.subn(r'(?m)^model\s*=\s*"[^"]+"$', f'model = "{model}"', text, count=1)
|
||||
if count == 0:
|
||||
raise SystemExit(f"failed to locate model field: {path}")
|
||||
if new_text == text:
|
||||
return False
|
||||
path.write_text(new_text, encoding="utf-8")
|
||||
return True
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser(description="Apply model profile to agent files")
|
||||
parser.add_argument("--profile", required=True, help="Profile name from configs/models.yaml")
|
||||
parser.add_argument("--target", choices=["opencode", "codex", "both"], default="both")
|
||||
parser.add_argument(
|
||||
"--model-override",
|
||||
action="append",
|
||||
default=[],
|
||||
metavar="ROLE=MODEL",
|
||||
help="Override one role model, repeatable",
|
||||
)
|
||||
parser.add_argument("--dry-run", action="store_true")
|
||||
args = parser.parse_args()
|
||||
|
||||
try:
|
||||
resolved = resolve_model_profile(
|
||||
profile=args.profile,
|
||||
overrides=parse_model_overrides(args.model_override),
|
||||
)
|
||||
except ModelConfigError as exc:
|
||||
raise SystemExit(f"model profile resolution failed: {exc}") from exc
|
||||
|
||||
roles = resolved["roles"]
|
||||
changed: list[str] = []
|
||||
|
||||
def apply_map(mapping: dict[str, str], mode: str) -> None:
|
||||
for role, rel_path in mapping.items():
|
||||
model = roles.get(role)
|
||||
if not model:
|
||||
continue
|
||||
file_path = REPO_ROOT / rel_path
|
||||
if not file_path.exists():
|
||||
continue
|
||||
if args.dry_run:
|
||||
changed.append(f"{mode}:{rel_path} -> {model}")
|
||||
continue
|
||||
did_change = replace_opencode_model(file_path, model) if mode == "opencode" else replace_codex_model(file_path, model)
|
||||
if did_change:
|
||||
changed.append(f"{mode}:{rel_path} -> {model}")
|
||||
|
||||
if args.target in ("opencode", "both"):
|
||||
apply_map(OPENCODE_ROLE_TO_FILE, "opencode")
|
||||
if args.target in ("codex", "both"):
|
||||
apply_map(CODEX_ROLE_TO_FILE, "codex")
|
||||
|
||||
print(f"Profile applied: {resolved['profile']}")
|
||||
print(f"Target: {args.target}")
|
||||
if changed:
|
||||
print("Updated:")
|
||||
for item in changed:
|
||||
print(f" - {item}")
|
||||
else:
|
||||
print("No files changed.")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
Reference in New Issue
Block a user