"""ENTRYPOINT #1 — the nightly research run. Brings up oMLX, then runs the 3 personas strictly sequentially (one model, time-sliced), each under a rollover wall-clock budget bounded by the hard stop. Every agent's output is checkpointed to state/output//. Run with --compressed for a fast end-to-end test (short budgets). uv run python -m orchestration.run_nightly [--compressed] """ from __future__ import annotations import sys import time from datetime import datetime, timezone from pathlib import Path sys.path.insert(0, str(Path(__file__).resolve().parent.parent)) from loguru import logger # noqa: E402 from agents.graph import run_agent # noqa: E402 from agents.schemas import RunManifest # noqa: E402 from config.agent_registry import load_personas # noqa: E402 from config.settings import get_settings # noqa: E402 from llm import server # noqa: E402 from llm.client import resolve_model_id # noqa: E402 from orchestration.budget import ( # noqa: E402 local_hard_stop_ts, next_deadline, next_market_trade_date, ) from orchestration import committee as committee_mod # noqa: E402 from orchestration.checkpoint import Checkpointer # noqa: E402 from orchestration.regime import market_regime # noqa: E402 from reporting import report_writer # noqa: E402 from reporting.journal import lessons_for # noqa: E402 def main(compressed: bool = False) -> int: s = get_settings() s.ensure_dirs() trade_date = next_market_trade_date(s.market_tz) logger.add(s.log_dir / f"nightly_{trade_date}.log", rotation="10 MB", retention="14 days") logger.info("=== nightly run trade_date={} profile={} compressed={} ===", trade_date, s.model_profile, compressed) if not server.ensure_ready(): logger.error("oMLX not ready; aborting.") return 1 # preflight: confirm internet before committing to a long run (warn but continue) try: from tools.web import web_search probe = web_search.invoke({"query": "stock market news today"}) logger.info("internet preflight: {}", "OK" if "http" in probe else "no results (degraded)") except Exception as e: logger.warning("internet preflight failed (continuing): {}", e) model_name = resolve_model_id() run_id = datetime.now(timezone.utc).strftime("%Y%m%dT%H%M%SZ") cp = Checkpointer(s.output_dir, trade_date) manifest = RunManifest( run_id=run_id, trade_date=trade_date, model=model_name, started_at=datetime.now(timezone.utc), status="running", ) cp.write_manifest(manifest) hard_stop = local_hard_stop_ts(s.hard_stop, s.local_tz) step_budget = 12 if compressed else s.per_agent_step_budget min_steps = 4 if compressed else s.per_agent_min_steps # dashboard-managed registry: built-ins + overrides + custom agents, disabled excluded personas = load_personas() logger.info("agents tonight: {}", ", ".join(p.id for p in personas)) # shared ground truth for all agents tonight (computed, deterministic) regime = market_regime() if regime: logger.info("regime: {}", regime[:140]) outputs = [] records: dict[str, str] = {} for i, persona in enumerate(personas): # per-agent budgets (dashboard-editable), falling back to the global settings if compressed: per_agent_cap_s, min_seconds = 5 * 60, 60 else: per_agent_cap_s = (persona.time_budget_min or s.per_agent_wallclock_min) * 60 min_seconds = (persona.min_research_min or s.per_agent_min_minutes) * 60 now = time.time() if now >= hard_stop: logger.warning("hard stop reached; skipping {} remaining agents", len(personas) - i) break deadline = next_deadline(now, hard_stop, len(personas) - i, per_agent_cap_s) logger.info("--- {} until {} ---", persona.id, datetime.fromtimestamp(deadline).strftime("%H:%M:%S")) if not server.server_up(): # recover if a prior agent left oMLX unresponsive logger.warning("oMLX unreachable before {}; recovering...", persona.id) server.ensure_ready(warm=False) lessons = lessons_for(persona.id) # self-learning: past picks marked to market records[persona.id] = lessons blurb = "\n\n".join(x for x in (regime, lessons) if x) out = run_agent(persona, deadline_ts=deadline, step_budget=step_budget, min_steps=min_steps, min_seconds=min_seconds, context_blurb=blurb, checkpointer=cp, model_name=model_name) cp.write(out) outputs.append(out) manifest.agents.append(persona.id) try: report_writer.write_agent_report(out, trade_date) # analyst research note except Exception as e: logger.warning("agent report failed for {}: {}", persona.id, e) # Investment Committee: risk-officer pass over the whole book (verdicts enforce at open) try: committee_mod.run_committee(outputs, trade_date, regime, records) except Exception as e: logger.warning("committee failed (fail-open): {}", e) # combined daily report (execution results appended in the morning by run_market_open) try: daily = report_writer.write_daily_report(trade_date, outputs) report_writer.to_pdf(daily) except Exception as e: logger.warning("daily report failed: {}", e) manifest.status = "complete" manifest.ended_at = datetime.now(timezone.utc) cp.write_manifest(manifest) logger.info("=== nightly run complete; output in {} ===", cp.dir) return 0 if __name__ == "__main__": raise SystemExit(main(compressed="--compressed" in sys.argv))