Commit Graph
107 Commits
Author SHA1 Message Date
Timothy Jaeryang Baek 85c47fb467 refac 2026-07-26 21:51:35 -04:00
Classic298 54f06d8c53 perf: build chat responses without deep-copying the blob through model_dump (#27388)
Chat search built each result row with ChatTitleIdResponse(**chat.model_dump(), ...), which recursively copies the entire chat blob per row only for the constructor to ignore everything except id, title and timestamps: a 60-row search page deep-copied up to 60 full conversations. The folder listing, archived and export endpoints and every single-chat response did the same dump-and-revalidate dance via ChatResponse(**chat.model_dump()).

Search rows are now built from the five fields the response actually has (the snippet helper receives the blob by reference as before), and all 18 ChatResponse constructions use ChatResponse.model_validate(chat, from_attributes=True), which reads the fields off the already-validated ChatModel without copying the blob.

Benchmark (~500 KB chat blob):

| metric | before | after |
| --- | --- | --- |
| search result row | 0.05 ms | 0.003 ms |
| ChatResponse construction | 0.05 ms | 0.003 ms |
| per search page (60 rows) | 3 ms | 0.2 ms |

Beyond CPU, each converted row also stops materializing a second full copy of the conversation in memory while the page is being built.

Functionally verified: both construction styles produce identical model_dump() output for ChatResponse (including defaulted fields absent on ChatModel) and for search rows including the snippet.
2026-07-26 18:57:52 -04:00
Classic298andGabrielGomesAL 4f93c3e36c fix: authorize before cancelling tasks in the chat delete endpoint (#27006)
DELETE /api/v1/chats/{id} called stop_item_tasks(id) before checking the
caller's chat.delete permission or ownership of the target chat. An
authenticated user who knew another user's chat id could therefore cancel that
chat's in-flight generation (streaming response, title or tag generation) even
though the deletion was then rejected. The chat id is discoverable through
legitimate read-only access to a shared chat or folder.

Reorder the handler to authorize first (admin, or owner holding chat.delete) and
only then cancel tasks and delete, matching the dedicated task-stop endpoint.
Legitimate deletions are unchanged; an unauthorized caller now returns 404 or 401
before any cancellation. The duplicated tag-cleanup and event-publish blocks are
merged.

Co-authored-by: GabrielGomesAL <193945687+GabrielGomesAL@users.noreply.github.com>
2026-07-26 18:57:35 -04:00
Timothy Jaeryang Baek 1f0dc90abe refac 2026-07-26 18:06:03 -04:00
Timothy Jaeryang Baek 1f5b0d816f refac 2026-07-24 01:19:28 -04:00
Classic298 a35b37adcd fix: keep chats shared with an admin readable when ENABLE_ADMIN_CHAT_ACCESS is off (#27127)
get_chat_by_id sent admins down a branch that returned the chat only when
ENABLE_ADMIN_CHAT_ACCESS was on, or the chat was internal, and never fell
through to the access-grant and shared-folder checks. With the setting off,
an admin was therefore denied a chat that had been deliberately shared with
them, either directly or through a shared folder, while any non-admin holding
the same grant could open it. The admin role removed access the user had been
given rather than only closing the admin-only path.

Try the admin path first, then let everyone fall through to the grant and
folder checks. ENABLE_ADMIN_CHAT_ACCESS=false still closes the admin-only
route to other users' chats, and internal chats stay reachable.
2026-07-24 00:02:12 -05:00
Timothy Jaeryang Baek 1e88367cc8 refac 2026-07-24 00:43:31 -04:00
Timothy Jaeryang Baek ca11bd90a7 chore: format 2026-07-23 13:41:16 -04:00
Timothy Jaeryang Baek cf887b68ea refac 2026-07-23 02:54:56 -04:00
Timothy Jaeryang Baek 743b9fd3ce refac 2026-07-16 21:57:43 -04:00
Timothy Jaeryang Baek 7a9928ef17 refac 2026-07-14 23:08:41 -04:00
Timothy Jaeryang Baek 7088d245bb refac 2026-07-14 00:10:28 -04:00
Timothy Jaeryang Baek c89b6c50bc refac 2026-07-09 17:37:17 -05:00
Timothy Jaeryang Baek 5c389ad93f refac 2026-07-09 17:33:30 -05:00
Timothy Jaeryang Baek 0443ab3a61 refac 2026-06-29 13:30:28 -05:00
Timothy Jaeryang Baek 22a44e67a8 refac 2026-06-29 13:15:29 -05:00
Timothy Jaeryang Baek 517cd8d102 refac 2026-06-29 13:03:14 -05:00
Timothy Jaeryang Baek 0eba3df119 refac 2026-06-29 05:21:49 -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 b5c43968db refac 2026-06-25 03:31:45 +01:00
Timothy Jaeryang Baek 3f0c0e0a0d refac 2026-06-19 00:16:06 +02:00
G30 4584adf900 feat(ui): add 'Unshare All Shared Chats' button to Shared Chats modal (#25848) 2026-06-17 03:06:41 +02:00
G30 dee07d8a30 feat(ui): show total archived chat count in ChatsModal title (#25872) 2026-06-17 02:57:27 +02:00
Timothy Jaeryang Baek 5cdcdbaeec refac 2026-06-17 02:52:35 +02:00
Timothy Jaeryang Baek 9ccda6715c refac 2026-06-17 00:35:08 +02:00
Timothy Jaeryang Baek edf3ae9209 refac 2026-06-17 00:33:30 +02:00
Timothy Jaeryang Baek d65ac445a4 refac 2026-06-15 23:34:24 +02:00
Timothy Jaeryang Baek 6fce92aa12 chore: format 2026-06-01 13:56:55 -07:00
Timothy Jaeryang Baek 778dba1d6b refac 2026-06-01 13:18:44 -07:00
Classic298andClaude Opus 4.8 a089842368 fix: don't revert replace/outlet content on chat save (#25485)
update_chat_by_id re-derived assistant content from `output` on every
save (serialize_output) so frontend edits to output items reflect in
content. But it ran unconditionally, so content set independently of
output — an __event_emitter__ {"type":"replace"} from an Action, or an
outlet filter footer — was reverted to the original output-derived text
on the next save. The reload reads chat.chat directly, so the change
vanished after navigating away (regression vs 0.9.2, which predates the
output mechanism).

Re-derive only when the message's `output` actually changed versus what's
stored, which still reflects genuine output edits but leaves
independently-set content intact.

Fixes #24585

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 11:45:05 -07:00
Timothy Jaeryang Baek 470a074cd1 refac 2026-05-21 16:56:56 +04:00
Timothy Jaeryang Baek 260ead64da refac 2026-05-21 14:01:57 +04:00
Classic298andClaude Opus 4.7 c306a7e16e refactor: remove unused DELETE /chats/{id}/tags/all endpoint (#24785)
The bulk-clear-chat-tags endpoint's only frontend wrapper,
deleteTagsById in src/lib/apis/chats/index.ts, is dead: nothing imports
or calls it, the path is referenced nowhere else, and the route handler
has no internal caller. Removes the route handler, the dead wrapper, and
the now-orphaned Chats.delete_all_tags_by_id_and_user_id model method
(its sole caller was this route). The shared
Chats.delete_orphan_tags_for_user method is untouched.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 21:03:58 +04:00
Timothy Jaeryang Baek aa06200f78 refac 2026-05-19 20:37:53 +04:00
Classic298andShigekiTsuchiyama ba78ebea27 fix: validate folder_id ownership on chat create + folder-update endpoints (#24588)
POST /api/v1/chats/new and POST /api/v1/chats/{id}/folder accepted a
caller-supplied folder_id with no validation — neither ownership, nor
existence, nor UUID format. The row was persisted with the supplied
value verbatim, so the DB ended up with chat rows whose folder_id
referenced another user's folder, a non-existent UUID, or even a
non-UUID string.

No read path surfaces this across users — every chat-folder read is
user_id-filtered on both sides — so this is referential-integrity
hardening rather than a security boundary fix. But there's no reason
to accept dangling references either, and the downstream consumers
shouldn't have to assume the column is clean.

Add a Folders.get_folder_by_id_and_user_id() lookup at both writers:
if a folder_id is supplied, it must match a folder owned by the
caller. None remains allowed (chat-without-folder is the default).
Non-existent and non-UUID values fall through to 404.

Reported by ShigekiTsuchiyama in GHSA-4vrg-2vcq-q7jc.

Co-authored-by: ShigekiTsuchiyama <ShigekiTsuchiyama@users.noreply.github.com>
2026-05-14 02:57:57 +09:00
Timothy Jaeryang Baek 6d0295588e refac: modernize type annotations (PEP 604 / PEP 585) 2026-05-12 17:10:15 +09:00
Timothy Jaeryang Baek 3a21b334cc refac 2026-05-11 02:15:46 +09:00
Timothy Jaeryang Baek 315566064a refac 2026-05-11 01:57:50 +09:00
Timothy Jaeryang Baek 3fcad2f627 refac 2026-05-09 08:28:29 +09:00
Classic298 55d1db1f38 fix: stream GET /chats/all to prevent OOM on large chat histories (#24461)
Convert the /chats/all endpoint from loading all user chats into memory
at once to a streaming NDJSON response that fetches chats in batches of
100. This prevents Out-of-Memory crashes for users with large chat
histories.

Backend: Added async generator that paginates through chats with
short-lived DB sessions per batch (critical for SQLite lock release).

Frontend: Updated getAllChats to consume the NDJSON stream via
ReadableStream reader, accumulating results for the export file.

Ref: open-webui#22206
2026-05-09 04:11:52 +09:00
Timothy Jaeryang Baek cde21b9f6d refac 2026-05-05 03:33:47 +09:00
Timothy Jaeryang Baek 47329b5032 refac 2026-04-20 08:55:34 +09:00
Timothy Jaeryang Baek 2e52ad8ff2 refac: shared chat 2026-04-17 10:16:32 +09:00
Timothy Jaeryang Baek cf4218e688 refac 2026-04-13 21:29:03 -05:00
Timothy Jaeryang Baek 25898116ea chore: format 2026-04-12 18:12:59 -05:00
Timothy Jaeryang Baek 27169124f2 refac: async db 2026-04-12 14:22:11 -05:00
Timothy Jaeryang Baek 0e5696de74 refac 2026-04-01 05:55:48 -05:00
Timothy Jaeryang Baek 0638b9f56c refac 2026-04-01 04:00:18 -05:00
Timothy Jaeryang Baek 70285fb6ca refac 2026-03-24 04:53:23 -05:00
Timothy Jaeryang Baek ade617efa8 refac 2026-03-24 04:49:48 -05:00