@lunora/ai 1.0.0-alpha.21 → 1.0.0-alpha.23

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/dist/index.d.mts CHANGED
@@ -1,5 +1,5 @@
1
- import { L as LunoraAiOptions, a as LunoraAi } from "./packem_shared/types.d-BXCiRv1x.mjs";
2
- export type { A as AiBindingLike, b as AiGatewayOptions, E as EmbeddingModelInput, M as ModelInput, W as WorkersAiProviderLike } from "./packem_shared/types.d-BXCiRv1x.mjs";
1
+ import { L as LunoraAiOptions, a as LunoraAi } from "./packem_shared/types.d-DGmh5Hq4.mjs";
2
+ export type { A as AiBindingLike, b as AiGatewayOptions, E as EmbeddingModelInput, M as ModelInput, W as WorkersAiProviderLike } from "./packem_shared/types.d-DGmh5Hq4.mjs";
3
3
  export { type EmbeddingModel, type LanguageModel, embed, embedMany, generateObject, generateText, hasToolCall, jsonSchema, streamObject, streamText, tool } from 'ai';
4
4
  export { createWorkersAI } from 'workers-ai-provider';
5
5
  /**
@@ -25,4 +25,66 @@ export { createWorkersAI } from 'workers-ai-provider';
25
25
  * @experimental
26
26
  */
27
27
  declare const createAi: (options: LunoraAiOptions) => LunoraAi;
28
- export { type LunoraAi, type LunoraAiOptions, createAi };
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
+ /** The 32-hex trace id the call belongs to, so the gateway log joins the trace. */
38
+ traceId?: string;
39
+ }
40
+ /**
41
+ * The resolved AI Gateway coordinates.
42
+ *
43
+ * `gatewayId`/`accountId` drive the Workers AI provider's native `gateway` option
44
+ * (Cloudflare routes the binding call internally). `baseURL` + `headers` are for
45
+ * bring-your-own AI SDK providers (`@ai-sdk/openai`, …): append the provider
46
+ * slug to `baseURL` and spread `headers` into the provider config, e.g.
47
+ *
48
+ * ```ts
49
+ * const gw = resolveAiGateway(env);
50
+ * const openai = createOpenAI(gw ? { baseURL: `${gw.baseURL}/openai`, headers: gw.headers } : {});
51
+ * ```
52
+ * @experimental
53
+ */
54
+ interface ResolvedAiGateway {
55
+ /** The Cloudflare account id owning the gateway. */
56
+ accountId: string;
57
+ /**
58
+ * The universal gateway base URL:
59
+ * `https://gateway.ai.cloudflare.com/v1/{account}/{gateway}`. Append the
60
+ * provider slug (`/openai`, `/anthropic`, `/workers-ai`, …) per provider.
61
+ */
62
+ baseURL: string;
63
+ /** The gateway id (slug). */
64
+ gatewayId: string;
65
+ /**
66
+ * Request headers for a gateway-routed call: `cf-aig-authorization` when the
67
+ * gateway is authenticated, and `cf-aig-metadata` when correlation metadata
68
+ * was supplied. Empty object when neither applies.
69
+ */
70
+ headers: Record<string, string>;
71
+ }
72
+ /** Env var naming the Cloudflare account that owns the gateway. */
73
+ declare const AI_GATEWAY_ACCOUNT_ID_ENV = "LUNORA_AI_GATEWAY_ACCOUNT_ID";
74
+ /** Env var naming the AI Gateway id (the gateway's slug). */
75
+ declare const AI_GATEWAY_ID_ENV = "LUNORA_AI_GATEWAY_ID";
76
+ /** Env var carrying the gateway's authentication token (only for authenticated gateways). */
77
+ declare const AI_GATEWAY_TOKEN_ENV = "LUNORA_AI_GATEWAY_TOKEN";
78
+ /**
79
+ * Resolve the Cloudflare AI Gateway coordinates from the Worker `env`, or
80
+ * `undefined` when the gateway is not configured (both `LUNORA_AI_GATEWAY_ACCOUNT_ID`
81
+ * and `LUNORA_AI_GATEWAY_ID` must be present). Opt-in and backward-compatible:
82
+ * an unconfigured app gets `undefined` and every caller keeps its direct-provider
83
+ * behavior.
84
+ *
85
+ * Pass optional {@link AiGatewayMetadata} to fold a `cf-aig-metadata` correlation
86
+ * header into `headers` — only its defined fields are sent.
87
+ * @experimental
88
+ */
89
+ declare const resolveAiGateway: (env: Record<string, unknown>, metadata?: AiGatewayMetadata) => ResolvedAiGateway | undefined;
90
+ export { AI_GATEWAY_ACCOUNT_ID_ENV, AI_GATEWAY_ID_ENV, AI_GATEWAY_TOKEN_ENV, type AiGatewayMetadata, type LunoraAi, type LunoraAiOptions, type ResolvedAiGateway, createAi, resolveAiGateway };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { L as LunoraAiOptions, a as LunoraAi } from "./packem_shared/types.d-BXCiRv1x.js";
2
- export type { A as AiBindingLike, b as AiGatewayOptions, E as EmbeddingModelInput, M as ModelInput, W as WorkersAiProviderLike } from "./packem_shared/types.d-BXCiRv1x.js";
1
+ import { L as LunoraAiOptions, a as LunoraAi } from "./packem_shared/types.d-DGmh5Hq4.js";
2
+ export type { A as AiBindingLike, b as AiGatewayOptions, E as EmbeddingModelInput, M as ModelInput, W as WorkersAiProviderLike } from "./packem_shared/types.d-DGmh5Hq4.js";
3
3
  export { type EmbeddingModel, type LanguageModel, embed, embedMany, generateObject, generateText, hasToolCall, jsonSchema, streamObject, streamText, tool } from 'ai';
