openwebui-claude-agent-pipe
Health Uyari
- License — License: NOASSERTION
- Description — Repository has a description
- Active repo — Last push 0 days ago
- Low visibility — Only 5 GitHub stars
Code Basarisiz
- exec() — Shell command execution in _loader.py
- exec() — Shell command execution in redact_stdin.py
Permissions Gecti
- Permissions — No dangerous permissions requested
Bu listing icin henuz AI raporu yok.
Open WebUI pipe that runs each chat turn as a Claude Agent SDK session: Claude Code's agent loop behind a chat UI, on your subscription
Open WebUI Claude Agent Pipe
An Open WebUI pipe function that
runs each chat turn as a headless Claude Agent SDK
session: Claude Code's full agent loop, with a real working directory and
tools, behind a normal chat UI, billed to an Anthropic API key or, on a
single-user install, your Claude subscription. It is for
one person (or one trusted admin) who already runs Open WebUI and wants Claude
Code in it, on the web and on mobile clients, rather than only in a terminal.
The agent can stop mid-turn and ask through Open WebUI's own form:

Every reply ends with what the turn cost:

Credit
This is a fork of Thomas Friedel's
openwebui-claude-code
(MIT), taken at commit 5bbc1fc. He built the bridge: the pipe, the valves,
the knowledge-base tools, inline tool details, image attachments, and the
artifact scanner are his work and still form the backbone of this file. This
repo adds durable sessions, output redaction, the status stream, ask_user,
chat search, and the rest of the changelog on top, and
maintains it now that upstream has gone quiet. If you find it useful, his
repo is where the idea came from.
Features
- Real agent loop — Read/Write/Edit/Bash/Glob/Grep/WebSearch/WebFetch and
subagents, in a per-chat working directory, streamed token by token. - Durable sessions — each chat maps to one Claude Code session that
survives Open WebUI restarts and function redeploys (in a container, setCLAUDE_CONFIG_DIRto a persistent path too); a cold start replays the
chat history once and resumes warm from then on. - Keyless clients — OpenAI-API callers that send no chat id (mobile apps,
voice) get a session too, fingerprinted from the conversation prefix. - Live status — tool activity, a heartbeat while a tool runs, subagents
grouped under their parent, and aDone · 1m42s · 74k/200k (37%) · 12 tools
line at the end; the message's ⓘ usage popover also gets the turn's
duration and the subscription's usage windows (session, weekly, per-model,
extra usage) with the time left until each resets. ask_user— the agent can pause and ask up to three multiple-choice
questions through Open WebUI's own form, and get the answers in the same turn.
The questions also appear in the reply, and typing an answer in the chat
works when the form is not showing.- Earlier chats —
search_chats/read_chatlet the agent look up the
calling user's past conversations (sqlite deployments; read-only; scoped to
that user). - Knowledge bases — a Workspace Model's attached knowledge becomes
search_knowledge/list/read/greptools the agent calls itself. - Images and artifacts — attached images are written to the workdir for
the agent to read; files the agent creates in the workdir (and in/tmp,
ifSCAN_TMP_ARTIFACTSis on) come back inline or as links. - Repo-rooted chats —
#repo:<name>on a first message runs the chat
inside an allowlisted repository (and loads itsCLAUDE.mdwhenSETTING_SOURCESincludesproject). - Output redaction — API keys, tokens and private keys are scrubbed from
the reply stream and status events before they reach the chat database. - Effort, budget, fallback — per-chat Reasoning Effort or per-turn
/effort <level>, a task token
budget the model paces itself against, and a fallback model. - Cold-resume guard (opt-in,
COLD_RESUME_GUARD) — a message to a large
chat idle past the prompt cache gets a cost warning and a pickup note for a
new chat instead of a full-price context rebuild;continueor/resume
goes ahead.
Requirements
- Open WebUI with the Functions framework; tested on 0.11.3.
- The Python Open WebUI runs on (3.11 or newer).
claude-agent-sdk0.2.152
or newer is installed by Open WebUI from the file'srequirements:line
and bundles the Claude Code CLI, so no Node.js and no separateclaude
install on the host. - An Anthropic API key from the Claude Console,
or, for a single-user install, a Claude Pro/Max/Team subscription (one-timeclaude setup-tokenon any machine with a browser). See
Which credential before choosing. - A directory the Open WebUI process can write, for
WORKDIR_ROOT.
Tested on macOS 15 (native install) and on Linux as root in the officialghcr.io/open-webui/open-webui:main image, with Open WebUI 0.11.3,claude-agent-sdk 0.2.152, Claude Code CLI 2.1.259.
Install
Five steps: a credential, the function, its valves, the toggle, a first message.
1. Get a credential
API key (recommended). Create one in the
Claude Console. Billed per token. This is the
path Anthropic's docs name for products built on the Agent SDK
(Legal and compliance,
"Authentication and credential use"), and the only one to use on any
instance with more than one user.
Subscription token (single-user installs only). On any machine with a
browser and Claude Code installed:
claude setup-token
Copy the long-lived OAuth token it prints. It authenticates your own
Pro/Max/Team subscription for your own use. Anthropic currently counts Agent
SDK usage against subscription limits and says it is still working out how
plans should cover it; its docs also say SDK-based products should use an API
key, and its position on subscription use outside Claude Code has changed
before. Treat this path as unsupported and subject to change. Never put a
subscription token on an instance other people use: routing other people's
requests through your plan is what Anthropic's terms forbid outright.
2. Add the function
In the UI: Admin Panel → Functions → +, paste the contents ofclaude_agent_pipe.py from the latest release
(the file on main may be ahead of the changelog), set the id to claude_code
and any name, Save.
Or over the admin API (bash, from the repository root):
curl -sf -X POST http://localhost:8080/api/v1/functions/create \
-H "Authorization: Bearer $OWUI_ADMIN_KEY" -H 'Content-Type: application/json' \
--data-binary @<(python3 -c 'import json;print(json.dumps({"id":"claude_code","name":"Claude Code","meta":{"description":"Claude Code agent loop"},"content":open("claude_agent_pipe.py").read()}))')
Either way, Open WebUI installs claude-agent-sdk from the file'srequirements: line on save. Allow a minute. Two things can go wrong:
- The save fails with "Error creating function": the install did not
succeed. Check the Open WebUI log for the pip error. - The host runs with
OFFLINE_MODE=true: Open WebUI skips the install
entirely. Install the SDK into Open WebUI's Python environment yourself
(pip install 'claude-agent-sdk>=0.2.152') and save again.
3. Set the valves
Functions → Claude Code → ⚙ Valves. Two matter on first install:
ANTHROPIC_API_KEY, orCLAUDE_CODE_OAUTH_TOKENon a single-user
install: the credential from step 1. If both are set the token wins and
the key is unset for the agent.WORKDIR_ROOT: a directory the Open WebUI process can write. The default
is/tmp/claude-agent-pipe; use a persistent path so sessions survive
reboots.
In a container, also set CLAUDE_CONFIG_DIR to a persistent path (for
example a subdirectory of a bind-mounted WORKDIR_ROOT). The Claude Code
CLI keeps its session transcripts there; left at the default they live in
the container's $HOME/.claude and vanish when the image is recreated.
Chat titles, tags and follow-up suggestions come from Open WebUI's Task
Model (Admin → Settings → Interface). With none set, Open WebUI asks the
chat's own model, and the pipe answers with one short tool-less call onTASK_MODEL (Haiku by default). A local Task Model costs nothing per chat.
Read the Security section before leaving PERMISSION_MODE at
its default. Every other valve is described in
docs/valves.md, generated from the code so it is always
current.
Over the API, valves are a JSON object posted toPOST /api/v1/functions/id/claude_code/valves/update.
4. Enable it
Flip the toggle on the Functions page (orPOST /api/v1/functions/id/claude_code/toggle), then pick Claude Code in
the model picker. MODELS adds one picker entry per extra model id; the API
model id is claude_code.claude-code.
5. Send a message
The status line shows Session: new chat, then tool activity, then the Done
line. A second message shows Session: resumed.
Uninstall
Disable and delete the function in Admin Panel → Functions, then removeWORKDIR_ROOT (and CLAUDE_CONFIG_DIR if you set one). Nothing else is
written outside those two directories and Open WebUI's own database.
How it works
pipe() is called once per turn. It resolves the working directory
(WORKDIR_ROOT/<chat_id>, or anon-<uuid> for keyless callers, or the#repo: target), decides whether a Claude Code session can be resumed, and
runs the turn through ClaudeSDKClient, translating the SDK's message stream
into text chunks and Open WebUI status events.
- Sessions. The chat id → session id map is persisted to
WORKDIR_ROOT/.sessions/<chat_id>.json; the in-process dict is only a
cache. Keyless callers are fingerprinted from their conversation prefix intoWORKDIR_ROOT/_sessions.json(entries expire after 30 days). A dead resume
id is dropped and the turn is retried cold. - Cold starts. When no session can be resumed, the prior turns are packed
into a<conversation_history>block ahead of the prompt (last 30
messages, 24k chars), so nothing is lost; the next turn resumes warm. - Tools. Everything Claude Code has, gated by
ALLOWED_TOOLSandPERMISSION_MODE, plus in-process MCP servers forask_user, chat search,
and knowledge. Those are registered withalwaysLoadbecause Claude Code
2.1 otherwise hides MCP tools behindToolSearchand the model never
finds them. - The agent's environment. The chat id is exported to the agent's
subprocess asHUB_CHAT_ID(only when one exists), so tooling the agent
runs can find out which chat it is serving. Nothing in this repo reads it. - Redaction. Every chunk and event passes through
_redact_secrets
before leavingpipe(). A hit is logged by kind, never by value.
Security
Read this before exposing the function to anyone but yourself.
bypassPermissionsis the default. Every user who can pick the model
runs arbitrary code as the Open WebUI process user, on the Open WebUI host,
with no prompts, using your credential. Treat the function as a shell for
everyone it is enabled for. Restrict it to admins in Open WebUI's model
access controls, or tightenPERMISSION_MODEandALLOWED_TOOLS.- Valves are global. One token, one
WORKDIR_ROOT, oneREPO_MAPfor
every user of the function. Two users cannot read each other's chats
(chat search is scoped by user id) but they share the filesystem and the
credential. This is a single-user or single-admin design, and with a
subscription token it must be single-user: see step 1 of the install. REPO_MAPhands out paths. Anyone who can start a#repo:chat runs
the agent inside that repository. It grants nothingbypassPermissions
did not already reach; it only sets the working directory.SETTING_SOURCESloads the host user's~/.claude(withuser),
which can define hooks that execute code. Leave it empty unless you
control the host account; see Persistent context.- What the redactor catches: prefix-shaped credentials (Anthropic,
OpenAI, Slack, GitHub, Google, AWS, 1Password, JWTs) and PEM private keys,
in the reply and in status events. What it cannot catch: bare UUID
tokens, passwords, hostnames, or anything that looks like prose. The
agent can stillcata secret into a file the chat never sees. Keep
credentials out ofWORKDIR_ROOTand the mapped repos, or give the agent
a hook (Claude Code'sPreToolUse) that refuses to publish them. - Running as root (the official Docker image does) makes the CLI refuse
bypassPermissionsunlessIS_SANDBOX=1; the pipe sets it. That is the
CLI's own sandbox flag, not an actual sandbox.
Report a vulnerability as described in SECURITY.md.
Persistent context
By default the pipe passes setting_sources=[] to the SDK: each chat starts
from a clean baseline and inherits nothing from the host user's ~/.claude/
or the workdir's .claude/. That is the safe default for anything shared.
On a single-user instance, standing instructions in ~/.claude/CLAUDE.md
(a host inventory, house rules) can be loaded into every chat with theSETTING_SOURCES valve:
| Value | Loads |
|---|---|
| (empty) | Nothing; isolated baseline (default). |
user |
~/.claude/CLAUDE.md and ~/.claude/settings.json. |
project |
The working directory's CLAUDE.md and .claude/settings.json; what makes #repo: chats pick up a repository's own instructions. |
user,project,local |
Both of the above plus .claude/settings.local.json. |
Unknown tokens are dropped. There is no way to load a CLAUDE.md without
also loading the settings.json next to it: that coupling is Claude Code's,
not the pipe's, and settings.json can define hooks that run shell commands,
permission grants, environment variables and MCP servers, for every chat,
as the Open WebUI process user, under bypassPermissions. WithCLAUDE_CONFIG_DIR set, user reads from that directory instead of~/.claude.
Open WebUI coupling
The pipe imports a few Open WebUI internals. Each import is wrapped so a
version that moved them degrades a feature instead of failing the turn:
| Import | Used for | If missing |
|---|---|---|
open_webui.env.DATA_DIR |
locating webui.db for chat search |
chat search reports itself unavailable (or set CHAT_DB_PATH) |
open_webui.models.chats.Chats.upsert_message_to_chat_by_id_and_message_id |
the ⓘ usage popover | no popover; the status line still shows context |
open_webui.models.files, open_webui.storage.provider.Storage |
artifact upload | files the agent creates are not linked |
open_webui.models.knowledge, open_webui.models.users, open_webui.retrieval.utils.query_collection, open_webui.main.app |
knowledge tools | the tools answer "unavailable" |
Chat search reads the sqlite chat table directly and is off by construction
on Postgres deployments (DATABASE_URL starting with postgres makes the
tools report unavailable).
Claude Code CLI coupling: alwaysLoad on SDK MCP servers (2.1.x), the
subagent tool being named Task or Agent (both handled), and ToolSearch
deferral. test_turn.py pins the name cases.
Files
src/*.py— the source, one module per concern, concatenated in filename
order into the built file. Edit these, never the built file.claude_agent_pipe.py— the built function: what you install and what
the redaction consumers slice.python3 build.pyregenerates it (anddocs/valves.md);python3 build.py --checkfails CI when either is stale.redact_stdin.py— a CLI over the pipe's redactor for job workers that
deliver agent output outside the chat stream (--known-envscrubs live
values by value, not just by shape). Optional; the pipe does not need it.test_*.py— standalone suites, stdlib only:python3 test_sessions.py,
optionally against a deployed copy (python3 test_sessions.py <pipe.py>).
See CONTRIBUTING.md for the development loop and
CHANGELOG.md for what changed on top of upstream.
Updating a deployed copy
Merging here deploys nothing: Open WebUI runs the copy stored in webui.db.
Paste the new claude_agent_pipe.py over the function in Admin → Functions,
or post it to POST /api/v1/functions/id/claude_code/update with the same
JSON shape as the create call. Valve state lives in webui.db and survives
redeploys. The author's own deploy wrapper (backup, post, verify parity, run
the suites against the deployed copy) lives in a separate homelab repo and is
not needed to use this one.
License
MIT, see LICENSE. Copyright is shared with the upstream author, as
the file says.
Yorumlar (0)
Yorum birakmak icin giris yap.
Yorum birakSonuc bulunamadi