From zero to a renderable AI-generated surface — no Docker, no setup beyond pip install.
- Python 3.11+
- Node.js 18+ (for the React renderer, optional)
pip install tilo
tilo init my-agent
cd my-agentThis creates:
my-agent/
├── .env # SQLite by default, no Docker needed
├── requirements.txt
├── hello.py # end-to-end demo script
├── openai_agent.py # LLM integration example
└── README.md
tilo serveExpected output:
▶ Tilo API → http://127.0.0.1:8000
Health → http://127.0.0.1:8000/api/health
API docs → http://127.0.0.1:8000/docs
Tilo uses SQLite by default. No database setup needed.
In a new terminal:
python hello.pyExpected output:
Session: <uuid>
Run status: completed
Artifact: Contract Review
Blocks: 5
· [heading] {'text': 'Contract Risk Analysis'}
· [markdown] {'content': 'Found 2 high-risk clauses...'}
· [metric] {'label': 'Risk Level', 'value': 'High'}
Render with @adam2go/tilo-react:
import { renderArtifactBlock } from '@adam2go/tilo-react'
· [card] ...
You've just run the full ROAM loop: Render → Observe → Act → Memorize.
npm install @adam2go/tilo-react recharts lucide-reactimport { TiloRenderer, createTiloClient, useTiloSurface } from "@adam2go/tilo-react";
const client = createTiloClient({ baseUrl: "http://localhost:8000" });
function AgentSurface({ runId }: { runId: string }) {
const { turns, loading } = useTiloSurface({ client, runId });
if (loading) return <div>Loading...</div>;
return (
<div>
{turns.map((turn) => (
<TiloRenderer
key={turn.id}
surface={turn.spec}
onAction={async (event) => {
await client.executeSurfaceAction({
surface: turn.spec,
actionId: event.action.id,
workspaceId: "my-workspace",
runId,
});
}}
/>
))}
</div>
);
}pip install openai
export OPENAI_API_KEY=sk-...from openai import OpenAI
from tilo.adapters.openai import tilo_spec_from_completion
client = OpenAI()
response = client.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": "Analyse this contract for payment risks."}],
)
spec = tilo_spec_from_completion(response, title="Contract Analysis")
# → spec is a Tilo AIP v1 dict, ready to renderpip install anthropic
export ANTHROPIC_API_KEY=sk-ant-...import anthropic
from tilo.adapters.anthropic_sdk import tilo_spec_from_message
client = anthropic.Anthropic()
response = client.messages.create(
model="claude-opus-4-8",
max_tokens=1024,
messages=[{"role": "user", "content": "Review this contract."}],
)
spec = tilo_spec_from_message(response, title="Contract Review")pip install langchain-openai langchain-corefrom langchain_openai import ChatOpenAI
from tilo.adapters.langchain import TiloCallbackHandler
handler = TiloCallbackHandler(title="My Agent")
llm = ChatOpenAI(model="gpt-4o")
llm.invoke("Analyse this contract.", config={"callbacks": [handler]})
spec = handler.to_spec()Your code LLM response Tilo adapter AIP spec
───────────── ───────────────── ───────────────── ──────────────────────
openai call → ChatCompletion → tilo_spec_from_ → {version, blocks, views}
anthropic call→ Message → completion() → ↓
langchain call→ AIMessage → tilo_spec_from_ → @adam2go/tilo-react
message() renderArtifactBlock(block)
| LLM output | Tilo block |
|---|---|
| Plain text | markdown |
JSON {"key": value} with ≤8 entries |
metric blocks |
| JSON array of objects | table |
| Tool call / tool_use | tool_preview |
examples/integrations/— full working examples for each LLM providerdocs/AIP_DESIGN.md— Agent Interaction Protocol specdocs/INTEGRATION_GUIDE.md— advanced integration modes- npm: @adam2go/tilo-react — React renderer
- PyPI: tilo — Python backend