@convex-dev/rag 0.5.3 → 0.6.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.
Files changed (82) hide show
  1. package/README.md +89 -82
  2. package/dist/client/index.d.ts +39 -26
  3. package/dist/client/index.d.ts.map +1 -1
  4. package/dist/client/index.js +26 -8
  5. package/dist/client/index.js.map +1 -1
  6. package/dist/component/_generated/api.d.ts +100 -481
  7. package/dist/component/_generated/api.d.ts.map +1 -1
  8. package/dist/component/_generated/api.js +10 -1
  9. package/dist/component/_generated/api.js.map +1 -1
  10. package/dist/component/_generated/component.d.ts +380 -0
  11. package/dist/component/_generated/component.d.ts.map +1 -0
  12. package/dist/component/_generated/component.js +11 -0
  13. package/dist/component/_generated/component.js.map +1 -0
  14. package/dist/component/_generated/dataModel.d.ts +4 -18
  15. package/dist/component/_generated/dataModel.d.ts.map +1 -0
  16. package/dist/component/_generated/dataModel.js +11 -0
  17. package/dist/component/_generated/dataModel.js.map +1 -0
  18. package/dist/component/_generated/server.d.ts +10 -38
  19. package/dist/component/_generated/server.d.ts.map +1 -1
  20. package/dist/component/_generated/server.js +9 -5
  21. package/dist/component/_generated/server.js.map +1 -1
  22. package/dist/component/chunks.d.ts +5 -5
  23. package/dist/component/chunks.d.ts.map +1 -1
  24. package/dist/component/chunks.js +11 -44
  25. package/dist/component/chunks.js.map +1 -1
  26. package/dist/component/embeddings/tables.d.ts +4 -5
  27. package/dist/component/embeddings/tables.d.ts.map +1 -1
  28. package/dist/component/embeddings/tables.js.map +1 -1
  29. package/dist/component/entries.d.ts +6 -6
  30. package/dist/component/namespaces.d.ts +8 -8
  31. package/dist/component/namespaces.d.ts.map +1 -1
  32. package/dist/component/namespaces.js +2 -2
  33. package/dist/component/namespaces.js.map +1 -1
  34. package/dist/component/schema.d.ts +185 -224
  35. package/dist/component/schema.d.ts.map +1 -1
  36. package/dist/component/search.d.ts +4 -3
  37. package/dist/component/search.d.ts.map +1 -1
  38. package/dist/component/search.js +1 -1
  39. package/dist/component/search.js.map +1 -1
  40. package/dist/shared.d.ts +9 -4
  41. package/dist/shared.d.ts.map +1 -1
  42. package/dist/shared.js +1 -4
  43. package/dist/shared.js.map +1 -1
  44. package/package.json +71 -42
  45. package/src/client/defaultChunker.test.ts +1 -1
  46. package/src/client/defaultChunker.ts +7 -7
  47. package/src/client/fileUtils.ts +3 -3
  48. package/src/client/hybridRank.ts +1 -1
  49. package/src/client/index.test.ts +18 -18
  50. package/src/client/index.ts +135 -90
  51. package/src/client/setup.test.ts +2 -2
  52. package/src/component/_generated/api.ts +152 -0
  53. package/src/component/_generated/component.ts +442 -0
  54. package/src/component/_generated/{server.d.ts → server.ts} +33 -21
  55. package/src/component/chunks.test.ts +14 -14
  56. package/src/component/chunks.ts +49 -82
  57. package/src/component/embeddings/importance.test.ts +4 -4
  58. package/src/component/embeddings/importance.ts +1 -1
  59. package/src/component/embeddings/index.test.ts +3 -4
  60. package/src/component/embeddings/index.ts +6 -6
  61. package/src/component/embeddings/tables.ts +9 -8
  62. package/src/component/entries.test.ts +10 -10
  63. package/src/component/entries.ts +29 -29
  64. package/src/component/filters.ts +8 -8
  65. package/src/component/namespaces.ts +31 -34
  66. package/src/component/schema.ts +2 -2
  67. package/src/component/search.test.ts +5 -5
  68. package/src/component/search.ts +8 -9
  69. package/src/component/setup.test.ts +2 -8
  70. package/src/shared.ts +47 -45
  71. package/src/test.ts +20 -0
  72. package/dist/client/types.d.ts +0 -29
  73. package/dist/client/types.d.ts.map +0 -1
  74. package/dist/client/types.js +0 -2
  75. package/dist/client/types.js.map +0 -1
  76. package/dist/package.json +0 -3
  77. package/src/client/types.ts +0 -69
  78. package/src/component/_generated/api.d.ts +0 -507
  79. package/src/component/_generated/api.js +0 -23
  80. package/src/component/_generated/server.js +0 -90
  81. package/src/vitest.config.ts +0 -7
  82. /package/src/component/_generated/{dataModel.d.ts → dataModel.ts} +0 -0
