@lunora/ai 1.0.0-alpha.17 → 1.0.0-alpha.19

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.
@@ -109,6 +109,10 @@ const resolveEmbeddingModel = (input, ai) => {
109
109
  }
110
110
  return ai.embeddingModel(input);
111
111
  };
112
+ const modelIdOf = (model) => {
113
+ const id = model.modelId;
114
+ return typeof id === "string" && id.length > 0 ? id : void 0;
115
+ };
112
116
  const defineRag = (config) => {
113
117
  if (typeof config.index !== "string" || config.index.length === 0) {
114
118
  throw new LunoraError("BAD_REQUEST", "@lunora/ai/rag: `index` must be a non-empty Vectorize index name");
@@ -143,10 +147,22 @@ const defineRag = (config) => {
143
147
  };
144
148
  return (context) => {
145
149
  let model;
150
+ const tracer = typeof context.trace === "function" ? context.trace : void 0;
146
151
  const embedText = async (text) => {
147
152
  model ??= resolveEmbeddingModel(config.embeddingModel, context.ai);
148
- const { embedding } = await embed({ model, value: text });
149
- return embedding;
153
+ const resolvedModel = model;
154
+ const run = async () => {
155
+ const { embedding } = await embed({ model: resolvedModel, value: text });
156
+ return embedding;
157
+ };
158
+ if (tracer === void 0) {
159
+ return run();
160
+ }
161
+ const modelId = modelIdOf(resolvedModel);
162
+ return tracer("ai.embed", run, {
163
+ "gen_ai.operation.name": "embeddings",
164
+ ...modelId === void 0 ? {} : { "gen_ai.request.model": modelId }
165
+ });
150
166
  };
151
167
  const checkNamespace = (namespace) => {
152
168
  if (namespace !== void 0) {
@@ -109,6 +109,16 @@ interface RagContext {
109
109
  * stays decoupled from `@lunora/server`'s identity type; `rlsFilter` narrows.
110
110
  */
111
111
  auth?: unknown;
112
+ /**
113
+ * Optional `ctx.trace` span factory — an `ActionCtx`'s `ctx.trace` satisfies
114
+ * it structurally. When present, `defineRag` wraps each embedding-model
115
+ * call in a `generation` span (`gen_ai.operation.name: "embeddings"` +
116
+ * `gen_ai.request.model`), so the embed shows up on the trace waterfall like
117
+ * any other instrumented sub-operation. `unknown` on purpose — the same
118
+ * decoupling rationale as `auth`: `defineRag` narrows it to a callable and
119
+ * runs embeds untraced when it is absent (a hand-built context / test).
120
+ */
121
+ trace?: unknown;
112
122
  vectors: RagVectors;
113
123
  }
114
124
  /**
@@ -476,30 +486,6 @@ interface Rag {
476
486
  /** Embed the query and return ranked chunks + prompt-ready context. */
477
487
  retrieve: (query: string, options?: RetrieveOptions) => Promise<RetrieveResult>;
478
488
  }
479
- /**
480
- * Declare a RAG index over the two facades every Lunora action already has:
481
- * `ctx.ai` (embeddings) + `ctx.vectors` (Vectorize). Returns a per-request
482
- * factory — bind a ctx to get `{ index, retrieve, remove, asTool }`:
483
- *
484
- * ```ts
485
- * // lunora/rag.ts
486
- * export const docs = defineRag({ embeddingModel: "@cf/baai/bge-base-en-v1.5", index: "docs" });
487
- *
488
- * // inside an action:
489
- * await docs(ctx).index({ id: doc._id, metadata: { title: doc.title }, namespace: ctx.shardKey, text: doc.body });
490
- * const { chunks, context, sources } = await docs(ctx).retrieve(question, { namespace: ctx.shardKey });
491
- * ```
492
- *
493
- * Pure composition — no new binding, no I/O until a method runs. Chunk text
494
- * lives in vector metadata by default (`topK` ≤ 20); supply `textStore` to move
495
- * it into your own storage and lift the ceiling to 100 — see `RagTextStore`.
496
- *
497
- * `ctx.ai` is only needed to resolve a Workers AI `embeddingModel` id. Pass a
498
- * direct AI SDK `EmbeddingModel` object (`@ai-sdk/openai`, …) and the helper
499
- * embeds through it without `ctx.ai` — so a bring-your-own-embeddings index
500
- * needs no `env.AI` binding (bind any context carrying just `vectors`).
501
- * @experimental
502
- */
503
489
  declare const defineRag: (config: RagConfig) => ((context: RagContext) => Rag);
504
490
  /**
505
491
  * Guess a MIME type from a file extension. Lowercases and strips a leading `.`
@@ -109,6 +109,16 @@ interface RagContext {
109
109
  * stays decoupled from `@lunora/server`'s identity type; `rlsFilter` narrows.
110
110
  */
111
111
  auth?: unknown;
112
+ /**
113
+ * Optional `ctx.trace` span factory — an `ActionCtx`'s `ctx.trace` satisfies
114
+ * it structurally. When present, `defineRag` wraps each embedding-model
115
+ * call in a `generation` span (`gen_ai.operation.name: "embeddings"` +
116
+ * `gen_ai.request.model`), so the embed shows up on the trace waterfall like
117
+ * any other instrumented sub-operation. `unknown` on purpose — the same
118
+ * decoupling rationale as `auth`: `defineRag` narrows it to a callable and
119
+ * runs embeds untraced when it is absent (a hand-built context / test).
120
+ */
121
+ trace?: unknown;
112
122
  vectors: RagVectors;
113
123
  }
114
124
  /**
@@ -476,30 +486,6 @@ interface Rag {
476
486
  /** Embed the query and return ranked chunks + prompt-ready context. */
477
487
  retrieve: (query: string, options?: RetrieveOptions) => Promise<RetrieveResult>;
478
488
  }
479
- /**
480
- * Declare a RAG index over the two facades every Lunora action already has:
481
- * `ctx.ai` (embeddings) + `ctx.vectors` (Vectorize). Returns a per-request
482
- * factory — bind a ctx to get `{ index, retrieve, remove, asTool }`:
483
- *
484
- * ```ts
485
- * // lunora/rag.ts
486
- * export const docs = defineRag({ embeddingModel: "@cf/baai/bge-base-en-v1.5", index: "docs" });
487
- *
488
- * // inside an action:
489
- * await docs(ctx).index({ id: doc._id, metadata: { title: doc.title }, namespace: ctx.shardKey, text: doc.body });
490
- * const { chunks, context, sources } = await docs(ctx).retrieve(question, { namespace: ctx.shardKey });
491
- * ```
492
- *
493
- * Pure composition — no new binding, no I/O until a method runs. Chunk text
494
- * lives in vector metadata by default (`topK` ≤ 20); supply `textStore` to move
495
- * it into your own storage and lift the ceiling to 100 — see `RagTextStore`.
496
- *
497
- * `ctx.ai` is only needed to resolve a Workers AI `embeddingModel` id. Pass a
498
- * direct AI SDK `EmbeddingModel` object (`@ai-sdk/openai`, …) and the helper
499
- * embeds through it without `ctx.ai` — so a bring-your-own-embeddings index
500
- * needs no `env.AI` binding (bind any context carrying just `vectors`).
501
- * @experimental
502
- */
503
489
  declare const defineRag: (config: RagConfig) => ((context: RagContext) => Rag);
504
490
  /**
505
491
  * Guess a MIME type from a file extension. Lowercases and strips a leading `.`
@@ -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-CH8wZ0d5.mjs';
2
+ export { default as defineRag } from '../packem_shared/defineRag-DgVXmNGw.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.17",
3
+ "version": "1.0.0-alpha.19",
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",