feat(web): add dialectic reasoning playground
Lets users fire one query at every Honcho reasoning level (minimal / low / medium / high / max) in parallel and compare answers + latency side-by-side in a 5-column grid. - New route: /workspaces/:ws/peers/:peer/playground - New component: DialecticPlayground with per-column state, level checkboxes to skip expensive levels, and a single Run button that fans out via Promise.all - Extends useChat() to accept an optional reasoning_level parameter (was hardcoded to "low") - Adds a Playground button next to Chat on the peer detail page - Tests cover the parallel fan-out (peak concurrency = 5, no serial awaiting) and per-column latency measurement - Screenshot Playwright spec (idle / mid-flight / settled) drives the images in docs/screenshots/ — regen with PLAYWRIGHT_BASE_URL=... pnpm exec playwright test e2e/playground.screenshots.spec.ts
This commit is contained in:
committed by
Offending Commit
parent
6960bf4ffe
commit
2340e65028
52
packages/web/src/test/playground.test.tsx
Normal file
52
packages/web/src/test/playground.test.tsx
Normal file
@@ -0,0 +1,52 @@
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { REASONING_LEVELS, type ReasoningLevel } from "@/api/queries";
|
||||
import {
|
||||
buildInitialColumns,
|
||||
fanoutQuery,
|
||||
latencyMs,
|
||||
} from "@/components/playground/DialecticPlayground";
|
||||
|
||||
describe("fanoutQuery", () => {
|
||||
it("fires every selected level concurrently rather than sequentially", async () => {
|
||||
const inFlight = new Set<ReasoningLevel>();
|
||||
let peakConcurrency = 0;
|
||||
const released: Array<() => void> = [];
|
||||
|
||||
const runOne = vi.fn(async (level: ReasoningLevel) => {
|
||||
inFlight.add(level);
|
||||
peakConcurrency = Math.max(peakConcurrency, inFlight.size);
|
||||
await new Promise<void>((resolve) => released.push(resolve));
|
||||
inFlight.delete(level);
|
||||
return { ok: true as const, content: `answer-${level}` };
|
||||
});
|
||||
|
||||
let nowVal = 0;
|
||||
const fanout = fanoutQuery(REASONING_LEVELS, "ping", {
|
||||
now: () => nowVal++,
|
||||
runOne,
|
||||
onStart: () => {},
|
||||
onEnd: () => {},
|
||||
});
|
||||
|
||||
await Promise.resolve();
|
||||
await Promise.resolve();
|
||||
|
||||
expect(peakConcurrency).toBe(REASONING_LEVELS.length);
|
||||
|
||||
for (const release of released) release();
|
||||
await fanout;
|
||||
|
||||
expect(runOne).toHaveBeenCalledTimes(REASONING_LEVELS.length);
|
||||
});
|
||||
});
|
||||
|
||||
describe("latencyMs", () => {
|
||||
it("measures the gap between request start and response end per column", () => {
|
||||
const columns = buildInitialColumns();
|
||||
columns.medium.status = "success";
|
||||
columns.medium.startedAt = 1000;
|
||||
columns.medium.endedAt = 1842;
|
||||
|
||||
expect(latencyMs(columns.medium)).toBe(842);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user