@@ -2,13 +2,14 @@ import {
2
2
  embed,
3
3
  embedMany,
4
4
  generateText,
5
- type ModelMessage,
6
5
  type EmbeddingModel,
7
6
  type EmbeddingModelUsage,
7
+ type ModelMessage,
8
8
  } from "ai";
9
9
  import { assert } from "convex-helpers";
10
10
  import {
11
11
  createFunctionHandle,
12
+ FunctionReference,
12
13
  internalActionGeneric,
13
14
  internalMutationGeneric,
14
15
  type FunctionArgs,
@@ -23,36 +24,30 @@ import {
23
24
  type RegisteredMutation,
24
25
  } from "convex/server";
25
26
  import { type Value } from "convex/values";
27
+ import { ComponentApi } from "../component/_generated/component.js";
28
+ import type { NamedFilter } from "../component/filters.js";
26
29
  import {
27
30
  CHUNK_BATCH_SIZE,
28
31
  filterNamesContain,
32
+ OnCompleteArgs,
29
33
  vChunkerArgs,
30
34
  vEntryId,
31
35
  vNamespaceId,
32
36
  vOnCompleteArgs,
33
37
  type Chunk,
38
+ type ChunkerAction,
34
39
  type CreateChunkArgs,
35
40
  type Entry,
36
41
  type EntryFilter,
37
42
  type EntryId,
38
43
  type Namespace,
39
44
  type NamespaceId,
45
+ type OnComplete,
46
+ type OnCompleteNamespace,
40
47
  type SearchEntry,
41
48
  type SearchResult,
42
49
  type Status,
43
50
  } from "../shared.js";
44
- import {
45
- type RAGComponent,
46
- type RunActionCtx,
47
- type RunMutationCtx,
48
- type RunQueryCtx,
49
- } from "./types.js";
50
- import {
51
- type ChunkerAction,
52
- type OnComplete,
53
- type OnCompleteNamespace,
54
- } from "../shared.js";
55
- import type { NamedFilter } from "../component/filters.js";
56
51
  import { defaultChunker } from "./defaultChunker.js";
57
52
 
58
53
  export { hybridRank } from "./hybridRank.js";
@@ -61,7 +56,6 @@ export type {
61
56
  ChunkerAction,
62
57
  Entry,
63
58
  EntryId,
64
- RAGComponent,
65
59
  NamespaceId,
66
60
  OnComplete,
67
61
  OnCompleteNamespace,
@@ -71,18 +65,18 @@ export type {
71
65
  };
72
66
 
73
67
  export {
74
- type VEntry,
75
- type VSearchEntry,
76
- type EntryFilter,
77
68
  vEntry,
69
+ vOnCompleteArgs,
78
70
  vSearchEntry,
79
71
  vSearchResult,
80
- vOnCompleteArgs,
72
+ type EntryFilter,
73
+ type VEntry,
74
+ type VSearchEntry,
81
75
  } from "../shared.js";
82
76
  export {
83
77
  contentHashFromArrayBuffer,
84
- guessMimeTypeFromExtension,
85
78
  guessMimeTypeFromContents,
79
+ guessMimeTypeFromExtension,
86
80
  } from "./fileUtils.js";
87
81
 
88
82
  const DEFAULT_SEARCH_LIMIT = 10;
@@ -115,12 +109,12 @@ export class RAG<
115
109
  * and then entry results will have the metadata type `{ source: "website" }`.
116
110
  */
117
111
  constructor(
118
- public component: RAGComponent,
112
+ public component: ComponentApi,
119
113
  public options: {
120
114
  embeddingDimension: number;
121
115
  textEmbeddingModel: EmbeddingModel<string>;
122
116
  filterNames?: FilterNames<FitlerSchemas>;
123
- }
117
+ },
124
118
  ) {}
125
119
 
126
120
  /**
@@ -134,7 +128,7 @@ export class RAG<
134
128
  * The filterValues you provide can be used later to search for it.
135
129
  */
136
130
  async add(
137
- ctx: RunMutationCtx,
131
+ ctx: CtxWith<"runMutation">,
138
132
  args: NamespaceSelection &
139
133
  EntryArgs<FitlerSchemas, EntryMetadata> &
140
134
  (
@@ -158,7 +152,7 @@ export class RAG<
158
152
  /** @deprecated You cannot specify both chunks and text currently. */
159
153
  chunks?: undefined;
160
154
  }
161
- )
155
+ ),
162
156
  ): Promise<{
163
157
  entryId: EntryId;
164
158
  status: Status;
@@ -185,7 +179,7 @@ export class RAG<
185
179
  if (Array.isArray(chunks) && chunks.length < CHUNK_BATCH_SIZE) {
186
180
  const result = await createChunkArgsBatch(
187
181
  this.options.textEmbeddingModel,
188
- chunks
182
+ chunks,
189
183
  );
190
184
  allChunks = result.chunks;
191
185
  totalUsage.tokens += result.usage.tokens;
@@ -208,7 +202,7 @@ export class RAG<
208
202
  },
209
203
  onComplete,
210
204
  allChunks,
211
- }
205
+ },
212
206
  );
