Open WebUI tracing with Future AGI
Install the Future AGI filter function in Open WebUI so each completed chat turn becomes a trace in an Observe project. Covers what is captured, the metadata-only default, the opt-in for content, and the limits of the Open WebUI filter hooks.
Send completed Open WebUI chat turns to an Observe project with version 0.3.0 of the Future AGI filter Function. Each exported turn has a chat span and an LLM span with model, usage, session and user IDs.
This setup uses a single-worker, self-hosted Open WebUI 0.11.4 instance with Python 3.12 and a reachable self-hosted Future AGI collector. Open WebUI’s native OpenTelemetry export covers infrastructure spans rather than model and token data; it is separate from the filter’s traces. See Setup Observability for keys and projects.
1. Install the filter
Use Open WebUI 0.11.4 with Python 3.12, and keep the SDK and OTLP/HTTP exporter on the matching 1.45.1 release line.
- In Open WebUI open Admin Panel, Functions and create a function. Paste the whole of
futureagi_filter.py, keeping the opening"""block as the first line, and save. Open WebUI installs the requirements named in that block (opentelemetry-exporter-otlp-proto-http,certifi). Keep the preinstalled SDK and exporter on the qualified 1.45.1 release line. IfOFFLINE_MODE=trueorENABLE_PIP_INSTALL_FRONTMATTER_REQUIREMENTS=false, the dependencies must already be present or saving fails. - Turn the function on and either mark it Global, or attach it to the models you want traced (Workspace, Models, model, Filters). A filter that is off, or neither global nor attached, never runs.
- Open the function’s Valves (gear icon) and set:
| Valve | Default | Meaning |
|---|---|---|
api_key, secret_key | empty | Future AGI keys, sent as X-Api-Key and X-Secret-Key. With either one empty nothing is exported. |
endpoint | https://api.futureagi.com | Override this default with your self-hosted collector origin reachable from the Open WebUI process, for example http://localhost:4318 when both share the host. The filter posts to {endpoint}/tracer/v1/traces. In a container, localhost refers to that container, not the Docker host. |
project | open-webui | The Observe project, sent as the resource attribute project_name with project_type=observe. |
redact | true | Keeps prompts and replies out of every span. Set false to opt in to content. |
include_email_hash | false | Adds a pseudonymous HMAC of the user’s email as user.email_hash. |
email_hash_salt | empty | Optional secret salt for that HMAC. |
max_chars | 32000 | Maximum characters copied into each input or output value. 0 means unlimited. |
Set ENABLE_VALVE_ENCRYPTION=true so Open WebUI encrypts the valves in its database.
- Send a chat message. After a few seconds (the filter batches spans, 5 s delay) open the project in Observe.
2. What is captured
| Span | Type | Attributes |
|---|---|---|
openwebui.chat | chain | session.id and gen_ai.conversation.id (the Open WebUI chat id), user.id. With redact=false: input.value (last user message) and output.value. |
openwebui.llm (child) | llm | gen_ai.request.model, llm.model_name, token usage, the same ids. With redact=false: the conversation as input.value and the reply as output.value. |
- Tokens come from the usage Open WebUI stored for the reply. When a tool loop ran, the cumulative input and output tokens of the loop are used. If Open WebUI has no usage for the reply the span carries
gen_ai.usage.unavailable=trueand no token counts. Tokens are never estimated. - Latency of the llm span is the turn latency between
inletandoutlet. It includes Open WebUI preprocessing, retrieval, tools and database work, not only the model call. - Tool calls and retrieval are not separate spans. A turn that used a native tool produces the same two spans; the tool name and arguments are not exported, and the usage covers the whole loop.
- Errors: a turn whose provider stream carried an error object, or whose reply is empty with no usage, is exported with status
ERRORon both spans and the fixed messageprovider returned a failed response. The provider’s own error text is never exported. - The root span is detached from Open WebUI’s own server span, so it is a real trace root even when native Open WebUI tracing is on.
3. Privacy
- Default: no content. With
redact=trueno message text, no tool arguments or results and no error text leave Open WebUI. Ids, model, status and token counts are still sent. - Opt in with
redact=false. For a saved chat the stored history is copied; it does not necessarily include the system prompt, RAG context or tool results. Temporary chats (temporary:orlocal:chat ids) stay redacted even withredact=false. - The user’s email is never sent as a standalone attribute.
include_email_hash=trueadds an HMAC-SHA256 of the normalised email (pseudonymous, not anonymous). If you turn content capture on, treat prompt text as able to contain any user field, including an email. - Local paths, host names and stack traces are not exported in either mode.
- The Future AGI keys travel only as request headers, never as span attributes. The filter ignores
OTEL_RESOURCE_ATTRIBUTES,OTEL_BSP_*, sampler, certificate and compression environment variables of the Open WebUI process and sends uncompressed OTLP/HTTP. - Valves are stored by Open WebUI. Without
ENABLE_VALVE_ENCRYPTION=truethey are stored in plain text.
4. Troubleshooting
| What you see | What it means |
|---|---|
| No spans at all | The function is off, or neither Global nor attached to the model. Or api_key / secret_key is empty. Or the Observe project in project does not exist in the key’s workspace. |
| Function fails to save | The OpenTelemetry exporter could not be installed (offline mode, or no pip in the Open WebUI environment). Install opentelemetry-exporter-otlp-proto-http and certifi yourself and restart. |
| 401 in the Open WebUI log, chat still works | Wrong keys. The filter sends X-Api-Key and X-Secret-Key; Bearer tokens are ignored. |
| Chat works but spans stop after the collector is down | Expected. The filter never blocks or fails a chat; queued spans are dropped. |
| Input and output are empty | redact is true (the default), or the chat is temporary. |
| The last few spans are missing after a restart | Spans still queued when the worker is killed are lost. |
| A failed request has no span | See limitations. |
5. Limitations
- A provider HTTP error (for example a 500 from the model connection) and a user Stop bypass the filter’s hooks in Open WebUI, so no span is produced for those turns. Only failures that reach the stream or the outlet are exported as
ERROR. - No per-tool and per-retrieval spans (see section 2).
- Each Open WebUI worker process exports on its own; function edits create a new instance and shut the old provider down.
- Model ids are Open WebUI’s, which can differ from the provider’s model name.
- Use an existing saved chat when tracing API requests; requests without a
chat_idmay not invoke the filter hooks.
6. How this differs from related integrations
- Native Open WebUI OpenTelemetry: infrastructure spans only, no model or token data. The filter adds the GenAI view. The two are not correlated.
- traceAI instrumentors (for example OpenAI): patch an SDK client inside your own code. Open WebUI makes the model call itself, so there is no client to patch; the filter observes Open WebUI’s hooks instead.
- Open WebUI Pipelines: a separate container that can also run filters. This integration is a Function and needs no extra container.
Questions & Discussion