voyageai-cli 1.20.6 → 1.22.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 (42) hide show
  1. package/CHANGELOG.md +142 -26
  2. package/README.md +130 -2
  3. package/package.json +3 -2
  4. package/src/cli.js +10 -0
  5. package/src/commands/bug.js +249 -0
  6. package/src/commands/eval.js +420 -10
  7. package/src/commands/generate.js +220 -0
  8. package/src/commands/playground.js +93 -0
  9. package/src/commands/purge.js +271 -0
  10. package/src/commands/refresh.js +322 -0
  11. package/src/commands/scaffold.js +217 -0
  12. package/src/lib/codegen.js +339 -0
  13. package/src/lib/explanations.js +155 -0
  14. package/src/lib/scaffold-structure.js +114 -0
  15. package/src/lib/templates/nextjs/README.md.tpl +106 -0
  16. package/src/lib/templates/nextjs/env.example.tpl +8 -0
  17. package/src/lib/templates/nextjs/layout.jsx.tpl +29 -0
  18. package/src/lib/templates/nextjs/lib-mongo.js.tpl +111 -0
  19. package/src/lib/templates/nextjs/lib-voyage.js.tpl +103 -0
  20. package/src/lib/templates/nextjs/package.json.tpl +33 -0
  21. package/src/lib/templates/nextjs/page-search.jsx.tpl +147 -0
  22. package/src/lib/templates/nextjs/route-ingest.js.tpl +114 -0
  23. package/src/lib/templates/nextjs/route-search.js.tpl +97 -0
  24. package/src/lib/templates/nextjs/theme.js.tpl +84 -0
  25. package/src/lib/templates/python/README.md.tpl +145 -0
  26. package/src/lib/templates/python/app.py.tpl +221 -0
  27. package/src/lib/templates/python/chunker.py.tpl +127 -0
  28. package/src/lib/templates/python/env.example.tpl +12 -0
  29. package/src/lib/templates/python/mongo_client.py.tpl +125 -0
  30. package/src/lib/templates/python/requirements.txt.tpl +10 -0
  31. package/src/lib/templates/python/voyage_client.py.tpl +124 -0
  32. package/src/lib/templates/vanilla/README.md.tpl +156 -0
  33. package/src/lib/templates/vanilla/client.js.tpl +103 -0
  34. package/src/lib/templates/vanilla/connection.js.tpl +126 -0
  35. package/src/lib/templates/vanilla/env.example.tpl +11 -0
  36. package/src/lib/templates/vanilla/ingest.js.tpl +231 -0
  37. package/src/lib/templates/vanilla/package.json.tpl +31 -0
  38. package/src/lib/templates/vanilla/retrieval.js.tpl +100 -0
  39. package/src/lib/templates/vanilla/search-api.js.tpl +175 -0
  40. package/src/lib/templates/vanilla/server.js.tpl +81 -0
  41. package/src/lib/zip.js +130 -0
  42. package/src/playground/index.html +708 -3
@@ -1065,6 +1065,140 @@ const concepts = {
1065
1065
  'vai eval --mode rerank --test-set test.jsonl --json',
1066
1066
  ],
1067
1067
  },