4
4
  export { createWorkersAI } from 'workers-ai-provider';
5
5
  /**
@@ -25,4 +25,66 @@ export { createWorkersAI } from 'workers-ai-provider';
25
25
  * @experimental
26
26
  */
27
27
  declare const createAi: (options: LunoraAiOptions) => LunoraAi;
28
- export { type LunoraAi, type LunoraAiOptions, createAi };
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
+ /** The 32-hex trace id the call belongs to, so the gateway log joins the trace. */
38
+ traceId?: string;
39
+ }
40
+ /**
41
+ * The resolved AI Gateway coordinates.
42
+ *
43
+ * `gatewayId`/`accountId` drive the Workers AI provider's native `gateway` option
44
+ * (Cloudflare routes the binding call internally). `baseURL` + `headers` are for
45
+ * bring-your-own AI SDK providers (`@ai-sdk/openai`, …): append the provider
46
+ * slug to `baseURL` and spread `headers` into the provider config, e.g.
47
+ *
48
+ * ```ts
49
+ * const gw = resolveAiGateway(env);
50
+ * const openai = createOpenAI(gw ? { baseURL: `${gw.baseURL}/openai`, headers: gw.headers } : {});
51
+ * ```
52
+ * @experimental
53
+ */
54
+ interface ResolvedAiGateway {
55
+ /** The Cloudflare account id owning the gateway. */
56
+ accountId: string;
57
+ /**
58
+ * The universal gateway base URL:
59
+ * `https://gateway.ai.cloudflare.com/v1/{account}/{gateway}`. Append the
60
+ * provider slug (`/openai`, `/anthropic`, `/workers-ai`, …) per provider.
61
+ */
62
+ baseURL: string;
63
+ /** The gateway id (slug). */
64
+ gatewayId: string;
65
+ /**
66
+ * Request headers for a gateway-routed call: `cf-aig-authorization` when the
67
+ * gateway is authenticated, and `cf-aig-metadata` when correlation metadata
68
+ * was supplied. Empty object when neither applies.
69
+ */
70
+ headers: Record<string, string>;
71
+ }
72
+ /** Env var naming the Cloudflare account that owns the gateway. */
73
+ declare const AI_GATEWAY_ACCOUNT_ID_ENV = "LUNORA_AI_GATEWAY_ACCOUNT_ID";
74
+ /** Env var naming the AI Gateway id (the gateway's slug). */
75
+ declare const AI_GATEWAY_ID_ENV = "LUNORA_AI_GATEWAY_ID";
76
+ /** Env var carrying the gateway's authentication token (only for authenticated gateways). */
77
+ declare const AI_GATEWAY_TOKEN_ENV = "LUNORA_AI_GATEWAY_TOKEN";
78
+ /**
79
+ * Resolve the Cloudflare AI Gateway coordinates from the Worker `env`, or
80
+ * `undefined` when the gateway is not configured (both `LUNORA_AI_GATEWAY_ACCOUNT_ID`
81
+ * and `LUNORA_AI_GATEWAY_ID` must be present). Opt-in and backward-compatible:
82
+ * an unconfigured app gets `undefined` and every caller keeps its direct-provider
83
+ * behavior.
84
+ *
85
+ * Pass optional {@link AiGatewayMetadata} to fold a `cf-aig-metadata` correlation
86
+ * header into `headers` — only its defined fields are sent.
87
+ * @experimental
88
+ */
89
+ declare const resolveAiGateway: (env: Record<string, unknown>, metadata?: AiGatewayMetadata) => ResolvedAiGateway | undefined;
90
+ export { AI_GATEWAY_ACCOUNT_ID_ENV, AI_GATEWAY_ID_ENV, AI_GATEWAY_TOKEN_ENV, type AiGatewayMetadata, type LunoraAi, type LunoraAiOptions, type ResolvedAiGateway, createAi, resolveAiGateway };
package/dist/index.mjs CHANGED
@@ -1,3 +1,4 @@
1
- export { default as createAi } from './packem_shared/createAi-CYxQuvEL.mjs';
1
+ export { default as createAi } from './packem_shared/createAi-Dx7n-UJm.mjs';
2
+ export { AI_GATEWAY_ACCOUNT_ID_ENV, AI_GATEWAY_ID_ENV, AI_GATEWAY_TOKEN_ENV, resolveAiGateway } from './packem_shared/AI_GATEWAY_ACCOUNT_ID_ENV-2CS2QFH7.mjs';
2
3
  export { embed, embedMany, generateObject, generateText, hasToolCall, jsonSchema, streamObject, streamText, tool } from 'ai';
