voyageai-cli 1.4.0 → 1.6.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.
@@ -86,4 +86,47 @@ describe('models command', () => {
86
86
  assert.ok(parsed[0].name);
87
87
  assert.ok(parsed[0].type);
88
88
  });
89
+
90
+ it('hides legacy models by default', async () => {
91
+ const program = new Command();
92
+ program.exitOverride();
93
+ registerModels(program);
94
+
95
+ await program.parseAsync(['node', 'test', 'models', '--quiet']);
96
+
97
+ const combined = output.join('\n');
98
+ assert.ok(combined.includes('voyage-4-large'), 'Should include current models');
99
+ assert.ok(!combined.includes('voyage-3-large'), 'Should hide legacy voyage-3-large');
100
+ assert.ok(!combined.includes('rerank-2-lite'), 'Should hide legacy rerank-2-lite');
101
+ assert.ok(!combined.includes('voyage-code-2'), 'Should hide legacy voyage-code-2');
102
+ });
103
+
104
+ it('shows legacy models when --all is used', async () => {
105
+ const program = new Command();
106
+ program.exitOverride();
107
+ registerModels(program);
108
+
109
+ await program.parseAsync(['node', 'test', 'models', '--all', '--quiet']);
110
+
111
+ const combined = output.join('\n');
112
+ assert.ok(combined.includes('voyage-4-large'), 'Should include current models');
113
+ assert.ok(combined.includes('voyage-3-large'), 'Should include legacy voyage-3-large');
114
+ assert.ok(combined.includes('rerank-2'), 'Should include legacy rerank-2');
115
+ assert.ok(combined.includes('Legacy Models'), 'Should show legacy header');
116
+ });
117
+
118
+ it('--all with --json shows legacy models in JSON', async () => {
119
+ const program = new Command();
120
+ program.exitOverride();
121
+ registerModels(program);
122
+
123
+ await program.parseAsync(['node', 'test', 'models', '--all', '--json']);
124
+
125
+ const combined = output.join('\n');
126
+ const parsed = JSON.parse(combined);
127
+ assert.ok(Array.isArray(parsed));
128
+ const legacyNames = parsed.filter(m => m.legacy).map(m => m.name);
129
+ assert.ok(legacyNames.includes('voyage-3-large'), 'JSON should include legacy models');
130
+ assert.ok(legacyNames.includes('rerank-2'), 'JSON should include legacy rerankers');
131
+ });
89
132
  });
@@ -105,8 +105,17 @@ describe('ping command', () => {
105
105
  assert.ok(combined.includes('Authentication failed'), 'Should show auth error');
106
106
  });
107
107
 
