Commit graph

1829 commits

Author SHA1 Message Date
Classic298
00a245b9fa
fix: Manage Ollama ignores a connection's custom headers and auth type (#31489)
Listing, pulling, creating, copying and deleting models from the Manage Ollama dialog ignored the connection's custom headers and authentication type, so the dialog failed behind gateways such as Cloudflare Access and sent the key as a Bearer token even with the authentication type set to None. Checking a single connection's version sent no key at all. All of these, and the other requests to an Ollama connection such as text generation and embeddings, now use the connection's headers and authentication type, matching what verifying the connection and chatting already do.

Fixes #31487
2026-09-28 00:04:18 +04:00
Timothy Jaeryang Baek
bc2416c5db refac 2026-09-27 23:29:38 +04:00
Classic298
6e5e5fe6e9
feat: add a Tavily search depth setting (#31308)
Tavily web search always ran at Tavily's default depth (basic), because the search request never sent `search_depth`. The only Tavily depth control in Admin > Settings > Web Search, "Tavily Extract Depth", applies to the Extract API used by the web loader, never to search.

This adds `TAVILY_SEARCH_DEPTH` (env var and persisted setting, default `basic`) and a "Tavily Search Depth" select (ultra-fast, fast, basic, advanced) under the Tavily search engine settings. The value is sent as `search_depth` on every Tavily search request, so admins can set search and extract depth independently, for example fast search with advanced extraction.

The default matches Tavily's own default, so existing setups keep the same behaviour until the setting is changed.

Fixes #29891
2026-09-27 23:15:09 +04:00
Classic298
91fb33ef57
fix(retrieval): name the link when process/url cannot fetch it (#31354)
Attaching a link in chat or to a knowledge base that cannot be fetched (closed port, blocked by the fetch filter, an HTTP error such as 404) showed the toast "Error processing URL", which never said which link failed or that fetching it was the problem.

The fetch step now answers with "Could not read content from <url>", the same message process/web gives for a link it cannot read, so both endpoints report a dead link the same way. The too-large 413 still passes through unchanged, and a working link returns exactly what it did before.

The new handler covers only the fetch. Rewording the endpoint's existing catch-all would be one line, but that handler also receives database errors from the config and file lookups, which would then be reported as an unreadable link.

Related to #31347
2026-09-27 23:11:20 +04:00
Classic298
420b4a2797
fix(retrieval): name the link when process/web cannot fetch it (#31351)
Some checks are pending
Python CI / Ruff Format (3.11) (push) Waiting to run
Python CI / Ruff Format (3.12) (push) Waiting to run
Create and publish Docker images with specific build args / build (map[arch:linux/arm64 runner:ubuntu-24.04-arm], map[build_args:USE_SLIM=true free_disk:false name:slim suffix:-slim]) (push) Waiting to run
Create and publish Docker images with specific build args / build (map[arch:linux/amd64 runner:ubuntu-latest], map[build_args: free_disk:false name:main suffix:]) (push) Waiting to run
Create and publish Docker images with specific build args / build (map[arch:linux/amd64 runner:ubuntu-latest], map[build_args:USE_CUDA=true USE_CUDA_VER=cu126 free_disk:true name:cuda126 suffix:-cuda126]) (push) Waiting to run
Create and publish Docker images with specific build args / build (map[arch:linux/amd64 runner:ubuntu-latest], map[build_args:USE_CUDA=true free_disk:true name:cuda suffix:-cuda]) (push) Waiting to run
Create and publish Docker images with specific build args / build (map[arch:linux/amd64 runner:ubuntu-latest], map[build_args:USE_OLLAMA=true free_disk:false name:ollama suffix:-ollama]) (push) Waiting to run
Create and publish Docker images with specific build args / build (map[arch:linux/amd64 runner:ubuntu-latest], map[build_args:USE_SLIM=true free_disk:false name:slim suffix:-slim]) (push) Waiting to run
Create and publish Docker images with specific build args / build (map[arch:linux/arm64 runner:ubuntu-24.04-arm], map[build_args: free_disk:false name:main suffix:]) (push) Waiting to run
Create and publish Docker images with specific build args / build (map[arch:linux/arm64 runner:ubuntu-24.04-arm], map[build_args:USE_CUDA=true USE_CUDA_VER=cu126 free_disk:true name:cuda126 suffix:-cuda126]) (push) Waiting to run
Create and publish Docker images with specific build args / build (map[arch:linux/arm64 runner:ubuntu-24.04-arm], map[build_args:USE_CUDA=true free_disk:true name:cuda suffix:-cuda]) (push) Waiting to run
Create and publish Docker images with specific build args / copy-to-dockerhub (, main) (push) Blocked by required conditions
Create and publish Docker images with specific build args / copy-to-dockerhub (-cuda, cuda) (push) Blocked by required conditions
Create and publish Docker images with specific build args / copy-to-dockerhub (-cuda126, cuda126) (push) Blocked by required conditions
Create and publish Docker images with specific build args / copy-to-dockerhub (-ollama, ollama) (push) Blocked by required conditions
Create and publish Docker images with specific build args / copy-to-dockerhub (-slim, slim) (push) Blocked by required conditions
Frontend Build / Format & Build (push) Waiting to run
Frontend Build / Unit Tests (push) Waiting to run
Create and publish Docker images with specific build args / build (map[arch:linux/arm64 runner:ubuntu-24.04-arm], map[build_args:USE_OLLAMA=true free_disk:false name:ollama suffix:-ollama]) (push) Waiting to run
Create and publish Docker images with specific build args / merge (map[name:cuda suffix:-cuda]) (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge (map[name:cuda126 suffix:-cuda126]) (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge (map[name:main suffix:]) (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge (map[name:ollama suffix:-ollama]) (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge (map[name:slim suffix:-slim]) (push) Blocked by required conditions
Create and publish Docker images with specific build args / notify-helm-charts (push) Blocked by required conditions
A link whose host refuses the connection, such as a closed port, still came back from POST /api/v1/retrieval/process/web as "Error querying knowledge base", so the caller was told the knowledge base failed when the link was the problem.

The web loaders log a failed fetch and return no documents. That empty result then failed while being saved to the vector store, and the save error was the one reported.

process_web now answers with the existing "Could not read content from <url>" 400 as soon as the loader returns no documents, the same message a link refused by the fetch filter already gets. The check sits in the endpoint so web search and the other users of the loaders keep their current behaviour.

With process=false or embedding bypassed, an unreachable link now gets the same 400 where it used to return 200 with empty content.

Fixes #31347
2026-09-26 07:28:13 +04:00
Classic298
42cd4f0ec0
refactor: check shared chat access before loading the snapshot on clone (#30388)
The clone endpoint now resolves and checks the share before reading its snapshot, matching the order used by the shared chat view endpoint.
2026-09-26 07:20:14 +04:00
G30
9836367ab2
fix: apply the group filter to the Analytics hourly chart (#30978) 2026-09-24 12:11:13 -04:00
Timothy Jaeryang Baek
f412538756 refac 2026-09-24 12:10:30 -04:00
G30
a046e07044
fix: save a file edit made through write access to a shared knowledge base (#30448) 2026-09-23 23:05:37 -05:00
Classic298
f7ce4024d6
fix: keep requested MCP OAuth scope when DCR response omits it (#30384)
MCP tool servers using OAuth 2.1 with dynamic client registration authorized without any scope when the authorization server left scope out of its registration response, which RFC 7591 allows (Atlassian and Notion do). Consent completed and the tool showed as connected, but the issued token lacked the scopes the resource requires, so every tool call was refused. Discovered scopes and the custom OAuth Scopes field were both affected.

The stored client now falls back to the scope sent in the registration request when the response has none. A scope the server does return is kept as is.

Connections registered before this fix already have a null scope stored. The protected resource metadata recovery that static-credential clients already use now also runs for dynamically registered clients, so those connections pick up the discovered scopes on the next load without registering again.

Fixes #29967
2026-09-23 23:52:29 -04:00
Classic298
744ce6cbfe
fix: send the configured USER_AGENT on the Attach Webpage pre-check (#30385)
Attach Webpage fails with 403 Forbidden on sites that reject the bare aiohttp user agent, Wikipedia among them, even when USER_AGENT is set. The web loader sends USER_AGENT, but the request that runs first to decide whether the URL is a page or a file does not, so the attachment fails before the loader is ever reached.

The pre-check now sends USER_AGENT as the request User-Agent when it is set. With it unset the request is unchanged and keeps the aiohttp default.

Verified against the real _fetch_url with https://en.wikipedia.org/wiki/OpenAI: 403 before, page detected after; USER_AGENT unset still returns the same 403 as before, and a direct PDF URL is still detected as a file.

Fixes #29617
2026-09-23 23:50:08 -04:00
Classic298
2867f7225b
refac: sync channel room on member removal (#30446)
Removing members from a group or DM channel now also removes their sessions from the channel room, matching how access grant changes are handled.
2026-09-23 23:43:20 -04:00
Classic298
a973e77cfa
refac: folder file checks (#30442)
Folder file entries are also checked against the user making the change.
2026-09-23 23:36:15 -04:00
G30
0ab3a2b335
fix: restore tag rows after unarchiving all chats and remove unused ones after deleting all chats (#30453) 2026-09-23 23:32:54 -04:00
G30
7e2bea8253
fix: load the leaderboard activity chart for model ids that contain a slash (#30456) 2026-09-23 23:32:42 -04:00
Timothy Jaeryang Baek
e93a59f4dd refac
Co-Authored-By: Classic298 <27028174+Classic298@users.noreply.github.com>
2026-09-22 13:13:22 -04:00
Classic298
0a2e9a42e7
fix: detect the real image type of bare base64 generated images (#30359)
Generated images that come back as bare base64 (OpenAI b64_json, Gemini
bytesBase64Encoded and inlineData, Automatic1111) were always stored as
generated-image.png with content type image/png, even when the provider
returned JPEG or WebP, for example with {"output_format": "jpeg"} in the
OpenAI extra params. The image still rendered because browsers read the
bytes, but the download name, the served Content-Type and the type sent
along on a later image edit were wrong.

Bare base64 carries no format, so the type is now read from the bytes with
Pillow, the same way the file already inspects images elsewhere. A response
that is not an image at all now fails the generation instead of storing a
broken png. The file extension comes from the module's own extension map
first, because the Python 3.11 Docker image has no mime database entry for
WebP and would otherwise name the file generated-imageNone.

Fixes #29948
2026-09-22 11:35:59 -04:00
Timothy Jaeryang Baek
ee3ece1e2b refac 2026-09-21 14:43:04 -04:00
Timothy Jaeryang Baek
30881dbcc9 refac 2026-09-21 11:09:09 -04:00
Timothy Jaeryang Baek
3fc1146c13 refac 2026-09-21 10:44:37 -04:00
Timothy Jaeryang Baek
e8bd0661d3 refac 2026-09-21 10:30:53 -04:00
Timothy Jaeryang Baek
754c4b5762 refac
Co-Authored-By: Classic298 <27028174+Classic298@users.noreply.github.com>
2026-09-21 10:25:20 -04:00
G30
c864e3ae7c
fix: stop asking for chat variables a model's system prompt no longer declares (#30173) 2026-09-21 10:23:21 -04:00
Timothy Jaeryang Baek
a9541c18ca refac 2026-09-21 10:19:59 -04:00
Timothy Jaeryang Baek
5fb869db22 refac 2026-09-21 08:59:39 -04:00
Classic298
7fa8673296
fix: resolve this instance's own file URLs in edit_image regardless of the URL host (#29691)
The native edit_image tool fails with "400: [ERROR: Error loading image]" whenever the model hands it an absolute URL for an image Open WebUI already stores. Such a URL is treated as local only when its host string matches the incoming request's host exactly, so a default-port form, a container name or any host the model composed itself falls through to an outbound HTTP fetch instead. That fetch asks /api/v1/files/{id}/content without a session, gets a 401, and the user sees the generic 400.

Match the file URL on its path and let the existing local branch resolve it. Fetching that endpoint over the network can never succeed for a local or a remote instance, because it requires an authenticated user, so the host comparison only decided which way the request failed. Access control is unchanged: the local branch still goes through get_file_content_by_id, which enforces owner, admin or shared access.

Fixes #29220
2026-09-21 08:32:38 -04:00
Timothy Jaeryang Baek
7f703dcc98 refac 2026-09-21 08:31:27 -04:00
Timothy Jaeryang Baek
dc4f5da5a0 refac 2026-09-21 08:28:36 -04:00
G30
65a3a115eb
fix: keep the speech-to-text extension allowlist when the Audio settings are saved (#30208) 2026-09-19 17:36:38 -04:00
Timothy Jaeryang Baek
10d1cfe637 refac 2026-09-19 17:26:18 -04:00
G30
b64cb06043
fix: let a user without the chat delete permission delete a folder while keeping its chats (#30163) 2026-09-19 16:07:34 -05:00
G30
57063aaad8
fix: dropping a folder onto the folder it is already in no longer fails with Folder already exists (#30169) 2026-09-19 15:41:10 -05:00
G30
2690d04cac
fix: clean up orphan tags for the chat's owner, not the admin, when an admin deletes another user's chat (#30171) 2026-09-19 15:40:58 -05:00
G30
9e293a58ea
fix: label the search modal's archive action Unarchive for archived chats and report what happened (#30177) 2026-09-19 10:02:11 -05:00
G30
dbe538c033
fix: keep a note's sharing when a collaborator saves it (#30175) 2026-09-19 10:00:39 -05:00
Classic298
52cd298411
fix: allow forking a chat that holds a stale unfinished message (#30131)
Forking returned 409 "Wait for the current response to finish before forking." forever once any assistant message anywhere in the chat was left at done = false, with nothing generating and even when that message sat on a branch that was not being forked. Interrupted turns leave the flag behind and nothing clears it, so an affected chat could never be forked again.

The endpoint now refuses only while a task is actually running, which is the check /compact has always relied on by itself. A message still unfinished on the forked branch is marked done in the copy so the fork does not open showing a spinner, while a turn paused waiting on tool approval keeps its unfinished state and its pending call so the fork still shows the prompt. The source chat is left untouched either way.

The client-side check had the same shape of bug: it read history.currentId instead of the message actually being forked, so a paused last turn also blocked forking earlier, finished messages. It now reads the message it is about to fork.

Fixes #30128
2026-09-18 19:31:20 -04:00
Timothy Jaeryang Baek
64bbdf7a73 refac 2026-09-18 19:28:40 -04:00
Classic298
d5cacb3c0a
fix: convert forced tool_choice and non-streaming tool calls for Responses API connections (#30095)
On a connection with API type "responses", a forced tool choice sent in
the Chat Completions shape was forwarded to the provider unchanged, so
providers that validate the Responses API schema rejected the request.
A non-streaming reply that carried a function call was also flattened
to empty text with finish_reason "stop", so API clients never saw the
tool call.

The request converter now flattens a forced function choice to the
Responses API shape, and the result converter turns every function_call
output item into a Chat Completions tool_calls entry, mirroring the
existing Responses output mapping in utils/misc.py.

Fixes #30085
2026-09-18 19:18:26 -04:00
Classic298
9ee810ad09
feat: add Staan as a native web search provider (#30138)
European deployments that need EU data residency have no hosted web search option out of the box: every plug-and-play provider shipped today is US-based, and the only sovereign alternative is self-hosting SearXNG, which means running and maintaining that infrastructure yourself. Staan (staan.ai) is a European search API with EU data residency, so adding it gives those deployments a drop-in choice.

It is configured like any other provider, through the admin UI or STAAN_API_KEY, STAAN_MARKET and STAAN_MAX_SNIPPETS. Market sets the region and language of the results and defaults to en-us. Max snippets asks Staan to fetch each result page and return semantically scored chunks of it, which get merged into that result's snippet so retrieval has more to work with; leaving it at 0 uses the plain search endpoint.

Wired the same way as Tavily and Exa, with domain filtering going through the shared get_filtered_results.

Requested in #26006.
2026-09-18 10:38:36 -05:00
Timothy Jaeryang Baek
ad9da98168 refac 2026-09-17 20:11:05 -04:00
Classic298
48fb2b84ba
refac: derive a forked chat's folder from the caller's write access (#30069)
A fork copied the source chat's folder id unchanged. It now keeps that folder only when the caller has write access to it, matching what chat creation and chat moves already do, and is created outside any folder otherwise.
2026-09-16 15:42:03 -04:00
Classic298
82da9093aa
fix: default converted Responses API function tools to strict=false (#30046)
Fixes #27750

When a model runs on the Responses API, every Chat Completions style tool (workspace tools, MCP and OpenAPI servers) is converted to the Responses tool shape. Only an explicit strict value was carried over, so tools without one were sent with strict omitted. Chat Completions defaults strict to false, the Responses API defaults it to true, so the conversion silently switched loose schemas into strict mode. The model then filled every optional property (empty strings, zeros, false, empty arrays) and combined mutually exclusive filters, and those arguments went to the tool unchanged. Search and filter tools received meaningful values where omission was intended and rejected the call or returned wrong results.

The converter now defaults strict to false when the source tool does not set it. Explicit true or false values are still preserved and already native Responses tools are passed through untouched, matching what the same tool definition gets on Chat Completions.
2026-09-16 10:19:08 -04:00
Timothy Jaeryang Baek
e9a0164690 refac 2026-09-16 00:34:24 -04:00
Timothy Jaeryang Baek
6d8e63e366 refac 2026-09-16 00:02:48 -04:00
Timothy Jaeryang Baek
66addbd6b4 refac 2026-09-15 22:47:03 -04:00
Classic298
d2e62db69b
fix: stop the sign-in rate limiter blocking the loop and leaking memory (#29977)
A slow Redis freezes the whole worker during sign-in, not just the user signing in. RateLimiter held a synchronous redis-py client and signin called is_limited inline from a coroutine, so every attempt did blocking round trips on the event-loop thread, with REDIS_SOCKET_TIMEOUT defaulting to None so nothing bounded the wait. Its Redis methods are now async and take the handle as their first argument, and both handlers pass request.app.state.redis, the async client the lifespan already creates. Building one in the limiter instead would pin its pooled connection to the first event loop that used it.

Without Redis, which is the default single-instance setup, the fallback store leaked. It was keyed by the rate-limit key and pruned a key's expired buckets only when that same key was checked again, so a login email never seen again was never reclaimed, and that email comes straight from an unauthenticated request body. It is now keyed by bucket, so one prune drops every key an expired bucket held, and it lives on the instance: pruning uses the per-instance num_buckets, so a shared store would let a limiter with a short window delete buckets a longer-windowed one still needs.

With a Redis costing a second per call, the widest event-loop tick gap drops from 2.010s to 0.010s and a concurrent request is answered at 0.05s instead of 2.05s, at no cost to the caller's own latency. Across 20,000 distinct keys the store goes from 40,000 entries and 6.4 MB, growing linearly, to a flat 1,004 entries and 100 KB. Rate-limiting decisions are unchanged across 700,000 randomised calls over 14 window, bucket and limit combinations, against a real Redis and the in-memory fallback alike, and sign-in still returns its first 429 on attempt 16.

Two behaviour changes worth naming. Pruning is now global rather than per key, so a wall clock that jumps forward past a full window and back forgets a hit it previously kept. The two limiters also stop sharing a store, which previously let a sign-in attempt with an IP-shaped email touch the token-exchange limiter's counters.
2026-09-13 20:28:41 -05:00
Classic298
22d522c558
fix: drop a deleted plugin's source from the content cache (#29983)
Deleting a tool or a function leaves its full source text in memory for the life of the process. Each delete handler pops the module cache and leaves the matching content cache untouched, so the code of every plugin ever deleted stays resident.

Both handlers now pop the content cache next to the module cache. Measured over 200 deletes of an 8.8KB plugin: 1.78 MB of source retained per kind before, nothing after.

This is memory only, never a stale module. A cache hit needs the id present in both caches and delete already popped the module cache, so the leftover source could not have produced one.

The two routers are one change because it is the same pair of lines at the same point in sibling handlers, with no per-site reasoning and no reason to revert one without the other.
2026-09-13 20:21:40 -04:00
Timothy Jaeryang Baek
6786ae1797 refac 2026-09-13 20:00:29 -04:00
Timothy Jaeryang Baek
51bb8cb142 refac 2026-09-12 18:53:22 -04:00
Timothy Jaeryang Baek
199490eadb refac 2026-09-12 18:16:19 -04:00