Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 12 additions & 3 deletions open-sse/services/combo/comboStructure.ts
Original file line number Diff line number Diff line change
Expand Up @@ -529,7 +529,10 @@ function hasKnownCompatibleContextLimit(
requirements: RequestCompatibilityRequirements
): boolean {
if (requirements.requiredContextTokens <= 0) return false;
const capabilities = getResolvedModelCapabilities(target.modelStr);
const capabilities = getResolvedModelCapabilities({
provider: target.providerId || target.provider || null,
model: target.modelStr,
});
return evaluateContextLimit(capabilities, requirements, target.modelStr) === true;
}

Expand All @@ -546,7 +549,10 @@ export function isVisionIncompatibleTarget(
requirements: RequestCompatibilityRequirements
): boolean {
if (!requirements.requiresVision) return false;
const capabilities = getResolvedModelCapabilities(target.modelStr);
const capabilities = getResolvedModelCapabilities({
provider: target.providerId || target.provider || null,
model: target.modelStr,
});
return capabilities.supportsVision !== true;
}

Expand All @@ -571,7 +577,10 @@ function getTargetCompatibilityFailures(
target: ResolvedComboTarget,
requirements: RequestCompatibilityRequirements
): string[] {
const capabilities = getResolvedModelCapabilities(target.modelStr);
const capabilities = getResolvedModelCapabilities({
provider: target.providerId || target.provider || null,
model: target.modelStr,
});
const failures: string[] = [];

if (
Expand Down
11 changes: 10 additions & 1 deletion src/lib/guardrails/visionBridge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,16 @@ export async function getComboVisionBridgeDecision(
hasModelStep = true;
const targetModel = s.model;
if (typeof targetModel === "string") {
const caps = getResolvedModelCapabilities(targetModel);
const provider =
typeof s.providerId === "string"
? s.providerId
: typeof s.provider === "string"
? s.provider
: null;
const caps = getResolvedModelCapabilities({
provider,
model: targetModel,
});
if (caps.supportsVision === true) {
hasVisionCapableStep = true;
} else {
Expand Down
1 change: 1 addition & 0 deletions stryker.conf.json
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,7 @@
"tests/unit/github-model-not-supported-lockout.test.ts",
"tests/unit/grok-cli-oauth.test.ts",
"tests/unit/guardrails-api-3496.test.ts",
"tests/unit/guardrails/combo-vision-provider-id-12112.test.ts",
"tests/unit/guardrails/visionBridge-responses-9597.test.ts",
"tests/unit/headroom-codex-quota-snapshot-6379.test.ts",
"tests/unit/headroom-proxy-lifecycle.test.ts",
Expand Down
69 changes: 69 additions & 0 deletions tests/unit/guardrails/combo-vision-provider-id-12112.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import test from "node:test";
import assert from "node:assert/strict";

process.env.DATA_DIR = `/tmp/omniroute-test-12112-${Date.now()}`;

const { getComboVisionBridgeDecision } = await import(
"../../../src/lib/guardrails/visionBridge.ts"
);
const combosDb = await import("../../../src/lib/db/combos.ts");
const core = await import("../../../src/lib/db/core.ts");
const { isVisionIncompatibleTarget } = await import(
"../../../open-sse/services/combo/comboStructure.ts"
);
import type { ResolvedComboTarget } from "../../../open-sse/services/combo/types.ts";

test.after(() => {
core.resetDbInstance();
});

test("#12112: checkComboVision respects providerId for namespaced vision models (e.g. nvidia/nemotron-nano-12b-v2-vl)", async () => {
// Model 'nvidia/nemotron-nano-12b-v2-vl' is declared with supportsVision: true in nvidia provider registry.
// It has a slash in model id and requires providerId="nvidia" to resolve capabilities.
await combosDb.createCombo({
name: "nvidia-vision-combo-12112",
models: [
{
providerId: "nvidia",
model: "nvidia/nemotron-nano-12b-v2-vl",
weight: 1,
},
],
});

const decision = await getComboVisionBridgeDecision("nvidia-vision-combo-12112");
assert.equal(
decision,
"skip",
"Combo with explicit nvidia vision model must resolve as 'skip' (vision supported), not 'no-vision'"
);
});

test("#12112: isVisionIncompatibleTarget passes providerId to resolve vision capability", () => {
const target: ResolvedComboTarget = {
kind: "model",
stepId: "step-1",
executionKey: "step-1",
modelStr: "nvidia/nemotron-nano-12b-v2-vl",
provider: "nvidia",
providerId: "nvidia",
connectionId: "conn-1",
weight: 1,
label: null,
};

const incompatible = isVisionIncompatibleTarget(target, {
requiresTools: false,
requiresVision: true,
requiresStructuredOutput: false,
estimatedInputTokens: 10,
requestedOutputTokens: 10,
requiredContextTokens: 10,
});

assert.equal(
incompatible,
false,
"Target with providerId='nvidia' and model='nvidia/nemotron-nano-12b-v2-vl' must be vision-compatible"
);
});
Loading