Skip to Content
GuidesThe model server

The model server

The model key must never reach the browser. The browser sends its request to your server; your server checks it, asks the model, and always answers.

The route

app.post("/api/adapt", async (c) => { // 1. Only the app on this machine (or through your CDN) may spend the key. const refused = checkRequest( { host: c.req.header("host"), origin: c.req.header("origin"), contentType: c.req.header("content-type") }, { requireJson: true }, ); if (refused) return c.json({ error: refused.error }, refused.status); if (!allow()) return c.json({ error: "Too many requests." }, 429); // 2. Check and clip the body. const parsed = parseAdaptRequest(await c.req.json()); if (!parsed.ok) return c.json({ error: parsed.error }, 400); const { version, snapshot, command } = parsed.request; // 3. One round, with a budget and a fallback. const { state, questions } = buildRound(CATALOG, { app: APP_DESCRIPTION, snapshot, command: command ?? null }); const round = await askJev({ client, state, questions, read: (answers) => readRound(answers, questions, CATALOG), fallback: { name: "fallback", answer: () => neutralJudgments(CATALOG, command ? { command } : {}) }, budgetMs: 4_500, signal: c.req.raw.signal, logTag: `[adapt] v${version}`, }); return c.json({ version, ...round }); });

Also add a GET /api/health that says whether a key is set ({ ok: true, jev: client !== null }). The store can watch it (watchHealth) and show “offline” in the header.

The app description

APP_DESCRIPTION is one or two sentences the model reads every round, for example “A help desk for a small software company. Its panels show support tickets, customers, help articles, and macros (saved replies).”

The budget and the fallback

budgetMs is the total time for the round, the retry included. Keep it below the browser’s patience: 4.5 s in the playground. When the model is slow, fails, sends an answer that does not fit the questions, or there is no key, askJev answers with the fallback and logs one line. neutralJudgments(catalog) is a calm fallback: every panel supporting, no goal, no next step, so the layout stays as it is. With a command it also answers the command’s panel when the command names one.

The response’s source says who answered: "jev" or your fallback’s name. The store shows “offline” for a no-key fallback and “error” only for a real failure.

Deployed

Behind a CDN, pass edge to checkRequest: { secret, origin }. The CDN adds the secret in the x-origin-verify header (EDGE_SECRET_HEADER), and the server refuses any request without it. The studio demo deploys this way on AWS Lambda behind CloudFront.

The key

Read the key from the environment, never from the request:

const apiKey = process.env.JEV_API_KEY; const client = apiKey ? createRealtimeJevClient({ apiKey, model: "jev-latest" }) : null;

Keep it in a .env file that git ignores.

Last updated on