mirror of
https://github.com/lllllllama/RigorPilot-Skills.git
synced 2026-09-14 13:43:27 +08:00
feat: surface fuller training command hints
This commit is contained in:
@@ -77,9 +77,15 @@ def main() -> int:
|
||||
raise AssertionError(f"orchestrator dry-run failed to emit train_outputs/{rel}")
|
||||
if payload["setup_commands"][0]["command"] != "conda env create -f environment.yml":
|
||||
raise AssertionError("orchestrator failed to propagate the environment setup plan")
|
||||
if payload["full_training_command"] != "python train.py --config configs/demo.yaml":
|
||||
raise AssertionError("orchestrator failed to preserve the fuller training command hint")
|
||||
if "hours" not in (payload["training_duration_hint"] or "") and "unknown" not in (payload["training_duration_hint"] or ""):
|
||||
raise AssertionError("orchestrator failed to surface a conservative training duration hint")
|
||||
if "Planned command:" not in payload["next_action"]:
|
||||
raise AssertionError("orchestrator failed to mention the fuller training command in next_action")
|
||||
|
||||
print("ok: True")
|
||||
print("checks: 7")
|
||||
print("checks: 10")
|
||||
print("failures: 0")
|
||||
return 0
|
||||
finally:
|
||||
|
||||
@@ -72,6 +72,10 @@ def run_case(orchestrator: Path, sample_repo: Path, temp_root: Path, lane: str)
|
||||
raise AssertionError(f"{lane} case failed to preserve the lane in train_outputs/status.json")
|
||||
if train_status["completed_steps"] < 1:
|
||||
raise AssertionError(f"{lane} case failed to parse any completed steps from the training log")
|
||||
if train_status["full_training_command"] != "python train.py --config configs/demo.yaml":
|
||||
raise AssertionError(f"{lane} case failed to preserve the fuller training command")
|
||||
if "likely" not in (train_status["training_duration_hint"] or "") and "hours" not in (train_status["training_duration_hint"] or ""):
|
||||
raise AssertionError(f"{lane} case failed to emit a conservative training duration hint")
|
||||
|
||||
if lane == "trusted":
|
||||
if payload["run_mode"] != "startup_verification":
|
||||
@@ -80,6 +84,8 @@ def run_case(orchestrator: Path, sample_repo: Path, temp_root: Path, lane: str)
|
||||
raise AssertionError("trusted case should require explicit confirmation before fuller training")
|
||||
if train_status["stop_reason"] != "startup_verification_window_elapsed":
|
||||
raise AssertionError("trusted case should stop at the startup verification window")
|
||||
if "Estimated duration:" not in payload["next_action"]:
|
||||
raise AssertionError("trusted case should mention the expected fuller training duration in next_action")
|
||||
else:
|
||||
if payload["run_mode"] != "full_kickoff":
|
||||
raise AssertionError("explore case should switch directly to full_kickoff mode")
|
||||
|
||||
@@ -230,6 +230,8 @@ def write_repro_status(output_dir: Path, context: Dict[str, Any]) -> None:
|
||||
"human_decisions_required": context.get("human_decisions_required", []),
|
||||
"next_safe_action": context.get("next_safe_action"),
|
||||
"artifact_provenance": context.get("artifact_provenance", []),
|
||||
"full_training_command": context.get("full_training_command"),
|
||||
"training_duration_hint": context.get("training_duration_hint"),
|
||||
"verified_commit_count": len(context.get("verified_commits", [])),
|
||||
"outputs": {
|
||||
"summary": "repro_outputs/SUMMARY.md",
|
||||
@@ -430,6 +432,8 @@ def write_train_status(output_dir: Path, context: Dict[str, Any]) -> None:
|
||||
"resume_from": context.get("resume_from"),
|
||||
"dataset": context.get("dataset"),
|
||||
"checkpoint_source": context.get("checkpoint_source"),
|
||||
"full_training_command": context.get("full_training_command"),
|
||||
"training_duration_hint": context.get("training_duration_hint"),
|
||||
"max_steps": context.get("max_steps"),
|
||||
"completed_steps": context.get("completed_steps"),
|
||||
"last_epoch": context.get("last_epoch"),
|
||||
|
||||
@@ -5,6 +5,7 @@ from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import re
|
||||
import shlex
|
||||
import subprocess
|
||||
import sys
|
||||
@@ -88,6 +89,56 @@ def derive_checkpoint_hint(asset_data: Dict[str, Any]) -> str:
|
||||
return "none"
|
||||
|
||||
|
||||
def extract_config_path(command: str) -> str | None:
|
||||
tokens = shlex.split(command, posix=False)
|
||||
for index, token in enumerate(tokens):
|
||||
if token in {"--config", "--cfg"} and index + 1 < len(tokens):
|
||||
return tokens[index + 1]
|
||||
if token.startswith("--config="):
|
||||
return token.split("=", 1)[1]
|
||||
if token.startswith("--cfg="):
|
||||
return token.split("=", 1)[1]
|
||||
return None
|
||||
|
||||
|
||||
def estimate_training_duration(repo_path: Path, command: str, max_train_steps: int) -> str:
|
||||
if max_train_steps > 0:
|
||||
if max_train_steps <= 200:
|
||||
return f"roughly minutes to under 1 hour for about {max_train_steps} steps, depending on dataset size and GPU throughput"
|
||||
if max_train_steps <= 5000:
|
||||
return f"roughly hours for about {max_train_steps} steps, depending on dataset size and GPU throughput"
|
||||
return f"likely many hours to multi-day for about {max_train_steps} steps, depending on dataset size and GPU throughput"
|
||||
|
||||
config_rel = extract_config_path(command)
|
||||
if config_rel:
|
||||
config_path = (repo_path / config_rel).resolve()
|
||||
if config_path.exists() and config_path.suffix.lower() in {".yaml", ".yml", ".json", ".toml", ".py"}:
|
||||
text_content = config_path.read_text(encoding="utf-8", errors="replace")
|
||||
step_match = None
|
||||
for key in ["max_steps", "total_steps", "train_steps", "num_steps"]:
|
||||
step_match = re.search(rf"{key}\s*[:=]\s*(\d+)", text_content, flags=re.IGNORECASE)
|
||||
if step_match:
|
||||
steps = int(step_match.group(1))
|
||||
if steps <= 200:
|
||||
return f"roughly minutes to under 1 hour from config-bound {steps} steps, depending on GPU throughput"
|
||||
if steps <= 5000:
|
||||
return f"roughly hours from config-bound {steps} steps, depending on GPU throughput"
|
||||
return f"likely many hours to multi-day from config-bound {steps} steps, depending on dataset size and GPU throughput"
|
||||
|
||||
epoch_match = None
|
||||
for key in ["epochs", "max_epochs", "num_epochs", "train_epochs"]:
|
||||
epoch_match = re.search(rf"{key}\s*[:=]\s*(\d+)", text_content, flags=re.IGNORECASE)
|
||||
if epoch_match:
|
||||
epochs = int(epoch_match.group(1))
|
||||
if epochs <= 3:
|
||||
return f"roughly minutes to under 1 hour for about {epochs} epochs, depending on dataset size and GPU throughput"
|
||||
if epochs <= 20:
|
||||
return f"roughly hours for about {epochs} epochs, depending on dataset size and GPU throughput"
|
||||
return f"likely many hours to multi-day for about {epochs} epochs, depending on dataset size and GPU throughput"
|
||||
|
||||
return "unknown; likely hours to multi-day on the full dataset until a bounded schedule is confirmed"
|
||||
|
||||
|
||||
def command_score(command: Dict[str, Any]) -> int:
|
||||
text_value = str(command.get("command", "")).lower()
|
||||
kind = command.get("kind", "run")
|
||||
@@ -316,6 +367,11 @@ def build_context(
|
||||
asset_commands = build_asset_commands(asset_data)
|
||||
dataset_hint = run_data.get("dataset") or derive_dataset_hint(asset_data)
|
||||
checkpoint_hint = run_data.get("checkpoint_source") or derive_checkpoint_hint(asset_data)
|
||||
training_duration_hint = (
|
||||
estimate_training_duration(repo_path, chosen["documented_command"], int(run_data.get("max_steps") or 0))
|
||||
if chosen["selected_goal"] == "training" and chosen["documented_command"]
|
||||
else None
|
||||
)
|
||||
|
||||
notes: List[str] = []
|
||||
notes.extend(scan_data.get("warnings", []))
|
||||
@@ -420,8 +476,8 @@ def build_context(
|
||||
if lane == "trusted" and not full_training_authorized:
|
||||
next_action = text(
|
||||
user_language,
|
||||
"Review `train_outputs/status.json` and decide whether to authorize a fuller training reproduction run.",
|
||||
"先检查 `train_outputs/status.json`,再决定是否授权更完整的训练复现。",
|
||||
f"Review `train_outputs/status.json`, then decide whether to authorize a fuller training reproduction run. Planned command: `{chosen['documented_command']}`. Estimated duration: {training_duration_hint}.",
|
||||
f"先检查 `train_outputs/status.json`,再决定是否授权更完整的训练复现。计划继续执行的命令是:`{chosen['documented_command']}`。保守预估时长:{training_duration_hint}。",
|
||||
)
|
||||
next_safe_action = "Keep the repo unchanged, review startup evidence, and only continue with fuller training after explicit researcher approval."
|
||||
elif lane == "explore":
|
||||
@@ -488,6 +544,8 @@ def build_context(
|
||||
]
|
||||
if chosen["selected_goal"] == "training":
|
||||
timeline.append(text(user_language, f"Training lane `{lane}` selected with run mode `{run_data.get('run_mode', 'startup_verification')}`.", f"已选择训练 lane `{lane}`,运行模式为 `{run_data.get('run_mode', 'startup_verification')}`。"))
|
||||
if training_duration_hint:
|
||||
timeline.append(text(user_language, f"Estimated fuller training duration: {training_duration_hint}.", f"保守估计完整训练时长:{training_duration_hint}。"))
|
||||
|
||||
artifact_provenance = [
|
||||
{"artifact": "readme", "source": scan_data.get("readme_path") or "not found", "kind": "repo_file"},
|
||||
@@ -548,6 +606,8 @@ def build_context(
|
||||
"resume_from": run_data.get("resume_from"),
|
||||
"dataset": dataset_hint,
|
||||
"checkpoint_source": checkpoint_hint,
|
||||
"full_training_command": chosen["documented_command"] if chosen["selected_goal"] == "training" else None,
|
||||
"training_duration_hint": training_duration_hint,
|
||||
"max_steps": run_data.get("max_steps"),
|
||||
"completed_steps": run_data.get("completed_steps"),
|
||||
"best_metric": run_data.get("best_metric"),
|
||||
|
||||
Reference in New Issue
Block a user