voyageai-cli 1.12.1 → 1.15.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 (44) hide show
  1. package/README.md +3 -3
  2. package/demo-readme.gif +0 -0
  3. package/package.json +1 -1
  4. package/src/cli.js +2 -0
  5. package/src/commands/benchmark.js +164 -0
  6. package/src/commands/completions.js +18 -1
  7. package/src/commands/estimate.js +209 -0
  8. package/src/commands/models.js +32 -4
  9. package/src/lib/catalog.js +42 -18
  10. package/src/lib/explanations.js +183 -0
  11. package/.github/workflows/ci.yml +0 -22
  12. package/CONTRIBUTING.md +0 -81
  13. package/demo.gif +0 -0
  14. package/demo.tape +0 -39
  15. package/scripts/record-demo.sh +0 -63
  16. package/test/commands/about.test.js +0 -23
  17. package/test/commands/benchmark.test.js +0 -319
  18. package/test/commands/completions.test.js +0 -166
  19. package/test/commands/config.test.js +0 -35
  20. package/test/commands/demo.test.js +0 -46
  21. package/test/commands/embed.test.js +0 -42
  22. package/test/commands/explain.test.js +0 -207
  23. package/test/commands/ingest.test.js +0 -261
  24. package/test/commands/models.test.js +0 -132
  25. package/test/commands/ping.test.js +0 -172
  26. package/test/commands/playground.test.js +0 -137
  27. package/test/commands/rerank.test.js +0 -32
  28. package/test/commands/similarity.test.js +0 -79
  29. package/test/commands/store.test.js +0 -26
  30. package/test/fixtures/sample.csv +0 -6
  31. package/test/fixtures/sample.json +0 -7
  32. package/test/fixtures/sample.jsonl +0 -5
  33. package/test/fixtures/sample.txt +0 -5
  34. package/test/lib/api.test.js +0 -133
  35. package/test/lib/banner.test.js +0 -44
  36. package/test/lib/catalog.test.js +0 -99
  37. package/test/lib/config.test.js +0 -124
  38. package/test/lib/explanations.test.js +0 -141
  39. package/test/lib/format.test.js +0 -75
  40. package/test/lib/input.test.js +0 -48
  41. package/test/lib/math.test.js +0 -43
  42. package/test/lib/ui.test.js +0 -79
  43. package/voyageai-cli-playground.png +0 -0
  44. package/voyageai-cli.png +0 -0
@@ -513,6 +513,169 @@ const concepts = {
513
513
  'vai benchmark similarity --query "your search query" --file your-docs.txt',
514
514
  ],
515
515
  },