108
- it('exits when VOYAGE_API_KEY is not set', async () => {
108
+ it('exits when VOYAGE_API_KEY is not set and no config', async () => {
109
109
  delete process.env.VOYAGE_API_KEY;
110
+ // Mock config to return nothing so the key isn't found in ~/.vai/config.json
111
+ delete require.cache[require.resolve('../../src/lib/config')];
112
+ delete require.cache[require.resolve('../../src/lib/api')];
113
+ delete require.cache[require.resolve('../../src/commands/ping')];
114
+ const config = require('../../src/lib/config');
115
+ const origGetConfigValue = config.getConfigValue;
116
+ config.getConfigValue = () => undefined;
117
+
118
+ const { registerPing: registerPingFresh } = require('../../src/commands/ping');
110
119
 
111
120
  let exitCode = null;
112
121
  process.exit = (code) => {
@@ -116,16 +125,20 @@ describe('ping command', () => {
116
125
 
117
126
  const program = new Command();
118
127
  program.exitOverride();
119
- registerPing(program);
120
-
121
- await assert.rejects(
122
- () => program.parseAsync(['node', 'test', 'ping']),
123
- /process\.exit called/
124
- );
125
-
126
- assert.equal(exitCode, 1);
127
- const combined = errorOutput.join('\n');
128
- assert.ok(combined.includes('VOYAGE_API_KEY'), 'Should mention missing key');
128
+ registerPingFresh(program);
129
+
130
+ try {
131
+ await assert.rejects(
132
+ () => program.parseAsync(['node', 'test', 'ping']),
133
+ /process\.exit called/
134
+ );
135
+
136
+ assert.equal(exitCode, 1);
137
+ const combined = errorOutput.join('\n');
138
+ assert.ok(combined.includes('VOYAGE_API_KEY'), 'Should mention missing key');
139
+ } finally {
140
+ config.getConfigValue = origGetConfigValue;
141
+ }
129
142
  });
130
143
 
131
144
  it('outputs JSON when --json flag is used', async () => {
@@ -0,0 +1,32 @@
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 { registerRerank } = require('../../src/commands/rerank');
7
+
8
+ describe('rerank command', () => {
9
+ it('registers correctly on a program', () => {
10
+ const program = new Command();
11
+ registerRerank(program);
12
+ const rerankCmd = program.commands.find(c => c.name() === 'rerank');
13
+ assert.ok(rerankCmd, 'rerank command should be registered');
14
+ });
15
+
16
+ it('has --truncation flag', () => {
17
+ const program = new Command();
18
+ registerRerank(program);
19
+ const rerankCmd = program.commands.find(c => c.name() === 'rerank');
20
+ const optionNames = rerankCmd.options.map(o => o.long);
21
+ assert.ok(optionNames.includes('--truncation'), 'should have --truncation option');
22
+ assert.ok(optionNames.includes('--no-truncation'), 'should have --no-truncation option');
23
+ });
24
+
25
+ it('has --return-documents flag', () => {
26
+ const program = new Command();
27
+ registerRerank(program);
28
+ const rerankCmd = program.commands.find(c => c.name() === 'rerank');
29
+ const optionNames = rerankCmd.options.map(o => o.long);
30
+ assert.ok(optionNames.includes('--return-documents'), 'should have --return-documents option');
31
+ });
32
+ });
@@ -0,0 +1,26 @@
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 { registerStore } = require('../../src/commands/store');
7
+
8
+ describe('store command', () => {
9
+ it('registers correctly on a program', () => {
10
+ const program = new Command();
11
+ registerStore(program);
12
+ const storeCmd = program.commands.find(c => c.name() === 'store');
13
+ assert.ok(storeCmd, 'store command should be registered');
14
+ });
15
+
16
+ it('has --input-type flag defaulting to document', () => {
17
+ const program = new Command();
18
+ registerStore(program);
19
+ const storeCmd = program.commands.find(c => c.name() === 'store');
20
+ const optionNames = storeCmd.options.map(o => o.long);
21
+ assert.ok(optionNames.includes('--input-type'), 'should have --input-type option');
22
+ // Check the default value
23
+ const inputTypeOpt = storeCmd.options.find(o => o.long === '--input-type');
24
+ assert.equal(inputTypeOpt.defaultValue, 'document', '--input-type should default to document');
25
+ });
26
+ });
@@ -25,8 +25,13 @@ describe('api', () => {
25
25
  describe('requireApiKey', () => {
26
26
  it('throws/exits when VOYAGE_API_KEY is not set', () => {
27
27
  delete process.env.VOYAGE_API_KEY;
28
- // Re-require to get fresh module
28
+ // Re-require to get fresh modules (clear config cache too)
29
29
  delete require.cache[require.resolve('../../src/lib/api')];
30
+ delete require.cache[require.resolve('../../src/lib/config')];
31
+ const config = require('../../src/lib/config');
32
+ const originalGetConfigValue = config.getConfigValue;
33
+ config.getConfigValue = () => undefined;
34
+
30
35
  const { requireApiKey } = require('../../src/lib/api');
31
36
 
32
37
  let exitCode = null;
@@ -35,8 +40,12 @@ describe('api', () => {
35
40
  throw new Error('process.exit called');
36
41
  };
37
42
 
38
- assert.throws(() => requireApiKey(), /process\.exit called/);
39
- assert.equal(exitCode, 1);
43
+ try {
44
+ assert.throws(() => requireApiKey(), /process\.exit called/);
45
+ assert.equal(exitCode, 1);
46
+ } finally {
47
+ config.getConfigValue = originalGetConfigValue;
48
+ }
40
49
  });
41
50
 
42
51
  it('returns key when VOYAGE_API_KEY is set', () => {
@@ -64,4 +64,36 @@ describe('catalog', () => {
64
64
  const rerankCount = MODEL_CATALOG.filter(m => m.type === 'reranking').length;
65
65
  assert.ok(embedCount > rerankCount);
66
66
  });
67
+
68
+ it('contains voyage-4-nano as current model', () => {
69
+ const nano = MODEL_CATALOG.find(m => m.name === 'voyage-4-nano');
70
+ assert.ok(nano, 'Should have voyage-4-nano');
71
+ assert.equal(nano.type, 'embedding');
72
+ assert.ok(!nano.legacy, 'voyage-4-nano should not be legacy');
73
+ });
74
+
75
+ it('contains legacy models with legacy flag', () => {
76
+ const legacyModels = MODEL_CATALOG.filter(m => m.legacy);
77
+ assert.ok(legacyModels.length > 0, 'Should have legacy models');
78
+
79
+ const legacyNames = legacyModels.map(m => m.name);
80
+ assert.ok(legacyNames.includes('voyage-3-large'), 'Should have voyage-3-large');
81
+ assert.ok(legacyNames.includes('voyage-3.5'), 'Should have voyage-3.5');
82
+ assert.ok(legacyNames.includes('voyage-3.5-lite'), 'Should have voyage-3.5-lite');
83
+ assert.ok(legacyNames.includes('voyage-code-2'), 'Should have voyage-code-2');
84
+ assert.ok(legacyNames.includes('voyage-multimodal-3'), 'Should have voyage-multimodal-3');
85
+ assert.ok(legacyNames.includes('rerank-2'), 'Should have rerank-2');
86
+ assert.ok(legacyNames.includes('rerank-2-lite'), 'Should have rerank-2-lite');
87
+ });
88
+
89
+ it('legacy models have required fields', () => {
90
+ const legacyModels = MODEL_CATALOG.filter(m => m.legacy);
91
+ for (const model of legacyModels) {
92
+ assert.ok(model.name, `Legacy model missing name`);
93
+ assert.ok(model.type, `Legacy model ${model.name} missing type`);
94
+ assert.ok(model.context, `Legacy model ${model.name} missing context`);
95
+ assert.ok(model.price, `Legacy model ${model.name} missing price`);
96
+ assert.ok(model.bestFor, `Legacy model ${model.name} missing bestFor`);
97
+ }
98
+ });
67
99
  });