fix: improve model resolution with client API fallback and explicit model passing

- fetchAvailableModels now falls back to client.model.list() when cache is empty
- provider-models cache empty → models.json → client API (3-tier fallback)
- look-at tool explicitly passes registered agent's model to session.prompt
- Ensures multimodal-looker uses correctly resolved model (e.g., gemini-3-flash-preview)
- Add comprehensive tests for fuzzy matching and fallback scenarios
This commit is contained in:
justsisyphus
2026-01-30 16:57:13 +09:00
parent 2f7e188cb5
commit 80ee52fe3b
5 changed files with 298 additions and 46 deletions
+103 -44
View File
@@ -119,85 +119,144 @@ export async function getConnectedProviders(client: any): Promise<string[]> {
}
export async function fetchAvailableModels(
_client?: any,
client?: any,
options?: { connectedProviders?: string[] | null }
): Promise<Set<string>> {
const connectedProvidersUnknown = options?.connectedProviders === null || options?.connectedProviders === undefined
let connectedProviders = options?.connectedProviders ?? null
let connectedProvidersUnknown = connectedProviders === null
log("[fetchAvailableModels] CALLED", {
connectedProvidersUnknown,
connectedProviders: options?.connectedProviders
})
if (connectedProvidersUnknown && client) {
const liveConnected = await getConnectedProviders(client)
if (liveConnected.length > 0) {
connectedProviders = liveConnected
connectedProvidersUnknown = false
log("[fetchAvailableModels] connected providers fetched from client", { count: liveConnected.length })
}
}
if (connectedProvidersUnknown) {
if (client?.model?.list) {
const modelSet = new Set<string>()
try {
const modelsResult = await client.model.list()
const models = modelsResult.data ?? []
for (const model of models) {
if (model?.provider && model?.id) {
modelSet.add(`${model.provider}/${model.id}`)
}
}
log("[fetchAvailableModels] fetched models from client without provider filter", {
count: modelSet.size,
})
return modelSet
} catch (err) {
log("[fetchAvailableModels] client.model.list error", { error: String(err) })
}
}
log("[fetchAvailableModels] connected providers unknown, returning empty set for fallback resolution")
return new Set<string>()
}
const connectedProviders = options!.connectedProviders!
const connectedSet = new Set(connectedProviders)
const connectedProvidersList = connectedProviders ?? []
const connectedSet = new Set(connectedProvidersList)
const modelSet = new Set<string>()
const providerModelsCache = readProviderModelsCache()
if (providerModelsCache) {
log("[fetchAvailableModels] using provider-models cache (whitelist-filtered)")
for (const [providerId, modelIds] of Object.entries(providerModelsCache.models)) {
if (!connectedSet.has(providerId)) {
continue
const providerCount = Object.keys(providerModelsCache.models).length
if (providerCount === 0) {
log("[fetchAvailableModels] provider-models cache empty, falling back to models.json")
} else {
log("[fetchAvailableModels] using provider-models cache (whitelist-filtered)")
for (const [providerId, modelIds] of Object.entries(providerModelsCache.models)) {
if (!connectedSet.has(providerId)) {
continue
}
for (const modelId of modelIds) {
modelSet.add(`${providerId}/${modelId}`)
}
}
for (const modelId of modelIds) {
modelSet.add(`${providerId}/${modelId}`)
log("[fetchAvailableModels] parsed from provider-models cache", {
count: modelSet.size,
connectedProviders: connectedProvidersList.slice(0, 5)
})
if (modelSet.size > 0) {
return modelSet
}
log("[fetchAvailableModels] provider-models cache produced no models for connected providers, falling back to models.json")
}
log("[fetchAvailableModels] parsed from provider-models cache", {
count: modelSet.size,
connectedProviders: connectedProviders.slice(0, 5)
})
return modelSet
}
log("[fetchAvailableModels] provider-models cache not found, falling back to models.json")
const cacheFile = join(getOpenCodeCacheDir(), "models.json")
if (!existsSync(cacheFile)) {
log("[fetchAvailableModels] models.json cache file not found, returning empty set")
return modelSet
}
log("[fetchAvailableModels] models.json cache file not found, falling back to client")
} else {
try {
const content = readFileSync(cacheFile, "utf-8")
const data = JSON.parse(content) as Record<string, { id?: string; models?: Record<string, { id?: string }> }>
try {
const content = readFileSync(cacheFile, "utf-8")
const data = JSON.parse(content) as Record<string, { id?: string; models?: Record<string, { id?: string }> }>
const providerIds = Object.keys(data)
log("[fetchAvailableModels] providers found in models.json", { count: providerIds.length, providers: providerIds.slice(0, 10) })
const providerIds = Object.keys(data)
log("[fetchAvailableModels] providers found in models.json", { count: providerIds.length, providers: providerIds.slice(0, 10) })
for (const providerId of providerIds) {
if (!connectedSet.has(providerId)) {
continue
}
for (const providerId of providerIds) {
if (!connectedSet.has(providerId)) {
continue
const provider = data[providerId]
const models = provider?.models
if (!models || typeof models !== "object") continue
for (const modelKey of Object.keys(models)) {
modelSet.add(`${providerId}/${modelKey}`)
}
}
const provider = data[providerId]
const models = provider?.models
if (!models || typeof models !== "object") continue
log("[fetchAvailableModels] parsed models from models.json (NO whitelist filtering)", {
count: modelSet.size,
connectedProviders: connectedProvidersList.slice(0, 5)
})
for (const modelKey of Object.keys(models)) {
modelSet.add(`${providerId}/${modelKey}`)
if (modelSet.size > 0) {
return modelSet
}
} catch (err) {
log("[fetchAvailableModels] error", { error: String(err) })
}
log("[fetchAvailableModels] parsed models from models.json (NO whitelist filtering)", {
count: modelSet.size,
connectedProviders: connectedProviders.slice(0, 5)
})
return modelSet
} catch (err) {
log("[fetchAvailableModels] error", { error: String(err) })
return modelSet
}
if (client?.model?.list) {
try {
const modelsResult = await client.model.list()
const models = modelsResult.data ?? []
for (const model of models) {
if (!model?.provider || !model?.id) continue
if (connectedSet.has(model.provider)) {
modelSet.add(`${model.provider}/${model.id}`)
}
}
log("[fetchAvailableModels] fetched models from client (filtered)", {
count: modelSet.size,
connectedProviders: connectedProvidersList.slice(0, 5),
})
} catch (err) {
log("[fetchAvailableModels] client.model.list error", { error: String(err) })
}
}
return modelSet
}
export function __resetModelCache(): void {}