3
4
  export { createWorkersAI } from 'workers-ai-provider';
@@ -0,0 +1,44 @@
1
+ const readEnv = (env, key) => {
2
+ const value = env[key];
3
+ return typeof value === "string" && value.length > 0 ? value : void 0;
4
+ };
5
+ const encodeMetadata = (metadata) => {
6
+ if (metadata === void 0) {
7
+ return void 0;
8
+ }
9
+ const fields = {};
10
+ if (typeof metadata.functionPath === "string" && metadata.functionPath.length > 0) {
11
+ fields["functionPath"] = metadata.functionPath;
12
+ }
13
+ if (typeof metadata.traceId === "string" && metadata.traceId.length > 0) {
14
+ fields["traceId"] = metadata.traceId;
15
+ }
16
+ return Object.keys(fields).length > 0 ? JSON.stringify(fields) : void 0;
17
+ };
18
+ const AI_GATEWAY_ACCOUNT_ID_ENV = "LUNORA_AI_GATEWAY_ACCOUNT_ID";
19
+ const AI_GATEWAY_ID_ENV = "LUNORA_AI_GATEWAY_ID";
20
+ const AI_GATEWAY_TOKEN_ENV = "LUNORA_AI_GATEWAY_TOKEN";
21
+ const resolveAiGateway = (env, metadata) => {
22
+ const accountId = readEnv(env, AI_GATEWAY_ACCOUNT_ID_ENV);
23
+ const gatewayId = readEnv(env, AI_GATEWAY_ID_ENV);
24
+ if (accountId === void 0 || gatewayId === void 0) {
25
+ return void 0;
26
+ }
27
+ const token = readEnv(env, AI_GATEWAY_TOKEN_ENV);
28
+ const headers = {};
29
+ if (token !== void 0) {
30
+ headers["cf-aig-authorization"] = `Bearer ${token}`;
31
+ }
32
+ const metadataHeader = encodeMetadata(metadata);
33
+ if (metadataHeader !== void 0) {
34
+ headers["cf-aig-metadata"] = metadataHeader;
35
+ }
36
+ return {
37
+ accountId,
38
+ baseURL: `https://gateway.ai.cloudflare.com/v1/${accountId}/${gatewayId}`,
39
+ gatewayId,
40
+ headers
41
+ };
42
+ };
43
+
44
+ export { AI_GATEWAY_ACCOUNT_ID_ENV, AI_GATEWAY_ID_ENV, AI_GATEWAY_TOKEN_ENV, resolveAiGateway };
@@ -1,13 +1,25 @@
1
1
  import { LunoraError } from '@lunora/errors';
2
2
  import { createWorkersAI } from 'workers-ai-provider';
3
+ import { resolveAiGateway } from './AI_GATEWAY_ACCOUNT_ID_ENV-2CS2QFH7.mjs';
3
4
 
