@lunora/ai 1.0.0-alpha.16 → 1.0.0-alpha.18

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.
@@ -1,173 +1,546 @@
1
1
  import { Tool } from 'ai';
2
- import { E as EmbeddingModelInput, a as LunoraAi } from "../packem_shared/types.d-C6dA8LCy.mjs";
2
+ import { E as EmbeddingModelInput, a as LunoraAi } from "../packem_shared/types.d-BXCiRv1x.mjs";
3
+ /**
4
+ * Built-in fixed-window chunker: split into `size`-char windows overlapping by
5
+ * `overlap` chars. Deliberately simple and deterministic — the zero-config
6
+ * default. Token-aware / sentence / semantic strategies plug in via
7
+ * `RagConfig.chunk`.
8
+ * @experimental
9
+ */
3
10
  declare const fixedWindowChunks: (text: string, size: number, overlap: number) => ReadonlyArray<string>;
11
+ /**
12
+ * `(text) => vector` — the embedder shape `ctx.vectors` accepts on both its
13
+ * write (`upsert`) and read (`query`) inputs. Matches `@lunora/server`'s
14
+ * `VectorEmbedder` and `@lunora/bindings/vectors`' `EmbedFunction&lt;string>`.
15
+ * @experimental
16
+ */
4
17
  type RagEmbedder = (input: string) => Promise<ReadonlyArray<number>> | ReadonlyArray<number>;
18
+ /**
19
+ * `RagVectorMatch` is part of the experimental `@lunora/ai` API and may change without a major version bump.
20
+ * @experimental
21
+ */
5
22
  interface RagVectorMatch {
6
23
  id: string;
7
24
  metadata?: Record<string, unknown>;
8
25
  score: number;
9
26
  }
27
+ /**
28
+ * `RagVectorMatches` is part of the experimental `@lunora/ai` API and may change without a major version bump.
29
+ * @experimental
30
+ */
10
31
  interface RagVectorMatches {
11
32
  count: number;
12
33
  matches: ReadonlyArray<RagVectorMatch>;
13
34
  }
35
+ /**
36
+ * `RagVectorQueryInput` is part of the experimental `@lunora/ai` API and may change without a major version bump.
37
+ * @experimental
38
+ */
14
39
  interface RagVectorQueryInput {
40
+ /** Embedder used to vectorize `input`. */
15
41
  embed?: RagEmbedder;
16
42
  filter?: Record<string, unknown>;
43
+ /** Natural-language query text, embedded via `embed`. */
17
44
  input?: string;
18
45
  namespace?: string;
46
+ /**
47
+ * How much stored metadata to return on matches. The runtime honours it even
48
+ * though `@lunora/server`'s ctx type does not declare it — the helper relies
49
+ * on it to read chunk text back in metadata mode.
50
+ */
19
51
  returnMetadata?: "all" | "indexed" | "none";
20
52
  topK?: number;
21
53
  }
54
+ /**
55
+ * `RagVectorRecord` is part of the experimental `@lunora/ai` API and may change without a major version bump.
56
+ * @experimental
57
+ */
22
58
  interface RagVectorRecord {
23
59
  id: string;
24
60
  metadata?: Record<string, unknown>;
25
61
  }
62
+ /**
63
+ * `RagVectorUpsertInput` is part of the experimental `@lunora/ai` API and may change without a major version bump.
64
+ * @experimental
65
+ */
26
66
  interface RagVectorUpsertInput {
67
+ /** Embedder used to vectorize `input`. Optional — omitted for text-search indexes. */
27
68
  embed?: RagEmbedder;
28
69
  id: string;
29
70
  input: string;
30
71
  metadata?: Record<string, unknown>;
31
72
  namespace?: string;
32
73
  }
