fix(look-at): preserve variant metadata in fallback chain and block non-vision models

- fallback-chain.ts: cache-derived entries inherit variant from matching hardcoded entries
- agent-metadata.ts: new isVisionCapableAgentModel() guard blocks non-vision registered models
- tools.ts: early vision-capability check before session creation
- Added regression tests for variant preservation and non-vision model rejection
This commit is contained in:
YeonGyu-Kim
2026-03-11 21:45:48 +09:00
parent b40de091b4
commit fee414fd61
5 changed files with 145 additions and 11 deletions
@@ -65,6 +65,35 @@ describe("resolveMultimodalLookerAgentMetadata", () => {
})
})
test("preserves hardcoded fallback variant when the registered model matches a cache-derived entry", async () => {
// given
setVisionCapableModelsCache(new Map([
[
"openai/gpt-5.4",
{ providerID: "openai", modelID: "gpt-5.4" },
],
]))
spyOn(modelAvailability, "fetchAvailableModels").mockResolvedValue(
new Set(["openai/gpt-5.4"]),
)
spyOn(connectedProvidersCache, "readConnectedProvidersCache").mockReturnValue(["openai"])
const ctx = createPluginInput([
{
name: "multimodal-looker",
model: { providerID: "openai", modelID: "gpt-5.4" },
},
])
// when
const result = await resolveMultimodalLookerAgentMetadata(ctx)
// then
expect(result).toEqual({
agentModel: { providerID: "openai", modelID: "gpt-5.4" },
agentVariant: "medium",
})
})
test("prefers connected vision-capable provider models before the hardcoded fallback chain", async () => {
// given
setVisionCapableModelsCache(new Map([
@@ -97,6 +126,12 @@ describe("resolveMultimodalLookerAgentMetadata", () => {
test("falls back to the hardcoded multimodal chain when no dynamic vision model exists", async () => {
// given
setVisionCapableModelsCache(new Map([
[
"google/gemini-3-flash",
{ providerID: "google", modelID: "gemini-3-flash" },
],
]))
spyOn(modelAvailability, "fetchAvailableModels").mockResolvedValue(
new Set(["google/gemini-3-flash"]),
)
@@ -112,4 +147,24 @@ describe("resolveMultimodalLookerAgentMetadata", () => {
agentVariant: undefined,
})
})
test("does not return a registered model when no vision-capable model is available", async () => {
// given
spyOn(modelAvailability, "fetchAvailableModels").mockResolvedValue(
new Set(["openai/gpt-5.4"]),
)
spyOn(connectedProvidersCache, "readConnectedProvidersCache").mockReturnValue(["openai"])
const ctx = createPluginInput([
{
name: "multimodal-looker",
model: { providerID: "openai", modelID: "gpt-5.4" },
},
])
// when
const result = await resolveMultimodalLookerAgentMetadata(ctx)
// then
expect(result).toEqual({})
})
})