Commit Graph

18048 Commits

Author SHA1 Message Date
Timothy Jaeryang Baek f1a64ccfc2 refac 2026-08-17 00:35:12 -07:00
Timothy Jaeryang Baek 76d0160295 refac 2026-08-17 00:31:01 -07:00
Timothy Jaeryang Baek 9550731cc1 refac 2026-08-17 00:24:47 -07:00
Classic298 189c14fc4d fix: match both JSON text spellings when searching serialised JSON columns (#28399)
Three searches LIKE against cast(json_col AS text), which means they have to match
bytes a JSON encoder wrote. Encoders disagree on non-ASCII: stdlib escapes it to
\uXXXX, orjson writes it raw. Which one produced a row depends on the codec in force
when it was written, so any single pattern finds only half the table.

models.py hard-codes the stdlib spelling, with a comment asserting SQLite stores
JSON via json.dumps(ensure_ascii=True). Model.meta is a JSONField, which has
serialised through JSONCodec since ENABLE_ORJSON was introduced, so on that setting
it stores raw UTF-8 and the escaped pattern matches nothing: non-ASCII workspace
model tag search is broken today. prompts.py and automations.py hard-code the
opposite spelling and miss rows written the other way.

json_text_variants returns both spellings a string can take inside serialised JSON,
collapsing to one for ASCII, and the three call sites OR over them. Rows written
under either setting are now found under either setting, which also covers a
database holding a mix of the two.

Case handling is unchanged. models.py keeps matching non-ASCII tags case-sensitively
on SQLite, whose LOWER() is ASCII-only and would not fold the stored text the way
str.lower() folds the tag. ASCII tags collapse to a single variant and take exactly
the query they took before.

Verified on SQLite across every combination of codec-that-wrote-the-row and
codec-the-app-is-running, for an ASCII and a CJK tag, over all three call sites: 24
of 24 match, against 12 of 24 before. Quoting still bounds whole-tag matches, so
searching "weather" does not match a row tagged "weathervane".

Co-authored-by: Claude <noreply@anthropic.com>
2026-08-17 01:24:05 -06:00
G30 695d33aa7c fix: let the chat column shrink so the collapsed sidebar rail is not pushed off screen (#28501) 2026-08-17 01:23:25 -06:00
G30 f5a5a434b9 fix: record an error state when a timer's chat completion raises (#27785) 2026-08-17 01:22:47 -06:00
G30 31897b7e34 fix: stop channel message hover actions overlapping code and table toolbars (#27737) 2026-08-17 01:20:54 -06:00
Timothy Jaeryang Baek ad8c79f686 refac 2026-08-17 00:18:35 -07:00
Lin Junrong 211906d799 fix: keep references to lifespan background tasks (#28053)
periodic_usage_pool_cleanup, periodic_session_pool_cleanup and
scheduler_worker_loop were started with asyncio.create_task and their
handles discarded. The event loop keeps only a weak reference to a task,
so a task with no other referent can be garbage collected while it is
suspended at an await. All three are while True loops meant to run for
the process lifetime, and if one is collected the failure is silent:
pool entries stop being cleaned up, or automations and calendar alerts
stop firing, with nothing logged.

Six lines above, redis_task_command_listener is already stored on
app.state and cancelled on shutdown. This applies the same treatment to
the other three.

Closes #28052
2026-08-17 01:18:22 -06:00
Timothy Jaeryang Baek 0007369f4e refac 2026-08-17 00:16:11 -07:00
Timothy Jaeryang Baek b6dc70c93b refac 2026-08-17 00:16:07 -07:00
Timothy Jaeryang Baek d2af19ae3c refac 2026-08-17 00:15:36 -07:00
Classic298 3d630491c6 fix: re-syncing an existing model no longer fails silently (#28036)
POST /api/v1/models/sync only worked when every model in the payload was new. As soon as one id already existed, the whole call blew up and the endpoint still answered HTTP 200 with an empty list, so nothing was updated and well-behaved clients saw a success. Only a first-ever sync into an empty catalogue went through.

The update branch splatted the model dump (which already carries user_id and updated_at) and then passed both again as explicit keyword arguments, which is a duplicate-keyword TypeError before SQLAlchemy ever sees it. The insert branch right below merged the same values into a dict first, so it never collided.

Fixed by building that dict once and using it for both branches, matching how sync_functions already does it. Left the broad exception handler alone: it is the reason the failure was silent, but changing the error contract of sync_models is a separate call.

Fixes #28033
2026-08-17 01:14:11 -06:00
Classic298 b933292d63 refactor: track visited ids when resolving a chat's current message (#28035)
`delete_message_from_history` follows `childrenIds` down to the deepest leaf without recording where it has been. Record it.
2026-08-17 01:13:51 -06:00
Timothy Jaeryang Baek f100edb708 refac 2026-08-17 00:12:13 -07:00
Classic298 27402ff210 refac: issue Playwright web loader requests from the shared HTTP clients (#28634)
The Playwright loader's route interceptor now performs each intercepted request with the same requests/aiohttp clients the other web loader paths already use and fulfills the page with that response, rather than having the browser issue it. Redirect handling, header forwarding and cookie delivery to the browser are unchanged.

Two consequences worth knowing. Page requests now leave from the backend instead of the browser, so with PLAYWRIGHT_WS_URL set they originate from a different host, and TLS is verified against certifi plus AIOHTTP_CLIENT_SSL_CERT_FILE rather than the browser's own trust store. And because the synchronous interceptor blocks, sub-resources on that path fetch one at a time: 30 assets at 40ms went from 2.01s to 3.01s, and 8 assets at 500ms from 1.05s to 4.50s. The asynchronous path is unaffected, at 0.65s and 1.05s respectively.
2026-08-17 01:06:12 -06:00
Classic298 73c1f5806a refactor: match provider identity lookups via JSON subscript (#28624)
Both the OAuth and SCIM user lookups now compare the nested JSON value with SQLAlchemy's subscript operator, which emits the correct SQL for each supported database on its own. This replaces the hand-written sqlite and postgresql branches and the column-level contains() call they used.
2026-08-17 01:05:16 -06:00
G30 90724cdee0 fix: drop the white backdrop behind model icons in the admin Models list (#27612) 2026-08-17 01:03:37 -06:00
G30 8fc5ffe26e fix: persist the Open Sharing permission in default user permissions (#27609) 2026-08-17 01:03:07 -06:00
Timothy Jaeryang Baek 0480ca9653 refac
Co-Authored-By: Solaris-star <67425364+solaris-star@users.noreply.github.com>
2026-08-17 00:01:38 -07:00
Timothy Jaeryang Baek 927ce0eae6 refac
Co-Authored-By: Classic298 <27028174+Classic298@users.noreply.github.com>
2026-08-16 23:58:56 -07:00
Damien S 54cefd2b99 fix: preserve complete user context in agentic retrieval (#27642)
* fix: preserve user info in agentic RAG tools

* fix: preserve user info in file access checks

---------

Co-authored-by: Damien SPINELLI <damien.spinelli@external.list.lu>
2026-08-17 00:56:48 -06:00
xyonium 686d8dc54c fix: strip prefix id from model name in /responses endpoint (#28575)
The /openai/responses endpoint forwarded the prefixed model id (e.g.
"myprovider.gpt-4o") to the upstream provider instead of the stripped
native name, causing "model not found" errors when a connection has a
Prefix ID configured.

generate_chat_completion() already strips the prefix before forwarding;
apply the same strip_provider_model_prefix() call in responses() after
the urlIdx routing (which needs the prefixed id) and re-serialize the
body afterwards.

Also fixes the Azure non-v1 deployment path, which built the deployment
URL from the prefixed model name.

Co-authored-by: Claude <noreply@anthropic.com>
2026-08-17 00:53:00 -06:00
Classic298 3df485582d fix: reject skill IDs that are not URL path safe (#27660)
A skill ID goes straight into the path of every mutating skill endpoint (/api/v1/skills/id/{id}/...), but create only replaced spaces with hyphens. An ID containing a "/" was stored verbatim as the primary key, so the route never matched, the request fell through to the SPA static mount and the client got 405 Method Not Allowed. The skill could not be opened, edited, toggled or deleted, by admins either, and since skill.name is UNIQUE it could not be recreated under a corrected ID. Percent-encoding does not help: uvicorn decodes the path before Starlette routes it, so the only remaining fix was a direct database write.

Create now rejects any ID outside [a-z0-9_-] with 400 instead of silently storing an unreachable one. Two frontend paths that fed unsanitized IDs into it are fixed as well: the manual "Skill ID" field, which was bound with no sanitization at all and is the path that reproduces on every version, and the markdown import, which put the raw frontmatter name into the ID before opening the editor in clone mode, where the reactive slugify is disabled.

Existing rows with an unreachable ID are not repaired here; rewriting a primary key would also have to re-point the access grants keyed on it.

Fixes #27655
2026-08-17 00:52:16 -06:00
Timothy Jaeryang Baek 954613944b refac 2026-08-16 23:51:38 -07:00
Classic298 805bfca5af feat: emit group events on OAuth group sync (#27657)
With ENABLE_OAUTH_GROUP_MANAGEMENT enabled, every SSO login reconciles the user's group membership against the IdP claims, adding and removing them from groups and, with ENABLE_OAUTH_GROUP_CREATION, creating groups that do not exist yet. None of it emitted an event, so the same membership change was observable when an admin made it through the UI or when it arrived over SCIM, but invisible when the IdP drove it. That is the path that changes membership most often.

Emits group.member_added and group.member_removed per membership transition and group.created for each auto-created group, using the same payload keys as the groups router. The member events are published only when the write returned a group, so a failed or no-op write emits nothing, and both loops already run only on an actual transition. update_user_groups takes the request so the events can be published; it has a single caller.
2026-08-17 00:50:47 -06:00
Timothy Jaeryang Baek d5d50169f4 refac 2026-08-16 23:48:05 -07:00
Timothy Jaeryang Baek 75df30c0ea refac 2026-08-16 23:47:02 -07:00
Timothy Jaeryang Baek 3e186abdd9 refac
Co-Authored-By: Classic298 <27028174+Classic298@users.noreply.github.com>
2026-08-16 23:41:58 -07:00
G30 2813eb44f2 fix: stop the What's New modal drawing two bullets per changelog entry (#28676) 2026-08-17 00:40:57 -06:00
G30 ad72dc6658 fix: scroll to the top of a chat on the first click of Scroll to Top (#28659) 2026-08-17 00:40:14 -06:00
Timothy Jaeryang Baek 16f118d77a refac 2026-08-16 23:38:34 -07:00
G30 884388cac3 fix(search): wire up mark as unread in the search chats modal (#28136) 2026-08-17 00:34:49 -06:00
Timothy Jaeryang Baek 736e38338e refac 2026-08-16 23:32:23 -07:00
Timothy Jaeryang Baek a40f6f2860 refac 2026-08-16 23:28:48 -07:00
Timothy Jaeryang Baek b5da50f3df refac 2026-08-16 23:26:24 -07:00
Classic298 cd9db21c52 refac: bind tool server cookies per connection (#28630)
The tool callable now takes its connection's cookie jar as a parameter, matching how its headers are already passed and how the terminal tool factory in the same module builds its callables.
2026-08-17 00:25:07 -06:00
Classic298 7d392bedc9 refac: align the channel completion gate with the message update route (#28631)
The gate now applies the same authorship condition the channel message update route already uses, so both paths agree on which messages a caller may modify.
2026-08-17 00:24:49 -06:00
Classic298 1756c9d5d2 fix: default pinned models stop applying after a user's first page load (#28069)
Changing "Default Pinned Models" in admin settings had no effect for anyone who had already opened Open WebUI once. The sidebar copied the admin default into that user's own settings the first time it rendered and saved it to the server, which marked them as having customized their pins, so every later change to the default was ignored for them. Merely loading the page was enough, the user never had to touch a pin.

The default is now resolved for display only, through a shared store that falls back to the admin list while the user has no pins of their own, the same way default models already work. Nothing is written to the user's settings until they actually pin, unpin or reorder something, at which point their choice takes over for good. Unpinning everything still persists an empty list rather than snapping back to the default.

Users whose settings were already overwritten by the old behaviour keep that copy, since a stored pin list cannot be told apart from a deliberate one.

Fixes a drag-reorder path that mixed sidebar positions with stored ones, and stops the sidebar section reopening itself after any unrelated settings change.
2026-08-17 00:22:51 -06:00
Timothy Jaeryang Baek 1a376ac17f refac 2026-08-16 23:21:00 -07:00
Timothy Jaeryang Baek b211c407f4 refac 2026-08-16 23:14:08 -07:00
G30 1d1c14bd77 fix: compare folder names for uniqueness with lower() equality instead of a LIKE pattern (#28695) 2026-08-17 00:00:02 -06:00
Timothy Jaeryang Baek e4694d6c3f refac 2026-08-16 22:59:10 -07:00
Timothy Jaeryang Baek f7a533cda6 refac 2026-08-16 22:57:01 -07:00
Timothy Jaeryang Baek 3258330729 refac
Co-Authored-By: Classic298 <27028174+Classic298@users.noreply.github.com>
2026-08-16 22:56:19 -07:00
Timothy Jaeryang Baek 62fc436999 refac 2026-08-16 22:52:24 -07:00
Timothy Jaeryang Baek 31d08d592c refac 2026-08-15 01:10:14 -06:00
Timothy Jaeryang Baek 467be93e6d refac 2026-08-15 01:09:39 -06:00
Timothy Jaeryang Baek 7fc5fa1ff3 refac 2026-08-15 01:02:06 -06:00
Timothy Jaeryang Baek 3eb65f4715 refac 2026-08-15 00:06:38 -06:00