#!/usr/bin/env python3 from __future__ import annotations import argparse import json import os import subprocess import time from pathlib import Path def read_text(path: Path, limit: int = 20000) -> str: if not path.exists(): return "" text = path.read_text(encoding="utf-8", errors="replace") if len(text) <= limit: return text return text[-limit:] def write_json(path: Path, value: dict) -> None: path.parent.mkdir(parents=True, exist_ok=True) path.write_text(json.dumps(value, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") def append_jsonl(path: Path, value: dict) -> None: path.parent.mkdir(parents=True, exist_ok=True) with path.open("a", encoding="utf-8") as handle: handle.write(json.dumps(value, ensure_ascii=False, sort_keys=True) + "\n") def next_iteration(research: Path) -> int: highest = 0 for folder in ("logs", "prompts", "runs"): path = research / folder if not path.exists(): continue for item in path.iterdir(): try: highest = max(highest, int(item.stem)) except ValueError: pass return highest + 1 def build_prompt(workspace: Path, iteration: int) -> str: research = workspace / ".dumont_research" brief = read_text(workspace / "docs" / "autoresearch_tts_foundation_brief.md", 24000) qwen = read_text(workspace / "docs" / "qwen3_tts_smoke_test.md", 24000) memory = read_text(research / "wiki" / "memory.md", 24000) experiments = read_text(research / "experiments.jsonl", 16000) return f""" You are running Dumont Rust AutoResearch iteration {iteration}. Use model judgment, but act like a measured ML researcher. Hard constraints: - Use the Rust Dumont tool loop only. - Do not add TypeScript backend code. - Generated code must follow the project style: no comments, no docstrings, simplest correct change. - Work inside {workspace}. - Keep changes small and reversible. - Prefer creating or improving benchmarks, manifests, prompts, evaluation scripts, and experiment plans before launching expensive training. - Use web_search or WebFetch for at least one current external source unless the previous iteration already fetched the same source. - Read local files before proposing changes. - Record the result in .dumont_research/runs/{iteration:04d}.md. - Append a compact JSON object to .dumont_research/experiments.jsonl. - Update .dumont_research/wiki/memory.md with durable learnings. Research question: How can Qwen/Qwen3.5-0.8B drive Qwen/Qwen3-TTS-Tokenizer-12Hz tokens for faster, clearer Brazilian Portuguese teacher audio, using the validated project facts, fine-tuning fundamentals, and current literature? Focus this iteration on exactly one useful step: 1. strengthen the benchmark for the student/teacher audio loop, 2. improve the hypothesis space for TTS token generation, 3. prepare a safe small experiment, 4. implement a small evaluation helper, 5. or run a cheap smoke/eval if dependencies are available. Foundation brief: {brief} Qwen smoke doc: {qwen} Current memory: {memory} Recent experiments: {experiments} End with a short status block containing: iteration, files_changed, command_run, metric, keep, next_step. """.strip() def run_iteration(args: argparse.Namespace, iteration: int) -> int: workspace = Path(args.workspace).resolve() research = workspace / ".dumont_research" prompt_dir = research / "prompts" log_dir = research / "logs" prompt_dir.mkdir(parents=True, exist_ok=True) log_dir.mkdir(parents=True, exist_ok=True) prompt = build_prompt(workspace, iteration) prompt_path = prompt_dir / f"{iteration:04d}.txt" prompt_path.write_text(prompt, encoding="utf-8") env = os.environ.copy() if args.config: env["DUMONT_CONFIG"] = args.config command = [ args.dumont_bin, "-p", prompt, "--model", args.model, "--output-format", "text", ] started = time.time() result = subprocess.run( command, cwd=workspace, env=env, text=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, timeout=args.timeout, ) elapsed = time.time() - started log_path = log_dir / f"{iteration:04d}.log" log_path.write_text(result.stdout, encoding="utf-8", errors="replace") append_jsonl( research / "driver.jsonl", { "iteration": iteration, "exit_code": result.returncode, "elapsed_seconds": round(elapsed, 3), "log": str(log_path), "prompt": str(prompt_path), "time": int(time.time()), }, ) return result.returncode def main() -> int: parser = argparse.ArgumentParser() parser.add_argument("--workspace", required=True) parser.add_argument("--dumont-bin", required=True) parser.add_argument("--config", default="") parser.add_argument("--model", default="minimax/MiniMax-M3") parser.add_argument("--iterations", type=int, default=20) parser.add_argument("--sleep", type=int, default=30) parser.add_argument("--timeout", type=int, default=1800) args = parser.parse_args() workspace = Path(args.workspace).resolve() research = workspace / ".dumont_research" (research / "wiki").mkdir(parents=True, exist_ok=True) memory = research / "wiki" / "memory.md" if not memory.exists(): memory.write_text("# Research Memory\n\n", encoding="utf-8") state_path = research / "state.json" state = {"started": int(time.time()), "workspace": str(workspace), "model": args.model} write_json(state_path, state) exit_code = 0 first_iteration = next_iteration(research) for offset in range(args.iterations): iteration = first_iteration + offset try: code = run_iteration(args, iteration) if code != 0: exit_code = code except subprocess.TimeoutExpired as exc: append_jsonl( research / "driver.jsonl", { "iteration": iteration, "exit_code": 124, "elapsed_seconds": args.timeout, "error": "timeout", "time": int(time.time()), }, ) exit_code = 124 if offset + 1 < args.iterations: time.sleep(args.sleep) state["finished"] = int(time.time()) state["exit_code"] = exit_code write_json(state_path, state) return exit_code if __name__ == "__main__": raise SystemExit(main())