@chai-ui/core

中文文档
1.4.1发布于 2026/10/31MITTypeScriptES Module零依赖构建来源已验证近 30 天下载 4423

Chai UI headless core — the Request/Run/Result data model and pluggable generation engines (Fal.ai, OpenRouter, Vercel AI Gateway, mock). No framework, no rendering.

原始使用文档
文档目录5 节
  1. Layout
  2. Suggested models, and checking them
  3. The server route: @chai-ui/core/server
  4. Writing an engine
  5. Without a server route

Chai UI

npm version MIT license CI Coverage Docs

@chai-ui/core

The headless core behind Chai UI's components: the data types, the Request → Run → Result model a submitted prompt flows through, and the pluggable generation engines that actually call models, and the components' behavior as small framework-free stores. No framework, no rendering: @chai-ui/react wraps the stores in hooks, and @chai-ui/vue and @chai-ui/svelte wrap the same stores.

Layout

  • types.ts — shared types: MediaKind, DroppedMedia, ModelOption, Usage, GenerationUseCase.
  • run.ts — Request/Run/Result plus pure transitions over them (createRun, startResult, resolveResult, failResult, deriveGenerationUseCase).
  • engine.ts — the GenerationEngine interface every provider implements, GenerationError, and mockEngine/createMockEngine (placeholder output, no network call, no API key).
  • engines/fal.ts — createFalEngine, media generation through Fal.ai (the default).
  • engines/openrouter.ts — createOpenRouterEngine, text/reasoning through OpenRouter (prompt enhancement, media analysis).
  • engines/vercel.ts — createVercelEngine, text and image models through Vercel AI Gateway.
  • media-analysis-prompts.ts — the instruction text MediaAnalyzer sends, per media kind × prompt length.
  • suggested-models.ts — the suggested model lists per use case (Fal for generation, OpenRouter for analysis), each model with its verified aspect ratios and Fal input format; suggestedModels(useCase).
  • check-suggested-models.ts — checkSuggestedModels(): checks those lists against Fal's and OpenRouter's catalogs as they are today. No key, no model run.
  • model-routing.ts — auto-select: buildModelRoutingPrompt, parseModelRoutingReply, and routeModel, which falls back to the first listed model instead of throwing.
  • reasoning.ts — the reasoning engine and models a ChaiProvider shares: createReasoning, pickReasoning, reasoningModelFor and the suggested defaults.
  • composer.ts — createComposerStore, what useComposer wraps: submit payload → runs, auto-select routing, Enhance (buildEnhancePrompt, its default instruction), cancel, image edit versions and the evaluation judge. Plus the Composer's shared types (ComposerSubmitPayload, EditVersion, …).
  • composer-view.ts — what the Composer shows and submits, worked out from its props, for every binding: model menus, aspect ratios, use-case rules (nextUseCases).
  • analytics-events.ts — front-end analytics: the ChaiEvent type and the composerEvents, resultCardEvents, sidePanelEvents, mediaAnalyzerEvents and editCardEvents the components report through onEvent.
  • evaluation-judge.ts — the opt-in judge: EvaluationCheck, DEFAULT_EVALUATION_CHECKS, buildJudgePrompt, parseJudgeReply.
  • image-fit.ts — fitImageToRatio: crops or pads a video's start image to the picked ratio (startImageFit).
  • region-edit.ts — region editing: the region shape, the models that read regions as boxes, and buildRegionEditPrompt.
  • edit-card.ts — EditCard's geometry: region boxes, zoom and pan, and where the instruction field goes.
  • side-panel.ts — SidePanel's behavior: readPageContext (the page's title, URL, selection and main text), buildSidePanelPrompt (the question with the page and recent turns), and the thread helpers.
  • voice-input.ts — the Composer's voice input: record, send to a speech-to-text model, return the transcript.
  • result-format.ts — how ResultCard shows a result's details.
  • media-actions.ts — download and share for generated media.
  • menu-placement.ts — which way a component's menus open from their trigger.
  • media-analyzer.ts — createMediaAnalyzerStore, what useMediaAnalyzer wraps.
  • server/ — createChaiHandler (chai-handler.ts) and toNodeHandler (node.ts), exported from @chai-ui/core/server (server-only).

Suggested models, and checking them

