Skip to content

Open WebUI Integration

Start with UserValves Simple / simple-graph to try static per-user runtime settings. Its small uservalves_simple.py Filter declares two settings and passes their values to the shared Responses Pipe. Open WebUI owns the settings form and persistence.

The demo includes two Open WebUI Functions:

  • functions/uservalves_simple.py demonstrates a fixed UserValves schema for one graph, using Open WebUI's native Filter and UserValves support.

  • demo/ui/openwebui/src/lgos_openwebui/functions/generic/ is the modular source for a manifold Pipe for all registered graphs. It uses OpenAI Responses, graph-specific runtime settings, and the standard Files API, and adapts LGOS interrupts to Open WebUI's native question UI.

The sync command also generates one Open WebUI Workspace Model per discovered LGOS model. Each Workspace Model wraps the corresponding manifold model and projects its LGOS settings schema into the pinned release's native Chat Variables form. The generated server-tool models add fixed Package version and Web search Chat Variable checkboxes. Generated advanced-graph models add only the Web search checkbox; their gateway MCP tools are attached separately from the discovered mcp_tools capability. The Pipe maps enabled tool boxes to a name-only {"type":"custom","name":"lgos_package_version"} declaration or {"type":"web_search"}; it keeps them out of metadata.lgos_settings. The names are client constants, not discovered metadata. The server registry determines which names execute in LGOS. Asking the advanced graph to remember or save something triggers its note-review flow without a graph-specific setting. The Pipe executes only native function_call items. Server custom calls and searches have distinct native types and are already complete. When lgos-a/simple-graph is available with valid metadata, sync also creates the dedicated UserValves example over the same manifold base.

Server Tool Switches

Select LGOS / ... / server-tool, open the Chat Variables control beside the chat input, and enable Package version, Web search, or both. The checkboxes default to off and their values belong to the chat. LGOS executes the selected tools server-side without a client-tool continuation.

The pinned Open WebUI runtime's OpenAI 2.29 SDK omits custom_tool_call_output from one generated response union. The Function adds that existing SDK model to the affected response annotations at load time. The shim is feature-detected, changes no installed package files, and becomes a no-op when Open WebUI updates to an SDK containing the corrected union.

For LGOS / ... / advanced-graph, the same control contains Web search. Search is sent as a standard Responses tool. Note saving is an intent expressed in the user's message.

Simple Per-User Settings

After setup below, select UserValves Simple / simple-graph. Open Controls → Valves, select Functions → UserValves Simple, and choose use_history and audience. These preferences belong to the user and apply across chats using this example. The field definitions are static; their values are editable.

The Filter supplies those values through Open WebUI's request metadata chat_variables slot. The shared Pipe serializes them into metadata.lgos_settings; LGOS validates and applies them. The example has no Chat Variables form, so there is only one settings control.

The Filter is enabled only on the dedicated Workspace Model lgos.uservalves_simple. Keep it attached there rather than enabling it globally. It depends on the Generic Pipe for Responses transport. The generated LGOS / ... models below demonstrate schema-driven per-chat settings.

Select one first-class gateway

Set OPENAI_GATEWAY_TYPE=litellm|bifrost once for both demo UIs. LiteLLM uses managed Responses; Bifrost uses native Responses. Files also use the selected gateway's normal route. Metadata comes from LiteLLM's native /model/info or Bifrost's catalog-detail pass-through. Neither the Function nor the sync logic connects directly to LGOS.

Gateway MCP

The sync command reconciles one managed Streamable HTTP connection. It attaches lgos-gateway to each generated Workspace Model whose gateway metadata advertises mcp_tools. The Generic Pipe forwards the gateway tools from Open WebUI's native __tools__ map through Responses and returns matching calls to the native tool loop. mcp-postgres adds its fixed report allowlist at the API boundary, while general-purpose graphs can use the gateway-authorized tool catalog without knowing which MCP servers provide it.

The connection derives /mcp from OPENAI_GATEWAY_BASE_URL and stores OPENAI_GATEWAY_API_KEY as native bearer authentication. The same values drive model discovery, Responses, and Files. The gateway credential determines which MCP tools can be discovered, while the downstream DBHub token remains private to the gateway.

Keep streaming enabled because Open WebUI's native tool middleware consumes the streamed tool-call shape. The current database example is PostgreSQL Through Native MCP; see it for the complete flow and security boundaries, and Open WebUI's official MCP documentation for its native server administration and access controls.

