Commit Graph
136 Commits
Author SHA1 Message Date
rgriffithandClaude Opus 4.6 409aba26f4 feat: AI bug fixes, deck/collection migration, and enrichment pipeline
- Fix AI turn pipeline: add cmc to Scryfall cache, tag isRamp/isRemoval/
  isBoardWipe from oracle text during enrichment, enable post-combat spell
  casting for all difficulty levels
- Migrate deck builder, deck generator, and collection from ui/ to
  lan-client/ as iframe-embedded panels with LAB_API_BASE patching
- Fix ai-bridge.js: turnNumber, deck_name, battlefield serialization,
  5-playstyle mapping, window exports for cross-script access
- Fix engine.js: battlefieldCards flat array (not player.battlefield),
  aiDeclareAttacks extraction, combat overlay fixes
- Fix collection.js: template literal, column visibility alignment,
  set_code key, oracle text column, sortable category
- Add exile/graveyard last-card image rendering in ui.js
- Make state_encoder.py resilient to mixed input types
- Enrich type_line from Scryfall at card insert in deckbuilder.py

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-05 00:05:23 -04:00
KoalaTrapLord 85af163123 Wire Forge IPC into POST /api/game/start via run_batch_subprocess + --policy flag 2026-04-03 17:57:35 -04:00
KoalaTrapLord e4f35a2d8b debug: log top-5 action probabilities on every decide() call
Adds a single INFO log line after each successful inference showing:
  - chosen action + confidence
  - top-5 actions sorted by probability
  - turn, phase, vector_source

This lets us diagnose the 'only plays lands' behaviour without a Python
REPL. Remove or downgrade to DEBUG once the training issue is resolved.
2026-04-03 16:08:50 -04:00
KoalaTrapLord 4ca21e5e29 debug: add exc_info=True + unique logger names to expose real traceback
Both 'Inference error' log lines use identical logger strings, making it
impossible to tell which except block is catching the crash. Also, broad
except blocks were swallowing the full traceback so the real crash site
and cause were hidden.

Changes:
- policy_server.py: logger renamed to 'ml.serving.policy_server';
  predict() and predict_batch() error handlers add exc_info=True
- routes/policy.py: logger renamed to 'routes.policy.decide';
  _infer_from_vector() error handler adds exc_info=True;
  decide() fallback warning adds exc_info=True

The next crash will print the full stack trace, pinpointing the exact
line and call chain causing the TypeError.
2026-04-03 15:39:54 -04:00
KoalaTrapLord 28665bf663 fix: apply _unwrap_logits guard in _infer_from_vector (routes/policy.py)
The crash `unsupported operand type(s) for /: 'dict' and 'float'` was
firing from _infer_from_vector(), which is the code path taken when
has_precomputed_vector() is True (GameSession 1.1.0+ snapshots with
a precomputed state_vector float[29]).

Unlike PolicyInferenceService.predict() — which was fixed in the
previous commit via _unwrap_logits — this function calls
policy_service.model(state_tensor) directly and immediately uses the
return value in arithmetic (logits / max(temperature, 0.01)), so any
tuple/dict return (DataParallel, torch.compile, PolicyValueNetwork
swap) causes the TypeError.

Fix: import _unwrap_logits from ml.serving.policy_server and apply it
to the raw model output in _infer_from_vector before the temperature
division and argmax calls. All three return shapes are now handled:
  - plain tensor  → passthrough (normal case, zero overhead)
  - tuple/list    → raw[0]  (PolicyValueNetwork (logits, value))
  - dict          → first recognised key or first tensor value
2026-04-03 15:16:49 -04:00
KoalaTrapLord f29a5a31b5 feat: add /api/lab/precons and /api/lab/precons/deck endpoints
Fixes 404 errors from lan-client ai-bridge.js which calls these routes
to populate the deck selector. Routes were missing from lab.py — only
precon_router (routes/precon.py) existed but served different paths.

Also added PRECON_DECKS_DIR env var alias in lab_api.py _parse_args()
to match the .env variable name users commonly set.
2026-04-03 11:08:12 -04:00
Commander AI Lab 7750b43494 fix: deck loading — wire startGameWithDecks, fetch .dck content, parse [sections]
Three fixes that prevented precon decks from loading into actual gameplay:

1. ui.js: window.startGame() now calls startGameWithDecks() when
   setupDeckChoices has entries (the 3-tab selector stores choices there).
   Previously it called initGame() which looked for old deck-sel-{i}
   dropdowns that no longer exist.