The components fill their Model menus from the suggested lists when a builder passes none (see @chai-ui/react's README). Each Fal model in them carries falInput, read from its Fal schema: where the image goes (image_url, image_urls or start_image_url) and how the aspect ratio is sent (aspect_ratio, or a named image_size). buildFalRequestBody uses it; for a model outside the lists it falls back to guessing from the id.

The lists change only through package releases. checkSuggestedModels() compares them with the providers right now, from a browser or Node, with no key:

const { ok, checked, problems } = await checkSuggestedModels();

It reports a model that's gone or no longer active, a Fal model that no longer takes an aspect ratio or image field Chai sends or now requires a field Chai doesn't send, and an OpenRouter model that no longer accepts its media kind. Fal's catalog is read 10 models a page (api.fal.ai/v1/models), OpenRouter's from openrouter.ai/api/v1/models.

The server route: @chai-ui/core/server

The built-in engines never hold an API key. They call one route on your server, /api/chai/*, and createChaiHandler serves it: it adds the key and forwards the request to Fal.ai, OpenRouter or Vercel AI Gateway, streaming replies through.

// Next.js: app/api/chai/[...path]/route.ts
import { createChaiHandler } from "@chai-ui/core/server";

export const { GET, POST, PUT } = createChaiHandler();

Set FAL_KEY, OPENROUTER_API_KEY or AI_GATEWAY_API_KEY in the server's environment (never with a VITE_ or NEXT_PUBLIC_ prefix). On Vercel, VERCEL_OIDC_TOKEN is set for you and works in place of the gateway key. A missing key comes back as a clear error, e.g. "FAL_KEY isn't set on the server."

Where it runs. The route lives in your app's framework (React, Vue or Svelte) or on the API server behind it. Mount it at /api/chai, where the components send their requests. handle(request) takes a standard web Request and returns a Response; GET, POST and PUT (Fal's cancel call) are the same function. When the browser disconnects, the provider call is aborted too.

Your setupAdd this
Next.js (App Router)export const { GET, POST, PUT } = createChaiHandler(); in app/api/chai/[...path]/route.ts
Remix / React Routerexport const loader = ({ request }) => chai.handle(request); export const action = loader; in app/routes/api.chai.$.ts
Nuxtexport default defineEventHandler((event) => chai.handle(toWebRequest(event))); in server/api/chai/[...path].ts
SvelteKitexport const fallback = ({ request }) => chai.handle(request); in src/routes/api/chai/[...path]/+server.ts, with the keys passed in (see the @chai-ui/svelte README)
Honoapp.all("/api/chai/*", (c) => chai.handle(c.req.raw))
Bun, Denoroute /api/chai/* to chai.handle in Bun.serve / Deno.serve
Cloudflare WorkerscreateChaiHandler({ falKey: env.FAL_KEY, openRouterKey: env.OPENROUTER_API_KEY, aiGatewayKey: env.AI_GATEWAY_API_KEY }).handle(request)
Express, plain Nodeapp.use("/api/chai", toNodeHandler(createChaiHandler()))

Before going live, limit who can use the route and what it runs. Without these, anyone who reaches your site can spend your API credit:

createChaiHandler({
  authorize: async (request) => Boolean(await getSession(request)), // false → 403
  allowedModels: ["fal-ai/flux/schnell", "anthropic/claude-opus-5"], // anything else → 403
});

Never import @chai-ui/core/server from browser code.

Writing an engine

One method: a prompt plus any attached media in, one piece of output back.

import type { GenerationEngine } from "@chai-ui/core";

const myEngine: GenerationEngine = {
  async generate({ modelId, prompt, attachments }) {
    // call your provider…
    return { src: "https://…/output.png", kind: "image" };
  },
};

Throw a GenerationError(message, reasons?) to fail a call with user-facing content rather than a bare network error. API keys never belong in an engine: call a server route that adds the key, like createChaiHandler above.

Without a server route

Desktop apps (Tauri, Electron) have no server route for the built-in engines to call, and their CSP usually limits connect-src to the app itself. Chai doesn't ship a desktop engine: write your own that hands each run to your app's backend (a Tauri command, or a local daemon), which holds the key and calls the model. In Tauri:

// src/lib/desktop-engine.ts: one run per call, through your app's backend.
import { invoke } from "@tauri-apps/api/core";
import type { GenerationEngine, MediaKind } from "@chai-ui/core";

export const desktopEngine: GenerationEngine = {
  // signal and onText can't cross to the backend; every other field goes.
  async generate({ signal, onText, ...run }) {
    const result = await invoke<{ src: string; kind: MediaKind }>("chai_generate", run);
    signal?.throwIfAborted();
    return result;
  },
};

Pass it as useComposer's engine, and give the same engine to ChaiProvider as reasoningEngine: Enhance, auto-select, text requests, media analysis and the judge otherwise still call the server route. Attachments arrive as data URLs, so the backend gets the whole file. The key never reaches the webview, and nothing needs connect-src beyond the app. For a local daemon, swap invoke for a fetch to its localhost address and add that address to connect-src.

Voice input works the same way: voiceInput={{ model }} calls the server route, so pass your own Transcribe instead, which gets the recording as a data URL and returns the text:

import type { Transcribe } from "@chai-ui/core";

const transcribe: Transcribe = (clip) => invoke<string>("chai_transcribe", { clip });
// voiceInput={{ transcribe }}

Stop. Results turn cancelled at once, and throwIfAborted() keeps a late reply from replacing them, but the backend run keeps going, and billing. To end it too, give the backend a cancel command and call it from the signal's abort event.

版本与发布记录

@chai-ui/core 共发布 32 个版本,首次发布于 2026/10/02;过去 12 个月发布了 32 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
1.4.1latest2026/10/319 天前
1.4.02026/10/319 天前
1.3.02026/10/319 天前
1.2.02026/10/3010 天前
1.1.22026/10/2812 天前
1.1.12026/10/2713 天前

关于 @chai-ui/core 的常见问题

@chai-ui/core 是什么?

Chai UI headless core — the Request/Run/Result data model and pluggable generation engines (Fal.ai, OpenRouter, Vercel AI Gateway, mock). No framework, no rendering.

如何安装 @chai-ui/core?

在项目目录运行 npm install @chai-ui/core,或使用 pnpm add / yarn add / bun add。

@chai-ui/core 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@chai-ui/core 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@chai-ui/core 可以商用吗?

@chai-ui/core 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@chai-ui/core 的下载量有多少?

根据 npm 官方下载统计,@chai-ui/core 在 2026/10/10 – 2026/11/08 这 30 天内被下载 4,423 次,日均约 147 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@chai-ui/core 还在维护吗?

最新版本 1.4.1 发布于 2026/10/31(9 天前);至今共发布 32 个版本,其中过去 12 个月发布了 32 个;首次发布于 2026/10/02。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。