5
+ const resolveGatewayOption = (gateway, env) => {
6
+ if (gateway !== void 0) {
7
+ return gateway;
8
+ }
9
+ if (env === void 0) {
10
+ return void 0;
11
+ }
12
+ const resolved = resolveAiGateway(env);
13
+ return resolved === void 0 ? void 0 : { id: resolved.gatewayId };
14
+ };
4
15
  const buildProvider = (binding, gateway) => createWorkersAI({ binding, gateway });
5
16
  const createAi = (options) => {
6
- const { binding, defaultEmbeddingModel, defaultModel, gateway, provider } = options;
17
+ const { binding, defaultEmbeddingModel, defaultModel, env, gateway, provider } = options;
7
18
  if (!provider && !binding) {
8
19
  throw new LunoraError("INTERNAL", "@lunora/ai: createAi requires a `binding` (env.AI) or a pre-built `provider`");
9
20
  }
10
- const workersai = provider ?? buildProvider(binding, gateway);
21
+ const resolvedGateway = resolveGatewayOption(gateway, env);
22
+ const workersai = provider ?? buildProvider(binding, resolvedGateway);
11
23
  const model = (input) => {
12
24
  if (input === void 0) {
13
25
  if (!defaultModel) {
@@ -47,7 +59,8 @@ const createAi = (options) => {
47
59
  "@lunora/ai: ai.run requires the `binding` (env.AI) — it is unavailable when only a custom `provider` was supplied"
48
60
  );
49
61
  }
50
- return binding.run(modelId, inputs, runOptions);
62
+ const mergedOptions = resolvedGateway !== void 0 && runOptions?.gateway === void 0 ? { ...runOptions, gateway: resolvedGateway } : runOptions;
63
+ return binding.run(modelId, inputs, mergedOptions);
51
64
  };
52
65
  return { embeddingModel, model, run, workersai };
53
66
  };
@@ -183,9 +183,12 @@ const defineRag = (config) => {
183
183
  return run();
184
184
  }
185
185
  const modelId = modelIdOf(resolvedModel);
186
+ const conversationId = typeof context.conversationId === "string" && context.conversationId.length > 0 ? context.conversationId : void 0;
186
187
  return tracer("ai.embed", (_trace, span) => run(span), {
187
188
  "gen_ai.operation.name": "embeddings",
188
- ...modelId === void 0 ? {} : { "gen_ai.request.model": modelId }
189
+ ...modelId === void 0 ? {} : { "gen_ai.request.model": modelId },
190
+ // Session/thread grouping — absent unless a conversation id was set.
191
+ ...conversationId === void 0 ? {} : { "gen_ai.conversation.id": conversationId }
189
192
  });
190
193
  };
