feat(dashboard): add compression-mode selector to Context & Cache combos page (#6760) (#7219)

Extracts the routing-combo compression-mode dropdown (Default/Off/Lite/
Standard/Aggressive/Ultra) from the combo card into a shared
ComboCompressionModeSelect component, reused on both the combo card
(compact) and the Compression Combos page's "Assign to routing" list
under Context & Cache. Both surfaces persist through the existing
PUT /api/combos/{id} route -- no backend or schema change.
This commit is contained in:
Diego Rodrigues de Sa e Souza
2026-07-17 10:40:05 -03:00
committed by GitHub
parent 69c778eb45
commit 0f10225f1d
7 changed files with 454 additions and 74 deletions

View File

@@ -0,0 +1 @@
- **feat(dashboard):** add per-routing-combo compression-mode override to the Compression Combos page under Context & Cache, alongside the existing combo-card quick override. (#6760)

View File

@@ -188,6 +188,14 @@ Combo: "free-forever"
This lets you use stacked compression on free/coding providers while keeping lite mode on paid
subscriptions.
This "Per-Combo Override" assignment is a different control from the **routing-combo compression
mode** override (Default/Off/Lite/Standard/Aggressive/Ultra) — that override does not pick a named
compression-combo pipeline; it just sets the `compressionMode` field consulted by
`resolveCompressionPlan`. It can be set either on the combo card (`Dashboard → Combos`) or, since
#6760, per routing combo in the "Assign to routing" list on
`Dashboard → Context & Cache → Compression Combos`, right next to the pipeline-assignment checkbox
documented above. Both surfaces persist through the same `PUT /api/combos/{id}` endpoint.
### Per-request override
Send the `x-omniroute-compression` request header to override the compression plan for a single

View File

@@ -12,6 +12,7 @@ import Input from "@/shared/components/Input";
import Modal from "@/shared/components/Modal";
import Toggle from "@/shared/components/Toggle";
import Tooltip from "@/shared/components/Tooltip";
import { ComboCompressionModeSelect } from "@/shared/components/compression/ComboCompressionModeSelect";
import { useCopyToClipboard } from "@/shared/hooks/useCopyToClipboard";
import { filterUsableConnections } from "@/shared/utils/connectionStatus";
import { FieldLabelWithHelp, WeightTotalBar } from "./parts";
@@ -1576,46 +1577,6 @@ function ComboCard({
const tc = useTranslations("common");
const emailsVisible = useEmailPrivacyStore((s) => s.emailsVisible);
const strategyDescription = getStrategyDescription(t, strategy);
const hasRuntimeConfig = combo?.config && typeof combo.config === "object";
const initialCompressionMode =
typeof combo?.config?.compressionMode === "string"
? combo.config.compressionMode
: hasRuntimeConfig
? ""
: combo.compressionOverride || "";
const [compressionOverride, setCompressionOverride] = useState(initialCompressionMode);
const [isSavingCompression, setIsSavingCompression] = useState(false);
useEffect(() => {
setCompressionOverride(initialCompressionMode);
}, [initialCompressionMode]);
const handleCompressionOverrideChange = async (value) => {
setCompressionOverride(value);
setIsSavingCompression(true);
const nextConfig = { ...(combo.config || {}) };
if (value) {
nextConfig.compressionMode = value;
} else {
delete nextConfig.compressionMode;
}
try {
const response = await fetch(`/api/combos/${combo.id}`, {
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ config: nextConfig }),
});
if (!response.ok) {
console.error("Failed to update compression override");
setCompressionOverride(initialCompressionMode);
}
} catch (error) {
console.error("Error updating compression override:", error);
setCompressionOverride(initialCompressionMode);
} finally {
setIsSavingCompression(false);
}
};
return (
<Card
@@ -1745,32 +1706,11 @@ function ComboCard({
</div>
<div className="flex items-center gap-1.5 transition-opacity">
{compressionEnabled && (
<select
value={compressionOverride}
onChange={(e) => handleCompressionOverrideChange(e.target.value)}
disabled={isSavingCompression}
className="text-xs py-1 px-2 rounded border border-black/10 dark:border-white/10 bg-surface text-text-main focus:border-primary focus:outline-none transition-colors disabled:opacity-50 max-w-[130px] md:max-w-none"
<ComboCompressionModeSelect
combo={combo}
title={t("compressionOverride")}
>
<option value="" className="bg-surface text-text-main">
Default
</option>
<option value="off" className="bg-surface text-text-main">
Off
</option>
<option value="lite" className="bg-surface text-text-main">
Lite
</option>
<option value="standard" className="bg-surface text-text-main">
Standard
</option>
<option value="aggressive" className="bg-surface text-text-main">
Aggressive
</option>
<option value="ultra" className="bg-surface text-text-main">
Ultra
</option>
</select>
className="text-xs py-1 px-2 rounded border border-black/10 dark:border-white/10 bg-surface text-text-main focus:border-primary focus:outline-none transition-colors disabled:opacity-50 max-w-[130px] md:max-w-none"
/>
)}
<Link
href={`/dashboard/combos/${combo.id}`}

View File

@@ -10,6 +10,7 @@
import { useEffect, useState } from "react";
import { STACKED_PIPELINE_ENGINE_INTENSITIES } from "@/shared/validation/compressionConfigSchemas";
import { CompressionPipelineEditor } from "@/shared/components/compression/CompressionPipelineEditor";
import { ComboCompressionModeSelect } from "@/shared/components/compression/ComboCompressionModeSelect";
import CompressionHub from "./CompressionHub";
type PipelineStep = { engine: string; intensity?: string };
@@ -23,7 +24,11 @@ type CompressionCombo = {
outputModeIntensity: string;
isDefault: boolean;
};
type RoutingCombo = { id?: string; name?: string };
type RoutingCombo = {
id?: string;
name?: string;
config?: { compressionMode?: string } | null;
};
type LanguagePack = { language: string; ruleCount: number };
const EMPTY_PIPELINE: PipelineStep[] = [
@@ -49,6 +54,7 @@ function NamedCombosManager() {
const [assignmentIds, setAssignmentIds] = useState<string[]>([]);
const [saving, setSaving] = useState(false);
const [activeComboId, setActiveComboId] = useState<string | null>(null);
const [compressionEnabled, setCompressionEnabled] = useState(false);
const [error, setError] = useState<string | null>(null);
const refresh = () => {
@@ -70,7 +76,10 @@ function NamedCombosManager() {
.catch(() => {});
fetch("/api/settings/compression")
.then((res) => (res.ok ? res.json() : null))
.then((data) => setActiveComboId(data?.activeComboId ?? null))
.then((data) => {
setActiveComboId(data?.activeComboId ?? null);
setCompressionEnabled(Boolean(data?.enabled));
})
.catch(() => {});
}, []);
@@ -255,14 +264,22 @@ function NamedCombosManager() {
const id = combo.id ?? combo.name ?? "";
if (!id) return null;
return (
<label key={id} className="flex items-center justify-between gap-2">
<span className="truncate">{combo.name ?? id}</span>
<input
type="checkbox"
checked={assignmentIds.includes(id)}
onChange={(event) => toggleAssignment(id, event.target.checked)}
<div key={id} className="flex items-center justify-between gap-2">
<label className="flex min-w-0 flex-1 items-center justify-between gap-2">
<span className="truncate">{combo.name ?? id}</span>
<input
type="checkbox"
checked={assignmentIds.includes(id)}
onChange={(event) => toggleAssignment(id, event.target.checked)}
/>
</label>
<ComboCompressionModeSelect
combo={{ id, config: combo.config }}
disabled={!compressionEnabled}
title="Compression override"
className="w-24 shrink-0 rounded-lg border border-border bg-bg px-2 py-1 text-xs text-text-main disabled:opacity-50"
/>
</label>
</div>
);
})
)}

View File

@@ -0,0 +1,108 @@
"use client";
import { useEffect, useState } from "react";
export interface ComboCompressionModeSelectCombo {
id: string;
config?: { compressionMode?: string } | null;
compressionOverride?: string;
}
export interface ComboCompressionModeSelectProps {
combo: ComboCompressionModeSelectCombo;
disabled?: boolean;
title?: string;
className?: string;
onSaved?: (nextConfig: Record<string, unknown>) => void;
}
const OPTIONS: Array<{ value: string; label: string }> = [
{ value: "", label: "Default" },
{ value: "off", label: "Off" },
{ value: "lite", label: "Lite" },
{ value: "standard", label: "Standard" },
{ value: "aggressive", label: "Aggressive" },
{ value: "ultra", label: "Ultra" },
];
function getInitialCompressionMode(combo: ComboCompressionModeSelectCombo): string {
const hasRuntimeConfig = combo?.config && typeof combo.config === "object";
if (typeof combo?.config?.compressionMode === "string") {
return combo.config.compressionMode;
}
return hasRuntimeConfig ? "" : combo.compressionOverride || "";
}
// Extracted from src/app/(dashboard)/dashboard/combos/page.tsx so both the combo card
// and the Compression Combos page (#6760) can persist the same per-routing-combo
// compression-mode override through the same `PUT /api/combos/{id}` endpoint.
//
// Deliberately does NOT call `useTranslations` — this component is rendered from
// CompressionCombosPageClient.tsx, which avoids page-level `useTranslations` to
// prevent a documented production hydration regression. Callers that want localized
// labels pass a resolved `title` string; option labels stay literal English.
export function ComboCompressionModeSelect({
combo,
disabled,
title,
className,
onSaved,
}: ComboCompressionModeSelectProps) {
const initialCompressionMode = getInitialCompressionMode(combo);
const [compressionOverride, setCompressionOverride] = useState(initialCompressionMode);
const [isSaving, setIsSaving] = useState(false);
useEffect(() => {
setCompressionOverride(initialCompressionMode);
}, [initialCompressionMode]);
const handleChange = async (value: string) => {
setCompressionOverride(value);
setIsSaving(true);
const nextConfig: Record<string, unknown> = { ...(combo.config || {}) };
if (value) {
nextConfig.compressionMode = value;
} else {
delete nextConfig.compressionMode;
}
try {
const response = await fetch(`/api/combos/${combo.id}`, {
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ config: nextConfig }),
});
if (!response.ok) {
console.error("Failed to update compression override");
setCompressionOverride(initialCompressionMode);
return;
}
onSaved?.(nextConfig);
} catch (error) {
console.error("Error updating compression override:", error);
setCompressionOverride(initialCompressionMode);
} finally {
setIsSaving(false);
}
};
return (
<select
value={compressionOverride}
onChange={(e) => handleChange(e.target.value)}
disabled={disabled || isSaving}
className={
className ||
"text-xs py-1 px-2 rounded border border-black/10 dark:border-white/10 bg-surface text-text-main focus:border-primary focus:outline-none transition-colors disabled:opacity-50"
}
title={title}
>
{OPTIONS.map((option) => (
<option key={option.value} value={option.value} className="bg-surface text-text-main">
{option.label}
</option>
))}
</select>
);
}
export default ComboCompressionModeSelect;

View File

@@ -0,0 +1,142 @@
// @vitest-environment jsdom
import React, { act } from "react";
import { createRoot } from "react-dom/client";
import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
const containers: HTMLElement[] = [];
const roots: Array<{ unmount: () => void }> = [];
function mount(ui: React.ReactElement): HTMLElement {
const container = document.createElement("div");
document.body.appendChild(container);
containers.push(container);
const root = createRoot(container);
roots.push(root);
act(() => {
root.render(ui);
});
return container;
}
beforeEach(() => {
(
globalThis as typeof globalThis & { IS_REACT_ACT_ENVIRONMENT?: boolean }
).IS_REACT_ACT_ENVIRONMENT = true;
});
afterEach(async () => {
vi.restoreAllMocks();
await act(async () => {
while (roots.length > 0) roots.pop()?.unmount();
});
for (let i = 0; i < 10; i++) await Promise.resolve();
while (containers.length > 0) containers.pop()?.remove();
document.body.innerHTML = "";
});
async function flush() {
await act(async () => {
for (let i = 0; i < 10; i++) await Promise.resolve();
});
}
describe("ComboCompressionModeSelect (#6760)", () => {
it("hydrates the initial value from combo.config.compressionMode", async () => {
const { ComboCompressionModeSelect } = await import(
"../../../src/shared/components/compression/ComboCompressionModeSelect"
);
const combo = { id: "c1", config: { compressionMode: "lite" } };
const container = mount(<ComboCompressionModeSelect combo={combo} />);
await flush();
const select = container.querySelector("select") as HTMLSelectElement;
expect(select.value).toBe("lite");
});
it("hydrates from legacy combo.compressionOverride when config is absent", async () => {
const { ComboCompressionModeSelect } = await import(
"../../../src/shared/components/compression/ComboCompressionModeSelect"
);
const combo = { id: "c1", compressionOverride: "aggressive" };
const container = mount(<ComboCompressionModeSelect combo={combo} />);
await flush();
const select = container.querySelector("select") as HTMLSelectElement;
expect(select.value).toBe("aggressive");
});
it("PUTs the correct config payload to /api/combos/{id} on selection change", async () => {
const { ComboCompressionModeSelect } = await import(
"../../../src/shared/components/compression/ComboCompressionModeSelect"
);
const calls: Array<{ url: string; init?: RequestInit }> = [];
vi.spyOn(globalThis, "fetch").mockImplementation(async (input, init) => {
calls.push({ url: input.toString(), init });
return new Response(JSON.stringify({ ok: true }), { status: 200 });
});
const combo = { id: "c1", config: { compressionMode: "lite" } };
const container = mount(<ComboCompressionModeSelect combo={combo} />);
await flush();
const select = container.querySelector("select") as HTMLSelectElement;
await act(async () => {
select.value = "standard";
select.dispatchEvent(new Event("change", { bubbles: true }));
});
await flush();
expect(calls).toHaveLength(1);
expect(calls[0].url).toBe("/api/combos/c1");
expect(calls[0].init?.method).toBe("PUT");
const body = JSON.parse(calls[0].init?.body as string);
expect(body).toEqual({ config: { compressionMode: "standard" } });
});
it('selecting "Default" removes compressionMode from the PUT payload', async () => {
const { ComboCompressionModeSelect } = await import(
"../../../src/shared/components/compression/ComboCompressionModeSelect"
);
const calls: Array<{ init?: RequestInit }> = [];
vi.spyOn(globalThis, "fetch").mockImplementation(async (_input, init) => {
calls.push({ init });
return new Response(JSON.stringify({ ok: true }), { status: 200 });
});
const combo = { id: "c1", config: { compressionMode: "lite" } };
const container = mount(<ComboCompressionModeSelect combo={combo} />);
await flush();
const select = container.querySelector("select") as HTMLSelectElement;
await act(async () => {
select.value = "";
select.dispatchEvent(new Event("change", { bubbles: true }));
});
await flush();
const body = JSON.parse(calls[0].init?.body as string);
expect(body).toEqual({ config: {} });
});
it("rolls back the displayed value when the PUT response is not OK", async () => {
const { ComboCompressionModeSelect } = await import(
"../../../src/shared/components/compression/ComboCompressionModeSelect"
);
vi.spyOn(globalThis, "fetch").mockImplementation(async () => {
return new Response(JSON.stringify({ error: "nope" }), { status: 500 });
});
const combo = { id: "c1", config: { compressionMode: "lite" } };
const container = mount(<ComboCompressionModeSelect combo={combo} />);
await flush();
const select = container.querySelector("select") as HTMLSelectElement;
await act(async () => {
select.value = "ultra";
select.dispatchEvent(new Event("change", { bubbles: true }));
});
await flush();
expect(select.value).toBe("lite");
});
it("disables the control when disabled=true", async () => {
const { ComboCompressionModeSelect } = await import(
"../../../src/shared/components/compression/ComboCompressionModeSelect"
);
const combo = { id: "c1", config: { compressionMode: "lite" } };
const container = mount(<ComboCompressionModeSelect combo={combo} disabled />);
await flush();
const select = container.querySelector("select") as HTMLSelectElement;
expect(select.disabled).toBe(true);
});
});

View File

@@ -0,0 +1,164 @@
// @vitest-environment jsdom
import React, { act } from "react";
import { createRoot } from "react-dom/client";
import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
const containers: HTMLElement[] = [];
const roots: Array<{ unmount: () => void }> = [];
function mount(ui: React.ReactElement): HTMLElement {
const container = document.createElement("div");
document.body.appendChild(container);
containers.push(container);
const root = createRoot(container);
roots.push(root);
act(() => {
root.render(ui);
});
return container;
}
beforeEach(() => {
(
globalThis as typeof globalThis & { IS_REACT_ACT_ENVIRONMENT?: boolean }
).IS_REACT_ACT_ENVIRONMENT = true;
});
afterEach(async () => {
vi.restoreAllMocks();
await act(async () => {
while (roots.length > 0) roots.pop()?.unmount();
});
for (let i = 0; i < 10; i++) await Promise.resolve();
while (containers.length > 0) containers.pop()?.remove();
document.body.innerHTML = "";
});
async function flush() {
await act(async () => {
for (let i = 0; i < 10; i++) await Promise.resolve();
});
}
function compressionCombo(id: string, name: string) {
return {
id,
name,
description: `${name} desc`,
pipeline: [{ engine: "rtk", intensity: "standard" }],
languagePacks: ["en"],
outputMode: false,
outputModeIntensity: "full",
isDefault: false,
};
}
function setupFetchMock() {
const calls: Array<{ url: string; init?: RequestInit }> = [];
const json = (body: unknown, status = 200) =>
new Response(JSON.stringify(body), {
status,
headers: { "Content-Type": "application/json" },
});
const routingCombos = [
{ id: "rc1", name: "Routing Alpha", config: { compressionMode: "lite" } },
{ id: "rc2", name: "Routing Bravo" },
];
vi.spyOn(globalThis, "fetch").mockImplementation(
async (input: RequestInfo | URL, init?: RequestInit) => {
const url = input.toString();
calls.push({ url, init });
if (url.includes("/api/context/combos/") && url.includes("/assignments")) {
return json({ assignments: [] });
}
if (url.includes("/api/context/combos")) {
return json({ combos: [compressionCombo("cc1", "Named Combo")] });
}
if (url.includes("/api/combos/")) {
return json({ ok: true });
}
if (url.includes("/api/combos")) {
return json({ combos: routingCombos });
}
if (url.includes("/api/compression/language-packs")) {
return json({ packs: [] });
}
if (url.includes("/api/settings/compression")) {
return json({ activeComboId: null, enabled: true });
}
return json({}, 404);
}
);
return calls;
}
describe("CompressionCombosPageClient — routing-combo compression mode selector (#6760)", () => {
async function render() {
const { default: CompressionCombosPageClient } = await import(
"../../../src/app/(dashboard)/dashboard/context/combos/CompressionCombosPageClient"
);
let container!: HTMLElement;
await act(async () => {
container = mount(<CompressionCombosPageClient />);
});
await flush();
return container;
}
it("renders a compression-mode select per routing combo, hydrated from combo.config", async () => {
setupFetchMock();
const container = await render();
expect(container.textContent).toContain("Routing Alpha");
expect(container.textContent).toContain("Routing Bravo");
const selects = Array.from(container.querySelectorAll("select")) as HTMLSelectElement[];
// one per routing combo (2) — the shared ComboCompressionModeSelect renders exactly
// the 6-option Default/Off/Lite/Standard/Aggressive/Ultra set.
const modeSelects = selects.filter((s) => {
const values = Array.from(s.options).map((o) => o.value);
return values.join(",") === ",off,lite,standard,aggressive,ultra";
});
expect(modeSelects).toHaveLength(2);
expect(modeSelects[0].value).toBe("lite");
expect(modeSelects[1].value).toBe("");
});
it("changing the routing combo's selector PUTs /api/combos/{id} independently of the assignment checkbox", async () => {
const calls = setupFetchMock();
const container = await render();
const selects = Array.from(container.querySelectorAll("select")) as HTMLSelectElement[];
const modeSelects = selects.filter((s) => {
const values = Array.from(s.options).map((o) => o.value);
return values.join(",") === ",off,lite,standard,aggressive,ultra";
});
await act(async () => {
modeSelects[0].value = "ultra";
modeSelects[0].dispatchEvent(new Event("change", { bubbles: true }));
});
await flush();
const putCalls = calls.filter(
(c) => c.url === "/api/combos/rc1" && c.init?.method === "PUT"
);
expect(putCalls).toHaveLength(1);
const body = JSON.parse(putCalls[0].init?.body as string);
expect(body).toEqual({ config: { compressionMode: "ultra" } });
// The assignment checkbox for the same routing combo still toggles independently.
const checkbox = container.querySelector(
'input[type="checkbox"]'
) as HTMLInputElement | null;
expect(checkbox).toBeTruthy();
const before = checkbox!.checked;
await act(async () => {
checkbox!.click();
});
await flush();
expect(checkbox!.checked).toBe(!before);
// The unrelated outputMode/outputModeIntensity fields were not touched by the change above.
const outputModeIntensitySelect = selects.find((s) =>
Array.from(s.options).some((o) => o.value === "full")
) as HTMLSelectElement;
expect(outputModeIntensitySelect.value).toBe("full");
});
});