213
207
  if (status === "ready") {
214
208
  return {
@@ -230,7 +224,7 @@ export class RAG<
230
224
  for await (const batch of batchIterator(chunks, CHUNK_BATCH_SIZE)) {
231
225
  const result = await createChunkArgsBatch(
232
226
  this.options.textEmbeddingModel,
233
- batch
227
+ batch,
234
228
  );
235
229
  totalUsage.tokens += result.usage.tokens;
236
230
  const { status } = await ctx.runMutation(this.component.chunks.insert, {
@@ -250,7 +244,7 @@ export class RAG<
250
244
  while (true) {
251
245
  const { status, nextStartOrder } = await ctx.runMutation(
252
246
  this.component.chunks.replaceChunksPage,
253
- { entryId, startOrder }
247
+ { entryId, startOrder },
254
248
  );
255
249
  if (status === "ready") {
256
250
  break;
@@ -268,7 +262,7 @@ export class RAG<
268
262
  }
269
263
  const promoted = await ctx.runMutation(
270
264
  this.component.entries.promoteToReady,
271
- { entryId }
265
+ { entryId },
272
266
  );
273
267
  return {
274
268
  entryId: entryId as EntryId,
@@ -306,7 +300,7 @@ export class RAG<
306
300
  * ```
307
301
  */
308
302
  async addAsync(
309
- ctx: RunMutationCtx,
303
+ ctx: CtxWith<"runMutation">,
310
304
  args: NamespaceSelection &
311
305
  EntryArgs<FitlerSchemas, EntryMetadata> & {
312
306
  /**
@@ -324,7 +318,7 @@ export class RAG<
324
318
  * });
325
319
  */
326
320
  chunkerAction: ChunkerAction;
327
- }
321
+ },
328
322
  ): Promise<{ entryId: EntryId; status: "ready" | "pending" }> {
329
323
  let namespaceId: NamespaceId;
330
324
  if ("namespaceId" in args) {
@@ -358,7 +352,7 @@ export class RAG<
358
352
  },
359
353
  onComplete,
360
354
  chunker,
361
- }
355
+ },
362
356
  );
363
357
  return { entryId: entryId as EntryId, status };
364
358
  }
@@ -369,7 +363,7 @@ export class RAG<
369
363
  * parameters to filter and constrain the results.
370
364
  */
371
365
  async search(
372
- ctx: RunActionCtx,
366
+ ctx: CtxWith<"runAction">,
373
367
  args: {
374
368
  /**
375
369
  * The namespace to search in. e.g. a userId if entries are per-user.
@@ -381,7 +375,7 @@ export class RAG<
381
375
  * The query to search for. Optional if embedding is provided.
382
376
  */
383
377
  query: string | Array<number>;
384
- } & SearchOptions<FitlerSchemas>
378
+ } & SearchOptions<FitlerSchemas>,
385
379
  ): Promise<{
386
380
  results: SearchResult[];
387
381
  text: string;
@@ -410,12 +404,12 @@ export class RAG<
410
404
  {
411
405
  embedding,
412
406
  namespace,
413
- modelId: this.options.textEmbeddingModel.modelId,
407
+ modelId: getModelId(this.options.textEmbeddingModel),
414
408
  filters,
415
409
  limit,
416
410
  vectorScoreThreshold,
417
411
  chunkContext,
418
- }
412
+ },
419
413
  );
420
414
  const entriesWithTexts = entries.map((e) => {
421
415
  const ranges = results
@@ -458,7 +452,7 @@ export class RAG<
458
452
  * extra context / conversation history.
459
453
  */
460
454
  async generateText(
461
- ctx: RunActionCtx,
455
+ ctx: CtxWith<"runAction">,
462
456
  args: {
463
457
  /**
464
458
  * The search options to use for context search, including the namespace.
@@ -485,7 +479,7 @@ export class RAG<
485
479
  * to the prompt, in which case it will precede the prompt.
486
480
  */
487
481
  messages?: ModelMessage[];
488
- } & Parameters<typeof generateText>[0]
482
+ } & Parameters<typeof generateText>[0],
489
483
  ): Promise<
490
484
  Awaited<ReturnType<typeof generateText>> & {
491
485
  context: {
@@ -530,7 +524,7 @@ export class RAG<
530
524
  .map((e) =>
531
525
  e.title
532
526
  ? `<document title="${e.title}">${e.text}</document>`
533
- : `<document>${e.text}</document>`
527
+ : `<document>${e.text}</document>`,
534
528
  )
535
529
  .join("\n");
536
530
  contextFooter = "</context>";
@@ -576,12 +570,12 @@ export class RAG<
576
570
  * List all entries in a namespace.
577
571
  */
578
572
  async list(
579
- ctx: RunQueryCtx,
573
+ ctx: CtxWith<"runQuery">,
580
574
  args: {
581
575
  namespaceId?: NamespaceId;
582
576
  order?: "desc" | "asc";
583
577
  status?: Status;
584
- } & ({ paginationOpts: PaginationOptions } | { limit: number })
578
+ } & ({ paginationOpts: PaginationOptions } | { limit: number }),
585
579
  ): Promise<PaginationResult<Entry<FitlerSchemas, EntryMetadata>>> {
586
580
  const paginationOpts =
587
581
  "paginationOpts" in args
@@ -600,10 +594,10 @@ export class RAG<
600
594
  * Get entry metadata by its id.
601
595
  */
602
596
  async getEntry(
603
- ctx: RunQueryCtx,
597
+ ctx: CtxWith<"runQuery">,
604
598
  args: {
605
599
  entryId: EntryId;
606
- }
600
+ },
607
601
  ): Promise<Entry<FitlerSchemas, EntryMetadata> | null> {
608
602
  const entry = await ctx.runQuery(this.component.entries.get, {
609
603
  entryId: args.entryId,
@@ -617,19 +611,19 @@ export class RAG<
617
611
  * when updating content.
618
612
  */
619
613
  async findEntryByContentHash(
620
- ctx: RunQueryCtx,
614
+ ctx: CtxWith<"runQuery">,
621
615
  args: {
622
616
  namespace: string;
623
617
  key: string;
624
618
  /** The hash of the entry contents to try to match. */
625
619
  contentHash: string;
626
- }
620
+ },
627
621
  ): Promise<Entry<FitlerSchemas, EntryMetadata> | null> {
628
622
  const entry = await ctx.runQuery(this.component.entries.findByContentHash, {
629
623
  namespace: args.namespace,
630
624
  dimension: this.options.embeddingDimension,
631
625
  filterNames: this.options.filterNames ?? [],
632
- modelId: this.options.textEmbeddingModel.modelId,
626
+ modelId: getModelId(this.options.textEmbeddingModel),
633
627
  key: args.key,
634
628
  contentHash: args.contentHash,
635
629
  });
@@ -641,7 +635,7 @@ export class RAG<
641
635
  * filterNames of the RAG instance. If it doesn't exist, it will be created.
642
636
  */
643
637
  async getOrCreateNamespace(
644
- ctx: RunMutationCtx,
638
+ ctx: CtxWith<"runMutation">,
645
639
  args: {
646
640
  /**
647
641
  * The namespace to get or create. e.g. a userId if entries are per-user.
@@ -657,7 +651,7 @@ export class RAG<
657
651
  * along the way.
658
652
  */
659
653
  onComplete?: OnCompleteNamespace;
660
- }
654
+ },
661
655
  ): Promise<{
662
656
  namespaceId: NamespaceId;
663
657
  status: "pending" | "ready";
@@ -667,7 +661,7 @@ export class RAG<
667
661
  : undefined;
668
662
  assert(
669
663
  !onComplete || args.status === "pending",
670
- "You can only supply an onComplete handler for pending namespaces"
664
+ "You can only supply an onComplete handler for pending namespaces",
671
665
  );
672
666
  const { namespaceId, status } = await ctx.runMutation(
673
667
  this.component.namespaces.getOrCreate,
@@ -675,10 +669,10 @@ export class RAG<
675
669
  namespace: args.namespace,
676
670
  status: args.status ?? "ready",
677
671
  onComplete,
678
- modelId: this.options.textEmbeddingModel.modelId,
672
+ modelId: getModelId(this.options.textEmbeddingModel),
679
673
  dimension: this.options.embeddingDimension,
680
674
  filterNames: this.options.filterNames ?? [],
681
- }
675
+ },
682
676
  );
683
677
  return { namespaceId: namespaceId as NamespaceId, status };
684
678
  }
@@ -688,14 +682,14 @@ export class RAG<
688
682
  * filterNames of the RAG instance. If it doesn't exist, it will return null.
689
683
  */
690
684
  async getNamespace(
691
- ctx: RunQueryCtx,
685
+ ctx: CtxWith<"runQuery">,
692
686
  args: {
693
687
  namespace: string;
694
- }
688
+ },
695
689
  ): Promise<Namespace | null> {
696
690
  return ctx.runQuery(this.component.namespaces.get, {
697
691
  namespace: args.namespace,
698
- modelId: this.options.textEmbeddingModel.modelId,
692
+ modelId: getModelId(this.options.textEmbeddingModel),
699
693
  dimension: this.options.embeddingDimension,
700
694
  filterNames: this.options.filterNames ?? [],
701
695
  }) as Promise<Namespace | null>;
@@ -705,12 +699,12 @@ export class RAG<
705
699
  * List all chunks for an entry, paginated.
706
700
  */
707
701
  async listChunks(
708
- ctx: RunQueryCtx,
702
+ ctx: CtxWith<"runQuery">,
709
703
  args: {
710
704
  paginationOpts: PaginationOptions;
711
705
  entryId: EntryId;
712
706
  order?: "desc" | "asc";
713
- }
707
+ },
714
708
  ): Promise<PaginationResult<Chunk>> {
715
709
  return ctx.runQuery(this.component.chunks.list, {
716
710
  entryId: args.entryId,
@@ -722,7 +716,7 @@ export class RAG<
722
716
  /**
723
717
  * Delete an entry and all its chunks in the background using a workpool.
724
718
  */
725
- async deleteAsync(ctx: RunMutationCtx, args: { entryId: EntryId }) {
719
+ async deleteAsync(ctx: CtxWith<"runMutation">, args: { entryId: EntryId }) {
726
720
  await ctx.runMutation(this.component.entries.deleteAsync, {
727
721
  entryId: args.entryId,
728
722
  startOrder: 0,
@@ -735,17 +729,26 @@ export class RAG<
735
729
  * you're likely running this in a mutation.
736
730
  * Use `deleteAsync` or run `delete` in an action.
737
731
  */
738
- async delete(ctx: RunActionCtx, args: { entryId: EntryId }): Promise<void>;
732
+ async delete(
733
+ ctx: CtxWith<"runAction">,
734
+ args: { entryId: EntryId },
735
+ ): Promise<void>;
739
736
  /** @deprecated Use `deleteAsync` in mutations. */
740
- async delete(ctx: RunMutationCtx, args: { entryId: EntryId }): Promise<void>;
741
- async delete(ctx: RunActionCtx | RunMutationCtx, args: { entryId: EntryId }) {
737
+ async delete(
738
+ ctx: CtxWith<"runMutation">,
739
+ args: { entryId: EntryId },
740
+ ): Promise<void>;
741
+ async delete(
742
+ ctx: CtxWith<"runMutation"> | CtxWith<"runAction">,
743
+ args: { entryId: EntryId },
744
+ ) {
742
745
  if ("runAction" in ctx) {
743
746
  await ctx.runAction(this.component.entries.deleteSync, {
744
747
  entryId: args.entryId,
745
748
  });
746
749
  } else {
747
750
  console.warn(
748
- "You are running `rag.delete` in a mutation. This is deprecated. Use `rag.deleteAsync` from mutations, or `rag.delete` in actions."
751
+ "You are running `rag.delete` in a mutation. This is deprecated. Use `rag.deleteAsync` from mutations, or `rag.delete` in actions.",
749
752
  );
750
753
  await ctx.runMutation(this.component.entries.deleteAsync, {
751
754
  entryId: args.entryId,
@@ -758,8 +761,8 @@ export class RAG<
758
761
  * Delete all entries with a given key (asynchrounously).
759
762
  */
760
763
  async deleteByKeyAsync(
761
- ctx: RunMutationCtx,
762
- args: { namespaceId: NamespaceId; key: string; beforeVersion?: number }
764
+ ctx: CtxWith<"runMutation">,
765
+ args: { namespaceId: NamespaceId; key: string; beforeVersion?: number },
763
766
  ) {
764
767
  await ctx.runMutation(this.component.entries.deleteByKeyAsync, {
765
768
  namespaceId: args.namespaceId,
@@ -775,8 +778,8 @@ export class RAG<
775
778
  * Use `deleteByKeyAsync` or run `delete` in an action.
776
779
  */
777
780
  async deleteByKey(
778
- ctx: RunActionCtx,
779
- args: { namespaceId: NamespaceId; key: string; beforeVersion?: number }
781
+ ctx: CtxWith<"runAction">,
782
+ args: { namespaceId: NamespaceId; key: string; beforeVersion?: number },
780
783
  ) {
781
784
  await ctx.runAction(this.component.entries.deleteByKeySync, args);
782
785
  }
@@ -802,13 +805,9 @@ export class RAG<
802
805
  defineOnComplete<DataModel extends GenericDataModel>(
803
806
  fn: (
804
807
  ctx: GenericMutationCtx<DataModel>,
805
- args: FunctionArgs<OnComplete<FitlerSchemas, EntryMetadata>>
806
- ) => Promise<void>
807
- ): RegisteredMutation<
808
- "internal",
809
- FunctionArgs<OnComplete<FitlerSchemas, EntryMetadata>>,
810
- null
811
- > {
808
+ args: OnCompleteArgs<FitlerSchemas, EntryMetadata>,
809
+ ) => Promise<void>,
810
+ ): RegisteredMutation<"internal", FunctionArgs<OnComplete>, null> {
812
811
  return internalMutationGeneric({
813
812
  args: vOnCompleteArgs,
814
813
  handler: fn,
@@ -836,8 +835,11 @@ export class RAG<
836
835
  defineChunkerAction<DataModel extends GenericDataModel>(
837
836
  fn: (
838
837
  ctx: GenericActionCtx<DataModel>,
839
- args: { namespace: Namespace; entry: Entry<FitlerSchemas, EntryMetadata> }
840
- ) => AsyncIterable<InputChunk> | Promise<{ chunks: InputChunk[] }>
838
+ args: {
839
+ namespace: Namespace;
840
+ entry: Entry<FitlerSchemas, EntryMetadata>;
841
+ },
842
+ ) => AsyncIterable<InputChunk> | Promise<{ chunks: InputChunk[] }>,
841
843
  ): RegisteredAction<
842
844
  "internal",
843
845
  FunctionArgs<ChunkerAction>,
@@ -847,29 +849,30 @@ export class RAG<
847
849
  args: vChunkerArgs,
848
850
  handler: async (ctx, args) => {
849
851
  const { namespace, entry } = args;
850
- if (namespace.modelId !== this.options.textEmbeddingModel.modelId) {
852
+ const modelId = getModelId(this.options.textEmbeddingModel);
853
+ if (namespace.modelId !== modelId) {
851
854
  console.error(
852
- `You are using a different embedding model ${this.options.textEmbeddingModel.modelId} for asynchronously ` +
853
- `generating chunks than the one provided when it was started: ${namespace.modelId}`
855
+ `You are using a different embedding model ${modelId} for asynchronously ` +
856
+ `generating chunks than the one provided when it was started: ${namespace.modelId}`,
854
857
  );
855
858
  return;
856
859
  }
857
860
  if (namespace.dimension !== this.options.embeddingDimension) {
858
861
  console.error(
859
862
  `You are using a different embedding dimension ${this.options.embeddingDimension} for asynchronously ` +
860
- `generating chunks than the one provided when it was started: ${namespace.dimension}`
863
+ `generating chunks than the one provided when it was started: ${namespace.dimension}`,
861
864
  );
862
865
  return;
863
866
  }
864
867
  if (
865
868
  !filterNamesContain(
866
869
  namespace.filterNames,
867
- this.options.filterNames ?? []
870
+ this.options.filterNames ?? [],
868
871
  )
869
872
  ) {
870
873
  console.error(
871
874
  `You are using a different filters (${this.options.filterNames?.join(", ")}) for asynchronously ` +
872
- `generating chunks than the one provided when it was started: ${namespace.filterNames.join(", ")}`
875
+ `generating chunks than the one provided when it was started: ${namespace.filterNames.join(", ")}`,
873
876
  );
874
877
  return;
875
878
  }
@@ -891,23 +894,23 @@ export class RAG<
891
894
  let batchOrder = 0;
892
895
  for await (const batch of batchIterator(
893
896
  chunkIterator,
894
- CHUNK_BATCH_SIZE
897
+ CHUNK_BATCH_SIZE,
895
898
  )) {
896
899
  const result = await createChunkArgsBatch(
897
900
  this.options.textEmbeddingModel,
898
- batch
901
+ batch,
899
902
  );
900
903
  await ctx.runMutation(
901
904
  args.insertChunks as FunctionHandle<
902
905
  "mutation",
903
- FunctionArgs<RAGComponent["chunks"]["insert"]>,
906
+ FunctionArgs<ComponentApi["chunks"]["insert"]>,
904
907
  null
905
908
  >,
906
909
  {
907
910
  entryId: entry.entryId,
908
911
  startOrder: batchOrder,
909
912
  chunks: result.chunks,
910
- }
913
+ },
911
914
  );
912
915
  batchOrder += result.chunks.length;
913
916
  }
@@ -918,7 +921,7 @@ export class RAG<
918
921
 
919
922
  async function* batchIterator<T>(
920
923
  iterator: Iterable<T> | AsyncIterable<T>,
921
- batchSize: number
924
+ batchSize: number,
922
925
  ): AsyncIterable<T[]> {
923
926
  let batch: T[] = [];
924
927
  for await (const item of iterator) {
@@ -935,21 +938,21 @@ async function* batchIterator<T>(
935
938
 
936
939
  function validateAddFilterValues(
937
940
  filterValues: NamedFilter[] | undefined,
938
- filterNames: string[] | undefined
941
+ filterNames: string[] | undefined,
939
942
  ) {
940
943
  if (!filterValues) {
941
944
  return;
942
945
  }
943
946
  if (!filterNames) {
944
947
  throw new Error(
945
- "You must provide filter names to RAG to add entries with filters."
948
+ "You must provide filter names to RAG to add entries with filters.",
946
949
  );
947
950
  }
948
951
  const seen = new Set<string>();
949
952
  for (const filterValue of filterValues) {
950
953
  if (seen.has(filterValue.name)) {
951
954
  throw new Error(
952
- `You cannot provide the same filter name twice: ${filterValue.name}.`
955
+ `You cannot provide the same filter name twice: ${filterValue.name}.`,
953
956
  );
954
957
  }
955
958
  seen.add(filterValue.name);
@@ -957,7 +960,7 @@ function validateAddFilterValues(
957
960
  for (const filterName of filterNames) {
958
961
  if (!seen.has(filterName)) {
959
962
  throw new Error(
960
- `Filter name ${filterName} is not valid (one of ${filterNames.join(", ")}).`
963
+ `Filter name ${filterName} is not valid (one of ${filterNames.join(", ")}).`,
961
964
  );
962
965
  }
963
966
  }
@@ -973,7 +976,7 @@ function makeBatches<T>(items: T[], batchSize: number): T[][] {
973
976
 
974
977
  async function createChunkArgsBatch(
975
978
  embedModel: EmbeddingModel<string>,
976
- chunks: InputChunk[]
979
+ chunks: InputChunk[],
977
980
  ): Promise<{ chunks: CreateChunkArgs[]; usage: EmbeddingModelUsage }> {
978
981
  const argsMaybeMissingEmbeddings: (Omit<CreateChunkArgs, "embedding"> & {
979
982
  embedding?: number[];
@@ -1005,7 +1008,7 @@ async function createChunkArgsBatch(
1005
1008
  : {
1006
1009
  text: arg.content.text,
1007
1010
  index,
1008
- }
1011
+ },
1009
1012
  )
1010
1013
  .filter((b) => b !== null);
1011
1014
  const totalUsage: EmbeddingModelUsage = { tokens: 0 };
@@ -1191,3 +1194,45 @@ function getModelCategory(model: string | { provider: string }) {
1191
1194
  }
1192
1195
  return model;
1193
1196
  }
1197
+
1198
+ // fetch metadata from either a string or EmbeddingModelV2 or LanguageModelV2
1199
+ export type ModelOrMetadata =
1200
+ | string
1201
+ | ({ provider: string } & ({ modelId: string } | { model: string }));
1202
+
1203
+ export function getModelId(embeddingModel: ModelOrMetadata): string {
1204
+ if (typeof embeddingModel === "string") {
1205
+ if (embeddingModel.includes("/")) {
1206
+ return embeddingModel.split("/").slice(1).join("/");
1207
+ }
1208
+ return embeddingModel;
1209
+ }
1210
+ return "modelId" in embeddingModel
1211
+ ? embeddingModel.modelId
1212
+ : embeddingModel.model;
1213
+ }
1214
+
1215
+ export function getProviderName(embeddingModel: ModelOrMetadata): string {
1216
+ if (typeof embeddingModel === "string") {
1217
+ return embeddingModel.split("/").at(0)!;
1218
+ }
1219
+ return embeddingModel.provider;
1220
+ }
1221
+
1222
+ type CtxWith<T extends "runQuery" | "runMutation" | "runAction"> = Pick<
1223
+ {
1224
+ runQuery: <Query extends FunctionReference<"query", "internal">>(
1225
+ query: Query,
1226
+ args: FunctionArgs<Query>,
1227
+ ) => Promise<FunctionReturnType<Query>>;
1228
+ runMutation: <Mutation extends FunctionReference<"mutation", "internal">>(
1229
+ mutation: Mutation,
1230
+ args: FunctionArgs<Mutation>,
1231
+ ) => Promise<FunctionReturnType<Mutation>>;
1232
+ runAction: <Action extends FunctionReference<"action", "internal">>(
1233
+ action: Action,
1234
+ args: FunctionArgs<Action>,
1235
+ ) => Promise<FunctionReturnType<Action>>;
1236
+ },
1237
+ T
1238
+ >;
@@ -8,7 +8,7 @@ import {
8
8
  type GenericSchema,
9
9
  type SchemaDefinition,
10
10
  } from "convex/server";
11
- import { type RAGComponent } from "./index.js";
11
+ import { type ComponentApi } from "../component/_generated/component.js";
12
12
  import { componentsGeneric } from "convex/server";
13
13
  import componentSchema from "../component/schema.js";
14
14
  export { componentSchema };
@@ -22,7 +22,7 @@ export function initConvexTest<
22
22
  return t;
23
23
  }
24
24
  export const components = componentsGeneric() as unknown as {
25
- rag: RAGComponent;
25
+ rag: ComponentApi;
26
26
  };
27
27
 
28
28
  test("setup", () => {});