fix: let server decide model when no user selection exists (#37)

Registry defaults unreliable for deployed models; return null so server uses its configured model. Fixes #35.
This commit is contained in:
Den
2026-06-22 01:15:13 -07:00
committed by GitHub
parent 669618b7b7
commit f92f995b48
2 changed files with 28 additions and 24 deletions

View File

@@ -5,7 +5,7 @@ import { chooseModelSelection } from "./model-selection.ts"
const providers = [ const providers = [
{ {
id: "azure", id: "azure",
models: [{ id: "gpt-5.4" }, { id: "gpt-5.2" }], models: [{ id: "gpt-5.4" }, { id: "gpt-5.2" }, { id: "claude-sonnet-4-6" }],
}, },
] ]
@@ -14,29 +14,31 @@ test("keeps existing selection when still available", () => {
providers, providers,
defaults: { azure: "gpt-5.4" }, defaults: { azure: "gpt-5.4" },
existing: { providerID: "azure", modelID: "gpt-5.2" }, existing: { providerID: "azure", modelID: "gpt-5.2" },
agentModel: { providerID: "azure", modelID: "claude-sonnet-4-6" }, agentModel: null,
}) })
assert.deepEqual(selected, { providerID: "azure", modelID: "gpt-5.2" }) assert.deepEqual(selected, { providerID: "azure", modelID: "gpt-5.2" })
}) })
test("prefers provider default over unavailable agent model", () => { test("returns null when no existing selection (let server decide)", () => {
// Even though defaults and providers exist, we return null to let
// the server use its own configured model from opencode.json
const selected = chooseModelSelection({ const selected = chooseModelSelection({
providers, providers,
defaults: { azure: "gpt-5.4" }, defaults: { azure: "claude-sonnet-4-6" },
existing: null, existing: null,
agentModel: { providerID: "azure", modelID: "claude-sonnet-4-6" }, agentModel: { providerID: "azure", modelID: "claude-sonnet-4-6" },
}) })
assert.deepEqual(selected, { providerID: "azure", modelID: "gpt-5.4" }) assert.equal(selected, null)
}) })
test("falls back to first connected provider model when default missing", () => { test("returns null when existing selection no longer available", () => {
const selected = chooseModelSelection({ const selected = chooseModelSelection({
providers, providers,
defaults: {}, defaults: { azure: "gpt-5.4" },
existing: null, existing: { providerID: "azure", modelID: "nonexistent-model" },
agentModel: null, agentModel: null,
}) })
assert.deepEqual(selected, { providerID: "azure", modelID: "gpt-5.4" }) assert.equal(selected, null)
}) })
test("returns null when no connected providers", () => { test("returns null when no connected providers", () => {

View File

@@ -22,29 +22,31 @@ export function isModelAvailable(
return provider.models.some((m) => m.id === selection.modelID) return provider.models.some((m) => m.id === selection.modelID)
} }
/**
* Chooses a model selection for the session.
*
* Returns `null` when there is no prior user-explicit choice. A null model
* means the server's configured default (opencode.json `"model"` field) will
* be used — which is the correct behavior for first-launch and CI where the
* provider registry's "default" model may not be deployed on the user's
* resource.
*
* Only returns a non-null value when the user previously made an explicit
* selection that is still valid (model available on a connected provider).
*/
export function chooseModelSelection(params: { export function chooseModelSelection(params: {
providers: ProviderRef[] providers: ProviderRef[]
defaults: Record<string, string> defaults: Record<string, string>
existing: ModelSelection | null existing: ModelSelection | null
agentModel: ModelSelection | null agentModel: ModelSelection | null
}): ModelSelection | null { }): ModelSelection | null {
const { providers, defaults, existing, agentModel } = params const { providers, existing } = params
// Keep existing user-chosen selection if it's still reachable
if (isModelAvailable(providers, existing)) return existing if (isModelAvailable(providers, existing)) return existing
for (const provider of providers) { // Otherwise return null — let the server decide via its own config.
const defaultModelID = defaults[provider.id] // The provider registry's "defaults" map is unreliable (it lists the
if (!defaultModelID) continue // upstream registry default, not what's deployed on the user's resource).
if (provider.models.some((m) => m.id === defaultModelID)) {
return { providerID: provider.id, modelID: defaultModelID }
}
}
if (providers.length > 0 && providers[0].models.length > 0) {
return { providerID: providers[0].id, modelID: providers[0].models[0].id }
}
if (isModelAvailable(providers, agentModel)) return agentModel
return null return null
} }