@chai-ui/core 0.9.0 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -63,8 +63,8 @@ OpenRouter's from `openrouter.ai/api/v1/models`.
63
63
 
64
64
  The built-in engines never hold an API key. They call one route on your
65
65
  server, `/api/chai/*`, and `createChaiHandler` serves it: it adds the key
66
- and forwards the request to Fal.ai or OpenRouter, streaming replies
67
- through.
66
+ and forwards the request to Fal.ai, OpenRouter or Vercel AI Gateway,
67
+ streaming replies through.
68
68
 
69
69
  ```ts
70
70
  // Next.js: app/api/chai/[...path]/route.ts
@@ -73,8 +73,9 @@ import { createChaiHandler } from "@chai-ui/core/server";
73
73
  export const { GET, POST, PUT } = createChaiHandler();
74
74
  ```
75
75
 
76
- Set `FAL_KEY` and `OPENROUTER_API_KEY` in the server's environment (never
77
- with a `VITE_` or `NEXT_PUBLIC_` prefix). A missing key comes back as a
76
+ Set `FAL_KEY`, `OPENROUTER_API_KEY` or `AI_GATEWAY_API_KEY` in the server's
77
+ environment (never with a `VITE_` or `NEXT_PUBLIC_` prefix). On Vercel,
78
+ `VERCEL_OIDC_TOKEN` is set for you and works in place of the gateway key. A missing key comes back as a
78
79
  clear error, e.g. "FAL_KEY isn't set on the server."
79
80
 
80
81
  **Where it runs.** The route lives in your app's framework (React or Vue)
@@ -91,7 +92,7 @@ disconnects, the provider call is aborted too.
91
92
  | Nuxt | `export default defineEventHandler((event) => chai.handle(toWebRequest(event)));` in `server/api/chai/[...path].ts` |
92
93
  | Hono | `app.all("/api/chai/*", (c) => chai.handle(c.req.raw))` |
93
94
  | Bun, Deno | route `/api/chai/*` to `chai.handle` in `Bun.serve` / `Deno.serve` |
94
- | Cloudflare Workers | `createChaiHandler({ falKey: env.FAL_KEY, openRouterKey: env.OPENROUTER_API_KEY }).handle(request)` |
95
+ | Cloudflare Workers | `createChaiHandler({ falKey: env.FAL_KEY, openRouterKey: env.OPENROUTER_API_KEY, aiGatewayKey: env.AI_GATEWAY_API_KEY }).handle(request)` |
95
96
  | Express, plain Node | `app.use("/api/chai", toNodeHandler(createChaiHandler()))` |
96
97
 
97
98
  **Before going live**, limit who can use the route and what it runs.
package/dist/engine.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * A pluggable generation engine — the one interface every model provider
3
- * (`createFalEngine`, `createOpenRouterEngine`, `mockEngine`, or your own)
3
+ * (`createFalEngine`, `createOpenRouterEngine`, `createVercelEngine`, `mockEngine`, or your own)
4
4
  * implements, and the one `useComposer` calls. One call = one model run:
5
5
  * a prompt plus any attached media in, one piece of output back.
6
6
  *
package/dist/engine.js CHANGED
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * A pluggable generation engine — the one interface every model provider
3
- * (`createFalEngine`, `createOpenRouterEngine`, `mockEngine`, or your own)
3
+ * (`createFalEngine`, `createOpenRouterEngine`, `createVercelEngine`, `mockEngine`, or your own)
4
4
  * implements, and the one `useComposer` calls. One call = one model run:
5
5
  * a prompt plus any attached media in, one piece of output back.
6
6
  *
@@ -13,7 +13,7 @@
13
13
  * browser bundle; don't add one.
14
14
  */
15
15
  import { type GenerationEngine } from "../engine.js";
16
- import type { DroppedMedia, Evaluation, MediaKind } from "../types.js";
16
+ import type { DroppedMedia, Evaluation, MediaKind, Usage } from "../types.js";
17
17
  export interface OpenRouterEngineOptions {
18
18
  /** Same-origin proxy path that attaches the real key server-side. */
19
19
  endpoint?: string;
@@ -66,7 +66,7 @@ type ChatContentPart = {
66
66
  format: string;
67
67
  };
68
68
  };
