feat(compression): playground fidelity-gate toggle + lane rejection display

This commit is contained in:
Diego Rodrigues de Sa e Souza
2026-06-26 21:22:55 -03:00
committed by Diego Rodrigues de Sa e Souza
parent 8df1aff301
commit ce802fa296
5 changed files with 39 additions and 8 deletions

View File

@@ -7,6 +7,8 @@ import { PlaygroundInput, LANE_ENGINES } from "./PlaygroundInput";
export interface PlayViewProps { text: string; onText: (t: string) => void; laneEngines?: readonly string[]; }
function laneStatus(l: Lane): string {
const rejected = l.run?.steps?.find((s) => s.rejected);
if (rejected) return `⚠ rejeitado: ${rejected.rejectReason ?? ""}`;
return l.error ? "⚠ erro" : l.run ? `${l.run.savingsPercent}%` : "—";
}
@@ -31,15 +33,16 @@ function LaneList({ lanes, onSelect }: { lanes: Lane[]; onSelect: (e: string) =>
export function PlayView({ text, onText, laneEngines = LANE_ENGINES }: PlayViewProps) {
const [active, setActive] = useState<string[]>(["rtk", "caveman"]);
const [selectedLane, setSelectedLane] = useState<string | null>(null);
const [fidelityGate, setFidelityGate] = useState(false);
const { batch, loading, run } = usePreviewCompression();
const messages = [{ role: "user", content: text }];
const toggle = (e: string) => setActive((a) => (a.includes(e) ? a.filter((x) => x !== e) : [...a, e]));
const onRun = () => run({ messages, laneEngines: [...laneEngines], activeEngines: orderByStack(active, laneEngines) });
const onRun = () => run({ messages, laneEngines: [...laneEngines], activeEngines: orderByStack(active, laneEngines), fidelityGate });
const activeDiff = resolveActiveDiff(batch, selectedLane);
return (
<div className="flex h-full gap-3">
<div className="w-[260px] shrink-0">
<PlaygroundInput text={text} onText={onText} active={active} onToggleActive={toggle} onRun={onRun} loading={loading} />
<PlaygroundInput text={text} onText={onText} active={active} onToggleActive={toggle} onRun={onRun} loading={loading} fidelityGate={fidelityGate} onToggleFidelity={() => setFidelityGate((v) => !v)} />
</div>
<div className="flex min-w-0 flex-1 flex-col gap-3 overflow-auto">
{batch?.combined && (

View File

@@ -1,7 +1,7 @@
"use client";
export const LANE_ENGINES = ["session-dedup", "ccr", "lite", "rtk", "headroom", "caveman", "aggressive", "ultra"] as const;
export interface PlaygroundInputProps { text: string; onText: (t: string) => void; active: string[]; onToggleActive: (engine: string) => void; onRun: () => void; loading: boolean; }
export function PlaygroundInput({ text, onText, active, onToggleActive, onRun, loading }: PlaygroundInputProps) {
export interface PlaygroundInputProps { text: string; onText: (t: string) => void; active: string[]; onToggleActive: (engine: string) => void; onRun: () => void; loading: boolean; fidelityGate: boolean; onToggleFidelity: () => void; }
export function PlaygroundInput({ text, onText, active, onToggleActive, onRun, loading, fidelityGate, onToggleFidelity }: PlaygroundInputProps) {
return (
<div className="flex flex-col gap-3">
<textarea data-testid="play-input" className="min-h-[160px] w-full rounded border p-2 font-mono text-xs" value={text} onChange={(e) => onText(e.target.value)} placeholder="Cole prompt / tool-output / contexto..." />
@@ -10,6 +10,10 @@ export function PlaygroundInput({ text, onText, active, onToggleActive, onRun, l
{LANE_ENGINES.map((e) => (<label key={e} className="flex items-center gap-2 text-sm"><input type="checkbox" checked={active.includes(e)} onChange={() => onToggleActive(e)} />{e}</label>))}
<label className="flex items-center gap-2 text-sm opacity-50"><input type="checkbox" disabled /> llmlingua <span className="text-[10px]">(requer modelo ONNX)</span></label>
</div>
<label className="flex items-center gap-2 text-sm">
<input type="checkbox" data-testid="fidelity-toggle" checked={fidelityGate} onChange={onToggleFidelity} />
Verificar fidelidade (rejeitar camada que corromper)
</label>
<button data-testid="play-run" className="rounded bg-blue-500/30 py-2 font-semibold" onClick={onRun} disabled={loading}>{loading ? "Rodando..." : "▶ Run"}</button>
</div>
);

View File

@@ -21,6 +21,8 @@ export interface CompressionEngineStep {
techniquesUsed: string[];
rulesApplied?: string[];
durationMs?: number;
rejected?: boolean;
rejectReason?: string;
}
// ── Diff ─────────────────────────────────────────────────────────────────

View File

@@ -4,7 +4,7 @@ import { previewToRunModel, type CompressionRunModel, type PreviewResponse } fro
export interface PreviewMessage { role: string; content: unknown; }
export interface Lane { engine: string; run: CompressionRunModel | null; error: string | null; }
export interface PreviewBatch { lanes: Lane[]; combined: CompressionRunModel | null; diff: PreviewResponse["diff"] | null; }
export interface RunPreviewArgs { messages: PreviewMessage[]; laneEngines: string[]; activeEngines: string[]; language?: string; }
export interface RunPreviewArgs { messages: PreviewMessage[]; laneEngines: string[]; activeEngines: string[]; language?: string; fidelityGate?: boolean; }
async function postPreview(payload: Record<string, unknown>): Promise<PreviewResponse> {
const res = await fetch("/api/compression/preview", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(payload) });
const data = await res.json();
@@ -12,17 +12,17 @@ async function postPreview(payload: Record<string, unknown>): Promise<PreviewRes
return data as PreviewResponse;
}
export async function runPreviewBatch(args: RunPreviewArgs): Promise<PreviewBatch> {
const { messages, laneEngines, activeEngines } = args;
const { messages, laneEngines, activeEngines, fidelityGate } = args;
const lanes: Lane[] = await Promise.all(
laneEngines.map(async (engine): Promise<Lane> => {
try { const res = await postPreview({ messages, engineId: engine }); return { engine, run: previewToRunModel(res, engine), error: null }; }
try { const res = await postPreview({ messages, engineId: engine, ...(fidelityGate ? { fidelityGate: { enabled: true } } : {}) }); return { engine, run: previewToRunModel(res, engine), error: null }; }
catch (e) { return { engine, run: null, error: e instanceof Error ? e.message : "error" }; }
})
);
let combined: CompressionRunModel | null = null;
let diff: PreviewResponse["diff"] | null = null;
if (activeEngines.length > 0) {
try { const res = await postPreview({ messages, pipeline: activeEngines }); combined = previewToRunModel(res, activeEngines.join(" → ")); diff = res.diff; }
try { const res = await postPreview({ messages, pipeline: activeEngines, ...(fidelityGate ? { fidelityGate: { enabled: true } } : {}) }); combined = previewToRunModel(res, activeEngines.join(" → ")); diff = res.diff; }
catch { combined = null; }
}
return { lanes, combined, diff };

View File

@@ -0,0 +1,22 @@
// @vitest-environment jsdom
import { describe, it, expect, vi, beforeEach } from "vitest";
import { runPreviewBatch } from "@/hooks/usePreviewCompression";
beforeEach(() => vi.restoreAllMocks());
describe("runPreviewBatch fidelityGate", () => {
it("includes fidelityGate:{enabled:true} in every preview payload when on", async () => {
const payloads: any[] = [];
vi.stubGlobal("fetch", vi.fn(async (_u: string, init: any) => {
payloads.push(JSON.parse(init.body));
return { ok: true, json: async () => ({
original: "o", compressed: "c", originalTokens: 5, compressedTokens: 5, savingsPct: 0,
mode: "stacked", durationMs: 1, engineBreakdown: [], diff: [], preservedBlocks: [], ruleRemovals: [],
}) } as any;
}));
await runPreviewBatch({
messages: [{ role: "user", content: "x" }],
laneEngines: ["rtk"], activeEngines: ["rtk"], fidelityGate: true,
});
expect(payloads.length).toBe(2); // 1 lane + 1 combined
expect(payloads.every((p) => p.fidelityGate?.enabled === true)).toBe(true);
});
});