@vellumai/cli 0.10.7-dev.202607072228.38de414 → 0.10.7-dev.202607072325.09ad030
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/package.json
CHANGED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { describe, expect, test } from "bun:test";
|
|
4
|
+
|
|
5
|
+
import { inferProviderFromModel } from "../lib/provider-secrets.js";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Drift guard for the CLI's model → provider inference heuristic.
|
|
9
|
+
*
|
|
10
|
+
* `inferProviderFromModel` mirrors the assistant resolver's
|
|
11
|
+
* `getCatalogProviderForModel` semantics without importing from
|
|
12
|
+
* `assistant/src/`: a model ID listed by multiple catalog providers resolves
|
|
13
|
+
* to the FIRST provider in catalog order (e.g. `anthropic/*` IDs shared by
|
|
14
|
+
* OpenRouter and the Vercel AI Gateway resolve to openrouter), while an ID
|
|
15
|
+
* unique to one provider resolves to that provider (e.g. `openai/gpt-5.5` and
|
|
16
|
+
* `xai/grok-4.3` to vercel-ai-gateway). This test recomputes that expectation
|
|
17
|
+
* from `meta/llm-provider-catalog.json` for every vendor-prefixed
|
|
18
|
+
* (slash-containing) model ID and fails if a catalog change breaks the
|
|
19
|
+
* heuristic.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
const REPO_ROOT = join(import.meta.dir, "..", "..", "..");
|
|
23
|
+
|
|
24
|
+
interface LlmCatalog {
|
|
25
|
+
providers: Array<{ id: string; models: Array<{ id: string }> }>;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function loadLlmCatalog(): LlmCatalog {
|
|
29
|
+
const path = join(REPO_ROOT, "meta", "llm-provider-catalog.json");
|
|
30
|
+
return JSON.parse(readFileSync(path, "utf-8"));
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
describe("CLI provider inference parity", () => {
|
|
34
|
+
test("inferProviderFromModel matches first-catalog-provider resolution for every vendor-prefixed model ID", () => {
|
|
35
|
+
const catalog = loadLlmCatalog();
|
|
36
|
+
const expected: Record<string, string> = {};
|
|
37
|
+
for (const provider of catalog.providers) {
|
|
38
|
+
for (const model of provider.models) {
|
|
39
|
+
if (!model.id.includes("/")) {
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
expected[model.id] ??= provider.id;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Sanity: the catalog still exercises both the unique-ID and shared-ID
|
|
47
|
+
// paths this guard exists for.
|
|
48
|
+
expect(Object.values(expected)).toContain("vercel-ai-gateway");
|
|
49
|
+
expect(Object.values(expected)).toContain("openrouter");
|
|
50
|
+
|
|
51
|
+
const actual: Record<string, string | undefined> = {};
|
|
52
|
+
for (const modelId of Object.keys(expected)) {
|
|
53
|
+
actual[modelId] = inferProviderFromModel(modelId);
|
|
54
|
+
}
|
|
55
|
+
expect(actual).toEqual(expected);
|
|
56
|
+
});
|
|
57
|
+
});
|
|
@@ -71,6 +71,10 @@ const PROVIDER_LABELS: Record<LlmProviderId, string> = {
|
|
|
71
71
|
gemini: "Gemini",
|
|
72
72
|
fireworks: "Fireworks",
|
|
73
73
|
openrouter: "OpenRouter",
|
|
74
|
+
"vercel-ai-gateway": "Vercel AI Gateway",
|
|
75
|
+
minimax: "MiniMax",
|
|
76
|
+
atlascloud: "Atlas Cloud",
|
|
77
|
+
together: "Together AI",
|
|
74
78
|
};
|
|
75
79
|
|
|
76
80
|
export function formatProviderName(provider: LlmProviderId): string {
|
|
@@ -151,13 +155,42 @@ function readConfigValue(
|
|
|
151
155
|
return value && value.length > 0 ? value : undefined;
|
|
152
156
|
}
|
|
153
157
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
158
|
+
/**
|
|
159
|
+
* Infer the provider a bare model ID implies, mirroring the assistant
|
|
160
|
+
* resolver's `getCatalogProviderForModel`: an ID listed by multiple catalog
|
|
161
|
+
* providers resolves to the FIRST one in catalog order. Vendor prefixes unique
|
|
162
|
+
* to one provider map to it; shared gateway IDs (e.g. `anthropic/*`) fall
|
|
163
|
+
* through to openrouter, the earlier catalog entry. Drift guard:
|
|
164
|
+
* `cli/src/__tests__/provider-inference-parity.test.ts`. Exported for tests.
|
|
165
|
+
*/
|
|
166
|
+
export function inferProviderFromModel(model: string): string | undefined {
|
|
167
|
+
if (model.startsWith("claude-")) {
|
|
168
|
+
return "anthropic";
|
|
169
|
+
}
|
|
170
|
+
if (model.startsWith("gpt-")) {
|
|
171
|
+
return "openai";
|
|
172
|
+
}
|
|
173
|
+
if (model.startsWith("gemini-")) {
|
|
174
|
+
return "gemini";
|
|
175
|
+
}
|
|
176
|
+
if (model.startsWith("accounts/fireworks/models/")) {
|
|
177
|
+
return "fireworks";
|
|
178
|
+
}
|
|
179
|
+
if (model.startsWith("openai/") || model.startsWith("xai/")) {
|
|
180
|
+
return "vercel-ai-gateway";
|
|
181
|
+
}
|
|
182
|
+
if (model.startsWith("MiniMaxAI/")) {
|
|
183
|
+
return "together";
|
|
184
|
+
}
|
|
185
|
+
if (model.startsWith("deepseek-ai/")) {
|
|
186
|
+
return "atlascloud";
|
|
187
|
+
}
|
|
188
|
+
if (model.includes("/")) {
|
|
189
|
+
return "openrouter";
|
|
190
|
+
}
|
|
191
|
+
if (model === "llama3.2" || model === "mistral") {
|
|
192
|
+
return "ollama";
|
|
193
|
+
}
|
|
161
194
|
return undefined;
|
|
162
195
|
}
|
|
163
196
|
|
|
@@ -26,6 +26,7 @@ export const LLM_PROVIDER_ENV_VAR_NAMES: Record<string, string> = {
|
|
|
26
26
|
gemini: "GEMINI_API_KEY",
|
|
27
27
|
fireworks: "FIREWORKS_API_KEY",
|
|
28
28
|
openrouter: "OPENROUTER_API_KEY",
|
|
29
|
+
"vercel-ai-gateway": "AI_GATEWAY_API_KEY",
|
|
29
30
|
minimax: "MINIMAX_API_KEY",
|
|
30
31
|
atlascloud: "ATLASCLOUD_API_KEY",
|
|
31
32
|
together: "TOGETHER_API_KEY",
|