Setup

Start the pinned official Open WebUI image unchanged:

cp demo/.env.example demo/.env
just demo/up lgos-openwebui --wait

For independently started components, first sync LGOS model metadata when using LiteLLM. Then run the locked synchronization project on the host:

just demo/sync-openwebui

The command replaces the container-only gateway root with DEMO_GATEWAY_HOST_URL and reuses the same credential as the Open WebUI runtime. For a standalone Open WebUI deployment, run uv run --directory demo/ui/openwebui --locked lgos-openwebui-sync from an environment where DEMO_OPENWEBUI_URL and the shared gateway URL are both reachable.

The full-stack just demo/compose [--dev] [--otel] variants handle synchronization automatically after their dependencies are healthy.

The sync command signs in through /api/v1/auths/signin and reads LGOS metadata from the selected gateway before changing Functions or Workspace Models. An unavailable or malformed catalog stops the command without modifying them. It then updates the bundled Functions and bulk-imports each generated Workspace Model with an active, public, hidden override for its manifold base. Run it again after changing a Function, the configured model catalog, or a graph's client settings schema.

Generated Workspace Model descriptions come from the selected graph's required GraphConfig.description. The sync marks a model as Limited functionality when the API omits a description.

LiteLLM's managed /v1/models response is not the UI catalog. Both the Generic Pipe and Workspace Model sync read native GET /model/info using their configured gateway key. Entries with model_info.lgos supply descriptions, features, and complete settings; model_name remains the inference ID. No provider allowlist, per-provider catalog URL, or LGOS fallback is used. Bifrost uses aggregate /v1/models for discovery and its pass-through only for provider-specific detail. This preserves LGOS descriptions, features, and detailed client-settings schemas without a direct connection to LGOS. Inference still uses the selected gateway's normal Responses route.

After importing the current catalog, sync deletes obsolete generated lgos.* Workspace Models and generic.* base visibility records. It does not delete unrelated user-managed Functions or Workspace Models. New generated Workspace Models are public; later syncs preserve their access grants and active state. The sync owns the generated bases' hidden, public, and active state.

The command discovers every top-level .py file and directory-backed Function under demo/ui/openwebui/src/lgos_openwebui/functions/, except entries whose names start with _. A modular Function directory contains function.py for its frontmatter and entrypoint; the Generic Function's modules are flattened into one executable source string at sync time because Open WebUI stores each Function directly in its database. The filename stem or directory name is the Function ID, and the required Open WebUI frontmatter title is its display name. Function IDs must be lowercase Python identifiers.

The shared demo/.env supplies the sync credentials and gateway selection. See sync settings for their purposes. Set secrets in the environment rather than passing them on the command line.

Choose a generated entry such as LGOS / lgos-a/simple-graph to use Chat Variables. Its Workspace Model ID is lgos.lgos-a/simple-graph, and its base model is generic.lgos-a/simple-graph. The raw Generic / ... manifold entry remains active and public but is hidden from the chat selector, following Open WebUI's curated-interface guidance.

Configure the required OPENAI_GATEWAY_TYPE, OPENAI_GATEWAY_BASE_URL, and OPENAI_GATEWAY_API_KEY values, plus OPENAI_API_TIMEOUT, in the generic Function's admin valves. Compose initializes the required values from demo/.env; use a key issued by the selected gateway. LiteLLM sends the catalog's model_name unchanged for managed routing. Bifrost removes that provider prefix and sends it as x-model-provider to native Responses. Open WebUI stores Function code in its database, so a bind mount of the Python file does not update it.

File Input

Generated models enable Open WebUI's native file-upload control only when the graph advertises file_inputs. Select LGOS / lgos-a/file-input in the bundled demo to process an attachment. The Generic Function receives non-image attachments through Open WebUI's documented __files__ argument and image bytes from their base64 image_url content. Raw uploads can omit the documented hydrated file.path; the Function then reads the original bytes through Open WebUI's authenticated file-content endpoint. In the pinned release, __metadata__["user_message"] identifies the message that started this turn. Because __files__ also includes files from earlier turns, the Function intersects it with that current message, uploads each current attachment's original bytes with purpose="user_data", and appends the returned OpenAI file_id to the message. It never reuploads historical chat attachments or moves them to the latest message. Images use input_file.file_id too; the current LGOS Responses subset does not accept input_image items.