69
- interface ChatCompletionResponse {
69
+ export interface ChatCompletionResponse {
70
70
  choices?: {
71
71
  message?: {
72
72
  content?: string;
@@ -84,6 +84,13 @@ interface ChatCompletionResponse {
84
84
  };
85
85
  };
86
86
  }
87
+ /** Converts OpenRouter's raw `usage` into `Usage`, computing `costUsd` only when a real rate was supplied. */
88
+ export declare function toUsage(usage: ChatCompletionResponse["usage"], pricePerMillionTokens?: {
89
+ input: number;
90
+ output: number;
91
+ }): Usage | undefined;
92
+ /** Prefers the upstream provider's own error message over OpenRouter's generic wrapper, when both are present. */
93
+ export declare function describeError(json: ChatCompletionResponse, fallback: string): string;
87
94
  /** Builds the one non-text content part for an attached image/video/audio file. */
88
95
  export declare function contentPartForMedia(media: DroppedMedia): ChatContentPart;
89
96
  /** Builds the OpenRouter chat-completions request body for a prompt plus any attached media. */
@@ -1 +1 @@
1
- {"version":3,"file":"openrouter.d.ts","sourceRoot":"","sources":["../../src/engines/openrouter.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"openrouter.d.ts","sourceRoot":"","sources":["../../src/engines/openrouter.ts"],"names":[],"mappings":"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"}
@@ -16,7 +16,7 @@ import { readEvaluations } from "../engine.js";
16
16
  const DEFAULT_MODEL = "google/gemini-2.5-flash";
17
17
  const DEFAULT_MAX_TOKENS = 800;
18
18
  /** Converts OpenRouter's raw `usage` into `Usage`, computing `costUsd` only when a real rate was supplied. */
19
- function toUsage(usage, pricePerMillionTokens) {
19
+ export function toUsage(usage, pricePerMillionTokens) {
20
20
  if (!usage)
21
21
  return undefined;
22
22
  const promptTokens = usage.prompt_tokens ?? 0;
@@ -34,7 +34,7 @@ function toUsage(usage, pricePerMillionTokens) {
34
34
  };
35
35
  }
36
36
  /** Prefers the upstream provider's own error message over OpenRouter's generic wrapper, when both are present. */
37
- function describeError(json, fallback) {
37
+ export function describeError(json, fallback) {
38
38
  const base = json.error?.message ?? fallback;
39
39
  const raw = json.error?.metadata?.raw;
40
40
  if (!raw)
@@ -1 +1 @@
1
- {"version":3,"file":"openrouter.js","sourceRoot":"","sources":["../../src/engines/openrouter.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"openrouter.js","sourceRoot":"","sources":["../../src/engines/openrouter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,eAAe,EAAyB,MAAM,cAAc,CAAC;AAiCtE,MAAM,aAAa,GAAG,yBAAyB,CAAC;AAChD,MAAM,kBAAkB,GAAG,GAAG,CAAC;AAqB/B,8GAA8G;AAC9G,MAAM,UAAU,OAAO,CACrB,KAAsC,EACtC,qBAAyD;IAEzD,IAAI,CAAC,KAAK;QAAE,OAAO,SAAS,CAAC;IAC7B,MAAM,YAAY,GAAG,KAAK,CAAC,aAAa,IAAI,CAAC,CAAC;IAC9C,MAAM,gBAAgB,GAAG,KAAK,CAAC,iBAAiB,IAAI,CAAC,CAAC;IACtD,MAAM,OAAO,GAAG,qBAAqB;QACnC,CAAC,CAAC,CAAC,YAAY,GAAG,qBAAqB,CAAC,KAAK;YACzC,gBAAgB,GAAG,qBAAqB,CAAC,MAAM,CAAC;YAClD,SAAS;QACX,CAAC,CAAC,SAAS,CAAC;IACd,OAAO;QACL,YAAY;QACZ,gBAAgB;QAChB,WAAW,EAAE,KAAK,CAAC,YAAY,IAAI,YAAY,GAAG,gBAAgB;QAClE,OAAO;KACR,CAAC;AACJ,CAAC;AAED,kHAAkH;AAClH,MAAM,UAAU,aAAa,CAAC,IAA4B,EAAE,QAAgB;IAC1E,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,EAAE,OAAO,IAAI,QAAQ,CAAC;IAC7C,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,EAAE,QAAQ,EAAE,GAAG,CAAC;IACtC,IAAI,CAAC,GAAG;QAAE,OAAO,IAAI,CAAC;IACtB,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAqC,CAAC;QACnE,IAAI,MAAM,CAAC,KAAK,EAAE,OAAO,IAAI,MAAM,CAAC,KAAK,CAAC,OAAO,KAAK,IAAI,EAAE,CAAC;YAC3D,OAAO,GAAG,IAAI,MAAM,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC;QAC7C,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,gDAAgD;IAClD,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,mFAAmF;AACnF,MAAM,UAAU,mBAAmB,CAAC,KAAmB;IACrD,IAAI,KAAK,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;QAC3B,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,SAAS,EAAE,EAAE,GAAG,EAAE,KAAK,CAAC,GAAG,EAAE,EAAE,CAAC;IAC9D,CAAC;IACD,IAAI,KAAK,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;QAC3B,qEAAqE;QACrE,uEAAuE;QACvE,MAAM,KAAK,GAAG,iCAAiC,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAChE,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,MAAM,IAAI,KAAK,CACb,0FAA0F,CAC3F,CAAC;QACJ,CAAC;QACD,OAAO,EAAE,IAAI,EAAE,aAAa,EAAE,WAAW,EAAE,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC,CAAE,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC,CAAE,EAAE,EAAE,CAAC;IACtF,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,SAAS,EAAE,EAAE,GAAG,EAAE,KAAK,CAAC,GAAG,EAAE,EAAE,CAAC;AAC9D,CAAC;AAED,gGAAgG;AAChG,MAAM,UAAU,gBAAgB,CAAC,IAKhC;IAKC,MAAM,OAAO,GAAsB,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;IACzE,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,KAAK,IAAI,EAAE;QAAE,OAAO,CAAC,IAAI,CAAC,mBAAmB,CAAC,KAAK,CAAC,CAAC,CAAC;IAC/E,OAAO;QACL,KAAK,EAAE,IAAI,CAAC,KAAK;QACjB,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC;QACrC,UAAU,EAAE,IAAI,CAAC,SAAS,IAAI,kBAAkB;KACjD,CAAC;AACJ,CAAC;AAQD;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,GAAa,EACb,MAAmC;IAEnC,IAAI,IAAI,GAAG,EAAE,CAAC;IACd,IAAI,KAAsC,CAAC;IAC3C,IAAI,WAAqC,CAAC;IAC1C,IAAI,MAAM,GAAG,EAAE,CAAC;IAEhB,MAAM,UAAU,GAAG,CAAC,IAAY,EAAE,EAAE;QAClC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC;YAAE,OAAO,CAAC,+CAA+C;QACtF,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QAClC,IAAI,CAAC,IAAI,IAAI,IAAI,KAAK,QAAQ;YAAE,OAAO;QACvC,IAAI,KAA0B,CAAC;QAC/B,IAAI,CAAC;YACH,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAwB,CAAC;QAClD,CAAC;QAAC,MAAM,CAAC;YACP,OAAO;QACT,CAAC;QACD,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC;YAChB,MAAM,IAAI,KAAK,CAAC,qBAAqB,aAAa,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,EAAE,cAAc,CAAC,EAAE,CAAC,CAAC;QAChG,CAAC;QACD,IAAI,KAAK,CAAC,KAAK;YAAE,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC;QACrC,4EAA4E;QAC5E,WAAW,GAAG,eAAe,CAAC,KAAK,CAAC,IAAI,WAAW,CAAC;QACpD,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC;QACjD,IAAI,KAAK,EAAE,CAAC;YACV,IAAI,IAAI,KAAK,CAAC;YACd,MAAM,CAAC,IAAI,CAAC,CAAC;QACf,CAAC;IACH,CAAC,CAAC;IAEF,MAAM,KAAK,GAAG,CAAC,KAAc,EAAE,EAAE;QAC/B,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QACjC,MAAM,GAAG,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,EAAG,CAAC;QACnC,KAAK,MAAM,IAAI,IAAI,KAAK;YAAE,UAAU,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC;IACvD,CAAC,CAAC;IAEF,IAAI,GAAG,CAAC,IAAI,EAAE,CAAC;QACb,MAAM,MAAM,GAAG,GAAG,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;QACpC,MAAM,OAAO,GAAG,IAAI,WAAW,EAAE,CAAC;QAClC,SAAS,CAAC;YACR,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,MAAM,MAAM,CAAC,IAAI,EAAE,CAAC;YAC5C,IAAI,IAAI;gBAAE,MAAM;YAChB,MAAM,IAAI,OAAO,CAAC,MAAM,CAAC,KAAK,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;YAClD,KAAK,CAAC,KAAK,CAAC,CAAC;QACf,CAAC;QACD,MAAM,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;IAC7B,CAAC;SAAM,CAAC;QACN,MAAM,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;IAC5B,CAAC;IACD,KAAK,CAAC,IAAI,CAAC,CAAC;IACZ,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,WAAW,EAAE,CAAC;AACtC,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,sBAAsB,CACpC,UAAmC,EAAE;IAErC,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,2BAA2B,CAAC;IACjE,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,KAAK,CAAC;IAE7C,OAAO;QACL,KAAK,CAAC,QAAQ,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE;YAC7D,MAAM,SAAS,GAAG,WAAW,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC;YACvC,MAAM,KAAK,GACT,OAAO,IAAI,CAAC,SAAS,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC,SAAS,CAAC,CAAC,IAAI,OAAO,CAAC,KAAK,IAAI,aAAa,CAAC;YAC/F,MAAM,IAAI,GAAG,gBAAgB,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,OAAO,CAAC,SAAS,EAAE,CAAC,CAAC;YAEnG,qEAAqE;YACrE,yDAAyD;YACzD,uDAAuD;YACvD,MAAM,GAAG,GAAG,MAAM,SAAS,CAAC,QAAQ,EAAE;gBACpC,MAAM,EAAE,MAAM;gBACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;gBAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,GAAG,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;gBAC/D,MAAM;aACP,CAAC,CAAC;YAEH,sEAAsE;YACtE,gEAAgE;YAChE,IAAI,MAAM,IAAI,GAAG,CAAC,EAAE,EAAE,CAAC;gBACrB,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,WAAW,EAAE,GAAG,MAAM,cAAc,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;gBACvE,IAAI,CAAC,IAAI;oBAAE,MAAM,IAAI,KAAK,CAAC,kDAAkD,CAAC,CAAC;gBAC/E,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,OAAO,CAAC,qBAAqB,CAAC,EAAE,WAAW,EAAE,CAAC;YACxG,CAAC;YAED,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAA2B,CAAC;YAC5E,IAAI,CAAC,GAAG,CAAC,EAAE,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;gBAC1B,MAAM,IAAI,KAAK,CACb,qBAAqB,aAAa,CAAC,IAAI,EAAE,GAAG,GAAG,CAAC,MAAM,IAAI,GAAG,CAAC,UAAU,EAAE,CAAC,EAAE,CAC9E,CAAC;YACJ,CAAC;YACD,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC;YACjD,IAAI,CAAC,IAAI;gBAAE,MAAM,IAAI,KAAK,CAAC,kDAAkD,CAAC,CAAC;YAC/E,OAAO;gBACL,GAAG,EAAE,IAAI;gBACT,IAAI,EAAE,MAAM;gBACZ,KAAK,EAAE,OAAO,CAAC,IAAI,CAAC,KAAK,EAAE,OAAO,CAAC,qBAAqB,CAAC;gBACzD,WAAW,EAAE,eAAe,CAAC,IAAI,CAAC;aACnC,CAAC;QACJ,CAAC;KACF,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,UAA2E,EAAE;IAE7E,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,2BAA2B,CAAC;IACjE,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,KAAK,CAAC;IAC7C,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,aAAa,CAAC;IAC7C,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,SAAS,CAAC,QAAQ,EAAE;YACpC,MAAM,EAAE,MAAM;YACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;YAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;gBACnB,KAAK;gBACL,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,kCAAkC,EAAE,CAAC;gBACzE,UAAU,EAAE,CAAC;aACd,CAAC;SACH,CAAC,CAAC;QACH,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAA2B,CAAC;QAC5E,IAAI,CAAC,GAAG,CAAC,EAAE,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YAC1B,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,aAAa,CAAC,IAAI,EAAE,GAAG,GAAG,CAAC,MAAM,IAAI,GAAG,CAAC,UAAU,EAAE,CAAC,EAAE,CAAC;QACtF,CAAC;QACD,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,CAAC;IAChF,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC;IAChF,CAAC;AACH,CAAC"}
@@ -0,0 +1,64 @@
1
+ /**
2
+ * A `GenerationEngine` backed by Vercel's AI Gateway (vercel.com/docs/ai-gateway)
3
+ * — one OpenAI-compatible endpoint in front of 200+ models from many labs.
4
+ * Covers text/reasoning (chat completions, the same wire format as
5
+ * OpenRouter) and image generation (the OpenAI-style images endpoint).
6
+ *
7
+ * Video isn't offered: the gateway's OpenAI-compatible surface has no
8
+ * confirmed video-generation endpoint, so `outputKind: "video"` doesn't
9
+ * exist here. Use `createFalEngine` for video.
10
+ *
11
+ * Security: this engine never sees an API key and never calls
12
+ * `ai-gateway.vercel.sh` directly. It POSTs to a same-origin `endpoint`
13
+ * (default `/api/chai/vercel`, served by `createChaiHandler`), which adds
14
+ * `AI_GATEWAY_API_KEY` (or `VERCEL_OIDC_TOKEN` on Vercel) server-side.
15
+ * Passing a key into this file would put it in the browser bundle; don't
16
+ * add one.
17
+ */
18
+ import { type GenerationEngine } from "../engine.js";
19
+ /** Output kinds the gateway can produce. Video is excluded: see the file doc comment. */
20
+ export type VercelOutputKind = "text" | "image";
21
+ export interface VercelEngineOptions {
22
+ /** Same-origin proxy base path; the engine appends `/chat` or `/images`. */
23
+ endpoint?: string;
24
+ /**
25
+ * Gateway model id (e.g. `"anthropic/claude-opus-5"`), used when the
26
+ * caller passes no `modelId` and no `modelByKind` entry matches. There is
27
+ * no built-in default: the gateway catalog changes often, so name the
28
+ * model you want.
29
+ */
30
+ model?: string;
31
+ /** Per-output-kind model override, e.g. a text model and an image model. */
32
+ modelByKind?: Partial<Record<VercelOutputKind, string>>;
33
+ /** Which kind of output this engine instance produces. Defaults to "text". */
34
+ outputKind?: VercelOutputKind;
35
+ /** Cap on a text response's length, as in `createOpenRouterEngine`. */
36
+ maxTokens?: number;
37
+ /** USD per million tokens, if you want `usage.costUsd` populated. Not defaulted, for the same reason as OpenRouter's. */
38
+ pricePerMillionTokens?: {
39
+ input: number;
40
+ output: number;
41
+ };
42
+ /** Injectable for tests; defaults to the global `fetch`. */
43
+ fetchImpl?: typeof fetch;
44
+ }
45
+ export declare function createVercelEngine(options?: VercelEngineOptions): GenerationEngine;
46
+ /**
47
+ * A minimal, cheap connectivity check through the same-origin route — the
48
+ * "use real inference sparingly" path. Like `pingOpenRouter` and `pingFal`,
49
+ * it's triggered once by a person from the Develop page's "Test connection"
50
+ * button, never by a test or on mount. It needs a `model` because the
51
+ * gateway has no default.
52
+ */
53
+ export declare function pingVercel(options: {
54
+ endpoint?: string;
55
+ model: string;
56
+ fetchImpl?: typeof fetch;
57
+ }): Promise<{
58
+ ok: true;
59
+ reply: string;
60
+ } | {
61
+ ok: false;
62
+ error: string;
63
+ }>;
64
+ //# sourceMappingURL=vercel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vercel.d.ts","sourceRoot":"","sources":["../../src/engines/vercel.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAmB,KAAK,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAUtE,yFAAyF;AACzF,MAAM,MAAM,gBAAgB,GAAG,MAAM,GAAG,OAAO,CAAC;AAEhD,MAAM,WAAW,mBAAmB;IAClC,4EAA4E;IAC5E,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;;OAKG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,4EAA4E;IAC5E,WAAW,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,gBAAgB,EAAE,MAAM,CAAC,CAAC,CAAC;IACxD,8EAA8E;IAC9E,UAAU,CAAC,EAAE,gBAAgB,CAAC;IAC9B,uEAAuE;IACvE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,yHAAyH;IACzH,qBAAqB,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAC1D,4DAA4D;IAC5D,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;CAC1B;AAMD,wBAAgB,kBAAkB,CAAC,OAAO,GAAE,mBAAwB,GAAG,gBAAgB,CAkCtF;AA4ED;;;;;;GAMG;AACH,wBAAsB,UAAU,CAC9B,OAAO,EAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,OAAO,KAAK,CAAA;CAAE,GACtE,OAAO,CAAC;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,CAqBrE"}
@@ -0,0 +1,137 @@
1
+ /**
2
+ * A `GenerationEngine` backed by Vercel's AI Gateway (vercel.com/docs/ai-gateway)
3
+ * — one OpenAI-compatible endpoint in front of 200+ models from many labs.
4
+ * Covers text/reasoning (chat completions, the same wire format as
5
+ * OpenRouter) and image generation (the OpenAI-style images endpoint).
6
+ *
7
+ * Video isn't offered: the gateway's OpenAI-compatible surface has no
8
+ * confirmed video-generation endpoint, so `outputKind: "video"` doesn't
9
+ * exist here. Use `createFalEngine` for video.
10
+ *
11
+ * Security: this engine never sees an API key and never calls
12
+ * `ai-gateway.vercel.sh` directly. It POSTs to a same-origin `endpoint`
13
+ * (default `/api/chai/vercel`, served by `createChaiHandler`), which adds
14
+ * `AI_GATEWAY_API_KEY` (or `VERCEL_OIDC_TOKEN` on Vercel) server-side.
15
+ * Passing a key into this file would put it in the browser bundle; don't
16
+ * add one.
17
+ */
18
+ import { readEvaluations } from "../engine.js";
19
+ import { buildChatRequest, describeError, readChatStream, toUsage, } from "./openrouter.js";
20
+ export function createVercelEngine(options = {}) {
21
+ const endpoint = (options.endpoint ?? "/api/chai/vercel").replace(/\/$/, "");
22
+ const fetchImpl = options.fetchImpl ?? fetch;
23
+ const outputKind = options.outputKind ?? "text";
24
+ return {
25
+ async generate({ modelId, prompt, attachments, onText, signal }) {
26
+ const model = modelId ?? options.modelByKind?.[outputKind] ?? options.model;
27
+ if (!model) {
28
+ throw new Error(`vercelEngine: no model configured for outputKind "${outputKind}" — pass \`model\` or \`modelByKind.${outputKind}\`.`);
29
+ }
30
+ if (outputKind === "image") {
31
+ if (attachments.length > 0) {
32
+ throw new Error("vercelEngine: image generation takes a prompt only; attachments aren't supported here.");
33
+ }
34
+ return generateImage({ endpoint, fetchImpl, model, prompt, signal });
35
+ }
36
+ return generateText({
37
+ endpoint,
38
+ fetchImpl,
39
+ model,
40
+ prompt,
41
+ attachments,
42
+ onText,
43
+ signal,
44
+ maxTokens: options.maxTokens,
45
+ pricePerMillionTokens: options.pricePerMillionTokens,
46
+ });
47
+ },
48
+ };
49
+ }
50
+ async function generateText(args) {
51
+ const body = buildChatRequest({
52
+ model: args.model,
53
+ prompt: args.prompt,
54
+ media: args.attachments,
55
+ maxTokens: args.maxTokens,
56
+ });
57
+ const res = await args.fetchImpl(`${args.endpoint}/chat`, {
58
+ method: "POST",
59
+ headers: { "Content-Type": "application/json" },
60
+ body: JSON.stringify(args.onText ? { ...body, stream: true } : body),
61
+ signal: args.signal,
62
+ });
63
+ // A rejected request comes back as plain JSON even when streaming was asked for.
64
+ if (args.onText && res.ok) {
65
+ const { text, usage, evaluations } = await readChatStream(res, args.onText);
66
+ if (!text)
67
+ throw new Error("vercelEngine: empty response from the AI Gateway");
68
+ return { src: text, kind: "text", usage: toUsage(usage, args.pricePerMillionTokens), evaluations };
69
+ }
70
+ const json = (await res.json().catch(() => ({})));
71
+ if (!res.ok || json.error) {
72
+ throw new Error(`vercelEngine: ${describeError(json, `${res.status} ${res.statusText}`)}`);
73
+ }
74
+ const text = json.choices?.[0]?.message?.content;
75
+ if (!text)
76
+ throw new Error("vercelEngine: empty response from the AI Gateway");
77
+ return {
78
+ src: text,
79
+ kind: "text",
80
+ usage: toUsage(json.usage, args.pricePerMillionTokens),
81
+ evaluations: readEvaluations(json),
82
+ };
83
+ }
84
+ /**
85
+ * Image generation through `/v1/images/generations`. The OpenAI images
86
+ * shape returns either a hosted `url` or inline `b64_json`; both come back
87
+ * as a usable `src`. The gateway's image endpoint takes no aspect-ratio
88
+ * field here, so the engine doesn't send one — size is the model's default.
89
+ */
90
+ async function generateImage(args) {
91
+ const res = await args.fetchImpl(`${args.endpoint}/images`, {
92
+ method: "POST",
93
+ headers: { "Content-Type": "application/json" },
94
+ body: JSON.stringify({ model: args.model, prompt: args.prompt, n: 1 }),
95
+ signal: args.signal,
96
+ });
97
+ const json = (await res.json().catch(() => ({})));
98
+ if (!res.ok || json.error) {
99
+ throw new Error(`vercelEngine: ${describeError(json, `${res.status} ${res.statusText}`)}`);
100
+ }
101
+ const item = json.data?.[0];
102
+ const src = item?.url ?? (item?.b64_json ? `data:image/png;base64,${item.b64_json}` : undefined);
103
+ if (!src)
104
+ throw new Error("vercelEngine: no image in the AI Gateway's response");
105
+ return { src, kind: "image" };
106
+ }
107
+ /**
108
+ * A minimal, cheap connectivity check through the same-origin route — the
109
+ * "use real inference sparingly" path. Like `pingOpenRouter` and `pingFal`,
110
+ * it's triggered once by a person from the Develop page's "Test connection"
111
+ * button, never by a test or on mount. It needs a `model` because the
112
+ * gateway has no default.
113
+ */
114
+ export async function pingVercel(options) {
115
+ const endpoint = (options.endpoint ?? "/api/chai/vercel").replace(/\/$/, "");
116
+ const fetchImpl = options.fetchImpl ?? fetch;
117
+ try {
118
+ const res = await fetchImpl(`${endpoint}/chat`, {
119
+ method: "POST",
120
+ headers: { "Content-Type": "application/json" },
121
+ body: JSON.stringify({
122
+ model: options.model,
123
+ messages: [{ role: "user", content: "Reply with exactly one word: OK." }],
124
+ max_tokens: 5,
125
+ }),
126
+ });
127
+ const json = (await res.json().catch(() => ({})));
128
+ if (!res.ok || json.error) {
129
+ return { ok: false, error: describeError(json, `${res.status} ${res.statusText}`) };
130
+ }
131
+ return { ok: true, reply: json.choices?.[0]?.message?.content?.trim() ?? "" };
132
+ }
133
+ catch (err) {
134
+ return { ok: false, error: err instanceof Error ? err.message : String(err) };
135
+ }
136
+ }
137
+ //# sourceMappingURL=vercel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vercel.js","sourceRoot":"","sources":["../../src/engines/vercel.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -4,6 +4,7 @@ export * from "./run.js";
4
4
  export * from "./media-analysis-prompts.js";
5
5
  export * from "./engines/openrouter.js";
6
6
  export * from "./engines/fal.js";
7
+ export * from "./engines/vercel.js";
7
8
  export * from "./model-routing.js";
8
9
  export * from "./region-edit.js";
9
10
  export * from "./suggested-models.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC;AAC5B,cAAc,UAAU,CAAC;AACzB,cAAc,6BAA6B,CAAC;AAC5C,cAAc,yBAAyB,CAAC;AACxC,cAAc,kBAAkB,CAAC;AACjC,cAAc,oBAAoB,CAAC;AACnC,cAAc,kBAAkB,CAAC;AACjC,cAAc,uBAAuB,CAAC;AACtC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,gBAAgB,CAAC;AAC/B,cAAc,eAAe,CAAC;AAC9B,cAAc,qBAAqB,CAAC;AACpC,cAAc,oBAAoB,CAAC;AACnC,cAAc,oBAAoB,CAAC;AACnC,cAAc,oBAAoB,CAAC;AACnC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,kBAAkB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC;AAC5B,cAAc,UAAU,CAAC;AACzB,cAAc,6BAA6B,CAAC;AAC5C,cAAc,yBAAyB,CAAC;AACxC,cAAc,kBAAkB,CAAC;AACjC,cAAc,qBAAqB,CAAC;AACpC,cAAc,oBAAoB,CAAC;AACnC,cAAc,kBAAkB,CAAC;AACjC,cAAc,uBAAuB,CAAC;AACtC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,gBAAgB,CAAC;AAC/B,cAAc,eAAe,CAAC;AAC9B,cAAc,qBAAqB,CAAC;AACpC,cAAc,oBAAoB,CAAC;AACnC,cAAc,oBAAoB,CAAC;AACnC,cAAc,oBAAoB,CAAC;AACnC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,kBAAkB,CAAC"}
package/dist/index.js CHANGED
@@ -4,6 +4,7 @@ export * from "./run.js";
4
4
  export * from "./media-analysis-prompts.js";
5
5
  export * from "./engines/openrouter.js";
6
6
  export * from "./engines/fal.js";
7
+ export * from "./engines/vercel.js";
7
8
  export * from "./model-routing.js";
8
9
  export * from "./region-edit.js";
9
10
  export * from "./suggested-models.js";
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC;AAC5B,cAAc,UAAU,CAAC;AACzB,cAAc,6BAA6B,CAAC;AAC5C,cAAc,yBAAyB,CAAC;AACxC,cAAc,kBAAkB,CAAC;AACjC,cAAc,oBAAoB,CAAC;AACnC,cAAc,kBAAkB,CAAC;AACjC,cAAc,uBAAuB,CAAC;AACtC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,gBAAgB,CAAC;AAC/B,cAAc,eAAe,CAAC;AAC9B,cAAc,qBAAqB,CAAC;AACpC,cAAc,oBAAoB,CAAC;AACnC,cAAc,oBAAoB,CAAC;AACnC,cAAc,oBAAoB,CAAC;AACnC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,kBAAkB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC;AAC5B,cAAc,UAAU,CAAC;AACzB,cAAc,6BAA6B,CAAC;AAC5C,cAAc,yBAAyB,CAAC;AACxC,cAAc,kBAAkB,CAAC;AACjC,cAAc,qBAAqB,CAAC;AACpC,cAAc,oBAAoB,CAAC;AACnC,cAAc,kBAAkB,CAAC;AACjC,cAAc,uBAAuB,CAAC;AACtC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,gBAAgB,CAAC;AAC/B,cAAc,eAAe,CAAC;AAC9B,cAAc,qBAAqB,CAAC;AACpC,cAAc,oBAAoB,CAAC;AACnC,cAAc,oBAAoB,CAAC;AACnC,cAAc,oBAAoB,CAAC;AACnC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,kBAAkB,CAAC"}
@@ -8,12 +8,17 @@
8
8
  * runs in Next.js, React Router (Remix), Hono, Cloudflare Workers, Bun and
9
9
  * Deno. `toNodeHandler` (./node.ts) adapts it for Express and plain Node.
10
10
  */
11
- export type ChaiProviderId = "fal" | "openrouter";
11
+ export type ChaiProviderId = "fal" | "openrouter" | "vercel";
12
12
  export interface ChaiHandlerOptions {
13
13
  /** Defaults to the `FAL_KEY` environment variable. */
14
14
  falKey?: string;
15
15
  /** Defaults to the `OPENROUTER_API_KEY` environment variable. */
16
16
  openRouterKey?: string;
17
+ /**
18
+ * Vercel AI Gateway key. Defaults to `AI_GATEWAY_API_KEY`, then to
19
+ * `VERCEL_OIDC_TOKEN`, which Vercel sets automatically on deployed apps.
20
+ */
21
+ aiGatewayKey?: string;
17
22
  /**
18
23
  * Who may use this route. Return false to reject with 403. Without it,
19
24
  * anyone who can reach your site can spend your API credit: check your
@@ -21,7 +26,7 @@ export interface ChaiHandlerOptions {
21
26
  */
22
27
  authorize?: (request: Request) => boolean | Promise<boolean>;
23
28
  /**
24
- * The only model ids this route will run, across both providers (e.g.
29
+ * The only model ids this route will run, across all providers (e.g.
25
30
  * `"fal-ai/flux/schnell"`, `"anthropic/claude-opus-5"`). Anything else is
26
31
  * rejected with 403, so nobody can run an expensive model through you.
27
32
  * Omit to allow every model.
@@ -1 +1 @@
1
- {"version":3,"file":"chai-handler.d.ts","sourceRoot":"","sources":["../../src/server/chai-handler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAKH,MAAM,MAAM,cAAc,GAAG,KAAK,GAAG,YAAY,CAAC;AAElD,MAAM,WAAW,kBAAkB;IACjC,sDAAsD;IACtD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,iEAAiE;IACjE,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;;OAIG;IACH,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC7D;;;;;OAKG;IACH,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IACzB,4DAA4D;IAC5D,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;CACtB;AAED,MAAM,WAAW,WAAW;IAC1B,2CAA2C;IAC3C,MAAM,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;IAChD,oFAAoF;IACpF,GAAG,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;IAC7C,IAAI,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;IAC9C,yBAAyB;IACzB,GAAG,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;CAC9C;AAED,wBAAgB,iBAAiB,CAAC,OAAO,GAAE,kBAAuB,GAAG,WAAW,CAmD/E"}
1
+ {"version":3,"file":"chai-handler.d.ts","sourceRoot":"","sources":["../../src/server/chai-handler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAMH,MAAM,MAAM,cAAc,GAAG,KAAK,GAAG,YAAY,GAAG,QAAQ,CAAC;AAQ7D,MAAM,WAAW,kBAAkB;IACjC,sDAAsD;IACtD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,iEAAiE;IACjE,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;;OAIG;IACH,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC7D;;;;;OAKG;IACH,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IACzB,4DAA4D;IAC5D,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;CACtB;AAED,MAAM,WAAW,WAAW;IAC1B,2CAA2C;IAC3C,MAAM,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;IAChD,oFAAoF;IACpF,GAAG,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;IAC7C,IAAI,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;IAC9C,yBAAyB;IACzB,GAAG,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;CAC9C;AAED,wBAAgB,iBAAiB,CAAC,OAAO,GAAE,kBAAuB,GAAG,WAAW,CAuE/E"}
@@ -10,6 +10,12 @@
10
10
  */
11
11
  const FAL_QUEUE_BASE = "https://queue.fal.run";
12
12
  const OPENROUTER_CHAT_URL = "https://openrouter.ai/api/v1/chat/completions";
13
+ const VERCEL_GATEWAY_BASE = "https://ai-gateway.vercel.sh/v1";
14
+ const PROVIDER_LABEL = {
15
+ fal: "Fal.ai",
16
+ openrouter: "OpenRouter",
17
+ vercel: "Vercel AI Gateway",
18
+ };
13
19
  export function createChaiHandler(options = {}) {
14
20
  const fetchImpl = options.fetch ?? fetch;
15
21
  async function handle(request) {
@@ -40,6 +46,28 @@ export function createChaiHandler(options = {}) {
40
46
  body,
41
47
  });
42
48
  }
49
+ // Vercel AI Gateway is OpenAI-compatible: /chat for text, /images for image generation.
50
+ if (route.provider === "vercel") {
51
+ const upstream = route.rest === "chat" ? "chat/completions" : route.rest === "images" ? "images/generations" : null;
52
+ if (!upstream)
53
+ return fail(404, `Unknown Vercel AI Gateway path "${route.rest}".`);
54
+ if (request.method !== "POST")
55
+ return fail(405, "Use POST.");
56
+ const key = options.aiGatewayKey ?? readEnv("AI_GATEWAY_API_KEY") ?? readEnv("VERCEL_OIDC_TOKEN");
57
+ if (!key)
58
+ return fail(500, "AI_GATEWAY_API_KEY isn't set on the server.");
59
+ const body = await request.text();
60
+ const model = readModel(body);
61
+ if (options.allowedModels && !(model && options.allowedModels.includes(model))) {
62
+ return fail(403, `Model "${model ?? "(none)"}" isn't allowed on this server.`);
63
+ }
64
+ return forward(fetchImpl, route.provider, `${VERCEL_GATEWAY_BASE}/${upstream}`, {
65
+ signal: request.signal,
66
+ method: "POST",
67
+ headers: { Authorization: `Bearer ${key}`, "Content-Type": "application/json" },
68
+ body,
69
+ });
70
+ }
43
71
  // Fal's queue API: POST /<model> submits, GET /<model>/requests/<id>[/status]
44
72
  // polls, PUT /<model>/requests/<id>/cancel cancels.
45
73
  const isCancel = request.method === "PUT" && route.rest.endsWith("/cancel");
@@ -63,7 +91,7 @@ export function createChaiHandler(options = {}) {
63
91
  /** Finds the provider segment anywhere in the path, so the route can be mounted at any prefix. */
64
92
  function parseRoute(pathname) {
65
93
  const parts = pathname.split("/").filter(Boolean);
66
- const i = parts.findIndex((p) => p === "fal" || p === "openrouter");
94
+ const i = parts.findIndex((p) => p === "fal" || p === "openrouter" || p === "vercel");
67
95
  if (i === -1 || i === parts.length - 1)
68
96
  return null;
69
97
  return { provider: parts[i], rest: parts.slice(i + 1).join("/") };
@@ -78,10 +106,10 @@ async function forward(fetchImpl, provider, url, init) {
78
106
  });
79
107
  }
80
108
  catch (err) {
81
- return errorResponse(provider, 502, `Couldn't reach ${provider === "fal" ? "Fal.ai" : "OpenRouter"}: ${err instanceof Error ? err.message : String(err)}`);
109
+ return errorResponse(provider, 502, `Couldn't reach ${PROVIDER_LABEL[provider]}: ${err instanceof Error ? err.message : String(err)}`);
82
110
  }
83
111
  }
84
- /** Errors in the shape each engine already reads: Fal's `{ error }`, OpenRouter's `{ error: { message } }`. */
112
+ /** Errors in the shape each engine already reads: Fal's `{ error }`, OpenRouter's and Vercel's `{ error: { message } }`. */
85
113
  function errorResponse(provider, status, message) {
86
114
  const body = provider === "fal" ? { error: message } : { error: { message } };
87
115
  return Response.json(body, { status });
@@ -1 +1 @@
1
- {"version":3,"file":"chai-handler.js","sourceRoot":"","sources":["../../src/server/chai-handler.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"chai-handler.js","sourceRoot":"","sources":["../../src/server/chai-handler.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@chai-ui/core",
3
- "version": "0.9.0",
4
- "description": "CHAI UI headless core — the Request/Run/Result data model and pluggable generation engines (Fal.ai, OpenRouter, mock). No framework, no rendering.",
3
+ "version": "0.10.0",
4
+ "description": "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
5
  "license": "MIT",
6
6
  "author": "kazarazat",
7
7
  "homepage": "https://chai-ui.com",
@@ -21,7 +21,8 @@
21
21
  "headless",
22
22
  "ai-engine",
23
23
  "fal",
24
- "openrouter"
24
+ "openrouter",
25
+ "vercel-ai-gateway"
25
26
  ],
26
27
  "type": "module",
27
28
  "sideEffects": false,
package/src/engine.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * A pluggable generation engine — the one interface every model provider
3
- * (`createFalEngine`, `createOpenRouterEngine`, `mockEngine`, or your own)
3
+ * (`createFalEngine`, `createOpenRouterEngine`, `createVercelEngine`, `mockEngine`, or your own)
4
4
  * implements, and the one `useComposer` calls. One call = one model run:
5
5
  * a prompt plus any attached media in, one piece of output back.
6
6
  *
@@ -55,7 +55,7 @@ type ChatContentPart =
55
55
  | { type: "video_url"; video_url: { url: string } }
56
56
  | { type: "input_audio"; input_audio: { data: string; format: string } };
57
57
 
58
- interface ChatCompletionResponse {
58
+ export interface ChatCompletionResponse {
59
59
  choices?: { message?: { content?: string } }[];
60
60
  // OpenAI-compatible shape OpenRouter itself uses; absent on some
61
61
  // providers routed through it, hence all-optional.
@@ -69,7 +69,7 @@ interface ChatCompletionResponse {
69
69
  }
70
70
 
71
71
  /** Converts OpenRouter's raw `usage` into `Usage`, computing `costUsd` only when a real rate was supplied. */
72
- function toUsage(
72
+ export function toUsage(
73
73
  usage: ChatCompletionResponse["usage"],
74
74
  pricePerMillionTokens?: { input: number; output: number }
75
75
  ): Usage | undefined {
@@ -90,7 +90,7 @@ function toUsage(
90
90
  }
91
91
 
92
92
  /** Prefers the upstream provider's own error message over OpenRouter's generic wrapper, when both are present. */
93
- function describeError(json: ChatCompletionResponse, fallback: string): string {
93
+ export function describeError(json: ChatCompletionResponse, fallback: string): string {
94
94
  const base = json.error?.message ?? fallback;
95
95
  const raw = json.error?.metadata?.raw;
96
96
  if (!raw) return base;
@@ -0,0 +1,198 @@
1
+ /**
2
+ * A `GenerationEngine` backed by Vercel's AI Gateway (vercel.com/docs/ai-gateway)
3
+ * — one OpenAI-compatible endpoint in front of 200+ models from many labs.
4
+ * Covers text/reasoning (chat completions, the same wire format as
5
+ * OpenRouter) and image generation (the OpenAI-style images endpoint).
6
+ *
7
+ * Video isn't offered: the gateway's OpenAI-compatible surface has no
8
+ * confirmed video-generation endpoint, so `outputKind: "video"` doesn't
9
+ * exist here. Use `createFalEngine` for video.
10
+ *
11
+ * Security: this engine never sees an API key and never calls
12
+ * `ai-gateway.vercel.sh` directly. It POSTs to a same-origin `endpoint`
13
+ * (default `/api/chai/vercel`, served by `createChaiHandler`), which adds
14
+ * `AI_GATEWAY_API_KEY` (or `VERCEL_OIDC_TOKEN` on Vercel) server-side.
15
+ * Passing a key into this file would put it in the browser bundle; don't
16
+ * add one.
17
+ */
18
+
19
+ import { readEvaluations, type GenerationEngine } from "../engine.js";
20
+ import type { DroppedMedia, Evaluation, Usage } from "../types.js";
21
+ import {
22
+ buildChatRequest,
23
+ describeError,
24
+ readChatStream,
25
+ toUsage,
26
+ type ChatCompletionResponse,
27
+ } from "./openrouter.js";
28
+
29
+ /** Output kinds the gateway can produce. Video is excluded: see the file doc comment. */
30
+ export type VercelOutputKind = "text" | "image";
31
+
32
+ export interface VercelEngineOptions {
33
+ /** Same-origin proxy base path; the engine appends `/chat` or `/images`. */
34
+ endpoint?: string;
35
+ /**
36
+ * Gateway model id (e.g. `"anthropic/claude-opus-5"`), used when the
37
+ * caller passes no `modelId` and no `modelByKind` entry matches. There is
38
+ * no built-in default: the gateway catalog changes often, so name the
39
+ * model you want.
40
+ */
41
+ model?: string;
42
+ /** Per-output-kind model override, e.g. a text model and an image model. */
43
+ modelByKind?: Partial<Record<VercelOutputKind, string>>;
44
+ /** Which kind of output this engine instance produces. Defaults to "text". */
45
+ outputKind?: VercelOutputKind;
46
+ /** Cap on a text response's length, as in `createOpenRouterEngine`. */
47
+ maxTokens?: number;
48
+ /** USD per million tokens, if you want `usage.costUsd` populated. Not defaulted, for the same reason as OpenRouter's. */
49
+ pricePerMillionTokens?: { input: number; output: number };
50
+ /** Injectable for tests; defaults to the global `fetch`. */
51
+ fetchImpl?: typeof fetch;
52
+ }
53
+
54
+ interface ImagesResponse extends ChatCompletionResponse {
55
+ data?: { url?: string; b64_json?: string }[];
56
+ }
57
+
58
+ export function createVercelEngine(options: VercelEngineOptions = {}): GenerationEngine {
59
+ const endpoint = (options.endpoint ?? "/api/chai/vercel").replace(/\/$/, "");
60
+ const fetchImpl = options.fetchImpl ?? fetch;
61
+ const outputKind = options.outputKind ?? "text";
62
+
63
+ return {
64
+ async generate({ modelId, prompt, attachments, onText, signal }) {
65
+ const model = modelId ?? options.modelByKind?.[outputKind] ?? options.model;
66
+ if (!model) {
67
+ throw new Error(
68
+ `vercelEngine: no model configured for outputKind "${outputKind}" — pass \`model\` or \`modelByKind.${outputKind}\`.`
69
+ );
70
+ }
71
+
72
+ if (outputKind === "image") {
73
+ if (attachments.length > 0) {
74
+ throw new Error("vercelEngine: image generation takes a prompt only; attachments aren't supported here.");
75
+ }
76
+ return generateImage({ endpoint, fetchImpl, model, prompt, signal });
77
+ }
78
+
79
+ return generateText({
80
+ endpoint,
81
+ fetchImpl,
82
+ model,
83
+ prompt,
84
+ attachments,
85
+ onText,
86
+ signal,
87
+ maxTokens: options.maxTokens,
88
+ pricePerMillionTokens: options.pricePerMillionTokens,
89
+ });
90
+ },
91
+ };
92
+ }
93
+
94
+ async function generateText(args: {
95
+ endpoint: string;
96
+ fetchImpl: typeof fetch;
97
+ model: string;
98
+ prompt: string;
99
+ attachments: DroppedMedia[];
100
+ onText?: (textSoFar: string) => void;
101
+ signal?: AbortSignal;
102
+ maxTokens?: number;
103
+ pricePerMillionTokens?: { input: number; output: number };
104
+ }): Promise<{ src: string; kind: "text"; usage?: Usage; evaluations?: Evaluation[] }> {
105
+ const body = buildChatRequest({
106
+ model: args.model,
107
+ prompt: args.prompt,
108
+ media: args.attachments,
109
+ maxTokens: args.maxTokens,
110
+ });
111
+ const res = await args.fetchImpl(`${args.endpoint}/chat`, {
112
+ method: "POST",
113
+ headers: { "Content-Type": "application/json" },
114
+ body: JSON.stringify(args.onText ? { ...body, stream: true } : body),
115
+ signal: args.signal,
116
+ });
117
+
118
+ // A rejected request comes back as plain JSON even when streaming was asked for.
119
+ if (args.onText && res.ok) {
120
+ const { text, usage, evaluations } = await readChatStream(res, args.onText);
121
+ if (!text) throw new Error("vercelEngine: empty response from the AI Gateway");
122
+ return { src: text, kind: "text", usage: toUsage(usage, args.pricePerMillionTokens), evaluations };
123
+ }
124
+
125
+ const json = (await res.json().catch(() => ({}))) as ChatCompletionResponse;
126
+ if (!res.ok || json.error) {
127
+ throw new Error(`vercelEngine: ${describeError(json, `${res.status} ${res.statusText}`)}`);
128
+ }
129
+ const text = json.choices?.[0]?.message?.content;
130
+ if (!text) throw new Error("vercelEngine: empty response from the AI Gateway");
131
+ return {
132
+ src: text,
133
+ kind: "text",
134
+ usage: toUsage(json.usage, args.pricePerMillionTokens),
135
+ evaluations: readEvaluations(json),
136
+ };
137
+ }
138
+
139
+ /**
140
+ * Image generation through `/v1/images/generations`. The OpenAI images
141
+ * shape returns either a hosted `url` or inline `b64_json`; both come back
142
+ * as a usable `src`. The gateway's image endpoint takes no aspect-ratio
143
+ * field here, so the engine doesn't send one — size is the model's default.
144
+ */
145
+ async function generateImage(args: {
146
+ endpoint: string;
147
+ fetchImpl: typeof fetch;
148
+ model: string;
149
+ prompt: string;
150
+ signal?: AbortSignal;
151
+ }): Promise<{ src: string; kind: "image" }> {
152
+ const res = await args.fetchImpl(`${args.endpoint}/images`, {
153
+ method: "POST",
154
+ headers: { "Content-Type": "application/json" },
155
+ body: JSON.stringify({ model: args.model, prompt: args.prompt, n: 1 }),
156
+ signal: args.signal,
157
+ });
158
+ const json = (await res.json().catch(() => ({}))) as ImagesResponse;
159
+ if (!res.ok || json.error) {
160
+ throw new Error(`vercelEngine: ${describeError(json, `${res.status} ${res.statusText}`)}`);
161
+ }
162
+ const item = json.data?.[0];
163
+ const src = item?.url ?? (item?.b64_json ? `data:image/png;base64,${item.b64_json}` : undefined);
164
+ if (!src) throw new Error("vercelEngine: no image in the AI Gateway's response");
165
+ return { src, kind: "image" };
166
+ }
167
+
168
+ /**
169
+ * A minimal, cheap connectivity check through the same-origin route — the
170
+ * "use real inference sparingly" path. Like `pingOpenRouter` and `pingFal`,
171
+ * it's triggered once by a person from the Develop page's "Test connection"
172
+ * button, never by a test or on mount. It needs a `model` because the
173
+ * gateway has no default.
174
+ */
175
+ export async function pingVercel(
176
+ options: { endpoint?: string; model: string; fetchImpl?: typeof fetch }
177
+ ): Promise<{ ok: true; reply: string } | { ok: false; error: string }> {
178
+ const endpoint = (options.endpoint ?? "/api/chai/vercel").replace(/\/$/, "");
179
+ const fetchImpl = options.fetchImpl ?? fetch;
180
+ try {
181
+ const res = await fetchImpl(`${endpoint}/chat`, {
182
+ method: "POST",
183
+ headers: { "Content-Type": "application/json" },
184
+ body: JSON.stringify({
185
+ model: options.model,
186
+ messages: [{ role: "user", content: "Reply with exactly one word: OK." }],
187
+ max_tokens: 5,
188
+ }),
189
+ });
190
+ const json = (await res.json().catch(() => ({}))) as ChatCompletionResponse;
191
+ if (!res.ok || json.error) {
192
+ return { ok: false, error: describeError(json, `${res.status} ${res.statusText}`) };
193
+ }
194
+ return { ok: true, reply: json.choices?.[0]?.message?.content?.trim() ?? "" };
195
+ } catch (err) {
196
+ return { ok: false, error: err instanceof Error ? err.message : String(err) };
197
+ }
198
+ }
package/src/index.ts CHANGED
@@ -4,6 +4,7 @@ export * from "./run.js";
4
4
  export * from "./media-analysis-prompts.js";
5
5
  export * from "./engines/openrouter.js";
6
6
  export * from "./engines/fal.js";
7
+ export * from "./engines/vercel.js";
7
8
  export * from "./model-routing.js";
8
9
  export * from "./region-edit.js";
9
10
  export * from "./suggested-models.js";
@@ -11,14 +11,26 @@
11
11
 
12
12
  const FAL_QUEUE_BASE = "https://queue.fal.run";
13
13
  const OPENROUTER_CHAT_URL = "https://openrouter.ai/api/v1/chat/completions";
14
+ const VERCEL_GATEWAY_BASE = "https://ai-gateway.vercel.sh/v1";
14
15
 
15
- export type ChaiProviderId = "fal" | "openrouter";
16
+ export type ChaiProviderId = "fal" | "openrouter" | "vercel";
17
+
18
+ const PROVIDER_LABEL: Record<ChaiProviderId, string> = {
19
+ fal: "Fal.ai",
20
+ openrouter: "OpenRouter",
21
+ vercel: "Vercel AI Gateway",
22
+ };
16
23
 
17
24
  export interface ChaiHandlerOptions {
18
25
  /** Defaults to the `FAL_KEY` environment variable. */
19
26
  falKey?: string;
20
27
  /** Defaults to the `OPENROUTER_API_KEY` environment variable. */
21
28
  openRouterKey?: string;
29
+ /**
30
+ * Vercel AI Gateway key. Defaults to `AI_GATEWAY_API_KEY`, then to
31
+ * `VERCEL_OIDC_TOKEN`, which Vercel sets automatically on deployed apps.
32
+ */
33
+ aiGatewayKey?: string;
22
34
  /**
23
35
  * Who may use this route. Return false to reject with 403. Without it,
24
36
  * anyone who can reach your site can spend your API credit: check your
@@ -26,7 +38,7 @@ export interface ChaiHandlerOptions {
26
38
  */
27
39
  authorize?: (request: Request) => boolean | Promise<boolean>;
28
40
  /**
29
- * The only model ids this route will run, across both providers (e.g.
41
+ * The only model ids this route will run, across all providers (e.g.
30
42
  * `"fal-ai/flux/schnell"`, `"anthropic/claude-opus-5"`). Anything else is
31
43
  * rejected with 403, so nobody can run an expensive model through you.
32
44
  * Omit to allow every model.
@@ -79,6 +91,26 @@ export function createChaiHandler(options: ChaiHandlerOptions = {}): ChaiHandler
79
91
  });
80
92
  }
81
93
 
94
+ // Vercel AI Gateway is OpenAI-compatible: /chat for text, /images for image generation.
95
+ if (route.provider === "vercel") {
96
+ const upstream = route.rest === "chat" ? "chat/completions" : route.rest === "images" ? "images/generations" : null;
97
+ if (!upstream) return fail(404, `Unknown Vercel AI Gateway path "${route.rest}".`);
98
+ if (request.method !== "POST") return fail(405, "Use POST.");
99
+ const key = options.aiGatewayKey ?? readEnv("AI_GATEWAY_API_KEY") ?? readEnv("VERCEL_OIDC_TOKEN");
100
+ if (!key) return fail(500, "AI_GATEWAY_API_KEY isn't set on the server.");
101
+ const body = await request.text();
102
+ const model = readModel(body);
103
+ if (options.allowedModels && !(model && options.allowedModels.includes(model))) {
104
+ return fail(403, `Model "${model ?? "(none)"}" isn't allowed on this server.`);
105
+ }
106
+ return forward(fetchImpl, route.provider, `${VERCEL_GATEWAY_BASE}/${upstream}`, {
107
+ signal: request.signal,
108
+ method: "POST",
109
+ headers: { Authorization: `Bearer ${key}`, "Content-Type": "application/json" },
110
+ body,
111
+ });
112
+ }
113
+
82
114
  // Fal's queue API: POST /<model> submits, GET /<model>/requests/<id>[/status]
83
115
  // polls, PUT /<model>/requests/<id>/cancel cancels.
84
116
  const isCancel = request.method === "PUT" && route.rest.endsWith("/cancel");
@@ -102,7 +134,7 @@ export function createChaiHandler(options: ChaiHandlerOptions = {}): ChaiHandler
102
134
  /** Finds the provider segment anywhere in the path, so the route can be mounted at any prefix. */
103
135
  function parseRoute(pathname: string): { provider: ChaiProviderId; rest: string } | null {
104
136
  const parts = pathname.split("/").filter(Boolean);
105
- const i = parts.findIndex((p) => p === "fal" || p === "openrouter");
137
+ const i = parts.findIndex((p) => p === "fal" || p === "openrouter" || p === "vercel");
106
138
  if (i === -1 || i === parts.length - 1) return null;
107
139
  return { provider: parts[i] as ChaiProviderId, rest: parts.slice(i + 1).join("/") };
108
140
  }
@@ -116,11 +148,11 @@ async function forward(fetchImpl: typeof fetch, provider: ChaiProviderId, url: s
116
148
  headers: { "Content-Type": upstream.headers.get("content-type") ?? "application/json" },
117
149
  });
118
150
  } catch (err) {
119
- return errorResponse(provider, 502, `Couldn't reach ${provider === "fal" ? "Fal.ai" : "OpenRouter"}: ${err instanceof Error ? err.message : String(err)}`);
151
+ return errorResponse(provider, 502, `Couldn't reach ${PROVIDER_LABEL[provider]}: ${err instanceof Error ? err.message : String(err)}`);
120
152
  }
121
153
  }
122
154
 
123
- /** Errors in the shape each engine already reads: Fal's `{ error }`, OpenRouter's `{ error: { message } }`. */
155
+ /** Errors in the shape each engine already reads: Fal's `{ error }`, OpenRouter's and Vercel's `{ error: { message } }`. */
124
156
  function errorResponse(provider: ChaiProviderId, status: number, message: string): Response {
125
157
  const body = provider === "fal" ? { error: message } : { error: { message } };
126
158
  return Response.json(body, { status });