@lunora/ai 1.0.0-alpha.13 → 1.0.0-alpha.131

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.
Files changed (34) hide show
  1. package/README.md +22 -9
  2. package/dist/create.d.mts +109 -0
  3. package/dist/create.d.ts +109 -0
  4. package/dist/create.mjs +1 -0
  5. package/dist/index.d.mts +3 -30
  6. package/dist/index.d.ts +3 -30
  7. package/dist/index.mjs +1 -3
  8. package/dist/packem_shared/AI_DEFAULT_EMBEDDING_MODEL_ENV-IwcHZiKj.mjs +1 -0
  9. package/dist/packem_shared/DEFAULT_MODEL_PRICES-C5oaHRiN.mjs +1 -0
  10. package/dist/packem_shared/VECTORIZE_CAPABILITIES-l9ceRO5j.mjs +1 -0
  11. package/dist/packem_shared/batchReranker-Bc38FBLH.mjs +1 -0
  12. package/dist/packem_shared/bm25-9q0Avwi-.mjs +1 -0
  13. package/dist/packem_shared/bm25LexicalStore-DMUzAL0O.mjs +1 -0
  14. package/dist/packem_shared/concurrent-C6nqBv41.mjs +1 -0
  15. package/dist/packem_shared/contentHash-BIn6ECP8.mjs +1 -0
  16. package/dist/packem_shared/createAi-DHYK5EHD.mjs +1 -0
  17. package/dist/packem_shared/defineRag-Dq2bA1Xl.mjs +8 -0
  18. package/dist/packem_shared/defineRagSource-Q3f3niU8.mjs +1 -0
  19. package/dist/packem_shared/fixedWindowChunks-C461ahRE.mjs +1 -0
  20. package/dist/packem_shared/hybridRank-B4skyCLx.mjs +1 -0
  21. package/dist/packem_shared/markdownChunker-iW8V4klY.mjs +5 -0
  22. package/dist/packem_shared/matchesMetadataFilter-BbIOyA5g.mjs +1 -0
  23. package/dist/packem_shared/ragSyncTriggers-D933fCwt.mjs +1 -0
  24. package/dist/packem_shared/sql-D5aqEMCY.mjs +1 -0
  25. package/dist/packem_shared/sqlLexicalStore-4C_cIwef.mjs +1 -0
  26. package/dist/packem_shared/sqliteVectorStore-f24ySqtJ.mjs +1 -0
  27. package/dist/packem_shared/types.d-KCPinSfY.d.mts +614 -0
  28. package/dist/packem_shared/types.d-KCPinSfY.d.ts +614 -0
  29. package/dist/packem_shared/usage-DQdygGEV.mjs +1 -0
  30. package/dist/rag/index.d.mts +1381 -0
  31. package/dist/rag/index.d.ts +1381 -0
  32. package/dist/rag/index.mjs +1 -0
  33. package/package.json +19 -16
  34. package/dist/packem_shared/createAi-CYxQuvEL.mjs +0 -55
