Playground Studio
Feature: Playground Studio — unified AI testing workspace for
/dashboard/playground. Plans:17-playground-studio-redesign.plan.md+_orchestration/master-plan-group-C.mdStatus: Released in v3.8.6
Overview
Section titled “Overview”Playground Studio transforms /dashboard/playground from a simple Monaco-based editor into
a full-featured testing workspace. It replaces the legacy page.tsx with a PlaygroundStudio
shell that renders four tabs and a shared config pane.
┌ Playground ──────────────────────────────────────────────────────────┐│ [💬 Chat] [⚖ Compare] [{} API] [🔧 Build] 142↑ 38↓ · $0.002 </>│├──────────────────────────────────────────┬───────────────────────────┤│ {active tab content} │ ─ Config ││ │ Endpoint [chat ∨] ││ │ Model [gpt-5.4 ∨] ││ │ System [textarea] ││ │ Temp ▕▕▔▔ 0.7 ││ │ Presets [▾ load][save] ││ │ [✨ Improve prompt] │└──────────────────────────────────────────┴───────────────────────────┘Chat Tab
Section titled “Chat Tab”Evolves ChatPlayground.tsx into a multi-turn streaming workbench:
- Full markdown rendering via
MarkdownMessage.tsx(code blocks, tables, lists, links). - System prompt sourced from the shared Config pane.
- Token/cost per message (prompt + completion tokens).
- Regenerate last response.
- Sends to
POST /v1/chat/completionswith SSE streaming.
Compare Tab
Section titled “Compare Tab”The key differentiator for a proxy: run 1 prompt across up to 4 models in parallel.
- Up to 4 columns, each independently streaming from
/v1/chat/completions. + Add modelbutton (Cmd+K shortcut) to add columns.Run all ▶triggers all streams simultaneously viaPromise.all+ per-columnAbortController.- Global Cancel all aborts every in-flight stream.
- Per-column
ProviderMetricsshows TTFT, TPS, tokens, and estimated cost in real time. - Metrics labelled “client-side estimate” (D12) — measured from first SSE chunk.
API Tab
Section titled “API Tab”Preserves 100% of the original Monaco editor for power users (D14):
- 10 endpoints: chat completions, completions, embeddings, images, audio, speech, transcriptions, moderations, rerank, search.
- Multimodal file upload.
- SSE streaming with real-time output.
- Wrapped as
ApiTab.tsx(lazy-loaded,ssr: false).
Build Tab
Section titled “Build Tab”Tools/function calling and structured output UI:
ToolsBuilder.tsx— add/edit/removetools[]with JSON schema editor per tool. Validates parameters viaToolDefinitionSchema(Zod).StructuredOutputEditor.tsx— toggle JSON mode + JSON schema editor. Validates response against schema viaStructuredOutputSchema(Zod).- Sends request to
/v1/chat/completionswithtools[]and/orresponse_format.
Config Pane (Shared)
Section titled “Config Pane (Shared)”StudioConfigPane.tsx — always visible, collapsible.
| Field | Component | Notes |
|---|---|---|
| Endpoint | <select> |
10 options matching PlaygroundEndpoint |
| Model | <input> |
free text, e.g. openai/gpt-4o |
| System prompt | <textarea> |
fed into all tabs |
| Parameters | ParamSliders |
temperature, max_tokens, top_p, presence/frequency penalty, seed, stop |
| Presets | PresetPicker |
load/save named config snapshots (persisted in DB) |
| Improve prompt | ImprovePromptButton |
opens quota-warning modal, calls /api/playground/improve-prompt |
State is lifted to PlaygroundStudio.tsx and passed down to all tabs. Switching tabs
preserves config state.
Top Bar
Section titled “Top Bar”StudioTopBar.tsx:
- Tab switcher (role=“tablist”).
TokenCostCounter— live token (↑/↓) and estimated cost display.- Export code button (
</>) — opensExportCodeModal.
Export Code Modal
Section titled “Export Code Modal”ExportCodeModal.tsx uses codeExport.ts to generate curl / Python / TypeScript snippets
from the current PlaygroundState. API key placeholder is always $OMNIROUTE_API_KEY (D11).
Prompt Improver
Section titled “Prompt Improver”ImprovePromptButton.tsx → useImprovePrompt.ts → POST /api/playground/improve-prompt:
- Modal warns “will consume quota”.
- On confirm, sends
{ system, prompt, model, tone }to the route. - Route calls
/v1/chat/completionsinternally withpromptImprover.META_SYSTEM_PROMPT. - Returns
{ improvedSystem?, improvedPrompt?, tokensIn, tokensOut }. - UI patches the Config pane system prompt and the Chat tab user prompt.
Presets
Section titled “Presets”PresetPicker.tsx → usePresets.ts → /api/playground/presets/*:
- Stored in
playground_presetsSQLite table (migration084_playground_presets.sql). - Each preset stores:
name,endpoint,model,system,params_json,created_at. - CRUD:
GETlist,POSTcreate,GET /:id,PUT /:id,DELETE /:id.
Stream Metrics
Section titled “Stream Metrics”useStreamMetrics.ts + streamMetrics.ts (pure function):
start()— records request start time.onFirstChunk()— records TTFT.onChunk(n)— accumulates completion token count.finish(usage?)— computes final metrics:ttftMs,totalMs,tps,tokensIn,tokensOut,costUsd.- Pricing from static table in
src/lib/playground/types.ts(labelled “estimated” — D13).
Backend Routes
Section titled “Backend Routes”| Method | Path | Handler |
|---|---|---|
POST |
/api/playground/improve-prompt |
Zod-validates ImprovePromptRequestSchema; calls /v1/chat/completions with meta-prompt |
GET |
/api/playground/presets |
Returns { presets: PlaygroundPresetListItem[] } |
POST |
/api/playground/presets |
Creates preset; validates PlaygroundPresetCreateSchema |
GET |
/api/playground/presets/:id |
Returns one preset or 404 |
PUT |
/api/playground/presets/:id |
Partial update |
DELETE |
/api/playground/presets/:id |
204 |
Auth: optional (REQUIRE_API_KEY). Errors via buildErrorBody() (Hard Rule #12).
Key Files
Section titled “Key Files”| Path | Purpose |
|---|---|
src/app/(dashboard)/dashboard/playground/PlaygroundStudio.tsx |
Shell component, tab orchestrator |
src/app/(dashboard)/dashboard/playground/components/StudioTopBar.tsx |
Tabs + counter + export button |
src/app/(dashboard)/dashboard/playground/components/StudioConfigPane.tsx |
Shared config panel |
src/app/(dashboard)/dashboard/playground/components/tabs/ChatTab.tsx |
Chat workbench |
src/app/(dashboard)/dashboard/playground/components/tabs/CompareTab.tsx |
Multi-model compare |
src/app/(dashboard)/dashboard/playground/components/tabs/ApiTab.tsx |
Monaco editor (preserved) |
src/app/(dashboard)/dashboard/playground/components/tabs/BuildTab.tsx |
Tools + structured output |
src/app/(dashboard)/dashboard/playground/components/ExportCodeModal.tsx |
Code export modal |
src/app/(dashboard)/dashboard/playground/components/CompareColumn.tsx |
Single compare column |
src/app/(dashboard)/dashboard/playground/components/ProviderMetrics.tsx |
TTFT/TPS display |
src/app/(dashboard)/dashboard/playground/hooks/useStreamMetrics.ts |
Client-side metric hook |
src/app/(dashboard)/dashboard/playground/hooks/usePresets.ts |
Presets CRUD hook |
src/app/(dashboard)/dashboard/playground/hooks/useImprovePrompt.ts |
Improve-prompt hook |
src/lib/playground/codeExport.ts |
curl/Python/TS generator (shared with Search Tools) |
src/lib/playground/promptImprover.ts |
Meta-prompt builder |
src/lib/playground/streamMetrics.ts |
Pure metrics computation |
src/lib/db/playgroundPresets.ts |
DB module (CRUD) |
src/app/api/playground/improve-prompt/route.ts |
Improve-prompt REST route |
src/app/api/playground/presets/route.ts |
Presets list + create |
src/app/api/playground/presets/[id]/route.ts |
Presets get/update/delete |
src/lib/db/migrations/084_playground_presets.sql |
DB migration |
Troubleshooting
Section titled “Troubleshooting”| Symptom | Cause | Fix |
|---|---|---|
| Monaco editor not rendering in API tab | SSR loaded Monaco | Verify ApiTab uses dynamic(..., { ssr: false }) |
| Compare streams fire sequentially | Wrong Promise.all usage |
All stream starts must be dispatched in one Promise.all call |
Metrics show null TTFT |
First chunk handler not wired | Check useStreamMetrics.onFirstChunk() is called in the SSE reader loop |
| Preset not persisting | DB migration not run | Run npm run db:migrate or restart the server (migration auto-runs on startup) |
| Improve prompt returns 502 | Model not set in Config | User must enter a model name in the Config pane before improving |
Export code shows MISSING_API_KEY |
Placeholder not inserted | codeExport.ts always uses API_KEY_PLACEHOLDER = "$OMNIROUTE_API_KEY" |
References
Section titled “References”- Master plan:
_tasks/features-v3.8.6/refactorpages/_orchestration/master-plan-group-C.md - Feature plan:
_tasks/features-v3.8.6/refactorpages/17-playground-studio-redesign.plan.md - Code export:
src/lib/playground/codeExport.ts - Prompt improver:
src/lib/playground/promptImprover.ts - Search Tools Studio:
docs/frameworks/SEARCH_TOOLS_STUDIO.md
HagiCode
HagiCode is an agentic coding workspace. Rich, convenient UI interactions make planning, agent execution, and project work approachable and easy to navigate, creating an engaging development experience.
Explore an engaging development experience through rich, convenient UI interactions.
- SmartOpenSpec workflows turn intent into an executable path, from proposal to design, tasks, and a shipped change.
- EfficientMulti-agent execution keeps research, implementation, and review moving in parallel, across several repositories and the Agent CLIs you already use.
- InterestingRich, convenient UI interactions make planning, agent execution, and project work approachable and easy to navigate.
Download for Windows
Share HagiCode
Turn ideas into shipped software.
HagiCode is an agentic coding workspace. OpenSpec workflows plan the change, multi-agent execution carries it out, and Hero Scenario makes the long run visible, so ideas become shipped software.
- Smart
- OpenSpec workflows turn intent into a path from proposal to shipped change.
- Efficient
- Multi-agent execution runs research, build, and review in parallel.
- Fun
- Rich UI interactions make planning, agent work, and projects easier to follow.
© 2026 HagiCode | Powered By hagilight-starlight@0.21.0 | Branch: main | Commit: de60bcd | Built:

