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.
This commit is contained in:
diegosouzapw
2026-04-13 14:35:26 -03:00
parent 0856854c81
commit 7efa672976
5 changed files with 29 additions and 5 deletions

View File

@@ -1042,7 +1042,10 @@ export async function handleComboChat({
const text = sanitizeDecoder.decode(chunk, { stream: true });
if (text) {
if (text.includes("<omniModel>")) {
const cleaned = text.replace(/\n?<omniModel>[^<]+<\/omniModel>\n?/g, "");
const cleaned = text.replace(
/(?:\\n|\n)?<omniModel>[^<]+<\/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("<omniModel>")) {
const cleaned = tail.replace(/\n?<omniModel>[^<]+<\/omniModel>\n?/g, "");
const cleaned = tail.replace(
/(?:\\n|\n)?<omniModel>[^<]+<\/omniModel>(?:\\n|\n)?/g,
""
);
if (cleaned) controller.enqueue(encoder.encode(cleaned));
} else {
controller.enqueue(encoder.encode(tail));

View File

@@ -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) {

View File

@@ -125,6 +125,21 @@ export async function registerNodejs(): Promise<void> {
}
}
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(

View File

@@ -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(),
});

View File

@@ -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, /<omniModel>/);
});