-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
2026 06 21 Compression Phase2 Named Profiles Design
Status: approved direction (2026-06-21), pending spec review
Base branch: release/v3.8.32 (Phase 1 merged via #4432)
Goal: Let the operator pick which compression profile is globally active — the
panel-derived Default or one of the existing named combos — via a single
active-profile selector that writes CompressionConfig.activeComboId, and wire that
selection into the runtime so the chosen profile actually runs. Remove the now-duplicate
"master mode selector" and "Set as Default" controls so there is exactly one concept:
the active profile.
Phase 1 (#4432) already built the engines map (the Default), deriveDefaultPlan,
resolveCompressionPlan (header/active-combo-aware), and persisted activeComboId.
Phase 3 (the x-omniroute-compression per-request header) is a separate later plan.
The named-combos infrastructure already exists; Phase 2 wires it up and removes duplication. From the code map:
-
compression_combostable (migration 042) +src/lib/db/compressionCombos.ts: full CRUD (listCompressionCombos,createCompressionCombo,updateCompressionCombo,deleteCompressionCombo), routing-combo assignments, and anis_defaultflag (getDefaultCompressionCombo/setDefaultCompressionCombo). A combo'spipelineisCompressionPipelineStep[]({engine, intensity?, config?}). -
CompressionCombosPageClient.tsxrenders two blocks oncontext/combos:-
CompressionHub— today: a Token-Saver (master) toggle, a Mode selector (Off/Lite/Standard/…/Stacked), a read-only active-pipeline display (read from the 410-shim/api/context/combos/default), and reorder buttons. -
NamedCombosManager— full create/list/edit/delete of named combos + pipeline editor + language packs + output mode + routing-combo assignments + a "Set as Default" button (setsis_default).
-
-
CompressionConfig.activeComboIdis persisted (/api/settings/compression) but not wired into dispatch:resolveCompressionPlanhas the active-combo branch (ctx.combos[config.activeComboId]), but Phase 1 only reaches it from insidederiveDefaultPlanFromConfig(gated onenginesExplicit) and passescombos: {}(empty), so the branch never fires.
The reconciliation (decided): unify under the active profile. The active-profile
selector (writing activeComboId) is the single user-facing source of "which profile
runs". The is_default flag stays as a backend-only legacy detail (the fallback the
chatCore legacy block + deriveEnginesMap backfill read for installs that never opted
into the panel); it is no longer user-settable. The "Set as Default" button is removed.
master off → off
→ routing-combo override (config.comboOverrides[comboId]) → that mode
→ ACTIVE PROFILE (config.activeComboId set + combo exists) → that combo's pipeline [NEW]
→ auto-trigger (estimatedTokens ≥ autoTriggerTokens) → autoTriggerMode
→ Default derived (enginesExplicit → engines map; else legacy defaultMode)
→ off
The active profile is an explicit operator choice, so it resolves:
-
regardless of
enginesExplicit(settingactiveComboIdis itself an explicit opt-in, even on a legacy install), and - above auto-trigger (a manually-chosen profile wins over automatic escalation),
- but below a per-routing-combo override (a route-scoped override is more specific).
Phase 1's resolveBasePlan (in strategySelector.ts) does: master-off → routing-combo
override → auto-trigger → deriveDefaultPlanFromConfig. The activeComboId branch lives
inside resolveCompressionPlan, which deriveDefaultPlanFromConfig only calls when
enginesExplicit, with combos: {}. So Phase 2 lifts the active-profile resolution
into resolveBasePlan, right after the routing-combo override and before auto-trigger:
// resolveBasePlan, after checkComboOverride, before shouldAutoTrigger:
if (config.activeComboId && ctx.combos?.[config.activeComboId]) {
return { mode: "stacked", stackedPipeline: ctx.combos[config.activeComboId] };
}ctx.combos is Record<comboId, CompressionPipelineStep[]>. selectCompressionPlan/
selectCompressionStrategy gain a combos param (threaded into ctx) so callers supply
it; existing callers that pass nothing default to {} (unchanged behavior).
chatCore.ts already loads named combos for routing-combo assignment lookup. Phase 2
extends that to build the full combos map once and pass it to selectCompressionPlan:
const { listCompressionCombos } = await import("../../src/lib/db/compressionCombos.ts");
const combos = Object.fromEntries(listCompressionCombos().map((c) => [c.id, c.pipeline]));
// …passed as the new combos arg to selectCompressionStrategy / selectCompressionPlanWhen the active profile resolves to stacked, its pipeline reaches applyCompressionAsync
the same way Phase 1's engines-map override does — chatCore sets
config.stackedPipeline = ctx.combos[activeComboId] under the same guard
(!compressionComboApplied && !config.compressionComboId) so the operator's chosen profile
runs instead of the built-in default. strategySelector stays pure (no src/lib/db
import); only chatCore touches the DB.
is_default, getDefaultCompressionCombo, setDefaultCompressionCombo,
setEngineInDefaultCombo, the chatCore legacy default-combo block, and the
deriveEnginesMap backfill are unchanged. Setting activeComboId never writes
is_default. The active-profile path resolves before (and independently of) the legacy
default-combo fallback, so legacy installs that never set activeComboId keep their exact
behavior; an install that sets activeComboId runs that profile.
No new API endpoints or DB migrations: /api/settings/compression already carries
activeComboId, and the combos CRUD API already exists.
Active profile: [ Default (from panel) ▼ ]
• Default (from panel) → activeComboId = null
• Standard Savings → a named combo id
• <other named combos>
On change → PUT /api/settings/compression { activeComboId } (null for "Default";
debounced/merge-patch like the panel's save()). Below it, a read-only preview of the
active profile's effective pipeline ("runs: rtk → caveman → …"):
- Default →
deriveDefaultPlan(config.engines, config.enabled)(the pure fn, client-side). - Named combo → that combo's pipeline.
- Remove the Mode selector (Off/Lite/…/Stacked) — the mode is now derived; this is the "master mode selector" the redesign removes.
-
Remove the Token Saver (master on/off) toggle — it lives in the Panel
(
context/settings), the single source since Phase 1. -
Remove the pipeline reorder buttons — ordering a named combo is done in the
combo editor (
NamedCombosManager); the Default's order is auto-derived bystackPriority. - Keep only the active-profile selector + the read-only preview.
- Remove the "Set as Default" button (the active selector replaces it).
-
Keep everything else: create/list/edit/delete, the pipeline editor (add/remove steps
- per-step intensity), language packs, output mode, routing-combo assignments.
-
Add an "● Active" badge on the card whose id equals
activeComboId.
Sidebar order unchanged (Settings → Combos → per-engine pages → Studio).
Both runners green (test:unit node + test:vitest); typecheck:core + lint clean;
check:file-size (rebaseline CompressionHub.tsx/CompressionCombosPageClient.tsx if
they grow, with a justification key).
-
Resolver / dispatch (unit, node):
selectCompressionStrategy/resolveBasePlanwithactiveComboId+ctx.combos[id]→ resolves to that combo's stacked pipeline, regardless ofenginesExplicit;activeComboIdnull → Default derived;activeComboIdset but combo missing → graceful fall-through. Precedence: routing-combo override > active profile > auto-trigger > Default derived (one test per boundary). -
chatCore integration:
activeComboIdset → chatCore loads named combos → the active combo's pipeline runs viaapplyCompressionAsync(engineBreakdown reflects the combo's engines), mirroring Phase 1'sderived-pipeline-integrationtest. Plus: settingactiveComboIddoes not mutateis_default(getDefaultCompressionCombounchanged). -
DB + API (unit):
activeComboIdround-trips inupdateCompressionSettings/getCompressionSettingsandPUT/GET /api/settings/compression, including the "switch combo → null" path. -
UI (vitest component):
CompressionHubrenders the selector (Default + named combos), changing it issues the rightPUT, the preview reflects the selection, and the removed controls (mode selector, master toggle, reorder) are absent (alignment, not masking).NamedCombosManager: "Set as Default" gone; the active combo shows the "● Active" badge. Honor the Phase-1 vitest gotchas (i18n renders English/keys → assert on stable strings/data-testid; new@omniroute/...imports don't resolve under vitest → use relative/re-export). -
No regression:
is_default/getDefaultCompressionCombo/legacy chatCore block/ backfill tests stay green and untouched.
-
Phase 3 — the
x-omniroute-compressionper-request header — is a separate plan (the resolver is already header-aware). - No new compression engines; no new named-combo features (templates, sharing, import/ export).
- No change to the combo CRUD API/DB (already shaped correctly).
- The
is_defaultflag is kept (legacy backend fallback), not removed — removing it is a larger migration out of scope here. - The Compression Studio (analytics) is untouched.
OmniRoute · Website · npm · Docker Hub
- Setup Guide
- User Guide
- Features
- Quick Start (Docker)
- Electron Desktop App
- Termux (Android)
- PWA Guide
- MCP Server
- A2A Server
- Agent Protocols
- OpenCode Plugin
- Webhooks
- Cloud Agents
- Skills
- Memory
- Evals
- Gamification
- Guardrails
- Compliance
- Error Sanitization
- Public Credentials
- Route Guard Tiers
- Stealth Guide
- CLI Token Auth