2. engine.js: loadPreconForPlayer() now fetches .dck file content from
   the backend (/api/lab/precons/deck?fileName=...) when precon.decklist
   is empty. Falls back to fetching precon-decks/{file} directly.
   PRECON_DATA entries only have metadata (name, commander, set) — the
   actual card list lives in the .dck files.

3. engine.js: parseDeckList() now handles .dck bracket-style section
   headers like [Commander], [Main], [metadata]. Previously only matched
   bare text like 'COMMANDER:' without brackets. Also skips [metadata]
   lines (e.g. 'Name=Elven Empire').

4. routes/precon.py: Added GET /api/lab/precons/deck?fileName=... endpoint
   to serve raw .dck file content for the LAN client.
2026-04-03 13:45:15 +00:00
KoalaTrapLord ca6c23a881 fix: wrap shutil.copy2 in try/except for install and install-batch endpoints 2026-04-01 21:32:19 -04:00
KoalaTrapLord e4a6ce644d fix(#176): draw-game life capture, DrawGameAnalyzer, clock 6000→3000 (#181)
Captures life-at-timeout snapshots for every draw game via [DRAW] [PARSE-SUMMARY] log lines. Adds services/draw_game_analyzer.py with DrawGameSnapshot + summarize_draw_games(). Wires drawGames summary into GET /api/lab/result response. Lowers batch clock default from 6000 to 3000 (30s per-game). Closes #176.
2026-04-01 14:40:29 -04:00
KoalaTrapLord 5411fd6bf6 fix(#175): alternate win condition parser & winType tagging (#179)
Adds win_condition_parser.py, GameResult.win_type field, and winType stamping on both the Java/Forge and DeepSeek batch paths. Closes #175.
2026-04-01 14:28:10 -04:00
KoalaTrapLord 3f5d6d12e8 feat: 4-player Commander Battleground UI (issues #138–#151)
* chore: add battleground UI task tracker (2026-03-28)

* feat(#142): add [NonSerialized] battleground runtime state to CardModel

* feat(#143): add LoadArtCrop and LoadCardRaw (RawImage) to ImageCache

* feat(#148): add GameStateManager singleton and GameState/SeatState POCOs

* feat(#138): add BattlegroundController scene orchestrator

* feat(#139): add LifeTrackerWidget, CommanderDamageRow, PoisonCounter

* feat(#140): add PhaseTrackerWidget and PhaseStepButton

* feat(#141): add TurnOrderBar and TurnOrderPill

* feat(#142): add CardZone and CardView battlefield zone components

* feat(#143): add CommanderZoneWidget with art crop, cast counter, and tax display

* feat(#144): add TokenModel POCO, TokenPanelController, and TokenRowView

* chore: mark issues 1-8 as complete in task tracker

* feat(#146): add StackZoneController, StackItemView, and ManaPoolDisplay

* feat(#147 #150 #151): add LobbySetupModal, ZoneViewerModal, EliminationHandler; add Play Live button to SimulationController

* feat(#151): add Play Live button to SimulationController

* feat(#149): add GameWebSocketClient with reconnect, ping, and main-thread dispatch

* fix: add PostGameAction to ApiClient + update TODO.md all issues complete

- ApiClient.cs: add PostGameAction() wrapper → POST /api/game/action
  (fixes compile error in HumanActionBar.cs)
- TODO.md: mark issues #145, #146, #147, #149, #150, #151 as ✅
  all 14 battleground issues now committed

* feat(game): add routes/game.py + wire lab_api + fix GameWebSocketClient game_id URL

- routes/game.py: POST /api/game/start, POST /api/game/action (new)
- lab_api.py: import + register game_router
- GameWebSocketClient.cs: read game_id from PlayerPrefs, build
  ws://{host}/ws/game/{game_id} — fixes URL mismatch with ws_game.py

* fix(lobby): wire OnConfirm to POST /api/game/start, save game_id to PlayerPrefs

- OnConfirm() now calls ApiClient.PostGameStart() with 4-seat config
- On success: saves BattleSeats + GameId + WsUrl to PlayerPrefs,
  loads Battleground scene
- On failure: shows inline error label (red), re-enables Confirm button
- Added StartGameRequest/Response POCOs matching routes/game.py schema
- Added _errorLabel [SerializeField] for user-facing error feedback
2026-03-29 05:35:29 -04:00
KoalaTrapLord b1c17359f0 feat(policy): wire real Forge board stats into encoder snapshot (#83 Step 4)
Previously _build_encoder_snapshot() stubbed:
  - lands = 0  (always zero)
  - creatures = len(bf_names)  (name-list length, ignores tokens/etc)
  - mana = mana_available  (was already correct)
  - total_power / total_toughness / artifacts / enchantments = missing

Now reads real pre-computed values from PlayerZoneState:
  - creatures      ← creaturesOnField   (Forge board stat)
  - lands          ← landCount          (Forge board stat)
  - total_power    ← totalPowerOnBoard  (fed to global feature index 7)
  - total_toughness← totalToughnessOnBoard
  - artifacts      ← artifactsOnField
  - enchantments   ← enchantmentsOnField
  - mana           ← manaAvailable (unchanged)

Also replaces the `creatures * 3` proxy in _build_encoder_snapshot with
the real totalPowerOnBoard value so index-7 of the global feature vector
tracks actual power rather than a heuristic.

PlayerZoneState gains 5 new optional int fields (default 0) matching
DecisionSnapshot.PlayerSnapshot field names so JSON from both the live
IPC path and the JSONL training path deserialise without changes.
2026-03-29 04:49:40 -04:00
KoalaTrapLord 4d73f134c2 feat(policy): consume state_vector from GameSession snapshots (#83 Step 2)
DecideRequest now accepts an optional state_vector field (float[29]).
When present, /api/policy/decide uses it directly for the global scalar
block instead of re-deriving it from nested player fields.

StateEncoder gets a new encode_with_precomputed_global() path that:
  - Accepts the precomputed float[29] global block from Java
  - Still runs full zone embedding pooling (hand/battlefield/graveyard/
    command_zone) from the players[] zone contents
  - Appends the playstyle one-hot as normal
  - Returns the same 6177-dim tensor the policy network expects

Fallback: if state_vector is absent or wrong length, encode() is called
as before so all existing callers (batch predict, JSONL training) are
unaffected.

Also maps GameSession player field names to encoder field names:
  handCount  -> hand size proxy when hand[] is empty (AI seats)
  battlefield list -> creatures count proxy
  commanderTax -> cmdr_tax
  turnNumber  -> turn
  activePlayer -> active_seat
2026-03-29 04:32:52 -04:00
KoalaTrapLord aefd0cace6 feat(rag): Phase 1 — rewrite build_card_text to natural-language format (#137)
* feat(rag): Phase 1 — rewrite build_card_text to natural-language format

* feat(rag): Phase 2 — Forge-enriched ChromaDB RAG store (2a–2e)

Phase 2a: Forge lazy import guard (_try_import_forge_lookup)
Phase 2b: _build_document appends Forge trigger/replacement/static/keyword tags
Phase 2c: _build_metadata adds has_trigger, has_replacement, has_static, trigger_modes fields
Phase 2d: query_cards extended with require_trigger, require_replacement, require_static params + ChromaDB where clause support
Phase 2e: get_rag_stats reports forge_enriched and forge_coverage; _write_build_meta tracks coverage; build loop counts enriched cards

* feat(rag): Phase 3 — add mtgrules second ChromaDB collection

Adds build_rules_index() and query_rules() operating on a separate
'mtgrules' ChromaDB collection. Chunks MagicCompRules.txt by rule-number
boundaries, embeds each chunk, and exposes semantic search. Completely
independent from the mtg_oracle card collection.

* feat(coach): Phase 4 — behavior-aware RAG + rules context in coach_service

Phase 4a: run_coaching_session uses dual RAG query
  - Primary: commander synergy (n=10)
  - Secondary: require_replacement=True for graveyard/protection cards (n=5)
  - Deduplicates by name before merging
Phase 4b: query_rules() fetches 3 Commander rules chunks to ground the prompt
  - Passed as rules_context kwarg to build_user_prompt()

* feat(rag): Phase 5 — add /api/rag/build-rules and /api/rag/query-rules endpoints
2026-03-29 02:11:38 -04:00
KoalaTrapLord 64526b7bee fix: exclude lands from cmc_buckets in _build_deck_report_from_db (matches deck_service behaviour) 2026-03-27 20:38:49 -04:00
KoalaTrapLord fa5349ea7c fix: basic land name fallback in _build_deck_report_from_db when type_line is missing from collection join 2026-03-27 20:32:15 -04:00
KoalaTrapLord 81fe5108d2 fix: coach_apply rollback on partial write, 4-space reindent; ws_game hoist inline imports 2026-03-27 20:24:55 -04:00
KoalaTrapLord bcf558418a fix(rag): squash all RAG/ChromaDB bugs #123–#130
Fixes critical API contract mismatch, batch_size wiring, lifespan init, failure tracking, build_meta persistence, mana_cost in embeddings, pyproject.toml deps, and adds full test suite.

Closes #123, #124, #125, #126, #127, #128, #129, #130
2026-03-27 13:24:41 -04:00
KoalaTrapLord 4b0cd34eda feat(rag): Phase 2 — ChromaDB vector index over bulk card DB (#120)
* feat(rag): Phase 2 — ChromaDB vector index over bulk card DB

* feat(rag): add routes/rag.py with build, search, status endpoints

* Update lab_api.pyfeat(rag): register rag_router in lab_api.py, remove old inline rag_status

* Update requirements.txtfeat(rag): add chromadb>=0.5.0 to requirements.txtfeat(rag): add chromadb to requirements.txt
2026-03-27 08:39:02 -04:00
KoalaTrapLord 83502ce726 feat: Phase 3/5 deep analysis + Quick Digest endpoint (issue #108) (#119)
* feat(coach): Phase 3/5 JSON schema + QUICK_DIGEST prompt (issue #108)

- Append removalCoverage, rampQuality, drawEngineProfile, winConditions,
  antiSynergyFlags, podPresence, tempoAssessment, metaMatchups to schema
- Add 8 new RULES for Phase 3/5 analysis
- Add QUICK_DIGEST_SYSTEM_PROMPT constant
- Add build_quick_system_prompt() function

* feat(coach): Phase 3/5 JSON parsing + run_quick_digest() (issue #108)

* feat(routes): add POST /api/coach/decks/{deck_id}/quick endpoint (issue #108)

* feat(coach): Phase 3/5 parsing + run_quick_digest() (issue #108)

* feat(coach): Phase 3/5 parsing + run_quick_digest() (issue #108)

- Import RemovalCoverage, RampQuality, DrawEngineProfile, WinCondition,
  AntiSynergyFlag, PodPresence, TempoAssessment, MetaMatchup
- Add Phase 3/5 blocks to _populate_session_from_json()
- Add run_quick_digest() method

* feat(ui): Top Priority card + Quick/Deep buttons in coach.html (issue #108)

* feat(coach): Phase 3/5 parsing + run_quick_digest() (issue #108)

* feat(ui): wire Quick Digest button + displayTopPriority() (issue #108)
2026-03-27 07:51:01 -04:00
KoalaTrapLord 6c03e56794 fix(#116): dual card_records fallback JOIN, card_type/card_category fields, simulator land detection
- deck_service._compute_deck_analysis: add LEFT JOIN card_records with COALESCE on all fields
- deck_service: add _classify_card_category() with oracle-text keyword matching
- deckbuilder.GET /api/decks/{id}/cards: add card_records fallback JOIN + COALESCE, emit card_type/card_category per card
- deepseek._fetch_deck_card_data: add card_records fallback JOIN so lands get correct type_line before enrich_card()

Closes #116
2026-03-27 04:10:17 -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 b46e1e0877 fix: remove stray string literal in init_coach_service (#105) 2026-03-26 12:31:52 -04:00
KoalaTrapLord e17b877dcc fix: repair syntax errors in coach_status and init_coach_service (#105) 2026-03-26 12:18:07 -04:00
KoalaTrapLord 6ce21908d3 Update coach.py 2026-03-26 11:53:39 -04:00
KoalaTrapLord 15495483f3 fix: remove _coach_init_error reference from status endpoint (#105) 2026-03-26 11:36:33 -04:00
KoalaTrapLord 540fbbf1b3 fix: remove broken _coach_init_error lines from coach.py (#105) 2026-03-26 10:52:41 -04:00
KoalaTrapLord 8c6248cf84 feat: increase max threads from 16 to 32 2026-03-25 21:37:21 -04:00
KoalaTrapLord 22d63fd15e Update coach.py 2026-03-25 21:29:24 -04:00
KoalaTrapLord 2dc0ba2d25 Update coach.py 2026-03-25 21:18:27 -04:00
KoalaTrapLord b283418879 fix: correct indentation in _call_anthropic_api else-block (#105) 2026-03-25 20:43:11 -04:00
KoalaTrapLord 3906b93c6a feat: add WebSocket support for Unity game state streaming (#107)
* feat: add ws/ package for WebSocket game state streaming

* feat: add WebSocket ConnectionManager with per-game broadcasting

* feat: add GameStateTracker with deep-diff delta engine

* feat: add WebSocket route with delta push and REST bridge endpoints

* feat: register ws_game_router in lab_api.py
2026-03-25 19:51:15 -04:00
KoalaTrapLord af2a71d1a5 fix: eliminate event-loop-blocking sync calls in async FastAPI routes (#106)
* feat: add async DB wrapper and eliminate event-loop blocking

* fix: rewrite llm_client.py with native async httpx - removes urlopen and run_in_executor

* fix: rewrite routes/coach.py - replace all sync DB/HTTP with async (aiosqlite + httpx)

* fix: add async DB shutdown hook to lab_api.py

* feat: add aiosqlite>=0.19.0 to requirements
2026-03-25 19:17:41 -04:00
KoalaTrapLord 9de0965c78 feat: replace Perplexity with Anthropic Claude in routes/deckgen.py (#105) 2026-03-25 15:09:46 -04:00
KoalaTrapLord 38bfa51d85 feat: replace Perplexity with Anthropic Claude in routes/coach.py (#105) 2026-03-25 14:41:34 -04:00
KoalaTrapLord 2e3c1ec0f1 fix: startup logs and DeckBuilderAdapter respect COACH_PROVIDER/DECK_GEN_PROVIDER instead of hardcoding Ollama 2026-03-25 10:34:04 -04:00
KoalaTrapLord e162d50608 feat: bulk quantity edit for collection table 2026-03-25 10:12:03 -04:00
KoalaTrapLord 34e88a3a14 fix: correct indentation of commander_keywords and oracle overlap scoring in deckgen.py 2026-03-25 08:45:51 -04:00
KoalaTrapLord 77c92d569d fix: correct indentation of elif/else branches in _run_sim_thread_deepseek 2026-03-25 08:24:21 -04:00
KoalaTrapLord f24bd05be8 feat: collection stats dashboard (CMC curve, color/type/rarity/value) 2026-03-25 07:51:09 -04:00
KoalaTrapLord 3ba44f68d9 feat: oracle-text keyword overlap scoring in _generate_deck 2026-03-24 22:56:30 -04:00
KoalaTrapLord 4c47b9d5e2 fix: add V3 result cache to avoid re-generating deck on export 2026-03-24 21:07:59 -04:00
KoalaTrapLord 8903432d67 fix: add missing export filter params (rarity, setCode, power, toughness, keyword, edhrec, qty) 2026-03-24 21:01:35 -04:00
KoalaTrapLord b1e473397f fix: shopping list field name + scan-add refresh function name 2026-03-24 20:53:19 -04:00
KoalaTrapLord 07b2c9d5a9 fix: add missing LLM_TIMEOUT constant in coach.py
LLM_TIMEOUT was referenced but never defined, causing a NameError
in the Ollama streaming SSE code path when COACH_PROVIDER != 'perplexity'.
2026-03-24 20:43:36 -04:00
KoalaTrapLord 58bbb514ad fix: skip ML decision flush when brain not connected (heuristic fallback guard) 2026-03-24 20:27:02 -04:00
KoalaTrapLord 475849e477 fix: prevent double-flush of ML decisions in deepseek sim thread
_run_sim_thread_deepseek was manually calling engine.flush_ml_decisions()
and writing the .jsonl, then passing engine=engine to _finish_sim which
called flush_ml_decisions() a second time — returning an empty list and
producing empty training data files.

Fix: pass engine=None to _finish_sim when the deepseek thread has already
flushed manually, so _finish_sim skips the second flush entirely.
2026-03-24 19:19:45 -04:00
KoalaTrapLord 456ef927f9 feat: add Perplexity deep research support to coach chat endpoint 2026-03-24 12:53:42 -04:00
KoalaTrapLord 977dbd96d7 feat: use PPLX_MODEL config instead of hardcoded 'sonar' in deckgen 2026-03-24 12:47:09 -04:00
KoalaTrapLord 29ad996966 rename: batch-sim- to ml-decision-sim- prefix in deepseek.py 2026-03-23 23:04:13 -04:00