fix(types): preserve video URL override contracts (#9747)

This commit is contained in:
backryun
2026-08-08 19:15:28 +09:00
committed by GitHub
parent 4f8dccc8a3
commit 52a6b04f8e
3 changed files with 76 additions and 11 deletions

View File

@@ -372,7 +372,9 @@ export function translateRequest(
preserveReasoningContent: isReasoner,
// Per-provider/model preserveVideoUrl flag from compat overrides.
// Falls back to true for moonshot/kimi when unset (legacy behavior).
preserveVideoUrl: getModelPreserveVideoUrl(normalizedProvider, options?.model ?? "") ?? (normalizedProvider === "moonshot" || normalizedProvider === "kimi"),
preserveVideoUrl:
getModelPreserveVideoUrl(normalizedProvider, normalizedModel) ??
(normalizedProvider === "moonshot" || normalizedProvider === "kimi"),
});
}

View File

@@ -17,6 +17,7 @@ export { MODEL_COMPAT_PROTOCOL_KEYS, type ModelCompatProtocolKey };
export type ModelCompatPerProtocol = {
normalizeToolCallId?: boolean;
preserveOpenAIDeveloperRole?: boolean;
preserveVideoUrl?: boolean;
/** Merged into upstream HTTP requests for this model (after default auth headers). */
upstreamHeaders?: Record<string, string>;
};
@@ -76,6 +77,7 @@ export function deepMergeCompatByProtocol(
const hasDelta =
Object.prototype.hasOwnProperty.call(deltas, "normalizeToolCallId") ||
Object.prototype.hasOwnProperty.call(deltas, "preserveOpenAIDeveloperRole") ||
Object.prototype.hasOwnProperty.call(deltas, "preserveVideoUrl") ||
Object.prototype.hasOwnProperty.call(deltas, "upstreamHeaders");
if (!hasDelta) continue;
const cur: ModelCompatPerProtocol = { ...(out[key] || {}) };
@@ -85,6 +87,9 @@ export function deepMergeCompatByProtocol(
if ("preserveOpenAIDeveloperRole" in deltas) {
cur.preserveOpenAIDeveloperRole = Boolean(deltas.preserveOpenAIDeveloperRole);
}
if ("preserveVideoUrl" in deltas) {
cur.preserveVideoUrl = Boolean(deltas.preserveVideoUrl);
}
if ("upstreamHeaders" in deltas) {
const uh = deltas.upstreamHeaders;
if (uh === undefined) {
@@ -105,6 +110,7 @@ export type ModelCompatOverride = {
id: string;
normalizeToolCallId?: boolean;
preserveOpenAIDeveloperRole?: boolean;
preserveVideoUrl?: boolean;
compatByProtocol?: CompatByProtocolMap;
upstreamHeaders?: Record<string, string>;
isHidden?: boolean;
@@ -159,6 +165,7 @@ export function getModelCompatOverrides(providerId: string): ModelCompatOverride
export type ModelCompatPatch = {
normalizeToolCallId?: boolean;
preserveOpenAIDeveloperRole?: boolean | null;
preserveVideoUrl?: boolean | null;
compatByProtocol?: CompatByProtocolMap;
/** Replace top-level extra headers for override-only rows; omit to leave unchanged. */
upstreamHeaders?: Record<string, string> | null;
@@ -195,6 +202,13 @@ export function mergeModelCompatOverride(
next.preserveOpenAIDeveloperRole = Boolean(patch.preserveOpenAIDeveloperRole);
}
}
if ("preserveVideoUrl" in patch) {
if (patch.preserveVideoUrl === null) {
delete next.preserveVideoUrl;
} else {
next.preserveVideoUrl = Boolean(patch.preserveVideoUrl);
}
}
if (patch.compatByProtocol && Object.keys(patch.compatByProtocol).length > 0) {
const merged = deepMergeCompatByProtocol(next.compatByProtocol, patch.compatByProtocol);
if (compatByProtocolHasEntries(merged)) next.compatByProtocol = merged;
@@ -211,6 +225,7 @@ export function mergeModelCompatOverride(
}
const filtered = list.filter((e) => e.id !== modelId);
const hasPreserveFlag = Object.prototype.hasOwnProperty.call(next, "preserveOpenAIDeveloperRole");
const hasVideoUrlFlag = Object.prototype.hasOwnProperty.call(next, "preserveVideoUrl");
const hasTopUpstream = next.upstreamHeaders && Object.keys(next.upstreamHeaders).length > 0;
if ("isHidden" in patch) {
if (patch.isHidden === null) {
@@ -231,6 +246,7 @@ export function mergeModelCompatOverride(
if (
next.normalizeToolCallId ||
hasPreserveFlag ||
hasVideoUrlFlag ||
hasHiddenFlag ||
hasDeletedFlag ||
compatByProtocolHasEntries(next.compatByProtocol) ||

View File

@@ -1,5 +1,5 @@
import { describe, it } from "node:test";
import { ok, equal } from "node:assert/strict";
import { deepEqual, equal, ok } from "node:assert/strict";
describe("getModelPreserveVideoUrl", () => {
it("exports getModelPreserveVideoUrl as a function", async () => {
@@ -8,8 +8,7 @@ describe("getModelPreserveVideoUrl", () => {
});
it("fallback preserves moonshot and kimi legacy behavior", () => {
const fallback = (provider: string) =>
provider === "moonshot" || provider === "kimi";
const fallback = (provider: string) => provider === "moonshot" || provider === "kimi";
ok(fallback("moonshot"));
ok(fallback("kimi"));
equal(fallback("dashscope"), false);
@@ -27,19 +26,67 @@ describe("getModelPreserveVideoUrl", () => {
});
it("mergeModelCompatOverride accepts preserveVideoUrl", async () => {
const { mergeModelCompatOverride, removeModelCompatOverride } = await import("@/lib/db/models/compat");
const { mergeModelCompatOverride, removeModelCompatOverride } =
await import("@/lib/db/models/compat");
const { getModelPreserveVideoUrl } = await import("@/lib/db/models/modelPreserveVideoUrl");
const PROVIDER = "test_provider_9248v3";
const MODEL = "test_model_qwen_vl";
mergeModelCompatOverride(PROVIDER, MODEL, { preserveVideoUrl: true });
removeModelCompatOverride(PROVIDER, MODEL);
ok(true, "should accept preserveVideoUrl in ModelCompatPatch");
try {
mergeModelCompatOverride(PROVIDER, MODEL, { preserveVideoUrl: true });
equal(getModelPreserveVideoUrl(PROVIDER, MODEL), true);
} finally {
removeModelCompatOverride(PROVIDER, MODEL);
}
});
it("translateRequest resolves preserveVideoUrl with the routed model", async () => {
const { mergeModelCompatOverride, removeModelCompatOverride } =
await import("@/lib/db/models/compat");
const { translateRequest } = await import("@omniroute/open-sse/translator/index.ts");
const provider = "test-provider-video-override";
const model = "test-model-video-override";
mergeModelCompatOverride(provider, model, { preserveVideoUrl: true });
try {
const translated = translateRequest(
"openai",
"openai",
model,
{
messages: [
{
role: "user",
content: [
{
type: "video_url",
video_url: { url: "https://cdn.example.com/input.mp4" },
},
],
},
],
},
false,
null,
provider
) as { messages: Array<{ content: Array<{ type: string }> }> };
deepEqual(
translated.messages[0].content.map((part) => part.type),
["video_url"]
);
} finally {
removeModelCompatOverride(provider, model);
}
});
it("deepMergeCompatByProtocol accepts preserveVideoUrl under openai protocol", async () => {
const { deepMergeCompatByProtocol } = await import("@/lib/db/models/compat");
const result = deepMergeCompatByProtocol({}, {
openai: { preserveVideoUrl: true },
});
const result = deepMergeCompatByProtocol(
{},
{
openai: { preserveVideoUrl: true },
}
);
// Valid protocol keys are 'openai', 'openai-responses', 'claude'
equal(result.openai?.preserveVideoUrl, true);
});