mirror of
https://github.com/microsoft/SkillOpt.git
synced 2026-07-07 00:15:39 +08:00
Initial commit
This commit is contained in:
20
scripts/codex_azure_mi.sh
Executable file
20
scripts/codex_azure_mi.sh
Executable file
@@ -0,0 +1,20 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ROOT="/home/azureuser/workspace-gzy/SkillReflection"
|
||||
PY="/home/azureuser/workspace-gzy/miniconda3/envs/reflact/bin/python"
|
||||
REAL_CODEX="/home/azureuser/.nvm/versions/node/v18.20.8/bin/codex"
|
||||
CLIENT_ID="8cafa2b1-a2a7-4ad9-814a-ffe4aed7e800"
|
||||
SCOPE="https://cognitiveservices.azure.com/.default"
|
||||
|
||||
token="$("$PY" - <<PY
|
||||
from azure.identity import ManagedIdentityCredential, get_bearer_token_provider
|
||||
cred = ManagedIdentityCredential(client_id="$CLIENT_ID")
|
||||
print(get_bearer_token_provider(cred, "$SCOPE")())
|
||||
PY
|
||||
)"
|
||||
|
||||
export CODEX_HOME="${CODEX_HOME:-$ROOT/.codex_azure}"
|
||||
export AZURE_OPENAI_AUTH_HEADER="Bearer $token"
|
||||
|
||||
exec "$REAL_CODEX" "$@"
|
||||
53
scripts/download_babyvision.py
Normal file
53
scripts/download_babyvision.py
Normal file
@@ -0,0 +1,53 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Download BabyVision from Hugging Face and convert it to local meta_data.jsonl + images/ format."""
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def parse_args() -> argparse.Namespace:
|
||||
p = argparse.ArgumentParser(description=__doc__)
|
||||
p.add_argument("--out_dir", type=str, required=True)
|
||||
p.add_argument("--dataset", type=str, default="UnipatAI/BabyVision")
|
||||
p.add_argument("--split", type=str, default="train")
|
||||
return p.parse_args()
|
||||
|
||||
|
||||
def main() -> None:
|
||||
args = parse_args()
|
||||
|
||||
try:
|
||||
from datasets import load_dataset
|
||||
except ImportError as exc: # pragma: no cover
|
||||
raise SystemExit("Please install `datasets` first: pip install datasets pillow") from exc
|
||||
|
||||
out_dir = Path(args.out_dir).resolve()
|
||||
images_dir = out_dir / "images"
|
||||
meta_path = out_dir / "meta_data.jsonl"
|
||||
images_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
dataset = load_dataset(args.dataset, split=args.split)
|
||||
with open(meta_path, "w", encoding="utf-8") as outf:
|
||||
for idx, row in enumerate(dataset):
|
||||
image = row.get("image")
|
||||
if image is None:
|
||||
continue
|
||||
task_id = str(row.get("taskId") or row.get("id") or idx + 1)
|
||||
image_name = f"{task_id}.png"
|
||||
image_path = images_dir / image_name
|
||||
image.save(image_path)
|
||||
|
||||
record = dict(row)
|
||||
record["image"] = image_name
|
||||
outf.write(json.dumps(record, ensure_ascii=False) + "\n")
|
||||
|
||||
print(f"Saved BabyVision to {out_dir}")
|
||||
print(f"Metadata: {meta_path}")
|
||||
print(f"Images: {images_dir}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
333
scripts/eval_livemathematicianbench_baseline.py
Normal file
333
scripts/eval_livemathematicianbench_baseline.py
Normal file
@@ -0,0 +1,333 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Evaluate LiveMathematicianBench under current or official-style prompts."""
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import os
|
||||
import random
|
||||
import re
|
||||
import sys
|
||||
import time
|
||||
from concurrent.futures import ThreadPoolExecutor, as_completed
|
||||
from pathlib import Path
|
||||
|
||||
_SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||
_PROJECT_ROOT = os.path.dirname(_SCRIPT_DIR)
|
||||
if _PROJECT_ROOT not in sys.path:
|
||||
sys.path.insert(0, _PROJECT_ROOT)
|
||||
|
||||
from reflact.envs.livemathematicianbench.dataloader import load_items
|
||||
from reflact.envs.livemathematicianbench.evaluator import evaluate as current_evaluate
|
||||
from reflact.envs.livemathematicianbench.rollout import _build_system, _build_user
|
||||
from reflact.model import (
|
||||
chat_with_deployment,
|
||||
configure_azure_openai,
|
||||
set_backend,
|
||||
set_reasoning_effort,
|
||||
)
|
||||
|
||||
_LABELS = ["A", "B", "C", "D", "E"]
|
||||
|
||||
|
||||
def parse_args() -> argparse.Namespace:
|
||||
p = argparse.ArgumentParser(description=__doc__)
|
||||
p.add_argument("--data_path", type=str, required=True)
|
||||
p.add_argument("--model", type=str, default="gpt-5.4")
|
||||
p.add_argument("--backend", type=str, choices=["azure_openai", "codex", "claude"], default="azure_openai")
|
||||
p.add_argument("--mode", type=str, choices=["current", "official"], required=True)
|
||||
p.add_argument("--reasoning_effort", type=str, default=None)
|
||||
p.add_argument("--azure_endpoint", type=str, default="")
|
||||
p.add_argument("--azure_api_version", type=str, default="")
|
||||
p.add_argument("--azure_api_key", type=str, default="")
|
||||
p.add_argument("--max_completion_tokens", type=int, default=0)
|
||||
p.add_argument("--workers", type=int, default=8)
|
||||
p.add_argument("--seed", type=int, default=20260227)
|
||||
p.add_argument("--skill_path", type=str, default="reflact/envs/livemathematicianbench/skills/initial.md")
|
||||
p.add_argument("--limit", type=int, default=0)
|
||||
p.add_argument("--resume", action="store_true")
|
||||
p.add_argument("--output_json", type=str, required=True)
|
||||
return p.parse_args()
|
||||
|
||||
def read_skill(skill_path: str) -> str:
|
||||
with open(skill_path, encoding="utf-8") as f:
|
||||
return f.read()
|
||||
|
||||
|
||||
def official_extract_answer(response_text: str) -> str | None:
|
||||
if not response_text:
|
||||
return None
|
||||
boxed_match = re.search(r"\\boxed\{([A-Ea-e])\}", response_text)
|
||||
if boxed_match:
|
||||
return boxed_match.group(1).upper()
|
||||
boxed_match = re.search(r"boxed\{([A-Ea-e])\}", response_text)
|
||||
if boxed_match:
|
||||
return boxed_match.group(1).upper()
|
||||
answer_match = re.search(r"answer is[:\s]*([A-Ea-e])", response_text, re.IGNORECASE)
|
||||
if answer_match:
|
||||
return answer_match.group(1).upper()
|
||||
answer_match = re.search(r"Answer[:\s]*\(?([A-Ea-e])\)?", response_text)
|
||||
if answer_match:
|
||||
return answer_match.group(1).upper()
|
||||
final_match = re.search(r"\b([A-Ea-e])\b\s*[.)]?\s*$", response_text.strip())
|
||||
if final_match:
|
||||
return final_match.group(1).upper()
|
||||
return None
|
||||
|
||||
|
||||
def official_format_mcq_prompt(question: str, choices: list[dict]) -> str:
|
||||
lines = [
|
||||
"Answer the following multiple-choice question.",
|
||||
"Think carefully, then provide your final answer in the format: \\boxed{X} where X is A, B, C, D, or E.",
|
||||
"",
|
||||
"Question:",
|
||||
question,
|
||||
"",
|
||||
"Choices:",
|
||||
]
|
||||
for choice in choices:
|
||||
lines.append(f"{choice['label']}. {choice['text']}")
|
||||
lines.append("")
|
||||
lines.append("Your answer:")
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
def shuffle_choices(item: dict, seed: int) -> tuple[list[dict], dict]:
|
||||
correct_choice = dict(item["correct_choice"])
|
||||
all_choices = [dict(choice) for choice in item["choices"]]
|
||||
rng = random.Random(f"{seed}:{item['id']}")
|
||||
rng.shuffle(all_choices)
|
||||
|
||||
shuffled: list[dict] = []
|
||||
new_correct = dict(correct_choice)
|
||||
correct_text = correct_choice["text"]
|
||||
|
||||
for idx, choice in enumerate(all_choices[: len(_LABELS)]):
|
||||
relabeled = {"label": _LABELS[idx], "text": choice["text"]}
|
||||
shuffled.append(relabeled)
|
||||
if choice["text"] == correct_text:
|
||||
new_correct = dict(relabeled)
|
||||
|
||||
return shuffled, new_correct
|
||||
|
||||
|
||||
def load_existing(output_path: Path) -> dict[str, dict]:
|
||||
if not output_path.exists():
|
||||
return {}
|
||||
with open(output_path, encoding="utf-8") as f:
|
||||
payload = json.load(f)
|
||||
existing = {}
|
||||
for row in payload.get("results", []):
|
||||
existing[str(row["id"])] = row
|
||||
return existing
|
||||
|
||||
|
||||
def save_results(output_path: Path, meta: dict, results: list[dict]) -> None:
|
||||
correct = sum(1 for row in results if row.get("is_correct"))
|
||||
total = len(results)
|
||||
payload = {
|
||||
**meta,
|
||||
"summary": {
|
||||
"correct": correct,
|
||||
"total": total,
|
||||
"accuracy": (correct / total) if total else 0.0,
|
||||
},
|
||||
"results": sorted(results, key=lambda row: str(row["id"])),
|
||||
}
|
||||
output_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
with open(output_path, "w", encoding="utf-8") as f:
|
||||
json.dump(payload, f, ensure_ascii=False, indent=2)
|
||||
|
||||
|
||||
def call_model(
|
||||
*,
|
||||
model: str,
|
||||
system: str,
|
||||
user: str,
|
||||
max_completion_tokens: int | None,
|
||||
reasoning_effort: str | None,
|
||||
) -> str:
|
||||
last_error: Exception | None = None
|
||||
for attempt in range(5):
|
||||
try:
|
||||
set_reasoning_effort(reasoning_effort)
|
||||
raw, _ = chat_with_deployment(
|
||||
deployment=model,
|
||||
system=system,
|
||||
user=user,
|
||||
max_completion_tokens=max_completion_tokens if max_completion_tokens and max_completion_tokens > 0 else 4096,
|
||||
retries=1,
|
||||
stage="rollout",
|
||||
)
|
||||
return str(raw or "")
|
||||
except Exception as exc: # noqa: BLE001
|
||||
last_error = exc
|
||||
if attempt == 4:
|
||||
break
|
||||
time.sleep(min(2 ** attempt, 10))
|
||||
raise RuntimeError(f"LLM call failed after retries: {last_error}")
|
||||
|
||||
|
||||
def evaluate_one(
|
||||
item: dict,
|
||||
*,
|
||||
mode: str,
|
||||
model: str,
|
||||
skill_content: str,
|
||||
max_completion_tokens: int,
|
||||
reasoning_effort: str | None,
|
||||
seed: int,
|
||||
) -> dict:
|
||||
shuffled_choices, correct_choice = shuffle_choices(item, seed)
|
||||
|
||||
if mode == "official":
|
||||
system = "You are an expert mathematician. Answer accurately."
|
||||
user = official_format_mcq_prompt(item["question"], shuffled_choices)
|
||||
effective_max_completion_tokens = max_completion_tokens if max_completion_tokens > 0 else None
|
||||
else:
|
||||
materialized = dict(item)
|
||||
materialized["choices"] = shuffled_choices
|
||||
materialized["correct_choice"] = correct_choice
|
||||
system = _build_system(skill_content)
|
||||
user = _build_user(materialized, use_theorem=False, use_sketch=False)
|
||||
effective_max_completion_tokens = max_completion_tokens if max_completion_tokens > 0 else 768
|
||||
|
||||
t0 = time.time()
|
||||
response = call_model(
|
||||
model=model,
|
||||
system=system,
|
||||
user=user,
|
||||
max_completion_tokens=effective_max_completion_tokens,
|
||||
reasoning_effort=reasoning_effort,
|
||||
)
|
||||
elapsed = time.time() - t0
|
||||
|
||||
if mode == "official":
|
||||
predicted = official_extract_answer(response)
|
||||
predicted_text = ""
|
||||
for choice in shuffled_choices:
|
||||
if choice["label"] == predicted:
|
||||
predicted_text = choice["text"]
|
||||
break
|
||||
is_correct = predicted == correct_choice["label"]
|
||||
return {
|
||||
"id": item["id"],
|
||||
"question": item["question"],
|
||||
"correct_label": correct_choice["label"],
|
||||
"correct_text": correct_choice["text"],
|
||||
"predicted_label": predicted,
|
||||
"predicted_text": predicted_text,
|
||||
"is_correct": is_correct,
|
||||
"elapsed_seconds": elapsed,
|
||||
"response": response,
|
||||
}
|
||||
|
||||
eval_result = current_evaluate(response, correct_choice, shuffled_choices)
|
||||
return {
|
||||
"id": item["id"],
|
||||
"question": item["question"],
|
||||
"correct_label": correct_choice["label"],
|
||||
"correct_text": correct_choice["text"],
|
||||
"predicted_label": eval_result["predicted_label"],
|
||||
"predicted_text": eval_result["predicted_text"],
|
||||
"is_correct": bool(eval_result["em"]),
|
||||
"elapsed_seconds": elapsed,
|
||||
"response": response,
|
||||
}
|
||||
|
||||
|
||||
def main() -> None:
|
||||
args = parse_args()
|
||||
set_backend(args.backend)
|
||||
configure_azure_openai(
|
||||
endpoint=args.azure_endpoint or None,
|
||||
api_version=args.azure_api_version or None,
|
||||
api_key=args.azure_api_key or None,
|
||||
)
|
||||
set_reasoning_effort(args.reasoning_effort)
|
||||
output_path = Path(args.output_json).resolve()
|
||||
skill_content = read_skill(args.skill_path) if args.mode == "current" else ""
|
||||
|
||||
items = load_items(args.data_path)
|
||||
if args.limit:
|
||||
items = items[:args.limit]
|
||||
|
||||
existing = load_existing(output_path) if args.resume else {}
|
||||
pending = [item for item in items if str(item["id"]) not in existing]
|
||||
results = list(existing.values())
|
||||
|
||||
print("=" * 72, flush=True)
|
||||
print("LiveMathematicianBench baseline eval", flush=True)
|
||||
print("=" * 72, flush=True)
|
||||
print(f"Mode: {args.mode}", flush=True)
|
||||
print(f"Model: {args.model}", flush=True)
|
||||
print(f"Reasoning effort: {args.reasoning_effort}", flush=True)
|
||||
print(f"Items: {len(items)} total, {len(pending)} pending, {len(existing)} resumed", flush=True)
|
||||
print(f"Output: {output_path}", flush=True)
|
||||
print("=" * 72, flush=True)
|
||||
|
||||
meta = {
|
||||
"mode": args.mode,
|
||||
"model": args.model,
|
||||
"reasoning_effort": args.reasoning_effort,
|
||||
"seed": args.seed,
|
||||
"max_completion_tokens": args.max_completion_tokens,
|
||||
}
|
||||
|
||||
if not pending:
|
||||
save_results(output_path, meta, results)
|
||||
summary = json.loads(output_path.read_text(encoding="utf-8"))["summary"]
|
||||
print(f"Accuracy: {summary['correct']}/{summary['total']} = {summary['accuracy']:.4f}", flush=True)
|
||||
return
|
||||
|
||||
with ThreadPoolExecutor(max_workers=args.workers) as ex:
|
||||
futs = {
|
||||
ex.submit(
|
||||
evaluate_one,
|
||||
item,
|
||||
mode=args.mode,
|
||||
model=args.model,
|
||||
skill_content=skill_content,
|
||||
max_completion_tokens=args.max_completion_tokens,
|
||||
reasoning_effort=args.reasoning_effort,
|
||||
seed=args.seed,
|
||||
): item
|
||||
for item in pending
|
||||
}
|
||||
completed = 0
|
||||
for fut in as_completed(futs):
|
||||
item = futs[fut]
|
||||
try:
|
||||
row = fut.result()
|
||||
except Exception as exc: # noqa: BLE001
|
||||
row = {
|
||||
"id": item["id"],
|
||||
"question": item["question"],
|
||||
"correct_label": None,
|
||||
"correct_text": item["correct_choice"]["text"],
|
||||
"predicted_label": None,
|
||||
"predicted_text": "",
|
||||
"is_correct": False,
|
||||
"elapsed_seconds": 0.0,
|
||||
"response": "",
|
||||
"error": str(exc),
|
||||
}
|
||||
results.append(row)
|
||||
completed += 1
|
||||
correct = sum(1 for result in results if result.get("is_correct"))
|
||||
total = len(results)
|
||||
print(
|
||||
f"[{completed}/{len(pending)}] id={row['id']} "
|
||||
f"pred={row['predicted_label']} gold={row['correct_label']} "
|
||||
f"acc={correct}/{total}={correct/total:.4f}",
|
||||
flush=True,
|
||||
)
|
||||
save_results(output_path, meta, results)
|
||||
|
||||
summary = json.loads(output_path.read_text(encoding="utf-8"))["summary"]
|
||||
print("=" * 72, flush=True)
|
||||
print(f"Accuracy: {summary['correct']}/{summary['total']} = {summary['accuracy']:.4f}", flush=True)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
451
scripts/eval_only.py
Normal file
451
scripts/eval_only.py
Normal file
@@ -0,0 +1,451 @@
|
||||
#!/usr/bin/env python3
|
||||
"""ReflACT eval-only: run a single skill on a dataset without training.
|
||||
|
||||
Usage
|
||||
-----
|
||||
python scripts/eval_only.py \
|
||||
--config configs/spreadsheetbench/default.yaml \
|
||||
--skill reflact/envs/spreadsheetbench/skills/initial.md \
|
||||
--split_dir /path/to/split \
|
||||
--out_root outputs/eval_skill0
|
||||
|
||||
All YAML keys can be overridden from the CLI, same as train.py.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import datetime
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
|
||||
_SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||
_PROJECT_ROOT = os.path.dirname(_SCRIPT_DIR)
|
||||
if _PROJECT_ROOT not in sys.path:
|
||||
sys.path.insert(0, _PROJECT_ROOT)
|
||||
|
||||
from reflact.model import (
|
||||
configure_azure_openai,
|
||||
configure_claude_code_exec,
|
||||
configure_codex_exec,
|
||||
set_reasoning_effort,
|
||||
set_student_backend,
|
||||
set_student_deployment,
|
||||
set_teacher_backend,
|
||||
set_teacher_deployment,
|
||||
)
|
||||
from reflact.model.common import default_model_for_backend, normalize_backend_name
|
||||
|
||||
_OPENAI_DEFAULT_MODEL_SENTINELS = {"gpt-5.4", "gpt-5.5"}
|
||||
from reflact.utils import compute_score
|
||||
|
||||
|
||||
# ── Reuse registry from train.py ───────────────────────────────────────────
|
||||
|
||||
_ENV_REGISTRY: dict[str, type] = {}
|
||||
|
||||
|
||||
def _register_builtins() -> None:
|
||||
try:
|
||||
from reflact.envs.alfworld.adapter import ALFWorldAdapter
|
||||
_ENV_REGISTRY["alfworld"] = ALFWorldAdapter
|
||||
except ImportError:
|
||||
pass
|
||||
try:
|
||||
from reflact.envs.searchqa.adapter import SearchQAAdapter
|
||||
_ENV_REGISTRY["searchqa"] = SearchQAAdapter
|
||||
except ImportError:
|
||||
pass
|
||||
try:
|
||||
from reflact.envs.livemathematicianbench.adapter import LiveMathematicianBenchAdapter
|
||||
_ENV_REGISTRY["livemathematicianbench"] = LiveMathematicianBenchAdapter
|
||||
except ImportError:
|
||||
pass
|
||||
try:
|
||||
from reflact.envs.babyvision.adapter import BabyVisionAdapter
|
||||
_ENV_REGISTRY["babyvision"] = BabyVisionAdapter
|
||||
except ImportError:
|
||||
pass
|
||||
try:
|
||||
from reflact.envs.spreadsheetbench.adapter import SpreadsheetBenchAdapter
|
||||
_ENV_REGISTRY["spreadsheetbench"] = SpreadsheetBenchAdapter
|
||||
except ImportError:
|
||||
pass
|
||||
try:
|
||||
from reflact.envs.mmrb.adapter import MMRBAdapter
|
||||
_ENV_REGISTRY["mmrb"] = MMRBAdapter
|
||||
except ImportError:
|
||||
pass
|
||||
try:
|
||||
from reflact.envs.docvqa.adapter import DocVQAAdapter
|
||||
_ENV_REGISTRY["docvqa"] = DocVQAAdapter
|
||||
except ImportError:
|
||||
pass
|
||||
try:
|
||||
from reflact.envs.mathverse.adapter import MathVerseAdapter
|
||||
_ENV_REGISTRY["mathverse"] = MathVerseAdapter
|
||||
except ImportError:
|
||||
pass
|
||||
try:
|
||||
from reflact.envs.officeqa.adapter import OfficeQAAdapter
|
||||
_ENV_REGISTRY["officeqa"] = OfficeQAAdapter
|
||||
except ImportError:
|
||||
pass
|
||||
try:
|
||||
from reflact.envs.sealqa.adapter import SealQAAdapter
|
||||
_ENV_REGISTRY["sealqa"] = SealQAAdapter
|
||||
except ImportError:
|
||||
pass
|
||||
try:
|
||||
from reflact.envs.swebench.adapter import SWEBenchAdapter
|
||||
_ENV_REGISTRY["swebench"] = SWEBenchAdapter
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
|
||||
def get_adapter(cfg: dict):
|
||||
_register_builtins()
|
||||
env_name = cfg.get("env", "alfworld")
|
||||
if env_name not in _ENV_REGISTRY:
|
||||
raise ValueError(
|
||||
f"Unknown environment '{env_name}'. "
|
||||
f"Available: {list(_ENV_REGISTRY.keys())}"
|
||||
)
|
||||
adapter_cls = _ENV_REGISTRY[env_name]
|
||||
|
||||
import inspect
|
||||
sig = inspect.signature(adapter_cls.__init__)
|
||||
accepted = set(sig.parameters.keys()) - {"self"}
|
||||
adapter_kwargs = {k: cfg[k] for k in accepted if k in cfg}
|
||||
return adapter_cls(**adapter_kwargs)
|
||||
|
||||
|
||||
# ── CLI ────────────────────────────────────────────────────────────────────
|
||||
|
||||
_BOOL = lambda x: str(x).lower() in ("true", "1", "yes") # noqa: E731
|
||||
|
||||
|
||||
def parse_args() -> argparse.Namespace:
|
||||
p = argparse.ArgumentParser(description="ReflACT eval-only")
|
||||
p.add_argument("--config", type=str, required=True)
|
||||
p.add_argument("--skill", type=str, required=True,
|
||||
help="Path to skill .md file to evaluate")
|
||||
p.add_argument("--split", type=str, default="all",
|
||||
help="Which split to eval: train/valid_seen/valid_unseen/all (default: all)")
|
||||
p.add_argument("--cfg-options", nargs="+", default=[],
|
||||
help="Override config: section.key=value")
|
||||
# Legacy flat overrides
|
||||
p.add_argument("--env", type=str)
|
||||
p.add_argument("--backend", type=str,
|
||||
choices=["azure_openai", "codex", "codex_exec", "claude", "claude_chat", "claude_code_exec"])
|
||||
p.add_argument("--teacher_model", type=str)
|
||||
p.add_argument("--student_model", type=str)
|
||||
p.add_argument("--teacher_backend", type=str)
|
||||
p.add_argument("--student_backend", type=str)
|
||||
p.add_argument("--reasoning_effort", type=str,
|
||||
choices=["", "low", "medium", "high", "xhigh", "max"])
|
||||
p.add_argument("--azure_endpoint", type=str)
|
||||
p.add_argument("--azure_api_version", type=str)
|
||||
p.add_argument("--azure_api_key", type=str)
|
||||
p.add_argument("--azure_openai_endpoint", type=str)
|
||||
p.add_argument("--azure_openai_api_version", type=str)
|
||||
p.add_argument("--azure_openai_api_key", type=str)
|
||||
p.add_argument("--azure_openai_auth_mode", type=str)
|
||||
p.add_argument("--azure_openai_ad_scope", type=str)
|
||||
p.add_argument("--azure_openai_managed_identity_client_id", type=str)
|
||||
p.add_argument("--teacher_azure_openai_endpoint", type=str)
|
||||
p.add_argument("--teacher_azure_openai_api_version", type=str)
|
||||
p.add_argument("--teacher_azure_openai_api_key", type=str)
|
||||
p.add_argument("--teacher_azure_openai_auth_mode", type=str)
|
||||
p.add_argument("--teacher_azure_openai_ad_scope", type=str)
|
||||
p.add_argument("--teacher_azure_openai_managed_identity_client_id", type=str)
|
||||
p.add_argument("--student_azure_openai_endpoint", type=str)
|
||||
p.add_argument("--student_azure_openai_api_version", type=str)
|
||||
p.add_argument("--student_azure_openai_api_key", type=str)
|
||||
p.add_argument("--student_azure_openai_auth_mode", type=str)
|
||||
p.add_argument("--student_azure_openai_ad_scope", type=str)
|
||||
p.add_argument("--student_azure_openai_managed_identity_client_id", type=str)
|
||||
p.add_argument("--codex_exec_path", type=str)
|
||||
p.add_argument("--codex_exec_sandbox", type=str)
|
||||
p.add_argument("--codex_exec_profile", type=str)
|
||||
p.add_argument("--codex_exec_full_auto", type=_BOOL)
|
||||
p.add_argument("--codex_exec_reasoning_effort", type=str)
|
||||
p.add_argument("--codex_exec_use_sdk", type=str)
|
||||
p.add_argument("--codex_exec_network_access", type=_BOOL)
|
||||
p.add_argument("--codex_exec_web_search", type=_BOOL)
|
||||
p.add_argument("--codex_exec_approval_policy", type=str)
|
||||
p.add_argument("--claude_code_exec_path", type=str)
|
||||
p.add_argument("--claude_code_exec_profile", type=str)
|
||||
p.add_argument("--claude_code_exec_use_sdk", type=str)
|
||||
p.add_argument("--claude_code_exec_effort", type=str)
|
||||
p.add_argument("--claude_code_exec_max_thinking_tokens", type=int)
|
||||
p.add_argument("--out_root", type=str)
|
||||
p.add_argument("--data_path", type=str)
|
||||
p.add_argument("--split_mode", type=str,
|
||||
choices=["ratio", "split_dir"])
|
||||
p.add_argument("--split_ratio", type=str)
|
||||
p.add_argument("--split_seed", type=int)
|
||||
p.add_argument("--split_dir", type=str)
|
||||
p.add_argument("--split_output_dir", type=str)
|
||||
p.add_argument("--data_root", type=str)
|
||||
p.add_argument("--max_turns", type=int)
|
||||
p.add_argument("--workers", type=int)
|
||||
p.add_argument("--max_api_workers", type=int)
|
||||
p.add_argument("--seed", type=int)
|
||||
p.add_argument("--test_env_num", type=int)
|
||||
p.add_argument("--mode", type=str,
|
||||
help="SpreadsheetBench: single/multi/react (default comes from config)")
|
||||
return p.parse_args()
|
||||
|
||||
|
||||
def main() -> None:
|
||||
args = parse_args()
|
||||
|
||||
from reflact.config import load_config as _load, flatten_config, is_structured
|
||||
|
||||
cfg = _load(args.config, overrides=args.cfg_options)
|
||||
structured = is_structured(cfg)
|
||||
|
||||
# Apply legacy --key value overrides
|
||||
cli = {k: v for k, v in vars(args).items()
|
||||
if v is not None and k not in ("config", "skill", "split", "cfg_options")}
|
||||
if cli:
|
||||
if structured:
|
||||
from reflact.config import apply_overrides
|
||||
_MAP = {
|
||||
"backend": "model.backend",
|
||||
"teacher_model": "model.teacher",
|
||||
"student_model": "model.student",
|
||||
"teacher_backend": "model.teacher_backend",
|
||||
"student_backend": "model.student_backend",
|
||||
"reasoning_effort": "model.reasoning_effort",
|
||||
"azure_endpoint": "model.azure_endpoint",
|
||||
"azure_api_version": "model.azure_api_version",
|
||||
"azure_api_key": "model.azure_api_key",
|
||||
"azure_openai_endpoint": "model.azure_openai_endpoint",
|
||||
"azure_openai_api_version": "model.azure_openai_api_version",
|
||||
"azure_openai_api_key": "model.azure_openai_api_key",
|
||||
"azure_openai_auth_mode": "model.azure_openai_auth_mode",
|
||||
"azure_openai_ad_scope": "model.azure_openai_ad_scope",
|
||||
"azure_openai_managed_identity_client_id": "model.azure_openai_managed_identity_client_id",
|
||||
"teacher_azure_openai_endpoint": "model.teacher_azure_openai_endpoint",
|
||||
"teacher_azure_openai_api_version": "model.teacher_azure_openai_api_version",
|
||||
"teacher_azure_openai_api_key": "model.teacher_azure_openai_api_key",
|
||||
"teacher_azure_openai_auth_mode": "model.teacher_azure_openai_auth_mode",
|
||||
"teacher_azure_openai_ad_scope": "model.teacher_azure_openai_ad_scope",
|
||||
"teacher_azure_openai_managed_identity_client_id": "model.teacher_azure_openai_managed_identity_client_id",
|
||||
"student_azure_openai_endpoint": "model.student_azure_openai_endpoint",
|
||||
"student_azure_openai_api_version": "model.student_azure_openai_api_version",
|
||||
"student_azure_openai_api_key": "model.student_azure_openai_api_key",
|
||||
"student_azure_openai_auth_mode": "model.student_azure_openai_auth_mode",
|
||||
"student_azure_openai_ad_scope": "model.student_azure_openai_ad_scope",
|
||||
"student_azure_openai_managed_identity_client_id": "model.student_azure_openai_managed_identity_client_id",
|
||||
"codex_exec_path": "model.codex_exec_path",
|
||||
"codex_exec_sandbox": "model.codex_exec_sandbox",
|
||||
"codex_exec_profile": "model.codex_exec_profile",
|
||||
"codex_exec_full_auto": "model.codex_exec_full_auto",
|
||||
"codex_exec_reasoning_effort": "model.codex_exec_reasoning_effort",
|
||||
"codex_exec_use_sdk": "model.codex_exec_use_sdk",
|
||||
"codex_exec_network_access": "model.codex_exec_network_access",
|
||||
"codex_exec_web_search": "model.codex_exec_web_search",
|
||||
"codex_exec_approval_policy": "model.codex_exec_approval_policy",
|
||||
"claude_code_exec_path": "model.claude_code_exec_path",
|
||||
"claude_code_exec_profile": "model.claude_code_exec_profile",
|
||||
"claude_code_exec_use_sdk": "model.claude_code_exec_use_sdk",
|
||||
"claude_code_exec_effort": "model.claude_code_exec_effort",
|
||||
"claude_code_exec_max_thinking_tokens": "model.claude_code_exec_max_thinking_tokens",
|
||||
"seed": "train.seed",
|
||||
"test_env_num": "evaluation.test_env_num",
|
||||
"env": "env.name",
|
||||
"out_root": "env.out_root",
|
||||
}
|
||||
mapped = []
|
||||
for k, v in cli.items():
|
||||
dotted = _MAP.get(k)
|
||||
if dotted:
|
||||
mapped.append(f"{dotted}={v}")
|
||||
else:
|
||||
mapped.append(f"env.{k}={v}")
|
||||
apply_overrides(cfg, mapped)
|
||||
else:
|
||||
cfg.update(cli)
|
||||
|
||||
cfg = flatten_config(cfg) if structured else cfg
|
||||
|
||||
for new_key, old_key in (
|
||||
("azure_openai_endpoint", "azure_endpoint"),
|
||||
("azure_openai_api_version", "azure_api_version"),
|
||||
("azure_openai_api_key", "azure_api_key"),
|
||||
):
|
||||
if cfg.get(new_key) in (None, "") and cfg.get(old_key) not in (None, ""):
|
||||
cfg[new_key] = cfg[old_key]
|
||||
|
||||
explicit_backend = getattr(args, "backend", None)
|
||||
if explicit_backend is None:
|
||||
for option in args.cfg_options or []:
|
||||
key = str(option).split("=", 1)[0].strip()
|
||||
if key == "model.backend":
|
||||
explicit_backend = str(option).split("=", 1)[1].strip()
|
||||
break
|
||||
|
||||
backend = normalize_backend_name(cfg.get("model_backend") or cfg.get("student_backend") or "azure_openai")
|
||||
|
||||
def _has_model_override(dotted_key: str, legacy_key: str) -> bool:
|
||||
if getattr(args, legacy_key, None) is not None:
|
||||
return True
|
||||
for option in args.cfg_options or []:
|
||||
key = str(option).split("=", 1)[0].strip()
|
||||
if key == dotted_key:
|
||||
return True
|
||||
return False
|
||||
|
||||
if explicit_backend is not None:
|
||||
backend = normalize_backend_name(explicit_backend)
|
||||
cfg["model_backend"] = backend
|
||||
if backend in {"claude", "claude_chat"}:
|
||||
cfg.setdefault("teacher_backend", "claude_chat")
|
||||
cfg.setdefault("student_backend", "claude_chat")
|
||||
elif backend in {"codex", "codex_exec"}:
|
||||
cfg.setdefault("teacher_backend", "openai_chat")
|
||||
cfg.setdefault("student_backend", "codex_exec")
|
||||
elif backend == "claude_code_exec":
|
||||
cfg.setdefault("teacher_backend", "openai_chat")
|
||||
cfg.setdefault("student_backend", "claude_code_exec")
|
||||
else:
|
||||
cfg.setdefault("teacher_backend", "openai_chat")
|
||||
cfg.setdefault("student_backend", "openai_chat")
|
||||
else:
|
||||
cfg.setdefault("teacher_backend", "openai_chat")
|
||||
cfg.setdefault("student_backend", "openai_chat")
|
||||
|
||||
if cfg.get("teacher_backend") == "claude_chat":
|
||||
if (
|
||||
str(cfg.get("teacher_model", "") or "").strip() in _OPENAI_DEFAULT_MODEL_SENTINELS
|
||||
and not _has_model_override("model.teacher", "teacher_model")
|
||||
):
|
||||
cfg["teacher_model"] = default_model_for_backend("claude_chat")
|
||||
if cfg.get("student_backend") == "claude_chat":
|
||||
if (
|
||||
str(cfg.get("student_model", "") or "").strip() in _OPENAI_DEFAULT_MODEL_SENTINELS
|
||||
and not _has_model_override("model.student", "student_model")
|
||||
):
|
||||
cfg["student_model"] = default_model_for_backend("claude_chat")
|
||||
if cfg.get("student_backend") == "claude_code_exec":
|
||||
if (
|
||||
str(cfg.get("student_model", "") or "").strip() in _OPENAI_DEFAULT_MODEL_SENTINELS
|
||||
and not _has_model_override("model.student", "student_model")
|
||||
):
|
||||
cfg["student_model"] = default_model_for_backend("claude_chat")
|
||||
|
||||
if not cfg.get("out_root"):
|
||||
env = cfg.get("env", "unknown")
|
||||
model = cfg.get("student_model", "unknown").replace("/", "-")
|
||||
ts = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
|
||||
cfg["out_root"] = os.path.join("outputs", f"eval_{env}_{model}_{ts}")
|
||||
|
||||
cfg["out_root"] = os.path.abspath(cfg["out_root"])
|
||||
|
||||
out_root = cfg["out_root"]
|
||||
os.makedirs(out_root, exist_ok=True)
|
||||
|
||||
# Load skill
|
||||
skill_path = os.path.abspath(args.skill)
|
||||
with open(skill_path) as f:
|
||||
skill_content = f.read()
|
||||
print(f" [skill] {skill_path} ({len(skill_content)} chars)")
|
||||
|
||||
# Configure models
|
||||
configure_azure_openai(
|
||||
endpoint=(cfg.get("azure_openai_endpoint") or cfg.get("azure_endpoint") or None),
|
||||
api_version=(cfg.get("azure_openai_api_version") or cfg.get("azure_api_version") or None),
|
||||
api_key=(cfg.get("azure_openai_api_key") or cfg.get("azure_api_key") or None),
|
||||
auth_mode=cfg.get("azure_openai_auth_mode") or None,
|
||||
ad_scope=cfg.get("azure_openai_ad_scope") or None,
|
||||
managed_identity_client_id=cfg.get("azure_openai_managed_identity_client_id") or None,
|
||||
teacher_endpoint=cfg.get("teacher_azure_openai_endpoint") or None,
|
||||
teacher_api_version=cfg.get("teacher_azure_openai_api_version") or None,
|
||||
teacher_api_key=cfg.get("teacher_azure_openai_api_key") or None,
|
||||
teacher_auth_mode=cfg.get("teacher_azure_openai_auth_mode") or None,
|
||||
teacher_ad_scope=cfg.get("teacher_azure_openai_ad_scope") or None,
|
||||
teacher_managed_identity_client_id=(
|
||||
cfg.get("teacher_azure_openai_managed_identity_client_id") or None
|
||||
),
|
||||
student_endpoint=cfg.get("student_azure_openai_endpoint") or None,
|
||||
student_api_version=cfg.get("student_azure_openai_api_version") or None,
|
||||
student_api_key=cfg.get("student_azure_openai_api_key") or None,
|
||||
student_auth_mode=cfg.get("student_azure_openai_auth_mode") or None,
|
||||
student_ad_scope=cfg.get("student_azure_openai_ad_scope") or None,
|
||||
student_managed_identity_client_id=(
|
||||
cfg.get("student_azure_openai_managed_identity_client_id") or None
|
||||
),
|
||||
)
|
||||
set_teacher_backend(cfg.get("teacher_backend", "openai_chat"))
|
||||
set_student_backend(cfg.get("student_backend", "openai_chat"))
|
||||
set_teacher_deployment(cfg.get("teacher_model", default_model_for_backend(backend)))
|
||||
set_student_deployment(cfg.get("student_model", default_model_for_backend(backend)))
|
||||
configure_codex_exec(
|
||||
path=cfg.get("codex_exec_path", "codex"),
|
||||
sandbox=cfg.get("codex_exec_sandbox", "workspace-write"),
|
||||
profile=cfg.get("codex_exec_profile", ""),
|
||||
full_auto=cfg.get("codex_exec_full_auto", False),
|
||||
reasoning_effort=cfg.get("codex_exec_reasoning_effort", "none"),
|
||||
use_sdk=cfg.get("codex_exec_use_sdk", None),
|
||||
network_access=cfg.get("codex_exec_network_access", False),
|
||||
web_search=cfg.get("codex_exec_web_search", False),
|
||||
approval_policy=cfg.get("codex_exec_approval_policy", "never"),
|
||||
)
|
||||
configure_claude_code_exec(
|
||||
path=cfg.get("claude_code_exec_path", "claude"),
|
||||
profile=cfg.get("claude_code_exec_profile", ""),
|
||||
use_sdk=cfg.get("claude_code_exec_use_sdk", None),
|
||||
effort=cfg.get("claude_code_exec_effort", cfg.get("reasoning_effort", "medium")),
|
||||
max_thinking_tokens=cfg.get("claude_code_exec_max_thinking_tokens", 16384),
|
||||
)
|
||||
set_reasoning_effort(cfg.get("reasoning_effort", "") or None)
|
||||
|
||||
# Build adapter
|
||||
adapter = get_adapter(cfg)
|
||||
adapter.setup(cfg)
|
||||
|
||||
seed = cfg.get("seed", 42)
|
||||
split = args.split or "all"
|
||||
|
||||
if split == "all":
|
||||
items = (
|
||||
adapter.build_eval_env(0, "train", seed)
|
||||
+ adapter.build_eval_env(0, "valid_seen", seed)
|
||||
+ adapter.build_eval_env(0, "valid_unseen", seed)
|
||||
)
|
||||
else:
|
||||
env_num = cfg.get("test_env_num", 0)
|
||||
items = adapter.build_eval_env(env_num, split, seed)
|
||||
|
||||
print(f"\n [eval] split={split} items={len(items)}")
|
||||
print(f" [eval] out_root={out_root}")
|
||||
print(f"{'='*60}")
|
||||
|
||||
# Run rollout
|
||||
results = adapter.rollout(items, skill_content, out_root)
|
||||
|
||||
# Score
|
||||
hard, soft = compute_score(results)
|
||||
print(f"\n{'='*60}")
|
||||
print(f" Results: hard={hard:.4f} soft={soft:.4f} (n={len(results)})")
|
||||
print(f"{'='*60}")
|
||||
|
||||
# Save summary
|
||||
summary = {
|
||||
"skill": skill_path,
|
||||
"split": split,
|
||||
"n_items": len(results),
|
||||
"hard": hard,
|
||||
"soft": soft,
|
||||
}
|
||||
with open(os.path.join(out_root, "eval_summary.json"), "w") as f:
|
||||
json.dump(summary, f, indent=2, ensure_ascii=False)
|
||||
|
||||
print(f" Saved to: {out_root}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
361
scripts/eval_prompt_custom.py
Normal file
361
scripts/eval_prompt_custom.py
Normal file
@@ -0,0 +1,361 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Standalone eval: CUSTOM prompt (with Critical Rules) on verified-400.
|
||||
|
||||
Usage:
|
||||
python scripts/eval_prompt_custom.py --workers 8
|
||||
python scripts/eval_prompt_custom.py --workers 32 --limit 20
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import glob
|
||||
import json
|
||||
import os
|
||||
import random
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
import textwrap
|
||||
import time
|
||||
import traceback
|
||||
from concurrent.futures import ThreadPoolExecutor, as_completed, TimeoutError as FuturesTimeoutError
|
||||
|
||||
import openpyxl
|
||||
|
||||
_SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||
_PROJECT_ROOT = os.path.dirname(_SCRIPT_DIR)
|
||||
if _PROJECT_ROOT not in sys.path:
|
||||
sys.path.insert(0, _PROJECT_ROOT)
|
||||
|
||||
from reflact.model import (
|
||||
chat_messages_with_deployment,
|
||||
configure_azure_openai,
|
||||
set_backend,
|
||||
set_student_deployment,
|
||||
)
|
||||
from reflact.envs.spreadsheetbench.evaluator import evaluate
|
||||
|
||||
|
||||
# ── Config ──────────────────────────────────────────────────────────────────
|
||||
|
||||
DATA_ROOT = "/home/azureuser/workspace-yqh/sr/spreadsheetbench/data/spreadsheetbench_verified_400"
|
||||
JSONL_PATH = os.path.join(DATA_ROOT, "dataset.json")
|
||||
MODEL = "gpt-5-mini"
|
||||
|
||||
# ── Custom Prompt (with Critical Rules) ─────────────────────────────────────
|
||||
|
||||
_SYSTEM_TEMPLATE = """\
|
||||
You are an expert Python programmer specializing in spreadsheet manipulation.
|
||||
You will be given a user instruction together with a preview of an input .xlsx file.
|
||||
Your job is to write a single self-contained Python script that reads the input file
|
||||
at the path stored in the variable INPUT_PATH, performs the requested manipulation,
|
||||
and saves the result to OUTPUT_PATH.
|
||||
|
||||
## Critical Rules
|
||||
1. NEVER write Excel formulas to cells. openpyxl does NOT compute formulas —
|
||||
the evaluator will see None. Compute results in Python and write literal values.
|
||||
2. Use only: standard library, openpyxl, pandas.
|
||||
3. Do NOT hardcode cell values from the preview — iterate over actual rows.
|
||||
4. The script must define INPUT_PATH and OUTPUT_PATH at the top.
|
||||
|
||||
{skill_section}\
|
||||
Return ONLY the Python code inside a single ```python ... ``` fenced block.
|
||||
"""
|
||||
|
||||
|
||||
def build_system(skill_content: str = "") -> str:
|
||||
if skill_content.strip():
|
||||
skill_section = f"## Skill\n{skill_content.strip()}\n\n"
|
||||
else:
|
||||
skill_section = ""
|
||||
return _SYSTEM_TEMPLATE.format(skill_section=skill_section)
|
||||
|
||||
|
||||
def build_user(instruction, input_xlsx, instruction_type="", answer_position=""):
|
||||
try:
|
||||
preview = _preview_workbook(input_xlsx)
|
||||
except Exception as e:
|
||||
preview = f"(failed to preview: {e})"
|
||||
extra = ""
|
||||
if instruction_type:
|
||||
extra += f"\nInstruction type: {instruction_type}"
|
||||
if answer_position:
|
||||
extra += f"\nExpected answer position: {answer_position}"
|
||||
return (
|
||||
f"# Instruction\n{instruction}\n{extra}\n\n"
|
||||
f"# Input spreadsheet preview\n{preview}\n\n"
|
||||
"# Task\n"
|
||||
"Write a Python script that reads the workbook from the variable `INPUT_PATH`, "
|
||||
"applies the instruction, and writes the modified workbook to `OUTPUT_PATH`. "
|
||||
"Preserve all other cells unchanged. "
|
||||
"The preview may be truncated — do not hardcode row counts; "
|
||||
"iterate over all actual rows in the workbook instead.\n"
|
||||
"Return only a ```python``` code block."
|
||||
)
|
||||
|
||||
|
||||
# ── Shared utilities ────────────────────────────────────────────────────────
|
||||
|
||||
def _preview_workbook(path, max_rows=5, max_cols=20):
|
||||
wb = openpyxl.load_workbook(path, data_only=False)
|
||||
chunks = []
|
||||
for sn in wb.sheetnames:
|
||||
ws = wb[sn]
|
||||
chunks.append(f"## Sheet: {sn} (dim={ws.dimensions}, max_row={ws.max_row}, max_col={ws.max_column})")
|
||||
for row in ws.iter_rows(min_row=1, max_row=min(ws.max_row, max_rows),
|
||||
max_col=min(ws.max_column, max_cols), values_only=False):
|
||||
cells = []
|
||||
for c in row:
|
||||
v = c.value
|
||||
s = "" if v is None else str(v)
|
||||
if len(s) > 40: s = s[:37] + "..."
|
||||
cells.append(f"{c.coordinate}={s}")
|
||||
chunks.append(" | ".join(cells))
|
||||
if ws.max_row > max_rows:
|
||||
chunks.append(f"... ({ws.max_row - max_rows} more rows)")
|
||||
chunks.append("")
|
||||
wb.close()
|
||||
return "\n".join(chunks)
|
||||
|
||||
|
||||
def extract_code(text):
|
||||
if "```" not in text:
|
||||
return text.strip()
|
||||
start = text.find("```")
|
||||
nl = text.find("\n", start)
|
||||
end = text.find("```", nl + 1)
|
||||
if nl == -1 or end == -1:
|
||||
return text.strip()
|
||||
return text[nl + 1:end].strip()
|
||||
|
||||
|
||||
_PATH_RE = re.compile(r'^\s*(INPUT_PATH|OUTPUT_PATH)\s*=\s*.+$', re.MULTILINE)
|
||||
|
||||
def strip_paths(code):
|
||||
return _PATH_RE.sub("", code)
|
||||
|
||||
|
||||
RUNNER_TEMPLATE = textwrap.dedent("""
|
||||
import os, sys, traceback
|
||||
INPUT_PATH = {input_path!r}
|
||||
OUTPUT_PATH = {output_path!r}
|
||||
try:
|
||||
{code_indented}
|
||||
except Exception:
|
||||
traceback.print_exc()
|
||||
sys.exit(2)
|
||||
""")
|
||||
|
||||
|
||||
def run_code(code, input_path, output_path, timeout=120):
|
||||
os.makedirs(os.path.dirname(output_path), exist_ok=True)
|
||||
cleaned = strip_paths(code)
|
||||
indented = textwrap.indent(cleaned, " ")
|
||||
script = RUNNER_TEMPLATE.format(input_path=input_path, output_path=output_path, code_indented=indented)
|
||||
with tempfile.NamedTemporaryFile("w", suffix=".py", delete=False) as f:
|
||||
f.write(script)
|
||||
tmp = f.name
|
||||
try:
|
||||
proc = subprocess.run([sys.executable, tmp], capture_output=True, text=True, timeout=timeout)
|
||||
if proc.returncode != 0:
|
||||
return False, (proc.stdout + "\n" + proc.stderr).strip()
|
||||
if not os.path.exists(output_path):
|
||||
return False, "output file was not created"
|
||||
return True, ""
|
||||
except subprocess.TimeoutExpired:
|
||||
return False, f"timeout after {timeout}s"
|
||||
finally:
|
||||
try: os.unlink(tmp)
|
||||
except OSError: pass
|
||||
|
||||
|
||||
def find_test_cases(task_dir):
|
||||
cases = []
|
||||
for ip in sorted(glob.glob(os.path.join(task_dir, "*_input.xlsx"))):
|
||||
no = os.path.basename(ip).split("_", 1)[0]
|
||||
ap = ip.replace("_input.xlsx", "_answer.xlsx")
|
||||
if os.path.exists(ap): cases.append((no, ip, ap))
|
||||
for ip in sorted(glob.glob(os.path.join(task_dir, "*_init.xlsx"))):
|
||||
no = os.path.basename(ip).split("_", 1)[0]
|
||||
ap = ip.replace("_init.xlsx", "_golden.xlsx")
|
||||
if os.path.exists(ap): cases.append((no, ip, ap))
|
||||
if not cases:
|
||||
bare_init = os.path.join(task_dir, "initial.xlsx")
|
||||
bare_gold = os.path.join(task_dir, "golden.xlsx")
|
||||
if os.path.exists(bare_init) and os.path.exists(bare_gold):
|
||||
cases.append(("1", bare_init, bare_gold))
|
||||
return cases
|
||||
|
||||
|
||||
def load_items(path):
|
||||
if path.endswith(".json"):
|
||||
with open(path) as f:
|
||||
data = json.load(f)
|
||||
if isinstance(data, dict):
|
||||
data = data.get("data") or list(data.values())
|
||||
return list(data)
|
||||
items = []
|
||||
with open(path) as f:
|
||||
for line in f:
|
||||
line = line.strip()
|
||||
if line: items.append(json.loads(line))
|
||||
return items
|
||||
|
||||
|
||||
# ── LLM call ────────────────────────────────────────────────────────────────
|
||||
|
||||
def llm_call(messages, deployment, max_tokens=16384, retries=5, llm_timeout=120):
|
||||
raw, _ = chat_messages_with_deployment(
|
||||
deployment=deployment,
|
||||
messages=messages,
|
||||
max_completion_tokens=max_tokens,
|
||||
retries=retries,
|
||||
stage="rollout",
|
||||
timeout=llm_timeout,
|
||||
)
|
||||
return str(raw or "")
|
||||
|
||||
|
||||
# ── Process one task ────────────────────────────────────────────────────────
|
||||
|
||||
def process_one(item, data_root, out_root, model):
|
||||
task_id = str(item["id"])
|
||||
instruction = item["instruction"]
|
||||
instruction_type = item.get("instruction_type", "")
|
||||
answer_position = item.get("answer_position", "")
|
||||
answer_sheet = item.get("answer_sheet", "")
|
||||
if answer_position and answer_sheet and "!" not in answer_position:
|
||||
answer_position = f"{answer_sheet}!{answer_position}"
|
||||
|
||||
sp = item.get("spreadsheet_path", f"spreadsheet/{task_id}")
|
||||
task_dir = sp if os.path.isabs(sp) else os.path.join(data_root, sp)
|
||||
|
||||
result = {"id": task_id, "ok": False, "hard": 0, "soft": 0.0,
|
||||
"n_cases": 0, "n_pass": 0, "fail_reason": "", "error": ""}
|
||||
try:
|
||||
cases = find_test_cases(task_dir)
|
||||
result["n_cases"] = len(cases)
|
||||
if not cases:
|
||||
result["fail_reason"] = "no-test-cases"
|
||||
return result
|
||||
|
||||
task_out = os.path.join(out_root, "predictions", task_id)
|
||||
os.makedirs(task_out, exist_ok=True)
|
||||
|
||||
# LLM call
|
||||
system = build_system("")
|
||||
user = build_user(instruction, cases[0][1], instruction_type, answer_position)
|
||||
messages = [{"role": "system", "content": system}, {"role": "user", "content": user}]
|
||||
|
||||
raw = llm_call(messages, model)
|
||||
time.sleep(3)
|
||||
code = extract_code(raw)
|
||||
|
||||
with open(os.path.join(task_out, "code.py"), "w") as f: f.write(code)
|
||||
with open(os.path.join(task_out, "raw.txt"), "w") as f: f.write(raw)
|
||||
|
||||
if not code.strip():
|
||||
result["fail_reason"] = "empty-code"
|
||||
return result
|
||||
|
||||
# Execute + evaluate each test case
|
||||
for no, ip, ap in cases:
|
||||
pred = os.path.join(task_out, f"{no}_pred.xlsx")
|
||||
ok_exec, err = run_code(code, ip, pred)
|
||||
if not ok_exec:
|
||||
if not result["fail_reason"]:
|
||||
result["fail_reason"] = f"exec: {err[:200]}"
|
||||
continue
|
||||
try:
|
||||
ev = evaluate(pred, ap, instruction_type, answer_position)
|
||||
except Exception as e:
|
||||
ev = {"ok": False, "reason": str(e)}
|
||||
if ev["ok"]:
|
||||
result["n_pass"] += 1
|
||||
|
||||
nc, np = result["n_cases"], result["n_pass"]
|
||||
result["soft"] = np / nc if nc else 0.0
|
||||
result["hard"] = 1 if nc > 0 and np == nc else 0
|
||||
result["ok"] = bool(result["hard"])
|
||||
if result["ok"]: result["fail_reason"] = ""
|
||||
return result
|
||||
except Exception as e:
|
||||
result["fail_reason"] = f"unexpected: {e}"
|
||||
result["error"] = traceback.format_exc()
|
||||
return result
|
||||
|
||||
|
||||
# ── Main ────────────────────────────────────────────────────────────────────
|
||||
|
||||
def main():
|
||||
ap = argparse.ArgumentParser(description="Eval CUSTOM prompt on verified-400")
|
||||
ap.add_argument("--model", default=MODEL)
|
||||
ap.add_argument("--backend", choices=["azure_openai", "codex", "claude"], default="azure_openai")
|
||||
ap.add_argument("--azure_endpoint", default="")
|
||||
ap.add_argument("--azure_api_version", default="")
|
||||
ap.add_argument("--azure_api_key", default="")
|
||||
ap.add_argument("--workers", type=int, default=8)
|
||||
ap.add_argument("--limit", type=int, default=0)
|
||||
ap.add_argument("--out_root", default="")
|
||||
args = ap.parse_args()
|
||||
|
||||
set_backend(args.backend)
|
||||
configure_azure_openai(
|
||||
endpoint=args.azure_endpoint or None,
|
||||
api_version=args.azure_api_version or None,
|
||||
api_key=args.azure_api_key or None,
|
||||
)
|
||||
set_student_deployment(args.model)
|
||||
ts = time.strftime("%Y%m%d_%H%M%S")
|
||||
out_root = args.out_root or os.path.join(_PROJECT_ROOT, "outputs", f"prompt_custom_{args.model}_{ts}")
|
||||
out_root = os.path.abspath(out_root)
|
||||
os.makedirs(out_root, exist_ok=True)
|
||||
|
||||
items = load_items(JSONL_PATH)
|
||||
if args.limit: items = items[:args.limit]
|
||||
|
||||
print(f"{'='*60}")
|
||||
print(f" Prompt: CUSTOM (Critical Rules)")
|
||||
print(f" Model: {args.model}")
|
||||
print(f" Items: {len(items)}")
|
||||
print(f" Output: {out_root}")
|
||||
print(f"{'='*60}")
|
||||
|
||||
t0 = time.time()
|
||||
results = []
|
||||
with ThreadPoolExecutor(max_workers=args.workers) as ex:
|
||||
futs = {ex.submit(process_one, it, DATA_ROOT, out_root, args.model): it for it in items}
|
||||
for i, fut in enumerate(as_completed(futs), 1):
|
||||
item = futs[fut]
|
||||
try:
|
||||
res = fut.result(timeout=300)
|
||||
except FuturesTimeoutError:
|
||||
res = {"id": str(item["id"]), "ok": False, "hard": 0, "soft": 0.0,
|
||||
"n_cases": 0, "n_pass": 0, "fail_reason": "timeout"}
|
||||
except Exception as e:
|
||||
res = {"id": str(item["id"]), "ok": False, "hard": 0, "soft": 0.0,
|
||||
"n_cases": 0, "n_pass": 0, "fail_reason": str(e)}
|
||||
results.append(res)
|
||||
status = "PASS" if res.get("hard") else "FAIL"
|
||||
dt = time.time() - t0
|
||||
print(f" {i}/{len(items)} id={res['id']:<10} {status} cases={res.get('n_pass',0)}/{res.get('n_cases',0)} dt={dt:.0f}s")
|
||||
|
||||
# Summary
|
||||
hard_sum = sum(r.get("hard", 0) for r in results)
|
||||
soft_sum = sum(r.get("soft", 0.0) for r in results)
|
||||
n = len(results)
|
||||
print(f"\n{'='*60}")
|
||||
print(f" CUSTOM prompt: hard={hard_sum}/{n}={hard_sum/n:.4f} soft={soft_sum/n:.4f}")
|
||||
print(f"{'='*60}")
|
||||
|
||||
with open(os.path.join(out_root, "results.jsonl"), "w") as f:
|
||||
for r in results:
|
||||
f.write(json.dumps(r, ensure_ascii=False) + "\n")
|
||||
with open(os.path.join(out_root, "summary.json"), "w") as f:
|
||||
json.dump({"prompt": "custom", "model": args.model, "n": n,
|
||||
"hard": hard_sum/n, "soft": soft_sum/n}, f, indent=2)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
352
scripts/eval_prompt_official.py
Normal file
352
scripts/eval_prompt_official.py
Normal file
@@ -0,0 +1,352 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Standalone eval: OFFICIAL prompt (SpreadsheetBench original) on verified-400.
|
||||
|
||||
Usage:
|
||||
python scripts/eval_prompt_official.py --workers 8
|
||||
python scripts/eval_prompt_official.py --workers 32 --limit 20
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import glob
|
||||
import json
|
||||
import os
|
||||
import random
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
import textwrap
|
||||
import time
|
||||
import traceback
|
||||
from concurrent.futures import ThreadPoolExecutor, as_completed, TimeoutError as FuturesTimeoutError
|
||||
|
||||
import openpyxl
|
||||
|
||||
_SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||
_PROJECT_ROOT = os.path.dirname(_SCRIPT_DIR)
|
||||
if _PROJECT_ROOT not in sys.path:
|
||||
sys.path.insert(0, _PROJECT_ROOT)
|
||||
|
||||
from reflact.model import (
|
||||
chat_messages_with_deployment,
|
||||
configure_azure_openai,
|
||||
set_backend,
|
||||
set_student_deployment,
|
||||
)
|
||||
from reflact.envs.spreadsheetbench.evaluator import evaluate
|
||||
|
||||
|
||||
# ── Config ──────────────────────────────────────────────────────────────────
|
||||
|
||||
DATA_ROOT = "/home/azureuser/workspace-yqh/sr/spreadsheetbench/data/spreadsheetbench_verified_400"
|
||||
JSONL_PATH = os.path.join(DATA_ROOT, "dataset.json")
|
||||
MODEL = "gpt-5-mini"
|
||||
|
||||
# ── Official Prompt (from SpreadsheetBench src/prompt.py) ───────────────────
|
||||
|
||||
_SYSTEM_PROMPT = (
|
||||
"You are an expert Python programmer specializing in spreadsheet manipulation. "
|
||||
"You will be given a user instruction together with a preview of an input .xlsx file. "
|
||||
"Your job is to write a single self-contained Python script that reads the input file "
|
||||
"at the path stored in the variable INPUT_PATH, performs the requested manipulation, "
|
||||
"and saves the result to OUTPUT_PATH. Use only the standard library, openpyxl, and pandas. "
|
||||
"Do not print anything. Do not use input(). Do not hardcode file paths. "
|
||||
"Return ONLY the Python code inside a single ```python ... ``` fenced block."
|
||||
)
|
||||
|
||||
|
||||
def build_system(skill_content: str = "") -> str:
|
||||
base = _SYSTEM_PROMPT
|
||||
if skill_content.strip():
|
||||
base += f"\n\n## Skill\n{skill_content.strip()}"
|
||||
return base
|
||||
|
||||
|
||||
def build_user(instruction, input_xlsx, instruction_type="", answer_position=""):
|
||||
try:
|
||||
preview = _preview_workbook(input_xlsx)
|
||||
except Exception as e:
|
||||
preview = f"(failed to preview: {e})"
|
||||
extra = ""
|
||||
if instruction_type:
|
||||
extra += f"\nInstruction type: {instruction_type}"
|
||||
if answer_position:
|
||||
extra += f"\nExpected answer position: {answer_position}"
|
||||
return (
|
||||
f"# Instruction\n{instruction}\n{extra}\n\n"
|
||||
f"# Input spreadsheet preview\n{preview}\n\n"
|
||||
"# Task\n"
|
||||
"Write a Python script that reads the workbook from the variable `INPUT_PATH`, "
|
||||
"applies the instruction, and writes the modified workbook to `OUTPUT_PATH`. "
|
||||
"Preserve all other cells unchanged. "
|
||||
"The preview may be truncated — do not hardcode row counts or assume the data ends at the last previewed row; "
|
||||
"iterate over all actual rows in the workbook instead. "
|
||||
"Return only a ```python``` code block."
|
||||
)
|
||||
|
||||
|
||||
# ── Shared utilities (identical to custom version) ──────────────────────────
|
||||
|
||||
def _preview_workbook(path, max_rows=5, max_cols=20):
|
||||
wb = openpyxl.load_workbook(path, data_only=False)
|
||||
chunks = []
|
||||
for sn in wb.sheetnames:
|
||||
ws = wb[sn]
|
||||
chunks.append(f"## Sheet: {sn} (dim={ws.dimensions}, max_row={ws.max_row}, max_col={ws.max_column})")
|
||||
for row in ws.iter_rows(min_row=1, max_row=min(ws.max_row, max_rows),
|
||||
max_col=min(ws.max_column, max_cols), values_only=False):
|
||||
cells = []
|
||||
for c in row:
|
||||
v = c.value
|
||||
s = "" if v is None else str(v)
|
||||
if len(s) > 40: s = s[:37] + "..."
|
||||
cells.append(f"{c.coordinate}={s}")
|
||||
chunks.append(" | ".join(cells))
|
||||
if ws.max_row > max_rows:
|
||||
chunks.append(f"... ({ws.max_row - max_rows} more rows)")
|
||||
chunks.append("")
|
||||
wb.close()
|
||||
return "\n".join(chunks)
|
||||
|
||||
|
||||
def extract_code(text):
|
||||
if "```" not in text:
|
||||
return text.strip()
|
||||
start = text.find("```")
|
||||
nl = text.find("\n", start)
|
||||
end = text.find("```", nl + 1)
|
||||
if nl == -1 or end == -1:
|
||||
return text.strip()
|
||||
return text[nl + 1:end].strip()
|
||||
|
||||
|
||||
_PATH_RE = re.compile(r'^\s*(INPUT_PATH|OUTPUT_PATH)\s*=\s*.+$', re.MULTILINE)
|
||||
|
||||
def strip_paths(code):
|
||||
return _PATH_RE.sub("", code)
|
||||
|
||||
|
||||
RUNNER_TEMPLATE = textwrap.dedent("""
|
||||
import os, sys, traceback
|
||||
INPUT_PATH = {input_path!r}
|
||||
OUTPUT_PATH = {output_path!r}
|
||||
try:
|
||||
{code_indented}
|
||||
except Exception:
|
||||
traceback.print_exc()
|
||||
sys.exit(2)
|
||||
""")
|
||||
|
||||
|
||||
def run_code(code, input_path, output_path, timeout=120):
|
||||
os.makedirs(os.path.dirname(output_path), exist_ok=True)
|
||||
cleaned = strip_paths(code)
|
||||
indented = textwrap.indent(cleaned, " ")
|
||||
script = RUNNER_TEMPLATE.format(input_path=input_path, output_path=output_path, code_indented=indented)
|
||||
with tempfile.NamedTemporaryFile("w", suffix=".py", delete=False) as f:
|
||||
f.write(script)
|
||||
tmp = f.name
|
||||
try:
|
||||
proc = subprocess.run([sys.executable, tmp], capture_output=True, text=True, timeout=timeout)
|
||||
if proc.returncode != 0:
|
||||
return False, (proc.stdout + "\n" + proc.stderr).strip()
|
||||
if not os.path.exists(output_path):
|
||||
return False, "output file was not created"
|
||||
return True, ""
|
||||
except subprocess.TimeoutExpired:
|
||||
return False, f"timeout after {timeout}s"
|
||||
finally:
|
||||
try: os.unlink(tmp)
|
||||
except OSError: pass
|
||||
|
||||
|
||||
def find_test_cases(task_dir):
|
||||
cases = []
|
||||
for ip in sorted(glob.glob(os.path.join(task_dir, "*_input.xlsx"))):
|
||||
no = os.path.basename(ip).split("_", 1)[0]
|
||||
ap = ip.replace("_input.xlsx", "_answer.xlsx")
|
||||
if os.path.exists(ap): cases.append((no, ip, ap))
|
||||
for ip in sorted(glob.glob(os.path.join(task_dir, "*_init.xlsx"))):
|
||||
no = os.path.basename(ip).split("_", 1)[0]
|
||||
ap = ip.replace("_init.xlsx", "_golden.xlsx")
|
||||
if os.path.exists(ap): cases.append((no, ip, ap))
|
||||
if not cases:
|
||||
bare_init = os.path.join(task_dir, "initial.xlsx")
|
||||
bare_gold = os.path.join(task_dir, "golden.xlsx")
|
||||
if os.path.exists(bare_init) and os.path.exists(bare_gold):
|
||||
cases.append(("1", bare_init, bare_gold))
|
||||
return cases
|
||||
|
||||
|
||||
def load_items(path):
|
||||
if path.endswith(".json"):
|
||||
with open(path) as f:
|
||||
data = json.load(f)
|
||||
if isinstance(data, dict):
|
||||
data = data.get("data") or list(data.values())
|
||||
return list(data)
|
||||
items = []
|
||||
with open(path) as f:
|
||||
for line in f:
|
||||
line = line.strip()
|
||||
if line: items.append(json.loads(line))
|
||||
return items
|
||||
|
||||
|
||||
# ── LLM call ────────────────────────────────────────────────────────────────
|
||||
|
||||
def llm_call(messages, deployment, max_tokens=16384, retries=5, llm_timeout=120):
|
||||
raw, _ = chat_messages_with_deployment(
|
||||
deployment=deployment,
|
||||
messages=messages,
|
||||
max_completion_tokens=max_tokens,
|
||||
retries=retries,
|
||||
stage="rollout",
|
||||
timeout=llm_timeout,
|
||||
)
|
||||
return str(raw or "")
|
||||
|
||||
|
||||
# ── Process one task ────────────────────────────────────────────────────────
|
||||
|
||||
def process_one(item, data_root, out_root, model):
|
||||
task_id = str(item["id"])
|
||||
instruction = item["instruction"]
|
||||
instruction_type = item.get("instruction_type", "")
|
||||
answer_position = item.get("answer_position", "")
|
||||
answer_sheet = item.get("answer_sheet", "")
|
||||
if answer_position and answer_sheet and "!" not in answer_position:
|
||||
answer_position = f"{answer_sheet}!{answer_position}"
|
||||
|
||||
sp = item.get("spreadsheet_path", f"spreadsheet/{task_id}")
|
||||
task_dir = sp if os.path.isabs(sp) else os.path.join(data_root, sp)
|
||||
|
||||
result = {"id": task_id, "ok": False, "hard": 0, "soft": 0.0,
|
||||
"n_cases": 0, "n_pass": 0, "fail_reason": "", "error": ""}
|
||||
try:
|
||||
cases = find_test_cases(task_dir)
|
||||
result["n_cases"] = len(cases)
|
||||
if not cases:
|
||||
result["fail_reason"] = "no-test-cases"
|
||||
return result
|
||||
|
||||
task_out = os.path.join(out_root, "predictions", task_id)
|
||||
os.makedirs(task_out, exist_ok=True)
|
||||
|
||||
# LLM call
|
||||
system = build_system("")
|
||||
user = build_user(instruction, cases[0][1], instruction_type, answer_position)
|
||||
messages = [{"role": "system", "content": system}, {"role": "user", "content": user}]
|
||||
|
||||
raw = llm_call(messages, model)
|
||||
time.sleep(3)
|
||||
code = extract_code(raw)
|
||||
|
||||
with open(os.path.join(task_out, "code.py"), "w") as f: f.write(code)
|
||||
with open(os.path.join(task_out, "raw.txt"), "w") as f: f.write(raw)
|
||||
|
||||
if not code.strip():
|
||||
result["fail_reason"] = "empty-code"
|
||||
return result
|
||||
|
||||
# Execute + evaluate each test case
|
||||
for no, ip, ap in cases:
|
||||
pred = os.path.join(task_out, f"{no}_pred.xlsx")
|
||||
ok_exec, err = run_code(code, ip, pred)
|
||||
if not ok_exec:
|
||||
if not result["fail_reason"]:
|
||||
result["fail_reason"] = f"exec: {err[:200]}"
|
||||
continue
|
||||
try:
|
||||
ev = evaluate(pred, ap, instruction_type, answer_position)
|
||||
except Exception as e:
|
||||
ev = {"ok": False, "reason": str(e)}
|
||||
if ev["ok"]:
|
||||
result["n_pass"] += 1
|
||||
|
||||
nc, np = result["n_cases"], result["n_pass"]
|
||||
result["soft"] = np / nc if nc else 0.0
|
||||
result["hard"] = 1 if nc > 0 and np == nc else 0
|
||||
result["ok"] = bool(result["hard"])
|
||||
if result["ok"]: result["fail_reason"] = ""
|
||||
return result
|
||||
except Exception as e:
|
||||
result["fail_reason"] = f"unexpected: {e}"
|
||||
result["error"] = traceback.format_exc()
|
||||
return result
|
||||
|
||||
|
||||
# ── Main ────────────────────────────────────────────────────────────────────
|
||||
|
||||
def main():
|
||||
ap = argparse.ArgumentParser(description="Eval OFFICIAL prompt on verified-400")
|
||||
ap.add_argument("--model", default=MODEL)
|
||||
ap.add_argument("--backend", choices=["azure_openai", "codex", "claude"], default="azure_openai")
|
||||
ap.add_argument("--azure_endpoint", default="")
|
||||
ap.add_argument("--azure_api_version", default="")
|
||||
ap.add_argument("--azure_api_key", default="")
|
||||
ap.add_argument("--workers", type=int, default=8)
|
||||
ap.add_argument("--limit", type=int, default=0)
|
||||
ap.add_argument("--out_root", default="")
|
||||
args = ap.parse_args()
|
||||
|
||||
set_backend(args.backend)
|
||||
configure_azure_openai(
|
||||
endpoint=args.azure_endpoint or None,
|
||||
api_version=args.azure_api_version or None,
|
||||
api_key=args.azure_api_key or None,
|
||||
)
|
||||
set_student_deployment(args.model)
|
||||
ts = time.strftime("%Y%m%d_%H%M%S")
|
||||
out_root = args.out_root or os.path.join(_PROJECT_ROOT, "outputs", f"prompt_official_{args.model}_{ts}")
|
||||
out_root = os.path.abspath(out_root)
|
||||
os.makedirs(out_root, exist_ok=True)
|
||||
|
||||
items = load_items(JSONL_PATH)
|
||||
if args.limit: items = items[:args.limit]
|
||||
|
||||
print(f"{'='*60}")
|
||||
print(f" Prompt: OFFICIAL (SpreadsheetBench original)")
|
||||
print(f" Model: {args.model}")
|
||||
print(f" Items: {len(items)}")
|
||||
print(f" Output: {out_root}")
|
||||
print(f"{'='*60}")
|
||||
|
||||
t0 = time.time()
|
||||
results = []
|
||||
with ThreadPoolExecutor(max_workers=args.workers) as ex:
|
||||
futs = {ex.submit(process_one, it, DATA_ROOT, out_root, args.model): it for it in items}
|
||||
for i, fut in enumerate(as_completed(futs), 1):
|
||||
item = futs[fut]
|
||||
try:
|
||||
res = fut.result(timeout=300)
|
||||
except FuturesTimeoutError:
|
||||
res = {"id": str(item["id"]), "ok": False, "hard": 0, "soft": 0.0,
|
||||
"n_cases": 0, "n_pass": 0, "fail_reason": "timeout"}
|
||||
except Exception as e:
|
||||
res = {"id": str(item["id"]), "ok": False, "hard": 0, "soft": 0.0,
|
||||
"n_cases": 0, "n_pass": 0, "fail_reason": str(e)}
|
||||
results.append(res)
|
||||
status = "PASS" if res.get("hard") else "FAIL"
|
||||
dt = time.time() - t0
|
||||
print(f" {i}/{len(items)} id={res['id']:<10} {status} cases={res.get('n_pass',0)}/{res.get('n_cases',0)} dt={dt:.0f}s")
|
||||
|
||||
# Summary
|
||||
hard_sum = sum(r.get("hard", 0) for r in results)
|
||||
soft_sum = sum(r.get("soft", 0.0) for r in results)
|
||||
n = len(results)
|
||||
print(f"\n{'='*60}")
|
||||
print(f" OFFICIAL prompt: hard={hard_sum}/{n}={hard_sum/n:.4f} soft={soft_sum/n:.4f}")
|
||||
print(f"{'='*60}")
|
||||
|
||||
with open(os.path.join(out_root, "results.jsonl"), "w") as f:
|
||||
for r in results:
|
||||
f.write(json.dumps(r, ensure_ascii=False) + "\n")
|
||||
with open(os.path.join(out_root, "summary.json"), "w") as f:
|
||||
json.dump({"prompt": "official", "model": args.model, "n": n,
|
||||
"hard": hard_sum/n, "soft": soft_sum/n}, f, indent=2)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
37
scripts/eval_searchqa_val500.sh
Executable file
37
scripts/eval_searchqa_val500.sh
Executable file
@@ -0,0 +1,37 @@
|
||||
#!/usr/bin/env bash
|
||||
# ──────────────────────────────────────────────────────────────────────────────
|
||||
# ReflACT3 — SearchQA Eval-Only (验证集 500)
|
||||
#
|
||||
# Usage:
|
||||
# bash scripts/eval_searchqa_val500.sh --skill_path outputs/xxx/best_skill.md
|
||||
# bash scripts/eval_searchqa_val500.sh --skill_path outputs/xxx/best_skill.md --workers 32
|
||||
# ──────────────────────────────────────────────────────────────────────────────
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
PROJECT_ROOT="$(dirname "${SCRIPT_DIR}")"
|
||||
export PYTHONPATH="${PROJECT_ROOT}:${PYTHONPATH:-}"
|
||||
|
||||
export STUDENT_DEPLOYMENT="${STUDENT_DEPLOYMENT:-gpt-5-mini}"
|
||||
|
||||
VAL_PATH="/home/azureuser/workspace-yqh/refleAct/search-qa/data/searchqa_val_500.json"
|
||||
|
||||
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
|
||||
DEFAULT_OUT_ROOT="${PROJECT_ROOT}/outputs/searchqa_eval_val500_${STUDENT_DEPLOYMENT}_${TIMESTAMP}"
|
||||
|
||||
echo "============================================================"
|
||||
echo " ReflACT3 — SearchQA Eval-Only (val-500)"
|
||||
echo " Student: ${STUDENT_DEPLOYMENT}"
|
||||
echo " Data: ${VAL_PATH}"
|
||||
echo "============================================================"
|
||||
|
||||
cd "${PROJECT_ROOT}"
|
||||
|
||||
python scripts/eval_only.py \
|
||||
--config configs/searchqa_default.yaml \
|
||||
--data_path "${VAL_PATH}" \
|
||||
--out_root "${DEFAULT_OUT_ROOT}" \
|
||||
"$@"
|
||||
|
||||
echo ""
|
||||
echo "Done! Results saved to: ${DEFAULT_OUT_ROOT}"
|
||||
41
scripts/eval_verified400.sh
Executable file
41
scripts/eval_verified400.sh
Executable file
@@ -0,0 +1,41 @@
|
||||
#!/usr/bin/env bash
|
||||
# ──────────────────────────────────────────────────────────────────────────────
|
||||
# Eval skill0 on full SpreadsheetBench verified-400
|
||||
#
|
||||
# Usage:
|
||||
# bash scripts/eval_verified400.sh
|
||||
# bash scripts/eval_verified400.sh --workers 64
|
||||
# ──────────────────────────────────────────────────────────────────────────────
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
PROJECT_ROOT="$(dirname "${SCRIPT_DIR}")"
|
||||
|
||||
export PYTHONPATH="${PROJECT_ROOT}:${PYTHONPATH:-}"
|
||||
|
||||
# ── Paths ────────────────────────────────────────────────────────────────────
|
||||
DATA_ROOT="/home/azureuser/workspace-yqh/sr/spreadsheetbench/data/spreadsheetbench_verified_400"
|
||||
JSONL_PATH="${DATA_ROOT}/dataset.json"
|
||||
SKILL_PATH="${PROJECT_ROOT}/reflact/envs/spreadsheetbench/skills/initial.md"
|
||||
|
||||
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
|
||||
OUT_ROOT="${PROJECT_ROOT}/outputs/eval_verified400_${TIMESTAMP}"
|
||||
|
||||
echo "============================================================"
|
||||
echo " Eval skill0 on verified-400 (full)"
|
||||
echo "============================================================"
|
||||
echo " data_root: ${DATA_ROOT}"
|
||||
echo " skill: ${SKILL_PATH}"
|
||||
echo " out_root: ${OUT_ROOT}"
|
||||
echo "============================================================"
|
||||
|
||||
cd "${PROJECT_ROOT}"
|
||||
|
||||
python scripts/eval_only.py \
|
||||
--config configs/spreadsheetbench_default.yaml \
|
||||
--skill "${SKILL_PATH}" \
|
||||
--split all \
|
||||
--data_root "${DATA_ROOT}" \
|
||||
--jsonl_path "${JSONL_PATH}" \
|
||||
--out_root "${OUT_ROOT}" \
|
||||
"$@"
|
||||
42
scripts/eval_verified400_multi.sh
Executable file
42
scripts/eval_verified400_multi.sh
Executable file
@@ -0,0 +1,42 @@
|
||||
#!/usr/bin/env bash
|
||||
# ──────────────────────────────────────────────────────────────────────────────
|
||||
# Eval skill0 on full SpreadsheetBench verified-400 (MULTI-ROUND codegen)
|
||||
#
|
||||
# Usage:
|
||||
# bash scripts/eval_verified400_multi.sh
|
||||
# bash scripts/eval_verified400_multi.sh --workers 64 --max_turns 5
|
||||
# ──────────────────────────────────────────────────────────────────────────────
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
PROJECT_ROOT="$(dirname "${SCRIPT_DIR}")"
|
||||
|
||||
export PYTHONPATH="${PROJECT_ROOT}:${PYTHONPATH:-}"
|
||||
|
||||
DATA_ROOT="/home/azureuser/workspace-yqh/sr/spreadsheetbench/data/spreadsheetbench_verified_400"
|
||||
JSONL_PATH="${DATA_ROOT}/dataset.json"
|
||||
SKILL_PATH="${PROJECT_ROOT}/reflact/envs/spreadsheetbench/skills/initial.md"
|
||||
|
||||
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
|
||||
OUT_ROOT="${PROJECT_ROOT}/outputs/eval_multi_verified400_${TIMESTAMP}"
|
||||
|
||||
echo "============================================================"
|
||||
echo " Eval skill0 — MULTI-ROUND codegen — verified-400"
|
||||
echo "============================================================"
|
||||
echo " data_root: ${DATA_ROOT}"
|
||||
echo " skill: ${SKILL_PATH}"
|
||||
echo " mode: multi"
|
||||
echo " out_root: ${OUT_ROOT}"
|
||||
echo "============================================================"
|
||||
|
||||
cd "${PROJECT_ROOT}"
|
||||
|
||||
python scripts/eval_only.py \
|
||||
--config configs/spreadsheetbench_default.yaml \
|
||||
--skill "${SKILL_PATH}" \
|
||||
--split all \
|
||||
--mode multi \
|
||||
--data_root "${DATA_ROOT}" \
|
||||
--jsonl_path "${JSONL_PATH}" \
|
||||
--out_root "${OUT_ROOT}" \
|
||||
"$@"
|
||||
42
scripts/eval_verified400_single.sh
Executable file
42
scripts/eval_verified400_single.sh
Executable file
@@ -0,0 +1,42 @@
|
||||
#!/usr/bin/env bash
|
||||
# ──────────────────────────────────────────────────────────────────────────────
|
||||
# Eval skill0 on full SpreadsheetBench verified-400 (SINGLE-ROUND codegen)
|
||||
#
|
||||
# Usage:
|
||||
# bash scripts/eval_verified400_single.sh
|
||||
# bash scripts/eval_verified400_single.sh --workers 64
|
||||
# ──────────────────────────────────────────────────────────────────────────────
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
PROJECT_ROOT="$(dirname "${SCRIPT_DIR}")"
|
||||
|
||||
export PYTHONPATH="${PROJECT_ROOT}:${PYTHONPATH:-}"
|
||||
|
||||
DATA_ROOT="/home/azureuser/workspace-yqh/sr/spreadsheetbench/data/spreadsheetbench_verified_400"
|
||||
JSONL_PATH="${DATA_ROOT}/dataset.json"
|
||||
SKILL_PATH="${PROJECT_ROOT}/reflact/envs/spreadsheetbench/skills/initial.md"
|
||||
|
||||
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
|
||||
OUT_ROOT="${PROJECT_ROOT}/outputs/eval_single_verified400_${TIMESTAMP}"
|
||||
|
||||
echo "============================================================"
|
||||
echo " Eval skill0 — SINGLE-ROUND codegen — verified-400"
|
||||
echo "============================================================"
|
||||
echo " data_root: ${DATA_ROOT}"
|
||||
echo " skill: ${SKILL_PATH}"
|
||||
echo " mode: single"
|
||||
echo " out_root: ${OUT_ROOT}"
|
||||
echo "============================================================"
|
||||
|
||||
cd "${PROJECT_ROOT}"
|
||||
|
||||
python scripts/eval_only.py \
|
||||
--config configs/spreadsheetbench_default.yaml \
|
||||
--skill "${SKILL_PATH}" \
|
||||
--split all \
|
||||
--mode single \
|
||||
--data_root "${DATA_ROOT}" \
|
||||
--jsonl_path "${JSONL_PATH}" \
|
||||
--out_root "${OUT_ROOT}" \
|
||||
"$@"
|
||||
120
scripts/launch_harness_bestsetting_from_scratch.sh
Executable file
120
scripts/launch_harness_bestsetting_from_scratch.sh
Executable file
@@ -0,0 +1,120 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
PY="${PY:-python}"
|
||||
RUN_ROOT="${RUN_ROOT:-$ROOT/outputs/harness_bestsetting_fromscratch_$(date -u +%Y%m%d_%H%M%S)_run}"
|
||||
MAX_PARALLEL="${MAX_PARALLEL:-2}"
|
||||
|
||||
mkdir -p "$RUN_ROOT/logs"
|
||||
cd "$ROOT"
|
||||
export PYTHONPATH="$ROOT:${PYTHONPATH:-}"
|
||||
|
||||
COMMON=(
|
||||
model.teacher_backend=openai_chat
|
||||
model.teacher=gpt-5.5
|
||||
model.teacher_azure_openai_endpoint=https://t2vgoaigpt4o3.openai.azure.com/
|
||||
model.teacher_azure_openai_api_version=2024-12-01-preview
|
||||
model.teacher_azure_openai_auth_mode=azure_cli
|
||||
model.reasoning_effort=medium
|
||||
train.num_epochs=4
|
||||
train.train_size=0
|
||||
train.accumulation=1
|
||||
train.seed=42
|
||||
gradient.minibatch_size=8
|
||||
gradient.merge_batch_size=8
|
||||
gradient.analyst_workers=16
|
||||
gradient.use_deep_reflect=false
|
||||
optimizer.min_learning_rate=2
|
||||
optimizer.lr_control_mode=fixed
|
||||
optimizer.skill_update_mode=patch
|
||||
optimizer.use_slow_update=true
|
||||
optimizer.slow_update_samples=20
|
||||
optimizer.use_meta_skill=true
|
||||
optimizer.use_meta_reflect=false
|
||||
evaluation.use_gate=true
|
||||
evaluation.eval_test=true
|
||||
env.split_mode=split_dir
|
||||
)
|
||||
|
||||
CODEX=(
|
||||
model.student_backend=codex_exec
|
||||
model.student=gpt-5.5
|
||||
model.codex_exec_use_sdk=auto
|
||||
model.codex_exec_sandbox=workspace-write
|
||||
model.codex_exec_approval_policy=never
|
||||
model.codex_trace_to_teacher=true
|
||||
)
|
||||
|
||||
CLAUDE=(
|
||||
model.student_backend=claude_code_exec
|
||||
model.student=claude-sonnet-4-6
|
||||
model.claude_code_exec_use_sdk=auto
|
||||
model.codex_trace_to_teacher=false
|
||||
)
|
||||
|
||||
active=0
|
||||
launch() {
|
||||
local run_id="$1"; shift
|
||||
local config="$1"; shift
|
||||
local out="$RUN_ROOT/$run_id"
|
||||
local log="$RUN_ROOT/logs/$run_id.log"
|
||||
echo "START $run_id"
|
||||
setsid "$PY" -u scripts/train.py \
|
||||
--config "$config" \
|
||||
--cfg-options "${COMMON[@]}" "$@" "env.out_root=$out" \
|
||||
> "$log" 2>&1 < /dev/null &
|
||||
active=$((active + 1))
|
||||
if (( active >= MAX_PARALLEL )); then
|
||||
wait -n
|
||||
active=$((active - 1))
|
||||
fi
|
||||
}
|
||||
|
||||
# SearchQA best openai-chat setting: optimizer.lr_scheduler=constant.
|
||||
launch HARNESS-BESTSETTING-searchqa-codex configs/searchqa/default.yaml \
|
||||
"${CODEX[@]}" \
|
||||
train.batch_size=40 optimizer.learning_rate=4 optimizer.min_learning_rate=2 optimizer.lr_scheduler=constant \
|
||||
env.split_dir=data/searchqa/splits
|
||||
|
||||
launch HARNESS-BESTSETTING-searchqa-claude configs/searchqa/default.yaml \
|
||||
"${CLAUDE[@]}" \
|
||||
train.batch_size=40 optimizer.learning_rate=4 optimizer.min_learning_rate=2 optimizer.lr_scheduler=constant \
|
||||
env.split_dir=data/searchqa/splits
|
||||
|
||||
# SpreadsheetBench best openai-chat setting: optimizer.lr_scheduler=constant.
|
||||
# Must stay env.mode=multi; exec-backend multi support is fixed on this branch.
|
||||
launch HARNESS-BESTSETTING-spreadsheetbench-codex configs/spreadsheetbench/default.yaml \
|
||||
"${CODEX[@]}" \
|
||||
train.batch_size=40 optimizer.learning_rate=4 optimizer.min_learning_rate=2 optimizer.lr_scheduler=constant \
|
||||
env.split_dir=data/spreadsheetbench env.data_root=data/spreadsheetbench/files env.mode=multi env.workers=4
|
||||
|
||||
launch HARNESS-BESTSETTING-spreadsheetbench-claude configs/spreadsheetbench/default.yaml \
|
||||
"${CLAUDE[@]}" \
|
||||
train.batch_size=40 optimizer.learning_rate=4 optimizer.min_learning_rate=2 optimizer.lr_scheduler=constant \
|
||||
env.split_dir=data/spreadsheetbench env.data_root=data/spreadsheetbench/files env.mode=multi env.workers=4
|
||||
|
||||
# LiveMathBench best openai-chat setting: optimizer.learning_rate=8.
|
||||
launch HARNESS-BESTSETTING-livemathematicianbench-codex configs/livemathematicianbench/default.yaml \
|
||||
"${CODEX[@]}" \
|
||||
train.batch_size=40 optimizer.learning_rate=8 optimizer.min_learning_rate=1 optimizer.lr_scheduler=constant \
|
||||
env.split_dir=data/livemathbench/splits
|
||||
|
||||
launch HARNESS-BESTSETTING-livemathematicianbench-claude configs/livemathematicianbench/default.yaml \
|
||||
"${CLAUDE[@]}" \
|
||||
train.batch_size=40 optimizer.learning_rate=8 optimizer.min_learning_rate=1 optimizer.lr_scheduler=constant \
|
||||
env.split_dir=data/livemathbench/splits
|
||||
|
||||
# DocVQA best openai-chat setting was full batch. On 10% harness split, train=107.
|
||||
launch HARNESS-BESTSETTING-docvqa10pct-codex configs/docvqa/default.yaml \
|
||||
"${CODEX[@]}" \
|
||||
train.batch_size=107 optimizer.learning_rate=4 optimizer.min_learning_rate=2 optimizer.lr_scheduler=cosine \
|
||||
env.split_dir=data/harness_splits/docvqa_zisu_first10pct
|
||||
|
||||
launch HARNESS-BESTSETTING-docvqa10pct-claude configs/docvqa/default.yaml \
|
||||
"${CLAUDE[@]}" \
|
||||
train.batch_size=107 optimizer.learning_rate=4 optimizer.min_learning_rate=2 optimizer.lr_scheduler=cosine \
|
||||
env.split_dir=data/harness_splits/docvqa_zisu_first10pct
|
||||
|
||||
wait
|
||||
echo "All launched runs finished or exited. RUN_ROOT=$RUN_ROOT"
|
||||
178
scripts/launch_harness_canonical_claude18.sh
Executable file
178
scripts/launch_harness_canonical_claude18.sh
Executable file
@@ -0,0 +1,178 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
REPO="/home/azureuser/workspace-gzy/SkillReflection"
|
||||
PYTHON="/home/azureuser/workspace-gzy/miniconda3/envs/reflact/bin/python"
|
||||
|
||||
cd "$REPO"
|
||||
|
||||
# Claude Code on this machine must use the local copilot-api proxy.
|
||||
export ANTHROPIC_BASE_URL="${ANTHROPIC_BASE_URL:-http://127.0.0.1:4343}"
|
||||
export ANTHROPIC_AUTH_TOKEN="${ANTHROPIC_AUTH_TOKEN:-dummy}"
|
||||
export ANTHROPIC_MODEL="${ANTHROPIC_MODEL:-claude-sonnet-4-6}"
|
||||
export ANTHROPIC_SMALL_FAST_MODEL="${ANTHROPIC_SMALL_FAST_MODEL:-claude-sonnet-4-6}"
|
||||
export DISABLE_NON_ESSENTIAL_MODEL_CALLS="${DISABLE_NON_ESSENTIAL_MODEL_CALLS:-1}"
|
||||
export CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC="${CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC:-1}"
|
||||
|
||||
if [[ -f ".secrets/teacher_oaidr9.env" ]]; then
|
||||
# shellcheck disable=SC1091
|
||||
source ".secrets/teacher_oaidr9.env"
|
||||
else
|
||||
echo "missing .secrets/teacher_oaidr9.env" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
stamp="$(date -u +%Y%m%d_%H%M%S)"
|
||||
RUN_ROOT="${1:-outputs/harness_canonical_claude18_workers2_timeout1020_${stamp}_run}"
|
||||
SESSION="${2:-harness_canon_claude18_${stamp}}"
|
||||
|
||||
mkdir -p "$RUN_ROOT/logs" "$RUN_ROOT/commands"
|
||||
|
||||
COMMON_CFG=(
|
||||
model.teacher_backend=openai_chat
|
||||
model.teacher=gpt-5.5
|
||||
model.teacher_azure_openai_endpoint="${TEACHER_AZURE_OPENAI_ENDPOINT}"
|
||||
model.teacher_azure_openai_api_version="${TEACHER_AZURE_OPENAI_API_VERSION}"
|
||||
model.teacher_azure_openai_auth_mode="${TEACHER_AZURE_OPENAI_AUTH_MODE}"
|
||||
model.teacher_azure_openai_managed_identity_client_id="${TEACHER_AZURE_OPENAI_MANAGED_IDENTITY_CLIENT_ID}"
|
||||
model.teacher_azure_openai_ad_scope="${TEACHER_AZURE_OPENAI_AD_SCOPE}"
|
||||
model.reasoning_effort=medium
|
||||
train.num_epochs=4
|
||||
train.train_size=0
|
||||
train.batch_size=40
|
||||
train.accumulation=1
|
||||
train.seed=42
|
||||
gradient.minibatch_size=8
|
||||
gradient.merge_batch_size=8
|
||||
gradient.analyst_workers=16
|
||||
gradient.use_deep_reflect=false
|
||||
optimizer.lr_control_mode=fixed
|
||||
optimizer.skill_update_mode=patch
|
||||
optimizer.use_slow_update=true
|
||||
optimizer.slow_update_samples=20
|
||||
optimizer.use_meta_skill=true
|
||||
optimizer.use_meta_reflect=false
|
||||
evaluation.use_gate=true
|
||||
evaluation.eval_test=true
|
||||
env.split_mode=split_dir
|
||||
env.workers=2
|
||||
env.exec_timeout=1020
|
||||
model.student_backend=claude_code_exec
|
||||
model.student=claude-sonnet-4-6
|
||||
model.claude_code_exec_use_sdk=sdk
|
||||
model.claude_code_exec_effort=medium
|
||||
model.claude_code_exec_max_thinking_tokens=16384
|
||||
model.codex_trace_to_teacher=false
|
||||
)
|
||||
|
||||
tmux_started=0
|
||||
|
||||
launch_run() {
|
||||
local run_id="$1"
|
||||
local config="$2"
|
||||
local skill="$3"
|
||||
shift 3
|
||||
|
||||
local cmd_file="$RUN_ROOT/commands/${run_id}.sh"
|
||||
local log_file="$RUN_ROOT/logs/${run_id}.log"
|
||||
local out_root="$RUN_ROOT/$run_id"
|
||||
|
||||
local -a cmd=(
|
||||
"$PYTHON" -u scripts/train.py
|
||||
--config "$config"
|
||||
--cfg-options
|
||||
"${COMMON_CFG[@]}"
|
||||
env.skill_init="$skill"
|
||||
env.out_root="$out_root"
|
||||
"$@"
|
||||
)
|
||||
|
||||
{
|
||||
echo "#!/usr/bin/env bash"
|
||||
echo "set -euo pipefail"
|
||||
echo "cd '$REPO'"
|
||||
printf 'export ANTHROPIC_BASE_URL=%q\n' "$ANTHROPIC_BASE_URL"
|
||||
printf 'export ANTHROPIC_AUTH_TOKEN=%q\n' "$ANTHROPIC_AUTH_TOKEN"
|
||||
printf 'export ANTHROPIC_MODEL=%q\n' "$ANTHROPIC_MODEL"
|
||||
printf 'export ANTHROPIC_SMALL_FAST_MODEL=%q\n' "$ANTHROPIC_SMALL_FAST_MODEL"
|
||||
printf 'export DISABLE_NON_ESSENTIAL_MODEL_CALLS=%q\n' "$DISABLE_NON_ESSENTIAL_MODEL_CALLS"
|
||||
printf 'export CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=%q\n' "$CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC"
|
||||
printf '%q ' "${cmd[@]}"
|
||||
printf ' >%q 2>&1 < /dev/null\n' "$log_file"
|
||||
} > "$cmd_file"
|
||||
chmod +x "$cmd_file"
|
||||
|
||||
if [[ "$tmux_started" -eq 0 ]]; then
|
||||
tmux new-session -d -s "$SESSION" -n "$run_id" "bash '$cmd_file'; code=\$?; echo EXIT:\$code; sleep 3600"
|
||||
tmux_started=1
|
||||
else
|
||||
tmux new-window -t "$SESSION" -n "$run_id" "bash '$cmd_file'; code=\$?; echo EXIT:\$code; sleep 3600"
|
||||
fi
|
||||
echo "$run_id"
|
||||
}
|
||||
|
||||
SEARCHQA_SKILL="docs/harness_source_skills/searchqa_best_skill.md"
|
||||
LIVEMATH_SKILL="docs/harness_source_skills/livemathematicianbench_best_skill.md"
|
||||
DOCVQA_SKILL="docs/harness_source_skills/docvqa_best_skill.md"
|
||||
SPREADSHEET_SKILL="docs/harness_source_skills/spreadsheetbench_best_skill.md"
|
||||
|
||||
launch_run "HARNESS-Claude-SearchQA-sched-constant" "configs/searchqa/default.yaml" "$SEARCHQA_SKILL" \
|
||||
env.split_dir=data/searchqa/splits \
|
||||
optimizer.learning_rate=4 optimizer.min_learning_rate=2 optimizer.lr_scheduler=constant
|
||||
launch_run "HARNESS-Claude-SearchQA-sched-linear" "configs/searchqa/default.yaml" "$SEARCHQA_SKILL" \
|
||||
env.split_dir=data/searchqa/splits \
|
||||
optimizer.learning_rate=4 optimizer.min_learning_rate=2 optimizer.lr_scheduler=linear
|
||||
launch_run "HARNESS-Claude-SearchQA-batch-full" "configs/searchqa/default.yaml" "$SEARCHQA_SKILL" \
|
||||
env.split_dir=data/searchqa/splits \
|
||||
train.batch_size=400 optimizer.learning_rate=4 optimizer.min_learning_rate=2 optimizer.lr_scheduler=cosine
|
||||
launch_run "HARNESS-Claude-SearchQA-lr8" "configs/searchqa/default.yaml" "$SEARCHQA_SKILL" \
|
||||
env.split_dir=data/searchqa/splits \
|
||||
optimizer.learning_rate=8 optimizer.min_learning_rate=1 optimizer.lr_scheduler=constant
|
||||
|
||||
launch_run "HARNESS-Claude-LiveMath-lr8" "configs/livemathematicianbench/default.yaml" "$LIVEMATH_SKILL" \
|
||||
env.split_dir=data/livemathbench/splits \
|
||||
optimizer.learning_rate=8 optimizer.min_learning_rate=1 optimizer.lr_scheduler=constant
|
||||
launch_run "HARNESS-Claude-LiveMath-lr16" "configs/livemathematicianbench/default.yaml" "$LIVEMATH_SKILL" \
|
||||
env.split_dir=data/livemathbench/splits \
|
||||
optimizer.learning_rate=16 optimizer.min_learning_rate=1 optimizer.lr_scheduler=constant
|
||||
launch_run "HARNESS-Claude-LiveMath-slow10" "configs/livemathematicianbench/default.yaml" "$LIVEMATH_SKILL" \
|
||||
env.split_dir=data/livemathbench/splits \
|
||||
optimizer.learning_rate=4 optimizer.min_learning_rate=2 optimizer.lr_scheduler=cosine optimizer.slow_update_samples=10
|
||||
launch_run "HARNESS-Claude-LiveMath-sched-linear" "configs/livemathematicianbench/default.yaml" "$LIVEMATH_SKILL" \
|
||||
env.split_dir=data/livemathbench/splits \
|
||||
optimizer.learning_rate=4 optimizer.min_learning_rate=2 optimizer.lr_scheduler=linear
|
||||
launch_run "HARNESS-Claude-LiveMath-minibatch4" "configs/livemathematicianbench/default.yaml" "$LIVEMATH_SKILL" \
|
||||
env.split_dir=data/livemathbench/splits \
|
||||
gradient.minibatch_size=4 optimizer.learning_rate=4 optimizer.min_learning_rate=2 optimizer.lr_scheduler=cosine
|
||||
|
||||
launch_run "HARNESS-Claude-DocVQA10-batch-full" "configs/docvqa/default.yaml" "$DOCVQA_SKILL" \
|
||||
env.split_dir=data/harness_splits/docvqa_zisu_first10pct \
|
||||
train.batch_size=107 optimizer.learning_rate=4 optimizer.min_learning_rate=2 optimizer.lr_scheduler=cosine
|
||||
launch_run "HARNESS-Claude-DocVQA10-lr16" "configs/docvqa/default.yaml" "$DOCVQA_SKILL" \
|
||||
env.split_dir=data/harness_splits/docvqa_zisu_first10pct \
|
||||
optimizer.learning_rate=16 optimizer.min_learning_rate=1 optimizer.lr_scheduler=constant
|
||||
launch_run "HARNESS-Claude-DocVQA10-lr8" "configs/docvqa/default.yaml" "$DOCVQA_SKILL" \
|
||||
env.split_dir=data/harness_splits/docvqa_zisu_first10pct \
|
||||
optimizer.learning_rate=8 optimizer.min_learning_rate=1 optimizer.lr_scheduler=constant
|
||||
launch_run "HARNESS-Claude-DocVQA10-minibatch32" "configs/docvqa/default.yaml" "$DOCVQA_SKILL" \
|
||||
env.split_dir=data/harness_splits/docvqa_zisu_first10pct \
|
||||
gradient.minibatch_size=32 optimizer.learning_rate=4 optimizer.min_learning_rate=2 optimizer.lr_scheduler=cosine
|
||||
launch_run "HARNESS-Claude-DocVQA10-batch24" "configs/docvqa/default.yaml" "$DOCVQA_SKILL" \
|
||||
env.split_dir=data/harness_splits/docvqa_zisu_first10pct \
|
||||
train.batch_size=24 optimizer.learning_rate=4 optimizer.min_learning_rate=2 optimizer.lr_scheduler=cosine
|
||||
|
||||
launch_run "HARNESS-Claude-Spreadsheet-sched-constant-multi" "configs/spreadsheetbench/default.yaml" "$SPREADSHEET_SKILL" \
|
||||
env.split_dir=data/spreadsheetbench env.data_root=data/spreadsheetbench/files env.mode=multi \
|
||||
optimizer.learning_rate=4 optimizer.min_learning_rate=2 optimizer.lr_scheduler=constant
|
||||
launch_run "HARNESS-Claude-Spreadsheet-lr4-multi" "configs/spreadsheetbench/default.yaml" "$SPREADSHEET_SKILL" \
|
||||
env.split_dir=data/spreadsheetbench env.data_root=data/spreadsheetbench/files env.mode=multi \
|
||||
optimizer.learning_rate=4 optimizer.min_learning_rate=1 optimizer.lr_scheduler=constant
|
||||
launch_run "HARNESS-Claude-Spreadsheet-lr16-multi" "configs/spreadsheetbench/default.yaml" "$SPREADSHEET_SKILL" \
|
||||
env.split_dir=data/spreadsheetbench env.data_root=data/spreadsheetbench/files env.mode=multi \
|
||||
optimizer.learning_rate=16 optimizer.min_learning_rate=1 optimizer.lr_scheduler=constant
|
||||
launch_run "HARNESS-Claude-Spreadsheet-minibatch16-multi" "configs/spreadsheetbench/default.yaml" "$SPREADSHEET_SKILL" \
|
||||
env.split_dir=data/spreadsheetbench env.data_root=data/spreadsheetbench/files env.mode=multi \
|
||||
gradient.minibatch_size=16 optimizer.learning_rate=4 optimizer.min_learning_rate=2 optimizer.lr_scheduler=cosine
|
||||
|
||||
echo "RUN_ROOT=$RUN_ROOT"
|
||||
echo "SESSION=$SESSION"
|
||||
130
scripts/launch_harness_canonical_claude4_smoke.sh
Executable file
130
scripts/launch_harness_canonical_claude4_smoke.sh
Executable file
@@ -0,0 +1,130 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
REPO="/home/azureuser/workspace-gzy/SkillReflection"
|
||||
PYTHON="/home/azureuser/workspace-gzy/miniconda3/envs/reflact/bin/python"
|
||||
|
||||
cd "$REPO"
|
||||
|
||||
export ANTHROPIC_BASE_URL="${ANTHROPIC_BASE_URL:-http://127.0.0.1:4343}"
|
||||
export ANTHROPIC_AUTH_TOKEN="${ANTHROPIC_AUTH_TOKEN:-dummy}"
|
||||
export ANTHROPIC_MODEL="${ANTHROPIC_MODEL:-claude-sonnet-4-6}"
|
||||
export ANTHROPIC_SMALL_FAST_MODEL="${ANTHROPIC_SMALL_FAST_MODEL:-claude-sonnet-4-6}"
|
||||
export DISABLE_NON_ESSENTIAL_MODEL_CALLS="${DISABLE_NON_ESSENTIAL_MODEL_CALLS:-1}"
|
||||
export CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC="${CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC:-1}"
|
||||
|
||||
if [[ -f ".secrets/teacher_oaidr9.env" ]]; then
|
||||
# shellcheck disable=SC1091
|
||||
source ".secrets/teacher_oaidr9.env"
|
||||
else
|
||||
echo "missing .secrets/teacher_oaidr9.env" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
stamp="$(date -u +%Y%m%d_%H%M%S)"
|
||||
RUN_ROOT="${1:-outputs/harness_canonical_claude4_smoke_workers2_timeout1020_${stamp}_run}"
|
||||
SESSION="${2:-harness_canon_claude4_${stamp}}"
|
||||
|
||||
mkdir -p "$RUN_ROOT/logs" "$RUN_ROOT/commands"
|
||||
|
||||
COMMON_CFG=(
|
||||
model.teacher_backend=openai_chat
|
||||
model.teacher=gpt-5.5
|
||||
model.teacher_azure_openai_endpoint="${TEACHER_AZURE_OPENAI_ENDPOINT}"
|
||||
model.teacher_azure_openai_api_version="${TEACHER_AZURE_OPENAI_API_VERSION}"
|
||||
model.teacher_azure_openai_auth_mode="${TEACHER_AZURE_OPENAI_AUTH_MODE}"
|
||||
model.teacher_azure_openai_managed_identity_client_id="${TEACHER_AZURE_OPENAI_MANAGED_IDENTITY_CLIENT_ID}"
|
||||
model.teacher_azure_openai_ad_scope="${TEACHER_AZURE_OPENAI_AD_SCOPE}"
|
||||
model.reasoning_effort=medium
|
||||
train.num_epochs=4
|
||||
train.train_size=0
|
||||
train.batch_size=40
|
||||
train.accumulation=1
|
||||
train.seed=42
|
||||
gradient.minibatch_size=8
|
||||
gradient.merge_batch_size=8
|
||||
gradient.analyst_workers=16
|
||||
gradient.use_deep_reflect=false
|
||||
optimizer.lr_control_mode=fixed
|
||||
optimizer.skill_update_mode=patch
|
||||
optimizer.use_slow_update=true
|
||||
optimizer.slow_update_samples=20
|
||||
optimizer.use_meta_skill=true
|
||||
optimizer.use_meta_reflect=false
|
||||
evaluation.use_gate=true
|
||||
evaluation.eval_test=true
|
||||
env.split_mode=split_dir
|
||||
env.workers=2
|
||||
env.exec_timeout=1020
|
||||
model.student_backend=claude_code_exec
|
||||
model.student=claude-sonnet-4-6
|
||||
model.claude_code_exec_use_sdk=sdk
|
||||
model.claude_code_exec_effort=medium
|
||||
model.claude_code_exec_max_thinking_tokens=16384
|
||||
model.codex_trace_to_teacher=false
|
||||
)
|
||||
|
||||
tmux_started=0
|
||||
|
||||
launch_run() {
|
||||
local run_id="$1"
|
||||
local config="$2"
|
||||
local skill="$3"
|
||||
shift 3
|
||||
|
||||
local cmd_file="$RUN_ROOT/commands/${run_id}.sh"
|
||||
local log_file="$RUN_ROOT/logs/${run_id}.log"
|
||||
local out_root="$RUN_ROOT/$run_id"
|
||||
|
||||
local -a cmd=(
|
||||
"$PYTHON" -u scripts/train.py
|
||||
--config "$config"
|
||||
--cfg-options
|
||||
"${COMMON_CFG[@]}"
|
||||
env.skill_init="$skill"
|
||||
env.out_root="$out_root"
|
||||
"$@"
|
||||
)
|
||||
|
||||
{
|
||||
echo "#!/usr/bin/env bash"
|
||||
echo "set -euo pipefail"
|
||||
echo "cd '$REPO'"
|
||||
printf 'export ANTHROPIC_BASE_URL=%q\n' "$ANTHROPIC_BASE_URL"
|
||||
printf 'export ANTHROPIC_AUTH_TOKEN=%q\n' "$ANTHROPIC_AUTH_TOKEN"
|
||||
printf 'export ANTHROPIC_MODEL=%q\n' "$ANTHROPIC_MODEL"
|
||||
printf 'export ANTHROPIC_SMALL_FAST_MODEL=%q\n' "$ANTHROPIC_SMALL_FAST_MODEL"
|
||||
printf 'export DISABLE_NON_ESSENTIAL_MODEL_CALLS=%q\n' "$DISABLE_NON_ESSENTIAL_MODEL_CALLS"
|
||||
printf 'export CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=%q\n' "$CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC"
|
||||
printf '%q ' "${cmd[@]}"
|
||||
printf ' >%q 2>&1 < /dev/null\n' "$log_file"
|
||||
} > "$cmd_file"
|
||||
chmod +x "$cmd_file"
|
||||
|
||||
if [[ "$tmux_started" -eq 0 ]]; then
|
||||
tmux new-session -d -s "$SESSION" -n "$run_id" "bash '$cmd_file'; code=\$?; echo EXIT:\$code; sleep 3600"
|
||||
tmux_started=1
|
||||
else
|
||||
tmux new-window -t "$SESSION" -n "$run_id" "bash '$cmd_file'; code=\$?; echo EXIT:\$code; sleep 3600"
|
||||
fi
|
||||
echo "$run_id"
|
||||
}
|
||||
|
||||
launch_run "HARNESS-Claude-SearchQA-sched-constant" "configs/searchqa/default.yaml" "docs/harness_source_skills/searchqa_best_skill.md" \
|
||||
env.split_dir=data/searchqa/splits \
|
||||
optimizer.learning_rate=4 optimizer.min_learning_rate=2 optimizer.lr_scheduler=constant
|
||||
|
||||
launch_run "HARNESS-Claude-LiveMath-lr8" "configs/livemathematicianbench/default.yaml" "docs/harness_source_skills/livemathematicianbench_best_skill.md" \
|
||||
env.split_dir=data/livemathbench/splits \
|
||||
optimizer.learning_rate=8 optimizer.min_learning_rate=1 optimizer.lr_scheduler=constant
|
||||
|
||||
launch_run "HARNESS-Claude-DocVQA10-lr8" "configs/docvqa/default.yaml" "docs/harness_source_skills/docvqa_best_skill.md" \
|
||||
env.split_dir=data/harness_splits/docvqa_zisu_first10pct \
|
||||
optimizer.learning_rate=8 optimizer.min_learning_rate=1 optimizer.lr_scheduler=constant
|
||||
|
||||
launch_run "HARNESS-Claude-Spreadsheet-lr4-multi" "configs/spreadsheetbench/default.yaml" "docs/harness_source_skills/spreadsheetbench_best_skill.md" \
|
||||
env.split_dir=data/spreadsheetbench env.data_root=data/spreadsheetbench/files env.mode=multi \
|
||||
optimizer.learning_rate=4 optimizer.min_learning_rate=1 optimizer.lr_scheduler=constant
|
||||
|
||||
echo "RUN_ROOT=$RUN_ROOT"
|
||||
echo "SESSION=$SESSION"
|
||||
168
scripts/launch_harness_canonical_wave1.sh
Executable file
168
scripts/launch_harness_canonical_wave1.sh
Executable file
@@ -0,0 +1,168 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
REPO="/home/azureuser/workspace-gzy/SkillReflection"
|
||||
PYTHON="/home/azureuser/workspace-gzy/miniconda3/envs/reflact/bin/python"
|
||||
CODEX_WRAPPER="$REPO/scripts/codex_azure_mi.sh"
|
||||
|
||||
cd "$REPO"
|
||||
|
||||
# Claude Code is routed through the local copilot-api proxy on this machine.
|
||||
# Do not rely on interactive Claude login state inside tmux/train workers.
|
||||
export ANTHROPIC_BASE_URL="${ANTHROPIC_BASE_URL:-http://127.0.0.1:4343}"
|
||||
export ANTHROPIC_AUTH_TOKEN="${ANTHROPIC_AUTH_TOKEN:-dummy}"
|
||||
export ANTHROPIC_MODEL="${ANTHROPIC_MODEL:-claude-sonnet-4-6}"
|
||||
export ANTHROPIC_SMALL_FAST_MODEL="${ANTHROPIC_SMALL_FAST_MODEL:-claude-sonnet-4-6}"
|
||||
export DISABLE_NON_ESSENTIAL_MODEL_CALLS="${DISABLE_NON_ESSENTIAL_MODEL_CALLS:-1}"
|
||||
export CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC="${CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC:-1}"
|
||||
|
||||
if [[ -f ".secrets/teacher_oaidr9.env" ]]; then
|
||||
# shellcheck disable=SC1091
|
||||
source ".secrets/teacher_oaidr9.env"
|
||||
else
|
||||
echo "missing .secrets/teacher_oaidr9.env" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
stamp="$(date -u +%Y%m%d_%H%M%S)"
|
||||
RUN_ROOT="${1:-outputs/harness_canonical_step12_wave1_workers2_timeout1020_${stamp}_run}"
|
||||
SESSION="${2:-harness_canon_wave1_${stamp}}"
|
||||
|
||||
mkdir -p "$RUN_ROOT/logs" "$RUN_ROOT/commands"
|
||||
|
||||
COMMON_CFG=(
|
||||
model.teacher_backend=openai_chat
|
||||
model.teacher=gpt-5.5
|
||||
model.teacher_azure_openai_endpoint="${TEACHER_AZURE_OPENAI_ENDPOINT}"
|
||||
model.teacher_azure_openai_api_version="${TEACHER_AZURE_OPENAI_API_VERSION}"
|
||||
model.teacher_azure_openai_auth_mode="${TEACHER_AZURE_OPENAI_AUTH_MODE}"
|
||||
model.teacher_azure_openai_managed_identity_client_id="${TEACHER_AZURE_OPENAI_MANAGED_IDENTITY_CLIENT_ID}"
|
||||
model.teacher_azure_openai_ad_scope="${TEACHER_AZURE_OPENAI_AD_SCOPE}"
|
||||
model.reasoning_effort=medium
|
||||
train.num_epochs=4
|
||||
train.train_size=0
|
||||
train.batch_size=40
|
||||
train.accumulation=1
|
||||
train.seed=42
|
||||
gradient.minibatch_size=8
|
||||
gradient.merge_batch_size=8
|
||||
gradient.analyst_workers=16
|
||||
gradient.use_deep_reflect=false
|
||||
optimizer.lr_control_mode=fixed
|
||||
optimizer.skill_update_mode=patch
|
||||
optimizer.use_slow_update=true
|
||||
optimizer.slow_update_samples=20
|
||||
optimizer.use_meta_skill=true
|
||||
optimizer.use_meta_reflect=false
|
||||
evaluation.use_gate=true
|
||||
evaluation.eval_test=true
|
||||
env.split_mode=split_dir
|
||||
env.workers=2
|
||||
env.exec_timeout=1020
|
||||
)
|
||||
|
||||
tmux_started=0
|
||||
|
||||
launch_run() {
|
||||
local run_id="$1"
|
||||
local backend="$2"
|
||||
local config="$3"
|
||||
local skill="$4"
|
||||
local split_dir="$5"
|
||||
shift 5
|
||||
|
||||
local -a backend_cfg=()
|
||||
if [[ "$backend" == "codex" ]]; then
|
||||
backend_cfg=(
|
||||
model.student_backend=codex_exec
|
||||
model.student=gpt-5.5
|
||||
model.codex_exec_path="$CODEX_WRAPPER"
|
||||
model.codex_exec_use_sdk=auto
|
||||
model.codex_exec_sandbox=workspace-write
|
||||
model.codex_exec_approval_policy=never
|
||||
model.codex_exec_reasoning_effort=medium
|
||||
model.codex_trace_to_teacher=true
|
||||
)
|
||||
elif [[ "$backend" == "claude" ]]; then
|
||||
backend_cfg=(
|
||||
model.student_backend=claude_code_exec
|
||||
model.student=claude-sonnet-4-6
|
||||
model.claude_code_exec_use_sdk=sdk
|
||||
model.claude_code_exec_effort=medium
|
||||
model.claude_code_exec_max_thinking_tokens=16384
|
||||
model.codex_trace_to_teacher=false
|
||||
)
|
||||
else
|
||||
echo "unknown backend: $backend" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
local cmd_file="$RUN_ROOT/commands/${run_id}.sh"
|
||||
local log_file="$RUN_ROOT/logs/${run_id}.log"
|
||||
local out_root="$RUN_ROOT/$run_id"
|
||||
|
||||
local -a cmd=(
|
||||
"$PYTHON" -u scripts/train.py
|
||||
--config "$config"
|
||||
--cfg-options
|
||||
"${COMMON_CFG[@]}"
|
||||
"${backend_cfg[@]}"
|
||||
env.split_dir="$split_dir"
|
||||
env.skill_init="$skill"
|
||||
env.out_root="$out_root"
|
||||
"$@"
|
||||
)
|
||||
|
||||
{
|
||||
echo "#!/usr/bin/env bash"
|
||||
echo "set -euo pipefail"
|
||||
echo "cd '$REPO'"
|
||||
printf 'export ANTHROPIC_BASE_URL=%q\n' "$ANTHROPIC_BASE_URL"
|
||||
printf 'export ANTHROPIC_AUTH_TOKEN=%q\n' "$ANTHROPIC_AUTH_TOKEN"
|
||||
printf 'export ANTHROPIC_MODEL=%q\n' "$ANTHROPIC_MODEL"
|
||||
printf 'export ANTHROPIC_SMALL_FAST_MODEL=%q\n' "$ANTHROPIC_SMALL_FAST_MODEL"
|
||||
printf 'export DISABLE_NON_ESSENTIAL_MODEL_CALLS=%q\n' "$DISABLE_NON_ESSENTIAL_MODEL_CALLS"
|
||||
printf 'export CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=%q\n' "$CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC"
|
||||
printf '%q ' "${cmd[@]}"
|
||||
printf ' >%q 2>&1 < /dev/null\n' "$log_file"
|
||||
} > "$cmd_file"
|
||||
chmod +x "$cmd_file"
|
||||
|
||||
if [[ "$tmux_started" -eq 0 ]]; then
|
||||
tmux new-session -d -s "$SESSION" -n "$run_id" "bash '$cmd_file'; code=\$?; echo EXIT:\$code; sleep 3600"
|
||||
tmux_started=1
|
||||
else
|
||||
tmux new-window -t "$SESSION" -n "$run_id" "bash '$cmd_file'; code=\$?; echo EXIT:\$code; sleep 3600"
|
||||
fi
|
||||
echo "$run_id"
|
||||
}
|
||||
|
||||
SEARCHQA_SKILL="docs/harness_source_skills/searchqa_best_skill.md"
|
||||
LIVEMATH_SKILL="docs/harness_source_skills/livemathematicianbench_best_skill.md"
|
||||
|
||||
SEARCHQA_CFG="configs/searchqa/default.yaml"
|
||||
LIVEMATH_CFG="configs/livemathematicianbench/default.yaml"
|
||||
|
||||
SEARCHQA_SPLIT="data/searchqa/splits"
|
||||
LIVEMATH_SPLIT="data/livemathbench/splits"
|
||||
|
||||
for backend in codex claude; do
|
||||
prefix="HARNESS-Codex"
|
||||
[[ "$backend" == "claude" ]] && prefix="HARNESS-Claude"
|
||||
|
||||
launch_run "${prefix}-SearchQA-sched-constant" "$backend" "$SEARCHQA_CFG" "$SEARCHQA_SKILL" "$SEARCHQA_SPLIT" \
|
||||
optimizer.learning_rate=4 optimizer.min_learning_rate=2 optimizer.lr_scheduler=constant
|
||||
launch_run "${prefix}-SearchQA-sched-linear" "$backend" "$SEARCHQA_CFG" "$SEARCHQA_SKILL" "$SEARCHQA_SPLIT" \
|
||||
optimizer.learning_rate=4 optimizer.min_learning_rate=2 optimizer.lr_scheduler=linear
|
||||
launch_run "${prefix}-SearchQA-batch-full" "$backend" "$SEARCHQA_CFG" "$SEARCHQA_SKILL" "$SEARCHQA_SPLIT" \
|
||||
train.batch_size=400 optimizer.learning_rate=4 optimizer.min_learning_rate=2 optimizer.lr_scheduler=cosine
|
||||
launch_run "${prefix}-SearchQA-lr8" "$backend" "$SEARCHQA_CFG" "$SEARCHQA_SKILL" "$SEARCHQA_SPLIT" \
|
||||
optimizer.learning_rate=8 optimizer.min_learning_rate=1 optimizer.lr_scheduler=constant
|
||||
launch_run "${prefix}-LiveMath-lr8" "$backend" "$LIVEMATH_CFG" "$LIVEMATH_SKILL" "$LIVEMATH_SPLIT" \
|
||||
optimizer.learning_rate=8 optimizer.min_learning_rate=1 optimizer.lr_scheduler=constant
|
||||
launch_run "${prefix}-LiveMath-lr16" "$backend" "$LIVEMATH_CFG" "$LIVEMATH_SKILL" "$LIVEMATH_SPLIT" \
|
||||
optimizer.learning_rate=16 optimizer.min_learning_rate=1 optimizer.lr_scheduler=constant
|
||||
done
|
||||
|
||||
echo "RUN_ROOT=$RUN_ROOT"
|
||||
echo "SESSION=$SESSION"
|
||||
128
scripts/launch_harness_initial_claude4.sh
Executable file
128
scripts/launch_harness_initial_claude4.sh
Executable file
@@ -0,0 +1,128 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
REPO="/home/azureuser/workspace-gzy/SkillReflection"
|
||||
PYTHON="/home/azureuser/workspace-gzy/miniconda3/envs/reflact/bin/python"
|
||||
|
||||
cd "$REPO"
|
||||
|
||||
export ANTHROPIC_BASE_URL="${ANTHROPIC_BASE_URL:-http://127.0.0.1:4343}"
|
||||
export ANTHROPIC_AUTH_TOKEN="${ANTHROPIC_AUTH_TOKEN:-dummy}"
|
||||
export ANTHROPIC_MODEL="${ANTHROPIC_MODEL:-claude-sonnet-4-6}"
|
||||
export ANTHROPIC_SMALL_FAST_MODEL="${ANTHROPIC_SMALL_FAST_MODEL:-claude-sonnet-4-6}"
|
||||
export DISABLE_NON_ESSENTIAL_MODEL_CALLS="${DISABLE_NON_ESSENTIAL_MODEL_CALLS:-1}"
|
||||
export CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC="${CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC:-1}"
|
||||
|
||||
if [[ -f ".secrets/teacher_oaidr9.env" ]]; then
|
||||
# shellcheck disable=SC1091
|
||||
source ".secrets/teacher_oaidr9.env"
|
||||
else
|
||||
echo "missing .secrets/teacher_oaidr9.env" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
stamp="$(date -u +%Y%m%d_%H%M%S)"
|
||||
RUN_ROOT="${1:-outputs/harness_initial_claude4_workers2_timeout1020_${stamp}_run}"
|
||||
SESSION="${2:-harness_initial_claude4_${stamp}}"
|
||||
|
||||
mkdir -p "$RUN_ROOT/logs" "$RUN_ROOT/commands"
|
||||
|
||||
COMMON_CFG=(
|
||||
model.teacher_backend=openai_chat
|
||||
model.teacher=gpt-5.5
|
||||
model.teacher_azure_openai_endpoint="${TEACHER_AZURE_OPENAI_ENDPOINT}"
|
||||
model.teacher_azure_openai_api_version="${TEACHER_AZURE_OPENAI_API_VERSION}"
|
||||
model.teacher_azure_openai_auth_mode="${TEACHER_AZURE_OPENAI_AUTH_MODE}"
|
||||
model.teacher_azure_openai_managed_identity_client_id="${TEACHER_AZURE_OPENAI_MANAGED_IDENTITY_CLIENT_ID}"
|
||||
model.teacher_azure_openai_ad_scope="${TEACHER_AZURE_OPENAI_AD_SCOPE}"
|
||||
model.reasoning_effort=medium
|
||||
train.num_epochs=4
|
||||
train.train_size=0
|
||||
train.batch_size=40
|
||||
train.accumulation=1
|
||||
train.seed=42
|
||||
gradient.minibatch_size=8
|
||||
gradient.merge_batch_size=8
|
||||
gradient.analyst_workers=16
|
||||
gradient.use_deep_reflect=false
|
||||
optimizer.lr_control_mode=fixed
|
||||
optimizer.skill_update_mode=patch
|
||||
optimizer.use_slow_update=true
|
||||
optimizer.slow_update_samples=20
|
||||
optimizer.use_meta_skill=true
|
||||
optimizer.use_meta_reflect=false
|
||||
evaluation.use_gate=true
|
||||
evaluation.eval_test=true
|
||||
env.split_mode=split_dir
|
||||
env.workers=2
|
||||
env.exec_timeout=1020
|
||||
model.student_backend=claude_code_exec
|
||||
model.student=claude-sonnet-4-6
|
||||
model.claude_code_exec_use_sdk=sdk
|
||||
model.claude_code_exec_effort=medium
|
||||
model.claude_code_exec_max_thinking_tokens=16384
|
||||
model.codex_trace_to_teacher=false
|
||||
)
|
||||
|
||||
tmux_started=0
|
||||
|
||||
launch_run() {
|
||||
local run_id="$1"
|
||||
local config="$2"
|
||||
shift 2
|
||||
|
||||
local cmd_file="$RUN_ROOT/commands/${run_id}.sh"
|
||||
local log_file="$RUN_ROOT/logs/${run_id}.log"
|
||||
local out_root="$RUN_ROOT/$run_id"
|
||||
|
||||
local -a cmd=(
|
||||
"$PYTHON" -u scripts/train.py
|
||||
--config "$config"
|
||||
--cfg-options
|
||||
"${COMMON_CFG[@]}"
|
||||
env.out_root="$out_root"
|
||||
"$@"
|
||||
)
|
||||
|
||||
{
|
||||
echo "#!/usr/bin/env bash"
|
||||
echo "set -euo pipefail"
|
||||
echo "cd '$REPO'"
|
||||
printf 'export ANTHROPIC_BASE_URL=%q\n' "$ANTHROPIC_BASE_URL"
|
||||
printf 'export ANTHROPIC_AUTH_TOKEN=%q\n' "$ANTHROPIC_AUTH_TOKEN"
|
||||
printf 'export ANTHROPIC_MODEL=%q\n' "$ANTHROPIC_MODEL"
|
||||
printf 'export ANTHROPIC_SMALL_FAST_MODEL=%q\n' "$ANTHROPIC_SMALL_FAST_MODEL"
|
||||
printf 'export DISABLE_NON_ESSENTIAL_MODEL_CALLS=%q\n' "$DISABLE_NON_ESSENTIAL_MODEL_CALLS"
|
||||
printf 'export CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=%q\n' "$CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC"
|
||||
printf '%q ' "${cmd[@]}"
|
||||
printf ' >%q 2>&1 < /dev/null\n' "$log_file"
|
||||
} > "$cmd_file"
|
||||
chmod +x "$cmd_file"
|
||||
|
||||
if [[ "$tmux_started" -eq 0 ]]; then
|
||||
tmux new-session -d -s "$SESSION" -n "$run_id" "bash '$cmd_file'; code=\$?; echo EXIT:\$code; sleep 3600"
|
||||
tmux_started=1
|
||||
else
|
||||
tmux new-window -t "$SESSION" -n "$run_id" "bash '$cmd_file'; code=\$?; echo EXIT:\$code; sleep 3600"
|
||||
fi
|
||||
echo "$run_id"
|
||||
}
|
||||
|
||||
launch_run "HARNESS-ClaudeInit-SearchQA-sched-constant" "configs/searchqa/default.yaml" \
|
||||
env.split_dir=data/searchqa/splits \
|
||||
optimizer.learning_rate=4 optimizer.min_learning_rate=2 optimizer.lr_scheduler=constant
|
||||
|
||||
launch_run "HARNESS-ClaudeInit-LiveMath-lr8" "configs/livemathematicianbench/default.yaml" \
|
||||
env.split_dir=data/livemathbench/splits \
|
||||
optimizer.learning_rate=8 optimizer.min_learning_rate=1 optimizer.lr_scheduler=constant
|
||||
|
||||
launch_run "HARNESS-ClaudeInit-DocVQA10-lr8" "configs/docvqa/default.yaml" \
|
||||
env.split_dir=data/harness_splits/docvqa_zisu_first10pct \
|
||||
optimizer.learning_rate=8 optimizer.min_learning_rate=1 optimizer.lr_scheduler=constant
|
||||
|
||||
launch_run "HARNESS-ClaudeInit-Spreadsheet-lr4-multi" "configs/spreadsheetbench/default.yaml" \
|
||||
env.split_dir=data/spreadsheetbench env.data_root=data/spreadsheetbench/files env.mode=multi \
|
||||
optimizer.learning_rate=4 optimizer.min_learning_rate=1 optimizer.lr_scheduler=constant
|
||||
|
||||
echo "RUN_ROOT=$RUN_ROOT"
|
||||
echo "SESSION=$SESSION"
|
||||
103
scripts/launch_harness_initial_spreadsheet_clean.sh
Executable file
103
scripts/launch_harness_initial_spreadsheet_clean.sh
Executable file
@@ -0,0 +1,103 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
REPO="/home/azureuser/workspace-gzy/SkillReflection"
|
||||
PYTHON="/home/azureuser/workspace-gzy/miniconda3/envs/reflact/bin/python"
|
||||
|
||||
cd "$REPO"
|
||||
|
||||
export ANTHROPIC_BASE_URL="${ANTHROPIC_BASE_URL:-http://127.0.0.1:4343}"
|
||||
export ANTHROPIC_AUTH_TOKEN="${ANTHROPIC_AUTH_TOKEN:-dummy}"
|
||||
export ANTHROPIC_MODEL="${ANTHROPIC_MODEL:-claude-sonnet-4-6}"
|
||||
export ANTHROPIC_SMALL_FAST_MODEL="${ANTHROPIC_SMALL_FAST_MODEL:-claude-sonnet-4-6}"
|
||||
export DISABLE_NON_ESSENTIAL_MODEL_CALLS="${DISABLE_NON_ESSENTIAL_MODEL_CALLS:-1}"
|
||||
export CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC="${CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC:-1}"
|
||||
|
||||
if [[ -f ".secrets/teacher_oaidr9.env" ]]; then
|
||||
# shellcheck disable=SC1091
|
||||
source ".secrets/teacher_oaidr9.env"
|
||||
else
|
||||
echo "missing .secrets/teacher_oaidr9.env" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
stamp="$(date -u +%Y%m%d_%H%M%S)"
|
||||
RUN_ROOT="${1:-outputs/harness_initial_spreadsheet_clean_workers2_timeout1020_${stamp}_run}"
|
||||
SESSION="${2:-harness_initial_spreadsheet_clean_${stamp}}"
|
||||
RUN_ID="HARNESS-ClaudeInit-Spreadsheet-lr4-multi-clean"
|
||||
SPLIT_DIR="${SPREADSHEET_SPLIT_DIR:-data/harness_splits/spreadsheetbench_full}"
|
||||
DATA_ROOT="${SPREADSHEET_DATA_ROOT:-data/spreadsheetbench/files}"
|
||||
|
||||
mkdir -p "$RUN_ROOT/logs" "$RUN_ROOT/commands"
|
||||
|
||||
cmd_file="$RUN_ROOT/commands/${RUN_ID}.sh"
|
||||
log_file="$RUN_ROOT/logs/${RUN_ID}.log"
|
||||
out_root="$RUN_ROOT/$RUN_ID"
|
||||
|
||||
cmd=(
|
||||
"$PYTHON" -u scripts/train.py
|
||||
--config configs/spreadsheetbench/default.yaml
|
||||
--cfg-options
|
||||
model.teacher_backend=openai_chat
|
||||
model.teacher=gpt-5.5
|
||||
model.teacher_azure_openai_endpoint="${TEACHER_AZURE_OPENAI_ENDPOINT}"
|
||||
model.teacher_azure_openai_api_version="${TEACHER_AZURE_OPENAI_API_VERSION}"
|
||||
model.teacher_azure_openai_auth_mode="${TEACHER_AZURE_OPENAI_AUTH_MODE}"
|
||||
model.teacher_azure_openai_managed_identity_client_id="${TEACHER_AZURE_OPENAI_MANAGED_IDENTITY_CLIENT_ID}"
|
||||
model.teacher_azure_openai_ad_scope="${TEACHER_AZURE_OPENAI_AD_SCOPE}"
|
||||
model.reasoning_effort=medium
|
||||
train.num_epochs=4
|
||||
train.train_size=0
|
||||
train.batch_size=40
|
||||
train.accumulation=1
|
||||
train.seed=42
|
||||
gradient.minibatch_size=8
|
||||
gradient.merge_batch_size=8
|
||||
gradient.analyst_workers=16
|
||||
gradient.use_deep_reflect=false
|
||||
optimizer.lr_control_mode=fixed
|
||||
optimizer.skill_update_mode=patch
|
||||
optimizer.use_slow_update=true
|
||||
optimizer.slow_update_samples=20
|
||||
optimizer.use_meta_skill=true
|
||||
optimizer.use_meta_reflect=false
|
||||
evaluation.use_gate=true
|
||||
evaluation.eval_test=true
|
||||
env.split_mode=split_dir
|
||||
env.workers=2
|
||||
env.exec_timeout=1020
|
||||
model.student_backend=claude_code_exec
|
||||
model.student=claude-sonnet-4-6
|
||||
model.claude_code_exec_use_sdk=sdk
|
||||
model.claude_code_exec_effort=medium
|
||||
model.claude_code_exec_max_thinking_tokens=16384
|
||||
model.codex_trace_to_teacher=false
|
||||
env.out_root="$out_root"
|
||||
env.split_dir="$SPLIT_DIR"
|
||||
env.data_root="$DATA_ROOT"
|
||||
env.mode=multi
|
||||
optimizer.learning_rate=4
|
||||
optimizer.min_learning_rate=1
|
||||
optimizer.lr_scheduler=constant
|
||||
)
|
||||
|
||||
{
|
||||
echo "#!/usr/bin/env bash"
|
||||
echo "set -euo pipefail"
|
||||
echo "cd '$REPO'"
|
||||
printf 'export ANTHROPIC_BASE_URL=%q\n' "$ANTHROPIC_BASE_URL"
|
||||
printf 'export ANTHROPIC_AUTH_TOKEN=%q\n' "$ANTHROPIC_AUTH_TOKEN"
|
||||
printf 'export ANTHROPIC_MODEL=%q\n' "$ANTHROPIC_MODEL"
|
||||
printf 'export ANTHROPIC_SMALL_FAST_MODEL=%q\n' "$ANTHROPIC_SMALL_FAST_MODEL"
|
||||
printf 'export DISABLE_NON_ESSENTIAL_MODEL_CALLS=%q\n' "$DISABLE_NON_ESSENTIAL_MODEL_CALLS"
|
||||
printf 'export CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=%q\n' "$CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC"
|
||||
printf '%q ' "${cmd[@]}"
|
||||
printf ' >%q 2>&1 < /dev/null\n' "$log_file"
|
||||
} > "$cmd_file"
|
||||
chmod +x "$cmd_file"
|
||||
|
||||
tmux new-session -d -s "$SESSION" -n "$RUN_ID" "bash '$cmd_file'; code=\$?; echo EXIT:\$code; sleep 3600"
|
||||
|
||||
echo "RUN_ROOT=$RUN_ROOT"
|
||||
echo "SESSION=$SESSION"
|
||||
echo "RUN_ID=$RUN_ID"
|
||||
116
scripts/launch_lrctrl_fullrewrite_neutral3.sh
Executable file
116
scripts/launch_lrctrl_fullrewrite_neutral3.sh
Executable file
@@ -0,0 +1,116 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
REPO="/home/azureuser/workspace-gzy/SkillReflection"
|
||||
PYTHON="/home/azureuser/workspace-gzy/miniconda3/envs/reflact/bin/python"
|
||||
|
||||
cd "$REPO"
|
||||
|
||||
if [[ -f ".secrets/teacher_oaidr9.env" ]]; then
|
||||
# shellcheck disable=SC1091
|
||||
source ".secrets/teacher_oaidr9.env"
|
||||
else
|
||||
echo "missing .secrets/teacher_oaidr9.env" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
stamp="$(date -u +%Y%m%d_%H%M%S)"
|
||||
RUN_ROOT="${1:-outputs/lrctrl_fullrewrite_neutral3_workers2_timeout1020_${stamp}_run}"
|
||||
SESSION="${2:-lrctrl_fullrewrite_neutral3_${stamp}}"
|
||||
SEED="${3:-42}"
|
||||
|
||||
mkdir -p "$RUN_ROOT/logs" "$RUN_ROOT/commands"
|
||||
|
||||
COMMON_CFG=(
|
||||
model.teacher_backend=openai_chat
|
||||
model.student_backend=openai_chat
|
||||
model.teacher=gpt-5.5
|
||||
model.student=gpt-5.5
|
||||
model.teacher_azure_openai_endpoint="${TEACHER_AZURE_OPENAI_ENDPOINT}"
|
||||
model.teacher_azure_openai_api_version="${TEACHER_AZURE_OPENAI_API_VERSION}"
|
||||
model.teacher_azure_openai_auth_mode="${TEACHER_AZURE_OPENAI_AUTH_MODE}"
|
||||
model.teacher_azure_openai_managed_identity_client_id="${TEACHER_AZURE_OPENAI_MANAGED_IDENTITY_CLIENT_ID}"
|
||||
model.teacher_azure_openai_ad_scope="${TEACHER_AZURE_OPENAI_AD_SCOPE}"
|
||||
model.student_azure_openai_endpoint="${STUDENT_AZURE_OPENAI_ENDPOINT:-$TEACHER_AZURE_OPENAI_ENDPOINT}"
|
||||
model.student_azure_openai_api_version="${STUDENT_AZURE_OPENAI_API_VERSION:-$TEACHER_AZURE_OPENAI_API_VERSION}"
|
||||
model.student_azure_openai_auth_mode="${STUDENT_AZURE_OPENAI_AUTH_MODE:-$TEACHER_AZURE_OPENAI_AUTH_MODE}"
|
||||
model.student_azure_openai_managed_identity_client_id="${STUDENT_AZURE_OPENAI_MANAGED_IDENTITY_CLIENT_ID:-$TEACHER_AZURE_OPENAI_MANAGED_IDENTITY_CLIENT_ID}"
|
||||
model.student_azure_openai_ad_scope="${STUDENT_AZURE_OPENAI_AD_SCOPE:-$TEACHER_AZURE_OPENAI_AD_SCOPE}"
|
||||
model.reasoning_effort=medium
|
||||
train.num_epochs=4
|
||||
train.train_size=0
|
||||
train.batch_size=40
|
||||
train.accumulation=1
|
||||
train.seed="${SEED}"
|
||||
gradient.minibatch_size=8
|
||||
gradient.merge_batch_size=8
|
||||
gradient.analyst_workers=16
|
||||
gradient.use_deep_reflect=false
|
||||
optimizer.learning_rate=4
|
||||
optimizer.min_learning_rate=2
|
||||
optimizer.lr_scheduler=cosine
|
||||
optimizer.lr_control_mode=none
|
||||
optimizer.skill_update_mode=full_rewrite_minibatch
|
||||
optimizer.use_slow_update=true
|
||||
optimizer.slow_update_samples=20
|
||||
optimizer.use_meta_skill=true
|
||||
optimizer.use_meta_reflect=false
|
||||
evaluation.use_gate=true
|
||||
evaluation.eval_test=true
|
||||
env.split_mode=split_dir
|
||||
env.workers=2
|
||||
env.exec_timeout=1020
|
||||
)
|
||||
|
||||
tmux_started=0
|
||||
|
||||
launch_run() {
|
||||
local run_id="$1"
|
||||
local config="$2"
|
||||
shift 2
|
||||
|
||||
local cmd_file="$RUN_ROOT/commands/${run_id}.sh"
|
||||
local log_file="$RUN_ROOT/logs/${run_id}.log"
|
||||
local out_root="$RUN_ROOT/$run_id"
|
||||
|
||||
local -a cmd=(
|
||||
"$PYTHON" -u scripts/train.py
|
||||
--config "$config"
|
||||
--cfg-options
|
||||
"${COMMON_CFG[@]}"
|
||||
env.out_root="$out_root"
|
||||
"$@"
|
||||
)
|
||||
|
||||
{
|
||||
echo "#!/usr/bin/env bash"
|
||||
echo "set -euo pipefail"
|
||||
echo "cd '$REPO'"
|
||||
printf '%q ' "${cmd[@]}"
|
||||
printf ' >%q 2>&1 < /dev/null\n' "$log_file"
|
||||
} > "$cmd_file"
|
||||
chmod +x "$cmd_file"
|
||||
|
||||
if [[ "$tmux_started" -eq 0 ]]; then
|
||||
tmux new-session -d -s "$SESSION" -n "$run_id" "bash '$cmd_file'; code=\$?; echo EXIT:\$code; sleep 3600"
|
||||
tmux_started=1
|
||||
else
|
||||
tmux new-window -t "$SESSION" -n "$run_id" "bash '$cmd_file'; code=\$?; echo EXIT:\$code; sleep 3600"
|
||||
fi
|
||||
echo "$run_id"
|
||||
}
|
||||
|
||||
launch_run "LRCTRL-searchqa-full-rewrite-neutral3-seed${SEED}" "configs/searchqa/default.yaml" \
|
||||
env.split_dir=data/ablation_splits/searchqa/2-1-7_seed42
|
||||
|
||||
launch_run "LRCTRL-spreadsheetbench-full-rewrite-neutral3-seed${SEED}" "configs/spreadsheetbench/default.yaml" \
|
||||
env.split_dir=data/ablation_splits/spreadsheetbench/2-1-7_seed42 \
|
||||
env.data_root=data/spreadsheetbench_verified_400 \
|
||||
env.mode=multi
|
||||
|
||||
launch_run "LRCTRL-livemathematicianbench-full-rewrite-neutral3-seed${SEED}" "configs/livemathematicianbench/default.yaml" \
|
||||
env.split_dir=data/ablation_splits/livemathematicianbench/2-1-7_seed42
|
||||
|
||||
echo "RUN_ROOT=$RUN_ROOT"
|
||||
echo "SESSION=$SESSION"
|
||||
echo "SEED=$SEED"
|
||||
111
scripts/launch_lrctrl_fullrewrite_neutral3_spreadsheet_repeats.sh
Executable file
111
scripts/launch_lrctrl_fullrewrite_neutral3_spreadsheet_repeats.sh
Executable file
@@ -0,0 +1,111 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
REPO="/home/azureuser/workspace-gzy/SkillReflection"
|
||||
PYTHON="/home/azureuser/workspace-gzy/miniconda3/envs/reflact/bin/python"
|
||||
|
||||
cd "$REPO"
|
||||
|
||||
if [[ -f ".secrets/teacher_oaidr9.env" ]]; then
|
||||
# shellcheck disable=SC1091
|
||||
source ".secrets/teacher_oaidr9.env"
|
||||
else
|
||||
echo "missing .secrets/teacher_oaidr9.env" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
stamp="$(date -u +%Y%m%d_%H%M%S)"
|
||||
RUN_ROOT="${1:-outputs/lrctrl_fullrewrite_neutral3_spreadsheet_promptweak_workers2_timeout1020_${stamp}_run}"
|
||||
SESSION="${2:-lrctrl_fr_spreadsheet_promptweak_${stamp}}"
|
||||
START_INDEX="${3:-4}"
|
||||
N_REPEATS="${4:-3}"
|
||||
|
||||
mkdir -p "$RUN_ROOT/logs" "$RUN_ROOT/commands"
|
||||
|
||||
COMMON_CFG=(
|
||||
model.teacher_backend=openai_chat
|
||||
model.student_backend=openai_chat
|
||||
model.teacher=gpt-5.5
|
||||
model.student=gpt-5.5
|
||||
model.teacher_azure_openai_endpoint="${TEACHER_AZURE_OPENAI_ENDPOINT}"
|
||||
model.teacher_azure_openai_api_version="${TEACHER_AZURE_OPENAI_API_VERSION}"
|
||||
model.teacher_azure_openai_auth_mode="${TEACHER_AZURE_OPENAI_AUTH_MODE}"
|
||||
model.teacher_azure_openai_managed_identity_client_id="${TEACHER_AZURE_OPENAI_MANAGED_IDENTITY_CLIENT_ID}"
|
||||
model.teacher_azure_openai_ad_scope="${TEACHER_AZURE_OPENAI_AD_SCOPE}"
|
||||
model.student_azure_openai_endpoint="${STUDENT_AZURE_OPENAI_ENDPOINT:-$TEACHER_AZURE_OPENAI_ENDPOINT}"
|
||||
model.student_azure_openai_api_version="${STUDENT_AZURE_OPENAI_API_VERSION:-$TEACHER_AZURE_OPENAI_API_VERSION}"
|
||||
model.student_azure_openai_auth_mode="${STUDENT_AZURE_OPENAI_AUTH_MODE:-$TEACHER_AZURE_OPENAI_AUTH_MODE}"
|
||||
model.student_azure_openai_managed_identity_client_id="${STUDENT_AZURE_OPENAI_MANAGED_IDENTITY_CLIENT_ID:-$TEACHER_AZURE_OPENAI_MANAGED_IDENTITY_CLIENT_ID}"
|
||||
model.student_azure_openai_ad_scope="${STUDENT_AZURE_OPENAI_AD_SCOPE:-$TEACHER_AZURE_OPENAI_AD_SCOPE}"
|
||||
model.reasoning_effort=medium
|
||||
train.num_epochs=4
|
||||
train.train_size=0
|
||||
train.batch_size=40
|
||||
train.accumulation=1
|
||||
train.seed=42
|
||||
gradient.minibatch_size=8
|
||||
gradient.merge_batch_size=8
|
||||
gradient.analyst_workers=16
|
||||
gradient.use_deep_reflect=false
|
||||
optimizer.learning_rate=4
|
||||
optimizer.min_learning_rate=2
|
||||
optimizer.lr_scheduler=cosine
|
||||
optimizer.lr_control_mode=none
|
||||
optimizer.skill_update_mode=full_rewrite_minibatch
|
||||
optimizer.use_slow_update=true
|
||||
optimizer.slow_update_samples=20
|
||||
optimizer.use_meta_skill=true
|
||||
optimizer.use_meta_reflect=false
|
||||
evaluation.use_gate=true
|
||||
evaluation.eval_test=true
|
||||
env.split_mode=split_dir
|
||||
env.workers=2
|
||||
env.exec_timeout=1020
|
||||
env.split_dir=data/ablation_splits/spreadsheetbench/2-1-7_seed42
|
||||
env.data_root=data/spreadsheetbench_verified_400
|
||||
env.mode=multi
|
||||
)
|
||||
|
||||
tmux_started=0
|
||||
|
||||
launch_run() {
|
||||
local run_id="$1"
|
||||
|
||||
local cmd_file="$RUN_ROOT/commands/${run_id}.sh"
|
||||
local log_file="$RUN_ROOT/logs/${run_id}.log"
|
||||
local out_root="$RUN_ROOT/$run_id"
|
||||
|
||||
local -a cmd=(
|
||||
"$PYTHON" -u scripts/train.py
|
||||
--config configs/spreadsheetbench/default.yaml
|
||||
--cfg-options
|
||||
"${COMMON_CFG[@]}"
|
||||
env.out_root="$out_root"
|
||||
)
|
||||
|
||||
{
|
||||
echo "#!/usr/bin/env bash"
|
||||
echo "set -euo pipefail"
|
||||
echo "cd '$REPO'"
|
||||
printf '%q ' "${cmd[@]}"
|
||||
printf ' >%q 2>&1 < /dev/null\n' "$log_file"
|
||||
} > "$cmd_file"
|
||||
chmod +x "$cmd_file"
|
||||
|
||||
if [[ "$tmux_started" -eq 0 ]]; then
|
||||
tmux new-session -d -s "$SESSION" -n "$run_id" "bash '$cmd_file'; code=\$?; echo EXIT:\$code; sleep 3600"
|
||||
tmux_started=1
|
||||
else
|
||||
tmux new-window -t "$SESSION" -n "$run_id" "bash '$cmd_file'; code=\$?; echo EXIT:\$code; sleep 3600"
|
||||
fi
|
||||
echo "$run_id"
|
||||
}
|
||||
|
||||
for ((i=START_INDEX; i<START_INDEX+N_REPEATS; i++)); do
|
||||
launch_run "LRCTRL-spreadsheetbench-full-rewrite-neutral3-promptweak-r${i}"
|
||||
done
|
||||
|
||||
echo "RUN_ROOT=$RUN_ROOT"
|
||||
echo "SESSION=$SESSION"
|
||||
echo "START_INDEX=$START_INDEX"
|
||||
echo "N_REPEATS=$N_REPEATS"
|
||||
115
scripts/launch_lrctrl_fullrewrite_neutral3_sq_lm_repeats.sh
Executable file
115
scripts/launch_lrctrl_fullrewrite_neutral3_sq_lm_repeats.sh
Executable file
@@ -0,0 +1,115 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
REPO="/home/azureuser/workspace-gzy/SkillReflection"
|
||||
PYTHON="/home/azureuser/workspace-gzy/miniconda3/envs/reflact/bin/python"
|
||||
|
||||
cd "$REPO"
|
||||
|
||||
if [[ -f ".secrets/teacher_oaidr9.env" ]]; then
|
||||
# shellcheck disable=SC1091
|
||||
source ".secrets/teacher_oaidr9.env"
|
||||
else
|
||||
echo "missing .secrets/teacher_oaidr9.env" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
stamp="$(date -u +%Y%m%d_%H%M%S)"
|
||||
RUN_ROOT="${1:-outputs/lrctrl_fullrewrite_neutral3_sq_lm_extra_workers2_timeout1020_${stamp}_run}"
|
||||
SESSION="${2:-lrctrl_fr_sq_lm_extra_${stamp}}"
|
||||
START_INDEX="${3:-4}"
|
||||
N_REPEATS="${4:-3}"
|
||||
|
||||
mkdir -p "$RUN_ROOT/logs" "$RUN_ROOT/commands"
|
||||
|
||||
COMMON_CFG=(
|
||||
model.teacher_backend=openai_chat
|
||||
model.student_backend=openai_chat
|
||||
model.teacher=gpt-5.5
|
||||
model.student=gpt-5.5
|
||||
model.teacher_azure_openai_endpoint="${TEACHER_AZURE_OPENAI_ENDPOINT}"
|
||||
model.teacher_azure_openai_api_version="${TEACHER_AZURE_OPENAI_API_VERSION}"
|
||||
model.teacher_azure_openai_auth_mode="${TEACHER_AZURE_OPENAI_AUTH_MODE}"
|
||||
model.teacher_azure_openai_managed_identity_client_id="${TEACHER_AZURE_OPENAI_MANAGED_IDENTITY_CLIENT_ID}"
|
||||
model.teacher_azure_openai_ad_scope="${TEACHER_AZURE_OPENAI_AD_SCOPE}"
|
||||
model.student_azure_openai_endpoint="${STUDENT_AZURE_OPENAI_ENDPOINT:-$TEACHER_AZURE_OPENAI_ENDPOINT}"
|
||||
model.student_azure_openai_api_version="${STUDENT_AZURE_OPENAI_API_VERSION:-$TEACHER_AZURE_OPENAI_API_VERSION}"
|
||||
model.student_azure_openai_auth_mode="${STUDENT_AZURE_OPENAI_AUTH_MODE:-$TEACHER_AZURE_OPENAI_AUTH_MODE}"
|
||||
model.student_azure_openai_managed_identity_client_id="${STUDENT_AZURE_OPENAI_MANAGED_IDENTITY_CLIENT_ID:-$TEACHER_AZURE_OPENAI_MANAGED_IDENTITY_CLIENT_ID}"
|
||||
model.student_azure_openai_ad_scope="${STUDENT_AZURE_OPENAI_AD_SCOPE:-$TEACHER_AZURE_OPENAI_AD_SCOPE}"
|
||||
model.reasoning_effort=medium
|
||||
train.num_epochs=4
|
||||
train.train_size=0
|
||||
train.batch_size=40
|
||||
train.accumulation=1
|
||||
train.seed=42
|
||||
gradient.minibatch_size=8
|
||||
gradient.merge_batch_size=8
|
||||
gradient.analyst_workers=16
|
||||
gradient.use_deep_reflect=false
|
||||
optimizer.learning_rate=4
|
||||
optimizer.min_learning_rate=2
|
||||
optimizer.lr_scheduler=cosine
|
||||
optimizer.lr_control_mode=none
|
||||
optimizer.skill_update_mode=full_rewrite_minibatch
|
||||
optimizer.use_slow_update=true
|
||||
optimizer.slow_update_samples=20
|
||||
optimizer.use_meta_skill=true
|
||||
optimizer.use_meta_reflect=false
|
||||
evaluation.use_gate=true
|
||||
evaluation.eval_test=true
|
||||
env.split_mode=split_dir
|
||||
env.workers=2
|
||||
env.exec_timeout=1020
|
||||
)
|
||||
|
||||
tmux_started=0
|
||||
|
||||
launch_run() {
|
||||
local run_id="$1"
|
||||
local config="$2"
|
||||
shift 2
|
||||
|
||||
local cmd_file="$RUN_ROOT/commands/${run_id}.sh"
|
||||
local log_file="$RUN_ROOT/logs/${run_id}.log"
|
||||
local out_root="$RUN_ROOT/$run_id"
|
||||
|
||||
local -a cmd=(
|
||||
"$PYTHON" -u scripts/train.py
|
||||
--config "$config"
|
||||
--cfg-options
|
||||
"${COMMON_CFG[@]}"
|
||||
env.out_root="$out_root"
|
||||
"$@"
|
||||
)
|
||||
|
||||
{
|
||||
echo "#!/usr/bin/env bash"
|
||||
echo "set -euo pipefail"
|
||||
echo "cd '$REPO'"
|
||||
printf '%q ' "${cmd[@]}"
|
||||
printf ' >%q 2>&1 < /dev/null\n' "$log_file"
|
||||
} > "$cmd_file"
|
||||
chmod +x "$cmd_file"
|
||||
|
||||
if [[ "$tmux_started" -eq 0 ]]; then
|
||||
tmux new-session -d -s "$SESSION" -n "$run_id" "bash '$cmd_file'; code=\$?; echo EXIT:\$code; sleep 3600"
|
||||
tmux_started=1
|
||||
else
|
||||
tmux new-window -t "$SESSION" -n "$run_id" "bash '$cmd_file'; code=\$?; echo EXIT:\$code; sleep 3600"
|
||||
fi
|
||||
echo "$run_id"
|
||||
}
|
||||
|
||||
for ((i=START_INDEX; i<START_INDEX+N_REPEATS; i++)); do
|
||||
launch_run "LRCTRL-searchqa-full-rewrite-neutral3-extra-r${i}" "configs/searchqa/default.yaml" \
|
||||
env.split_dir=data/ablation_splits/searchqa/2-1-7_seed42
|
||||
|
||||
launch_run "LRCTRL-livemathematicianbench-full-rewrite-neutral3-extra-r${i}" "configs/livemathematicianbench/default.yaml" \
|
||||
env.split_dir=data/ablation_splits/livemathematicianbench/2-1-7_seed42
|
||||
done
|
||||
|
||||
echo "RUN_ROOT=$RUN_ROOT"
|
||||
echo "SESSION=$SESSION"
|
||||
echo "START_INDEX=$START_INDEX"
|
||||
echo "N_REPEATS=$N_REPEATS"
|
||||
175
scripts/launch_spreadsheet_full_replacements.sh
Executable file
175
scripts/launch_spreadsheet_full_replacements.sh
Executable file
@@ -0,0 +1,175 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
REPO="/home/azureuser/workspace-gzy/SkillReflection"
|
||||
PYTHON="/home/azureuser/workspace-gzy/miniconda3/envs/reflact/bin/python"
|
||||
|
||||
cd "$REPO"
|
||||
|
||||
export ANTHROPIC_BASE_URL="${ANTHROPIC_BASE_URL:-http://127.0.0.1:4343}"
|
||||
export ANTHROPIC_AUTH_TOKEN="${ANTHROPIC_AUTH_TOKEN:-dummy}"
|
||||
export ANTHROPIC_MODEL="${ANTHROPIC_MODEL:-claude-sonnet-4-6}"
|
||||
export ANTHROPIC_SMALL_FAST_MODEL="${ANTHROPIC_SMALL_FAST_MODEL:-claude-sonnet-4-6}"
|
||||
export DISABLE_NON_ESSENTIAL_MODEL_CALLS="${DISABLE_NON_ESSENTIAL_MODEL_CALLS:-1}"
|
||||
export CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC="${CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC:-1}"
|
||||
|
||||
if [[ -f ".secrets/teacher_oaidr9.env" ]]; then
|
||||
# shellcheck disable=SC1091
|
||||
source ".secrets/teacher_oaidr9.env"
|
||||
else
|
||||
echo "missing .secrets/teacher_oaidr9.env" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
stamp="$(date -u +%Y%m%d_%H%M%S)"
|
||||
RUN_ROOT="${1:-outputs/spreadsheet_full_replacements_workers2_timeout1020_${stamp}_run}"
|
||||
SESSION="${2:-spreadsheet_full_replacements_${stamp}}"
|
||||
|
||||
mkdir -p "$RUN_ROOT/logs" "$RUN_ROOT/commands"
|
||||
|
||||
tmux_started=0
|
||||
|
||||
launch_run() {
|
||||
local run_id="$1"
|
||||
shift
|
||||
|
||||
local cmd_file="$RUN_ROOT/commands/${run_id}.sh"
|
||||
local log_file="$RUN_ROOT/logs/${run_id}.log"
|
||||
|
||||
{
|
||||
echo "#!/usr/bin/env bash"
|
||||
echo "set -euo pipefail"
|
||||
echo "cd '$REPO'"
|
||||
printf 'export ANTHROPIC_BASE_URL=%q\n' "$ANTHROPIC_BASE_URL"
|
||||
printf 'export ANTHROPIC_AUTH_TOKEN=%q\n' "$ANTHROPIC_AUTH_TOKEN"
|
||||
printf 'export ANTHROPIC_MODEL=%q\n' "$ANTHROPIC_MODEL"
|
||||
printf 'export ANTHROPIC_SMALL_FAST_MODEL=%q\n' "$ANTHROPIC_SMALL_FAST_MODEL"
|
||||
printf 'export DISABLE_NON_ESSENTIAL_MODEL_CALLS=%q\n' "$DISABLE_NON_ESSENTIAL_MODEL_CALLS"
|
||||
printf 'export CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=%q\n' "$CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC"
|
||||
printf '%q ' "$@"
|
||||
printf ' >%q 2>&1 < /dev/null\n' "$log_file"
|
||||
} > "$cmd_file"
|
||||
chmod +x "$cmd_file"
|
||||
|
||||
if [[ "$tmux_started" -eq 0 ]]; then
|
||||
tmux new-session -d -s "$SESSION" -n "$run_id" "bash '$cmd_file'; code=\$?; echo EXIT:\$code; sleep 3600"
|
||||
tmux_started=1
|
||||
else
|
||||
tmux new-window -t "$SESSION" -n "$run_id" "bash '$cmd_file'; code=\$?; echo EXIT:\$code; sleep 3600"
|
||||
fi
|
||||
echo "$run_id"
|
||||
}
|
||||
|
||||
OPENAI_COMMON=(
|
||||
"$PYTHON" -u scripts/train.py
|
||||
--config configs/spreadsheetbench/default.yaml
|
||||
--cfg-options
|
||||
model.teacher_backend=openai_chat
|
||||
model.student_backend=openai_chat
|
||||
model.teacher=gpt-5.5
|
||||
model.student=gpt-5.5
|
||||
model.teacher_azure_openai_endpoint="${TEACHER_AZURE_OPENAI_ENDPOINT}"
|
||||
model.teacher_azure_openai_api_version="${TEACHER_AZURE_OPENAI_API_VERSION}"
|
||||
model.teacher_azure_openai_auth_mode="${TEACHER_AZURE_OPENAI_AUTH_MODE}"
|
||||
model.teacher_azure_openai_managed_identity_client_id="${TEACHER_AZURE_OPENAI_MANAGED_IDENTITY_CLIENT_ID}"
|
||||
model.teacher_azure_openai_ad_scope="${TEACHER_AZURE_OPENAI_AD_SCOPE}"
|
||||
model.student_azure_openai_endpoint="${STUDENT_AZURE_OPENAI_ENDPOINT:-$TEACHER_AZURE_OPENAI_ENDPOINT}"
|
||||
model.student_azure_openai_api_version="${STUDENT_AZURE_OPENAI_API_VERSION:-$TEACHER_AZURE_OPENAI_API_VERSION}"
|
||||
model.student_azure_openai_auth_mode="${STUDENT_AZURE_OPENAI_AUTH_MODE:-$TEACHER_AZURE_OPENAI_AUTH_MODE}"
|
||||
model.student_azure_openai_managed_identity_client_id="${STUDENT_AZURE_OPENAI_MANAGED_IDENTITY_CLIENT_ID:-$TEACHER_AZURE_OPENAI_MANAGED_IDENTITY_CLIENT_ID}"
|
||||
model.student_azure_openai_ad_scope="${STUDENT_AZURE_OPENAI_AD_SCOPE:-$TEACHER_AZURE_OPENAI_AD_SCOPE}"
|
||||
model.reasoning_effort=medium
|
||||
train.num_epochs=4
|
||||
train.train_size=0
|
||||
train.batch_size=40
|
||||
train.accumulation=1
|
||||
train.seed=42
|
||||
gradient.minibatch_size=8
|
||||
gradient.merge_batch_size=8
|
||||
gradient.analyst_workers=16
|
||||
gradient.use_deep_reflect=false
|
||||
optimizer.learning_rate=4
|
||||
optimizer.min_learning_rate=2
|
||||
optimizer.lr_scheduler=cosine
|
||||
optimizer.use_slow_update=true
|
||||
optimizer.slow_update_samples=20
|
||||
optimizer.use_meta_reflect=false
|
||||
evaluation.use_gate=true
|
||||
evaluation.eval_test=true
|
||||
env.split_mode=split_dir
|
||||
env.workers=2
|
||||
env.exec_timeout=1020
|
||||
env.split_dir=data/ablation_splits/spreadsheetbench/2-1-7_seed42
|
||||
env.data_root=data/spreadsheetbench_verified_400
|
||||
env.mode=multi
|
||||
)
|
||||
|
||||
HARNESS_RUN="HARNESS-ClaudeInit-Spreadsheet-lr4-multi-full"
|
||||
launch_run "$HARNESS_RUN" \
|
||||
"$PYTHON" -u scripts/train.py \
|
||||
--config configs/spreadsheetbench/default.yaml \
|
||||
--cfg-options \
|
||||
model.teacher_backend=openai_chat \
|
||||
model.teacher=gpt-5.5 \
|
||||
model.teacher_azure_openai_endpoint="${TEACHER_AZURE_OPENAI_ENDPOINT}" \
|
||||
model.teacher_azure_openai_api_version="${TEACHER_AZURE_OPENAI_API_VERSION}" \
|
||||
model.teacher_azure_openai_auth_mode="${TEACHER_AZURE_OPENAI_AUTH_MODE}" \
|
||||
model.teacher_azure_openai_managed_identity_client_id="${TEACHER_AZURE_OPENAI_MANAGED_IDENTITY_CLIENT_ID}" \
|
||||
model.teacher_azure_openai_ad_scope="${TEACHER_AZURE_OPENAI_AD_SCOPE}" \
|
||||
model.reasoning_effort=medium \
|
||||
train.num_epochs=4 \
|
||||
train.train_size=0 \
|
||||
train.batch_size=40 \
|
||||
train.accumulation=1 \
|
||||
train.seed=42 \
|
||||
gradient.minibatch_size=8 \
|
||||
gradient.merge_batch_size=8 \
|
||||
gradient.analyst_workers=16 \
|
||||
gradient.use_deep_reflect=false \
|
||||
optimizer.lr_control_mode=fixed \
|
||||
optimizer.skill_update_mode=patch \
|
||||
optimizer.use_slow_update=true \
|
||||
optimizer.slow_update_samples=20 \
|
||||
optimizer.use_meta_skill=true \
|
||||
optimizer.use_meta_reflect=false \
|
||||
evaluation.use_gate=true \
|
||||
evaluation.eval_test=true \
|
||||
env.split_mode=split_dir \
|
||||
env.workers=2 \
|
||||
env.exec_timeout=1020 \
|
||||
model.student_backend=claude_code_exec \
|
||||
model.student=claude-sonnet-4-6 \
|
||||
model.claude_code_exec_use_sdk=sdk \
|
||||
model.claude_code_exec_effort=medium \
|
||||
model.claude_code_exec_max_thinking_tokens=16384 \
|
||||
model.codex_trace_to_teacher=false \
|
||||
env.out_root="$RUN_ROOT/$HARNESS_RUN" \
|
||||
env.split_dir=data/spreadsheetbench/splits \
|
||||
env.data_root=data/spreadsheetbench/files \
|
||||
env.mode=multi \
|
||||
optimizer.learning_rate=4 \
|
||||
optimizer.min_learning_rate=1 \
|
||||
optimizer.lr_scheduler=constant
|
||||
|
||||
for repeat in r1 r2 r3; do
|
||||
run_id="LRCTRL-spreadsheetbench-full-rewrite-neutral3-full-${repeat}"
|
||||
launch_run "$run_id" \
|
||||
"${OPENAI_COMMON[@]}" \
|
||||
optimizer.lr_control_mode=none \
|
||||
optimizer.skill_update_mode=full_rewrite_minibatch \
|
||||
optimizer.use_meta_skill=true \
|
||||
env.out_root="$RUN_ROOT/$run_id"
|
||||
done
|
||||
|
||||
for repeat in r1 r2 r3; do
|
||||
run_id="SLOWMETA-spreadsheetbench-true-false-full-${repeat}"
|
||||
launch_run "$run_id" \
|
||||
"${OPENAI_COMMON[@]}" \
|
||||
optimizer.lr_control_mode=fixed \
|
||||
optimizer.skill_update_mode=patch \
|
||||
optimizer.use_meta_skill=false \
|
||||
env.out_root="$RUN_ROOT/$run_id"
|
||||
done
|
||||
|
||||
echo "RUN_ROOT=$RUN_ROOT"
|
||||
echo "SESSION=$SESSION"
|
||||
61
scripts/monitor_harness_claude18.sh
Executable file
61
scripts/monitor_harness_claude18.sh
Executable file
@@ -0,0 +1,61 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ROOT="${1:?usage: monitor_harness_claude18.sh RUN_ROOT}"
|
||||
|
||||
while true; do
|
||||
ts="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
||||
echo "===== $ts CLAUDE18 ====="
|
||||
uptime | sed 's/^/uptime=/'
|
||||
active="$(pgrep -af "scripts/train.py.*${ROOT}" | grep -v pgrep | wc -l || true)"
|
||||
claude_child="$(pgrep -af 'claude.*--output-format stream-json' | grep -v pgrep | wc -l || true)"
|
||||
echo "active_train_total=$active"
|
||||
echo "active_codex_train=0"
|
||||
echo "active_claude_train=$active"
|
||||
echo "claude_child=$claude_child"
|
||||
|
||||
for d in "$ROOT"/HARNESS-Claude-*; do
|
||||
[[ -d "$d" ]] || continue
|
||||
rid="$(basename "$d")"
|
||||
read -r base best < <(python3 - "$d" <<'PY'
|
||||
import json, sys
|
||||
from pathlib import Path
|
||||
d = Path(sys.argv[1])
|
||||
s = d / "summary.json"
|
||||
if not s.exists():
|
||||
print("pending pending")
|
||||
raise SystemExit
|
||||
try:
|
||||
obj = json.loads(s.read_text())
|
||||
except Exception:
|
||||
print("pending pending")
|
||||
raise SystemExit
|
||||
base = obj.get("baseline_test_hard", obj.get("base_test", "pending"))
|
||||
best = obj.get("test_hard", obj.get("best_test", "pending"))
|
||||
def fmt(x):
|
||||
if isinstance(x, (int, float)):
|
||||
return f"{x:.4f}"
|
||||
return str(x)
|
||||
print(fmt(base), fmt(best))
|
||||
PY
|
||||
)
|
||||
scan_files="$(mktemp)"
|
||||
find "$d" \
|
||||
\( -path '*/codex_exec' -o -path '*/codex_multi' \) -prune -o \
|
||||
-maxdepth 6 -type f \
|
||||
\( -name 'claude_trace_summary.txt' -o -name 'codex_trace_summary.txt' -o -name '*.log' -o -name 'summary.json' \) \
|
||||
-print > "$scan_files" 2>/dev/null || true
|
||||
auth="$({ xargs -r rg -l 'Not logged in|authentication_failed' < "$scan_files" 2>/dev/null || true; } | wc -l | tr -d ' ')"
|
||||
e429="$({ xargs -r rg -l 'Too Many Requests|RateLimitError|Error code: 429|api_error_status.: 429|rate_limit|too_many_requests' < "$scan_files" 2>/dev/null || true; } | wc -l | tr -d ' ')"
|
||||
e401="$({ xargs -r rg -l '401 Unauthorized|Error code: 401|HTTP 401|AuthenticationTypeDisabled|PermissionDeniedError' < "$scan_files" 2>/dev/null || true; } | wc -l | tr -d ' ')"
|
||||
timeout="$({ xargs -r rg -l 'TimeoutError|Task timed out|timed out after|subprocess.TimeoutExpired|timeout_exceeded' < "$scan_files" 2>/dev/null || true; } | wc -l | tr -d ' ')"
|
||||
teacher="$({ xargs -r rg -l 'APITimeoutError|APIConnectionError|AuthenticationError|Azure OpenAI Responses API is enabled only|teacher.*error' < "$scan_files" 2>/dev/null || true; } | wc -l | tr -d ' ')"
|
||||
results="$(find "$d" -maxdepth 5 -path '*/results.jsonl' -type f -print0 2>/dev/null | xargs -0 -r wc -l | awk 'END{print $1+0}')"
|
||||
empty="$({ xargs -r rg -l 'final response chars: 0|\"final_response\"\\s*:\\s*\"\"|\"result\"\\s*:\\s*\"\"' < "$scan_files" 2>/dev/null || true; } | wc -l | tr -d ' ')"
|
||||
rm -f "$scan_files"
|
||||
errors=$((auth + e429 + e401 + timeout + teacher))
|
||||
echo "$rid Base=$base Best=$best Errors=$errors auth=$auth 429=$e429 401=$e401 timeout=$timeout teacher=$teacher Results=$results Empty=$empty"
|
||||
done | sort
|
||||
echo
|
||||
sleep 60
|
||||
done
|
||||
130
scripts/prepare_ablation_splits.py
Normal file
130
scripts/prepare_ablation_splits.py
Normal file
@@ -0,0 +1,130 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Prepare fixed data splits for ablation experiments."""
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import random
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
PROJECT_ROOT = Path(__file__).resolve().parents[1]
|
||||
|
||||
DATASETS = {
|
||||
"searchqa": {
|
||||
"raw": PROJECT_ROOT / "data/searchqa_train_2000.json",
|
||||
"out": PROJECT_ROOT / "data/ablation_splits/searchqa",
|
||||
"filenames": {"train": "train.json", "val": "selection.json", "test": "test.json"},
|
||||
},
|
||||
"spreadsheetbench": {
|
||||
"raw": PROJECT_ROOT / "data/spreadsheetbench_verified_400/dataset.json",
|
||||
"out": PROJECT_ROOT / "data/ablation_splits/spreadsheetbench",
|
||||
"filenames": {"train": "train.json", "val": "sel.json", "test": "test.json"},
|
||||
},
|
||||
}
|
||||
|
||||
SPLITS = ("1shot", "1:1:8", "2:1:7", "4:1:5")
|
||||
|
||||
|
||||
def load_items(path: Path) -> list[dict]:
|
||||
with path.open(encoding="utf-8") as f:
|
||||
data = json.load(f)
|
||||
if not isinstance(data, list):
|
||||
raise TypeError(f"Expected JSON array in {path}, got {type(data).__name__}")
|
||||
return data
|
||||
|
||||
|
||||
def split_counts(total: int, split: str) -> tuple[int, int, int]:
|
||||
if split == "1shot":
|
||||
if total < 3:
|
||||
raise ValueError(f"Need at least 3 items for 1shot split, got {total}")
|
||||
return 1, 1, total - 2
|
||||
|
||||
ratio = split
|
||||
weights = [int(part) for part in ratio.split(":")]
|
||||
if len(weights) != 3 or min(weights) <= 0:
|
||||
raise ValueError(f"Invalid ratio: {ratio}")
|
||||
denom = sum(weights)
|
||||
raw = [total * weight / denom for weight in weights]
|
||||
counts = [int(value) for value in raw]
|
||||
remaining = total - sum(counts)
|
||||
order = sorted(
|
||||
range(3),
|
||||
key=lambda idx: (raw[idx] - counts[idx], weights[idx]),
|
||||
reverse=True,
|
||||
)
|
||||
for idx in order[:remaining]:
|
||||
counts[idx] += 1
|
||||
return counts[0], counts[1], counts[2]
|
||||
|
||||
|
||||
def split_tag(split: str) -> str:
|
||||
return "1shot" if split == "1shot" else split.replace(":", "-")
|
||||
|
||||
|
||||
def write_json(path: Path, items: list[dict]) -> None:
|
||||
path.parent.mkdir(parents=True, exist_ok=True)
|
||||
with path.open("w", encoding="utf-8") as f:
|
||||
json.dump(items, f, ensure_ascii=False, indent=2)
|
||||
|
||||
|
||||
def prepare_dataset(name: str, *, seed: int, force: bool) -> None:
|
||||
spec = DATASETS[name]
|
||||
raw_path = spec["raw"]
|
||||
out_root = spec["out"]
|
||||
filenames = spec["filenames"]
|
||||
|
||||
items = load_items(raw_path)
|
||||
for split in SPLITS:
|
||||
ratio_tag = split_tag(split)
|
||||
split_dir = out_root / f"{ratio_tag}_seed{seed}"
|
||||
manifest_path = split_dir / "split_manifest.json"
|
||||
if manifest_path.exists() and not force:
|
||||
print(f"skip {name} {split}: {split_dir} exists")
|
||||
continue
|
||||
|
||||
shuffled = list(items)
|
||||
random.Random(seed).shuffle(shuffled)
|
||||
train_n, val_n, test_n = split_counts(len(shuffled), split)
|
||||
train_items = shuffled[:train_n]
|
||||
val_items = shuffled[train_n: train_n + val_n]
|
||||
test_items = shuffled[train_n + val_n: train_n + val_n + test_n]
|
||||
|
||||
write_json(split_dir / "train" / filenames["train"], train_items)
|
||||
write_json(split_dir / "val" / filenames["val"], val_items)
|
||||
write_json(split_dir / "test" / filenames["test"], test_items)
|
||||
write_json(
|
||||
manifest_path,
|
||||
{
|
||||
"dataset": name,
|
||||
"source": str(raw_path),
|
||||
"split_mode": "precomputed_ratio",
|
||||
"split_name": split,
|
||||
"split_ratio": split if split != "1shot" else "1 train / 1 val / rest test",
|
||||
"split_seed": seed,
|
||||
"counts": {
|
||||
"train": len(train_items),
|
||||
"val": len(val_items),
|
||||
"test": len(test_items),
|
||||
},
|
||||
},
|
||||
)
|
||||
print(
|
||||
f"wrote {name} {split} -> {split_dir} "
|
||||
f"(train={len(train_items)}, val={len(val_items)}, test={len(test_items)})"
|
||||
)
|
||||
|
||||
|
||||
def main() -> None:
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("--seed", type=int, default=42)
|
||||
parser.add_argument("--force", action="store_true")
|
||||
parser.add_argument("--dataset", choices=sorted(DATASETS), action="append")
|
||||
args = parser.parse_args()
|
||||
|
||||
for name in args.dataset or sorted(DATASETS):
|
||||
prepare_dataset(name, seed=args.seed, force=args.force)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
680
scripts/run_ablation_matrix.py
Executable file
680
scripts/run_ablation_matrix.py
Executable file
@@ -0,0 +1,680 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Launch the SearchQA / SpreadsheetBench ablation matrix.
|
||||
|
||||
By default this script prints commands only. Pass --execute to actually start
|
||||
runs. Every run writes to a unique out_root under the run root and logs stdout
|
||||
/ stderr to logs/<run_id>.log.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
PROJECT_ROOT = Path(__file__).resolve().parents[1]
|
||||
PYTHON_BIN = Path("/home/azureuser/workspace-gzy/miniconda3/envs/reflact/bin/python")
|
||||
|
||||
T2_ENDPOINT = "https://t2vgoaigpt4o3.openai.azure.com/"
|
||||
SEARCHAGENT5_ENDPOINT = "https://searchagent5.cognitiveservices.azure.com/"
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class Experiment:
|
||||
run_id: str
|
||||
benchmark: str
|
||||
config: str
|
||||
overrides: tuple[str, ...]
|
||||
|
||||
|
||||
BENCH_CONFIG = {
|
||||
"searchqa": "configs/searchqa/default.yaml",
|
||||
"spreadsheetbench": "configs/spreadsheetbench/default.yaml",
|
||||
"livemathematicianbench": "configs/livemathematicianbench/default.yaml",
|
||||
"alfworld": "configs/alfworld/default.yaml",
|
||||
"docvqa": "configs/docvqa/default.yaml",
|
||||
}
|
||||
|
||||
DEFAULT_SPLIT = {
|
||||
"searchqa": "data/ablation_splits/searchqa/2-1-7_seed42",
|
||||
"spreadsheetbench": "data/ablation_splits/spreadsheetbench/2-1-7_seed42",
|
||||
"livemathematicianbench": "data/ablation_splits/livemathematicianbench/2-1-7_seed42",
|
||||
"alfworld": "data/ablation_splits/alfworld/2-1-7_seed42",
|
||||
"docvqa": "/home/azureuser/zisu/SkillReflection/data/docvqa/splits",
|
||||
}
|
||||
|
||||
DEFAULT_TRAIN_SIZE = {
|
||||
"searchqa": 400,
|
||||
"spreadsheetbench": 80,
|
||||
"livemathematicianbench": 35,
|
||||
"alfworld": 39,
|
||||
"docvqa": 1070,
|
||||
}
|
||||
|
||||
BATCH_SIZE_VALUES: tuple[int | str, ...] = (8, 24, 40, 56, "full")
|
||||
|
||||
SPLITS = {
|
||||
"searchqa": {
|
||||
"1shot": ("data/ablation_splits/searchqa/1shot_seed42", ("optimizer.slow_update_samples=1",)),
|
||||
"1-1-8": ("data/ablation_splits/searchqa/1-1-8_seed42", ()),
|
||||
"2-1-7": ("data/ablation_splits/searchqa/2-1-7_seed42", ()),
|
||||
"4-1-5": ("data/ablation_splits/searchqa/4-1-5_seed42", ()),
|
||||
},
|
||||
"spreadsheetbench": {
|
||||
"1shot": ("data/ablation_splits/spreadsheetbench/1shot_seed42", ("optimizer.slow_update_samples=1",)),
|
||||
"1-1-8": ("data/ablation_splits/spreadsheetbench/1-1-8_seed42", ()),
|
||||
"2-1-7": ("data/ablation_splits/spreadsheetbench/2-1-7_seed42", ()),
|
||||
"4-1-5": ("data/ablation_splits/spreadsheetbench/4-1-5_seed42", ()),
|
||||
},
|
||||
"livemathematicianbench": {
|
||||
"1shot": ("data/ablation_splits/livemathematicianbench/1shot_seed42", ("optimizer.slow_update_samples=1",)),
|
||||
"1-1-8": ("data/ablation_splits/livemathematicianbench/1-1-8_seed42", ()),
|
||||
"2-1-7": ("data/ablation_splits/livemathematicianbench/2-1-7_seed42", ()),
|
||||
"4-1-5": ("data/ablation_splits/livemathematicianbench/4-1-5_seed42", ()),
|
||||
},
|
||||
"alfworld": {
|
||||
"1shot": ("data/ablation_splits/alfworld/1shot_seed42", ("optimizer.slow_update_samples=1",)),
|
||||
"1-1-8": ("data/ablation_splits/alfworld/1-1-8_seed42", ()),
|
||||
"2-1-7": ("data/ablation_splits/alfworld/2-1-7_seed42", ()),
|
||||
"4-1-5": ("data/ablation_splits/alfworld/4-1-5_seed42", ()),
|
||||
},
|
||||
"docvqa": {
|
||||
"1shot": ("data/ablation_splits/docvqa/1shot_seed42", ("optimizer.slow_update_samples=1",)),
|
||||
"1-1-8": ("data/ablation_splits/docvqa/1-1-8_seed42", ()),
|
||||
"2-1-7": ("/home/azureuser/zisu/SkillReflection/data/docvqa/splits", ()),
|
||||
"4-1-5": ("data/ablation_splits/docvqa/4-1-5_seed42", ()),
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def common_overrides(benchmark: str, out_root: Path) -> list[str]:
|
||||
return [
|
||||
"model.teacher_backend=openai_chat",
|
||||
"model.student_backend=openai_chat",
|
||||
"model.teacher=gpt-5.5",
|
||||
"model.student=gpt-5.5",
|
||||
f"model.teacher_azure_openai_endpoint={T2_ENDPOINT}",
|
||||
"model.teacher_azure_openai_api_version=2024-12-01-preview",
|
||||
"model.teacher_azure_openai_auth_mode=azure_cli",
|
||||
f"model.student_azure_openai_endpoint={T2_ENDPOINT}",
|
||||
"model.student_azure_openai_api_version=2024-12-01-preview",
|
||||
"model.student_azure_openai_auth_mode=azure_cli",
|
||||
"model.reasoning_effort=medium",
|
||||
"train.num_epochs=4",
|
||||
"train.train_size=0",
|
||||
"train.batch_size=40",
|
||||
"train.accumulation=1",
|
||||
"train.seed=42",
|
||||
"gradient.minibatch_size=8",
|
||||
"gradient.merge_batch_size=8",
|
||||
"gradient.analyst_workers=16",
|
||||
"gradient.use_deep_reflect=false",
|
||||
"optimizer.learning_rate=4",
|
||||
"optimizer.min_learning_rate=2",
|
||||
"optimizer.lr_scheduler=cosine",
|
||||
"optimizer.skill_update_mode=patch",
|
||||
"optimizer.use_slow_update=true",
|
||||
"optimizer.slow_update_samples=20",
|
||||
"optimizer.use_meta_skill=true",
|
||||
"optimizer.use_meta_reflect=false",
|
||||
"evaluation.use_gate=true",
|
||||
"evaluation.eval_test=true",
|
||||
"env.split_mode=split_dir",
|
||||
f"env.split_dir={DEFAULT_SPLIT[benchmark]}",
|
||||
f"env.out_root={out_root}",
|
||||
]
|
||||
|
||||
|
||||
def make_experiment(
|
||||
group: str,
|
||||
benchmark: str,
|
||||
suffix: str,
|
||||
run_root: Path,
|
||||
overrides: list[str],
|
||||
) -> Experiment:
|
||||
run_id = f"{group}-{benchmark}-{suffix}"
|
||||
out_root = run_root / run_id
|
||||
all_overrides = common_overrides(benchmark, out_root)
|
||||
all_overrides.extend(overrides)
|
||||
return Experiment(
|
||||
run_id=run_id,
|
||||
benchmark=benchmark,
|
||||
config=BENCH_CONFIG[benchmark],
|
||||
overrides=tuple(all_overrides),
|
||||
)
|
||||
|
||||
|
||||
def build_matrix(
|
||||
groups: set[str],
|
||||
benchmarks: list[str],
|
||||
run_root: Path,
|
||||
*,
|
||||
include_duplicate_defaults: bool = False,
|
||||
) -> list[Experiment]:
|
||||
exps: list[Experiment] = []
|
||||
group_order = [
|
||||
"default",
|
||||
"split",
|
||||
"batch",
|
||||
"mbs",
|
||||
"lr",
|
||||
"sched",
|
||||
"slown",
|
||||
"mod",
|
||||
"smodel",
|
||||
"longpair",
|
||||
"lrctrl",
|
||||
]
|
||||
|
||||
for group in group_order:
|
||||
if group not in groups:
|
||||
continue
|
||||
for benchmark in benchmarks:
|
||||
if group == "default":
|
||||
exps.append(make_experiment("DEFAULT", benchmark, "5.5", run_root, []))
|
||||
continue
|
||||
|
||||
if group == "split":
|
||||
for tag, (split_dir, extra) in SPLITS[benchmark].items():
|
||||
if not include_duplicate_defaults and tag == "2-1-7":
|
||||
continue
|
||||
exps.append(make_experiment(
|
||||
"SPLIT",
|
||||
benchmark,
|
||||
tag,
|
||||
run_root,
|
||||
[f"env.split_dir={split_dir}", *extra],
|
||||
))
|
||||
continue
|
||||
|
||||
if group == "mbs":
|
||||
for value in (1, 2, 4, 8, 16, 32):
|
||||
if not include_duplicate_defaults and value == 8:
|
||||
continue
|
||||
exps.append(make_experiment(
|
||||
"MBS",
|
||||
benchmark,
|
||||
str(value),
|
||||
run_root,
|
||||
[f"gradient.minibatch_size={value}"],
|
||||
))
|
||||
continue
|
||||
|
||||
if group == "batch":
|
||||
for value in BATCH_SIZE_VALUES:
|
||||
if not include_duplicate_defaults and value == 40:
|
||||
continue
|
||||
batch_size = DEFAULT_TRAIN_SIZE[benchmark] if value == "full" else int(value)
|
||||
exps.append(make_experiment(
|
||||
"BATCH",
|
||||
benchmark,
|
||||
str(value),
|
||||
run_root,
|
||||
[
|
||||
f"train.batch_size={batch_size}",
|
||||
"gradient.minibatch_size=8",
|
||||
],
|
||||
))
|
||||
continue
|
||||
|
||||
if group == "lr":
|
||||
for value in (1, 2, 4, 8, 16):
|
||||
exps.append(make_experiment(
|
||||
"LR",
|
||||
benchmark,
|
||||
str(value),
|
||||
run_root,
|
||||
[
|
||||
"optimizer.lr_scheduler=constant",
|
||||
"optimizer.min_learning_rate=1",
|
||||
f"optimizer.learning_rate={value}",
|
||||
],
|
||||
))
|
||||
continue
|
||||
|
||||
if group == "sched":
|
||||
for value in ("constant", "cosine", "linear"):
|
||||
if not include_duplicate_defaults and value == "cosine":
|
||||
continue
|
||||
exps.append(make_experiment(
|
||||
"SCHED",
|
||||
benchmark,
|
||||
value,
|
||||
run_root,
|
||||
[f"optimizer.lr_scheduler={value}"],
|
||||
))
|
||||
continue
|
||||
|
||||
if group == "slown":
|
||||
for value in (5, 10, 20, 40):
|
||||
if not include_duplicate_defaults and value == 20:
|
||||
continue
|
||||
exps.append(make_experiment(
|
||||
"SLOWN",
|
||||
benchmark,
|
||||
str(value),
|
||||
run_root,
|
||||
[f"optimizer.slow_update_samples={value}"],
|
||||
))
|
||||
continue
|
||||
|
||||
if group == "mod":
|
||||
settings = {
|
||||
"slow-meta": ("true", "true"),
|
||||
"slow-only": ("true", "false"),
|
||||
"meta-only": ("false", "true"),
|
||||
"none": ("false", "false"),
|
||||
}
|
||||
for tag, (slow, meta) in settings.items():
|
||||
if not include_duplicate_defaults and tag == "slow-meta":
|
||||
continue
|
||||
exps.append(make_experiment(
|
||||
"MOD",
|
||||
benchmark,
|
||||
tag,
|
||||
run_root,
|
||||
[
|
||||
f"optimizer.use_slow_update={slow}",
|
||||
f"optimizer.use_meta_skill={meta}",
|
||||
],
|
||||
))
|
||||
continue
|
||||
|
||||
if group == "smodel":
|
||||
student_settings = {
|
||||
"5.4": [
|
||||
"model.student=gpt-5.4-pro",
|
||||
f"model.student_azure_openai_endpoint={T2_ENDPOINT}",
|
||||
"model.student_azure_openai_api_version=2025-03-01-preview",
|
||||
"model.student_azure_openai_auth_mode=azure_cli",
|
||||
],
|
||||
"5.4-mini": [
|
||||
"model.student=gpt-5.4-mini",
|
||||
f"model.student_azure_openai_endpoint={SEARCHAGENT5_ENDPOINT}",
|
||||
"model.student_azure_openai_api_version=2024-12-01-preview",
|
||||
"model.student_azure_openai_auth_mode=azure_cli",
|
||||
],
|
||||
"5.5": [],
|
||||
}
|
||||
for tag, overrides in student_settings.items():
|
||||
if not include_duplicate_defaults and tag == "5.5":
|
||||
continue
|
||||
exps.append(make_experiment("SMODEL", benchmark, tag, run_root, overrides))
|
||||
continue
|
||||
|
||||
if group == "longpair":
|
||||
for value in ("changed", "unchanged"):
|
||||
exps.append(make_experiment(
|
||||
"LONGPAIR",
|
||||
benchmark,
|
||||
value,
|
||||
run_root,
|
||||
[f"optimizer.longitudinal_pair_policy={value}"],
|
||||
))
|
||||
continue
|
||||
|
||||
if group == "lrctrl":
|
||||
settings = {
|
||||
"autonomous": ["optimizer.lr_control_mode=autonomous"],
|
||||
"full-rewrite": [
|
||||
"optimizer.lr_control_mode=none",
|
||||
"optimizer.skill_update_mode=full_rewrite_minibatch",
|
||||
],
|
||||
}
|
||||
for tag, overrides in settings.items():
|
||||
exps.append(make_experiment("LRCTRL", benchmark, tag, run_root, overrides))
|
||||
continue
|
||||
|
||||
return exps
|
||||
|
||||
|
||||
def _build_matrix_legacy(
|
||||
groups: set[str],
|
||||
benchmarks: list[str],
|
||||
run_root: Path,
|
||||
*,
|
||||
include_duplicate_defaults: bool = False,
|
||||
) -> list[Experiment]:
|
||||
exps: list[Experiment] = []
|
||||
for benchmark in benchmarks:
|
||||
if "default" in groups:
|
||||
exps.append(make_experiment("DEFAULT", benchmark, "5.5", run_root, []))
|
||||
|
||||
if "split" in groups:
|
||||
for tag, (split_dir, extra) in SPLITS[benchmark].items():
|
||||
if not include_duplicate_defaults and tag == "2-1-7":
|
||||
continue
|
||||
exps.append(make_experiment(
|
||||
"SPLIT",
|
||||
benchmark,
|
||||
tag,
|
||||
run_root,
|
||||
[f"env.split_dir={split_dir}", *extra],
|
||||
))
|
||||
|
||||
if "mbs" in groups:
|
||||
for value in (1, 2, 4, 8, 16, 32):
|
||||
if not include_duplicate_defaults and value == 8:
|
||||
continue
|
||||
exps.append(make_experiment(
|
||||
"MBS",
|
||||
benchmark,
|
||||
str(value),
|
||||
run_root,
|
||||
[f"gradient.minibatch_size={value}"],
|
||||
))
|
||||
|
||||
if "batch" in groups:
|
||||
for value in BATCH_SIZE_VALUES:
|
||||
if not include_duplicate_defaults and value == 40:
|
||||
continue
|
||||
batch_size = DEFAULT_TRAIN_SIZE[benchmark] if value == "full" else int(value)
|
||||
exps.append(make_experiment(
|
||||
"BATCH",
|
||||
benchmark,
|
||||
str(value),
|
||||
run_root,
|
||||
[
|
||||
f"train.batch_size={batch_size}",
|
||||
"gradient.minibatch_size=8",
|
||||
],
|
||||
))
|
||||
|
||||
if "lr" in groups:
|
||||
for value in (1, 2, 4, 8, 16):
|
||||
exps.append(make_experiment(
|
||||
"LR",
|
||||
benchmark,
|
||||
str(value),
|
||||
run_root,
|
||||
[
|
||||
"optimizer.lr_scheduler=constant",
|
||||
"optimizer.min_learning_rate=1",
|
||||
f"optimizer.learning_rate={value}",
|
||||
],
|
||||
))
|
||||
|
||||
if "sched" in groups:
|
||||
for value in ("constant", "cosine", "linear"):
|
||||
if not include_duplicate_defaults and value == "cosine":
|
||||
continue
|
||||
exps.append(make_experiment(
|
||||
"SCHED",
|
||||
benchmark,
|
||||
value,
|
||||
run_root,
|
||||
[f"optimizer.lr_scheduler={value}"],
|
||||
))
|
||||
|
||||
if "slown" in groups:
|
||||
for value in (5, 10, 20, 40):
|
||||
if not include_duplicate_defaults and value == 20:
|
||||
continue
|
||||
exps.append(make_experiment(
|
||||
"SLOWN",
|
||||
benchmark,
|
||||
str(value),
|
||||
run_root,
|
||||
[f"optimizer.slow_update_samples={value}"],
|
||||
))
|
||||
|
||||
if "mod" in groups:
|
||||
settings = {
|
||||
"slow-meta": ("true", "true"),
|
||||
"slow-only": ("true", "false"),
|
||||
"meta-only": ("false", "true"),
|
||||
"none": ("false", "false"),
|
||||
}
|
||||
for tag, (slow, meta) in settings.items():
|
||||
if not include_duplicate_defaults and tag == "slow-meta":
|
||||
continue
|
||||
exps.append(make_experiment(
|
||||
"MOD",
|
||||
benchmark,
|
||||
tag,
|
||||
run_root,
|
||||
[
|
||||
f"optimizer.use_slow_update={slow}",
|
||||
f"optimizer.use_meta_skill={meta}",
|
||||
],
|
||||
))
|
||||
|
||||
if "smodel" in groups:
|
||||
student_settings = {
|
||||
"5.4": [
|
||||
"model.student=gpt-5.4-pro",
|
||||
f"model.student_azure_openai_endpoint={T2_ENDPOINT}",
|
||||
"model.student_azure_openai_api_version=2025-03-01-preview",
|
||||
"model.student_azure_openai_auth_mode=azure_cli",
|
||||
],
|
||||
"5.4-mini": [
|
||||
"model.student=gpt-5.4-mini",
|
||||
f"model.student_azure_openai_endpoint={SEARCHAGENT5_ENDPOINT}",
|
||||
"model.student_azure_openai_api_version=2024-12-01-preview",
|
||||
"model.student_azure_openai_auth_mode=azure_cli",
|
||||
],
|
||||
"5.5": [],
|
||||
}
|
||||
for tag, overrides in student_settings.items():
|
||||
if not include_duplicate_defaults and tag == "5.5":
|
||||
continue
|
||||
exps.append(make_experiment("SMODEL", benchmark, tag, run_root, overrides))
|
||||
|
||||
if "longpair" in groups:
|
||||
for value in ("changed", "unchanged"):
|
||||
exps.append(make_experiment(
|
||||
"LONGPAIR",
|
||||
benchmark,
|
||||
value,
|
||||
run_root,
|
||||
[f"optimizer.longitudinal_pair_policy={value}"],
|
||||
))
|
||||
|
||||
if "lrctrl" in groups:
|
||||
settings = {
|
||||
"autonomous": ["optimizer.lr_control_mode=autonomous"],
|
||||
"full-rewrite": [
|
||||
"optimizer.lr_control_mode=none",
|
||||
"optimizer.skill_update_mode=full_rewrite_minibatch",
|
||||
],
|
||||
}
|
||||
for tag, overrides in settings.items():
|
||||
exps.append(make_experiment("LRCTRL", benchmark, tag, run_root, overrides))
|
||||
|
||||
return exps
|
||||
|
||||
|
||||
def command_for(exp: Experiment) -> list[str]:
|
||||
return [
|
||||
str(PYTHON_BIN),
|
||||
"scripts/train.py",
|
||||
"--config",
|
||||
exp.config,
|
||||
"--cfg-options",
|
||||
*exp.overrides,
|
||||
]
|
||||
|
||||
|
||||
def active_run_ids(run_root: Path, valid_run_ids: set[str] | None = None) -> set[str]:
|
||||
try:
|
||||
raw = subprocess.check_output(["pgrep", "-af", "scripts/train.py"], text=True)
|
||||
except subprocess.CalledProcessError:
|
||||
return set()
|
||||
pattern = re.compile(re.escape(str(run_root)) + r"/([^\s]+)")
|
||||
active: set[str] = set()
|
||||
for line in raw.splitlines():
|
||||
for match in pattern.finditer(line):
|
||||
run_id = match.group(1).strip("'\"")
|
||||
if run_id.endswith(".log") or "/" in run_id:
|
||||
continue
|
||||
if valid_run_ids is not None and run_id not in valid_run_ids:
|
||||
continue
|
||||
active.add(run_id)
|
||||
return active
|
||||
|
||||
|
||||
def completed_run_ids(run_root: Path) -> set[str]:
|
||||
return {
|
||||
path.parent.name
|
||||
for path in run_root.glob("*/summary.json")
|
||||
if path.is_file()
|
||||
}
|
||||
|
||||
|
||||
def print_commands(exps: list[Experiment]) -> None:
|
||||
for exp in exps:
|
||||
cmd = command_for(exp)
|
||||
print(f"\n# {exp.run_id}")
|
||||
print(" ".join(subprocess.list2cmdline([part]) for part in cmd))
|
||||
|
||||
|
||||
def run_commands(
|
||||
exps: list[Experiment],
|
||||
run_root: Path,
|
||||
max_parallel: int,
|
||||
run_retries: int,
|
||||
) -> int:
|
||||
logs_dir = run_root / "logs"
|
||||
logs_dir.mkdir(parents=True, exist_ok=True)
|
||||
active: list[tuple[Experiment, subprocess.Popen, object]] = []
|
||||
valid_run_ids = {exp.run_id for exp in exps}
|
||||
skipped_completed = completed_run_ids(run_root)
|
||||
skipped_active = active_run_ids(run_root, valid_run_ids)
|
||||
pending: list[tuple[Experiment, int]] = [
|
||||
(exp, 0)
|
||||
for exp in exps
|
||||
if exp.run_id not in skipped_completed and exp.run_id not in skipped_active
|
||||
]
|
||||
for run_id in sorted(skipped_completed):
|
||||
print(f"[SKIP_COMPLETED] {run_id}", flush=True)
|
||||
for run_id in sorted(skipped_active):
|
||||
print(f"[SKIP_ACTIVE] {run_id}", flush=True)
|
||||
failures = 0
|
||||
|
||||
while pending or active:
|
||||
external_active = active_run_ids(run_root, valid_run_ids) - {exp.run_id for exp, _, _ in active}
|
||||
while pending and len(active) + len(external_active) < max_parallel:
|
||||
exp, attempt = pending.pop(0)
|
||||
log_path = logs_dir / f"{exp.run_id}.log"
|
||||
if attempt:
|
||||
log_path = logs_dir / f"{exp.run_id}.retry{attempt}.log"
|
||||
log_f = open(log_path, "w", encoding="utf-8")
|
||||
print(f"[START] {exp.run_id} attempt={attempt + 1} log={log_path}", flush=True)
|
||||
proc = subprocess.Popen(
|
||||
command_for(exp),
|
||||
cwd=PROJECT_ROOT,
|
||||
stdout=log_f,
|
||||
stderr=subprocess.STDOUT,
|
||||
text=True,
|
||||
)
|
||||
setattr(proc, "_attempt", attempt)
|
||||
active.append((exp, proc, log_f))
|
||||
|
||||
time.sleep(5)
|
||||
still_active: list[tuple[Experiment, subprocess.Popen, object]] = []
|
||||
for exp, proc, log_f in active:
|
||||
rc = proc.poll()
|
||||
if rc is None:
|
||||
still_active.append((exp, proc, log_f))
|
||||
continue
|
||||
log_f.close()
|
||||
if rc == 0:
|
||||
print(f"[DONE] {exp.run_id}", flush=True)
|
||||
else:
|
||||
if getattr(proc, "_attempt", 0) < run_retries:
|
||||
next_attempt = getattr(proc, "_attempt", 0) + 1
|
||||
pending.append((exp, next_attempt))
|
||||
print(f"[RETRY] {exp.run_id} rc={rc} next_attempt={next_attempt + 1}", flush=True)
|
||||
else:
|
||||
failures += 1
|
||||
print(f"[FAIL] {exp.run_id} rc={rc}", flush=True)
|
||||
active = still_active
|
||||
|
||||
return failures
|
||||
|
||||
|
||||
def parse_args() -> argparse.Namespace:
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument(
|
||||
"--groups",
|
||||
nargs="+",
|
||||
default=["default"],
|
||||
choices=[
|
||||
"default",
|
||||
"split",
|
||||
"batch",
|
||||
"mbs",
|
||||
"lr",
|
||||
"sched",
|
||||
"slown",
|
||||
"mod",
|
||||
"smodel",
|
||||
"longpair",
|
||||
"lrctrl",
|
||||
"all",
|
||||
],
|
||||
help="Experiment groups to include. Default: default.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--bench",
|
||||
nargs="+",
|
||||
default=["searchqa", "spreadsheetbench"],
|
||||
choices=["searchqa", "spreadsheetbench", "livemathematicianbench", "alfworld", "docvqa"],
|
||||
)
|
||||
parser.add_argument("--run-root", default="", help="Output root. Default: outputs/ablation_<UTC timestamp>.")
|
||||
parser.add_argument("--max-parallel", type=int, default=1)
|
||||
parser.add_argument("--run-retries", type=int, default=1, help="Retry failed runs this many times. Default: 1.")
|
||||
parser.add_argument(
|
||||
"--include-duplicate-defaults",
|
||||
action="store_true",
|
||||
help="Also run ablation points that are exactly the default setting.",
|
||||
)
|
||||
parser.add_argument("--execute", action="store_true", help="Actually start runs. Without this, print commands only.")
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
def main() -> None:
|
||||
args = parse_args()
|
||||
groups = set(args.groups)
|
||||
if "all" in groups:
|
||||
groups = {"default", "split", "batch", "mbs", "lr", "sched", "slown", "mod", "smodel"}
|
||||
|
||||
ts = time.strftime("%Y%m%d_%H%M%S", time.gmtime())
|
||||
run_root = Path(args.run_root) if args.run_root else PROJECT_ROOT / "outputs" / f"ablation_{ts}"
|
||||
if not run_root.is_absolute():
|
||||
run_root = PROJECT_ROOT / run_root
|
||||
run_root.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
exps = build_matrix(
|
||||
groups,
|
||||
args.bench,
|
||||
run_root,
|
||||
include_duplicate_defaults=args.include_duplicate_defaults,
|
||||
)
|
||||
print(f"run_root={run_root}")
|
||||
print(f"num_experiments={len(exps)}")
|
||||
print(f"groups={','.join(sorted(groups))}")
|
||||
print(f"bench={','.join(args.bench)}")
|
||||
|
||||
if not args.execute:
|
||||
print_commands(exps)
|
||||
return
|
||||
|
||||
max_parallel = max(1, int(args.max_parallel))
|
||||
failures = run_commands(
|
||||
exps,
|
||||
run_root,
|
||||
max_parallel=max_parallel,
|
||||
run_retries=max(0, int(args.run_retries)),
|
||||
)
|
||||
if failures:
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
68
scripts/run_alfworld.sh
Executable file
68
scripts/run_alfworld.sh
Executable file
@@ -0,0 +1,68 @@
|
||||
#!/usr/bin/env bash
|
||||
# ──────────────────────────────────────────────────────────────────────────────
|
||||
# ReflACT — ALFWorld training launch script
|
||||
#
|
||||
# Usage:
|
||||
# bash scripts/run_alfworld.sh
|
||||
# bash scripts/run_alfworld.sh --num_epochs 2 --edit_budget 6
|
||||
# ──────────────────────────────────────────────────────────────────────────────
|
||||
set -euo pipefail
|
||||
|
||||
# ── Paths ────────────────────────────────────────────────────────────────────
|
||||
WORKSPACE="/home/azureuser/workspace-gzy"
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
PROJECT_ROOT="$(dirname "${SCRIPT_DIR}")"
|
||||
|
||||
# Activate conda environment
|
||||
export PATH="${WORKSPACE}/miniconda3/envs/reflact/bin:${WORKSPACE}/miniconda3/bin:${PATH}"
|
||||
|
||||
# ALFWorld data — uses ~/.cache/alfworld by default (standard alfworld location)
|
||||
export ALFWORLD_DATA="${ALFWORLD_DATA:-${HOME}/.cache/alfworld}"
|
||||
|
||||
# Ensure ReflACT is importable
|
||||
export PYTHONPATH="${PROJECT_ROOT}:${PYTHONPATH:-}"
|
||||
|
||||
# ── Verify ALFWorld data exists ──────────────────────────────────────────────
|
||||
if [ ! -d "${ALFWORLD_DATA}/json_2.1.1" ]; then
|
||||
echo "ERROR: ALFWorld data not found at ${ALFWORLD_DATA}/json_2.1.1"
|
||||
echo ""
|
||||
echo "To download ALFWorld data, run:"
|
||||
echo " pip install alfworld[full]"
|
||||
echo " alfworld-download"
|
||||
echo ""
|
||||
echo "Or set ALFWORLD_DATA to the directory containing json_2.1.1/"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# ── Azure OpenAI credentials ────────────────────────────────────────────────
|
||||
export AZURE_OPENAI_ENDPOINT="${AZURE_OPENAI_ENDPOINT:-https://agl-dev.cognitiveservices.azure.com/}"
|
||||
export AZURE_OPENAI_API_KEY="${AZURE_OPENAI_API_KEY:-<your-azure-openai-api-key>}"
|
||||
export AZURE_OPENAI_API_VERSION="${AZURE_OPENAI_API_VERSION:-2025-04-01-preview}"
|
||||
|
||||
# ── Model configuration ─────────────────────────────────────────────────────
|
||||
export TEACHER_DEPLOYMENT="${TEACHER_DEPLOYMENT:-gpt-5.5}"
|
||||
export STUDENT_DEPLOYMENT="${STUDENT_DEPLOYMENT:-gpt-5.5}"
|
||||
|
||||
# ── Output directory ─────────────────────────────────────────────────────────
|
||||
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
|
||||
DEFAULT_OUT_ROOT="${PROJECT_ROOT}/outputs/reflact_alfworld_${STUDENT_DEPLOYMENT}_${TIMESTAMP}"
|
||||
|
||||
# ── Run ──────────────────────────────────────────────────────────────────────
|
||||
echo "============================================================"
|
||||
echo " ReflACT — Reflective Agent Tuning (ALFWorld)"
|
||||
echo "============================================================"
|
||||
echo " Teacher: ${TEACHER_DEPLOYMENT}"
|
||||
echo " Student: ${STUDENT_DEPLOYMENT}"
|
||||
echo " ALFWORLD_DATA: ${ALFWORLD_DATA}"
|
||||
echo " Output: ${DEFAULT_OUT_ROOT}"
|
||||
echo "============================================================"
|
||||
|
||||
cd "${PROJECT_ROOT}"
|
||||
|
||||
python scripts/train.py \
|
||||
--config configs/alfworld_default.yaml \
|
||||
--out_root "${DEFAULT_OUT_ROOT}" \
|
||||
"$@"
|
||||
|
||||
echo ""
|
||||
echo "Done! Results saved to: ${DEFAULT_OUT_ROOT}"
|
||||
94
scripts/run_meta_skill_ablation.sh
Executable file
94
scripts/run_meta_skill_ablation.sh
Executable file
@@ -0,0 +1,94 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
PROJECT_ROOT="/home/azureuser/workspace-gzy/SkillReflection_dev"
|
||||
PYTHON_BIN="/home/azureuser/workspace-gzy/miniconda3/envs/reflact/bin/python"
|
||||
TS="$(date -u +%Y%m%d_%H%M%S)"
|
||||
RUN_ROOT="${PROJECT_ROOT}/outputs/meta_skill_ablation_${TS}"
|
||||
|
||||
mkdir -p "${RUN_ROOT}"
|
||||
|
||||
run_train() {
|
||||
local benchmark="$1"
|
||||
local reasoning="$2"
|
||||
local condition="$3"
|
||||
local config_path=""
|
||||
local reasoning_override=""
|
||||
local meta_skill_flag=""
|
||||
|
||||
case "${benchmark}" in
|
||||
searchqa)
|
||||
config_path="configs/searchqa/default.yaml"
|
||||
;;
|
||||
spreadsheetbench)
|
||||
config_path="configs/spreadsheetbench/default.yaml"
|
||||
;;
|
||||
*)
|
||||
echo "Unknown benchmark: ${benchmark}" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
case "${reasoning}" in
|
||||
medium)
|
||||
reasoning_override="model.reasoning_effort=medium"
|
||||
;;
|
||||
none)
|
||||
reasoning_override="model.reasoning_effort="
|
||||
;;
|
||||
*)
|
||||
echo "Unknown reasoning setting: ${reasoning}" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
case "${condition}" in
|
||||
slow)
|
||||
meta_skill_flag="optimizer.use_meta_skill=false"
|
||||
;;
|
||||
slow_meta)
|
||||
meta_skill_flag="optimizer.use_meta_skill=true"
|
||||
;;
|
||||
*)
|
||||
echo "Unknown condition: ${condition}" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
local out_root="${RUN_ROOT}/${benchmark}_${reasoning}_${condition}"
|
||||
|
||||
echo
|
||||
echo "============================================================"
|
||||
echo "START ${benchmark} ${reasoning} ${condition}"
|
||||
echo "out_root=${out_root}"
|
||||
echo "============================================================"
|
||||
|
||||
(
|
||||
cd "${PROJECT_ROOT}"
|
||||
"${PYTHON_BIN}" scripts/train.py \
|
||||
--config "${config_path}" \
|
||||
--cfg-options \
|
||||
"${reasoning_override}" \
|
||||
"optimizer.use_slow_update=true" \
|
||||
"${meta_skill_flag}" \
|
||||
"optimizer.use_meta_reflect=false" \
|
||||
"gradient.use_deep_reflect=false" \
|
||||
"env.out_root=${out_root}"
|
||||
)
|
||||
|
||||
echo
|
||||
echo "============================================================"
|
||||
echo "DONE ${benchmark} ${reasoning} ${condition}"
|
||||
echo "============================================================"
|
||||
}
|
||||
|
||||
for benchmark in searchqa spreadsheetbench; do
|
||||
for reasoning in medium none; do
|
||||
run_train "${benchmark}" "${reasoning}" "slow"
|
||||
run_train "${benchmark}" "${reasoning}" "slow_meta"
|
||||
done
|
||||
done
|
||||
|
||||
echo
|
||||
echo "All runs completed."
|
||||
echo "Run root: ${RUN_ROOT}"
|
||||
43
scripts/run_missing_meta_parallel.sh
Normal file
43
scripts/run_missing_meta_parallel.sh
Normal file
@@ -0,0 +1,43 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
PROJECT_ROOT="/home/azureuser/workspace-gzy/SkillReflection_dev"
|
||||
PYTHON_BIN="/home/azureuser/workspace-gzy/miniconda3/envs/reflact/bin/python"
|
||||
RUN_ROOT="${PROJECT_ROOT}/outputs/meta_skill_parallel_20260430_072356"
|
||||
LOG_DIR="${PROJECT_ROOT}/logs/meta_skill_parallel_20260430_072356"
|
||||
|
||||
mkdir -p "${RUN_ROOT}" "${LOG_DIR}"
|
||||
|
||||
start_run() {
|
||||
local name="$1"
|
||||
local config_path="$2"
|
||||
local meta_skill="$3"
|
||||
local out_root="${RUN_ROOT}/${name}"
|
||||
local log_path="${LOG_DIR}/${name}.log"
|
||||
|
||||
echo "[START] ${name}"
|
||||
echo " out_root=${out_root}"
|
||||
echo " log=${log_path}"
|
||||
|
||||
(
|
||||
cd "${PROJECT_ROOT}"
|
||||
PYTHONUNBUFFERED=1 "${PYTHON_BIN}" scripts/train.py \
|
||||
--config "${config_path}" \
|
||||
--cfg-options \
|
||||
"model.reasoning_effort=medium" \
|
||||
"optimizer.use_slow_update=true" \
|
||||
"optimizer.use_meta_skill=${meta_skill}" \
|
||||
"optimizer.use_meta_reflect=false" \
|
||||
"gradient.use_deep_reflect=false" \
|
||||
"env.out_root=${out_root}"
|
||||
) > "${log_path}" 2>&1 &
|
||||
|
||||
echo "$!" > "${LOG_DIR}/${name}.pid"
|
||||
}
|
||||
|
||||
start_run "searchqa_medium_slow_meta" "configs/searchqa/default.yaml" "true"
|
||||
start_run "spreadsheetbench_medium_slow" "configs/spreadsheetbench/default.yaml" "false"
|
||||
start_run "spreadsheetbench_medium_slow_meta" "configs/spreadsheetbench/default.yaml" "true"
|
||||
|
||||
echo "[WAIT] missing comparison runs are active"
|
||||
wait
|
||||
43
scripts/run_searchqa.sh
Executable file
43
scripts/run_searchqa.sh
Executable file
@@ -0,0 +1,43 @@
|
||||
#!/usr/bin/env bash
|
||||
# ──────────────────────────────────────────────────────────────────────────────
|
||||
# ReflACT — SearchQA training launch script
|
||||
#
|
||||
# Usage:
|
||||
# bash scripts/run_searchqa.sh
|
||||
# bash scripts/run_searchqa.sh --data_path data/searchqa_train_2000.json
|
||||
# bash scripts/run_searchqa.sh --num_epochs 2 --edit_budget 6
|
||||
# ──────────────────────────────────────────────────────────────────────────────
|
||||
set -euo pipefail
|
||||
|
||||
# ── Paths ────────────────────────────────────────────────────────────────────
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
PROJECT_ROOT="$(dirname "${SCRIPT_DIR}")"
|
||||
|
||||
# Ensure ReflACT is importable
|
||||
export PYTHONPATH="${PROJECT_ROOT}:${PYTHONPATH:-}"
|
||||
|
||||
# ── Model configuration ─────────────────────────────────────────────────────
|
||||
export TEACHER_DEPLOYMENT="${TEACHER_DEPLOYMENT:-gpt-5.5}"
|
||||
export STUDENT_DEPLOYMENT="${STUDENT_DEPLOYMENT:-gpt-5.5}"
|
||||
|
||||
# ── Output directory ─────────────────────────────────────────────────────────
|
||||
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
|
||||
DEFAULT_OUT_ROOT="${PROJECT_ROOT}/outputs/reflact_searchqa_${STUDENT_DEPLOYMENT}_${TIMESTAMP}"
|
||||
|
||||
# ── Run ──────────────────────────────────────────────────────────────────────
|
||||
echo "============================================================"
|
||||
echo " ReflACT — Reflective Agent Tuning (SearchQA)"
|
||||
echo "============================================================"
|
||||
echo " Teacher: ${TEACHER_DEPLOYMENT}"
|
||||
echo " Student: ${STUDENT_DEPLOYMENT}"
|
||||
echo "============================================================"
|
||||
|
||||
cd "${PROJECT_ROOT}"
|
||||
|
||||
python scripts/train.py \
|
||||
--config configs/searchqa_default.yaml \
|
||||
--out_root "${DEFAULT_OUT_ROOT}" \
|
||||
"$@"
|
||||
|
||||
echo ""
|
||||
echo "Done! Results saved to: ${DEFAULT_OUT_ROOT}"
|
||||
48
scripts/run_spreadsheetbench.sh
Executable file
48
scripts/run_spreadsheetbench.sh
Executable file
@@ -0,0 +1,48 @@
|
||||
#!/usr/bin/env bash
|
||||
# ──────────────────────────────────────────────────────────────────────────────
|
||||
# ReflACT — SpreadsheetBench training launch script
|
||||
#
|
||||
# Usage:
|
||||
# bash scripts/run_spreadsheetbench.sh \
|
||||
# --data_root /path/to/data \
|
||||
# --jsonl_path /path/to/benchmark.jsonl
|
||||
#
|
||||
# bash scripts/run_spreadsheetbench.sh \
|
||||
# --data_root /path/to/data \
|
||||
# --jsonl_path /path/to/benchmark.jsonl \
|
||||
# --num_epochs 2 --edit_budget 6
|
||||
# ──────────────────────────────────────────────────────────────────────────────
|
||||
set -euo pipefail
|
||||
|
||||
# ── Paths ────────────────────────────────────────────────────────────────────
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
PROJECT_ROOT="$(dirname "${SCRIPT_DIR}")"
|
||||
|
||||
# Ensure ReflACT is importable
|
||||
export PYTHONPATH="${PROJECT_ROOT}:${PYTHONPATH:-}"
|
||||
|
||||
# ── Model configuration ─────────────────────────────────────────────────────
|
||||
export TEACHER_DEPLOYMENT="${TEACHER_DEPLOYMENT:-gpt-5.5}"
|
||||
export STUDENT_DEPLOYMENT="${STUDENT_DEPLOYMENT:-gpt-5.5}"
|
||||
|
||||
# ── Output directory ─────────────────────────────────────────────────────────
|
||||
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
|
||||
DEFAULT_OUT_ROOT="${PROJECT_ROOT}/outputs/reflact_spreadsheetbench_${STUDENT_DEPLOYMENT}_${TIMESTAMP}"
|
||||
|
||||
# ── Run ──────────────────────────────────────────────────────────────────────
|
||||
echo "============================================================"
|
||||
echo " ReflACT — Reflective Agent Tuning (SpreadsheetBench)"
|
||||
echo "============================================================"
|
||||
echo " Teacher: ${TEACHER_DEPLOYMENT}"
|
||||
echo " Student: ${STUDENT_DEPLOYMENT}"
|
||||
echo "============================================================"
|
||||
|
||||
cd "${PROJECT_ROOT}"
|
||||
|
||||
python scripts/train.py \
|
||||
--config configs/spreadsheetbench_default.yaml \
|
||||
--out_root "${DEFAULT_OUT_ROOT}" \
|
||||
"$@"
|
||||
|
||||
echo ""
|
||||
echo "Done! Results saved to: ${DEFAULT_OUT_ROOT}"
|
||||
483
scripts/train.py
Normal file
483
scripts/train.py
Normal file
@@ -0,0 +1,483 @@
|
||||
#!/usr/bin/env python3
|
||||
"""ReflACT unified training entry point.
|
||||
|
||||
Usage
|
||||
-----
|
||||
python scripts/train.py --config configs/alfworld/default.yaml
|
||||
|
||||
Any YAML key can be overridden from the command line::
|
||||
|
||||
python scripts/train.py --config configs/alfworld/default.yaml \\
|
||||
--batch_size 40 --num_epochs 2 --seed 123
|
||||
|
||||
Run ``python scripts/train.py --help`` for a full list of options.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import datetime
|
||||
import os
|
||||
import sys
|
||||
|
||||
# Ensure the project root is on sys.path so ``import reflact`` works
|
||||
# regardless of where the script is invoked from.
|
||||
_SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||
_PROJECT_ROOT = os.path.dirname(_SCRIPT_DIR)
|
||||
if _PROJECT_ROOT not in sys.path:
|
||||
sys.path.insert(0, _PROJECT_ROOT)
|
||||
|
||||
from reflact.model.common import default_model_for_backend, normalize_backend_name
|
||||
|
||||
_OPENAI_DEFAULT_MODEL_SENTINELS = {"gpt-5.4", "gpt-5.5"}
|
||||
|
||||
|
||||
# ── Environment registry ────────────────────────────────────────────────────
|
||||
|
||||
_ENV_REGISTRY: dict[str, type] = {}
|
||||
|
||||
|
||||
def _register_builtins() -> None:
|
||||
"""Lazy-import built-in adapters so we don't pull heavy deps at CLI parse time."""
|
||||
try:
|
||||
from reflact.envs.alfworld.adapter import ALFWorldAdapter
|
||||
_ENV_REGISTRY["alfworld"] = ALFWorldAdapter
|
||||
except ImportError:
|
||||
pass # ALFWorld deps not installed — skip
|
||||
try:
|
||||
from reflact.envs.searchqa.adapter import SearchQAAdapter
|
||||
_ENV_REGISTRY["searchqa"] = SearchQAAdapter
|
||||
except ImportError:
|
||||
pass
|
||||
try:
|
||||
from reflact.envs.livemathematicianbench.adapter import LiveMathematicianBenchAdapter
|
||||
_ENV_REGISTRY["livemathematicianbench"] = LiveMathematicianBenchAdapter
|
||||
except ImportError:
|
||||
pass
|
||||
try:
|
||||
from reflact.envs.babyvision.adapter import BabyVisionAdapter
|
||||
_ENV_REGISTRY["babyvision"] = BabyVisionAdapter
|
||||
except ImportError:
|
||||
pass
|
||||
try:
|
||||
from reflact.envs.spreadsheetbench.adapter import SpreadsheetBenchAdapter
|
||||
_ENV_REGISTRY["spreadsheetbench"] = SpreadsheetBenchAdapter
|
||||
except ImportError:
|
||||
pass
|
||||
try:
|
||||
from reflact.envs.mmrb.adapter import MMRBAdapter
|
||||
_ENV_REGISTRY["mmrb"] = MMRBAdapter
|
||||
except ImportError:
|
||||
pass
|
||||
try:
|
||||
from reflact.envs.docvqa.adapter import DocVQAAdapter
|
||||
_ENV_REGISTRY["docvqa"] = DocVQAAdapter
|
||||
except ImportError:
|
||||
pass
|
||||
try:
|
||||
from reflact.envs.mathverse.adapter import MathVerseAdapter
|
||||
_ENV_REGISTRY["mathverse"] = MathVerseAdapter
|
||||
except ImportError:
|
||||
pass
|
||||
try:
|
||||
from reflact.envs.officeqa.adapter import OfficeQAAdapter
|
||||
_ENV_REGISTRY["officeqa"] = OfficeQAAdapter
|
||||
except ImportError:
|
||||
pass
|
||||
try:
|
||||
from reflact.envs.sealqa.adapter import SealQAAdapter
|
||||
_ENV_REGISTRY["sealqa"] = SealQAAdapter
|
||||
except ImportError:
|
||||
pass
|
||||
try:
|
||||
from reflact.envs.swebench.adapter import SWEBenchAdapter
|
||||
_ENV_REGISTRY["swebench"] = SWEBenchAdapter
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
|
||||
def get_adapter(cfg: dict):
|
||||
"""Instantiate the environment adapter specified in ``cfg["env"]``."""
|
||||
_register_builtins()
|
||||
env_name = cfg.get("env", "alfworld")
|
||||
if env_name not in _ENV_REGISTRY:
|
||||
raise ValueError(
|
||||
f"Unknown environment '{env_name}'. "
|
||||
f"Available: {list(_ENV_REGISTRY.keys())}"
|
||||
)
|
||||
adapter_cls = _ENV_REGISTRY[env_name]
|
||||
|
||||
# Inspect adapter __init__ signature and only pass accepted kwargs
|
||||
import inspect
|
||||
sig = inspect.signature(adapter_cls.__init__)
|
||||
accepted = set(sig.parameters.keys()) - {"self"}
|
||||
adapter_kwargs: dict = {}
|
||||
for key in accepted:
|
||||
if key in cfg:
|
||||
adapter_kwargs[key] = cfg[key]
|
||||
|
||||
return adapter_cls(**adapter_kwargs)
|
||||
|
||||
|
||||
# ── CLI ──────────────────────────────────────────────────────────────────────
|
||||
|
||||
_BOOL = lambda x: x.lower() in ("true", "1", "yes") # noqa: E731
|
||||
|
||||
|
||||
def parse_args() -> argparse.Namespace:
|
||||
p = argparse.ArgumentParser(
|
||||
description="ReflACT: Reflective Agent Tuning",
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
epilog=__doc__,
|
||||
)
|
||||
p.add_argument("--config", type=str, required=True,
|
||||
help="Path to YAML config file")
|
||||
p.add_argument("--cfg-options", nargs="+", default=[],
|
||||
help="Override config: section.key=value (e.g. train.batch_size=40)")
|
||||
|
||||
# Legacy flat CLI overrides (still work, prefer --cfg-options for new usage)
|
||||
p.add_argument("--env", type=str)
|
||||
p.add_argument("--backend", type=str,
|
||||
choices=["azure_openai", "codex", "codex_exec", "claude", "claude_chat", "claude_code_exec"])
|
||||
p.add_argument("--teacher_model", type=str)
|
||||
p.add_argument("--student_model", type=str)
|
||||
p.add_argument("--teacher_backend", type=str)
|
||||
p.add_argument("--student_backend", type=str)
|
||||
p.add_argument("--reasoning_effort", type=str,
|
||||
choices=["", "low", "medium", "high", "xhigh", "max"])
|
||||
p.add_argument("--rewrite_reasoning_effort", type=str)
|
||||
p.add_argument("--rewrite_max_completion_tokens", type=int)
|
||||
p.add_argument("--azure_endpoint", type=str)
|
||||
p.add_argument("--azure_api_version", type=str)
|
||||
p.add_argument("--azure_api_key", type=str)
|
||||
p.add_argument("--azure_openai_endpoint", type=str)
|
||||
p.add_argument("--azure_openai_api_version", type=str)
|
||||
p.add_argument("--azure_openai_api_key", type=str)
|
||||
p.add_argument("--azure_openai_auth_mode", type=str)
|
||||
p.add_argument("--azure_openai_ad_scope", type=str)
|
||||
p.add_argument("--azure_openai_managed_identity_client_id", type=str)
|
||||
p.add_argument("--teacher_azure_openai_endpoint", type=str)
|
||||
p.add_argument("--teacher_azure_openai_api_version", type=str)
|
||||
p.add_argument("--teacher_azure_openai_api_key", type=str)
|
||||
p.add_argument("--teacher_azure_openai_auth_mode", type=str)
|
||||
p.add_argument("--teacher_azure_openai_ad_scope", type=str)
|
||||
p.add_argument("--teacher_azure_openai_managed_identity_client_id", type=str)
|
||||
p.add_argument("--student_azure_openai_endpoint", type=str)
|
||||
p.add_argument("--student_azure_openai_api_version", type=str)
|
||||
p.add_argument("--student_azure_openai_api_key", type=str)
|
||||
p.add_argument("--student_azure_openai_auth_mode", type=str)
|
||||
p.add_argument("--student_azure_openai_ad_scope", type=str)
|
||||
p.add_argument("--student_azure_openai_managed_identity_client_id", type=str)
|
||||
p.add_argument("--codex_exec_path", type=str)
|
||||
p.add_argument("--codex_exec_sandbox", type=str)
|
||||
p.add_argument("--codex_exec_profile", type=str)
|
||||
p.add_argument("--codex_exec_full_auto", type=_BOOL)
|
||||
p.add_argument("--codex_exec_reasoning_effort", type=str)
|
||||
p.add_argument("--codex_exec_use_sdk", type=str)
|
||||
p.add_argument("--codex_exec_network_access", type=_BOOL)
|
||||
p.add_argument("--codex_exec_web_search", type=_BOOL)
|
||||
p.add_argument("--codex_exec_approval_policy", type=str)
|
||||
p.add_argument("--claude_code_exec_path", type=str)
|
||||
p.add_argument("--claude_code_exec_profile", type=str)
|
||||
p.add_argument("--claude_code_exec_use_sdk", type=str)
|
||||
p.add_argument("--claude_code_exec_effort", type=str)
|
||||
p.add_argument("--claude_code_exec_max_thinking_tokens", type=int)
|
||||
p.add_argument("--codex_trace_to_teacher", type=_BOOL)
|
||||
p.add_argument("--skill_init", type=str)
|
||||
p.add_argument("--num_epochs", type=int)
|
||||
p.add_argument("--train_size", type=int)
|
||||
p.add_argument("--steps_per_epoch", type=int)
|
||||
p.add_argument("--batch_size", type=int)
|
||||
p.add_argument("--accumulation", type=int)
|
||||
p.add_argument("--seed", type=int)
|
||||
p.add_argument("--edit_budget", type=int)
|
||||
p.add_argument("--min_edit_budget", type=int)
|
||||
p.add_argument("--lr_scheduler", type=str,
|
||||
choices=["constant", "linear", "cosine", "autonomous"])
|
||||
p.add_argument("--lr_control_mode", type=str,
|
||||
choices=["fixed", "autonomous", "none"])
|
||||
p.add_argument("--merge_batch_size", type=int)
|
||||
p.add_argument("--max_analyst_rounds", type=int)
|
||||
p.add_argument("--sel_env_num", type=int)
|
||||
p.add_argument("--test_env_num", type=int)
|
||||
p.add_argument("--eval_test", type=_BOOL)
|
||||
p.add_argument("--use_gate", type=_BOOL)
|
||||
p.add_argument("--max_steps", type=int)
|
||||
p.add_argument("--max_api_workers", type=int)
|
||||
p.add_argument("--analyst_workers", type=int)
|
||||
p.add_argument("--failure_only", type=_BOOL)
|
||||
p.add_argument("--minibatch_size", type=int)
|
||||
p.add_argument("--use_meta_reflect", type=_BOOL)
|
||||
p.add_argument("--meta_edit_budget", type=int)
|
||||
p.add_argument("--skill_update_mode", type=str,
|
||||
choices=[
|
||||
"patch",
|
||||
"rewrite_from_suggestions",
|
||||
"rewrite",
|
||||
"suggestions",
|
||||
"full_rewrite",
|
||||
"full_rewrite_minibatch",
|
||||
"minibatch_full_rewrite",
|
||||
])
|
||||
p.add_argument("--use_deep_reflect", type=_BOOL)
|
||||
p.add_argument("--deep_reflect_failures", type=int)
|
||||
p.add_argument("--deep_reflect_successes", type=int)
|
||||
p.add_argument("--use_slow_update", type=_BOOL)
|
||||
p.add_argument("--slow_update_samples", type=int)
|
||||
p.add_argument("--longitudinal_pair_policy", type=str,
|
||||
choices=["mixed", "changed", "unchanged"])
|
||||
p.add_argument("--use_meta_skill", type=_BOOL)
|
||||
p.add_argument("--data_path", type=str)
|
||||
p.add_argument("--split_mode", type=str,
|
||||
choices=["ratio", "split_dir"])
|
||||
p.add_argument("--split_ratio", type=str)
|
||||
p.add_argument("--split_seed", type=int)
|
||||
p.add_argument("--split_dir", type=str)
|
||||
p.add_argument("--split_output_dir", type=str)
|
||||
p.add_argument("--data_root", type=str)
|
||||
p.add_argument("--max_turns", type=int)
|
||||
p.add_argument("--workers", type=int)
|
||||
p.add_argument("--limit", type=int)
|
||||
p.add_argument("--shuffle_choices", type=_BOOL)
|
||||
p.add_argument("--use_theorem", type=_BOOL)
|
||||
p.add_argument("--use_sketch", type=_BOOL)
|
||||
p.add_argument("--image_detail", type=str)
|
||||
p.add_argument("--judge_model", type=str)
|
||||
p.add_argument("--judge_max_completion_tokens", type=int)
|
||||
p.add_argument("--judge_retries", type=int)
|
||||
p.add_argument("--out_root", type=str)
|
||||
p.add_argument("--mode", type=str)
|
||||
|
||||
return p.parse_args()
|
||||
|
||||
|
||||
# ── Flat key → structured path mapping (for legacy CLI → structured config) ──
|
||||
|
||||
_LEGACY_TO_STRUCTURED: dict[str, str] = {
|
||||
"backend": "model.backend",
|
||||
"teacher_model": "model.teacher",
|
||||
"student_model": "model.student",
|
||||
"teacher_backend": "model.teacher_backend",
|
||||
"student_backend": "model.student_backend",
|
||||
"reasoning_effort": "model.reasoning_effort",
|
||||
"rewrite_reasoning_effort": "model.rewrite_reasoning_effort",
|
||||
"rewrite_max_completion_tokens": "model.rewrite_max_completion_tokens",
|
||||
"azure_endpoint": "model.azure_endpoint",
|
||||
"azure_api_version": "model.azure_api_version",
|
||||
"azure_api_key": "model.azure_api_key",
|
||||
"azure_openai_endpoint": "model.azure_openai_endpoint",
|
||||
"azure_openai_api_version": "model.azure_openai_api_version",
|
||||
"azure_openai_api_key": "model.azure_openai_api_key",
|
||||
"azure_openai_auth_mode": "model.azure_openai_auth_mode",
|
||||
"azure_openai_ad_scope": "model.azure_openai_ad_scope",
|
||||
"azure_openai_managed_identity_client_id": "model.azure_openai_managed_identity_client_id",
|
||||
"teacher_azure_openai_endpoint": "model.teacher_azure_openai_endpoint",
|
||||
"teacher_azure_openai_api_version": "model.teacher_azure_openai_api_version",
|
||||
"teacher_azure_openai_api_key": "model.teacher_azure_openai_api_key",
|
||||
"teacher_azure_openai_auth_mode": "model.teacher_azure_openai_auth_mode",
|
||||
"teacher_azure_openai_ad_scope": "model.teacher_azure_openai_ad_scope",
|
||||
"teacher_azure_openai_managed_identity_client_id": "model.teacher_azure_openai_managed_identity_client_id",
|
||||
"student_azure_openai_endpoint": "model.student_azure_openai_endpoint",
|
||||
"student_azure_openai_api_version": "model.student_azure_openai_api_version",
|
||||
"student_azure_openai_api_key": "model.student_azure_openai_api_key",
|
||||
"student_azure_openai_auth_mode": "model.student_azure_openai_auth_mode",
|
||||
"student_azure_openai_ad_scope": "model.student_azure_openai_ad_scope",
|
||||
"student_azure_openai_managed_identity_client_id": "model.student_azure_openai_managed_identity_client_id",
|
||||
"codex_exec_path": "model.codex_exec_path",
|
||||
"codex_exec_sandbox": "model.codex_exec_sandbox",
|
||||
"codex_exec_profile": "model.codex_exec_profile",
|
||||
"codex_exec_full_auto": "model.codex_exec_full_auto",
|
||||
"codex_exec_reasoning_effort": "model.codex_exec_reasoning_effort",
|
||||
"codex_exec_use_sdk": "model.codex_exec_use_sdk",
|
||||
"codex_exec_network_access": "model.codex_exec_network_access",
|
||||
"codex_exec_web_search": "model.codex_exec_web_search",
|
||||
"codex_exec_approval_policy": "model.codex_exec_approval_policy",
|
||||
"claude_code_exec_path": "model.claude_code_exec_path",
|
||||
"claude_code_exec_profile": "model.claude_code_exec_profile",
|
||||
"claude_code_exec_use_sdk": "model.claude_code_exec_use_sdk",
|
||||
"claude_code_exec_effort": "model.claude_code_exec_effort",
|
||||
"claude_code_exec_max_thinking_tokens": "model.claude_code_exec_max_thinking_tokens",
|
||||
"codex_trace_to_teacher": "model.codex_trace_to_teacher",
|
||||
"num_epochs": "train.num_epochs",
|
||||
"train_size": "train.train_size",
|
||||
"steps_per_epoch": "train.steps_per_epoch",
|
||||
"batch_size": "train.batch_size",
|
||||
"accumulation": "train.accumulation",
|
||||
"seed": "train.seed",
|
||||
"minibatch_size": "gradient.minibatch_size",
|
||||
"merge_batch_size": "gradient.merge_batch_size",
|
||||
"analyst_workers": "gradient.analyst_workers",
|
||||
"max_analyst_rounds": "gradient.max_analyst_rounds",
|
||||
"failure_only": "gradient.failure_only",
|
||||
"use_deep_reflect": "gradient.use_deep_reflect",
|
||||
"deep_reflect_failures": "gradient.deep_reflect_failures",
|
||||
"deep_reflect_successes": "gradient.deep_reflect_successes",
|
||||
"edit_budget": "optimizer.learning_rate",
|
||||
"min_edit_budget": "optimizer.min_learning_rate",
|
||||
"lr_scheduler": "optimizer.lr_scheduler",
|
||||
"lr_control_mode": "optimizer.lr_control_mode",
|
||||
"skill_update_mode": "optimizer.skill_update_mode",
|
||||
"use_meta_reflect": "optimizer.use_meta_reflect",
|
||||
"meta_edit_budget": "optimizer.meta_learning_rate",
|
||||
"use_slow_update": "optimizer.use_slow_update",
|
||||
"slow_update_samples": "optimizer.slow_update_samples",
|
||||
"longitudinal_pair_policy": "optimizer.longitudinal_pair_policy",
|
||||
"use_meta_skill": "optimizer.use_meta_skill",
|
||||
"use_gate": "evaluation.use_gate",
|
||||
"sel_env_num": "evaluation.sel_env_num",
|
||||
"test_env_num": "evaluation.test_env_num",
|
||||
"eval_test": "evaluation.eval_test",
|
||||
"env": "env.name",
|
||||
"skill_init": "env.skill_init",
|
||||
"out_root": "env.out_root",
|
||||
}
|
||||
|
||||
|
||||
def load_config(args: argparse.Namespace) -> dict:
|
||||
"""Load config with _base_ inheritance, then apply CLI overrides."""
|
||||
from reflact.config import load_config as _load, flatten_config, is_structured
|
||||
|
||||
cfg = _load(args.config, overrides=args.cfg_options)
|
||||
structured = is_structured(cfg)
|
||||
|
||||
# Apply legacy --key value overrides
|
||||
cli = {k: v for k, v in vars(args).items()
|
||||
if v is not None and k not in ("config", "cfg_options")}
|
||||
if cli:
|
||||
if structured:
|
||||
from reflact.config import apply_overrides
|
||||
mapped = []
|
||||
for k, v in cli.items():
|
||||
dotted = _LEGACY_TO_STRUCTURED.get(k)
|
||||
if dotted:
|
||||
mapped.append(f"{dotted}={v}")
|
||||
else:
|
||||
mapped.append(f"env.{k}={v}")
|
||||
apply_overrides(cfg, mapped)
|
||||
else:
|
||||
cfg.update(cli)
|
||||
|
||||
# Flatten structured config → flat dict for trainer/adapter
|
||||
flat = flatten_config(cfg) if structured else cfg
|
||||
|
||||
for new_key, old_key in (
|
||||
("azure_openai_endpoint", "azure_endpoint"),
|
||||
("azure_openai_api_version", "azure_api_version"),
|
||||
("azure_openai_api_key", "azure_api_key"),
|
||||
):
|
||||
if flat.get(new_key) in (None, "") and flat.get(old_key) not in (None, ""):
|
||||
flat[new_key] = flat[old_key]
|
||||
|
||||
explicit_backend = getattr(args, "backend", None)
|
||||
if explicit_backend is None:
|
||||
for option in args.cfg_options or []:
|
||||
key = str(option).split("=", 1)[0].strip()
|
||||
if key == "model.backend":
|
||||
explicit_backend = str(option).split("=", 1)[1].strip()
|
||||
break
|
||||
|
||||
backend = normalize_backend_name(flat.get("model_backend") or flat.get("student_backend") or "azure_openai")
|
||||
|
||||
def _has_model_override(dotted_key: str, legacy_key: str) -> bool:
|
||||
if getattr(args, legacy_key, None) is not None:
|
||||
return True
|
||||
for option in args.cfg_options or []:
|
||||
key = str(option).split("=", 1)[0].strip()
|
||||
if key == dotted_key:
|
||||
return True
|
||||
return False
|
||||
|
||||
if explicit_backend is not None:
|
||||
backend = normalize_backend_name(explicit_backend)
|
||||
flat["model_backend"] = backend
|
||||
if backend in {"claude", "claude_chat"}:
|
||||
flat.setdefault("teacher_backend", "claude_chat")
|
||||
flat.setdefault("student_backend", "claude_chat")
|
||||
elif backend in {"codex", "codex_exec"}:
|
||||
flat.setdefault("teacher_backend", "openai_chat")
|
||||
flat.setdefault("student_backend", "codex_exec")
|
||||
elif backend == "claude_code_exec":
|
||||
flat.setdefault("teacher_backend", "openai_chat")
|
||||
flat.setdefault("student_backend", "claude_code_exec")
|
||||
else:
|
||||
flat.setdefault("teacher_backend", "openai_chat")
|
||||
flat.setdefault("student_backend", "openai_chat")
|
||||
else:
|
||||
flat.setdefault("teacher_backend", "openai_chat")
|
||||
flat.setdefault("student_backend", "openai_chat")
|
||||
|
||||
if flat.get("teacher_backend") == "claude_chat":
|
||||
if (
|
||||
str(flat.get("teacher_model", "") or "").strip() in _OPENAI_DEFAULT_MODEL_SENTINELS
|
||||
and not _has_model_override("model.teacher", "teacher_model")
|
||||
):
|
||||
flat["teacher_model"] = default_model_for_backend("claude_chat")
|
||||
if flat.get("student_backend") == "claude_chat":
|
||||
if (
|
||||
str(flat.get("student_model", "") or "").strip() in _OPENAI_DEFAULT_MODEL_SENTINELS
|
||||
and not _has_model_override("model.student", "student_model")
|
||||
):
|
||||
flat["student_model"] = default_model_for_backend("claude_chat")
|
||||
if flat.get("student_backend") == "claude_code_exec":
|
||||
if (
|
||||
str(flat.get("student_model", "") or "").strip() in _OPENAI_DEFAULT_MODEL_SENTINELS
|
||||
and not _has_model_override("model.student", "student_model")
|
||||
):
|
||||
flat["student_model"] = default_model_for_backend("claude_chat")
|
||||
|
||||
# Auto-generate output root
|
||||
if not flat.get("out_root"):
|
||||
env = flat.get("env", "unknown")
|
||||
model = flat.get("teacher_model", "unknown").replace("/", "-")
|
||||
ts = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
|
||||
flat["out_root"] = os.path.join("outputs", f"reflact_{env}_{model}_{ts}")
|
||||
|
||||
flat["out_root"] = os.path.abspath(flat["out_root"])
|
||||
return flat
|
||||
|
||||
|
||||
# ── Main ─────────────────────────────────────────────────────────────────────
|
||||
|
||||
def main() -> None:
|
||||
args = parse_args()
|
||||
cfg = load_config(args)
|
||||
|
||||
print(f"\n{'='*60}")
|
||||
print(f" ReflACT — Reflective Agent Tuning")
|
||||
print(f"{'='*60}")
|
||||
print(f" env: {cfg.get('env')}")
|
||||
print(f" teacher_model: {cfg.get('teacher_model')}")
|
||||
print(f" student_model: {cfg.get('student_model')}")
|
||||
print(f" teacher_backend:{cfg.get('teacher_backend', 'openai_chat')}")
|
||||
print(f" student_backend:{cfg.get('student_backend', 'openai_chat')}")
|
||||
print(f" reasoning: {cfg.get('reasoning_effort') or 'off'}")
|
||||
print(f" rewrite_effort: {cfg.get('rewrite_reasoning_effort') or 'off'}")
|
||||
print(f" epochs: {cfg.get('num_epochs')}")
|
||||
print(f" train_size: {cfg.get('train_size') or 'from dataset'}")
|
||||
print(f" steps/epoch: auto")
|
||||
print(f" batch_size: {cfg.get('batch_size')}")
|
||||
print(f" edit_budget: {cfg.get('edit_budget')}")
|
||||
print(f" lr_scheduler: {cfg.get('lr_scheduler', 'constant')}")
|
||||
print(f" update_mode: {cfg.get('skill_update_mode', 'patch')}")
|
||||
print(f" min_edit_budget:{cfg.get('min_edit_budget', 2)}")
|
||||
print(f" minibatch_size: {cfg.get('minibatch_size')}")
|
||||
print(f" seed: {cfg.get('seed')}")
|
||||
print(f" meta_reflect: {cfg.get('use_meta_reflect', False)}")
|
||||
print(f" meta_skill: {cfg.get('use_meta_skill', False)}")
|
||||
print(f" out_root: {cfg.get('out_root')}")
|
||||
print(f"{'='*60}\n")
|
||||
|
||||
# Build adapter
|
||||
adapter = get_adapter(cfg)
|
||||
|
||||
# Build trainer and run
|
||||
from reflact.engine.trainer import ReflACTTrainer
|
||||
trainer = ReflACTTrainer(cfg, adapter)
|
||||
summary = trainer.train()
|
||||
|
||||
print(f"\n Output saved to: {cfg['out_root']}")
|
||||
if summary.get("test_hard") is not None:
|
||||
print(f" Final test: {summary['test_hard']:.4f}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
43
scripts/train_searchqa.sh
Executable file
43
scripts/train_searchqa.sh
Executable file
@@ -0,0 +1,43 @@
|
||||
#!/usr/bin/env bash
|
||||
# ──────────────────────────────────────────────────────────────────────────────
|
||||
# ReflACT3 — SearchQA Training
|
||||
#
|
||||
# Usage:
|
||||
# bash scripts/train_searchqa.sh
|
||||
# bash scripts/train_searchqa.sh --limit 50
|
||||
# bash scripts/train_searchqa.sh --num_epochs 2 --workers 32
|
||||
# ──────────────────────────────────────────────────────────────────────────────
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
PROJECT_ROOT="$(dirname "${SCRIPT_DIR}")"
|
||||
export PYTHONPATH="${PROJECT_ROOT}:${PYTHONPATH:-}"
|
||||
|
||||
# ── Models ───────────────────────────────────────────────────────────────────
|
||||
export TEACHER_DEPLOYMENT="${TEACHER_DEPLOYMENT:-gpt-5.5}"
|
||||
export STUDENT_DEPLOYMENT="${STUDENT_DEPLOYMENT:-gpt-5.5}"
|
||||
|
||||
# ── Data ─────────────────────────────────────────────────────────────────────
|
||||
DATA_PATH="/home/azureuser/workspace-yqh/refleAct/search-qa/data/searchqa_train_2000.json"
|
||||
|
||||
# ── Output ───────────────────────────────────────────────────────────────────
|
||||
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
|
||||
DEFAULT_OUT_ROOT="${PROJECT_ROOT}/outputs/searchqa-metaskill/searchqa_${STUDENT_DEPLOYMENT}"
|
||||
|
||||
echo "============================================================"
|
||||
echo " ReflACT3 — SearchQA Training"
|
||||
echo " Teacher: ${TEACHER_DEPLOYMENT}"
|
||||
echo " Student: ${STUDENT_DEPLOYMENT}"
|
||||
echo " Data: ${DATA_PATH}"
|
||||
echo "============================================================"
|
||||
|
||||
cd "${PROJECT_ROOT}"
|
||||
|
||||
python scripts/train.py \
|
||||
--config configs/searchqa_default.yaml \
|
||||
--data_path "${DATA_PATH}" \
|
||||
--out_root "${DEFAULT_OUT_ROOT}" \
|
||||
"$@"
|
||||
|
||||
echo ""
|
||||
echo "Done! Results saved to: ${DEFAULT_OUT_ROOT}"
|
||||
48
scripts/train_spreadsheet_multi.sh
Executable file
48
scripts/train_spreadsheet_multi.sh
Executable file
@@ -0,0 +1,48 @@
|
||||
#!/usr/bin/env bash
|
||||
# ──────────────────────────────────────────────────────────────────────────────
|
||||
# ReflACT — SpreadsheetBench training (MULTI-ROUND codegen, no tool-call)
|
||||
#
|
||||
# Usage:
|
||||
# bash scripts/train_spreadsheet_multi.sh
|
||||
# bash scripts/train_spreadsheet_multi.sh --num_epochs 2 --max_turns 5
|
||||
# ──────────────────────────────────────────────────────────────────────────────
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
PROJECT_ROOT="$(dirname "${SCRIPT_DIR}")"
|
||||
|
||||
export PYTHONPATH="${PROJECT_ROOT}:${PYTHONPATH:-}"
|
||||
export TEACHER_DEPLOYMENT="${TEACHER_DEPLOYMENT:-gpt-5.5}"
|
||||
export STUDENT_DEPLOYMENT="${STUDENT_DEPLOYMENT:-gpt-5.5}"
|
||||
|
||||
DATA_ROOT="/home/azureuser/workspace-yqh/sr/spreadsheetbench/data/spreadsheetbench_verified_400"
|
||||
JSONL_PATH="${DATA_ROOT}/dataset.json"
|
||||
SPLIT_DIR="/home/azureuser/workspace-yqh/refleACT3/data/spreadsheetbench_split_2_1_7"
|
||||
|
||||
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
|
||||
OUT_ROOT="${PROJECT_ROOT}/outputs/spreadsheet-metaskill-new/train_multi_${STUDENT_DEPLOYMENT}"
|
||||
|
||||
echo "============================================================"
|
||||
echo " ReflACT — SpreadsheetBench Training (MULTI-ROUND)"
|
||||
echo "============================================================"
|
||||
echo " Teacher: ${TEACHER_DEPLOYMENT}"
|
||||
echo " Student: ${STUDENT_DEPLOYMENT}"
|
||||
echo " Mode: multi"
|
||||
echo " Data: ${DATA_ROOT}"
|
||||
echo " Split: ${SPLIT_DIR}"
|
||||
echo " Output: ${OUT_ROOT}"
|
||||
echo "============================================================"
|
||||
|
||||
cd "${PROJECT_ROOT}"
|
||||
|
||||
python scripts/train.py \
|
||||
--config configs/spreadsheetbench_default.yaml \
|
||||
--mode multi \
|
||||
--data_root "${DATA_ROOT}" \
|
||||
--jsonl_path "${JSONL_PATH}" \
|
||||
--split_dir "${SPLIT_DIR}" \
|
||||
--out_root "${OUT_ROOT}" \
|
||||
"$@"
|
||||
|
||||
echo ""
|
||||
echo "Done! Results saved to: ${OUT_ROOT}"
|
||||
48
scripts/train_spreadsheet_single.sh
Executable file
48
scripts/train_spreadsheet_single.sh
Executable file
@@ -0,0 +1,48 @@
|
||||
#!/usr/bin/env bash
|
||||
# ──────────────────────────────────────────────────────────────────────────────
|
||||
# ReflACT — SpreadsheetBench training (SINGLE-ROUND codegen, no tool-call)
|
||||
#
|
||||
# Usage:
|
||||
# bash scripts/train_spreadsheet_single.sh
|
||||
# bash scripts/train_spreadsheet_single.sh --num_epochs 2 --edit_budget 6
|
||||
# ──────────────────────────────────────────────────────────────────────────────
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
PROJECT_ROOT="$(dirname "${SCRIPT_DIR}")"
|
||||
|
||||
export PYTHONPATH="${PROJECT_ROOT}:${PYTHONPATH:-}"
|
||||
export TEACHER_DEPLOYMENT="${TEACHER_DEPLOYMENT:-gpt-5.5}"
|
||||
export STUDENT_DEPLOYMENT="${STUDENT_DEPLOYMENT:-gpt-5.5}"
|
||||
|
||||
DATA_ROOT="/home/azureuser/workspace-yqh/sr/spreadsheetbench/data/spreadsheetbench_verified_400"
|
||||
JSONL_PATH="${DATA_ROOT}/dataset.json"
|
||||
SPLIT_DIR="/home/azureuser/workspace-yqh/refleACT3/data/spreadsheetbench_split_2_1_7"
|
||||
|
||||
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
|
||||
OUT_ROOT="${PROJECT_ROOT}/outputs/spreadsheet-metaskill-new/train_single_${STUDENT_DEPLOYMENT}"
|
||||
|
||||
echo "============================================================"
|
||||
echo " ReflACT — SpreadsheetBench Training (SINGLE-ROUND)"
|
||||
echo "============================================================"
|
||||
echo " Teacher: ${TEACHER_DEPLOYMENT}"
|
||||
echo " Student: ${STUDENT_DEPLOYMENT}"
|
||||
echo " Mode: single"
|
||||
echo " Data: ${DATA_ROOT}"
|
||||
echo " Split: ${SPLIT_DIR}"
|
||||
echo " Output: ${OUT_ROOT}"
|
||||
echo "============================================================"
|
||||
|
||||
cd "${PROJECT_ROOT}"
|
||||
|
||||
python scripts/train.py \
|
||||
--config configs/spreadsheetbench_default.yaml \
|
||||
--mode single \
|
||||
--data_root "${DATA_ROOT}" \
|
||||
--jsonl_path "${JSONL_PATH}" \
|
||||
--split_dir "${SPLIT_DIR}" \
|
||||
--out_root "${OUT_ROOT}" \
|
||||
"$@"
|
||||
|
||||
echo ""
|
||||
echo "Done! Results saved to: ${OUT_ROOT}"
|
||||
218
scripts/watch_ablation.py
Normal file
218
scripts/watch_ablation.py
Normal file
@@ -0,0 +1,218 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Watch an ablation run root and rerun final failures.
|
||||
|
||||
This watcher is intended to run in tmux next to scripts/run_ablation_matrix.py.
|
||||
It writes STATUS.md on every poll and starts a direct rerun for any run that
|
||||
the launcher marks as final [FAIL].
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
import time
|
||||
from pathlib import Path
|
||||
|
||||
from run_ablation_matrix import PROJECT_ROOT, build_matrix, command_for
|
||||
|
||||
|
||||
RUN_RE = re.compile(r"\[(START|DONE|FAIL|RETRY)\]\s+([^\s]+)")
|
||||
ERROR_RE = re.compile(
|
||||
r"Traceback|RuntimeError|AuthenticationError|PermissionDenied|"
|
||||
r"DeploymentNotFound|LLM call failed|LLM message call failed|"
|
||||
r"BadRequestError|RateLimitError",
|
||||
re.IGNORECASE,
|
||||
)
|
||||
|
||||
|
||||
def read_text(path: Path) -> str:
|
||||
try:
|
||||
return path.read_text(encoding="utf-8", errors="replace")
|
||||
except FileNotFoundError:
|
||||
return ""
|
||||
|
||||
|
||||
def parse_launcher(path: Path) -> dict[str, list[str]]:
|
||||
events = {"START": [], "DONE": [], "FAIL": [], "RETRY": []}
|
||||
for line in read_text(path).splitlines():
|
||||
match = RUN_RE.search(line)
|
||||
if match:
|
||||
events[match.group(1)].append(match.group(2))
|
||||
return events
|
||||
|
||||
|
||||
def active_run_ids(run_root: Path) -> list[str]:
|
||||
try:
|
||||
raw = subprocess.check_output(["pgrep", "-af", "scripts/train.py"], text=True)
|
||||
except subprocess.CalledProcessError:
|
||||
return []
|
||||
active: list[str] = []
|
||||
pattern = re.compile(re.escape(str(run_root)) + r"/([^\s]+)")
|
||||
for line in raw.splitlines():
|
||||
for match in pattern.finditer(line):
|
||||
active.append(match.group(1))
|
||||
return sorted(set(active))
|
||||
|
||||
|
||||
def scan_errors(logs_dir: Path) -> dict[str, str]:
|
||||
errors: dict[str, str] = {}
|
||||
for log_path in sorted(logs_dir.glob("*.log")):
|
||||
text = read_text(log_path)
|
||||
match = ERROR_RE.search(text)
|
||||
if match:
|
||||
run_id = log_path.name.split(".watchrerun", 1)[0].removesuffix(".log")
|
||||
errors[run_id] = match.group(0)
|
||||
return errors
|
||||
|
||||
|
||||
def load_state(path: Path) -> dict:
|
||||
try:
|
||||
return json.loads(path.read_text(encoding="utf-8"))
|
||||
except Exception:
|
||||
return {"reruns": {}}
|
||||
|
||||
|
||||
def save_state(path: Path, state: dict) -> None:
|
||||
tmp = path.with_suffix(".tmp")
|
||||
tmp.write_text(json.dumps(state, indent=2, ensure_ascii=False), encoding="utf-8")
|
||||
tmp.replace(path)
|
||||
|
||||
|
||||
def write_status(
|
||||
run_root: Path,
|
||||
total: int,
|
||||
events: dict[str, list[str]],
|
||||
active: list[str],
|
||||
completed: list[str],
|
||||
pending: list[str],
|
||||
errors: dict[str, str],
|
||||
reruns: dict[str, int],
|
||||
) -> None:
|
||||
now = time.strftime("%Y-%m-%d %H:%M:%S UTC", time.gmtime())
|
||||
failed = sorted(set(events["FAIL"]))
|
||||
retrying = sorted(set(events["RETRY"]))
|
||||
lines = [
|
||||
"# Ablation Status",
|
||||
"",
|
||||
f"Updated: {now}",
|
||||
f"Run root: `{run_root}`",
|
||||
"",
|
||||
"| Metric | Count |",
|
||||
"| --- | ---: |",
|
||||
f"| Total planned | {total} |",
|
||||
f"| Completed summaries | {len(completed)} |",
|
||||
f"| Active train processes | {len(active)} |",
|
||||
f"| Pending/not summarized | {len(pending)} |",
|
||||
f"| Launcher final fails | {len(failed)} |",
|
||||
f"| Launcher retries | {len(retrying)} |",
|
||||
f"| Logs with error patterns | {len(errors)} |",
|
||||
"",
|
||||
"## Active",
|
||||
"",
|
||||
*(f"- `{run_id}`" for run_id in active),
|
||||
"",
|
||||
"## Final Fails",
|
||||
"",
|
||||
*(f"- `{run_id}` watcher_reruns={reruns.get(run_id, 0)}" for run_id in failed),
|
||||
"",
|
||||
"## Error Patterns",
|
||||
"",
|
||||
*(f"- `{run_id}`: `{err}`" for run_id, err in sorted(errors.items())),
|
||||
"",
|
||||
"## Recent Launcher",
|
||||
"",
|
||||
"```text",
|
||||
"\n".join(read_text(run_root / "launcher.log").splitlines()[-30:]),
|
||||
"```",
|
||||
]
|
||||
(run_root / "STATUS.md").write_text("\n".join(lines) + "\n", encoding="utf-8")
|
||||
|
||||
|
||||
def main() -> None:
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("--run-root", required=True)
|
||||
parser.add_argument("--interval", type=int, default=60)
|
||||
parser.add_argument("--watcher-retries", type=int, default=1)
|
||||
parser.add_argument("--groups", nargs="+", default=["all"])
|
||||
parser.add_argument("--bench", nargs="+", default=["searchqa", "spreadsheetbench"])
|
||||
args = parser.parse_args()
|
||||
|
||||
run_root = Path(args.run_root).resolve()
|
||||
logs_dir = run_root / "logs"
|
||||
logs_dir.mkdir(parents=True, exist_ok=True)
|
||||
state_path = run_root / "watcher_state.json"
|
||||
|
||||
groups = set(args.groups)
|
||||
if "all" in groups:
|
||||
groups = {"default", "split", "mbs", "lr", "sched", "slown", "mod", "smodel"}
|
||||
experiments = {
|
||||
exp.run_id: exp
|
||||
for exp in build_matrix(groups, args.bench, run_root, include_duplicate_defaults=False)
|
||||
}
|
||||
|
||||
active_reruns: dict[str, subprocess.Popen] = {}
|
||||
while True:
|
||||
state = load_state(state_path)
|
||||
reruns = state.setdefault("reruns", {})
|
||||
events = parse_launcher(run_root / "launcher.log")
|
||||
active = active_run_ids(run_root)
|
||||
completed = sorted(
|
||||
run_id for run_id in experiments
|
||||
if (run_root / run_id / "summary.json").exists()
|
||||
)
|
||||
pending = sorted(set(experiments) - set(completed))
|
||||
errors = scan_errors(logs_dir)
|
||||
|
||||
# Reap watcher-started reruns.
|
||||
for run_id, proc in list(active_reruns.items()):
|
||||
rc = proc.poll()
|
||||
if rc is None:
|
||||
continue
|
||||
active_reruns.pop(run_id, None)
|
||||
with open(logs_dir / f"{run_id}.watcher.log", "a", encoding="utf-8") as f:
|
||||
f.write(f"\n[WATCHER_DONE] rc={rc} time={time.time()}\n")
|
||||
|
||||
for run_id in sorted(set(events["FAIL"])):
|
||||
if run_id not in experiments:
|
||||
continue
|
||||
if (run_root / run_id / "summary.json").exists():
|
||||
continue
|
||||
if run_id in active or run_id in active_reruns:
|
||||
continue
|
||||
count = int(reruns.get(run_id, 0))
|
||||
if count >= args.watcher_retries:
|
||||
continue
|
||||
reruns[run_id] = count + 1
|
||||
save_state(state_path, state)
|
||||
log_path = logs_dir / f"{run_id}.watchrerun{count + 1}.log"
|
||||
with open(log_path, "w", encoding="utf-8") as log_f:
|
||||
log_f.write(f"[WATCHER_START] run_id={run_id} attempt={count + 1}\n")
|
||||
log_f.flush()
|
||||
proc = subprocess.Popen(
|
||||
command_for(experiments[run_id]),
|
||||
cwd=PROJECT_ROOT,
|
||||
stdout=log_f,
|
||||
stderr=subprocess.STDOUT,
|
||||
text=True,
|
||||
close_fds=True,
|
||||
)
|
||||
active_reruns[run_id] = proc
|
||||
|
||||
save_state(state_path, state)
|
||||
write_status(
|
||||
run_root=run_root,
|
||||
total=len(experiments),
|
||||
events=events,
|
||||
active=active,
|
||||
completed=completed,
|
||||
pending=pending,
|
||||
errors=errors,
|
||||
reruns={k: int(v) for k, v in reruns.items()},
|
||||
)
|
||||
time.sleep(max(5, int(args.interval)))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user