Commit Graph
29 Commits
Author SHA1 Message Date
KoalaTrapLord ddfe8e601e fix(eval): parse batch JSON output instead of JSONL for Forge game results 2026-03-30 16:12:20 -04:00
KoalaTrapLord 9e3cd45936 fix(eval): require >= 3 decks for Forge availability check 2026-03-30 15:45:20 -04:00
rgriffith 6fadeaa387 fix(eval): replace Java bridge imports with subprocess CLI launch for Forge eval 2026-03-30 14:23:52 -04:00
rgriffith 517c17047b fix: strip value_head keys when loading PPO checkpoint into PolicyNetwork 2026-03-30 13:31:59 -04:00
rgriffith 574bb79a0b fix(eval): correct indentation for load/reload in _load_policy_service 2026-03-30 10:36:43 -04:00
KoalaTrapLord 5554ef4dd7 fix(eval): call load() for embeddings before reload() for checkpoint 2026-03-30 09:53:56 -04:00
KoalaTrapLord cf2d7ed806 fix(eval): use reload() with explicit checkpoint path instead of load()
load() hardcodes best_policy.pt but PPO trainer saves as best_ppo.pt, causing the model to never load during eval. reload() accepts an explicit path.
2026-03-30 09:51:22 -04:00
KoalaTrapLord 6e71ba88fd fix(encoder): read real total_power from snapshot dict at global index 7 (#83 Step 4)
Previously _encode_global() always computed:
  total_power = creatures * 3  (heuristic)

Now reads p.get('total_power', 0) first, falling back to the heuristic
only when the key is absent (legacy JSONL snapshots without Step 4 data).

Also wires total_toughness, artifacts, and enchantments into _empty_player()
so padding players have consistent keys.
2026-03-29 05:13:19 -04:00
KoalaTrapLord bff7b3a81e feat(eval): add forge_evaluator, eval_policy, compare_eval for issue #153
- ml/eval/forge_evaluator.py: reusable eval harness wrapping
  MultiThreadBatchRunner + PolicyInferenceService; collects win rate,
  avg turns, life differential, commander damage, illegal action rate,
  policy entropy per game; writes results/eval-{run_id}.json
- scripts/eval_policy.py: CLI to run N Forge eval games with any
  checkpoint vs forge-builtin or policy-server opponent; supports
  --threads, --clock, --seed, --greedy, --temperature
- scripts/compare_eval.py: loads two result JSONs, computes absolute
  win-rate delta + 95% CI, runs Fisher's exact test for significance,
  prints formatted comparison table and pass/fail verdict
2026-03-29 04:49:43 -04:00
KoalaTrapLord 11990c2fd3 feat(ml): add --data-source forge flag to PPO trainer (issue #83 phase 1.3)
Wires forge_episode_generator into the PPO training loop:
- New --data-source flag: synthetic (default) | forge | mixed
- New --forge-results-dir, --forge-batch-id, --forge-weight flags
- ForgeDataSource class: runs run_forge_pipeline() in background thread,
  drains episodes into RolloutBuffer via episodes_to_buffer()
- Mixed mode: blends Forge episodes + synthetic self-play per iteration
  using --forge-weight ratio (0.0–1.0)
- Synthetic path unchanged — no regression for existing training runs
2026-03-29 03:02:30 -04:00
KoalaTrapLord cb38081a8b fix: add top_n cutoff to ELO tournament (default top 10 generations) 2026-03-27 01:48:10 -04:00
KoalaTrapLord cb99388e3c fix: correct indentation of total_steps in collect_rollouts 2026-03-26 13:14:34 -04:00
KoalaTrapLord 9bce919690 fix: replace non-existent ForgeEpisodeGenerator import with actual exports 2026-03-24 13:45:48 -04:00
KoalaTrapLord 1f3b17d173 fix: remove ForgeEpisodeQueue import from training __init__
ForgeEpisodeQueue does not exist in rollout_buffer.py yet.
Removing premature import to unblock supervised training pipeline.
2026-03-24 12:26:06 -04:00
KoalaTrapLord f51ff61568 feat(83): Phase 3 — Performance & Scale (#83) (#101)
* feat(83): Phase 3.1 — persistent JVM worker pool in MultiThreadBatchRunner (#83)

* feat(83): Phase 3.2 — async Forge episode generator with producer-consumer pipeline (#83)

* feat(83): Phase 3.3 — deprecate self_play.py synthetic mode for training (#83)

Mark self_play.py synthetic training as deprecated per Phase 3.3:
- Add deprecation notice to module docstring
- Add import warnings and _TRAINING_DEPRECATION_MSG constant
- Add DeprecationWarning in collect_rollouts()
- Synthetic sim retained for local testing/unit tests only
- scope.py already defaults forge_weight=1.0, ppo_weight=0.0

* feat(83): Phase 3.3 — update ml/training/__init__.py exports (#83)
2026-03-24 10:43:51 -04:00
d795e1a8fd feat: Phase 5 architecture follow-up — zone boundary, WAL store, Forge-only defaults (#87)
Implements the three approved Phase 5 architecture decisions:

1. Zone embedding boundary: add explicit documentation in StateDimensions
   and StateEncoder._encode_zones making the 6144-dim zero-padded zone
   contract visible to future maintainers.

2. SQLite WAL online learning store: new OnlineLearningStore class backed
   by WAL-mode SQLite for concurrent read/write on single-machine baseline.
   New /api/policy/decide endpoint collects decisions during inference.
   Stats endpoint at /api/policy/decide/stats.

3. Forge-only default weights: change ppo_weight default from 0.5 to 0.0
   across scope.py, DistillationConfig, DatasetConfig, and CLI defaults.
   Add MIXED_MODE_PRESETS dict with forge_only, forge_90_10, forge_80_20
   presets. Distill/start endpoint now accepts a "preset" parameter.

27 new tests covering all three decisions (all passing).

Co-authored-by: Claude <claude@anthropic.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-22 14:41:33 -04:00
rgriffith ea3d488a41 fix: distillation loop uses wrong results_dir path
The distillation pipeline was looking for JSONL decision files in
data/results/ but they are stored in results/ (matching CFG.results_dir).

This caused all generations to fail at Step 1 with:
  'No ml-decisions-*.jsonl files found in data/results'

Fixed:
- routes/ml.py: results_dir and history_dir now use 'results/' not 'data/results/'
- ml/training/distillation_loop.py: updated DistillationConfig defaults to match
- History endpoint paths updated to match
2026-03-22 02:18:23 +00:00
rgriffith e843528d8b Phase 6: ELO Tracking & Model Tournament (#70)
- Create ml/eval/elo_tracker.py:
  - EloTracker class with standard ELO formula (K=32, start=1200)
  - EloResult dataclass for tournament output
  - EloHistory class for JSON persistence (data/elo_history.json)
  - run_elo_tournament(): round-robin ELO-rated matches using
    existing run_match() from tournament.py
  - run_generation_tournament(): discovers gen-*/best_policy.pt
    checkpoints and runs cross-generation ELO tournament
  - CLI entry point: python -m ml.eval.elo_tracker

- Add ELO API endpoints to routes/ml.py:
  - POST /api/ml/elo/tournament — start background ELO tournament
  - GET  /api/ml/elo/tournament/status — poll progress
  - GET  /api/ml/elo/history — load ELO history from disk

- Integrate ELO convergence in distillation_loop.py:
  - New config fields: elo_convergence_enabled (default False),
    elo_convergence_threshold (20.0), elo_tournament_episodes (30)
  - New field: GenerationRecord.elo_rating
  - _run_elo_mini_tournament() runs after quality gate pass
  - _check_convergence() checks ELO delta when enabled

- Add ELO UI to distillation page:
  - ELO Ratings panel with canvas multi-line chart showing per-model
    ELO progression across generations
  - Run Tournament button with progress indicator
  - ELO leaderboard table with rank, rating, and visual bars
  - Responsive chart resize handling

Closes #70
2026-03-22 00:31:02 +00:00
rgriffith ae00dd47e2 feat: Phase 4 — Distillation Loop Orchestrator (#68)
Implements the closed-loop distillation pipeline that automates:
  1. Supervised train on current dataset (Forge + PPO data)
  2. PPO self-play using supervised model as starting policy
  3. Export winning PPO decisions to JSONL
  4. Quality gate check (win rate threshold)
  5. If passed, merge into dataset and repeat
  6. If failed, discard batch, adjust hyperparameters, retry

New files:
  - ml/training/distillation_loop.py — DistillationLoop orchestrator,
    DistillationConfig, GenerationRecord, CLI entry point

Updated files:
  - ml/config/scope.py — DistillationDefaults dataclass with all
    default hyperparameters for the pipeline

Features:
  - Generation tagging: data and models tagged with gen-NNN
  - Convergence detection: stops when win rate plateaus over N gens
  - Automatic retry with LR reduction + entropy boost on gate failure
  - Per-generation history saved to JSON for observability
  - Full CLI with all tunable params
  - progress_callback for UI integration (Phase 5 ready)

Closes #68
2026-03-22 00:11:14 +00:00
KoalaTrapLord 0f6faa5fe4 feat(ml): integrate DecisionExporter into self-play rollouts (#65) 2026-03-22 00:06:55 +00:00
KoalaTrapLord 8ed3fbc627 feat(ml): add quality gate & validation module (#67)
Phase 3 of closed-loop distillation pipeline.

- QualityGateConfig with configurable thresholds
- create_forge_holdout() for Forge-only eval split (12% by game_id)
- check_accuracy_gate() rejects PPO batch if Forge eval accuracy drops
- check_win_rate_gate() blocks export below configurable win rate
- compute_quality_metrics() logs action entropy, reward distribution
- QualityGate orchestrator class with evaluate_ppo_batch()
2026-03-21 19:34:37 -04:00
KoalaTrapLord 4d50449145 feat(ml): add source awareness to dataset builder (#66)
Phase 2 of closed-loop distillation pipeline.

- Scan both ml-decisions-sim-*.jsonl (Forge) and ml-decisions-ppo-*.jsonl (PPO)
- Add source_weights config for configurable sampling ratios
- Add min_reward_threshold filter for PPO data quality
- Tag each sample with source for downstream analysis
- New CLI args: --forge-weight, --ppo-weight, --min-reward
- NPZ output now includes 'sources' array
2026-03-21 19:28:22 -04:00
KoalaTrapLord 3957285a2e feat(ml): add PPO DecisionExporter for closed-loop distillation (#65)
Phase 1 of closed-loop distillation pipeline.

Adds DecisionExporter class that captures PPO self-play decisions
as JSONL files compatible with the Forge DecisionSnapshot schema.
Includes PPO-specific metadata fields (source, model_version,
reward, episode_return) and configurable win-only filtering.
2026-03-21 18:06:45 -04:00
KoalaTrapLord f290523ce1 fix(ml): replace undefined 'log' with 'logger' in labeler.py 2026-03-21 15:47:47 -04:00
KoalaTrapLord 36760dd922 fix(ml): rename log -> logger in dataset_builder.py to fix NameError 2026-03-21 12:42:02 -04:00
KoalaTrapLord 8d1a916f04 feat(ml): add update_weights.py — outcome-weighted RL learning from decision logs 2026-03-14 02:58:32 -04:00
KoalaTrapLord a1fd290d5f refactor: adopt Python logging module with named loggers
Replace all print() calls with structured logging using the Python
logging module. This provides:

- Named loggers per subsystem: commander_ai_lab.api, .batch, .sim,
  .coach, .deckgen, .collection, .scanner, .ml, .cache, .pplx
- Dual output: console (StreamHandler) + rotating file (RotatingFileHandler)
  at logs/commander-ai-lab.log (25 MB x 5 backups = 125 MB max)
- Configurable via env vars: CAL_LOG_DIR, CAL_LOG_MAX_MB, CAL_LOG_BACKUP_COUNT
- --verbose / -v flag for DEBUG-level output
- overnight-run.py: separate rotating log at logs/overnight-run.log (10 MB x 5)
- Sub-module loggers in ml/, scanner/, deck_sources/, src/ all propagate
  to the root commander_ai_lab logger
- Stripped redundant [TAG] prefixes from messages (logger name carries context)
- Format: '2026-03-13 14:00:00 INFO  [commander_ai_lab.batch] message'

Files changed: lab_api.py, overnight-run.py, and 11 sub-module .py files
176 total print() calls converted (101 in lab_api.py, 76 in sub-modules)
2026-03-13 18:09:52 +00:00
KoalaTrapLord a5fe8cb3fa v23: React + TypeScript SPA migration
Complete frontend rewrite from vanilla HTML/CSS/JS to React 19 + TypeScript + Tailwind CSS v4.

Frontend (frontend/commander-ai-lab-ui/):
- Vite + React 19 + TypeScript + Tailwind CSS v4 + React Router
- 7 pages: BatchSim, Collection, Decks, AutoGen, Simulator, Coach, Training
- Reusable components: Spinner, StatusBadge, CardImage, ManaSymbols, EmptyState
- Full typed API client layer (8 modules matching all backend endpoints)
- Recharts for mana curves, win rate charts, progress bars
- Lucide icons throughout
- Custom dark theme with MTG mana color tokens
- Code splitting (recharts + react in separate chunks)

Backend (lab_api.py):
- SPA-aware static file serving with catch-all for client-side routing
- Fallback to legacy ui/ folder if React dist not built
- Updated startup log with SPA route list

Build system:
- .gitignore added (was missing - removes pyc, db, node_modules, etc.)
- build-full-installer-v2.py: v23 with Node.js check + React build step
- Vite build config with manual chunks for optimal loading

Also includes: V3 deck generator fixes, coach improvements, DeepSeek sim modules
2026-03-12 01:12:02 +00:00
KoalaTrapLord e6b5e0cedb Initial commit 2026-03-10 12:13:56 -04:00