geekwrestler's picture
Part 2: @app .api game endpoints + @gradio/client frontend (ZeroGPU auth-forwarded GPU calls)
8461a29 verified
Raw
History Blame Contribute Delete
2.15 kB
// 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 });
}