mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-20 22:22:57 +03:00
fix(types): preserve video URL override contracts (#9747)
This commit is contained in:
@@ -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"),
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -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) ||
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user