516
+ 'mixture-of-experts': {
517
+ title: 'Mixture-of-Experts (MoE) Architecture',
518
+ summary: 'How voyage-4-large achieves SOTA quality at 40% lower cost',
519
+ content: [
520
+ `${pc.cyan('Mixture-of-Experts (MoE)')} is a neural network architecture where multiple`,
521
+ `specialized sub-networks ("experts") share a single model. A learned ${pc.cyan('router')}`,
522
+ `selects which experts activate for each input — typically 2-4 out of 8-64 total.`,
523
+ ``,
524
+ `${pc.bold('Why MoE matters for embeddings:')}`,
525
+ ` ${pc.dim('•')} ${pc.cyan('Higher capacity, lower cost')} — the model has more total parameters`,
526
+ ` (knowledge) but only activates a fraction per input, keeping inference fast`,
527
+ ` ${pc.dim('•')} ${pc.cyan('Specialization')} — different experts learn different domains (code,`,
528
+ ` legal, medical) without interfering with each other`,
529
+ ` ${pc.dim('•')} ${pc.cyan('State-of-the-art quality')} — voyage-4-large beats all competitors on`,
530
+ ` RTEB benchmarks while costing 40% less than comparable dense models`,
531
+ ``,
532
+ `${pc.bold('voyage-4-large')} is the ${pc.cyan('first production-grade embedding model')} to use MoE.`,
533
+ `Previous MoE successes (Mixtral, Switch Transformer) were language models —`,
534
+ `applying MoE to embedding models required solving alignment across the shared`,
535
+ `embedding space, which is what makes the Voyage 4 family unique.`,
536
+ ``,
537
+ `${pc.bold('Dense vs MoE:')}`,
538
+ ` ${pc.dim('Dense (voyage-4, voyage-4-lite):')} Every parameter is used for every input.`,
539
+ ` Simpler, predictable latency, lower total parameter count.`,
540
+ ` ${pc.dim('MoE (voyage-4-large):')} Sparse activation — more total parameters, but each`,
541
+ ` input only uses a subset. Higher quality ceiling, similar serving cost.`,
542
+ ``,
543
+ `${pc.bold('In practice:')} You don't need to do anything special to use MoE — the API`,
544
+ `interface is identical. The architecture difference shows up in quality and cost:`,
545
+ ` ${pc.dim('•')} voyage-4-large: $0.12/1M tokens — better quality than voyage-3-large ($0.18/1M)`,
546
+ ` ${pc.dim('•')} 40% cheaper than comparable dense models at the same quality tier`,
547
+ ].join('\n'),
548
+ links: [
549
+ 'https://blog.voyageai.com/2026/01/15/voyage-4-model-family/',
550
+ 'https://www.mongodb.com/docs/voyageai/models/text-embeddings/',
551
+ ],
552
+ tryIt: [
553
+ 'vai embed "test MoE quality" --model voyage-4-large',
554
+ 'vai benchmark embed --models voyage-4-large,voyage-4,voyage-4-lite',
555
+ 'vai models --wide',
556
+ ],
557
+ },
558
+
559
+ 'shared-embedding-space': {
560
+ title: 'Shared Embedding Space',
561
+ summary: 'How Voyage 4 models produce compatible, interchangeable embeddings',
562
+ content: [
563
+ `The Voyage 4 series introduces an ${pc.cyan('industry-first capability')}: all four models`,
564
+ `(voyage-4-large, voyage-4, voyage-4-lite, voyage-4-nano) produce embeddings in`,
565
+ `the ${pc.cyan('same vector space')}. Embeddings from different models are directly comparable.`,
566
+ ``,
567
+ `${pc.bold('What this means:')}`,
568
+ ` ${pc.dim('•')} Embed documents with ${pc.cyan('voyage-4-large')} (best quality, one-time cost)`,
569
+ ` ${pc.dim('•')} Query with ${pc.cyan('voyage-4-lite')} or ${pc.cyan('voyage-4-nano')} (low cost, high volume)`,
570
+ ` ${pc.dim('•')} Cosine similarity works across model boundaries`,
571
+ ` ${pc.dim('•')} Upgrade query model later ${pc.cyan('without re-vectorizing documents')}`,
572
+ ``,
573
+ `${pc.bold('Why this is new:')} Previously, embeddings from different models lived in`,
574
+ `incompatible vector spaces. Switching models meant re-embedding your entire`,
575
+ `corpus — expensive and slow. The shared space eliminates this constraint.`,
576
+ ``,
577
+ `${pc.bold('Recommended workflow:')}`,
578
+ ` ${pc.dim('1.')} Vectorize your document corpus once with ${pc.cyan('voyage-4-large')}`,
579
+ ` ${pc.dim('2.')} Start with ${pc.cyan('voyage-4-lite')} for queries in development / early production`,
580
+ ` ${pc.dim('3.')} Upgrade to ${pc.cyan('voyage-4')} or ${pc.cyan('voyage-4-large')} as accuracy needs grow`,
581
+ ` ${pc.dim('4.')} No re-vectorization needed at any step`,
582
+ ``,
583
+ `${pc.bold('Validate it yourself:')} Use ${pc.cyan('vai benchmark space')} to embed identical text`,
584
+ `with all Voyage 4 models and see the cross-model cosine similarities.`,
585
+ ].join('\n'),
586
+ links: [
587
+ 'https://blog.voyageai.com/2026/01/15/voyage-4-model-family/',
588
+ ],
589
+ tryIt: [
590
+ 'vai benchmark space',
591
+ 'vai benchmark asymmetric --query "your search" --file corpus.txt',
592
+ 'vai estimate --docs 1M --queries 10M',
593
+ ],
594
+ },
595
+
596
+ 'rteb-benchmarks': {
597
+ title: 'RTEB Benchmark Scores',
598
+ summary: 'Retrieval quality scores across embedding providers',
599
+ content: [
600
+ `The ${pc.cyan('Retrieval Embedding Benchmark (RTEB)')} evaluates general-purpose retrieval`,
601
+ `quality across 29 diverse datasets. Scores are ${pc.cyan('NDCG@10')} (normalized discounted`,
602
+ `cumulative gain at top 10 results) — higher is better.`,
603
+ ``,
604
+ `${pc.bold('Current standings (Jan 2026):')}`,
605
+ ` ${pc.cyan('voyage-4-large')} ${pc.bold('71.41')} ${pc.dim('— SOTA, MoE architecture')}`,
606
+ ` ${pc.cyan('voyage-4')} ${pc.bold('70.07')} ${pc.dim('— near voyage-3-large quality')}`,
607
+ ` ${pc.cyan('Gemini Embedding 001')} ${pc.bold('68.66')} ${pc.dim('— Google')}`,
608
+ ` ${pc.cyan('voyage-4-lite')} ${pc.bold('68.10')} ${pc.dim('— near voyage-3.5 quality')}`,
609
+ ` ${pc.cyan('Cohere Embed v4')} ${pc.bold('65.75')} ${pc.dim('— Cohere')}`,
610
+ ` ${pc.cyan('OpenAI v3 Large')} ${pc.bold('62.57')} ${pc.dim('— OpenAI')}`,
611
+ ``,
612
+ `${pc.bold('What the numbers mean:')}`,
613
+ ` ${pc.dim('•')} voyage-4-large beats Gemini by ${pc.cyan('3.87%')}, Cohere by ${pc.cyan('8.20%')}, OpenAI by ${pc.cyan('14.05%')}`,
614
+ ` ${pc.dim('•')} voyage-4 (mid-tier pricing) outperforms all non-Voyage models`,
615
+ ` ${pc.dim('•')} Even voyage-4-lite ($0.02/1M) is competitive with Gemini Embedding`,
616
+ ``,
617
+ `${pc.bold('Asymmetric retrieval bonus:')} When documents are embedded with voyage-4-large`,
618
+ `and queries with a smaller Voyage 4 model, retrieval quality ${pc.cyan('improves')} over`,
619
+ `using the smaller model alone — you get the benefit of the larger model's`,
620
+ `document representations.`,
621
+ ``,
622
+ `${pc.bold('Note:')} These scores are from Voyage AI's evaluation. Independent benchmarks`,
623
+ `may differ. Always test on your own data with ${pc.cyan('vai benchmark similarity')}.`,
624
+ ].join('\n'),
625
+ links: [
626
+ 'https://blog.voyageai.com/2026/01/15/voyage-4-model-family/',
627
+ 'https://docs.google.com/spreadsheets/d/1GfPkqCAjPKaGS9f66IDhMRxVpd2bMuqL2wXjj-kNS7E/',
628
+ ],
629
+ tryIt: [
630
+ 'vai models --benchmarks',
631
+ 'vai benchmark similarity --query "your query" --file your-docs.txt',
632
+ 'vai estimate --docs 1M --queries 10M',
633
+ ],
634
+ },
635
+
636
+ 'voyage-4-nano': {
637
+ title: 'voyage-4-nano — Open-Weight Local Model',
638
+ summary: 'Free, local-first embeddings with shared space compatibility',
639
+ content: [
640
+ `${pc.cyan('voyage-4-nano')} is Voyage AI's first ${pc.cyan('open-weight')} embedding model, freely`,
641
+ `available on Hugging Face under the ${pc.bold('Apache 2.0')} license.`,
642
+ ``,
643
+ `${pc.bold('Key specs:')}`,
644
+ ` ${pc.dim('•')} Dimensions: 512 (default), 128, 256`,
645
+ ` ${pc.dim('•')} Context: 32K tokens`,
646
+ ` ${pc.dim('•')} License: Apache 2.0 (fully open)`,
647
+ ` ${pc.dim('•')} Shared space: Compatible with voyage-4-large/4/4-lite embeddings`,
648
+ ``,
649
+ `${pc.bold('Use cases:')}`,
650
+ ` ${pc.dim('•')} ${pc.cyan('Local development')} — no API key, no network, no cost`,
651
+ ` ${pc.dim('•')} ${pc.cyan('Prototyping')} — fast iteration before committing to API models`,
652
+ ` ${pc.dim('•')} ${pc.cyan('Edge/on-device')} — run inference on your own hardware`,
653
+ ` ${pc.dim('•')} ${pc.cyan('Asymmetric queries')} — use nano for queries against voyage-4-large docs`,
654
+ ``,
655
+ `${pc.bold('Getting started with Hugging Face:')}`,
656
+ ` ${pc.dim('pip install sentence-transformers')}`,
657
+ ` ${pc.dim('from sentence_transformers import SentenceTransformer')}`,
658
+ ` ${pc.dim('model = SentenceTransformer("voyageai/voyage-4-nano")')}`,
659
+ ` ${pc.dim('embeddings = model.encode(["your text here"])')}`,
660
+ ``,
661
+ `${pc.bold('With the Voyage API:')} voyage-4-nano is also available via the standard API`,
662
+ `endpoint, so you can use ${pc.cyan('vai embed --model voyage-4-nano')} for testing before`,
663
+ `switching to local inference.`,
664
+ ``,
665
+ `${pc.bold('Shared space advantage:')} Since nano shares the same embedding space as the`,
666
+ `larger Voyage 4 models, you can prototype locally with nano, then seamlessly`,
667
+ `use the same document embeddings with voyage-4 or voyage-4-large in production.`,
668
+ ].join('\n'),
669
+ links: [
670
+ 'https://huggingface.co/voyageai/voyage-4-nano',
671
+ 'https://blog.voyageai.com/2026/01/15/voyage-4-model-family/',
672
+ ],
673
+ tryIt: [
674
+ 'vai embed "test nano" --model voyage-4-nano',
675
+ 'vai benchmark space',
676
+ 'vai benchmark asymmetric --doc-model voyage-4-large --query-models voyage-4-nano',
677
+ ],
678
+ },
516
679
  };
