voyageai-cli 1.28.0 โ 1.30.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/README.md +82 -8
- package/package.json +2 -1
- package/src/commands/app.js +15 -0
- package/src/commands/benchmark.js +22 -8
- package/src/commands/chat.js +18 -0
- package/src/commands/chunk.js +10 -0
- package/src/commands/demo.js +4 -0
- package/src/commands/embed.js +13 -0
- package/src/commands/estimate.js +3 -0
- package/src/commands/eval.js +6 -0
- package/src/commands/explain.js +2 -0
- package/src/commands/generate.js +2 -0
- package/src/commands/ingest.js +4 -0
- package/src/commands/init.js +2 -0
- package/src/commands/mcp-server.js +2 -0
- package/src/commands/models.js +2 -0
- package/src/commands/ping.js +7 -0
- package/src/commands/pipeline.js +15 -0
- package/src/commands/playground.js +685 -8
- package/src/commands/query.js +16 -0
- package/src/commands/rerank.js +12 -0
- package/src/commands/scaffold.js +2 -0
- package/src/commands/search.js +11 -0
- package/src/commands/similarity.js +9 -0
- package/src/commands/store.js +4 -0
- package/src/commands/workflow.js +702 -13
- package/src/lib/capability-report.js +134 -0
- package/src/lib/chat.js +32 -1
- package/src/lib/config.js +2 -0
- package/src/lib/cost-display.js +107 -0
- package/src/lib/explanations.js +94 -0
- package/src/lib/llm.js +125 -18
- package/src/lib/npm-utils.js +265 -0
- package/src/lib/quality-audit.js +71 -0
- package/src/lib/security/blocked-domains.json +17 -0
- package/src/lib/security-audit.js +198 -0
- package/src/lib/telemetry.js +23 -1
- package/src/lib/workflow-registry.js +416 -0
- package/src/lib/workflow-scaffold.js +380 -0
- package/src/lib/workflow-test-runner.js +208 -0
- package/src/lib/workflow.js +559 -7
- package/src/playground/announcements.md +80 -0
- package/src/playground/assets/announcements/appstore.jpg +0 -0
- package/src/playground/assets/announcements/circuits.jpg +0 -0
- package/src/playground/assets/announcements/csvingest.jpg +0 -0
- package/src/playground/assets/announcements/green-wave.jpg +0 -0
- package/src/playground/help/workflow-nodes.js +472 -0
- package/src/playground/icons/V.png +0 -0
- package/src/playground/index.html +3634 -226
- package/src/workflows/consistency-check.json +4 -0
- package/src/workflows/cost-analysis.json +4 -0
- package/src/workflows/enrich-and-ingest.json +56 -0
- package/src/workflows/intelligent-ingest.json +66 -0
- package/src/workflows/kb-health-report.json +45 -0
- package/src/workflows/multi-collection-search.json +4 -0
- package/src/workflows/research-and-summarize.json +4 -0
- package/src/workflows/search-with-fallback.json +66 -0
- package/src/workflows/smart-ingest.json +4 -0
package/README.md
CHANGED
|
@@ -38,27 +38,34 @@ vai quickstart # Interactive tutorial โ zero to semantic search in 2 minute
|
|
|
38
38
|
|
|
39
39
|
<table>
|
|
40
40
|
<tr>
|
|
41
|
-
<td align="center" width="
|
|
41
|
+
<td align="center" width="25%">
|
|
42
42
|
<h3>๐ฅ๏ธ CLI</h3>
|
|
43
43
|
<code>vai</code><br/><br/>
|
|
44
44
|
22 commands ยท 5 chunking strategies<br/>
|
|
45
45
|
End-to-end RAG pipeline from your terminal<br/><br/>
|
|
46
|
-
<code>
|
|
46
|
+
<code>curl -fsSL https://vaicli.com/install.sh | sh</code>
|
|
47
47
|
</td>
|
|
48
|
-
<td align="center" width="
|
|
48
|
+
<td align="center" width="25%">
|
|
49
49
|
<h3>๐ Web Playground</h3>
|
|
50
50
|
<code>vai playground</code><br/><br/>
|
|
51
|
-
7 interactive tabs
|
|
52
|
-
|
|
51
|
+
7 interactive tabs + Workflow Store<br/>
|
|
52
|
+
with 20+ installable workflow packages<br/><br/>
|
|
53
53
|
<em>Launches in your browser</em>
|
|
54
54
|
</td>
|
|
55
|
-
<td align="center" width="
|
|
55
|
+
<td align="center" width="25%">
|
|
56
56
|
<h3>๐ป Desktop App</h3>
|
|
57
57
|
Standalone Electron app<br/><br/>
|
|
58
58
|
Secure keychain storage, dark/light themes,<br/>
|
|
59
59
|
MongoDB LeafyGreen design system<br/><br/>
|
|
60
60
|
<a href="https://github.com/mrlynn/voyageai-cli/releases">Download from GitHub Releases</a>
|
|
61
61
|
</td>
|
|
62
|
+
<td align="center" width="25%">
|
|
63
|
+
<h3>๐ช Workflow Store</h3>
|
|
64
|
+
<code>vai store</code> or Playground<br/><br/>
|
|
65
|
+
Browse, install, and run pre-built<br/>
|
|
66
|
+
RAG workflows from npm<br/><br/>
|
|
67
|
+
<em>Official + community packages</em>
|
|
68
|
+
</td>
|
|
62
69
|
</tr>
|
|
63
70
|
</table>
|
|
64
71
|
|
|
@@ -81,6 +88,7 @@ MongoDB LeafyGreen design system<br/><br/>
|
|
|
81
88
|
- [Environment & Auth](#environment--auth)
|
|
82
89
|
- [Shell Completions](#shell-completions)
|
|
83
90
|
- [All Commands](#all-commands)
|
|
91
|
+
- [Workflow Store](#workflow-store)
|
|
84
92
|
- [MCP Server](#mcp-server)
|
|
85
93
|
- [Screenshots](#screenshots)
|
|
86
94
|
- [Requirements](#requirements)
|
|
@@ -115,6 +123,8 @@ Download the latest release for your platform from [GitHub Releases](https://git
|
|
|
115
123
|
| Windows | `.exe` installer |
|
|
116
124
|
| Linux | `.AppImage` / `.deb` |
|
|
117
125
|
|
|
126
|
+
> **Prefer the CLI?** Install with `curl -fsSL https://vaicli.com/install.sh | sh` or `brew install mrlynn/vai/vai`
|
|
127
|
+
|
|
118
128
|
---
|
|
119
129
|
|
|
120
130
|
## Web Playground
|
|
@@ -125,7 +135,7 @@ An interactive, browser-based interface for exploring Voyage AI embeddings witho
|
|
|
125
135
|
vai playground
|
|
126
136
|
```
|
|
127
137
|
|
|
128
|
-
Your default browser opens with a full-featured UI
|
|
138
|
+
Your default browser opens with a full-featured UI:
|
|
129
139
|
|
|
130
140
|
| Tab | What It Does |
|
|
131
141
|
|-----|-------------|
|
|
@@ -134,6 +144,7 @@ Your default browser opens with a full-featured UI organized into **7 tabs**:
|
|
|
134
144
|
| **Search** | Connect to MongoDB Atlas and run vector similarity searches with filters and reranking |
|
|
135
145
|
| **Benchmark** | Compare model latency, cost, and quality across the Voyage 4 family on your own data |
|
|
136
146
|
| **Explore** | Visualize embedding spaces with dimensionality reduction (PCA/t-SNE) and clustering |
|
|
147
|
+
| **Workflow Store** | Browse, install, and run 20+ official and community workflow packages |
|
|
137
148
|
| **About** | Project info, links, and version details |
|
|
138
149
|
| **Settings** | Configure API keys, MongoDB URI, default model, and preferences |
|
|
139
150
|
|
|
@@ -143,12 +154,19 @@ The playground connects to the same backend as the CLI. Any API keys or MongoDB
|
|
|
143
154
|
|
|
144
155
|
## CLI โ Quick Start
|
|
145
156
|
|
|
146
|
-
**22 commands ยท
|
|
157
|
+
**22 commands ยท 1,000+ tests ยท 5 chunking strategies ยท End-to-end RAG pipeline**
|
|
147
158
|
|
|
148
159
|
### Install
|
|
149
160
|
|
|
150
161
|
```bash
|
|
162
|
+
# Fastest โ one command, no dependencies
|
|
163
|
+
curl -fsSL https://vaicli.com/install.sh | sh
|
|
164
|
+
|
|
165
|
+
# Via npm
|
|
151
166
|
npm install -g voyageai-cli
|
|
167
|
+
|
|
168
|
+
# Via Homebrew
|
|
169
|
+
brew install mrlynn/vai/vai
|
|
152
170
|
```
|
|
153
171
|
|
|
154
172
|
### 5-Minute RAG Pipeline
|
|
@@ -535,6 +553,11 @@ Covers all 22 commands, subcommands, flags, model names, and explain topics.
|
|
|
535
553
|
| `vai eval` | Evaluate retrieval quality (MRR, nDCG, Recall) |
|
|
536
554
|
| `vai eval compare` | Compare configurations side-by-side |
|
|
537
555
|
| `vai benchmark` | 8 subcommands for model comparison |
|
|
556
|
+
| **Workflow Store** | |
|
|
557
|
+
| `vai store list` | Browse available workflows (official + community) |
|
|
558
|
+
| `vai store install` | Install a workflow package from npm |
|
|
559
|
+
| `vai store run` | Run an installed workflow |
|
|
560
|
+
| `vai store uninstall` | Remove an installed workflow |
|
|
538
561
|
| **MCP Server** | |
|
|
539
562
|
| `vai mcp` | Start the MCP server (expose vai tools to AI agents) |
|
|
540
563
|
| `vai mcp install` | Install vai into AI tool configs (Claude, Cursor, etc.) |
|
|
@@ -554,6 +577,57 @@ Covers all 22 commands, subcommands, flags, model names, and explain topics.
|
|
|
554
577
|
|
|
555
578
|
---
|
|
556
579
|
|
|
580
|
+
## Workflow Store
|
|
581
|
+
|
|
582
|
+
Browse, install, and run pre-built RAG workflows โ from the Playground UI or the CLI. The Workflow Store features **20 official workflows** and a growing ecosystem of community packages published on npm.
|
|
583
|
+
|
|
584
|
+
### Browse & Install
|
|
585
|
+
|
|
586
|
+
```bash
|
|
587
|
+
# Open the visual Workflow Store in the Playground
|
|
588
|
+
vai playground # Click the grid icon โ Store
|
|
589
|
+
|
|
590
|
+
# Or from the CLI
|
|
591
|
+
vai store list # Browse available workflows
|
|
592
|
+
vai store install model-shootout # Install a workflow
|
|
593
|
+
vai store run model-shootout # Run it
|
|
594
|
+
```
|
|
595
|
+
|
|
596
|
+
### Official Workflows
|
|
597
|
+
|
|
598
|
+
| Workflow | Category | What It Does |
|
|
599
|
+
|----------|----------|-------------|
|
|
600
|
+
| **model-shootout** | Utility | Compare voyage-4-large, voyage-4, and voyage-4-lite side-by-side on your data |
|
|
601
|
+
| **asymmetric-search** | Retrieval | Embed with voyage-4-large, query with voyage-4-lite โ ~83% cost reduction |
|
|
602
|
+
| **cost-optimizer** | Utility | Quantify exact cost savings of asymmetric retrieval |
|
|
603
|
+
| **question-decomposition** | Retrieval | Break complex questions into sub-queries, search in parallel, merge & rerank |
|
|
604
|
+
| **knowledge-base-bootstrap** | Integration | End-to-end onboarding: ingest โ verify โ test query โ status report |
|
|
605
|
+
| **hybrid-precision-search** | Retrieval | Three retrieval strategies in parallel, merged and reranked |
|
|
606
|
+
| **embedding-drift-detector** | Analysis | Monitor embedding quality over time |
|
|
607
|
+
| **multilingual-search** | Retrieval | Translate queries into multiple languages, search each in parallel |
|
|
608
|
+
|
|
609
|
+
Plus 12 more covering code migration, financial risk scanning, clinical protocol matching, meeting action items, and more.
|
|
610
|
+
|
|
611
|
+
### Community Packages
|
|
612
|
+
|
|
613
|
+
Anyone can publish a workflow to npm. Tag your package with `vai-workflow` and add a `vai-workflow` field to your `package.json`:
|
|
614
|
+
|
|
615
|
+
```json
|
|
616
|
+
{
|
|
617
|
+
"name": "vai-workflow-my-pipeline",
|
|
618
|
+
"keywords": ["vai-workflow"],
|
|
619
|
+
"vai-workflow": {
|
|
620
|
+
"category": "retrieval",
|
|
621
|
+
"tags": ["custom", "my-use-case"],
|
|
622
|
+
"tools": ["query", "rerank", "generate"]
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
```
|
|
626
|
+
|
|
627
|
+
Community workflows appear automatically in the Store alongside official packages.
|
|
628
|
+
|
|
629
|
+
---
|
|
630
|
+
|
|
557
631
|
## MCP Server
|
|
558
632
|
|
|
559
633
|
Expose vai's RAG tools to any MCP-compatible AI agent โ Claude Desktop, Claude Code, Cursor, Windsurf, VS Code, and more. **11 tools** for embedding, retrieval, reranking, ingestion, and learning โ all accessible without writing code.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "voyageai-cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.30.0",
|
|
4
4
|
"description": "CLI for Voyage AI embeddings, reranking, and MongoDB Atlas Vector Search",
|
|
5
5
|
"bin": {
|
|
6
6
|
"vai": "./src/cli.js"
|
|
@@ -42,6 +42,7 @@
|
|
|
42
42
|
"dependencies": {
|
|
43
43
|
"@clack/prompts": "^1.0.0",
|
|
44
44
|
"@modelcontextprotocol/sdk": "^1.26.0",
|
|
45
|
+
"@vaicli/vai-workflow-model-shootout": "^1.0.0",
|
|
45
46
|
"commander": "^12.0.0",
|
|
46
47
|
"dotenv": "^17.2.3",
|
|
47
48
|
"mongodb": "^6.0.0",
|
package/src/commands/app.js
CHANGED
|
@@ -138,6 +138,21 @@ function registerApp(program) {
|
|
|
138
138
|
const electronPkg = path.join(electronDir, 'package.json');
|
|
139
139
|
|
|
140
140
|
if (!fs.existsSync(electronPkg)) {
|
|
141
|
+
// Check for system-installed Vai.app on macOS
|
|
142
|
+
if (process.platform === 'darwin') {
|
|
143
|
+
const appPaths = [
|
|
144
|
+
'/Applications/Vai.app',
|
|
145
|
+
path.join(process.env.HOME || '', 'Applications', 'Vai.app'),
|
|
146
|
+
];
|
|
147
|
+
const installed = appPaths.find((p) => fs.existsSync(p));
|
|
148
|
+
if (installed) {
|
|
149
|
+
console.log('๐งญ Launching Vai desktop app...');
|
|
150
|
+
spawn('open', ['-a', installed], { stdio: 'ignore', detached: true }).unref();
|
|
151
|
+
setTimeout(() => process.exit(0), 500);
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
141
156
|
console.log('');
|
|
142
157
|
console.log('๐ฅ๏ธ The Vai desktop app is not installed locally.');
|
|
143
158
|
console.log('');
|
|
@@ -1112,6 +1112,20 @@ function formatBytes(bytes) {
|
|
|
1112
1112
|
* Register the benchmark command tree on a Commander program.
|
|
1113
1113
|
* @param {import('commander').Command} program
|
|
1114
1114
|
*/
|
|
1115
|
+
function withTelemetry(subcommand, fn) {
|
|
1116
|
+
return async (...args) => {
|
|
1117
|
+
const telemetry = require('../lib/telemetry');
|
|
1118
|
+
const done = telemetry.timer('cli_benchmark', { subcommand });
|
|
1119
|
+
try {
|
|
1120
|
+
await fn(...args);
|
|
1121
|
+
done();
|
|
1122
|
+
} catch (err) {
|
|
1123
|
+
telemetry.send('cli_error', { command: 'benchmark', errorType: err.constructor.name });
|
|
1124
|
+
throw err;
|
|
1125
|
+
}
|
|
1126
|
+
};
|
|
1127
|
+
}
|
|
1128
|
+
|
|
1115
1129
|
function registerBenchmark(program) {
|
|
1116
1130
|
const bench = program
|
|
1117
1131
|
.command('benchmark')
|
|
@@ -1131,7 +1145,7 @@ function registerBenchmark(program) {
|
|
|
1131
1145
|
.option('--json', 'Machine-readable JSON output')
|
|
1132
1146
|
.option('-q, --quiet', 'Suppress non-essential output')
|
|
1133
1147
|
.option('-s, --save [path]', 'Save results to JSON file')
|
|
1134
|
-
.action(benchmarkEmbed);
|
|
1148
|
+
.action(withTelemetry("embed", benchmarkEmbed));
|
|
1135
1149
|
|
|
1136
1150
|
// โโ benchmark rerank โโ
|
|
1137
1151
|
bench
|
|
@@ -1145,7 +1159,7 @@ function registerBenchmark(program) {
|
|
|
1145
1159
|
.option('--json', 'Machine-readable JSON output')
|
|
1146
1160
|
.option('-q, --quiet', 'Suppress non-essential output')
|
|
1147
1161
|
.option('-s, --save [path]', 'Save results to JSON file')
|
|
1148
|
-
.action(benchmarkRerank);
|
|
1162
|
+
.action(withTelemetry("rerank", benchmarkRerank));
|
|
1149
1163
|
|
|
1150
1164
|
// โโ benchmark similarity โโ
|
|
1151
1165
|
bench
|
|
@@ -1158,7 +1172,7 @@ function registerBenchmark(program) {
|
|
|
1158
1172
|
.option('-d, --dimensions <n>', 'Output dimensions')
|
|
1159
1173
|
.option('--json', 'Machine-readable JSON output')
|
|
1160
1174
|
.option('-q, --quiet', 'Suppress non-essential output')
|
|
1161
|
-
.action(benchmarkSimilarity);
|
|
1175
|
+
.action(withTelemetry("similarity", benchmarkSimilarity));
|
|
1162
1176
|
|
|
1163
1177
|
// โโ benchmark cost โโ
|
|
1164
1178
|
bench
|
|
@@ -1170,7 +1184,7 @@ function registerBenchmark(program) {
|
|
|
1170
1184
|
.option('--volumes <list>', 'Comma-separated daily query volumes', '100,1000,10000,100000')
|
|
1171
1185
|
.option('--json', 'Machine-readable JSON output')
|
|
1172
1186
|
.option('-q, --quiet', 'Suppress non-essential output')
|
|
1173
|
-
.action(benchmarkCost);
|
|
1187
|
+
.action(withTelemetry("cost", benchmarkCost));
|
|
1174
1188
|
|
|
1175
1189
|
// โโ benchmark batch โโ
|
|
1176
1190
|
bench
|
|
@@ -1182,7 +1196,7 @@ function registerBenchmark(program) {
|
|
|
1182
1196
|
.option('-f, --file <path>', 'Load texts from file')
|
|
1183
1197
|
.option('--json', 'Machine-readable JSON output')
|
|
1184
1198
|
.option('-q, --quiet', 'Suppress non-essential output')
|
|
1185
|
-
.action(benchmarkBatch);
|
|
1199
|
+
.action(withTelemetry("batch", benchmarkBatch));
|
|
1186
1200
|
|
|
1187
1201
|
// โโ benchmark quantization โโ
|
|
1188
1202
|
bench
|
|
@@ -1198,7 +1212,7 @@ function registerBenchmark(program) {
|
|
|
1198
1212
|
.option('--json', 'Machine-readable JSON output')
|
|
1199
1213
|
.option('-q, --quiet', 'Suppress non-essential output')
|
|
1200
1214
|
.option('-s, --save [path]', 'Save results to JSON file')
|
|
1201
|
-
.action(benchmarkQuantization);
|
|
1215
|
+
.action(withTelemetry("quantization", benchmarkQuantization));
|
|
1202
1216
|
|
|
1203
1217
|
// โโ benchmark asymmetric โโ
|
|
1204
1218
|
bench
|
|
@@ -1211,7 +1225,7 @@ function registerBenchmark(program) {
|
|
|
1211
1225
|
.option('-k, --top-k <n>', 'Show top K results', '5')
|
|
1212
1226
|
.option('--json', 'Machine-readable JSON output')
|
|
1213
1227
|
.option('-q, --quiet', 'Suppress non-essential output')
|
|
1214
|
-
.action(benchmarkAsymmetric);
|
|
1228
|
+
.action(withTelemetry("asymmetric", benchmarkAsymmetric));
|
|
1215
1229
|
|
|
1216
1230
|
// โโ benchmark space โโ
|
|
1217
1231
|
bench
|
|
@@ -1223,7 +1237,7 @@ function registerBenchmark(program) {
|
|
|
1223
1237
|
.option('-d, --dimensions <n>', 'Output dimensions (must be supported by all models)')
|
|
1224
1238
|
.option('--json', 'Machine-readable JSON output')
|
|
1225
1239
|
.option('-q, --quiet', 'Suppress non-essential output')
|
|
1226
|
-
.action(benchmarkSpace);
|
|
1240
|
+
.action(withTelemetry("space", benchmarkSpace));
|
|
1227
1241
|
}
|
|
1228
1242
|
|
|
1229
1243
|
/**
|
package/src/commands/chat.js
CHANGED
|
@@ -223,6 +223,11 @@ async function runChat(opts) {
|
|
|
223
223
|
}
|
|
224
224
|
}
|
|
225
225
|
|
|
226
|
+
// Telemetry: track session
|
|
227
|
+
const telemetry = require('../lib/telemetry');
|
|
228
|
+
const chatStartTime = Date.now();
|
|
229
|
+
let turnCount = 0;
|
|
230
|
+
|
|
226
231
|
// Track tool calls from last agent response (for /tools and /export-workflow)
|
|
227
232
|
let lastToolCalls = [];
|
|
228
233
|
|
|
@@ -276,6 +281,7 @@ async function runChat(opts) {
|
|
|
276
281
|
}
|
|
277
282
|
|
|
278
283
|
// Execute chat turn
|
|
284
|
+
turnCount++;
|
|
279
285
|
try {
|
|
280
286
|
if (isAgent) {
|
|
281
287
|
lastToolCalls = await handleAgentTurn(input, {
|
|
@@ -296,13 +302,25 @@ async function runChat(opts) {
|
|
|
296
302
|
rl.prompt();
|
|
297
303
|
});
|
|
298
304
|
|
|
305
|
+
function sendChatTelemetry() {
|
|
306
|
+
telemetry.send('cli_chat', {
|
|
307
|
+
provider: llmConfig.provider,
|
|
308
|
+
llmModel: llmConfig.model,
|
|
309
|
+
embeddingModel: proj.model || undefined,
|
|
310
|
+
turnCount,
|
|
311
|
+
durationMs: Date.now() - chatStartTime,
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
|
|
299
315
|
rl.on('close', async () => {
|
|
316
|
+
sendChatTelemetry();
|
|
300
317
|
await cleanup(historyMongo);
|
|
301
318
|
process.exit(0);
|
|
302
319
|
});
|
|
303
320
|
|
|
304
321
|
// Handle Ctrl+C gracefully
|
|
305
322
|
rl.on('SIGINT', async () => {
|
|
323
|
+
sendChatTelemetry();
|
|
306
324
|
console.log('');
|
|
307
325
|
await cleanup(historyMongo);
|
|
308
326
|
process.exit(0);
|
package/src/commands/chunk.js
CHANGED
|
@@ -51,6 +51,7 @@ function registerChunk(program) {
|
|
|
51
51
|
.option('--json', 'Machine-readable JSON output')
|
|
52
52
|
.option('-q, --quiet', 'Suppress non-essential output')
|
|
53
53
|
.action(async (input, opts) => {
|
|
54
|
+
const telemetry = require('../lib/telemetry');
|
|
54
55
|
try {
|
|
55
56
|
// Load project config, merge with CLI opts
|
|
56
57
|
const { config: projectConfig } = loadProject();
|
|
@@ -62,6 +63,12 @@ function registerChunk(program) {
|
|
|
62
63
|
const minSize = opts.minSize || chunkConfig.minSize || DEFAULTS.minSize;
|
|
63
64
|
const textField = opts.textField || 'text';
|
|
64
65
|
|
|
66
|
+
const done = telemetry.timer('cli_chunk', {
|
|
67
|
+
strategy,
|
|
68
|
+
chunkSize,
|
|
69
|
+
overlap,
|
|
70
|
+
});
|
|
71
|
+
|
|
65
72
|
if (!STRATEGIES.includes(strategy)) {
|
|
66
73
|
console.error(ui.error(`Unknown strategy: "${strategy}". Available: ${STRATEGIES.join(', ')}`));
|
|
67
74
|
process.exit(1);
|
|
@@ -227,7 +234,10 @@ function registerChunk(program) {
|
|
|
227
234
|
console.log(ui.label('Est. cost', ui.dim(`~$${cost < 0.01 ? cost.toFixed(4) : cost.toFixed(2)} with voyage-4-large`)));
|
|
228
235
|
}
|
|
229
236
|
}
|
|
237
|
+
|
|
238
|
+
done({ chunkCount: allChunks.length });
|
|
230
239
|
} catch (err) {
|
|
240
|
+
telemetry.send('cli_error', { command: 'chunk', errorType: err.constructor.name });
|
|
231
241
|
console.error(ui.error(err.message));
|
|
232
242
|
process.exit(1);
|
|
233
243
|
}
|
package/src/commands/demo.js
CHANGED
|
@@ -105,6 +105,8 @@ function registerDemo(program) {
|
|
|
105
105
|
.option('--skip-pipeline', 'Skip the full pipeline step (Step 5)')
|
|
106
106
|
.option('--keep', 'Keep the demo collection after pipeline step')
|
|
107
107
|
.action(async (opts) => {
|
|
108
|
+
const telemetry = require('../lib/telemetry');
|
|
109
|
+
const demoStart = Date.now();
|
|
108
110
|
const noPause = !opts.pause;
|
|
109
111
|
|
|
110
112
|
// โโ Preflight: check API key โโ
|
|
@@ -425,6 +427,8 @@ function registerDemo(program) {
|
|
|
425
427
|
console.log('');
|
|
426
428
|
console.log(' Happy searching! ๐');
|
|
427
429
|
console.log('');
|
|
430
|
+
|
|
431
|
+
telemetry.send('cli_demo', { durationMs: Date.now() - demoStart });
|
|
428
432
|
});
|
|
429
433
|
}
|
|
430
434
|
|
package/src/commands/embed.js
CHANGED
|
@@ -4,6 +4,7 @@ const { getDefaultModel } = require('../lib/catalog');
|
|
|
4
4
|
const { generateEmbeddings } = require('../lib/api');
|
|
5
5
|
const { resolveTextInput } = require('../lib/input');
|
|
6
6
|
const ui = require('../lib/ui');
|
|
7
|
+
const { showCostSummary } = require('../lib/cost-display');
|
|
7
8
|
|
|
8
9
|
/**
|
|
9
10
|
* Register the embed command on a Commander program.
|
|
@@ -26,6 +27,7 @@ function registerEmbed(program) {
|
|
|
26
27
|
.option('-q, --quiet', 'Suppress non-essential output')
|
|
27
28
|
.action(async (text, opts) => {
|
|
28
29
|
try {
|
|
30
|
+
const telemetry = require('../lib/telemetry');
|
|
29
31
|
const texts = await resolveTextInput(text, opts.file);
|
|
30
32
|
|
|
31
33
|
// --estimate: show cost comparison, optionally switch model
|
|
@@ -45,6 +47,13 @@ function registerEmbed(program) {
|
|
|
45
47
|
console.error(ui.dim('โน Tip: Use --input-type query or --input-type document for better retrieval accuracy.'));
|
|
46
48
|
}
|
|
47
49
|
|
|
50
|
+
const done = telemetry.timer('cli_embed', {
|
|
51
|
+
model: opts.model,
|
|
52
|
+
inputType: opts.inputType || undefined,
|
|
53
|
+
textCount: texts.length,
|
|
54
|
+
outputDtype: opts.outputDtype,
|
|
55
|
+
});
|
|
56
|
+
|
|
48
57
|
let spin;
|
|
49
58
|
if (useSpinner) {
|
|
50
59
|
spin = ui.spinner('Generating embeddings...');
|
|
@@ -91,6 +100,7 @@ function registerEmbed(program) {
|
|
|
91
100
|
console.log(ui.label('Tokens', ui.dim(String(result.usage.total_tokens))));
|
|
92
101
|
}
|
|
93
102
|
console.log(ui.label('Dimensions', ui.bold(String(result.data[0]?.embedding?.length || 'N/A'))));
|
|
103
|
+
showCostSummary(result.model || model, result.usage?.total_tokens || 0, opts);
|
|
94
104
|
console.log('');
|
|
95
105
|
}
|
|
96
106
|
|
|
@@ -101,7 +111,10 @@ function registerEmbed(program) {
|
|
|
101
111
|
|
|
102
112
|
console.log('');
|
|
103
113
|
console.log(ui.success('Embeddings generated'));
|
|
114
|
+
|
|
115
|
+
done({ dimensions: result.data[0]?.embedding?.length });
|
|
104
116
|
} catch (err) {
|
|
117
|
+
telemetry.send('cli_error', { command: 'embed', errorType: err.constructor.name });
|
|
105
118
|
console.error(ui.error(err.message));
|
|
106
119
|
process.exit(1);
|
|
107
120
|
}
|
package/src/commands/estimate.js
CHANGED
|
@@ -67,6 +67,7 @@ function registerEstimate(program) {
|
|
|
67
67
|
.option('--json', 'Machine-readable JSON output')
|
|
68
68
|
.option('-q, --quiet', 'Suppress non-essential output')
|
|
69
69
|
.action((opts) => {
|
|
70
|
+
const telemetry = require('../lib/telemetry');
|
|
70
71
|
const numDocs = parseShorthand(opts.docs);
|
|
71
72
|
const numQueries = parseShorthand(opts.queries);
|
|
72
73
|
const docTokens = parseInt(opts.docTokens, 10) || DEFAULT_DOC_TOKENS;
|
|
@@ -92,6 +93,8 @@ function registerEstimate(program) {
|
|
|
92
93
|
process.exit(1);
|
|
93
94
|
}
|
|
94
95
|
|
|
96
|
+
telemetry.send('cli_estimate', { model: opts.docModel, tokenCount: numDocs * docTokens });
|
|
97
|
+
|
|
95
98
|
const docTotalTokens = numDocs * docTokens;
|
|
96
99
|
const queryTotalTokensPerMonth = numQueries * queryTokens;
|
|
97
100
|
|
package/src/commands/eval.js
CHANGED
|
@@ -192,9 +192,13 @@ function registerEval(program) {
|
|
|
192
192
|
.option('--json', 'Machine-readable JSON output')
|
|
193
193
|
.option('-q, --quiet', 'Suppress non-essential output')
|
|
194
194
|
.action(async (opts) => {
|
|
195
|
+
const telemetry = require('../lib/telemetry');
|
|
196
|
+
const done = telemetry.timer('cli_eval');
|
|
197
|
+
|
|
195
198
|
// Dispatch to rerank eval mode
|
|
196
199
|
if (opts.mode === 'rerank') {
|
|
197
200
|
await evalRerank(opts);
|
|
201
|
+
done();
|
|
198
202
|
return;
|
|
199
203
|
}
|
|
200
204
|
|
|
@@ -438,7 +442,9 @@ function registerEval(program) {
|
|
|
438
442
|
console.log(ui.dim(' ๐ก Low recall? Try: increasing --limit, different chunking strategy, or review your test set'));
|
|
439
443
|
}
|
|
440
444
|
console.log(ui.dim(' ๐ก Evaluate reranking quality: vai eval --mode rerank --test-set rerank-test.jsonl'));
|
|
445
|
+
done();
|
|
441
446
|
} catch (err) {
|
|
447
|
+
telemetry.send('cli_error', { command: 'eval', errorType: err.constructor.name });
|
|
442
448
|
console.error(ui.error(err.message));
|
|
443
449
|
process.exit(1);
|
|
444
450
|
} finally {
|
package/src/commands/explain.js
CHANGED
|
@@ -127,6 +127,8 @@ function registerExplain(program) {
|
|
|
127
127
|
.description('Learn about embeddings, reranking, vector search, and more')
|
|
128
128
|
.option('--json', 'Output in JSON format')
|
|
129
129
|
.action((concept, opts) => {
|
|
130
|
+
const telemetry = require('../lib/telemetry');
|
|
131
|
+
telemetry.send('cli_explain', { topic: concept || 'list' });
|
|
130
132
|
if (!concept) {
|
|
131
133
|
// Show topic list
|
|
132
134
|
if (opts.json) {
|
package/src/commands/generate.js
CHANGED
|
@@ -123,7 +123,9 @@ function registerGenerate(program) {
|
|
|
123
123
|
.option('-l, --list', 'List available components')
|
|
124
124
|
.option('-q, --quiet', 'Suppress hints and metadata')
|
|
125
125
|
.action(async (component, opts) => {
|
|
126
|
+
const telemetry = require('../lib/telemetry');
|
|
126
127
|
try {
|
|
128
|
+
telemetry.send('cli_generate', { provider: opts.target, model: opts.model });
|
|
127
129
|
// Determine target
|
|
128
130
|
const target = opts.target || detectTarget();
|
|
129
131
|
|
package/src/commands/ingest.js
CHANGED
|
@@ -208,6 +208,8 @@ function registerIngest(program) {
|
|
|
208
208
|
.option('--json', 'Machine-readable JSON output')
|
|
209
209
|
.option('-q, --quiet', 'Suppress progress, show only final summary')
|
|
210
210
|
.action(async (opts) => {
|
|
211
|
+
const telemetry = require('../lib/telemetry');
|
|
212
|
+
const done = telemetry.timer('cli_ingest');
|
|
211
213
|
const startTime = Date.now();
|
|
212
214
|
|
|
213
215
|
// Validate file exists
|
|
@@ -394,7 +396,9 @@ function registerIngest(program) {
|
|
|
394
396
|
}
|
|
395
397
|
}
|
|
396
398
|
}
|
|
399
|
+
done({ format, docCount: succeeded });
|
|
397
400
|
} catch (err) {
|
|
401
|
+
telemetry.send('cli_error', { command: 'ingest', errorType: err.constructor.name });
|
|
398
402
|
console.error(ui.error(err.message));
|
|
399
403
|
process.exit(1);
|
|
400
404
|
} finally {
|
package/src/commands/init.js
CHANGED
|
@@ -21,6 +21,8 @@ function registerInit(program) {
|
|
|
21
21
|
.option('--json', 'Output created config as JSON (non-interactive)')
|
|
22
22
|
.option('-q, --quiet', 'Suppress non-essential output')
|
|
23
23
|
.action(async (opts) => {
|
|
24
|
+
const telemetry = require('../lib/telemetry');
|
|
25
|
+
telemetry.send('cli_init');
|
|
24
26
|
// Check for existing config
|
|
25
27
|
const existing = findProjectFile();
|
|
26
28
|
if (existing && !opts.force) {
|
|
@@ -17,6 +17,8 @@ function registerMcpServer(program) {
|
|
|
17
17
|
.option('--no-sse', 'Disable SSE transport (SSE is enabled by default for HTTP)')
|
|
18
18
|
.option('--verbose', 'Enable debug logging to stderr')
|
|
19
19
|
.action(async (opts) => {
|
|
20
|
+
const telemetry = require('../lib/telemetry');
|
|
21
|
+
telemetry.send('cli_mcp_start', { transport: opts.transport });
|
|
20
22
|
if (opts.verbose) {
|
|
21
23
|
process.env.VAI_MCP_VERBOSE = '1';
|
|
22
24
|
}
|
package/src/commands/models.js
CHANGED
|
@@ -46,6 +46,8 @@ function registerModels(program) {
|
|
|
46
46
|
.option('--json', 'Machine-readable JSON output')
|
|
47
47
|
.option('-q, --quiet', 'Suppress non-essential output')
|
|
48
48
|
.action((opts) => {
|
|
49
|
+
const telemetry = require('../lib/telemetry');
|
|
50
|
+
telemetry.send('cli_models', { category: opts.type });
|
|
49
51
|
let models = MODEL_CATALOG;
|
|
50
52
|
|
|
51
53
|
// Separate current and legacy models
|
package/src/commands/ping.js
CHANGED
|
@@ -15,6 +15,7 @@ function registerPing(program) {
|
|
|
15
15
|
.option('-q, --quiet', 'Suppress non-essential output')
|
|
16
16
|
.option('--mask', 'Mask sensitive info (cluster hostnames, endpoints) in output. Also enabled by VAI_MASK=1 env var.')
|
|
17
17
|
.action(async (opts) => {
|
|
18
|
+
const telemetry = require('../lib/telemetry');
|
|
18
19
|
// Support env var so all recordings are masked without remembering the flag
|
|
19
20
|
if (process.env.VAI_MASK === '1' || process.env.VAI_MASK === 'true') {
|
|
20
21
|
opts.mask = true;
|
|
@@ -242,6 +243,12 @@ function registerPing(program) {
|
|
|
242
243
|
}
|
|
243
244
|
}
|
|
244
245
|
|
|
246
|
+
// Telemetry
|
|
247
|
+
telemetry.send('cli_ping', {
|
|
248
|
+
voyageOk: !!results.voyage?.ok,
|
|
249
|
+
mongoOk: !!results.mongodb?.ok,
|
|
250
|
+
});
|
|
251
|
+
|
|
245
252
|
// Emit JSON at the end with all results
|
|
246
253
|
if (opts.json) {
|
|
247
254
|
console.log(JSON.stringify({ ok: true, ...results }, null, 2));
|
package/src/commands/pipeline.js
CHANGED
|
@@ -67,6 +67,7 @@ function registerPipeline(program) {
|
|
|
67
67
|
.option('-q, --quiet', 'Suppress non-essential output')
|
|
68
68
|
.action(async (input, opts) => {
|
|
69
69
|
let client;
|
|
70
|
+
const telemetry = require('../lib/telemetry');
|
|
70
71
|
try {
|
|
71
72
|
// Merge project config
|
|
72
73
|
const { config: proj } = loadProject();
|
|
@@ -89,6 +90,13 @@ function registerPipeline(program) {
|
|
|
89
90
|
process.exit(1);
|
|
90
91
|
}
|
|
91
92
|
|
|
93
|
+
const done = telemetry.timer('cli_pipeline', {
|
|
94
|
+
model,
|
|
95
|
+
chunkStrategy: strategy,
|
|
96
|
+
chunkSize,
|
|
97
|
+
createIndex: !!opts.createIndex,
|
|
98
|
+
});
|
|
99
|
+
|
|
92
100
|
if (!STRATEGIES.includes(strategy)) {
|
|
93
101
|
console.error(ui.error(`Unknown strategy: "${strategy}". Available: ${STRATEGIES.join(', ')}`));
|
|
94
102
|
process.exit(1);
|
|
@@ -313,7 +321,14 @@ function registerPipeline(program) {
|
|
|
313
321
|
console.log('');
|
|
314
322
|
console.log(ui.dim(' Next: vai query "your search" --db ' + db + ' --collection ' + collection));
|
|
315
323
|
}
|
|
324
|
+
|
|
325
|
+
done({
|
|
326
|
+
fileCount: files.length,
|
|
327
|
+
chunkCount: allChunks.length,
|
|
328
|
+
docCount: insertResult.insertedCount,
|
|
329
|
+
});
|
|
316
330
|
} catch (err) {
|
|
331
|
+
telemetry.send('cli_error', { command: 'pipeline', errorType: err.constructor.name });
|
|
317
332
|
console.error(ui.error(err.message));
|
|
318
333
|
process.exit(1);
|
|
319
334
|
} finally {
|