Skip to content

Open WebUI

BMO can expose an OpenAI-compatible API that lets Open WebUI and other OpenAI-API-compatible clients drive BMO sessions.

Prerequisite: the headless HTTP/SSE hub (bmo serve / options.http_server) must be running and reachable.

  • GET /v1/models — Lists the BMO-backed model entries that Open WebUI can show in its model picker.
  • POST /v1/chat/completions — Accepts OpenAI-compatible chat requests and routes them into BMO’s session and agent loop.
[options.openai_compat]
enabled = true
tool_policy = "auto_approve"

Open WebUI uses the same routing, agent selection, and tool policy as the shared OpenAI-compatible API surface. This page stays focused on the client boundary: enable options.openai_compat, point Open WebUI at /v1, and use the shared posture and tracing surfaces below when you need to inspect live behavior.

If your Open WebUI-backed sessions need MCP-backed capabilities, configure those servers through the standard [mcp.*] settings and grant them through the agent policy you want BMO to use. Open WebUI does not require a separate MCP transport or client-specific tool configuration layer.

ValueDescription
chat_only (default)No tool_calls in the response; agent uses tools internally but returns plain text
auto_approveTool calls pass through to the client in OpenAI format

For Open WebUI deployments, use tool_policy = "auto_approve". Open WebUI can drive BMO’s OpenAI-compatible chat API, but it does not complete BMO’s native permission challenge flow. With chat_only, prompts that need web research or other tools can stall while waiting for a permission decision that the client never sends.

  1. Enable the compat API in bmo.toml

  2. Choose one of these BMO server setups:

    Local-only, unauthenticated BMO (development only)

    • Start BMO on loopback: bmo serve --addr 127.0.0.1:<port> --allow-no-auth
    • In Open WebUI, set API base URL to http://127.0.0.1:<port>/v1
    • The Open WebUI API key field can be any non-empty string in this mode; it is only satisfying the client UI, not authenticating BMO

    Authenticated BMO

    • Start BMO with a bearer token: bmo serve --addr 127.0.0.1:<port> --auth-token <token>
    • In Open WebUI, set API base URL to http://127.0.0.1:<port>/v1
    • Set the Open WebUI API key to the exact same <token> so it is sent as Authorization: Bearer <token>

Open WebUI then shows BMO as a model in the model picker.

Warning: The Open WebUI-side API key field is not, by itself, a BMO security boundary. Keep this integration on loopback by default, or put BMO behind a trusted reverse proxy / access-control layer when exposing it beyond the local machine.

Once BMO is serving, four operator surfaces answer the question “is OpenAI-compat enabled, what’s the resolved policy, and did Open WebUI hit BMO?” without scraping logs by hand.

Use this shortcut when the question starts from the Open WebUI side:

If you need to know…Use…
Is the feature enabled and how is it configured?bmo config show-openai-compat or the client-named alias bmo config show-open-webui
What is the live feature posture right now?/openai-compat (alias: /openwebui) or GET /v1/openai-compat/posture
Did this request hit BMO and how did it end?GET /v1/openai-compat/runs, GET /v1/openai-compat/runs/{id}/events, and the tracing recipes below
Was this a session-reuse, queued, resumed, or auth-boundary case?The openai_compat.* tracing recipes in agent tracing
Which surfaces are shared with the generic OpenAI-compatible API?The shared openai_compat posture family; Open WebUI does not have a separate HTTP, agent, or MCP status transport
RoutePurpose
POST /v1/chat/completionsOpenAI-compatible chat — accepts streaming and non-streaming requests
GET /v1/modelsAdvertises the BMO-backed model entries Open WebUI shows in its picker
GET /v1/openai-compat/postureSummarizes live feature posture, bounded per-route counts, and top client User-Agents
GET /v1/openai-compat/runsLists recent compat runs from the durable run ledger
GET /v1/openai-compat/runs/{id}/eventsStreams the per-run terminal-arm event timeline

The two /v1/openai-compat/runs* routes are the inspection counterparts of the chat surface: every request that reaches BMO records a row in the run ledger plus a terminal-arm event row, both queryable after the fact.

bmo config show-openai-compat prints the resolved [options.openai_compat] configuration after layered config merge — the same shape the running server sees. Sibling of bmo config show-ripple, show-file-tools, show-loop-detection, show-length-recovery, and show-spawning.

If you search by consumer name instead of protocol name, use the alias bmo config show-open-webui; it resolves to the exact same config-only output.