@@ -0,0 +1,614 @@
1
+ import { EmbeddingModel, LanguageModel } from 'ai';
2
+ /**
3
+ * AI Gateway's hard limit on `cf-aig-metadata` keys. Exceeding it makes the
4
+ * gateway reject the metadata object entirely, so the builder trims to this
5
+ * rather than sending something that will be thrown away.
6
+ */
7
+ declare const AI_GATEWAY_METADATA_MAX_KEYS = 5;
8
+ /**
9
+ * Project {@link AiGatewayMetadata} to a plain string-map of only its defined,
10
+ * non-empty fields, or `undefined` when nothing is set. This is the shared source
11
+ * of truth behind both gateway-correlation forms: the `cf-aig-metadata` HTTP
12
+ * header (bring-your-own providers) and the Workers AI binding's native
13
+ * `gateway.metadata` option — both encode the same `{ functionPath, traceId }`.
14
+ * @experimental
15
+ */
16
+ declare const buildAiGatewayMetadataFields: (metadata: AiGatewayMetadata | undefined) => Record<string, string> | undefined;
17
+ /**
18
+ * Which surface resolved the gateway. The two paths handle the auth token
19
+ * differently: a bring-your-own AI SDK provider sends it as the
20
+ * `cf-aig-authorization` header (`ResolvedAiGateway.headers`), but the Workers AI
21
+ * **binding** routes through the gateway using the account's own credentials and
22
+ * its native `gateway` option (Cloudflare `GatewayOptions`) has **no** field to
23
+ * carry an authorization token — so a token set for the binding path cannot be
24
+ * delivered and is warned about instead of silently dropped.
25
+ * @experimental
26
+ */
27
+ type AiGatewayConsumer = "byo-provider" | "workers-ai-binding";
28
+ /**
29
+ * Correlation metadata folded into the `cf-aig-metadata` request header so a
30
+ * gateway log entry can be tied back to the Lunora function + trace that made
31
+ * the call. Every field is optional — only defined ones are sent.
32
+ * @experimental
33
+ */
34
+ interface AiGatewayMetadata {
35
+ /** The Lunora function path that issued the model call (e.g. `messages:send`). */
36
+ functionPath?: string;
37
+ /**
38
+ * App-supplied correlation tags — what the app slices its own AI spend by:
39
+ * feature, plan, tenant, a hashed user id. AI Gateway's cost filtering can
40
+ * only group by keys that were sent WITH the call, so a tag not set here is
41
+ * a question that cannot be asked later.
42
+ *
43
+ * Merged under the built-in fields, which win on a key collision, and
44
+ * trimmed to AI Gateway's {@link AI_GATEWAY_METADATA_MAX_KEYS}-key limit —
45
+ * over it, the gateway rejects the metadata outright and the correlation is
46
+ * lost rather than truncated.
47
+ *
48
+ * Values should stay low-cardinality for the same reason a route label
49
+ * does: a raw user id per call makes every call its own group. Hash it.
50
+ */
51
+ tags?: Record<string, string>;
52
+ /** The 32-hex trace id the call belongs to, so the gateway log joins the trace. */
53
+ traceId?: string;
54
+ }
55
+ /**
56
+ * The resolved AI Gateway coordinates.
57
+ *
58
+ * `gatewayId`/`accountId` drive the Workers AI provider's native `gateway` option
59
+ * (Cloudflare routes the binding call internally). `baseURL` + `headers` are for
60
+ * bring-your-own AI SDK providers (`@ai-sdk/openai`, …): append the provider
61
+ * slug to `baseURL` and spread `headers` into the provider config, e.g.
62
+ *
63
+ * ```ts
64
+ * const gw = resolveAiGateway(env);
65
+ * const openai = createOpenAI(gw ? { baseURL: `${gw.baseURL}/openai`, headers: gw.headers } : {});
66
+ * ```
67
+ * @experimental
68
+ */
69
+ interface ResolvedAiGateway {
70
+ /** The Cloudflare account id owning the gateway. */
71
+ accountId: string;
72
+ /**
73
+ * The universal gateway base URL:
74
+ * `https://gateway.ai.cloudflare.com/v1/{account}/{gateway}`. Append the
75
+ * provider slug (`/openai`, `/anthropic`, `/workers-ai`, …) per provider.
76
+ */
77
+ baseURL: string;
78
+ /** The gateway id (slug). */
79
+ gatewayId: string;
80
+ /**
81
+ * Request headers for a gateway-routed call: `cf-aig-authorization` when the
82
+ * gateway is authenticated, and `cf-aig-metadata` when correlation metadata
83
+ * was supplied. Empty object when neither applies.
84
+ */
85
+ headers: Record<string, string>;
86
+ }
87
+ /**
88
+ * Env var carrying the default Workers AI **language** model id, used by
89
+ * `ctx.ai.model()` when it is called with no argument.
90
+ *
91
+ * This exists because `env` is the only configuration seam `ctx.ai` has. The
92
+ * generated shard constructs the facade as `createAi({ binding, env, metadata })`
93
+ * with those three fields fixed, so `LunoraAiOptions.defaultModel` — the field
94
+ * `model()` reads — was unreachable from an app: `ctx.ai.model()` with no
95
+ * argument threw, always. Reading the default off `env` puts it back in the
96
+ * app's hands (a wrangler `vars` entry or `.dev.vars` line) without a new
97
+ * constructor argument codegen would have to learn to emit.
98
+ *
99
+ * An explicit `defaultModel` option still wins — the env var is
100
+ * the fallback, exactly as with the gateway vars above.
101
+ */
102
+ declare const AI_DEFAULT_MODEL_ENV = "LUNORA_AI_DEFAULT_MODEL";
103
+ /**
104
+ * Env var carrying the default Workers AI **embedding** model id, used by
105
+ * `ctx.ai.embeddingModel()` with no argument — and so by `defineRag`, whose
106
+ * `embeddingModel` is documented as optional and resolves through exactly that
107
+ * call. Separate from {@link AI_DEFAULT_MODEL_ENV} because a language-model id
108
+ * and an embedding-model id are never interchangeable.
109
+ */
110
+ declare const AI_DEFAULT_EMBEDDING_MODEL_ENV = "LUNORA_AI_DEFAULT_EMBEDDING_MODEL";
111
+ /** Env var naming the Cloudflare account that owns the gateway. */
112
+ declare const AI_GATEWAY_ACCOUNT_ID_ENV = "LUNORA_AI_GATEWAY_ACCOUNT_ID";
113
+ /** Env var naming the AI Gateway id (the gateway's slug). */
114
+ declare const AI_GATEWAY_ID_ENV = "LUNORA_AI_GATEWAY_ID";
115
+ /**
116
+ * Env var carrying the gateway's authentication token (only for authenticated
117
+ * gateways).
118
+ *
119
+ * **Workers AI binding limitation.** This token is delivered only on the
120
+ * bring-your-own AI SDK provider path, as the `cf-aig-authorization` header (see
121
+ * {@link ResolvedAiGateway.headers}). The Workers AI **binding** (`ctx.ai` over
122
+ * `env.AI`) routes through the gateway with the account's own credentials and its
123
+ * native `gateway` option (Cloudflare's `GatewayOptions`: `id` / `cacheKey` /
124
+ * `metadata` / …) has no authorization field — so an *authenticated* gateway that
125
+ * requires a token cannot be reached on the binding path. Set this only for a BYO
126
+ * provider; for Workers AI, leave the gateway unauthenticated (or front it with a
127
+ * BYO provider). {@link resolveAiGateway} warns once per isolate if the token is
128
+ * set on the binding path.
129
+ */
130
+ declare const AI_GATEWAY_TOKEN_ENV = "LUNORA_AI_GATEWAY_TOKEN";
131
+ /**
132
+ * Env var carrying deployment-scoped AI Gateway tags as a flat JSON object of
133
+ * string values, e.g. `{"app":"checkout","env":"prod"}`.
134
+ *
135
+ * Deployment-scoped because that is the dimension you cannot recover in code:
136
+ * AI Gateway can only filter cost by keys that were sent WITH the call, and
137
+ * "which environment / which app spent this" is fixed at deploy time. Per-call
138
+ * tags (feature, hashed user) go through {@link AiGatewayMetadata.tags}
139
+ * instead. A malformed value is ignored with a warning rather than failing the
140
+ * call — telemetry configuration must not take inference down.
141
+ */
142
+ declare const AI_GATEWAY_TAGS_ENV = "LUNORA_AI_GATEWAY_TAGS";
143
+ /**
144
+ * Env var carrying the base URL of a self-hosted OpenAI-compatible proxy
145
+ * (LiteLLM, OpenRouter, your own), e.g. `https://ai-proxy.internal/v1`.
146
+ *
147
+ * When set, `ctx.ai.model("<provider>/<model>")` sends the slug unchanged as the
148
+ * `model` of an OpenAI chat-completions request to this URL instead of routing
149
+ * it through Cloudflare AI Gateway — which needs no `AI` binding, so it is how
150
+ * `ctx.ai` works on hosts without Workers AI (celld). `@cf/…` ids still need
151
+ * the binding.
152
+ */
153
+ declare const AI_PROXY_URL_ENV = "LUNORA_AI_PROXY_URL";
154
+ /** Env var carrying the bearer token sent to {@link AI_PROXY_URL_ENV}, when the proxy requires one. */
155
+ declare const AI_PROXY_TOKEN_ENV = "LUNORA_AI_PROXY_TOKEN";
156
+ /**
157
+ * Parse {@link AI_GATEWAY_TAGS_ENV} into tag fields. Non-string values are
158
+ * dropped rather than coerced — a number silently becoming `"1"` is a worse
159
+ * outcome than the tag being absent and visibly so.
160
+ */
161
+ declare const readAiGatewayEnvTags: (env: Record<string, unknown>) => Record<string, string> | undefined;
162
+ /**
163
+ * Resolve the Cloudflare AI Gateway coordinates from the Worker `env`, or
164
+ * `undefined` when the gateway is not configured (both `LUNORA_AI_GATEWAY_ACCOUNT_ID`
165
+ * and `LUNORA_AI_GATEWAY_ID` must be present). Opt-in and backward-compatible:
166
+ * an unconfigured app gets `undefined` and every caller keeps its direct-provider
167
+ * behavior.
168
+ *
169
+ * Pass optional {@link AiGatewayMetadata} to fold a `cf-aig-metadata` correlation
170
+ * header into `headers` — only its defined fields are sent.
171
+ *
172
+ * `consumer` names the surface resolving the gateway (default `"byo-provider"`).
173
+ * When it is `"workers-ai-binding"` and an {@link AI_GATEWAY_TOKEN_ENV} token is
174
+ * configured, this warns once per isolate: the binding path cannot carry the
175
+ * token (see {@link AI_GATEWAY_TOKEN_ENV}), so it would otherwise be dropped with
176
+ * no diagnostic and every `ctx.ai.model(...)` call would fail against an
177
+ * authenticated gateway while the token var reads as "configured".
178
+ * @experimental
179
+ */
180
+ declare const resolveAiGateway: (env: Record<string, unknown>, metadata?: AiGatewayMetadata, consumer?: AiGatewayConsumer) => ResolvedAiGateway | undefined;
181
+ /**
182
+ * Structural projection of the Cloudflare Workers `AI` binding (`env.AI`).
183
+ * Declared locally so unit tests can pass a plain-object double and the real
184
+ * binding satisfies the same shape without importing `@cloudflare/workers-types`
185
+ * into the public surface. Mirrors the `run` method documented at
186
+ * https://developers.cloudflare.com/workers-ai/.
187
+ * @experimental
188
+ */
189
+ interface AiBindingLike {
190
+ run: (model: string, inputs: Record<string, unknown>, options?: Record<string, unknown>) => Promise<unknown>;
191
+ /**
192
+ * Markdown Conversion API (beta) — converts a document (PDF, Office file,
193
+ * HTML, image) to Markdown. Optional because runtimes and test doubles that
194
+ * predate it have no such method; `ctx.ai.toMarkdown` reports that instead
195
+ * of a bare `TypeError`. Mirrors `env.AI.toMarkdown(files, options)` at
196
+ * https://developers.cloudflare.com/workers-ai/features/markdown-conversion/.
197
+ */
198
+ toMarkdown?: (files: AiMarkdownDocument | AiMarkdownDocument[], options?: Record<string, unknown>) => Promise<AiMarkdownConversionResult | AiMarkdownConversionResult[]>;
199
+ /**
200
+ * Web Search API (beta). Optional because runtimes and test doubles that
201
+ * predate it have no such method; `ctx.ai.websearch` reports that instead of
202
+ * a bare `TypeError`.
203
+ */
204
+ websearch?: (input: Omit<AiWebSearchOptions, "gatewayId"> & {
205
+ gatewayId: string;
206
+ query: string;
207
+ }) => Promise<Response>;
208
+ }
209
+ /**
210
+ * One document handed to `ctx.ai.toMarkdown(...)`: the Workers AI
211
+ * `MarkdownDocument` shape (a name plus a Blob carrying the bytes).
212
+ * @experimental
213
+ */
214
+ interface AiMarkdownDocument {
215
+ blob: Blob;
216
+ name: string;
217
+ }
218
+ /**
219
+ * One Workers AI `ConversionResult`. `data` and `tokens` are present only when
220
+ * `format` is `"markdown"` (or `"text"`); an `"error"` result carries `error`
221
+ * instead. `pages` is not in the current binding contract — carried here so a
222
+ * binding that starts reporting it passes through unchanged.
223
+ * @experimental
224
+ */
225
+ interface AiMarkdownConversionResult {
226
+ data?: string;
227
+ error?: string;
228
+ format: "error" | "markdown" | "text";
229
+ id?: string;
230
+ /** The detected document mime type (spelled `mimetype` by the binding). */
231
+ mimetype?: string;
232
+ name?: string;
233
+ /** Page count, when the binding reports it. */
234
+ pages?: number;
235
+ tokens?: number;
236
+ }
237
+ /**
238
+ * What `ctx.ai.toMarkdown(...)` accepts: a `Blob`, raw bytes, or a `URL` that
239
+ * is fetched first.
240
+ * @experimental
241
+ */
242
+ type AiToMarkdownInput = ArrayBuffer | Blob | Uint8Array | URL;
243
+ /**
244
+ * Options for `ctx.ai.toMarkdown(...)`.
245
+ * @experimental
246
+ */
247
+ interface AiToMarkdownOptions {
248
+ /** Conversion options forwarded verbatim to the binding's `toMarkdown(files, options)`. */
249
+ conversionOptions?: Record<string, unknown>;
250
+ /**
251
+ * Byte cap for a `URL`/raw-bytes input, guarding against an accidental huge
252
+ * download. Defaults to 20 MiB. A `Blob` is checked against the same cap.
253
+ */
254
+ maxBytes?: number;
255
+ /** MIME type for a raw `ArrayBuffer`/`Uint8Array` input (a `Blob`/`URL` carries its own). */
256
+ mimeType?: string;
257
+ /** Document name sent to the binding; defaults to the URL's last path segment, else `"document"`. */
258
+ name?: string;
259
+ }
260
+ /**
261
+ * What `ctx.ai.toMarkdown(...)` resolves to — the first (and only) converted
262
+ * document, normalised across the binding's single/array return.
263
+ * @experimental
264
+ */
265
+ interface AiToMarkdownResult {
266
+ markdown: string;
267
+ /** The detected mime type of the source document, when the binding reports it. */
268
+ mimeType?: string;
269
+ /** Page count, when the binding reports it. */
270
+ pages?: number;
271
+ /** The binding's estimated token count for the converted Markdown, when reported. */
272
+ tokens?: number;
273
+ }
274
+ /**
275
+ * One generated image: base64 bytes (`base64`), a URL, or the provider's media
276
+ * type. Which fields are present depends on the provider.
277
+ * @experimental
278
+ */
279
+ interface AiImage {
280
+ base64?: string;
281
+ mimeType?: string;
282
+ url?: string;
283
+ }
284
+ /**
285
+ * Input for `ctx.ai.images.generate(...)`.
286
+ * @experimental
287
+ */
288
+ interface AiImageGenerateInput {
289
+ /** How many images to generate. Defaults to 1. */
290
+ count?: number;
291
+ /**
292
+ * A model id: a Workers AI image model (`@cf/black-forest-labs/flux-1-schnell`),
293
+ * or an `"openai/<model>"` / `"google/<model>"` slug routed through the AI
294
+ * Gateway. Any other slug throws `AI_UNKNOWN_IMAGE_MODEL`.
295
+ */
296
+ model: string;
297
+ prompt: string;
298
+ /**
299
+ * Seed for reproducible generation, for providers that support it. A Workers AI
300
+ * `count` batch uses `seed + i` for image `i`, so the images differ; provider
301
+ * models receive the seed as given.
302
+ */
303
+ seed?: number;
304
+ /** Image size as `"<width>x<height>"`, e.g. `"1024x1024"`. */
305
+ size?: `${number}x${number}`;
306
+ }
307
+ /**
308
+ * What `ctx.ai.images.generate(...)` resolves to.
309
+ * @experimental
310
+ */
311
+ interface AiImageGenerateResult {
312
+ images: AiImage[];
313
+ /** The model id the caller asked for (the unmodified `input.model`). */
314
+ model: string;
315
+ }
316
+ /**
317
+ * A Workers AI provider instance — the value returned by `createWorkersAI(...)`.
318
+ * Calling it with a model id yields an AI SDK {@link LanguageModel}; the
319
+ * optional `textEmbeddingModel` factory yields an {@link EmbeddingModel}.
320
+ * Typed structurally so `@lunora/ai` neither re-declares the provider's full
321
+ * surface nor hard-pins its exact type across minor releases. It is the shape of
322
+ * the lazy delegate `ctx.ai.workersai` exposes, so only the callable factory and
323
+ * `textEmbeddingModel` are part of it.
324
+ * @experimental
325
+ */
326
+ interface WorkersAiProviderLike {
327
+ (modelId: string, settings?: Record<string, unknown>): LanguageModel;
328
+ textEmbeddingModel?: (modelId: string) => EmbeddingModel;
329
+ }
330
+ /**
331
+ * AI Gateway options forwarded to `createWorkersAI`. Lets inference route
332
+ * through a Cloudflare AI Gateway for caching, rate-limiting, and observability.
333
+ * @experimental
334
+ */
335
+ interface AiGatewayOptions {
336
+ [key: string]: unknown;
337
+ id: string;
338
+ /**
339
+ * Custom correlation metadata surfaced in the AI Gateway logs (Cloudflare's
340
+ * native `gateway.metadata`). `@lunora/ai` folds `{ functionPath, traceId }`
341
+ * here from {@link LunoraAiOptions.metadata} when a gateway is configured.
342
+ */
343
+ metadata?: Record<string, string>;
344
+ }
345
+ /**
346
+ * Options for the raw `ctx.ai.run(...)` passthrough — the Workers AI binding's
347
+ * third argument. Unlisted keys are forwarded to the binding unchanged.
348
+ * @experimental
349
+ */
350
+ interface AiRunOptions {
351
+ [key: string]: unknown;
352
+ /** Route this call through a Cloudflare AI Gateway. Defaults to the gateway `createAi` resolved. */
353
+ gateway?: AiGatewayOptions;
354
+ /**
355
+ * Fail at once instead of waiting in the Workers AI capacity queue when no
356
+ * capacity is free. The rejection surfaces as a `LunoraError` with code
357
+ * `RATE_LIMITED` (Workers AI error `3040`, HTTP 429).
358
+ */
359
+ rejectIfBusy?: boolean;
360
+ }
361
+ /**
362
+ * Per-call settings for `ctx.ai.model(...)`. Applied to Workers AI model ids
363
+ * (`@cf/…`) only; a gateway slug or a bring-your-own model ignores them.
364
+ * @experimental
365
+ */
366
+ interface AiModelOptions {
367
+ /**
368
+ * Fail at once instead of waiting in the Workers AI capacity queue. The
369
+ * provider reports the rejection as an AI SDK `APICallError` with
370
+ * `statusCode: 429`, which the AI SDK retries unless the call sets
371
+ * `maxRetries: 0`.
372
+ */
373
+ rejectIfBusy?: boolean;
374
+ }
375
+ /**
376
+ * Structural slice of the span handle `ctx.trace` hands its body — enough to
377
+ * attach a model call's usage once it is known. Declared here rather than
378
+ * imported so `@lunora/ai` takes no dependency on `@lunora/server`; the real
379
+ * handle is assignable to it.
380
+ * @experimental
381
+ */
382
+ interface AiSpan {
383
+ setAttribute: (key: string, value: unknown) => void;
384
+ setAttributes: (fields: Record<string, unknown>) => void;
385
+ }
386
+ /**
387
+ * Structural slice of `ctx.trace` (the server `LunoraTracer`): runs `function_`
388
+ * inside a named span and hands it the span's {@link AiSpan}.
389
+ * @experimental
390
+ */
391
+ type AiTracer = <T>(name: string, function_: (trace: AiTracer, span: AiSpan) => Promise<T> | T, attributes?: Record<string, unknown>) => Promise<T>;
392
+ /**
393
+ * Structural slice of `ctx.metrics` (the server `LunoraMetrics`) — only the
394
+ * counter, which is what usage accounting needs.
395
+ * @experimental
396
+ */
397
+ interface AiMetrics {
398
+ count: (name: string, value?: number, attributes?: Record<string, unknown>) => void;
399
+ }
400
+ /**
401
+ * Where `ctx.ai` reports model usage. The generated `ctx.ai` passes the
402
+ * function's own `ctx.trace` / `ctx.metrics`, so every call made through
403
+ * `ctx.ai.model(...)` gets an `ai.generate` / `ai.stream` span and
404
+ * `gen_ai.usage.input_tokens` / `gen_ai.usage.output_tokens` /
405
+ * `gen_ai.usage.cost` counters attributed to that function.
406
+ * @experimental
407
+ */
408
+ interface AiTelemetry {
409
+ metrics?: AiMetrics;
410
+ trace?: AiTracer;
411
+ }
412
+ /**
413
+ * `LunoraAiOptions` is part of the experimental `@lunora/ai` API and may change without a major version bump.
414
+ * @experimental
415
+ */
416
+ interface LunoraAiOptions {
417
+ /**
418
+ * The Workers `AI` binding (`env.AI`). Required for the zero-config Workers
419
+ * AI default and for the raw `ai.run(...)` passthrough. May be omitted when
420
+ * a pre-built `provider` is supplied (e.g. in tests or a custom setup).
421
+ */
422
+ binding?: AiBindingLike;
423
+ /**
424
+ * Default Workers AI **embedding** model id used by `embeddingModel()` when no
425
+ * explicit model is passed (e.g. `@cf/baai/bge-base-en-v1.5`). Kept separate
426
+ * from `defaultModel` because a language-model id and an embedding-model
427
+ * id belong to different Workers AI families and are never interchangeable —
428
+ * reusing the language-model default here would defer a wrong-family error to
429
+ * inference time. Has no effect on bring-your-own providers.
430
+ *
431
+ * Falls back to `LUNORA_AI_DEFAULT_EMBEDDING_MODEL` in {@link LunoraAiOptions.env}
432
+ * — which is how an app sets it, since the generated `ctx.ai` is constructed
433
+ * with a fixed `{ binding, env, metadata }`.
434
+ */
435
+ defaultEmbeddingModel?: string;
436
+ /**
437
+ * Default Workers AI **language** model id used by `model()` when no explicit
438
+ * model is passed. For embeddings, set `defaultEmbeddingModel` instead.
439
+ * Has no effect on bring-your-own providers.
440
+ *
441
+ * Falls back to `LUNORA_AI_DEFAULT_MODEL` in {@link LunoraAiOptions.env} — the
442
+ * seam a Lunora app actually has (see `defaultEmbeddingModel`).
443
+ */
444
+ defaultModel?: string;
445
+ /**
446
+ * The Worker `env`, read for opt-in Cloudflare AI Gateway routing (the
447
+ * `LUNORA_AI_GATEWAY_*` vars, via `resolveAiGateway`) and for the default
448
+ * model ids (`LUNORA_AI_DEFAULT_MODEL` / `LUNORA_AI_DEFAULT_EMBEDDING_MODEL`).
449
+ * When it configures a gateway and no explicit {@link LunoraAiOptions.gateway}
450
+ * is given, the Workers AI provider is routed through that gateway so token +
451
+ * dollar-cost telemetry is computed on the app's behalf. Unset, or with no
452
+ * gateway vars, behavior is unchanged (calls go straight to Workers AI).
453
+ */
454
+ env?: Record<string, unknown>;
455
+ /**
456
+ * Route Workers AI inference through a Cloudflare AI Gateway. An explicit
457
+ * value wins over anything derived from {@link LunoraAiOptions.env}.
458
+ */
459
+ gateway?: AiGatewayOptions;
460
+ /**
461
+ * Correlation metadata folded into the active gateway call (the Workers AI
462
+ * binding's native `gateway.metadata`) so an AI Gateway log entry ties back
463
+ * to the Lunora function + trace that made it. Only applied when a gateway is
464
+ * actually configured (explicit or env-derived) and only its defined fields
465
+ * are sent — absent otherwise, so behavior is unchanged. The generated
466
+ * `ctx.ai` facade threads `{ functionPath, traceId }` here automatically.
467
+ */
468
+ metadata?: AiGatewayMetadata;
469
+ /**
470
+ * Pre-built Workers AI provider. When omitted, one is constructed from
471
+ * `binding` via `createWorkersAI`. Supplying it directly is the seam used by
472
+ * tests and advanced setups; it also lets callers configure the provider
473
+ * (e.g. `safePrompt`) before handing it to `@lunora/ai`.
474
+ */
475
+ provider?: WorkersAiProviderLike;
476
+ /**
477
+ * Record every language-model call resolved by `model()` — a span plus
478
+ * token and cost counters (see {@link AiTelemetry}). Omitted, models are
479
+ * returned unwrapped.
480
+ */
481
+ telemetry?: AiTelemetry;
482
+ }
483
+ /**
484
+ * A search provider the Web Search API brokers. Each one runs under Cloudflare's
485
+ * Zero Data Retention terms. A search bills a provider key stored on the gateway
486
+ * when there is one (the `default` alias unless `byokAlias` names another),
487
+ * otherwise AI Gateway credits at list price.
488
+ * @experimental
489
+ */
490
+ type AiWebSearchProvider = "ceramic" | "exa" | "linkup";
491
+ /**
492
+ * Options for `ctx.ai.websearch(...)`.
493
+ * @experimental
494
+ */
495
+ interface AiWebSearchOptions {
496
+ /** Alias of a provider key stored on the gateway to bill instead of the `default` alias. Without a stored key, gateway credits pay. */
497
+ byokAlias?: string;
498
+ /**
499
+ * The AI Gateway that brokers and bills the search. Defaults to the gateway
500
+ * `ctx.ai` routes inference through (`LUNORA_AI_GATEWAY_ID`), else the
501
+ * account's `default` gateway.
502
+ */
503
+ gatewayId?: string;
504
+ /** Maximum results, 1–10. Defaults to 10. */
505
+ limit?: number;
506
+ /** Defaults to `"ceramic"`. */
507
+ provider?: AiWebSearchProvider;
508
+ }
509
+ /**
510
+ * One result. Web search is discovery only: a result describes a page, and
511
+ * reading the page is a separate `fetch` of its `url`. The optional fields are
512
+ * present only when the provider returns them.
513
+ * @experimental
514
+ */
515
+ interface AiWebSearchItem {
516
+ description?: string;
517
+ faviconUrl?: string;
518
+ imageUrl?: string;
519
+ /** Naive (no timezone) ISO-8601 datetime, e.g. `"2025-11-30T04:39:48"`. */
520
+ lastModifiedDate?: string;
521
+ title: string;
522
+ url: string;
523
+ }
524
+ /**
525
+ * What `ctx.ai.websearch(...)` resolves to.
526
+ * @experimental
527
+ */
528
+ interface AiWebSearchResult {
529
+ items: AiWebSearchItem[];
530
+ metadata: {
531
+ latencyMs: number;
532
+ query: string;
533
+ requestId: string;
534
+ };
535
+ }
536
+ /**
537
+ * A model to run against. The AI SDK's {@link LanguageModel} already admits a
538
+ * bare `string`, so this alias covers both arms of the provider-agnostic seam:
539
+ * a string id is resolved by `ctx.ai.model` — a Workers AI id (`@cf/…`), a
540
+ * `"<provider>/<model>"` slug (`anthropic/claude-sonnet-5`, `openai/gpt-5`, …)
541
+ * routed through Cloudflare AI Gateway, or a gateway dynamic route
542
+ * (`dynamic/<route>`); a built model object is bring-your-own (`@ai-sdk/openai`,
543
+ * `@ai-sdk/anthropic`, `@ai-sdk/google`, OpenRouter, …).
544
+ * @experimental
545
+ */
546
+ type ModelInput = LanguageModel;
547
+ /**
548
+ * Likewise for embeddings: a Workers AI embedding model id (e.g.
549
+ * `@cf/baai/bge-base-en-v1.5`) or any AI SDK {@link EmbeddingModel}.
550
+ * @experimental
551
+ */
552
+ type EmbeddingModelInput = EmbeddingModel | string;
553
+ /**
554
+ * The `ctx.ai` surface. `model`/`embeddingModel` resolve a Workers AI model from
555
+ * a string (the default provider) and pass any non-string model straight through,
556
+ * so both accept Workers AI and bring-your-own providers. Feed the resolved model
557
+ * to the AI SDK functions re-exported from `@lunora/ai` (`generateText`,
558
+ * `streamText`, `generateObject`, `embed`, …); `run` is the raw binding escape
559
+ * hatch, and `workersai` is the lazy provider delegate for direct model access.
560
+ * @experimental
561
+ */
562
+ interface LunoraAi {
563
+ /** Resolve an {@link EmbeddingModel}: a string → Workers AI, an object → passthrough. */
564
+ embeddingModel: (model?: EmbeddingModelInput) => EmbeddingModel;
565
+ /**
566
+ * Generate images. A Workers AI image model id (`@cf/…`) runs through the
567
+ * binding (and the AI Gateway when configured); an `"openai/<model>"` or
568
+ * `"google/<model>"` slug routes to that provider's image endpoint through
569
+ * the AI Gateway. Any other slug throws `AI_UNKNOWN_IMAGE_MODEL`; a
570
+ * Workers AI id without a binding throws the same directed error `run` does.
571
+ */
572
+ images: {
573
+ generate: (input: AiImageGenerateInput) => Promise<AiImageGenerateResult>;
574
+ };
575
+ /**
576
+ * Resolve a {@link LanguageModel}: a `@cf/…` id → Workers AI, a
577
+ * `"<provider>/<model>"` slug or `dynamic/<route>` → Cloudflare AI Gateway
578
+ * over the same binding (Unified Billing or the gateway's stored keys — no
579
+ * provider key in the app) or, with `LUNORA_AI_PROXY_URL` set, to that
580
+ * OpenAI-compatible proxy; an object → passthrough.
581
+ */
582
+ model: (model?: ModelInput, options?: AiModelOptions) => LanguageModel;
583
+ /**
584
+ * Raw Workers AI binding passthrough (void-style `ai.run`). Bypasses the AI
585
+ * SDK entirely — useful for Workers-AI-only model families (image, ASR,
586
+ * translation) not surfaced through the provider. Throws if no binding was
587
+ * supplied.
588
+ */
589
+ run: (model: string, inputs: Record<string, unknown>, options?: AiRunOptions) => Promise<unknown>;
590
+ /**
591
+ * Convert a document (PDF, Office file, HTML, image) to Markdown through the
592
+ * binding's Markdown Conversion API (beta). Accepts a `Blob`, raw bytes or a
593
+ * `URL` (fetched first). Throws if no binding was supplied, or if the
594
+ * runtime's binding has no `toMarkdown()`.
595
+ */
596
+ toMarkdown: (input: AiToMarkdownInput, options?: AiToMarkdownOptions) => Promise<AiToMarkdownResult>;
597
+ /**
598
+ * Search the web through the binding's Web Search API (beta) to ground a
599
+ * response in live information. Billed to a stored provider key or AI Gateway
600
+ * credits (see {@link AiWebSearchProvider}). Throws if no
601
+ * binding was supplied, or if the runtime's binding has no `websearch()`.
602
+ */
603
+ websearch: (query: string, options?: AiWebSearchOptions) => Promise<AiWebSearchResult>;
604
+ /**
605
+ * A lazy delegate to the Workers AI provider: call it as
606
+ * `ai.workersai("@cf/…")` for a raw language model, or use
607
+ * `ai.workersai.textEmbeddingModel("@cf/…")` for embeddings. The provider
608
+ * module loads on first use, so only the callable factory and
609
+ * `textEmbeddingModel` are exposed — the rest of the `createWorkersAI`
610
+ * factories (image, transcription, speech, …) are not part of this surface.
611
+ */
612
+ workersai: WorkersAiProviderLike;
613
+ }
614
+ export { AiBindingLike as A, AiTelemetry as B, AiTracer as C, AiWebSearchOptions as D, EmbeddingModelInput as E, AiWebSearchResult as F, buildAiGatewayMetadataFields as G, readAiGatewayEnvTags as H, resolveAiGateway as I, LunoraAi as L, ModelInput as M, ResolvedAiGateway as R, WorkersAiProviderLike as W, LunoraAiOptions as a, AiImage as b, AiImageGenerateInput as c, AiImageGenerateResult as d, AiToMarkdownInput as e, AiToMarkdownOptions as f, AiToMarkdownResult as g, AiWebSearchItem as h, AiWebSearchProvider as i, AI_DEFAULT_EMBEDDING_MODEL_ENV as j, AI_DEFAULT_MODEL_ENV as k, AI_GATEWAY_ACCOUNT_ID_ENV as l, AI_GATEWAY_ID_ENV as m, AI_GATEWAY_METADATA_MAX_KEYS as n, AI_GATEWAY_TAGS_ENV as o, AI_GATEWAY_TOKEN_ENV as p, AI_PROXY_TOKEN_ENV as q, AI_PROXY_URL_ENV as r, AiGatewayMetadata as s, AiGatewayOptions as t, AiMarkdownConversionResult as u, AiMarkdownDocument as v, AiMetrics as w, AiModelOptions as x, AiRunOptions as y, AiSpan as z };