fix(core): refine small model defaults (#33926)
This commit is contained in:
@@ -235,6 +235,11 @@ export const layer = Layer.effect(
|
|||||||
if (!record) return
|
if (!record) return
|
||||||
const provider = record.provider
|
const provider = record.provider
|
||||||
|
|
||||||
|
// TODO: Remove these provider-specific assumptions once model syncing reliably reports available deployments.
|
||||||
|
if (providerID === ProviderV2.ID.azure || providerID === ProviderV2.ID.make("azure-cognitive-services")) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if (providerID === ProviderV2.ID.opencode) {
|
if (providerID === ProviderV2.ID.opencode) {
|
||||||
const gpt5Nano = record.models.get(ModelV2.ID.make("gpt-5-nano"))
|
const gpt5Nano = record.models.get(ModelV2.ID.make("gpt-5-nano"))
|
||||||
if (gpt5Nano?.enabled && gpt5Nano.status === "active") return projectModel(gpt5Nano, provider)
|
if (gpt5Nano?.enabled && gpt5Nano.status === "active") return projectModel(gpt5Nano, provider)
|
||||||
|
|||||||
@@ -1868,44 +1868,43 @@ export const layer = Layer.effect(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const defaultPriority = [
|
// TODO: Remove these provider-specific assumptions once model syncing reliably reports available deployments.
|
||||||
"claude-haiku-4-5",
|
if (providerID === ProviderV2.ID.azure || providerID === ProviderV2.ID.make("azure-cognitive-services")) {
|
||||||
"claude-haiku-4.5",
|
return undefined
|
||||||
"3-5-haiku",
|
}
|
||||||
"3.5-haiku",
|
|
||||||
"gemini-3-flash",
|
|
||||||
"gemini-2.5-flash",
|
|
||||||
"gpt-5-nano",
|
|
||||||
]
|
|
||||||
const priority = providerID.startsWith("opencode")
|
const priority = providerID.startsWith("opencode")
|
||||||
? ["gpt-5-nano"]
|
? ["gpt-nano"]
|
||||||
: providerID.startsWith("github-copilot")
|
: providerID.startsWith("github-copilot")
|
||||||
? ["gpt-5-mini", "claude-haiku-4.5", ...defaultPriority]
|
? ["gpt-mini", ...smallModelFamilyPriority]
|
||||||
: defaultPriority
|
: smallModelFamilyPriority
|
||||||
for (const item of priority) {
|
const models = sortBy(
|
||||||
|
Object.values(provider.models),
|
||||||
|
[(model) => model.release_date, "desc"],
|
||||||
|
[(model) => model.id, "desc"],
|
||||||
|
)
|
||||||
|
for (const family of priority) {
|
||||||
|
const candidates = models.filter((model) => model.family === family)
|
||||||
if (providerID === ProviderV2.ID.amazonBedrock) {
|
if (providerID === ProviderV2.ID.amazonBedrock) {
|
||||||
const crossRegionPrefixes = ["global.", "us.", "eu."]
|
const crossRegionPrefixes = ["global.", "us.", "eu."]
|
||||||
const candidates = Object.keys(provider.models).filter((m) => m.includes(item))
|
|
||||||
|
|
||||||
const globalMatch = candidates.find((m) => m.startsWith("global."))
|
const globalMatch = candidates.find((model) => model.id.startsWith("global."))
|
||||||
if (globalMatch) return provider.models[globalMatch]
|
if (globalMatch) return globalMatch
|
||||||
|
|
||||||
const region = provider.options?.region
|
const region = provider.options?.region
|
||||||
if (region) {
|
if (region) {
|
||||||
const regionPrefix = region.split("-")[0]
|
const regionPrefix = region.split("-")[0]
|
||||||
if (regionPrefix === "us" || regionPrefix === "eu") {
|
if (regionPrefix === "us" || regionPrefix === "eu") {
|
||||||
const regionalMatch = candidates.find((m) => m.startsWith(`${regionPrefix}.`))
|
const regionalMatch = candidates.find((model) => model.id.startsWith(`${regionPrefix}.`))
|
||||||
if (regionalMatch) return provider.models[regionalMatch]
|
if (regionalMatch) return regionalMatch
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const unprefixed = candidates.find((m) => !crossRegionPrefixes.some((p) => m.startsWith(p)))
|
const unprefixed = candidates.find((model) => !crossRegionPrefixes.some((p) => model.id.startsWith(p)))
|
||||||
if (unprefixed) return provider.models[unprefixed]
|
if (unprefixed) return unprefixed
|
||||||
} else {
|
continue
|
||||||
for (const model of Object.keys(provider.models)) {
|
|
||||||
if (model.includes(item)) return provider.models[model]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
if (candidates[0]) return candidates[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
return undefined
|
return undefined
|
||||||
@@ -1962,6 +1961,7 @@ export const defaultLayer = Layer.suspend(() =>
|
|||||||
)
|
)
|
||||||
|
|
||||||
const priority = ["gpt-5", "claude-sonnet-4", "big-pickle", "gemini-3-pro"]
|
const priority = ["gpt-5", "claude-sonnet-4", "big-pickle", "gemini-3-pro"]
|
||||||
|
const smallModelFamilyPriority = ["gemini-flash", "gpt-nano", "claude-haiku"]
|
||||||
export function sort<T extends { id: string }>(models: T[]) {
|
export function sort<T extends { id: string }>(models: T[]) {
|
||||||
return sortBy(
|
return sortBy(
|
||||||
models,
|
models,
|
||||||
|
|||||||
@@ -652,6 +652,102 @@ it.instance("getSmallModel returns appropriate small model", () =>
|
|||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
it.instance("getSmallModel prefers Gemini for Google Vertex", () =>
|
||||||
|
Effect.gen(function* () {
|
||||||
|
yield* set("GOOGLE_VERTEX_PROJECT", "test-project")
|
||||||
|
const model = yield* Provider.use.getSmallModel(ProviderV2.ID.googleVertex)
|
||||||
|
expect(model).toBeDefined()
|
||||||
|
expect(model?.id).toContain("gemini")
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
|
it.instance(
|
||||||
|
"getSmallModel selects the latest model in the preferred family",
|
||||||
|
Effect.gen(function* () {
|
||||||
|
const model = yield* Provider.use.getSmallModel(ProviderV2.ID.make("test-provider"))
|
||||||
|
expect(model?.id).toBe(ModelV2.ID.make("new-flash"))
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
config: {
|
||||||
|
provider: {
|
||||||
|
"test-provider": {
|
||||||
|
name: "Test Provider",
|
||||||
|
npm: "@ai-sdk/openai-compatible",
|
||||||
|
models: {
|
||||||
|
"old-flash": { family: "gemini-flash", release_date: "2025-01-01" },
|
||||||
|
"new-flash": { family: "gemini-flash", release_date: "2026-01-01" },
|
||||||
|
"newer-haiku": { family: "claude-haiku", release_date: "2026-06-01" },
|
||||||
|
},
|
||||||
|
options: { apiKey: "test-key" },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
it.instance(
|
||||||
|
"getSmallModel matches exact model families",
|
||||||
|
Effect.gen(function* () {
|
||||||
|
const model = yield* Provider.use.getSmallModel(ProviderV2.ID.make("test-provider"))
|
||||||
|
expect(model?.id).toBe(ModelV2.ID.make("claude-haiku"))
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
config: {
|
||||||
|
provider: {
|
||||||
|
"test-provider": {
|
||||||
|
name: "Test Provider",
|
||||||
|
npm: "@ai-sdk/openai-compatible",
|
||||||
|
models: {
|
||||||
|
"glm-flash": { family: "glm-flash", release_date: "2026-06-01" },
|
||||||
|
"claude-haiku": { family: "claude-haiku", release_date: "2026-01-01" },
|
||||||
|
},
|
||||||
|
options: { apiKey: "test-key" },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
it.instance(
|
||||||
|
"getSmallModel ignores model IDs without family metadata",
|
||||||
|
Effect.gen(function* () {
|
||||||
|
const model = yield* Provider.use.getSmallModel(ProviderV2.ID.make("test-provider"))
|
||||||
|
expect(model).toBeUndefined()
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
config: {
|
||||||
|
provider: {
|
||||||
|
"test-provider": {
|
||||||
|
name: "Test Provider",
|
||||||
|
npm: "@ai-sdk/openai-compatible",
|
||||||
|
models: {
|
||||||
|
"gpt-5-nano": { release_date: "2026-01-01" },
|
||||||
|
},
|
||||||
|
options: { apiKey: "test-key" },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
it.instance("getSmallModel skips inferred models for Azure", () =>
|
||||||
|
Effect.gen(function* () {
|
||||||
|
yield* set("AZURE_RESOURCE_NAME", "test-resource")
|
||||||
|
yield* set("AZURE_API_KEY", "test-key")
|
||||||
|
const model = yield* Provider.use.getSmallModel(ProviderV2.ID.azure)
|
||||||
|
expect(model).toBeUndefined()
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
|
it.instance("getSmallModel skips inferred models for Azure Cognitive Services", () =>
|
||||||
|
Effect.gen(function* () {
|
||||||
|
yield* set("AZURE_COGNITIVE_SERVICES_RESOURCE_NAME", "test-resource")
|
||||||
|
yield* set("AZURE_COGNITIVE_SERVICES_API_KEY", "test-key")
|
||||||
|
const model = yield* Provider.use.getSmallModel(ProviderV2.ID.make("azure-cognitive-services"))
|
||||||
|
expect(model).toBeUndefined()
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
it.instance(
|
it.instance(
|
||||||
"getSmallModel respects config small_model override",
|
"getSmallModel respects config small_model override",
|
||||||
Effect.gen(function* () {
|
Effect.gen(function* () {
|
||||||
|
|||||||
Reference in New Issue
Block a user