Files
OmniRoute/tests/unit/vision-detection-consistency.test.ts
Diego Rodrigues de Sa e Souza 3c9883bb73 Release v3.8.29 (#4126)
OmniRoute v3.8.29 — 115 commits since v3.8.28. Full CHANGELOG + 41 i18n mirrors. All content quality gates green (build, unit 8/8, vitest 188/188, PR test policy, quality gates extended, docs sync, quality ratchet). Remaining red CI checks are pre-existing release flakes (coverage-shard/integration/node-compat teardown), a new transitive undici advisory in electron devDeps, and a workflow-level CodeQL fail (0 open alerts). VPS-validated by the operator.
2026-06-19 06:49:01 -03:00

81 lines
3.2 KiB
TypeScript

/**
* #4072 — one shared vision-detection source.
*
* Three code paths kept independent vision-model lists and drifted apart, giving
* the same model id up to three different verdicts:
* - `src/lib/modelCapabilities.ts` (`modelIdLikelyVision`) — routing fallback (#4071)
* - `src/app/api/v1/models/catalog.ts` (`isVisionModelId`) — /v1/models listing
* - `open-sse/services/compression/lite.ts` (`replaceImageUrls`) — lite image strip
*
* The two concrete bugs:
* - lite stripped images for real vision models it didn't know (pixtral, llava,
* qwen-vl, glm-4v, kimi-vl, mistral-medium-3) → blinded them;
* - catalog flagged text models as vision (`gemma`, bare `kimi` like `kimi-k2`).
*
* After unification all three delegate to `@/shared/constants/visionModels`.
*/
import { describe, it } from "node:test";
import assert from "node:assert/strict";
import { isVisionModelId } from "../../src/shared/constants/visionModels.ts";
import { isVisionModelId as catalogIsVisionModelId } from "../../src/app/api/v1/models/catalog.ts";
import { modelIdLikelyVision } from "../../src/lib/modelCapabilities.ts";
import { replaceImageUrls } from "../../open-sse/services/compression/lite.ts";
const VISION = [
"mistral/pixtral-12b-latest",
"llava-1.5-7b",
"qwen-vl-max",
"gpt-4o",
"glm-4v",
"kimi-vl-a3b",
"mistral-medium-3",
];
const NOT_VISION = ["ministral-14b-latest", "mistral-large-latest", "gemma-2-9b", "kimi-k2"];
function imageBody() {
return {
messages: [
{
role: "user",
content: [
{ type: "image_url", image_url: { url: "data:image/png;base64,iVBOR" } },
],
},
],
};
}
// `replaceImageUrls` strips the image (applied=true) only when the model is NOT a
// vision model. So `applied === !isVision`.
function liteStripsImage(modelId: string): boolean {
return replaceImageUrls(imageBody(), modelId).applied;
}
describe("#4072 vision detection is consistent across all three sources", () => {
for (const id of VISION) {
it(`treats ${id} as vision everywhere`, () => {
assert.equal(isVisionModelId(id), true, `shared isVisionModelId(${id})`);
assert.equal(catalogIsVisionModelId(id), true, `catalog isVisionModelId(${id})`);
assert.equal(modelIdLikelyVision(id), true, `modelCapabilities modelIdLikelyVision(${id})`);
assert.equal(liteStripsImage(id), false, `lite must KEEP the image for ${id}`);
});
}
for (const id of NOT_VISION) {
it(`treats ${id} as non-vision everywhere`, () => {
assert.equal(isVisionModelId(id), false, `shared isVisionModelId(${id})`);
assert.equal(catalogIsVisionModelId(id), false, `catalog isVisionModelId(${id})`);
assert.equal(modelIdLikelyVision(id), false, `modelCapabilities modelIdLikelyVision(${id})`);
assert.equal(liteStripsImage(id), true, `lite must STRIP the image for ${id}`);
});
}
it("preserves the MiniMax M3 #3328 carve-out and Gemini 3 multimodal", () => {
for (const id of ["minimax-m3", "minimax-m3-free", "oc/minimax-m3-free", "gemini-3-pro"]) {
assert.equal(isVisionModelId(id), true, `${id} should be vision`);
assert.equal(liteStripsImage(id), false, `lite must keep the image for ${id}`);
}
});
});