diff --git a/open-sse/services/capacityAdapter.js b/open-sse/services/capacityAdapter.js index 875f3aa2..b18626aa 100644 --- a/open-sse/services/capacityAdapter.js +++ b/open-sse/services/capacityAdapter.js @@ -13,18 +13,19 @@ import { getCapabilitiesForModel } from "../providers/capabilities.js"; const CAPABILITY_KEYS = ["vision", "pdf", "audioInput", "videoInput"]; const HARD_CAPS = new Set(CAPABILITY_KEYS); const DEFAULT_FALLBACK_MODEL = "oc/mimo-v2.6-flash-free"; +const upgradeLegacyModel = (m) => (m === "oc/mimo-v2.5-free" ? DEFAULT_FALLBACK_MODEL : m); // Normalize a capability entry to { enabled, roundRobin, models }. Backward-compat: // accept the legacy array form [{model, enabled}] (treated as enabled, fallback). function normalizeCapEntry(entry) { if (Array.isArray(entry)) { - return { enabled: true, roundRobin: false, models: entry.map((e) => e?.model || e).filter(Boolean) }; + return { enabled: true, roundRobin: false, models: entry.map((e) => upgradeLegacyModel(e?.model || e)).filter(Boolean) }; } if (entry && typeof entry === "object") { return { enabled: entry.enabled !== false, roundRobin: !!entry.roundRobin, - models: Array.isArray(entry.models) ? entry.models.filter(Boolean) : [], + models: Array.isArray(entry.models) ? entry.models.map(upgradeLegacyModel).filter(Boolean) : [], }; } return { enabled: false, roundRobin: false, models: [] }; diff --git a/src/app/(dashboard)/dashboard/combos/page.js b/src/app/(dashboard)/dashboard/combos/page.js index 56471beb..60f633a5 100644 --- a/src/app/(dashboard)/dashboard/combos/page.js +++ b/src/app/(dashboard)/dashboard/combos/page.js @@ -17,9 +17,9 @@ const VALID_NAME_REGEX = /^[a-zA-Z0-9_.\-]+$/; // A request needing a capability the target model/combo lacks switches straight // to the first enabled model here instead of erroring or dropping the data. const CAPACITY_ADAPTER_CAPS = [ - { key: "vision", label: "Vision", icon: "visibility", desc: "Images" }, + { key: "vision", label: "Vision", icon: "visibility", desc: "images (png, jpg, webp, …)" }, // pdf, videoInput temporarily hidden — no translator support yet for those blocks. - { key: "audioInput", label: "Audio", icon: "graphic_eq", desc: "Audio input" }, + { key: "audioInput", label: "Audio", icon: "graphic_eq", desc: "audio input" }, ]; const DEFAULT_FALLBACK_MODEL = "oc/mimo-v2.6-flash-free"; const EMPTY_CAP_ENTRY = { enabled: true, roundRobin: false, models: [] }; @@ -29,16 +29,18 @@ const EMPTY_CAPACITY_ADAPTER = { audioInput: { ...EMPTY_CAP_ENTRY }, videoInput: { ...EMPTY_CAP_ENTRY }, }; +const upgradeLegacyModel = (m) => (m === "oc/mimo-v2.5-free" ? DEFAULT_FALLBACK_MODEL : m); + // Backward-compat: legacy stored form was an array of {model, enabled}. function normalizeCapEntry(entry) { if (Array.isArray(entry)) { - return { enabled: true, roundRobin: false, models: entry.map((e) => e?.model || e).filter(Boolean) }; + return { enabled: true, roundRobin: false, models: entry.map((e) => upgradeLegacyModel(e?.model || e)).filter(Boolean) }; } if (entry && typeof entry === "object") { return { enabled: entry.enabled !== false, roundRobin: !!entry.roundRobin, - models: Array.isArray(entry.models) ? entry.models.filter(Boolean) : [], + models: Array.isArray(entry.models) ? entry.models.map(upgradeLegacyModel).filter(Boolean) : [], }; } return { ...EMPTY_CAP_ENTRY }; @@ -371,7 +373,7 @@ export default function CombosPage() {
  • Round Robin — rotates models across requests to spread load
  • Fusion — queries all models in parallel, then a judge synthesizes one answer. Best quality, but costs the most: every request bills all panel models + the judge (N+1 calls)
  • -

    +

    Cursor / Claude Default create combos named exactly like those clients' model IDs (e.g. composer-2.5, opus), seeded with the matching cu/… or cc/… route so traffic can hit 9router without the prefix. {" "}Note: Cursor IDE itself often blocks built-in Composer / Grok from Override OpenAI Base URL ("model does not support custom API"); add them via Cursor's Add Custom Model using the combo name, or pick a model Cursor allows through the custom endpoint.

    @@ -380,7 +382,7 @@ export default function CombosPage() { -
    +
    diff --git a/src/lib/db/repos/settingsRepo.js b/src/lib/db/repos/settingsRepo.js index b94f770a..8b8023f0 100644 --- a/src/lib/db/repos/settingsRepo.js +++ b/src/lib/db/repos/settingsRepo.js @@ -86,6 +86,16 @@ export function mergeWithDefaults(raw) { } } } + if (merged.capacityAdapter && typeof merged.capacityAdapter === "object") { + for (const capKey of Object.keys(merged.capacityAdapter)) { + const entry = merged.capacityAdapter[capKey]; + if (Array.isArray(entry?.models)) { + entry.models = entry.models.map((m) => + m === "oc/mimo-v2.5-free" ? "oc/mimo-v2.6-flash-free" : m + ); + } + } + } return merged; }