import { beforeEach, describe, expect, test } from "bun:test"; import { setConfig } from "../__tests__/helpers/set-config.js"; import { getConfig } from "../config/loader.js"; import { getDb } from "../persistence/db-connection.js"; import { initializeDb } from "../persistence/db-init.js"; import { providerConnections } from "../persistence/schema/inference.js"; import type { ModelProfileInfo } from "./types.js"; // Entry-name translation reads provider_connections rows through the real DB. await initializeDb(); // ─── Fixture config ───────────────────────────────────────────────────────── interface MockProfileEntry { provider?: string; model?: string; status?: string; mix?: Array<{ profile: string; weight: number }>; } let mockProfiles: Record = {}; let mockDefaultProvider: { provider: string; connectionName?: string } | null = null; // Real model catalog — don't mock it, the test exercises real catalog lookups. const { doesSupportVision } = await import("./vision-support.js"); // ─── Helpers ──────────────────────────────────────────────────────────────── function profile(key: string): ModelProfileInfo { return { key, label: key, description: null, isActive: false, isDisabled: false, isMix: false, }; } /** * Install the current fixture `llm` config for real. A schema-valid baseline * is seeded first so the loader caches a config object; `llm` is then * overwritten on that live cached object so fixtures the schema would strip * (non-enum provider ids) reach the resolver exactly as authored. */ function applyConfig(): void { setConfig("llm", { profiles: {} }); const config = getConfig() as { llm: unknown }; config.llm = { profiles: mockProfiles, ...(mockDefaultProvider != null ? { defaultProvider: mockDefaultProvider } : {}), }; } function setMockConfig( profiles: Record, defaultProvider?: { provider: string; connectionName?: string }, ) { mockProfiles = profiles; mockDefaultProvider = defaultProvider ?? null; applyConfig(); } beforeEach(() => { mockProfiles = {}; mockDefaultProvider = null; applyConfig(); }); // ─── Tests ────────────────────────────────────────────────────────────────── describe("doesSupportVision", () => { test("returns true for a known vision-capable model", () => { setMockConfig({ "vision-profile": { provider: "anthropic", model: "claude-opus-4-6" }, }); expect(doesSupportVision(profile("vision-profile"))).toBe(true); }); test("returns false for a known text-only model", () => { setMockConfig({ "text-profile": { provider: "fireworks", model: "accounts/fireworks/models/glm-5p2", }, }); expect(doesSupportVision(profile("text-profile"))).toBe(false); }); test("returns false for an unknown profile key not in config", () => { setMockConfig({}); expect(doesSupportVision(profile("nonexistent"))).toBe(false); }); test("returns false for an unknown provider/model pair", () => { setMockConfig({ "unknown-model": { provider: "unknown-provider", model: "unknown-model" }, }); expect(doesSupportVision(profile("unknown-model"))).toBe(false); }); test("implies the provider from the catalog when profile only sets model", () => { setMockConfig({ "model-only": { model: "claude-opus-4-6" } }); expect(doesSupportVision(profile("model-only"))).toBe(true); }); test("resolves a routing-identity profile through the model's catalog owner", () => { setMockConfig({ managed: { provider: "vellum", model: "claude-fable-5" }, "managed-text": { provider: "vellum", model: "accounts/fireworks/models/deepseek-v4-flash-0731", }, }); expect(doesSupportVision(profile("managed"))).toBe(true); expect(doesSupportVision(profile("managed-text"))).toBe(false); }); test("fails safe to false for a profile without a model", () => { // A model-less entry is not a usable resolution target, so vision // resolution treats it as "can't show images" (caption instead). setMockConfig({ "provider-only": { provider: "anthropic" } }); expect(doesSupportVision(profile("provider-only"))).toBe(false); }); test("mix profile returns true when any arm supports vision", () => { setMockConfig({ "mix-profile": { mix: [ { profile: "text-arm", weight: 0.5 }, { profile: "vision-arm", weight: 0.5 }, ], }, "text-arm": { provider: "fireworks", model: "accounts/fireworks/models/glm-5p2", }, "vision-arm": { provider: "anthropic", model: "claude-opus-4-6" }, }); expect(doesSupportVision(profile("mix-profile"))).toBe(true); }); test("mix profile returns false when all arms are text-only", () => { setMockConfig({ "mix-profile": { mix: [ { profile: "text-arm-1", weight: 0.5 }, { profile: "text-arm-2", weight: 0.5 }, ], }, "text-arm-1": { provider: "fireworks", model: "accounts/fireworks/models/glm-5p2", }, "text-arm-2": { provider: "fireworks", model: "accounts/fireworks/models/glm-5p2", }, }); expect(doesSupportVision(profile("mix-profile"))).toBe(false); }); }); describe("doesSupportVision with a BYO default provider", () => { test("judges a default profile against the default provider's column model", () => { // Managed/vellum column: cost-optimized → deepseek-v4-flash (text-only, // supportsVision: false). Anthropic column: cost-optimized carries // intent "latency-optimized" → claude-haiku-4-5 (supportsVision: true). // The judged model must be the one the BYO install actually runs. setMockConfig({}, { provider: "anthropic" }); expect(doesSupportVision(profile("cost-optimized"))).toBe(true); }); test("without a default provider, a default profile judges the managed column", () => { // Null-reduction: no defaultProvider resolves cost-optimized through the // vellum column (deepseek-v4-flash, text-only). setMockConfig({}); expect(doesSupportVision(profile("cost-optimized"))).toBe(false); }); test("mix arms naming a default profile resolve through the same column", () => { setMockConfig( { "mix-profile": { mix: [ { profile: "text-arm", weight: 0.5 }, { profile: "cost-optimized", weight: 0.5 }, ], }, "text-arm": { provider: "fireworks", model: "accounts/fireworks/models/glm-5p2", }, }, { provider: "anthropic" }, ); // The "cost-optimized" arm resolves to the anthropic column's // vision-capable model, so the mix can route to vision. expect(doesSupportVision(profile("mix-profile"))).toBe(true); }); test("a user-owned profile is unaffected by the default provider", () => { setMockConfig( { "custom-text": { provider: "fireworks", model: "accounts/fireworks/models/glm-5p2", }, }, { provider: "anthropic" }, ); expect(doesSupportVision(profile("custom-text"))).toBe(false); }); }); describe("doesSupportVision with connection entry-name providers", () => { function seedConnection(opts: { name: string; provider: string }): void { const now = Date.now(); getDb() .insert(providerConnections) .values({ name: opts.name, provider: opts.provider, auth: JSON.stringify({ type: "api_key", credential: `${opts.provider}/api_key`, }), createdAt: now, updatedAt: now, }) .run(); } beforeEach(() => { getDb().delete(providerConnections).run(); }); test("judges an entry-name profile by its row's vendor (vision-capable)", () => { seedConnection({ name: "openrouter-personal", provider: "openrouter" }); setMockConfig({ "personal-opus": { provider: "openrouter-personal", model: "anthropic/claude-opus-4.6", }, }); expect(doesSupportVision(profile("personal-opus"))).toBe(true); }); test("judges an entry-name profile by its row's vendor (text-only)", () => { seedConnection({ name: "openrouter-personal", provider: "openrouter" }); setMockConfig({ "personal-deepseek": { provider: "openrouter-personal", model: "deepseek/deepseek-v4-pro", }, }); expect(doesSupportVision(profile("personal-deepseek"))).toBe(false); }); test("an entry name naming no row fails safe to false", () => { setMockConfig({ dangling: { provider: "ghost-connection", model: "anthropic/claude-opus-4.6", }, }); expect(doesSupportVision(profile("dangling"))).toBe(false); }); test("mix arms with entry-name providers translate the same way", () => { seedConnection({ name: "openrouter-personal", provider: "openrouter" }); setMockConfig({ "mix-profile": { mix: [ { profile: "personal-deepseek", weight: 0.5 }, { profile: "personal-opus", weight: 0.5 }, ], }, "personal-deepseek": { provider: "openrouter-personal", model: "deepseek/deepseek-v4-pro", }, "personal-opus": { provider: "openrouter-personal", model: "anthropic/claude-opus-4.6", }, }); expect(doesSupportVision(profile("mix-profile"))).toBe(true); }); }); describe("doesSupportVision with a bare string", () => { test("returns true for a known vision-capable model id", () => { expect(doesSupportVision("claude-opus-4-6")).toBe(true); }); test("returns false for a known text-only model id", () => { expect(doesSupportVision("accounts/fireworks/models/glm-5p2")).toBe(false); }); test("falls back to resolving the string as a profile key", () => { // "vision-profile" is not a catalog model id, so it resolves as a profile // key → anthropic/claude-opus-4-6 (vision-capable). setMockConfig({ "vision-profile": { provider: "anthropic", model: "claude-opus-4-6" }, }); expect(doesSupportVision("vision-profile")).toBe(true); }); test("returns false for a string that is neither a model nor a profile", () => { setMockConfig({}); expect(doesSupportVision("some-unknown-string")).toBe(false); }); });