1068
+
1069
+ 'code-generation': {
1070
+ title: 'Code Generation — vai generate',
1071
+ summary: 'Generate production code from your vai configuration',
1072
+ content: [
1073
+ `${pc.bold('What is vai generate?')}`,
1074
+ `The ${pc.cyan('vai generate')} command outputs production-ready code snippets that you can`,
1075
+ `pipe directly into your project files. It reads your ${pc.cyan('.vai.json')} configuration`,
1076
+ `and generates code pre-configured with your model, database, and collection settings.`,
1077
+ ``,
1078
+ `${pc.bold('Available components:')}`,
1079
+ ` ${pc.dim('•')} ${pc.cyan('client')} — Voyage AI API client (embed, rerank functions)`,
1080
+ ` ${pc.dim('•')} ${pc.cyan('connection')} — MongoDB connection helper with vector search`,
1081
+ ` ${pc.dim('•')} ${pc.cyan('retrieval')} — Full RAG retrieval: embed → search → rerank`,
1082
+ ` ${pc.dim('•')} ${pc.cyan('ingest')} — Document ingestion: chunk → embed → store`,
1083
+ ` ${pc.dim('•')} ${pc.cyan('search-api')} — HTTP endpoints for search and ingest`,
1084
+ ``,
1085
+ `${pc.bold('Supported targets:')}`,
1086
+ ` ${pc.dim('•')} ${pc.cyan('vanilla')} — Node.js + Express (default)`,
1087
+ ` ${pc.dim('•')} ${pc.cyan('nextjs')} — Next.js 14 + App Router + Material UI`,
1088
+ ` ${pc.dim('•')} ${pc.cyan('python')} — Python + Flask`,
1089
+ ``,
1090
+ `${pc.bold('Auto-detection:')}`,
1091
+ `vai automatically detects your project type from:`,
1092
+ ` ${pc.dim('•')} ${pc.cyan('next.config.js')} → nextjs`,
1093
+ ` ${pc.dim('•')} ${pc.cyan('requirements.txt')} → python`,
1094
+ ` ${pc.dim('•')} ${pc.cyan('package.json')} → vanilla`,
1095
+ ``,
1096
+ `${pc.bold('Pipe-friendly:')}`,
1097
+ `Output goes to stdout, so you can pipe directly to files:`,
1098
+ ` ${pc.cyan('vai generate retrieval > lib/retrieval.js')}`,
1099
+ ].join('\n'),
1100
+ links: ['https://github.com/mrlynn/voyageai-cli#code-generation--scaffolding'],
1101
+ tryIt: [
1102
+ 'vai generate --list',
1103
+ 'vai generate client --target python',
1104
+ 'vai generate retrieval > lib/retrieval.js',
1105
+ 'vai generate search-api --json',
1106
+ ],
1107
+ },
1108
+
1109
+ scaffolding: {
1110
+ title: 'Project Scaffolding — vai scaffold',
1111
+ summary: 'Create complete starter projects with one command',
1112
+ content: [
1113
+ `${pc.bold('What is vai scaffold?')}`,
1114
+ `The ${pc.cyan('vai scaffold')} command creates a complete, ready-to-run project directory`,
1115
+ `with all the files you need for a Voyage AI RAG application. It reads your`,
1116
+ `${pc.cyan('.vai.json')} and generates everything pre-configured.`,
1117
+ ``,
1118
+ `${pc.bold('What you get:')}`,
1119
+ ` ${pc.dim('•')} Server entry point (Express, Next.js, or Flask)`,
1120
+ ` ${pc.dim('•')} Voyage AI client library`,
1121
+ ` ${pc.dim('•')} MongoDB connection helper with vector search`,
1122
+ ` ${pc.dim('•')} RAG retrieval module with optional reranking`,
1123
+ ` ${pc.dim('•')} Document ingestion pipeline`,
1124
+ ` ${pc.dim('•')} API routes for search and ingest`,
1125
+ ` ${pc.dim('•')} ${pc.cyan('.env.example')} with required variables`,
1126
+ ` ${pc.dim('•')} ${pc.cyan('README.md')} with setup instructions`,
1127
+ ` ${pc.dim('•')} ${pc.cyan('package.json')} or ${pc.cyan('requirements.txt')}`,
1128
+ ``,
1129
+ `${pc.bold('Available targets:')}`,
1130
+ ` ${pc.dim('•')} ${pc.cyan('vanilla')} — Node.js + Express (9 files)`,
1131
+ ` ${pc.dim('•')} ${pc.cyan('nextjs')} — Next.js + MUI search UI (13 files)`,
1132
+ ` ${pc.dim('•')} ${pc.cyan('python')} — Flask API server (8 files)`,
1133
+ ``,
1134
+ `${pc.bold('Configuration:')}`,
1135
+ `All generated files use your ${pc.cyan('.vai.json')} settings:`,
1136
+ ` ${pc.dim('•')} Embedding model and dimensions`,
1137
+ ` ${pc.dim('•')} Database and collection names`,
1138
+ ` ${pc.dim('•')} Vector index name and field`,
1139
+ ` ${pc.dim('•')} Chunking strategy, size, and overlap`,
1140
+ ` ${pc.dim('•')} Reranking (enabled/disabled, model)`,
1141
+ ``,
1142
+ `${pc.bold('Preview mode:')}`,
1143
+ `Use ${pc.cyan('--dry-run')} to see what would be created without writing files.`,
1144
+ ].join('\n'),
1145
+ links: ['https://github.com/mrlynn/voyageai-cli#code-generation--scaffolding'],
1146
+ tryIt: [
1147
+ 'vai scaffold my-rag-api',
1148
+ 'vai scaffold my-app --target nextjs',
1149
+ 'vai scaffold flask-api --target python',
1150
+ 'vai scaffold test-project --dry-run',
1151
+ ],
1152
+ },
1153
+
1154
+ 'eval-comparison': {
1155
+ title: 'Evaluation Comparison — vai eval compare',
1156
+ summary: 'Compare configurations and track quality over time',
1157
+ content: [
1158
+ `${pc.bold('Comparing configurations')}`,
1159
+ `The ${pc.cyan('vai eval compare')} subcommand runs the same test set against multiple`,
1160
+ `configurations and shows a side-by-side comparison of metrics.`,
1161
+ ``,
1162
+ `${pc.bold('Config file format (JSON):')}`,
1163
+ ` ${pc.dim('{')}`,
1164
+ ` ${pc.dim('"name": "Large + Rerank",')}`,
1165
+ ` ${pc.dim('"model": "voyage-4-large",')}`,
1166
+ ` ${pc.dim('"rerank": true,')}`,
1167
+ ` ${pc.dim('"rerankModel": "rerank-2.5",')}`,
1168
+ ` ${pc.dim('"dimensions": 1024')}`,
1169
+ ` ${pc.dim('}')}`,
1170
+ ``,
1171
+ `${pc.bold('Comparison output:')}`,
1172
+ `Shows a table with best values highlighted in green:`,
1173
+ ` ${pc.dim('Config MRR NDCG@5 R@5')}`,
1174
+ ` ${pc.dim('baseline 0.69 0.78 0.82')}`,
1175
+ ` ${pc.green('experiment 0.75 0.81 0.84')} ← best`,
1176
+ ``,
1177
+ `${pc.bold('Saving results:')}`,
1178
+ `Use ${pc.cyan('--save <path>')} to persist results for later comparison:`,
1179
+ ` ${pc.cyan('vai eval --test-set test.jsonl --save baseline.json')}`,
1180
+ ``,
1181
+ `${pc.bold('Baseline comparison:')}`,
1182
+ `Use ${pc.cyan('--baseline <path>')} to compare against a previous run:`,
1183
+ ` ${pc.cyan('vai eval --test-set test.jsonl --baseline baseline.json')}`,
1184
+ ``,
1185
+ `Shows deltas with color-coded improvement/regression:`,
1186
+ ` ${pc.dim('MRR 0.7523 vs 0.6891')} ${pc.green('+0.0632 (+9.2%)')}`,
1187
+ ` ${pc.dim('NDCG@5 0.8102 vs 0.7845')} ${pc.green('+0.0257 (+3.3%)')}`,
1188
+ ``,
1189
+ `${pc.bold('Use cases:')}`,
1190
+ ` ${pc.dim('•')} A/B test embedding models (voyage-4-large vs voyage-4-lite)`,
1191
+ ` ${pc.dim('•')} Compare rerank on/off`,
1192
+ ` ${pc.dim('•')} Track quality after chunking changes`,
1193
+ ` ${pc.dim('•')} CI/CD regression testing`,
1194
+ ].join('\n'),
1195
+ links: ['https://github.com/mrlynn/voyageai-cli#evaluation'],
1196
+ tryIt: [
1197
+ 'vai eval --test-set test.jsonl --save baseline.json',
1198
+ 'vai eval --test-set test.jsonl --baseline baseline.json',
1199
+ 'vai eval compare --test-set test.jsonl --configs baseline.json,experiment.json',
1200
+ ],
1201
+ },
1068
1202
  };
