// api.js — the ONLY network layer. Talks to the backend's GRADIO API endpoints via @gradio/client // (NOT plain fetch): ZeroGPU only grants the GPU to calls carrying the HF iframe auth headers, and // @gradio/client is what forwards them. Endpoints: /turn (game loop incl. the GPU dispatcher), // /game_model (toggle/switch), /announce (VoxCPM voice). One session id per tab, passed as `sid`. import { Client } from '@gradio/client'; export function sessionId() { let sid = sessionStorage.getItem('mlp_session_id'); if (!sid) { sid = `web-${Math.random().toString(36).slice(2, 10)}-${Date.now().toString(36)}`; sessionStorage.setItem('mlp_session_id', sid); } return sid; } export class ApiError extends Error { constructor(status, message) { super(message); this.status = status; } } // Connect once (lazily). On the Space this runs inside the HF iframe, so @gradio/client forwards the // auth ZeroGPU needs; locally it just hits the dev gr.Server. let _clientPromise = null; function client() { if (!_clientPromise) { _clientPromise = Client.connect(window.location.origin).catch((e) => { _clientPromise = null; // allow retry on next call throw new ApiError(0, e?.message || 'could not connect to the game server'); }); } return _clientPromise; } async function call(endpoint, args) { const app = await client(); const res = await app.predict(endpoint, args); return res?.data?.[0]; } /** new_game / play_card / next_turn -> RenderState. Throws ApiError on {error} (rejected -> 409). */ export async function turn(body) { const d = await call('/turn', { payload: body, sid: sessionId() }); if (d && typeof d === 'object' && d.error && !('round' in d)) { throw new ApiError(d.rejected ? 409 : 400, d.error); } return d; } /** {on?, model?} -> {on, model, models, tts}. */ export async function gameModel(body) { return call('/game_model', { payload: body || {}, sid: sessionId() }); } /** Voice the last turn's announcement -> {audio: base64|null}. */ export async function announce(round) { return call('/announce', { sid: sessionId(), round }); }