Imported from minghanminghan/jev-demo (
AGENTS.md). Install upstream withnpx skills add minghanminghan/jev-demo. Copyright stays with the author.
jev demo
Next.js 16 (App Router, Turbopack) + React 19. One page, no router.
Next is here for one reason: app/api/jev and app/api/llm have to run
somewhere other than a browser. Everything else still runs in the tab.
app/layout.tsx→app/page.tsx→components/Studio.tsx. One page.app/page.tsxloads Studio withnext/dynamicandssr: false. That is load bearing, not an optimisation: every component measures the DOM or readslocalStorage, andlib/route-engine.tsruns in the browser."use client"appears onapp/page.tsxonly; the whole graph below it inherits it.@/resolves to the repo root (tsconfig.json).components/,lib/anddata/stay at the root rather than moving underapp/.lib/route-engine.tsruns in the browser. Keepnode:imports out oflib/andcomponents/.app/api/,next.config.tsandscripts/are the only places server or Node code belongs.data/*.jsonis imported, not read off disk. Nothing writes a file.- The jev API allows no browser origin, so the SDK is pointed at
/api/jevandapp/api/jev/[...path]/route.tspasses it through toapi.typesafe.ai. The comparison LLM goes through/api/llm, which reads its real endpoint off anx-llm-urlheader — the provider is whatever the user typed, so the target cannot be fixed at config time./api/llmforwards only to hosts on the allowlist inapp/api/llm/allowlist.ts, and never to link-local addresses. If you change either path, change it in bothlib/types.ts(JEV_PROXY_PATH,LLM_PROXY_PATH,LLM_TARGET_HEADER) and the folder name underapp/api/. - The app is served under
/jev-demo, not at a host root.lib/base-path.tsholds the prefix;next.config.tsfeeds it tobasePathandlib/types.tsputs it on the front of both proxy paths, becausebasePathdoes not touch a hand-writtenfetch. The personal site rewrites/jev-demo/*through to this project, so a URL without the prefix lands on the site instead of here. - Both routes are relays: they attach whatever key the caller sends to a request the server makes. A deployed copy needs Vercel Deployment Protection or equivalent in front of it. See the README.
lib/route-engine.tsmust not learn which engine answered it. It takes anOracle(lib/oracle.ts);jevOracleandllmOracleare the two. That is what makes the side-by-side comparison worth anything, so keep engine-specific behaviour behind that interface rather than branching in the walk.- The AI SDK builds its request URL as
new URL(baseURL + path)with no base to resolve against, so any baseURL handed to a provider has to stay absolute. - Fonts come from
next/fontinapp/layout.tsx, which sets--next-interand--next-geist-monoon<html>.app/globals.cssre-exports both as--font-inter/--font-geist-monoand adds the fallback chain. data/providers.jsonis generated —node scripts/snapshot-providers.mjs, which reads models.dev. Do not hand-edit it.- Only models reporting
structured_outputare snapshotted. That is load bearing: a model that cannot be held to a schema cannot answer the question, and offering one makes the comparison lie.
npm run build is next build, which type-checks as it goes. Run
npm run lint too.
next dev wants to append a generated block to this file. agentRules: false
in next.config.ts turns that off, so everything here is hand-written. If a
<!-- BEGIN:nextjs-agent-rules --> section reappears, that setting was lost.