Skip to content

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.

Terminal window
$ 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 browser

The playground requires your app to register an @app.on_inbound handler — that’s the entry point a user message hits.

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.

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.

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).

Terminal window
$ export ANTHROPIC_API_KEY=sk-ant-…
$ linkworld chat --live

If ANTHROPIC_API_KEY is missing, agent calls return a placeholder string so the playground still runs (with a warning printed at startup).

The playground HTTP server exposes:

MethodPathPurpose
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).

  • 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.

  • 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 with linkworld deploy.
  • HTTP routes (@app.http_route): not exposed by the playground. The chat surface only drives on_inbound.
  • Schedules: not triggerable from the chat UI — use linkworld eval with when.type: schedule to exercise those.