Skip to content

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 and reserve pass-through for detailed model catalogs. 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, and phase; normalized model detail and error metadata remain lossy. Its raw pass-through route passes the complete direct contract suite. The bundled homeserver-litellm image preserves native wildcard streaming and commentary; error metadata remains rewritten. The UIs exercise the selected gateway's managed/native inference path; only 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 + pass-through catalogs"]
  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"]

  model["Upstream OpenAI-compatible model"]

  user <--> chainlit
  user <--> openwebui
  chainlit <-->|"OpenAI API"| gateway
  openwebui <-->|"OpenAI API"| 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 + catalog detail"| api_a
  litellm <-->|"managed + catalog detail"| api_b
  litellm <-->|"provider: litellm_proxy"| files
  api_a <-->|"when a graph calls a model"| model
  api_b <-->|"when a graph calls a model"| model

With LiteLLM selected, the UIs merge the explicit lgos-a and lgos-b catalog pass-throughs, qualify graph IDs, and send Responses through managed 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.

LiteLLM uses one wildcard route per graph API. Each API owns graph discovery; adding a graph requires no gateway configuration change. Raw gateway routes remain available for protocol-reference tests, but the UI clients do not use them for Responses.

At startup, Compose waits for PostgreSQL, runs the one-shot API schema setup, starts both healthy graph APIs and the Files service, and then starts Bifrost, LiteLLM, and the 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.