fix: respect user-configured agent models over system defaults
When user explicitly configures an agent model in oh-my-opencode.json, that model should take priority over the active model in OpenCode's config (which may just be the system default, not a deliberate UI selection). This fixes the issue where user-configured models from plugin providers (e.g., google/antigravity-*) were being overridden by the fallback chain because config.model was being passed as uiSelectedModel regardless of whether the user had an explicit config. The fix: - Only pass uiSelectedModel when there's no explicit userModel config - If user has configured a model, let resolveModelPipeline use it directly Fixes #1573 Co-authored-by: Rishi Vhavle <rishivhavle21@gmail.com>
This commit is contained in:
parent
4c6b31e5b4
commit
1c0b41aa65
@ -79,6 +79,72 @@ describe("createBuiltinAgents with model overrides", () => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test("user config model takes priority over uiSelectedModel for sisyphus", async () => {
|
||||||
|
// #given
|
||||||
|
const fetchSpy = spyOn(shared, "fetchAvailableModels").mockResolvedValue(
|
||||||
|
new Set(["openai/gpt-5.2", "anthropic/claude-sonnet-4-5"])
|
||||||
|
)
|
||||||
|
const uiSelectedModel = "openai/gpt-5.2"
|
||||||
|
const overrides = {
|
||||||
|
sisyphus: { model: "google/antigravity-claude-opus-4-5-thinking" },
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// #when
|
||||||
|
const agents = await createBuiltinAgents(
|
||||||
|
[],
|
||||||
|
overrides,
|
||||||
|
undefined,
|
||||||
|
TEST_DEFAULT_MODEL,
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
[],
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
uiSelectedModel
|
||||||
|
)
|
||||||
|
|
||||||
|
// #then
|
||||||
|
expect(agents.sisyphus).toBeDefined()
|
||||||
|
expect(agents.sisyphus.model).toBe("google/antigravity-claude-opus-4-5-thinking")
|
||||||
|
} finally {
|
||||||
|
fetchSpy.mockRestore()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
test("user config model takes priority over uiSelectedModel for atlas", async () => {
|
||||||
|
// #given
|
||||||
|
const fetchSpy = spyOn(shared, "fetchAvailableModels").mockResolvedValue(
|
||||||
|
new Set(["openai/gpt-5.2", "anthropic/claude-sonnet-4-5"])
|
||||||
|
)
|
||||||
|
const uiSelectedModel = "openai/gpt-5.2"
|
||||||
|
const overrides = {
|
||||||
|
atlas: { model: "google/antigravity-claude-opus-4-5-thinking" },
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// #when
|
||||||
|
const agents = await createBuiltinAgents(
|
||||||
|
[],
|
||||||
|
overrides,
|
||||||
|
undefined,
|
||||||
|
TEST_DEFAULT_MODEL,
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
[],
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
uiSelectedModel
|
||||||
|
)
|
||||||
|
|
||||||
|
// #then
|
||||||
|
expect(agents.atlas).toBeDefined()
|
||||||
|
expect(agents.atlas.model).toBe("google/antigravity-claude-opus-4-5-thinking")
|
||||||
|
} finally {
|
||||||
|
fetchSpy.mockRestore()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
test("Sisyphus is created on first run when no availableModels or cache exist", async () => {
|
test("Sisyphus is created on first run when no availableModels or cache exist", async () => {
|
||||||
// #given
|
// #given
|
||||||
const systemDefaultModel = "anthropic/claude-opus-4-6"
|
const systemDefaultModel = "anthropic/claude-opus-4-6"
|
||||||
@ -422,6 +488,58 @@ describe("createBuiltinAgents with requiresAnyModel gating (sisyphus)", () => {
|
|||||||
cacheSpy.mockRestore()
|
cacheSpy.mockRestore()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test("sisyphus uses user-configured plugin model even when not in cache or fallback chain", async () => {
|
||||||
|
// #given - user configures a model from a plugin provider (like antigravity)
|
||||||
|
// that is NOT in the availableModels cache and NOT in the fallback chain
|
||||||
|
const fetchSpy = spyOn(shared, "fetchAvailableModels").mockResolvedValue(
|
||||||
|
new Set(["openai/gpt-5.2"])
|
||||||
|
)
|
||||||
|
const cacheSpy = spyOn(connectedProvidersCache, "readConnectedProvidersCache").mockReturnValue(
|
||||||
|
["openai"]
|
||||||
|
)
|
||||||
|
const overrides = {
|
||||||
|
sisyphus: { model: "google/antigravity-claude-opus-4-5-thinking" },
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// #when
|
||||||
|
const agents = await createBuiltinAgents([], overrides, undefined, TEST_DEFAULT_MODEL, undefined, undefined, [], {})
|
||||||
|
|
||||||
|
// #then
|
||||||
|
expect(agents.sisyphus).toBeDefined()
|
||||||
|
expect(agents.sisyphus.model).toBe("google/antigravity-claude-opus-4-5-thinking")
|
||||||
|
} finally {
|
||||||
|
fetchSpy.mockRestore()
|
||||||
|
cacheSpy.mockRestore()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
test("sisyphus uses user-configured plugin model when availableModels is empty but cache exists", async () => {
|
||||||
|
// #given - connected providers cache exists but models cache is empty
|
||||||
|
// This reproduces the exact scenario where provider-models.json has models: {}
|
||||||
|
const fetchSpy = spyOn(shared, "fetchAvailableModels").mockResolvedValue(
|
||||||
|
new Set()
|
||||||
|
)
|
||||||
|
const cacheSpy = spyOn(connectedProvidersCache, "readConnectedProvidersCache").mockReturnValue(
|
||||||
|
["google", "openai", "opencode"]
|
||||||
|
)
|
||||||
|
const overrides = {
|
||||||
|
sisyphus: { model: "google/antigravity-claude-opus-4-5-thinking" },
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// #when
|
||||||
|
const agents = await createBuiltinAgents([], overrides, undefined, TEST_DEFAULT_MODEL, undefined, undefined, [], {})
|
||||||
|
|
||||||
|
// #then
|
||||||
|
expect(agents.sisyphus).toBeDefined()
|
||||||
|
expect(agents.sisyphus.model).toBe("google/antigravity-claude-opus-4-5-thinking")
|
||||||
|
} finally {
|
||||||
|
fetchSpy.mockRestore()
|
||||||
|
cacheSpy.mockRestore()
|
||||||
|
}
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("buildAgent with category and skills", () => {
|
describe("buildAgent with category and skills", () => {
|
||||||
|
|||||||
@ -304,7 +304,7 @@ export async function createBuiltinAgents(
|
|||||||
const isPrimaryAgent = isFactory(source) && source.mode === "primary"
|
const isPrimaryAgent = isFactory(source) && source.mode === "primary"
|
||||||
|
|
||||||
const resolution = applyModelResolution({
|
const resolution = applyModelResolution({
|
||||||
uiSelectedModel: isPrimaryAgent ? uiSelectedModel : undefined,
|
uiSelectedModel: (isPrimaryAgent && !override?.model) ? uiSelectedModel : undefined,
|
||||||
userModel: override?.model,
|
userModel: override?.model,
|
||||||
requirement,
|
requirement,
|
||||||
availableModels,
|
availableModels,
|
||||||
@ -356,7 +356,7 @@ export async function createBuiltinAgents(
|
|||||||
|
|
||||||
if (!disabledAgents.includes("sisyphus") && meetsSisyphusAnyModelRequirement) {
|
if (!disabledAgents.includes("sisyphus") && meetsSisyphusAnyModelRequirement) {
|
||||||
let sisyphusResolution = applyModelResolution({
|
let sisyphusResolution = applyModelResolution({
|
||||||
uiSelectedModel,
|
uiSelectedModel: sisyphusOverride?.model ? undefined : uiSelectedModel,
|
||||||
userModel: sisyphusOverride?.model,
|
userModel: sisyphusOverride?.model,
|
||||||
requirement: sisyphusRequirement,
|
requirement: sisyphusRequirement,
|
||||||
availableModels,
|
availableModels,
|
||||||
@ -454,7 +454,7 @@ export async function createBuiltinAgents(
|
|||||||
const atlasRequirement = AGENT_MODEL_REQUIREMENTS["atlas"]
|
const atlasRequirement = AGENT_MODEL_REQUIREMENTS["atlas"]
|
||||||
|
|
||||||
const atlasResolution = applyModelResolution({
|
const atlasResolution = applyModelResolution({
|
||||||
uiSelectedModel,
|
uiSelectedModel: orchestratorOverride?.model ? undefined : uiSelectedModel,
|
||||||
userModel: orchestratorOverride?.model,
|
userModel: orchestratorOverride?.model,
|
||||||
requirement: atlasRequirement,
|
requirement: atlasRequirement,
|
||||||
availableModels,
|
availableModels,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user