517
680
 
518
681
  /**
@@ -567,6 +730,26 @@ const aliases = {
567
730
  'model-selection': 'benchmarking',
568
731
  choosing: 'benchmarking',
569
732
  compare: 'benchmarking',
733
+ moe: 'mixture-of-experts',
734
+ 'mixture-of-experts': 'mixture-of-experts',
735
+ 'moe-architecture': 'mixture-of-experts',
736
+ experts: 'mixture-of-experts',
737
+ sparse: 'mixture-of-experts',
738
+ 'shared-space': 'shared-embedding-space',
739
+ 'shared-embedding-space': 'shared-embedding-space',
740
+ 'embedding-space': 'shared-embedding-space',
741
+ interchangeable: 'shared-embedding-space',
742
+ compatible: 'shared-embedding-space',
743
+ rteb: 'rteb-benchmarks',
744
+ 'rteb-benchmarks': 'rteb-benchmarks',
745
+ ndcg: 'rteb-benchmarks',
746
+ scores: 'rteb-benchmarks',
747
+ leaderboard: 'rteb-benchmarks',
748
+ nano: 'voyage-4-nano',
749
+ 'voyage-4-nano': 'voyage-4-nano',
750
+ 'open-weight': 'voyage-4-nano',
751
+ huggingface: 'voyage-4-nano',
752
+ local: 'voyage-4-nano',
570
753
  };
571
754
 
572
755
  /**
@@ -1,22 +0,0 @@
1
- name: CI
2
-
3
- on:
4
- push:
5
- branches: [main]
6
- pull_request:
7
- branches: [main]
8
- workflow_dispatch:
9
-
10
- jobs:
11
- test:
12
- runs-on: ubuntu-latest
13
- strategy:
14
- matrix:
15
- node-version: [18, 20, 22]
16
- steps:
17
- - uses: actions/checkout@v4
18
- - uses: actions/setup-node@v4
19
- with:
20
- node-version: ${{ matrix.node-version }}
21
- - run: npm ci
22
- - run: npm test
package/CONTRIBUTING.md DELETED
@@ -1,81 +0,0 @@
1
- # Contributing to voyageai-cli
2
-
3
- Thanks for your interest in contributing! Here's how to get started.
4
-
5
- ## Development Setup
6
-
7
- ```bash
8
- git clone https://github.com/mrlynn/voyageai-cli.git
9
- cd voyageai-cli
10
- npm install
11
- npm link # makes `vai` available globally for testing
12
- ```
13
-
14
- ## Running Tests
15
-
16
- ```bash
17
- npm test
18
- ```
19
-
20
- Tests use Node.js built-in test runner (`node:test`). No external test framework needed.
21
-
22
- ## Project Structure
23
-
24
- ```
25
- src/
26
- ├── cli.js # Entry point
27
- ├── commands/ # One file per command
28
- │ ├── embed.js
29
- │ ├── rerank.js
30
- │ ├── store.js
31
- │ ├── search.js
32
- │ ├── index.js
33
- │ ├── models.js
34
- │ ├── ping.js
35
- │ ├── config.js
36
- │ └── demo.js
37
- └── lib/ # Shared utilities
38
- ├── api.js # Voyage AI API client
39
- ├── mongo.js # MongoDB connection
40
- ├── catalog.js # Model catalog
41
- ├── config.js # Config file management
42
- ├── format.js # Table formatting
43
- ├── input.js # Text input resolution
44
- ├── ui.js # Colors, spinners, output helpers
45
- └── banner.js # ASCII banner
46
- test/
47
- ├── commands/ # Command tests
48
- └── lib/ # Library tests
49
- ```
50
-
51
- ## Adding a New Command
52
-
53
- 1. Create `src/commands/mycommand.js` exporting a `registerMyCommand(program)` function
54
- 2. Register it in `src/cli.js`
55
- 3. Add tests in `test/commands/mycommand.test.js`
56
- 4. Update README.md with usage examples
57
-
58
- ## Code Style
59
-
60
- - CommonJS (`require`/`module.exports`)
61
- - `'use strict';` at the top of every file
62
- - JSDoc comments on exported functions
63
- - `parseInt(x, 10)` — always include radix
64
- - Errors go to stderr (`console.error`)
65
- - Support `--json` and `--quiet` flags on all commands
66
- - No colors or spinners in `--json` mode
67
-
68
- ## Pull Requests
69
-
70
- - Create a feature branch from `main`
71
- - Include tests for new functionality
72
- - Run `npm test` before submitting
73
- - Write clear commit messages
74
-
75
- ## Reporting Issues
76
-
77
- Open an issue at https://github.com/mrlynn/voyageai-cli/issues with:
78
- - Node.js version (`node --version`)
79
- - OS and version
80
- - Steps to reproduce
81
- - Expected vs actual behavior
package/demo.gif DELETED
Binary file
package/demo.tape DELETED
@@ -1,39 +0,0 @@
1
- # VHS demo tape for voyageai-cli
2
- # Run: vhs demo.tape
3
- # Requires: VOYAGE_API_KEY set in environment
4
-
5
- Output demo.gif
6
-
7
- Set FontSize 16
8
- Set Width 900
9
- Set Height 600
10
- Set Theme "Catppuccin Mocha"
11
- Set Padding 20
12
-
13
- # Show version
14
- Type "vai --version"
15
- Enter
16
- Sleep 1.5s
17
-
18
- # List embedding models
19
- Type "vai models --type embedding"
20
- Enter
21
- Sleep 3s
22
-
23
- # Generate an embedding
24
- Type 'vai embed "What is MongoDB Atlas?"'
25
- Enter
26
- Sleep 4s
27
-
28
- # Explain embeddings (first few lines)
29
- Type "vai explain embeddings"
30
- Enter
31
- Sleep 4s
32
-
33
- # Compare similarity
34
- Type 'vai similarity "MongoDB is great" "MongoDB Atlas is amazing"'
35
- Enter
36
- Sleep 4s
37
-
38
- # Pause at end to show results
39
- Sleep 2s
@@ -1,63 +0,0 @@
1
- #!/usr/bin/env bash
2
- # Record a demo GIF for voyageai-cli
3
- # Requires: VOYAGE_API_KEY environment variable
4
- #
5
- # Usage:
6
- # ./scripts/record-demo.sh # Uses vhs (preferred)
7
- # ./scripts/record-demo.sh asciinema # Uses asciinema instead
8
- #
9
- # Output:
10
- # demo.gif (vhs) or demo.cast (asciinema)
11
-
12
- set -euo pipefail
13
- cd "$(dirname "$0")/.."
14
-
15
- METHOD="${1:-vhs}"
16
-
17
- if [ "$METHOD" = "vhs" ]; then
18
- if ! command -v vhs &>/dev/null; then
19
- echo "❌ vhs not found. Install: brew install charmbracelet/tap/vhs"
20
- echo " Or run: ./scripts/record-demo.sh asciinema"
21
- exit 1
22
- fi
23
-
24
- if [ -z "${VOYAGE_API_KEY:-}" ]; then
25
- echo "⚠️ VOYAGE_API_KEY not set. Commands that call the API will fail."
26
- echo " Set it: export VOYAGE_API_KEY=your-key"
27
- exit 1
28
- fi
29
-
30
- echo "🎬 Recording demo with vhs..."
31
- vhs demo.tape
32
- echo "✅ Demo GIF saved to demo.gif"
33
-
34
- elif [ "$METHOD" = "asciinema" ]; then
35
- if ! command -v asciinema &>/dev/null; then
36
- echo "❌ asciinema not found. Install: brew install asciinema"
37
- exit 1
38
- fi
39
-
40
- CAST_FILE="demo.cast"
41
- echo "🎬 Recording demo with asciinema..."
42
- echo " Run the following commands, then press Ctrl-D when done:"
43
- echo ""
44
- echo " vai --version"
45
- echo " vai models --type embedding"
46
- echo ' vai embed "What is MongoDB Atlas?"'
47
- echo " vai explain embeddings"
48
- echo ' vai similarity "MongoDB is great" "MongoDB Atlas is amazing"'
49
- echo ""
50
-
51
- asciinema rec "$CAST_FILE"
52
- echo "✅ Recording saved to $CAST_FILE"
53
- echo ""
54
- echo "Convert to GIF with agg or svg-term-cli:"
55
- echo " agg $CAST_FILE demo.gif"
56
- echo " # or"
57
- echo " npx svg-term-cli --in $CAST_FILE --out demo.svg --window"
58
-
59
- else
60
- echo "Unknown method: $METHOD"
61
- echo "Usage: $0 [vhs|asciinema]"
62
- exit 1
63
- fi
@@ -1,23 +0,0 @@
1
- 'use strict';
2
-
3
- const { describe, it } = require('node:test');
4
- const assert = require('node:assert/strict');
5
- const { Command } = require('commander');
6
- const { registerAbout } = require('../../src/commands/about');
7
-
8
- describe('about command', () => {
9
- it('registers correctly on a program', () => {
10
- const program = new Command();
11
- registerAbout(program);
12
- const aboutCmd = program.commands.find(c => c.name() === 'about');
13
- assert.ok(aboutCmd, 'about command should be registered');
14
- });
15
-
16
- it('has --json option', () => {
17
- const program = new Command();
18
- registerAbout(program);
19
- const aboutCmd = program.commands.find(c => c.name() === 'about');
20
- const optionNames = aboutCmd.options.map(o => o.long);
21
- assert.ok(optionNames.includes('--json'), 'should have --json option');
22
- });
23
- });