linkworld chat — live playground
linkworld chat is the interactive sibling of linkworld eval. It
loads your main.py in-process, opens a browser at localhost:5175
with a chat surface, and routes every message you type through your
app’s on_inbound handler. Tool calls and agent calls are captured
into a side panel so you can see exactly what your handler did.
Quickstart
Section titled “Quickstart”$ linkworld chat # mock mode (no LLM cost)$ linkworld chat --live # use real Anthropic LLM (needs ANTHROPIC_API_KEY)$ linkworld chat --port 6000 # custom port$ linkworld chat --no-open # don't auto-open browserThe playground requires your app to register an @app.on_inbound
handler — that’s the entry point a user message hits.
What you see
Section titled “What you see”Two panes side by side:
- Chat (left): bubbles for what you sent and what your handler
produced. The “response” shown is the latest agent reply (
ctx.agent.ask) or the last successful tool result, whichever came last in the handler. - Trace (right): every
ctx.tools.call,ctx.agent.ask, every result, every error — in order, with timing and full payloads. One block per turn; scroll up to revisit earlier turns.
Errors (e.g. a missing tool mock or an exception in your handler) are shown as red bubbles in the chat with the exception type and message, and the underlying stack trace is captured into the trace panel.
Mock mode (default)
Section titled “Mock mode (default)”Tool responses and agent responses come from linkworld.dev.json
(same file linkworld dev uses for tools, with two extra top-level
keys for the chat playground):
{ "crm_lookup": { "ok": true, "result": { "customer": "Acme Inc" } }, "email_send": { "ok": false, "error": { "decision": "scope_denied", "message": "App didn't request mail.send.", "neededScopes": ["mail.send"] } }, "_agents": { "replier": { "text": "Thanks for your message — I'll look into that." }, "__any__": { "text": "(generic stub for any agent slug)" } }, "_secrets": { "OPENAI_KEY": "sk-test-not-real" }}The file is re-read on every message — edit it while the playground is running and the next turn picks up your changes. No restart.
When the handler calls a tool or agent that has no mock wired, the turn fails with a clear error pointing you at the missing key.
Live mode (--live)
Section titled “Live mode (--live)”With --live, every ctx.agent.ask(...) call hits the real Anthropic
API using ANTHROPIC_API_KEY from your environment. The system prompt
and model come from your manifest’s agents: declaration — the same
ones that run in production.
Tool calls remain mocked even in live mode — the playground exercises agent reasoning, not platform tools (those need real tenant credentials which aren’t available locally).
$ export ANTHROPIC_API_KEY=sk-ant-…$ linkworld chat --liveIf ANTHROPIC_API_KEY is missing, agent calls return a placeholder
string so the playground still runs (with a warning printed at startup).
Endpoints
Section titled “Endpoints”The playground HTTP server exposes:
| Method | Path | Purpose |
|---|---|---|
| GET | / | Single-page chat UI |
| GET | /api/health | {"ok": true} health check |
| POST | /api/chat | {"text": "..."} → dispatched response |
The /api/chat response shape:
{ "ok": true, "response": "the bubble text shown to the user", "elapsed_ms": 137.4, "trace": [ { "kind": "tool_call", "name": "crm_lookup", "detail": {...}, "ts_ms": 0.1 }, { "kind": "tool_response", "name": "crm_lookup", "detail": {...}, "ts_ms": 12.3 }, { "kind": "agent_call", "name": "replier", "detail": {...}, "ts_ms": 13.0 }, { "kind": "agent_response","name": "replier", "detail": {...}, "ts_ms": 130.2 } ], "error": null}You can hit this endpoint directly from your own scripts if you want to drive the playground programmatically (curl, integration tests).
When to use this vs. linkworld eval
Section titled “When to use this vs. linkworld eval”- eval — non-interactive regression testing. Fast, deterministic, CI-friendly. Right for asserting “given input X, the handler calls tool Y with these args.” See eval reference.
- chat — interactive sanity-checking and demos. Right for “let me poke at the agent and see what it actually does for free-form inputs.” Not for CI — there’s no pass/fail.
Both share the same in-process loader, so the same main.py runs in
both.
Limitations
Section titled “Limitations”- Multi-tenant scenarios: the playground hardcodes
tenant_id = "t-playground",user_id = "u-playground"— you can’t test tenant isolation here. - Cross-app calls (
ctx.apps.invoke): not supported in the playground — those need the platform’s grant/wire infrastructure. Use eval scenarios with mocked responses, or test on a real dev tenant withlinkworld deploy. - HTTP routes (
@app.http_route): not exposed by the playground. The chat surface only driveson_inbound. - Schedules: not triggerable from the chat UI — use
linkworld evalwithwhen.type: scheduleto exercise those.