Merge pull request #1475 from code-yeongyu/fix/model-availability-connected-providers
Merging PR #1475 into dev as requested. Cubic review 5/5 accepted.
This commit is contained in:
commit
708d15ebcc
@ -241,11 +241,12 @@ describe("createBuiltinAgents without systemDefaultModel", () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
describe("createBuiltinAgents with requiresModel gating", () => {
|
describe("createBuiltinAgents with requiresModel gating", () => {
|
||||||
test("hephaestus is not created when gpt-5.2-codex is unavailable", async () => {
|
test("hephaestus is not created when gpt-5.2-codex is unavailable and provider not connected", async () => {
|
||||||
// #given
|
// #given
|
||||||
const fetchSpy = spyOn(shared, "fetchAvailableModels").mockResolvedValue(
|
const fetchSpy = spyOn(shared, "fetchAvailableModels").mockResolvedValue(
|
||||||
new Set(["anthropic/claude-opus-4-5"])
|
new Set(["anthropic/claude-opus-4-5"])
|
||||||
)
|
)
|
||||||
|
const cacheSpy = spyOn(connectedProvidersCache, "readConnectedProvidersCache").mockReturnValue([])
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// #when
|
// #when
|
||||||
@ -255,6 +256,7 @@ describe("createBuiltinAgents with requiresModel gating", () => {
|
|||||||
expect(agents.hephaestus).toBeUndefined()
|
expect(agents.hephaestus).toBeUndefined()
|
||||||
} finally {
|
} finally {
|
||||||
fetchSpy.mockRestore()
|
fetchSpy.mockRestore()
|
||||||
|
cacheSpy.mockRestore()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -368,11 +370,12 @@ describe("createBuiltinAgents with requiresAnyModel gating (sisyphus)", () => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
test("sisyphus is not created when no fallback model is available (unrelated model only)", async () => {
|
test("sisyphus is not created when no fallback model is available and provider not connected", async () => {
|
||||||
// #given - only openai/gpt-5.2 available, not in sisyphus fallback chain
|
// #given - only openai/gpt-5.2 available, not in sisyphus fallback chain
|
||||||
const fetchSpy = spyOn(shared, "fetchAvailableModels").mockResolvedValue(
|
const fetchSpy = spyOn(shared, "fetchAvailableModels").mockResolvedValue(
|
||||||
new Set(["openai/gpt-5.2"])
|
new Set(["openai/gpt-5.2"])
|
||||||
)
|
)
|
||||||
|
const cacheSpy = spyOn(connectedProvidersCache, "readConnectedProvidersCache").mockReturnValue([])
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// #when
|
// #when
|
||||||
@ -382,6 +385,7 @@ describe("createBuiltinAgents with requiresAnyModel gating (sisyphus)", () => {
|
|||||||
expect(agents.sisyphus).toBeUndefined()
|
expect(agents.sisyphus).toBeUndefined()
|
||||||
} finally {
|
} finally {
|
||||||
fetchSpy.mockRestore()
|
fetchSpy.mockRestore()
|
||||||
|
cacheSpy.mockRestore()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@ -398,7 +398,7 @@ export async function createBuiltinAgents(
|
|||||||
!hephaestusRequirement?.requiresModel ||
|
!hephaestusRequirement?.requiresModel ||
|
||||||
hasHephaestusExplicitConfig ||
|
hasHephaestusExplicitConfig ||
|
||||||
isFirstRunNoCache ||
|
isFirstRunNoCache ||
|
||||||
(availableModels.size > 0 && isModelAvailable(hephaestusRequirement.requiresModel, availableModels))
|
isAnyFallbackModelAvailable(hephaestusRequirement.fallbackChain, availableModels)
|
||||||
|
|
||||||
if (hasRequiredModel) {
|
if (hasRequiredModel) {
|
||||||
let hephaestusResolution = applyModelResolution({
|
let hephaestusResolution = applyModelResolution({
|
||||||
|
|||||||
@ -22,6 +22,7 @@ const AGENT_RESTRICTIONS: Record<string, Record<string, boolean>> = {
|
|||||||
edit: false,
|
edit: false,
|
||||||
task: false,
|
task: false,
|
||||||
delegate_task: false,
|
delegate_task: false,
|
||||||
|
call_omo_agent: false,
|
||||||
},
|
},
|
||||||
|
|
||||||
metis: {
|
metis: {
|
||||||
|
|||||||
@ -2,7 +2,7 @@ import { existsSync, readFileSync } from "fs"
|
|||||||
import { join } from "path"
|
import { join } from "path"
|
||||||
import { log } from "./logger"
|
import { log } from "./logger"
|
||||||
import { getOpenCodeCacheDir } from "./data-path"
|
import { getOpenCodeCacheDir } from "./data-path"
|
||||||
import { readProviderModelsCache, hasProviderModelsCache } from "./connected-providers-cache"
|
import { readProviderModelsCache, hasProviderModelsCache, readConnectedProvidersCache } from "./connected-providers-cache"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fuzzy match a target model name against available models
|
* Fuzzy match a target model name against available models
|
||||||
@ -278,19 +278,35 @@ export function isAnyFallbackModelAvailable(
|
|||||||
fallbackChain: Array<{ providers: string[]; model: string }>,
|
fallbackChain: Array<{ providers: string[]; model: string }>,
|
||||||
availableModels: Set<string>,
|
availableModels: Set<string>,
|
||||||
): boolean {
|
): boolean {
|
||||||
if (availableModels.size === 0) {
|
// If we have models, check them first
|
||||||
return false
|
if (availableModels.size > 0) {
|
||||||
}
|
for (const entry of fallbackChain) {
|
||||||
|
const hasAvailableProvider = entry.providers.some((provider) => {
|
||||||
for (const entry of fallbackChain) {
|
return fuzzyMatchModel(entry.model, availableModels, [provider]) !== null
|
||||||
const hasAvailableProvider = entry.providers.some((provider) => {
|
})
|
||||||
return fuzzyMatchModel(entry.model, availableModels, [provider]) !== null
|
if (hasAvailableProvider) {
|
||||||
})
|
return true
|
||||||
if (hasAvailableProvider) {
|
}
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
log("[isAnyFallbackModelAvailable] no model available in chain", { chainLength: fallbackChain.length })
|
|
||||||
|
// Fallback: check if any provider in the chain is connected
|
||||||
|
// This handles race conditions where availableModels is empty or incomplete
|
||||||
|
// but we know the provider is connected.
|
||||||
|
const connectedProviders = readConnectedProvidersCache()
|
||||||
|
if (connectedProviders) {
|
||||||
|
const connectedSet = new Set(connectedProviders)
|
||||||
|
for (const entry of fallbackChain) {
|
||||||
|
if (entry.providers.some((p) => connectedSet.has(p))) {
|
||||||
|
log("[isAnyFallbackModelAvailable] model not in available set, but provider is connected", {
|
||||||
|
model: entry.model,
|
||||||
|
availableCount: availableModels.size,
|
||||||
|
})
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user