@mastra/voyageai 0.0.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.
package/CHANGELOG.md ADDED
@@ -0,0 +1,14 @@
1
+ # @mastra/voyageai
2
+
3
+ ## 0.1.0-alpha.0
4
+
5
+ ### Minor Changes
6
+
7
+ - feat(voyageai): add VoyageAI embeddings and reranker integration ([#14296](https://github.com/mastra-ai/mastra/pull/14296))
8
+
9
+ Adds the `@mastra/voyageai` package under `embedders/` with:
10
+ - Text embeddings (voyage-4 and voyage-3 series, plus code/finance/law models)
11
+ with token-aware batching via the SDK `tokenize()` method
12
+ - Multimodal embeddings (text + images + video) via voyage-multimodal-3/3.5
13
+ - Contextualized chunk embeddings via voyage-context-3
14
+ - Rerankers (rerank-2.5 and rerank-2 families) implementing `RelevanceScoreProvider`
package/README.md ADDED
@@ -0,0 +1,365 @@
1
+ # @mastra/voyageai
2
+
3
+ VoyageAI embeddings integration for Mastra. Provides text, multimodal, and contextualized chunk embeddings using the official VoyageAI TypeScript SDK.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @mastra/voyageai
9
+ # or
10
+ pnpm add @mastra/voyageai
11
+ ```
12
+
13
+ ## Configuration
14
+
15
+ Set your VoyageAI API key:
16
+
17
+ ```bash
18
+ export VOYAGE_API_KEY=your-api-key
19
+ ```
20
+
21
+ Or pass it directly in the configuration.
22
+
23
+ ## Usage
24
+
25
+ ### Text Embeddings
26
+
27
+ ```typescript
28
+ import { voyage, voyageEmbedding } from '@mastra/voyageai';
29
+
30
+ // Use default model (voyage-3.5)
31
+ const result = await voyage.doEmbed({ values: ['Hello world'] });
32
+ console.log(result.embeddings); // [[0.1, 0.2, ...]]
33
+
34
+ // Use specific model with options
35
+ const model = voyageEmbedding({
36
+ model: 'voyage-3-large',
37
+ inputType: 'query',
38
+ outputDimension: 512,
39
+ });
40
+ const queryResult = await model.doEmbed({ values: ['search query'] });
41
+ ```
42
+
43
+ ### Pre-configured Models
44
+
45
+ ```typescript
46
+ import { voyage } from '@mastra/voyageai';
47
+
48
+ // Voyage-4 series (highest throughput)
49
+ await voyage.v4large.doEmbed({ values: ['...'] }); // voyage-4-large (120k batch tokens)
50
+ await voyage.v4.doEmbed({ values: ['...'] }); // voyage-4 (320k batch tokens)
51
+ await voyage.v4lite.doEmbed({ values: ['...'] }); // voyage-4-lite (1M batch tokens)
52
+
53
+ // Voyage-3 series
54
+ await voyage.large.doEmbed({ values: ['...'] }); // voyage-3-large
55
+ await voyage.v35.doEmbed({ values: ['...'] }); // voyage-3.5
56
+ await voyage.v35lite.doEmbed({ values: ['...'] }); // voyage-3.5-lite
57
+ await voyage.code.doEmbed({ values: ['...'] }); // voyage-code-3
58
+ await voyage.finance.doEmbed({ values: ['...'] }); // voyage-finance-2
59
+ await voyage.law.doEmbed({ values: ['...'] }); // voyage-law-2
60
+ ```
61
+
62
+ ### With Mastra Memory
63
+
64
+ ```typescript
65
+ import { Memory } from '@mastra/memory';
66
+ import { PgVector } from '@mastra/pg';
67
+ import { voyage } from '@mastra/voyageai';
68
+
69
+ const memory = new Memory({
70
+ vector: new PgVector(connectionString),
71
+ embedder: voyage,
72
+ options: {
73
+ semanticRecall: { topK: 5 },
74
+ },
75
+ });
76
+ ```
77
+
78
+ ### VoyageAI-Specific Options
79
+
80
+ ```typescript
81
+ import { voyageEmbedding } from '@mastra/voyageai';
82
+
83
+ const model = voyageEmbedding({
84
+ model: 'voyage-3.5',
85
+ inputType: 'query', // 'query' | 'document' for retrieval optimization
86
+ outputDimension: 512, // 256 | 512 | 1024 | 2048
87
+ outputDtype: 'float', // 'float' | 'int8' | 'uint8' | 'binary' | 'ubinary'
88
+ truncation: true, // Handle long inputs
89
+ });
90
+ ```
91
+
92
+ ### Runtime Options Override
93
+
94
+ ```typescript
95
+ const result = await model.doEmbed({
96
+ values: ['query text'],
97
+ providerOptions: {
98
+ voyage: {
99
+ inputType: 'query',
100
+ outputDimension: 256,
101
+ },
102
+ },
103
+ });
104
+ ```
105
+
106
+ ## Multimodal Embeddings
107
+
108
+ Embed interleaved text + images + video (3.5 only):
109
+
110
+ ```typescript
111
+ import { voyageMultimodalEmbedding } from '@mastra/voyageai';
112
+
113
+ const multimodal = voyageMultimodalEmbedding('voyage-multimodal-3.5');
114
+
115
+ const result = await multimodal.doEmbed({
116
+ values: [
117
+ {
118
+ content: [
119
+ { type: 'text', text: 'A photo of a cat' },
120
+ { type: 'image_url', image_url: 'https://example.com/cat.jpg' },
121
+ // video_url supported on voyage-multimodal-3.5
122
+ ],
123
+ },
124
+ ],
125
+ });
126
+
127
+ // Use with vector store directly
128
+ await vectorStore.upsert({
129
+ vectors: result.embeddings,
130
+ metadata: [{ description: 'cat photo' }],
131
+ });
132
+ ```
133
+
134
+ ### Content Types
135
+
136
+ - `{ type: 'text', text: string }` - Text content
137
+ - `{ type: 'image_url', image_url: string }` - Image from URL
138
+ - `{ type: 'image_base64', image_base64: string }` - Base64-encoded image
139
+ - `{ type: 'video_url', video_url: string }` - Video from URL (3.5 only)
140
+
141
+ ## Contextualized Chunk Embeddings
142
+
143
+ Embed chunks with document context to avoid "context loss":
144
+
145
+ ```typescript
146
+ import { voyageContextualizedEmbedding } from '@mastra/voyageai';
147
+
148
+ const contextual = voyageContextualizedEmbedding('voyage-context-3');
149
+
150
+ // Embed document chunks (inner arrays = chunks from same document)
151
+ const result = await contextual.doEmbed({
152
+ values: [['Paragraph 1 from doc 1...', 'Paragraph 2 from doc 1...'], ['Content from doc 2...']],
153
+ inputType: 'document',
154
+ });
155
+
156
+ // Returns embeddings for each chunk, preserving document context
157
+ console.log(result.embeddings.length); // 3 (2 from doc 1, 1 from doc 2)
158
+ console.log(result.chunkCounts); // [2, 1]
159
+
160
+ // Query embedding
161
+ const queryEmbedding = await contextual.embedQuery('What was the revenue?');
162
+ ```
163
+
164
+ ### Helper Methods
165
+
166
+ ```typescript
167
+ // Embed a query
168
+ const queryEmbedding = await contextual.embedQuery('search query');
169
+
170
+ // Embed chunks from a single document
171
+ const docEmbeddings = await contextual.embedDocumentChunks(['First paragraph...', 'Second paragraph...']);
172
+
173
+ // Get embeddings grouped by document
174
+ const grouped = await contextual.doEmbedGrouped({
175
+ values: [['chunk1', 'chunk2'], ['chunk3']],
176
+ inputType: 'document',
177
+ });
178
+ console.log(grouped.embeddingsByDocument); // [[[...], [...]], [[...]]]
179
+ ```
180
+
181
+ ## Available Models
182
+
183
+ ### Text Embedding Models
184
+
185
+ | Model | Use Case | Dimensions | Batch Tokens |
186
+ | ------------------ | --------------------------------------- | ----------------- | ------------ |
187
+ | `voyage-4-large` | Best quality, highest batch capacity | 256/512/1024/2048 | 120k |
188
+ | `voyage-4` | Balanced quality/speed, high throughput | 256/512/1024/2048 | 320k |
189
+ | `voyage-4-lite` | Maximum throughput | 256/512/1024/2048 | 1M |
190
+ | `voyage-3-large` | Best quality, multilingual | 256/512/1024/2048 | 120k |
191
+ | `voyage-3.5` | Balanced quality/speed | 256/512/1024/2048 | 320k |
192
+ | `voyage-3.5-lite` | Lowest latency/cost | 256/512/1024/2048 | 1M |
193
+ | `voyage-code-3` | Code retrieval | 256/512/1024/2048 | 32k |
194
+ | `voyage-finance-2` | Finance domain | 1024 | 32k |
195
+ | `voyage-law-2` | Legal domain | 1024 | 32k |
196
+
197
+ ### Multimodal Models
198
+
199
+ | Model | Capabilities |
200
+ | ----------------------- | --------------------- |
201
+ | `voyage-multimodal-3` | Text + images |
202
+ | `voyage-multimodal-3.5` | Text + images + video |
203
+
204
+ ### Contextualized Models
205
+
206
+ | Model | Use Case |
207
+ | ------------------ | ---------------------------- |
208
+ | `voyage-context-3` | Chunks with document context |
209
+
210
+ ### Reranker Models
211
+
212
+ | Model | Context Length | Description |
213
+ | ----------------- | -------------- | --------------------------------------- |
214
+ | `rerank-2.5` | 32000 | Best quality with instruction-following |
215
+ | `rerank-2.5-lite` | 32000 | Optimized for latency and quality |
216
+ | `rerank-2` | 16000 | Second-gen with multilingual support |
217
+ | `rerank-2-lite` | 8000 | Second-gen, latency-optimized |
218
+ | `rerank-1` | 8000 | First-gen, quality-focused |
219
+ | `rerank-lite-1` | 4000 | First-gen, latency-optimized |
220
+
221
+ ## Reranking
222
+
223
+ VoyageAI rerankers implement the `RelevanceScoreProvider` interface for use with Mastra's reranking system.
224
+
225
+ ### Basic Usage
226
+
227
+ ```typescript
228
+ import { voyage, voyageReranker, createVoyageReranker } from '@mastra/voyageai';
229
+
230
+ // Use pre-configured reranker (rerank-2.5)
231
+ const defaultReranker = voyage.reranker;
232
+
233
+ // Or create with specific model
234
+ const liteReranker = createVoyageReranker('rerank-2.5-lite');
235
+
236
+ // Or with full config
237
+ const customReranker = createVoyageReranker({
238
+ model: 'rerank-2.5',
239
+ truncation: true,
240
+ });
241
+ ```
242
+
243
+ ### Get Relevance Score
244
+
245
+ ```typescript
246
+ // Score a single document against a query
247
+ const score = await reranker.getRelevanceScore(
248
+ 'What is machine learning?',
249
+ 'Machine learning is a subset of artificial intelligence...',
250
+ );
251
+ console.log(score); // 0.85
252
+ ```
253
+
254
+ ### Rerank Multiple Documents
255
+
256
+ ```typescript
257
+ // Rerank multiple documents efficiently in one API call
258
+ const results = await reranker.rerankDocuments(
259
+ 'What is the capital of France?',
260
+ ['Paris is the capital of France.', 'London is the capital of England.', 'Berlin is the capital of Germany.'],
261
+ 2, // topK - optional
262
+ );
263
+
264
+ // Results sorted by relevance
265
+ console.log(results);
266
+ // [
267
+ // { document: 'Paris is the capital of France.', index: 0, score: 0.95 },
268
+ // { document: 'Berlin is the capital of Germany.', index: 2, score: 0.32 },
269
+ // ]
270
+ ```
271
+
272
+ ### With Mastra RAG
273
+
274
+ ```typescript
275
+ import { createVectorQueryTool } from '@mastra/rag';
276
+ import { voyage } from '@mastra/voyageai';
277
+
278
+ const tool = createVectorQueryTool({
279
+ vectorStore,
280
+ model: voyage, // Embedder
281
+ reranker: {
282
+ model: voyage.reranker, // VoyageAI reranker
283
+ options: { topK: 5 },
284
+ },
285
+ });
286
+ ```
287
+
288
+ ### Pre-configured Reranker Models
289
+
290
+ ```typescript
291
+ import { voyage } from '@mastra/voyageai';
292
+
293
+ // Default reranker (rerank-2.5)
294
+ voyage.reranker;
295
+
296
+ // Specific models
297
+ voyage.reranker25; // rerank-2.5
298
+ voyage.reranker25lite; // rerank-2.5-lite
299
+ voyage.reranker2; // rerank-2
300
+ voyage.reranker2lite; // rerank-2-lite
301
+
302
+ // Create custom
303
+ voyage.createReranker({ model: 'rerank-1', truncation: false });
304
+ ```
305
+
306
+ ## AI SDK Compatibility
307
+
308
+ The package exports models compatible with both AI SDK v5 (V2) and v6 (V3):
309
+
310
+ ```typescript
311
+ // V3 (default, AI SDK v6)
312
+ const v3Model = voyageEmbedding('voyage-3.5');
313
+ v3Model.specificationVersion; // 'v3'
314
+
315
+ // V2 (AI SDK v5)
316
+ const v2Model = voyageEmbeddingV2('voyage-3.5');
317
+ v2Model.specificationVersion; // 'v2'
318
+
319
+ // Pre-configured V2 models
320
+ voyage.largeV2; // voyage-3-large with V2 interface
321
+ voyage.v35V2; // voyage-3.5 with V2 interface
322
+ ```
323
+
324
+ ## API Reference
325
+
326
+ ### Types
327
+
328
+ ```typescript
329
+ type VoyageTextModel =
330
+ | 'voyage-4-large'
331
+ | 'voyage-4'
332
+ | 'voyage-4-lite'
333
+ | 'voyage-3-large'
334
+ | 'voyage-3.5'
335
+ | 'voyage-3.5-lite'
336
+ | 'voyage-code-3'
337
+ | 'voyage-finance-2'
338
+ | 'voyage-law-2';
339
+
340
+ type VoyageMultimodalModel = 'voyage-multimodal-3' | 'voyage-multimodal-3.5';
341
+
342
+ type VoyageContextModel = 'voyage-context-3';
343
+
344
+ type VoyageInputType = 'query' | 'document' | null;
345
+ type VoyageOutputDimension = 256 | 512 | 1024 | 2048;
346
+ type VoyageOutputDtype = 'float' | 'int8' | 'uint8' | 'binary' | 'ubinary';
347
+
348
+ type VoyageRerankerModel =
349
+ | 'rerank-2.5'
350
+ | 'rerank-2.5-lite'
351
+ | 'rerank-2'
352
+ | 'rerank-2-lite'
353
+ | 'rerank-1'
354
+ | 'rerank-lite-1';
355
+
356
+ interface VoyageRerankerConfig {
357
+ model: VoyageRerankerModel;
358
+ apiKey?: string;
359
+ truncation?: boolean;
360
+ }
361
+ ```
362
+
363
+ ## License
364
+
365
+ Apache-2.0
@@ -0,0 +1,137 @@
1
+ /**
2
+ * VoyageAI Contextualized Chunk Embedding Model
3
+ *
4
+ * Embeds text chunks with document context, addressing the "context loss" problem
5
+ * that occurs when documents are split into individual chunks.
6
+ *
7
+ * Each chunk receives an embedding that reflects both its independent meaning
8
+ * AND its position within the broader document context.
9
+ */
10
+ import type { VoyageContextModel, VoyageContextualizedEmbeddingConfig, VoyageProviderOptions, VoyageInputType, VoyageOutputDimension, VoyageOutputDtype } from './types.js';
11
+ /**
12
+ * VoyageAI Contextualized Chunk Embedding Model
13
+ *
14
+ * Note: This does NOT implement EmbeddingModelV2<string> because contextualized
15
+ * inputs have a different structure (string[][] vs string[]).
16
+ *
17
+ * Input format: Nested lists where each inner list contains related chunks
18
+ * from the same document. Example:
19
+ * ```
20
+ * [
21
+ * ['chunk1_from_doc1', 'chunk2_from_doc1'], // Document 1 chunks
22
+ * ['chunk1_from_doc2', 'chunk2_from_doc2'], // Document 2 chunks
23
+ * ]
24
+ * ```
25
+ *
26
+ * @example
27
+ * ```typescript
28
+ * const model = new VoyageContextualizedEmbeddingModel({ model: 'voyage-context-3' });
29
+ *
30
+ * // Embed document chunks with context
31
+ * const docResult = await model.doEmbed({
32
+ * values: [
33
+ * ['Leafy Inc Q2 2024...', 'Revenue grew 15%...'],
34
+ * ['Acme Corp announced...', 'The merger will...']
35
+ * ],
36
+ * inputType: 'document',
37
+ * });
38
+ *
39
+ * // Embed a query (single item per inner list)
40
+ * const queryResult = await model.doEmbed({
41
+ * values: [['What was Leafy Inc revenue growth?']],
42
+ * inputType: 'query',
43
+ * });
44
+ * ```
45
+ */
46
+ export declare class VoyageContextualizedEmbeddingModel {
47
+ readonly provider: "voyage";
48
+ readonly modelId: string;
49
+ readonly maxEmbeddingsPerCall = 1000;
50
+ readonly maxTotalChunks = 16000;
51
+ readonly supportsParallelCalls = true;
52
+ private client;
53
+ private config;
54
+ constructor(config: VoyageContextualizedEmbeddingConfig);
55
+ /**
56
+ * Generate contextualized embeddings for grouped chunks
57
+ *
58
+ * @param args.values - Nested array where each inner array contains chunks from the same document
59
+ * @param args.inputType - 'query' for search queries, 'document' for content being indexed
60
+ * @param args.outputDimension - Output embedding dimension (256, 512, 1024, or 2048)
61
+ * @param args.outputDtype - Output data type
62
+ * @param args.providerOptions - Runtime options to override config
63
+ * @returns Object containing flattened embeddings array (one per chunk across all documents)
64
+ */
65
+ doEmbed(args: {
66
+ values: string[][];
67
+ inputType?: VoyageInputType;
68
+ outputDimension?: VoyageOutputDimension;
69
+ outputDtype?: VoyageOutputDtype;
70
+ abortSignal?: AbortSignal;
71
+ headers?: Record<string, string>;
72
+ providerOptions?: VoyageProviderOptions;
73
+ }): Promise<{
74
+ embeddings: number[][];
75
+ chunkCounts: number[];
76
+ }>;
77
+ /**
78
+ * Generate contextualized embeddings and return grouped by document
79
+ *
80
+ * @param args - Same as doEmbed
81
+ * @returns Embeddings grouped by document
82
+ */
83
+ doEmbedGrouped(args: {
84
+ values: string[][];
85
+ inputType?: VoyageInputType;
86
+ outputDimension?: VoyageOutputDimension;
87
+ outputDtype?: VoyageOutputDtype;
88
+ abortSignal?: AbortSignal;
89
+ headers?: Record<string, string>;
90
+ providerOptions?: VoyageProviderOptions;
91
+ }): Promise<{
92
+ embeddingsByDocument: number[][][];
93
+ }>;
94
+ /**
95
+ * Generate a query embedding (contextualized with itself)
96
+ *
97
+ * @param query - The search query text
98
+ * @returns Single embedding vector
99
+ */
100
+ embedQuery(query: string): Promise<number[]>;
101
+ /**
102
+ * Generate document chunk embeddings with context
103
+ *
104
+ * @param chunks - Array of text chunks from the same document
105
+ * @returns Array of embeddings, one per chunk
106
+ */
107
+ embedDocumentChunks(chunks: string[]): Promise<number[][]>;
108
+ }
109
+ /**
110
+ * Create a VoyageAI contextualized chunk embedding model
111
+ *
112
+ * @param config - Model configuration or model name string
113
+ * @returns VoyageContextualizedEmbeddingModel instance
114
+ *
115
+ * @example
116
+ * ```typescript
117
+ * // With model name only
118
+ * const model = createVoyageContextualizedEmbedding('voyage-context-3');
119
+ *
120
+ * // With full config
121
+ * const model = createVoyageContextualizedEmbedding({
122
+ * model: 'voyage-context-3',
123
+ * outputDimension: 512,
124
+ * });
125
+ *
126
+ * // Embed document chunks with context
127
+ * const result = await model.doEmbed({
128
+ * values: [
129
+ * ['First paragraph of doc 1...', 'Second paragraph...'],
130
+ * ['Content from doc 2...']
131
+ * ],
132
+ * inputType: 'document',
133
+ * });
134
+ * ```
135
+ */
136
+ export declare function createVoyageContextualizedEmbedding(config: VoyageContextualizedEmbeddingConfig | VoyageContextModel): VoyageContextualizedEmbeddingModel;
137
+ //# sourceMappingURL=contextualized-embedding.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contextualized-embedding.d.ts","sourceRoot":"","sources":["../src/contextualized-embedding.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,KAAK,EACV,kBAAkB,EAClB,mCAAmC,EACnC,qBAAqB,EACrB,eAAe,EACf,qBAAqB,EACrB,iBAAiB,EAClB,MAAM,SAAS,CAAC;AAUjB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,qBAAa,kCAAkC;IAC7C,QAAQ,CAAC,QAAQ,EAAG,QAAQ,CAAU;IACtC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,oBAAoB,QAAQ;IACrC,QAAQ,CAAC,cAAc,SAAS;IAChC,QAAQ,CAAC,qBAAqB,QAAQ;IAEtC,OAAO,CAAC,MAAM,CAAiB;IAC/B,OAAO,CAAC,MAAM,CAAsC;gBAExC,MAAM,EAAE,mCAAmC;IAcvD;;;;;;;;;OASG;IACG,OAAO,CAAC,IAAI,EAAE;QAClB,MAAM,EAAE,MAAM,EAAE,EAAE,CAAC;QACnB,SAAS,CAAC,EAAE,eAAe,CAAC;QAC5B,eAAe,CAAC,EAAE,qBAAqB,CAAC;QACxC,WAAW,CAAC,EAAE,iBAAiB,CAAC;QAChC,WAAW,CAAC,EAAE,WAAW,CAAC;QAC1B,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QACjC,eAAe,CAAC,EAAE,qBAAqB,CAAC;KACzC,GAAG,OAAO,CAAC;QAAE,UAAU,EAAE,MAAM,EAAE,EAAE,CAAC;QAAC,WAAW,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC;IAsC9D;;;;;OAKG;IACG,cAAc,CAAC,IAAI,EAAE;QACzB,MAAM,EAAE,MAAM,EAAE,EAAE,CAAC;QACnB,SAAS,CAAC,EAAE,eAAe,CAAC;QAC5B,eAAe,CAAC,EAAE,qBAAqB,CAAC;QACxC,WAAW,CAAC,EAAE,iBAAiB,CAAC;QAChC,WAAW,CAAC,EAAE,WAAW,CAAC;QAC1B,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QACjC,eAAe,CAAC,EAAE,qBAAqB,CAAC;KACzC,GAAG,OAAO,CAAC;QAAE,oBAAoB,EAAE,MAAM,EAAE,EAAE,EAAE,CAAA;KAAE,CAAC;IA2BnD;;;;;OAKG;IACG,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAQlD;;;;;OAKG;IACG,mBAAmB,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC;CAOjE;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,mCAAmC,CACjD,MAAM,EAAE,mCAAmC,GAAG,kBAAkB,GAC/D,kCAAkC,CAGpC"}