mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-14 19:22:32 +03:00
Merge-train validated (tip 6ce4effef8). Vitest failures confirmed as base-red (#9679).
This commit is contained in:
committed by
GitHub
parent
915b383a71
commit
74cf860ccc
1
changelog.d/features/9248-video-url-passthrough.md
Normal file
1
changelog.d/features/9248-video-url-passthrough.md
Normal file
@@ -0,0 +1 @@
|
||||
- **feat(providers):** make video_url passthrough configurable per provider/model via compat override ([#9248](https://github.com/diegosouzapw/OmniRoute/issues/9248)) — thanks @HellFiveOsborn
|
||||
@@ -25,6 +25,7 @@ import { bootstrapTranslatorRegistry } from "./bootstrap.ts";
|
||||
import { hasThinkingConfig, normalizeThinkingConfig } from "../services/provider.ts";
|
||||
import { applyThinkingBudget } from "../services/thinkingBudget.ts";
|
||||
import { applyReasoningRuleDirective } from "@/lib/reasoningRouting/policy";
|
||||
import { getModelPreserveVideoUrl } from "@/lib/db/models/modelPreserveVideoUrl";
|
||||
import { getResolvedModelCapabilities, supportsReasoning } from "../services/modelCapabilities.ts";
|
||||
import { normalizeRoles } from "../services/roleNormalizer.ts";
|
||||
import { hoistLeadingSystemMessage } from "./helpers/strictSystemHoist.ts";
|
||||
@@ -368,8 +369,9 @@ export function translateRequest(
|
||||
providerHonorsOpenAIFormatCacheControl(provider, connectionCacheOverride),
|
||||
// #4849 regression guard: keep client reasoning_content for replay providers.
|
||||
preserveReasoningContent: isReasoner,
|
||||
// Moonshot's Chat API accepts its own OpenAI-compatible `video_url` block.
|
||||
preserveVideoUrl: normalizedProvider === "moonshot" || normalizedProvider === "kimi",
|
||||
// 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"),
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
67
src/lib/db/models/modelPreserveVideoUrl.ts
Normal file
67
src/lib/db/models/modelPreserveVideoUrl.ts
Normal file
@@ -0,0 +1,67 @@
|
||||
/**
|
||||
* modelPreserveVideoUrl.ts — preserveVideoUrl resolver for model-compat overrides.
|
||||
*
|
||||
* Extracted from models.ts to avoid growing the frozen file-size baseline.
|
||||
* Follows the same pattern as getModelPreserveOpenAIDeveloperRole.
|
||||
*/
|
||||
|
||||
import { getDbInstance } from "../core";
|
||||
import { type CompatByProtocolMap, readCompatList, isCompatProtocolKey } from "./compat";
|
||||
|
||||
/** The model-compat override key for the preserveVideoUrl flag. */
|
||||
const KEY = "preserveVideoUrl";
|
||||
|
||||
function getCustomModelRow(providerId: string, modelId: string): Record<string, unknown> | undefined {
|
||||
const db = getDbInstance();
|
||||
const row = db
|
||||
.prepare(
|
||||
"SELECT value FROM key_value WHERE namespace = 'modelCompatOverrides' AND key = ?"
|
||||
)
|
||||
.get(`${providerId}::${modelId}`);
|
||||
if (!row) return undefined;
|
||||
try {
|
||||
const v = JSON.parse((row as { value: string }).value);
|
||||
return typeof v === "object" && v !== null ? (v as Record<string, unknown>) : undefined;
|
||||
} catch {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the explicit preserve-video-url preference for a provider/model.
|
||||
* `undefined` = unset → fall back to moonshot/kimi hardcoded behavior (caller decides).
|
||||
* `true` = keep video_url content parts in the translated request.
|
||||
* Per-protocol overrides live under `compatByProtocol[sourceFormat]`.
|
||||
*/
|
||||
export function getModelPreserveVideoUrl(
|
||||
providerId: string,
|
||||
modelId: string,
|
||||
sourceFormat?: string | null
|
||||
): boolean | undefined {
|
||||
const m = getCustomModelRow(providerId, modelId);
|
||||
const protocol = sourceFormat && isCompatProtocolKey(sourceFormat) ? sourceFormat : null;
|
||||
|
||||
if (m) {
|
||||
if (protocol) {
|
||||
const pc = (m.compatByProtocol as CompatByProtocolMap | undefined)?.[protocol];
|
||||
if (pc && Object.prototype.hasOwnProperty.call(pc, KEY)) {
|
||||
return Boolean(pc[KEY]);
|
||||
}
|
||||
}
|
||||
if (Object.prototype.hasOwnProperty.call(m, KEY)) {
|
||||
return Boolean(m[KEY]);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
const co = readCompatList(providerId).find((e) => e.id === modelId);
|
||||
if (protocol && co?.compatByProtocol?.[protocol]) {
|
||||
const pc = co.compatByProtocol[protocol]!;
|
||||
if (Object.prototype.hasOwnProperty.call(pc, KEY)) {
|
||||
return Boolean(pc[KEY]);
|
||||
}
|
||||
}
|
||||
if (co && Object.prototype.hasOwnProperty.call(co, KEY)) {
|
||||
return Boolean(co[KEY]);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
46
tests/unit/preserve-video-url-compat.test.ts
Normal file
46
tests/unit/preserve-video-url-compat.test.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
import { describe, it } from "node:test";
|
||||
import { ok, equal } from "node:assert/strict";
|
||||
|
||||
describe("getModelPreserveVideoUrl", () => {
|
||||
it("exports getModelPreserveVideoUrl as a function", async () => {
|
||||
const mod = await import("@/lib/db/models/modelPreserveVideoUrl");
|
||||
equal(typeof mod.getModelPreserveVideoUrl, "function");
|
||||
});
|
||||
|
||||
it("fallback preserves moonshot and kimi legacy behavior", () => {
|
||||
const fallback = (provider: string) =>
|
||||
provider === "moonshot" || provider === "kimi";
|
||||
ok(fallback("moonshot"));
|
||||
ok(fallback("kimi"));
|
||||
equal(fallback("dashscope"), false);
|
||||
equal(fallback("unknown"), false);
|
||||
});
|
||||
|
||||
it("translator import resolves correctly", async () => {
|
||||
const mod = await import("@/lib/db/models/modelPreserveVideoUrl");
|
||||
// Calling with unknown provider/model returns undefined (no compat override)
|
||||
const result = mod.getModelPreserveVideoUrl("test_provider", "test_model");
|
||||
equal(result, undefined);
|
||||
// Calling with known hardcoded defaults also returns undefined (no compat row)
|
||||
const result2 = mod.getModelPreserveVideoUrl("moonshot", "moonshot-v1");
|
||||
equal(result2, undefined);
|
||||
});
|
||||
|
||||
it("mergeModelCompatOverride accepts preserveVideoUrl", async () => {
|
||||
const { mergeModelCompatOverride, removeModelCompatOverride } = await import("@/lib/db/models/compat");
|
||||
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");
|
||||
});
|
||||
|
||||
it("deepMergeCompatByProtocol accepts preserveVideoUrl under openai protocol", async () => {
|
||||
const { deepMergeCompatByProtocol } = await import("@/lib/db/models/compat");
|
||||
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