Commit Graph
16994 Commits
Author SHA1 Message Date
maco 8bc4ac2641 fix: add aria-pressed and aria-label to Web Search and Image Generation toggles (#25258) 2026-06-29 03:36:37 -05:00
Classic298andClaude Opus 4.8 58f9170319 fix: trim terminal bearer token so whitespace can't break the WebSocket (#25686)
A trailing space in the Open Terminal bearer token broke only the
interactive terminal: HTTP calls put the token in the Authorization
header, where the spec strips trailing whitespace, so the connection
test, file listing and tool calls all worked. The terminal WebSocket
can't set headers from the browser, so it sends the token inside a JSON
auth message that preserves the space verbatim, failing auth with
[Connection closed]. Normalize the key on save so whitespace never
enters storage, and trim it in the WebSocket auth message so existing
saved configs work without re-saving.


Fixes #25613

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-29 03:35:19 -05:00
Classic298 e98730b20d fix: pass web search results to model when embedding & retrieval enabled (#25600)
Web search results stored as a vector collection were silently dropped
before retrieval when BYPASS_RETRIEVAL_ACCESS_CONTROL is False (the
default). The server-generated web_search file item carries a
'collection_name' but its 'web_search' type is not matched by any
explicit dispatch branch in get_sources_from_items, so it fell through
to the untrusted client-supplied collection_name branch and was ignored.

Add an explicit branch for type == 'web_search' items so the collection
is queried again. Access control is preserved: the collection still
passes through filter_accessible_collections, which already allowlists
web-search-* and bypasses only for admins.

Regression introduced when the retrieval access-control hardening gated
the bare collection_name fallback behind BYPASS_RETRIEVAL_ACCESS_CONTROL.
2026-06-29 03:34:55 -05:00
Timothy Jaeryang Baek 9802b0d135 refac 2026-06-29 03:33:49 -05:00
G30 ed2d7d4acd fix(i18n): standardize title case for settings toggle labels (#25765) 2026-06-29 03:30:40 -05:00
Timothy Jaeryang Baek f85e906dec refac 2026-06-29 03:28:50 -05:00
G30 70b89d01c2 feat(ui): add drag-to-reorder for pinned notes in sidebar (#25677) 2026-06-29 03:22:33 -05:00
Timothy Jaeryang Baek 3fd0384ffc refac 2026-06-29 03:21:41 -05:00
G30 78d276b4ff fix(ui): update groups count to reflect filtered search results (#25689) 2026-06-29 03:21:31 -05:00
Classic298andClaude Opus 4.8 5796d44363 fix: defer missing-local-embedding error so it can't crash boot (#25683)
get_embedding_function runs at import time (main.py), so the empty-engine
+ no-model guard added in 55ca719b raised ValueError during startup and
bricked the instance: a blank embedding model set via the UI made the app
unbootable, with no way back into settings to undo it. Move the check into
the returned coroutine so construction always succeeds and the error only
fires when something actually embeds, surfacing as a clear request error
instead of the old cryptic 'NoneType has no attribute encode'.


Fixes #25634
Fixes #25165

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-29 03:21:09 -05:00
G30 6e14e446cb fix(api): handle orphaned shared_chat rows when unsharing (#25632) 2026-06-29 03:20:46 -05:00
Timothy Jaeryang Baek c93d4f04aa refac 2026-06-29 03:20:02 -05:00
Timothy Jaeryang Baek 33cd199e6d refac 2026-06-29 03:16:59 -05:00
G30 4712544d5e feat(search): add chat context menu to search modal (#25490) 2026-06-29 03:16:03 -05:00
maco 958fdbdc88 feat: add ariaLabel prop to Switch component (#25230) 2026-06-29 03:15:11 -05:00
Timothy Jaeryang Baek 390e200f76 refac 2026-06-29 03:13:17 -05:00
Kylapaallikko 462b66b807 Update fi-FI translation.json (#25672)
Added missing translations and improved existing ones.
2026-06-29 03:07:49 -05:00
Timothy Jaeryang Baek 388f62f8a0 refac 2026-06-29 03:03:36 -05:00
Timothy Jaeryang Baek 7be009649a refac 2026-06-29 02:57:58 -05:00
Classic298andClaude Opus 4.8 534206095f fix: route Account Pending sign out through backend signout flow (#25681)
The Sign Out button on the Account Pending overlay only cleared the
local token and redirected to /auth, skipping the backend signout. This
left the OAuth/OIDC session alive at the IdP, so re-login bounced the
user straight back into pending and never re-fetched updated role/group
claims. Call userSignOut() and honor the returned redirect_url, matching
the sidebar logout flow.


Fixes #25644

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-29 02:54:19 -05:00
G30 525c115a3a fix(ui): remove unnecessary margin causing scrollbar on About settings page (#25802) 2026-06-29 02:54:00 -05:00
Timothy Jaeryang Baek b34d6c836e refac 2026-06-29 02:50:55 -05:00
G30andTim Baek 6fdf9b4340 perf(auth): make password hashing non-blocking and batch CSV user import (#25804)
Co-authored-by: Tim Baek <tim@openwebui.com>
2026-06-29 02:45:39 -05:00
G30 18e6a10778 fix(ui): update model share URL to new community post endpoint (#25801) 2026-06-29 02:44:28 -05:00
Classic298andClaude Opus 4.8 36d08fa2a7 fix: request file list/search without extracted content by default (#25774)
The file picker and Files modal only render filenames and metadata, but
searchFiles()/getFiles() never passed content=false, so the backend
serialized data.content (full extracted text) of every file into each
response. On instances with many large documents this turns a metadata
list into tens of MB per request, re-issued on every picker keystroke.

The backend already supports content=false on GET /files/ and
GET /files/search; this just makes the list/search callers opt out of the
payload by default. The param stays overridable for callers that need it.


Fixes #25741

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-29 02:43:56 -05:00
Timothy Jaeryang Baek 2bdd2ab94e refac 2026-06-29 02:43:14 -05:00
Classic298andClaude Opus 4.8 2414dfca70 fix: mark in-place created chats as read on new chat (#25782)
A chat created in place (new chat -> first message) keeps chatIdProp
undefined because the URL is swapped via history.replaceState without a
remount, so none of the chatIdProp-gated updateLastReadAt paths fire and
the chat is never persisted with a last_read_at. Starting another new
chat via initNewChat reset $chatId without marking the outgoing chat
read, so on refresh updated_at > last_read_at (NULL) and the chat shows
as unread in the sidebar.

Mark the outgoing chat read in initNewChat, mirroring navigateHandler.


Fixes #25108

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-29 02:32:00 -05:00
G30andTim Baek 6ea591491e perf(images): offload validate_url() DNS resolution with asyncio.to_thread (#25825)
validate_url() calls socket.getaddrinfo() for SSRF protection, which
blocks the event loop for 100-700ms per DNS lookup. This affects:

- Image generation (get_image_data) — every external image URL
- Image editing (load_url_image) — every external image URL
- OAuth profile pictures (_process_picture_url) — every login
- Webhook delivery (post_webhook) — every notification
- Image base64 conversion (get_image_base64_from_url) — chat images

Wrap all 5 async call sites in asyncio.to_thread() so DNS resolution
runs in the thread pool. The event loop remains free to serve other
requests during the lookup.

Benchmark (3 domains, 3 trials averaged):
- BEFORE: max jitter 479ms, 1 blocked ping per trial
- AFTER:  max jitter 1ms, 0 blocked pings (324x improvement)

Co-authored-by: Tim Baek <tim@openwebui.com>
2026-06-29 02:31:24 -05:00
Aleix Dorca d4d9786434 i18n: Update catalan translation.json (#25595) 2026-06-29 02:27:11 -05:00
Timothy Jaeryang Baek ac3449cac9 refac 2026-06-29 02:26:27 -05:00
G30 71d6212ab8 fix(redis): use await asyncio.sleep() instead of time.sleep() in async generator (#25823)
time.sleep() inside the _wrap_async_gen() async generator blocks the
entire event loop during Sentinel failover retries. The sibling method
_wrap_async_call() already correctly uses await asyncio.sleep().
2026-06-29 02:25:46 -05:00
Timothy Jaeryang Baek 2308b59f13 refac 2026-06-29 02:21:24 -05:00
Timothy Jaeryang Baek 61a2672215 refac 2026-06-29 02:20:54 -05:00
Classic298andClaude Opus 4.8 386ac95814 fix: scope Socket.IO event-caller to the requesting user's own session (#25763)
get_event_call() routed execute:python / execute:tool events to a client-supplied session_id after only checking the session was connected, not that it belonged to the requester. Verify the target session is owned by the requesting user (metadata user_id) before delivering, so a client cannot route code/tool execution into another user's session.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-29 02:17:40 -05:00
914039ac81 Escape voice-derived attributes in Azure TTS SSML (#25776)
The Azure TTS handler (_tts_azure) interpolated the user-supplied voice,
and the locale derived from it, into the SSML xml:lang and <voice name>
attributes without XML-escaping, while the text body was already escaped
(2e75c6dbd). Escape both attributes too, so every user-derived value in
the SSML document is consistently encoded.

Co-authored-by: alanturing881 <alanturing881@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-29 02:16:37 -05:00
G30 ff25ccca65 feat: add repeat/recurrence dropdown to calendar event modal (#25865)
* feat(calendar): add repeat/recurrence dropdown to event modal

* fix(calendar): anchor recurring event expansion to event start date

The expand_recurring_event() utility used the view range start as dtstart,
causing FREQ=WEEKLY events to land on the wrong day of the week. Use the
event's original start date instead so recurrence patterns stay correct.
2026-06-29 02:16:19 -05:00
Classic298andClaude Opus 4.8 01198eaeef Close DNS-rebinding SSRF gap in get_content_from_url probe (#25775)
The web-ingest probe in get_content_from_url validated the URL at resolve
time (validate_url) but then fetched with a bare requests.get, which
re-resolves the hostname at connect time. An attacker-controlled name
server can answer with a public IP during validation and an internal IP
at connect, reaching cloud metadata / loopback / internal services (blind
always; binary content-types are read back to the caller). The
connection-layer guard (#24759) that closes this for the SafeWebBaseLoader
path was never mounted on this probe.

Route the probe through the same _SSRFSafeAdapter the loader uses, so the
resolution that feeds the TCP connect is re-validated against the
global-IP check.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-29 02:15:31 -05:00
Classic298 15d96b1f2a fix: chroma has_collection always returns False (name vs Collection) (#25780)
has_collection did `collection_name in self.client.list_collections()`,
but chromadb's list_collections() returns Collection objects (1.x), not
name strings — so the membership test is always False, even when the
collection exists. Compare against the collection names instead (with a
hasattr guard tolerating versions that yield plain names).

Found via the dependency-contract test suite (unit/deps/test_chromadb.py).
2026-06-29 02:15:05 -05:00
G30 342539f1e1 perf(audio): make ML model loading non-blocking with asyncio.to_thread (#25806) 2026-06-29 02:14:15 -05:00
Classic298andClaude Opus 4.8 c31694af09 chore: Update SECURITY.md (#25773)
* Update SECURITY.md

* Update SECURITY.md

* Update SECURITY.md

* Update SECURITY.md

* Update SECURITY.md

* Update SECURITY.md

* Update SECURITY.md

* Update SECURITY.md

* Extend the already-fixed/monitoring rule to public PRs and credit

Broaden the rule from "already fixed" to also cover issues already being fixed in
the open (e.g. an open pull request), extend the commit-monitoring pattern to PRs,
and fold in the credit consequence on provable grounds rather than an unprovable
bad-faith claim: a report of an already-public, already-fixed-or-being-fixed issue
filed strictly last is a duplicate we cannot distinguish from scraping, so it earns
no advisory. Credit belongs to whoever found or fixed it, who forfeits it by
disclosing publicly instead of reporting confidentially first — so a public fix
earns no advisory and no credit for anyone.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Remove Rule 14 (One Vulnerability Per Report)

The one-CVE-per-vulnerability constraint it restated is a CVE Program counting
rule, already binding through the "Alignment with the CVE Program" section.
Dropping the standalone rule removes the duplication; bundled reports are still
split on that basis when they arise.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* security policy: surface "What a Valid Report Gets You" near the top, refresh date

Move the "What a Valid Report Gets You" section up to directly under the good-faith
reporting section (it leads with what reporters receive, rather than burying it
below the rules), and update the last-updated date.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Update SECURITY.md

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-29 02:11:17 -05:00
G30 964a098a4b fix(chat): use code block language tag to detect Vega-Lite specs for rendering (#25843) 2026-06-29 02:07:11 -05:00
Classic298 c3394288bb fix: repair Mistral OCR async upload (aiohttp.streams.FilePayload removed) (#25779)
_upload_file_async built its multipart body with
`aiohttp.streams.FilePayload(...)`, which no longer exists in aiohttp
(payload classes live in aiohttp.payload, and there is no FilePayload).
The reference sits inside a lazy closure, so import succeeds and only the
async Mistral OCR file-upload path blows up at runtime with
AttributeError on every call.

Mirror the working sync path: open the file in a context manager and let
MultipartWriter.append(file_obj, {...}) build a streaming
BufferedReaderPayload, with the POST issued inside the open() block so
the handle stays valid for the whole upload. Preserves the streaming /
memory-efficiency intent.

Found via the dependency-contract test suite (unit/deps/test_aiohttp.py),
which pins aiohttp.streams.FilePayload as absent.
2026-06-29 02:05:54 -05:00
Classic298 8a016931f1 refac(telemetry): drop deprecated semconv SpanAttributes subclass (#25784)
constants.py subclassed opentelemetry.semconv.trace.SpanAttributes, which
is deprecated since semconv 1.25.0 (emits a DeprecationWarning; the pinned
0.63b1 has it live). Source the legacy span-attribute keys from the
non-deprecated _incubating attribute modules instead:
  http.url / http.method / http.status_code  <- _incubating http_attributes
  db.name / db.statement / db.operation      <- _incubating db_attributes
The stable http module renamed these (http.request.method, ...), so only
the incubating module preserves the original values. Custom keys
(db.instance/type/ip/port, error.*, result.*) stay literals.

Verified: emitted attribute keys are byte-identical before/after for every
key instrumentors.py reads, and importing constants no longer emits a
DeprecationWarning.
2026-06-29 02:05:34 -05:00
G30 e69ce6e1c6 perf(channels): batch N+1 queries for reactions and thread replies (#25831)
Replace per-message database queries with batch IN-clause queries in
channel message handlers. This eliminates the N+1 query pattern that
caused ~102 queries per channel page load (50 messages × 2 queries each).

Changes:
- Add get_reactions_by_message_ids() to MessageTable: single query
  fetches all reactions for multiple messages using IN clause with
  User JOIN, returns dict[message_id, list[Reactions]]
- Add get_thread_reply_counts_by_message_ids() to MessageTable: single
  GROUP BY aggregate query returns (count, max_created_at) per parent,
  replacing full object loads just to call len()
- Refactor get_channel_messages(): 102 → 4 queries per page
- Refactor get_pinned_channel_messages(): 22 → 3 queries per page
- Refactor get_channel_thread_messages(): 53 → 4 queries per page
- Refactor send_notification(): N+1 membership check → batch set lookup
2026-06-29 02:05:16 -05:00
Timothy Jaeryang Baek 6050a94d77 refac 2026-06-29 02:03:58 -05:00
Classic298 5fd26b7549 docs: note pydub/audioop Python 3.13 constraint at the import (#25785)
pydub imports the stdlib `audioop`, removed in Python 3.13, so audio
preprocessing would break there. requires-python is already capped at
< 3.13; this one-line pointer flags what to handle (audioop-lts, or drop
pydub) before raising that cap.
2026-06-29 02:03:11 -05:00
G30 2a9a023172 fix(ui): add Edit option to prompts 3-dots menu (#25789) 2026-06-29 02:02:39 -05:00
Classic298andClaude Opus 4.8 b295a20b9d chore: bump Python backend dependencies, drop unused peewee (#25786)
* chore: bump Python backend dependencies, drop unused peewee

Minor/patch + reviewed major bumps across requirements.txt,
requirements-min.txt, pyproject.toml and uv.lock; playwright image bumped in
docker-compose.playwright.yaml. peewee/peewee-migrate removed (zero imports).

Security-relevant: cryptography 46->48, authlib 1.6.10->1.7.2, PyJWT 2.11->2.13,
requests 2.33.1->2.34.2, RestrictedPython 8.1->8.2, pillow 12.1.1->12.2.0.
Reviewed majors: redis 7->8, pymilvus ->2.6.14, azure-search-documents 11->12,
chardet 5->7, unstructured 0.18->0.22, pycrdt 0.12->0.13.

Testing:
- Resolution: `uv lock` resolves the full bumped set with no conflicts; uv.lock
  regenerated to match (peewee dropped, every pin including
  azure-search-documents==12.0.0 resolves).
- Per-dependency contract tests (external tests repo, unit/deps/): 105 files,
  2205 passed / 6 skipped, ruff-clean. One file per dependency pins the symbols,
  signatures and behaviour the backend actually uses, so an API removal/rename in
  a bumped version fails loudly instead of at runtime. Offline/deterministic.
- End-to-end embed->retrieve test driving transformers + sentence-transformers +
  chromadb together through Open WebUI's real RAG path (cached model, in-memory
  chroma, semantic retrieval asserted).
- Install/startup/health resolution gate added to the dep-bump workflow and the
  integration suite (uv/pip resolve + uvicorn /health + Playwright dev visibility).
- Bugs surfaced while testing each got an isolated fix branch + regression test:
  Mistral OCR aiohttp FilePayload (#25779), chroma has_collection (#25780),
  aiocache per-user model-cache key (security), otel semconv deprecation,
  pydub/audioop <3.13 note.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Bump python-multipart 0.0.22 -> 0.0.27 (CVE-2026-42561, CVE-2026-40347)

0.0.22 is affected by two DoS CVEs in the multipart parser that
Starlette/FastAPI run for every multipart/form-data request, so any
authenticated user hitting an upload endpoint can trigger them:
- CVE-2026-42561: unbounded part-header count/size -> CPU exhaustion (fixed 0.0.27)
- CVE-2026-40347: large multipart preamble/epilogue DoS (fixed 0.0.26)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-29 02:02:18 -05:00
G30 f923edcaaa feat(ui): add Clone option to automations 3-dots menu (#25790) 2026-06-29 02:00:23 -05:00
Timothy Jaeryang Baek c8260745a6 refac 2026-06-29 01:59:06 -05:00