Demo Architecture¶
The Docker demo runs two independently addressable LGOS API containers and one
logical central Files service. OPENAI_GATEWAY_TYPE=litellm|bifrost selects a
first-class OpenAI-compatible edge for both Chainlit and Open WebUI. LiteLLM
uses managed Responses and Bifrost uses native Responses. Both use normal
Files routing. LiteLLM serves metadata from native /model/info; Bifrost uses
catalog-detail pass-through. No UI
connects directly to an upstream container, and neither UI imports
langgraph-openai-serve. See
Package Architecture for what happens inside
each API process.
Managed gateway normalization boundaries
The bundled Bifrost native Responses route preserves standard fields, file
input, commentary, phase, and store: false; normalized model detail and
error metadata remain lossy. Its raw pass-through route
preserves successful-request contracts, while virtual-key governance
rejects an unknown model before its upstream error can pass through. The
bundled homeserver-litellm image preserves native streaming and
commentary; error metadata remains rewritten.
The UIs exercise the selected gateway's managed/native inference path;
only Bifrost model-detail lookup uses a lossless pass-through. See
Docker Compose and Bifrost Gateway.
Request Path¶
flowchart LR
user["Browser user"]
subgraph clients["Demo clients"]
direction TB
chainlit["Chainlit"]
openwebui["Open WebUI"]
end
bifrost["Bifrost gateway"]
litellm["LiteLLM<br/>managed inference + model/info"]
gateway["OPENAI_GATEWAY_TYPE<br/>selects one gateway"]
sdk["OpenAI SDK test"]
subgraph apis["LGOS demo APIs"]
direction TB
api_a["API A<br/>FastAPI + LGOS + demo graphs"]
api_b["API B<br/>FastAPI + LGOS + demo graphs"]
end
files["Files service<br/>OpenAI Files API + S3 repository"]
dbhub["DBHub<br/>read-only MCP server"]
database[("lgos-db PostgreSQL<br/>dedicated mcp_demo schema")]
model["Upstream OpenAI-compatible model"]
user <--> chainlit
user <--> openwebui
chainlit <-->|"OpenAI API + native MCP"| gateway
openwebui <-->|"OpenAI API + native MCP"| gateway
gateway <-.->|"bifrost"| bifrost
gateway <-.->|"litellm"| litellm
sdk <-->|"catalog + native/raw Responses"| bifrost
sdk <-->|"Responses"| litellm
bifrost <-->|"provider: lgos-a"| api_a
bifrost <-->|"provider: lgos-b"| api_b
bifrost <-->|"provider: lgos-files"| files
litellm <-->|"managed inference"| api_a
litellm <-->|"managed inference"| api_b
litellm <-->|"provider: litellm_proxy"| files
bifrost <-->|"allowlisted MCP tools"| dbhub
litellm <-->|"allowlisted MCP tools"| dbhub
dbhub -->|"lgos_mcp read-only role"| database
api_a <-->|"when a graph calls a model"| model
api_b <-->|"when a graph calls a model"| model
With LiteLLM selected, the UIs read native /model/info, use model_info.lgos
for capabilities and settings, and send model_name unchanged through managed
Responses routing. With Bifrost selected, they discover provider-qualified IDs through
its aggregate catalog, use raw pass-through only for model detail, and send
Responses through native routing with x-model-provider. Both choices upload
attachments through normal gateway Files routing before sending the returned
file_id to a graph. This preserves descriptions and runtime capabilities
without allowing UI inference to bypass the gateway's normal data plane.
For mcp-postgres, the clients also discover and execute the gateway's native
MCP tools; DBHub and the database credential remain behind that gateway. See
PostgreSQL Through Native MCP.
The LGOS-owned sync command registers concrete models and full metadata in LiteLLM's database. Run it after graph changes; the gateway needs no LGOS-specific code. LiteLLM exposes no demo pass-through routes. Protocol tests compare its managed stream with the direct LGOS endpoint; UI clients never make that direct connection.
At startup, Compose waits for PostgreSQL and runs the one-shot API and Chainlit schema migrations. The idempotent MCP setup then creates the reporting views, role, and grants before DBHub starts. Both healthy graph APIs and the Files service start before the selected gateway and UI clients. The diagram shows request traffic rather than those readiness dependencies. Compose runs one Files process for the demo; production deployments may run multiple stateless replicas over the same repository.
State Ownership¶
The UIs own their conversations. The API stores only paused interrupt execution and explicit graph data; it does not copy either UI transcript into LGOS.
flowchart LR
subgraph clients["UI-owned state"]
direction TB
chainlit["Chainlit"]
openwebui["Open WebUI"]
end
subgraph api["LGOS API processes"]
direction TB
interrupts["LGOS interrupt handling"]
plot["persistent-plot-agent graph"]
end
subgraph postgres["One PostgreSQL database"]
direction TB
chainlit_rows["Chainlit users, threads, and steps"]
checkpoints["LangGraph checkpoints"]
store["LangGraph Store documents"]
locks["PostgreSQL advisory locks"]
end
files_service["Central Files service"]
s3[("S3-compatible service<br/>separate UI and Files buckets")]
openwebui_data[("Open WebUI data volume<br/>transcripts, raw uploads, and embeds")]
chainlit -->|"conversation and UI metadata"| chainlit_rows
chainlit -->|"element content"| s3
files_service -->|"opaque inference files"| s3
openwebui -->|"conversation and UI state"| openwebui_data
interrupts -->|"paused execution"| checkpoints
interrupts -->|"same-run coordination"| locks
plot -->|"thread-scoped chart document"| store
Both API containers run the same image and graph set, but Bifrost
treats them as separate providers. They share PostgreSQL for durable LangGraph
checkpoints, thread-scoped data, and interrupt-run coordination. Chainlit
uses the same database for UI metadata and S3 for element bodies. Open WebUI
keeps its state and native raw-upload copy in its bind-mounted data directory;
the central Files service owns the separate inference copy. Detailed ownership
and recovery behavior live in
Persistent Plot Agent and Interruptible
Human Review. When
LGOS_ENABLE_LANGFUSE=true, each API adds the Langfuse callback to graph runs
and exports observations directly to the configured Langfuse service. Langfuse
is not a Compose service or a proxy in the request path.
The optional Compose overlay adds a separate telemetry path without changing request or state ownership. Its complete signal flow and operational boundary are documented in Demo OpenTelemetry Overlay.