This local file bridge uses the HTTPX shipped by the pinned Open WebUI runtime; it will move to HTTPX2 when Open WebUI adopts OpenAI v3.

The generated Workspace Model is the upload-capability boundary. The raw manifold entry is intended for diagnostics and does not add a second remote metadata check to every Responses request.

Compose sends file uploads through the selected gateway's normal /v1 Files route. Bifrost assigns the request to lgos-files; LiteLLM assigns it to litellm_proxy. Both providers target the central Files API. Neither UI uses a Files pass-through.

The Compose service mounts a small ASGI wrapper that forces process=false on Open WebUI's native file-upload endpoint. Open WebUI therefore stores the original bytes without extracting or embedding their content before the Pipe runs. The generated Workspace Model also disables chat-time file-context retrieval and its built-in file tools while preserving other built-in tools, including ask_user.

Open WebUI still owns its raw upload copy because its native attachment UI requires an Open WebUI file record. The central Files API is the only processing source of truth and owns the separate inference copy referenced by file_id. The policy applies to every file uploaded through this demo Open WebUI instance, not only to generated LGOS models.

Temporary upstream workaround

Open WebUI v0.11.3 always requests processing for non-image chat uploads, before a Pipe or Filter can run. The wrapper exists only to change that upload request to process=false; a Filter can control later retrieval but cannot prevent the earlier extraction.

Remove upload_policy.py, its Compose mount, and the custom Uvicorn command when the pinned Open WebUI release provides native per-model control for raw uploads. See the related upstream issue and the unmerged File Processing capability PR.

Limited Functionality

Every generated model remains visible when its native detail response lacks the required lgos extension. Its name and description say Limited functionality. Standard assistant text may still work; runtime settings, file-upload controls, and gateway tools are not assumed.

Runtime Settings

LGOS remains the schema and default-value source of truth. The sync command uses the same deliberately small JSON Schema subset as the Chainlit demo:

  • boolean with a boolean default becomes a checkbox;
  • string enum with a valid string default becomes a selector;
  • string with a string default becomes a text input;
  • nested objects, arrays, numbers, and unsupported schemas are omitted.

Open WebUI stores Chat Variable values on the conversation. Select a generated LGOS model, then use the Chat Variables control beside the message input. Since LGOS supplies defaults for every setting, the form does not block the first message merely to confirm them.

Open WebUI Chat Variables showing conversation-history and audience controls

Runtime settings synchronized from lgos-a/simple-graph and rendered as native Open WebUI Chat Variables.

When a chat has values, the Pipe serializes Open WebUI's generated Chat Variables and sends them as metadata.lgos_settings. LGOS performs the authoritative runtime validation.

The shared Pipe maps Open WebUI's stable chat_id to metadata.conversation_id on every Responses request, including the UserValves example. Langfuse can therefore group the chat's independent request traces into one session, while Open WebUI continues to own and resend the conversation history. The generic Pipe also forwards the opaque Open WebUI user ID as the standard OpenAI user; persistent-plot-agent uses both values to scope its chart document. Interrupt resumes reuse the same conversation value. See the persistent plot agent ownership flow for the API Store and Open WebUI persistence boundaries.

The Workspace Model schema is a generated projection, not a second configuration source. Open WebUI does not fetch a remote schema when the model selector changes, so rerun just demo/sync-openwebui after an LGOS schema change. Model selection then switches among the already-synchronized native forms.

Pinned Open WebUI contract

The demo pins Open WebUI v0.11.3. The sync imports its native meta.chat_variables_schema model metadata directly instead of putting form declarations in a system prompt. This preserves JSON booleans and ensures UI configuration never becomes graph prompt content. This behavior is version-specific; rerun the Open WebUI sync and model tests before changing the image pin.

Streaming, Status, And Citations

The general manifold Pipe uses OpenAI Responses for every model. The SDK stream manager owns event accumulation and supplies the terminal Response; the Pipe adapts final-answer deltas to Open WebUI's native stream interface and maps completed commentary messages to native status history. It translates standard answer URL annotations from the completed Response into persistent native source events, with each cited span as the source excerpt. The SDK owns the complete annotation objects; the Pipe does not rebuild them from deltas. Both modes exclude commentary and accept answer messages without the optional phase field. Transcript replay labels assistant answers as final_answer and preserves explicit phase values, following OpenAI's assistant phase guidance. Inline citation markers remain part of assistant content.

