fix(vision-bridge): force GPT-family image fallback (#1571)

Integrated into release/v3.7.0
This commit is contained in:
Cong Vu Chi
2026-04-25 01:56:16 +07:00
committed by GitHub
parent 1e60f0ce51
commit af5d800759
3 changed files with 43 additions and 1 deletions

View File

@@ -15,6 +15,7 @@ import {
import {
VISION_BRIDGE_DEFAULTS,
getVisionBridgeConfig,
isVisionBridgeForcedModel,
} from "@/shared/constants/visionBridgeDefaults";
export interface VisionBridgeDependencies {
@@ -55,9 +56,11 @@ export class VisionBridgeGuardrail extends BaseGuardrail {
return { block: false };
}
const forceVisionBridge = isVisionBridgeForcedModel(model);
// 4. Check if model supports vision
const capabilities = getResolvedModelCapabilities(model);
if (capabilities.supportsVision === true) {
if (capabilities.supportsVision === true && !forceVisionBridge) {
return { block: false };
}

View File

@@ -2,6 +2,14 @@
* Vision Bridge default configuration values.
*/
const NORMALIZED_GPT_MODEL_PATTERN = /^gpt-/i;
export function isVisionBridgeForcedModel(model: string | null | undefined): boolean {
if (!model) return false;
const normalizedModel = model.includes("/") ? model.split("/").pop() || model : model;
return NORMALIZED_GPT_MODEL_PATTERN.test(normalizedModel);
}
export const VISION_BRIDGE_DEFAULTS = {
enabled: true,
model: "openai/gpt-4o-mini",

View File

@@ -164,6 +164,37 @@ test("VB-S02: passthroughs for vision-capable model (gpt-4o)", async () => {
}
});
test("VB-S02b: forces Vision Bridge for GPT-family models even when model capabilities advertise vision", async () => {
const guardrail = createGuardrail();
for (const model of ["gpt-5.4", "gpt-5.4-mini", "gpt-4o", "openai/gpt-4o-mini"]) {
mockVisionResponse = `Forced bridge description for ${model}`;
visionCallCount = 0;
const payload = createPayload({
model,
messages: [
{
role: "user",
content: [
{ type: "text", text: "What is this?" },
{
type: "image_url",
image_url: { url: "https://example.com/image.png" },
},
],
},
],
});
const result = await guardrail.preCall(payload, createContext({ model }));
assert.strictEqual(result.block, false, `expected passthrough=false for ${model}`);
assert.ok(result.modifiedPayload, `expected modified payload for ${model}`);
assert.strictEqual(visionCallCount, 1, `expected one forced bridge call for ${model}`);
}
});
test("VB-S02: model capabilities returns supportsVision for known models", () => {
const gpt4oCaps = getResolvedModelCapabilities("openai/gpt-4o");
// supportsVision may be true (if sync data exists) or null (if not synced)