74
+ /**
75
+ * Structural subset of the vector surface the RAG helper needs. Both the
76
+ * `ctx.vectors` facade on Mutation/Action ctx (`@lunora/server`'s
77
+ * `VectorSearch`) and the raw `@lunora/bindings/vectors` `LunoraVectors`
78
+ * satisfy it — declared here so `@lunora/ai` depends on neither package.
79
+ * @experimental
80
+ */
33
81
  interface RagVectors {
34
82
  deleteByIds: (indexName: string, ids: ReadonlyArray<string>, namespace?: string) => Promise<unknown>;
35
83
  getByIds: (indexName: string, ids: ReadonlyArray<string>, namespace?: string) => Promise<ReadonlyArray<RagVectorRecord>>;
36
84
  query: (indexName: string, input: RagVectorQueryInput) => Promise<RagVectorMatches>;
37
85
  upsert: (indexName: string, input: RagVectorUpsertInput) => Promise<unknown>;
38
86
  }
87
+ /**
88
+ * The two facades `defineRag` binds. An `ActionCtx` satisfies this directly
89
+ * (`ctx.ai` is action-only, so RAG methods run inside actions); any object
90
+ * carrying the two facades works in tests.
91
+ * @experimental
92
+ */
39
93
  interface RagContext {
94
+ /**
95
+ * Resolves a Workers AI embedding-model id (or the omitted default) — an
96
+ * `ActionCtx`'s `ctx.ai` satisfies it. OPTIONAL: when
97
+ * {@link RagConfig.embeddingModel} is a direct AI SDK `EmbeddingModel` object
98
+ * (bring-your-own embeddings, e.g. `@ai-sdk/openai`), the helper uses that
99
+ * object as-is and never reads `ai`, so a hand-built context may omit it and
100
+ * no `env.AI` binding is needed. A model-id string (or an omitted model) with
101
+ * no `ai` present throws a directed error.
102
+ */
40
103
  ai?: Pick<LunoraAi, "embeddingModel">;
104
+ /**
105
+ * The verified retrieval identity, read by {@link RagConfig.rlsFilter} to
106
+ * derive a per-request row filter. An `ActionCtx` carrying `ctx.auth`
107
+ * satisfies this structurally, so `docs(ctx)` picks the identity up
108
+ * automatically; tests pass any value. `unknown` on purpose — `@lunora/ai`
109
+ * stays decoupled from `@lunora/server`'s identity type; `rlsFilter` narrows.
110
+ */
41
111
  auth?: unknown;
112
+ /**
113
+ * Optional `ctx.trace` span factory — an `ActionCtx`'s `ctx.trace` satisfies
114
+ * it structurally. When present, {@link 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;
42
122
  vectors: RagVectors;
43
123
  }
124
+ /**
125
+ * Pluggable chunk-text storage. By default chunk text is stored in vector
126
+ * metadata (`__ragText`), which forces `returnMetadata: "all"` on retrieval and
127
+ * caps `topK` at 20 (the Vectorize full-metadata ceiling) — and each vector's
128
+ * metadata must stay under the ~10 KiB Vectorize cap. Supplying a text store
129
+ * (a DO table, KV, …) moves the text out of metadata: retrieval queries with
130
+ * `returnMetadata: "indexed"` (topK up to 100) and hydrates text by chunk id.
131
+ * @experimental
132
+ */
44
133
  interface RagTextStore {
134
+ /** Fetch chunk texts by id, aligned with the input order; `undefined` for misses. */
45
135
  getMany: (ids: ReadonlyArray<string>, options: {
46
136
  namespace?: string;
47
137
  }) => Promise<ReadonlyArray<string | undefined>>;
138
+ /** Persist chunk texts. Must be idempotent by chunk `id` (re-index re-puts). */
48
139
  put: (chunks: ReadonlyArray<StoredRagChunk>, options: {
49
140
  namespace?: string;
50
141
  }) => Promise<void>;
142
+ /** Optional cleanup hook, invoked when a source's chunks are deleted. */
51
143
  remove?: (ids: ReadonlyArray<string>, options: {
52
144
  namespace?: string;
53
145
  }) => Promise<void>;
54
146
  }
147
+ /**
148
+ * A chunk handed to {@link RagTextStore.put} / {@link RagLexicalStore.index}.
149
+ * @experimental
150
+ */
55
151
  interface StoredRagChunk {
56
152
  chunkIndex: number;
57
153
  id: string;
58
154
  sourceId: string;
59
155
  text: string;
60
156
  }
157
+ /**
158
+ * One lexical (BM25) hit returned by {@link RagLexicalStore.search}.
159
+ * @experimental
160
+ */
61
161
  interface LexicalMatch {
162
+ /** The chunk vector id — the same id scheme the vector leg uses, so RRF can fuse the two. */
62
163
  id: string;
164
+ /** BM25 relevance score (higher = better). Used only for the leg's internal ranking; RRF fuses by rank. */
63
165
  score: number;
166
+ /** The chunk text, returned so a fused lexical-only hit needs no extra hydration round-trip. */
64
167
  text: string;
65
168
  }
169
+ /**
170
+ * Pluggable lexical (BM25 / keyword) store — the production seam for hybrid
171
+ * retrieval. When {@link RagConfig.lexicalStore} is set, `index()` mirrors each
172
+ * chunk's text here and `retrieve()` fuses this store's keyword ranking with the
173
+ * vector store's semantic ranking via Reciprocal Rank Fusion. Mirrors the
174
+ * {@link RagTextStore} shape (idempotent by chunk `id`, namespace-partitioned).
175
+ *
176
+ * `@lunora/ai/rag` ships `bm25LexicalStore()`, an in-memory reference adapter;
177
+ * production deployments plug a durable one (DO SQLite inverted index, D1,
178
+ * Vectorize-adjacent search service, …) behind this same interface.
179
+ * @experimental
180
+ */
66
181
  interface RagLexicalStore {
182
+ /** Index chunk texts for keyword search. Must be idempotent by chunk `id` (re-index re-puts). */
67
183
  index: (chunks: ReadonlyArray<StoredRagChunk>, options: {
68
184
  namespace?: string;
69
185
  }) => Promise<void>;
186
+ /** Optional cleanup hook, invoked when a source's chunks are deleted or a re-index shrinks it. */
70
187
  remove?: (ids: ReadonlyArray<string>, options: {
71
188
  namespace?: string;
72
189
  }) => Promise<void>;
190
+ /**
191
+ * Rank chunks by lexical relevance to `query`. `filter` carries the same
192
+ * (RLS-merged) metadata predicate handed to the vector leg — a store that
193
+ * indexes metadata MUST honour it so hybrid retrieval can't surface a row
194
+ * the RLS filter would exclude; a namespace-only store (the reference
195
+ * adapter) isolates by `namespace` and documents that it ignores `filter`.
196
+ */
73
197
  search: (query: string, options: {
74
198
  filter?: Record<string, unknown>;
75
199
  namespace?: string;
76
200
  topK: number;
77
201
  }) => Promise<ReadonlyArray<LexicalMatch>>;
78
202
  }
203
+ /**
204
+ * A pre-defined, reusable filter expression. Declared on `RagConfig.filters`
205
+ * (keyed by name) and referenced by name from `RetrieveOptions.filter` — avoids
206
+ * repeating the same tenant/RBAC filter shape across every retrieval site.
207
+ * @example
208
+ * ```ts
209
+ * const docs = defineRag({
210
+ * index: "docs",
211
+ * filters: {
212
+ * published: { filter: { status: "published", deleted: false }, description: "Only published content" },
213
+ * },
214
+ * });
215
+ * // Later — reference by name:
216
+ * docs(ctx).retrieve("query", { filter: "published" });
217
+ * ```
218
+ * @experimental
219
+ */
79
220
  interface RagNamedFilter {
221
+ /** Optional human-readable description for observability / Studio display. */
80
222
  description?: string;
223
+ /** The filter expression passed verbatim to Vectorize's `filter` parameter. */
81
224
  filter: Record<string, unknown>;
82
225
  }
226
+ /**
227
+ * `RagConfig` is part of the experimental `@lunora/ai` API and may change without a major version bump.
228
+ * @experimental
229
+ */
83
230
  interface RagConfig {
231
+ /**
232
+ * Suppress the one-time dev warning emitted when `index`/`retrieve` run
233
+ * without a `namespace`. Only appropriate for genuinely single-tenant apps —
234
+ * Vectorize indexes are account-global, so a namespace-less index shares
235
+ * vectors across every tenant.
236
+ */
84
237
  allowSharedNamespace?: boolean;
238
+ /** Custom chunker; overrides the built-in fixed-window splitter. */
85
239
  chunk?: (text: string) => ReadonlyArray<string>;
240
+ /** Overlap (chars) between adjacent chunks. Default 200. Must be < `chunkSize`. */
86
241
  chunkOverlap?: number;
242
+ /** Target chunk size (chars). Default 1000. */
87
243
  chunkSize?: number;
244
+ /**
245
+ * Embedding model, declared once so index + retrieve embed identically: a
246
+ * Workers AI id (e.g. `@cf/baai/bge-base-en-v1.5`) or any AI SDK
247
+ * `EmbeddingModel`. Falls back to `createAi`'s `defaultModel` when omitted.
248
+ */
88
249
  embeddingModel?: EmbeddingModelInput;
250
+ /**
251
+ * Embedding-model version tag — an opt-in discriminator that partitions the
252
+ * vector space so a model swap can never silently return garbage. Vectors
253
+ * embedded by one model live in a different space from another's, and
254
+ * querying across the two returns meaningless neighbours. When set, the tag
255
+ * is folded into the effective Vectorize namespace (and chunk-id prefix) of
256
+ * every index/retrieve/remove, so bumping it re-partitions cleanly: old
257
+ * vectors become unreachable to new queries (empty ≫ wrong) until sources
258
+ * are re-indexed under the new tag.
259
+ *
260
+ * Set + bump this whenever you change {@link RagConfig.embeddingModel} (or
261
+ * its dimensions). Opt-in and non-breaking — omitting it keeps the exact
262
+ * chunk-id/namespace scheme of un-versioned indexes. Must match
263
+ * `^[A-Za-z0-9._-]{1,40}$` (e.g. `"bge-v1.5"`, `"v2"`).
264
+ */
89
265
  embeddingModelVersion?: string;
266
+ /**
267
+ * Pre-defined named filter expressions. Each key is a filter name users
268
+ * pass through `RetrieveOptions.filter`. Throws at retrieve-time if the
269
+ * name is not found here — catches spelling mistakes early.
270
+ */
90
271
  filters?: Record<string, RagNamedFilter>;
272
+ /** The Vectorize index name (a `ctx.vectors` index binding key). */
91
273
  index: string;
274
+ /**
275
+ * Pluggable lexical (BM25) store for hybrid retrieval. When set, `index()`
276
+ * mirrors chunk text into it and `retrieve()` fuses the vector (semantic)
277
+ * and lexical (keyword) rankings via Reciprocal Rank Fusion — recovering the
278
+ * exact-term / rare-token matches a pure-embedding search misses. Use the
279
+ * shipped `bm25LexicalStore()` reference adapter or plug your own durable
280
+ * one. See {@link RagLexicalStore}.
281
+ */
92
282
  lexicalStore?: RagLexicalStore;
283
+ /** Retrieval depth for the lexical leg of hybrid search. Defaults to the effective `topK`. */
93
284
  lexicalTopK?: number;
285
+ /**
286
+ * Enforce tenant isolation: throw (instead of the one-time dev warning)
287
+ * when `index`/`retrieve`/`remove` run without a `namespace`. Recommended
288
+ * for every multi-tenant app — Vectorize indexes are account-global, and
289
+ * in metadata mode the leaked payload includes raw chunk text.
290
+ */
94
291
  requireNamespace?: boolean;
292
+ /**
293
+ * Row-level-security filter derived from the retrieval identity. Called once
294
+ * per `retrieve()` with {@link RagContext.auth} (the bound ctx's `auth`); the
295
+ * returned Vectorize metadata filter is merged over the caller's `filter`
296
+ * with **RLS keys winning** (a caller can never widen past what RLS allows),
297
+ * then applied to both the vector and the lexical legs. Return `undefined` to
298
+ * add no constraint (e.g. an admin identity). Runs on retrieval only —
299
+ * indexing is a trusted server path.
300
+ * @example
301
+ * ```ts
302
+ * const docs = defineRag({
303
+ * index: "docs",
304
+ * // only ever return the caller's own org, whatever else they ask for:
305
+ * rlsFilter: (auth) => ({ orgId: (auth as { orgId: string }).orgId }),
306
+ * });
307
+ * ```
308
+ */
95
309
  rlsFilter?: (auth: unknown) => Promise<Record<string, unknown> | undefined> | Record<string, unknown> | undefined;
310
+ /** Chunk-text storage override — see {@link RagTextStore}. */
96
311
  textStore?: RagTextStore;
312
+ /** Default retrieval depth. Default 5. Capped at 20 (metadata mode) / 100 (text-store mode). */
97
313
  topK?: number;
98
314
  }
315
+ /**
316
+ * `IndexInput` is part of the experimental `@lunora/ai` API and may change without a major version bump.
317
+ * @experimental
318
+ */
99
319
  interface IndexInput {
320
+ /**
321
+ * When `false`, throws if the source text produces zero chunks (e.g. empty
322
+ * or whitespace-only text). Default `true` (silently produces zero chunks).
323
+ */
100
324
  allowEmptySources?: boolean;
325
+ /** Source document id — chunk ids derive from it as `${id}#${chunkIndex}`. */
101
326
  id: string;
327
+ /**
328
+ * Relative weight in `[0, 1]` multiplied into this source's match scores at
329
+ * retrieval time (default 1). Lets canonical docs outrank incidental ones.
330
+ */
102
331
  importance?: number;
332
+ /** Source metadata copied onto every chunk vector (e.g. title, url). */
103
333
  metadata?: Record<string, unknown>;
334
+ /** Tenant/shard key. Required for multi-tenant apps — Vectorize is account-global. */
104
335
  namespace?: string;
336
+ /**
337
+ * Called after each chunk is successfully upserted. Useful for progress
338
+ * tracking during large indexing operations — e.g. updating a UI progress
339
+ * bar or logging per-chunk status.
340
+ */
105
341
  onChunk?: (info: {
106
342
  chunkIndex: number;
107
343
  id: string;
108
344
  text: string;
109
345
  total: number;
110
346
  }) => void;
347
+ /** The document body to chunk + embed + upsert. */
111
348
  text: string;
112
349
  }
350
+ /**
351
+ * `IndexResult` is part of the experimental `@lunora/ai` API and may change without a major version bump.
352
+ * @experimental
353
+ */
113
354
  interface IndexResult {
355
+ /** Number of chunks the source is indexed into. */
114
356
  chunks: number;
357
+ /** The deterministic chunk vector ids, in chunk order. */
115
358
  ids: ReadonlyArray<string>;
359
+ /**
360
+ * True when the source's content hash matched the previously indexed hash —
361
+ * chunking/embedding/upserts were skipped entirely (a no-op re-sync).
362
+ */
116
363
  unchanged: boolean;
117
364
  }
365
+ /**
366
+ * `RemoveInput` is part of the experimental `@lunora/ai` API and may change without a major version bump.
367
+ * @experimental
368
+ */
118
369
  interface RemoveInput {
370
+ /** The source document id whose chunks are removed. */
119
371
  id: string;
120
372
  namespace?: string;
121
373
  }
374
+ /**
375
+ * `RetrieveOptions` is part of the experimental `@lunora/ai` API and may change without a major version bump.
376
+ * @experimental
377
+ */
122
378
  interface RetrieveOptions {
379
+ /**
380
+ * Also return this many neighbouring chunks around each match (fetched by
381
+ * deterministic id, not re-queried) — "embed small, retrieve big". Neighbour
382
+ * text is stitched into the chunk's `text` in document order. Best combined
383
+ * with `chunkOverlap: 0`, since overlapping windows repeat boundary text.
384
+ */
123
385
  chunkContext?: {
124
386
  after?: number;
125
387
  before?: number;
126
388
  };
389
+ /**
390
+ * Vectorize filter expression — or the name of a pre-defined filter declared
391
+ * in `RagConfig.filters`. Passing a name that is not registered throws at
392
+ * call time, catching spelling mistakes early.
393
+ */
127
394
  filter?: Record<string, unknown> | string;
395
+ /** Drop matches whose (importance-adjusted) score falls below this threshold. */
128
396
  minScore?: number;
129
397
  namespace?: string;
398
+ /**
399
+ * Fires after retrieval completes, before chunk expansion. Useful for
400
+ * observability — logging query latency, hit counts, etc.
401
+ */
130
402
  onRetrieve?: (info: {
131
403
  matches: number;
132
404
  query: string;
133
405
  }) => void;
134
406
  topK?: number;
135
407
  }
408
+ /**
409
+ * `RetrievedChunk` is part of the experimental `@lunora/ai` API and may change without a major version bump.
410
+ * @experimental
411
+ */
136
412
  interface RetrievedChunk {
137
413
  chunkIndex: number;
138
414
  id: string;
415
+ /**
416
+ * The source-level importance weight that was multiplied into this chunk's
417
+ * score. `1` when no importance was set at index time.
418
+ */
139
419
  importance: number;
420
+ /** Caller metadata stored on the vector (internal `__rag*` keys stripped). */
140
421
  metadata?: Record<string, unknown>;
422
+ /** Cosine similarity, multiplied by the source's `importance` when one was set. */
141
423
  score: number;
142
424
  sourceId: string;
143
425
  text: string;
144
426
  }
427
+ /**
428
+ * `RagSource` is part of the experimental `@lunora/ai` API and may change without a major version bump.
429
+ * @experimental
430
+ */
145
431
  interface RagSource {
146
432
  id: string;
433
+ /** Caller metadata from the source's first-seen chunk (internal keys stripped). */
147
434
  metadata?: Record<string, unknown>;
435
+ /**
436
+ * The source's importance weight (the `importance` value passed at index
437
+ * time, default 1), propagated so downstream consumers can factor it into
438
+ * their own ranking or UI.
439
+ */
148
440
  weight?: number;
149
441
  }
442
+ /**
443
+ * The retrieve return shape — designed so an agent memory step consumes it directly.
444
+ * @experimental
445
+ */
150
446
  interface RetrieveResult {
447
+ /** Ranked chunks (best first). */
151
448
  chunks: ReadonlyArray<RetrievedChunk>;
449
+ /** Ready-to-inject prompt context: chunks joined under `[source:&lt;id>#&lt;n>]` headers. */
152
450
  context: string;
451
+ /** Deduped source references, in first-seen (best) order. */
153
452
  sources: ReadonlyArray<RagSource>;
154
453
  }
454
+ /**
455
+ * `RagToolOptions` is part of the experimental `@lunora/ai` API and may change without a major version bump.
456
+ * @experimental
457
+ */
155
458
  interface RagToolOptions {
459
+ /** Tool description shown to the model. Defaults to a search description naming the index. */
156
460
  description?: string;
461
+ /** Namespace applied to every tool-invoked retrieval (the tenant key). */
157
462
  namespace?: string;
463
+ /** Retrieval depth for tool-invoked retrievals. */
158
464
  topK?: number;
159
465
  }
466
+ /**
467
+ * The per-request RAG surface returned by binding a ctx: `docs(ctx)`.
468
+ * @experimental
469
+ */
160
470
  interface Rag {
471
+ /**
472
+ * Expose `retrieve` as an AI SDK tool (for `generateText`/`streamText`
473
+ * `tools:` maps), so a model can decide to search the index itself.
474
+ */
161
475
  asTool: (options?: RagToolOptions) => Tool<{
162
476
  query: string;
163
477
  }, RetrieveResult>;
478
+ /**
479
+ * Chunk + embed + upsert one source document. Re-indexing the same `id` is
480
+ * an atomic-enough replace: unchanged content short-circuits via content
481
+ * hash, and stale chunks beyond the new count are deleted automatically.
482
+ */
164
483
  index: (input: IndexInput) => Promise<IndexResult>;
484
+ /** Delete every chunk of a previously indexed source. */
165
485
  remove: (input: RemoveInput) => Promise<void>;
486
+ /** Embed the query and return ranked chunks + prompt-ready context. */
166
487
  retrieve: (query: string, options?: RetrieveOptions) => Promise<RetrieveResult>;
167
488
  }
168
489
  declare const defineRag: (config: RagConfig) => ((context: RagContext) => Rag);
490
+ /**
491
+ * Guess a MIME type from a file extension. Lowercases and strips a leading `.`
492
+ * from `extension`; returns `"application/octet-stream"` for unknown extensions.
493
+ *
494
+ * Covers the broad set of extensions users are likely to encounter in a web /
495
+ * document-processing context — images, video, audio, office docs, PDF, text,
496
+ * archives, and source code. Follows the same approach as Convex's
497
+ * `guessMimeType` helper.
498
+ * @experimental
499
+ */
169
500
  declare const guessMimeTypeFromExtension: (extension: string) => string;
501
+ /**
502
+ * SHA-256 hex digest of binary data. Accepts a `BufferSource` (`ArrayBuffer` or
503
+ * `ArrayBufferView` such as `Uint8Array`). Useful for content-addressable
504
+ * storage — pair with `IndexInput.text` to detect duplicates across re-indexes.
505
+ * @experimental
506
+ */
170
507
  declare const contentHash: (data: BufferSource) => Promise<string>;
508
+ /**
509
+ * Reciprocal Rank Fusion (RRF): merge two ranked lists of chunks by their
510
+ * _rank position_ rather than their absolute scores, which are not comparable
511
+ * across different search methods (cosine vs BM25).
512
+ *
513
+ * Each result set contributes `1 / (k + rank)` to each chunk's fused score,
514
+ * where `rank` is 0-based position in the list. The constant `k` (default 60)
515
+ * dampens the influence of high ranks — the standard value from the RRF
516
+ * literature that works well across domains.
517
+ *
518
+ * The fused list is sorted descending by fused score. Ties are broken by
519
+ * preferring the chunk ranked higher in the vector search result (typically
520
+ * the more semantically accurate of the two methods).
521
+ *
522
+ * Callers MUST ensure every chunk in both lists carries a unique, comparable
523
+ * `id` — this is guaranteed by the chunk-id scheme `${sourceId}#${chunkIndex}`.
524
+ * @experimental
525
+ */
171
526
  declare const hybridRank: (vectorResults: ReadonlyArray<RetrievedChunk>, textResults: ReadonlyArray<RetrievedChunk>, k?: number) => ReadonlyArray<RetrievedChunk>;
527
+ /**
528
+ * An **in-memory** Okapi BM25 lexical store — the reference adapter behind
529
+ * `RagConfig.lexicalStore`, giving hybrid retrieval its keyword leg with zero
530
+ * infrastructure. State lives in the worker isolate: it is **not durable and not
531
+ * shared across isolates**, so it is intended for tests, local development, and
532
+ * single-isolate workloads. Production deployments plug a durable
533
+ * {@link RagLexicalStore} (a DO-SQLite inverted index, D1, or an external search
534
+ * service) behind the same seam.
535
+ *
536
+ * Tenant isolation is by `namespace` (each namespace keeps its own index). This
537
+ * store holds **no metadata**, so it cannot evaluate a metadata `filter`
538
+ * (including an `rlsFilter` result): when `search` is called with a non-empty
539
+ * filter it **fails closed** — returns no lexical hits and warns once — rather
540
+ * than risk surfacing a row the filter would exclude. If your RLS is
541
+ * metadata-based (not namespace-based) and you want a lexical leg, fold the RLS
542
+ * dimension into the `namespace` or plug a filter-aware store.
543
+ * @experimental
544
+ */
172
545
  declare const bm25LexicalStore: () => RagLexicalStore;
173
546
  export { type IndexInput, type IndexResult, type LexicalMatch, type Rag, type RagConfig, type RagContext, type RagEmbedder, type RagLexicalStore, type RagNamedFilter, type RagSource, type RagTextStore, type RagToolOptions, type RagVectorMatch, type RagVectorMatches, type RagVectorQueryInput, type RagVectorRecord, type RagVectorUpsertInput, type RagVectors, type RemoveInput, type RetrieveOptions, type RetrieveResult, type RetrievedChunk, type StoredRagChunk, bm25LexicalStore, contentHash, defineRag, fixedWindowChunks, guessMimeTypeFromExtension, hybridRank };