Status descriptions remain active while the Responses/tool loop runs and are finalized when it completes or stops, using Open WebUI's native status events. Both response modes display native refusals. Failed and incomplete streaming events are handled directly so their reason remains visible; incomplete responses never trigger client functions.

Keep streaming enabled

In Open WebUI v0.11.3, native citation sources, tool calls, and ask_user use its streaming middleware. The UI does not render equivalent native controls from non-streaming adapter output.

The persistent plot graph returns a standard display_file function call. The Pipe downloads the Plotly JSON through the OpenAI Files API and embeds the figure in a small HTML document. The browser renders it with the native Plotly.newPlot API; the Open WebUI backend needs no Python Plotly package. It emits the native persistent embeds event to render an interactive chart in Open WebUI's sandboxed iframe, then returns the matching function_call_output before requesting the final answer. The HTML uses Plotly's versioned CDN script, so browsers must be able to reach cdn.plot.ly. Open WebUI saves the embed with the message for chat reloads; HTML and chart bytes stay out of the upstream model transcript. Image files still use authenticated Open WebUI file storage and the native files event. Each continuation retains the original input, including instructions and file references, then appends complete Response output items and matching tool results. Final-answer text from every call is retained in both modes.

Server custom call/result items have already been executed by LGOS; the Pipe does not execute them or send another result. The chat displays their final assistant answer.

The Pipe returns plain text for non-streaming answers and uses the OpenAI SDK's typed chunk schema for streamed text. Open WebUI JSON-encodes these chunks, so literal text such as data: [DONE] cannot be mistaken for a stream event. Open WebUI owns stream termination. The native ask_user bridge also uses the host's tool-call dictionaries to persist question cards and submit answers. These shapes belong to the UI boundary; inference uses Responses exclusively. See the pinned Pipe host. Shared prompts and graph behavior are documented under Events And Citations and Persistent Plot Agent.

Interrupt Input

The Pipe translates each LGOS lgos_interrupt batch into one built-in Open WebUI ask_user call. Open WebUI persists that pending call on the saved assistant message, so its native question card survives a page reload. The Pipe keeps the original LGOS calls in the opaque ask_user call ID; answering the card needs no adapter database or live socket callback.

The deliberately small UI profile is an object containing a non-empty question, two or three unique string choices, and optional boolean allow_other. When allow_other is true, Open WebUI adds its free-form Other input. This is a demo-client presentation convention, not an LGOS payload restriction. Responses carries each resume value as a string, and this adapter maps Open WebUI choices and free-form answers directly to those strings.

The advanced graph includes exact note bytes in its review payload after the user explicitly asks to save something. When details exceed the native question's 500-character limit, the Pipe renders the complete payload above the question card; nothing is truncated from the saved interrupt cursor. Knowledge citations remain ordinary answer text with filenames and provider file IDs. The Pipe does not add a knowledge-base selector or bridge the demo S3 Files namespace.

After the user answers, the Pipe decodes the paused Response ID and original calls from the opaque cursor. It sends the Response ID as previous_response_id with one function_call_output item per interrupt containing the user's answer. One native ask_user call can contain one to three questions, matching Open WebUI's built-in limit. LGOS itself remains generic and can expose larger atomic batches to clients that support them.

Saved chats restore pending input

Open WebUI's built-in ask_user persistence requires a saved chat. Refreshing the page restores the unanswered card; the LangGraph checkpoint remains pending until the answer reaches LGOS. Cancel ends the Open WebUI turn without resuming the graph, so its checkpoint remains pending. The demo has no expiry worker; production deployments must reap abandoned runs.

The refund demo offers approve, reject, and a custom response. Approval executes the simulated refund and notification, rejection stops the workflow, and custom text is returned as reviewer feedback without executing an action.

Open WebUI native human input card for a LangGraph interrupt

Open WebUI renders the interrupt as a native ask_user card with choices and an optional free-form answer.

LGOS still owns the pending graph checkpoint and its retention policy. See Interruptible Human Review for server-side checkpoint retention.

See the core citation contract and interrupt protocol for the API behavior beneath the adapter.