mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-17 04:32:31 +03:00
* test(bridge): explicit native-vision skip guard + skip log * feat(bridge): configurable describe output cap (modalityBridgeVisionMaxChars) * feat(dashboard): maxChars field on Modality Bridge vision tab Add the "Max description characters" field to the Vision tab's Advanced panel (modalityBridgeVisionMaxChars, clamped to the 100-50000 schema range with 0 treated as the explicit "unlimited" sentinel), wire the en.json copy and sync it across all 42 locales, and document the new setting in GUARDRAILS.md. * fix(bridge): allow explicit 0 to disable the describe cap updateSettingsSchema previously rejected modalityBridgeVisionMaxChars: 0 because the field's range was min(100).max(50000), so a dashboard PATCH sending the explicit "unlimited" sentinel would 400. Widen the schema to z.union([z.literal(0), z.number().int().min(100).max(50000)]) so 0 validates as its own valid value, not just an implicit default. * chore(i18n): resync locale keys after release merge * chore(quality): rebaseline deadExports for the OCR/image-to-text series --------- Co-authored-by: Xiangzhe <bakryun0718@proton.me>
119 lines
4.4 KiB
TypeScript
119 lines
4.4 KiB
TypeScript
/**
|
|
* Modality Bridge default configuration values (PR-1).
|
|
*
|
|
* New `modalityBridge*` settings keys supersede the legacy `visionBridge*`
|
|
* keys; the legacy keys stay accepted as a fallback for one release cycle
|
|
* (rollback window) and are resolved here in a single place.
|
|
*/
|
|
import { VISION_BRIDGE_DEFAULTS } from "./visionBridgeDefaults";
|
|
|
|
export type VisionBridgeMode = "auto" | "describe" | "reroute";
|
|
|
|
export const MODALITY_BRIDGE_DEFAULTS = {
|
|
visionMode: "auto" as VisionBridgeMode,
|
|
visionTaskAware: true,
|
|
cacheEnabled: true,
|
|
cacheTtlMinutes: 60,
|
|
cacheMaxEntries: 200,
|
|
// 0 = no cap (existing behavior, description is passed through in full).
|
|
visionMaxChars: 0,
|
|
audioEnabled: true,
|
|
audioModel: "",
|
|
audioTimeoutMs: 60000,
|
|
audioMaxClips: 3,
|
|
} as const;
|
|
|
|
export interface VisionBridgeRuntimeSettings {
|
|
enabled: boolean;
|
|
mode: VisionBridgeMode;
|
|
model: string;
|
|
taskAware: boolean;
|
|
prompt: string;
|
|
timeoutMs: number;
|
|
maxImages: number;
|
|
maxChars: number;
|
|
cacheEnabled: boolean;
|
|
cacheTtlMinutes: number;
|
|
cacheMaxEntries: number;
|
|
}
|
|
|
|
export interface AudioBridgeRuntimeSettings {
|
|
enabled: boolean;
|
|
model: string;
|
|
timeoutMs: number;
|
|
maxClips: number;
|
|
cacheEnabled: boolean;
|
|
cacheTtlMinutes: number;
|
|
cacheMaxEntries: number;
|
|
}
|
|
|
|
// Typed candidate pickers: a stored value of the wrong type (e.g. the string
|
|
// "off" in a boolean field) is skipped so the next candidate/default wins.
|
|
function pickBoolean(...values: unknown[]): boolean | undefined {
|
|
for (const v of values) if (typeof v === "boolean") return v;
|
|
return undefined;
|
|
}
|
|
|
|
function pickNumber(...values: unknown[]): number | undefined {
|
|
for (const v of values) if (typeof v === "number" && Number.isFinite(v)) return v;
|
|
return undefined;
|
|
}
|
|
|
|
function pickString(...values: unknown[]): string | undefined {
|
|
for (const v of values) if (typeof v === "string") return v;
|
|
return undefined;
|
|
}
|
|
|
|
/** New modalityBridge* keys win; legacy visionBridge* keys are a one-cycle fallback. */
|
|
export function resolveVisionBridgeRuntimeSettings(
|
|
settings: Record<string, unknown> | null | undefined
|
|
): VisionBridgeRuntimeSettings {
|
|
const s = settings ?? {};
|
|
const mode = pickString(s.modalityBridgeVisionMode);
|
|
return {
|
|
enabled:
|
|
pickBoolean(s.modalityBridgeVisionEnabled, s.visionBridgeEnabled) ??
|
|
VISION_BRIDGE_DEFAULTS.enabled,
|
|
mode: mode === "describe" || mode === "reroute" ? mode : MODALITY_BRIDGE_DEFAULTS.visionMode,
|
|
model:
|
|
pickString(s.modalityBridgeVisionModel, s.visionBridgeModel) ?? VISION_BRIDGE_DEFAULTS.model,
|
|
taskAware:
|
|
pickBoolean(s.modalityBridgeVisionTaskAware) ?? MODALITY_BRIDGE_DEFAULTS.visionTaskAware,
|
|
prompt:
|
|
pickString(s.modalityBridgeVisionPrompt, s.visionBridgePrompt) ??
|
|
VISION_BRIDGE_DEFAULTS.prompt,
|
|
timeoutMs:
|
|
pickNumber(s.modalityBridgeVisionTimeout, s.visionBridgeTimeout) ??
|
|
VISION_BRIDGE_DEFAULTS.timeoutMs,
|
|
maxImages:
|
|
pickNumber(s.modalityBridgeVisionMaxImages, s.visionBridgeMaxImages) ??
|
|
VISION_BRIDGE_DEFAULTS.maxImagesPerRequest,
|
|
maxChars: pickNumber(s.modalityBridgeVisionMaxChars) ?? MODALITY_BRIDGE_DEFAULTS.visionMaxChars,
|
|
cacheEnabled:
|
|
pickBoolean(s.modalityBridgeCacheEnabled) ?? MODALITY_BRIDGE_DEFAULTS.cacheEnabled,
|
|
cacheTtlMinutes:
|
|
pickNumber(s.modalityBridgeCacheTtlMinutes) ?? MODALITY_BRIDGE_DEFAULTS.cacheTtlMinutes,
|
|
cacheMaxEntries:
|
|
pickNumber(s.modalityBridgeCacheMaxEntries) ?? MODALITY_BRIDGE_DEFAULTS.cacheMaxEntries,
|
|
};
|
|
}
|
|
|
|
/** Resolve persisted Audio Bridge settings with the shared PR-1 defaults. */
|
|
export function resolveAudioBridgeRuntimeSettings(
|
|
settings: Record<string, unknown> | null | undefined
|
|
): AudioBridgeRuntimeSettings {
|
|
const s = settings ?? {};
|
|
return {
|
|
enabled: pickBoolean(s.modalityBridgeAudioEnabled) ?? MODALITY_BRIDGE_DEFAULTS.audioEnabled,
|
|
model: pickString(s.modalityBridgeAudioModel) ?? MODALITY_BRIDGE_DEFAULTS.audioModel,
|
|
timeoutMs: pickNumber(s.modalityBridgeAudioTimeout) ?? MODALITY_BRIDGE_DEFAULTS.audioTimeoutMs,
|
|
maxClips: pickNumber(s.modalityBridgeAudioMaxClips) ?? MODALITY_BRIDGE_DEFAULTS.audioMaxClips,
|
|
cacheEnabled:
|
|
pickBoolean(s.modalityBridgeCacheEnabled) ?? MODALITY_BRIDGE_DEFAULTS.cacheEnabled,
|
|
cacheTtlMinutes:
|
|
pickNumber(s.modalityBridgeCacheTtlMinutes) ?? MODALITY_BRIDGE_DEFAULTS.cacheTtlMinutes,
|
|
cacheMaxEntries:
|
|
pickNumber(s.modalityBridgeCacheMaxEntries) ?? MODALITY_BRIDGE_DEFAULTS.cacheMaxEntries,
|
|
};
|
|
}
|