# Letta + OpenWebUI Run local [Letta](https://www.letta.com/) agents in [OpenWebUI](https://openwebui.com/) through Letta App Server's OpenAI-compatible API. This is an integration example, not an OpenWebUI fork. It runs the official OpenWebUI image and keeps the two projects independently upgradeable. ```text Browser | v OpenWebUI container (localhost:3000) | | OpenAI Chat Completions or Responses API v Letta App Server (host.docker.internal:4500/v1) | v Local Letta agents, memory, and tools ``` ## What works - Local Letta agents appear in OpenWebUI's model selector. - Text and reasoning stream normally. - Both `/v1/chat/completions` and `/v1/responses` are available. - Agent state stays in Letta's local backend. - OpenWebUI settings and chat history persist in a Docker volume. - The OpenWebUI port binds to localhost by default. > [!IMPORTANT] > Tool execution can currently stall after OpenWebUI displays `Executing ...`. The same failure reproduces when calling Letta's OpenAI-compatible endpoints directly, so it is not only a UI problem. Track [letta-code#3647](https://github.com/letta-ai/letta-code/issues/3647). Use this example for chat and Responses API evaluation until that App Server tool-lifecycle issue is fixed. ## Prerequisites - macOS, Windows, or Linux - [Letta CLI](https://docs.letta.com/self-hosting) with at least one local agent - [Docker Desktop](https://docs.docker.com/desktop/) or Docker Engine with Compose v2 - macOS/Linux: Bash, `openssl`, `curl`, and Python 3 - Windows: PowerShell 7 Check the required commands: ```bash letta --version docker info docker compose version ``` ## Quickstart ### 1. Generate local secrets macOS or Linux: ```bash ./scripts/setup.sh ``` Windows PowerShell: ```powershell .\scripts\setup.ps1 ``` This creates two ignored files: - `.env` supplies OpenWebUI's API key and session secret. - `.secrets/app-server-token` is the matching bearer token used by Letta App Server. The script never overwrites an existing setup. ### 2. Start Letta App Server In one terminal on macOS or Linux: ```bash ./scripts/start-letta.sh ``` Or in Windows PowerShell: ```powershell .\scripts\start-letta.ps1 ``` The script runs: ```bash letta server \ --backend local \ --listen ws://0.0.0.0:4500 \ --openai-api \ --ws-auth capability-token \ --ws-token-file .secrets/app-server-token ``` The non-loopback listener allows the Docker container to reach the host. Bearer authentication protects the listener. ### 3. Start OpenWebUI In another terminal: ```bash docker compose up -d ``` Wait for both services and verify model discovery: macOS or Linux: ```bash ./scripts/verify.sh ``` Windows PowerShell: ```powershell .\scripts\verify.ps1 ``` Then open [http://localhost:3000](http://localhost:3000) and select a Letta agent from the model menu. ## Responses API mode Chat Completions is the default. OpenWebUI 0.11.0 also has experimental [Open Responses support](https://docs.openwebui.com/getting-started/quick-start/connect-a-provider/starting-with-open-responses/), and Letta App Server exposes `/v1/responses`. Choose one of these approaches: ### Use the OpenWebUI settings 1. Open **Admin Settings → Connections → OpenAI → Manage**. 2. Edit the Letta connection. 3. Change **API Type** from **Chat Completions** to **Responses**. 4. Save and start a new chat. ### Use the Compose override ```bash docker compose \ -f compose.yaml \ -f compose.responses.yaml \ up -d --force-recreate ``` Or use the shortcut: ```bash make responses ``` The override sets `ENABLE_PERSISTENT_CONFIG=False` so the environment remains authoritative. Admin UI configuration changes apply to the running process but do not survive restarts in that mode. Test Letta's Responses endpoint directly with an agent name or ID: ```bash ./scripts/test-responses.sh "Your agent name" ``` Keep OpenWebUI's `ENABLE_RESPONSES_API_STATEFUL` disabled unless the upstream server explicitly supports `previous_response_id` anchoring. Stateless mode is the safe default for this example. ## Authentication and exposure The generated configuration is for one person testing on one machine: ```dotenv OPENWEBUI_HOST=127.0.0.1 WEBUI_AUTH=False ENABLE_SIGNUP=False ``` OpenWebUI is therefore reachable only from the host and does not show a login screen. For a shared deployment, decide on authentication **before the first start**. OpenWebUI does not support changing an existing data volume between single-user and multi-account modes. At minimum: 1. Set `WEBUI_AUTH=True` in `.env` before `docker compose up`. 2. Configure the intended signup or SSO policy. 3. Put OpenWebUI behind HTTPS and a trusted reverse proxy. 4. Restrict network access to Letta App Server and protect its bearer token. 5. Do not expose App Server directly to untrusted clients; it can execute tools on its host. See the [OpenWebUI environment reference](https://docs.openwebui.com/reference/env-configuration/) before using this example beyond localhost. ## Common commands ```bash # Start or update OpenWebUI docker compose up -d # Follow OpenWebUI logs docker compose logs -f openwebui # Stop OpenWebUI and retain its data docker compose down # Start it again later docker compose up -d # Stop and delete OpenWebUI's data volume docker compose down -v ``` Stop Letta App Server with `Ctrl-C` in its terminal. On macOS and Linux, `make setup`, `make letta`, `make up`, `make responses`, `make verify`, `make logs`, and `make down` provide equivalent shortcuts. ## Troubleshooting ### No agents appear Verify Letta directly: ```bash curl -fsS \ -H "Authorization: Bearer $(<.secrets/app-server-token)" \ http://127.0.0.1:4500/v1/models ``` If the response contains no models, create or import a local Letta agent first. If it contains models but OpenWebUI does not, confirm that the container has: ```text OPENAI_API_BASE_URLS=http://host.docker.internal:4500/v1 ``` Do not use `localhost` there: inside the container, `localhost` refers to OpenWebUI itself. `compose.yaml` adds the Linux `host-gateway` mapping while remaining compatible with Docker Desktop. ### Connection refused - Keep `./scripts/start-letta.sh` running. - Confirm port 4500 is free before starting App Server. - Run `docker compose logs openwebui`. - Run `./scripts/verify.sh` to localize which side is unavailable. ### A tool call stays on “Executing” This is a known App Server OpenAI-bridge problem, not evidence that the command is still running. It reproduces through both Chat Completions and Responses mode. Cancel the generation and follow [letta-code#3647](https://github.com/letta-ai/letta-code/issues/3647). The Letta CLI remains the reliable surface for tool-heavy work. ### Regenerate secrets This removes only the example's local credentials, not Letta agent state or OpenWebUI's Docker volume: macOS or Linux: ```bash rm -rf .env .secrets ./scripts/setup.sh ``` Windows PowerShell: ```powershell Remove-Item -Recurse -Force .env, .secrets .\scripts\setup.ps1 ``` Recreate OpenWebUI afterward so it receives the new token: ```bash docker compose up -d --force-recreate ``` ## Data ownership - Letta local agent and conversation data use Letta's local backend storage. - OpenWebUI stores its own users, settings, and chat records in the `openwebui-data` Compose volume. - `.env` and `.secrets/` contain local credentials and are gitignored. - Removing the OpenWebUI volume does not remove Letta agents. ## Source documentation - [Letta: OpenAI-compatible App Server API](https://docs.letta.com/self-hosting#openai-compatible-api) - [Letta App Server](https://docs.letta.com/platform/app-server/) - [OpenWebUI: OpenAI-compatible providers](https://docs.openwebui.com/getting-started/quick-start/connect-a-provider/starting-with-openai-compatible/) - [OpenWebUI: Open Responses](https://docs.openwebui.com/getting-started/quick-start/connect-a-provider/starting-with-open-responses/) ## License The example configuration and scripts are available under the [MIT License](LICENSE). OpenWebUI and Letta are separate projects distributed under their own licenses.