Skip to content

Commit 923c220

Browse files
committed
feat: keep only known providers
1 parent c7bf41a commit 923c220

2 files changed

Lines changed: 59 additions & 24 deletions

File tree

src/models.ts

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,16 @@ const DEFAULT_COST = {
3131
cache_write: 0,
3232
};
3333

34+
type Protocol = "openai" | "anthropic";
35+
36+
// Providers we explicitly know how to route. Models from any provider not
37+
// listed here are skipped. Adding a provider is a one-line change here.
38+
const PROVIDERS = new Map<string, { protocol: Protocol }>([
39+
["openai", { protocol: "openai" }],
40+
["anthropic", { protocol: "anthropic" }],
41+
["deepseek", { protocol: "anthropic" }],
42+
]);
43+
3444
type ModelDevEntry = {
3545
id?: string;
3646
name?: string;
@@ -97,10 +107,9 @@ export type ConfigModel = {
97107

98108
function buildLookupMap(modelsDevData: ModelsDevData) {
99109
const byFullId = new Map<string, ModelDevEntry>();
100-
const allowedProviders = new Set(["openai", "anthropic", "deepseek"]);
101110

102111
for (const [provider, providerData] of Object.entries(modelsDevData)) {
103-
if (!allowedProviders.has(provider) || !providerData?.models) continue;
112+
if (!PROVIDERS.has(provider) || !providerData?.models) continue;
104113
for (const [modelId, entry] of Object.entries(providerData.models)) {
105114
byFullId.set(`${provider}/${modelId}`, entry);
106115
}
@@ -156,10 +165,6 @@ export async function fetchHubModels(): Promise<HubResponse> {
156165
return res.json() as Promise<HubResponse>;
157166
}
158167

159-
function usesAnthropicApi(provider: string): boolean {
160-
return provider === "anthropic" || provider === "deepseek";
161-
}
162-
163168
export function buildConfigModels(
164169
modelsDevData: ModelsDevData,
165170
hubData: HubResponse,
@@ -169,9 +174,10 @@ export function buildConfigModels(
169174
const warnings: string[] = [];
170175

171176
for (const [provider, providerData] of Object.entries(hubData.providers)) {
172-
if (!providerData?.models) continue;
177+
const known = PROVIDERS.get(provider);
178+
if (!known || !providerData?.models) continue;
173179

174-
const anthropic = usesAnthropicApi(provider);
180+
const anthropic = known.protocol === "anthropic";
175181

176182
for (const [modelId, hubModel] of Object.entries(providerData.models)) {
177183
const entry = resolveEntry(provider, modelId, byFullId);

test/models.test.ts

Lines changed: 45 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -290,36 +290,65 @@ describe("buildConfigModels", () => {
290290
expect(models["shared-id"].limit.context).toBe(100000);
291291
});
292292

293-
it("does not resolve from non-allowed providers in models.dev", () => {
294-
const modelsDevData = {
295-
"provider-a": {
296-
models: {
297-
"some-model": {
298-
id: "some-model",
299-
name: "Should Not Match",
300-
cost: { input: 99 },
293+
it("skips unknown hub providers silently", () => {
294+
const hubData = {
295+
providers: {
296+
google: {
297+
models: {
298+
"gemini-3-pro": { display_name: "Gemini 3 Pro" },
301299
},
302300
},
303301
},
304302
};
305303

304+
const { models, warnings } = buildConfigModels(MODELS_DEV_FIXTURE, hubData);
305+
306+
expect(models).toEqual({});
307+
expect(warnings).toEqual([]);
308+
});
309+
310+
it("skips providers named like inherited object properties", () => {
306311
const hubData = {
307312
providers: {
308-
"provider-a": {
313+
toString: {
314+
models: {
315+
"evil-model": { display_name: "Evil Model" },
316+
},
317+
},
318+
constructor: {
309319
models: {
310-
"some-model": { display_name: "Hub Model" },
320+
"ctor-model": { display_name: "Ctor Model" },
311321
},
312322
},
313323
},
314324
};
315325

316-
const { models, warnings } = buildConfigModels(modelsDevData, hubData);
326+
const { models, warnings } = buildConfigModels(MODELS_DEV_FIXTURE, hubData);
317327

318-
expect(warnings).toEqual([
319-
"provider-a/some-model not found in models.dev — using defaults",
320-
]);
321-
expect(models["some-model"].name).toBe("Hub Model");
322-
expect(models["some-model"].cost.input).toBe(0);
328+
expect(models).toEqual({});
329+
expect(warnings).toEqual([]);
330+
});
331+
332+
it("registers only known providers when hub mixes known and unknown", () => {
333+
const hubData = {
334+
providers: {
335+
openai: {
336+
models: {
337+
"gpt-5.4-nano": { display_name: "GPT-5.4 Nano" },
338+
},
339+
},
340+
google: {
341+
models: {
342+
"gemini-3-pro": { display_name: "Gemini 3 Pro" },
343+
},
344+
},
345+
},
346+
};
347+
348+
const { models, warnings } = buildConfigModels(MODELS_DEV_FIXTURE, hubData);
349+
350+
expect(Object.keys(models)).toEqual(["gpt-5.4-nano"]);
351+
expect(warnings).toEqual([]);
323352
});
324353

325354
it("uses hub display_name as fallback when models.dev entry has no name", () => {

0 commit comments

Comments
 (0)