1069
1203
 
1070
1204
  /**
@@ -1165,6 +1299,27 @@ const aliases = {
1165
1299
  recall: 'rerank-eval',
1166
1300
  mrr: 'rerank-eval',
1167
1301
  'eval-rerank': 'rerank-eval',
1302
+ // Code generation aliases
1303
+ generate: 'code-generation',
1304
+ 'code-generation': 'code-generation',
1305
+ codegen: 'code-generation',
1306
+ 'vai-generate': 'code-generation',
1307
+ templates: 'code-generation',
1308
+ // Scaffolding aliases
1309
+ scaffold: 'scaffolding',
1310
+ scaffolding: 'scaffolding',
1311
+ 'vai-scaffold': 'scaffolding',
1312
+ 'project-scaffold': 'scaffolding',
1313
+ 'starter-project': 'scaffolding',
1314
+ boilerplate: 'scaffolding',
1315
+ // Eval comparison aliases
1316
+ 'eval-comparison': 'eval-comparison',
1317
+ 'eval-compare': 'eval-comparison',
1318
+ compare: 'eval-comparison',
1319
+ baseline: 'eval-comparison',
1320
+ 'save-results': 'eval-comparison',
1321
+ 'a-b-test': 'eval-comparison',
1322
+ regression: 'eval-comparison',
1168
1323
  // Provider comparison aliases
1169
1324
  'provider-comparison': 'provider-comparison',
1170
1325
  providers: 'provider-comparison',
@@ -0,0 +1,114 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Project structure definitions for each target.
5
+ * Maps template names to output paths within the project.
6
+ *
7
+ * Separated from scaffold.js to avoid @clack/prompts dependency
8
+ * when loaded from Electron.
9
+ */
10
+ const PROJECT_STRUCTURE = {
11
+ vanilla: {
12
+ files: [
13
+ { template: 'package.json', output: 'package.json' },
14
+ { template: 'env.example', output: '.env.example' },
15
+ { template: 'README.md', output: 'README.md' },
16
+ { template: 'server.js', output: 'server.js' },
17
+ { template: 'client.js', output: 'lib/client.js' },
18
+ { template: 'connection.js', output: 'lib/connection.js' },
19
+ { template: 'retrieval.js', output: 'lib/retrieval.js' },
20
+ { template: 'ingest.js', output: 'lib/ingest.js' },
21
+ { template: 'search-api.js', output: 'lib/search-api.js' },
22
+ ],
23
+ description: 'Node.js + Express API server',
24
+ postInstall: 'npm install',
25
+ startCommand: 'npm start',
26
+ },
27
+
28
+ nextjs: {
29
+ files: [
30
+ { template: 'package.json', output: 'package.json' },
31
+ { template: 'env.example', output: '.env.example' },
32
+ { template: 'README.md', output: 'README.md' },
33
+ { template: 'layout.jsx', output: 'app/layout.jsx' },
34
+ { template: 'page-search.jsx', output: 'app/search/page.jsx' },
35
+ { template: 'route-search.js', output: 'app/api/search/route.js' },
36
+ { template: 'route-ingest.js', output: 'app/api/ingest/route.js' },
37
+ { template: 'lib-voyage.js', output: 'lib/voyage.js' },
38
+ { template: 'lib-mongo.js', output: 'lib/mongodb.js' },
39
+ { template: 'theme.js', output: 'lib/theme.js' },
40
+ ],
41
+ extraFiles: [
42
+ {
43
+ output: 'app/page.jsx',
44
+ content: `'use client';
45
+ import { redirect } from 'next/navigation';
46
+ export default function Home() {
47
+ redirect('/search');
48
+ }
49
+ `,
50
+ },
51
+ {
52
+ output: 'next.config.js',
53
+ content: `/** @type {import('next').NextConfig} */
54
+ const nextConfig = {
55
+ experimental: {
56
+ serverComponentsExternalPackages: ['mongodb'],
57
+ },
58
+ };
59
+ module.exports = nextConfig;
60
+ `,
61
+ },
62
+ {
63
+ output: '.gitignore',
64
+ content: `node_modules/
65
+ .next/
66
+ .env
67
+ .env.local
68
+ `,
69
+ },
70
+ {
71
+ output: 'jsconfig.json',
72
+ content: `{
73
+ "compilerOptions": {
74
+ "baseUrl": ".",
75
+ "paths": {
76
+ "@/*": ["./*"]
77
+ }
78
+ }
79
+ }
80
+ `,
81
+ },
82
+ ],
83
+ description: 'Next.js + Material UI application',
84
+ postInstall: 'npm install',
85
+ startCommand: 'npm run dev',
86
+ },
87
+
88
+ python: {
89
+ files: [
90
+ { template: 'requirements.txt', output: 'requirements.txt' },
91
+ { template: 'env.example', output: '.env.example' },
92
+ { template: 'README.md', output: 'README.md' },
93
+ { template: 'app.py', output: 'app.py' },
94
+ { template: 'voyage_client.py', output: 'voyage_client.py' },
95
+ { template: 'mongo_client.py', output: 'mongo_client.py' },
96
+ { template: 'chunker.py', output: 'chunker.py' },
97
+ ],
98
+ extraFiles: [
99
+ {
100
+ output: '.gitignore',
101
+ content: `venv/
102
+ __pycache__/
103
+ *.pyc
104
+ .env
105
+ `,
106
+ },
107
+ ],
108
+ description: 'Python + Flask API server',
109
+ postInstall: 'pip install -r requirements.txt',
110
+ startCommand: 'python app.py',
111
+ },
112
+ };
113
+
114
+ module.exports = { PROJECT_STRUCTURE };
@@ -0,0 +1,106 @@
1
+ # {{projectName}}
2
+
3
+ A semantic search application powered by Voyage AI embeddings, MongoDB Atlas Vector Search, and Next.js with Material UI.
4
+
5
+ ## Configuration
6
+
7
+ | Setting | Value |
8
+ |---------|-------|
9
+ | Embedding Model | `{{model}}` |
10
+ | Dimensions | {{dimensions}} |
11
+ | Database | `{{db}}` |
12
+ | Collection | `{{collection}}` |
13
+ | Vector Index | `{{index}}` |
14
+ {{#if rerank}}
15
+ | Rerank Model | `{{rerankModel}}` |
16
+ {{/if}}
17
+
18
+ ## Setup
19
+
20
+ ### 1. Install dependencies
21
+
22
+ ```bash
23
+ npm install
24
+ ```
25
+
26
+ ### 2. Configure environment
27
+
28
+ Copy `.env.example` to `.env.local` and fill in your credentials:
29
+
30
+ ```bash
31
+ cp .env.example .env.local
32
+ ```
33
+
34
+ Required variables:
35
+ - `VOYAGE_API_KEY` - Your Voyage AI API key from [dash.voyageai.com](https://dash.voyageai.com)
36
+ - `MONGODB_URI` - Your MongoDB Atlas connection string
37
+
38
+ ### 3. Create vector index
39
+
40
+ In MongoDB Atlas, create a vector search index on your collection:
41
+
42
+ ```json
43
+ {
44
+ "fields": [
45
+ {
46
+ "type": "vector",
47
+ "path": "{{field}}",
48
+ "numDimensions": {{dimensions}},
49
+ "similarity": "cosine"
50
+ }
51
+ ]
52
+ }
53
+ ```
54
+
55
+ Name the index `{{index}}`.
56
+
57
+ ### 4. Start the development server
58
+
59
+ ```bash
60
+ npm run dev
61
+ ```
62
+
63
+ Open [http://localhost:3000](http://localhost:3000) to see the search interface.
64
+
65
+ ## Project Structure
66
+
67
+ ```
68
+ {{projectName}}/
69
+ ├── app/
70
+ │ ├── layout.jsx # Root layout with MUI theme
71
+ │ ├── page.jsx # Home page
72
+ │ ├── search/
73
+ │ │ └── page.jsx # Search page (MUI)
74
+ │ └── api/
75
+ │ ├── search/route.js # Search endpoint
76
+ │ └── ingest/route.js # Ingest endpoint
77
+ ├── lib/
78
+ │ ├── voyage.js # Voyage AI client
79
+ │ ├── mongodb.js # MongoDB connection
80
+ │ └── theme.js # MUI theme
81
+ ├── .env.example
82
+ ├── package.json
83
+ └── README.md
84
+ ```
85
+
86
+ ## API Endpoints
87
+
88
+ ### POST /api/search
89
+
90
+ ```bash
91
+ curl -X POST http://localhost:3000/api/search \
92
+ -H "Content-Type: application/json" \
93
+ -d '{"query": "How does vector search work?", "limit": 5}'
94
+ ```
95
+
96
+ ### POST /api/ingest
97
+
98
+ ```bash
99
+ curl -X POST http://localhost:3000/api/ingest \
100
+ -H "Content-Type: application/json" \
101
+ -d '{"text": "Your document content...", "metadata": {"source": "api"}}'
102
+ ```
103
+
104
+ ---
105
+
106
+ Generated by [vai](https://github.com/mrlynn/voyageai-cli) v{{vaiVersion}}
@@ -0,0 +1,8 @@
1
+ # Voyage AI API
2
+ VOYAGE_API_KEY=your_voyage_api_key_here
3
+
4
+ # MongoDB Atlas
5
+ MONGODB_URI=mongodb+srv://username:password@cluster.mongodb.net/{{db}}?retryWrites=true&w=majority
6
+
7
+ # Optional: Override defaults
8
+ # VOYAGE_API_URL=https://api.voyageai.com/v1
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Root Layout with MUI Theme
3
+ * Generated by vai v{{vaiVersion}} on {{generatedAt}}
4
+ */
5
+
6
+ import { AppRouterCacheProvider } from '@mui/material-nextjs/v14-appRouter';
7
+ import { ThemeProvider } from '@mui/material/styles';
8
+ import CssBaseline from '@mui/material/CssBaseline';
9
+ import { theme } from '@/lib/theme';
10
+
11
+ export const metadata = {
12
+ title: '{{projectName}}',
13
+ description: 'Semantic search powered by Voyage AI',
14
+ };
15
+
16
+ export default function RootLayout({ children }) {
17
+ return (
18
+ <html lang="en">
19
+ <body>
20
+ <AppRouterCacheProvider>
21
+ <ThemeProvider theme={theme}>
22
+ <CssBaseline />
23
+ {children}
24
+ </ThemeProvider>
25
+ </AppRouterCacheProvider>
26
+ </body>
27
+ </html>
28
+ );
29
+ }
@@ -0,0 +1,111 @@
1
+ /**
2
+ * MongoDB Connection Helper (Next.js)
3
+ * Generated by vai v{{vaiVersion}} on {{generatedAt}}
4
+ *
5
+ * Uses connection caching for serverless environments.
6
+ */
7
+
8
+ import { MongoClient } from 'mongodb';
9
+
10
+ const MONGODB_URI = process.env.MONGODB_URI;
11
+
12
+ if (!MONGODB_URI) {
13
+ throw new Error('MONGODB_URI environment variable is required');
14
+ }
15
+
16
+ // Cache the client promise for connection reuse in serverless
17
+ let cached = global.mongo;
18
+
19
+ if (!cached) {
20
+ cached = global.mongo = { conn: null, promise: null };
21
+ }
22
+
23
+ /**
24
+ * Get a cached MongoDB client connection.
25
+ * Reuses existing connection in serverless environment.
26
+ */
27
+ export async function getMongoClient() {
28
+ if (cached.conn) {
29
+ return cached.conn;
30
+ }
31
+
32
+ if (!cached.promise) {
33
+ cached.promise = MongoClient.connect(MONGODB_URI);
34
+ }
35
+
36
+ cached.conn = await cached.promise;
37
+ return cached.conn;
38
+ }
39
+
40
+ /**
41
+ * Get the database instance.
42
+ */
43
+ export async function getDb() {
44
+ const client = await getMongoClient();
45
+ return client.db('{{db}}');
46
+ }
47
+
48
+ /**
49
+ * Get the documents collection.
50
+ */
51
+ export async function getCollection() {
52
+ const db = await getDb();
53
+ return db.collection('{{collection}}');
54
+ }
55
+
56
+ /**
57
+ * Perform a vector search.
58
+ */
59
+ export async function vectorSearch(embedding, options = {}) {
60
+ const collection = await getCollection();
61
+ const limit = options.limit || 10;
62
+ const numCandidates = options.numCandidates || limit * 10;
63
+
64
+ const pipeline = [
65
+ {
66
+ $vectorSearch: {
67
+ index: '{{index}}',
68
+ path: '{{field}}',
69
+ queryVector: embedding,
70
+ numCandidates,
71
+ limit,
72
+ },
73
+ },
74
+ {
75
+ $project: {
76
+ _id: 1,
77
+ text: 1,
78
+ metadata: 1,
79
+ score: { $meta: 'vectorSearchScore' },
80
+ },
81
+ },
82
+ ];
83
+
84
+ if (options.filter) {
85
+ pipeline[0].$vectorSearch.filter = options.filter;
86
+ }
87
+
88
+ const results = await collection.aggregate(pipeline).toArray();
89
+ return results.map(doc => ({
90
+ document: doc,
91
+ score: doc.score,
92
+ }));
93
+ }
94
+
95
+ /**
96
+ * Insert documents with embeddings.
97
+ */
98
+ export async function insertDocuments(docs) {
99
+ const collection = await getCollection();
100
+
101
+ const documents = docs.map(doc => ({
102
+ text: doc.text,
103
+ {{field}}: doc.embedding,
104
+ metadata: doc.metadata || {},
105
+ _embeddedAt: new Date(),
106
+ _model: '{{model}}',
107
+ }));
108
+
109
+ const result = await collection.insertMany(documents);
110
+ return { insertedCount: result.insertedCount };
111
+ }
@@ -0,0 +1,103 @@
1
+ /**
2
+ * Voyage AI API Client (Next.js)
3
+ * Generated by vai v{{vaiVersion}} on {{generatedAt}}
4
+ *
5
+ * Model: {{model}}
6
+ * Dimensions: {{dimensions}}
7
+ */
8
+
9
+ const VOYAGE_API_URL = process.env.VOYAGE_API_URL || 'https://api.voyageai.com/v1';
10
+ const VOYAGE_API_KEY = process.env.VOYAGE_API_KEY;
11
+
12
+ /**
13
+ * Generate embeddings for text(s) using Voyage AI.
14
+ */
15
+ export async function embed(input, options = {}) {
16
+ if (!VOYAGE_API_KEY) {
17
+ throw new Error('VOYAGE_API_KEY environment variable is required');
18
+ }
19
+
20
+ const texts = Array.isArray(input) ? input : [input];
21
+
22
+ const response = await fetch(`${VOYAGE_API_URL}/embeddings`, {
23
+ method: 'POST',
24
+ headers: {
25
+ 'Content-Type': 'application/json',
26
+ 'Authorization': `Bearer ${VOYAGE_API_KEY}`,
27
+ },
28
+ body: JSON.stringify({
29
+ model: options.model || '{{model}}',
30
+ input: texts,
31
+ input_type: options.inputType || '{{inputType}}',
32
+ output_dimension: options.outputDimension || {{dimensions}},
33
+ }),
34
+ });
35
+
36
+ if (!response.ok) {
37
+ const error = await response.text();
38
+ throw new Error(`Voyage AI API error: ${response.status} ${error}`);
39
+ }
40
+
41
+ const data = await response.json();
42
+ return {
43
+ embeddings: data.data.map(d => d.embedding),
44
+ usage: data.usage,
45
+ };
46
+ }
47
+
48
+ /**
49
+ * Embed a single query.
50
+ */
51
+ export async function embedQuery(query, options = {}) {
52
+ const result = await embed(query, { ...options, inputType: 'query' });
53
+ return result.embeddings[0];
54
+ }
55
+
56
+ /**
57
+ * Embed multiple documents.
58
+ */
59
+ export async function embedDocuments(documents, options = {}) {
60
+ const result = await embed(documents, { ...options, inputType: 'document' });
61
+ return result.embeddings;
62
+ }
63
+
64
+ {{#if rerank}}
65
+ /**
66
+ * Rerank documents by relevance to a query.
67
+ */
68
+ export async function rerank(query, documents, options = {}) {
69
+ if (!VOYAGE_API_KEY) {
70
+ throw new Error('VOYAGE_API_KEY environment variable is required');
71
+ }
72
+
73
+ const response = await fetch(`${VOYAGE_API_URL}/rerank`, {
74
+ method: 'POST',
75
+ headers: {
76
+ 'Content-Type': 'application/json',
77
+ 'Authorization': `Bearer ${VOYAGE_API_KEY}`,
78
+ },
79
+ body: JSON.stringify({
80
+ model: options.model || '{{rerankModel}}',
81
+ query,
82
+ documents,
83
+ top_k: options.topK,
84
+ return_documents: true,
85
+ }),
86
+ });
87
+
88
+ if (!response.ok) {
89
+ const error = await response.text();
90
+ throw new Error(`Voyage AI rerank error: ${response.status} ${error}`);
91
+ }
92
+
93
+ const data = await response.json();
94
+ return {
95
+ results: data.data.map(d => ({
96
+ index: d.index,
97
+ relevanceScore: d.relevance_score,
98
+ document: d.document,
99
+ })),
100
+ usage: data.usage,
101
+ };
102
+ }
103
+ {{/if}}
@@ -0,0 +1,33 @@
1
+ {
2
+ "name": "{{projectName}}",
3
+ "version": "1.0.0",
4
+ "private": true,
5
+ "scripts": {
6
+ "dev": "next dev",
7
+ "build": "next build",
8
+ "start": "next start",
9
+ "lint": "next lint"
10
+ },
11
+ "dependencies": {
12
+ "@emotion/cache": "^11.11.0",
13
+ "@emotion/react": "^11.11.3",
14
+ "@emotion/styled": "^11.11.0",
15
+ "@mui/icons-material": "^5.15.0",
16
+ "@mui/material": "^5.15.0",
17
+ "@mui/material-nextjs": "^5.15.0",
18
+ "mongodb": "^6.3.0",
19
+ "next": "^14.1.0",
20
+ "react": "^18.2.0",
21
+ "react-dom": "^18.2.0"
22
+ },
23
+ "devDependencies": {
24
+ "eslint": "^8.56.0",
25
+ "eslint-config-next": "^14.1.0"
26
+ },
27
+ "generated": {
28
+ "by": "vai",
29
+ "version": "{{vaiVersion}}",
30
+ "model": "{{model}}",
31
+ "at": "{{generatedAt}}"
32
+ }
33
+ }