Cookbook
Complete, runnable recipes for building on AI Hub, from a first request to a coding agent.
Each recipe is a complete program you can copy and run. Every example reads its settings from three environment variables and has no default model:
# Copy to .env (never commit it) or export these in your shell. # AI Hub public API base URL. TOKENHUB_BASE_URL=https://tokenhub-api.belive.sg/api/v1 TOKENHUB_API_KEY=sk-tokenhub-… # An exact model id from GET $TOKENHUB_BASE_URL/models. Tool calling needs a model whose # supported_parameters include "tools". TOKENHUB_MODEL=
Download and configure
Install Python 3 or Node 22 and download the complete examples from the recipe archive. The archive has the files shown below, including the browser app and local stream checks. Choose an exact model ID from the public catalogue and export your inference key from a secret store.
curl --fail "https://aihub.belive.sg/docs/examples/tokenhub.zip" -o tokenhub-examples.zip unzip tokenhub-examples.zip cd tokenhub-examples cp .env.example .env # Edit .env: set TOKENHUB_API_KEY and TOKENHUB_MODEL. # Keep .env local. Scripts read process environment, not the .env file itself. set -a . ./.env set +a # Use the API base for the environment shown by this docs site. export TOKENHUB_BASE_URL="https://tokenhub-api.belive.sg/api/v1"
Recipes
- First requestOne completion with curl, Python and TypeScript.
- Terminal chatA streaming chat loop with history and strict stream handling.
- Browser chatA Next.js chat page that keeps the key on its server.
- Function toolsOne bounded tool-calling loop over an allowlisted function.
- Migrate an OpenAI clientPoint existing OpenAI-compatible code at AI Hub.
- Coding agentsWhich route each agent uses and how to configure it.
- AI Hub MCP serverGive an assistant live docs and catalogue tools.
- AI Hub SkillsA packaged skill that guides an agent through an integration.
Recover an accepted job
A timeout or 5xx can occur after work starts. Keep the returned job ID and the original supported idempotency key/body pair. The recipe below only reads an existing status resource; it never submits generation. Set TOKENHUB_JOB_PATH to that job's returned status path. Inspect terminal failures before considering new work.
export TOKENHUB_JOB_PATH="/api/v1/jobs/$TOKENHUB_JOB_ID" node --experimental-strip-types troubleshooting/poll-job.ts
// Read an existing job. This program never submits generation or retries a submission. // node --experimental-strip-types troubleshooting/poll-job.ts const base = process.env.TOKENHUB_BASE_URL; const apiKey = process.env.TOKENHUB_API_KEY; const jobPath = process.env.TOKENHUB_JOB_PATH; if (!base || !apiKey || !jobPath) throw new Error("Set TOKENHUB_BASE_URL, TOKENHUB_API_KEY and TOKENHUB_JOB_PATH to an existing job path"); const root = new URL(base.endsWith("/") ? base : base + "/"); const url = new URL(jobPath, root); // Never forward the key to a different origin, signed storage URL, or arbitrary resource. if (url.origin !== root.origin || !/^\/(?:api\/)?v1\/(?:chat\/jobs|jobs|videos|audio\/(?:generations|transcriptions|speech\/jobs))\/[^/]+$/.test(url.pathname)) { throw new Error("Use an existing AI Hub job status path on the configured API host"); } for (let attempt = 0; attempt < 30; attempt++) { const response = await fetch(url, { method: "GET", headers: { Authorization: `Bearer ${apiKey}` }, signal: AbortSignal.timeout(30_000), redirect: "error" }); if (!response.ok && ![429, 500, 502, 503].includes(response.status)) throw new Error(`Job lookup refused (${response.status}); check the job id and key`); if (response.ok) { const job = await response.json(); if (["failed", "cancelled", "canceled"].includes(job.status)) throw new Error(`Job ${job.id ?? ""} ended with ${job.status}; inspect its error before any new submission`); if (["completed", "succeeded"].includes(job.status)) { console.log(JSON.stringify(job, null, 2)); break; } } if (attempt === 29) throw new Error("Polling limit reached; keep the existing job id and resume its lookup later"); const hint = response.headers.get("Retry-After"); const seconds = hint && /^\d+$/.test(hint) ? Number(hint) : 2 ** Math.min(attempt, 4); await new Promise((resolve) => setTimeout(resolve, Math.min(Math.max(seconds, 1), 30) * 1000)); } export {};
Use the endpoint index for the operation's polling route, protocol, schema and availability boundary, and Errors for safe recovery. Use the client-specific guides for known framework and desktop boundaries.
For agents
Every page here is also plain Markdown: add /markdown after /docs, as in /docs/markdown/first-request. The index of all pages is /llms.txt.