Lattis
Run AI agents on a server, interact from anywhere.
I built an agent I liked (Binsmith) and wanted to use it from my laptop, my phone, wherever. Lattis is what emerged: a server that hosts agents with a TUI for terminals and a web UI for browsers.
I run it on a Linux box in my Tailscale network. Start a conversation on my laptop, pick it up on my phone, everything stays in sync.
Quick start
# Run the TUI (starts a local server automatically)
uvx lattis
# Or run the server explicitly, then connect from anywhere
uvx lattis server
Open http://localhost:8000 for the web UI, or run uvx lattis from another machine pointing at your server.
What you get
- Server + clients: FastAPI backend, Textual TUI, bundled web UI
- Persistent conversations: threads stored in SQLite, survive restarts
- Pluggable agents: different agents per thread, swap anytime
- Works anywhere: if it can hit HTTP, it can use your agents
The setup I use
┌─────────────────────────────────────────────────────────────┐
│ Linux server (Tailscale) │
│ lattis server │
│ :8000 │
└─────────────────────────────────────────────────────────────┘
▲ ▲ ▲
│ │ │
┌────┴────┐ ┌─────┴─────┐ ┌────┴────┐
│ laptop │ │ phone │ │ tablet │
│ TUI │ │ web UI │ │ web UI │
└─────────┘ └───────────┘ └─────────┘
Threads persist on the server. I can start something on my laptop, continue on my phone, come back to it days later.
Agents
Lattis discovers agents automatically from:
- Built-ins:
assistant,poetry(included with Lattis) - Entry points: any installed package that registers with
lattis.agents - Explicit specs:
module:attrpaths via--agentsorAGENT_PLUGINS
Binsmith is what started all this. Now it's just another plugin:
uv pip install binsmith
uvx lattis --agent binsmith
Building your own agent
The simplest case - just export a pydantic-ai Agent:
# my_agent.py
from pydantic_ai import Agent
plugin = Agent("google-gla:gemini-2.0-flash", system_prompt="You are helpful.")
uvx lattis --agents my_agent:plugin
For more control (custom dependencies, lifecycle hooks), use the plugin API:
from pydantic_ai import Agent
from lattis.plugins import AgentPlugin, AgentRunContext
def create_agent(model: str) -> Agent:
return Agent(model, system_prompt="...")
def create_deps(ctx: AgentRunContext):
# Access ctx.workspace, ctx.project_root, ctx.session_id, etc.
return MyDeps(...)
plugin = AgentPlugin(
id="my-agent",
name="My Agent",
create_agent=create_agent,
create_deps=create_deps,
)
Register via entry point in pyproject.toml:
[project.entry-points."lattis.agents"]
my-agent = "my_package:plugin"
CLI
lattis # TUI (default)
lattis tui # TUI explicitly
lattis server # API server + web UI
TUI options
--server <url> Connect to a remote server
--local Skip server discovery, run in-process
--agent <id> Default agent (local mode)
--agents <specs> Extra plugins to load (local mode)
The TUI auto-discovers a server on localhost:8000 if one is running for the same project. Use --server to point at a remote machine.
Server options
--host <host> Interface to bind (default: 127.0.0.1)
--port <port> Port (default: 8000)
--reload Auto-reload on code changes
--agent <id> Default agent
--agents <specs> Extra plugins to load
TUI commands
/help Show help
/threads List threads
/thread <id> Switch to thread (creates if needed)
/thread new [id] Create new thread
/thread delete <id> Delete thread
/clear Clear current thread
/agent Show current agent
/agent list [filter] List available agents
/agent set <id> Switch agent for this thread
/model Show current model
/model list [filter] List available models
/model set <name> Switch model
/quit Exit
Storage
.lattis/
lattis.db # SQLite - threads, messages, state
session_id # Persistent session identifier
workspace/ # Shared directory for agents that need it
Configuration
| Variable | Default | Description |
|---|---|---|
AGENT_DEFAULT |
assistant |
Default agent |
AGENT_PLUGINS |
Extra plugins (comma-separated module:attr) |
|
LATTIS_SERVER_URL |
Server URL for remote connections | |
LATTIS_PROJECT_ROOT |
cwd | Project root for storage |
LATTIS_DATA_DIR |
.lattis |
Data directory |
LATTIS_WORKSPACE_DIR |
Override workspace location |
Requirements
- Python 3.12+
- uv (recommended)
- An API key for at least one model provider (Gemini, Anthropic, OpenAI)
export GEMINI_API_KEY=... # Google
export ANTHROPIC_API_KEY=... # Anthropic
export OPENAI_API_KEY=... # OpenAI
Web UI development
The web UI is bundled from frontend/. To rebuild:
cd frontend
npm install
npm run build
Static files are served from lattis/web/static when present.
Metadata
Release files for lattis 0.6.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| lattis-0.6.1-py3-none-any.whl | Python 3 | none | any | Details for lattis-0.6.1-py3-none-any.whl |
Release files / lattis-0.6.1-py3-none-any.whl
| Download URL | lattis-0.6.1-py3-none-any.whl |
|---|---|
| Size | 212.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
75f13e3df1fa982f90b5fd867fefebb4535ffb8ca82b565bae7dec9784f04d9a
|
|
BLAKE2b-256 checksum How to use checksums |
db07d2b115b87d2a35172e0feec092aac04dd4defa0347325f66def511041d26
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.9.22 {"installer":{"name":"uv","version":"0.9.22","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Security for lattis 0.6.1
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.