From 1e05f0fb5e25c7d50e12e48c688448242ef7cb2f Mon Sep 17 00:00:00 2001 From: KoalaTrapLord Date: Thu, 12 Mar 2026 03:36:36 +0000 Subject: [PATCH] =?UTF-8?q?v27.3:=20Fix=20blank=20page=20=E2=80=94=20respo?= =?UTF-8?q?nse=20shape=20mismatches?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. /api/lab/status: StatusResponse now includes run_id, games_completed, total_games, current_decks aliases so the React SPA gets the field names it expects. 2. /api/lab/decks returns {decks: [...]}, /api/lab/history returns {results: [...]}, /api/lab/precons returns {precons: [...]} — but the frontend expected bare arrays. Updated lab.ts API client to unwrap these wrapper objects automatically. --- frontend/commander-ai-lab-ui/src/api/lab.ts | 9 ++++--- lab_api.py | 26 +++++++++++++++------ 2 files changed, 25 insertions(+), 10 deletions(-) diff --git a/frontend/commander-ai-lab-ui/src/api/lab.ts b/frontend/commander-ai-lab-ui/src/api/lab.ts index a243950..09bbdbe 100644 --- a/frontend/commander-ai-lab-ui/src/api/lab.ts +++ b/frontend/commander-ai-lab-ui/src/api/lab.ts @@ -2,7 +2,8 @@ import { get, post } from './client' import type { LabDeck, LabStatus, LabResult, LabHistoryEntry, PreconDeck } from '../types' export async function getLabDecks() { - return get('/api/lab/decks') + const res = await get<{ decks: LabDeck[] } | LabDeck[]>('/api/lab/decks') + return Array.isArray(res) ? res : (res as { decks: LabDeck[] }).decks || [] } export async function startBatchSim(config: { decks: string[]; games: number; threads?: number; use_deepseek?: boolean; deepseek_deck?: string }) { @@ -22,11 +23,13 @@ export async function getLabResult() { } export async function getLabHistory() { - return get('/api/lab/history') + const res = await get<{ results: LabHistoryEntry[] } | LabHistoryEntry[]>('/api/lab/history') + return Array.isArray(res) ? res : (res as { results: LabHistoryEntry[] }).results || [] } export async function getPrecons() { - return get('/api/lab/precons') + const res = await get<{ precons: PreconDeck[] } | PreconDeck[]>('/api/lab/precons') + return Array.isArray(res) ? res : (res as { precons: PreconDeck[] }).precons || [] } export async function installPrecon(name: string) { diff --git a/lab_api.py b/lab_api.py index bf14d99..79435c4 100644 --- a/lab_api.py +++ b/lab_api.py @@ -129,14 +129,19 @@ class StartResponse(BaseModel): message: str = "" class StatusResponse(BaseModel): - batchId: str - running: bool - completed: int - total: int - threads: int - elapsedMs: int + batchId: str = "" + running: bool = False + completed: int = 0 + total: int = 0 + threads: int = 0 + elapsedMs: int = 0 error: Optional[str] = None - simsPerSec: float = 0.0 # v24 (Issue #5): Real-time throughput + simsPerSec: float = 0.0 + # React SPA aliases (same data, expected field names) + run_id: str = "" + games_completed: int = 0 + total_games: int = 0 + current_decks: list = [] class DeckInfo(BaseModel): name: str @@ -431,8 +436,10 @@ async def get_status(batchId: Optional[str] = None): return StatusResponse( batchId="", running=False, completed=0, total=0, threads=0, elapsedMs=0, error=None, simsPerSec=0.0, + run_id="", games_completed=0, total_games=0, current_decks=[], ) elapsed = int((datetime.now() - state.start_time).total_seconds() * 1000) + deck_names = getattr(state, 'deck_names', []) if hasattr(state, 'deck_names') else [] return StatusResponse( batchId=state.batch_id, running=state.running, @@ -442,6 +449,11 @@ async def get_status(batchId: Optional[str] = None): elapsedMs=elapsed if state.running else state.elapsed_ms, error=state.error, simsPerSec=state.sims_per_sec, + # React SPA fields + run_id=state.batch_id, + games_completed=state.completed_games, + total_games=state.total_games, + current_decks=deck_names, )