18146 Commits
Author SHA1 Message Date
G30andGitHub 336d8841f4 fix: make embedded message content inert in the sidebar chat hover preview (#27770) 2026-08-24 07:36:44 -04:00
G30andGitHub fd7024f198 fix: log tool server connectivity failures without a traceback (#27757) 2026-08-24 07:35:57 -04:00
G30andGitHub 6b4131d1d7 fix: log terminal proxy connectivity failures as single lines and handle client disconnects (#27755) 2026-08-24 07:35:38 -04:00
Classic298andGitHub baeb2dfb83 fix: apply RDS IAM token auth to the pgvector engine (#27754)
With `DATABASE_ENABLE_IAM_TOKEN_AUTH=true` and `VECTOR_DB=pgvector`, startup failed at vector store initialisation with `fe_sendauth: no password supplied`, so the two features could not be used together.

`PgvectorClient` builds its own engine and never got the `do_connect` listener that refreshes the RDS IAM token, and the `ScopedSession` branch that would have reused the instrumented main engine is unreachable because `PGVECTOR_DB_URL` defaults to `DATABASE_URL` and is therefore never falsy.

The pgvector engine now goes through `enable_iam_token_auth()` like the main and Alembic engines. Since a token authenticates exactly one host/port/user, that function now attaches the listener only to engines pointing at the same target, so a `PGVECTOR_DB_URL` aimed at a separate database keeps the password from its own URL instead of having it overwritten; the skip is logged with both identities.

Fixes #27752
2026-08-24 07:35:19 -04:00
G30andGitHub 16b20c651d fix: restore per-item retrieval settings in the model editor knowledge section (#27686) 2026-08-24 07:34:30 -04:00
G30andGitHub 01452ff62f fix: mount the code editor in its own container so duplicated message renders don't collide (#27740) 2026-08-24 07:34:07 -04:00
Classic298andGitHub 9cf1a07960 fix: use the pooled client timeout for the Anthropic Messages passthrough (#27675)
* fix: use the pooled client timeout for the Anthropic Messages passthrough

The native `/api/v1/messages` passthrough still referenced `openai.AIOHTTP_CLIENT_TIMEOUT`, which stopped existing when `routers/openai.py` moved onto `session_pool.get_client_timeout()`. Every passthrough request therefore raised `AttributeError: module 'open_webui.routers.openai' has no attribute 'AIOHTTP_CLIENT_TIMEOUT'` before it was sent, and the surrounding handler turned that into a 502 "Open WebUI: Server Connection Error", so Anthropic-format clients such as Cline could not reach any model at all.

Use `get_client_timeout(stream=...)` like the OpenAI and Ollama proxies do, so the configured `AIOHTTP_CLIENT_TIMEOUT` applies and streaming requests additionally get the idle-read timeout.

Fixes #27595

* fix: authenticate native Anthropic requests with x-api-key

The Anthropic Messages passthrough and the token-count forwarding both build their upstream request through `get_anthropic_request_target`, which sends the connection key as `Authorization: Bearer <key>`. Anthropic's OpenAI-compatible `/chat/completions` endpoint accepts that, which is why the model works in the chat UI, but the native `/v1/messages` and `/v1/messages/count_tokens` endpoints do not: they require the key in `x-api-key` and reject a bearer token with 401 `Invalid bearer token` (and `jwt auth is not yet supported on count_tokens`). They also require an `anthropic-version` header, which was never sent.

For `api.anthropic.com` connections, send `anthropic-version` and move the key into `x-api-key`, dropping the bearer header. Connections using session, OAuth or Entra ID auth keep their token untouched, LiteLLM passthrough connections are unaffected, and admin-configured custom headers still win over both defaults.

Fixes #27695
2026-08-24 07:33:33 -04:00
Timothy Jaeryang Baek 8a170897ba refac 2026-08-24 07:28:09 -04:00
Timothy Jaeryang Baek 495296346e refac 2026-08-24 06:26:49 -04:00
Timothy Jaeryang Baek ef455fcef9 refac 2026-08-24 06:25:09 -04:00
Classic298andGitHub 091c44c621 perf: stop rescanning the whole response for tag boundaries on every streamed chunk (#28861)
Streamed responses are scanned for reasoning and code interpreter tags. To work out where the last complete tag ended, the scanner searched backwards from the start of the accumulated text on every chunk, once per tag set. Ordinary prose contains no angle bracket, so that search never stopped early and read the entire response back every time. The cost grows with the square of the response length, and this scanning is on unless a model turns it off.

The two positions are now carried forward as the text grows, so each chunk only scans the characters it added.

Measured on CPython 3.12, a 270 KB response streamed in 27000 chunks:

| response text | before | after |
|---|---|---|
| no newlines | 7690 ms | 40.6 ms |
| with newlines | 5695 ms | 41.7 ms |

The carried positions match a full rescan at every step of 36282 randomized replays, covering text with no markers, newlines only, dense markers, real tags and truncation part way through.
2026-08-24 05:11:32 -05:00
Timothy Jaeryang Baek 978d257214 refac 2026-08-24 05:40:53 -04:00
Classic298andGitHub 16c2a9eda4 fix: index the chat queries that make large SQLite instances unusable (#27663)
The timer scheduler polls once a second and cancels on every message send and chat open, the sidebar lists chats ordered by `updated_at`, and the folder badges count unread chats per folder. None of those could be served by an index, so each call read most of the `chat` table, and because `meta` sits after the chat payload column SQLite had to walk every row's overflow pages to get there. On a large history that stalls the sidebar, every chat switch and every send, and the idle poll alone burns about a quarter of a CPU core.

Timers now keep their due time in a dedicated `chat.timer_at` column behind a partial index, and the chat list, unread and unfinished-reply queries each get an index matching their filter and ordering. Existing pending timers are backfilled from their meta by the migration. Dropping the `internal` and `type` checks also makes a forked timer chat inert, where a fork used to copy `meta` verbatim and become a second claim target that could fire a duplicate timer.

Measured on SQLite, same rows returned:

| query | before | after |
|---|---|---|
| idle timer poll (2000 chats, 0.43 GB) | 170 ms | 0.04 ms |
| cancel on send and chat open (4000 chats, 377 MB) | 200 ms | 0.04 ms |
| sidebar chat list (15000 chats, 1.26 GB) | 157 ms | 1.8 ms |
| folder unread badges (15000 chats, 1.4 GB) | 54 ms | 0.2 ms |

PostgreSQL 17 serves all of them as index-only scans with no sort node. Exercised through fresh install, upgrade with seeded data, downgrade and re-upgrade on SQLite and PostgreSQL 17.

Fixes #27622
2026-08-23 16:11:54 -05:00
enixCodeandGitHub 29e8d7db67 i18n: fill missing fr-FR translations (#28951)
75 entries in the French locale had an empty value. i18next falls back to the
key when a value is empty, so French users were shown raw English strings
across permission settings, empty states, form placeholders and error toasts.

- fill those 75 entries; no key is added, removed or reordered
- follow the conventions already present in the file: "Entrez ..." for input
  hints, "Échec de ..." for failures, Chat -> Conversation, Token left as is
- interpolation placeholders preserved; no existing translation modified
2026-08-23 16:02:27 -04:00
Classic298andGitHub f73f09a3e0 refac: drop the redundant .keys() from two dict membership tests (#28859)
`x in d` and `x in d.keys()` are identical for a plain dict, so the `.keys()` call builds a throwaway view and reads as if it were doing something. Both sites operate on a plain dict: `combined` in `merge_and_sort_query_results` is a local `dict()`, and `ui_settings` comes from `UserSettings.model_dump()` where `ui` is annotated `dict | None` and is already guarded against None on the preceding line.

No behaviour change, and no measurable speedup either, so this is a readability cleanup rather than a performance one.

Sites where `.keys()` is load-bearing are left alone: the `list(d.keys())` snapshots taken before mutating during iteration, and the places where `.keys()` is the iteration or comprehension source rather than a membership test.
2026-08-23 16:02:09 -04:00
Timothy Jaeryang Baek b30b11d4c9 refac 2026-08-23 16:01:36 -04:00
Classic298andGitHub ac091273b7 fix: keep streamed text when a filter or provider sends non-string content (#28840)
A stream filter function, or a provider that puts something other than a string in a delta, makes the streaming handler concatenate a string with a non-string. That raises TypeError, and the broad handler wrapped around the whole per-chunk block swallows it at debug level and moves on. The chunk's text never reaches the message the user sees, and nothing above debug level says why.

The content and reasoning fields are now coerced to text once, where they are read off the delta, ahead of every consumer. The coercion is guarded on truthiness, so falsy values such as an empty list still skip the block exactly as before, and the accumulated content receives byte for byte what it received previously.

Checked against 14 delta shapes covering strings, empty values, numbers, booleans, None, lists, dicts and a content array: the truthiness gate and the accumulated content are identical before and after.
2026-08-23 15:35:53 -04:00
G30andGitHub 603e85c569 fix: apply connection prefix id to the model display name as well as the id (#28950) 2026-08-23 15:27:09 -04:00
Timothy Jaeryang Baek f64c0c87e8 refac 2026-08-23 15:14:43 -04:00
Timothy Jaeryang Baek e623c02acc refac 2026-08-23 15:06:30 -04:00
Timothy Jaeryang Baek 78f48a21ee refac 2026-08-23 14:40:48 -04:00
Timothy Jaeryang Baek fb4f476316 refac 2026-08-23 13:49:50 -04:00
Timothy Jaeryang Baek 2578174637 refac 2026-08-23 13:47:02 -04:00
Solaris-starandGitHub 5ee7140b4c fix: render code editor drawer above settings modal (#27648)
The ComfyUI workflow.json Edit drawer (CodeEditorModal -> Drawer) used
z-999, while the admin Settings dialog (Modal) uses z-9999. Since both
are appended to <body>, the drawer rendered behind the settings dialog
and appeared to open 'in the background' (#27647).

Add an optional zIndexClass prop to Drawer (default z-999, preserving
existing behaviour for all other callers) and pass z-99999 from
CodeEditorModal so the editor surfaces above any enclosing modal.
2026-08-23 13:44:41 -04:00
Timothy Jaeryang Baek bf3a58dbcd refac 2026-08-23 13:40:13 -04:00
Timothy Jaeryang Baek 5093a99389 refac 2026-08-23 13:36:53 -04:00
Timothy Jaeryang Baek 886248de36 refac 2026-08-23 13:33:51 -04:00
Classic298andGitHub d16d62d1f1 fix: duplicate checkbox markers when serializing note task lists (#27671)
Task lists in Notes serialized to markdown as `- [ ] [ ]` with the item text pushed onto a separate line after a blank line, so previewing or downloading a note produced a broken checklist, and checking an item left the second `[ ]` behind as plain text.

TipTap renders each task item as a checkbox inside a label plus a block-wrapped body. The GFM turndown plugin matches that checkbox and emits its own `[ ]`, which landed next to the marker the task item rule already writes, and the block wrapper left blank lines around the text that the old leading-whitespace strip could not remove.

Register a rule that drops the checkbox so the task item rule is the only source of the marker, and trim the block wrapper while indenting continuation lines so nested lists and code fences stay inside the item.

Fixes #26067
2026-08-23 13:31:46 -04:00
Classic298andGitHub 945c521ed2 refac: make the web search error message a plain constant (#28948)
The web search error message was a lambda with a passthrough branch that returned whatever it was handed. Since #28942 both call sites pass no arguments, so that branch is unreachable, and it is the trap that let a caller drop a raw exception object into an HTTP response body and turn an intended 400 into an unserialisable 500.

A plain string constant removes the trap and lines the message up with every other fixed message in that file. Behaviour is unchanged: the response detail comes out byte for byte identical, because the enum already overrides __str__ to render members as their value. Verified on Python 3.11 and 3.12, both producing the same string and the same JSON body.
2026-08-23 13:27:02 -04:00
Timothy Jaeryang Baek 842c1f9d67 refac 2026-08-23 13:10:07 -04:00
Timothy Jaeryang Baek 0fb542b376 refac 2026-08-23 12:59:59 -04:00
Classic298andGitHub fca3be5416 fix: web search failures return HTTP 500 with an empty body instead of 400 (#28942)
Any failure during a web search comes back to the client as a bare HTTP 500 with nothing in it. The handler tries to build a 400 whose detail is the caught exception object itself, FastAPI cannot serialise that into a response body, so rendering the error response fails and the request falls through to the generic 500 handler. In chat this surfaces as a web search that fails with no explanation at all, and the most common trigger is simply selecting a search engine without configuring its API key.

This routes the failure through the standard error formatter, which is what the sibling handler for content loading failures in the same function already does. Web search failures now return 400 with a readable message, and the exception itself keeps going to the server log exactly as before.

Passing str(e) into the response was the other option and was rejected: the rest of the backend deliberately keeps provider exception text out of client responses and in the log, and provider exceptions here can carry request details that should not be echoed back.
2026-08-23 12:59:25 -04:00
Classic298andGitHub 069f49fcd2 refac: remove unreachable rate limit handler from DuckDuckGo web search (#28943)
The DuckDuckGo search path catches RatelimitException from the ddgs library. That exception is defined by the library but never raised anywhere in it, checked against the pinned 9.14.4 and against 9.11.3, so the handler could never run. The two fallbacks around it were dead for the same reason: ddgs.text() returns a non-empty list or raises, so None and an empty list are not outcomes it can produce.

Removing all three leaves one call and changes nothing observable. A refused or rate limited search already came out as a failed search, with the error shown to the user and the traceback in the log, and it still does.

The backend argument is now passed as backend or 'auto' rather than conditionally omitted, because 'auto' is the library's own default for that parameter, so every configured value including unset and empty resolves exactly as before. Verified by running the old and the new function side by side against a stubbed library covering normal results, the domain filter, all four backend settings and a failing search, with identical results in every case.
2026-08-23 12:59:05 -04:00
Timothy Jaeryang Baek 3c66d639e3 refac 2026-08-23 12:33:34 -04:00
Timothy Jaeryang Baek c1c81f8127 refac 2026-08-23 03:40:40 -04:00
Timothy Jaeryang Baek f3f76095d1 refac 2026-08-23 02:34:08 -04:00
Timothy Jaeryang BaekandClassic298 4807866a1c refac
Co-Authored-By: Classic298 <27028174+Classic298@users.noreply.github.com>
2026-08-23 01:31:30 -04:00
Timothy Jaeryang Baek 7abe11346a refac 2026-08-22 09:13:13 -04:00
Timothy Jaeryang Baek d17f06a235 refac 2026-08-22 08:43:46 -04:00
Timothy Jaeryang Baek 1b3b9375bb refac 2026-08-22 08:21:53 -04:00
Timothy Jaeryang Baek d7d935275a refac 2026-08-22 08:18:38 -04:00
G30andGitHub 302ffc8b7e fix: skip the autocompletion debounce when the document shrank past the captured position (#28824) 2026-08-22 08:12:40 -04:00
G30andGitHub d1c207d091 fix: give docx and pptx previews in the file modal a definite frame height (#28878) 2026-08-22 08:11:29 -04:00
Timothy Jaeryang Baek ccbb3303f2 refac 2026-08-22 08:05:52 -04:00
G30andGitHub 883c7434fb fix: tolerate reasoning items without started_at when closing them at stream end (#28872) 2026-08-21 15:51:21 -07:00
Timothy Jaeryang Baek dcff244f9e refac 2026-08-21 15:50:50 -07:00
Aleix DorcaandGitHub 6a999f357b i18n: Update catalan translation.json (#28826) 2026-08-21 12:08:53 -07:00
G30andGitHub d2bc98eaeb fix: let the composer's model selector shrink so narrow containers keep every control visible (#28912) 2026-08-21 12:04:59 -07:00
Classic298andGitHub 7229fac0c4 refactor: remove the orphaned admin settings components (#28855)
Two components under the admin settings area are not imported by any route or component. The admin settings shell went dead when the admin settings route became a redirect into the settings modal, which imports every admin tab directly and carries its own tab list and search. The model selector beside it lost its last importer in a separate models refactor. Every child component the shell used is still imported by the settings modal, so nothing goes with them.

This removes around 590 lines that still turn up in every search across the admin area.
2026-08-20 13:37:53 -07:00
Classic298andGitHub c7f306031d refactor: remove the unreachable async half of the Mistral loader and an unused Datalab helper (#28839)
The Mistral OCR loader has a full async pipeline beside its synchronous one: an async load, its own upload, signed URL, OCR, delete and retry helpers, a pooled session and a batch loader on top. The only way in was the batch loader, which nothing calls, so the entire async half was unreachable. Everything that loads documents goes through the synchronous path, and the shared loader entry point runs it in a worker thread. The Datalab loader carries a public request status poller with no caller either, since its own load inlines the polling it needs.

With the async half gone, the retry classifier's two aiohttp branches can no longer be reached, since the only retried calls are synchronous, so those go with it along with the aiohttp import that existed solely to feed them, and a timeout attribute that nothing reads any more. The class docstring loses the three bullets that only described the removed pipeline, and four docstrings stop calling themselves the sync version of something that no longer has an async counterpart.

This removes around 350 lines and leaves one code path per loader instead of one live path and one that cannot be entered.
2026-08-20 13:14:14 -07:00