191
194
  const checkNamespace = (namespace) => {
@@ -57,7 +57,19 @@ interface LunoraAiOptions {
57
57
  * Has no effect on bring-your-own providers.
58
58
  */
59
59
  defaultModel?: string;
60
- /** Route Workers AI inference through a Cloudflare AI Gateway. */
60
+ /**
61
+ * The Worker `env`, read for opt-in Cloudflare AI Gateway routing (the
62
+ * `LUNORA_AI_GATEWAY_*` vars, via `resolveAiGateway`). When it configures a
63
+ * gateway and no explicit {@link LunoraAiOptions.gateway} is given, the
64
+ * Workers AI provider is routed through that gateway so token + dollar-cost
65
+ * telemetry is computed on the app's behalf. Unset, or with no gateway vars,
66
+ * behavior is unchanged (calls go straight to Workers AI).
67
+ */
68
+ env?: Record<string, unknown>;
69
+ /**
70
+ * Route Workers AI inference through a Cloudflare AI Gateway. An explicit
71
+ * value wins over anything derived from {@link LunoraAiOptions.env}.
72
+ */
61
73
  gateway?: AiGatewayOptions;
62
74
  /**
63
75
  * Pre-built Workers AI provider. When omitted, one is constructed from
@@ -57,7 +57,19 @@ interface LunoraAiOptions {
57
57
  * Has no effect on bring-your-own providers.
58
58
  */
59
59
  defaultModel?: string;
60
- /** Route Workers AI inference through a Cloudflare AI Gateway. */
60
+ /**
61
+ * The Worker `env`, read for opt-in Cloudflare AI Gateway routing (the
62
+ * `LUNORA_AI_GATEWAY_*` vars, via `resolveAiGateway`). When it configures a
63
+ * gateway and no explicit {@link LunoraAiOptions.gateway} is given, the
64
+ * Workers AI provider is routed through that gateway so token + dollar-cost
65
+ * telemetry is computed on the app's behalf. Unset, or with no gateway vars,
66
+ * behavior is unchanged (calls go straight to Workers AI).
67
+ */
68
+ env?: Record<string, unknown>;
69
+ /**
70
+ * Route Workers AI inference through a Cloudflare AI Gateway. An explicit
71
+ * value wins over anything derived from {@link LunoraAiOptions.env}.
72
+ */
61
73
  gateway?: AiGatewayOptions;
62
74
  /**
63
75
  * Pre-built Workers AI provider. When omitted, one is constructed from
@@ -1,5 +1,5 @@
1
1
  import { Tool } from 'ai';
2
- import { E as EmbeddingModelInput, a as LunoraAi } from "../packem_shared/types.d-BXCiRv1x.mjs";
2
+ import { E as EmbeddingModelInput, a as LunoraAi } from "../packem_shared/types.d-DGmh5Hq4.mjs";
3
3
  /**
4
4
  * Built-in fixed-window chunker: split into `size`-char windows overlapping by
5
5
  * `overlap` chars. Deliberately simple and deterministic — the zero-config
@@ -109,6 +109,14 @@ interface RagContext {
109
109
  * stays decoupled from `@lunora/server`'s identity type; `rlsFilter` narrows.
110
110
  */
111
111
  auth?: unknown;
112
+ /**
113
+ * Optional conversation / session id. When set (and `trace` is present), each
114
+ * embedding-model span additionally carries `gen_ai.conversation.id`, so a
115
+ * RAG embed done inside a multi-turn conversation groups with that
116
+ * conversation's other generation spans in the trace store. Omitted → the
117
+ * attribute is absent (backward-compatible).
118
+ */
119
+ conversationId?: string;
112
120
  /**
113
121
  * Optional `ctx.trace` span factory — an `ActionCtx`'s `ctx.trace` satisfies
114
122
  * it structurally. When present, `defineRag` wraps each embedding-model
@@ -1,5 +1,5 @@
1
1
  import { Tool } from 'ai';
2
- import { E as EmbeddingModelInput, a as LunoraAi } from "../packem_shared/types.d-BXCiRv1x.js";
2
+ import { E as EmbeddingModelInput, a as LunoraAi } from "../packem_shared/types.d-DGmh5Hq4.js";
3
3
  /**
4
4
  * Built-in fixed-window chunker: split into `size`-char windows overlapping by
5
5
  * `overlap` chars. Deliberately simple and deterministic — the zero-config
@@ -109,6 +109,14 @@ interface RagContext {
109
109
  * stays decoupled from `@lunora/server`'s identity type; `rlsFilter` narrows.
110
110
  */
111
111
  auth?: unknown;
112
+ /**
113
+ * Optional conversation / session id. When set (and `trace` is present), each
114
+ * embedding-model span additionally carries `gen_ai.conversation.id`, so a
115
+ * RAG embed done inside a multi-turn conversation groups with that
116
+ * conversation's other generation spans in the trace store. Omitted → the
117
+ * attribute is absent (backward-compatible).
118
+ */
119
+ conversationId?: string;
112
120
  /**
113
121
  * Optional `ctx.trace` span factory — an `ActionCtx`'s `ctx.trace` satisfies
114
122
  * it structurally. When present, `defineRag` wraps each embedding-model
@@ -1,5 +1,5 @@
1
1
  export { default as fixedWindowChunks } from '../packem_shared/fixedWindowChunks-J-WfQDw9.mjs';
2
- export { default as defineRag } from '../packem_shared/defineRag-CQ3pnKUh.mjs';
2
+ export { default as defineRag } from '../packem_shared/defineRag-DmczmIDj.mjs';
3
3
  export { contentHash, guessMimeTypeFromExtension } from '../packem_shared/contentHash-Cgz5KRGD.mjs';
4
4
  export { default as hybridRank } from '../packem_shared/hybridRank-DPC9c2ON.mjs';
5
5
  export { default as bm25LexicalStore } from '../packem_shared/bm25LexicalStore-BQzWLMqX.mjs';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lunora/ai",
3
- "version": "1.0.0-alpha.21",
3
+ "version": "1.0.0-alpha.23",
4
4
  "description": "Workers AI helper for Lunora: provider-agnostic AI SDK access from functions, Workers AI by default",
5
5
  "keywords": [
6
6
  "ai",