Add a thin async loop (run_campaign_loop) that ticks on real wall-clock time,
maps elapsed×time_scale to a window offset via the pure decide_schedule, spawns
due child Runs, and marks the campaign COMPLETED at window end. All authority
lives in the DB (started_at, spawned_indices, status), so the app lifespan can
resume every RUNNING campaign on startup without double-spawning and stop all
loops gracefully on shutdown. A failing plan entry is skipped and recorded
rather than wedging the campaign.
Creating a campaign now starts its loop; POST /api/campaigns/{id}/cancel stops
further spawning (completed child Runs are kept); GET /api/campaigns/{id}
reports live progress (window offset, spawned/completed Run counts).
|
||
|---|---|---|
| .. | ||
| __init__.py | ||
| auth.py | ||
| campaigns.py | ||
| files.py | ||
| model_configs.py | ||
| proxy.py | ||
| reports.py | ||
| runs.py | ||
| scenarios.py | ||
| stats.py | ||
| targets.py | ||