voyageai-cli 1.29.0 → 1.30.1
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 +1 -1
- package/src/cli.js +6 -0
- package/src/commands/benchmark.js +22 -8
- package/src/commands/chat.js +50 -11
- 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/export.js +124 -0
- package/src/commands/generate.js +2 -0
- package/src/commands/import.js +195 -0
- package/src/commands/index-workspace.js +239 -0
- package/src/commands/ingest.js +4 -0
- package/src/commands/init.js +2 -0
- package/src/commands/mcp-server.js +115 -3
- 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 +163 -9
- 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 +286 -0
- 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 +6 -0
- package/src/lib/export/contexts/benchmark-export.js +27 -0
- package/src/lib/export/contexts/chat-export.js +41 -0
- package/src/lib/export/contexts/explore-export.js +22 -0
- package/src/lib/export/contexts/search-export.js +54 -0
- package/src/lib/export/contexts/workflow-export.js +80 -0
- package/src/lib/export/formats/clipboard-export.js +29 -0
- package/src/lib/export/formats/csv-export.js +45 -0
- package/src/lib/export/formats/json-export.js +50 -0
- package/src/lib/export/formats/markdown-export.js +189 -0
- package/src/lib/export/formats/mermaid-export.js +274 -0
- package/src/lib/export/formats/pdf-export.js +117 -0
- package/src/lib/export/formats/png-export.js +96 -0
- package/src/lib/export/formats/svg-export.js +116 -0
- package/src/lib/export/index.js +175 -0
- package/src/lib/llm.js +125 -18
- 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-scaffold.js +61 -0
- package/src/lib/workflow-test-runner.js +208 -0
- package/src/lib/workflow.js +333 -28
- package/src/mcp/install.js +280 -7
- package/src/mcp/schemas/index.js +40 -0
- package/src/mcp/server.js +2 -0
- package/src/mcp/tools/workspace.js +463 -0
- package/src/playground/announcements.md +56 -0
- package/src/playground/help/workflow-nodes.js +472 -0
- package/src/playground/index.html +13134 -8507
- package/src/playground/vendor/mermaid.min.js +2811 -0
- package/src/workflows/rag-chat.json +165 -0
- package/src/workflows/tests/consistency-check.happy-path.test.json +28 -0
- package/src/workflows/tests/consistency-check.missing-source.test.json +26 -0
- package/src/workflows/tests/cost-analysis.happy-path.test.json +28 -0
- package/src/workflows/tests/enrich-and-ingest.happy-path.test.json +38 -0
- package/src/workflows/tests/enrich-and-ingest.notify-fails.test.json +38 -0
- package/src/workflows/tests/intelligent-ingest.all-filtered.test.json +26 -0
- package/src/workflows/tests/intelligent-ingest.happy-path.test.json +28 -0
- package/src/workflows/tests/kb-health-report.custom-queries.test.json +24 -0
- package/src/workflows/tests/kb-health-report.happy-path.test.json +26 -0
- package/src/workflows/tests/multi-collection-search.happy-path.test.json +40 -0
- package/src/workflows/tests/multi-collection-search.one-empty.test.json +28 -0
- package/src/workflows/tests/rag-chat.happy-path.test.json +26 -0
- package/src/workflows/tests/rag-chat.no-relevant-results.test.json +25 -0
- package/src/workflows/tests/research-and-summarize.happy-path.test.json +33 -0
- package/src/workflows/tests/research-and-summarize.no-results.test.json +29 -0
- package/src/workflows/tests/search-with-fallback.empty-both.test.json +24 -0
- package/src/workflows/tests/search-with-fallback.fallback-branch.test.json +24 -0
- package/src/workflows/tests/search-with-fallback.happy-path.test.json +27 -0
- package/src/workflows/tests/smart-ingest.duplicate-detected.test.json +34 -0
- package/src/workflows/tests/smart-ingest.happy-path.test.json +31 -0
|
@@ -67,6 +67,9 @@ function loadAnnouncementsFromMarkdown() {
|
|
|
67
67
|
badge: metadata.badge || 'Info',
|
|
68
68
|
published: metadata.published || new Date().toISOString().split('T')[0],
|
|
69
69
|
expires: metadata.expires || '2099-12-31',
|
|
70
|
+
bg_image: metadata.bg_image || null,
|
|
71
|
+
bg_color: metadata.bg_color || null,
|
|
72
|
+
icon: metadata.icon || null,
|
|
70
73
|
cta: {
|
|
71
74
|
label: metadata.cta_label || 'Learn More',
|
|
72
75
|
action: metadata.cta_action || 'link',
|
|
@@ -159,7 +162,9 @@ function createPlaygroundServer() {
|
|
|
159
162
|
try {
|
|
160
163
|
// Serve HTML
|
|
161
164
|
if (req.method === 'GET' && (req.url === '/' || req.url === '/index.html')) {
|
|
162
|
-
const
|
|
165
|
+
const { getVersion } = require('../lib/banner');
|
|
166
|
+
let html = fs.readFileSync(htmlPath, 'utf8');
|
|
167
|
+
html = html.replace('</head>', `<script>window.__VAI_VERSION__="${getVersion()}";</script></head>`);
|
|
163
168
|
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
|
|
164
169
|
res.end(html);
|
|
165
170
|
return;
|
|
@@ -223,6 +228,43 @@ function createPlaygroundServer() {
|
|
|
223
228
|
return;
|
|
224
229
|
}
|
|
225
230
|
|
|
231
|
+
// Serve announcement assets: /assets/announcements/{filename}
|
|
232
|
+
const assetMatch = req.url.match(/^\/assets\/announcements\/([a-zA-Z0-9_.-]+\.(jpg|jpeg|png|webp|gif))$/);
|
|
233
|
+
if (req.method === 'GET' && assetMatch) {
|
|
234
|
+
const assetPath = path.join(__dirname, '..', 'playground', 'assets', 'announcements', assetMatch[1]);
|
|
235
|
+
if (fs.existsSync(assetPath)) {
|
|
236
|
+
const mimeTypes = { jpg: 'image/jpeg', jpeg: 'image/jpeg', png: 'image/png', webp: 'image/webp', gif: 'image/gif' };
|
|
237
|
+
const data = fs.readFileSync(assetPath);
|
|
238
|
+
res.writeHead(200, {
|
|
239
|
+
'Content-Type': mimeTypes[assetMatch[2]] || 'application/octet-stream',
|
|
240
|
+
'Cache-Control': 'public, max-age=86400',
|
|
241
|
+
});
|
|
242
|
+
res.end(data);
|
|
243
|
+
} else {
|
|
244
|
+
res.writeHead(404);
|
|
245
|
+
res.end('Asset not found');
|
|
246
|
+
}
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// Serve vendor assets (bundled JS libraries)
|
|
251
|
+
const vendorMatch = req.url.match(/^\/vendor\/([a-zA-Z0-9_.-]+\.js)$/);
|
|
252
|
+
if (req.method === 'GET' && vendorMatch) {
|
|
253
|
+
const vendorPath = path.join(__dirname, '..', 'playground', 'vendor', vendorMatch[1]);
|
|
254
|
+
if (fs.existsSync(vendorPath)) {
|
|
255
|
+
const data = fs.readFileSync(vendorPath);
|
|
256
|
+
res.writeHead(200, {
|
|
257
|
+
'Content-Type': 'application/javascript; charset=utf-8',
|
|
258
|
+
'Cache-Control': 'public, max-age=86400',
|
|
259
|
+
});
|
|
260
|
+
res.end(data);
|
|
261
|
+
} else {
|
|
262
|
+
res.writeHead(404);
|
|
263
|
+
res.end('Vendor asset not found');
|
|
264
|
+
}
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
|
|
226
268
|
// API: Models
|
|
227
269
|
if (req.method === 'GET' && req.url === '/api/models') {
|
|
228
270
|
const models = MODEL_CATALOG.filter(m => !m.legacy && !m.local && !m.unreleased);
|
|
@@ -360,6 +402,14 @@ function createPlaygroundServer() {
|
|
|
360
402
|
return;
|
|
361
403
|
}
|
|
362
404
|
|
|
405
|
+
// API: Workflow node help
|
|
406
|
+
if (req.method === 'GET' && req.url === '/api/workflows/node-help') {
|
|
407
|
+
const nodeHelp = require('../playground/help/workflow-nodes');
|
|
408
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
409
|
+
res.end(JSON.stringify({ nodeHelp }));
|
|
410
|
+
return;
|
|
411
|
+
}
|
|
412
|
+
|
|
363
413
|
// API: Chat config (GET)
|
|
364
414
|
if (req.method === 'GET' && req.url === '/api/chat/config') {
|
|
365
415
|
const { resolveLLMConfig } = require('../lib/llm');
|
|
@@ -428,6 +478,62 @@ function createPlaygroundServer() {
|
|
|
428
478
|
return;
|
|
429
479
|
}
|
|
430
480
|
|
|
481
|
+
// API: MCP status — installation status across all tools
|
|
482
|
+
if (req.method === 'GET' && req.url === '/api/mcp/status') {
|
|
483
|
+
try {
|
|
484
|
+
const { statusAll } = require('../mcp/install');
|
|
485
|
+
const results = statusAll();
|
|
486
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
487
|
+
res.end(JSON.stringify(results));
|
|
488
|
+
} catch (err) {
|
|
489
|
+
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
490
|
+
res.end(JSON.stringify({ error: err.message }));
|
|
491
|
+
}
|
|
492
|
+
return;
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
// API: MCP install — install vai into a target tool
|
|
496
|
+
if (req.method === 'POST' && req.url === '/api/mcp/install') {
|
|
497
|
+
try {
|
|
498
|
+
const body = await readBody(req);
|
|
499
|
+
const { target, force } = JSON.parse(body);
|
|
500
|
+
if (!target) {
|
|
501
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
502
|
+
res.end(JSON.stringify({ error: 'target is required' }));
|
|
503
|
+
return;
|
|
504
|
+
}
|
|
505
|
+
const { installTarget } = require('../mcp/install');
|
|
506
|
+
const result = installTarget(target, { force: force || false });
|
|
507
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
508
|
+
res.end(JSON.stringify(result));
|
|
509
|
+
} catch (err) {
|
|
510
|
+
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
511
|
+
res.end(JSON.stringify({ error: err.message }));
|
|
512
|
+
}
|
|
513
|
+
return;
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
// API: MCP uninstall — remove vai from a target tool
|
|
517
|
+
if (req.method === 'POST' && req.url === '/api/mcp/uninstall') {
|
|
518
|
+
try {
|
|
519
|
+
const body = await readBody(req);
|
|
520
|
+
const { target } = JSON.parse(body);
|
|
521
|
+
if (!target) {
|
|
522
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
523
|
+
res.end(JSON.stringify({ error: 'target is required' }));
|
|
524
|
+
return;
|
|
525
|
+
}
|
|
526
|
+
const { uninstallTarget } = require('../mcp/install');
|
|
527
|
+
const result = uninstallTarget(target);
|
|
528
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
529
|
+
res.end(JSON.stringify(result));
|
|
530
|
+
} catch (err) {
|
|
531
|
+
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
532
|
+
res.end(JSON.stringify({ error: err.message }));
|
|
533
|
+
}
|
|
534
|
+
return;
|
|
535
|
+
}
|
|
536
|
+
|
|
431
537
|
// API: Settings origins — where each config value comes from
|
|
432
538
|
if (req.method === 'GET' && req.url === '/api/settings/origins') {
|
|
433
539
|
const { resolveLLMConfig } = require('../lib/llm');
|
|
@@ -656,6 +762,17 @@ function createPlaygroundServer() {
|
|
|
656
762
|
description: def.description || '',
|
|
657
763
|
}));
|
|
658
764
|
|
|
765
|
+
// Derive capabilities from tools
|
|
766
|
+
const toolsList = vai.tools || [];
|
|
767
|
+
const capabilities = [];
|
|
768
|
+
if (toolsList.includes('http')) capabilities.push('NETWORK');
|
|
769
|
+
if (toolsList.includes('ingest') || toolsList.includes('aggregate')) capabilities.push('WRITE_DB');
|
|
770
|
+
if (toolsList.includes('generate')) capabilities.push('LLM');
|
|
771
|
+
if (toolsList.includes('loop') || toolsList.includes('forEach')) capabilities.push('LOOP');
|
|
772
|
+
if (toolsList.some(t => ['query','search','collections','aggregate'].includes(t))) capabilities.push('READ_DB');
|
|
773
|
+
|
|
774
|
+
const isOfficial = (r.name || '').startsWith('@vaicli/');
|
|
775
|
+
|
|
659
776
|
return {
|
|
660
777
|
name: shortName,
|
|
661
778
|
packageName: r.name,
|
|
@@ -663,11 +780,10 @@ function createPlaygroundServer() {
|
|
|
663
780
|
description: r.description || '',
|
|
664
781
|
category,
|
|
665
782
|
tags: vai.tags || [],
|
|
666
|
-
tools:
|
|
667
|
-
steps: vai.steps ||
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
tier: (r.name || '').startsWith('@vaicli/') ? 'official' : 'community',
|
|
783
|
+
tools: toolsList,
|
|
784
|
+
steps: vai.steps || toolsList.length || 0,
|
|
785
|
+
toolCount: toolsList.length,
|
|
786
|
+
tier: isOfficial ? 'official' : 'community',
|
|
671
787
|
downloads: 0,
|
|
672
788
|
featured: FEATURED.includes(shortName),
|
|
673
789
|
installed: installedNames.has(r.name),
|
|
@@ -676,6 +792,10 @@ function createPlaygroundServer() {
|
|
|
676
792
|
author,
|
|
677
793
|
assets,
|
|
678
794
|
inputs,
|
|
795
|
+
capabilities,
|
|
796
|
+
verified: isOfficial,
|
|
797
|
+
security: [],
|
|
798
|
+
rating: null,
|
|
679
799
|
};
|
|
680
800
|
});
|
|
681
801
|
|
|
@@ -1374,15 +1494,23 @@ function createPlaygroundServer() {
|
|
|
1374
1494
|
// API: Validate a workflow definition
|
|
1375
1495
|
if (req.url === '/api/workflows/validate') {
|
|
1376
1496
|
const { validateWorkflow } = require('../lib/workflow');
|
|
1377
|
-
const { definition } = parsed;
|
|
1497
|
+
const { definition, mode } = parsed;
|
|
1378
1498
|
if (!definition) {
|
|
1379
1499
|
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
1380
1500
|
res.end(JSON.stringify({ error: 'definition is required' }));
|
|
1381
1501
|
return;
|
|
1382
1502
|
}
|
|
1383
|
-
const
|
|
1503
|
+
const validationMode = mode || 'strict';
|
|
1504
|
+
const result = validateWorkflow(definition, { mode: validationMode });
|
|
1505
|
+
|
|
1384
1506
|
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
1385
|
-
|
|
1507
|
+
|
|
1508
|
+
if (validationMode === 'draft') {
|
|
1509
|
+
res.end(JSON.stringify(result));
|
|
1510
|
+
} else {
|
|
1511
|
+
// Backward compatible format for strict mode
|
|
1512
|
+
res.end(JSON.stringify({ valid: result.length === 0, errors: result }));
|
|
1513
|
+
}
|
|
1386
1514
|
return;
|
|
1387
1515
|
}
|
|
1388
1516
|
|
|
@@ -1486,6 +1614,32 @@ function createPlaygroundServer() {
|
|
|
1486
1614
|
}
|
|
1487
1615
|
}
|
|
1488
1616
|
|
|
1617
|
+
// ── Export API endpoints ──
|
|
1618
|
+
const exportMatch = req.url.match(/^\/api\/export\/(workflow|chat|search|benchmark)$/);
|
|
1619
|
+
if (req.method === 'POST' && exportMatch) {
|
|
1620
|
+
const context = exportMatch[1];
|
|
1621
|
+
try {
|
|
1622
|
+
const body = JSON.parse(await readBody(req));
|
|
1623
|
+
const { exportArtifact } = require('../lib/export');
|
|
1624
|
+
const result = await exportArtifact({
|
|
1625
|
+
context,
|
|
1626
|
+
format: body.format || 'json',
|
|
1627
|
+
data: body.data || {},
|
|
1628
|
+
options: body.options || {},
|
|
1629
|
+
});
|
|
1630
|
+
const isBinary = Buffer.isBuffer(result.content);
|
|
1631
|
+
res.writeHead(200, {
|
|
1632
|
+
'Content-Type': result.mimeType,
|
|
1633
|
+
'Content-Disposition': `attachment; filename="${result.suggestedFilename}"`,
|
|
1634
|
+
});
|
|
1635
|
+
res.end(isBinary ? result.content : result.content);
|
|
1636
|
+
} catch (err) {
|
|
1637
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
1638
|
+
res.end(JSON.stringify({ error: err.message }));
|
|
1639
|
+
}
|
|
1640
|
+
return;
|
|
1641
|
+
}
|
|
1642
|
+
|
|
1489
1643
|
// 404
|
|
1490
1644
|
res.writeHead(404, { 'Content-Type': 'application/json' });
|
|
1491
1645
|
res.end(JSON.stringify({ error: 'Not found' }));
|
package/src/commands/query.js
CHANGED
|
@@ -5,6 +5,7 @@ const { generateEmbeddings, apiRequest } = require('../lib/api');
|
|
|
5
5
|
const { getMongoCollection } = require('../lib/mongo');
|
|
6
6
|
const { loadProject } = require('../lib/project');
|
|
7
7
|
const ui = require('../lib/ui');
|
|
8
|
+
const { showCombinedCostSummary } = require('../lib/cost-display');
|
|
8
9
|
|
|
9
10
|
/**
|
|
10
11
|
* Register the query command on a Commander program.
|
|
@@ -33,6 +34,7 @@ function registerQuery(program) {
|
|
|
33
34
|
.option('-q, --quiet', 'Suppress non-essential output')
|
|
34
35
|
.action(async (text, opts) => {
|
|
35
36
|
let client;
|
|
37
|
+
const telemetry = require('../lib/telemetry');
|
|
36
38
|
try {
|
|
37
39
|
// Merge project config
|
|
38
40
|
const { config: proj } = loadProject();
|
|
@@ -51,6 +53,14 @@ function registerQuery(program) {
|
|
|
51
53
|
process.exit(1);
|
|
52
54
|
}
|
|
53
55
|
|
|
56
|
+
const done = telemetry.timer('cli_query', {
|
|
57
|
+
model,
|
|
58
|
+
rerankModel: doRerank ? rerankModel : undefined,
|
|
59
|
+
rerank: doRerank,
|
|
60
|
+
limit: opts.limit,
|
|
61
|
+
topK: opts.topK,
|
|
62
|
+
});
|
|
63
|
+
|
|
54
64
|
const useColor = !opts.json;
|
|
55
65
|
const useSpinner = useColor && !opts.quiet;
|
|
56
66
|
|
|
@@ -242,8 +252,14 @@ function registerQuery(program) {
|
|
|
242
252
|
if (!opts.quiet) {
|
|
243
253
|
const totalTokens = embedTokens + rerankTokens;
|
|
244
254
|
console.log(ui.dim(` Tokens: ${totalTokens} (embed: ${embedTokens}${rerankTokens ? `, rerank: ${rerankTokens}` : ''})`));
|
|
255
|
+
const costOps = [{ model, tokens: embedTokens, label: `embed (${model})` }];
|
|
256
|
+
if (rerankTokens) costOps.push({ model: rerankModel, tokens: rerankTokens, label: `rerank (${rerankModel})` });
|
|
257
|
+
showCombinedCostSummary(costOps, opts);
|
|
245
258
|
}
|
|
259
|
+
|
|
260
|
+
done({ resultCount: finalResults.length });
|
|
246
261
|
} catch (err) {
|
|
262
|
+
telemetry.send('cli_error', { command: 'query', errorType: err.constructor.name });
|
|
247
263
|
console.error(ui.error(err.message));
|
|
248
264
|
process.exit(1);
|
|
249
265
|
} finally {
|
package/src/commands/rerank.js
CHANGED
|
@@ -4,6 +4,7 @@ const fs = require('fs');
|
|
|
4
4
|
const { DEFAULT_RERANK_MODEL } = require('../lib/catalog');
|
|
5
5
|
const { apiRequest } = require('../lib/api');
|
|
6
6
|
const ui = require('../lib/ui');
|
|
7
|
+
const { showCostSummary } = require('../lib/cost-display');
|
|
7
8
|
|
|
8
9
|
/**
|
|
9
10
|
* Register the rerank command on a Commander program.
|
|
@@ -25,6 +26,7 @@ function registerRerank(program) {
|
|
|
25
26
|
.option('--json', 'Machine-readable JSON output')
|
|
26
27
|
.option('-q, --quiet', 'Suppress non-essential output')
|
|
27
28
|
.action(async (opts) => {
|
|
29
|
+
const telemetry = require('../lib/telemetry');
|
|
28
30
|
try {
|
|
29
31
|
let documents = opts.documents;
|
|
30
32
|
|
|
@@ -81,6 +83,12 @@ function registerRerank(program) {
|
|
|
81
83
|
opts.model = chosenModel;
|
|
82
84
|
}
|
|
83
85
|
|
|
86
|
+
const done = telemetry.timer('cli_rerank', {
|
|
87
|
+
model: opts.model,
|
|
88
|
+
docCount: documents.length,
|
|
89
|
+
topK: opts.topK,
|
|
90
|
+
});
|
|
91
|
+
|
|
84
92
|
const body = {
|
|
85
93
|
query: opts.query,
|
|
86
94
|
documents,
|
|
@@ -120,6 +128,7 @@ function registerRerank(program) {
|
|
|
120
128
|
if (result.usage) {
|
|
121
129
|
console.log(ui.label('Tokens', ui.dim(String(result.usage.total_tokens))));
|
|
122
130
|
}
|
|
131
|
+
showCostSummary(result.model || opts.model, result.usage?.total_tokens || 0, opts);
|
|
123
132
|
console.log('');
|
|
124
133
|
}
|
|
125
134
|
|
|
@@ -134,7 +143,10 @@ function registerRerank(program) {
|
|
|
134
143
|
|
|
135
144
|
console.log('');
|
|
136
145
|
console.log(ui.success('Reranking complete'));
|
|
146
|
+
|
|
147
|
+
done();
|
|
137
148
|
} catch (err) {
|
|
149
|
+
telemetry.send('cli_error', { command: 'rerank', errorType: err.constructor.name });
|
|
138
150
|
console.error(ui.error(err.message));
|
|
139
151
|
process.exit(1);
|
|
140
152
|
}
|
package/src/commands/scaffold.js
CHANGED
|
@@ -55,7 +55,9 @@ function registerScaffold(program) {
|
|
|
55
55
|
.option('--dry-run', 'Show what would be created without writing')
|
|
56
56
|
.option('-q, --quiet', 'Suppress non-essential output')
|
|
57
57
|
.action(async (name, opts) => {
|
|
58
|
+
const telemetry = require('../lib/telemetry');
|
|
58
59
|
try {
|
|
60
|
+
telemetry.send('cli_scaffold', { template: opts.target });
|
|
59
61
|
const target = opts.target;
|
|
60
62
|
const projectDir = path.resolve(process.cwd(), name);
|
|
61
63
|
|
package/src/commands/search.js
CHANGED
|
@@ -4,6 +4,7 @@ const { getDefaultModel } = require('../lib/catalog');
|
|
|
4
4
|
const { generateEmbeddings } = require('../lib/api');
|
|
5
5
|
const { getMongoCollection } = require('../lib/mongo');
|
|
6
6
|
const ui = require('../lib/ui');
|
|
7
|
+
const { showCostSummary } = require('../lib/cost-display');
|
|
7
8
|
|
|
8
9
|
/**
|
|
9
10
|
* Register the search command on a Commander program.
|
|
@@ -29,7 +30,13 @@ function registerSearch(program) {
|
|
|
29
30
|
.option('-q, --quiet', 'Suppress non-essential output')
|
|
30
31
|
.action(async (opts) => {
|
|
31
32
|
let client;
|
|
33
|
+
const telemetry = require('../lib/telemetry');
|
|
32
34
|
try {
|
|
35
|
+
const done = telemetry.timer('cli_search', {
|
|
36
|
+
model: opts.model,
|
|
37
|
+
limit: opts.limit,
|
|
38
|
+
});
|
|
39
|
+
|
|
33
40
|
const useColor = !opts.json;
|
|
34
41
|
const useSpinner = useColor && !opts.quiet;
|
|
35
42
|
let spin;
|
|
@@ -93,9 +100,12 @@ function registerSearch(program) {
|
|
|
93
100
|
if (!opts.quiet) {
|
|
94
101
|
console.log(ui.label('Query', ui.cyan(`"${opts.query}"`)));
|
|
95
102
|
console.log(ui.label('Results', String(cleanResults.length)));
|
|
103
|
+
showCostSummary(opts.model, embedResult.usage?.total_tokens || 0, opts);
|
|
96
104
|
console.log('');
|
|
97
105
|
}
|
|
98
106
|
|
|
107
|
+
done({ resultCount: cleanResults.length });
|
|
108
|
+
|
|
99
109
|
if (cleanResults.length === 0) {
|
|
100
110
|
console.log(ui.yellow('No results found.'));
|
|
101
111
|
return;
|
|
@@ -113,6 +123,7 @@ function registerSearch(program) {
|
|
|
113
123
|
console.log('');
|
|
114
124
|
}
|
|
115
125
|
} catch (err) {
|
|
126
|
+
telemetry.send('cli_error', { command: 'search', errorType: err.constructor.name });
|
|
116
127
|
console.error(ui.error(err.message));
|
|
117
128
|
process.exit(1);
|
|
118
129
|
} finally {
|
|
@@ -5,6 +5,7 @@ const { generateEmbeddings } = require('../lib/api');
|
|
|
5
5
|
const { cosineSimilarity } = require('../lib/math');
|
|
6
6
|
const { getDefaultModel } = require('../lib/catalog');
|
|
7
7
|
const ui = require('../lib/ui');
|
|
8
|
+
const { showCostSummary } = require('../lib/cost-display');
|
|
8
9
|
|
|
9
10
|
/**
|
|
10
11
|
* Register the similarity command on a Commander program.
|
|
@@ -23,6 +24,7 @@ function registerSimilarity(program) {
|
|
|
23
24
|
.option('--json', 'Machine-readable JSON output')
|
|
24
25
|
.option('-q, --quiet', 'Suppress non-essential output')
|
|
25
26
|
.action(async (texts, opts) => {
|
|
27
|
+
const telemetry = require('../lib/telemetry');
|
|
26
28
|
try {
|
|
27
29
|
let textA = null;
|
|
28
30
|
let compareTexts = [];
|
|
@@ -57,6 +59,8 @@ function registerSimilarity(program) {
|
|
|
57
59
|
process.exit(1);
|
|
58
60
|
}
|
|
59
61
|
|
|
62
|
+
const done = telemetry.timer('cli_similarity', { model: opts.model });
|
|
63
|
+
|
|
60
64
|
// Batch all texts into one API call
|
|
61
65
|
const allTexts = [textA, ...compareTexts];
|
|
62
66
|
|
|
@@ -113,6 +117,7 @@ function registerSimilarity(program) {
|
|
|
113
117
|
console.log(ui.label('Text B', `"${truncate(compareTexts[0], 70)}"`));
|
|
114
118
|
console.log(ui.label('Model', ui.cyan(model)));
|
|
115
119
|
console.log(ui.label('Tokens', ui.dim(String(tokens))));
|
|
120
|
+
showCostSummary(model, tokens, opts);
|
|
116
121
|
console.log('');
|
|
117
122
|
} else {
|
|
118
123
|
// One-vs-many comparison
|
|
@@ -152,9 +157,13 @@ function registerSimilarity(program) {
|
|
|
152
157
|
|
|
153
158
|
console.log('');
|
|
154
159
|
console.log(` ${ui.dim(`${results.length} comparisons, ${tokens} tokens`)}`);
|
|
160
|
+
showCostSummary(model, tokens, opts);
|
|
155
161
|
console.log('');
|
|
156
162
|
}
|
|
163
|
+
|
|
164
|
+
done();
|
|
157
165
|
} catch (err) {
|
|
166
|
+
telemetry.send('cli_error', { command: 'similarity', errorType: err.constructor.name });
|
|
158
167
|
console.error(ui.error(err.message));
|
|
159
168
|
process.exit(1);
|
|
160
169
|
}
|
package/src/commands/store.js
CHANGED
|
@@ -29,6 +29,8 @@ function registerStore(program) {
|
|
|
29
29
|
.option('-q, --quiet', 'Suppress non-essential output')
|
|
30
30
|
.action(async (opts) => {
|
|
31
31
|
let client;
|
|
32
|
+
const telemetry = require('../lib/telemetry');
|
|
33
|
+
const done = telemetry.timer('cli_store', { model: opts.model });
|
|
32
34
|
try {
|
|
33
35
|
// Batch mode: .jsonl file
|
|
34
36
|
if (opts.file && opts.file.endsWith('.jsonl')) {
|
|
@@ -102,7 +104,9 @@ function registerStore(program) {
|
|
|
102
104
|
console.log(ui.label('Tokens', String(embedResult.usage.total_tokens)));
|
|
103
105
|
}
|
|
104
106
|
}
|
|
107
|
+
done();
|
|
105
108
|
} catch (err) {
|
|
109
|
+
telemetry.send('cli_error', { command: 'store', errorType: err.constructor.name });
|
|
106
110
|
console.error(ui.error(err.message));
|
|
107
111
|
process.exit(1);
|
|
108
112
|
} finally {
|