Commit Graph

6393 Commits

Author SHA1 Message Date
Timothy Jaeryang Baek
655afbe90b refac 2026-06-29 11:59:50 -05:00
Timothy Jaeryang Baek
6f8221df58 refac 2026-06-29 11:59:29 -05:00
Timothy Jaeryang Baek
ff5cec43bd refac 2026-06-29 11:56:00 -05:00
Timothy Jaeryang Baek
10558173fb refac 2026-06-29 11:53:29 -05:00
Timothy Jaeryang Baek
754787f43d refac 2026-06-29 11:51:45 -05:00
Timothy Jaeryang Baek
40b655e99e refac 2026-06-29 11:44:21 -05:00
Timothy Jaeryang Baek
b696c5deff refac 2026-06-29 11:38:59 -05:00
Timothy Jaeryang Baek
815446d5bb refac 2026-06-29 11:33:49 -05:00
Timothy Jaeryang Baek
a146e17bdc refac 2026-06-29 11:33:32 -05:00
Timothy Jaeryang Baek
2c4e1fce8f refac 2026-06-29 11:13:36 -05:00
Timothy Jaeryang Baek
81e245548d refac 2026-06-29 11:00:53 -05:00
Timothy Jaeryang Baek
2d3035a112 refac 2026-06-29 10:54:53 -05:00
Timothy Jaeryang Baek
39837e0a3a refac 2026-06-29 10:51:52 -05:00
G30
8c7428122b fix(rag): allow special tokens in document text during chunk measurement (#26210)
Documents (especially AI/LLM documentation) legitimately contain special token strings like <|endoftext|> as literal text. The tiktoken encoder raises a ValueError when encountering these during chunk size measurement in merge_docs_to_target_size(), preventing the entire file from being indexed. Pass disallowed_special=() to encoding.encode() to treat all text as normal content.
2026-06-29 10:43:24 -05:00
Juan Calderon-Perez
51246bcb31 perf(backend): offload blocking calls in async paths to threads (#26381)
Audit of asyncio.sleep vs time.sleep and event-loop-blocking calls:

- utils/plugin.py: run pip `install_frontmatter_requirements`
  (subprocess.check_call) via asyncio.to_thread in load_tool_module_by_id,
  load_function_module_by_id, and install_tool_and_function_dependencies.
- retrieval/utils.py: move the synchronous SSRF-guarded requests probe and
  loader.load() in get_content_from_url into a sync helper run via
  asyncio.to_thread.
- routers/audio.py: write uploaded audio to disk off the event loop in
  transcription().
- routers/pipelines.py: write uploaded pipeline file off the event loop in
  upload_pipeline().

The existing time.sleep call sites are all in genuinely synchronous
functions (sync requests/DB drivers/daemon threads) with async
counterparts that already use asyncio.sleep, so no time.sleep -> asyncio.sleep
changes were needed.


Claude-Session: https://claude.ai/code/session_01LXR5bYfsfSS42RGHQZu2Ta

Co-authored-by: Claude <noreply@anthropic.com>
2026-06-29 10:42:26 -05:00
Timothy Jaeryang Baek
eb53281c9a refac
Co-Authored-By: Classic298 <27028174+Classic298@users.noreply.github.com>
2026-06-29 06:08:31 -05:00
Timothy Jaeryang Baek
a285a390c1 refac 2026-06-29 06:01:05 -05:00
Classic298
75df948f34 feat: forward client User-Agent to model backends via {{USER_AGENT}} placeholder (#26333)
Adds a {{USER_AGENT}} custom-header placeholder that relays the inbound
client's User-Agent to upstream model backends, so providers see the real
client instead of Open WebUI's internal aiohttp UA. This makes upstream
usage/cost attribution and backend telemetry possible, and is opt-in
per-connection (no global flag): admins add {{USER_AGENT}} to a connection's
custom headers in Admin > Settings > Connections.

The placeholder is sourced from the live inbound request (with a metadata
fallback for detached RAG/tool calls), so it resolves on every prompt-sending
path, not just chat completions:

- OpenAI completions, Responses API, and proxy — all route through
  get_headers_and_cookies, which now passes the request into get_custom_headers.
- Anthropic Messages API (/api/v1/messages) — already covered, it delegates
  to the chat completion handler.
- Ollama (/api/chat, /v1/completions, /v1/chat/completions, /v1/messages,
  /v1/responses) — previously had no custom-header support at all; send_request
  now applies per-connection custom headers (with templating) for every
  Ollama prompt endpoint.

Custom headers are applied after the built-in user-info headers so explicit
admin-configured headers take precedence. The other existing placeholders
({{CHAT_ID}}, {{USER_ID}}, ...) now also work on the newly covered paths.

Frontend: the connection editor's Headers field is now shown for Ollama
connections too (previously gated to non-Ollama), so the placeholder can be
configured there.

Ref: open-webui/open-webui#26159
2026-06-29 05:58:19 -05:00
Timothy Jaeryang Baek
260f3c3a22 refac 2026-06-29 05:56:08 -05:00
Timothy Jaeryang Baek
b0487dd6dd refac 2026-06-29 05:49:54 -05:00
Timothy Jaeryang Baek
70e4ffcc65 refac 2026-06-29 05:49:49 -05:00
Timothy Jaeryang Baek
0883638027 refac 2026-06-29 05:47:21 -05:00
Timothy Jaeryang Baek
ee5de69e37 refac 2026-06-29 05:46:55 -05:00
Timothy Jaeryang Baek
0cc331d1c6 refac 2026-06-29 05:46:51 -05:00
Timothy Jaeryang Baek
41f256321b refac 2026-06-29 05:46:47 -05:00
Timothy Jaeryang Baek
44b9463498 refac 2026-06-29 05:44:47 -05:00
Timothy Jaeryang Baek
396d9ac181 refac 2026-06-29 05:27:51 -05:00
Timothy Jaeryang Baek
0eba3df119 refac 2026-06-29 05:21:49 -05:00
Timothy Jaeryang Baek
fa76764c3b refac 2026-06-29 05:16:57 -05:00
Timothy Jaeryang Baek
83ec36cd38 refac 2026-06-29 05:15:55 -05:00
Timothy Jaeryang Baek
cdd7b88bec refac 2026-06-29 05:14:57 -05:00
Timothy Jaeryang Baek
8927c9bb3d refac 2026-06-29 05:14:34 -05:00
maco
c65b29ec0f fix: handle MCP resource content type blob and URI variants (#25260) 2026-06-29 05:12:11 -05:00
Timothy Jaeryang Baek
0be069c165 refac 2026-06-29 05:04:00 -05:00
Timothy Jaeryang Baek
416baef813 refac 2026-06-29 04:57:28 -05:00
Timothy Jaeryang Baek
45fea34bd0 refac
Co-Authored-By: Pedro Machado <45520277+pedrofcm@users.noreply.github.com>
2026-06-29 04:46:56 -05:00
Timothy Jaeryang Baek
953432b5fe refac 2026-06-29 04:43:40 -05:00
Timothy Jaeryang Baek
e5b5e5917b refac 2026-06-29 04:43:08 -05:00
Timothy Jaeryang Baek
67c9de8efd refac 2026-06-29 04:42:59 -05:00
Timothy Jaeryang Baek
a3b487422d refac 2026-06-29 04:42:02 -05:00
Alberto de la Cruz
718b226177 fix: discover MCP Protected Resource Metadata when server does not return 401 (#25980)
get_protected_resource_metadata() only attempted RFC 9728 discovery when
the anonymous `initialize` probe returned 401 with a WWW-Authenticate
header. Some remote MCP servers — notably Google's Gmail/Drive/Calendar
MCPs (gmailmcp.googleapis.com, etc.) — answer 200 to an anonymous
initialize, so OAuth scope and authorization-server discovery silently
failed and connections to them could not be established.

Run the discovery regardless of the probe's HTTP status: prefer the
resource_metadata URL from WWW-Authenticate when present, and otherwise
fall back to the RFC 9728 §4.2 well-known URIs. The trade-off is a couple
of extra well-known GETs during MCP connection setup for servers that
expose no PRM document; behavior for 401-responding servers is unchanged.
2026-06-29 04:34:36 -05:00
Timothy Jaeryang Baek
f8ec63203c refac 2026-06-29 04:31:43 -05:00
Timothy Jaeryang Baek
fd7a59d37a refac 2026-06-29 04:27:48 -05:00
Timothy Jaeryang Baek
ab84bbf08c refac 2026-06-29 04:19:33 -05:00
Timothy Jaeryang Baek
d3676b4f71 refac 2026-06-29 04:11:46 -05:00
Timothy Jaeryang Baek
c4688b958d refac 2026-06-29 04:03:06 -05:00
Timothy Jaeryang Baek
33b91bd8ae refac 2026-06-29 03:58:00 -05:00
Classic298
0fc630b34b fix: per-user model cache used static key= (cross-user model exposure) (#25783)
routers/openai.py and routers/ollama.py decorated get_all_models with
`@cached(key=lambda _, user: ...)`. In aiocache 0.12, `key=` is a STATIC
cache key: get_cache_key returns `self.key` verbatim (the lambda object)
without calling it, so every caller collides to ONE shared entry within
the TTL. The intended per-user namespacing never happened — one user's
permission-filtered model list could be served to another user (or an
anonymous caller) during the cache window.

The per-call hook is `key_builder=` (called as key_builder(func, *args,
**kwargs)). Switch both sites to key_builder with a (func, request,
user=None) signature so the key is built per call. user=None mirrors
ollama's optional-user signature and stays correct whether user is passed
positionally, as a kwarg, or omitted.

Verified offline: old form returns the same key object for distinct users
(collision); new form yields distinct openai_all_models_<id> /
ollama_all_models_<id> keys, and the unauthenticated base key when no user.
These two were the only @cached(key=lambda ...) sites in the backend.
2026-06-29 03:51:02 -05:00
G30
ee11069ef2 perf(ollama): offload multi-GB model file I/O with asyncio.to_thread (#25829)
The Ollama model upload and download handlers perform three stages
of sync file I/O on multi-GB model files inside async handlers:

1. Persist upload — file.file.read() + write() in a loop
2. SHA-256 hash — calculate_sha256() reads the file sequentially
3. Read for blob push — open().read() loads entire model into memory

All three stages block the event loop for the duration of the I/O.
For a 4GB model, each stage freezes the event loop for 10+ seconds
while every other user's request stalls.

Wrap each blocking stage in asyncio.to_thread() in both handlers:

- upload_model(): persist upload, calculate_sha256, blob read
- download_file_stream(): calculate_sha256, blob read

Benchmark (full pipeline: write + SHA-256 + read back, 3 trials):
- 200MB: max jitter 145ms → 1ms (145x improvement)
- 500MB: max jitter 1,026ms → 1ms (1,026x improvement)

File I/O is pure I/O — no GIL contention. asyncio.to_thread()
completely eliminates event loop blocking.
2026-06-29 03:48:48 -05:00
Classic298
a66477b710 fix: bind channel thread parent/reply to the URL channel (#25766)
GET /api/v1/channels/{id}/messages/{message_id}/thread authorized only the URL channel, but get_messages_by_parent_id() appended the thread parent (loaded by id) without checking it belonged to that channel, so a caller could read a message from a channel they cannot access by passing its id as the thread root. Require the parent to be in the requested channel before returning it, and reject a posted parent_id/reply_to_id that does not belong to the channel.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-29 03:46:50 -05:00