From 7efa6729768cc344ab1bed19cac403d3043a29ce Mon Sep 17 00:00:00 2001 From: diegosouzapw Date: Mon, 13 Apr 2026 14:35:26 -0300 Subject: [PATCH] fix(core): restore degradation settings and clean combo stream output Persist background degradation settings as structured data so they can be reloaded during node startup without double-encoding JSON. Update settings validation to accept the missing fields used by the API and align models.dev sync interval bounds with millisecond-based values. Also strip omniModel tags when they are wrapped by either literal escaped newlines or actual newline characters, and adjust the combo routing test to match the cleaned streamed content. --- open-sse/services/combo.ts | 10 ++++++++-- .../api/settings/background-degradation/route.ts | 2 +- src/instrumentation-node.ts | 15 +++++++++++++++ src/shared/validation/settingsSchemas.ts | 5 ++++- tests/unit/combo-routing-engine.test.mjs | 2 +- 5 files changed, 29 insertions(+), 5 deletions(-) diff --git a/open-sse/services/combo.ts b/open-sse/services/combo.ts index 061ccb9de5..6c1e2e9bc4 100644 --- a/open-sse/services/combo.ts +++ b/open-sse/services/combo.ts @@ -1042,7 +1042,10 @@ export async function handleComboChat({ const text = sanitizeDecoder.decode(chunk, { stream: true }); if (text) { if (text.includes("")) { - const cleaned = text.replace(/\n?[^<]+<\/omniModel>\n?/g, ""); + const cleaned = text.replace( + /(?:\\n|\n)?[^<]+<\/omniModel>(?:\\n|\n)?/g, + "" + ); if (cleaned) controller.enqueue(encoder.encode(cleaned)); } else { controller.enqueue(encoder.encode(text)); @@ -1053,7 +1056,10 @@ export async function handleComboChat({ const tail = sanitizeDecoder.decode(); if (tail) { if (tail.includes("")) { - const cleaned = tail.replace(/\n?[^<]+<\/omniModel>\n?/g, ""); + const cleaned = tail.replace( + /(?:\\n|\n)?[^<]+<\/omniModel>(?:\\n|\n)?/g, + "" + ); if (cleaned) controller.enqueue(encoder.encode(cleaned)); } else { controller.enqueue(encoder.encode(tail)); diff --git a/src/app/api/settings/background-degradation/route.ts b/src/app/api/settings/background-degradation/route.ts index 6fb7021c91..cb19443fd6 100644 --- a/src/app/api/settings/background-degradation/route.ts +++ b/src/app/api/settings/background-degradation/route.ts @@ -53,7 +53,7 @@ export async function PUT(request) { // Persist to database (excluding stats) const { stats, ...persistable } = getBackgroundDegradationConfig(); - await updateSettings({ backgroundDegradation: JSON.stringify(persistable) }); + await updateSettings({ backgroundDegradation: persistable }); return NextResponse.json({ success: true, ...getBackgroundDegradationConfig() }); } catch (error) { diff --git a/src/instrumentation-node.ts b/src/instrumentation-node.ts index becbc020fe..88bab3ae68 100755 --- a/src/instrumentation-node.ts +++ b/src/instrumentation-node.ts @@ -125,6 +125,21 @@ export async function registerNodejs(): Promise { } } + if (settings.backgroundDegradation) { + try { + const bgSettings = + typeof settings.backgroundDegradation === "string" + ? JSON.parse(settings.backgroundDegradation) + : settings.backgroundDegradation; + const { setBackgroundDegradationConfig } = + await import("@omniroute/open-sse/services/backgroundTaskDetector.ts"); + setBackgroundDegradationConfig(bgSettings); + console.log(`[STARTUP] Restored background task degradation config from settings`); + } catch (err: unknown) { + console.warn(`[STARTUP] Failed to parse background degradation settings:`, err); + } + } + const migration = await migrateCodexConnectionDefaultsFromLegacySettings(); if (migration.migrated) { console.log( diff --git a/src/shared/validation/settingsSchemas.ts b/src/shared/validation/settingsSchemas.ts index 9deb2be992..582bbc8890 100644 --- a/src/shared/validation/settingsSchemas.ts +++ b/src/shared/validation/settingsSchemas.ts @@ -88,5 +88,8 @@ export const updateSettingsSchema = z.object({ skillsmpApiKey: z.string().max(200).optional(), // models.dev sync settings modelsDevSyncEnabled: z.boolean().optional(), - modelsDevSyncInterval: z.number().int().min(3600).max(604800).optional(), + modelsDevSyncInterval: z.number().int().min(1000).max(604800000).optional(), + // Missing settings + lkgpEnabled: z.boolean().optional(), + backgroundDegradation: z.unknown().optional(), }); diff --git a/tests/unit/combo-routing-engine.test.mjs b/tests/unit/combo-routing-engine.test.mjs index 0c3c46ef2b..82dbc2b35b 100644 --- a/tests/unit/combo-routing-engine.test.mjs +++ b/tests/unit/combo-routing-engine.test.mjs @@ -1618,7 +1618,7 @@ test("handleComboChat context cache protection flushes cleanly when a stream end assert.equal(result.ok, true); assert.equal(result.headers.get("X-OmniRoute-Model"), "openai/gpt-4o-mini"); assert.match(text, /data: \[DONE\]/); - assert.match(text, /"content":"\\\\n\\\\n"/); + assert.match(text, /"content":""/); assert.doesNotMatch(text, //); });