OpenWebUI loads Functions as a single in-memory module via exec()
(open_webui/utils/plugin.py: load_function_module_by_id). There is no
mechanism to ship sibling .py files alongside a Function, so
'from session_store import get_store' / 'from session_marker import ...'
fail with ModuleNotFoundError as soon as the function is pasted/imported
into OpenWebUI (see traceback: exec(content, module.__dict__) -> line 24
-> ModuleNotFoundError: No module named 'session_store').
Fix: inline both modules' full content directly into
claude_agent_pipe.py so it is fully self-contained, and remove the now
redundant standalone session_store.py / session_marker.py files (their
logic lives inline now; keeping both would only invite drift).
Verified by:
- python3 -m py_compile claude_agent_pipe.py
- exec()'ing the file the same way OpenWebUI's plugin loader does (with
claude_agent_sdk stubbed out), confirming no ModuleNotFoundError/NameError
and that SessionStore/get_store/make_marker/extract_session_id_from_messages
and the Pipe class all load correctly.
- Add session_store.py: SQLite-backed, WAL-mode session store keyed by
chat_id, robust to multi-worker access and process restarts.
- Add session_marker.py: fallback mechanism that embeds an invisible
markdown reference marker in assistant responses so the session id
survives even without SQLite access, by parsing it back out of
body messages history.
- Update claude_agent_pipe.py: resolution order is in-memory cache ->
SQLite store -> marker fallback. Session id is persisted to both the
in-memory dict and SQLite on every init SystemMessage, and the
marker is appended to the final visible response text.
- Warn when chat_id is missing/None instead of silently starting
a fresh session (related to open-webui/open-webui#20563).
The rate-limit-surfacing feature (RateLimitEvent handling) added in
ee6fc4c/4387685 left a stray leading space on the claude_agent_sdk
import block, causing a module-level IndentationError that broke the
whole pipe. Also cleans up two orphaned whitespace-only lines left
between the RateLimitEvent and ResultMessage branches.
Add OpenWebUI Tools/MCP passthrough, fix multi-arity bug, bump to 0.2
- Fix _build_kb_mcp_server: the no-knowledge branch returned a 2-tuple
(None, []) while every call site unpacked 3 values, raising
ValueError: not enough values to unpack (expected 3, got 2) on any
turn without an attached knowledge base. Now consistently returns
(None, [], {}).
- Add __tools__ passthrough: wrap OpenWebUI's Tools / external tool
servers (incl. MCP via mcpo) attached to the Workspace Model as an
in-process MCP server ("owui-tools"), merged alongside the existing
"helm-kb" server. Lets Claude Code use whatever tools/connectors are
configured in OpenWebUI natively, without hardcoding a server URL in
the pipe — stays in sync if the attached tools change later.
New: _JSON_SCHEMA_TYPE_MAP, _build_owui_tools_mcp_server().
Known limitation: OpenWebUI's built-in tools (web_search,
image_generation, execute_code) aren't included in __tools__ yet
(upstream limitation); only user-defined Tools and external/MCP
tool servers are. Not an issue here since Claude Code already ships
its own WebSearch/WebFetch.
- version: 0.1 -> 0.2, contributors: Speedliner (author unchanged)
The SDK pipe hardcoded setting_sources=[], so chats never loaded
~/.claude/CLAUDE.md or any settings.json. Expose it as a valve
(default empty = isolated baseline) so single-user/homelab instances
can opt into persistent context, while shared deployments keep the
safe default.
Parse via _parse_setting_sources(): comma-split, lowercase, drop
unknown tokens so a typo can't silently widen inheritance. README
documents the opt-in plus the hooks/permissions security tradeoff
(CLAUDE.md and settings.json load together — a Claude Code coupling)
and why the valve does not apply to the sandboxed pipe.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>