mirror of
https://github.com/open-webui/open-webui.git
synced 2026-09-16 23:43:03 +00:00
Bug: request.app.state.TOOLS / FUNCTIONS are per-worker Python dicts.
Save on worker A does not touch worker B's cache, so worker B keeps
serving the stale compiled module until the process restarts.
utils/tools.py's invocation path was also completely cache-blind —
a bare dict.get with no content-hash fallback, so even same-worker
edits skipped the TOOL_CONTENTS check and served the first-seen
module forever.
Symptom: editing a tool in the Workspace UI, triggering a chat, and
watching the OLD code execute until backend restart. Confirmed on a
multi-worker deployment (UVICORN_WORKERS > 1) with Redis.
Fix — three parts:
1. backend/open_webui/utils/plugin_cache.py (new)
* publish_invalidation(app, kind, id)
- drops the local TOOLS/FUNCTIONS + _CONTENTS entry
- publishes {kind, id} on REDIS_PLUGIN_CACHE_CHANNEL
* plugin_cache_listener(app)
- subscribes on startup, drops caches on every inbound message
- mirrors the existing redis_task_command_listener pattern
* falls back to pure local invalidation when Redis is unconfigured
2. utils/tools.py
* invocation path swapped from `TOOLS.get(tool_id)` to
`await get_tool_module_from_cache(request, tool_id)`, which does
content-hash invalidation from DB. Covers edits that predate or
race the Redis pub/sub message.
3. routers/tools.py + routers/functions.py
* create / update / delete / toggle / toggle_global all call
publish_invalidation after the DB mutation commits.
* toggle_function_by_id + toggle_global_by_id gained a `request`
parameter they were missing.
main.py lifespan starts the listener alongside the task listener and
cancels it on shutdown. No new env vars — reuses REDIS_URL and
REDIS_KEY_PREFIX.
Single-worker deployments still work: publish_invalidation
unconditionally drops the local cache before trying to publish.
|
||
|---|---|---|
| .. | ||
| access_control | ||
| images | ||
| mcp | ||
| telemetry | ||
| actions.py | ||
| anthropic.py | ||
| asgi_middleware.py | ||
| audit.py | ||
| auth.py | ||
| automations.py | ||
| calendar.py | ||
| channels.py | ||
| chat.py | ||
| code_interpreter.py | ||
| embeddings.py | ||
| files.py | ||
| filter.py | ||
| groups.py | ||
| headers.py | ||
| logger.py | ||
| middleware.py | ||
| misc.py | ||
| models.py | ||
| oauth.py | ||
| payload.py | ||
| pdf_generator.py | ||
| plugin.py | ||
| plugin_cache.py | ||
| rate_limit.py | ||
| redis.py | ||
| response.py | ||
| sanitize.py | ||
| security_headers.py | ||
| session_pool.py | ||
| task.py | ||
| tools.py | ||
| validate.py | ||
| webhook.py | ||