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:
Ben Sheridan Edwards
2026-05-24 18:31:16 +01:00
committed by Offending Commit
parent 6960bf4ffe
commit 2340e65028
10 changed files with 644 additions and 16 deletions

View File

@@ -20,6 +20,7 @@ import { Route as WorkspacesWorkspaceIdPeersRouteImport } from './routes/workspa
import { Route as WorkspacesWorkspaceIdConclusionsRouteImport } from './routes/workspaces_.$workspaceId_.conclusions'
import { Route as WorkspacesWorkspaceIdSessionsSessionIdRouteImport } from './routes/workspaces_.$workspaceId_.sessions_.$sessionId'
import { Route as WorkspacesWorkspaceIdPeersPeerIdRouteImport } from './routes/workspaces_.$workspaceId_.peers_.$peerId'
import { Route as WorkspacesWorkspaceIdPeersPeerIdPlaygroundRouteImport } from './routes/workspaces_.$workspaceId_.peers_.$peerId_.playground'
import { Route as WorkspacesWorkspaceIdPeersPeerIdChatRouteImport } from './routes/workspaces_.$workspaceId_.peers_.$peerId_.chat'
const WorkspacesRoute = WorkspacesRouteImport.update({
@@ -83,6 +84,12 @@ const WorkspacesWorkspaceIdPeersPeerIdRoute =
path: '/workspaces/$workspaceId/peers/$peerId',
getParentRoute: () => rootRouteImport,
} as any)
const WorkspacesWorkspaceIdPeersPeerIdPlaygroundRoute =
WorkspacesWorkspaceIdPeersPeerIdPlaygroundRouteImport.update({
id: '/workspaces_/$workspaceId_/peers_/$peerId_/playground',
path: '/workspaces/$workspaceId/peers/$peerId/playground',
getParentRoute: () => rootRouteImport,
} as any)
const WorkspacesWorkspaceIdPeersPeerIdChatRoute =
WorkspacesWorkspaceIdPeersPeerIdChatRouteImport.update({
id: '/workspaces_/$workspaceId_/peers_/$peerId_/chat',
@@ -103,6 +110,7 @@ export interface FileRoutesByFullPath {
'/workspaces/$workspaceId/peers/$peerId': typeof WorkspacesWorkspaceIdPeersPeerIdRoute
'/workspaces/$workspaceId/sessions/$sessionId': typeof WorkspacesWorkspaceIdSessionsSessionIdRoute
'/workspaces/$workspaceId/peers/$peerId/chat': typeof WorkspacesWorkspaceIdPeersPeerIdChatRoute
'/workspaces/$workspaceId/peers/$peerId/playground': typeof WorkspacesWorkspaceIdPeersPeerIdPlaygroundRoute
}
export interface FileRoutesByTo {
'/': typeof IndexRoute
@@ -117,6 +125,7 @@ export interface FileRoutesByTo {
'/workspaces/$workspaceId/peers/$peerId': typeof WorkspacesWorkspaceIdPeersPeerIdRoute
'/workspaces/$workspaceId/sessions/$sessionId': typeof WorkspacesWorkspaceIdSessionsSessionIdRoute
'/workspaces/$workspaceId/peers/$peerId/chat': typeof WorkspacesWorkspaceIdPeersPeerIdChatRoute
'/workspaces/$workspaceId/peers/$peerId/playground': typeof WorkspacesWorkspaceIdPeersPeerIdPlaygroundRoute
}
export interface FileRoutesById {
__root__: typeof rootRouteImport
@@ -132,6 +141,7 @@ export interface FileRoutesById {
'/workspaces_/$workspaceId_/peers_/$peerId': typeof WorkspacesWorkspaceIdPeersPeerIdRoute
'/workspaces_/$workspaceId_/sessions_/$sessionId': typeof WorkspacesWorkspaceIdSessionsSessionIdRoute
'/workspaces_/$workspaceId_/peers_/$peerId_/chat': typeof WorkspacesWorkspaceIdPeersPeerIdChatRoute
'/workspaces_/$workspaceId_/peers_/$peerId_/playground': typeof WorkspacesWorkspaceIdPeersPeerIdPlaygroundRoute
}
export interface FileRouteTypes {
fileRoutesByFullPath: FileRoutesByFullPath
@@ -148,6 +158,7 @@ export interface FileRouteTypes {
| '/workspaces/$workspaceId/peers/$peerId'
| '/workspaces/$workspaceId/sessions/$sessionId'
| '/workspaces/$workspaceId/peers/$peerId/chat'
| '/workspaces/$workspaceId/peers/$peerId/playground'
fileRoutesByTo: FileRoutesByTo
to:
| '/'
@@ -162,6 +173,7 @@ export interface FileRouteTypes {
| '/workspaces/$workspaceId/peers/$peerId'
| '/workspaces/$workspaceId/sessions/$sessionId'
| '/workspaces/$workspaceId/peers/$peerId/chat'
| '/workspaces/$workspaceId/peers/$peerId/playground'
id:
| '__root__'
| '/'
@@ -176,6 +188,7 @@ export interface FileRouteTypes {
| '/workspaces_/$workspaceId_/peers_/$peerId'
| '/workspaces_/$workspaceId_/sessions_/$sessionId'
| '/workspaces_/$workspaceId_/peers_/$peerId_/chat'
| '/workspaces_/$workspaceId_/peers_/$peerId_/playground'
fileRoutesById: FileRoutesById
}
export interface RootRouteChildren {
@@ -191,6 +204,7 @@ export interface RootRouteChildren {
WorkspacesWorkspaceIdPeersPeerIdRoute: typeof WorkspacesWorkspaceIdPeersPeerIdRoute
WorkspacesWorkspaceIdSessionsSessionIdRoute: typeof WorkspacesWorkspaceIdSessionsSessionIdRoute
WorkspacesWorkspaceIdPeersPeerIdChatRoute: typeof WorkspacesWorkspaceIdPeersPeerIdChatRoute
WorkspacesWorkspaceIdPeersPeerIdPlaygroundRoute: typeof WorkspacesWorkspaceIdPeersPeerIdPlaygroundRoute
}
declare module '@tanstack/react-router' {
@@ -272,6 +286,13 @@ declare module '@tanstack/react-router' {
preLoaderRoute: typeof WorkspacesWorkspaceIdPeersPeerIdRouteImport
parentRoute: typeof rootRouteImport
}
'/workspaces_/$workspaceId_/peers_/$peerId_/playground': {
id: '/workspaces_/$workspaceId_/peers_/$peerId_/playground'
path: '/workspaces/$workspaceId/peers/$peerId/playground'
fullPath: '/workspaces/$workspaceId/peers/$peerId/playground'
preLoaderRoute: typeof WorkspacesWorkspaceIdPeersPeerIdPlaygroundRouteImport
parentRoute: typeof rootRouteImport
}
'/workspaces_/$workspaceId_/peers_/$peerId_/chat': {
id: '/workspaces_/$workspaceId_/peers_/$peerId_/chat'
path: '/workspaces/$workspaceId/peers/$peerId/chat'
@@ -297,6 +318,8 @@ const rootRouteChildren: RootRouteChildren = {
WorkspacesWorkspaceIdSessionsSessionIdRoute,
WorkspacesWorkspaceIdPeersPeerIdChatRoute:
WorkspacesWorkspaceIdPeersPeerIdChatRoute,
WorkspacesWorkspaceIdPeersPeerIdPlaygroundRoute:
WorkspacesWorkspaceIdPeersPeerIdPlaygroundRoute,
}
export const routeTree = rootRouteImport
._addFileChildren(rootRouteChildren)