refactor(provider): flow branded ProviderID/ModelID through internal signatures (#17182)

This commit is contained in:
Kit Langton
2026-03-12 10:48:17 -04:00
committed by GitHub
parent a4f8d66a9b
commit 1cb7df7159
24 changed files with 227 additions and 205 deletions

View File

@@ -4,6 +4,7 @@ import path from "path"
import { tmpdir } from "../fixture/fixture"
import { Instance } from "../../src/project/instance"
import { Provider } from "../../src/provider/provider"
import { ProviderID, ModelID } from "../../src/provider/schema"
import { Env } from "../../src/env"
test("provider loaded from env variable", async () => {
@@ -300,7 +301,7 @@ test("getModel returns model for valid provider/model", async () => {
Env.set("ANTHROPIC_API_KEY", "test-api-key")
},
fn: async () => {
const model = await Provider.getModel("anthropic", "claude-sonnet-4-20250514")
const model = await Provider.getModel(ProviderID.anthropic, ModelID.make("claude-sonnet-4-20250514"))
expect(model).toBeDefined()
expect(String(model.providerID)).toBe("anthropic")
expect(String(model.id)).toBe("claude-sonnet-4-20250514")
@@ -327,7 +328,7 @@ test("getModel throws ModelNotFoundError for invalid model", async () => {
Env.set("ANTHROPIC_API_KEY", "test-api-key")
},
fn: async () => {
expect(Provider.getModel("anthropic", "nonexistent-model")).rejects.toThrow()
expect(Provider.getModel(ProviderID.anthropic, ModelID.make("nonexistent-model"))).rejects.toThrow()
},
})
})
@@ -346,7 +347,7 @@ test("getModel throws ModelNotFoundError for invalid provider", async () => {
await Instance.provide({
directory: tmp.path,
fn: async () => {
expect(Provider.getModel("nonexistent-provider", "some-model")).rejects.toThrow()
expect(Provider.getModel(ProviderID.make("nonexistent-provider"), ModelID.make("some-model"))).rejects.toThrow()
},
})
})
@@ -572,10 +573,10 @@ test("closest finds model by partial match", async () => {
Env.set("ANTHROPIC_API_KEY", "test-api-key")
},
fn: async () => {
const result = await Provider.closest("anthropic", ["sonnet-4"])
const result = await Provider.closest(ProviderID.anthropic, ["sonnet-4"])
expect(result).toBeDefined()
expect(result?.providerID).toBe("anthropic")
expect(result?.modelID).toContain("sonnet-4")
expect(String(result?.providerID)).toBe("anthropic")
expect(String(result?.modelID)).toContain("sonnet-4")
},
})
})
@@ -594,7 +595,7 @@ test("closest returns undefined for nonexistent provider", async () => {
await Instance.provide({
directory: tmp.path,
fn: async () => {
const result = await Provider.closest("nonexistent", ["model"])
const result = await Provider.closest(ProviderID.make("nonexistent"), ["model"])
expect(result).toBeUndefined()
},
})
@@ -630,7 +631,7 @@ test("getModel uses realIdByKey for aliased models", async () => {
const providers = await Provider.list()
expect(providers["anthropic"].models["my-sonnet"]).toBeDefined()
const model = await Provider.getModel("anthropic", "my-sonnet")
const model = await Provider.getModel(ProviderID.anthropic, ModelID.make("my-sonnet"))
expect(model).toBeDefined()
expect(String(model.id)).toBe("my-sonnet")
expect(model.name).toBe("My Sonnet Alias")
@@ -933,7 +934,7 @@ test("getSmallModel returns appropriate small model", async () => {
Env.set("ANTHROPIC_API_KEY", "test-api-key")
},
fn: async () => {
const model = await Provider.getSmallModel("anthropic")
const model = await Provider.getSmallModel(ProviderID.anthropic)
expect(model).toBeDefined()
expect(model?.id).toContain("haiku")
},
@@ -958,7 +959,7 @@ test("getSmallModel respects config small_model override", async () => {
Env.set("ANTHROPIC_API_KEY", "test-api-key")
},
fn: async () => {
const model = await Provider.getSmallModel("anthropic")
const model = await Provider.getSmallModel(ProviderID.anthropic)
expect(model).toBeDefined()
expect(String(model?.providerID)).toBe("anthropic")
expect(String(model?.id)).toBe("claude-sonnet-4-20250514")
@@ -1466,8 +1467,8 @@ test("getModel returns consistent results", async () => {
Env.set("ANTHROPIC_API_KEY", "test-api-key")
},
fn: async () => {
const model1 = await Provider.getModel("anthropic", "claude-sonnet-4-20250514")
const model2 = await Provider.getModel("anthropic", "claude-sonnet-4-20250514")
const model1 = await Provider.getModel(ProviderID.anthropic, ModelID.make("claude-sonnet-4-20250514"))
const model2 = await Provider.getModel(ProviderID.anthropic, ModelID.make("claude-sonnet-4-20250514"))
expect(model1.providerID).toEqual(model2.providerID)
expect(model1.id).toEqual(model2.id)
expect(model1).toEqual(model2)
@@ -1528,7 +1529,7 @@ test("ModelNotFoundError includes suggestions for typos", async () => {
},
fn: async () => {
try {
await Provider.getModel("anthropic", "claude-sonet-4") // typo: sonet instead of sonnet
await Provider.getModel(ProviderID.anthropic, ModelID.make("claude-sonet-4")) // typo: sonet instead of sonnet
expect(true).toBe(false) // Should not reach here
} catch (e: any) {
expect(e.data.suggestions).toBeDefined()
@@ -1556,7 +1557,7 @@ test("ModelNotFoundError for provider includes suggestions", async () => {
},
fn: async () => {
try {
await Provider.getModel("antropic", "claude-sonnet-4") // typo: antropic
await Provider.getModel(ProviderID.make("antropic"), ModelID.make("claude-sonnet-4")) // typo: antropic
expect(true).toBe(false) // Should not reach here
} catch (e: any) {
expect(e.data.suggestions).toBeDefined()
@@ -1580,7 +1581,7 @@ test("getProvider returns undefined for nonexistent provider", async () => {
await Instance.provide({
directory: tmp.path,
fn: async () => {
const provider = await Provider.getProvider("nonexistent")
const provider = await Provider.getProvider(ProviderID.make("nonexistent"))
expect(provider).toBeUndefined()
},
})
@@ -1603,7 +1604,7 @@ test("getProvider returns provider info", async () => {
Env.set("ANTHROPIC_API_KEY", "test-api-key")
},
fn: async () => {
const provider = await Provider.getProvider("anthropic")
const provider = await Provider.getProvider(ProviderID.anthropic)
expect(provider).toBeDefined()
expect(String(provider?.id)).toBe("anthropic")
},
@@ -1627,7 +1628,7 @@ test("closest returns undefined when no partial match found", async () => {
Env.set("ANTHROPIC_API_KEY", "test-api-key")
},
fn: async () => {
const result = await Provider.closest("anthropic", ["nonexistent-xyz-model"])
const result = await Provider.closest(ProviderID.anthropic, ["nonexistent-xyz-model"])
expect(result).toBeUndefined()
},
})
@@ -1651,7 +1652,7 @@ test("closest checks multiple query terms in order", async () => {
},
fn: async () => {
// First term won't match, second will
const result = await Provider.closest("anthropic", ["nonexistent", "haiku"])
const result = await Provider.closest(ProviderID.anthropic, ["nonexistent", "haiku"])
expect(result).toBeDefined()
expect(result?.modelID).toContain("haiku")
},