Turn any LLM into an interactive UI. One line of Python — no React, no frontend setup.
中文 · 5-min Quickstart · AIP Design · Integration · Examples · Docs
pip install tilo openaiimport tilo
spec = tilo.generate(
"Review this SaaS contract for payment, liability, and IP risks.",
model="gpt-4o", # or claude-opus-4-8 — provider auto-detected
)
tilo.view(spec) # opens in your browser. that's it.The LLM doesn't return a wall of text — it generates a structured, interactive surface: a risk chart, a before/after diff, a checklist, a human-approval gate, and a memory card, organised into tabs. Rendered with zero frontend setup.
No API key? Run
tilo demoto open this exact surface from a sample spec. Want to tinker?tilo servethen openhttp://localhost:8000/playground— a live editor: paste any spec, see it render instantly.
Stronger models make text answers better — but a wall of text is still a wall of text. The bottleneck isn't what the model knows, it's how the user acts on it. Tilo turns model output into something a human can click, edit, approve, and reject — and turns those actions back into structured signal the model can learn from.
Three things stay valuable no matter how good the model gets:
- Structured UI beats prose for decisions. A risk chart + approval gate beats three paragraphs, every time.
- Human confirmation is infrastructure, not a feature. Stronger models take higher-stakes actions → you need more structured gates, not fewer.
- Confirmed memory, not auto-memory. A confident model that auto-saves a wrong preference is dangerous. Tilo proposes; the human confirms.
A real Tilo run — agent recalls memory, plans, calls tools, generates an interactive artifact, and hands it back as live, clickable UI. Zero LLM key required for this demo.
canvas-sf-trip.mp4
Plan a SF weekend — runs entirely from a baked-in fixture. Two more demos (PR Review · Sales Briefing) further down. ↓
Already using OpenAI, Anthropic, or LangChain? One import.
# OpenAI
from tilo.adapters.openai import generate_aip_spec
spec = generate_aip_spec(OpenAI(), "Analyse Q3 pipeline", skill="sales_dashboard")
# Anthropic
from tilo.adapters.anthropic_sdk import generate_aip_spec
spec = generate_aip_spec(Anthropic(), "Review this PR", skill="code_review", document=diff)
# LangChain / LangGraph
from tilo.adapters.langchain import generate_aip_spec
spec = generate_aip_spec(ChatOpenAI(model="gpt-4o"), "Plan a trip to Tokyo")# Already on AG-UI / CopilotKit? Emit a Tilo surface into the stream:
from tilo.adapters.agui import tilo_spec_to_agui_events
events = tilo_spec_to_agui_events(spec)12 built-in skills (contract review, code review, incident response,
meeting summary, bug report, trip planning, …) shape the output for your
domain — or load your own skill.yaml. Bring your own LLM client with
AIPPromptBuilder, or convert an existing response you already have.
Tilo is a library, not a framework. You already have an agent (your own loop, LangGraph, CrewAI, whatever). Tilo does one thing: it turns a model's output into a structured, interactive surface — a declarative spec of typed blocks (chart, diff, table, checklist, confirmation, memory card) that renders anywhere, from one function call, with no frontend.
spec = tilo.generate("Review this contract", model="gpt-4o") # → a spec (data)
tilo.view(spec) # → rendered, no ReactThe lean install is just that: pip install tilo pulls only pydantic +
PyYAML. The full server runtime (sessions, memory, the ROAM loop) is an
opt-in tilo[server] extra — most people never need it.
Tilo doesn't replace your tools, orchestrator, or agent-UI transport — it slots in as the layer that produces the structured view.
| Layer | Owned by | Tilo's relationship |
|---|---|---|
| Tool calling | MCP | mcp_* adapter → render tool results as a surface |
| Orchestration | LangChain / CrewAI / LangGraph | generate_aip_spec(llm, …) from your chain |
| Agent ↔ app transport | AG-UI (CopilotKit) | interop adapter — emit a Tilo surface as an AG-UI event |
| Agent-to-agent | A2A / ACP | a2a_* / acp_* adapters → render results |
AG-UI is an event-streaming protocol that carries an agent's live activity into a chat/copilot UI (via CopilotKit). Tilo produces a declarative artifact you can render with or without a frontend. They compose: let your AG-UI agent emit a Tilo surface as generative UI.
| AG-UI | Tilo | |
|---|---|---|
| Shape | Event stream (process) | Declarative spec (one artifact) |
| Frontend | Needs a client runtime (CopilotKit) | Renders anywhere — browser / Jupyter / HTML / React |
| UI form | Chat / copilot | Structured surface (report, review, dashboard) |
| Integration cost | Adopt the protocol + wire a frontend | pip install tilo + one function |
from tilo.adapters.agui import tilo_spec_to_agui_events
events = tilo_spec_to_agui_events(spec) # stream into a CopilotKit app| Goal | Install | What you get |
|---|---|---|
| See a surface now | pip install tilo → tilo demo |
A sample surface opens in your browser (lean: pydantic + PyYAML only) |
| Generate from your LLM | pip install "tilo[openai]" → tilo generate "…" |
Your prompt → a rendered surface |
| Full ROAM loop + frontend | pip install "tilo[server]" (or git clone … && make dev) |
Backend :8000 + reference UI :4001 |
The default pip install tilo is a lightweight library. The FastAPI server,
database, and /playground only come with the tilo[server] extra. The full
stack adds sessions, runs, confirmed memory, and the reference React Canvas:
git clone https://github.com/adam2go/tilo-framework.git
cd tilo-framework && make install && make devhttp://localhost:8000/playground— paste a spec, see it render livehttp://localhost:4001/canvas— 3D Agent Canvas: watch the agent stream a live tracehttp://localhost:4001/demo— classic scenario picker
Zero-config. Everything works without an API key in deterministic mode. Add
LLM_ENABLED=true+ a provider key in.envfor live LLM generation.
The library is three small layers — a spec, adapters that produce it, and renderers that draw it. Everything else (the full runtime) is optional.
~20 primitive block types (like HTML tags: markdown, table, chart, diff, form, card, …) plus an open extension mechanism. Any string is a valid block type — unknown types render with a generic JSON fallback. The spec is plain JSON, so it travels and renders anywhere.
Each adapter offers two modes: generate a full surface (the LLM authors chart/diff/confirmation/memory blocks), or convert a response you already have (simpler text/metric/table/tool blocks).
| Adapter | Status | Import |
|---|---|---|
| OpenAI | ✅ | from tilo.adapters.openai import generate_aip_spec |
| Anthropic | ✅ | from tilo.adapters.anthropic_sdk import generate_aip_spec |
| LangChain | ✅ | from tilo.adapters.langchain import generate_aip_spec |
| MCP | ✅ | from tilo.adapters.mcp import mcp_content_to_blocks |
| A2A | ✅ | from tilo.adapters.a2a import a2a_task_to_spec |
| ACP | ✅ | from tilo.adapters.acp import acp_message_to_spec |
| AG-UI | ✅ | from tilo.adapters.agui import tilo_spec_to_agui_events (interop) |
Bring your own client with AIPPromptBuilder (works with any LLM), or just
call tilo.generate(goal, model=…) and let Tilo pick the provider.
Tilo Spec JSON → any frontend.
- Zero-setup:
tilo.view(spec)(browser),tilo.notebook(spec)(Jupyter),tilo.to_html(spec)(standalone HTML) — no Node, no build step. - Production React:
@adam2go/tilo-react—TiloRendererwith per-block overrides, orrenderArtifactBlockfor one-off blocks. - Your own SDK: the spec is plain JSON; build a renderer for Vue, Flutter, Web Components, or a terminal CLI.
Skills provide hints (recommended block types, view organization) to the LLM. The LLM has full autonomy to decide the final views, blocks, and layout. Skills are recommendations, not constraints.
| Scenario | What the agent does | Mode |
|---|---|---|
| PR Review 🔍 | Flags risky changes in a pull request, lists verification items, gates the merge with a confirmation | LLM |
| SF Trip |
Plans a 3-day weekend with timeline, hotels, packing checklist, budget — fully interactive | offline · zero-config |
| Sales Briefing 📊 | Surfaces pipeline metrics + recommended actions + a ready-to-send email behind a confirmation | LLM |
The SF Trip video is at the top of this page. The other two:
canvas-pr-review.mp4Auth-refactor PR · diffs + verification checklist + merge confirmation. 53s · HQ download (42 MB) |
canvas-sales-briefing.mp4Pipeline metrics + recommended actions + gated outbound email. 68s · HQ download (36 MB) |
See docs/demos/ for the full goal text and reproduction steps.
Most people use Tilo as a library: generate → spec → render, done. But if you
don't already have an agent backend and want the whole loop, tilo[server]
adds a FastAPI runtime that closes the two-way loop — not just agent → UI,
but UI → agent:
1. Agent emits a spec → blocks + views, declarative JSON
2. Renderer paints the UI → @adam2go/tilo-react / your own
3. User clicks / edits / confirms
4. Backend records the action → UIInteractionEvent + observation
5. Next agent turn reasons over what the user actually did, not pixels
Two design choices keep it safe — and these are the parts worth borrowing even if you don't use the server:
- Confirmed memory, not automatic memory. The agent proposes what it
learned (
memory_card); the user decides what sticks. - Backend-owned action semantics. The frontend renders intent; the backend
decides what actually happens — high-risk actions stay gated behind a
confirmationblock.
This is the heavier path. If you already have your own agent, you don't need it
— just pip install tilo and use generate / view.
| Mode | When to use | What you touch |
|---|---|---|
| One-liner | Just want a surface | tilo.generate(goal, model="gpt-4o") → tilo.view(spec) |
| Your LLM client | Already have OpenAI/Anthropic/LangChain | generate_aip_spec(client, goal, skill=…) |
| Bring-your-own LLM | Custom client or provider | AIPPromptBuilder(goal).system_prompt() / .parse(out) |
| Convert a response | Already have LLM output | tilo_spec_from_completion(response) |
| Zero-setup render | No Node / build step | tilo.view / tilo.notebook / tilo.to_html |
| Production React | Have a React app | @adam2go/tilo-react — TiloRenderer, renderArtifactBlock |
| Backend sidecar | Full ROAM loop, your frontend | Call Tilo REST APIs |
| Skill author | Package a repeatable workflow | skill.yaml with block_hints + view_hints |
| Declarative app | Full agent workflow | app.yaml + interaction.policy.yaml |
Core APIs:
POST /api/conversations Create a session
POST /api/conversations/{id}/messages Send a message → Task → Run
GET /api/runs/{id}/trace Live trace stream
GET /api/artifacts?workspace_id=...&task_id=... Full artifact with views
POST /api/memories/{id}/confirm Confirm a memory candidate
See the 5-minute quickstart, examples/integrations/, docs/INTEGRATION_GUIDE.md and docs/AIP_DESIGN.md.
backend/ Python package `tilo` — FastAPI runtime, pip-installable
tilo/adapters/ MCP, LangChain, A2A, ACP protocol adapters
tilo/schemas/ AIP v1 spec: ~20 primitive block types + open extension
tilo/services/ Memory, Confirmation, Trace, Artifact, Skills
frontend/ @adam2go/tilo-react — Next.js reference UI, artifact-driven Canvas
skills/ Skill YAML definitions with block_hints + view_hints
examples/ Declarative agent apps and contract fixtures
docs/ Architecture, AIP design, integration guide, principles
evals/ Runtime quality checks and baseline metrics
v0.1 (current) — Complete working loop + AIP architecture.
- Task → Run → Trace → Artifact → Surface → Confirmation → Memory loop
- Three demo scenarios (PR Review, SF Trip, Sales Briefing)
- Agent Interaction Protocol (AIP) with ~20 primitive block types
- LLM-driven UI composition with skill hints
- MCP adapter —
mcp_content_to_blocks,mcp_tool_result_to_spec - LangChain adapter —
TiloCallbackHandler+langchain_result_to_spec - Three declarative example apps (contract-review, sales-followup, code-review)
- Alembic-managed schema migrations
-
pip install tilo+tilo serveCLI - Multi-turn conversation + LLM streaming with visible thinking
- Chart, diff, timeline, kanban, code, tool_preview, memory_card block rendering
- PyPI publication —
pip install tilois live - OpenAI adapter —
tilo_spec_from_completion()+generate_aip_spec() - Anthropic adapter —
tilo_spec_from_message()+generate_aip_spec() -
tilo.generate()— one-line LLM → full AIP spec, provider auto-detected -
AIPPromptBuilder— bring-your-own LLM client, 12 built-in skills + custom YAML - Zero-setup viewer —
tilo.view()/tilo.notebook()/tilo.to_html() -
tilo servewelcome page + live/playgroundspec editor -
@adam2go/tilo-reactnpm package published - A2A + ACP adapters —
a2a_task_to_spec()/acp_message_to_spec() - Skill marketplace + community renderer SDKs (Vue, Svelte)
Future — Multi-agent routing, real tool execution with confirmation gates, Slack / email channel adapters.
Tilo is early-stage and open source. Contributions are welcome.
Before contributing, please read:
AGENTS.md— Development rules for AI coding agentsCONTRIBUTING.mddocs/AIP_DESIGN.md— Agent Interaction Protocol design
The most important principle:
MCP is the Agent's hands. Tilo is the Agent's face and ears. Preserve the AIP loop: Goal → Spec → Interactive UI → Observation → Memory.
MIT License