Output covers:

  • enabled flag
  • tool_policy (chat_only or auto_approve)
  • ambiguity_action (ask, research, code)
  • model_override_policy (reject or allow_ephemeral)
  • Advertised model IDs (the same set /v1/models returns)
  • Bind boundary status — whether --auth-token is set, whether the bind address is loopback, or (check at serve-time) if the runtime flags are not reachable from the config-show context
  • Operator warning that mirrors this page’s loopback warning when the effective bind appears unsafe
  • A pointer to the live posture family (/openai-compat, get_openai_compat_status, or GET /v1/openai-compat/posture) for route/client aggregates

Run bmo config show-openai-compat --help for the full flag list and example output.

From inside the TUI, /openai-compat (aliases: /openwebui) renders a read-only status report:

  • Enabled flag and resolved tool/ambiguity/model-override policies
  • Advertised model IDs
  • Run ledger size
  • Per-route request counts and the top-5 client User-Agents
  • Last 5 terminal outcomes from the openai_compat_run_events table — each row shows truncated request_id, elapsed-ms badge, and the openai_compat.action outcome string
  • Sticky degraded — persist failures warning when the run ledger has observed an unrecovered persistence error

Mutation (enabling, changing tool_policy) stays in bmo.toml and is inspected with the CLI subcommand above; the slash is intentionally read-only.

The TUI sidebar shows a one-line OpenAI compat: status with four documented states:

LabelWhen
disabledenabled = false (or no [options.openai_compat] block)
enabled (chat_only)Enabled and tool_policy = "chat_only" (the default)
enabled (auto_approve)Enabled and tool_policy = "auto_approve"
degraded — persist failuresRun ledger persistence has failed and not yet recovered (overrides the policy label until persistence succeeds again)

The label is a pure function of the resolved config plus the run ledger’s sticky degraded flag — same source the slash command renders.

Each compat request emits exactly one openai_compat.fired slog record at route entry and exactly one terminal openai_compat.action record per request, sharing a request_id field that also keys the SQLite run ledger.

For copy-paste filter recipes that answer “did Open WebUI hit BMO and what happened?”, including session reuse, queued/resumed, and auth/proxy-boundary questions, see agent tracing — Open WebUI / OpenAI-compat.

FieldTypeWhen set
request_idstringAlways — pairs fired with its closing action and keys the run ledger row
session_idstringAlways (empty when no X-BMO-Session-Id header)
routestringOne of chat_completions, models_list, runs_list, run_events_list
tool_policystringchat_only or auto_approve
ambiguity_actionstringask, research, or code
modelstringResolved model ID for the request
streambooltrue when the client requested an SSE stream
is_session_reusebooltrue when X-BMO-Session-Id was set on the request
client_uastringUser-Agent header, truncated at 64 bytes
OutcomeSeverityDescription
final_answerinfoTerminal happy path — assistant content delivered to the client
clarificationinfoAmbiguity router returned a clarification body without running the agent
queuedinfoShared-session-busy — second request received 409 with the queued-error body
resumedinfoStream resumed from an existing queued run’s events
cancelledinfoClient disconnected mid-stream; agent run cancelled and ledger event recorded
model_override_appliedinfoPer-request model override accepted (allow-ephemeral path)
model_override_rejectedinfoPer-request model override rejected; route returned 400
auth_failedinfoAuth boundary refused the request — expected operator-visible outcome
recovery_failedwarnPost-tool recovery prompt also failed to produce a final answer
k8s_retry_failedwarnKubernetes inspection retry path exhausted without an answer
persist_failedwarnRun-ledger or run-events persistence failed; response still served
error_500warnGeneric 500 — list endpoints, malformed input handlers, unrecoverable arms

model_override_rejected and auth_failed are intentionally Info — they are expected boundary outcomes operators want to graph, not error noise. Per the same convention, cancelled is Info because client disconnects are routine.

Warning: The Open WebUI-side API key field is not, by itself, a BMO security boundary. The new operator surfaces above (CLI, slash, sidebar, filter recipes) make the integration easier to inspect, but they do not change the bind boundary. Keep this integration on loopback by default, or put BMO behind a trusted reverse proxy / access-control layer when exposing it beyond the local machine.

If you run BMO behind Envoy or another reverse proxy and want BMO to record the authenticated requester on sessions, permissions, and operation audit records, set BMO_REQUESTER_TRUSTED_PROXIES to the proxy IPs or CIDRs that are allowed to forward X-Auth-* identity headers. BMO ignores those headers on direct client traffic by default.

  • Use Open WebUI’s chat interface while BMO handles the coding agent loop in the background
  • Route requests through Open WebUI’s access controls before they reach BMO
  • Test BMO responses with OpenAI-compatible evaluation tooling
  • Protocol support - compare MCP, ACP, and A2A at a glance.
  • MCP Client - connect external MCP servers to BMO.
  • MCP Server - expose BMO tools to other clients.
  • Zed - another editor-facing integration path.