Compare commits

...
4448 Commits
Author SHA1 Message Date
Tim Baek 6f1486ffd0 Merge pull request #19466 from open-webui/dev
0.6.41
2025-12-02 17:28:46 -05:00
Timothy Jaeryang Baek 73f7e91dec chore: format 2025-12-02 17:16:12 -05:00
Classic298 8361f73ca6 chore: 0.6.41 Changelog (#19473)
* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md
2025-12-02 17:15:19 -05:00
Timothy Jaeryang Baek 11efb982c1 refac 2025-12-02 17:14:45 -05:00
Timothy Jaeryang Baek 9d87688ecc chore: bump 2025-12-02 16:53:05 -05:00
Classic298 4f9677ffcf Update translation.json (#19697)
* Update translation.json

* Update translation.json
2025-12-02 16:48:11 -05:00
a49e1d87ad fix: Default Group ID assignment on SSO/OAUTH and LDAP (#19685)
* fix (#99)

Co-authored-by: Tim Baek <tim@openwebui.com>
Co-authored-by: Claude <noreply@anthropic.com>

* Update auths.py

* unified logic

* PUSH

* remove getattr

* rem getattr

* whitespace

* Update oauth.py

* trusted header group sync

Added default group re-application after trusted header group sync

* not apply after syncs

* .

* rem

---------

Co-authored-by: Tim Baek <tim@openwebui.com>
Co-authored-by: Claude <noreply@anthropic.com>
2025-12-02 16:48:00 -05:00
Timothy Jaeryang Baek 9a65ed2260 chore: format 2025-12-02 16:06:57 -05:00
Classic298 864d54095f Update translation.json (#19696) 2025-12-02 16:06:06 -05:00
Classic298 b29fdc2a0c Update milvus_multitenancy.py (#19695) 2025-12-02 15:38:06 -05:00
Classic298andTim Baek 12f237ff80 fix: Update milvus.py (#19602)
* Update milvus.py

* Update milvus.py

* Update milvus.py

* Update milvus.py

* Update milvus.py

---------

Co-authored-by: Tim Baek <tim@openwebui.com>
2025-12-02 15:30:31 -05:00
Timothy Jaeryang Baek 192c2af7ba refac 2025-12-02 15:17:47 -05:00
Matthew Kusz 17bfd38696 Fix dropdown backgrounds (#19693) 2025-12-02 15:16:36 -05:00
Henne a7e614ca4c feat: Adds document intelligence model configuration (#19692)
* Adds document intelligence model configuration

Enables the configuration of the Document Intelligence model to be used by the RAG pipeline.

This allows users to specify the model they want to use for document processing, providing flexibility and control over the extraction process.

* Added Titel to Document Intelligence Model Config

Added Titel to Document Intelligence Model Config
2025-12-02 14:41:09 -05:00
Timothy Jaeryang Baek e5c6b739c2 refac 2025-12-02 11:43:00 -05:00
Timothy Jaeryang Baek 34169b3581 refac 2025-12-02 11:31:23 -05:00
Timothy Jaeryang Baek 01868e856a enh: group members endpoint 2025-12-02 11:24:23 -05:00
Timothy Jaeryang Baek e301d1962e refac/perf: has_access_to_file optimization 2025-12-02 11:11:17 -05:00
Timothy Jaeryang Baek 9f6c91987f refac 2025-12-02 11:00:34 -05:00
Timothy Jaeryang Baek d19023288e feat/enh: kb files db migration 2025-12-02 10:53:32 -05:00
Timothy Jaeryang Baek 29236aefe8 refac 2025-12-02 10:25:38 -05:00
Timothy Jaeryang Baek 6ce9afd95d refac 2025-12-02 09:21:03 -05:00
Timothy Jaeryang Baek 39f7575b64 refac: show connection type for custom models 2025-12-02 06:19:48 -05:00
Timothy Jaeryang Baek 954aaa6bdc refac: styling 2025-12-02 05:47:05 -05:00
Timothy Jaeryang Baek aa589fcbd9 refac 2025-12-02 05:36:45 -05:00
Timothy Jaeryang Baek 9f42b9369f refac 2025-12-02 05:29:34 -05:00
Timothy Jaeryang Baek 143d3fbce2 refac 2025-12-02 04:18:19 -05:00
PocciaandTim Baek 6e531679f4 fix/adjust web search to properly block domains (#19670)
Co-authored-by: Tim Baek <tim@openwebui.com>
2025-12-02 04:17:32 -05:00
Timothy Jaeryang Baek 562f22960c refac 2025-12-02 04:07:02 -05:00
Timothy Jaeryang Baek 5388cc1bc6 refac 2025-12-02 04:03:44 -05:00
Classic298 0a14196afb Update milvus_multitenancy.py (#19680) 2025-12-02 03:57:14 -05:00
Timothy Jaeryang Baek 7b16637043 feat: signin rate limit 2025-12-02 03:52:38 -05:00
Timothy Jaeryang Baek 734c04ebf0 refac 2025-12-02 02:53:49 -05:00
Classic298andTim Baek 4f50571b53 Chore: dep bump (#19667)
* Update pyproject.toml

* Update requirements-min.txt

* Update requirements.txt

---------

Co-authored-by: Tim Baek <tim@openwebui.com>
2025-12-02 02:34:57 -05:00
Timothy Jaeryang Baek 52ccab8fc0 refac 2025-12-01 13:52:09 -05:00
Timothy Jaeryang Baek f5e8d4d5a0 refac 2025-12-01 13:34:57 -05:00
Timothy Jaeryang Baek 51621ba91a feat/enh: user status 2025-12-01 13:18:59 -05:00
Timothy Jaeryang Baek dba86bc980 fix: audit 2025-12-01 10:59:01 -05:00
Shirasawa 21f3411692 i18n: improve Chinese translation (#19651) 2025-12-01 10:58:06 -05:00
Timothy Jaeryang Baek 91473c788c chore: otel bump 2025-12-01 10:57:00 -05:00
Timothy Jaeryang Baek 25f0c26b25 chore: otel bump 2025-12-01 10:29:20 -05:00
Timothy Jaeryang Baek 9791c9bd8b refac 2025-11-30 15:56:42 -05:00
Timothy Jaeryang Baek c62609faba refac 2025-11-30 14:51:44 -05:00
Timothy Jaeryang Baek 88decab9be refac 2025-11-30 14:23:08 -05:00
Timothy Jaeryang Baek d499c3aed8 refac 2025-11-30 14:17:54 -05:00
Timothy Jaeryang Baek 277f3a91f1 refac 2025-11-30 14:06:16 -05:00
joaoback 1818f2b3d9 Update translation.json (pt-BR) (#19603)
translations of the new items that have been included
2025-11-30 11:09:16 -05:00
Timothy Jaeryang Baek a0826ec9fe feat/enh: dm from user profile preview 2025-11-30 11:04:06 -05:00
Timothy Jaeryang Baek 3c846617cd refac 2025-11-30 10:45:54 -05:00
Timothy Jaeryang Baek 39645102d1 refac 2025-11-30 10:40:24 -05:00
Timothy Jaeryang Baek 3f1d9ccbf8 feat/enh: add/remove users from group channel 2025-11-30 10:33:50 -05:00
Timothy Jaeryang Baek 781aeebd2a refac 2025-11-30 08:28:19 -05:00
Timothy Jaeryang Baek f589b7c189 feat/enh: group channel 2025-11-30 08:24:27 -05:00
Timothy Jaeryang Baek 696f356881 refac 2025-11-30 08:12:22 -05:00
Timothy Jaeryang Baek 515f85fe1c refac 2025-11-30 05:17:52 -05:00
Timothy Jaeryang Baek 4d74e6cefa refac: styling 2025-11-30 05:01:41 -05:00
Timothy Jaeryang Baek 3ebb3e2143 refac: styling 2025-11-30 03:56:12 -05:00
Timothy Jaeryang Baek 69b82edd63 refac 2025-11-30 03:50:14 -05:00
Timothy Jaeryang Baek 9d39b9b42c refac: styling 2025-11-30 03:47:34 -05:00
Timothy Jaeryang Baek e65d92fc6f refac 2025-11-30 02:32:34 -05:00
Timothy Jaeryang Baek f3c8c7045d refac 2025-11-29 14:19:55 -05:00
Timothy Jaeryang Baek c9185aaf44 refac 2025-11-29 14:18:22 -05:00
Timothy Jaeryang Baek 05e79bdd0c enh: message reaction user names 2025-11-29 13:54:52 -05:00
Timothy Jaeryang Baek fb6b18faef refac: knowledge file delete behaviour 2025-11-29 13:19:06 -05:00
Tim Baek b56adf01e3 Merge pull request #19584 from Classic298/patch-1
refac: improve weaker model's ability of understanding that the image was created successfully
2025-11-29 13:09:45 -05:00
Timothy Jaeryang Baek 356e982d30 refac 2025-11-29 12:57:17 -05:00
Classic298 bb4b547574 Update middleware.py 2025-11-29 11:11:21 +01:00
Timothy Jaeryang Baek 20340c3e4e refac 2025-11-29 01:05:39 -05:00
Timothy Jaeryang Baek 6c53bf7175 refac: styling 2025-11-28 23:10:01 -05:00
Timothy Jaeryang Baek ff121413da refac 2025-11-28 22:54:00 -05:00
Timothy Jaeryang Baek c1d760692f refac: db group 2025-11-28 22:48:58 -05:00
Timothy Jaeryang Baek a7c7993bbf refac/fix: temp chat image generation 2025-11-28 11:11:56 -05:00
Timothy Jaeryang Baek 0f3156651c refac/fix: ollama model delete 2025-11-28 11:01:22 -05:00
Timothy Jaeryang Baek c8071a3180 refac 2025-11-28 10:51:30 -05:00
Timothy Jaeryang Baek 25994dd3da refac/enh: channel message 2025-11-28 10:45:48 -05:00
Timothy Jaeryang Baek b9e849f17d refac: styling 2025-11-28 10:08:37 -05:00
Timothy Jaeryang Baek 80fbb29ccc refac: styling 2025-11-28 10:07:57 -05:00
Timothy Jaeryang Baek 7b1895ec8a refac 2025-11-28 10:04:06 -05:00
Timothy Jaeryang Baek aae2fce173 feat/enh: pinned messages in channels 2025-11-28 09:58:44 -05:00
Timothy Jaeryang Baek 451907cc92 refac 2025-11-28 09:32:37 -05:00
Timothy Jaeryang Baek 1b095d12ff refac: admin user list active indicator 2025-11-28 08:45:31 -05:00
Timothy Jaeryang Baek 0518749d51 refac: pin icons 2025-11-28 08:01:42 -05:00
Tim Baek fc06c16dd4 Merge pull request #19573 from open-webui/update-user-table
refac/db: update user table
2025-11-28 07:55:00 -05:00
Timothy Jaeryang Baek 33b59adf27 refac 2025-11-28 07:42:45 -05:00
Timothy Jaeryang Baek 70948f8803 enh/refac: deprecate USER_POOL 2025-11-28 07:39:02 -05:00
Timothy Jaeryang Baek c2634d45ad refac 2025-11-28 07:27:55 -05:00
Timothy Jaeryang Baek 8ef482a52a refac: user oauth display 2025-11-28 06:59:59 -05:00
Timothy Jaeryang Baek dcf50c4758 refac: api_key table migration 2025-11-28 06:49:10 -05:00
Timothy Jaeryang Baek 742832a850 refac 2025-11-28 06:41:41 -05:00
Timothy Jaeryang Baek 0a4358c3d1 refac: oauth_sub -> oauth migration 2025-11-28 06:39:36 -05:00
Timothy Jaeryang Baek 369298a83e refac: user table db migration 2025-11-28 06:29:41 -05:00
Timothy Jaeryang Baek b99c9b277a refac: styling 2025-11-28 04:29:50 -05:00
Timothy Jaeryang Baek 4b6773885c enh: dm active user indicator 2025-11-28 04:24:25 -05:00
Timothy Jaeryang Baek d232e433e8 refac: profile preview 2025-11-28 04:22:54 -05:00
Timothy Jaeryang Baek 848f3fd4d8 refac: hide active user count in sidebar user menu 2025-11-28 03:40:16 -05:00
Timothy Jaeryang Baek 453ea9b9a1 refac/fix: db migration issue 2025-11-28 03:10:48 -05:00
Timothy Jaeryang Baek 6ee50770cd refac 2025-11-28 02:44:36 -05:00
Timothy Jaeryang Baek 15dc607779 refac: rm print 2025-11-28 02:34:25 -05:00
Timothy Jaeryang Baek 32c888c280 refac 2025-11-28 01:40:52 -05:00
Timothy Jaeryang Baek 99a7823e01 refac: db 2025-11-28 01:17:43 -05:00
RomualdYT 022f9ff3a5 Update french translation.json (#19547) 2025-11-27 09:21:27 -05:00
Timothy Jaeryang Baek ad86707605 refac 2025-11-27 08:20:14 -05:00
Timothy Jaeryang Baek 289801b608 refac: styling 2025-11-27 08:12:06 -05:00
Timothy Jaeryang Baek 6bb204eb80 refac 2025-11-27 08:07:39 -05:00
Timothy Jaeryang Baek 560702a8f7 refac 2025-11-27 08:04:41 -05:00
Timothy Jaeryang Baek 6752772c1d chore: format 2025-11-27 07:57:31 -05:00
Timothy Jaeryang Baek d645cdbaf3 refac 2025-11-27 07:49:19 -05:00
Timothy Jaeryang Baek 3b4d7d568b refac 2025-11-27 07:43:10 -05:00
Timothy Jaeryang Baek d5d0e72590 refac 2025-11-27 07:39:00 -05:00
Timothy Jaeryang Baek f1a7de94ba refac 2025-11-27 07:33:33 -05:00
Timothy Jaeryang Baek acccb9afdd feat: dm channels 2025-11-27 07:27:32 -05:00
Timothy Jaeryang Baek f2c56fc839 refac 2025-11-27 06:03:22 -05:00
Timothy Jaeryang Baek dd6b808e69 refac 2025-11-27 05:25:38 -05:00
Timothy Jaeryang Baek 7a374ca2a5 refac 2025-11-27 05:11:17 -05:00
Timothy Jaeryang Baek 421aba7cd7 refac: hide channel add button for users 2025-11-27 04:49:29 -05:00
Timothy Jaeryang Baek 09b6ea38c5 feat/enh: group export endpoint 2025-11-27 04:44:01 -05:00
Timothy Jaeryang Baek 28659f6af5 refac/fix: files batch/add endpoint 2025-11-27 04:35:12 -05:00
Timothy Jaeryang Baek 64b4d5d9c2 feat/enh: channels unread messages count 2025-11-27 04:31:04 -05:00
Aleix Dorca c7a48c50a3 Update catalan translation.json (#19536) 2025-11-27 03:02:08 -05:00
Timothy Jaeryang Baekandcw.a b5e5617a41 enh: redis dict for internal models state
Co-Authored-By: cw.a <57549718+acwoo97@users.noreply.github.com>
2025-11-27 01:33:52 -05:00
Timothy Jaeryang Baek ff4b1b9824 refac: chat history data structure 2025-11-27 00:10:53 -05:00
stevessr 86cdcda29a fix: button without type (#19534) 2025-11-27 00:01:36 -05:00
Timothy Jaeryang Baek 5a32ea9b49 refac 2025-11-26 23:54:55 -05:00
Timothy Jaeryang Baek 457af65df6 enh/feat: toggle folders & user perm 2025-11-26 22:47:48 -05:00
Tobias Genannt 04b337323a fix: correct role check on OAuth login (#19476)
When a users role is switched from admin to user in the OAuth provider
their groups are not correctly updated when ENABLE_OAUTH_GROUP_MANAGEMENT
is enabled.
2025-11-26 21:48:06 -05:00
Timothy Jaeryang Baek 384753c6ca refac/enh: drop profile_image_url field in responses 2025-11-26 21:47:20 -05:00
Timothy Jaeryang Baek 3fe5a47050 refac/enh: knowledge base name on icon hover 2025-11-26 21:33:27 -05:00
Timothy Jaeryang Baek d1bbf6ba92 refac 2025-11-26 21:29:52 -05:00
Timothy Jaeryang Baek 9f89cc5adc refac 2025-11-26 21:28:32 -05:00
Shirasawa fa0efae4d5 i18n: improve Chinese translation (#19497) 2025-11-26 17:42:56 -05:00
gerhardj-b f2d6a425de feat: also consider OAUTH_ROLES_SEPARATOR for string claims themselves (#19514) 2025-11-26 17:38:26 -05:00
Classic298 d071cdf7d4 chore: update transformers dependency to fix issue #19512 (#19513)
* Update pyproject.toml

* Update requirements.txt

* Update requirements.txt

* Update pyproject.toml
2025-11-26 16:41:56 -05:00
Classic298 4b21704498 chore: Update pymilvus dep (#19507)
* Update requirements.txt

* Update pyproject.toml
2025-11-26 16:41:20 -05:00
Classic298 9fca4969db chore: dep bump pypdf to ver 6.4.0 (#19508)
* Update pyproject.toml

* Update requirements.txt
2025-11-26 16:41:12 -05:00
Timothy Jaeryang Baek 4370dee79e fix: async save docs to vector db 2025-11-25 17:19:33 -05:00
Classic298 c631659327 i18n: de-de (#19471) 2025-11-25 16:31:38 -05:00
Classic298 4df5b7eb2e fix: update dependency to prevent rediss:// failure (#19488)
* Update pyproject.toml

* Update requirements.txt

* Update requirements-min.txt
2025-11-25 16:28:58 -05:00
Timothy Jaeryang Baek 8b2015a97b refac 2025-11-25 16:28:06 -05:00
Timothy Jaeryang Baek 477097c2e4 refac 2025-11-25 16:27:27 -05:00
Timothy Jaeryang Baek c5b73d7184 refac/fix: function name filter type 2025-11-25 16:25:40 -05:00
Timothy Jaeryang Baek c7eb713689 fix: user preview profile image 2025-11-25 08:00:30 -05:00
Aleix Dorca 1bfe2c92ba Merge pull request #19464 from aleixdorca/dev
i18n: Update Catalan translation.json
2025-11-25 07:03:49 -05:00
Timothy Jaeryang Baek 69722ba973 fix/refac: workspace shared model list 2025-11-25 06:32:27 -05:00
Tim Baek 140605e660 Merge pull request #19462 from open-webui/dev
0.6.40
2025-11-25 06:01:33 -05:00
Timothy Jaeryang Baek f3547568e4 refac: channel user list order by 2025-11-25 05:53:31 -05:00
Classic298 15c6860a49 Update CHANGELOG.md (#19463)
* Update CHANGELOG.md

* Update CHANGELOG.md
2025-11-25 05:50:39 -05:00
Timothy Jaeryang Baek 363ef194d8 chore: bump python-socketio==5.14.0 2025-11-25 05:49:30 -05:00
Timothy Jaeryang Baek 33a52628e6 chore: bump 2025-11-25 05:48:12 -05:00
Timothy Jaeryang Baek 35ab6b7667 fix: postgres user list issue 2025-11-25 05:47:04 -05:00
Timothy Jaeryang Baek 97ba5b8436 fix: changelog 2025-11-25 05:42:18 -05:00
Tim Baek 9899293f05 Merge pull request #19448 from open-webui/dev
0.6.39
2025-11-25 05:31:34 -05:00
Timothy Jaeryang Baek 3fa484f290 doc: changelog 2025-11-25 05:29:16 -05:00
Timothy Jaeryang Baek 03dc4d7182 refac/enh: copy formatted table 2025-11-25 05:25:49 -05:00
Classic298 82a5f11b72 CHANGELOG: 0.6.39 (#19446)
* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md
2025-11-25 05:13:23 -05:00
Timothy Jaeryang Baek 4847bdcc9b chore: format 2025-11-25 05:12:45 -05:00
Timothy Jaeryang Baek 0fa97bde00 fix: i18n 2025-11-25 05:11:37 -05:00
Timothy Jaeryang Baek d5c3e9ea42 refac 2025-11-25 05:10:08 -05:00
Timothy Jaeryang Baek 6235243b62 refac 2025-11-25 05:07:53 -05:00
Timothy Jaeryang Baek 63ca0a3519 refac 2025-11-25 04:56:26 -05:00
Classic298 6a095099d5 chore: add chardet (#19458)
* Update pyproject.toml

* Update requirements-min.txt

* Update requirements.txt

* Update requirements-min.txt

* Update requirements.txt

* Update pyproject.toml
2025-11-25 04:52:25 -05:00
Timothy Jaeryang Baek f22d92e102 refac: styling 2025-11-25 04:52:03 -05:00
Timothy Jaeryang Baek 84ca2258be refac 2025-11-25 04:45:52 -05:00
Timothy Jaeryang Baek e6d8f89850 chore: version bump 2025-11-25 04:38:13 -05:00
Timothy Jaeryang Baek c0e1203538 feat: user list in channels 2025-11-25 04:38:07 -05:00
Timothy Jaeryang Baek baa1e07aec refac 2025-11-25 04:37:58 -05:00
Timothy Jaeryang Baek f2ee70cbfc fix: ENABLE_CHAT_RESPONSE_BASE64_IMAGE_URL_CONVERSION env var 2025-11-25 04:15:41 -05:00
Timothy Jaeryang Baek 3b5710d0cd feat/enh: show user count in channels 2025-11-25 03:46:30 -05:00
Timothy Jaeryang Baek a7ee36266a refac: styling 2025-11-25 03:15:40 -05:00
Timothy Jaeryang Baek f0c7bd3f79 refac 2025-11-25 03:12:21 -05:00
Timothy Jaeryang Baek 743199f2d0 feat/enh: tool server function name filter list 2025-11-25 02:31:34 -05:00
Timothy Jaeryang Baek 488631db98 refac 2025-11-25 02:05:27 -05:00
Timothy Jaeryang BaekandClassic298 2328dc284e feat/enh: async embedding processing setting
Co-Authored-By: Classic298 <27028174+Classic298@users.noreply.github.com>
2025-11-25 01:55:43 -05:00
Timothy Jaeryang Baek b1c1e68e56 refac/fix: group member user list 2025-11-25 01:37:33 -05:00
Timothy Jaeryang Baek 38c6b0bff6 fix: inline citations 2025-11-24 23:04:01 -05:00
Timothy Jaeryang Baek 9c19d0abd4 refac/breaking: docling params 2025-11-24 16:01:13 -05:00
Classic298andTim Baek b875a438f0 Update translation.json (#19445)
Co-authored-by: Tim Baek <tim@openwebui.com>
2025-11-24 15:39:53 -05:00
Timothy Jaeryang Baek f0d75e3a48 refac/fix: db operations 2025-11-24 15:39:13 -05:00
Classic298 0a687980ee Update knowledge.py (#19434) 2025-11-24 15:22:23 -05:00
Alexandr Promakh a7b611c0e5 fix: "No connection adapters were found" routers/images.py (#19435) 2025-11-24 14:51:52 -05:00
Tim Baek e567f42020 Merge pull request #19428 from joaoback/patch-16
Update translation.json (pt-BR)
2025-11-24 14:51:17 -05:00
joaoback 3b23b96a27 Update translation.json (pt-BR)
New translations of the items added in the latest version.
2025-11-24 12:11:22 -03:00
Tim Baek e3faec62c5 Merge pull request #19416 from open-webui/dev
0.6.38
2025-11-24 07:00:31 -05:00
Timothy Jaeryang Baek 0f8729dea2 refac 2025-11-24 06:42:12 -05:00
Timothy Jaeryang Baek 7ad549b4fb Update docker-build.yaml 2025-11-24 06:27:52 -05:00
Timothy Jaeryang Baek 5183eaab4d doc: changelog 2025-11-24 06:16:17 -05:00
Timothy Jaeryang Baek b004a4a2c1 chore: bump 2025-11-24 06:13:21 -05:00
Timothy Jaeryang Baek b2a6597617 fix: tool server save error handling 2025-11-24 06:10:21 -05:00
Timothy Jaeryang Baek 286a5ad0db refac/fix: oauth 2025-11-24 06:03:19 -05:00
Timothy Jaeryang Baek 06f0bfd9f5 fix 2025-11-24 05:58:22 -05:00
Timothy Jaeryang Baek 662a1fac47 fix: hybrid search 2025-11-24 05:52:18 -05:00
Tim Baek cea99175ca Merge pull request #19407 from ShirasawaSama/i18n/improve-chinese-translation
i18n: improve Chinese translation
2025-11-24 03:06:27 -05:00
Shirasawa a470f6149f i18n: improve Chinese translation 2025-11-24 07:11:52 +00:00
Tim Baek fc05e0a6c5 Merge pull request #19405 from open-webui/dev
chore: format
2025-11-23 22:16:33 -05:00
Timothy Jaeryang Baek 819668b42d chore: format 2025-11-23 22:16:06 -05:00
Tim Baek fe6783c166 Merge pull request #19030 from open-webui/dev
0.6.37
2025-11-23 22:10:05 -05:00
Timothy Jaeryang Baek a91663c504 refac 2025-11-23 22:09:14 -05:00
Timothy Jaeryang Baek 1e01836c08 refac: styling 2025-11-23 22:08:17 -05:00
Timothy Jaeryang Baek 97f5c21485 refac 2025-11-23 21:24:36 -05:00
Timothy Jaeryang Baek 9d14bc2a8d refac: styling 2025-11-23 21:21:08 -05:00
Timothy Jaeryang Baek 4857c69bd3 chore: format 2025-11-23 21:18:35 -05:00
Timothy Jaeryang Baek de889f5ec7 refac: styling 2025-11-23 21:17:14 -05:00
Timothy Jaeryang Baek 0259312626 refac 2025-11-23 21:12:01 -05:00
Timothy Jaeryang Baek 5af5e1d3e4 refac 2025-11-23 21:10:38 -05:00
Timothy Jaeryang BaekandClassic298 2e1ddf823b refac: prompt suggestions component
Co-Authored-By: Classic298 <27028174+Classic298@users.noreply.github.com>
2025-11-23 21:08:13 -05:00
Timothy Jaeryang Baek 5b1fa9dd0d refac: styling 2025-11-23 20:37:29 -05:00
Timothy Jaeryang Baek fbbfa5aa79 refac 2025-11-23 20:35:50 -05:00
Timothy Jaeryang Baek 42fa92ee64 refac 2025-11-23 20:24:42 -05:00
Classic298 7d3f45eff9 chore: CHANGELOG 0.6.37 (#19126)
* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md
2025-11-23 20:21:38 -05:00
Timothy Jaeryang Baek 0eb1246c87 refac 2025-11-23 20:19:44 -05:00
Timothy Jaeryang Baek 48d1e67e79 chore: format 2025-11-23 20:15:52 -05:00
Timothy Jaeryang Baek cd008eeb50 refac: folder page chat list 2025-11-23 20:11:55 -05:00
Timothy Jaeryang Baek e29c262394 refac/fix: refresh folder chat list 2025-11-23 20:04:54 -05:00
Timothy Jaeryang Baek 5148970ef5 refac 2025-11-23 19:47:21 -05:00
Timothy Jaeryang Baek a5c359aede refac 2025-11-23 19:42:59 -05:00
Timothy Jaeryang Baek 4ad7a9bb9c fix: kokorojs tts 2025-11-23 19:23:15 -05:00
Timothy Jaeryang Baek 66ffd77f2c refac 2025-11-23 19:19:22 -05:00
Timothy Jaeryang Baek 46bbf760e8 enh: group members selector 2025-11-23 18:56:50 -05:00
Timothy Jaeryang Baek 103f92c8dd refac 2025-11-23 18:39:01 -05:00
Timothy Jaeryang Baek 9b7c3ff999 refac 2025-11-23 18:31:59 -05:00
Timothy Jaeryang Baek ec45d77ce9 refac: sources and citations 2025-11-23 18:27:57 -05:00
Timothy Jaeryang Baek b0491886bc refac: disable single tilde 2025-11-23 17:16:10 -05:00
Classic298 a4c3fa70c1 chore: Update README (#19398) 2025-11-23 16:49:55 -05:00
Timothy Jaeryang Baek 50813fcce4 chore: google-genai bump 2025-11-23 16:49:21 -05:00
Classic298 99f0fe7f32 chore: user header forward minimize code changes throughout codebase (#19392)
* Update external.py

* remove unused imports

* Update ollama.py

* Update ollama.py

* Update ollama.py

* Update openai.py
2025-11-23 16:48:03 -05:00
Timothy Jaeryang Baek 14baf6955d refac 2025-11-23 16:47:21 -05:00
Timothy Jaeryang Baek 288947a648 refac 2025-11-23 16:09:37 -05:00
Timothy Jaeryang Baek 682013cee3 refac 2025-11-23 16:08:03 -05:00
Timothy Jaeryang Baek f5809165d7 refac 2025-11-23 15:28:07 -05:00
_00_ e2ef3d9647 Upd:i18n es-ES_Spanish Translation_v0.6.37 (#19388)
* Upd:i18n es-ES_Spanish Translation_v0.6.37

### es-ES Spanish Translation v0.6.37

Added new strings.

* Corrected string
2025-11-23 04:40:18 -05:00
Classic298 60dbde7e19 chore (#19389) 2025-11-23 04:40:05 -05:00
Timothy Jaeryang Baek 7cf07b7e97 refac: rm folder id on chat archive 2025-11-23 00:05:27 -05:00
Timothy Jaeryang Baekandexpruc e6951e804a feat/enh: move chats in folder on delete
Co-Authored-By: expruc <25387342+expruc@users.noreply.github.com>
2025-11-23 00:01:49 -05:00
Timothy Jaeryang Baek b2034861ae refac: models workspace optimization 2025-11-22 23:20:51 -05:00
Timothy Jaeryang Baek f9c96d03ad refac 2025-11-22 22:57:27 -05:00
Timothy Jaeryang Baek 9bfc414d26 refac 2025-11-22 21:33:14 -05:00
Classic298andClaude 902c6cfbea perf: 50x performance improvement for external embeddings (#19296)
* Update utils.py (#77)

Co-authored-by: Claude <noreply@anthropic.com>

* refactor: address code review feedback for embedding performance improvements (#92)

Co-authored-by: Claude <noreply@anthropic.com>

* fix: prevent sentence transformers from blocking async event loop (#95)

Co-authored-by: Claude <noreply@anthropic.com>

---------

Co-authored-by: Claude <noreply@anthropic.com>
2025-11-22 20:54:59 -05:00
Timothy Jaeryang Baek bb3e222e09 refac: clean null bytes on load 2025-11-22 20:50:27 -05:00
Timothy Jaeryang Baek 4af7cc818e refac/fix: chat search null byte filter 2025-11-22 20:34:49 -05:00
joaoback 19ffa9fc19 Update translation.json (pt-BR) (#19384)
new translations of the newly added items
2025-11-22 19:37:34 -05:00
Siwadon S. (Jay) d48f34cc5b fix(i18n): comprehensive revision and improvement of all Thai translations across the app (#19377) 2025-11-22 16:56:12 -05:00
Timothy Jaeryang Baek b32f7815b8 refac: search chat postgres 2025-11-21 18:31:03 -05:00
Timothy Jaeryang Baek 8f2812d394 fix: translation 2025-11-21 18:30:43 -05:00
Timothy Jaeryang Baek 833e3c2690 refac 2025-11-21 17:45:55 -05:00
Timothy Jaeryang Baek 598650f70b refac 2025-11-21 17:01:38 -05:00
Classic298 de971d7aa2 Update translation.json (#19364) 2025-11-21 15:59:48 -05:00
Siwadon S. (Jay) eec697e00d fix(i18n): correct Thai translation in sidebar (#19363) 2025-11-21 15:59:42 -05:00
Timothy Jaeryang Baek b4c4d9baf5 refac 2025-11-21 15:59:08 -05:00
Timothy Jaeryang Baek 4d4c572bba refac 2025-11-21 15:42:13 -05:00
Timothy Jaeryang BaekandG30 b88f829dbb enh: clone system models
Co-Authored-By: G30 <50341825+silentoplayz@users.noreply.github.com>
2025-11-21 05:28:55 -05:00
Timothy Jaeryang Baek 2b58191b82 refac 2025-11-21 04:32:49 -05:00
Timothy Jaeryang Baek 0c18cd67d5 refac/fix: openai edit multiple images 2025-11-21 04:13:59 -05:00
Timothy Jaeryang Baek 0dd9ad7ffc refac 2025-11-21 03:58:30 -05:00
Timothy Jaeryang BaekandG30 a51579a84b refac/pref: chat import optimization
Co-Authored-By: G30 <50341825+silentoplayz@users.noreply.github.com>
2025-11-21 03:49:49 -05:00
Aleix Dorca 3ef7367f01 Update Catalan translation.json (#19338) 2025-11-21 03:28:38 -05:00
Timothy Jaeryang Baek 89b1ad649b refac: styling 2025-11-21 03:10:00 -05:00
Shirasawa 23cadaa41a fix: add missing i18n import to fix build (#19337) 2025-11-21 03:05:15 -05:00
Danny Liu bed201e46e fix: format date according to DEFAULT_LOCALE in chat search (#19305)
* fix: localized format

* load default_locale from backend
2025-11-21 01:49:27 -05:00
Timothy Jaeryang Baek 27b8775032 refac 2025-11-21 01:48:06 -05:00
Shirasawa 41701697ec i18n: improve Chinese translation (#19334) 2025-11-21 01:39:27 -05:00
Cyp 2d8e321add Korean update (#19336) 2025-11-21 01:39:19 -05:00
Timothy Jaeryang Baek 6442871947 refac: profile_image_url optimization 2025-11-20 20:43:59 -05:00
Timothy Jaeryang Baek cd30152c83 refac: styling 2025-11-20 19:57:11 -05:00
Timothy Jaeryang Baek 557170c0b6 refac/enh: dedicated enable image edit toggle 2025-11-20 19:52:31 -05:00
Timothy Jaeryang Baek 0c430629e5 chore: dep 2025-11-20 19:24:52 -05:00
Diwakar b8728064d8 feat: add support for Weaviate vector database (#14747) 2025-11-20 19:23:46 -05:00
Timothy Jaeryang Baek 7be750bcbb feat/enh: group share setting 2025-11-20 19:12:56 -05:00
Timothy Jaeryang Baek d1e7957e69 refac 2025-11-20 18:39:30 -05:00
Timothy Jaeryang Baek f69e37a850 feat/enh: user sharing perms 2025-11-20 18:32:34 -05:00
Timothy Jaeryang Baek 849278ca4f refac: styling 2025-11-20 18:27:08 -05:00
b65c728208 feat: Add default group assignment for new users (#94) (#19325)
Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
Co-authored-by: Claude <noreply@anthropic.com>
2025-11-20 17:47:15 -05:00
Timothy Jaeryang Baek 680cde8f9b feat/enh: optional password validation 2025-11-20 17:44:49 -05:00
Timothy Jaeryang Baek e6c7495c1a refac/fix: styling 2025-11-20 17:19:59 -05:00
Timothy Jaeryang Baek 6083960655 refac: feedback list optimisation 2025-11-20 17:10:12 -05:00
Classic298andClaude 485896753d feat: Add user header information for TTS/STT requests (#93) (#19323)
Resolves #19312

Co-authored-by: Claude <noreply@anthropic.com>
2025-11-20 16:43:22 -05:00
Blake 22e85df448 Support folder drag-n-drop (#19320) 2025-11-20 16:26:36 -05:00
Timothy Jaeryang Baek ff7a54653a refac/enh: unregisterServiceWorkers on update 2025-11-20 15:34:15 -05:00
Timothy Jaeryang BaekandShirasawa 4c28f19bdd enh/pref: convert markdown base64 images to urls
Co-Authored-By: Shirasawa <kaguyashirasawa@gmail.com>
2025-11-20 04:00:02 -05:00
Shirasawa 17ac79920f i18n: improve Chinese translation (#19309) 2025-11-20 03:51:07 -05:00
gerhardj-b 66c5b7380d feat: allow flat claims instead of nested claims as alternative (#19286) 2025-11-19 19:58:53 -05:00
Timothy Jaeryang Baek c2f889cf9a refac: add reasoning_effort to azure supported params 2025-11-19 14:59:20 -05:00
Timothy Jaeryang Baek c49bcc65c4 refac 2025-11-19 14:22:42 -05:00
Timothy Jaeryang Baek aad23e2e53 refac 2025-11-19 14:14:53 -05:00
Timothy Jaeryang Baek 17389e1b66 refac 2025-11-19 06:41:15 -05:00
Timothy Jaeryang Baek c4ecad0605 enh: revoked token handling 2025-11-19 06:08:59 -05:00
Shirasawa e486490451 i18n: improve Chinese translation (#19285) 2025-11-19 05:15:49 -05:00
Timothy Jaeryang BaekandClassic298 4bb15aa425 feat: default pinned models
Co-Authored-By: Classic298 <27028174+Classic298@users.noreply.github.com>
2025-11-19 05:04:03 -05:00
Timothy Jaeryang Baek 93d0b8241c refac 2025-11-19 04:44:40 -05:00
Timothy Jaeryang Baek ee26c0537e refac 2025-11-19 03:55:08 -05:00
Timothy Jaeryang Baek c2afd0d5aa refac 2025-11-19 03:54:05 -05:00
Timothy Jaeryang Baek 76dbbf57d2 refac: rm ai slop 2025-11-19 03:51:10 -05:00
Timothy Jaeryang Baek 88416161cc refac: styling 2025-11-19 03:26:10 -05:00
Timothy Jaeryang Baek a1d09eae95 chore: format 2025-11-19 03:23:33 -05:00
Timothy Jaeryang BaekandG30 3d50d5ff77 refac
Co-Authored-By: G30 <50341825+silentoplayz@users.noreply.github.com>
2025-11-19 03:21:28 -05:00
Timothy Jaeryang BaekandG30 5bec4a8005 refac
Co-Authored-By: G30 <50341825+silentoplayz@users.noreply.github.com>
2025-11-19 03:19:12 -05:00
Timothy Jaeryang Baek af1db82c7d refac: models endpoint 2025-11-19 03:18:16 -05:00
Timothy Jaeryang Baek 0c47cbd16a refac/enh: mcp oauth auth method support 2025-11-19 02:26:42 -05:00
Timothy Jaeryang Baek 76acdabdc3 chore: mcp bump 2025-11-19 02:18:59 -05:00
Timothy Jaeryang Baek 90f76d24ec refac 2025-11-19 02:16:09 -05:00
Shirasawa 31dc97b68b feat: Add image handling in middleware for delta updates (#19073)
* feat: Add image handling in middleware for delta updates

* refactor: optimize the code logic
2025-11-19 02:07:56 -05:00
Timothy Jaeryang Baek 7031bb9067 feat/enh: api keys user permission
breaking change, `ENABLE_API_KEY` renamed to `ENABLE_API_KEYS` and disabled by default and must be explicitly toggled on.
2025-11-19 01:50:52 -05:00
Adam Skalicky f89c170566 Add additional config elements to control how engineio and redis log and interact. (#19091) 2025-11-19 01:26:33 -05:00
Timothy Jaeryang BaekandG30 7e03637446 enh/feat: persist folder state
Co-Authored-By: G30 <50341825+silentoplayz@users.noreply.github.com>
2025-11-19 01:20:07 -05:00
Timothy Jaeryang Baek bbd48b3638 enh: images openai api params 2025-11-19 01:04:42 -05:00
davecrab 7762fa5ddf feat: Add adjustable text size setting to interface (#19186)
* Add adjustable text size setting to interface

Introduces a user-configurable text size (scale) setting, accessible via a slider in the interface settings. Updates CSS and Sidebar chat item components to respect the new --app-text-scale variable, and persists the setting in the store. Adds related i18n strings and ensures the text scale is applied globally and clamped to allowed values.

* Refactor text scale logic into utility module

Moved all text scale related constants and functions from components and stores into a new utility module (src/lib/utils/text-scale.ts). Updated imports and usage in Interface.svelte and index.ts to use the new module, improving code organization and reusability.

* Adjust sidebar chat scaling without extra classes

keep sidebar markup using existing Tailwind utility classes so chat items render identically pre-feature
move all text-scale sizing into app.css under the #sidebar-chat-item selectors
change the root font-size multiplier to use 1rem instead of an explicit 16px so browser/user preferences propagate

* Update Switch.svelte

Adjust toggles from fixed pixel to rem to scale with the text size

* Update Interface.svelte

Updated label from 'Text Scale' to 'UI Scale'.
Added padding around slider

* Update app.css

Added comments
2025-11-19 00:55:52 -05:00
Seth Argyle 720af637e6 fix: Use get_index() instead of list_indexes() in has_collection() to… (#19238)
* fix: Use get_index() instead of list_indexes() in has_collection() to handle pagination

Fixes #19233

  Replace list_indexes() pagination scan with direct get_index() lookup
  in has_collection() method. The previous implementation only checked
  the first ~1,000 indexes due to unhandled pagination, causing RAG
  queries to fail for indexes beyond the first page.

  Benefits:
  - Handles buckets with any number of indexes (no pagination needed)
  - ~8x faster (0.19s vs 1.53s in testing)
  - Proper exception handling for ResourceNotFoundException
  - Scales to millions of indexes

* Update s3vector.py

Unneeded exception handling removed to match original OWUI code
2025-11-19 00:19:10 -05:00
Timothy Jaeryang Baek 4386e5abb8 refac/enh: create new note 2025-11-18 18:37:57 -05:00
Jacob LeksanandTim Baek 07ef295a77 feat: Adding file metadata to hybrid search (#19095)
* Added metadata to hybrid search

* And config and env plus refac

* consistency

---------

Co-authored-by: Tim Baek <tim@openwebui.com>
2025-11-18 15:29:07 -05:00
Timothy Jaeryang Baek 42071cb8e8 refac 2025-11-18 15:27:26 -05:00
Timothy Jaeryang Baek d98b945d73 refac: styling 2025-11-18 15:23:29 -05:00
Timothy Jaeryang Baek 3f97a6993f refac: mineru api key required behaviour 2025-11-18 14:37:46 -05:00
Tom Haynes ccd80b9dba obfuscate TTS elevenlabs api key (#19262) 2025-11-18 14:36:36 -05:00
Timothy Jaeryang Baek baffa89f35 refac: styling 2025-11-18 14:27:17 -05:00
Timothy Jaeryang Baek 2af4c4b3c7 refac 2025-11-18 04:42:09 -05:00
Timothy Jaeryang BaekandClassic298 02238d3113 feat/security: Add SSRF protection with configurable blocklist
Co-Authored-By: Classic298 <27028174+Classic298@users.noreply.github.com>
2025-11-18 04:40:55 -05:00
lazarivandMoritz 6cdb13d5cb feat: pgvector hnsw index type (#19158)
* Adding hnsw index type for pgvector, allowing vector dimensions larger than 2000

* remove some variable assignments

* Make USE_HALFVEC variable configurable

* Simplify USE_HALFVEC handling

* Raise runtime error if the index requires rebuilt

---------

Co-authored-by: Moritz <moritz.mueller2@tu-dresden.de>
2025-11-18 04:14:43 -05:00
Timothy Jaeryang Baek 63ebc295ce refac: styling 2025-11-18 04:04:32 -05:00
Tim Baek 34684e7e58 feat/refac: group members db table (#19239)
* refac: group members table db migration

* refac: group members backend

* refac: group members frontend

* refac: group members frontend integration

* refac: styling
2025-11-18 03:59:56 -05:00
Timothy Jaeryang Baek ed6aabfbfd refac: styling 2025-11-18 03:54:19 -05:00
Timothy Jaeryang Baek 73734b186b refac: group members frontend integration 2025-11-18 03:44:26 -05:00
Classic298andClaude b4bc71d1bd feat: add granular import/export permissions for workspace items (#19242)
* feat: add granular import/export permissions for workspace items (#55)

Co-authored-by: Claude <noreply@anthropic.com>

* Fix permissions toggles not saving in EditGroupModal (#58)

Co-authored-by: Claude <noreply@anthropic.com>

* Fix permissions toggles not saving in EditGroupModal (#59)

Co-authored-by: Claude <noreply@anthropic.com>

---------

Co-authored-by: Claude <noreply@anthropic.com>
2025-11-17 18:25:23 -05:00
logan-hcg 0ed174f6a1 Update MCP Oauth server metadata discovery order (#19244) 2025-11-17 18:24:43 -05:00
FlorentMair80 58cff5e482 feat: add a metric to monitor daily unique users (#19236)
#19234
2025-11-17 15:31:24 -05:00
Timothy Jaeryang Baek 0a72d047ef refac: group members frontend 2025-11-17 05:09:21 -05:00
Timothy Jaeryang Baek bc576782d7 refac: group members backend 2025-11-17 05:09:06 -05:00
Timothy Jaeryang Baek f05e945a45 refac: group members table db migration 2025-11-17 05:08:45 -05:00
Timothy Jaeryang Baek e76f72576e refac 2025-11-17 05:07:59 -05:00
Timothy Jaeryang Baek f138be9d8a refac/fix: inherit model stream_response setting 2025-11-17 03:30:35 -05:00
Timothy Jaeryang Baek 3c381fad13 refac 2025-11-17 03:23:55 -05:00
Timothy Jaeryang Baek 03cc6ce8eb refac/sec: sanitize note pdf download 2025-11-16 19:17:08 -05:00
Timothy Jaeryang BaekandDaniel Pots 7c2bed2c73 fix: UserValves contamination between multiple tools
Co-Authored-By: Daniel Pots <3932988+podden@users.noreply.github.com>
2025-11-16 14:16:23 -05:00
Timothy Jaeryang Baek b1565e6913 refac 2025-11-16 13:52:45 -05:00
Timothy Jaeryang Baek ee10f372a0 refac/enh: web search domain allow/block filter 2025-11-16 13:52:09 -05:00
Davixk 21c0dd93e2 fix(images): correct config key for image edit engine (#19200)
Updates conditional to reference the appropriate configuration property for image editing, ensuring proper engine selection.
2025-11-16 13:28:43 -05:00
Classic298 a79a39bb64 Update translation.json (#19213) 2025-11-16 13:27:08 -05:00
Timothy Jaeryang Baek 31fb34918f refac/fix 2025-11-16 01:13:35 -05:00
Timothy Jaeryang Baek 80388855f4 refac/fix 2025-11-16 00:30:03 -05:00
Timothy Jaeryang Baek 8f48e96f5e refac/fix 2025-11-15 15:47:02 -05:00
Timothy Jaeryang Baek 9370b263f5 refac/fix: automatic1111 params 2025-11-15 15:43:23 -05:00
Timothy Jaeryang Baek 1b0bce529f refac 2025-11-13 20:55:34 -05:00
Timothy Jaeryang Baek bc2192e8bf refac: background image styling behaviour 2025-11-13 20:54:23 -05:00
Timothy Jaeryang Baek 4d41013804 chore: dep 2025-11-13 20:25:27 -05:00
Timothy Jaeryang Baek 8f3bd2ecbe feat: voice mode prompt template 2025-11-13 20:23:13 -05:00
Timothy Jaeryang Baek 757caeab55 chore: format 2025-11-13 20:01:21 -05:00
Timothy Jaeryang Baek 9df8d5b204 enh: copy table 2025-11-13 19:59:45 -05:00
Timothy Jaeryang Baek b160eef7eb refac: decouple api key restrictions from get user 2025-11-13 19:52:04 -05:00
Timothy Jaeryang Baek e2ff2ae252 refac 2025-11-13 19:42:32 -05:00
Timothy Jaeryang Baek a4b2dc22c4 wip: requirements-min 2025-11-13 19:24:32 -05:00
Timothy Jaeryang Baek 5d4134ba77 refac 2025-11-13 19:14:00 -05:00
Sang LêandTim Baek 64747f7f79 Add Azure Search (#19104)
Co-authored-by: Tim Baek <tim@openwebui.com>
2025-11-13 19:12:34 -05:00
Timothy Jaeryang Baek 117a33b030 fix: docling params issue 2025-11-13 17:05:30 -05:00
Mikael Schirén 7e05cf4e21 make path to audit log configurable (#19173) 2025-11-13 16:48:00 -05:00
Jeppe Kuhlmann Andersen 2bec5c5a5c Updated Danish translations (#19174) 2025-11-13 16:28:38 -05:00
Timothy Jaeryang Baek c43f95f4b8 refac: pass token_endpoint_auth_method 2025-11-13 15:34:45 -05:00
Timothy Jaeryang Baek 6d9a562edd refac: oauth pass client auth params 2025-11-13 15:30:22 -05:00
Siavash Vatanijalal 6b638db114 Updated Swedish translation (#19161)
Refined existing swedish translations and added most of the missing ones.
2025-11-13 14:39:04 -05:00
Timothy Jaeryang Baek 2de854fa02 enh: text select copy behaviour 2025-11-13 00:40:29 -05:00
Timothy Jaeryang Baek 38f45a38cb refac: chat tag suggestions behaviour 2025-11-13 00:06:32 -05:00
Timothy Jaeryang Baek 84912904fd chore: bump unstructured to 0.18.18 2025-11-12 23:41:54 -05:00
Timothy Jaeryang Baek da42850eff enh: custom headers for external tool servers 2025-11-12 23:39:27 -05:00
Timothy Jaeryang Baek 0bf686396d refac/fix 2025-11-12 23:27:03 -05:00
Timothy Jaeryang Baek f1c317349e enh/refac: enable autocompletion for non rich text input 2025-11-12 23:11:15 -05:00
xqqp 3207998114 Fix: Handle empty strings in OAuth registration response (#19144)
- The mcp package requires optional unset values to be None. If an empty string is passed, it gets validated and fails.
- Replace all empty strings with None.
2025-11-12 22:57:53 -05:00
Classic298andClaude ad17d35ac4 feat: Add custom API endpoint and user info headers for Perplexity Search (#31) (#19147)
Co-authored-by: Claude <noreply@anthropic.com>
2025-11-12 22:53:54 -05:00
Oleg Yermolenko 8dde493e8e fix verify mcp connection with oauth type (#19149) 2025-11-12 15:05:17 -05:00
Classic298andgoogle-labs-jules[bot] 6eea0d40ab Feat: optionally disable password login endpoints (#19113)
* Implement message cleaning before API call

* Filter out empty assistant messages before cleaning

* Update catalan translation.json (#29)

Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>

* Update main.py

* Update auths.py

* Update Chat.svelte

---------

Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
2025-11-11 17:07:57 -05:00
Mati ce19b7120b fix: Duplicate instructions in tool selection calling prompt (#19122)
* Fix duplicated query prefix in user prompt for function calling

* Fix duplicated last user message in prompt for function calling
2025-11-11 14:35:25 -05:00
Tim Baek 4eecbf8ee4 Merge pull request #19119 from fredrikblau/feat/improve-farsi-translation
feat(i18n): fill in missing Farsi translations
2025-11-11 11:26:31 -05:00
amir ahrari cd99df870d feat(i18n): fill in missing Farsi translations 2025-11-11 15:56:04 +03:30
Tim Baek ed3744b672 Merge pull request #19084 from aindriu80/update-ga-string-10-november
i18n: Update ie-GA translation
2025-11-11 00:30:56 -05:00
Timothy Jaeryang Baek 4673e120c4 refac/fix: mineru params
breaking change
2025-11-11 00:30:11 -05:00
Timothy Jaeryang Baek 8e41fea2d9 refac: rm redundant query tag 2025-11-11 00:25:56 -05:00
Tim Baek 8bff76f745 Merge pull request #19097 from adam-skalicky/api_models_perf_optimization
perf: Fetched user_group_ids prior to looping through models with has_access to reduce DB hits for group membership
2025-11-11 00:16:14 -05:00
Timothy Jaeryang Baek 5aa2d01c17 refac/fix: rag template placeholder substitution 2025-11-11 00:08:05 -05:00
Adam Skalicky dc6e1fe6bd Fetched user_group_ids prior to looping through models with has_access to reduce DB hits for group membership 2025-11-10 16:48:56 -08:00
Aindriú Mac Giolla Eoin ce23843506 i18n - Update ie-GA translation 2025-11-10 12:14:17 +00:00
Timothy Jaeryang Baek b2667470cd refac: get event emitter/caller 2025-11-09 23:13:25 -05:00
Timothy Jaeryang Baek 62073d3b7f fix: images edit openai base url/key save issue 2025-11-09 22:36:00 -05:00
Timothy Jaeryang Baek bc739de024 refac: rerank 2025-11-09 21:33:50 -05:00
Timothy Jaeryang Baek e76f77bcb7 refac: stream chunk max buffer size 2025-11-09 21:16:34 -05:00
Timothy Jaeryang Baek 6cb41a59da refac 2025-11-09 21:10:11 -05:00
Timothy Jaeryang Baek 413fa27b18 refac 2025-11-09 21:09:59 -05:00
Tim Baek 27df461abd Merge pull request #18884 from ShirasawaSama/feature/handle-large-stream-chunks
feat: handle large stream chunks responses to support Nano Banana [Test Needed]
2025-11-09 21:08:49 -05:00
Timothy Jaeryang BaekandSihyeon Jang a65cc196a5 refac: batch file processing
Co-Authored-By: Sihyeon Jang <24850223+sihyeonn@users.noreply.github.com>
2025-11-09 21:06:21 -05:00
Tim Baek 284764e178 Merge pull request #19025 from krishna-medapati/fix-hybrid-search-17046-clean
fix: Handle AttributeError in hybrid search with reranking (#17046)
2025-11-09 20:43:52 -05:00
Timothy Jaeryang Baek 908f504885 refac 2025-11-09 20:39:05 -05:00
Timothy Jaeryang Baek e69c2cf3f6 refac 2025-11-09 16:12:38 -05:00
Timothy Jaeryang BaekandClassic298 25c7f101f2 enh: optionally add user headers external websearch
Co-Authored-By: Classic298 <27028174+Classic298@users.noreply.github.com>
2025-11-09 16:09:29 -05:00
Timothy Jaeryang Baek 9578bac099 refac: suggestions display full name on hover 2025-11-09 15:44:27 -05:00
Tim Baek 79fd61cd5f Merge pull request #19042 from Classic298/remove-litellm-endpoint
refactor: Remove unused litellm endpoint and associated frontend code
2025-11-08 15:12:39 -05:00
Tim Baek c13a444326 Merge pull request #19038 from rgaricano/dev-es_ES
Upd: i18n_ es-ES Spanish Translation v0.6.36
2025-11-08 15:06:29 -05:00
Tim Baek 3621e2d56c Merge pull request #19034 from Kylapaallikko/dev
i18n: Update fi-FI translation
2025-11-08 15:06:17 -05:00
google-labs-jules[bot] dec59e87a3 refactor: Remove unused litellm endpoint and associated frontend code
Removes the unused `/litellm/config` endpoint, the corresponding `downloadLiteLLMConfig` frontend API function, and the unused import from the `Database.svelte` component. This code was identified as dead code as it was not being used in the UI.
2025-11-08 15:05:29 +00:00
_00_ bebba7424e Upd: i18n_ es-ES Spanish Translation v0.6.36
### UPD Spanish Translation v0.6.35

Added new strings
2025-11-08 13:52:29 +01:00
Kylapaallikko 2e5668e25c Update fi-FI translation.json
Improved and added missing translations.
2025-11-08 13:44:52 +02:00
Adam SkalickyandTim Baek e72a7e4eca perf Optimize Socket Emits Using User Rooms (#18996)
* This PR optimizes socket delta event broadcasting by leveraging rooms. Instead of iterating through a user's sessions and emitting events individually, this change sends a single event to a user-specific room. This approach is more efficient, reducing overhead and improving performance, particularly for users with multiple concurrent sessions.

In testing this dramatically reduces emits and server load.

* Update main.py

Added userroom join

---------

Co-authored-by: Tim Baek <tim@openwebui.com>
2025-11-07 21:11:58 -05:00
Tim Baek c260efa2f4 Merge pull request #19002 from aleixdorca/dev
i18n: Update Catalan Translation File
2025-11-07 16:26:08 -05:00
Tim Baek 7afa83e880 Merge pull request #19012 from joaoback/patch-14
Update translation.json (pt-BR)
2025-11-07 16:25:04 -05:00
Tim Baek 8114ddc5f9 Merge pull request #19004 from ShirasawaSama/i18n/improve-chinese-translation
i18n: improve Chinese translation
2025-11-07 16:21:54 -05:00
krishna-medapati 684324ae9e fix: Handle AttributeError in hybrid search with reranking (#17046)
- Split attribute existence checks from document content checks
- Added hasattr() check for metadatas attribute
- Prevents AttributeError when collection_result is missing attributes
- Maintains all original validation logic

Fixes #17046
2025-11-07 23:31:11 +05:30
joaoback 3d43797361 Update translation.json (pt-BR)
New translations have been made of the new items that were added in the latest version.
2025-11-07 08:27:37 -03:00
Shirasawa ce1079d358 feat: Allow configuration of not process large single-line data 2025-11-07 07:00:06 +00:00
Shirasawa 89c0e150c8 feat: handle large stream chunks responses 2025-11-07 07:00:06 +00:00
Shirasawa 1097838b35 i18n: improve Chinese translation 2025-11-07 06:56:26 +00:00
Aleix Dorca 9a8646157e Update catalan translation.json 2025-11-07 06:44:28 +01:00
Aleix Dorca f052b2801a Update catalan translation.json 2025-11-07 06:40:37 +01:00
Tim Baek e0d5de1697 Merge pull request #18978 from open-webui/dev
0.6.36
2025-11-06 16:45:23 -05:00
Timothy Jaeryang Baek 49d57ae82b chore: format 2025-11-06 16:44:33 -05:00
Tim Baek 918e720f97 Merge pull request #18990 from EventHorizon-AI/fix/shortcuts-i18n
fix: Shortcuts Modal i18n
2025-11-06 16:42:46 -05:00
Tim Baek 261a55b275 Merge pull request #18989 from Classic298/patch-2
chore: CHANGELOG 0.6.36
2025-11-06 16:42:28 -05:00
Timothy Jaeryang Baek 0d0a37c884 chore: format 2025-11-06 16:39:07 -05:00
Classic298 a32a3dfee4 chore: CHANGELOG 0.6.36 2025-11-06 22:38:29 +01:00
Timothy Jaeryang Baek 9b3ecb703a chore: bump 2025-11-06 16:37:41 -05:00
EntropyYue e239e17050 fix: Shortcuts Modal i18n 2025-11-07 05:37:30 +08:00
Timothy Jaeryang Baek c2c02846a8 fix: tool calling 2025-11-06 16:35:19 -05:00
Tim Baek 4754108253 Merge pull request #18987 from rndmcnlly/feat/oauth-groups-separator-v2
feat: add OAUTH_GROUPS_SEPARATOR for configurable group parsing
2025-11-06 16:34:27 -05:00
Adam M. Smith 96b98cd13c feat: add OAUTH_GROUPS_SEPARATOR for configurable group parsing 2025-11-06 21:01:51 +00:00
Timothy Jaeryang BaekandGero Doll 639d26252e fix: Socket.IO CORS warning
Co-Authored-By: Gero Doll <6284675+limbicnation@users.noreply.github.com>
2025-11-06 15:21:41 -05:00
Timothy Jaeryang Baek 7faf19dad9 refac 2025-11-06 15:21:06 -05:00
Timothy Jaeryang Baek c38f878e1e fix: firecrawl import 2025-11-06 15:19:08 -05:00
Timothy Jaeryang Baek 67c4ea1e57 fix: image edit workflow editor 2025-11-06 14:17:54 -05:00
Tim Baek e85c7f7931 Merge pull request #18402 from open-webui/dev
0.6.35
2025-11-06 13:40:46 -05:00
Timothy Jaeryang Baek d5fe0f6067 refac 2025-11-06 13:31:55 -05:00
Timothy Jaeryang Baek 9c0bd0c0ed refac 2025-11-06 13:24:34 -05:00
Timothy Jaeryang Baek 651f855289 refac 2025-11-06 13:20:03 -05:00
Timothy Jaeryang Baek dc3d704800 refac: shortcuts 2025-11-06 13:08:48 -05:00
Davixk 8da4e5bb19 fix(chats): fix chat search crash (#18576)
* fix(chats): handle null bytes in PostgreSQL search

Removes null bytes from message content before performing
case-insensitive search in PostgreSQL, preventing conversion
errors and ensuring reliable query results.

* fix(chats): prevent null byte errors in PostgreSQL queries

Ensures chat content and titles containing null bytes are excluded from PostgreSQL text queries to avoid conversion errors.

Improves reliability of search and filtering by handling problematic characters in JSON fields.
2025-11-06 12:41:21 -05:00
Timothy Jaeryang Baek cabbdd719b refac 2025-11-06 12:34:30 -05:00
Timothy Jaeryang Baek 4e18c8a689 refac 2025-11-06 11:41:20 -05:00
Timothy Jaeryang Baek 224e4c3a61 chore: format 2025-11-06 03:51:26 -05:00
Classic298 d3f40c5a56 chore: Update CHANGELOG for version 0.6.35 (#18481)
* chore: Update CHANGELOG for version 0.6.35

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG with recent feature additions

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md
2025-11-06 03:49:36 -05:00
Timothy Jaeryang Baek 7f4b45e7e8 revert/fix: edit valves modal 2025-11-06 03:48:53 -05:00
Timothy Jaeryang Baek 63e8ab7a05 feat: comfyui image edit support 2025-11-06 03:43:59 -05:00
Tim Baek 74db2b9f36 Merge pull request #18956 from Classic298/patch-2
chore: SECURITY: improved clarity and fixed layout issues
2025-11-06 03:42:56 -05:00
Classic298 e4e2f8352c Revise SECURITY.md for improved clarity
Updated security reporting guidelines for clarity and structure.
2025-11-06 08:43:56 +01:00
Tim Baek 5f3f5170b7 Merge pull request #18215 from avatsaev/fix-auto-show-artifacts
fix: auto show artifacts when opening a conversation
2025-11-06 01:49:57 -05:00
Tim Baek aed2d4a8ee Merge pull request #18955 from ShirasawaSama/i18n/improve-chinese-translation
i18n: improve Chinese translation
2025-11-06 01:48:49 -05:00
Timothy Jaeryang Baek 1cc3493dc8 enh/refac: read aloud audio queue 2025-11-06 01:48:10 -05:00
Shirasawa 3b944072e3 i18n: improve Chinese translation 2025-11-06 06:42:37 +00:00
Timothy Jaeryang BaekandClassic298 e2b9942648 feat: Optionally forward user headers to external document loader
Co-Authored-By: Classic298 <27028174+Classic298@users.noreply.github.com>
2025-11-06 00:05:46 -05:00
Timothy Jaeryang BaekandJacob Leksan f5c7152a6b enh/fix: update extra params for native function calling
Co-Authored-By: Jacob Leksan <63938553+jmleksan@users.noreply.github.com>
2025-11-05 23:46:30 -05:00
Tim Baek 1aa285edb9 Merge pull request #18911 from silentoplayz/fix-chat-preview-bug
fix: correct chat preview loading in chats search modal
2025-11-05 23:33:26 -05:00
Tim Baek 4daf81fba2 Merge pull request #18841 from acomarcho/fix/duplicate-prompt-suggestions-freezes-webpage
fix: duplicate prompt suggestions freezes webpage
2025-11-05 23:31:27 -05:00
Timothy Jaeryang Baek bdd198e946 a11y: message role 2025-11-05 23:30:23 -05:00
Timothy Jaeryang Baek 415b93c7c3 enh: configurable mistral ocr base url 2025-11-05 23:25:51 -05:00
Timothy Jaeryang BaekandMarchotridyo 00520a9602 fix: message input dictate
Co-Authored-By: Marchotridyo <29671825+acomarcho@users.noreply.github.com>
2025-11-05 23:06:00 -05:00
Timothy Jaeryang Baek 6c583ef9d3 refac/fix: mineru params 2025-11-05 14:43:25 -05:00
Timothy Jaeryang Baek cdf90222c7 refac 2025-11-05 03:59:09 -05:00
Timothy Jaeryang Baek 3dc20a25b1 refac: styling 2025-11-05 03:44:35 -05:00
Timothy Jaeryang Baek 5c1ba23026 refac 2025-11-05 03:42:34 -05:00
Timothy Jaeryang Baek 72f8539fd2 feat: image edit support 2025-11-05 03:31:37 -05:00
Timothy Jaeryang Baek 8d34fcb586 enh: gemini flash image generation support 2025-11-05 01:59:16 -05:00
Timothy Jaeryang Baek 72900cd686 refac: images 2025-11-05 00:54:25 -05:00
Timothy Jaeryang Baek 314cac0113 refac: dedup tags 2025-11-04 23:44:33 -05:00
Tim Baek cde4b93fa6 Merge pull request #18934 from mgl/feat/voxtral-support
feat: add voxtral support
2025-11-04 18:28:45 -05:00
Timothy Jaeryang Baek 89e59d0103 revert 2025-11-04 18:14:09 -05:00
Timothy Jaeryang Baek 2a98ba0ff8 refac/fix 2025-11-04 18:12:00 -05:00
Timothy Jaeryang Baek 75efa4f931 refac 2025-11-04 17:58:51 -05:00
mglo 3561c7eedd feat: add voxtral support 2025-11-04 20:57:36 +01:00
Timothy Jaeryang Baek a4fd26b478 enh/fix: google pse referer header 2025-11-04 13:50:07 -05:00
Timothy Jaeryang Baek 03f207b803 enh: display mcp connection error 2025-11-04 13:48:23 -05:00
Timothy Jaeryang Baek 414ab51d5d refac: user menu alignment 2025-11-04 13:45:04 -05:00
Timothy Jaeryang Baek 8e5690aab4 refac: images 2025-11-04 13:30:59 -05:00
Tim Baek 939521b75d Merge pull request #18912 from silentoplayz/fix-functions-page-delete
fix: update Functions page after deleting a function
2025-11-04 13:30:23 -05:00
Timothy Jaeryang Baek a8fe979cf8 refac 2025-11-04 13:20:13 -05:00
Timothy Jaeryang Baek bafeb76c41 refac/fix: trusted env for proxy 2025-11-04 12:21:18 -05:00
Timothy Jaeryang Baek ec21577f1a Merge branch 'dev' of https://github.com/open-webui/open-webui into dev 2025-11-03 23:47:45 -05:00
silentoplayz 244809bab7 fix: update Functions page after deleting a function
The Functions page was not updating after a function was deleted. This was because the local `functions` variable was not being updated after a successful deletion.

This commit fixes the bug by filtering the deleted function from the local `functions` array, which triggers a UI refresh.
2025-11-03 21:03:27 -05:00
silentoplayz 71b86c08ee fix: correct chat preview loading in search modal
This commit fixes a bug in the search modal where the chat preview would fail to load for the bottom search results, especially when using tags to filter.

The issue was caused by an incorrect index calculation in the `loadChatPreview` function, which resulted in an out-of-bounds error when accessing the `chatList` array.

This commit resolves the issue by adding a guard clause to the `loadChatPreview` function to ensure that the `selectedChatIdx` is always a valid index. This prevents the out-of-bounds error and ensures that the chat preview is always displayed for the selected chat.
2025-11-03 19:58:26 -05:00
Timothy Jaeryang Baek 989f192c92 enh: force refresh page on update 2025-11-03 13:43:07 -05:00
Tim Baek 08bc00ea77 Merge pull request #18879 from ShirasawaSama/i18n/improve-chinese-translation
i18n: improve Chinese translation
2025-11-03 09:47:08 -05:00
Shirasawa 1447102331 i18n: improve Chinese translation 2025-11-03 02:59:13 +00:00
Timothy Jaeryang Baek 67aa1b028d refac/fix: note export 2025-11-02 19:40:44 -05:00
Timothy Jaeryang Baek 024f78d3e0 refac/fix: reasoning content tag parsing 2025-11-02 18:52:53 -05:00
Timothy Jaeryang Baek d14329b285 refac 2025-11-02 18:47:41 -05:00
Timothy Jaeryang Baek 6681ff5cbd refac/fix: endpoint conflict 2025-11-02 18:41:57 -05:00
Tim Baek 478163eb3b Merge pull request #18867 from andrewbbaek/dev
chore: Add id attributes for better element identification
2025-11-02 11:51:35 -05:00
Andrew Baek 9e8e004929 Add id attributes for better element identification
Updated ResponseMessage.svelte

Update Sidebar.svelte
2025-11-03 01:46:50 +09:00
Tim Baek a9b4774bde Merge pull request #18847 from IllimarR/main
i18n: update Estonian translation file
2025-11-01 18:49:11 -04:00
Timothy Jaeryang Baek d98c539d89 refac 2025-11-01 18:48:11 -04:00
Timothy Jaeryang Baek 20af60be42 refac 2025-11-01 16:00:11 -04:00
IllimarR 00eacfcacc Merge branch 'dev' into main 2025-11-01 16:05:35 +02:00
IllimarR cf6a476998 Update translation.json (Estonian translations) 2025-11-01 16:00:35 +02:00
Timothy Jaeryang Baek fdf7ca15ea refac 2025-11-01 06:07:00 -04:00
Timothy Jaeryang Baek ee61970fb0 refac 2025-11-01 05:58:54 -04:00
Tim Baek 43f817a75e Merge pull request #18751 from ShirasawaSama/i18n/improve-chinese-translation
i18n: improve Chinese translation
2025-11-01 02:57:11 -07:00
Timothy Jaeryang Baek fdc0f04a36 refac 2025-11-01 05:56:44 -04:00
Tim Baek 7fc4c56ea3 Merge pull request #18761 from acomarcho/fix/chat-title-not-generated-if-title-auto-generation-is-off
fix: chat title not generated if title auto generation is off in interface settings
2025-11-01 02:46:14 -07:00
acomarcho da282ce5c7 fix: duplicate prompt suggestions freezes webpage 2025-11-01 11:25:20 +07:00
Tim Baek c787070dc9 Merge pull request #18765 from mkhludnev/patch-2
fix: Don't missguide Tika with mime-type
2025-10-31 14:18:43 -07:00
Mikhail Khludnev 24aeec9120 Don't missguide Tika with mime-type
Fix #18683 
* Tika is smart enough to detect content type. 
* Windows browsers just misguides Tika providing `application/ms-word` for .rtf files.
2025-10-31 23:12:23 +03:00
acomarcho 15cc9b6cee fix: titles are not generated if title auto-generation is set to false in interface settings 2025-11-01 00:07:59 +07:00
Shirasawa 9f837267b6 i18n: improve Chinese translation 2025-10-31 12:03:20 +00:00
Tim Baek f18f1db704 Merge pull request #18707 from silentoplayz/fix-tool-tooltip-description
fix: add tooltips to tools on model edit page
2025-10-30 16:23:36 -07:00
Timothy Jaeryang Baek 1d98a45b33 refac 2025-10-30 15:57:50 -07:00
Tim Baek 16af088f4e Merge pull request #18714 from OAburub/patch
fix: make SSL Verificiation async
2025-10-30 10:47:21 -07:00
G30 8feed02d40 fix: de-duplicate model tags case-insensitively (#18716)
* fix: de-duplicate model tags case-insensitively

This change updates the `setTags` function in the `Models.svelte` component and the `onMount` function in the `Selector.svelte` component to convert all model tags to lowercase before removing duplicates. This ensures that tags with different capitalization (e.g., "Best" and "best") are treated as a single tag, preventing duplicate entries in the tag filter dropdown on both the workspace models page and the new chat page.

* Update Selector.svelte

* refac
2025-10-30 10:44:05 -07:00
Omar Aburub 3bcf9a442a fix: make SSL verification async 2025-10-29 16:14:53 +03:00
silentoplayz 4a0359789f fix: add tooltips to tools on model edit page
This change adds tooltips to the tools on the model edit page, displaying the tool's description on hover.
2025-10-28 21:54:29 -04:00
Timothy Jaeryang Baek 292be82754 refac/fix: sidebar open status 2025-10-28 17:31:38 -07:00
Timothy Jaeryang Baek a0068c4a17 refac 2025-10-28 14:05:49 -07:00
Timothy Jaeryang Baek 61a2909a88 refac 2025-10-28 14:03:05 -07:00
Tim Baek 48b538f312 Merge pull request #18432 from silentoplayz/fix-clipboard-image-paste
fix: correctly handle clipboard images with {{CLIPBOARD}} in prompts
2025-10-28 12:42:01 -07:00
Timothy Jaeryang Baek d1c9555a0b refac 2025-10-28 01:46:36 -07:00
Timothy Jaeryang Baek f524a6a8e7 refac/fix: kb image upload handling 2025-10-28 00:34:53 -07:00
Timothy Jaeryang Baek e986488ab5 enh: ELEVENLABS_API_BASE_URL env var 2025-10-28 00:06:52 -07:00
Tim Baek 81c530a9c4 Merge pull request #18667 from wangweixuan/fix-7008
fix: use trusted env proxy setting in web search loader
2025-10-27 23:52:20 -07:00
Timothy Jaeryang Baek 76bde402fe refac 2025-10-27 23:49:26 -07:00
Timothy Jaeryang Baek a776dbd01d refac 2025-10-27 23:42:00 -07:00
Timothy Jaeryang Baek b9bbf22581 refac 2025-10-27 16:46:49 -07:00
Timothy Jaeryang Baek cbcab062eb refac 2025-10-27 16:46:04 -07:00
Timothy Jaeryang Baek c8b2313362 refac 2025-10-27 15:38:59 -07:00
Timothy Jaeryang Baek 92aafd6c06 refac 2025-10-27 15:31:25 -07:00
Tim Baek bfadbc9934 Merge pull request #18415 from taylorwilsdon/oauth_error_handling_enh
enh: More detailed OAuth2.1 tool callback error handling + fix for editing existing tools
2025-10-27 15:13:33 -07:00
Tim Baek f0834e397c Merge pull request #18648 from itk-dev/feature/danish-translations-added
i18n danish translation added
2025-10-27 15:10:00 -07:00
Timothy Jaeryang Baek 182e4138bf refac: styling 2025-10-27 15:07:00 -07:00
Timothy Jaeryang Baek b72443004d refac/chore: svelte 5 migration 2025-10-27 15:06:53 -07:00
Wang Weixuan 5e17882488 fix: use trusted env in web search loader
Signed-off-by: Wang Weixuan <wangweixvan@gmail.com>
2025-10-28 04:58:00 +08:00
sinejespersen b2ed5be457 add danish translation 2025-10-27 09:11:31 +01:00
Timothy Jaeryang Baek f47214314b refac 2025-10-26 22:20:57 -07:00
Timothy Jaeryang Baek 46a3f7de5e refac: rm rich text highlight 2025-10-26 22:12:34 -07:00
Timothy Jaeryang Baek a996497bf1 refac/fix: marker api key validation 2025-10-26 22:11:47 -07:00
Timothy Jaeryang Baek 5338edd644 refac 2025-10-26 21:35:16 -07:00
Timothy Jaeryang Baek c573321305 refac 2025-10-26 21:11:05 -07:00
Timothy Jaeryang Baek 20cd9e9461 chore: svelte5 bump 2025-10-26 21:06:32 -07:00
Tim Baek 69e1ba8234 Merge pull request #18533 from silentoplayz/fix-keyboard-shortcuts-display
fix: display correct keys for international keyboards
2025-10-26 19:34:13 -07:00
Timothy Jaeryang Baek a70bc52c34 chore: format 2025-10-26 19:33:39 -07:00
Timothy Jaeryang Baek 54c05ac6e0 refac: swtich 2025-10-26 19:25:38 -07:00
Timothy Jaeryang Baek ed6449d35f refac: chat navbar menu 2025-10-26 19:23:55 -07:00
Timothy Jaeryang Baek 82c08a3b5d enh: sidebar models collapsible 2025-10-26 19:00:27 -07:00
Timothy Jaeryang Baek 8197844ff7 refac 2025-10-26 17:22:23 -07:00
Tim Baek 0c4a1ac54d Merge pull request #18635 from wei840222/dev
refactor: replace requests with Firecrawl SDK in search and requests Firecrawl SDK in scrape rather than langchain_community FireCrawlLoader
2025-10-26 14:57:10 -07:00
Tim Baek c9465da8f2 Merge pull request #18636 from rgaricano/dev-FIXSTYLES_dark_mode_select_boxes
UPD: Refactor dark select styles using Tailwind CSS classes
2025-10-26 14:56:05 -07:00
_00_ 2f6a050325 Refactor dark mode select styles-more specific
Refactor dark mode select styles to be more specific and avoid interference with already classed select elements.
2025-10-26 15:05:42 +01:00
_00_ 235ed8956c UPD: Refactor dark select styles using Tailwind CSS classes
### UPD_Styles: Add dark mode styles for select elements and options.

Actually some select "boxes" have css dark theme support, but other not.
This PR add CSS for dark theme selects.
2025-10-26 09:40:05 +01:00
wei840222 7a3f4d85f6 refactor: replace requests with Firecrawl SDK in search and requests Firecrawl SDK in scrape rather than langchain_community FireCrawlLoader 2025-10-26 15:05:35 +08:00
Timothy Jaeryang Baek d11d49a08a refac 2025-10-25 23:01:13 -07:00
Tim Baek a4d0bd1073 Merge pull request #18537 from OAburub/patch
fix: prevent cancellation scope corruption by exitting in LIFO and ha…
2025-10-25 22:47:40 -07:00
Tim Baek f83dc60666 Merge pull request #18572 from attilaolah/issue-18542
feat: OAUTH_ROLES_SEPARATOR env var
2025-10-25 22:47:07 -07:00
Tim Baek 82c45b721e Merge pull request #18564 from iPagar/empty-names
fix: validate folder and channel names before creation
2025-10-25 22:30:53 -07:00
_00_ f06e2c1a4a FIX:style_dark_mode_select_boxes
### UPD_Styles: Add dark mode styles for select elements and options.

Actually some select "boxes" have css dark theme support, but other not.
This PR add CSS for dark theme selects.
2025-10-25 13:35:58 +02:00
Tim Baek 46a8e4acad Merge pull request #18594 from iPagar/chat-system-trim
fix: prevent icon layout shift near system instructions in Chat component
2025-10-24 17:50:29 -07:00
Tim Baek 06e280d831 Merge pull request #18577 from Classic298/fix-global-activated-actions
fix: global activated actions
2025-10-24 17:49:25 -07:00
Tim Baek 1f287be27f Merge pull request #18585 from silentoplayz/fix-ui-freeze-on-mixed-sources
fix: prevent UI freeze by initializing distances array
2025-10-24 17:47:34 -07:00
Tim Baek 5444210163 Merge pull request #18591 from iPagar/modal
fix: add stable scrollbar gutter to Modal component
2025-10-24 17:47:08 -07:00
Tim Baek d28559c49c Merge pull request #18595 from iPagar/menu-gaps-patch
fix: update spacing in UserMenu dropdown items
2025-10-24 17:45:31 -07:00
Tim Baek c7df4c5082 Merge pull request #18602 from htulipe/patch-1
i18n: fix french translation key
2025-10-24 17:44:17 -07:00
htulipe c0fc37d112 Update translation.json 2025-10-24 19:28:21 +02:00
Pavel Garaev 79b0c3af47 refac: update spacing in UserMenu dropdown items 2025-10-24 20:27:34 +04:00
Pavel Garaev 6471945076 fix: conditionally render system instructions in Chat component 2025-10-24 19:56:01 +04:00
Pavel Garaev 17e80ecd81 fix: add stable scrollbar gutter to Modal component 2025-10-24 19:26:31 +04:00
silentoplayz 7222a9aef6 fix: prevent UI freeze by initializing distances array
Fixes a bug where the UI would freeze when processing citation sources with mixed distance metrics.

The `Citations.svelte` component was attempting to call `.push()` on an `undefined` `distances` array. This happened when the first document for a source had no distance value, but a subsequent document for the same source did.

This patch ensures the `distances` array is always initialized as an empty array `[]` instead of `undefined`, preventing the `TypeError` and resolving the UI freeze.
2025-10-24 08:53:51 -04:00
Classic298 006a2d6bb3 fix: Modify ActionsSelector to handle global action states
Updated checkbox behavior to account for global actions.
2025-10-24 11:46:15 +02:00
Attila Oláh c165a6b6c2 fix: exclude empty roles
This is a minor tweak that allows using whitespace as a separator,
without it having to be exactly one space. Convenient for using YAML
text fold syntax in Helm charts when providing long lists of roles.
2025-10-24 08:48:57 +02:00
Attila Oláh 35504e8486 feat: add OAUTH_ROLES_SEPARATOR env var
This allows changing the separator for the `OAUTH_ALLOWED_ROLES` and
`OAUTH_ADMIN_ROLES` env vars, from the default comma (,) to something
that is not present in the role name. The intended audience is folks
with LDAP-syntax groups/roles, e.g.
`cn=webui_admin,ou=it_department,o=my_org` instead of just
`webui_admin`.
2025-10-24 08:42:28 +02:00
Timothy Jaeryang Baek 267794638c doc: readme 2025-10-23 18:05:22 -07:00
Pavel Garaev e0e50f7380 fix: validate folder and channel names before creation 2025-10-24 02:18:26 +04:00
Timothy Jaeryang Baek e171c7915a refac: styling 2025-10-23 12:48:48 -04:00
Omar Aburub 8f060ee2fa fix: prevent cancellation scope corruption by exitting in LIFO and handling exceptions 2025-10-23 15:34:47 +03:00
silentoplayz 02a2683eb0 fix: display correct keys for international keyboards
Updates the ShortcutsModal to dynamically display the correct physical keys for users with non-US keyboard layouts.

The `ShortcutItem` component now uses `navigator.keyboard.getLayoutMap()` to resolve `KeyboardEvent.code` values (e.g., "Slash") to the character they produce on the user's active keyboard layout (e.g., "-").

This ensures the displayed shortcuts match the keys the user needs to press. A fallback is included for older browsers that do not support this API.
2025-10-23 05:40:31 -04:00
Timothy Jaeryang Baek 6eecade06e refac 2025-10-22 22:35:39 -04:00
Timothy Jaeryang Baek 546a334328 refac 2025-10-22 17:12:05 -04:00
Timothy Jaeryang Baek 8f5eb03a40 refac: styling 2025-10-22 17:12:00 -04:00
Tim Baek 710f6eec12 Merge pull request #18473 from silentoplayz/hotkey-hints-sidebar
feat: add toggleable hotkey hints to sidebar buttons and refac ShortcutsModal
2025-10-22 16:50:24 -04:00
Tim Baek 92f359fb9b Merge pull request #18506 from duncansmart/patch-1
perf: compressImage - preserve image type
2025-10-22 16:46:15 -04:00
Duncan Smart fe192eb738 compressImage: preserve image type
Previously always exported as PNG, causing JPEG images to balloon 3-10x in size with considerable implications for front-end performance (UI gets sluggish e.g. https://github.com/open-webui/open-webui/discussions/11941)
2025-10-22 01:16:26 +01:00
Timothy Jaeryang Baek 23ea754061 fix: create new note 2025-10-21 18:21:00 -04:00
silentoplayz 3c7e739b3c refac 2025-10-21 18:11:56 -04:00
Timothy Jaeryang Baek 6593b7ccc8 refac 2025-10-21 18:11:30 -04:00
Timothy Jaeryang Baek eb9c4c0e35 refac/fix: insert prompt as rich text 2025-10-21 17:59:52 -04:00
Timothy Jaeryang Baek 9942de8011 refac/fix: mermaid 2025-10-21 17:03:04 -04:00
Timothy Jaeryang Baek 0a78ceef6d fix: dictate autosend 2025-10-21 16:40:22 -04:00
Tim Baek b68a5f330d Merge pull request #18496 from ShirasawaSama/patch-44
fix: Clear file list when selected models do not support file uploads
2025-10-21 16:36:05 -04:00
Tim Baek b9f0d239b0 Merge pull request #18484 from ShirasawaSama/patch-41
fix: do not display the move menu when folders are empty
2025-10-21 16:26:43 -04:00
Tim Baek a708cdf55e Merge pull request #18483 from ShirasawaSama/patch-40
fix: fix color of Attach Webpage button when model not support file uploads
2025-10-21 16:26:27 -04:00
Tim Baek bc9067d5aa Merge pull request #18489 from rgaricano/dev-es_ES
UPD_i18n:  es-ES Translation v0.6.35
2025-10-21 16:24:57 -04:00
Tim Baek 9c9085adfa Merge pull request #18487 from ShirasawaSama/patch-42
fix: Do not display divider when no integrations enabled
2025-10-21 16:24:52 -04:00
Tim Baek 8cbc472f7f Merge pull request #18491 from ShirasawaSama/i18n/improve-chinese-translation
i18n: improve Chinese translation
2025-10-21 16:24:26 -04:00
Shirasawa 1be9187236 fix: Clear file list when selected models do not support file uploads 2025-10-21 20:47:27 +08:00
Shirasawa 098e64b35d i18n: improve Chinese translation 2025-10-21 10:16:47 +00:00
_00_ c469369c35 UPD_i18n: es-ES Translation v0.6.35
### UPDATE of es-ES Translation
v0.6.35

Added new strings
2025-10-21 11:43:12 +02:00
Shirasawa da89e36abe fix: Do not display divider when no integrations enabled 2025-10-21 16:46:18 +08:00
Shirasawa b754aad987 fix: do not display the move menu when folders are empty 2025-10-21 16:14:38 +08:00
Shirasawa e225435c8e fix: fix color of Attach Webpage button when model not support file uploads 2025-10-21 16:05:56 +08:00
silentoplayz e361606c61 feat: add toggleable hotkey hints to sidebar buttons and refac ShortcutsModal
- Add src/lib/shortcuts.ts as the single source of truth for every shortcut
- Create HotkeyHint.svelte to show OS-aware key combos (⌘ on Mac, Ctrl elsewhere)
- Make sidebar “New Chat” and “Search” buttons display their shortcuts on hover
- Add user setting to toggle these sidebar hints
- Refactor ShortcutsModal into categorized (Global, Chat, Message, Input), data-driven sections
- Introduce ShortcutItem.svelte to render each row, dividers, and multi-line text
- Fix “Focus text area” action and include “Close modal” shortcut
- Wire everything through +layout.svelte and the shortcuts registry
2025-10-20 16:21:59 -04:00
Tim Baek e24fec0de4 Merge pull request #18452 from kaiwinut/fix/chat-getattr-error
fix: Handle invalid order_by attribute in Chat
2025-10-20 14:00:21 -04:00
Tim Baek 1f89eacb69 Merge pull request #18455 from Classic298/patch-1
chore: Revise feature request template for clarity
2025-10-20 11:24:07 -05:00
Classic298 892ddf9eac Revise feature request template for clarity
Updated the feature request template to clarify scope and submission guidelines.
2025-10-20 08:38:43 +02:00
kaiwdev 2b0b87c0f9 fix: handle invalid order_by attribute in Chat 2025-10-20 14:19:02 +09:00
Timothy Jaeryang Baek f5899e875c refac/fix: 72 bytes+ password 2025-10-20 01:18:03 -04:00
Timothy Jaeryang Baek 43eac35b5b refac: code block stdout/err styling 2025-10-20 01:11:19 -04:00
Tim Baek 9aea08ccb2 Merge pull request #18389 from Classic298/docs-chore
chore: Updated various templates to fix typos and improve them
2025-10-19 23:50:34 -05:00
Timothy Jaeryang Baek 3e003a5f17 refac 2025-10-20 00:48:01 -04:00
Timothy Jaeryang Baek acac6d5973 fix/refac: docling 2025-10-20 00:27:50 -04:00
Tim Baek 2a8b0b2581 Merge pull request #18388 from Ithanil/de_i18n
i18n: German translation of new strings
2025-10-19 23:22:55 -05:00
Tim Baek a2b3abfa14 Merge pull request #18385 from ShirasawaSama/patch-39
i18n: improve zh-TW translation
2025-10-19 23:22:45 -05:00
Tim Baek 32996a16cc Merge pull request #18449 from open-webui/main
dev
2025-10-19 23:18:24 -05:00
Tim Baek 3af6192495 Merge pull request #18384 from ShirasawaSama/patch-38
i18n: improve zh-CN translation
2025-10-19 22:45:34 -05:00
Tim Baek 7a83e7dfa3 Merge pull request #18395 from EkaterinePapava/main
Update Georgian translation
2025-10-19 22:45:09 -05:00
Tim Baek 3984184a82 Merge pull request #18411 from ricdikulous/feat/websocket-cors-security
feat: add CORS validation to WebSocket connections for defense-in-depth
2025-10-19 22:44:55 -05:00
Timothy Jaeryang Baek ca5bafcd2f refac 2025-10-19 23:41:25 -04:00
Timothy Jaeryang Baek ca332db7eb refac 2025-10-19 23:35:59 -04:00
Tim Baek 680361a88c Merge pull request #18419 from ivanostanin/fix-youtube-proxy-passing
fix: pass youtube_proxy as a `GenericProxyConfig` type object
2025-10-19 22:32:37 -05:00
Tim Baek 375d29bab4 Merge pull request #18438 from acwoo97/fix/load-defaults
fix: load default settings when sessionStorage is empty
2025-10-19 22:26:21 -05:00
Chanwoo An 9784eb4c0b fix: load default settings when sessionStorage is empty 2025-10-20 11:52:29 +09:00
silentoplayz 0031fb8274 fix: correctly handle clipboard images in prompts
The textVariableHandler was using URL.createObjectURL() for clipboard images, which created a blob URL instead of the required base64-encoded data URL. This caused an "illegal base64 data" error when sending messages with images pasted via a {{CLIPBOARD}} prompt.

This commit updates the handler to use FileReader.readAsDataURL() to properly encode the image, aligning it with the existing on:paste logic. Additionally, it adds error handling for navigator.clipboard.read() to address potential permission issues in Firefox.
2025-10-19 20:15:39 -04:00
Timothy Jaeryang Baek 8af6a4cf21 refac: direct connections 2025-10-19 18:30:36 -04:00
Taylor Wilsdon 4b74034967 black fmt 2025-10-19 16:58:09 -04:00
Timothy Jaeryang Baek 9762ef3ef6 refac 2025-10-19 12:53:34 -04:00
Tim Baek 691012782a Merge pull request #18413 from zhsh-12/fix-manifest-duplicate-crossorigin
Fix manifest duplicate crossorigin
2025-10-19 11:42:49 -05:00
Tim Baek f503ba499c Merge pull request #18424 from athoik/bump_python_jose
build(deps): bump python-jose from 1.4.0 to 1.5.0
2025-10-19 11:42:23 -05:00
Tim Baek d6915c0f40 Merge pull request #18430 from athoik/bump_unstructured
build(deps): bump unstructured from 0.16.17 to 0.18.15
2025-10-19 11:42:10 -05:00
Athanasios Oikonomou f39f29c38f build(deps): bump unstructured from 0.16.17 to 0.18.15
This PR updates the unstructured package from version 0.16.17 to 0.18.15 to enable installation and compatibility with Python 3.13.

More info #18349
2025-10-19 17:35:12 +03:00
Athanasios Oikonomou 6a0300fdb7 build(deps): bump python-jose from 1.4.0 to 1.5.0
Upgraded `python-jose` from **1.4.0** to **1.5.0** to ensure compatibility with newer Python versions and apply security improvements.
- Adds explicit support for Python 3.12 and 3.13.
- Improves cryptographic backend handling.
- Includes bug fixes and security enhancements (e.g., removes sensitive data from exceptions).

- [python-jose 1.5.0 Release Notes](https://github.com/mpdavis/python-jose/releases)
2025-10-19 12:45:12 +03:00
Ivan Ostanin 7d29991fa5 fix: pass youtube_proxy as a GenericProxyConfig type object 2025-10-19 02:01:27 +02:00
Taylor Wilsdon ecbf74dbea Added a preflight authorize check that automatically re-registers MCP OAuth clients when the stored client ID no longer exists on the server, so the browser flow never hits the stale-ID failure 2025-10-18 16:53:44 -04:00
Taylor Wilsdon d49fb9c010 complete cleanup of oauth clients 2025-10-18 14:16:10 -04:00
Taylor Wilsdon c107a3799f Added a targeted utility to wipe all OAuth sessions for a provider so the cleanup can remove stale access tokens across every user when a connection is updated 2025-10-18 14:00:46 -04:00
Taylor Wilsdon 40c450e6e5 Add more granular information to oauth failure messages 2025-10-18 13:43:51 -04:00
zhsh-12 6cb58af3db feat: add new feature or fix bug 2025-10-18 22:06:10 +08:00
Richard Watts-Seale 25087e09e6 feat: Add CORS validation to WebSocket connections. #18410 2025-10-18 20:25:45 +11:00
Classic298 d347497609 Merge branch 'dev' into docs-chore 2025-10-18 11:12:36 +02:00
Classic298 ab07bab140 Clarify PR guidelines for translation contributions
Emphasize the importance of standalone PRs for translation updates.
2025-10-18 11:11:51 +02:00
Tim Baek f97e73962a Merge pull request #18394 from joaoback/patch-13
Update translation.json (pt-BR)
2025-10-17 12:42:17 -05:00
joaoback c0a91b566b Update translation.json (pt-BR)
translations of the new items added in the last version have been carried out.
2025-10-17 08:29:01 -03:00
Ekaterine Papava 850ca01ca2 Update Georgian translation 2025-10-17 12:56:31 +02:00
Classic298 a483d41de2 Patch 1 (#22) 2025-10-17 11:32:17 +02:00
Jan Kessler 185e01eecc German translation of new strings in i18n 2025-10-17 11:14:55 +02:00
Shirasawa 7c393bc166 i18n: improve zh-TW translation 2025-10-17 08:42:17 +00:00
Shirasawa f170f37ba4 i18n: improve zh-CN translation 2025-10-17 16:29:53 +08:00
Tim Jaeryang Baek 27e85e448b Merge pull request #18372 from Tsafaras/i18n/more-greek-translations
i18n: Add more Greek translations
2025-10-16 16:46:39 -05:00
Konstantinos eff40229fe i18n: add more greek translations 2025-10-16 21:41:02 +03:00
Tim Jaeryang Baek 9ae06a3cac Merge pull request #18138 from open-webui/dev 2025-10-16 11:55:47 -05:00
Classic298 2783e0eb89 chore: Update CHANGELOG.md (#18199)
* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG for version 0.6.34

Updated changelog for version 0.6.34 with new features, fixes, and improvements.

* Woops
2025-10-16 11:44:37 -05:00
Timothy Jaeryang Baek efed0e3f63 fix: system prompt perm 2025-10-16 11:40:06 -05:00
Timothy Jaeryang Baek fd0e9652a8 chore: format 2025-10-16 11:36:26 -05:00
Timothy Jaeryang Baek d0da1d722c chore: i18n 2025-10-16 11:34:59 -05:00
Timothy Jaeryang Baek b48c790a76 chore: bump 2025-10-16 11:34:15 -05:00
Tim Jaeryang Baek 417c9d923a Merge pull request #18263 from silentoplayz/chat-deletion-bug
fix: reword misleading knowledge base warning in documents settings
2025-10-16 11:33:45 -05:00
Tim Jaeryang Baek e8c1dbb2da Merge pull request #18306 from palazski/main
feat: add mineru as document parser backend with support of both local and managed api
2025-10-16 10:49:29 -05:00
Tim Jaeryang Baek 2bd972305a Merge pull request #18361 from teephopdisawas/dev
Improved the Thai translations to match more with the contexts they're being used for.
2025-10-16 10:42:27 -05:00
Tim Jaeryang Baek 9b79486316 Merge pull request #18369 from taylorwilsdon/issues/11782
enh: restore visible scrollbar
2025-10-16 10:40:16 -05:00
Taylor Wilsdon fbeff475cf restore visible scrollbar 2025-10-16 09:05:17 -04:00
teephopdisawas 0dc04fd8b9 Fix Thai translations for consistency
Improve the translations to match more with the contexts.
2025-10-16 11:05:20 +07:00
palazski 288b323df8 feat: use MINERU_PARAMS json field for mineru settings 2025-10-15 22:59:59 +03:00
Tim Jaeryang Baek 29d4148fe9 Merge pull request #18344 from Classic298/patch-1
chore: feat template
2025-10-15 14:30:41 -05:00
Tim Jaeryang Baek 62e57fe32c Merge pull request #18346 from cubukcum/main
i18n: update Turkish translations for various UI elements
2025-10-15 11:46:52 -05:00
Mehmet Cubukcu 9dc7889493 update Turkish translations 2025-10-15 14:36:21 +03:00
Classic298 aa5de4c05b chore 2025-10-15 08:09:15 +02:00
Timothy Jaeryang Baek c46ea40f70 refac 2025-10-14 19:06:55 -05:00
silentoplayz ffe127e0d6 remove "Note:" 2025-10-14 19:36:43 -04:00
Timothy Jaeryang Baek bcb7b65553 chore: authlib bump 2025-10-14 18:36:28 -05:00
Tim Jaeryang Baek dd51808a6f Merge pull request #18258 from petrkrapek/community
i18n: Update Czech translation
2025-10-14 18:35:13 -05:00
Tim Jaeryang Baek 94806555bf Merge pull request #18261 from Classic298/patch-1
enh: lower JWT expiration default value and add warn message
2025-10-14 18:35:03 -05:00
Tim Jaeryang Baek 7a68f065fa Merge pull request #18260 from silentoplayz/fix-toast-click-issue
fix: allow toast notifications to be closed when a modal is open
2025-10-14 18:33:30 -05:00
Timothy Jaeryang Baek ad61460faf fix: tool server connection ui 2025-10-14 18:32:50 -05:00
Timothy Jaeryang BaekandClassic298 91a43848a1 enh: jwt_expires_in security warning
Co-Authored-By: Classic298 <27028174+Classic298@users.noreply.github.com>
2025-10-14 18:32:40 -05:00
Tim Jaeryang Baek 2a34a0ae10 Merge pull request #18273 from Cyp9715/dev
i18n: Korean translation updated
2025-10-14 18:24:34 -05:00
Timothy Jaeryang Baek fcc2bb5a05 refac: oracle23ai 2025-10-14 18:22:48 -05:00
Tim Jaeryang Baek 7031708c87 Merge pull request #18284 from Stoyan-Zlatev/feature/knowledge-response-file-hash
feat: Extend FileMetadataResponse to have hash field
2025-10-14 18:15:26 -05:00
Timothy Jaeryang Baek 8039cc40f7 refac/fix: support delete method for openapi tool servers 2025-10-14 18:12:19 -05:00
Tim Jaeryang Baek 515e136502 Merge pull request #18324 from ShirasawaSama/patch-37
fix: Fix missing model auto-pull when user settings are unmodified
2025-10-14 18:07:32 -05:00
Timothy Jaeryang Baek 5fe56a862b fix: pinned chats in ref chat 2025-10-14 18:06:29 -05:00
Tim Jaeryang Baek 9971919ca1 Merge pull request #18320 from BoFFire/patch-2
i18n : Kabyle translation
2025-10-14 15:01:33 -05:00
Shirasawa f0689b260a fix: Fix missing model auto-pull when user settings are unmodified 2025-10-14 11:43:51 +00:00
ButterflyOfFire 7ba752e2aa Update translation.json 2025-10-14 09:10:24 +01:00
Tim Jaeryang Baek 2b3b2e4aa6 Merge pull request #18310 from silentoplayz/fix-integrations-menu-closing-outside-click
fix: prevent integrations menu from closing when valves modal is open
2025-10-13 20:58:58 -05:00
silentoplayz 4b160d88a2 fix: prevent integrations menu from closing when valves modal is open
When the "Valves" modal is opened from the "Integrations" menu, a click outside the modal would incorrectly close the integrations menu first. This was because the dropdown's outside click handler was still active.

This commit fixes the issue by introducing a `closeOnOutsideClick` prop to the `Dropdown` component. This prop is controlled by the `MessageInput` component, which now disables the outside click handler on the integrations menu when the valves modal is open, and re-enables it when the modal is closed.
2025-10-13 18:13:08 -04:00
Timothy Jaeryang Baek a730a277b9 refac 2025-10-13 16:22:47 -05:00
palazski 40e9d9c330 feat: add mineru as document parser support with both local and managed api 2025-10-13 21:09:52 +03:00
Tim Jaeryang Baek f373784aaf Merge pull request #18266 from Classic298/patch-4
chore: expand SECURITY.MD once again (hopefully the last time)
2025-10-13 11:13:10 -05:00
Tim Jaeryang Baek 43d64b3bf8 Merge pull request #18267 from aleixdorca/dev
i18n: Update catalan translation.json
2025-10-13 11:11:17 -05:00
Stoyan Zlatev f0dfdb30c3 Extend FileMetadataResponse to have hash field 2025-10-13 12:38:10 +03:00
Cyp cf76922a8f Korean translation updated 2025-10-13 11:29:03 +09:00
Classic298 e41836f8bd Update SECURITY.md 2025-10-12 17:24:38 +02:00
Classic298 0417a456c3 Update SECURITY.md 2025-10-12 17:23:23 +02:00
Aleix Dorca 2f8685d0b7 Update catalan translation.json 2025-10-12 17:08:47 +02:00
Classic298 3fc29b292c chore: expand SECURITY.MD once again 2025-10-12 17:08:13 +02:00
silentoplayz e7476350e3 fix: reword misleading knowledge base warning 2025-10-12 07:49:38 -04:00
Classic298 31f6520ba9 enh: lower JWT expiration default value and add warn message 2025-10-12 12:33:35 +02:00
silentoplayz 1e6426ed4f fix: allow toast notifications to be closed when a modal is open
The focus trap in the modal component was preventing clicks on elements outside of the modal, including the notification toasts. This change configures the focus trap to allow clicks on toast notifications, so they can be dismissed even when a modal is open.
2025-10-12 05:52:24 -04:00
petrkrapek 6e78f855a3 Update Czech translations for various terms 2025-10-12 09:57:41 +02:00
Timothy Jaeryang Baek ffad1f1dd1 refac 2025-10-11 15:50:32 -05:00
Timothy Jaeryang Baek 5064506de4 refac/fix: inherit request form data 2025-10-11 14:54:07 -05:00
Tim Jaeryang Baek e14430daa8 Merge pull request #18248 from petrkrapek/community
i18n: Update Czech translation
2025-10-11 13:44:20 -05:00
Tim Jaeryang Baek 8c84501235 Merge pull request #18232 from silentoplayz/fix-chats-button-navigation
fix: prevent click on "Chats" in sidebar from going to new chat
2025-10-11 13:41:54 -05:00
Timothy Jaeryang Baek 74576b88f6 refac/fix 2025-10-11 13:40:49 -05:00
petrkrapek 7b3bd58735 Update Czech translations in translation.json 2025-10-11 12:04:31 +02:00
silentoplayz 95e3717f96 fix(sidebar): prevent click on Chats from going to new chat 2025-10-10 21:12:27 -04:00
Aslan Vatsaev a9c4e4b422 fix: auto show artifacts when opening a conversation 2025-10-10 13:47:33 +02:00
Aslan Vatsaev d13a35ab96 fix: auto show artifacts when opening a conversation 2025-10-10 13:26:12 +02:00
Tim Jaeryang Baek 43a2881074 Merge pull request #18179 from ShirasawaSama/patch-35
feat: do not initiate requests when the RecursiveFolder is not open to speed up page loading
2025-10-09 17:13:45 -05:00
Tim Jaeryang Baek a65b78fc61 Merge pull request #18198 from Classic298/patch-1
i18n: Update translation.json de-DE
2025-10-09 16:41:51 -05:00
Timothy Jaeryang Baek 4e763e8aa8 refac 2025-10-09 16:16:24 -05:00
Classic298 f9ff780422 Update translation.json 2025-10-09 22:02:47 +02:00
Tim Jaeryang Baek b93e9b1698 Merge pull request #18195 from EventHorizon-AI/fix/artifacts-duplicate-match
fix: Artifacts duplicate matching
2025-10-09 14:22:00 -05:00
Tim Jaeryang Baek de722390fc Merge pull request #18176 from Classic298/patch-1
chore: security.md
2025-10-09 14:20:58 -05:00
Tim Jaeryang Baek b37eb3dd5f Merge pull request #18178 from ShirasawaSama/patch-34
fix: fix incorrect display of undefined tool ID
2025-10-09 14:19:52 -05:00
Tim Jaeryang Baek f1113d2758 Merge pull request #18181 from ShirasawaSama/i18n/improve-chinese-translation
i18n: improve Chinese translation
2025-10-09 14:19:32 -05:00
EntropyYue 8ac30955a3 fix: Artifacts duplicate matching 2025-10-10 01:13:59 +08:00
Timothy Jaeryang Baek c4832fdb70 fix: full context issue 2025-10-09 11:55:12 -05:00
Shirasawa cce20f8bfc i18n: improve Chinese translation 2025-10-09 16:43:24 +08:00
Shirasawa ccfb0f54e1 feat: do not initiate requests when the RecursiveFolder is not open to speed up page loading 2025-10-09 15:57:50 +08:00
Shirasawa f1b50fb83a fix: fix incorrect display of undefined tool ID 2025-10-09 15:42:59 +08:00
Classic298 8ca4596918 Update SECURITY.md 2025-10-09 09:03:36 +02:00
Timothy Jaeryang Baek 5043e7fc8c refac: introduce model id length limit 2025-10-08 16:54:06 -05:00
+7 fbfbc29789 fix: search action i18n (#18162)
* 0.6.33 (#18118)

* feat: improve ollama model management experience

This commit introduces several improvements to the Ollama model management modal:

- Adds a cancel button to the model pulling operation, using the existing 'x' button pattern.
- Adds a cancel button to the "Update All" models operation, allowing the user to cancel the update for the currently processing model.
- Cleans up toast notifications when updating all models. A single toast is now shown at the beginning and a summary toast at the end, preventing notification spam.
- Refactors the `ManageOllama.svelte` component to support these new cancellation features.
- Adds tooltips to all buttons in the modal to improve clarity.
- Disables buttons when their corresponding input fields are empty to prevent accidental clicks.

* fix

* i18n: improve Chinese translation

* fix: handle non‑UTF8 chars in third‑party responses without error

* German translation of new strings in i18n

* log web search queries only with level 'debug' instead of 'info'

* Tool calls now only include text and dont inlcude other content like image b64

* fix onedrive

* fix: discovery url

* fix: default permissions not being loaded

* fix: ai hallucination

* fix: non rich text input copy

* refac: rm print statements

* refac: disable direct models from model editors

* refac/fix: do not process xlsx files with azure doc intelligence

* Update pull_request_template.md

* Update generated image translation in DE-de

* added missing danish translations

* feat(onedrive): Enable search and "My Organization" pivot

* style(onedrive): Formatting fix

* feat: Implement toggling for vertical and horizontal flow layouts

This commit introduces the necessary logic and UI controls to allow users to switch the Flow component layout between vertical and horizontal orientations.

*   **`Flow.svelte` Refactoring:**
    *   Updates logic for calculating level offsets and node positions to consistently respect the current flow orientation.
    *   Adds a control panel using `<Controls>` and `<SwitchButton>` components.
    *   Provides user interface elements to easily switch the flow layout between horizontal and vertical orientations.

* build(deps): bump pydantic from 2.11.7 to 2.11.9 in /backend

Bumps [pydantic](https://github.com/pydantic/pydantic) from 2.11.7 to 2.11.9.
- [Release notes](https://github.com/pydantic/pydantic/releases)
- [Changelog](https://github.com/pydantic/pydantic/blob/v2.11.9/HISTORY.md)
- [Commits](https://github.com/pydantic/pydantic/compare/v2.11.7...v2.11.9)

---
updated-dependencies:
- dependency-name: pydantic
  dependency-version: 2.11.9
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

* build(deps): bump black from 25.1.0 to 25.9.0 in /backend

Bumps [black](https://github.com/psf/black) from 25.1.0 to 25.9.0.
- [Release notes](https://github.com/psf/black/releases)
- [Changelog](https://github.com/psf/black/blob/main/CHANGES.md)
- [Commits](https://github.com/psf/black/compare/25.1.0...25.9.0)

---
updated-dependencies:
- dependency-name: black
  dependency-version: 25.9.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* build(deps): bump markdown from 3.8.2 to 3.9 in /backend

Bumps [markdown](https://github.com/Python-Markdown/markdown) from 3.8.2 to 3.9.
- [Release notes](https://github.com/Python-Markdown/markdown/releases)
- [Changelog](https://github.com/Python-Markdown/markdown/blob/master/docs/changelog.md)
- [Commits](https://github.com/Python-Markdown/markdown/compare/3.8.2...3.9.0)

---
updated-dependencies:
- dependency-name: markdown
  dependency-version: '3.9'
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* build(deps): bump chromadb from 1.0.20 to 1.1.0 in /backend

Bumps [chromadb](https://github.com/chroma-core/chroma) from 1.0.20 to 1.1.0.
- [Release notes](https://github.com/chroma-core/chroma/releases)
- [Changelog](https://github.com/chroma-core/chroma/blob/main/RELEASE_PROCESS.md)
- [Commits](https://github.com/chroma-core/chroma/compare/1.0.20...1.1.0)

---
updated-dependencies:
- dependency-name: chromadb
  dependency-version: 1.1.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* build(deps): bump opentelemetry-api from 1.36.0 to 1.37.0

Bumps [opentelemetry-api](https://github.com/open-telemetry/opentelemetry-python) from 1.36.0 to 1.37.0.
- [Release notes](https://github.com/open-telemetry/opentelemetry-python/releases)
- [Changelog](https://github.com/open-telemetry/opentelemetry-python/blob/main/CHANGELOG.md)
- [Commits](https://github.com/open-telemetry/opentelemetry-python/compare/v1.36.0...v1.37.0)

---
updated-dependencies:
- dependency-name: opentelemetry-api
  dependency-version: 1.37.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* refac: ollama embed form data

* fix: non rich text handling

* fix: oauth client registration

* refac

* chore: dep bump

* chore: fastapi bump

* chore/refac: bump bcrypt and remove passlib

* Improving Korean Translation

* refac

* Improving Korean Translation

* feat: PWA share_target implementation

Co-Authored-By: gjveld <19951982+gjveld@users.noreply.github.com>

* refac: message input mobile detection behaviour

* feat: model_ids per folder

* Update translation.json (pt-BR)

inclusion of new translations of items that have been added

* refac

* refac

* refac

* refac

* refac/fix: temp chat

* refac

* refac: stop task

* refac/fix: azure audio escape

* refac: external tool validation

* refac/enh: start.sh additional args support

* refac

* refac: styling

* refac/fix: direct connection floating action buttons

* refac/fix: system prompt duplication

* refac/enh: openai tts additional params support

* refac

* feat: load data in parallel to accelerate page loading speed

* i18n: improve Chinese translation

* refac

* refac: model selector

* UPD: i18n es-ES Translation v0.6.33

UPD: i18n es-ES Translation v0.6.33

Updated new strings.

* refac

* improved query pref by querying only relevant columns

* refac/enh: docling params

* refac

* refac: openai additional headers support

* refac

* FEAT: Add Vega Char Visualizer Renderer

### FEAT: Add Vega Char Visualizer Renderer

Feature required in https://github.com/open-webui/open-webui/discussions/18022

Added npm vega lib to package.json
Added function for visualization renderer to src/libs/utils/index.ts
Added logic to src/lib/components/chat/Messages/CodeBlock.svelte

The treatment is similar as for mermaid diagrams.

Reference: https://vega.github.io/vega/

* refac

* chore

* refac

* FEAT: Add Vega-Lite Char Visualizer Renderer

### FEAT: Add Vega Char Visualizer Renderer

Add suport for Vega-Lite Specifications.
Vega-Lite is a "compiled" version of Vega Char Visualizer.
For be rendered with Vega it have to be compiled.
This PR add the check and compile if necessary, is a complement of recent Vega Renderer Feature added.

* refac

* refac/fix: switch

* enh/refac: url input handling

* refac

* refac: styling

* UPD: Add Validators & Error Toast for Mermaid & Vega diagrams

### UPD: Feat:  Add Validators & Error Toast for Mermaid & Vega diagrams

Description:
As many time the diagrams generated or entered have syntax errors the diagrams are not rendered due to that errors, but as there isn't any notification is difficult to know what happend.

This PR add validator and toast notification when error on Mermaid and Vega/Vega-Lite diagrams, helping the user to fix its.

* removed redundant knowledge API call

* Fix Code Format

* refac: model workspace view

* refac

* refac: knowledge

* refac: prompts

* refac: tools

* refac

* feat: attach folder

* refac: make tencentcloud-sdk-python optional

* refac/fix: oauth

* enh: ENABLE_OAUTH_EMAIL_FALLBACK

* refac/fix: folders

* Update requirements.txt

* Update pyproject.toml

* UPD: Add Validators & Error Toast for Mermaid & Vega diagrams

### UPD: Feat:  Add Validators & Error Toast for Mermaid & Vega diagrams

Description:
As many time the diagrams generated or entered have syntax errors the diagrams are not rendered due to that errors, but as there isn't any notification is difficult to know what happend.

This PR add validator and toast notification when error on Mermaid and Vega/Vega-Lite diagrams, helping the user to fix its.

Note:
Another possibility of integrating this Graph Visualizer is through its svelte component: https://github.com/vega/svelte-vega/tree/main/packages/svelte-vega

* Removed unused toast import & Code Format

* refac

* refac: external tool server view

* refac

* refac: overview

* refac: styling

* refac

* Update bug_report.yaml

* refac

* refac

* refac

* refac

* refac: oauth client fallback

* Fixed: Cannot handle batch sizes > 1 if no padding token is defined

Fixes Cannot handle batch sizes > 1 if no padding token is defined

For reranker models that do not have this defined in their config by using the eos_token_id if present as pad_token_id.

* refac: fallback to reasoning content

* fix(i18n): corrected typo in Spanish translation for "Reasoning Tags"

Typo fixed in Spanish translation file at line 1240 of `open-webui/src/lib/i18n/locales/es-ES/translation.json`:

- Incorrect: "Eriquetas de Razonamiento"
- Correct:   "Etiquetas de Razonamiento"

This improves clarity and consistency in the UI.

* refac/fix: ENABLE_STAR_SESSIONS_MIDDLEWARE

* refac/fix: redirect

* refac

* refac

* refac

* refac: web search error handling

* refac: source parsing

* refac: functions

* refac

* refac/enh: note pdf export

* refac/fix: mcp oauth2.1

* chore: format

* chore: Changelog (#17995)

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* refac

* chore: dep bump

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: silentoplayz <jacwoo21@outlook.com>
Co-authored-by: Shirasawa <764798966@qq.com>
Co-authored-by: Jan Kessler <jakessle@uni-mainz.de>
Co-authored-by: Jacob Leksan <jacob.leksan@expedient.com>
Co-authored-by: Classic298 <27028174+Classic298@users.noreply.github.com>
Co-authored-by: sinejespersen <sinejespersen@protonmail.com>
Co-authored-by: Selene Blok <selene.blok@rws.nl>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Cyp <cypher9715@naver.com>
Co-authored-by: gjveld <19951982+gjveld@users.noreply.github.com>
Co-authored-by: joaoback <156559121+joaoback@users.noreply.github.com>
Co-authored-by: _00_ <131402327+rgaricano@users.noreply.github.com>
Co-authored-by: expruc <eygabi01@gmail.com>
Co-authored-by: YetheSamartaka <55753928+YetheSamartaka@users.noreply.github.com>
Co-authored-by: Akutangulo <akutangulo@gmail.com>

* fix: search action i18n

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: Tim Jaeryang Baek <tim@openwebui.com>
Co-authored-by: silentoplayz <jacwoo21@outlook.com>
Co-authored-by: Shirasawa <764798966@qq.com>
Co-authored-by: Jan Kessler <jakessle@uni-mainz.de>
Co-authored-by: Jacob Leksan <jacob.leksan@expedient.com>
Co-authored-by: Classic298 <27028174+Classic298@users.noreply.github.com>
Co-authored-by: sinejespersen <sinejespersen@protonmail.com>
Co-authored-by: Selene Blok <selene.blok@rws.nl>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Cyp <cypher9715@naver.com>
Co-authored-by: gjveld <19951982+gjveld@users.noreply.github.com>
Co-authored-by: joaoback <156559121+joaoback@users.noreply.github.com>
Co-authored-by: _00_ <131402327+rgaricano@users.noreply.github.com>
Co-authored-by: expruc <eygabi01@gmail.com>
Co-authored-by: YetheSamartaka <55753928+YetheSamartaka@users.noreply.github.com>
Co-authored-by: Akutangulo <akutangulo@gmail.com>
2025-10-08 15:31:15 -05:00
Tim Jaeryang Baek 86cb1058c3 Merge pull request #18151 from Kylapaallikko/dev
i18n: Update fi-FI translation
2025-10-08 15:30:42 -05:00
Tim Jaeryang Baek 40f7e17b3e Merge pull request #18153 from rgaricano/dev-es_ES
UPD: i18n: es-ES Translation v0.6.34
2025-10-08 15:29:29 -05:00
Tim Jaeryang Baek bc29e75116 Merge pull request #18152 from joaoback/patch-12
Update translation.json (pt-BR)
2025-10-08 15:28:42 -05:00
Tim Jaeryang Baek 17ad6f8aad Merge pull request #18141 from ShirasawaSama/i18n/improve-chinese-translation
i18n: improve Chinese translation
2025-10-08 15:28:24 -05:00
_00_ 3f030ef537 UPD: i18n: es-ES Translation v0.6.34
### UPD: i18n: es-ES Translation v0.6.34

Added new strings
2025-10-08 16:18:50 +02:00
joaoback 62456a606e Update translation.json (pt-BR)
Translations of new items added in the latest version.
2025-10-08 11:11:38 -03:00
joaoback 3512904e00 Update translation.json (pt-BR)
Translations of new items added in the latest version.
2025-10-08 11:04:27 -03:00
Kylapaallikko 38a39b5892 Update fi-FI translation.json
Added missing translations
2025-10-08 16:31:18 +03:00
Shirasawa 0127ac39eb i18n: improve Chinese translation 2025-10-08 14:20:47 +08:00
+6 bc0da24e9c 0.6.33 (#18118) (#18137)
* feat: improve ollama model management experience

This commit introduces several improvements to the Ollama model management modal:

- Adds a cancel button to the model pulling operation, using the existing 'x' button pattern.
- Adds a cancel button to the "Update All" models operation, allowing the user to cancel the update for the currently processing model.
- Cleans up toast notifications when updating all models. A single toast is now shown at the beginning and a summary toast at the end, preventing notification spam.
- Refactors the `ManageOllama.svelte` component to support these new cancellation features.
- Adds tooltips to all buttons in the modal to improve clarity.
- Disables buttons when their corresponding input fields are empty to prevent accidental clicks.

* fix

* i18n: improve Chinese translation

* fix: handle non‑UTF8 chars in third‑party responses without error

* German translation of new strings in i18n

* log web search queries only with level 'debug' instead of 'info'

* Tool calls now only include text and dont inlcude other content like image b64

* fix onedrive

* fix: discovery url

* fix: default permissions not being loaded

* fix: ai hallucination

* fix: non rich text input copy

* refac: rm print statements

* refac: disable direct models from model editors

* refac/fix: do not process xlsx files with azure doc intelligence

* Update pull_request_template.md

* Update generated image translation in DE-de

* added missing danish translations

* feat(onedrive): Enable search and "My Organization" pivot

* style(onedrive): Formatting fix

* feat: Implement toggling for vertical and horizontal flow layouts

This commit introduces the necessary logic and UI controls to allow users to switch the Flow component layout between vertical and horizontal orientations.

*   **`Flow.svelte` Refactoring:**
    *   Updates logic for calculating level offsets and node positions to consistently respect the current flow orientation.
    *   Adds a control panel using `<Controls>` and `<SwitchButton>` components.
    *   Provides user interface elements to easily switch the flow layout between horizontal and vertical orientations.

* build(deps): bump pydantic from 2.11.7 to 2.11.9 in /backend

Bumps [pydantic](https://github.com/pydantic/pydantic) from 2.11.7 to 2.11.9.
- [Release notes](https://github.com/pydantic/pydantic/releases)
- [Changelog](https://github.com/pydantic/pydantic/blob/v2.11.9/HISTORY.md)
- [Commits](https://github.com/pydantic/pydantic/compare/v2.11.7...v2.11.9)

---
updated-dependencies:
- dependency-name: pydantic
  dependency-version: 2.11.9
  dependency-type: direct:production
  update-type: version-update:semver-patch
...



* build(deps): bump black from 25.1.0 to 25.9.0 in /backend

Bumps [black](https://github.com/psf/black) from 25.1.0 to 25.9.0.
- [Release notes](https://github.com/psf/black/releases)
- [Changelog](https://github.com/psf/black/blob/main/CHANGES.md)
- [Commits](https://github.com/psf/black/compare/25.1.0...25.9.0)

---
updated-dependencies:
- dependency-name: black
  dependency-version: 25.9.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...



* build(deps): bump markdown from 3.8.2 to 3.9 in /backend

Bumps [markdown](https://github.com/Python-Markdown/markdown) from 3.8.2 to 3.9.
- [Release notes](https://github.com/Python-Markdown/markdown/releases)
- [Changelog](https://github.com/Python-Markdown/markdown/blob/master/docs/changelog.md)
- [Commits](https://github.com/Python-Markdown/markdown/compare/3.8.2...3.9.0)

---
updated-dependencies:
- dependency-name: markdown
  dependency-version: '3.9'
  dependency-type: direct:production
  update-type: version-update:semver-minor
...



* build(deps): bump chromadb from 1.0.20 to 1.1.0 in /backend

Bumps [chromadb](https://github.com/chroma-core/chroma) from 1.0.20 to 1.1.0.
- [Release notes](https://github.com/chroma-core/chroma/releases)
- [Changelog](https://github.com/chroma-core/chroma/blob/main/RELEASE_PROCESS.md)
- [Commits](https://github.com/chroma-core/chroma/compare/1.0.20...1.1.0)

---
updated-dependencies:
- dependency-name: chromadb
  dependency-version: 1.1.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...



* build(deps): bump opentelemetry-api from 1.36.0 to 1.37.0

Bumps [opentelemetry-api](https://github.com/open-telemetry/opentelemetry-python) from 1.36.0 to 1.37.0.
- [Release notes](https://github.com/open-telemetry/opentelemetry-python/releases)
- [Changelog](https://github.com/open-telemetry/opentelemetry-python/blob/main/CHANGELOG.md)
- [Commits](https://github.com/open-telemetry/opentelemetry-python/compare/v1.36.0...v1.37.0)

---
updated-dependencies:
- dependency-name: opentelemetry-api
  dependency-version: 1.37.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...



* refac: ollama embed form data

* fix: non rich text handling

* fix: oauth client registration

* refac

* chore: dep bump

* chore: fastapi bump

* chore/refac: bump bcrypt and remove passlib

* Improving Korean Translation

* refac

* Improving Korean Translation

* feat: PWA share_target implementation



* refac: message input mobile detection behaviour

* feat: model_ids per folder

* Update translation.json (pt-BR)

inclusion of new translations of items that have been added

* refac

* refac

* refac

* refac

* refac/fix: temp chat

* refac

* refac: stop task

* refac/fix: azure audio escape

* refac: external tool validation

* refac/enh: start.sh additional args support

* refac

* refac: styling

* refac/fix: direct connection floating action buttons

* refac/fix: system prompt duplication

* refac/enh: openai tts additional params support

* refac

* feat: load data in parallel to accelerate page loading speed

* i18n: improve Chinese translation

* refac

* refac: model selector

* UPD: i18n es-ES Translation v0.6.33

UPD: i18n es-ES Translation v0.6.33

Updated new strings.

* refac

* improved query pref by querying only relevant columns

* refac/enh: docling params

* refac

* refac: openai additional headers support

* refac

* FEAT: Add Vega Char Visualizer Renderer

### FEAT: Add Vega Char Visualizer Renderer

Feature required in https://github.com/open-webui/open-webui/discussions/18022

Added npm vega lib to package.json
Added function for visualization renderer to src/libs/utils/index.ts
Added logic to src/lib/components/chat/Messages/CodeBlock.svelte

The treatment is similar as for mermaid diagrams.

Reference: https://vega.github.io/vega/

* refac

* chore

* refac

* FEAT: Add Vega-Lite Char Visualizer Renderer

### FEAT: Add Vega Char Visualizer Renderer

Add suport for Vega-Lite Specifications.
Vega-Lite is a "compiled" version of Vega Char Visualizer.
For be rendered with Vega it have to be compiled.
This PR add the check and compile if necessary, is a complement of recent Vega Renderer Feature added.

* refac

* refac/fix: switch

* enh/refac: url input handling

* refac

* refac: styling

* UPD: Add Validators & Error Toast for Mermaid & Vega diagrams

### UPD: Feat:  Add Validators & Error Toast for Mermaid & Vega diagrams

Description:
As many time the diagrams generated or entered have syntax errors the diagrams are not rendered due to that errors, but as there isn't any notification is difficult to know what happend.

This PR add validator and toast notification when error on Mermaid and Vega/Vega-Lite diagrams, helping the user to fix its.

* removed redundant knowledge API call

* Fix Code Format

* refac: model workspace view

* refac

* refac: knowledge

* refac: prompts

* refac: tools

* refac

* feat: attach folder

* refac: make tencentcloud-sdk-python optional

* refac/fix: oauth

* enh: ENABLE_OAUTH_EMAIL_FALLBACK

* refac/fix: folders

* Update requirements.txt

* Update pyproject.toml

* UPD: Add Validators & Error Toast for Mermaid & Vega diagrams

### UPD: Feat:  Add Validators & Error Toast for Mermaid & Vega diagrams

Description:
As many time the diagrams generated or entered have syntax errors the diagrams are not rendered due to that errors, but as there isn't any notification is difficult to know what happend.

This PR add validator and toast notification when error on Mermaid and Vega/Vega-Lite diagrams, helping the user to fix its.

Note:
Another possibility of integrating this Graph Visualizer is through its svelte component: https://github.com/vega/svelte-vega/tree/main/packages/svelte-vega

* Removed unused toast import & Code Format

* refac

* refac: external tool server view

* refac

* refac: overview

* refac: styling

* refac

* Update bug_report.yaml

* refac

* refac

* refac

* refac

* refac: oauth client fallback

* Fixed: Cannot handle batch sizes > 1 if no padding token is defined

Fixes Cannot handle batch sizes > 1 if no padding token is defined

For reranker models that do not have this defined in their config by using the eos_token_id if present as pad_token_id.

* refac: fallback to reasoning content

* fix(i18n): corrected typo in Spanish translation for "Reasoning Tags"

Typo fixed in Spanish translation file at line 1240 of `open-webui/src/lib/i18n/locales/es-ES/translation.json`:

- Incorrect: "Eriquetas de Razonamiento"
- Correct:   "Etiquetas de Razonamiento"

This improves clarity and consistency in the UI.

* refac/fix: ENABLE_STAR_SESSIONS_MIDDLEWARE

* refac/fix: redirect

* refac

* refac

* refac

* refac: web search error handling

* refac: source parsing

* refac: functions

* refac

* refac/enh: note pdf export

* refac/fix: mcp oauth2.1

* chore: format

* chore: Changelog (#17995)

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* refac

* chore: dep bump

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: silentoplayz <jacwoo21@outlook.com>
Co-authored-by: Shirasawa <764798966@qq.com>
Co-authored-by: Jan Kessler <jakessle@uni-mainz.de>
Co-authored-by: Jacob Leksan <jacob.leksan@expedient.com>
Co-authored-by: Classic298 <27028174+Classic298@users.noreply.github.com>
Co-authored-by: sinejespersen <sinejespersen@protonmail.com>
Co-authored-by: Selene Blok <selene.blok@rws.nl>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Cyp <cypher9715@naver.com>
Co-authored-by: gjveld <19951982+gjveld@users.noreply.github.com>
Co-authored-by: joaoback <156559121+joaoback@users.noreply.github.com>
Co-authored-by: _00_ <131402327+rgaricano@users.noreply.github.com>
Co-authored-by: expruc <eygabi01@gmail.com>
Co-authored-by: YetheSamartaka <55753928+YetheSamartaka@users.noreply.github.com>
Co-authored-by: Akutangulo <akutangulo@gmail.com>
2025-10-07 23:06:04 -05:00
Tim Jaeryang Baek 46ae3f4f5d Merge branch 'dev' into main 2025-10-07 23:05:53 -05:00
Timothy Jaeryang Baek 19ba6c06dd refac/fix: verify connection 2025-10-07 22:56:52 -05:00
+6 8d7d79d54b 0.6.33 (#18118)
* feat: improve ollama model management experience

This commit introduces several improvements to the Ollama model management modal:

- Adds a cancel button to the model pulling operation, using the existing 'x' button pattern.
- Adds a cancel button to the "Update All" models operation, allowing the user to cancel the update for the currently processing model.
- Cleans up toast notifications when updating all models. A single toast is now shown at the beginning and a summary toast at the end, preventing notification spam.
- Refactors the `ManageOllama.svelte` component to support these new cancellation features.
- Adds tooltips to all buttons in the modal to improve clarity.
- Disables buttons when their corresponding input fields are empty to prevent accidental clicks.

* fix

* i18n: improve Chinese translation

* fix: handle non‑UTF8 chars in third‑party responses without error

* German translation of new strings in i18n

* log web search queries only with level 'debug' instead of 'info'

* Tool calls now only include text and dont inlcude other content like image b64

* fix onedrive

* fix: discovery url

* fix: default permissions not being loaded

* fix: ai hallucination

* fix: non rich text input copy

* refac: rm print statements

* refac: disable direct models from model editors

* refac/fix: do not process xlsx files with azure doc intelligence

* Update pull_request_template.md

* Update generated image translation in DE-de

* added missing danish translations

* feat(onedrive): Enable search and "My Organization" pivot

* style(onedrive): Formatting fix

* feat: Implement toggling for vertical and horizontal flow layouts

This commit introduces the necessary logic and UI controls to allow users to switch the Flow component layout between vertical and horizontal orientations.

*   **`Flow.svelte` Refactoring:**
    *   Updates logic for calculating level offsets and node positions to consistently respect the current flow orientation.
    *   Adds a control panel using `<Controls>` and `<SwitchButton>` components.
    *   Provides user interface elements to easily switch the flow layout between horizontal and vertical orientations.

* build(deps): bump pydantic from 2.11.7 to 2.11.9 in /backend

Bumps [pydantic](https://github.com/pydantic/pydantic) from 2.11.7 to 2.11.9.
- [Release notes](https://github.com/pydantic/pydantic/releases)
- [Changelog](https://github.com/pydantic/pydantic/blob/v2.11.9/HISTORY.md)
- [Commits](https://github.com/pydantic/pydantic/compare/v2.11.7...v2.11.9)

---
updated-dependencies:
- dependency-name: pydantic
  dependency-version: 2.11.9
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

* build(deps): bump black from 25.1.0 to 25.9.0 in /backend

Bumps [black](https://github.com/psf/black) from 25.1.0 to 25.9.0.
- [Release notes](https://github.com/psf/black/releases)
- [Changelog](https://github.com/psf/black/blob/main/CHANGES.md)
- [Commits](https://github.com/psf/black/compare/25.1.0...25.9.0)

---
updated-dependencies:
- dependency-name: black
  dependency-version: 25.9.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* build(deps): bump markdown from 3.8.2 to 3.9 in /backend

Bumps [markdown](https://github.com/Python-Markdown/markdown) from 3.8.2 to 3.9.
- [Release notes](https://github.com/Python-Markdown/markdown/releases)
- [Changelog](https://github.com/Python-Markdown/markdown/blob/master/docs/changelog.md)
- [Commits](https://github.com/Python-Markdown/markdown/compare/3.8.2...3.9.0)

---
updated-dependencies:
- dependency-name: markdown
  dependency-version: '3.9'
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* build(deps): bump chromadb from 1.0.20 to 1.1.0 in /backend

Bumps [chromadb](https://github.com/chroma-core/chroma) from 1.0.20 to 1.1.0.
- [Release notes](https://github.com/chroma-core/chroma/releases)
- [Changelog](https://github.com/chroma-core/chroma/blob/main/RELEASE_PROCESS.md)
- [Commits](https://github.com/chroma-core/chroma/compare/1.0.20...1.1.0)

---
updated-dependencies:
- dependency-name: chromadb
  dependency-version: 1.1.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* build(deps): bump opentelemetry-api from 1.36.0 to 1.37.0

Bumps [opentelemetry-api](https://github.com/open-telemetry/opentelemetry-python) from 1.36.0 to 1.37.0.
- [Release notes](https://github.com/open-telemetry/opentelemetry-python/releases)
- [Changelog](https://github.com/open-telemetry/opentelemetry-python/blob/main/CHANGELOG.md)
- [Commits](https://github.com/open-telemetry/opentelemetry-python/compare/v1.36.0...v1.37.0)

---
updated-dependencies:
- dependency-name: opentelemetry-api
  dependency-version: 1.37.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* refac: ollama embed form data

* fix: non rich text handling

* fix: oauth client registration

* refac

* chore: dep bump

* chore: fastapi bump

* chore/refac: bump bcrypt and remove passlib

* Improving Korean Translation

* refac

* Improving Korean Translation

* feat: PWA share_target implementation

Co-Authored-By: gjveld <19951982+gjveld@users.noreply.github.com>

* refac: message input mobile detection behaviour

* feat: model_ids per folder

* Update translation.json (pt-BR)

inclusion of new translations of items that have been added

* refac

* refac

* refac

* refac

* refac/fix: temp chat

* refac

* refac: stop task

* refac/fix: azure audio escape

* refac: external tool validation

* refac/enh: start.sh additional args support

* refac

* refac: styling

* refac/fix: direct connection floating action buttons

* refac/fix: system prompt duplication

* refac/enh: openai tts additional params support

* refac

* feat: load data in parallel to accelerate page loading speed

* i18n: improve Chinese translation

* refac

* refac: model selector

* UPD: i18n es-ES Translation v0.6.33

UPD: i18n es-ES Translation v0.6.33

Updated new strings.

* refac

* improved query pref by querying only relevant columns

* refac/enh: docling params

* refac

* refac: openai additional headers support

* refac

* FEAT: Add Vega Char Visualizer Renderer

### FEAT: Add Vega Char Visualizer Renderer

Feature required in https://github.com/open-webui/open-webui/discussions/18022

Added npm vega lib to package.json
Added function for visualization renderer to src/libs/utils/index.ts
Added logic to src/lib/components/chat/Messages/CodeBlock.svelte

The treatment is similar as for mermaid diagrams.

Reference: https://vega.github.io/vega/

* refac

* chore

* refac

* FEAT: Add Vega-Lite Char Visualizer Renderer

### FEAT: Add Vega Char Visualizer Renderer

Add suport for Vega-Lite Specifications.
Vega-Lite is a "compiled" version of Vega Char Visualizer.
For be rendered with Vega it have to be compiled.
This PR add the check and compile if necessary, is a complement of recent Vega Renderer Feature added.

* refac

* refac/fix: switch

* enh/refac: url input handling

* refac

* refac: styling

* UPD: Add Validators & Error Toast for Mermaid & Vega diagrams

### UPD: Feat:  Add Validators & Error Toast for Mermaid & Vega diagrams

Description:
As many time the diagrams generated or entered have syntax errors the diagrams are not rendered due to that errors, but as there isn't any notification is difficult to know what happend.

This PR add validator and toast notification when error on Mermaid and Vega/Vega-Lite diagrams, helping the user to fix its.

* removed redundant knowledge API call

* Fix Code Format

* refac: model workspace view

* refac

* refac: knowledge

* refac: prompts

* refac: tools

* refac

* feat: attach folder

* refac: make tencentcloud-sdk-python optional

* refac/fix: oauth

* enh: ENABLE_OAUTH_EMAIL_FALLBACK

* refac/fix: folders

* Update requirements.txt

* Update pyproject.toml

* UPD: Add Validators & Error Toast for Mermaid & Vega diagrams

### UPD: Feat:  Add Validators & Error Toast for Mermaid & Vega diagrams

Description:
As many time the diagrams generated or entered have syntax errors the diagrams are not rendered due to that errors, but as there isn't any notification is difficult to know what happend.

This PR add validator and toast notification when error on Mermaid and Vega/Vega-Lite diagrams, helping the user to fix its.

Note:
Another possibility of integrating this Graph Visualizer is through its svelte component: https://github.com/vega/svelte-vega/tree/main/packages/svelte-vega

* Removed unused toast import & Code Format

* refac

* refac: external tool server view

* refac

* refac: overview

* refac: styling

* refac

* Update bug_report.yaml

* refac

* refac

* refac

* refac

* refac: oauth client fallback

* Fixed: Cannot handle batch sizes > 1 if no padding token is defined

Fixes Cannot handle batch sizes > 1 if no padding token is defined

For reranker models that do not have this defined in their config by using the eos_token_id if present as pad_token_id.

* refac: fallback to reasoning content

* fix(i18n): corrected typo in Spanish translation for "Reasoning Tags"

Typo fixed in Spanish translation file at line 1240 of `open-webui/src/lib/i18n/locales/es-ES/translation.json`:

- Incorrect: "Eriquetas de Razonamiento"
- Correct:   "Etiquetas de Razonamiento"

This improves clarity and consistency in the UI.

* refac/fix: ENABLE_STAR_SESSIONS_MIDDLEWARE

* refac/fix: redirect

* refac

* refac

* refac

* refac: web search error handling

* refac: source parsing

* refac: functions

* refac

* refac/enh: note pdf export

* refac/fix: mcp oauth2.1

* chore: format

* chore: Changelog (#17995)

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* refac

* chore: dep bump

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: silentoplayz <jacwoo21@outlook.com>
Co-authored-by: Shirasawa <764798966@qq.com>
Co-authored-by: Jan Kessler <jakessle@uni-mainz.de>
Co-authored-by: Jacob Leksan <jacob.leksan@expedient.com>
Co-authored-by: Classic298 <27028174+Classic298@users.noreply.github.com>
Co-authored-by: sinejespersen <sinejespersen@protonmail.com>
Co-authored-by: Selene Blok <selene.blok@rws.nl>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Cyp <cypher9715@naver.com>
Co-authored-by: gjveld <19951982+gjveld@users.noreply.github.com>
Co-authored-by: joaoback <156559121+joaoback@users.noreply.github.com>
Co-authored-by: _00_ <131402327+rgaricano@users.noreply.github.com>
Co-authored-by: expruc <eygabi01@gmail.com>
Co-authored-by: YetheSamartaka <55753928+YetheSamartaka@users.noreply.github.com>
Co-authored-by: Akutangulo <akutangulo@gmail.com>
2025-10-07 16:20:27 -05:00
Timothy Jaeryang Baek c1e86ad0a6 chore: dep bump 2025-10-07 16:19:47 -05:00
Timothy Jaeryang Baek dbbdad3ebd refac 2025-10-07 16:13:20 -05:00
Classic298 73f38dce88 chore: Changelog (#17995)
* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md
2025-10-07 16:10:52 -05:00
Timothy Jaeryang Baek d11fba2dfd chore: format 2025-10-07 15:23:46 -05:00
Timothy Jaeryang Baek 911a114ad4 refac/fix: mcp oauth2.1 2025-10-07 14:56:10 -05:00
Timothy Jaeryang Baek 216fb5c3db refac/enh: note pdf export 2025-10-07 14:34:47 -05:00
Timothy Jaeryang Baek 18d74f2b10 refac 2025-10-07 14:23:44 -05:00
Timothy Jaeryang Baek f5e1a42f51 refac: functions 2025-10-07 14:20:07 -05:00
Tim Jaeryang Baek 299e0e2674 Merge pull request #18114 from Akutangulo/main
i18n: Fix typo in Spanish translation ("Eriquetas" → "Etiquetas")
2025-10-07 13:53:05 -05:00
Timothy Jaeryang Baek 3c47e49cf0 refac: source parsing 2025-10-07 12:46:18 -05:00
Timothy Jaeryang Baek 53e98620bf refac: web search error handling 2025-10-07 12:15:24 -05:00
Timothy Jaeryang Baek 7d205b1711 refac 2025-10-07 12:08:21 -05:00
Timothy Jaeryang Baek e000494e48 refac 2025-10-07 11:53:30 -05:00
Timothy Jaeryang Baek 2d71193bc0 refac 2025-10-07 10:59:53 -05:00
Tim Jaeryang Baek 696876d393 Merge pull request #18108 from YetheSamartaka/main
fix: "Cannot handle batch sizes > 1 if no padding token is defined" for some reranking models
2025-10-07 10:57:34 -05:00
Timothy Jaeryang Baek db1780678e refac/fix: redirect 2025-10-07 10:53:58 -05:00
Timothy Jaeryang Baek 861953fd2d refac/fix: ENABLE_STAR_SESSIONS_MIDDLEWARE 2025-10-07 10:52:34 -05:00
Akutangulo 7448cf6c65 fix(i18n): corrected typo in Spanish translation for "Reasoning Tags"
Typo fixed in Spanish translation file at line 1240 of `open-webui/src/lib/i18n/locales/es-ES/translation.json`:

- Incorrect: "Eriquetas de Razonamiento"
- Correct:   "Etiquetas de Razonamiento"

This improves clarity and consistency in the UI.
2025-10-07 15:06:42 +02:00
Timothy Jaeryang Baek 4bb5b39410 refac: fallback to reasoning content 2025-10-07 07:59:21 -05:00
YetheSamartaka f69426fd77 Fixed: Cannot handle batch sizes > 1 if no padding token is defined
Fixes Cannot handle batch sizes > 1 if no padding token is defined

For reranker models that do not have this defined in their config by using the eos_token_id if present as pad_token_id.
2025-10-07 14:47:35 +02:00
Timothy Jaeryang Baek 82a16f1305 refac: oauth client fallback 2025-10-07 07:46:13 -05:00
Timothy Jaeryang Baek b98d8aa8ec refac 2025-10-07 07:31:06 -05:00
Timothy Jaeryang Baek b6108527c2 refac 2025-10-06 18:42:48 -05:00
Timothy Jaeryang Baek c58a4a0166 refac 2025-10-06 18:42:24 -05:00
Tim Jaeryang Baek ca3563b3b4 Merge pull request #18079 from Classic298/patch-2
Update bug_report.yaml
2025-10-06 15:29:13 -05:00
Timothy Jaeryang Baek dec3748509 refac 2025-10-06 11:58:15 -05:00
Classic298 d62f21549f Update bug_report.yaml 2025-10-06 12:36:56 +02:00
Timothy Jaeryang Baek 3f71fa641f refac 2025-10-06 02:45:02 -05:00
Timothy Jaeryang Baek 2f398895c6 refac: styling 2025-10-06 00:02:22 -05:00
Timothy Jaeryang Baek b4536a691a refac: overview 2025-10-05 23:56:23 -05:00
Timothy Jaeryang Baek 270ca2ddbe refac 2025-10-05 23:36:26 -05:00
Timothy Jaeryang Baek 9b4f032660 refac: external tool server view 2025-10-05 23:35:04 -05:00
Timothy Jaeryang Baek 53de48d2b3 refac 2025-10-05 23:29:48 -05:00
Tim Jaeryang Baek 08f8713ee1 Merge pull request #18068 from rgaricano/dev-FEAT_Vega_Visualizer
UPD: Add Validators & Error Toast for Mermaid & Vega diagrams
2025-10-05 21:39:25 -05:00
Tim Jaeryang Baek a97f1457f6 Merge pull request #18066 from Classic298/pymilvus
chore: Update pymilvus dependency
2025-10-05 21:38:28 -05:00
_00_ fa2534a529 Removed unused toast import & Code Format 2025-10-06 00:16:01 +02:00
_00_ 8538d1b2cb UPD: Add Validators & Error Toast for Mermaid & Vega diagrams
### UPD: Feat:  Add Validators & Error Toast for Mermaid & Vega diagrams

Description:
As many time the diagrams generated or entered have syntax errors the diagrams are not rendered due to that errors, but as there isn't any notification is difficult to know what happend.

This PR add validator and toast notification when error on Mermaid and Vega/Vega-Lite diagrams, helping the user to fix its.

Note:
Another possibility of integrating this Graph Visualizer is through its svelte component: https://github.com/vega/svelte-vega/tree/main/packages/svelte-vega
2025-10-06 00:09:17 +02:00
Classic298 079b9ec86e Update pyproject.toml 2025-10-05 22:47:33 +02:00
Classic298 52998e6aaa Update requirements.txt 2025-10-05 22:47:07 +02:00
Timothy Jaeryang Baek 5448618dd5 refac/fix: folders 2025-10-05 15:20:47 -05:00
Timothy Jaeryang Baek eaf786c1ef enh: ENABLE_OAUTH_EMAIL_FALLBACK 2025-10-05 15:11:56 -05:00
Tim Jaeryang Baek c453d919b3 Merge pull request #18057 from expruc/perf/knowledge_page
Perf: knowledge page loading speed
2025-10-05 15:05:45 -05:00
Timothy Jaeryang Baek 062264c7f6 refac/fix: oauth 2025-10-05 14:22:00 -05:00
Timothy Jaeryang Baek 713f0ef9ce refac: make tencentcloud-sdk-python optional 2025-10-05 04:49:06 -05:00
Timothy Jaeryang Baek d2cb78179d feat: attach folder 2025-10-05 02:48:08 -05:00
Timothy Jaeryang Baek fff0e55f41 refac 2025-10-05 02:05:52 -05:00
Timothy Jaeryang Baek a743b16728 refac: tools 2025-10-05 02:03:57 -05:00
Timothy Jaeryang Baek 23c8f6d507 refac: prompts 2025-10-05 01:41:42 -05:00
Timothy Jaeryang Baek 2250d102b2 refac: knowledge 2025-10-05 01:06:40 -05:00
Timothy Jaeryang Baek 96ecb47bc7 refac 2025-10-05 00:25:40 -05:00
Timothy Jaeryang Baek 6050c86ab6 refac: model workspace view 2025-10-04 21:44:51 -05:00
_00_ e6cc7db3c1 Fix Code Format 2025-10-04 14:45:49 +02:00
expruc 0ba1cfc612 removed redundant knowledge API call 2025-10-04 15:35:47 +03:00
_00_ 2e08bda19d UPD: Add Validators & Error Toast for Mermaid & Vega diagrams
### UPD: Feat:  Add Validators & Error Toast for Mermaid & Vega diagrams

Description:
As many time the diagrams generated or entered have syntax errors the diagrams are not rendered due to that errors, but as there isn't any notification is difficult to know what happend.

This PR add validator and toast notification when error on Mermaid and Vega/Vega-Lite diagrams, helping the user to fix its.
2025-10-04 14:17:04 +02:00
Timothy Jaeryang Baek 2c59a28860 refac: styling 2025-10-04 02:35:15 -05:00
Timothy Jaeryang Baek f20723ca54 refac 2025-10-04 02:07:02 -05:00
Timothy Jaeryang Baek a2a2bafdf6 enh/refac: url input handling 2025-10-04 02:02:26 -05:00
Timothy Jaeryang Baek ce83276fa4 refac/fix: switch 2025-10-04 02:00:10 -05:00
Timothy Jaeryang Baek d40c710354 refac 2025-10-04 01:19:33 -05:00
Tim Jaeryang Baek f65231becb Merge pull request #18040 from rgaricano/dev-FEAT_Vega_Visualizer
FEAT: Add Vega-Lite Char Visualizer Renderer
2025-10-03 23:52:30 -05:00
_00_ 039358e049 FEAT: Add Vega-Lite Char Visualizer Renderer
### FEAT: Add Vega Char Visualizer Renderer

Add suport for Vega-Lite Specifications.
Vega-Lite is a "compiled" version of Vega Char Visualizer.
For be rendered with Vega it have to be compiled.
This PR add the check and compile if necessary, is a complement of recent Vega Renderer Feature added.
2025-10-04 06:41:07 +02:00
Timothy Jaeryang Baek 59929a8d06 refac 2025-10-03 18:11:46 -05:00
Timothy Jaeryang Baek 9ff6baf7d9 chore 2025-10-03 18:06:05 -05:00
Tim Jaeryang Baek 0a4922b40f Merge pull request #18033 from rgaricano/dev-FEAT_Vega_Visualizer
FEAT: Add Vega Char Visualizer Renderer
2025-10-03 12:51:03 -05:00
Timothy Jaeryang Baek c5c6c32f24 refac 2025-10-03 12:50:07 -05:00
_00_ 351ba167f5 FEAT: Add Vega Char Visualizer Renderer
### FEAT: Add Vega Char Visualizer Renderer

Feature required in https://github.com/open-webui/open-webui/discussions/18022

Added npm vega lib to package.json
Added function for visualization renderer to src/libs/utils/index.ts
Added logic to src/lib/components/chat/Messages/CodeBlock.svelte

The treatment is similar as for mermaid diagrams.

Reference: https://vega.github.io/vega/
2025-10-03 19:42:04 +02:00
Timothy Jaeryang Baek 8334149cb2 refac 2025-10-03 02:20:03 -05:00
Tim Jaeryang Baek 5b05f13ce5 Merge pull request #18013 from expruc/perf/query_file_metadatas
perf: improve file query time by querying only relevant columns
2025-10-03 00:29:39 -05:00
Timothy Jaeryang Baek f25b7b73b4 refac: openai additional headers support 2025-10-03 00:23:26 -05:00
Timothy Jaeryang Baek 7e70f8d2c1 refac 2025-10-02 17:45:05 -05:00
Tim Jaeryang Baek 227139aa33 Merge pull request #16863 from silentoplayz/ollama-cancel-and-toast-cleanup
feat: improve Ollama model management modal
2025-10-02 17:41:21 -05:00
Timothy Jaeryang Baek 339e95e9d7 refac/enh: docling params 2025-10-02 16:28:06 -05:00
Tim Jaeryang Baek 2494de8f12 Merge pull request #18009 from rgaricano/dev-es_ES
UPD: i18n es-ES Translation v0.6.33
2025-10-02 15:35:28 -05:00
expruc d12afc6039 improved query pref by querying only relevant columns 2025-10-02 22:26:24 +03:00
Timothy Jaeryang Baek abe70d1793 refac 2025-10-02 13:26:41 -05:00
_00_ 893d4fba5f UPD: i18n es-ES Translation v0.6.33
UPD: i18n es-ES Translation v0.6.33

Updated new strings.
2025-10-02 20:09:06 +02:00
Tim Jaeryang Baek d9fdbb627f Merge pull request #17559 from ShirasawaSama/patch-14
feat: load data in parallel to accelerate page loading speed
2025-10-02 13:06:54 -05:00
Timothy Jaeryang Baek 79afb7afdb refac: model selector 2025-10-02 13:03:33 -05:00
Tim Jaeryang Baek 5fe588b480 Merge pull request #18004 from ShirasawaSama/i18n/improve-chinese-translation
i18n: improve Chinese translation
2025-10-02 11:51:34 -05:00
Timothy Jaeryang Baek 8f41835352 refac 2025-10-02 11:48:56 -05:00
Shirasawa b8649fc364 i18n: improve Chinese translation 2025-10-02 23:29:31 +08:00
Shirasawa 981306fa2b feat: load data in parallel to accelerate page loading speed 2025-10-02 23:23:43 +08:00
Timothy Jaeryang Baek c8c6a48b94 refac 2025-10-02 04:09:17 -05:00
Timothy Jaeryang Baek 5d5b42d3f5 refac/enh: openai tts additional params support 2025-10-02 03:52:29 -05:00
Timothy Jaeryang Baek a1fc99c66f refac/fix: system prompt duplication 2025-10-02 02:57:54 -05:00
Timothy Jaeryang Baek 6c4deed37a refac/fix: direct connection floating action buttons 2025-10-02 02:21:21 -05:00
Timothy Jaeryang Baek b6538b2cdd refac: styling 2025-10-02 02:19:01 -05:00
Timothy Jaeryang Baek a57bb6a7d4 refac 2025-10-02 01:59:10 -05:00
Timothy Jaeryang Baek 6ff392edc0 refac/enh: start.sh additional args support 2025-10-02 01:58:06 -05:00
Tim Jaeryang Baek e9bced867d Merge pull request #17986 from joaoback/patch-11
Update translation.json (pt-BR)
2025-10-02 01:30:04 -05:00
Timothy Jaeryang Baek 39675434f6 refac: external tool validation 2025-10-02 01:29:44 -05:00
Timothy Jaeryang Baek 2e75c6dbdf refac/fix: azure audio escape 2025-10-01 23:01:08 -05:00
Timothy Jaeryang Baek c0d3e70296 refac: stop task 2025-10-01 22:58:04 -05:00
Timothy Jaeryang Baek 028f29556f refac 2025-10-01 22:51:57 -05:00
Timothy Jaeryang Baek 3a601e0fc3 refac/fix: temp chat 2025-10-01 22:49:25 -05:00
Timothy Jaeryang Baek af34e414e1 refac 2025-10-01 22:05:42 -05:00
Timothy Jaeryang Baek 9677871ce7 refac 2025-10-01 21:57:34 -05:00
Timothy Jaeryang Baek d87a2315ce refac 2025-10-01 21:56:37 -05:00
Timothy Jaeryang Baek 97faeccebf refac 2025-10-01 21:56:32 -05:00
joaoback 6afbcac9d0 Update translation.json (pt-BR)
inclusion of new translations of items that have been added
2025-10-01 23:05:56 -03:00
Timothy Jaeryang Baek e2ca7b8632 feat: model_ids per folder 2025-10-01 20:50:04 -05:00
Timothy Jaeryang Baek 557367cf48 refac: message input mobile detection behaviour 2025-10-01 20:02:27 -05:00
Timothy Jaeryang Baekandgjveld 6c8c3257fd feat: PWA share_target implementation
Co-Authored-By: gjveld <19951982+gjveld@users.noreply.github.com>
2025-10-01 19:57:19 -05:00
Tim Jaeryang Baek 6f88db1be2 Merge pull request #17983 from Cyp9715/dev
i18n: Improving Korean Translation
2025-10-01 19:48:06 -05:00
Cyp dc3911be14 Improving Korean Translation 2025-10-02 09:36:37 +09:00
Timothy Jaeryang Baek 03498bd2fd refac 2025-10-01 19:31:47 -05:00
Cyp 6ddb44985e Improving Korean Translation 2025-10-02 09:28:29 +09:00
Tim Jaeryang Baek 2f923e47ec Merge pull request #17941 from silentoplayz/vertical-and-horizontal-overview
feat: Implement toggling for vertical and horizontal flow layouts
2025-10-01 19:27:13 -05:00
Timothy Jaeryang Baek ebce0578e6 chore/refac: bump bcrypt and remove passlib 2025-10-01 19:19:56 -05:00
Timothy Jaeryang Baek 7563a62dfe chore: fastapi bump 2025-10-01 18:14:55 -05:00
Timothy Jaeryang Baek f5a4d27e57 chore: dep bump 2025-10-01 18:13:27 -05:00
Tim Jaeryang Baek 6eb479d5af Merge pull request #17952 from open-webui/dependabot/uv/dev/opentelemetry-api-1.37.0
build(deps): bump opentelemetry-api from 1.36.0 to 1.37.0
2025-10-01 18:07:20 -05:00
Tim Jaeryang Baek 8c9c2bec6c Merge pull request #17948 from open-webui/dependabot/pip/backend/dev/chromadb-1.1.0
build(deps): bump chromadb from 1.0.20 to 1.1.0 in /backend
2025-10-01 18:07:09 -05:00
Tim Jaeryang Baek 86727ae271 Merge pull request #17947 from open-webui/dependabot/pip/backend/dev/markdown-3.9
build(deps): bump markdown from 3.8.2 to 3.9 in /backend
2025-10-01 18:07:05 -05:00
Tim Jaeryang Baek c6e498e837 Merge pull request #17945 from open-webui/dependabot/pip/backend/dev/black-25.9.0
build(deps): bump black from 25.1.0 to 25.9.0 in /backend
2025-10-01 18:06:54 -05:00
Tim Jaeryang Baek be01b87cd4 Merge pull request #17944 from open-webui/dependabot/pip/backend/dev/pydantic-2.11.9
build(deps): bump pydantic from 2.11.7 to 2.11.9 in /backend
2025-10-01 18:06:49 -05:00
Timothy Jaeryang Baek 0330dc3159 refac 2025-10-01 15:35:37 -05:00
Timothy Jaeryang Baek e493562735 fix: oauth client registration 2025-10-01 15:15:24 -05:00
Timothy Jaeryang Baek b1c196ed83 fix: non rich text handling 2025-10-01 12:39:49 -05:00
Timothy Jaeryang Baek a8a0a2655d refac: ollama embed form data 2025-09-30 22:04:22 -05:00
dependabot[bot] b0e3afc0be build(deps): bump opentelemetry-api from 1.36.0 to 1.37.0
Bumps [opentelemetry-api](https://github.com/open-telemetry/opentelemetry-python) from 1.36.0 to 1.37.0.
- [Release notes](https://github.com/open-telemetry/opentelemetry-python/releases)
- [Changelog](https://github.com/open-telemetry/opentelemetry-python/blob/main/CHANGELOG.md)
- [Commits](https://github.com/open-telemetry/opentelemetry-python/compare/v1.36.0...v1.37.0)

---
updated-dependencies:
- dependency-name: opentelemetry-api
  dependency-version: 1.37.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-10-01 02:25:40 +00:00
dependabot[bot] 242bcc8643 build(deps): bump chromadb from 1.0.20 to 1.1.0 in /backend
Bumps [chromadb](https://github.com/chroma-core/chroma) from 1.0.20 to 1.1.0.
- [Release notes](https://github.com/chroma-core/chroma/releases)
- [Changelog](https://github.com/chroma-core/chroma/blob/main/RELEASE_PROCESS.md)
- [Commits](https://github.com/chroma-core/chroma/compare/1.0.20...1.1.0)

---
updated-dependencies:
- dependency-name: chromadb
  dependency-version: 1.1.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-10-01 02:09:24 +00:00
dependabot[bot] 17af98126a build(deps): bump markdown from 3.8.2 to 3.9 in /backend
Bumps [markdown](https://github.com/Python-Markdown/markdown) from 3.8.2 to 3.9.
- [Release notes](https://github.com/Python-Markdown/markdown/releases)
- [Changelog](https://github.com/Python-Markdown/markdown/blob/master/docs/changelog.md)
- [Commits](https://github.com/Python-Markdown/markdown/compare/3.8.2...3.9.0)

---
updated-dependencies:
- dependency-name: markdown
  dependency-version: '3.9'
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-10-01 02:09:20 +00:00
dependabot[bot] d9a723b0ab build(deps): bump black from 25.1.0 to 25.9.0 in /backend
Bumps [black](https://github.com/psf/black) from 25.1.0 to 25.9.0.
- [Release notes](https://github.com/psf/black/releases)
- [Changelog](https://github.com/psf/black/blob/main/CHANGES.md)
- [Commits](https://github.com/psf/black/compare/25.1.0...25.9.0)

---
updated-dependencies:
- dependency-name: black
  dependency-version: 25.9.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-10-01 02:09:14 +00:00
dependabot[bot] 6cc7a8b052 build(deps): bump pydantic from 2.11.7 to 2.11.9 in /backend
Bumps [pydantic](https://github.com/pydantic/pydantic) from 2.11.7 to 2.11.9.
- [Release notes](https://github.com/pydantic/pydantic/releases)
- [Changelog](https://github.com/pydantic/pydantic/blob/v2.11.9/HISTORY.md)
- [Commits](https://github.com/pydantic/pydantic/compare/v2.11.7...v2.11.9)

---
updated-dependencies:
- dependency-name: pydantic
  dependency-version: 2.11.9
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-10-01 02:09:11 +00:00
Tim Jaeryang Baek 2341c2e729 Merge pull request #17930 from selenecodes/feat/onedrive-file-picker-add-search-and-my-organisation-pivot
feat: Enable search and "My Organization" pivot in OneDrive integration
2025-09-30 19:26:56 -05:00
silentoplayz 80cbdbb535 feat: Implement toggling for vertical and horizontal flow layouts
This commit introduces the necessary logic and UI controls to allow users to switch the Flow component layout between vertical and horizontal orientations.

*   **`Flow.svelte` Refactoring:**
    *   Updates logic for calculating level offsets and node positions to consistently respect the current flow orientation.
    *   Adds a control panel using `<Controls>` and `<SwitchButton>` components.
    *   Provides user interface elements to easily switch the flow layout between horizontal and vertical orientations.
2025-09-30 16:09:55 -04:00
Tim Jaeryang Baek 39b6f4bbf5 Merge pull request #17912 from Classic298/patch-1
chore: proposed changes to pull request template
2025-09-30 10:35:51 -05:00
Tim Jaeryang Baek c850439f92 Merge pull request #17916 from Classic298/patch-2
i18n: Update generated image translation in DE-de
2025-09-30 10:35:28 -05:00
Tim Jaeryang Baek 44b71de41e Merge pull request #17923 from itk-dev/feature/danish-translations-added
added missing danish translations
2025-09-30 10:35:10 -05:00
Selene Blok 5c059e604b style(onedrive): Formatting fix 2025-09-30 16:49:53 +02:00
Selene Blok 07cc807bc9 feat(onedrive): Enable search and "My Organization" pivot 2025-09-30 16:43:38 +02:00
sinejespersen e96fb67367 added missing danish translations 2025-09-30 12:49:12 +02:00
Classic298 7259905114 Update generated image translation in DE-de 2025-09-30 10:51:39 +02:00
Classic298 0d0d28641d Update pull_request_template.md 2025-09-30 08:36:31 +02:00
Timothy Jaeryang Baek 01a5b97415 refac/fix: do not process xlsx files with azure doc intelligence 2025-09-29 23:05:24 -05:00
Timothy Jaeryang Baek 8c662c65a9 refac: disable direct models from model editors 2025-09-29 22:59:28 -05:00
Timothy Jaeryang Baek 88a6fe3c7a refac: rm print statements 2025-09-29 22:59:10 -05:00
Timothy Jaeryang Baek fcc3d9ed2b fix: non rich text input copy 2025-09-29 22:45:38 -05:00
Timothy Jaeryang Baek 58efa18f96 fix: ai hallucination 2025-09-29 21:37:38 -05:00
Timothy Jaeryang Baek 887772db22 fix: default permissions not being loaded 2025-09-29 21:36:07 -05:00
Timothy Jaeryang Baek 73f32a88e3 fix: discovery url 2025-09-29 21:30:19 -05:00
Tim Jaeryang Baek ddb109a456 Merge pull request #17888 from Ithanil/nolog_websearch
fix: log web search queries only with level 'debug' instead of 'info'
2025-09-29 21:27:27 -05:00
Tim Jaeryang Baek ccf53afad4 Merge pull request #17886 from Ithanil/de_i18n
i18n: German translation of new strings
2025-09-29 21:26:24 -05:00
Tim Jaeryang Baek 170ae9f3be Merge pull request #17882 from ShirasawaSama/patch-3
fix: handle non‑UTF8 chars in third‑party responses without error
2025-09-29 21:25:55 -05:00
Tim Jaeryang Baek 7d513ff1fe Merge pull request #17881 from ShirasawaSama/patch-20
i18n: improve Chinese (zhCN & zh-TW) translation
2025-09-29 21:25:26 -05:00
Tim Jaeryang Baek b387880194 Merge pull request #17897 from jmleksan/fix/tool-task-only-include-text
Fix: Tool task only include text
2025-09-29 21:23:24 -05:00
Tim Jaeryang Baek b15db795e1 Merge pull request #17902 from Classic298/patch-1
fix: onedrive
2025-09-29 21:22:56 -05:00
Classic298 2a3f57bc61 fix onedrive 2025-09-29 22:12:49 +02:00
Jacob Leksan 0a928d6e9d Tool calls now only include text and dont inlcude other content like image b64 2025-09-29 12:50:01 -04:00
Tim Jaeryang Baek 4d7fddaf7e Merge pull request #17892 from open-webui/dev
Dev
2025-09-29 10:07:13 -05:00
Timothy Jaeryang Baek 5438eb0057 doc: changelog 2025-09-29 10:04:21 -05:00
Tim Jaeryang Baek 88f7852928 Merge pull request #17890 from Classic298/patch-1
DEV branch changelog 0.6.32
2025-09-29 10:03:53 -05:00
Timothy Jaeryang Baek a5a098943c refac 2025-09-29 10:03:03 -05:00
Classic298 f267e479ba DEV branch changelog 0.6.32 2025-09-29 15:55:58 +02:00
Jan Kessler c9c0dd367f log web search queries only with level 'debug' instead of 'info' 2025-09-29 11:59:04 +02:00
Jan Kessler a395af31b1 German translation of new strings in i18n 2025-09-29 11:43:17 +02:00
Shirasawa 3389f0eece fix: handle non‑UTF8 chars in third‑party responses without error 2025-09-29 08:16:50 +00:00
Shirasawa 3b1197a1bb i18n: improve Chinese translation 2025-09-29 07:47:11 +00:00
Tim Jaeryang Baek 37d1c85c99 Merge pull request #17827 from open-webui/dev
0.6.32
2025-09-29 01:13:00 -05:00
Timothy Jaeryang Baek b6a485371d doc: changelog 2025-09-29 01:04:59 -05:00
Timothy Jaeryang Baek da236d5b38 chore: bump 2025-09-29 00:58:45 -05:00
Timothy Jaeryang Baek e7fa86aa26 chore: format 2025-09-29 00:58:21 -05:00
Timothy Jaeryang Baek 51a138aec3 refac: edit user modal 2025-09-29 00:51:06 -05:00
Timothy Jaeryang Baek aee7aaae68 refac 2025-09-29 00:35:21 -05:00
Tim Jaeryang Baek 2d94b8e905 Merge pull request #17837 from Classic298/milvus-multitenancy
feat: Impelement Milvus multitenancy // breaking: set milvus multitenancy as standard option (just like Qdrant already is)
2025-09-29 00:29:35 -05:00
Timothy Jaeryang Baek 8656bfc5f4 refac 2025-09-29 00:03:03 -05:00
Tim Jaeryang Baek d37f108ccd Merge pull request #17848 from Classic298/feat/group-permission-warning
Feat/chore: Add warning for conflicting group permissions / Refactored Permissions.svelte
2025-09-28 23:51:52 -05:00
Timothy Jaeryang Baek 88aca2e4d8 chore: format 2025-09-28 23:42:38 -05:00
Timothy Jaeryang Baek 234aa69753 refac 2025-09-28 22:16:11 -05:00
Timothy Jaeryang Baek f58fc753e3 feat/enh: embed citation 2025-09-28 22:15:47 -05:00
Timothy Jaeryang Baek 118549caf3 enh/fix: filter content metadata 2025-09-28 20:17:27 -05:00
Tim Jaeryang Baek 1c418a7f83 Merge pull request #17871 from silentoplayz/backend-json-model-import
feat: move JSON model import to backend for massive speedup
2025-09-28 19:25:40 -05:00
silentoplayz fe28097817 feat: refactor model import to a single backend endpoint
This refactors the model import functionality to improve performance and user experience by centralizing the logic on the backend.

Previously, the frontend would parse an imported JSON file and send an individual API request for each model, which was slow and inefficient.

This change introduces a new backend endpoint, `/api/v1/models/import`, that accepts a list of model objects. The frontend now reads the selected JSON file, parses it, and sends the entire payload to the backend in a single request. The backend then processes this list, creating or updating models as necessary.

This commit also includes the following fixes:
- Handles cases where the imported JSON contains models without `meta` or `params` fields by providing default empty values.
2025-09-28 18:49:42 -04:00
Timothy Jaeryang Baek 7bae9053ac refac: copy behaviour 2025-09-28 17:10:11 -05:00
silentoplayz 231d182c35 feat: move JSON model import to backend
This moves the JSON model import functionality to the backend. Instead of the frontend parsing the JSON file and sending multiple requests, it now uploads the file to a new endpoint (/api/v1/models/import), which processes the file and imports the models. This improves efficiency and provides better user feedback.
2025-09-28 18:09:58 -04:00
Timothy Jaeryang Baek fe54fb61aa fix: session middleware should be required by default 2025-09-28 16:35:13 -05:00
Timothy Jaeryang Baek 7e8ee46d2c refac 2025-09-28 15:18:12 -05:00
Timothy Jaeryang Baek da6748a68f refac 2025-09-28 14:59:30 -05:00
Timothy Jaeryang Baek 91b6483aa9 refac 2025-09-28 14:46:01 -05:00
Classic298 cf20f04bdc Update Permissions.svelte 2025-09-28 21:33:40 +02:00
Classic298 67b3929954 Delete src/lib/components/common/Warning.svelte 2025-09-28 21:27:31 +02:00
Classic298 ad9cae32dc Delete src/lib/components/icons/ExclamationTriangle.svelte 2025-09-28 21:27:20 +02:00
Classic298 a4e0c10f34 Delete src/lib/components/admin/Users/Groups/PermissionSwitch.svelte 2025-09-28 21:27:06 +02:00
Classic298 b1e63639cd ADD FAT WARNING - QDRANT 2025-09-28 21:17:07 +02:00
Classic298 0e99c43495 ADD FAT WARNING 2025-09-28 21:16:02 +02:00
Timothy Jaeryang Baek 4f06f29348 refac 2025-09-28 13:22:39 -05:00
Timothy Jaeryang Baek 4c2c58214c refac 2025-09-28 13:09:09 -05:00
Tim Jaeryang Baek 9a8c9ecb4d Merge pull request #17823 from ITOTI-Y/main
fix: correct messageId parameter in createMessagesList call
2025-09-28 12:57:56 -05:00
Timothy Jaeryang Baek 95462a394a refac 2025-09-28 12:54:31 -05:00
Tim Jaeryang Baek 11fd0735d1 Merge pull request #17851 from Classic298/feat-toggle-chat-title-in-browser-tab
feat: add toggle to show/hide chat title in browser tab
2025-09-28 12:53:19 -05:00
Tim Jaeryang Baek 2cb35e7142 Merge pull request #17843 from Classic298/feat/clone-default-group-permissions
feat/chore: Clone default group permissions for new groups and remove redundant modal
2025-09-28 12:50:59 -05:00
Tim Jaeryang Baek e71ed76165 Merge pull request #17857 from silentoplayz/feat-bulk-unarchive
feat: add backend handling for unarchiving all chats
2025-09-28 12:45:49 -05:00
Timothy Jaeryang Baek 742e2ff193 refac 2025-09-28 12:42:02 -05:00
Timothy Jaeryang Baek 3aad157006 refac 2025-09-28 12:26:13 -05:00
silentoplayz a572cf4842 feat: add backend handling for unarchiving all chats
The previous implementation for unarchiving all chats in `ArchivedChatsModal.svelte` was inefficient, as it sent a separate request for each chat, which could potentially overload the server.

This commit introduces a new backend endpoint, `/chats/unarchive/all`, to handle the bulk unarchiving of all chats for a user with a single API call.

The frontend has been updated to use this new endpoint, resolving the performance issue by minimizing the number of requests to the server.
2025-09-28 13:25:34 -04:00
Timothy Jaeryang Baek 97dae432ab refac 2025-09-28 12:23:20 -05:00
Timothy Jaeryang Baek db9d3d386d refac: mcp spec/response handling 2025-09-28 12:22:11 -05:00
Timothy Jaeryang Baek 4aa41aa139 refac 2025-09-28 12:00:19 -05:00
Tim Jaeryang Baek 5568a312cd Merge pull request #17835 from BakirBukvic/main
feat/ i18n: Add Bosnian (Latin) language
2025-09-28 11:48:35 -05:00
Tim Jaeryang Baek 2915bcf28c Merge pull request #17833 from ShirasawaSama/patch-20
i18n: improved Chinese (zh-CN & zh-TW) translation
2025-09-28 11:47:06 -05:00
Tim Jaeryang Baek 54ed62f7b0 Merge pull request #17832 from silentoplayz/pinned-chats-not-updating
fix: pinned chats not updating after archiving all chats
2025-09-28 11:46:53 -05:00
Tim Jaeryang Baek baf913c832 Merge pull request #17850 from silentoplayz/remove-unused-params
chore: Remove unused parameter definitions
2025-09-28 11:37:50 -05:00
google-labs-jules[bot] 57c02b2523 feat: add toggle to show/hide chat title in browser tab
This commit introduces a new setting in the Interface settings that allows users to control whether the chat title is used as the browser's tab title.

The following changes were made:
- Added `useChatTitleAsTabTitle` to the `Settings` type in `src/lib/stores/index.ts`.
- Added a toggle switch in `src/lib/components/chat/Settings/Interface.svelte` to manage this new setting.
- Updated `src/lib/components/chat/Chat.svelte` to conditionally set the document title based on the `useChatTitleAsTabTitle` setting.
2025-09-28 16:10:00 +00:00
silentoplayz 670dfca6a8 remove: used advanced params 2025-09-28 11:41:12 -04:00
Classic298 fb3eeaa126 Update Groups.svelte 2025-09-28 16:56:04 +02:00
google-labs-jules[bot] 30550d9190 Feat: Add warning for conflicting group permissions
This change introduces a visual warning in the group settings page. The warning appears when an admin attempts to disable a permission for a group that is already enabled in the default 'user' group. This is necessary because permissions are additive, and disabling a permission in a specific group will not revoke it if it's enabled in the default group.

To achieve this, the following changes were made:
- A new `PermissionSwitch.svelte` component was created to encapsulate the permission switch and its warning logic, avoiding redundant code.
- The `Groups.svelte` component was updated to correctly fetch the default user group's permissions.
- The `Permissions.svelte` component was refactored to use the new `PermissionSwitch.svelte` component, making the code cleaner and more maintainable.
2025-09-28 13:42:10 +00:00
google-labs-jules[bot] cafe748b1b feat(groups): Clone default group permissions for new groups
When creating a new user group, the permissions for the new group will now be pre-populated with the same permissions as the "default user group".

This is achieved by removing the separate `AddGroupModal` and instead using the `EditGroupModal` for both creating and editing groups. When creating a new group, the `EditGroupModal` is now pre-populated with the default permissions, saving administrators from having to manually configure them each time.

This change simplifies the codebase by removing a redundant component and directly addresses the user's request to streamline the group creation process.
2025-09-28 12:56:39 +00:00
Classic298 1773a4d4b8 typo 2025-09-28 11:14:27 +02:00
Classic298 27af4506df Update config.py 2025-09-28 11:10:37 +02:00
Classic298 01d4a8ab7a Update factory.py 2025-09-28 11:06:29 +02:00
Classic298 8dc43f9e3a Create milvus_multitenancy.py 2025-09-28 11:05:15 +02:00
Classic298 50a2204046 add env vars 2025-09-28 11:04:24 +02:00
bakir baed3035ac add bosnian to languages.json 2025-09-28 09:23:33 +02:00
bakir 88bfe777cd add Bosnian language pack 2025-09-28 09:19:51 +02:00
Shirasawa f912b06b42 i18n: improve Chinese translation 2025-09-28 03:39:00 +00:00
silentoplayz 2b5dca2929 fix: pinned chats not updating after archiving all chats
This commit fixes a UI bug where pinned chats would remain visible in the sidebar after all chats were archived from the Data Controls menu.

The `archiveAllChatsHandler` in `DataControls.svelte` has been updated to clear the `pinnedChats` store, ensuring the sidebar UI is correctly updated.
2025-09-27 22:44:04 -04:00
Tim Jaeryang Baek 2c80f60f3e Merge pull request #17812 from silentoplayz/long-text-truncation
fix: truncate long names in delete confirmation modals
2025-09-27 16:30:33 -05:00
Tim Jaeryang Baek 1283a8ce79 Merge pull request #17819 from silentoplayz/fix-layout-and-i18n
fix: i81n.t and correct button layout issue
2025-09-27 16:30:07 -05:00
ITOTI 68a4fdf0e9 fix: correct messageId parameter in createMessagesList call
- Fix FloatingButtons component to use messageId instead of id
2025-09-27 20:41:05 +00:00
silentoplayz 27cd87e9ad fix: i81n.t and correct button layout issue
1.  **i18n Regression:** A latent bug in `src/routes/(app)/workspace/models/create/+page.svelte` was causing an `i18n.t is not a function` error. This was due to an incorrect call to the `i18n` Svelte store. The fix corrects the call to use the proper auto-subscription syntax (`$i18n.t()`).

2.  **Vertical Button Text:** In `src/lib/components/playground/Chat.svelte`, the "Assistant"/"User" role button's text was displaying vertically. This was caused by a `flex-1` class on its container, which has been removed.
2025-09-27 15:45:25 -04:00
Timothy Jaeryang Baek 272c6f5ec5 refac 2025-09-27 04:38:54 -05:00
Timothy Jaeryang Baek 86ef57f6c3 refac 2025-09-27 04:33:00 -05:00
Timothy Jaeryang Baek b4eea78aff refac 2025-09-27 04:06:42 -05:00
Timothy Jaeryang Baek 1a18928c94 enh: reply to message 2025-09-27 04:05:12 -05:00
Timothy Jaeryang Baek d7c54d92b5 refac 2025-09-26 22:57:38 -05:00
silentoplayz e62f2b3c75 fix: truncate more long names in delete confirmation modals 2025-09-26 23:56:18 -04:00
Timothy Jaeryang Baek 45e1c72613 refac 2025-09-26 22:53:57 -05:00
Timothy Jaeryang Baek 23f62a7312 enh: tool server import/export 2025-09-26 22:53:02 -05:00
Timothy Jaeryang Baek d1a6c6b209 refac 2025-09-26 22:09:27 -05:00
Timothy Jaeryang Baek bad7d69a58 feat/enh: external tool server manual JSON spec 2025-09-26 22:02:48 -05:00
Timothy Jaeryang Baek a05dab6298 refac 2025-09-26 21:16:34 -05:00
Timothy Jaeryang Baek c80bb31968 refac/enh: folder optimization 2025-09-26 20:48:17 -05:00
Timothy Jaeryang Baek 54beeeaf72 refac: tools 2025-09-26 19:01:22 -05:00
Tim Jaeryang Baek 1de5827eb3 Merge pull request #17805 from silentoplayz/fix-username-truncation
fix: truncate long usernames in UI
2025-09-26 17:53:37 -05:00
Timothy Jaeryang Baek b77848244b refac: user valves 2025-09-26 17:49:42 -05:00
silentoplayz 16cf973ce5 fix: truncate long usernames in UI
Long usernames were causing layout issues in several parts of the application. This change truncates long usernames with an ellipsis to prevent them from overflowing.

The following areas have been fixed:
- Edit User modal
- User Chats modal
- Edit User Group modal
- Users table in the admin overview

fix: truncate long usernames in UI

Long usernames were causing layout issues in several parts of the application. This change truncates long usernames with an ellipsis to prevent them from overflowing.

The following areas have been fixed:
- Edit User modal
- User Chats modal
- Edit User Group modal
- Users table in the admin overview

Revert "fix: truncate long usernames in UI"

This reverts commit b623fdc95d0c494228b49f9369db3bbb3042cef0.
2025-09-26 18:30:48 -04:00
Tim Jaeryang Baek 807a8be299 Merge pull request #17767 from ShirasawaSama/patch-31
fix: fixed the hover effect for the MessageInput Integrations button
2025-09-26 16:58:15 -05:00
Tim Jaeryang Baek 5765a87720 Merge pull request #17779 from silentoplayz/fix-set-as-default
fix: 'Set as default' click position
2025-09-26 16:57:31 -05:00
Timothy Jaeryang Baek a1829f6a3e refac: styling 2025-09-26 16:55:38 -05:00
Timothy Jaeryang Baek b8c3e5ed3e refac 2025-09-26 16:43:12 -05:00
Tim Jaeryang Baek d10b518f42 Merge pull request #17801 from Classic298/feature/notes-public-sharing-permission
feat: add permission toggle for public sharing of notes
2025-09-26 16:11:17 -05:00
Timothy Jaeryang Baek 4997ef2662 refac 2025-09-26 15:57:03 -05:00
Timothy Jaeryang Baek cda4c95c86 fix: default tool calling 2025-09-26 15:50:16 -05:00
google-labs-jules[bot] 41e4e7395c feat: add permission toggle for public sharing of notes
This commit introduces a new permission toggle that allows administrators to control whether users can publicly share their notes.

- Adds a new environment variable `USER_PERMISSIONS_NOTES_ALLOW_PUBLIC_SHARING` to control the default setting.
- Adds a `public_notes` permission to the `sharing` section of the user permissions.
- Adds a toggle switch to the admin panel for managing this permission.
- Implements backend logic to enforce the permission when a user attempts to share a note publicly.
2025-09-26 20:48:01 +00:00
Timothy Jaeryang Baek 680783266c refac/fix: tool response header type check 2025-09-26 15:42:17 -05:00
Timothy Jaeryang Baek 99d7773230 refac: styling 2025-09-26 15:00:06 -05:00
Timothy Jaeryang Baek ac08529380 refac 2025-09-26 14:40:30 -05:00
Tim Jaeryang Baek a80d5240f6 Merge pull request #17796 from silentoplayz/truncate-selector-tags
fix: truncate long filter tags in model selector and prevent wrapping
2025-09-26 14:35:58 -05:00
Tim Jaeryang Baek 9b86932714 Merge pull request #17797 from SZegotaM/dev
i18n(de-DE): add translations for newly added UI items + other translation fixes
2025-09-26 14:35:12 -05:00
Timothy Jaeryang Baek 0431ad9cc4 refac: get_discovery_urls 2025-09-26 14:34:26 -05:00
Tim Jaeryang Baek f8a3ed2d18 Merge pull request #17770 from Classic298/feat-milvus-diskann-support
feat: Add DISKANN index type support for Milvus
2025-09-26 14:23:53 -05:00
SZegotaM dcb0933149 Update German translations in translation.json
Added missing German translation
2025-09-26 21:16:35 +02:00
silentoplayz b516431569 fix: truncate long filter tags in model selector and prevent wrapping
This commit addresses an issue where long filter tags at the top of the model selector dropdown were not truncated correctly and would wrap to a new line, causing layout issues.

- A hard character limit of 16 characters is applied to the filter tags within the `Selector.svelte` component. Tags longer than 16 characters are truncated with an ellipsis (...) directly in the code. The full tag name remains available in the tooltip.
- The `whitespace-nowrap` class has been added to the tag container to ensure that the tags remain on a single, horizontally scrollable line.
2025-09-26 15:03:44 -04:00
Tim Jaeryang Baek af6a25eed7 Merge pull request #17769 from Classic298/fix-milvus-limit-error
Fix: milvus error because the limit set to None by default
2025-09-26 12:54:00 -05:00
Tim Jaeryang Baek acdafcd18d Merge pull request #17777 from ShirasawaSama/patch-33
fix: show error message when the uploading file is modified
2025-09-26 12:52:09 -05:00
Tim Jaeryang Baek a97cdc0da0 Merge pull request #17791 from silentoplayz/truncate-models-tag
Fix: truncate long model tags with a 32 characters limit
2025-09-26 12:46:45 -05:00
silentoplayz ac6292b812 Fix: truncate long model tags with a character limit
Long model tags on the Models page in the workspace section were not truncated consistently, which could cause layout issues.

This change implements a hard character limit of 32 characters on the model tags. Tags longer than 32 characters are truncated with an ellipsis (...) directly in the code. The full tag name remains available in the tooltip.
2025-09-26 13:28:35 -04:00
Tim Jaeryang Baek d126c23ef8 Merge pull request #17783 from joaoback/patch-10
Update translation.json (pt-BR)
2025-09-26 12:26:25 -05:00
joaoback 36da2b06a5 Update translation.json (pt-BR)
translation of the new items that were included in the latest version.
2025-09-26 09:18:27 -03:00
silentoplayz 90a5b3befb fix: 'Set as default' 2025-09-26 06:42:23 -04:00
Shirasawa 42faa63227 fix: show error message when the uploading file is modified 2025-09-26 17:31:16 +08:00
Classic298 9e3d5407ae Merge branch 'open-webui:main' into feat-milvus-diskann-support 2025-09-26 10:43:01 +02:00
Classic298 b550d78905 Merge branch 'open-webui:main' into fix-milvus-limit-error 2025-09-26 10:42:53 +02:00
google-labs-jules[bot] 123dbf152e feat: Add DISKANN index type support for Milvus
This commit introduces support for the DISKANN index type in the Milvus vector database integration.

Changes include:
- Added `MILVUS_DISKANN_MAX_DEGREE` and `MILVUS_DISKANN_SEARCH_LIST_SIZE` configuration variables.
- Updated the Milvus client to recognize and configure the DISKANN index type during collection creation.
2025-09-26 06:54:06 +00:00
google-labs-jules[bot] e7ccaf6e78 Fix: milvus error because the limit set to None by default
The pymilvus library expects -1 for unlimited queries, but the code was passing None, which caused a TypeError. This commit changes the default value of the limit parameter in the query method from None to -1. It also updates the call site in the get method to pass -1 instead of None and updates the type hint and a comment to reflect this change.
2025-09-26 06:39:54 +00:00
Shirasawa 15bd5040f1 fix: fixed the hover effect for the MessageInput Integrations button 2025-09-26 11:56:34 +08:00
Tim Jaeryang Baek 598282cf75 Merge pull request #17747 from open-webui/dev
0.6.31
2025-09-25 15:28:06 -05:00
Timothy Jaeryang Baek f7ea60b500 Update CHANGELOG.md 2025-09-25 15:26:26 -05:00
Timothy Jaeryang Baek 2fa222d00a refac 2025-09-25 15:20:13 -05:00
Timothy Jaeryang Baek 6920fed97e refac 2025-09-25 14:53:21 -05:00
Timothy Jaeryang Baek 528e58155b doc: changelog 2025-09-25 14:49:23 -05:00
Tim Jaeryang Baek 503fc354cc Merge pull request #17752 from Classic298/changelog-done
chore: Changelog 0.6.31
2025-09-25 14:45:34 -05:00
Classic298 4eec423950 Update CHANGELOG.md 2025-09-25 21:42:07 +02:00
Timothy Jaeryang Baek 52bc5306b0 refac 2025-09-25 14:37:30 -05:00
Timothy Jaeryang Baek 90e4b49b88 refac/fix: onedrive input menu styling issue 2025-09-25 14:36:25 -05:00
Timothy Jaeryang Baek 750a659a9f security: svg xss fix 2025-09-25 14:29:50 -05:00
Classic298 6fde0f2960 Update CHANGELOG.md 2025-09-25 21:20:39 +02:00
Timothy Jaeryang Baek a0e323b89b refac 2025-09-25 14:20:02 -05:00
Classic298 a68ac4efe6 Update CHANGELOG.md 2025-09-25 21:14:46 +02:00
Timothy Jaeryang Baek 393bdd7ead chore: bump 2025-09-25 14:13:12 -05:00
Timothy Jaeryang Baek 7f411dd5cc feat/enh: perplexity search support 2025-09-25 14:02:46 -05:00
Classic298 c790ec7625 Update CHANGELOG.md 2025-09-25 20:58:17 +02:00
Classic298 c46f3d05a8 Update CHANGELOG.md 2025-09-25 20:56:59 +02:00
Timothy Jaeryang Baek b38d59dee6 refac 2025-09-25 13:53:56 -05:00
Timothy Jaeryang Baek da661756fa refac/fix: proper notes db operations 2025-09-25 13:47:43 -05:00
Classic298 46967ffd04 Update CHANGELOG.md 2025-09-25 20:47:12 +02:00
Timothy Jaeryang Baek 5b1f9e3e21 refac: search modal input filters behaviour 2025-09-25 13:25:58 -05:00
Timothy Jaeryang Baek 776d4773a9 refac: note & chat sticky attachment 2025-09-25 13:21:47 -05:00
Timothy Jaeryang Baek eeed0df743 refac 2025-09-25 13:17:58 -05:00
Timothy Jaeryang Baek b1006a2b57 refac 2025-09-25 13:16:52 -05:00
Timothy Jaeryang BaekandJacob Leksan 53cd660de7 refac/fix: valves array type handling
Co-Authored-By: Jacob Leksan <63938553+jmleksan@users.noreply.github.com>
2025-09-25 13:13:03 -05:00
Tim Jaeryang Baek a7061383e8 Merge pull request #17753 from rgaricano/dev-es_ES
UPD: i18n _ es-ES Translation v0.6.31
2025-09-25 13:01:10 -05:00
Tim Jaeryang Baek 97529e8ae4 Merge pull request #17749 from ShirasawaSama/patch-2
i18n: improve zh-TW translation
2025-09-25 13:00:58 -05:00
Tim Jaeryang Baek c579e1fe44 Merge pull request #17748 from ShirasawaSama/i18n/improve-chinese-translation
i18n: improve zh-CN translation
2025-09-25 13:00:49 -05:00
Classic298 17080a1c00 REORDER BY PRIORITY 2025-09-25 19:41:54 +02:00
_00_ e6b3c91343 UPD: i18n - es-ES Translation v0.6.31
UPD: i18n - es-ES Translation v0.6.31

Update of new strings.
2025-09-25 19:39:12 +02:00
Classic298 deb64fa6a6 Update CHANGELOG.md 2025-09-25 19:37:54 +02:00
Classic298 9bc8784e3e Changelog dev (#20)
* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md
2025-09-25 19:37:18 +02:00
Shirasawa d6f93753c1 i18n: improve zh-TW translation 2025-09-26 01:23:33 +08:00
Shirasawa 0f12d512c4 i18n: improve zh-CN translation 2025-09-26 01:14:09 +08:00
Tim Jaeryang Baek b76d234f97 Merge pull request #17607 from sihyeonn/perf/sh-notes
perf: optimize notes query and separate access control logic
2025-09-25 12:13:42 -05:00
Tim Jaeryang Baek 4aa5574ba7 Merge pull request #17746 from andrewbbaek/dev
chore: add IDs to elements for better maintainability
2025-09-25 12:07:35 -05:00
Andrew Baek ead1c1881c Merge branch 'dev' of https://github.com/andrewbbaek/open-webui into dev 2025-09-26 01:57:38 +09:00
Andrew Baek 3df5826d93 moved sidebar-folder-button from class to id 2025-09-26 01:56:45 +09:00
Tim Jaeryang Baek cd417ca0ba Merge pull request #17744 from Classic298/fix-rag-full-context
Fix: Prevent RAG queries when all files are in full context
2025-09-25 11:55:41 -05:00
Timothy Jaeryang Baek 4f8f8e0e0b chore: dep bump 2025-09-25 11:51:04 -05:00
Timothy Jaeryang Baek 05d7d1e562 chore: format 2025-09-25 11:43:10 -05:00
Andrew Baek 08f4a2dff8 Merge branch 'open-webui:dev' into dev 2025-09-26 01:31:20 +09:00
Timothy Jaeryang Baek ff44fa242d refac 2025-09-25 11:28:33 -05:00
Andrew Baek e4f27ab75f Merge branch 'open-webui:dev' into dev 2025-09-26 01:25:50 +09:00
Andrew Baek 86f024b9d7 added id and class 2025-09-26 01:23:58 +09:00
Timothy Jaeryang Baek 3c7d01163d refac 2025-09-25 11:02:49 -05:00
google-labs-jules[bot] 4a7e1b93e5 Fix: Prevent RAG queries when all files are in full context
This commit fixes an issue where Retrieval-Augmented Generation (RAG)
queries were still being generated even when all attached files were set
to 'full context' mode. This was inefficient as the full content of the
files was already available to the model.

The `chat_completion_files_handler` in `backend/open_webui/utils/middleware.py`
has been updated to:
- Check if all attached files have the `context: 'full'` property.
- Skip the `generate_queries` step if all files are in full context mode.
- Pass a `full_context=True` flag to the `get_sources_from_items`
  function to ensure it fetches the entire document content instead of
  performing a vector search.

This change ensures that RAG queries are only generated when necessary,
improving the efficiency of the system.
2025-09-25 15:54:58 +00:00
Timothy Jaeryang Baek 98883a68f2 refac 2025-09-25 02:18:10 -05:00
Tim Jaeryang Baek b8a51de977 Merge pull request #17721 from Classic298/fix-image-download-filename
fix: Use generic filename for downloaded images
2025-09-25 02:02:19 -05:00
Timothy Jaeryang Baek cd7bd0aa20 refac 2025-09-25 02:00:02 -05:00
Timothy Jaeryang Baek 4d16cf6bf3 refac 2025-09-25 01:59:18 -05:00
Timothy Jaeryang Baek 422d38fd11 refac 2025-09-25 01:56:09 -05:00
Timothy Jaeryang Baek 879abd7fee refac 2025-09-25 01:53:10 -05:00
Timothy Jaeryang Baek 77e971dd9f feat: oauth2.1 mcp integration 2025-09-25 01:49:16 -05:00
Classic298 4f22a1c5e4 Update translation.json 2025-09-25 07:55:17 +02:00
Classic298 e201dc995c Update ImagePreview.svelte 2025-09-25 07:54:58 +02:00
Timothy Jaeryang Baek 972be4eda5 enh: oauth2.1 dynamic client registration 2025-09-25 00:28:13 -05:00
Timothy Jaeryang Baek 34d16791a8 refac 2025-09-24 23:27:45 -05:00
Tim Jaeryang Baek d3b09c6a02 Merge pull request #17223 from itk-dev/feature/session-in-redis
feat: Added support for redis as session storage
2025-09-24 23:23:56 -05:00
Timothy Jaeryang Baek 27d61307cd refac: tools valves 2025-09-24 21:12:25 -05:00
Timothy Jaeryang Baek 685cca5bd5 chore: dep bump 2025-09-24 18:58:17 -05:00
Timothy Jaeryang Baek bd02a456ab chore: langchain dep bump 2025-09-24 18:55:33 -05:00
Timothy Jaeryang Baek e5daec32ba refac 2025-09-24 17:16:07 -05:00
Timothy Jaeryang Baek b03529b006 refac 2025-09-24 17:05:37 -05:00
Timothy Jaeryang Baek 612a52d7bb refac 2025-09-24 16:34:41 -05:00
Timothy Jaeryang Baek aa6f63a335 enh: search modal actions 2025-09-24 16:29:02 -05:00
Timothy Jaeryang Baek 9f0010e234 enh: channel model @ image support 2025-09-24 16:08:36 -05:00
Timothy Jaeryang Baek e7061b70e9 refac 2025-09-24 16:01:31 -05:00
Timothy Jaeryang Baek fe65fe0b97 refac: ollama cloud web search count support 2025-09-24 15:58:56 -05:00
Tim Jaeryang Baek 3d2f7e3143 Merge pull request #17715 from ShirasawaSama/revert-17498-patch-19
Revert "feat: Dynamically load CodeEditor.svelte to improve first-screen loading speed (-1MB)"
2025-09-24 15:40:08 -05:00
Timothy Jaeryang Baek 1df5f1f9f6 revert: code editor dynamic import 2025-09-24 15:39:55 -05:00
Timothy Jaeryang Baek 05c46008da refac 2025-09-24 15:38:09 -05:00
Tim Jaeryang Baek 1493a5fd32 Merge pull request #17722 from Classic298/bugfix/hidden-models-in-notes
Fix: Hide hidden models in notes section
2025-09-24 15:37:52 -05:00
Timothy Jaeryang Baek 4b6d34438b refac 2025-09-24 15:20:31 -05:00
Timothy Jaeryang Baek e06489d92b enh: search_ollama_cloud 2025-09-24 15:19:05 -05:00
google-labs-jules[bot] c32b407f8a Fix(notes): Hide hidden models in notes section
This commit fixes a bug where hidden models were still visible and selectable in the notes section. The following changes were made:

- The model dropdown in the chat section of the note editor is now filtered to exclude hidden models.
- The default model selection logic in the note editor is now filtered to exclude hidden models.
2025-09-24 19:43:07 +00:00
google-labs-jules[bot] ab30b105e2 fix(image): Use generic filename for downloaded images
Previously, when downloading a generated image, the filename was set to the AI's response text. This was not ideal as the response text could be long and contain characters that are not suitable for filenames.

This commit changes the behavior to use a generic, translatable filename for downloaded images. The new filename is 'ai_generated_image' (or its translation), which is more user-friendly and consistent.
2025-09-24 19:39:08 +00:00
Timothy Jaeryang Baek f27ab14bd8 refac 2025-09-24 12:37:19 -05:00
Timothy Jaeryang Baek 23a51f2d01 refac: robust file upload failed handling 2025-09-24 12:17:01 -05:00
Timothy Jaeryang Baek 05732de898 refac 2025-09-24 11:36:17 -05:00
Tim Jaeryang Baek 316f1d6ff0 Merge pull request #17717 from Classic298/patch-1
add youtube
2025-09-24 12:34:37 -04:00
Classic298 b4d8287946 add youtube 2025-09-24 18:31:48 +02:00
Timothy Jaeryang Baek 0dee15ba97 refac/enh: include foldered chats in ref chat input menu 2025-09-24 11:27:19 -05:00
Timothy Jaeryang Baek 0e3b6b3b8f refac 2025-09-24 11:22:48 -05:00
Timothy Jaeryang Baek 32086b6ecd refac 2025-09-24 11:20:39 -05:00
Timothy Jaeryang Baek 2a95cbcef7 enh: attach webpage input menu 2025-09-24 11:11:26 -05:00
Shirasawa 6614c73b6c Revert "feat: Dynamically load CodeEditor.svelte to improve first-screen loading speed (-1MB)" 2025-09-25 00:10:01 +08:00
Timothy Jaeryang Baek 2904a78222 refac 2025-09-24 10:14:38 -05:00
Timothy Jaeryang Baek ac879513e5 enh: channel read/write perm 2025-09-24 10:09:59 -05:00
Timothy Jaeryang Baek 6d69ea3ac7 refac 2025-09-24 09:46:19 -05:00
Timothy Jaeryang Baek f096e99059 refac: rag context handling 2025-09-24 09:38:14 -05:00
Timothy Jaeryang Baek 72d19e44dc refac 2025-09-24 09:37:37 -05:00
Timothy Jaeryang Baek b25c7da154 refac: valves 2025-09-24 09:21:53 -05:00
Timothy Jaeryang Baek b8086c5edf refac: folder delete logic 2025-09-24 09:04:54 -05:00
Timothy Jaeryang Baek 91a9f32904 fix: folder expanded update 2025-09-24 08:00:58 -05:00
Timothy Jaeryang Baek 199f371f58 refac 2025-09-24 07:17:41 -05:00
Tim Jaeryang Baek 6cdadc73f2 Merge pull request #17705 from aleixdorca/dev
i18n: Update the Catalan translation file
2025-09-24 07:57:16 -04:00
Timothy Jaeryang Baek c5a967e05f refac 2025-09-24 06:56:50 -05:00
Timothy Jaeryang Baek 651f385ba5 fix: oauth refresh server metadata 2025-09-24 06:56:24 -05:00
Timothy Jaeryang Baek f25a144e09 refac 2025-09-24 06:52:44 -05:00
Timothy Jaeryang Baek 5eaee44daa refac 2025-09-24 06:49:39 -05:00
Aleix Dorca 9c76fb267a Update catalan translation.json 2025-09-24 11:21:27 +02:00
Aleix Dorca cf49b823b0 Merge branch 'open-webui:dev' into dev 2025-09-24 10:59:32 +02:00
Andrew Baek 5be58f2601 Merge branch 'open-webui:dev' into dev 2025-09-24 17:10:48 +09:00
Timothy Jaeryang Baek 1c64269387 refac 2025-09-23 16:51:31 -05:00
Timothy Jaeryang Baek f33d6eabdf chore: bump azure identity 2025-09-23 16:48:31 -05:00
Tim Jaeryang Baek 7ae42f5ae3 Merge pull request #17660 from HikkaTown/i18n/russian-translation-updated
i18n: Complete Russian translation for Open WebUI
2025-09-23 17:30:08 -04:00
Tim Jaeryang Baek 9710060c27 Merge pull request #17645 from ShirasawaSama/patch-28
feat: only retain one language supports for highlight.js to reduce bundle size (-1.58MB)
2025-09-23 16:28:05 -04:00
Timothy Jaeryang Baek 06e5c00f24 refac 2025-09-23 03:52:43 -04:00
Timothy Jaeryang Baek f7b0fdde2a refac 2025-09-23 03:42:25 -04:00
Timothy Jaeryang Baek 7aa3903b6b refac 2025-09-23 03:36:11 -04:00
Timothy Jaeryang Baek 6bf4ba523d refac 2025-09-23 03:35:00 -04:00
Timothy Jaeryang Baek c42c0436f2 refac 2025-09-23 03:33:24 -04:00
Timothy Jaeryang Baek 61f20acf61 refac 2025-09-23 03:32:25 -04:00
Timothy Jaeryang Baek c55afc4255 refac 2025-09-23 03:19:36 -04:00
Timothy Jaeryang Baek f1bbf3a91e refac 2025-09-23 03:05:38 -04:00
Timothy Jaeryang Baek de7f7b3d85 refac 2025-09-23 02:40:59 -04:00
Timothy Jaeryang Baek 777e81f7a8 feat: experimental mcp support 2025-09-23 02:03:26 -04:00
Timothy Jaeryang Baek aeb5288a3c refac 2025-09-22 23:28:19 -04:00
Timothy Jaeryang Baek 2771c26729 refac 2025-09-22 20:03:44 -04:00
Timothy Jaeryang Baek e4e97e727e enh: note drag handle 2025-09-22 20:02:37 -04:00
Timothy Jaeryang Baek 1afa366dcb refac 2025-09-22 14:46:47 -04:00
Zaytsev Gennadiy 7c1b6f9157 i18n: Complete Russian translation for Open WebUI 2025-09-22 21:35:57 +03:00
Tim Jaeryang Baek 651dea3864 Merge pull request #17640 from ShirasawaSama/patch-27
feat: do not initiate requests when the SearchModal is not open to speed up page loading
2025-09-22 13:27:56 -04:00
Tim Jaeryang Baek 96e71cf181 Merge pull request #17647 from BoFFire/patch-2
i18n: updating kabyle translation
2025-09-22 13:22:20 -04:00
Tim Jaeryang Baek ac5cc4a050 Merge pull request #17657 from helax28/dev
Updated the french translations
2025-09-22 13:22:00 -04:00
ButterflyOfFire 48dc333a3d Update translation.json 2025-09-22 10:28:27 +01:00
ButterflyOfFire 628b753a35 Update translation.json 2025-09-22 10:07:54 +01:00
ButterflyOfFire c28fbcb232 i18n: updating kabyle translation
Translation progress : 90%
2025-09-22 09:51:55 +01:00
Shirasawa 2edfde1990 feat: only retain one language supports for highlight.js to reduce bundle size 2025-09-22 16:00:46 +08:00
Shirasawa 01fcd057b7 feat: do not initiate requests when the SearchModal is not open to speed up page loading 2025-09-22 12:01:29 +08:00
Tim Jaeryang Baek fb3b736a35 Merge pull request #17635 from open-webui/main
dev
2025-09-21 19:15:06 -04:00
athiththan 064d9d6141 Updated the french translations 2025-09-21 21:45:31 +02:00
Timothy Jaeryang Baek fd7385c392 refac 2025-09-21 03:12:24 -04:00
Timothy Jaeryang Baek 834824ce7b refac 2025-09-21 03:06:54 -04:00
Timothy Jaeryang Baek 466d5bb696 refac: add separate Client IDs for OneDrive 2025-09-21 01:40:14 -04:00
Timothy Jaeryang Baek 6e4a2f18e1 refac 2025-09-21 00:14:43 -04:00
Tim Jaeryang Baek 5e6e25f8c8 Merge pull request #17615 from Cyp9715/dev
i18n: Updated korean translation.
2025-09-20 23:07:01 -05:00
Tim Jaeryang Baek 428f9a22c4 Merge pull request #17616 from Kylapaallikko/dev
i18n: Update fi-FI translation.json
2025-09-20 16:10:06 -05:00
Tim Jaeryang Baek 92d79150bd Merge pull request #17618 from rgaricano/dev-es_ES
UPD: i18n es-ES Translation v.0.6.30
2025-09-20 16:04:13 -05:00
Timothy Jaeryang Baek 6bc5d331a2 doc: readme 2025-09-20 12:45:10 -05:00
Andrew Baek a7ae280a4f Update MessageInput.svelte 2025-09-21 01:01:29 +09:00
Andrew Baek 2a1c1e34dc Merge branch 'dev' of https://github.com/andrewbbaek/open-webui into dev 2025-09-21 00:52:28 +09:00
_00_ 4b4fda46cf Update translation.json 2025-09-20 14:46:18 +02:00
_00_ 4b029048db UPD: i18n es-ES Translation v.0.6.30
### Update i18n es-ES Translation v.0.6.30

Added new strings
2025-09-20 14:39:23 +02:00
Kylapaallikko 34feaeeb96 Update fi-FI translation.json
Added missing translations and fixed typos
2025-09-20 14:53:04 +03:00
ByoungGyu Lee a6c53303dd i18n(ko-KR): Updated korean translation. 2025-09-20 20:44:51 +09:00
ByoungGyu Lee 204484d8e7 i18n(ko-KR): Updated korean translations. 2025-09-20 20:40:50 +09:00
ByoungGyu Lee 41e4f33c1d Merge remote-tracking branch 'remote/dev' into dev 2025-09-20 19:43:01 +09:00
Timothy Jaeryang Baek 94770f6059 refac 2025-09-20 01:33:36 -05:00
Timothy Jaeryang Baek c4fed37af9 refac 2025-09-20 01:25:44 -05:00
Timothy Jaeryang Baek b0279c5090 refac 2025-09-20 01:25:00 -05:00
Timothy Jaeryang Baek 73f8237d43 refac 2025-09-20 01:19:23 -05:00
Timothy Jaeryang Baek 4ad7430986 refac 2025-09-20 01:15:27 -05:00
Timothy Jaeryang Baek 27a91cc80a refac: styling 2025-09-20 00:56:25 -05:00
Tim Jaeryang Baek 820321ad55 Merge pull request #17608 from sihyeonn/fix/sh-prevent-memory-leaks-on-ollama
fix: prevent memory leaks in file handling and HTTP connections
2025-09-19 22:22:08 -05:00
Sihyeon Jang 7042318c34 fix: prevent memory leaks in file handling and HTTP connections
- Fix file handle memory leak in download_file_stream by properly closing and reopening files
- Add requests.Session context manager for proper HTTP connection cleanup
- Remove unnecessary file.seek(0) after file reopening
- Add timeout to prevent hanging connections

This prevents memory accumulation during large file downloads and ensures
proper resource cleanup in all scenarios.

Signed-off-by: Sihyeon Jang <sihyeon.jang@navercorp.com>
2025-09-20 06:37:10 +09:00
Sihyeon Jang 6ae6cc9741 perf: optimize get_notes_by_user_id to reduce database queries
- Replace inefficient memory-based filtering with database-level filtering
- Add proper access control conditions to SQL query
- Reduce memory usage by filtering at database level instead of loading all notes
- Maintain access control validation with post-filtering for complex cases

This change significantly improves performance for users with many notes
by reducing the number of database queries and memory usage.

Signed-off-by: Sihyeon Jang <sihyeon.jang@navercorp.com>
2025-09-20 06:33:24 +09:00
Tim Jaeryang Baek dbd7b90123 Merge pull request #17593 from ShirasawaSama/patch-25
refactor: refactor editor's collaboration to reduce package size (-390KB) and minimize compile errors
2025-09-19 13:16:28 -05:00
Tim Jaeryang Baek afad34fd09 Merge pull request #17600 from aindriu80/update-ga-strings-19-sept
i18n: Updated Irish (ga) translation
2025-09-19 13:13:28 -05:00
Aindriú Mac Giolla Eoin 167f4ba8ad i18n(ga): Updated Irish translations 2025-09-19 16:02:56 +01:00
Shirasawa 7f6b260c35 feat: refactor editor's collaboration to reduce package size and minimize errors 2025-09-19 17:28:16 +08:00
Timothy Jaeryang Baek 62517f01e5 refac: built-in chart js 2025-09-19 02:58:29 -05:00
Timothy Jaeryang Baek 293531549c refac: alpine injection 2025-09-19 02:19:48 -05:00
Timothy Jaeryang Baek 60db9ec8ef refac: built-in tools ui component support 2025-09-19 01:38:44 -05:00
Timothy Jaeryang Baek 7528f24b61 refac: styling 2025-09-19 00:15:03 -05:00
Timothy Jaeryang Baek e4c4ba0979 fix: oauth token 2025-09-19 00:10:48 -05:00
Timothy Jaeryang Baek a89ffccd7e refac 2025-09-19 00:10:39 -05:00
Timothy Jaeryang Baek 7be5b7f50f refac 2025-09-18 22:03:14 -05:00
Timothy Jaeryang Baek a5d8882bba refac 2025-09-18 21:25:26 -05:00
Timothy Jaeryang Baek 07c5b25bc8 feat: tool ui element support 2025-09-18 20:55:23 -05:00
Cyp 10c9a7d75e Merge branch 'dev' of https://github.com/open-webui/open-webui into dev 2025-09-19 08:59:21 +09:00
Cyp 78e7f14360 Merge branch 'main' of https://github.com/open-webui/open-webui into dev 2025-09-19 08:59:11 +09:00
Timothy Jaeryang Baek 700894a13d refac: channel modal 2025-09-18 18:02:35 -05:00
Timothy Jaeryang Baek 1c78594379 refac 2025-09-18 17:53:11 -05:00
Timothy Jaeryang Baek 48cd72fab0 refac 2025-09-18 17:51:00 -05:00
Timothy Jaeryang Baek 04251b7bff refac 2025-09-18 17:50:39 -05:00
Tim Jaeryang Baek e150d8763b Merge pull request #17551 from ShirasawaSama/patch-20
i18n: improve Chinese translation
2025-09-18 17:49:09 -05:00
Tim Jaeryang Baek 0159a33306 Merge pull request #17542 from ShirasawaSama/patch-23
feat: do not initiate requests when the ChangelogModal is not open to speed up page loading
2025-09-18 17:48:51 -05:00
Timothy Jaeryang Baek c07086401e refac 2025-09-18 17:47:32 -05:00
Tim Jaeryang Baek 2de11f8daf Merge pull request #17577 from joaoback/patch-9
Update translation.json (pt-BR)
2025-09-18 17:09:03 -05:00
Tim Jaeryang Baek 0e75e8529e Merge pull request #17562 from rgaricano/dev-HybridSearch-bm25_slider
FIX: Inconsistency in the description of BM25 - Update Documents.svelte
2025-09-18 17:07:43 -05:00
Tim Jaeryang Baek 40da51d37f Merge pull request #17546 from Cyp9715/dev
i18n: improve Korean translation
2025-09-18 17:06:15 -05:00
joaoback bfc8f74477 Update translation.json (pt-BR)
translation of newly added items.
2025-09-18 13:53:41 -03:00
Timothy Jaeryang Baek d4f21c7e84 refac 2025-09-18 10:43:59 -05:00
Timothy Jaeryang Baek b9e200f104 fix: isdangerous dep 2025-09-18 09:39:40 -05:00
_00_ da7610f6cb FIX: Inconsistency in the description of BM25 - Update Documents.svelte
FIX: Inconsistency in the description of BM25

Fix issue: https://github.com/open-webui/open-webui/discussions/17553
2025-09-18 13:18:07 +02:00
Shirasawa f188d570f7 feat: improve Chinese translation 2025-09-18 07:48:19 +00:00
bglee 52e8c6c998 Update on the Korean translation 2025-09-18 15:09:43 +09:00
Shirasawa 7735a04783 feat: do not initiate requests when the ChangelogModal is not open 2025-09-18 11:50:00 +08:00
Tim Jaeryang Baek 8920bf2377 Merge pull request #17528 from open-webui/dev
0.6.30
2025-09-17 12:25:26 -05:00
Timothy Jaeryang Baek b55a38ee97 doc: changelog 2025-09-17 12:22:19 -05:00
Timothy Jaeryang Baek 6c39dc764d chore: bump 2025-09-17 12:19:18 -05:00
Timothy Jaeryang Baek cde5879987 fix: onedrive 2025-09-17 12:18:23 -05:00
Tim Jaeryang Baek dddd1e44f3 Merge pull request #17420 from open-webui/dev
0.6.29
2025-09-17 11:32:59 -05:00
Timothy Jaeryang Baek 60f62c2f59 refac 2025-09-17 11:28:04 -05:00
Timothy Jaeryang Baek a7267b5914 doc: changelog wording 2025-09-17 11:22:27 -05:00
Timothy Jaeryang Baek c01255570b chore: format 2025-09-17 11:11:46 -05:00
Timothy Jaeryang BaekandSelene Blok caf0a1fbb6 feat: Allow Azure OpenAI to authenticate using DefaultAzureCredential
Co-Authored-By: Selene Blok <20491756+selenecodes@users.noreply.github.com>
2025-09-17 11:04:47 -05:00
Timothy Jaeryang Baek 72cd3a54f7 refac 2025-09-17 10:48:56 -05:00
Timothy Jaeryang Baek c96252f7fe refac/fix: WHISPER_LANGUAGE 2025-09-17 10:46:11 -05:00
Tim Jaeryang Baek 1d83bc15de Merge pull request #17520 from Classic298/changelog-done
chore: Update CHANGELOG.md
2025-09-17 10:33:08 -05:00
Classic298 b8159fc43e Update CHANGELOG.md 2025-09-17 17:24:50 +02:00
Tim Jaeryang Baek 09502c6664 Merge pull request #17490 from Classic298/changelog-done
chore: Changelog 0.6.29
2025-09-17 10:23:05 -05:00
Timothy Jaeryang Baek db1730f47b refac 2025-09-17 10:18:26 -05:00
Timothy Jaeryang Baek 70ab38194f refac: map marker 2025-09-17 09:26:31 -05:00
Tim Jaeryang Baek 867fcc4d95 Merge pull request #17503 from ShirasawaSama/patch-13
feat: Dynamically load leaflet to improve first-screen loading speed (-454KB)
2025-09-17 09:21:09 -05:00
Tim Jaeryang Baek d88c6f81ee Merge pull request #17502 from ShirasawaSama/patch-4
feat: Dynamically load jspdf and html2canvas-pro to improve first-screen loading speed (-980KB)
2025-09-17 09:20:48 -05:00
Timothy Jaeryang Baek 0cfc8f5256 refac 2025-09-17 09:19:56 -05:00
Classic298 59f9a4cdf5 Update CHANGELOG.md 2025-09-17 14:22:13 +02:00
Shirasawa 382299d549 feat: Dynamically load leaflet to improve first-screen loading speed 2025-09-17 08:20:56 +00:00
Shirasawa df433f852f feat: Dynamically load jspdf and html2canvas-pro to improve first-screen loading speed 2025-09-17 07:54:51 +00:00
Tim Jaeryang Baek 980019bb50 Merge pull request #17499 from ShirasawaSama/patch-5
feat: Dynamically load @huggingface/transformers to improve first-screen loading speed (-1.9MB)
2025-09-17 02:23:58 -05:00
Timothy Jaeryang Baek 6549fc839f refac/enh: channel input paste behaviour 2025-09-17 02:21:45 -05:00
Tim Jaeryang Baek 365756c9a2 Merge pull request #17498 from ShirasawaSama/patch-19
feat: Dynamically load CodeEditor.svelte to improve first-screen loading speed (-1MB)
2025-09-17 02:20:04 -05:00
Shirasawa 03d5d00d28 feat: Dynamically load @huggingface/transformers to improve first-screen loading speed 2025-09-17 07:19:57 +00:00
Tim Jaeryang Baek 051765e77a Merge pull request #17497 from ShirasawaSama/patch-20
i18n: improve Chinese translation
2025-09-17 02:19:38 -05:00
Shirasawa 001775d6c3 feat: Dynamically load CodeEditor to improve first-screen loading speed 2025-09-17 07:08:39 +00:00
Shirasawa 2ceb769057 i18n: improve Chinese translation 2025-09-17 06:56:34 +00:00
Tim Jaeryang Baek 9f9f1a1517 Merge pull request #17496 from ShirasawaSama/patch-16
feat: Dynamically load katex to improve first-screen loading speed (-630KB)
2025-09-17 01:36:55 -05:00
Timothy Jaeryang Baek 71c672c90f chore: format 2025-09-17 01:35:07 -05:00
Timothy Jaeryang Baek 1d0881e283 refac 2025-09-17 01:28:04 -05:00
Classic298 fd2b9cfd61 Update CHANGELOG.md 2025-09-17 08:26:40 +02:00
Timothy Jaeryang Baek c1f37d9aed refac 2025-09-17 01:22:15 -05:00
Timothy Jaeryang Baek 4fe97d8794 feat: channel/thread @ model 2025-09-17 00:49:44 -05:00
Timothy Jaeryang Baek 9738ddfd99 refac 2025-09-16 23:19:21 -05:00
Shirasawa 9b3d71f0d2 feat: Dynamically load katex to improve first-screen loading speed 2025-09-17 04:18:16 +00:00
Timothy Jaeryang Baek 779db74d7e refac 2025-09-16 23:00:00 -05:00
Timothy Jaeryang Baek 1077b2ac8b fix: send notification 2025-09-16 22:53:54 -05:00
Timothy Jaeryang Baek c3ea4feca5 refac 2025-09-16 22:30:32 -05:00
Timothy Jaeryang Baek 9321a6ff60 refac 2025-09-16 22:23:45 -05:00
Timothy Jaeryang Baek 26764a0b92 refac: styling 2025-09-16 22:20:50 -05:00
Timothy Jaeryang Baek aef1e06f0b refac: user status 2025-09-16 22:17:35 -05:00
Timothy Jaeryang Baek b27243df81 refac 2025-09-16 21:47:08 -05:00
Timothy Jaeryang Baek bbd1d2b58c enh: channel suggestions 2025-09-16 21:41:47 -05:00
Timothy Jaeryang Baek 99bba12de2 refac: valves filter out external tools 2025-09-16 18:22:00 -05:00
Timothy Jaeryang Baek f0856bcb46 refac 2025-09-16 16:15:52 -05:00
Timothy Jaeryang Baek b34b200682 refac 2025-09-16 16:04:44 -05:00
Timothy Jaeryang Baek a7fd76df41 refac 2025-09-16 15:59:02 -05:00
Timothy Jaeryang Baek c870b2ea29 refac: styling 2025-09-16 15:57:20 -05:00
Timothy Jaeryang Baek 71f99228ef refac 2025-09-16 15:49:43 -05:00
Timothy Jaeryang Baek f0bd74b75b refac 2025-09-16 15:47:43 -05:00
Classic298 69646eb1de chore: Changelog 0.6.29 (#19)
* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md
2025-09-16 22:34:48 +02:00
Timothy Jaeryang Baek 8edfd29102 refac 2025-09-16 15:26:08 -05:00
Timothy Jaeryang Baek d8ad384b06 refac 2025-09-16 15:16:48 -05:00
Timothy Jaeryang Baek 10e39956ef refac 2025-09-16 15:12:51 -05:00
Timothy Jaeryang Baek 4a971eeaee refac 2025-09-16 15:12:01 -05:00
Timothy Jaeryang Baek 767acd1789 refac 2025-09-16 15:09:57 -05:00
Timothy Jaeryang Baek a3dd678bbd refac: styling 2025-09-16 15:03:26 -05:00
Timothy Jaeryang Baek 8bf0b40fdd refac: styling 2025-09-16 14:57:56 -05:00
Timothy Jaeryang Baek 3e58413268 refac 2025-09-16 13:48:01 -05:00
Timothy Jaeryang Baek b1e5bc8e49 refac 2025-09-16 13:42:37 -05:00
Timothy Jaeryang Baek 2b1ee8b0dc refac 2025-09-16 13:40:11 -05:00
Timothy Jaeryang Baek 2a234829f5 enh: folder background image 2025-09-16 13:32:42 -05:00
Timothy Jaeryang Baek d46b7b8f1b refac 2025-09-16 13:11:53 -05:00
Timothy Jaeryang Baek 32015c392d refac: access control 2025-09-16 13:03:33 -05:00
Timothy Jaeryang Baek c8780a7f93 refac 2025-09-16 12:52:13 -05:00
Timothy Jaeryang Baek 0f04227c34 refac: direct external tools now require explicit toggle from input 2025-09-16 12:43:53 -05:00
Timothy Jaeryang Baek b14617a653 refac: otel metrics handle 500 2025-09-16 12:11:32 -05:00
Timothy Jaeryang Baek e66e0526ed refac/enh: function valves validation 2025-09-16 12:00:59 -05:00
Timothy Jaeryang Baek fed5615c19 refac: image max compression size 2025-09-16 11:32:40 -05:00
Timothy Jaeryang Baek f68d11ec52 refac 2025-09-16 11:17:32 -05:00
Timothy Jaeryang Baek 034163e9f9 chore: format 2025-09-16 11:16:08 -05:00
Timothy Jaeryang Baek 77358031f5 refac: placeholder 2025-09-16 11:13:15 -05:00
Timothy Jaeryang Baek e1e3009a30 refac: granular onedrive integration types 2025-09-16 10:54:13 -05:00
Timothy Jaeryang Baek 76d358bd6c refac 2025-09-16 10:40:15 -05:00
Timothy Jaeryang Baek c2f98a4cd2 refac: usage info 2025-09-16 10:28:25 -05:00
Tim Jaeryang Baek 83699cf3ef Merge pull request #17479 from ShirasawaSama/patch-8
feat: Dynamically load @azure/msal-browser to improve first-screen loading speed
2025-09-16 10:25:10 -05:00
Tim Jaeryang Baek 5ba0b52cf3 Merge pull request #17477 from ShirasawaSama/patch-13
feat: Dynamically load mermaid.js to improve first-screen loading speed
2025-09-16 10:24:35 -05:00
Tim Jaeryang Baek 40b6e6802f Merge pull request #17475 from ShirasawaSama/patch-20
i18n: improve Chinese (zh-CN, zh-TW) translation
2025-09-16 10:23:40 -05:00
Shirasawa a341b9a4ea feat: dynamically load @azure/msal-browser 2025-09-16 10:19:00 +00:00
Shirasawa db05e34736 feat: dynamically load mermaid.js 2025-09-16 09:43:03 +00:00
Shirasawa 072de1fb7e i18n: improve Chinese translation 2025-09-16 08:42:49 +00:00
Timothy Jaeryang Baek d041d58bb6 refac/enh: include pdf export in chat item 2025-09-15 18:39:07 -05:00
Timothy Jaeryang Baek 3ec1efb6e0 refac: folders 2025-09-15 18:33:57 -05:00
Timothy Jaeryang Baek 3f27d9ada1 refac: styling 2025-09-15 17:38:34 -05:00
Timothy Jaeryang Baek 19e3214997 refac: folder click behaviour 2025-09-15 17:21:10 -05:00
Timothy Jaeryang Baek 49c9b3199f refac 2025-09-15 17:21:02 -05:00
Timothy Jaeryang Baek c17f3bc82b refac: styling 2025-09-15 16:39:09 -05:00
Timothy Jaeryang Baek 8b8908fa93 refac 2025-09-15 16:35:53 -05:00
Timothy Jaeryang Baek 45da209f85 refac 2025-09-15 16:29:53 -05:00
Timothy Jaeryang Baek ad58e6b297 refac: styling 2025-09-15 16:28:37 -05:00
Timothy Jaeryang Baek 4f72d5453f refac: styling 2025-09-15 16:25:13 -05:00
Timothy Jaeryang Baek 5eb26fe7ab refac 2025-09-15 16:25:04 -05:00
Timothy Jaeryang Baek e1386fe80b refac: message input optimization 2025-09-15 16:03:34 -05:00
Timothy Jaeryang Baek ac375fe3d2 refac: styling 2025-09-15 15:54:35 -05:00
Timothy Jaeryang Baek 218920f0c3 refac: styling 2025-09-15 15:53:11 -05:00
Timothy Jaeryang Baek 3d5699ef22 refac: styling 2025-09-15 15:50:28 -05:00
Timothy Jaeryang Baek 621e7679c4 refac/enh: notification toast click behaviour 2025-09-15 15:32:43 -05:00
Timothy Jaeryang Baek c04e1787c5 refac: styling 2025-09-15 15:32:32 -05:00
Timothy Jaeryang Baek e5bb366719 refac: styling 2025-09-15 15:12:01 -05:00
Timothy Jaeryang Baek c0f3352b71 refac 2025-09-15 15:05:56 -05:00
Timothy Jaeryang Baek 9701d127bf refac 2025-09-15 15:02:47 -05:00
Timothy Jaeryang Baek 0e5320c39e refac/fix: codeblock 2025-09-15 15:02:19 -05:00
Timothy Jaeryang Baek c744ae0d63 refac: styling 2025-09-15 14:55:05 -05:00
Timothy Jaeryang Baek 596be451ec refac: styling 2025-09-15 14:28:16 -05:00
Timothy Jaeryang Baek 5434172e88 refac 2025-09-15 14:13:14 -05:00
Timothy Jaeryang Baek cf77a4276e refac: styling 2025-09-15 14:11:44 -05:00
Timothy Jaeryang Baek adaa467f75 refac: styling 2025-09-15 14:08:07 -05:00
Timothy Jaeryang Baek 879778361d refac 2025-09-15 13:43:05 -05:00
Timothy Jaeryang Baek fb02ec52a5 refac: styling 2025-09-15 13:36:51 -05:00
Timothy Jaeryang Baek 2ca34217e6 refac: styling 2025-09-15 13:34:09 -05:00
Timothy Jaeryang Baek 9e50026107 refac 2025-09-15 13:28:29 -05:00
Timothy Jaeryang Baek 67549dcadd refac 2025-09-15 13:25:36 -05:00
Timothy Jaeryang Baek 308c277fd3 refac: styling 2025-09-15 13:22:41 -05:00
Timothy Jaeryang Baek 24971ffce0 refac 2025-09-15 12:10:45 -05:00
Timothy Jaeryang Baek b6a2ca877a refac 2025-09-15 12:07:53 -05:00
Timothy Jaeryang Baek e4c864de7e fix: connection url edit 2025-09-15 12:01:46 -05:00
Tim Jaeryang Baek a35469c669 Merge pull request #17418 from rgaricano/date_format_i18n
UPD: i18n es-ES Translation - dev_v0.6.29
2025-09-15 12:47:43 -04:00
Timothy Jaeryang Baek a51f0c30ec refac/fix: knowledge permission 2025-09-15 11:40:31 -05:00
Timothy Jaeryang Baek d5824b1b49 refac: prompt template variable made not required by default 2025-09-15 11:18:31 -05:00
Andrew Baek b0720ec2aa Merge branch 'dev' of https://github.com/andrewbbaek/open-webui into dev 2025-09-16 01:07:15 +09:00
Tim Jaeryang Baek 5afa42b0d9 Merge pull request #17441 from Kylapaallikko/dev
i18n: Update fi-FI translation
2025-09-15 12:06:17 -04:00
Timothy Jaeryang Baek 48ed800d91 refac 2025-09-15 11:05:26 -05:00
Tim Jaeryang Baek dd6f83e9c5 Merge pull request #17423 from aleprj/fix-filehandler
Fix file_handler filters
2025-09-15 11:35:39 -04:00
Tim Jaeryang Baek 29f9d1d0c2 Merge pull request #17450 from ShirasawaSama/patch-20
i18n: update translations in zh-CN locale
2025-09-15 11:08:35 -04:00
Shirasawa 530460d711 i18n: update translations in zh-CN locale 2025-09-15 06:37:12 +00:00
Timothy Jaeryang Baek 3e65109900 refac 2025-09-14 19:00:49 -04:00
Timothy Jaeryang Baek db0379030e refac 2025-09-14 18:59:09 -04:00
Timothy Jaeryang Baek 098f34f400 refac/enh: mention token rendering 2025-09-14 18:49:01 -04:00
Timothy Jaeryang Baek 22e11760a1 refac 2025-09-14 18:08:31 -04:00
Timothy Jaeryang Baek cae7ad8c70 refac: styling 2025-09-14 17:45:58 -04:00
Kylapaallikko 4e21e4ba0e Update fi-FI translation.json
Added missing translations
2025-09-14 17:40:55 +03:00
Timothy Jaeryang Baek e701c65db4 refac: styling 2025-09-14 10:50:50 +02:00
Timothy Jaeryang Baek e61e7434a0 refac 2025-09-14 10:46:49 +02:00
Timothy Jaeryang Baek 1ef8204359 refac 2025-09-14 10:45:52 +02:00
Timothy Jaeryang Baek 025eef754f refac 2025-09-14 10:41:52 +02:00
Timothy Jaeryang Baek b775826a27 refac: styling 2025-09-14 10:39:39 +02:00
Timothy Jaeryang Baek 58d7ca35e3 refac 2025-09-14 10:27:07 +02:00
Timothy Jaeryang Baek aa8ab349ed feat: ref chat 2025-09-14 10:26:46 +02:00
Timothy Jaeryang Baek c923461882 refac 2025-09-14 10:11:17 +02:00
Timothy Jaeryang Baek 8753c2ff51 refac 2025-09-14 10:09:52 +02:00
Timothy Jaeryang Baek cc788eb4f9 refac 2025-09-14 10:07:21 +02:00
Timothy Jaeryang Baek b53ddfbd19 refac 2025-09-14 10:06:02 +02:00
Timothy Jaeryang Baek c03ca7270e refac/feat: note/knowledge/chat select input menu 2025-09-14 09:54:06 +02:00
Timothy Jaeryang Baek eadec9e86e refac 2025-09-14 09:20:12 +02:00
Timothy Jaeryang Baek 3ed0a6d11f refac 2025-09-14 09:08:23 +02:00
Tim Jaeryang Baek caaaa07232 Merge pull request #17425 from joaoback/patch-8
Update translation.json (pt-BR)
2025-09-14 10:55:02 +04:00
Timothy Jaeryang Baek 8802c6e8db refac 2025-09-14 08:42:27 +02:00
joaoback a4ca9a981e Update translation.json (pt-BR)
added translations for newly added UI items
2025-09-13 20:57:01 -03:00
Alexandre Oliveira f9fd22d98e fix: setting file_handler in a filter would generate errors in messages with no files, because a "files: Null" in metadata would trigger an attempt to delete a non existent files object 2025-09-13 12:57:54 -03:00
Timothy Jaeryang Baek 031cf38655 refac 2025-09-13 19:04:07 +04:00
Timothy Jaeryang Baek f68d1ba394 refac 2025-09-13 18:41:21 +04:00
Timothy Jaeryang Baek 02f7c3258b refac 2025-09-13 18:39:32 +04:00
Timothy Jaeryang Baek 5c2db102d0 refac: styling 2025-09-13 03:09:27 +04:00
Timothy Jaeryang Baek 58a8cae3d7 refac 2025-09-13 03:00:40 +04:00
Timothy Jaeryang Baek ef56b14636 refac: styling 2025-09-13 02:58:10 +04:00
Timothy Jaeryang Baek cd740f436d refac 2025-09-13 02:45:15 +04:00
Timothy Jaeryang Baek d4c628de09 refac: styling 2025-09-13 02:42:10 +04:00
Timothy Jaeryang Baek 9208a84185 refac 2025-09-13 02:33:32 +04:00
Timothy Jaeryang Baek 49bb1255c5 refac: banner styling 2025-09-13 02:29:04 +04:00
Timothy Jaeryang Baek 7344bd3e66 refac: styling 2025-09-13 02:27:03 +04:00
_00_ 7f41bb6118 Update translation.json 2025-09-13 00:23:53 +02:00
Timothy Jaeryang Baek 74263c872c refac 2025-09-13 02:15:12 +04:00
Timothy Jaeryang Baek f3cd2ffb18 refac 2025-09-13 02:13:26 +04:00
Timothy Jaeryang Baek b623a9ea69 refac 2025-09-13 02:10:41 +04:00
Timothy Jaeryang Baek e097bbdf11 refac 2025-09-13 02:09:57 +04:00
_00_ b1cba685f4 UPD: i18n es-ES Translation - dev_v0.6.28
### UPD: i18n es-ES Translation - dev_v0.6.28

- Translated added strings.
2025-09-13 00:04:30 +02:00
Timothy Jaeryang Baek 721ea9cbd8 refac: styling 2025-09-13 01:58:09 +04:00
Timothy Jaeryang Baek 6a67a2217c refac 2025-09-13 01:52:27 +04:00
Timothy Jaeryang Baek d7f43bfc1a refac 2025-09-13 01:50:24 +04:00
Timothy Jaeryang Baek 384a53b339 refac: styling 2025-09-13 01:48:14 +04:00
Timothy Jaeryang Baek 9a55547827 refac/enh: model default filter/feature 2025-09-13 01:23:27 +04:00
Timothy Jaeryang Baek 3288b19795 refac: styling 2025-09-13 00:37:45 +04:00
Timothy Jaeryang Baek 56a8973fa0 refac 2025-09-13 00:30:00 +04:00
Timothy Jaeryang Baek 680adea4e7 refac: styling 2025-09-13 00:27:04 +04:00
Timothy Jaeryang Baek d6094c2881 refac 2025-09-13 00:24:42 +04:00
Timothy Jaeryang Baek f6b1b075e1 refac 2025-09-13 00:03:14 +04:00
Timothy Jaeryang Baek 210197fd43 refac/fix: web/youtube file attachment handling 2025-09-13 00:02:48 +04:00
Timothy Jaeryang Baek 2227f24bd6 refac/enh: date format i18n 2025-09-12 23:53:23 +04:00
Timothy Jaeryang Baek 3f137f9bbc chore: format 2025-09-12 23:45:23 +04:00
Tim Jaeryang Baek 9f6872a8e1 Merge pull request #17404 from ShirasawaSama/patch-13
i18n: improve zh-TW translation
2025-09-12 23:41:40 +04:00
Timothy Jaeryang Baek c55f557828 refac/enh: model suggestion tooltip 2025-09-12 23:36:40 +04:00
Timothy Jaeryang Baek 3e1b14f570 refac/enh: optional vectordb deps 2025-09-12 23:33:08 +04:00
Timothy Jaeryang Baek f51a0b181f refac: styling 2025-09-12 23:28:31 +04:00
Timothy Jaeryang Baek a4ce1ebf4f refac: styling 2025-09-12 23:22:54 +04:00
Timothy Jaeryang Baek 766e8bc022 refac 2025-09-12 23:04:59 +04:00
Timothy Jaeryang Baek 5df9cad671 refac 2025-09-12 22:00:33 +04:00
Timothy Jaeryang Baek 81cefd8541 chore: bump 2025-09-12 21:52:46 +04:00
Timothy Jaeryang Baek 06c1426e14 refac: channel input 2025-09-12 21:43:05 +04:00
Timothy Jaeryang Baek b2623c9799 refac 2025-09-12 21:00:15 +04:00
Timothy Jaeryang Baek 153afd832c refac: deprecate textarea input 2025-09-12 20:54:34 +04:00
Timothy Jaeryang Baek 19e18bc461 refac 2025-09-12 20:37:42 +04:00
Timothy Jaeryang Baek 06a6855f84 refac 2025-09-12 20:35:14 +04:00
Timothy Jaeryang Baek 6b69c4da0f refac/enh: commands ui 2025-09-12 20:31:57 +04:00
Timothy Jaeryang Baek d973db829f refac 2025-09-12 15:57:04 +04:00
Timothy Jaeryang Baek 4977e6d50f refac 2025-09-12 15:56:31 +04:00
Timothy Jaeryang Baek 136972ccf0 refac: styling 2025-09-12 15:54:42 +04:00
Timothy Jaeryang Baek 96b8aaf83f refac 2025-09-12 15:41:12 +04:00
Shirasawa 097b5e4c0b i18n: improve zh-TW translation 2025-09-12 19:35:19 +08:00
Timothy Jaeryang Baek ca853ca465 refac/enh: sort toggle filter by default 2025-09-12 15:06:11 +04:00
Timothy Jaeryang Baek a68342d5a8 refac: input menu 2025-09-12 15:05:37 +04:00
Andrew Baek f704d5b0b3 Merge branch 'dev' of https://github.com/andrewbbaek/open-webui into dev 2025-09-12 19:23:46 +09:00
Tim Jaeryang Baek fe7b8d9617 Merge pull request #17401 from ShirasawaSama/i18n/improve-chinese-translation
i18n: improve zh-CN translation
2025-09-12 13:50:41 +04:00
Timothy Jaeryang Baek 042191372a refac: styling 2025-09-12 13:49:53 +04:00
Timothy Jaeryang Baek fbf9b3f1bb refac: styling 2025-09-12 13:38:02 +04:00
Shirasawa cba2e44de4 i18n: improve zh-TW translation 2025-09-12 17:01:08 +08:00
Shirasawa ff8100dc6f feat: improve zh-CN translation 2025-09-12 16:54:24 +08:00
Tim Jaeryang Baek a156033d3c Merge pull request #17284 from xyb/feishucn
feat: add Feishu OAuth integration
2025-09-12 12:48:45 +04:00
Tim Jaeryang Baek b7f268f163 Merge pull request #17399 from Ithanil/german_i18n
i18n: German 100% translated
2025-09-12 12:47:25 +04:00
Tim Jaeryang Baek d5c236aa8e Merge pull request #17385 from silentoplayz/tailwind-config-fix
fix: correct spelling typo in Tailwind CSS config (containerQuries → containerQueries)
2025-09-12 12:47:00 +04:00
Jan Kessler c9e69fb2f4 100% german translation (~ 250 strings added/changed) 2025-09-12 10:38:47 +02:00
Xie Yanbo ee82439e67 feat: add Feishu OAuth integration
Implement Feishu OAuth provider using standard client:
- Set up Feishu-specific endpoints for authorization, token, and userinfo
- Use user_id as sub claim for Feishu user identification
- Extract correct user information from nested 'data' field in Feishu responses

Configuration requirements:
- Set FEISHU_CLIENT_ID and FEISHU_CLIENT_SECRET environment variables to enable Feishu OAuth
- Set ENABLE_OAUTH_SIGNUP=true to allow automatic user creation after OAuth login
- Set DEFAULT_USER_ROLE=user to grant immediate access after OAuth registration
- Set OAUTH_MERGE_ACCOUNTS_BY_EMAIL=true to enable merging of existing user accounts with matching emails
2025-09-12 14:09:32 +08:00
silentoplayz fcd3c7a10f fix: spelling 2025-09-12 00:47:54 -04:00
Timothy Jaeryang Baek 051b6daa82 refac/fix: large file upload 2025-09-11 22:29:02 +04:00
Timothy Jaeryang Baek 6c0a5fa91c refac 2025-09-11 21:56:59 +04:00
Timothy Jaeryang Baek edc056351d refac 2025-09-11 21:38:01 +04:00
Tim Jaeryang Baek 7347f95b24 Merge pull request #17370 from rgaricano/dev-es_ES
UPD: i18n- Translation es-ES v0.6.28
2025-09-11 21:35:55 +04:00
Timothy Jaeryang Baek a6f25b99f9 refac 2025-09-11 21:34:40 +04:00
Timothy Jaeryang Baek 2185fc61c0 refac 2025-09-11 21:29:56 +04:00
_00_ bf3a140254 UPD: i18n- Translation es-ES v0.6.28 2025-09-11 17:31:35 +02:00
Timothy Jaeryang Baek bbe6564bfd refac 2025-09-11 17:31:28 +04:00
Tim Jaeryang Baek 95674162f9 Merge pull request #17344 from BoFFire/patch-1
i18n: update kabyle translation
2025-09-11 12:28:41 +04:00
Tim Jaeryang Baek 24f78fed21 Merge pull request #17356 from ShirasawaSama/patch-19
feat: Add text truncation to ChatItem title display
2025-09-11 12:28:25 +04:00
Timothy Jaeryang Baek cf72f5503f refac 2025-09-11 12:26:25 +04:00
Shirasawa 932f759410 feat: add text truncation to ChatItem title display 2025-09-11 15:45:55 +08:00
ButterflyOfFire d6668515f6 i18n: update kabyle translation
Hi,

Added Kabyle translation progress : 88%
2025-09-10 17:56:01 +01:00
Tim Jaeryang Baek a05966af96 Merge pull request #17340 from Ithanil/i18n_prompts
i18n: translate '*required' string in prompts modal + added german translations
2025-09-10 17:07:58 +04:00
Jan Kessler 3802ab9fd1 translate '*required' string in prompts modal + added german translations 2025-09-10 15:03:12 +02:00
Jesper Kristensen 4ca43004ed feat: Added support for redis as session storage 2025-09-10 14:10:40 +02:00
Tim Jaeryang Baek 171021cfa4 Merge pull request #17327 from open-webui/dev
0.6.28
2025-09-10 14:53:30 +04:00
Timothy Jaeryang Baek d3d7b209b2 refac: changelog styling 2025-09-10 14:50:10 +04:00
Tim Jaeryang Baek 6ddbb41aac Merge pull request #17333 from Classic298/patch-2
Chore: Update CHANGELOG
2025-09-10 14:38:45 +04:00
Classic298 2a9925fe18 Update CHANGELOG.md 2025-09-10 12:36:46 +02:00
Timothy Jaeryang Baek 81e401a520 doc: changelog 2025-09-10 14:34:32 +04:00
Tim Jaeryang Baek ba432090c5 Merge pull request #17320 from ShirasawaSama/patch-16
i18n: Update translations in zh-CN locale
2025-09-10 14:14:35 +04:00
Timothy Jaeryang Baek de634a7599 chore: dep bump 2025-09-10 14:08:16 +04:00
Timothy Jaeryang Baek 803daebfe4 chore: format 2025-09-10 14:06:47 +04:00
Timothy Jaeryang Baek 958a76d448 chore: bump 2025-09-10 14:06:12 +04:00
Tim Jaeryang Baek 2dd0eaad73 Merge pull request #17328 from open-webui/main
dev
2025-09-10 14:04:03 +04:00
Timothy Jaeryang Baek 8339f59cdf fix: openai audio revert 2025-09-10 14:02:19 +04:00
Timothy Jaeryang Baek c51a65173e enh: folder modal focus title 2025-09-10 13:55:29 +04:00
Timothy Jaeryang Baek d5cb65527e fix: prompt template 2025-09-10 13:52:34 +04:00
Timothy Jaeryang Baek 956cb7beaa fix: openai connections key 2025-09-10 13:49:19 +04:00
Tim Jaeryang Baek 4eb928693b Merge pull request #17317 from joaoback/patch-7
Update translation.json (pt-BR)
2025-09-10 13:00:43 +04:00
Timothy Jaeryang BaekandClassic298 d5b903234d chore: PR template
Co-Authored-By: Classic298 <27028174+Classic298@users.noreply.github.com>
2025-09-10 12:57:13 +04:00
Shirasawa 86f191d015 i18n: Update translations in zh-CN locale 2025-09-10 07:39:15 +00:00
joaoback b35b56394a Update translation.json (pt-BR)
I added some translations of new items
2025-09-09 15:25:07 -03:00
Timothy Jaeryang Baek 23990276cb chore: bump chroma 2025-09-09 19:13:41 +04:00
Timothy Jaeryang Baek f2a09c7149 fix: model command filter 2025-09-09 19:01:39 +04:00
Timothy Jaeryang Baek 774c0056bd refac: tool server data handling 2025-09-09 19:00:01 +04:00
Tim Jaeryang Baek c299d3fd54 Merge pull request #17299 from open-webui/dependabot/pip/backend/dev/psycopg2-binary-2.9.10
build(deps): bump psycopg2-binary from 2.9.9 to 2.9.10 in /backend
2025-09-09 18:48:00 +04:00
Tim Jaeryang Baek 7d62ce5b19 Merge pull request #17301 from open-webui/dependabot/pip/backend/dev/elasticsearch-9.1.0
build(deps): bump elasticsearch from 9.0.1 to 9.1.0 in /backend
2025-09-09 18:47:54 +04:00
dependabot[bot] 5ad93b6fc9 build(deps): bump psycopg2-binary from 2.9.9 to 2.9.10 in /backend
Bumps [psycopg2-binary](https://github.com/psycopg/psycopg2) from 2.9.9 to 2.9.10.
- [Changelog](https://github.com/psycopg/psycopg2/blob/master/NEWS)
- [Commits](https://github.com/psycopg/psycopg2/compare/2.9.9...2.9.10)

---
updated-dependencies:
- dependency-name: psycopg2-binary
  dependency-version: 2.9.10
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-09 14:47:37 +00:00
Tim Jaeryang Baek e6a5618d13 Merge pull request #17302 from open-webui/dependabot/pip/backend/dev/googleapis-common-protos-1.70.0
build(deps): bump googleapis-common-protos from 1.63.2 to 1.70.0 in /backend
2025-09-09 18:46:52 +04:00
Tim Jaeryang Baek d335851a1c Merge pull request #17303 from open-webui/dependabot/pip/backend/dev/pgvector-0.4.1
build(deps): bump pgvector from 0.4.0 to 0.4.1 in /backend
2025-09-09 18:46:26 +04:00
Tim Jaeryang Baek 8bc6a30c7b Merge pull request #17297 from open-webui/dependabot/github_actions/dev/actions/setup-node-5
build(deps): bump actions/setup-node from 4 to 5
2025-09-09 18:43:58 +04:00
Tim Jaeryang Baek b7243e6efc Merge pull request #17298 from open-webui/dependabot/github_actions/dev/actions/setup-python-6
build(deps): bump actions/setup-python from 5 to 6
2025-09-09 18:43:51 +04:00
Tim Jaeryang Baek 40fab879ff Merge pull request #17300 from open-webui/dependabot/github_actions/dev/actions/github-script-8
build(deps): bump actions/github-script from 7 to 8
2025-09-09 18:43:44 +04:00
dependabot[bot] 5af4714bf0 build(deps): bump pgvector from 0.4.0 to 0.4.1 in /backend
Bumps [pgvector](https://github.com/pgvector/pgvector-python) from 0.4.0 to 0.4.1.
- [Changelog](https://github.com/pgvector/pgvector-python/blob/master/CHANGELOG.md)
- [Commits](https://github.com/pgvector/pgvector-python/compare/v0.4.0...v0.4.1)

---
updated-dependencies:
- dependency-name: pgvector
  dependency-version: 0.4.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-09 14:35:35 +00:00
dependabot[bot] edd65feff5 build(deps): bump googleapis-common-protos in /backend
Bumps [googleapis-common-protos](https://github.com/googleapis/google-cloud-python) from 1.63.2 to 1.70.0.
- [Release notes](https://github.com/googleapis/google-cloud-python/releases)
- [Changelog](https://github.com/googleapis/google-cloud-python/blob/main/packages/google-cloud-documentai/CHANGELOG.md)
- [Commits](https://github.com/googleapis/google-cloud-python/commits/googleapis-common-protos-v1.70.0)

---
updated-dependencies:
- dependency-name: googleapis-common-protos
  dependency-version: 1.70.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-09 14:35:32 +00:00
dependabot[bot] 202eada74f build(deps): bump elasticsearch from 9.0.1 to 9.1.0 in /backend
Bumps [elasticsearch](https://github.com/elastic/elasticsearch-py) from 9.0.1 to 9.1.0.
- [Release notes](https://github.com/elastic/elasticsearch-py/releases)
- [Commits](https://github.com/elastic/elasticsearch-py/compare/v9.0.1...v9.1.0)

---
updated-dependencies:
- dependency-name: elasticsearch
  dependency-version: 9.1.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-09 14:35:17 +00:00
dependabot[bot] d863fd3626 build(deps): bump actions/github-script from 7 to 8
Bumps [actions/github-script](https://github.com/actions/github-script) from 7 to 8.
- [Release notes](https://github.com/actions/github-script/releases)
- [Commits](https://github.com/actions/github-script/compare/v7...v8)

---
updated-dependencies:
- dependency-name: actions/github-script
  dependency-version: '8'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-09 14:35:13 +00:00
dependabot[bot] 3ff8deeea8 build(deps): bump actions/setup-python from 5 to 6
Bumps [actions/setup-python](https://github.com/actions/setup-python) from 5 to 6.
- [Release notes](https://github.com/actions/setup-python/releases)
- [Commits](https://github.com/actions/setup-python/compare/v5...v6)

---
updated-dependencies:
- dependency-name: actions/setup-python
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-09 14:35:10 +00:00
dependabot[bot] 041a89ef5c build(deps): bump actions/setup-node from 4 to 5
Bumps [actions/setup-node](https://github.com/actions/setup-node) from 4 to 5.
- [Release notes](https://github.com/actions/setup-node/releases)
- [Commits](https://github.com/actions/setup-node/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/setup-node
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-09 14:35:06 +00:00
Tim Jaeryang Baek 918f507d8c Merge pull request #17070 from open-webui/dev
0.6.27
2025-09-09 18:34:15 +04:00
Timothy Jaeryang Baek f7e85cd0bf refac 2025-09-09 18:31:06 +04:00
Timothy Jaeryang Baek 4b4583df62 refac: styling 2025-09-09 18:28:22 +04:00
Timothy Jaeryang Baek bcf76dfa74 refac: changelog 2025-09-09 18:24:16 +04:00
Timothy Jaeryang Baek 485392fe63 chore: format 2025-09-09 18:19:31 +04:00
Timothy Jaeryang Baek cbb4684b16 chore 2025-09-09 18:16:24 +04:00
Timothy Jaeryang Baek 0531ca6530 refac/fix 2025-09-09 18:10:48 +04:00
Timothy Jaeryang Baek 32cb9df3c4 refac/enh: knowledge ac backend validation 2025-09-09 18:08:31 +04:00
Tim Jaeryang Baek 71fd483fba Merge pull request #17276 from Elettrotecnica/extend-docling-configuration
feat: Extend docling configuration options
2025-09-09 18:04:30 +04:00
Timothy Jaeryang Baek 63f38c584f refac 2025-09-09 18:02:51 +04:00
Timothy Jaeryang Baek 77779b30d4 refac 2025-09-09 18:01:59 +04:00
Timothy Jaeryang Baek d0f338bb99 refac/enh: ability to export/sync function valves 2025-09-09 17:48:41 +04:00
Timothy Jaeryang Baek 63ca0b8cba refac 2025-09-09 17:36:18 +04:00
Timothy Jaeryang Baek d0b20df46c refac: citations display 2025-09-09 17:27:38 +04:00
Timothy Jaeryang Baek 0214c1e66c refac 2025-09-09 16:48:59 +04:00
Tim Jaeryang Baek 015342e669 Merge pull request #17281 from Classic298/changelog-final
chore: Changelog 0.6.27 - New Changelog Style
2025-09-09 16:43:22 +04:00
Classic298 0cd32b015b Update CHANGELOG.md 2025-09-09 14:06:21 +02:00
Timothy Jaeryang Baek 5f0d262c59 fix: yt embed 2025-09-09 16:00:42 +04:00
Timothy Jaeryang Baek 7fc3ac38ac chore 2025-09-09 15:58:58 +04:00
Tim Jaeryang Baek a04e8ee84e Merge pull request #17295 from ShirasawaSama/i18n/improve-chinese-translation
i18n: improve Chinese translation
2025-09-09 15:46:57 +04:00
Shirasawa 3cce416cab feat: improve Chinese translation 2025-09-09 18:56:45 +08:00
Timothy Jaeryang Baek f17d8b5d19 refac: forward cred to static paths 2025-09-09 14:11:17 +04:00
Tim Jaeryang Baek c881e033b2 Merge pull request #17285 from ShirasawaSama/patch-15
feat: change default permission check for regenerate and delete actions
2025-09-09 14:06:28 +04:00
Timothy Jaeryang Baek 3ae547f796 refac 2025-09-09 14:03:47 +04:00
Timothy Jaeryang Baek 79e92378de refac 2025-09-09 14:02:08 +04:00
Timothy Jaeryang Baek 53ecfc2b5d chore: bump authlib 2025-09-09 13:46:21 +04:00
Shirasawa d783708745 feat: change default permission check for regenerate and delete actions 2025-09-09 15:42:58 +08:00
Classic298 218701e617 Update CHANGELOG.md 2025-09-08 23:42:58 +02:00
Classic298 c6a46195c0 Changelog dev (#18)
* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md
2025-09-08 23:40:39 +02:00
Timothy Jaeryang Baek 4a76bea80c refac 2025-09-08 21:03:29 +04:00
Timothy Jaeryang Baek fcebc82ec2 refac: chat controls panel resize logic 2025-09-08 20:54:18 +04:00
Antonio Pisano daa2a036f8 Extend docling configuration options to include:
* do_ocr
* force_ocr
* pdf_backend
* table_mode
* pipeline

as per https://github.com/docling-project/docling-serve/blob/main/docs/usage.md

See https://github.com/open-webui/open-webui/issues/17148
2025-09-08 18:51:33 +02:00
Timothy Jaeryang Baek 41f9a8caff refac 2025-09-08 20:25:19 +04:00
Timothy Jaeryang Baek 6f6412dd16 refac 2025-09-08 19:53:44 +04:00
Timothy Jaeryang Baek 2b2d123531 refac: oauth auth type in openai connection 2025-09-08 19:42:50 +04:00
Timothy Jaeryang Baek 474df5e534 refac 2025-09-08 19:18:55 +04:00
Timothy Jaeryang Baek 30d1dc2c60 refac 2025-09-08 19:12:20 +04:00
Timothy Jaeryang Baek 4cea3a57be refac 2025-09-08 19:09:26 +04:00
Timothy Jaeryang Baek 8a9f862701 refac 2025-09-08 19:07:00 +04:00
Timothy Jaeryang Baek 001dab0439 refac: wording 2025-09-08 18:55:57 +04:00
Timothy Jaeryang Baek b786d1e3f3 refac 2025-09-08 18:52:59 +04:00
Timothy Jaeryang Baek b5bb6ae177 refac 2025-09-08 18:50:23 +04:00
Timothy Jaeryang Baek f71834720e refac 2025-09-08 18:35:09 +04:00
Timothy Jaeryang Baek f11bdc6ab5 refac 2025-09-08 18:23:44 +04:00
Timothy Jaeryang Baek 35c1c48fd2 refac 2025-09-08 18:18:04 +04:00
Timothy Jaeryang Baek fc11e4384f refac 2025-09-08 18:17:11 +04:00
Timothy Jaeryang Baek 7693d0e2b0 refac 2025-09-08 18:09:01 +04:00
Timothy Jaeryang BaekandClassic298 217f4daef0 feat: server-side OAuth token management system
Co-Authored-By: Classic298 <27028174+Classic298@users.noreply.github.com>
2025-09-08 18:05:43 +04:00
Timothy Jaeryang Baek 6d38ac41b6 refac 2025-09-08 14:36:00 +04:00
Timothy Jaeryang Baek 91755309ce refac 2025-09-08 14:18:25 +04:00
Timothy Jaeryang Baek a28ca30519 refac/fix: source citation 2025-09-08 02:35:51 +04:00
Timothy Jaeryang Baek e023a98f11 refac: submit suggestion prompt by default 2025-09-07 19:24:32 +04:00
Timothy Jaeryang Baek 6dc0df2473 refac 2025-09-07 05:17:38 +04:00
Timothy Jaeryang Baek cd5e2be27b refac 2025-09-07 05:09:14 +04:00
Timothy Jaeryang Baek 3d37e4a42d refac 2025-09-07 05:06:03 +04:00
Timothy Jaeryang Baek 7f523de408 refac 2025-09-07 04:27:42 +04:00
Timothy Jaeryang Baek f2525ebc44 refac 2025-09-07 04:25:52 +04:00
Timothy Jaeryang Baek 1cdb7aed1e refac/enh: status history 2025-09-07 04:21:46 +04:00
Timothy Jaeryang Baek 16090bc272 refac 2025-09-07 03:01:25 +04:00
Timothy Jaeryang Baek 0a85dd4bca refac: web search link display 2025-09-07 03:00:28 +04:00
Timothy Jaeryang Baek 33f04a7714 refac: styling 2025-09-07 02:36:32 +04:00
Timothy Jaeryang Baek c9282135c4 refac 2025-09-07 02:02:21 +04:00
Timothy Jaeryang Baek 3d6d050ad8 refac/enh: display oauth error as toast 2025-09-07 01:48:52 +04:00
Timothy Jaeryang Baek 9368d0ac75 refac/fix: web search always on setting 2025-09-07 01:15:10 +04:00
Timothy Jaeryang Baek 40e40d1ddd enh/sec: verify folder data integrity
#17182
2025-09-07 01:04:56 +04:00
Timothy Jaeryang Baek d9f97c832c refac 2025-09-07 00:28:52 +04:00
Timothy Jaeryang Baek 4f2b821088 refac/enh: oauth redirect support 2025-09-07 00:22:52 +04:00
Timothy Jaeryang Baek eb10ff2ae6 refac: rm substandard code 2025-09-07 00:16:07 +04:00
Tim Jaeryang Baek b70e910e4b Merge pull request #17232 from open-webui/dependabot/pip/pip-83da9e8e4c
build(deps): bump the pip group across 2 directories with 2 updates
2025-09-07 00:00:14 +04:00
Timothy Jaeryang Baek 955fc5c736 refac: styling 2025-09-06 23:58:49 +04:00
Timothy Jaeryang Baek 9aac148908 fix: knowledge update backend issue 2025-09-06 23:44:29 +04:00
Tim Jaeryang Baek 183ba83920 Merge pull request #17245 from Classic298/patch-1
chore: changes to bug report issue template
2025-09-06 23:31:51 +04:00
Classic298 85cc067599 chore: changes to bug report issue template 2025-09-06 21:01:28 +02:00
Tim Jaeryang Baek fe0665605a Merge pull request #17229 from ShirasawaSama/feat/dynamic-load-heic2any
feat: dynamically load heic2any in channel MessageInput
2025-09-06 18:26:57 +04:00
Tim Jaeryang Baek e593ea8935 Merge pull request #17228 from ShirasawaSama/patch-13
feat: improve Chinese translation
2025-09-06 18:26:46 +04:00
dependabot[bot] e114a40bb8 build(deps): bump the pip group across 2 directories with 2 updates
Bumps the pip group with 2 updates in the / directory: [langchain-community](https://github.com/langchain-ai/langchain) and [pypdf](https://github.com/py-pdf/pypdf).
Bumps the pip group with 2 updates in the /backend directory: [langchain-community](https://github.com/langchain-ai/langchain) and [pypdf](https://github.com/py-pdf/pypdf).


Updates `langchain-community` from 0.3.26 to 0.3.27
- [Release notes](https://github.com/langchain-ai/langchain/releases)
- [Commits](https://github.com/langchain-ai/langchain/compare/langchain==0.3.26...langchain==0.3.27)

Updates `pypdf` from 4.3.1 to 6.0.0
- [Release notes](https://github.com/py-pdf/pypdf/releases)
- [Changelog](https://github.com/py-pdf/pypdf/blob/main/CHANGELOG.md)
- [Commits](https://github.com/py-pdf/pypdf/compare/4.3.1...6.0.0)

Updates `langchain-community` from 0.3.26 to 0.3.27
- [Release notes](https://github.com/langchain-ai/langchain/releases)
- [Commits](https://github.com/langchain-ai/langchain/compare/langchain==0.3.26...langchain==0.3.27)

Updates `pypdf` from 4.3.1 to 6.0.0
- [Release notes](https://github.com/py-pdf/pypdf/releases)
- [Changelog](https://github.com/py-pdf/pypdf/blob/main/CHANGELOG.md)
- [Commits](https://github.com/py-pdf/pypdf/compare/4.3.1...6.0.0)

---
updated-dependencies:
- dependency-name: langchain-community
  dependency-version: 0.3.27
  dependency-type: direct:production
  dependency-group: pip
- dependency-name: pypdf
  dependency-version: 6.0.0
  dependency-type: direct:production
  dependency-group: pip
- dependency-name: langchain-community
  dependency-version: 0.3.27
  dependency-type: direct:production
  dependency-group: pip
- dependency-name: pypdf
  dependency-version: 6.0.0
  dependency-type: direct:production
  dependency-group: pip
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-05 15:50:11 +00:00
Aleix Dorca 7c0bf59c7c Update catalan translation.json 2025-09-05 17:17:20 +02:00
Shirasawa eb64f7bdb3 feat: dynamically load heic2any in channel MessageInput 2025-09-05 21:34:17 +08:00
Timothy Jaeryang Baek 1bc9711afd refac 2025-09-05 17:28:05 +04:00
Shirasawa de48a32ea2 feat: improve Chinese translation 2025-09-05 21:26:01 +08:00
Tim Jaeryang Baek 5b3eae3855 Merge pull request #17225 from ShirasawaSama/feat/dynamic-load-heic2any
feat: dynamically load heic2any to accelerate initial page loading speed and fix heic convert bug
2025-09-05 16:42:45 +04:00
Tim Jaeryang Baek 9764bf3b78 Merge pull request #17222 from ShirasawaSama/patch-14
feat: dynamically load PDFjs to accelerate initial page loading speed
2025-09-05 16:42:09 +04:00
Shirasawa a74ec200b3 feat: dynamically load heic2any to accelerate initial page loading speed 2025-09-05 20:07:12 +08:00
Shirasawa 49e045ea3d feat: dynamically load pdfjs 2025-09-05 17:55:04 +08:00
Timothy Jaeryang Baek c0a47169fa refac: emoji picker case sensitivity 2025-09-04 22:10:46 +04:00
Tim Jaeryang Baek a140cfc521 Merge pull request #17191 from Ithanil/fix_chat_button_text
fix: don't clip descending characters on chat buttons + don't capitalize
2025-09-04 20:23:40 +04:00
Tim Jaeryang Baek ea2f97004b Merge pull request #17186 from Viruzaum/tooltip-fix
fix: remove duplicated tooltip from follow-up
2025-09-04 20:22:59 +04:00
Tim Jaeryang Baek 93132abb40 Merge pull request #17195 from ShirasawaSama/patch-14
fix: fix error when stopping non-existent task
2025-09-04 20:21:09 +04:00
Tim Jaeryang Baek df0b1e5552 Merge pull request #17190 from aREversez/patch-17185-clean
i18n: improve Chinese localization
2025-09-04 20:13:46 +04:00
Shirasawa e5ea595425 fix: fix error when stopping non-existent task 2025-09-04 16:52:19 +08:00
Jan Kessler a5b781f3d8 prevent clipping of descending glyphs on chat button + don't capitalize 2025-09-04 09:28:16 +02:00
aREversez 5128402282 Update translation.json 2025-09-04 12:32:17 +08:00
Timothy Jaeryang Baek 11dea8795b refac 2025-09-04 02:56:21 +04:00
Timothy Jaeryang Baek b70c0f36c0 enh: emoji folder icon 2025-09-04 02:50:50 +04:00
Timothy Jaeryang Baek e42ee34672 refac 2025-09-03 20:56:25 +04:00
Timothy Jaeryang Baek 71b6a942fe refac: styling 2025-09-03 20:53:50 +04:00
Timothy Jaeryang Baek 5bdd334b74 enh: azure openai toggle 2025-09-03 20:28:29 +04:00
viruz 6d6794aa85 fix: remove duplicated tooltip from follow-up button in ResponseMessage component 2025-09-03 13:18:45 -03:00
Timothy Jaeryang Baek 1588f42fe7 refac 2025-09-03 20:17:43 +04:00
Timothy Jaeryang Baek 029db5c635 refac: styling 2025-09-03 19:59:27 +04:00
Timothy Jaeryang Baek b85700f6ca refac: styling 2025-09-03 19:13:17 +04:00
Timothy Jaeryang Baek fbbe1117ae refac/enh: file count in knowledge 2025-09-03 19:00:35 +04:00
Timothy Jaeryang Baek df66e21472 enh: regex pattern support for groups 2025-09-03 18:50:02 +04:00
Timothy Jaeryang Baek 51fc792501 refac: styling 2025-09-03 18:47:50 +04:00
Timothy Jaeryang Baek 7a166152d9 refac: styling 2025-09-03 18:40:25 +04:00
Timothy Jaeryang Baek 926954f93b refac: styling 2025-09-03 18:37:25 +04:00
Tim Jaeryang Baek 5a596ae40c Merge pull request #17180 from Viruzaum/untitled-translation-fix
fix/i18n: Fix missing translation of default title of AddTextContentModel
2025-09-03 18:05:56 +04:00
Tim Jaeryang Baek 048f30aa97 Merge pull request #17158 from sihyeonn/fix/sh-cache
perf: fix cache key generation for model list caching
2025-09-03 16:18:33 +04:00
Timothy Jaeryang Baek 66bf28cd85 refac 2025-09-03 15:48:07 +04:00
Timothy Jaeryang Baek bbe1167958 refac/fix: pyodide import issue 2025-09-03 15:46:42 +04:00
Tim Jaeryang Baek 357a53eb6f Merge pull request #17147 from anfuerer/dev
feat: Azure OpenAI image generation support
2025-09-03 14:12:48 +04:00
Timothy Jaeryang Baek 37bf0087e5 refac: tool message format 2025-09-03 13:57:14 +04:00
Tim Jaeryang Baek e5829572ff Merge pull request #17175 from garylab/bugfix/api-config
Bugfix: Add verify token from headers also for /api/config endpoint
2025-09-03 13:39:25 +04:00
Timothy Jaeryang Baek 4ca936f0bf refac 2025-09-03 13:38:07 +04:00
Tim Jaeryang Baek 0a351580f8 Merge pull request #17137 from acwoo97/feat/knowledge-update-race-condition
fix: prevent double-save race by awaiting API calls and adding isSaving guard
2025-09-03 13:36:51 +04:00
Tim Jaeryang Baek 472b71f331 Merge pull request #17166 from sihyeonn/perf/sh-model-layer
perf: fix N+1 query issues in user group access control validation
2025-09-03 13:36:01 +04:00
Tim Jaeryang Baek 308f4d6b26 Merge pull request #17159 from sihyeonn/perf/sh-prompts
perf: fix N+1 query issue in get_prompts method
2025-09-03 13:26:29 +04:00
Gary Meng 2d62796616 Allow user get /api/config with auth header 2025-09-03 13:25:09 +04:00
Tim Jaeryang Baek 330bec67b7 Merge pull request #17162 from sihyeonn/perf/sh-tools
perf: fix N+1 query issue in get_tools method
2025-09-03 13:22:37 +04:00
Tim Jaeryang Baek 2890c6d62d Merge pull request #17161 from sihyeonn/perf/sh-models
perf: fix N+1 query issue in get_models method
2025-09-03 13:22:27 +04:00
Tim Jaeryang Baek 9119db001d Merge pull request #17160 from sihyeonn/perf/sh-knowledge-base
perf: fix N+1 query issue in get_knowledge_bases method
2025-09-03 13:22:19 +04:00
Tim Jaeryang Baek c7faccd5fa Merge pull request #17165 from sihyeonn/perf/sh-tools-router
perf: fix N+1 query issue in tools access control checking
2025-09-03 13:13:49 +04:00
Sihyeon Jang eff06538a6 perf: fix N+1 query issues in user group access control validation
- Pre-fetch user group IDs in get_*_by_user_id methods across models layer
- Pass user_group_ids to has_access to avoid repeated group queries
- Reduce query count from 1+N to 1+1 pattern for access control validation
- Apply consistent optimization across knowledge, models, notes, prompts, and tools

Signed-off-by: Sihyeon Jang <sihyeon.jang@navercorp.com>
2025-09-03 05:56:48 +09:00
Sihyeon Jang 0503fbd2e3 perf: fix N+1 query issue in tools access control checking
- Pre-fetch user group IDs once per request in get_tools endpoint
- Pass user_group_ids to has_access to avoid repeated group queries
- Optimize access control validation from 1+N to 1+1 query pattern
- Reduce database load when checking multiple tools access permissions

Signed-off-by: Sihyeon Jang <sihyeon.jang@navercorp.com>
2025-09-03 05:49:53 +09:00
Sihyeon Jang 03d1d2a88b perf: fix N+1 query issue in get_tools method
- Replace individual user queries with batch fetching
- Use single query to fetch all required users at once
- Implement O(1) user lookup with dictionary mapping
- Reduce query count from 1+N to 1+1 pattern for tools listing

Signed-off-by: Sihyeon Jang <sihyeon.jang@navercorp.com>
2025-09-03 05:35:35 +09:00
Sihyeon Jang c0b3db38a5 perf: fix N+1 query issue in get_models method
- Replace individual user queries with batch fetching
- Use single query to fetch all required users at once
- Implement O(1) user lookup with dictionary mapping
- Reduce query count from 1+N to 1+1 pattern for models with base_model_id

Signed-off-by: Sihyeon Jang <sihyeon.jang@navercorp.com>
2025-09-03 05:33:41 +09:00
Sihyeon Jang f588655f7f perf: fix N+1 query issue in get_knowledge_bases method
- Replace individual user queries with batch fetching
- Use single query to fetch all required users at once
- Implement O(1) user lookup with dictionary mapping
- Reduce query count from 1+N to 1+1 pattern

Signed-off-by: Sihyeon Jang <sihyeon.jang@navercorp.com>
2025-09-03 05:29:47 +09:00
Sihyeon Jang c45201a8a2 perf: fix N+1 query issue in get_prompts method
- Replace individual user queries with batch fetching
- Use single query to fetch all required users at once
- Implement O(1) user lookup with dictionary mapping
- Reduce query count from 1+N to 1+1 pattern

Signed-off-by: Sihyeon Jang <sihyeon.jang@navercorp.com>
2025-09-03 05:25:26 +09:00
Sihyeon Jang 3ccbb46938 perf: fix cache key generation for model list caching
- Replace Request object with user.id in cache key for get_all_models
- Request objects are new instances per HTTP request, preventing cache hits
- Cache keys now use user.id ensuring proper cache functionality
- Affects both Ollama and OpenAI model list endpoints

Signed-off-by: Sihyeon Jang <sihyeon.jang@navercorp.com>
2025-09-03 05:17:41 +09:00
viruz a980af3ca4 fix: update default name to use i18n translation 2025-09-02 15:47:16 -03:00
Timothy Jaeryang Baek 22c4ef4fb0 enh: delete_file query param 2025-09-02 21:32:07 +04:00
Andreas Fuerer df0d29c81c style: fix formatting issues 2025-09-02 19:16:02 +02:00
Andreas Fuerer bc6afc9057 feature: Azure OpenAI image generation support
The image generation API used on Azure OpenAI requires to specify the API
version by appending an `api-version` query parameter to the endpoint URL.
Added the environment variable `IMAGES_OPENAI_API_VERSION` with
configuration functionality in the administration UI.
2025-09-02 15:51:45 +02:00
Timothy Jaeryang Baek 182408a52e refac 2025-09-02 17:03:59 +04:00
Chanwoo An 513cab94b1 fix: prevent double-save race by awaiting API calls and adding isSaving guard 2025-09-02 20:53:04 +09:00
Tim Jaeryang Baek d01439d144 Merge pull request #17093 from open-webui/dependabot/pip/backend/dev/argon2-cffi-25.1.0
build(deps): bump argon2-cffi from 23.1.0 to 25.1.0 in /backend
2025-09-02 02:41:49 +04:00
Tim Jaeryang Baek 0a535a6fed Merge pull request #17107 from rgaricano/dev-es_ES
UPD: i18n es-ES Translation v.0.6.26
2025-09-01 22:52:14 +04:00
Tim Jaeryang Baek 3a38173c66 Merge pull request #17098 from open-webui/dependabot/github_actions/dev/actions/download-artifact-5
build(deps): bump actions/download-artifact from 4 to 5
2025-09-01 22:51:34 +04:00
Tim Jaeryang Baek adbaaa65f9 Merge pull request #17097 from open-webui/dependabot/npm_and_yarn/dev/tiptap/extension-highlight-3.3.0
build(deps): bump @tiptap/extension-highlight from 3.0.7 to 3.3.0
2025-09-01 22:51:23 +04:00
Tim Jaeryang Baek 379592dae4 Merge pull request #17096 from open-webui/dependabot/pip/backend/dev/pytest-approx-eq-8.4.1
build(deps): update pytest requirement from ~=8.3.5 to ~=8.4.1 in /backend
2025-09-01 22:51:02 +04:00
Tim Jaeryang Baek 22ff239e3a Merge pull request #17094 from open-webui/dependabot/pip/backend/dev/youtube-transcript-api-1.2.2
build(deps): bump youtube-transcript-api from 1.1.0 to 1.2.2 in /backend
2025-09-01 22:50:57 +04:00
Timothy Jaeryang Baek e830b4959e enh: llama cpp timing stats 2025-09-01 22:49:49 +04:00
Tim Jaeryang Baek 1c1b32b390 Merge pull request #17099 from open-webui/dependabot/github_actions/dev/actions/checkout-5
build(deps): bump actions/checkout from 4 to 5
2025-09-01 22:48:04 +04:00
Timothy Jaeryang Baek e6daad2ab9 chore: bump mermaid 2025-09-01 22:45:06 +04:00
Timothy Jaeryang Baek 4f2e426fc7 refac 2025-09-01 14:27:20 +04:00
Timothy Jaeryang Baek 609a6a3721 refac 2025-09-01 14:22:02 +04:00
Timothy Jaeryang Baek 85153afda8 refac 2025-09-01 14:21:17 +04:00
Timothy Jaeryang Baek f56889c5c7 fix: fillter exception handling 2025-09-01 14:14:20 +04:00
_00_ cef4028c1c UPD: i18n Translation es-ES v.0.6.27
### Update of i18n Translation es-ES v.0.6.27

Added new strings
2025-09-01 09:48:05 +02:00
Timothy Jaeryang Baek 4b97884fce refac 2025-09-01 11:46:52 +04:00
Tim Jaeryang Baek 9ac7f2635c Merge pull request #17101 from open-webui/dependabot/npm_and_yarn/dev/dompurify-3.2.6
build(deps): bump dompurify from 3.2.5 to 3.2.6
2025-09-01 10:37:39 +04:00
Tim Jaeryang Baek 2d6f68eb2d Merge pull request #17095 from open-webui/dependabot/npm_and_yarn/dev/pyodide-0.28.2
build(deps): bump pyodide from 0.27.7 to 0.28.2
2025-09-01 10:37:28 +04:00
Tim Jaeryang Baek 61e7aa2229 Merge pull request #17089 from open-webui/dependabot/npm_and_yarn/dev/pdfjs-dist-5.4.149
build(deps): bump pdfjs-dist from 5.3.93 to 5.4.149
2025-09-01 10:35:49 +04:00
dependabot[bot] 9f5df72d7c build(deps): bump dompurify from 3.2.5 to 3.2.6
Bumps [dompurify](https://github.com/cure53/DOMPurify) from 3.2.5 to 3.2.6.
- [Release notes](https://github.com/cure53/DOMPurify/releases)
- [Commits](https://github.com/cure53/DOMPurify/compare/3.2.5...3.2.6)

---
updated-dependencies:
- dependency-name: dompurify
  dependency-version: 3.2.6
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-01 06:16:43 +00:00
dependabot[bot] 4dd9484b48 build(deps): bump actions/checkout from 4 to 5
Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 5.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-01 06:03:14 +00:00
dependabot[bot] dbe36e841a build(deps): bump actions/download-artifact from 4 to 5
Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 4 to 5.
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](https://github.com/actions/download-artifact/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/download-artifact
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-01 06:02:57 +00:00
dependabot[bot] 201eaa9655 build(deps): bump @tiptap/extension-highlight from 3.0.7 to 3.3.0
Bumps [@tiptap/extension-highlight](https://github.com/ueberdosis/tiptap/tree/HEAD/packages/extension-highlight) from 3.0.7 to 3.3.0.
- [Release notes](https://github.com/ueberdosis/tiptap/releases)
- [Changelog](https://github.com/ueberdosis/tiptap/blob/develop/packages/extension-highlight/CHANGELOG.md)
- [Commits](https://github.com/ueberdosis/tiptap/commits/v3.3.0/packages/extension-highlight)

---
updated-dependencies:
- dependency-name: "@tiptap/extension-highlight"
  dependency-version: 3.3.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-01 06:02:48 +00:00
dependabot[bot] 4cd550d1d0 build(deps): update pytest requirement in /backend
Updates the requirements on [pytest](https://github.com/pytest-dev/pytest) to permit the latest version.
- [Release notes](https://github.com/pytest-dev/pytest/releases)
- [Changelog](https://github.com/pytest-dev/pytest/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/pytest-dev/pytest/compare/8.3.5...8.4.1)

---
updated-dependencies:
- dependency-name: pytest
  dependency-version: 8.4.1
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-01 06:02:31 +00:00
dependabot[bot] 94dae76d0f build(deps): bump pyodide from 0.27.7 to 0.28.2
Bumps [pyodide](https://github.com/pyodide/pyodide) from 0.27.7 to 0.28.2.
- [Release notes](https://github.com/pyodide/pyodide/releases)
- [Commits](https://github.com/pyodide/pyodide/compare/0.27.7...0.28.2)

---
updated-dependencies:
- dependency-name: pyodide
  dependency-version: 0.28.2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-01 06:01:03 +00:00
dependabot[bot] 3e69f10af7 build(deps): bump youtube-transcript-api from 1.1.0 to 1.2.2 in /backend
Bumps [youtube-transcript-api](https://github.com/jdepoix/youtube-transcript-api) from 1.1.0 to 1.2.2.
- [Release notes](https://github.com/jdepoix/youtube-transcript-api/releases)
- [Commits](https://github.com/jdepoix/youtube-transcript-api/compare/v1.1.0...v1.2.2)

---
updated-dependencies:
- dependency-name: youtube-transcript-api
  dependency-version: 1.2.2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-01 05:59:27 +00:00
dependabot[bot] bcc0cbf895 build(deps): bump argon2-cffi from 23.1.0 to 25.1.0 in /backend
Bumps [argon2-cffi](https://github.com/hynek/argon2-cffi) from 23.1.0 to 25.1.0.
- [Release notes](https://github.com/hynek/argon2-cffi/releases)
- [Changelog](https://github.com/hynek/argon2-cffi/blob/main/CHANGELOG.md)
- [Commits](https://github.com/hynek/argon2-cffi/compare/23.1.0...25.1.0)

---
updated-dependencies:
- dependency-name: argon2-cffi
  dependency-version: 25.1.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-01 05:59:19 +00:00
dependabot[bot] 0e0c1c4ca4 build(deps): bump pdfjs-dist from 5.3.93 to 5.4.149
Bumps [pdfjs-dist](https://github.com/mozilla/pdf.js) from 5.3.93 to 5.4.149.
- [Release notes](https://github.com/mozilla/pdf.js/releases)
- [Commits](https://github.com/mozilla/pdf.js/compare/v5.3.93...v5.4.149)

---
updated-dependencies:
- dependency-name: pdfjs-dist
  dependency-version: 5.4.149
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-01 05:53:53 +00:00
Timothy Jaeryang Baek ed5d95f434 refac 2025-09-01 02:41:29 +04:00
Tim Jaeryang Baek 3d2fcef28f Merge pull request #17085 from ShirasawaSama/patch-10
fix: fix event binding for composition end in MessageInput
2025-09-01 02:10:04 +04:00
Timothy Jaeryang Baek 77b65ccbfb refac/enh: forward headers to tool server 2025-09-01 01:52:10 +04:00
Timothy Jaeryang Baek 487979859a fix: web/youtube attachements 2025-09-01 01:22:50 +04:00
Timothy Jaeryang Baek ac0243e8b7 refac 2025-09-01 00:57:13 +04:00
Shirasawa 5037197541 fix: fix event binding for composition end in MessageInput 2025-09-01 04:49:06 +08:00
Timothy Jaeryang Baek b45219c8b1 refac 2025-09-01 00:04:26 +04:00
Tim Jaeryang Baek ebfc887869 Merge pull request #17061 from AdamJohnSwan/dev
fix: for forming TTS request URL when there is a slash at the end of the base URL
2025-09-01 00:01:43 +04:00
Tim Jaeryang Baek 719d115d49 Merge pull request #17049 from rgaricano/dev-FIX_lex-sem
FIX: Hybrid Search
2025-09-01 00:00:25 +04:00
Tim Jaeryang Baek 4e7b0ea4b4 Merge pull request #17013 from athoik/fix-17000
fix: handle unicode filenames in external document loader
2025-08-31 23:58:52 +04:00
Timothy Jaeryang Baek c2b4976c82 enh: PGVECTOR_CREATE_EXTENSION env var 2025-08-31 23:58:18 +04:00
Timothy Jaeryang Baek e0ab5adb97 refac 2025-08-31 23:52:50 +04:00
Timothy Jaeryang Baek b0f6f24ca8 refac 2025-08-31 23:42:34 +04:00
Timothy Jaeryang Baek 61f530ff4b refac: styling 2025-08-31 23:06:58 +04:00
Tim Jaeryang Baek 7df1a059d6 Merge pull request #17012 from thomascooper/fork/feature/20250828-auth-oidc-support-pkce
fix: PKCE requires no secret, with no secret the login button does not eve…
2025-08-31 14:29:05 +04:00
Tim Jaeryang Baek 3e18187805 Merge pull request #17029 from aleixdorca/dev
i18n: Update Catalan translation.json
2025-08-31 13:59:40 +04:00
Tim Jaeryang Baek aa13ed885d Merge pull request #17019 from ShirasawaSama/patch-8
i18n: improve Chinese translations
2025-08-31 13:59:29 +04:00
Tim Jaeryang Baek 2a27db2d3b Merge pull request #17064 from hadadarjt/dev
fix: Resolve admin account creation on Hugging Face Spaces.
2025-08-31 13:34:20 +04:00
Tim Jaeryang Baek 89f005ece6 Merge pull request #17017 from joaoback/patch-3
Update translation.json (pt-BR)
2025-08-31 13:33:28 +04:00
Hadad 48afd424a3 fix: Resolve admin account creation on Hugging Face Spaces.
Signed-off-by: Hadad <hadad@linuxmail.org>
2025-08-31 14:33:48 +07:00
Andrew Baek b50ae5bf69 Update MessageInput.svelte 2025-08-31 14:48:37 +09:00
Adam 39ae9167ee removed test code. 2025-08-30 20:31:13 -04:00
Adam 20b6902b9f whitespace 2025-08-30 20:30:34 -04:00
Adam 08b958cfc9 re-add used var 2025-08-30 20:29:16 -04:00
Adam c62f30e22c remove whitespace 2025-08-30 20:12:46 -04:00
Adam 562710fe33 join the url instead of concatenating a string in case the user adds a slash to the end of their configured url. 2025-08-30 20:05:53 -04:00
_00_ 647e38f701 Revert bypass hybrid search when BM25_weight=0
Revert PR https://github.com/open-webui/open-webui/commit/74b1c801
2025-08-30 10:45:35 +02:00
_00_ 292cb62d4a FIX: Hybrid Search lexical-semantic tags
FIX Error in Hybrid Search lexical-semantic terms places

I was reviewing and I noticed that the lexical-semantic terms are inverted.

BM25 weight=1 --> lexical
BM25 weight=0 --> semantic
2025-08-30 01:48:31 +02:00
Andrew Baek 05223f720d Merge branch 'open-webui:dev' into dev 2025-08-29 22:12:04 +09:00
Aleix Dorca 32a303f392 Update catalan translation.json 2025-08-29 11:59:09 +02:00
Shirasawa 1d1a83b754 i18n: improve zh-CN translation 2025-08-29 03:49:15 +00:00
joaoback 1ca5ad47b1 Update translation.json (pt-BR)
Some minor translations improvements
2025-08-28 19:49:23 -03:00
Timothy Jaeryang Baek 3111d1bf61 refac 2025-08-29 02:07:31 +04:00
Athanasios Oikonomou d735b036fe fix: handle unicode filenames in external document loader
Files with special characters in their names (e.g., ü.pdf) caused issues since HTTP headers only allow Latin-1 characters.
This change URL-encodes `X-Filename` before adding it to request headers, preventing failures when uploading or processing such files.

Fixes: #17000
2025-08-28 22:19:50 +03:00
Thomas Cooper 9d80cc3b2d PKCE requires no secret, with no secret the login button does not ever show 2025-08-28 14:47:13 -04:00
Timothy Jaeryang Baek 0ebe4f8f84 refac: conditional USE_PERMISSION_HARDENING 2025-08-28 20:19:47 +04:00
Timothy Jaeryang Baek be373e9fd4 refac: dockerfile 2025-08-28 19:42:28 +04:00
Timothy Jaeryang Baek 898826dc22 Update dependabot.yml 2025-08-28 19:16:50 +04:00
Timothy Jaeryang Baek 0bca4e230e refac: rename tools to external tools for clarity 2025-08-28 15:08:13 +04:00
Timothy Jaeryang Baek ef5374a34e typo 2025-08-28 14:46:47 +04:00
Tim Jaeryang Baek 2407d9b905 Merge pull request #16859 from open-webui/dev
0.6.26
2025-08-28 14:40:19 +04:00
Timothy Jaeryang Baek 25d9a728f4 refac 2025-08-28 14:24:49 +04:00
Timothy Jaeryang Baek 103688936e doc: changelog 2025-08-28 14:19:12 +04:00
Tim Jaeryang Baek ac73f97604 Merge pull request #16993 from Classic298/patch-1
Changelog for 0.6.26
2025-08-28 14:05:53 +04:00
Classic298 0d09571ca2 Changelog for 0.6.26 2025-08-28 12:00:40 +02:00
Tim Jaeryang Baek 5b4409814b Merge pull request #16982 from a1cd/main
ci: slim image
2025-08-28 13:29:15 +04:00
Timothy Jaeryang Baek 8cca648efa refac 2025-08-28 13:25:48 +04:00
Timothy Jaeryang Baek a8222e391b chore: dep bump 2025-08-28 13:05:06 +04:00
Timothy Jaeryang Baek dca7ce9420 refac 2025-08-28 03:41:25 +04:00
Timothy Jaeryang Baek bcd5783752 chore: format 2025-08-28 03:38:06 +04:00
Timothy Jaeryang Baek 12bd04d24d refac/enh 2025-08-28 03:24:26 +04:00
Timothy Jaeryang Baek 2bb6063dcb refac/fix: marker 2025-08-28 03:03:31 +04:00
Timothy Jaeryang Baek 4d7c8b4a6a refac 2025-08-28 03:01:53 +04:00
Timothy Jaeryang Baek 40617b9e0e refac: file item modal 2025-08-28 02:59:45 +04:00
Timothy Jaeryang Baek 52030a241c refac 2025-08-28 02:50:19 +04:00
Timothy Jaeryang Baek 3d6605bbfd refac: hide steps in images 2025-08-28 02:48:08 +04:00
Everett Wilber a60b0a108a Ensure data directory exists before chown 2025-08-27 18:46:31 -04:00
Timothy Jaeryang Baek 48635ced35 refac 2025-08-28 02:45:06 +04:00
Everett Wilber f4dde86b36 Fix syntax error in Dockerfile pip install command 2025-08-27 18:40:17 -04:00
Everett Wilber fcc1e2729c Fix Dockerfile syntax for conditional installation 2025-08-27 18:37:49 -04:00
Timothy Jaeryang Baek e7c7c65227 refac/fix: error message 2025-08-28 02:35:29 +04:00
Everett Wilber b2d1aa3c6e Fix syntax error in conditional for Ollama installation 2025-08-27 18:35:00 -04:00
Everett Wilber 24767bb0fa Merge pull request #2 from a1cd/slimify
Slimify
2025-08-27 18:31:39 -04:00
Everett Wilber c000188728 Merge pull request #1 from a1cd/slimify-patch-1
Slimify patch 1
2025-08-27 18:29:28 -04:00
Everett Wilber db4adc0e89 Add build-slim-image job to Docker workflow 2025-08-27 18:27:16 -04:00
Timothy Jaeryang Baek cf08d34879 refac 2025-08-28 02:24:21 +04:00
Everett Wilber d2fdf6999b Add USE_SLIM argument to Dockerfile 2025-08-27 18:20:23 -04:00
Timothy Jaeryang Baek d9c8a2508f chore: format 2025-08-28 01:42:45 +04:00
Tim Jaeryang Baek df2428b356 Merge pull request #16965 from joaoback/patch-2
Update translation.json (pt-BR)
2025-08-27 17:25:54 +04:00
Timothy Jaeryang Baek e39ce16a86 enh: custom reasoning tags 2025-08-27 17:24:16 +04:00
joaoback d67cf5d9f2 Update translation.json (pt-BR)
I made some adjustments to some translations and also translated new items added.
2025-08-27 09:18:50 -03:00
Tim Jaeryang Baek 688bfac4ba Merge pull request #16946 from ShirasawaSama/patch-7
i18n: improve Chinese translation
2025-08-27 15:46:05 +04:00
Timothy Jaeryang Baek 7c2cb5899c refac 2025-08-27 15:25:24 +04:00
Shirasawa b16ad094be i18n: improve zh-CN translation 2025-08-27 17:06:45 +08:00
Tim Jaeryang Baek d5715cec6f Merge pull request #16944 from rgaricano/dev-FIX_RTL_in_messages-LTR_codeblock
FIX: Auto RTL-LTR text orientation in Messages & Reversion previous PR
2025-08-27 12:55:18 +04:00
Shirasawa c7caa268ce i18n: improve zh-TW translation 2025-08-27 14:08:32 +08:00
Shirasawa de8e8af989 i18n: improve zh-CN translation 2025-08-27 12:04:20 +08:00
_00_ ddc88d615c FIX: Auto RTL-LTR text orientation
FIX: Auto RTL-LTR text orientation

 This tag was a `<div>` and changed by `<li>` in commit https://github.com/open-webui/open-webui/commit/bb6864dd127e8bc0b0467dfb9eb87f460acecfa8#r164667886
& this change broke the previous behavior of auto text orientation.
2025-08-27 03:58:18 +02:00
_00_ 7644d66902 Revert last change in ResponseMessage.svelte 2025-08-27 03:53:51 +02:00
_00_ 177788540b Revert last changes in UserMessage.svelte 2025-08-27 03:51:16 +02:00
_00_ 65a9a7eae0 Revert changes for fix orientation in CodeBlock.svelte
Revert changes for fix orientation in CodeBlock.svelte
2025-08-27 03:46:07 +02:00
_00_ 804340ee26 Merge branch 'open-webui:main' into dev-FIX_RTL_in_messages-LTR_codeblock 2025-08-27 03:40:01 +02:00
Timothy Jaeryang Baek 937f97d7d6 chore: format 2025-08-27 04:28:24 +04:00
Timothy Jaeryang Baek f6637cc78e refac 2025-08-27 04:18:18 +04:00
Timothy Jaeryang Baek 29cd1bc739 enh: pull to refresh 2025-08-27 04:16:02 +04:00
Timothy Jaeryang BaekandJacob Leksan 31485835a7 enh: query caching
Co-Authored-By: Jacob Leksan <63938553+jmleksan@users.noreply.github.com>
2025-08-27 03:07:21 +04:00
Timothy Jaeryang Baek e4b6855984 enh: CHAT_RESPONSE_MAX_TOOL_CALL_RETRIES 2025-08-27 02:58:25 +04:00
Timothy Jaeryang Baek bbd3e38e25 enh: a11y 2025-08-27 00:52:18 +04:00
Timothy Jaeryang Baek b29d5b80cd refac: pypi optional-dependencies 2025-08-26 23:05:44 +04:00
Andrew Baek ceaafbbfd2 Update groups.py 2025-08-27 03:22:34 +09:00
Andrew Baek 7b0ce47eb8 Merge branch 'dev' of https://github.com/andrewbbaek/open-webui into dev 2025-08-27 03:08:21 +09:00
Timothy Jaeryang Baek edf7a3d142 refac 2025-08-26 22:05:27 +04:00
Andrew Baek a64586f9c2 Merge branch 'dev' of https://github.com/andrewbbaek/open-webui into dev 2025-08-27 02:57:43 +09:00
Timothy Jaeryang Baek 8b425a6295 refac 2025-08-26 21:56:52 +04:00
Timothy Jaeryang Baek d42e3fce4a refac 2025-08-26 21:26:18 +04:00
Andrew Baek e214d59d10 Update groups.py fix issue #16870 2025-08-27 01:04:27 +09:00
Timothy Jaeryang Baek 58cc57e8a4 refac 2025-08-26 17:39:52 +04:00
Timothy Jaeryang Baek d3a952877a refac: pdf export 2025-08-26 17:34:33 +04:00
Timothy Jaeryang Baekand_00_ 07357afcf6 refac
Co-Authored-By: _00_ <131402327+rgaricano@users.noreply.github.com>
2025-08-26 16:54:36 +04:00
Timothy Jaeryang Baek ceb4948a28 refac 2025-08-26 16:14:43 +04:00
Timothy Jaeryang Baek 86a8eb1023 refac/enh: pdf export 2025-08-26 16:05:02 +04:00
Timothy Jaeryang Baek dced9e4094 refac 2025-08-26 15:06:50 +04:00
Timothy Jaeryang BaekandG30 803b2e35be enh: delete_message, continue_response, regenerate_response, rate_response user permissions
Co-Authored-By: G30 <50341825+silentoplayz@users.noreply.github.com>
2025-08-26 15:05:35 +04:00
Timothy Jaeryang Baek 23a9731899 refac/fix: hybrid search 2025-08-26 15:04:46 +04:00
Tim Jaeryang Baek 4267e22d4a Merge pull request #16826 from selenecodes/feat/azure-document-intelligence-azure-entra-auth
feat: Authenticate Azure Document Intelligence using DefaultAzureCredential
2025-08-26 14:32:04 +04:00
Tim Jaeryang Baek e3207f35d7 Merge pull request #16885 from ShirasawaSama/patch-5
fix: fix Safari IME composition bug (#16615)
2025-08-26 14:26:12 +04:00
Tim Jaeryang Baek 85264e76de Merge pull request #16918 from ShirasawaSama/patch-6
fix: fix Windows sidebar button cursor icon
2025-08-26 13:52:29 +04:00
Tim Jaeryang Baek 1dc8056d84 Merge pull request #16853 from yuliang615/dev
Fix:Copy button in code blocks copies the original AI output instead of the edited content
2025-08-26 13:51:30 +04:00
Tim Jaeryang Baek 853c14b4c2 Merge pull request #16897 from ibuki2003/fix_translation_ja
i18n: improve ja-JP translation
2025-08-26 13:48:46 +04:00
Tim Jaeryang Baek 01dba25466 Merge pull request #16915 from SantiagoDePolonia/pl-translation-fix
i18n: updated Polish translation
2025-08-26 13:47:09 +04:00
Shirasawa 9c3f54cf1c fix: fix Windows sidebar button cursor style 2025-08-26 09:23:52 +00:00
Timothy Jaeryang Baek f4047eea77 fix: direct tool server 2025-08-26 13:15:47 +04:00
Jakub A. W 0583847943 fix: updated Polish translation 2025-08-26 06:45:01 +02:00
Tim Jaeryang Baek 4b22aa819c Merge pull request #16878 from rgaricano/dev-FIX_RTL_in_messages-LTR_codeblock
FIX- RTL text orientation in Messages, and LTR allways for CodeBlock
2025-08-26 02:25:01 +04:00
Timothy Jaeryang Baek 630cea105e fix: empty content in file modal 2025-08-25 18:22:28 +04:00
Timothy Jaeryang Baek c61698efcf enh: process_in_background query param for file upload endpoint 2025-08-25 18:18:52 +04:00
Timothy Jaeryang Baek a37d411dcf refac 2025-08-25 18:12:47 +04:00
ibuki2003 eacec793e9 i18n: improve ja-JP translation 2025-08-25 22:18:10 +09:00
Timothy Jaeryang Baek 0e46f8091f fix: __tools__ param issue 2025-08-25 14:29:05 +04:00
Shirasawa 7b9dc69362 fix: fix Safari IME composition bug 2025-08-25 14:37:51 +08:00
_00_ 0a16af4e96 rerun checks 2025-08-25 00:20:20 +02:00
_00_ 24e696e341 Format Update ResponseMessage.svelte 2025-08-25 00:01:01 +02:00
Timothy Jaeryang Baek 0ea421ea20 refac 2025-08-25 01:12:14 +04:00
Tim Jaeryang Baek 3da6049a4a Merge pull request #16862 from rgaricano/dev-es_ES
UPD: i18n es-ES Translation v.0.6.25
2025-08-25 01:11:16 +04:00
_00_ 3839f18bb1 FIX- LTR in Update CodeBlock.svelte
FIX- LTR in Update CodeBlock.svelte
For CodeBlock allways LTR
2025-08-24 23:07:32 +02:00
_00_ 2b0e90c032 FIX RTL in Update ResponseMessage.svelte
FIX RTL in Update ResponseMessage.svelte
2025-08-24 23:04:57 +02:00
_00_ 3a360e441e FIX-RTL in UserMessage.svelte
FIX-RTL in UserMessage.svelte
2025-08-24 23:03:24 +02:00
silentoplayz 8df74dde80 fix 2025-08-23 15:19:35 -04:00
silentoplayz c6d80496ab feat: improve ollama model management experience
This commit introduces several improvements to the Ollama model management modal:

- Adds a cancel button to the model pulling operation, using the existing 'x' button pattern.
- Adds a cancel button to the "Update All" models operation, allowing the user to cancel the update for the currently processing model.
- Cleans up toast notifications when updating all models. A single toast is now shown at the beginning and a summary toast at the end, preventing notification spam.
- Refactors the `ManageOllama.svelte` component to support these new cancellation features.
- Adds tooltips to all buttons in the modal to improve clarity.
- Disables buttons when their corresponding input fields are empty to prevent accidental clicks.
2025-08-23 15:08:07 -04:00
_00_ a463e58de6 Update translation.json
correctión lost "
2025-08-23 20:59:49 +02:00
_00_ 7a59cc9186 Update translation.json
Gramatical correction
2025-08-23 20:58:12 +02:00
_00_ ff6946c230 UPD: i18n es-ES Translation v.0.6.25
UPD: i18n es-ES Translation v.0.6.25

Added new strings in es-ES Translation
2025-08-23 20:53:32 +02:00
Tim Jaeryang Baek 3ff773c199 Merge pull request #16838 from silentoplayz/shift-to-delete-prompts
feat: Add Shift-to-delete functionality for prompts on Workspace `Prompts` page
2025-08-23 21:57:09 +04:00
Tim Jaeryang Baek 8048d4ac39 Merge pull request #16850 from rgaricano/dev-FIX_playwright_timeout
FIX: Playwright Timeout (ms) interpreted as seconds
2025-08-23 21:55:48 +04:00
Tim Jaeryang Baek 8d7319441e Merge pull request #16848 from Kylapaallikko/dev
i18n: Update fi-FI translation
2025-08-23 21:51:16 +04:00
洪宇亮 d98a60fbbb Add files via upload 2025-08-23 21:42:35 +08:00
_00_ 093af754e7 FIX: Playwright Timeout (ms) interpreted as seconds
Fix for Playwright Timeout (ms) interpreted as seconds.

To address https://github.com/open-webui/open-webui/issues/16801

In Frontend Playwright Timeout is setted as (ms), but in backend is interpreted as (s) doing a time conversion for playwright_timeout var (that have to be in ms).

& as  _Originally posted by @rawbby in [#16801](https://github.com/open-webui/open-webui/issues/16801#issuecomment-3216782565)_

> I personally think milliseconds are a reasonable choice for the timeout. Maybe the conversion should be fixed, not the label.
> This would further not break existing configurations from users that rely on their current config.
>
2025-08-23 14:15:00 +02:00
Kylapaallikko fa1590df57 Update fi-FI translation.json
Added missing translations and fixed typos.
2025-08-23 09:16:49 +03:00
silentoplayz 9e7ec0eb1e feat: add shift-to-delete to prompts workspace
This commit adds the Shift key shortcut to the Prompts workspace page to reveal a trash can icon for quick deletion of prompts. This feature is already present for the Models, Tools, and Functions pages.
2025-08-22 16:09:21 -04:00
Selene Blok 5051bfe7ab feat(document retrieval): Authenticate Azure Document Intelligence using AzureDefaultCredential if API key is not provided 2025-08-22 16:15:43 +02:00
Tim Jaeryang Baek 1db8dec4f5 Merge pull request #16821 from open-webui/dev
0.6.25
2025-08-22 17:22:31 +04:00
Timothy Jaeryang Baek dafedf5675 chore: bump 2025-08-22 17:20:05 +04:00
Timothy Jaeryang Baek 37a3de0703 fix 2025-08-22 17:19:57 +04:00
Timothy Jaeryang Baek 72b25ab78b fix: image generation 2025-08-22 16:58:25 +04:00
Timothy Jaeryang Baek fbff4e19de fix: reranking 2025-08-22 16:47:05 +04:00
Tim Jaeryang Baek 2777bab148 Merge pull request #16810 from open-webui/dev
0.6.24
2025-08-22 14:06:05 +04:00
Timothy Jaeryang Baek 4ec70e3d46 refac 2025-08-22 14:05:33 +04:00
Timothy Jaeryang Baek 43b2eca418 refac 2025-08-22 14:01:57 +04:00
Timothy Jaeryang Baek 99f7d1b62b doc: changelog 2025-08-22 13:57:37 +04:00
Timothy Jaeryang Baek 62a5db7719 chore: bump 2025-08-22 13:46:10 +04:00
Timothy Jaeryang Baek 278a4edd0b fix: tool server not loading 2025-08-22 13:44:03 +04:00
Timothy Jaeryang Baek 279e3e970f fix: redis session issue 2025-08-22 13:30:44 +04:00
Timothy Jaeryang Baek 88a5b5ff4c fix: image gen 2025-08-22 13:25:23 +04:00
Tim Jaeryang Baek c159a1a120 Merge pull request #16783 from itk-dev/feature/high-contrast-chat-messages
FEAT: highContrastMode implemented in chat messages
2025-08-22 13:22:09 +04:00
Tim Jaeryang Baek 1decefbe59 Merge pull request #16808 from aleixdorca/dev
i18n: Update Catalan translation.json
2025-08-22 13:21:32 +04:00
Tim Jaeryang Baek 28157b4f8c Merge pull request #16802 from joaoback/patch-1
Update translation.json (pt-BR)
2025-08-22 13:21:22 +04:00
Aleix Dorca e013df19f9 Update catalan translation.json 2025-08-22 10:23:17 +02:00
joaoback 486f953ba2 Update translation.json (pt-BR)
new translations for the new items that were included in the latest versions.
2025-08-21 22:49:53 -03:00
Tim Jaeryang Baek 407dc9a401 Merge pull request #16507 from open-webui/dev
0.6.23
2025-08-21 22:21:10 +04:00
Timothy Jaeryang Baek 9fb2c9c86d refac 2025-08-21 22:02:11 +04:00
Timothy Jaeryang Baek 60b8cfb9fa refac 2025-08-21 21:48:21 +04:00
Tim Jaeryang Baek 5a66f69460 Merge pull request #16779 from mahenning/fix--clean-unload-embed/reranker-models
Fix: Free VRAM memory when updating embedding / reranking models
2025-08-21 21:38:37 +04:00
Timothy Jaeryang Baek 578f116ebb refac 2025-08-21 21:37:40 +04:00
Tim Jaeryang Baek d3b5ba8472 Merge pull request #16767 from Classic298/patch-2
Chore: 0.6.23 changelog
2025-08-21 21:28:12 +04:00
Timothy Jaeryang Baek 3e28c46026 refac 2025-08-21 21:25:42 +04:00
Tim Jaeryang Baek 71d7ba6c07 Merge pull request #16785 from ShirasawaSama/i18n/improve-chinese-translation
i18n: improve Chinese translation
2025-08-21 21:21:50 +04:00
Shirasawa 581c0f137e i18n: improve chinese translation 2025-08-21 22:01:05 +08:00
Marko Henning f2e78d7940 More formatting 2025-08-21 13:42:03 +02:00
Marko Henning c821c3ecb0 Formatting 2025-08-21 13:40:56 +02:00
Marko Henning b3de3295d6 Chage torch import to conditional import 2025-08-21 13:19:24 +02:00
Sine Jespersen 0952772705 highContrastMode implemented in chat messages 2025-08-21 12:52:32 +02:00
Classic298 c0189e43ad Update CHANGELOG.md 2025-08-21 12:15:41 +02:00
Classic298 8fad4fb264 Update CHANGELOG.md 2025-08-21 12:07:29 +02:00
Timothy Jaeryang Baek e9cab818f8 chore: dep bump 2025-08-21 14:04:17 +04:00
Timothy Jaeryang Baek 3523cea8cc chore: bump 2025-08-21 14:01:10 +04:00
Timothy Jaeryang Baek 01002042a8 refac: styling 2025-08-21 13:59:45 +04:00
Tim Jaeryang Baek f81f3b3a02 Merge pull request #16777 from itk-dev/feature/make-chat-messages-an-unordered-list
FEAT: Make chat message an unordered list
2025-08-21 13:29:27 +04:00
Timothy Jaeryang Baek 99ee1a2702 chore: format 2025-08-21 13:19:11 +04:00
Timothy Jaeryang Baek 7ede54ee0a refac 2025-08-21 13:13:38 +04:00
Tim Jaeryang Baek e96d139180 Merge pull request #16778 from itk-dev/feature/aria-hidden-img
FEAT: hide image from screen readers
2025-08-21 13:09:13 +04:00
Timothy Jaeryang Baek e6da38464b refac: ENABLE_ADMIN_WORKSPACE_CONTENT_ACCESS renamed to BYPASS_ADMIN_ACCESS_CONTROL 2025-08-21 13:08:22 +04:00
Sine Jespersen 1e9abe6d8c hide image from screen readers 2025-08-21 11:01:57 +02:00
Classic298 61af00d0a6 Update CHANGELOG.md 2025-08-21 10:55:54 +02:00
Sine Jespersen bb6864dd12 Make chat message an unordered list 2025-08-21 10:55:38 +02:00
Classic298 1404fd9fbd Update CHANGELOG.md 2025-08-21 10:53:15 +02:00
Timothy Jaeryang Baek 02479425a5 refac 2025-08-21 12:51:41 +04:00
Timothy Jaeryang Baek cd762db9bf refac 2025-08-21 12:50:11 +04:00
Marko Henning 6663fc3a6c Unloads only if internal models are used. 2025-08-21 10:49:03 +02:00
Tim Jaeryang Baek 67db71f140 Merge pull request #16770 from itk-dev/feature/remove-extra-chat-message
Remove extra chat message as it is read twice for screen readers
2025-08-21 12:47:47 +04:00
Tim Jaeryang Baek aa94359f74 Merge pull request #16772 from itk-dev/feature/h2-and-section-in-messages
add h2 and section tags to messages
2025-08-21 12:45:31 +04:00
Timothy Jaeryang Baek 9726c0b7ee refac/fix: s3 checksum validation 2025-08-21 12:44:16 +04:00
Classic298 449f0c155b Update CHANGELOG.md 2025-08-21 10:40:10 +02:00
Sine Jespersen 48f0992300 add h2 and section tags to messages 2025-08-21 10:34:46 +02:00
Tim Jaeryang Baek 492bcf9d26 Merge pull request #16769 from itk-dev/feature/missing-id-banner-iteration
fix: banner id to iteration
2025-08-21 12:34:07 +04:00
Timothy Jaeryang Baekand_00_ bbfe456f90 fix: models not loading
Co-Authored-By: _00_ <131402327+rgaricano@users.noreply.github.com>
2025-08-21 12:33:26 +04:00
Sine Jespersen 774140ae0f Remove extra chat message as it is read twice for screen readers 2025-08-21 10:16:42 +02:00
Sine Jespersen 5590ad438f add banner id to iteration 2025-08-21 09:31:57 +02:00
Classic298 9eea1aff10 0.6.23 2025-08-21 08:58:26 +02:00
Timothy Jaeryang Baek 1a15a62b73 chore: format 2025-08-21 04:47:28 +04:00
Timothy Jaeryang Baek efcf6db687 refac 2025-08-21 04:46:43 +04:00
Timothy Jaeryang Baek 66f00ce4bf refac 2025-08-21 04:40:08 +04:00
Timothy Jaeryang Baek d720e027ad enh: move chat menu 2025-08-21 04:35:27 +04:00
Timothy Jaeryang Baek f46837fd10 refac 2025-08-21 04:02:34 +04:00
Timothy Jaeryang Baek 1477f14827 refac: styling 2025-08-21 03:41:01 +04:00
Timothy Jaeryang Baek 094a16ab49 refac 2025-08-21 03:38:26 +04:00
Timothy Jaeryang Baek 86011e40be refac: account details 2025-08-21 02:39:25 +04:00
Timothy Jaeryang Baek 4451f86eb0 refac 2025-08-21 01:22:32 +04:00
Timothy Jaeryang Baek 8f51c11bc2 refac: account setting styling 2025-08-21 01:20:38 +04:00
Timothy Jaeryang Baek 01f438c3fe refac 2025-08-21 00:05:37 +04:00
Timothy Jaeryang Baek 1f91e5d3c6 fix: multi-ollama mounted display issue 2025-08-21 00:03:38 +04:00
Timothy Jaeryang BaekandAdam Outler faf01bdeac fix: None Type tool installation during startup
Co-Authored-By: Adam Outler <adamoutler@gmail.com>
2025-08-20 23:58:08 +04:00
Tim Jaeryang Baek bb21bf5239 Merge pull request #16761 from ShirasawaSama/i18n/improve-chinese-translation
i18n: Improve Chinese translation
2025-08-20 23:16:41 +04:00
Timothy Jaeryang Baek 919d65f36f feat/enh: ENABLE_OTEL_TRACES granular otel support 2025-08-20 23:03:12 +04:00
Timothy Jaeryang Baek 19e82ace23 feat: temp chat as default 2025-08-20 22:49:05 +04:00
Shirasawa 98159dbcae i18n: improve chinese translation 2025-08-21 02:42:21 +08:00
Timothy Jaeryang Baek ba972ecd52 refac 2025-08-20 21:05:04 +04:00
Tim Jaeryang Baek d2d23f4abd Merge pull request #16753 from PeterDaveHello/i18n
i18n: add missing translations
2025-08-20 21:02:17 +04:00
Peter Dave Hello 45180e829a i18n: add missing translations 2025-08-20 22:14:23 +08:00
Marko Henning cd02ff2e07 Fix if checks 2025-08-20 14:07:13 +02:00
Marko Henning 39fe385017 Correctly unloads embedding/reranker models 2025-08-20 13:30:45 +02:00
Tim Jaeryang Baek 7452b87877 Merge pull request #16741 from 0xThresh/s3vector-support
fix: batch S3 vectors in groups of 500 to comply with API limitations
2025-08-20 13:25:42 +04:00
James W. 45d9a720b9 Merge branch 'open-webui:main' into s3vector-support 2025-08-19 22:06:16 -06:00
0xThresh.eth 7fcc545672 fix: batch S3 vectors in groups of 500 to comply with API limitations 2025-08-19 22:05:47 -06:00
Timothy Jaeryang Baek 5e1f4fa0ff feat: async file upload 2025-08-20 00:36:13 +04:00
Timothy Jaeryang Baek efebf4d3a0 refac: disable rich text input in knowledge base 2025-08-20 00:33:46 +04:00
Timothy Jaeryang Baek e304ca5bd5 refac: styling 2025-08-19 23:28:05 +04:00
Timothy Jaeryang Baek 8eb5807c5f refac: images generation endpoint size field 2025-08-19 23:14:41 +04:00
Timothy Jaeryang Baek 7670543a39 refac 2025-08-19 17:20:42 +04:00
Timothy Jaeryang Baek ed926c8f26 revert 2025-08-19 17:07:24 +04:00
Timothy Jaeryang Baekanddecent-engineer-decent-datascientist b3a95f40fc refac/enh: add performance indexes
Co-Authored-By: decent-engineer-decent-datascientist <77806775+decent-engineer-decent-datascientist@users.noreply.github.com>
2025-08-19 03:24:10 +04:00
Tim Jaeryang Baek 96643f5b6d Merge pull request #14682 from olivier-lacroix/genai-tool-function
refactor: Improve tool callable generation to allow for genai native function call
2025-08-19 03:16:12 +04:00
Timothy Jaeryang Baek 575db66295 feat: save temporary chats 2025-08-19 02:37:18 +04:00
Timothy Jaeryang Baek 47ec443728 refac: temp chat button location 2025-08-19 02:03:43 +04:00
Timothy Jaeryang Baek d6f709574e refac/enh: async process chat handling 2025-08-19 01:24:53 +04:00
Timothy Jaeryang Baek 4bc77b544e refac 2025-08-18 23:39:14 +04:00
Timothy Jaeryang Baek be93081bd7 refac: only generate tags when content is non-empty 2025-08-18 23:36:11 +04:00
Timothy Jaeryang Baek 70d0477418 refac: tool name collision handling 2025-08-18 21:28:28 +04:00
Timothy Jaeryang Baek f592748011 refac: tool server redis cache 2025-08-18 20:53:46 +04:00
Timothy Jaeryang Baek 8a157578f4 enh/refac: ability to specify external tool server id 2025-08-18 20:38:55 +04:00
Timothy Jaeryang Baek 8a1f321fd4 refac: refresh tool list on show 2025-08-18 20:13:09 +04:00
Timothy Jaeryang Baek f97f21bf3a refac/fix: rename WEB_SEARCH_CONCURRENT_REQUESTS to WEB_LOADER_CONCURRENT_REQUESTS 2025-08-18 20:06:36 +04:00
Tim Jaeryang Baek a651598d19 Merge pull request #16712 from ShirasawaSama/feat--dynamic-loading-of-kokoro-js
feat: dynamic loading of kokoro-js
2025-08-18 19:50:10 +04:00
Timothy Jaeryang Baek 68d42ef850 refac 2025-08-18 19:49:29 +04:00
Tim Jaeryang Baek 979ec71522 Merge pull request #16711 from ShirasawaSama/i18n/improve-chinese-translation
i18n: improve zh-TW translation
2025-08-18 19:46:17 +04:00
Timothy Jaeryang Baek 6d0f757848 refac: openai model list 2025-08-18 19:45:26 +04:00
Shirasawa dc9445c4ec feat: dynamic loading of kokorojs 2025-08-18 23:43:54 +08:00
Timothy Jaeryang Baek 901c997054 refac 2025-08-18 19:43:18 +04:00
Shirasawa 99b88c3024 i18n: improve zh-tw translation 2025-08-18 23:37:07 +08:00
Timothy Jaeryang Baek 094a82b264 refac: follow up prompt template 2025-08-18 15:53:44 +04:00
Tim Jaeryang Baek 99604fd644 Merge pull request #16683 from ShirasawaSama/i18n/improve-chinese-translation
i18n: improve zh-cn translation
2025-08-18 14:36:01 +04:00
Shirasawa 33ecc405cb i18n: improve zh-cn translation 2025-08-18 07:27:21 +00:00
Shirasawa ad49435a94 i18n: fix typo 2025-08-18 03:54:06 +00:00
Shirasawa 282a884fad i18n: improve zh-cn translation 2025-08-17 15:13:02 +08:00
Timothy Jaeryang Baek d7363fd65f fix: arena model selected model id 2025-08-17 04:55:45 +04:00
Timothy Jaeryang Baek f23eb2a31c refac: audio lang fallback logic 2025-08-17 04:33:42 +04:00
Timothy Jaeryang Baek ccd2a0be5b refac 2025-08-17 04:15:13 +04:00
Tim Jaeryang Baek 47560d4d72 Merge pull request #14703 from rragundez/code-interpreter-blacklist
feat: Blacklist modules from arbitrary code execution in code interpreter
2025-08-17 04:12:12 +04:00
Timothy Jaeryang Baek 7f0c50f445 refac 2025-08-17 04:06:16 +04:00
Timothy Jaeryang Baek bed6aa63e1 refac 2025-08-17 03:59:00 +04:00
Tim Jaeryang Baek 6a109e972e Merge pull request #15863 from tcx4c70/feat/sqlite-wal
perf(db): Improve performance of db, especially sqlite
2025-08-17 03:55:59 +04:00
Timothy Jaeryang Baek 2387877dae refac/fix: model name retrieval edge case 2025-08-17 03:50:55 +04:00
Timothy Jaeryang Baek 40f60c163d fix: her theme 2025-08-17 03:43:39 +04:00
Timothy Jaeryang Baek 28c1de237b fix: missing banner type 2025-08-17 03:31:50 +04:00
Timothy Jaeryang Baek 04bd09d027 refac: styling 2025-08-17 03:24:05 +04:00
Timothy Jaeryang Baek 35e8c77025 refac/fix 2025-08-17 03:21:14 +04:00
Tim Jaeryang Baek ffd9e9cbe7 Merge pull request #16654 from sihyeonn/fix/sh-handling-last-delta-data
fix(utils/middleware): flush pending chat deltas on stream termination
2025-08-17 00:04:24 +04:00
Tim Jaeryang Baek 8f5843c8bc Merge pull request #16647 from BoFFire/dev
i18n: update kabyle translation
2025-08-17 00:00:04 +04:00
Tim Jaeryang Baek 397421ce0b Merge pull request #16623 from Classic298/patch-1
Small DE-de updates
2025-08-16 23:59:53 +04:00
Tim Jaeryang Baek 0dc6220ae6 Merge pull request #16653 from FukkenShit/ios-overscroll-fix
fix: disabled overscroll for iOS frontend
2025-08-16 23:59:42 +04:00
Tim Jaeryang Baek 8ae99ad7bb Merge pull request #16665 from silentoplayz/valves-fix
fix: revert accidental change
2025-08-16 22:43:44 +04:00
silentoplayz fe4b7621b3 fix: revert accidental change 2025-08-16 02:26:51 -04:00
Sihyeon Jang 3da22af859 fix(utils/middleware): flush pending chat deltas on stream termination
Guarantees the last partial delta chunk is emitted when the SSE stream closes (EOF, break, or `[DONE]` sentinel).

* Buffer `last_delta_data` and track `delta_count`
* Flush automatically once `delta_count >= chunk_size`
* Perform a final flush after the iterator ends

Signed-off-by: Sihyeon Jang <sihyeon.jang@navercorp.com>
2025-08-16 13:03:57 +09:00
FukkenShit cb1445086e fixed ios overscroll 2025-08-16 04:50:45 +04:00
ButterflyOfFire 9ba424e18a i18n: update kabyle translation 2025-08-15 18:16:50 +01:00
Tim Jaeryang Baek dfc9412117 Merge pull request #16622 from SebLz/fix/arbitrary-uid
Fix/arbitrary uid
2025-08-15 14:55:40 +04:00
Timothy Jaeryang Baek 43aa23ea77 refac 2025-08-15 14:15:27 +04:00
Tim Jaeryang Baek 286896b941 Merge pull request #16641 from silentoplayz/fix-alembic-migration-fails
fix: Improve path resolution and Alembic configuration (also a fix)
2025-08-15 14:14:34 +04:00
silentoplayz b810868239 fix: Alembic CLI commands from failing 2025-08-15 04:17:47 -04:00
Tim Jaeryang Baek 0b59aa940e Merge pull request #16606 from Rain6435/fix/azure-postgresql-pgvector-permissions
fix: resolve Azure PostgreSQL pgvector extension permission issue
2025-08-15 00:59:04 +04:00
Timothy Jaeryang Baek f1c28455ad refac: async webhook request 2025-08-15 00:07:02 +04:00
Timothy Jaeryang Baek daa4b3284f refac/fix: OAUTH_GROUPS_CLAIM env var 2025-08-14 23:59:54 +04:00
Tim Jaeryang Baek 72023f4d03 Merge pull request #16620 from FrameXX/improve-cs-translation
i18n: Improve Czech translation
2025-08-14 23:48:32 +04:00
Classic298 313c54a4d1 Small DE-de updates 2025-08-14 16:13:39 +02:00
LIESLEN c82183f985 Remove CI workflow (not part of PR) 2025-08-14 15:56:47 +02:00
Jiří Král 84fe62d557 Improve Czech translation 2025-08-14 15:11:30 +02:00
Tim Jaeryang Baek 43234c3c4d Merge pull request #16619 from ShirasawaSama/patch-4
fix: fix the Enter issue of Chinese input (fix #16615)
2025-08-14 17:03:09 +04:00
Timothy Jaeryang Baek 9e2d8470d4 refac 2025-08-14 17:01:31 +04:00
Timothy Jaeryang Baek 057533f9cc refac 2025-08-14 16:56:08 +04:00
Shirasawa e277dba8fe fix: fix the Enter issue of Chinese input 2025-08-14 12:45:58 +00:00
LIESLEN 4525ac687b feat: add Docker publish workflow for multi-architecture builds
fix: Dockerfile: make image arbitrary-UID friendly for OpenShift (group 0 + g+rwX, SGID, no fixed USER)
2025-08-14 13:54:31 +02:00
Timothy Jaeryang Baek 53f1caf91f fix: inline citations click issue 2025-08-14 14:33:49 +04:00
Timothy Jaeryang Baek 7dd5c957cf refac 2025-08-14 13:31:04 +04:00
Rain6435 a1e62ab422 fix: Formatting 2025-08-14 01:50:57 -04:00
Rain6435 1a42e96a3b fix: resolve Azure PostgreSQL pgvector extension permission issue
Replace direct CREATE EXTENSION commands with conditional checks to avoid
  permission errors on Azure PostgreSQL Flexible Server where only
  azure_pg_admin members can create extensions.

  - Check pg_extension table before attempting to create vector extension
  - Apply same fix to pgcrypto extension for consistency
  - Allows following least privilege principle for database users

  Fixes #12453
2025-08-14 01:45:02 -04:00
Timothy Jaeryang Baek 13fe78428b refac 2025-08-14 04:50:33 +04:00
Tim Jaeryang Baek 50705f926e Merge pull request #16601 from silentoplayz/i18n-updates
i18n.t: internationalize more strings throughout the codebase
2025-08-14 04:28:17 +04:00
silentoplayz 709258bbb5 fix: Add missing '$' 2025-08-13 20:24:05 -04:00
Timothy Jaeryang Baek d303f0351d refac 2025-08-14 04:19:54 +04:00
silentoplayz 82ed9b0a97 i18n.t: updates 2025-08-13 20:15:16 -04:00
Timothy Jaeryang Baek e13f1738d4 refac/fix: user message toolbar display 2025-08-14 04:12:30 +04:00
Timothy Jaeryang Baek b43acc2b3d refac/fix: tag attribute handling 2025-08-14 04:04:34 +04:00
Timothy Jaeryang Baek 3e8d3b08fa refac 2025-08-14 04:04:20 +04:00
Timothy Jaeryang Baek c134de8799 refac: knowledge search fuse threshold 2025-08-14 03:52:11 +04:00
Timothy Jaeryang Baek ad98d4300b refac/fix: milvus query logic 2025-08-14 03:18:38 +04:00
Timothy Jaeryang Baek 115231c0e5 refac/fix: dockerfile ollama cache issue 2025-08-14 02:55:38 +04:00
Timothy Jaeryang Baek 72168b1586 fix: community function import 2025-08-14 02:42:28 +04:00
Timothy Jaeryang Baek 93205d4320 fix: pending user display content 2025-08-14 02:27:24 +04:00
Timothy Jaeryang Baek 64b6ac4c1f refac/fix: sortable items isseu 2025-08-14 02:21:28 +04:00
Timothy Jaeryang Baek 28faaa23c4 refac/fix: dynamic height for command options 2025-08-14 02:14:23 +04:00
Timothy Jaeryang Baek 2ed9896dea refac/fix: oauth jwt cookie 2025-08-14 02:00:38 +04:00
Timothy Jaeryang Baek 84fd0e4152 refac: styling 2025-08-13 23:10:50 +04:00
Timothy Jaeryang Baek 7fb0228ac8 refac: conditionally display update password 2025-08-13 21:58:17 +04:00
Tim Jaeryang Baek 0845b7ffe5 Merge pull request #16582 from koflerm/patch-1
Fix: Retrieve Username Claim from Userinfo Endpoint
2025-08-13 19:23:44 +04:00
Tim Jaeryang Baek 55b6a82cd5 Merge pull request #16497 from timovanasten/fix-dutch-translation
i18n: Fix dutch translation
2025-08-13 19:18:57 +04:00
Timothy Jaeryang Baek ff55ca4d75 fix: temp chat not working 2025-08-13 19:18:30 +04:00
Markus Kofler 2011e5711d Enable Retrieving Username Claim from Userinfo Endpoint 2025-08-13 16:27:25 +02:00
Timothy Jaeryang Baek e7d9755d97 refac 2025-08-13 18:18:30 +04:00
Tim Jaeryang Baek 20101579e0 Merge pull request #16498 from Classic298/fix_model_access
fix: Chat model selector bypasses ENABLE_ADMIN_WORKSPACE_CONTENT_ACCESS privacy setting
2025-08-13 18:11:46 +04:00
Tim Jaeryang Baek 8a745b9bbf Merge branch 'dev' into fix_model_access 2025-08-13 18:07:29 +04:00
Tim Jaeryang Baek 2cb87a71f7 Merge pull request #16492 from Kylapaallikko/dev
i18n: Update fi-FI translation
2025-08-13 18:03:17 +04:00
Tim Jaeryang Baek 86a3e0a0b8 Merge pull request #16503 from athoik/user_groups_nav
feat: Allow navigating to user group from user edit
2025-08-13 18:02:40 +04:00
Tim Jaeryang Baek 37b8ea3ab2 Merge pull request #16557 from ShirasawaSama/patch-3
feat: Add administrator permission control (BYPASS_MODEL_ACCESS_CONTROL) to the two remaining main APIs
2025-08-13 17:56:39 +04:00
Timothy Jaeryang Baek 4cca1829c2 refac 2025-08-13 17:49:57 +04:00
Timothy Jaeryang Baek 230e21b10c Revert "Merge pull request #16547 from Gyarbij/main"
This reverts commit dd3cd4ac4f, reversing
changes made to f0c2d47b24.
2025-08-13 17:49:19 +04:00
Tim Jaeryang Baek dd3cd4ac4f Merge pull request #16547 from Gyarbij/main
fix: improve model sorting by handling missing names
2025-08-13 17:48:46 +04:00
Tim Jaeryang Baek f0c2d47b24 Merge pull request #16563 from ShirasawaSama/i18n/imrpove-zh-tw-translation
i18n: Improve Chinese translation (zh_TW)
2025-08-13 17:39:42 +04:00
Tim Jaeryang Baek 5fa502e419 Merge pull request #16565 from silentoplayz/i18n-fix
i18n: a few localization fixes
2025-08-13 17:39:33 +04:00
Timothy Jaeryang Baek 5a2fafceae fix: releases link 2025-08-13 17:23:28 +04:00
silentoplayz b5ab9634e8 i18n.t: localization fixes 2025-08-13 07:36:45 -04:00
Shirasawa e289e02193 i18n: improve chinese translation 2025-08-13 19:00:47 +08:00
Shirasawa e966f54001 feat: Add administrator permission control to the two remaining APIs 2025-08-13 07:02:36 +00:00
Athanasios Oikonomou c11f02662f fix: use prettier to format last commit 2025-08-13 09:36:18 +03:00
Chono N 23575825a1 fix: improve model sorting by handling missing names 2025-08-12 22:27:59 +02:00
Tim Jaeryang Baek 1e67035bd3 Merge pull request #16523 from expruc/perf/hybrid_search_bm_25
perf: disable collection retrieval and bm_25 calculation if bm_25 weight is 0
2025-08-12 23:55:26 +04:00
Tim Jaeryang Baek 85077d198b Merge pull request #16537 from PeterDaveHello/i18n
Fix missing translation for system prompt placeholder
2025-08-12 23:54:47 +04:00
Timothy Jaeryang Baek 7f17b31c12 fix: temp chat indicator styling 2025-08-12 21:43:10 +04:00
Tim Jaeryang Baek 01e54dae94 Merge pull request #16519 from alpha-pet/feat-configurable-timeout-tool-calls
feat: use AIOHTTP_CLIENT_TIMEOUT for tool calls to make timeout configurable
2025-08-12 21:34:44 +04:00
Tim Jaeryang Baek 47cde38369 Merge pull request #16509 from ShirasawaSama/patch-2
i18n: Improve zh-CN translation
2025-08-12 21:33:12 +04:00
Tim Jaeryang Baek 4616997e6a Merge pull request #16502 from joaoback/patch-1
Update translation.json (pt-BR)
2025-08-12 21:32:50 +04:00
Tim Jaeryang Baek 1aba7accbf Merge pull request #16524 from BoFFire/patch-1
i18n: update kabyle translations
2025-08-12 21:23:17 +04:00
Peter Dave Hello 32753326fa i18n: Fix missing translation for system prompt placeholder 2025-08-12 22:33:50 +08:00
ButterflyOfFire 966e244e33 Merge branch 'open-webui:main' into patch-1 2025-08-12 15:32:52 +01:00
ButterflyOfFire 0c5950fb60 Using prettier with tabs 2025-08-12 14:47:11 +01:00
ButterflyOfFire 1d3c656286 i18n: update kabyle translations
Hi, Here is a 58% translation progress.
2025-08-12 14:32:14 +01:00
expruc 74b1c80132 disable collection retrieval and bm_25 calculation if bm_25 weight is 0 or less 2025-08-12 15:53:39 +03:00
Thomas Rehn 7aa41d5d0c feat: use AIOHTTP_CLIENT_TIMEOUT for tool calls to make timeout configurable 2025-08-12 12:29:02 +02:00
Shirasawa 4c30f22444 Update translation.json 2025-08-12 15:28:55 +08:00
Shirasawa 6af94ae8f3 Update translation.json 2025-08-12 14:16:26 +08:00
Shirasawa 2625cd8719 Update translation.json 2025-08-12 12:10:09 +08:00
Timothy Jaeryang Baek f91da291d9 refac: reactive user settings 2025-08-12 03:48:04 +04:00
Athanasios Oikonomou 5543f30c49 feat: Allow navigating to user group from user edit
This commit allow navigating from user edit to user group, allowing faster updates to groups.
The querystringValue function was moved to lib/utils to reuse it in multiple places.
2025-08-12 01:08:56 +03:00
joaoback 06666cb148 Update translation.json
Several improvements were made and new translations were included for those items that had not yet been translated.
2025-08-11 18:49:40 -03:00
Classic298 df314fda1d Update main.py 2025-08-11 23:41:49 +02:00
Classic298 f758bf74c2 Update main.py 2025-08-11 23:39:01 +02:00
Classic298 357b57e1d6 Update models.py 2025-08-11 23:36:48 +02:00
Timo van Asten a10e038fff Update translation.json 2025-08-11 23:23:49 +02:00
Classic298 d8c4dd6f79 Fix admin model access (#17)
* Update models.py

* Update models.py

* Update models.py

* Update ollama.py

* Update openai.py

* Update models.py

* Update openai.py

* Update ollama.py
2025-08-11 23:23:44 +02:00
Timo van Asten 7ace997c53 Update translation.json 2025-08-11 23:13:04 +02:00
Timothy Jaeryang Baek 62506b1955 fix: duckduckgo isn't duckduckgo anymore 2025-08-11 22:43:29 +04:00
Kylapaallikko 53c9c48387 Update fi-FI translation.json
Added missing translations
2025-08-11 19:11:56 +03:00
Timothy Jaeryang Baek 2df4e7207b refac 2025-08-11 17:38:00 +04:00
Timothy Jaeryang Baek f890fe6901 enh: allow plaintext for external tool servers 2025-08-11 17:36:36 +04:00
Tim Jaeryang Baek 438e5d966f Merge pull request #16456 from open-webui/dev
0.6.22
2025-08-11 17:15:28 +04:00
Timothy Jaeryang Baek 17cc3b7d72 fix: chat item generate title button 2025-08-11 17:13:22 +04:00
Timothy Jaeryang Baek d89c5b6c30 chore: format 2025-08-11 17:01:56 +04:00
Timothy Jaeryang Baek 8fd5006b6f refac 2025-08-11 17:01:18 +04:00
Timothy Jaeryang Baek 97448e25ec fix: openai error handling 2025-08-11 17:00:06 +04:00
Timothy Jaeryang Baek 8916a284bd chore: format 2025-08-11 16:43:17 +04:00
Timothy Jaeryang Baek 8e2aaaf94f chore: bump 2025-08-11 16:42:40 +04:00
Tim Jaeryang Baek e15f8b8baf Merge pull request #16458 from Classic298/patch-3
chore: 0.6.22 changelog
2025-08-11 16:37:24 +04:00
Timothy Jaeryang Baek 890691319f fix: s3vector import issue 2025-08-11 16:23:08 +04:00
Timothy Jaeryang Baek 21094ca88b fix: pinecone insert issue 2025-08-11 16:22:58 +04:00
Tim Jaeryang Baek 9aea18f90c Merge pull request #16479 from EventHorizon-AI/fix/landing-page-mode
fix: Correctly determine the Landing Page Mode
2025-08-11 16:18:55 +04:00
Timothy Jaeryang Baek 6202e09348 refac 2025-08-11 14:09:35 +04:00
EntropyYue 29ba56757d fix: Correctly determine the Landing Page Mode 2025-08-11 17:59:01 +08:00
Timothy Jaeryang Baek fbf5f2bb67 refac: disable direct connections by default to avoid confusion 2025-08-11 13:49:53 +04:00
Tim Jaeryang Baek 1134512b22 Merge pull request #16470 from ShirasawaSama/patch-1
i18n: Improve zh-CN translation
2025-08-11 13:27:35 +04:00
Shirasawa 8424acef4b Update translation.json 2025-08-11 07:35:46 +00:00
Classic298 bc9b51402f 0.6.22 changelog init 2025-08-10 23:20:17 +02:00
Timothy Jaeryang Baek 1623396086 refac: styling 2025-08-11 00:57:37 +04:00
Timothy Jaeryang Baek 29447366d1 refac: styling 2025-08-11 00:54:08 +04:00
Tim Jaeryang Baek 5941713b0c Merge pull request #16448 from BoFFire/dev
i18n add kabyle language
2025-08-11 00:49:08 +04:00
Timothy Jaeryang Baek 32a022a823 enh: v1 endpoint support 2025-08-11 00:45:59 +04:00
Timothy Jaeryang Baek 497471d16b enh: regenerate menu toggle setting 2025-08-11 00:43:41 +04:00
Timothy Jaeryang Baek fbb8c111ed refac 2025-08-11 00:39:12 +04:00
Timothy Jaeryang Baek 059cc636f6 fix: openai response propagation issue 2025-08-11 00:37:06 +04:00
ButterflyOfFire 4df4091899 Update languages.json
Adding kabyle language to the list
2025-08-10 17:40:27 +01:00
ButterflyOfFire c7753f2d9b i18n add kabyle translation
Adding 34% translation.
2025-08-10 17:39:09 +01:00
Adam Tao 7bd7559bfe refactor: format
Signed-off-by: Adam Tao <tcx4c70@gmail.com>
2025-08-10 22:28:31 +08:00
Adam Tao 635cb8e3ff perf(db): deduplicate update_user_last_active_by_id to reduce conflicts
Signed-off-by: Adam Tao <tcx4c70@gmail.com>
2025-08-10 22:28:31 +08:00
Adam Tao b23abcbfe5 feat(db): Add DATABASE_ENABLE_SQLITE_WAL to enable SQLite WAL
Signed-off-by: Adam Tao <tcx4c70@gmail.com>
2025-08-10 22:28:31 +08:00
Tim Jaeryang Baek 30d0f8b1f6 Merge pull request #16434 from open-webui/dev
0.6.21
2025-08-10 17:39:57 +04:00
Timothy Jaeryang Baek b581536a66 doc: changelog 2025-08-10 17:39:16 +04:00
Timothy Jaeryang Baek 77dcfb5062 chore: format 2025-08-10 16:54:49 +04:00
Tim Jaeryang Baek b82324b7b7 Merge pull request #16443 from rgaricano/dev_es-ES
UPD: Update es-ES Translation adapted to v0.6.20
2025-08-10 16:52:14 +04:00
Tim Jaeryang Baek e33fb2c751 Merge pull request #16442 from andrewbbaek/dev
update ko-KR translation.json
2025-08-10 16:52:00 +04:00
_00_ 1416387af1 UPD: Update es-ES Translation adapted to v0.6.20
Updated Spanish Translation adapted to v0.6.20

- Added translation for new strings
2025-08-10 14:43:24 +02:00
Andrew Baek f436f2c106 Update ko-KR translation.json 2025-08-10 16:20:29 +04:00
Andrew Baek 5229459c02 update ko-KR translation.json 2025-08-10 16:15:49 +04:00
Timothy Jaeryang Baek 662f3cd1b3 refac 2025-08-10 16:13:13 +04:00
Tim Jaeryang Baek ee60c3e92a Merge pull request #16441 from athoik/user_groups
feat: Display assigned user groups in Admin Panel
2025-08-10 16:08:29 +04:00
Timothy Jaeryang Baek 58a6f5df98 fix: sink list icon missing 2025-08-10 16:07:52 +04:00
Tim Jaeryang Baek 50f6db5ae2 Merge pull request #16437 from Classic298/patch-2
chore: 0.6.21 Changelog
2025-08-10 15:54:46 +04:00
Athanasios Oikonomou dc453efa5c feat: Display assigned user groups in Admin Panel
Description:
This PR adds the ability to view a user’s assigned groups in the Admin Panel when editing a user.

Backend Changes:
    Added a new endpoint:
    GET /api/v1/users/{user_id}/groups

        Returns the list of groups assigned to a specific user.
        Requires admin privileges.

Frontend Changes:
    Implemented getUserGroupsById API function to call the new backend endpoint, in lib/apis/users.

    Updated EditUserModal.svelte to:
        Load user groups asynchronously when the modal is opened.
        Display the groups inline in the form before the Save button.
        Show a loading state while fetching, and a “No groups assigned” message if none exist.

Result:
Admins can now see which groups a user belongs to directly from the edit user modal,
improving visibility and reducing the need to navigate away for group membership checks.
2025-08-10 14:49:01 +03:00
Classic298 ac3c271c9f Update CHANGELOG.md 2025-08-10 13:28:13 +02:00
Tim Jaeryang Baek 397c1e7684 Merge pull request #16440 from aindriu80/update-ga-string-10-august
(i18n) Updated Irish translations
2025-08-10 15:22:54 +04:00
Classic298 c8966b92f1 Update CHANGELOG.md 2025-08-10 13:19:21 +02:00
Timothy Jaeryang Baek c3ca241e57 fix: quick actions input 2025-08-10 15:10:50 +04:00
Aindriú Mac Giolla Eoin f9d829c1cd Updating Irish translations 2025-08-10 12:01:53 +01:00
Classic298 0a261b7401 Update CHANGELOG.md 2025-08-10 12:11:38 +02:00
Timothy Jaeryang Baek d5ad0ae862 refac 2025-08-10 13:49:28 +04:00
Timothy Jaeryang Baek cd778582d5 fix: undefined model_id issue 2025-08-10 13:48:12 +04:00
Tim Jaeryang Baek 25cc8049c8 Merge pull request #16424 from CERIT-SC/audit-fix
fix: Audit does not log user information
2025-08-10 13:43:09 +04:00
Tim Jaeryang Baek c8a02bf8a8 Merge pull request #16431 from aleixdorca/dev
i18n: Update Catalan translation.json
2025-08-10 13:41:58 +04:00
Aleix Dorca e2572dca43 Update catalan translation.json 2025-08-10 09:23:33 +02:00
xhejtman c4e0051ab2 Fix audit get_current_user
get_current_user has 4 args not 3 args:
```
get_current_user(
    request: Request,
    response: Response,
    background_tasks: BackgroundTasks,
    auth_token: HTTPAuthorizationCredentials = Depends(bearer_security)
```
2025-08-10 02:10:00 +02:00
Tim Jaeryang Baek 3f35ba27fc Merge pull request #16421 from open-webui/dev
0.6.20
2025-08-10 02:59:14 +04:00
Timothy Jaeryang Baek 2fa2728dbc chore: bump 2025-08-10 02:58:03 +04:00
Timothy Jaeryang Baek 57c082c155 doc: readme 2025-08-10 02:53:40 +04:00
Timothy Jaeryang Baek 86e0898920 fix: quick actions add issue 2025-08-10 02:52:40 +04:00
Tim Jaeryang Baek 2c3655a969 Merge pull request #15909 from open-webui/dev
0.6.19
2025-08-10 02:38:48 +04:00
Timothy Jaeryang Baek bfd8c6b87e refac 2025-08-10 02:37:27 +04:00
Timothy Jaeryang Baek 0af66558ae refac 2025-08-10 02:35:41 +04:00
Timothy Jaeryang Baek 9a145c591f chore: pyproject bump 2025-08-10 02:25:40 +04:00
Timothy Jaeryang Baek d7e9b0828b refac 2025-08-10 02:17:54 +04:00
Timothy Jaeryang Baek e9c93d082e chore: format 2025-08-10 02:15:53 +04:00
Timothy Jaeryang Baek 3f7d3def02 enh: folder filter 2025-08-10 02:10:18 +04:00
Timothy Jaeryang Baek 6497b46a78 refac 2025-08-10 01:44:33 +04:00
Timothy Jaeryang Baek bd2819a96a doc: changelog 2025-08-10 01:35:43 +04:00
Timothy Jaeryang Baek d8c76e9a0c refac 2025-08-10 01:31:07 +04:00
Timothy Jaeryang Baek b1d5e3b687 refac 2025-08-10 01:29:51 +04:00
Timothy Jaeryang Baek 50181ee99e refac 2025-08-10 01:03:02 +04:00
Timothy Jaeryang Baek eac6f6be0a chore: changelog reordering 2025-08-10 00:37:12 +04:00
Tim Jaeryang Baek 72f75afb33 Merge pull request #16420 from Classic298/patch-2
Update CHANGELOG.md
2025-08-10 00:33:49 +04:00
Classic298 ca9a545a50 Update CHANGELOG.md 2025-08-09 22:26:52 +02:00
Classic298 3395a25222 Update CHANGELOG.md 2025-08-09 22:22:22 +02:00
Classic298 9b5db894d4 Update CHANGELOG.md 2025-08-09 22:11:41 +02:00
Timothy Jaeryang Baek c77b36bdcc refac 2025-08-10 00:02:58 +04:00
Timothy Jaeryang Baek 77189664c2 chore: format 2025-08-09 23:57:35 +04:00
Timothy Jaeryang Baek f85aaa4ed9 refac: sqlcipher3-wheels as optional 2025-08-09 23:56:17 +04:00
Tim Jaeryang Baek 86fa564b44 Merge pull request #16132 from rndmcnlly/feature/sqlcipher-database-encryption
feat: Implement SQLCipher support for database encryption
2025-08-09 23:55:03 +04:00
Tim Jaeryang Baek 53425ffadb Merge pull request #16419 from expruc/feat/qdrant_improvements
feat: qdrant client improvements
2025-08-09 23:52:12 +04:00
Tim Jaeryang Baek 17ca02aa6b Merge pull request #16417 from Ithanil/prevent_idle_transactions
fix: Prevent idle transactions with PGvector DB
2025-08-09 23:51:51 +04:00
Timothy Jaeryang Baek 4e9c75be50 enh: CHAT_RESPONSE_STREAM_DELTA_CHUNK_SIZE 2025-08-09 23:49:56 +04:00
Timothy Jaeryang Baek a068ba88de chore: format 2025-08-09 23:47:02 +04:00
Timothy Jaeryang BaekandJan Kessler 1a93891d97 feat: stream delta chunk
Co-Authored-By: Jan Kessler <Ithanil@users.noreply.github.com>
2025-08-09 23:43:27 +04:00
expruc 8af9ad3f30 updated query function with scroll too 2025-08-09 22:04:41 +03:00
Timothy Jaeryang Baek e2b2e8b245 refac 2025-08-09 22:59:19 +04:00
Timothy Jaeryang Baek e9c2d9d035 refac: styling 2025-08-09 22:55:19 +04:00
Timothy Jaeryang Baek 98f38f00ff enh: shift unpin pinned models 2025-08-09 22:50:27 +04:00
expruc 88abd01b87 qdrant client improvements 2025-08-09 21:12:30 +03:00
Jan Kessler 3a9601c053 use .rollback() after read-only transaction on pgvector to avoid infinitely idle transactions (and errors in certain scenarios) 2025-08-09 20:09:45 +02:00
Timothy Jaeryang Baek 4485c7a5d2 refac 2025-08-09 21:38:31 +04:00
Timothy Jaeryang Baek caf8482fba refac 2025-08-09 21:34:47 +04:00
Timothy Jaeryang Baek f4d2c6027a refac 2025-08-09 21:10:12 +04:00
Timothy Jaeryang Baek 78cf70faec chore: version bump 2025-08-09 02:12:14 +04:00
Timothy Jaeryang Baek 4b41cd1891 chore: format 2025-08-09 02:05:07 +04:00
Timothy Jaeryang Baek 92f27bb21e feat/enh: regeneration options 2025-08-09 02:00:21 +04:00
Tim Jaeryang Baek 17084f629c Merge pull request #16385 from gaby/2025-08-08-13-38-31
feat: Propagate upstream OpenAI router errors
2025-08-09 00:58:14 +04:00
Timothy Jaeryang Baek c1d566bad4 enh: oauth_sub_claim 2025-08-09 00:46:14 +04:00
Timothy Jaeryang Baek e8cb57750b refac/fix: serply 2025-08-09 00:37:37 +04:00
Timothy Jaeryang Baek 736b29ddca refac 2025-08-09 00:33:41 +04:00
Timothy Jaeryang Baek f923a85f40 refac/fix: reasoning_content chunk 2025-08-09 00:31:28 +04:00
Tim Jaeryang Baek 8714df17dd Merge pull request #16381 from psy42a/patch-1
fix: failure to bind metadata variable on insert for PGVECTOR_PGCRYPTO feature returning syntax error
2025-08-09 00:26:30 +04:00
Tim Jaeryang Baek 9fbc76f4bb Merge pull request #16397 from 17jmumford/add_gpt_5_max_token_handling
fix: added gpt-5 to reasoning model payload handler
2025-08-09 00:22:30 +04:00
Tim Jaeryang Baek ea215aa8c2 Merge pull request #16396 from andrewbbaek/dev
refac: oidc provider url validation
2025-08-09 00:21:13 +04:00
Jeremy Mumford c69f2cc776 updated comments 2025-08-08 14:20:14 -06:00
Jeremy Mumford 0c40d93da4 renamed and added gpt-5 to reflect OpenAI updates 2025-08-08 14:16:28 -06:00
Andrew Baek 451b614f4d refac: oidc provider url validation 2025-08-09 00:15:19 +04:00
Timothy Jaeryang Baek 34689e37f8 refac 2025-08-08 20:58:52 +04:00
Timothy Jaeryang Baek 95f9d69e9a refac 2025-08-08 20:52:53 +04:00
Timothy Jaeryang Baek a5683c411a refac 2025-08-08 19:58:53 +04:00
Timothy Jaeryang Baek c42c1ee632 refac: styling 2025-08-08 18:48:27 +04:00
Timothy Jaeryang Baek 69f6fbe4e7 refac 2025-08-08 18:40:03 +04:00
Juan Calderon-Perez 7619f449c8 Format code base 2025-08-08 10:10:32 -04:00
Juan Calderon-Perez d2f2d42e09 Format python code 2025-08-08 10:09:31 -04:00
Juan Calderon-Perez 5d7e8c8e5f Format openai router 2025-08-08 10:04:02 -04:00
Juan Calderon-Perez 2ae7584686 feat: forward upstream OpenAI errors 2025-08-08 09:38:34 -04:00
Timothy Jaeryang Baek 9af64f0db1 refac 2025-08-08 14:27:28 +04:00
Timothy Jaeryang Baek 3f85375db2 refac: styling 2025-08-08 14:24:54 +04:00
Timothy Jaeryang Baek 2bccc7b8fc refac 2025-08-08 14:18:17 +04:00
Timothy Jaeryang Baek 9979ed4b46 refac 2025-08-08 14:07:23 +04:00
Timothy Jaeryang Baek 151c072bbc refac 2025-08-08 14:04:44 +04:00
Timothy Jaeryang Baek 81fed7291c refac 2025-08-08 13:56:34 +04:00
Timothy Jaeryang Baek f8e8d7e048 refac 2025-08-08 13:50:53 +04:00
Timothy Jaeryang Baek 056431606b enh: tool calling support in quick actions 2025-08-08 13:43:30 +04:00
Timothy Jaeryang Baek ff9f2614f3 refac: styling 2025-08-08 13:09:40 +04:00
Timothy Jaeryang Baek c92d1ce219 refac 2025-08-08 13:05:50 +04:00
Timothy Jaeryang Baek 4ba96fe3e5 refac 2025-08-08 12:55:24 +04:00
Timothy Jaeryang Baek 04f37ca279 refac 2025-08-08 12:50:21 +04:00
Timothy Jaeryang Baek 1fc40ad5ee refac 2025-08-08 12:40:13 +04:00
Timothy Jaeryang Baek 19bc5f1c34 refac 2025-08-08 12:34:27 +04:00
Timothy Jaeryang Baek bef5e6bb29 chore: boto3 bump 2025-08-08 12:26:45 +04:00
Timothy Jaeryang Baek 8b489cb31f refac: s3 vector 2025-08-08 12:24:47 +04:00
Timothy Jaeryang Baek 601f1af0a5 refac 2025-08-08 12:17:50 +04:00
Tim Jaeryang Baek 67dcef5e9a Merge pull request #16344 from Rain6435/fix-openapi-array-parameters
fix: include items property in OpenAPI array parameters
  for OpenAI function calling
2025-08-08 12:12:28 +04:00
Timothy Jaeryang Baek d2c7a60a43 refac 2025-08-08 12:07:15 +04:00
Timothy Jaeryang Baek 961df8ef3d refac 2025-08-08 11:57:51 +04:00
Timothy Jaeryang Baek d6326549d6 refac 2025-08-08 11:57:07 +04:00
Timothy Jaeryang Baek b084613004 refac: styling 2025-08-08 11:53:06 +04:00
Timothy Jaeryang Baek e2ce735d12 refac: styling 2025-08-08 04:37:08 +04:00
Timothy Jaeryang Baek 867a86b264 refac 2025-08-08 04:32:17 +04:00
Timothy Jaeryang Baek 0b68fc0a1b refac: styling 2025-08-08 04:10:58 +04:00
Timothy Jaeryang Baek d9f46baa70 refac: sidebar 2025-08-08 03:53:09 +04:00
Timothy Jaeryang Baek e6726d8444 fix: triple backtick enter rich text input 2025-08-08 02:57:44 +04:00
Tim Jaeryang Baek 55248bed2d Merge pull request #16348 from itk-dev/feature/add-label-to-settings-modal-controls
FEAT: add label to settings modal controls
2025-08-07 20:43:17 +04:00
Sine Jespersen 8d6c1c497f add aria labels to controls in interface modal 2025-08-07 12:19:27 +02:00
Rain6435 30540cb40e fix: include items property in OpenAPI array parameters for OpenAI function calling
Resolves issue where OpenAPI specs with array query parameters were generating
  invalid OpenAI function schemas missing the required 'items' property, causing
  400 Bad Request errors from OpenAI.

  The fix ensures that when converting OpenAPI parameter schemas to OpenAI function
  schemas, array parameters properly include their 'items' property definition.

  Fixes open-webui/open-webui#14115
2025-08-07 01:21:22 -04:00
Timothy Jaeryang Baek 761d439830 refac 2025-08-07 03:42:02 +04:00
Timothy Jaeryang Baek 7611441676 refac 2025-08-07 03:37:13 +04:00
Timothy Jaeryang Baek 2bfe684716 refac: styling 2025-08-07 03:35:12 +04:00
Timothy Jaeryang Baek 07b16cc8d1 refac 2025-08-07 02:53:28 +04:00
Timothy Jaeryang Baek 2eef60ced4 refac: sidebar styling 2025-08-07 02:50:05 +04:00
Timothy Jaeryang Baek 85a3c32f07 refac: styling 2025-08-07 02:47:13 +04:00
Timothy Jaeryang Baek ca213cbd12 refac 2025-08-07 02:29:54 +04:00
Timothy Jaeryang Baek 13682e28db refac 2025-08-07 02:25:52 +04:00
Timothy Jaeryang Baek db7ea6d23f refac 2025-08-07 02:20:32 +04:00
Timothy Jaeryang Baek 4a57e581d9 refac 2025-08-07 02:15:31 +04:00
Timothy Jaeryang Baek 2e733b46b0 enh: custom action buttons 2025-08-07 02:11:27 +04:00
Timothy Jaeryang Baek 5d670cb7ce refac: interface settings styling 2025-08-07 02:11:16 +04:00
Timothy Jaeryang Baek cce1f876c9 refac 2025-08-07 01:41:51 +04:00
Timothy Jaeryang Baek f47befa902 refac: styling 2025-08-07 01:22:55 +04:00
Timothy Jaeryang Baek 73c92e2e3e refac 2025-08-07 01:19:34 +04:00
Timothy Jaeryang Baek 6598812a25 refac 2025-08-07 00:14:35 +04:00
Timothy Jaeryang Baek 353ecda084 refac 2025-08-07 00:12:24 +04:00
Timothy Jaeryang Baek 4797799400 refac 2025-08-06 23:36:43 +04:00
Timothy Jaeryang Baek 93a91a678a enh: ability to switch off floating action buttons 2025-08-06 23:28:05 +04:00
Timothy Jaeryang Baek e16251c473 refac 2025-08-06 23:15:08 +04:00
Timothy Jaeryang Baek 07aafa5fea refac 2025-08-06 23:09:34 +04:00
Timothy Jaeryang Baek 0fd5881b7d refac: styling 2025-08-06 23:08:41 +04:00
Timothy Jaeryang Baek fa04711680 refac 2025-08-06 22:55:47 +04:00
Timothy Jaeryang Baek d2fa66bb9d refac: styling 2025-08-06 22:48:12 +04:00
Timothy Jaeryang Baek 8b9c5c4c1e enh: multi model response tabbed display 2025-08-06 22:47:34 +04:00
Tim Jaeryang Baek 0067cf6eff Merge pull request #16334 from leebeomhun/i18n-ko
i18n: update and fix ko-KR translation
2025-08-06 21:32:03 +04:00
Timothy Jaeryang Baek 0912a023c2 fix: jwt token exposed in url 2025-08-06 21:02:54 +04:00
Timothy Jaeryang BaekandG30 041da26756 feat: add pinned, shared and archived tags functionality for chat search moda
Co-Authored-By: G30 <50341825+silentoplayz@users.noreply.github.com>
2025-08-06 20:55:58 +04:00
BHLee 001d07f590 i18n update and fix:ko-KR translation.json 2025-08-07 01:29:37 +09:00
Tim Jaeryang Baek d1a654019b Merge pull request #16332 from itk-dev/feature/text-contrast-settings-modal-general
FEAT: text contrast settings modal general
2025-08-06 19:53:10 +04:00
Timothy Jaeryang Baek aae6008542 chore: bump pillow 2025-08-06 19:52:16 +04:00
Timothy Jaeryang Baek b9885529a5 refac: knowledge item modal 2025-08-06 19:50:51 +04:00
Sine Jespersen 8eee4d1c12 increase text contrast on highContrastMode 2025-08-06 15:49:20 +02:00
Tim Jaeryang Baek 8e25eb104b Merge pull request #16309 from jayteaftw/fips-compatible
fix: Resolved FATAL FIPS SELFTEST FAILURE
2025-08-06 15:14:12 +04:00
Tim Jaeryang Baek 91952096c2 Merge pull request #16321 from itk-dev/feature/more-accessible-menu
Feat: enhance accessibility of menu
2025-08-06 15:13:13 +04:00
Timothy Jaeryang Baek 80dbd76d92 enh: image compression in channels 2025-08-06 15:11:43 +04:00
Timothy Jaeryang Baek 2f349b5979 refac 2025-08-06 15:06:43 +04:00
Timothy Jaeryang Baek f2cae3d0a7 refac 2025-08-06 15:02:39 +04:00
Timothy Jaeryang Baek 4cc2c1915e refac 2025-08-06 14:47:27 +04:00
Timothy Jaeryang Baek 52b2158c67 refac 2025-08-06 14:42:57 +04:00
Tim Jaeryang Baek 70eb83b701 Merge pull request #16185 from hiwylee/vector-search-branch
feat: oracle 23ai Vector search for new supported vector db
2025-08-06 14:36:14 +04:00
Tim Jaeryang Baek 1ba8c3389e Merge pull request #16284 from silentoplayz/fix-clone-chat-tags
fix: also clone the chat's tags, folder stats, and pinned status when cloning a chat
2025-08-06 14:33:04 +04:00
Sine Jespersen 52ef3e74e6 add aria-labels and aria-pressed to enhance accessibility 2025-08-06 12:29:50 +02:00
Sine Jespersen 9df790a9f9 make buttons DropdownMenuItems to make them accessible by keyboard, add handling of highContrastMode 2025-08-06 12:29:28 +02:00
Sine Jespersen ba1e32f60d add aria-label to labelless button to enhance accessibility 2025-08-06 12:28:42 +02:00
Timothy Jaeryang Baek 5d4199bf52 chore: format 2025-08-06 14:27:58 +04:00
Timothy Jaeryang Baek 428db553e2 refac 2025-08-06 14:27:07 +04:00
Timothy Jaeryang Baek 7fffecd168 refac 2025-08-06 14:26:22 +04:00
Timothy Jaeryang Baek 6c06024cf9 refac: scim 2025-08-06 14:25:42 +04:00
Tim Jaeryang Baek f0e1c3f540 Merge pull request #15694 from dieu-bis/feat/scim-2.0-support
FEAT: Add SCIM 2.0 support for automated user provisioning
2025-08-06 14:22:48 +04:00
Sine Jespersen 502a36869e aria-hidden on icons to hide them from assistive technology 2025-08-06 12:17:44 +02:00
Timothy Jaeryang Baek 5ae0036265 refac 2025-08-06 14:03:40 +04:00
Timothy Jaeryang Baek 3a1bbcb12e refac 2025-08-06 13:48:43 +04:00
Timothy Jaeryang Baek 2e36540023 refac: tag handling 2025-08-06 13:32:28 +04:00
Timothy Jaeryang Baek e9fb161bcd refac 2025-08-06 12:36:35 +04:00
Timothy Jaeryang Baek bfa42c6277 enh: formatting toolbar for chat 2025-08-06 12:21:18 +04:00
Timothy Jaeryang Baek 0acd78049d refac: hide docs, releases for users 2025-08-06 11:50:35 +04:00
jayteaftw 4079ac55d8 Set av==14.0.1 2025-08-05 15:29:40 -07:00
Timothy Jaeryang BaekandClassic298 55ad48d1c3 feat: ENABLE_ADMIN_WORKSPACE_CONTENT_ACCESS
Co-Authored-By: Classic298 <27028174+Classic298@users.noreply.github.com>
2025-08-06 01:44:52 +04:00
silentoplayz 7c4550fa92 fix: cloned chat metadata not taken into consideration 2025-08-05 16:21:03 -04:00
Timothy Jaeryang Baek 182192f3c9 chore: analytics scaffolding 2025-08-05 22:28:16 +04:00
Timothy Jaeryang Baek d0657054a5 enh: abililty to stop merge response 2025-08-05 22:25:51 +04:00
Timothy Jaeryang Baek 8b46a9010e refac: styling 2025-08-05 22:22:49 +04:00
Timothy Jaeryang Baekandpickle-dice f24b76d9a3 refac: has_users
Co-Authored-By: pickle-dice <159401444+hassan-ajek@users.noreply.github.com>
2025-08-05 22:15:22 +04:00
silentoplayz bd570b7e2e fix: chat metadata wasn't being taken into consideration when cloning a chat
fix: chat metadata wasn't being taken into consideration when cloning a chat
2025-08-05 13:43:29 -04:00
Timothy Jaeryang Baek 53a328d00b refac: styling 2025-08-05 20:31:10 +04:00
Tim Jaeryang Baek f81964b412 Merge pull request #16295 from Jakobu5/cors-custom-scheme-patch
feat: add custom cors scheme option
2025-08-05 20:03:32 +04:00
Tim Jaeryang Baek 40c525b7b9 Merge pull request #16296 from itk-dev/feature/accessible-banner
feat: translate elements in banner for accessibility
2025-08-05 20:02:49 +04:00
psy42a f3b0f7d358 Fix syntax error where the previous use of :metadata::text in some sqlachamy/postgres versions doesn't bind at all
Fix syntax error where the previous use of :metadata::text in some sqlachamy/postgres versions doesn't bind the variable at all
2025-08-05 23:27:50 +10:00
Sine Jespersen 3494fa8707 aria hidden on svgs to hide them from assistive tech 2025-08-05 12:15:55 +02:00
Sine Jespersen e2b722deb2 translate elements in banner for accessibility 2025-08-05 11:18:56 +02:00
Jakob Hagl ae2a746d0c add custom cors scheme option 2025-08-05 10:38:08 +02:00
hiwylee 797822a3a3 add oracledb only
>      "moto[s3]>=5.0.26",
-
+    "oracledb>=3.2.0",
2025-08-04 16:03:29 +00:00
Tim Jaeryang Baek 66341124a6 Merge pull request #16009 from Classic298/patch-1
feat: add OAuth configuration warning for missing OPENID_PROVIDER_URL
2025-08-04 18:31:32 +04:00
Tim Jaeryang Baek 0b627248f5 Merge pull request #15640 from ipapapa/feat/cleaner-logs
refactor(logger): Conditionally include extra_json in logs
2025-08-04 17:52:12 +04:00
Timothy Jaeryang Baek 985e54b662 refac: styling 2025-08-04 17:51:22 +04:00
Tim Jaeryang Baek f41a8e67e8 Merge pull request #15705 from rgaricano/dev-HybridSearch-bm25_slider
FEAT: STYLE: Frontend HybridSearch-BM25 Slider
2025-08-04 17:48:24 +04:00
Timothy Jaeryang Baek 3f3a69568f enh: ability to sort folder chats 2025-08-04 17:37:33 +04:00
Tim Jaeryang Baek ae2de6b09c Merge pull request #16249 from silentoplayz/fix-richtext-getwordatdocpos
fix(frontend): Attempt to resolve TypeError in RichTextInput.svelte
2025-08-04 17:31:52 +04:00
Timothy Jaeryang Baek 173fa62af3 refac 2025-08-04 17:28:26 +04:00
Timothy Jaeryang Baek 88ff0b952e refac/fix: table markdown input 2025-08-04 17:27:28 +04:00
silentoplayz 55d3c07d85 revert: Reverts requested 2025-08-04 08:42:48 -04:00
Timothy Jaeryang Baek 2d2fbdb341 fix: styling 2025-08-04 16:02:48 +04:00
Timothy Jaeryang Baek e262069265 refac/fix: folder direct file upload 2025-08-04 15:33:44 +04:00
Timothy Jaeryang Baek 4c9ab7bd50 refac 2025-08-04 15:30:09 +04:00
Timothy Jaeryang Baek e8696c63fe refac 2025-08-04 15:23:43 +04:00
Tim Jaeryang Baek 5db60ca34f Merge pull request #15903 from Hisma/marker-api-update
feat: Add configurable API URL (for self-hosting) and additional_config parameter for Datalab Marker API
2025-08-04 15:21:03 +04:00
Timothy Jaeryang Baek 60c5ea1bcc enh: include chat id in feedback modal 2025-08-04 15:15:31 +04:00
Timothy Jaeryang Baek 7aeca7dee2 refac 2025-08-04 15:12:39 +04:00
Timothy Jaeryang Baek 8562e3e438 refac: styling 2025-08-04 15:04:16 +04:00
Tim Jaeryang Baek 6ce1166b98 Merge pull request #16033 from silentoplayz/sticky-search
refac: sticky headers & smarter pagination on users list page
2025-08-04 15:03:02 +04:00
Timothy Jaeryang Baek 35400daf19 enh/refac: redis cluster support 2025-08-04 14:15:08 +04:00
Tim Jaeryang Baek 01320d99d6 Merge pull request #16246 from silentoplayz/sensitiveinput-password-fields
feat: Add password visibility toggle to password fields with SensitiveInput.svelte component
2025-08-04 10:51:39 +04:00
silentoplayz 7a80e60785 fix(frontend): Attempt to resolve TypeError in RichTextInput.svelte
Fixes an issue where `ue.getWordAtDocPos is not a function` would be thrown in `MessageInput.svelte`.

The error was caused by a timing issue where the `getWordAtDocPos` method on the `RichTextInput` component was not available when called from an event handler within the same component.

This change refactors the code to pass the `getWordAtDocPos` function as a callback prop from `RichTextInput` to `MessageInput`, ensuring it's available when needed.
2025-08-03 22:36:08 -04:00
silentoplayz 56eeed6277 feat: Add password visibility toggle to password fields w SensitiveInput.svelte component 2025-08-03 16:07:12 -04:00
Tim Jaeryang Baek 7c29084a19 Merge pull request #16237 from expruc/chore/separate_otel_logs_config
chore: separate otel logs config
2025-08-03 21:08:55 +04:00
expruc 58180c0586 added otel lgos specific config 2025-08-02 22:15:22 +03:00
Timothy Jaeryang Baek 00084c6ca6 refac/fix: tool payload 2025-08-02 19:36:46 +04:00
Tim Jaeryang Baek f47a100730 Merge pull request #16231 from hadadarjt/dev
UserMenu: Fix quote typo in docs link.
2025-08-02 19:06:57 +04:00
Tim Jaeryang Baek a1fac8fc7f Merge pull request #16233 from andrewbbaek/dev
fix: add conditional password confirmation on signup
2025-08-02 19:06:14 +04:00
Andrew Baek c1ba289ab5 fix: add conditional password confirmation on signup
Password confirmation during signup is now only enforced if the 'enable_signup_password_confirmation' feature flag is enabled in the config. This allows for more flexible signup flows based on configuration.
2025-08-02 19:04:52 +04:00
Hadad ab140ea531 UserMenu: Fix quote typo in docs link.
* Docs link in the user menu are invalid when clicked via browser.

Signed-off-by: Hadad <hadad@linuxmail.org>
2025-08-02 20:21:41 +07:00
Tim Jaeryang Baek 09e475f6cc Merge pull request #16192 from expruc/feat/otel-logger-handler
feat: otel logger handler
2025-08-02 14:52:29 +04:00
Tim Jaeryang Baek 49926f06ee Merge branch 'dev' into feat/otel-logger-handler 2025-08-02 14:52:16 +04:00
Tim Jaeryang Baek e988750272 Merge pull request #16194 from ahangarha/patch-1
i18n:update fa translation
2025-08-02 14:49:47 +04:00
Tim Jaeryang Baek bf4e13e144 Merge pull request #16197 from open-webui/dependabot/pip/backend/dev/authlib-1.6.1
chore(deps): bump authlib from 1.4.1 to 1.6.1 in /backend
2025-08-02 14:01:11 +04:00
Tim Jaeryang Baek 9992dc98bb Merge pull request #16198 from open-webui/dependabot/pip/backend/dev/markdown-3.8.2
chore(deps): bump markdown from 3.7 to 3.8.2 in /backend
2025-08-02 14:01:04 +04:00
Tim Jaeryang Baek ea38d415ce Merge pull request #16199 from open-webui/dependabot/pip/backend/dev/fake-useragent-2.2.0
chore(deps): bump fake-useragent from 2.1.0 to 2.2.0 in /backend
2025-08-02 14:00:59 +04:00
Tim Jaeryang Baek 5855517a46 Merge pull request #16200 from open-webui/dependabot/pip/backend/dev/aiohttp-3.12.15
chore(deps): bump aiohttp from 3.11.11 to 3.12.15 in /backend
2025-08-02 14:00:53 +04:00
Tim Jaeryang Baek cf00df3b96 Merge pull request #16201 from open-webui/dependabot/pip/backend/dev/google-genai-1.28.0
chore(deps): bump google-genai from 1.15.0 to 1.28.0 in /backend
2025-08-02 14:00:48 +04:00
Timothy Jaeryang Baek 446f4ee5a8 refac 2025-08-02 13:59:07 +04:00
Tim Jaeryang Baek 093acb3724 Merge pull request #16222 from expruc/feat/seperate_otel_metrics_config
feat: separate otel metrics configuration
2025-08-02 13:57:10 +04:00
expruc a679fb3f45 split otel metrics from general otel configuration 2025-08-02 11:30:34 +03:00
Timothy Jaeryang Baek 994170b482 refac 2025-08-01 18:17:09 +04:00
Timothy Jaeryang Baek d4f70830d1 refac: markdown br rendering 2025-08-01 14:07:11 +04:00
dependabot[bot] 3098471a8a chore(deps): bump google-genai from 1.15.0 to 1.28.0 in /backend
Bumps [google-genai](https://github.com/googleapis/python-genai) from 1.15.0 to 1.28.0.
- [Release notes](https://github.com/googleapis/python-genai/releases)
- [Changelog](https://github.com/googleapis/python-genai/blob/main/CHANGELOG.md)
- [Commits](https://github.com/googleapis/python-genai/compare/v1.15.0...v1.28.0)

---
updated-dependencies:
- dependency-name: google-genai
  dependency-version: 1.28.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-08-01 02:27:15 +00:00
dependabot[bot] fce486fe30 chore(deps): bump aiohttp from 3.11.11 to 3.12.15 in /backend
---
updated-dependencies:
- dependency-name: aiohttp
  dependency-version: 3.12.15
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-08-01 02:26:36 +00:00
dependabot[bot] aee68aedb2 chore(deps): bump fake-useragent from 2.1.0 to 2.2.0 in /backend
Bumps [fake-useragent](https://github.com/fake-useragent/fake-useragent) from 2.1.0 to 2.2.0.
- [Release notes](https://github.com/fake-useragent/fake-useragent/releases)
- [Commits](https://github.com/fake-useragent/fake-useragent/compare/2.1.0...2.2.0)

---
updated-dependencies:
- dependency-name: fake-useragent
  dependency-version: 2.2.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-08-01 02:26:25 +00:00
dependabot[bot] 4b0bf3130b chore(deps): bump markdown from 3.7 to 3.8.2 in /backend
Bumps [markdown](https://github.com/Python-Markdown/markdown) from 3.7 to 3.8.2.
- [Release notes](https://github.com/Python-Markdown/markdown/releases)
- [Changelog](https://github.com/Python-Markdown/markdown/blob/master/docs/changelog.md)
- [Commits](https://github.com/Python-Markdown/markdown/compare/3.7...3.8.2)

---
updated-dependencies:
- dependency-name: markdown
  dependency-version: 3.8.2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-08-01 02:25:33 +00:00
dependabot[bot] 293cdb810e chore(deps): bump authlib from 1.4.1 to 1.6.1 in /backend
Bumps [authlib](https://github.com/authlib/authlib) from 1.4.1 to 1.6.1.
- [Release notes](https://github.com/authlib/authlib/releases)
- [Changelog](https://github.com/authlib/authlib/blob/main/docs/changelog.rst)
- [Commits](https://github.com/authlib/authlib/compare/v1.4.1...v1.6.1)

---
updated-dependencies:
- dependency-name: authlib
  dependency-version: 1.6.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-08-01 02:25:17 +00:00
Adam M. Smith 414d026d59 fix: swap sqlcipher dependency to sqlcipher3-wheels 2025-07-31 23:21:35 +00:00
Adam M. Smith c9a4bc18f4 feat: Implement SQLCipher support for database encryption
- Added sqlcipher3 dependency to requirements.txt for SQLCipher integration.
- Modified database connection handling in wrappers.py to support encrypted SQLite databases using the new sqlite+sqlcipher:// URL protocol.
- Updated db.py to handle SQLCipher URLs for SQLAlchemy connections.
- Enhanced Alembic migration environment to support SQLCipher URLs.
2025-07-31 23:21:35 +00:00
Mostafa Ahangarha 4e64c3eca9 Update fa translation
Fix a mistake in translation
2025-08-01 00:33:09 +03:30
hiwylee bd215a1b96 Merge branch 'dev' into vector-search-branch 2025-08-01 04:23:38 +09:00
expruc 2035eabb1f added otel logging handler 2025-07-31 21:58:49 +03:00
hiwylee 0e640dd71e resolve conflict 2025-08-01 02:58:51 +09:00
Timothy Jaeryang Baek d2a3aacaba refac: styling 2025-07-31 19:00:43 +04:00
Timothy Jaeryang Baek 56af3e7357 refac: granular chat controls permissions 2025-07-31 18:59:37 +04:00
Timothy Jaeryang Baek 708e0ed05e refac/enh: chat controls permissions 2025-07-31 18:58:58 +04:00
Timothy Jaeryang Baek 84289b9a8d refac 2025-07-31 18:45:39 +04:00
Tim Jaeryang Baek b378ac1f57 Merge pull request #16186 from itk-dev/feature/user-menu-tab-order-and-menu-item-accessibility
FEAT: user menu tab order and menu item accessibility
2025-07-31 17:48:17 +04:00
Timothy Jaeryang Baek 6a17ba5b7a refac: metadata handling in vectordb 2025-07-31 17:45:06 +04:00
Sine Jespersen 3dc23368aa use bits-ui dropdownmenu item to make menu items focusable 2025-07-31 15:33:27 +02:00
Timothy Jaeryang Baek aa83ebae58 refac: lazySpanExporter no longer needed 2025-07-31 17:30:37 +04:00
Timothy Jaeryang Baek cb487d3f61 chore: otel bump 2025-07-31 17:19:37 +04:00
Timothy Jaeryang Baek 6b34b2c946 refac 2025-07-31 16:47:02 +04:00
Tim Jaeryang Baek 1f22e1d84c Merge pull request #16096 from gkkachi/dev
fix: properly handle full URLs in form_data.path
2025-07-31 16:45:48 +04:00
Timothy Jaeryang Baek b510f21a5b feat: signup confirm password 2025-07-31 16:43:37 +04:00
hiwylee 6b639cd42b Ignore yarnlock 2025-07-31 21:39:03 +09:00
Timothy Jaeryang Baek 1c83f48c45 refac 2025-07-31 16:27:20 +04:00
Timothy Jaeryang Baek d3547f0f54 refac: error handling 2025-07-31 16:17:33 +04:00
Timothy Jaeryang Baek 73b07df28f enh/refac: debounce chat draft save 2025-07-31 16:02:43 +04:00
Timothy Jaeryang Baek 05895d9657 refac/enh: allow underscore for prompt command 2025-07-31 15:55:10 +04:00
Tim Jaeryang Baek f1300a8fd9 Merge pull request #16178 from itk-dev/feature/hide-decorative-images-from-assistive-tech
Feature/hide decorative images from assistive tech
2025-07-31 15:30:20 +04:00
Tim Jaeryang Baek 22f9481e99 Merge pull request #16176 from itk-dev/feature/toggle-user-assistant-button-accessibility
FEAT: toggle user assistant button accessibility
2025-07-31 15:26:41 +04:00
Sine Jespersen 6434f1ec99 remove alt text on image for accessibility 2025-07-31 13:25:14 +02:00
Sine Jespersen eac359d6b4 aria-hidden on icon for accessibility 2025-07-31 13:25:07 +02:00
Sine Jespersen 343e41886f add aria-label and aria-pressed to toggle button for accessibility 2025-07-31 13:14:53 +02:00
Sine Jespersen 77babb6b63 add aria label, make alt text translatable 2025-07-31 10:32:32 +02:00
Sine Jespersen 30218a6db0 remove duplicate button in user menu to improve tab order 2025-07-31 10:32:15 +02:00
Timothy Jaeryang Baek 0763d8e9b9 refac: message input styling 2025-07-30 14:21:18 +04:00
Timothy Jaeryang Baek f027df06d1 fix: leaderboard loading issue 2025-07-30 14:18:36 +04:00
Timothy Jaeryang Baek 3ab10c6758 enh: follow up hover tooltip 2025-07-30 14:14:02 +04:00
Timothy Jaeryang Baek 73a38aca72 refac 2025-07-30 14:09:33 +04:00
Timothy Jaeryang Baek 22d98e6ee5 fix: playground styling 2025-07-30 14:06:42 +04:00
Tim Jaeryang Baek 00a00f5162 Merge pull request #16063 from silentoplayz/fix-her-theme
fix(frontend): Ensure 'Her' theme is always a light theme
2025-07-30 13:01:31 +04:00
Tim Jaeryang Baek 47d86d3c2f Merge pull request #16061 from silentoplayz/fix/autoscrolling-in-FloatingButtons
fix: prevent error when autoscrolling in FloatingButtons
2025-07-30 13:01:02 +04:00
Tim Jaeryang Baek 4cccb097f9 Merge pull request #16131 from open-webui/main
refac
2025-07-29 23:46:00 +04:00
Timothy Jaeryang Baek b8da4a8cd8 refac 2025-07-29 23:45:25 +04:00
silentoplayz 694494ad5e Revert "fix: should fix the "breaking change""
This reverts commit f1de837fef.
2025-07-29 09:33:17 -04:00
silentoplayz f1de837fef fix: should fix the "breaking change" 2025-07-29 09:29:26 -04:00
Timothy Jaeryang Baek 071a2ac835 fix: python format issue 2025-07-29 17:08:47 +04:00
Tim Jaeryang Baek 526a98171e Merge pull request #16077 from silentoplayz/delete-duplicate-SVGs
chore: Remove `ChatBubbleOvalEllipsis` and `Download` icon components (duplicates of `ChatBubble` and `ArrowDownTray`)
2025-07-29 16:53:42 +04:00
Tim Jaeryang Baek 1339d0059a Merge pull request #16123 from ShirasawaSama/locale_improve_chinese_i18n
i18n: improve chinese
2025-07-29 16:42:25 +04:00
Shirasawa ad785c46dc Update translation.json 2025-07-29 20:38:00 +08:00
Shirasawa 57452ad49e i18n: improve zh-CN 2025-07-29 20:35:14 +08:00
Tim Jaeryang Baek 97a788e4a3 Merge pull request #16109 from amoshydra/pwa-remove-orientation-member
fix: remove orientation specifier, respect OS level rotation
2025-07-29 16:24:24 +04:00
Tim Jaeryang Baek fd9b6d1193 Merge pull request #16117 from silentoplayz/fix-autocomplete-toggle
fix: "Autocomplete Generation" toggle would appear enabled breifly, even when disabled
2025-07-29 16:23:31 +04:00
Timothy Jaeryang Baek 1cbbdf7771 fix: user menu buttons 2025-07-29 16:15:23 +04:00
silentoplayz a63b447992 fix-autocomplete-toggle 2025-07-29 06:35:54 -04:00
Tim Jaeryang Baek cd6eb9a9e2 Merge pull request #16115 from silentoplayz/draggable-pinned-models
feat: allow draggable reorganization of pinned models in chat sidebar
2025-07-29 12:56:06 +04:00
Tim Jaeryang Baek b8edcd41f3 Merge pull request #16110 from qingchunnh/Update_zh-CN-25729
i18n: Improve zh-CN
2025-07-29 12:54:59 +04:00
silentoplayz 3243344a85 feat: allow draggable reorganization of pinned models 2025-07-29 04:37:29 -04:00
Timothy Jaeryang Baek 22d53be3a5 refac 2025-07-29 11:22:14 +04:00
qingchun bf0e7651fa i18n: Improve zh-CN 2025-07-29 12:21:27 +08:00
amoshydra 56a094a34a fix(pwa): remove orientation specifier, respect OS level rotation
Related to #13949
2025-07-29 05:48:15 +08:00
Timothy Jaeryang Baek 807c34b40c fix 2025-07-29 00:51:23 +04:00
Timothy Jaeryang Baek b2a04bac03 refac 2025-07-28 18:44:27 +04:00
Konosuke Kachi 0a8f482264 fix: properly handle full URLs in form_data.path 2025-07-28 23:34:23 +09:00
Timothy Jaeryang BaekandG30 f2da65b778 fix: hide community sharing button when disabled
Co-Authored-By: G30 <50341825+silentoplayz@users.noreply.github.com>
2025-07-28 17:42:45 +04:00
Tim Jaeryang Baek 58dbd869ba Merge pull request #16071 from GSAlex/main
fix: Render template variables in folder system prompts
2025-07-28 17:40:47 +04:00
Tim Jaeryang Baek 49a928d48c Merge pull request #16048 from silentoplayz/discovery-links-adjustment
chore: update discover links
2025-07-28 17:37:40 +04:00
Timothy Jaeryang Baek bcfb4d1e43 refac 2025-07-28 13:12:38 +04:00
Timothy Jaeryang Baek c1e4139e5c feat: model sync endpoint 2025-07-28 13:06:05 +04:00
Timothy Jaeryang Baek 49a6211d36 refac 2025-07-28 11:31:06 +04:00
Timothy Jaeryang Baek 0c5fbdedd8 refac 2025-07-28 11:09:48 +04:00
silentoplayz 84e568f902 remove: ArrowRightTag.svelte and used a transform 2025-07-27 20:43:17 -04:00
silentoplayz b633cb7c24 fix: last commit here, I swear! 2025-07-27 20:23:49 -04:00
silentoplayz eeccf23e4d fix: I thought this got pushed 2025-07-27 20:23:06 -04:00
silentoplayz a64b91e2e2 chore: remove-duplicate-icons 2025-07-27 20:13:53 -04:00
Yang Yang b52d28c94c fix: Render template variables in folder system prompts
Fixes #16019
2025-07-27 23:20:43 +08:00
silentoplayz c03741df93 fix(frontend): Ensure 'Her' theme is always a light theme
This commit fixes a bug where the 'Her' theme would switch to dark mode after a logout/login cycle. The issue was caused by two problems:

1. The `dark` class was being incorrectly added to the `documentElement` when the 'Her' theme was selected in `src/app.html`.
2. The `applyTheme` function in `src/lib/components/chat/Settings/General.svelte` was not explicitly setting the 'Her' theme as a light theme.

This commit resolves both of these issues, ensuring that the 'Her' theme is always a light theme.
2025-07-27 04:02:05 -04:00
silentoplayz 1d85729b4a fix: prevent error when autoscrolling in FloatingButtons 2025-07-27 03:07:28 -04:00
silentoplayz 2d130f3bec chore: adjust discover links 2025-07-26 17:32:55 -04:00
silentoplayz 67baecdc8e fix 2025-07-25 16:05:42 -04:00
silentoplayz 92fe7544c8 Sticky top bars for Leaderboard and UserList pages 2025-07-25 15:57:03 -04:00
Tim Jaeryang Baek 48d233abc6 Merge pull request #16011 from open-webui/main
refac: styling
2025-07-25 01:09:54 +04:00
Timothy Jaeryang Baek b8912aa671 refac: styling 2025-07-25 01:09:04 +04:00
Classic298 723840dad5 add warning if OPENID_PROVIDER_URL Is not set 2025-07-24 22:15:58 +02:00
Timothy Jaeryang Baek df7b5ec907 enh/refac: function sync endpoint 2025-07-24 23:44:43 +04:00
Tim Jaeryang Baek 3220088411 Merge pull request #16007 from open-webui/main
fix: otel yaml
2025-07-24 22:48:26 +04:00
Tim Jaeryang Baek dcade8cdf8 Merge pull request #15785 from bekzod/patch-1
BREAKING CHANGE: Update docling endpoint
2025-07-24 21:09:13 +04:00
Timothy Jaeryang Baek 9f5c905592 fix: max count should not apply to knowledge 2025-07-24 19:43:17 +04:00
Tim Jaeryang Baek da01021836 Merge pull request #15885 from Classic298/admin_export_user_csv
feat: Add button (for admins) to export all users in DB as csv file
2025-07-24 19:29:38 +04:00
Timothy Jaeryang Baek 626ad9c83d fix: otel yaml 2025-07-24 19:25:13 +04:00
Tim Jaeryang Baek 4179e7e4c7 Merge pull request #15942 from amoshydra/pin-pyarrow-20-rpi-compatibility
chore: pin pyarrow version to 20 for RPI compatibility
2025-07-24 19:07:26 +04:00
Timothy Jaeryang Baek 6c476ec939 refac 2025-07-24 18:54:51 +04:00
Timothy Jaeryang Baek 7a97c25fa7 refac 2025-07-24 18:54:15 +04:00
Timothy Jaeryang Baek 274a65903b fix: otel.yaml 2025-07-24 18:53:04 +04:00
Tim Jaeryang Baek af45dfd002 Merge pull request #15980 from sihyeonn/feat/sh-share-connectionpool
feat: Reuse Redis Connection Pools to Eliminate Connection & IP Exhaustion
2025-07-24 18:38:39 +04:00
Sihyeon Jang f59da361f1 feat: Re-use Redis connection pools via local cache to prevent transient exhaustion
Every call to get_redis_connection() spawned a new pool, so workers slowly accumulated thousands of open sockets. Even though connections were eventually released, skewed release timing still pushed us past Redis’ max-clients and the cluster egress IP cap.

A module-level _CONNECTION_CACHE now memoises pools by (redis_url, sentinel_hosts, async_mode, decode_responses).

Result: flat connection count, no more IP or FD exhaustion. Public API unchanged.

Signed-off-by: Sihyeon Jang <sihyeon.jang@navercorp.com>
2025-07-24 18:44:42 +09:00
Tim Jaeryang Baek 78754fbe96 Merge pull request #15960 from itk-dev/feature/more-danish-translations
i18n: Danish translations (missing)
2025-07-23 20:15:40 +04:00
Jesper Kristensen 347e4dd73d Added missing danish translations 2025-07-23 11:35:52 +02:00
Tim Jaeryang Baek 4a3935ef9a Merge pull request #15935 from rgaricano/dev_FIX-transcription_language
FIX: STT default whisper trascription language
2025-07-23 12:20:08 +04:00
Tim Jaeryang Baek 4d93ad4474 Merge pull request #15941 from taylorwilsdon/tasks_redis_key_prefix
fix: Implement outstanding REDIS_KEY_PREFIX declarations
2025-07-23 12:18:03 +04:00
Timothy Jaeryang Baek ffa8ad0a22 refac: azure url detection 2025-07-23 12:15:02 +04:00
Tim Jaeryang Baek f44812b1a8 Merge pull request #15946 from rgaricano/dev_es-ES
i18n:UPD: Locale es-ES translation, updated to v.0.6.18
2025-07-23 12:02:34 +04:00
Tim Jaeryang Baek bd18bf5c83 Merge pull request #15951 from 0xThresh/s3vector-support
feat: Add S3 Vector Buckets Support for Knowledge
2025-07-23 12:02:20 +04:00
_00_ fc9d19e814 Update translation.json -Removed added strings
Removed added strings because are innecessary for autogenerating flow.
2025-07-23 09:22:48 +02:00
_00_ 86736a7817 Update translation.json REORDER STRING
REORDER STRING
2025-07-23 09:04:23 +02:00
_00_ 7a0b55758d Update translation.json - reorder strings
reorder strings
2025-07-23 09:01:26 +02:00
_00_ ab8e54dd29 Update audio.py Fix Format error
Fix Format error
2025-07-23 08:49:00 +02:00
_00_ 51758c429b Update audio.py - fix FORMAT error
Fix  FORMAT error
2025-07-23 08:45:10 +02:00
_00_ e71177f776 Merge branch 'dev' into dev_es-ES 2025-07-23 08:34:32 +02:00
0xThresh.eth 860f3b3cab chore: run formatting 2025-07-22 22:46:00 -06:00
0xThresh.eth 8dcf668448 chore: final cleanup 2025-07-22 22:37:57 -06:00
Richard Meyer 9590897f3a Merge branch 'open-webui:main' into marker-api-update 2025-07-23 00:35:48 -04:00
Hisma 216c991c01 move api key below url 2025-07-23 00:32:18 -04:00
0xThresh.eth 6c283cdd93 fix: boto3 version in uv.lock 2025-07-22 21:40:52 -06:00
0xThresh.eth d463a29ba1 feat: S3 vector support tested 2025-07-22 21:36:35 -06:00
Hisma 21337a2fd8 ci fix 2025-07-22 22:07:14 -04:00
Hisma a99e20cc3d add format_lines 2025-07-22 21:06:29 -04:00
Hisma f31cc07a9d feat: update marker api 2025-07-22 20:49:28 -04:00
_00_ 41ce1b5b28 i18n:UPD: Locale es-ES translation, updated to v.0.6.18
UPDATE i18n locale es-ES translation, updated to v.0.6.18.

Added translation of new strings
2025-07-23 01:38:34 +02:00
Timothy Jaeryang Baek 8da08ad73a refac: chat item edit title behaviour 2025-07-22 23:44:10 +04:00
Timothy Jaeryang Baek c03b574ef6 refac/fix 2025-07-22 23:39:33 +04:00
amoshydra b778bcd8e6 chore: pin pyarrow version to 20 for rpi compatibility
fix: #15897
2025-07-23 02:22:45 +08:00
Taylor Wilsdon 65654a3b4c fix last key prefix 2025-07-22 13:42:38 -04:00
Taylor Wilsdon 3e686e9915 implement redis_key_prefix env support in all areas invoking redis to allow for clustermode compatibility with new docs function 2025-07-22 13:40:29 -04:00
_00_ 0613563644 FIX: STT default whisper trascription language
FIX: STT default whisper trascription language

Fix the transcripcion language used by default whisper, setting as WHISPER_LANGUAGE if it is setted in env var, even if a language is detected in the file's metadata.
It is understood that if a language is set as an environment variable for transcriptions, this should be the preferred one and the one that should be used for that purpose.

It would be advisable to add this variable as configurable in UI
2025-07-22 16:47:06 +02:00
Timothy Jaeryang Baek d21a2581a6 refac/fix: title generation button issue 2025-07-22 17:31:01 +04:00
Timothy Jaeryang Baek 8bc7d85eac refac 2025-07-22 17:17:26 +04:00
Tim Jaeryang Baek dfdf94483b Merge pull request #15921 from expruc/feat/allow_admins_handle_feedbacks
fix: allow admins to get and post on feedbacks (avoid 404 errors)
2025-07-22 14:43:59 +04:00
Timothy Jaeryang Baek bf3c807047 refac 2025-07-22 11:38:47 +04:00
Timothy Jaeryang Baek ac0932f356 refac: styling 2025-07-22 11:22:01 +04:00
Timothy Jaeryang Baek 496ea40d1d refac: model item tag styling 2025-07-22 11:19:40 +04:00
0xThresh.eth f6ee1965cb merge main 2025-07-21 18:06:17 -06:00
expruc 1ff2ad0c08 ci fix 2025-07-21 22:17:08 +03:00
expruc 14c6f8bf80 added a check for admins to avoid 404 errors on feedbacks get and post 2025-07-21 21:18:37 +03:00
Timothy Jaeryang Baek d8b80caff3 refac/fix: remove insecure arg for otel http exporter 2025-07-21 16:35:23 +04:00
Timothy Jaeryang Baek a26607c720 refac: styling 2025-07-21 13:06:34 +04:00
Tim Jaeryang Baek 1e2ae1da1d Merge pull request #15888 from macnow/patch-2
i18n: update for polish translation
2025-07-21 10:57:02 +04:00
Tim Jaeryang Baek e95a93d80b Merge pull request #15894 from qingchunnh/Update_zh-CN-25721
i18n: Update & Improve & Fix zh-CN
2025-07-21 10:53:53 +04:00
qingchun 8f655ec0a6 i18n: Fix zh-CN 2025-07-21 06:59:12 +08:00
qingchun fa3c3c2443 i18n: Update & Improve & Fix zh-CN 2025-07-21 06:52:46 +08:00
0xThresh.eth 5c59c50e2d more prgoress on s3 vector 2025-07-20 16:48:23 -06:00
Maciej Nowakowski 58cbf5aba2 Update for Polish translation 2025-07-20 14:30:53 +02:00
Classic298 3acd286791 Merge branch 'dev' into admin_export_user_csv 2025-07-20 13:17:59 +02:00
Timothy Jaeryang Baek 24805ca79e refac/fix: channel messages 2025-07-20 15:17:17 +04:00
Timothy Jaeryang Baek ed20f2ea5f fix: dev.sh 2025-07-20 15:10:54 +04:00
Timothy Jaeryang Baek 40ebf8cd62 refac: memory handling 2025-07-20 15:00:24 +04:00
Tim Jaeryang Baek 521d223f05 Merge pull request #15883 from aleixdorca/dev
i18n: Update catalan translation.json
2025-07-20 14:14:57 +04:00
Classic298 a2aa68c84a Update Database.svelte 2025-07-20 11:16:08 +02:00
Classic298 74140ea251 Update Database.svelte 2025-07-20 11:12:38 +02:00
Classic298 84f7a55753 remove slash 2025-07-20 11:04:40 +02:00
Classic298 0d1b06d87c Update Database.svelte 2025-07-20 10:52:47 +02:00
Aleix Dorca 28d54f87f1 Update catalan translation.json 2025-07-20 06:51:10 +02:00
Tim Jaeryang Baek 5fbfe2bdca Merge pull request #15879 from open-webui/dev
0.6.18
2025-07-19 23:26:01 +04:00
Timothy Jaeryang Baek 6e0328798d chore: format 2025-07-19 23:23:54 +04:00
Tim Jaeryang Baek 0119036a11 Merge pull request #15871 from Classic298/cors
Enh: dev.sh default port export
2025-07-19 23:20:52 +04:00
Tim Jaeryang Baek 7a023e0841 Merge pull request #15875 from leebeomhun/i18n-ko
i18n: update and fix ko-KR translation
2025-07-19 23:20:29 +04:00
Timothy Jaeryang Baek b397008ee0 doc: changelog 2025-07-19 23:20:14 +04:00
Timothy Jaeryang Baek b329c63c80 refac 2025-07-19 23:17:14 +04:00
Timothy Jaeryang Baek 95b11207a1 fix: group user load 2025-07-19 23:16:44 +04:00
BHLee 74d0e6b2c7 i18n korean Fix typo 2025-07-20 04:10:26 +09:00
BHLee 7ffc12567f i18n update and fix:ko-KR translation.json 2025-07-20 03:46:37 +09:00
Classic298 d8ed4fa0c2 Merge branch 'open-webui:main' into cors 2025-07-19 19:53:13 +02:00
Classic298 549294ee30 Update dev.sh 2025-07-19 19:43:08 +02:00
Tim Jaeryang Baek b249809d2d Merge pull request #15738 from open-webui/dev
0.6.17
2025-07-19 21:39:46 +04:00
Timothy Jaeryang Baek a01e5126a3 refac 2025-07-19 21:34:38 +04:00
Timothy Jaeryang Baek c100442102 refac: compress image 2025-07-19 21:27:37 +04:00
Timothy Jaeryang Baek 32c85c59b1 refac 2025-07-19 21:22:12 +04:00
Timothy Jaeryang Baek 699c8073ff refac 2025-07-19 21:20:28 +04:00
Tim Jaeryang Baek 6f0def4ee3 Merge pull request #15869 from Classic298/patch-7
enh: Update CHANGELOG.md
2025-07-19 21:15:59 +04:00
Classic298 55d0534977 Update CHANGELOG.md 2025-07-19 19:13:46 +02:00
Classic298 9c82070ed9 Update CHANGELOG.md 2025-07-19 19:13:03 +02:00
Classic298 6bfe16367b Update CHANGELOG.md 2025-07-19 19:11:40 +02:00
Classic298 fb916087be Update CHANGELOG.md 2025-07-19 19:07:39 +02:00
Classic298 418a58674f Update CHANGELOG.md 2025-07-19 18:27:18 +02:00
Timothy Jaeryang Baek 4628097df9 doc: changelog 2025-07-19 19:51:47 +04:00
Timothy Jaeryang Baek 1159f3a781 enh: add folder modal 2025-07-19 19:46:35 +04:00
Timothy Jaeryang Baek a638a187bb refac 2025-07-19 19:32:52 +04:00
Timothy Jaeryang Baek 44a23a5c99 chore 2025-07-19 19:20:14 +04:00
Timothy Jaeryang Baek 08ff9863d5 refac 2025-07-19 19:15:05 +04:00
Timothy Jaeryang Baek d9a3b02e3f refac 2025-07-19 19:03:01 +04:00
Timothy Jaeryang Baek 53d94d6f61 enh: link prompt variable doc 2025-07-19 19:00:49 +04:00
Timothy Jaeryang Baek 81f8187e57 chore: format 2025-07-19 18:57:59 +04:00
Timothy Jaeryang Baek 4364dac226 refac 2025-07-19 18:20:17 +04:00
Timothy Jaeryang Baek 6362845065 refac 2025-07-19 18:12:16 +04:00
Timothy Jaeryang Baek 2db9d6b5d2 enh: disable edit button during generation 2025-07-19 18:08:58 +04:00
Timothy Jaeryang Baek 71ee33bf82 enh: search chat preview 2025-07-19 18:06:59 +04:00
Timothy Jaeryang Baek c94ce71ca3 refac 2025-07-19 16:44:28 +04:00
Timothy Jaeryang Baek 3af2938efc enh: note selection edit support 2025-07-19 16:35:03 +04:00
Tim Jaeryang Baek e35f7748f6 Merge pull request #15861 from tcx4c70/feat/log/trace_info
feat(logger): Add trace_id and span_id to log
2025-07-19 14:52:48 +04:00
Timothy Jaeryang Baek 157daa6def enh: allow direct file upload to knowledge attachment 2025-07-19 14:50:36 +04:00
Adam Tao 739098ab60 feat(logger): Add trace_id and span_id to log
Signed-off-by: Adam Tao <tcx4c70@gmail.com>
2025-07-19 18:36:54 +08:00
Timothy Jaeryang Baek 37c2fb0aa8 enh: folders 2025-07-19 14:29:08 +04:00
Timothy Jaeryang Baek 3251f8b14d refac 2025-07-19 12:44:23 +04:00
Timothy Jaeryang Baek ea18ffdade refac/fix 2025-07-19 12:17:35 +04:00
Tim Jaeryang Baek 2d4675dc1c Merge pull request #15842 from expruc/fix/handle_remove_files_from_vdb
fix: deleting files from vector DB upon file deletion
2025-07-19 12:13:03 +04:00
expruc 30a079cba8 added handler for deleting files from vdb upon files deletion 2025-07-18 18:40:29 +03:00
Timothy Jaeryang Baek 6478a50c72 refac 2025-07-18 18:26:43 +04:00
Timothy Jaeryang Baek c414f0c3ce enh: follow up prompts behaviour 2025-07-18 17:49:24 +04:00
Timothy Jaeryang Baek 5e4e1cf662 refac 2025-07-18 16:57:49 +04:00
Timothy Jaeryang Baek 18adb28da0 enh: chatFadeStreamingText toggle 2025-07-18 16:45:56 +04:00
Timothy Jaeryang Baek 853cf82ed8 enh: text fade in effect 2025-07-18 16:38:43 +04:00
Timothy Jaeryang Baek dcb1c0a146 refac 2025-07-18 16:22:49 +04:00
Timothy Jaeryang Baek c1c589d609 refac/enh: rich text input 2025-07-18 15:58:06 +04:00
Tim Jaeryang Baek 032fa52190 Merge pull request #15833 from rgaricano/dev-audio_Mime_comment
FIX: Correction of mimetypes string
2025-07-18 15:20:22 +04:00
Timothy Jaeryang Baek 671f577264 feat/enh: forward chat id in header 2025-07-18 15:03:46 +04:00
_00_ f08f6ac171 FIX: Correction of mimetypes string
Removed " , * for all) " from mimetype help string.

If mimetypes is set as * any file is tried to transcribe.
2025-07-18 12:54:08 +02:00
Tim Jaeryang Baek a395abd5d7 Merge pull request #15794 from expruc/feat/otel_user_metrics
feat: open telemetry user metrics
2025-07-18 14:22:21 +04:00
Tim Jaeryang Baek 25c3e7f851 Merge pull request #15810 from rgaricano/dev-Fix_AddPostHog_lib
FIX: Add posthog lib forcing version to avoid opentelemetry issue
2025-07-18 14:21:58 +04:00
Tim Jaeryang Baek 6bb5248812 Merge pull request #15789 from azurewtl/hotfix-web-config-clean-reranker
- fix: keep reranker_model config been removed by web search config
2025-07-18 13:00:40 +04:00
Tim Jaeryang Baek 3500d26931 Merge pull request #15825 from EventHorizon-AI/fix/ollama-tool-call
fix: ollama tool call
2025-07-18 12:58:07 +04:00
Tim Jaeryang Baek 0d3f066afc Merge pull request #15824 from zetavg/update-zh-tw-translation-0718
i18n: Update & fix zh-TW translation
2025-07-18 12:56:49 +04:00
Timothy Jaeryang Baek bcee131971 refac/fix: note chat permissions issue 2025-07-18 12:55:08 +04:00
Timothy Jaeryang Baek 3e67ce6e8e refac/fix: shortcut modal 2025-07-18 12:47:22 +04:00
Timothy Jaeryang Baek 43e11aa807 refac 2025-07-18 12:41:21 +04:00
Timothy Jaeryang Baek 2fa8aff82f refac/fix: tag handling 2025-07-18 11:56:40 +04:00
Timothy Jaeryang Baek 6ccf783f00 refac: /users/all endpoint 2025-07-18 11:46:14 +04:00
Pokai Chang bb19b71e39 i18n: Update & fix zh-TW translation 2025-07-18 06:50:03 +08:00
EntropyYue 260137fc12 fix: ollama tool call 2025-07-18 06:11:53 +08:00
Tim Jaeryang Baek 67bc431247 Merge pull request #15798 from MonsieurBibo/i18n-fr
feat(i18n): Add missing translations to fr-FR locale
2025-07-18 02:11:01 +04:00
Timothy Jaeryang Baek 86e46ebe6a chore 2025-07-17 17:53:36 +04:00
Timothy Jaeryang Baek d4ece7384c enh/refac: note image upload 2025-07-17 17:36:06 +04:00
Timothy Jaeryang Baek 9d633b062b refac/fix 2025-07-17 16:25:26 +04:00
Timothy Jaeryang Baek da91aba10f chore: tiptap bump 2025-07-17 14:43:42 +04:00
_00_ ca1a61ce60 Fix: Add posthog lib to requirement for avoid opentelemetry issue
Add "posthog==5.4.0" lib to requirements to avoid chromadb issue for lib versions incompatibility with that lib (chromadb  requiere posthog>=2.4.0,<6.0.0 ) which produce error in opentelemetry.
2025-07-17 10:15:18 +02:00
_00_ 5a0d0c6930 FIX: Add posthog lib to avoid opentelemetry issue
Add "posthog==5.4.0" lib to requirements to avoid chromadb issue for lib versions incompatibility with that lib (chromadb  requiere posthog>=2.4.0,<6.0.0 ) which produce error in opentelemetry.
2025-07-17 10:12:37 +02:00
Timothy Jaeryang Baek fce27202d9 refac 2025-07-17 02:37:18 +04:00
Timothy Jaeryang Baek 9634df4347 refac/enh: group add/remove users endpoints 2025-07-17 01:50:37 +04:00
MonsieurBibo 2e59d47e7d feat(i18n): Add missing translations to fr-FR locale
Completes various previously untranslated UI strings in the French translation file.
2025-07-16 22:25:14 +02:00
expruc cbbc4cfd26 added active and total user metrics 2025-07-16 22:06:36 +03:00
Timothy Jaeryang Baek 51242f4484 refac/fix 2025-07-16 22:22:59 +04:00
Azure Wang 9aff166f83 - fix: keep reranker_model config been removed by web search config 2025-07-16 23:51:23 +08:00
bekzod 4bc054a347 Update docling endpoint 2025-07-16 20:40:13 +05:00
Timothy Jaeryang Baek 4f5d949af6 enh: OAUTH_TOKEN_ENDPOINT_AUTH_METHOD 2025-07-16 15:46:33 +04:00
Timothy Jaeryang Baek 19f1286cc7 fix: emoji call 2025-07-16 15:38:48 +04:00
Timothy Jaeryang Baek 82093cf690 refac/fix: disable file upload by drag with unsupported model 2025-07-16 15:25:56 +04:00
Timothy Jaeryang Baek 83c09f15ac refac/enh: reasoning tag handling 2025-07-16 15:20:03 +04:00
Timothy Jaeryang Baek 980b7c55a5 refac/fix: sources handling 2025-07-16 15:12:04 +04:00
Timothy Jaeryang Baek 8807ac8cac refac/enh: file upload with paste 2025-07-16 15:01:03 +04:00
Timothy Jaeryang Baek 20b6892d9b refac: chat exception handling 2025-07-16 14:54:56 +04:00
Tim Jaeryang Baek 5832c3ea97 Merge pull request #15718 from sihyeonn/feat/sh-support-failover
feat: add Redis Sentinel failover support for high availability
2025-07-16 14:39:42 +04:00
Tim Jaeryang Baek ff8c429687 Merge pull request #15749 from Classic298/patch-1
i18n: small german updates
2025-07-16 13:58:04 +04:00
Timothy Jaeryang Baek abe280f0a3 refac/fix: reranking function 2025-07-16 13:56:02 +04:00
Sihyeon Jang 9067eac4ca chore: add log for redis.exceptions
Signed-off-by: Sihyeon Jang <sihyeon.jang@navercorp.com>
2025-07-16 15:23:18 +09:00
Sihyeon Jang 65882c30cb refactor: change MAX_RETRY_COUNT as env
Signed-off-by: Sihyeon Jang <sihyeon.jang@navercorp.com>
2025-07-16 15:23:18 +09:00
Sihyeon Jang 1b7ac7c739 chore: format, lint
Signed-off-by: Sihyeon Jang <sihyeon.jang@navercorp.com>
2025-07-16 15:23:18 +09:00
Sihyeon Jang 5fe0c835b1 chore: update lock file for pytest-asyncio
Signed-off-by: Sihyeon Jang <sihyeon.jang@navercorp.com>
2025-07-16 15:23:18 +09:00
Sihyeon Jang 728c39eed7 chore: add tests with pytest-asyncio
Signed-off-by: Sihyeon Jang <sihyeon.jang@navercorp.com>
2025-07-16 15:23:18 +09:00
Sihyeon Jang 423d0923d9 feat: add Redis Sentinel failover support for high availability
- Implement SentinelRedisProxy class with automatic master discovery
- Add retry logic for handling connection failures and read-only errors
- Support both async and sync Redis operations with Sentinel
- Ensure backward compatibility with existing Redis configurations
- Provide seamless failover during master node outages

This enhancement significantly improves system reliability by eliminating
single points of failure in Redis deployments and ensuring continuous
service availability during infrastructure issues.

Signed-off-by: Sihyeon Jang <sihyeon.jang@navercorp.com>
2025-07-16 15:23:17 +09:00
0xThresh.eth d9f2b6b14e feat: add starter config for s3 vector 2025-07-15 21:20:54 -06:00
Classic298 5b73f5065b i18n: small german updates 2025-07-15 21:34:49 +02:00
Tim Jaeryang Baek ae716bddee Merge pull request #15717 from sihyeonn/fix/sh-cleanup
fix: improve cleanup_response positioning for better resource management
2025-07-15 22:48:10 +04:00
Timothy Jaeryang Baek 500e6e64fe refac 2025-07-15 21:57:24 +04:00
Tim Jaeryang Baek 1f0cc2724a Merge pull request #15743 from byg1004/dev
fix: i18n korean
2025-07-15 21:43:27 +04:00
YG aea1a27f2f fix: i18n korean 2025-07-15 21:42:02 +04:00
Timothy J. Baek cecb3a42b0 refac 2025-07-15 21:24:05 +04:00
Timothy J. Baek fa91795b77 refac 2025-07-15 20:59:28 +04:00
Tim Jaeryang Baek 0aabbdd23a Merge pull request #15714 from macnow/patch-1
Update pl_PL locale
2025-07-15 20:50:02 +04:00
Tim Jaeryang Baek e03925ffc0 Merge pull request #15729 from rgaricano/dev-issues_15728
Fix: some FileItemModal.svelte strings to i18n
2025-07-15 20:49:34 +04:00
Tim Jaeryang Baek 2d46cc6d97 Merge pull request #15736 from ryanmrodriguez/ryanmrodriguez-patch-1
fix: DB Environment - Avoid Duplicate '@' Symbol in Credentials
2025-07-15 20:47:47 +04:00
Timothy J. Baek 14ef2e5dea refac: collaborative editor 2025-07-15 20:43:49 +04:00
Ryan Rodriguez 74392f4598 Avoid duplicate @ 2025-07-15 10:42:41 -05:00
Timothy Jaeryang Baek 6343f4293a refac 2025-07-15 18:45:59 +04:00
Timothy Jaeryang Baek e72da0df5a refac/fix: WEBSOCKET_REDIS_LOCK_TIMEOUT type 2025-07-15 18:10:36 +04:00
Timothy Jaeryang Baek f7fae947a7 fix/refac: hide system prompt for users w/o permission 2025-07-15 16:52:21 +04:00
_00_ 120b8f2a83 fix: update FileItemModal.svelte- locales strings
add strings "Using Entire Document" and "Using Focused Retrieval"  to i18n locales,

to address issue: #15728
2025-07-15 14:05:10 +02:00
_00_ 1092827895 Fix some FileItemModal.svelte- locales strings
convert strings "Using Entire Document" and "Using Focused Retrieval"  to i18n locales,

to address issue: https://github.com/open-webui/open-webui/issues/15728
2025-07-15 13:55:49 +02:00
Sihyeon Jang 058369adea fix: improve cleanup_response positioning for better resource management
Signed-off-by: Sihyeon Jang <sihyeon.jang@navercorp.com>
2025-07-15 10:14:30 +09:00
Sihyeon Jang 17f0bef2e2 refactor: use cleanup_response on openai
Signed-off-by: Sihyeon Jang <sihyeon.jang@navercorp.com>
2025-07-15 10:11:22 +09:00
Maciej Nowakowski 07bb5cc4aa Update pl_PL locale 2025-07-14 23:48:15 +02:00
_00_ 5889e8238a UPD: BM25 Hybrid Slider - Legend inline
BM25 Hybrid Slider - Legend (lexical - semantic) inline
2025-07-14 22:17:37 +02:00
Tim Jaeryang Baek 9063942055 Merge pull request #15712 from qingchunnh/Update_zh-CN-25715-2
i18n: Fix zh-CN
2025-07-14 23:58:39 +04:00
_00_ 3345514efc UPD: en-EN locales add lexical, semantic & fix previous incomplete
en-EN locales add lexical, semantic & fix previous incomplete
2025-07-14 21:23:35 +02:00
_00_ fd735590da UPD: Add lexical-semantic reference above slider
Add lexical-semantic reference above slider
2025-07-14 21:20:11 +02:00
qingchun 8a4ea2b801 i18n: Fix zh-CN 2025-07-15 03:18:12 +08:00
_00_ cd554ebce5 UPD: Add Weight BM25 Hybrid Search string
UPD: Add Weight BM25 Hybrid Search string
to fit with tooltip of new slider in PR: https://github.com/open-webui/open-webui/pull/15705
"The Weight of BM25 Hybrid Search. 0 more lexical, 1 more semantic. Default 0.5"
2025-07-14 20:19:06 +02:00
Tim Jaeryang Baek 2470da8336 Merge pull request #15708 from open-webui/dev
fix/refac: icon styling
2025-07-14 22:02:36 +04:00
Timothy Jaeryang Baek 9b6dd59b86 fix/refac: icon styling 2025-07-14 22:02:14 +04:00
Tim Jaeryang Baek f966935d1d Merge pull request #15448 from open-webui/dev
0.6.16
2025-07-14 21:39:26 +04:00
Timothy Jaeryang Baek 045984e86f doc: changelog 2025-07-14 21:25:00 +04:00
Timothy Jaeryang Baek b1752cfb6a refac: styling 2025-07-14 21:24:31 +04:00
Tim Jaeryang Baek 21eff61940 Merge pull request #15707 from Classic298/patch-1
fix: the changelog
2025-07-14 21:21:47 +04:00
Classic298 4897cfa9c8 rem backticks and md tags 2025-07-14 19:21:08 +02:00
Tim Jaeryang Baek f79746b148 Merge pull request #15706 from qingchunnh/Update_zh-CN-25715
i18n: Update zh-CN
2025-07-14 21:20:26 +04:00
Classic298 ebd2abdd0a Update CHANGELOG.md 2025-07-14 19:17:16 +02:00
Classic298 69fd8436aa Update CHANGELOG.md 2025-07-14 18:52:07 +02:00
Classic298 7063d7d103 Update CHANGELOG.md 2025-07-14 18:42:53 +02:00
Classic298 541f7bc82c Update CHANGELOG.md 2025-07-14 18:32:57 +02:00
qingchun ed8438628e i18n: Update zh-CN 2025-07-15 00:14:28 +08:00
Timothy Jaeryang Baek 288fba874e refac: styling 2025-07-14 20:08:49 +04:00
Timothy Jaeryang Baek 8f5d23ec37 refac 2025-07-14 19:57:41 +04:00
Timothy Jaeryang Baek d5b8acfbe7 chore: dep bump pyproject 2025-07-14 19:53:06 +04:00
Timothy Jaeryang Baek 50ed194c48 doc: changelog 2025-07-14 19:51:12 +04:00
_00_ 4a6d333a03 FEAT: Frontend HybridSearch-BM25 Slider
FEAT: Frontend HybridSearch-BM25 Slider

Front-end modification for the BM25-Weight parameter input.

This proposal modifies the numerical typed input to a slider similar to the advanced model parameters.
2025-07-14 17:48:49 +02:00
Timothy Jaeryang Baek 8b8ce2592d refac: styling 2025-07-14 19:34:40 +04:00
Timothy Jaeryang Baek 493e147be6 chore: format 2025-07-14 19:13:54 +04:00
Timothy Jaeryang Baek 51691adcbc refac 2025-07-14 19:12:17 +04:00
Timothy Jaeryang Baek 6a4542a321 refac: folder delete 2025-07-14 19:09:54 +04:00
Timothy Jaeryang Baek 92c9068369 refac 2025-07-14 17:50:03 +04:00
Timothy Jaeryang Baek f19d5b05a8 refac: styling 2025-07-14 17:47:21 +04:00
Timothy Jaeryang Baek 7f1f39058a enh/refac: distributed crdt 2025-07-14 17:14:56 +04:00
Timothy Jaeryang Baek fb71a4106f refac 2025-07-14 16:45:30 +04:00
Timothy Jaeryang Baek d76a3d5ce2 refac: ydoc 2025-07-14 16:29:42 +04:00
Timothy Jaeryang Baek 203c5b1956 refac 2025-07-14 14:37:28 +04:00
Timothy Jaeryang Baek f402957f05 refac 2025-07-14 14:35:13 +04:00
Timothy Jaeryang Baek 18bd83413b refac 2025-07-14 14:05:06 +04:00
Timothy Jaeryang Baek 0013f5c1fc refac/enh: forward user info header to reranker 2025-07-14 13:59:10 +04:00
Timothy Jaeryang Baek b4f04ff3a7 enh/refac: pgvector pool support 2025-07-14 12:18:44 +04:00
Timothy Jaeryang Baek 9c49d9e641 refac: aria label model item 2025-07-14 12:09:08 +04:00
Tim Jaeryang Baek c067cf996e Merge pull request #15687 from Classic298/patch-1
i18n: german translations
2025-07-14 10:12:19 +04:00
Tim Jaeryang Baek d87d6f99d3 Merge pull request #15692 from qingchunnh/Update_zh-CN-25714
i18n: Update & Improve & Fix zh-CN
2025-07-14 10:12:07 +04:00
Tim Jaeryang Baek a03bdba711 Merge pull request #15685 from rgaricano/dev-add_requirements_lib
UPD: requirements.txt adding httpx & optionals
2025-07-14 10:11:27 +04:00
Dieu 41faec758b Merge branch 'dev' into feat/scim-2.0-support 2025-07-14 00:48:50 +02:00
Dieu 6469822af1 Update configs.py 2025-07-14 00:44:25 +02:00
Dieu 519b7af6d8 remove useless 2025-07-14 00:36:14 +02:00
Dieu 1d9c1f741d Update scim.py 2025-07-13 23:54:33 +02:00
Dieu c9fda793e2 udpate 2025-07-13 23:42:33 +02:00
Dieu 39bcee3f7b remove ui config 2025-07-13 23:24:32 +02:00
qingchun 6649201fe5 i18n: Fix zh-CN 2025-07-14 02:07:09 +08:00
qingchun e89e0dea4a i18n: Improve zh-CN 2025-07-14 01:56:57 +08:00
qingchun 6ee0c0411e i18n: Update & Improve zh-CN 2025-07-14 01:45:22 +08:00
DieuandClaude f4d54c518e feat: Add SCIM 2.0 support for enterprise user provisioning
Implements SCIM 2.0 protocol for automated user and group provisioning from identity providers like Okta, Azure AD, and Google Workspace.

Backend changes:
- Add SCIM configuration with PersistentConfig for database persistence
- Implement SCIM 2.0 endpoints (Users, Groups, ServiceProviderConfig)
- Add bearer token authentication for SCIM requests
- Include comprehensive test coverage for SCIM functionality

Frontend changes:
- Add SCIM admin settings page with token generation
- Implement SCIM configuration management UI
- Add save functionality and proper error handling
- Include SCIM statistics display

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-07-13 16:34:41 +02:00
Timothy Jaeryang Baek 8e7731f139 refac: styling 2025-07-13 14:10:35 +04:00
Classic298 30fd6822f8 i18n: german translations 2025-07-13 12:08:58 +02:00
Timothy Jaeryang Baek a123629f74 refac 2025-07-13 14:08:19 +04:00
_00_ 3cde5fb3aa UPD: requirements.txt adding httpx
Update requirements.txt to add httpx and optional support for:

h2 - HTTP/2 support. (Optional, with httpx[http2])
socksio - SOCKS proxy support. (Optional, with httpx[socks])
rich - Rich terminal support. (Optional, with httpx[cli])
click - Command line client support. (Optional, with httpx[cli])
brotli or brotlicffi - Decoding for "brotli" compressed responses. (Optional, with httpx[brotli])
zstandard - Decoding for "zstd" compressed responses. (Optional, with httpx[zstd])
httpx[socks,http2,zstd,cli,brotli]==0.28.1
2025-07-13 11:01:25 +02:00
Tim Jaeryang Baek bd44ab64d0 refac: note editor model selection 2025-07-13 04:53:27 +04:00
Tim Jaeryang Baek d4dfece4f9 Merge pull request #15675 from rgaricano/dev_es-ES
UPD: Locale es-ES translation, rev.1 updated to v.0.6.16
2025-07-13 03:53:24 +04:00
Timothy Jaeryang Baek 98d38657cb refac: styling 2025-07-13 03:52:06 +04:00
Timothy Jaeryang Baek 6f55679a8a refac 2025-07-13 03:50:35 +04:00
Timothy Jaeryang Baek 217bd440c3 refac 2025-07-13 03:46:39 +04:00
Timothy Jaeryang Baek 2b9e30a107 refac 2025-07-13 03:43:30 +04:00
Timothy Jaeryang Baek 57156065e3 refac 2025-07-13 03:30:42 +04:00
Timothy Jaeryang Baek 751e32e427 refac 2025-07-13 03:26:52 +04:00
Timothy Jaeryang Baek ada78cb952 chore: format 2025-07-13 03:23:38 +04:00
Timothy Jaeryang BaekandClassic298 7607c53bd5 feat: folders as projects
Co-Authored-By: Classic298 <27028174+Classic298@users.noreply.github.com>
2025-07-13 03:23:05 +04:00
_00_ 027c08430a UPD: Locale es-ES translation, rev.1 updated to v.0.6.16
UPD: Locale es-ES translation, updated to v.0.6.16
rev.1

Description:

es-ES translation updated to v.0.6.16
2025-07-13 00:55:32 +02:00
Timothy Jaeryang BaekandClassic298 5abc03f4dd enh: enter into folder
Co-Authored-By: Classic298 <27028174+Classic298@users.noreply.github.com>
2025-07-13 02:40:48 +04:00
Timothy Jaeryang BaekandClassic298 6176dba3c9 refac
Co-Authored-By: Classic298 <27028174+Classic298@users.noreply.github.com>
2025-07-13 01:50:01 +04:00
Timothy Jaeryang BaekandClassic298 80f3c97668 refac: folder chat handling
Co-Authored-By: Classic298 <27028174+Classic298@users.noreply.github.com>
2025-07-13 01:26:56 +04:00
Timothy Jaeryang BaekandClassic298 5b14d15b00 feat: migrate folder table
Co-Authored-By: Classic298 <27028174+Classic298@users.noreply.github.com>
2025-07-13 00:51:58 +04:00
Timothy Jaeryang Baek 847a78a276 refac: styling 2025-07-13 00:37:24 +04:00
Timothy Jaeryang Baek d8ccb087ac refac: styling 2025-07-13 00:37:01 +04:00
Timothy Jaeryang Baek 3b4344c1bf fix: leaflet marker 2025-07-13 00:33:19 +04:00
Timothy Jaeryang Baek 87847ab31a chore: format 2025-07-13 00:15:16 +04:00
Timothy Jaeryang Baek 9ae22c5efe refac 2025-07-13 00:00:40 +04:00
Timothy Jaeryang Baek b5cbac7b28 refac 2025-07-12 23:50:41 +04:00
Timothy Jaeryang Baek 3be734fd84 enh: note editor prompts 2025-07-12 23:47:27 +04:00
Timothy Jaeryang Baek 6f896c4352 enh: note editor prompts 2025-07-12 23:34:40 +04:00
Timothy Jaeryang Baek 3e01286b61 refac 2025-07-12 23:31:04 +04:00
Timothy Jaeryang Baek b711c88bab refac: note browser title 2025-07-12 23:26:01 +04:00
Timothy Jaeryang Baek de343021c1 enh: note search 2025-07-12 23:08:46 +04:00
Timothy Jaeryang Baek f5613fb3c2 refac 2025-07-12 23:00:46 +04:00
Timothy Jaeryang Baek 9cabef67a7 refac 2025-07-12 22:55:45 +04:00
Timothy Jaeryang Baek 0df488e456 enh: generate note title 2025-07-12 22:43:09 +04:00
Timothy Jaeryang Baek bc7e5c7c04 refac 2025-07-12 22:18:52 +04:00
Timothy Jaeryang Baek 12edc3221e refac: styling 2025-07-12 22:14:41 +04:00
Timothy Jaeryang Baek 60fb643109 refac/fix: code block input 2025-07-12 21:54:05 +04:00
Timothy Jaeryang Baek fb78bff3c0 refac 2025-07-12 21:39:31 +04:00
Timothy Jaeryang Baek ce7cc37077 refac 2025-07-12 18:15:35 +04:00
Tim Jaeryang Baek 81d6e217f1 Merge pull request #15670 from rgaricano/dev_es-ES
UPD: Locale es-ES translation, updated to v.0.6.16
2025-07-12 17:18:57 +04:00
Tim Jaeryang Baek 33487283e8 Merge pull request #15669 from moblangeois/dev
i18n: Update fr-FR and fr-CA translation
2025-07-12 17:18:43 +04:00
Timothy Jaeryang Baek af59bee2ea refac 2025-07-12 17:17:31 +04:00
_00_ d3da236d96 UPD: Locale es-ES translation, updated to v.0.6.16
UPD: Locale es-ES translation, updated to v.0.6.16
2025-07-12 13:44:28 +02:00
Tim Jaeryang Baek fbaab9e446 refac 2025-07-12 15:36:30 +04:00
Tim Jaeryang Baek 4a69d857f3 refac 2025-07-12 15:32:26 +04:00
Tim Jaeryang Baek 290f4023b1 refac: add copy link functionality to notes view 2025-07-12 15:20:21 +04:00
Morgan Blangeois 4da2b050c6 Update translations files for fr-Fr and fr-CA 2025-07-12 10:12:11 +00:00
Timothy Jaeryang Baek d99698f7eb refac 2025-07-12 03:39:58 +04:00
Timothy Jaeryang Baek 12c4a7a3f7 refac: styling 2025-07-12 03:15:31 +04:00
Timothy Jaeryang Baek 955c2e2570 refac 2025-07-12 03:00:40 +04:00
Timothy Jaeryang Baek 627f680659 refac 2025-07-12 02:57:11 +04:00
Timothy Jaeryang Baek 01420fd189 refac 2025-07-12 02:51:51 +04:00
Timothy Jaeryang Baek 4351702587 refac 2025-07-12 02:38:52 +04:00
Timothy Jaeryang Baek b52a162597 refac 2025-07-12 02:20:32 +04:00
Timothy Jaeryang Baek baa1a44397 refac 2025-07-12 02:17:52 +04:00
Timothy Jaeryang Baek 3c80219808 refac 2025-07-12 01:47:41 +04:00
Timothy Jaeryang Baek bcfe1a087f refac 2025-07-12 00:38:25 +04:00
Timothy Jaeryang Baek d509ca1133 refac 2025-07-12 00:34:18 +04:00
Tim Jaeryang Baek 9b84a8e443 Merge pull request #15632 from athoik/quote
fix: don't over quote forwarded headers
2025-07-12 00:24:29 +04:00
Tim Jaeryang Baek 3402bfd96c Merge pull request #15655 from rgaricano/dev-audio_Mime_comment
DOC: Extend Audio MIME Type Setting Explanation
2025-07-12 00:23:32 +04:00
Timothy Jaeryang Baek 627b39cdbb refac 2025-07-12 00:19:24 +04:00
Timothy Jaeryang Baek 9b8d6ed6d2 refac: notes 2025-07-12 00:16:57 +04:00
Timothy Jaeryang Baek 2fbff741da feat: collaborative note 2025-07-11 23:59:48 +04:00
_00_ e3c8e1bb71 DOC: Extend Audio MIME Type Setting Explanation
DOC: Extend Audio MIME Type Setting Explanation

Add explanation to add multiple mime types in audio settings labeling
2025-07-11 20:18:54 +02:00
Timothy Jaeryang Baek d3b14ff827 refac 2025-07-11 18:41:09 +04:00
Timothy Jaeryang Baek 6820e41eb2 refac 2025-07-11 18:14:56 +04:00
Timothy Jaeryang Baek 22052d2c24 refac: task 2025-07-11 18:14:48 +04:00
Timothy Jaeryang Baek 0202926e35 refac 2025-07-11 18:09:24 +04:00
Timothy Jaeryang Baek 47cd0770e4 refac 2025-07-11 17:54:38 +04:00
Timothy Jaeryang Baek 5b50f3cd69 chore: dep 2025-07-11 17:54:27 +04:00
Timothy Jaeryang Baek 788e7d0487 refac 2025-07-11 17:53:53 +04:00
Timothy Jaeryang Baek 11914d20b4 refac: styling 2025-07-11 12:39:58 +04:00
Timothy Jaeryang Baek 77c1905609 refac 2025-07-11 12:35:42 +04:00
Ioannis Papapanagiotou 54d606eabf refactor(logger): Conditionally include extra_json in logs 2025-07-11 01:29:27 -07:00
Timothy Jaeryang Baek 033d07ee23 refac: file handling 2025-07-11 12:29:17 +04:00
Timothy Jaeryang Baek 0db8bedf45 refac: tts input 2025-07-11 12:15:13 +04:00
Timothy Jaeryang Baek 3bc5485867 enh: proper undo/redo note editor support 2025-07-11 12:00:41 +04:00
Timothy Jaeryang Baek 3b9d86de0b refac 2025-07-11 12:00:21 +04:00
Timothy Jaeryang Baek 6a3b0e3110 refac 2025-07-11 01:46:06 +04:00
Timothy Jaeryang Baek 249b12e44f refac: knowledge selector 2025-07-11 01:41:40 +04:00
Timothy Jaeryang Baek 84bf1892bb refac 2025-07-11 01:34:24 +04:00
Athanasios Oikonomou 96758176cc fix: don't over quote forwarded headers
Fix introduced on #15035 is over quoting headers.

Eg mails instead of user@example.com shown as user%40example.com
Eg names instead of First Last shown as First%20Last

Also we are spending some time quoting ids and roles without required.

Keep quote only on user name, initially had problem based on the discussion
https://github.com/open-webui/open-webui/discussions/14391

Also add space in safe characters, in order remove %20 from names.
2025-07-10 22:08:28 +03:00
Wonyong Lee a8a8fd537f yarn.lock removed 2025-07-10 12:15:13 +00:00
Wonyong Lee 46e0992a83 json_serialize returing varchar2(2096) 2025-07-10 12:12:43 +00:00
Timothy Jaeryang Baek 1f641ce1fb refac 2025-07-10 14:06:15 +04:00
Timothy Jaeryang Baek 22af53f60c refac 2025-07-10 11:14:53 +04:00
Tim Jaeryang Baek d1b0a5739d Merge pull request #15619 from amberyufangchiu/amberyufangchiu-dev
i18n: Update zh-TW (Traditional Chinese) translation.json
2025-07-10 10:33:30 +04:00
Tim Jaeryang Baek 18c19743c4 Merge branch 'dev' into amberyufangchiu-dev 2025-07-10 10:33:12 +04:00
Wonyong Lee 39e4551a45 add oracledb package to requirements.txt 2025-07-10 04:28:52 +00:00
Amber Chiu 77acb9bb41 i18n: Update zh-TW (Traditional Chinese) translation.json 2025-07-10 09:40:58 +08:00
Timothy Jaeryang Baek 24f0da172e fix: table align markdown rendering 2025-07-09 23:39:54 +04:00
Timothy Jaeryang Baek 45d58e73a9 refac: styling 2025-07-09 23:22:59 +04:00
Timothy Jaeryang Baek 8d84b4c2a4 enh/refac: temp chat file upload behaviour
client-side content extraction
2025-07-09 22:59:37 +04:00
Timothy Jaeryang Baek 8fb54b133e refac 2025-07-09 22:33:36 +04:00
Timothy Jaeryang Baek 6d31fc7875 refac: styling 2025-07-09 13:50:45 +04:00
Timothy Jaeryang Baek 49e57b6d13 refac 2025-07-09 13:47:10 +04:00
Timothy Jaeryang Baek 3b9f4a6f5e enh: note word/char counter 2025-07-09 13:38:54 +04:00
Timothy Jaeryang Baek 6ee4274b3b enh: rich text input link 2025-07-09 13:29:27 +04:00
Timothy Jaeryang Baek 04962922d7 refac 2025-07-09 13:00:28 +04:00
Tim Jaeryang Baek 9d8378d2e2 Merge pull request #15604 from EventHorizon-AI/fix/tool-results
fix: tool results
2025-07-09 12:45:10 +04:00
Tim Jaeryang Baek 67c88f90dc Merge pull request #15601 from headwAI-GmbH/streamline-resource-loading-url
chore: streamline the URL used for resources like favicon.png
2025-07-09 12:43:58 +04:00
Timothy Jaeryang Baek b615d13e46 refac: styling 2025-07-09 11:44:51 +04:00
guenhter 196af9eaf7 chore: streamline the URL used for resources like favicon.png 2025-07-09 09:14:51 +02:00
Timothy Jaeryang Baek 0a3c448a52 refac: styling 2025-07-09 11:06:40 +04:00
EntropyYue 5b8007fcb5 fix: tool result frontend 2025-07-09 15:01:19 +08:00
EntropyYue dc0d420af5 fix: tool result 2025-07-09 14:40:53 +08:00
Tim Jaeryang Baek 8882df5829 Merge pull request #15596 from qingchunnh/Update_zh-CN-2579
i18n: Update & Improve zh-CN
2025-07-09 10:26:50 +04:00
qingchun 9b5c9ffb30 i18n: Improve zh-CN 2025-07-09 10:03:09 +08:00
qingchun b09973cafe i18n: Update & Improve zh-CN 2025-07-09 09:44:55 +08:00
Timothy Jaeryang Baek 40b564dedd refac: styling 2025-07-09 04:53:31 +04:00
Timothy Jaeryang Baek a1ddbff744 refac: styling 2025-07-09 04:36:21 +04:00
Timothy Jaeryang Baek 04ac14463b refac: styling 2025-07-09 04:33:52 +04:00
Timothy Jaeryang Baek 784c5b5471 enh: remember note editor preference 2025-07-09 03:50:20 +04:00
Timothy Jaeryang Baek b93ee37f9a refac 2025-07-09 03:43:41 +04:00
Timothy Jaeryang Baek bdab9dd596 refac: task list handling 2025-07-09 03:39:57 +04:00
Timothy Jaeryang Baek e37e6e6b03 enh/refac: allow setting full context mode from model editor 2025-07-09 03:22:24 +04:00
Timothy Jaeryang Baek dd87111c80 refac 2025-07-09 03:15:20 +04:00
Timothy Jaeryang Baek c2ac797650 feat: formatting buttons 2025-07-09 03:11:51 +04:00
Timothy Jaeryang Baek 9a3a0070d6 refac: styling 2025-07-09 02:04:09 +04:00
Timothy Jaeryang Baek e2f5d7caec refac: styling 2025-07-09 01:53:51 +04:00
Timothy Jaeryang Baek 9632f40335 enh: support commands in note chat 2025-07-09 01:49:43 +04:00
Timothy Jaeryang Baek e9821c0881 refac 2025-07-09 01:31:28 +04:00
Timothy Jaeryang Baek b3c4bc6041 enh: allow full context mode for collections 2025-07-09 01:29:49 +04:00
Timothy Jaeryang Baek d5f9bbc7a7 enh: reference note in chat 2025-07-09 01:17:25 +04:00
Timothy Jaeryang Baek f2ee99d760 refac: styling 2025-07-09 01:15:32 +04:00
Tim Jaeryang Baek 91628f5392 Merge pull request #15593 from tarmst/add-openai-to-pyodide
Feat: Add OpenAI package to Pyodide code blocks
2025-07-09 00:54:06 +04:00
Timothy Jaeryang Baek 1de03c364f refac: checkbox styling 2025-07-09 00:34:03 +04:00
Timothy Jaeryang Baek 6bf218f720 refac: styling 2025-07-09 00:22:25 +04:00
Timothy Jaeryang Baek 40a715be89 refac: styling 2025-07-09 00:05:59 +04:00
Timothy Jaeryang Baek 248fd10406 enh: task list rich text input 2025-07-08 23:57:34 +04:00
tarmst 0033b3fe4e Add OpenAI package to pyodide code blocks 2025-07-08 19:28:43 +00:00
Timothy Jaeryang Baek f69db54b7e enh: model workspace tag filter view 2025-07-08 23:05:39 +04:00
Timothy Jaeryang Baek 0765f6c230 refac 2025-07-08 22:56:46 +04:00
Timothy Jaeryang Baek 42f2de68bd refac: styling 2025-07-08 22:38:31 +04:00
Timothy Jaeryang Baek 6f98379c82 enh: checkbox input variable 2025-07-08 21:33:51 +04:00
Timothy Jaeryang Baek 2cde746e38 refac: styling 2025-07-08 16:46:14 +04:00
Timothy Jaeryang Baek 74cafce4d2 refac: note revision history 2025-07-08 16:43:26 +04:00
Timothy Jaeryang Baek d3ba1a0592 refac: prompt variable handling 2025-07-08 16:30:06 +04:00
Tim Jaeryang Baek e3b8f700e4 Merge pull request #14264 from diwakar-s-maurya/patch-6
feat: add langchain markdown document splitter
2025-07-08 15:55:20 +04:00
Timothy Jaeryang Baek ed72ad1728 refac: styling 2025-07-08 14:51:50 +04:00
Timothy Jaeryang Baek 27aa628c86 chore: i18n 2025-07-08 13:29:04 +04:00
Tim Jaeryang Baek a748f19ac2 Merge pull request #15548 from expruc/fix/docling_ignore_html
fix: text/html files being detected as text when loaded with docling/tika
2025-07-08 13:16:01 +04:00
Timothy Jaeryang Baek 5d5bb9d1f2 refac 2025-07-08 13:14:22 +04:00
Timothy Jaeryang Baek 8f74070688 refac: styling 2025-07-08 12:54:11 +04:00
Timothy Jaeryang Baek 09b5c1b4dc refac 2025-07-08 12:50:06 +04:00
Timothy Jaeryang Baek 8febc54a88 refac: styling 2025-07-08 12:39:04 +04:00
Timothy Jaeryang Baek 7cfcf9b21d refac: styling 2025-07-08 12:35:35 +04:00
Timothy Jaeryang Baek 9a064d9623 feat: edit notes via chat 2025-07-08 12:31:31 +04:00
Timothy Jaeryang Baek 45085d04eb refac 2025-07-08 12:31:22 +04:00
Timothy Jaeryang Baek c107a0cbf4 refac 2025-07-08 10:09:32 +04:00
Timothy Jaeryang Baekandpython4004 793648bbd6 fix/security: note access control
Co-Authored-By: python4004 <36403473+python4004@users.noreply.github.com>
2025-07-08 10:04:13 +04:00
Timothy Jaeryang Baek c42a6d8029 refac/fix: node dom issue 2025-07-08 10:00:20 +04:00
Tim Jaeryang Baek a56e9e586b Merge pull request #15573 from flefevre/main
i18n french
2025-07-08 09:58:34 +04:00
Timothy Jaeryang BaekandG30 0450c7cf48 refac: banners
Co-Authored-By: G30 <50341825+silentoplayz@users.noreply.github.com>
2025-07-08 01:37:10 +04:00
Francois LE FEVRE c17b2f3ca0 i18n french 2025-07-07 22:15:39 +02:00
Timothy Jaeryang Baek 30521bcac7 refac: styling 2025-07-08 00:07:50 +04:00
Timothy Jaeryang Baek 3a9d5b0494 refac 2025-07-07 23:51:01 +04:00
Timothy Jaeryang Baek 78dce85b03 fix: thread image only message 2025-07-07 22:00:38 +04:00
Timothy Jaeryang Baek 2d0e55e8f6 refac 2025-07-07 21:52:46 +04:00
Timothy Jaeryang Baek 5722da8e3b feat/enh: insert prompt as rich text 2025-07-07 21:43:28 +04:00
Oracle Public Cloud User e0afd7f496 fianl : vector-search-feature 2025-07-07 17:25:16 +00:00
Timothy Jaeryang Baek ade4b0d691 feat: insert chat message to notes 2025-07-07 21:22:07 +04:00
Oracle Public Cloud User 12ebdbae81 refactor oracle23ai.py 2025-07-07 16:21:34 +00:00
Timothy Jaeryang Baek a3c2018a4a feat: chat with notes 2025-07-07 20:19:17 +04:00
Timothy Jaeryang Baek b3001f95f6 refac 2025-07-07 19:58:10 +04:00
Timothy Jaeryang Baek 8cd7de3740 refac: styling 2025-07-07 19:56:31 +04:00
Timothy Jaeryang Baek d04d0c0510 refac 2025-07-07 19:47:32 +04:00
Timothy Jaeryang Baek 821243ef40 refac 2025-07-07 19:42:08 +04:00
Timothy Jaeryang Baek 67063e6400 refac 2025-07-07 19:33:54 +04:00
Timothy Jaeryang Baek a5367145bf refac 2025-07-07 19:32:41 +04:00
Timothy Jaeryang Baek 7003395a5f refac: experimental wording 2025-07-07 19:30:05 +04:00
Timothy Jaeryang Baek 9bd001a14b feat: note chat 2025-07-07 19:26:12 +04:00
Timothy Jaeryang Baek a31a1f3c0d refac: note editor 2025-07-07 18:26:52 +04:00
Timothy Jaeryang Baek 3392e2ef15 refac/fix: channel input 2025-07-07 17:49:03 +04:00
Timothy Jaeryang Baek 57f9eadd75 feat: models?id= support for admin settings 2025-07-07 17:16:51 +04:00
Timothy Jaeryang Baek a519c62250 chore: bump 2025-07-07 16:13:18 +04:00
Oracle Public Cloud User 25e241ae41 added new feature : oracle23ai vector search 2025-07-07 12:13:05 +00:00
Timothy Jaeryang Baek 3e15c8ab69 refac 2025-07-07 15:56:05 +04:00
Timothy Jaeryang Baek be6633e3be chore: bump ddgs 2025-07-07 15:55:26 +04:00
Oracle Public Cloud User b56dbb26be alpha2 2025-07-07 08:52:58 +00:00
Timothy Jaeryang Baek cfcfefb20c refac: tools handling 2025-07-07 11:42:52 +04:00
Timothy Jaeryang Baek 05a9b72670 refac/fix: base model cache 2025-07-07 11:30:27 +04:00
Oracle Public Cloud User 3e2fd074bb oracle 23ai vector search 2025-07-07 05:58:02 +00:00
Timothy Jaeryang Baek 31c63d8898 refac 2025-07-06 21:40:53 +04:00
expruc 453a2bd9b5 fixed issue where text/html files being detected as text when loaded 2025-07-06 20:10:26 +03:00
Timothy Jaeryang Baek 4a825dce7f refac 2025-07-06 17:51:03 +04:00
Tim Jaeryang Baek 7cb105abd4 Merge pull request #15119 from denispol/dev
fix: Preserve line breaks on multi-line paste in mobile WebViews
2025-07-06 17:41:09 +04:00
Timothy Jaeryang Baek e84c521177 refac/fix: rich text input in ff 2025-07-06 17:32:03 +04:00
Timothy Jaeryang Baek be7166d6fc refac 2025-07-06 16:16:05 +04:00
Timothy Jaeryang Baek 1c41e95ba6 fix/refac: ollama api backward compatibility 2025-07-06 15:02:10 +04:00
Timothy Jaeryang Baek 9a476ae41d refac: ollama api backward compatibility 2025-07-06 14:40:21 +04:00
Timothy Jaeryang Baek 2eab43c35e fix 2025-07-06 14:38:28 +04:00
Timothy Jaeryang Baek f92890b741 refac 2025-07-06 14:32:39 +04:00
Timothy Jaeryang Baek fc2fc6516e enh: autofocus to first input variable input 2025-07-06 14:31:32 +04:00
Timothy Jaeryang Baek 0a1f9966ef refac: audio error handling 2025-07-06 14:20:38 +04:00
Timothy Jaeryang Baek 44754e4c4a enh/refac: show input modal by default for prompt variables 2025-07-06 14:16:34 +04:00
Timothy Jaeryang Baek 7cc2afe973 refac: rich text input 2025-07-06 14:15:45 +04:00
Tim Jaeryang Baek a4885ac549 Merge pull request #15527 from gmacario/gmacario-fix-api-show
FIX: Ollama.py API endpoints to match Ollama's
2025-07-05 21:23:32 +04:00
Tim Jaeryang Baek 26ce14d163 Merge pull request #15541 from open-webui/main
refac: readme
2025-07-05 21:06:51 +04:00
Timothy Jaeryang Baek 5eca495d3e refac: readme 2025-07-05 21:06:32 +04:00
Timothy Jaeryang Baek aeef5d4dcd refac: mobile navbar 2025-07-05 02:26:52 +04:00
Timothy Jaeryang BaekandClassic298 9f87a0cf21 feat: input variables
Co-Authored-By: Classic298 <27028174+Classic298@users.noreply.github.com>
2025-07-05 02:16:44 +04:00
Timothy Jaeryang Baek 5526b66165 chore: qdrant-client bump 2025-07-04 23:56:02 +04:00
Tim Jaeryang Baek a3add18fa9 Merge pull request #15289 from Anush008/main
refactor: Updated Qdrant multi-tenancy implementation
2025-07-04 23:49:11 +04:00
Timothy Jaeryang Baek 198b89faa5 refac: suggestion prompts 2025-07-04 23:40:31 +04:00
Timothy Jaeryang Baek db0d2ae6d4 refac: Improve banner handling and command visibility in chat UI 2025-07-04 20:55:31 +04:00
Timothy Jaeryang Baek c9e1f454fc refac 2025-07-04 20:30:34 +04:00
Tim Jaeryang Baek f58cb09d27 Merge pull request #15480 from itk-dev/feature/message-input-visible-outlines
Feat: message input visible outlines
2025-07-04 20:27:57 +04:00
Timothy Jaeryang Baek 2e2a63c201 fix: various rich text input issues
#15140
2025-07-04 20:26:01 +04:00
Gianpaolo Macario bdd5120407 fix: rename Ollama API parameter "name" to "model" 2025-07-04 17:33:57 +02:00
Timothy Jaeryang Baek 9b5da77ffc refac 2025-07-04 15:29:35 +04:00
Sine Jespersen 86044feaca button to div: avoid nested button, code intepreter button outline 2025-07-04 11:18:30 +02:00
Anush008 17debaa6de chore: Raise if QDRANT_URI is not set
Signed-off-by: Anush008 <anushshetty90@gmail.com>
2025-07-04 13:17:46 +05:30
Anush008 c8a49d373a refactor: Removed more swallows
Signed-off-by: Anush008 <anushshetty90@gmail.com>
2025-07-04 12:38:22 +05:30
Anush008 0ac57a088f refactor: More implementation improvements
Signed-off-by: Anush008 <anushshetty90@gmail.com>
2025-07-04 12:33:54 +05:30
Anush008 7c734d3fea Merge remote-tracking branch 'origin/dev' into Anush008/main
Signed-off-by: Anush008 <anushshetty90@gmail.com>
2025-07-04 12:22:08 +05:30
Tim Jaeryang Baek 600344f2e8 Merge pull request #15510 from kopero2000/bug/oauth_logout_fix
fix/oauth logout fix
2025-07-04 10:30:02 +04:00
Timothy Jaeryang Baek 1b237c7f60 enh: submit searchParam 2025-07-04 02:53:41 +04:00
Timothy Jaeryang Baek e28bc37e41 enh: AUDIT_UVICORN_LOGGER_NAMES 2025-07-03 21:54:25 +04:00
Timothy Jaeryang Baek 75096c24ac refac 2025-07-03 21:28:09 +04:00
Timothy Jaeryang Baek d2485bbf09 refac 2025-07-03 18:13:23 +04:00
8661c4416a feat: configurable db url
Co-Authored-By: Ryan Rodriguez <62253265+ryanmrodriguez@users.noreply.github.com>
Co-Authored-By: Mario Trangoni <mario@mariotrangoni.de>
2025-07-03 17:47:12 +04:00
Tim Jaeryang Baek e98dc0a745 Merge pull request #14957 from Classic298/fix-postgresql-null-value-search
Fix: postgresql null value search
2025-07-03 17:36:27 +04:00
Tim Jaeryang Baek ed6bbddf6e Merge pull request #15483 from headwAI-GmbH/support-deactivate-update-check
feat: Support deactivate update check without OFFLINE_MODE
2025-07-03 17:22:22 +04:00
Tim Jaeryang Baek bdbc8f048c Merge pull request #15501 from Green-PT/fix-oauth-signout-redirect
fix: sign out redirect for oauth
2025-07-03 17:18:25 +04:00
Tim Jaeryang Baek 74167f4b48 Merge pull request #15490 from sihyeonn/chore/sh-print
refactor: improve logging in task management system
2025-07-03 17:14:35 +04:00
Tim Jaeryang Baek 195e0221b8 Merge pull request #15493 from aleixdorca/dev
i18n: Update Catalan translation.json
2025-07-03 17:12:47 +04:00
Tim Jaeryang Baek 7b0fc0ad1c Merge pull request #15492 from qingchunnh/Update_zh-CN-2573
i18n: Update zh-CN
2025-07-03 16:13:17 +04:00
Tim Jaeryang Baek c093b88abe Merge pull request #15500 from aindriu80/update-ga-strings-june
i18n (ga): Update Irish translations
2025-07-03 16:12:59 +04:00
Aindriú Mac Giolla Eoin 34ec073b3d Merge branch 'dev' into update-ga-strings-june 2025-07-03 13:07:38 +01:00
Tim etc. bb0176085d fix signout redirect for oauth 2025-07-03 13:59:48 +02:00
Aindriú Mac Giolla Eoin 54e5e4bb4b i18n(ga): Update Irish translations 2025-07-03 12:40:58 +01:00
Aleix Dorca 4c7436a05c Update Catalan translation.json 2025-07-03 06:44:41 +02:00
qingchun ee00bdef63 i18n: Fix zh-CN 2025-07-03 11:52:45 +08:00
qingchun e5415b8a87 i18n: Fix zh-CN 2025-07-03 11:50:58 +08:00
qingchun 1f4e51edc6 i18n: Update zh-CN 2025-07-03 11:46:17 +08:00
Sihyeon Jang a9804e2566 refactor: replace print statements with appropriate logging levels
Signed-off-by: Sihyeon Jang <uneedsihyeon@gmail.com>
2025-07-03 08:34:25 +09:00
Timothy Jaeryang Baek e1efd54a70 fix/refac: heic image handling 2025-07-03 00:04:13 +04:00
Vizi Béla Ákos d1ee4e0925 Merge branch 'open-webui:main' into bug/oauth_logout_fix 2025-07-02 18:01:08 +02:00
Bela Vizi 9623ef4360 add trust env to clientsession 2025-07-02 17:59:56 +02:00
Timothy Jaeryang Baek bb506edcc3 refac 2025-07-02 17:50:07 +04:00
Timothy Jaeryang Baek f55e93cabe feat: additional valve input types 2025-07-02 17:45:42 +04:00
guenhter 661ea773f4 fix: avoid 'checking for updates...' always present in offline-mode 2025-07-02 15:20:43 +02:00
guenhter 34ddd62951 feat: support suppressing version checks 2025-07-02 15:20:43 +02:00
Timothy Jaeryang Baekandsilentoplayz 7adb15d4b0 refac: user pending screen
Co-Authored-By: silentoplayz <50341825+silentoplayz@users.noreply.github.com>
2025-07-02 16:11:13 +04:00
Timothy Jaeryang Baek 21f9982c5a chore: chart.js 2025-07-02 15:58:16 +04:00
Timothy Jaeryang Baek ff3dec9ef2 refac: styling 2025-07-02 15:14:48 +04:00
Timothy Jaeryang Baek 05773bc831 refac: styling 2025-07-02 14:58:28 +04:00
Tim Jaeryang Baek 6ff84d8ec0 Merge pull request #15479 from itk-dev/feature/suggestions-role-list
FEAT: give suggestions role list and items role listitem
2025-07-02 14:50:30 +04:00
Sine Jespersen 98317f4576 give suggestions role list and items role listitem 2025-07-02 12:45:15 +02:00
Timothy Jaeryang Baek 139e4f93cb refac 2025-07-02 14:30:05 +04:00
Tim Jaeryang Baek a3c9b46e9a Merge pull request #15477 from itk-dev/feature/accessibility-user-settings
Feat: accessibility user settings dropdown menu
2025-07-02 14:22:37 +04:00
Timothy Jaeryang Baekandconql 2c6227e4b6 fix: preserve dates for chat imports
Co-Authored-By: conql <49243542+conql@users.noreply.github.com>
2025-07-02 14:21:36 +04:00
Timothy Jaeryang Baek 3f800c069e refac: banner newlines 2025-07-02 14:06:06 +04:00
Timothy Jaeryang Baek 9b718e7817 refac: styling 2025-07-02 13:59:14 +04:00
Timothy Jaeryang Baek 7e273ebe86 refac: banners 2025-07-02 13:48:18 +04:00
Sine Jespersen 225d20fb84 aria hidden on decorative svgs to enhance accessibility 2025-07-02 11:29:03 +02:00
Sine Jespersen b7255854d2 change interactive elements in dropdownmenu to dropdownitems 2025-07-02 11:14:12 +02:00
Tim Jaeryang Baek bab7a65c2c Merge pull request #15452 from expruc/chore/remove_embedder_openai_api_key_requirement
chore: removed requirement for providing api key in openai embedder
2025-07-02 12:55:00 +04:00
Timothy Jaeryang Baek 1cc15e6470 refac: splash screen 2025-07-02 12:51:03 +04:00
Sine Jespersen 7e49c8733b remove nested button and add label for assistive technology 2025-07-02 10:12:48 +02:00
Tim Jaeryang Baek 40fc1a1f95 Merge pull request #15466 from itk-dev/feature/accessibility-dropdown-model-selector
Feat: accessibility in dropdown model selector
2025-07-02 12:10:29 +04:00
Sine Jespersen d7f247d7d2 make margin depend on high contrast mode, based on comment on pr 2025-07-02 09:55:47 +02:00
Timothy Jaeryang Baek 57972605f2 refac 2025-07-02 11:48:01 +04:00
Timothy Jaeryang Baek d54896ca66 chore: bump pydantic 2025-07-02 11:41:58 +04:00
Sine Jespersen 6a644cd03a aria hidden on svg, as this is decorative 2025-07-02 08:57:15 +02:00
Sine Jespersen 64b583dc29 add margin to make outline visible 2025-07-02 08:56:56 +02:00
Sine Jespersen bed2c7e34f remove nested buttons, as this is not valid html 2025-07-02 08:56:17 +02:00
Sine Jespersen e5435c6480 high contrast mode on model selector to enhance accessibility 2025-07-02 08:46:32 +02:00
expruc 555c449f49 removed requirement for providing api key in openai embedder 2025-07-01 22:03:50 +03:00
Tim Jaeryang Baek a85ecadfeb Merge pull request #15447 from itk-dev/feature/accessibility-file-item
Feat: accessibility of file input in messageinput
2025-07-01 15:57:39 +04:00
Timothy Jaeryang Baek 355a3eaedb enh: dynamic commands height 2025-07-01 15:57:01 +04:00
Timothy Jaeryang Baek 8d49288499 refac: skeleton 2025-07-01 15:32:32 +04:00
Tim Jaeryang Baek 6d0bd226c1 Merge pull request #15445 from itk-dev/feature/chat-page-accessibility
Feat: chat page accessibility
2025-07-01 15:23:06 +04:00
Sine Jespersen 3d4752480e aria label on remove file button to enchance accessibility 2025-07-01 13:21:40 +02:00
Tim Jaeryang Baek af9d264405 Merge pull request #15402 from itk-dev/feature/aria-hide-button-and-img
Feat: aria hide button and img
2025-07-01 15:18:21 +04:00
Sine Jespersen 1dd475cccf aria hidden on svg 2025-07-01 13:16:18 +02:00
Sine Jespersen 09ddf69a73 high contrast mode on file item to enhance accessibility 2025-07-01 13:15:38 +02:00
Sine Jespersen 67ac39b72b aria hidden on some svgs for accessibility 2025-07-01 12:40:03 +02:00
Sine Jespersen ab3c19dd36 aria labels and high contrast mode for accessibility 2025-07-01 12:39:35 +02:00
Tim Jaeryang Baek 7a25c9637a Merge pull request #15443 from open-webui/main
dev
2025-07-01 14:08:13 +04:00
Timothy Jaeryang Baek 59ba21bdf8 refac 2025-07-01 14:06:45 +04:00
Sine Jespersen 988fc3a72c aria-hidden and aria-label to enhance accessibility 2025-07-01 11:31:07 +02:00
Tim Jaeryang Baek 8b43641849 Merge pull request #15427 from open-webui/dependabot/npm_and_yarn/dev/html2canvas-pro-1.5.11
build(deps): bump html2canvas-pro from 1.5.8 to 1.5.11
2025-07-01 12:49:09 +04:00
Tim Jaeryang Baek b8c8c64720 Merge pull request #15419 from open-webui/dependabot/pip/backend/dev/uvicorn-standard--0.35.0
build(deps): bump uvicorn[standard] from 0.34.2 to 0.35.0 in /backend
2025-07-01 12:48:50 +04:00
Tim Jaeryang Baek 557be6c824 Merge pull request #15418 from open-webui/dependabot/pip/backend/dev/azure-identity-1.23.0
build(deps): bump azure-identity from 1.21.0 to 1.23.0 in /backend
2025-07-01 12:48:09 +04:00
Tim Jaeryang Baek c38177bcd5 Merge pull request #15438 from open-webui/dependabot/npm_and_yarn/dev/sortablejs-1.15.6
build(deps): bump sortablejs from 1.15.2 to 1.15.6
2025-07-01 12:44:58 +04:00
Sine Jespersen 00e2ad1a9f Aria labels on image 2025-07-01 10:10:37 +02:00
dependabot[bot] fffe0b8580 build(deps): bump sortablejs from 1.15.2 to 1.15.6
Bumps [sortablejs](https://github.com/SortableJS/Sortable) from 1.15.2 to 1.15.6.
- [Release notes](https://github.com/SortableJS/Sortable/releases)
- [Commits](https://github.com/SortableJS/Sortable/compare/1.15.2...1.15.6)

---
updated-dependencies:
- dependency-name: sortablejs
  dependency-version: 1.15.6
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-01 06:59:56 +00:00
Tim Jaeryang Baek 030e9ab1ae Merge pull request #15417 from jackthgu/websearch-searxng-url-required
fix: settings/web-search : make “Searxng Query URL” field required
2025-07-01 10:56:55 +04:00
dependabot[bot] b685fd4a33 build(deps): bump html2canvas-pro from 1.5.8 to 1.5.11
Bumps [html2canvas-pro](https://github.com/yorickshan/html2canvas-pro) from 1.5.8 to 1.5.11.
- [Release notes](https://github.com/yorickshan/html2canvas-pro/releases)
- [Changelog](https://github.com/yorickshan/html2canvas-pro/blob/main/CHANGELOG.md)
- [Commits](https://github.com/yorickshan/html2canvas-pro/compare/v1.5.8...v1.5.11)

---
updated-dependencies:
- dependency-name: html2canvas-pro
  dependency-version: 1.5.11
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-01 05:55:54 +00:00
dependabot[bot] 876bb4bdc6 build(deps): bump uvicorn[standard] from 0.34.2 to 0.35.0 in /backend
Bumps [uvicorn[standard]](https://github.com/encode/uvicorn) from 0.34.2 to 0.35.0.
- [Release notes](https://github.com/encode/uvicorn/releases)
- [Changelog](https://github.com/encode/uvicorn/blob/master/docs/release-notes.md)
- [Commits](https://github.com/encode/uvicorn/compare/0.34.2...0.35.0)

---
updated-dependencies:
- dependency-name: uvicorn[standard]
  dependency-version: 0.35.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-01 05:21:20 +00:00
dependabot[bot] 534c12b1b9 build(deps): bump azure-identity from 1.21.0 to 1.23.0 in /backend
Bumps [azure-identity](https://github.com/Azure/azure-sdk-for-python) from 1.21.0 to 1.23.0.
- [Release notes](https://github.com/Azure/azure-sdk-for-python/releases)
- [Changelog](https://github.com/Azure/azure-sdk-for-python/blob/main/doc/esrp_release.md)
- [Commits](https://github.com/Azure/azure-sdk-for-python/compare/azure-identity_1.21.0...azure-identity_1.23.0)

---
updated-dependencies:
- dependency-name: azure-identity
  dependency-version: 1.23.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-01 05:21:17 +00:00
Taehong Gu 15821af252 fix(admin/web-search): add required validation to “Searxng Query URL” field 2025-07-01 14:08:01 +09:00
Tim Jaeryang Baek a463ef3c03 Merge pull request #15407 from silentoplayz/bannerlengthincrease
refac: Increase banners height limit
2025-06-30 21:41:12 +04:00
Silentoplayz 9813019489 Update Banner.svelte 2025-06-30 12:22:39 -04:00
Timothy Jaeryang Baek de018f0912 refac 2025-06-30 18:04:11 +04:00
Timothy Jaeryang Baek 8b35ea6eea enh: OTEL_OTLP_SPAN_EXPORTER 2025-06-30 15:52:32 +04:00
Timothy Jaeryang Baek bab1453b4c refac: styling 2025-06-30 15:39:43 +04:00
Timothy Jaeryang Baek a14cfb6448 enh: more info in feedback modal 2025-06-30 15:38:25 +04:00
Timothy Jaeryang Baek 71d550dd26 enh: offline mode 2025-06-30 15:20:41 +04:00
Timothy Jaeryang Baek 2748fc1e4d refac 2025-06-30 13:29:47 +04:00
Timothy Jaeryang Baek ce311598cb refac: styling 2025-06-30 13:29:23 +04:00
Timothy Jaeryang Baek 8a334decf6 refac: ENABLE_MODEL_LIST_CACHE -> ENABLE_BASE_MODELS_CACHE 2025-06-30 13:27:07 +04:00
Timothy Jaeryang Baek eac2f36f4f refac: temporary enforced behaviour 2025-06-30 13:16:46 +04:00
Tim Jaeryang Baek c75d0bd20b Merge pull request #15389 from headwAI-GmbH/add-qdrant-indices
feat: Add qdrant indices
2025-06-30 11:36:31 +04:00
Tim Jaeryang Baek 74912a5383 Merge pull request #15380 from taylorwilsdon/issues/15074/REDIS_KEY_PREFIX
feat: Support configuration of redis key prefix via environment variable
2025-06-30 10:43:35 +04:00
guenhter 5c2e0e4beb feat: add qdrant indices for metadata fields
All fieldnames which are part of a query should
have an index for performance reasons. This is
even enforced on some qdrant cluster like those
on qdrant.io, and queries using a unindexed column
fail with an error.
2025-06-29 15:30:55 +02:00
Taylor Wilsdon 7b5df819b1 fmt 2025-06-28 10:18:30 -07:00
Taylor Wilsdon 345922a970 add REDIS_KEY_PREFIX env var and default to current hardcoded value 2025-06-28 10:11:26 -07:00
Timothy Jaeryang Baek 7523613fbc refac 2025-06-28 19:29:28 +04:00
Timothy Jaeryang Baek 962e078a9a refac 2025-06-28 19:15:52 +04:00
Timothy Jaeryang Baek 1b064a6c85 chore: format 2025-06-28 15:21:20 +04:00
Timothy Jaeryang Baek e06cdea0b5 refac 2025-06-28 15:20:38 +04:00
Timothy Jaeryang Baek 8f9e202a88 refac 2025-06-28 15:13:21 +04:00
Timothy Jaeryang Baek 1a52585769 enh: ENABLE_MODEL_LIST_CACHE 2025-06-28 15:12:31 +04:00
Timothy Jaeryang Baek 2b88f66762 refac: MODEL_LIST_CACHE_TTL -> MODELS_CACHE_TTL 2025-06-28 14:44:47 +04:00
Timothy Jaeryang Baek 53a08eb00d refac: styling 2025-06-28 14:36:26 +04:00
Timothy Jaeryang Baek ffd7b004f4 fix: admin chat share issue 2025-06-28 14:02:07 +04:00
Timothy Jaeryang Baek f123d70cff fix: enforced temp chat issue 2025-06-28 14:01:58 +04:00
Timothy Jaeryang Baek 372836c87c chore: langchain dep bump 2025-06-28 00:53:07 +04:00
Timothy Jaeryang Baek ff639a20d0 refac 2025-06-28 00:48:24 +04:00
Tim Jaeryang Baek bfd92ec4af Merge pull request #15366 from akiraro/feat/oidc-httpx-timeout
feat: Custom oauth timeout
2025-06-28 00:46:21 +04:00
Haziq Yusof 88ea0f523f feat: custom oauth timeout 2025-06-28 03:08:46 +08:00
Timothy Jaeryang Baek dd1e4ee983 refac 2025-06-27 18:29:37 +04:00
Timothy Jaeryang Baek c91b0cd8c4 refac: vite config 2025-06-27 16:42:10 +04:00
Timothy Jaeryang Baek 3ff864eb63 refac: select action model behaviour 2025-06-27 16:29:04 +04:00
Timothy Jaeryang Baek 90257442e3 refac: styling 2025-06-27 16:15:16 +04:00
Timothy Jaeryang Baek 0447790e6a feat: MODEL_LIST_CACHE_TTL 2025-06-27 15:59:30 +04:00
Timothy Jaeryang Baek 09f0c6beee fix: windows start script 2025-06-27 15:46:38 +04:00
Timothy Jaeryang Baek c8e8111dd9 refac 2025-06-27 15:44:26 +04:00
Timothy Jaeryang Baek 38dfa6e6db refac 2025-06-27 15:30:00 +04:00
Tim Jaeryang Baek 276bfa02d0 Merge pull request #15314 from silentoplayz/spinner-and-xmark-icon-components
refac: Spinner and XMark components
2025-06-27 15:23:29 +04:00
Tim Jaeryang Baek 79aa73347e Merge pull request #15318 from hula-la/fix/ko-translation
i18n: Fix Korean translation errors in UI messages
2025-06-27 15:22:33 +04:00
Timothy Jaeryang Baek 2ae9bc30bd refac: feedback item click issue 2025-06-27 15:21:46 +04:00
Timothy Jaeryang Baek aa13a7caf9 refac 2025-06-27 14:58:23 +04:00
Tim Jaeryang Baek 8c5beddb5f Merge pull request #15310 from silentoplayz/icons
chore: Remove `MagnifyingGlass` icon component and update to use Search icon component
2025-06-27 14:58:06 +04:00
Tim Jaeryang Baek c303232657 Merge pull request #15344 from silentoplayz/small-fixes
fix: A couple small fixes
2025-06-27 14:31:01 +04:00
Tim Jaeryang Baek 8b49d6dda0 Merge pull request #15325 from headwAI-GmbH/make-qdrant-collection-prefix-configurable
feat: support better qdrant collection isolation
2025-06-27 14:30:26 +04:00
Silentoplayz e3bacc3296 fix: Small fixes 2025-06-27 04:35:28 -04:00
Tim Jaeryang Baek 767c4426cd Merge pull request #15328 from acwoo97/feat/lock
feat : Retry acquiring usage cleanup lock to handle potential stale locks
2025-06-27 11:52:13 +04:00
안찬우[AI Product] 789e6a0db3 feat : Retry acquiring usage cleanup lock to handle potential stale locks 2025-06-26 22:24:57 +09:00
guenhter a66206f44f feat: support better qdrant collection isolation
The prefix string for qdrant collection is now
configurable,  which means the same qdrant cluster
can be used to host more open webui instances and
to be able to separate the collections between the
different owui instances.
2025-06-26 13:52:26 +02:00
soo-jin-kim 2f4604921f fix: fix korean translation 2025-06-26 17:18:07 +09:00
Timothy Jaeryang Baek 21d616f8ed fix: code interpreter code editor issue 2025-06-26 11:05:17 +04:00
Silentoplayz f429dd667f chore: remove comment 2025-06-25 18:56:08 -04:00
Silentoplayz d1e3940abd refac: Spinner and XMark components 2025-06-25 18:44:45 -04:00
Silentoplayz cf24e39404 chore: add strokeWidth back 2025-06-25 17:36:23 -04:00
Silentoplayz dd0e6bf58f remove class="w-5 h-5" /> 2025-06-25 14:04:40 -04:00
Silentoplayz 88c4f55a51 chore: update 2025-06-25 14:00:48 -04:00
Silentoplayz 3f865f0b66 chore: MagnifyingGlass -> Search 2025-06-25 13:42:18 -04:00
Tim Jaeryang Baek b6e2b2e514 Merge pull request #15236 from headwAI-GmbH/password-login-type
fix: Use correct password autocomplete type on signup
2025-06-25 13:39:08 +04:00
Timothy Jaeryang Baek 8e95e5267c refac 2025-06-25 13:36:41 +04:00
Tim Jaeryang Baek d4fcf561e5 Merge pull request #15293 from itk-dev/feature/make-toggle-button-accessible
feat: aria-pressed and aria-label
2025-06-25 13:36:02 +04:00
Sine Jespersen 0ba84a670d add aria-pressed and aria-label to toggle button to make it accessible 2025-06-25 10:34:40 +02:00
Timothy Jaeryang Baek 1f123eb100 refac 2025-06-25 12:20:08 +04:00
Tim Jaeryang Baek 6676824947 Merge pull request #15238 from manelcomiche/main
feat(i18n): update Spanish and Catalan translations
2025-06-25 11:05:31 +04:00
Tim Jaeryang Baek 2bad7eaa07 Merge pull request #15277 from hankewyczz/bug/restore-exa-search
fix Restore exa
2025-06-25 11:04:48 +04:00
Tim Jaeryang Baek d60c800d66 Merge pull request #15276 from zhangtyzzz/update_brave_search
[fix] Update brave.py to use the correct field
2025-06-25 11:04:06 +04:00
Anush008 05bee5663d Merge remote-tracking branch 'origin/dev' 2025-06-25 12:04:23 +05:30
Tim Jaeryang Baek baa5920b7a Merge pull request #15250 from dlamoris/dev
fix: opensearch race condition, use keyword search instead of full text search for filter query
2025-06-25 10:30:36 +04:00
Tim Jaeryang Baek 889a3e1c48 Merge pull request #15262 from bobend/updated-da-DK
i18n: Updated da-DK translation
2025-06-25 10:30:18 +04:00
Tim Jaeryang Baek 565ec1567b Merge pull request #15268 from jackthgu/added-docker-compose-otel
build: add docker-compose.otel.yaml for OpenTelemetry
2025-06-25 10:26:46 +04:00
Tim Jaeryang Baek 7f681e8c02 Merge pull request #15273 from c0mplex10/translate
i18n: Improve Turkish translations
2025-06-25 10:26:15 +04:00
Zachar Hankewycz 45d7726ee0 Restore exa 2025-06-24 21:24:53 -04:00
zhangtyzzz ac5567f78d Update brave.py to use the correct field
fixing issues caused by incorrect field names.
2025-06-25 09:11:58 +08:00
c0mplex10 1875d7d726 i18n: Improve Turkish translations 2025-06-25 01:40:23 +03:00
Taehong Gu c77c4ff3f7 build: add docker-compose.otel.yaml for OpenTelemetry 2025-06-25 00:11:06 +09:00
Bo Bendtsen 2a25d625b2 Merge remote-tracking branch 'upstream/dev' into updated-da-DK 2025-06-24 12:54:17 +02:00
Bo Bendtsen 559c25fa3c Updated da-DK translation 2025-06-24 12:45:48 +02:00
Anush008 9dd24ee8fa chore: Bump qdrant-client version
Signed-off-by: Anush008 <anushshetty90@gmail.com>
2025-06-24 14:15:07 +05:30
Anush008 5dba298c1e refactor: Updated Qdrant multi-tenancy implementation
Signed-off-by: Anush008 <anushshetty90@gmail.com>
2025-06-24 14:12:44 +05:30
Tim Jaeryang Baek 0b1db0b216 Merge pull request #15242 from c0mplex10/patch-2
i18n: Improve Turkish translations
2025-06-24 11:34:59 +04:00
Timothy Jaeryang Baek 947b77db24 refac 2025-06-24 10:36:33 +04:00
Doris Lam 74ae9ab897 fix opensearch race condition, use keyword search instead of full text search in filter query 2025-06-23 18:43:33 -07:00
c0mplex10 c9c5340762 Update translation.json 2025-06-23 17:46:12 +03:00
denispol 77a96870ea Merge branch 'open-webui:dev' into dev 2025-06-23 15:42:32 +02:00
LHCLYT e237600e51 feat(i18n): update Catalan translations
Updated several Catalan translation strings to improve grammar, consistency, and clarity.

Includes fixes for technical terms and overall localization quality.
2025-06-23 12:53:49 +02:00
LHCLYT 708010a16a feat(i18n): update Spanish translations
Updated several Spanish translation strings to improve grammar, consistency, and clarity.

Includes fixes for technical terms and overall localization quality.
2025-06-23 12:43:14 +02:00
guenhter f0e60c28df fix: use correct password autocomplete for signup
Password manager act based on the 'autocomplet' attribute
of the password fields. If the attribut is set to
"current-password" they try to fill the password with an
existing one. If it is set to "new-password" they try to
support the user by generating a new password.
For signup in owui, the password was always set to
"current-password", so the password manager never proposed
a password on signup.
2025-06-23 11:48:51 +02:00
Tim Jaeryang Baek 610680ac14 Merge pull request #15213 from beecho01/patch-1
i18n: en-GB English translation updates
2025-06-23 13:12:44 +04:00
Timothy Jaeryang Baek f4b92868c4 refac 2025-06-23 12:54:50 +04:00
Tim Jaeryang Baek 9b2c3dec3a Merge pull request #15220 from prilosac/allow_auto_gpt_image_1
fix: Allowing 'auto' for IMAGE_SIZE on gpt-image-1
2025-06-23 10:50:44 +04:00
Tim Jaeryang Baek 6c7a7bc891 Merge pull request #15219 from qingchunnh/Update_zh-CN-25623
i18n: Update & Improve zh-CN
2025-06-23 10:49:41 +04:00
qingchun 018000c41b i18n: Improve zh-CN 2025-06-23 04:56:35 +08:00
Lucas Teixeira 95610080fb allowing auto on gpt-image-1 2025-06-22 13:55:29 -07:00
qingchun a5cdd25802 i18n: Update & Improve zh-CN 2025-06-23 04:20:47 +08:00
James Beeching 2df1f83e79 Merge branch 'dev' into patch-1 2025-06-22 14:17:24 +01:00
James Beeching 13cd025a7d Update missed spelling to en-GB equivalent 2025-06-22 14:03:36 +01:00
James Beeching 986e583594 Update translation.json for en-GB 2025-06-22 13:52:16 +01:00
Tim Jaeryang Baek 0563738940 Merge pull request #15208 from yukebrillianth/main
i18n: Fix translation placeholder for SSO provider in Bahasa Indonesia
2025-06-22 15:32:32 +04:00
Tim Jaeryang Baek 21d0431178 Merge pull request #15211 from Keram-Yasin/main
i18n: translation edit for ug-CN
2025-06-22 15:32:16 +04:00
Keram Yasin bb1f8f8ae9 Uyghur Translations Editted
1. The Uyghur translations are edited based on live instance.
2025-06-22 12:25:19 +02:00
Yuke Brilliant Hestiavin b1f0ad79a8 fix: SSO provider translation in Bahasa Indonesia 2025-06-22 07:00:44 +07:00
Timothy Jaeryang Baek aef0ad2d10 refac 2025-06-21 19:12:43 +04:00
Timothy Jaeryang Baek 38e8209d71 enh: ENABLE_COMPRESSION_MIDDLEWARE env var 2025-06-21 17:58:56 +04:00
Tim Jaeryang Baek 2eae63bcd4 Merge pull request #15195 from prene/proper-default-pooling-settings
[PERF] Proper default pooling settings
2025-06-21 17:39:27 +04:00
René Pfitzner 63d99abf41 Change default db pooling 2025-06-21 14:01:57 +01:00
Tim Jaeryang Baek 7513dc7e34 Merge pull request #15146 from rgaricano/dev
i18n: en-US correction of num_keep parameter description
2025-06-21 15:11:26 +04:00
Timothy Jaeryang Baek 2658ab1e46 chore: format 2025-06-21 15:07:52 +04:00
Timothy Jaeryang Baek df060df88b refac: styling 2025-06-20 21:10:56 +04:00
Timothy Jaeryang Baek e035d5d767 refac: styling 2025-06-20 20:57:13 +04:00
Timothy Jaeryang Baek 493595c891 refac: styling 2025-06-20 20:51:40 +04:00
Timothy Jaeryang Baek cd70469a8a chore: format 2025-06-20 20:32:30 +04:00
Timothy Jaeryang Baek 17b9a81504 chore: format 2025-06-20 20:32:23 +04:00
Tim Jaeryang Baek e3e2bf841e Merge pull request #15170 from Keram-Yasin/main
i18n: add translation for ug-CN
2025-06-20 20:28:53 +04:00
Tim Jaeryang Baek 2f94b3947e Merge pull request #15167 from ascodeasice/dayjs-zh-tw-locale
i18n: Add zh-tw locale support for dayjs
2025-06-20 20:28:16 +04:00
ascodeasice 9f33abf384 feat(dayjs): add zh-tw locale support 2025-06-20 16:24:04 +08:00
Tim Jaeryang Baek a21519f8f2 Merge pull request #15034 from Classic298/system-prompt-high-contrast
feat: System prompt input fields high contrast mode
2025-06-20 10:07:41 +04:00
Timothy Jaeryang Baek e1f5f1e0b1 refac 2025-06-20 09:58:44 +04:00
Tim Jaeryang Baek 4e50dd4df6 Merge pull request #15122 from tcx4c70/feat/add_stream_options_to_azure
feat(azure): Add stream_options to payload if api_version supports
2025-06-20 09:57:27 +04:00
Tim Jaeryang Baek 13c16b974c Merge pull request #15154 from Classic298/patch-1
i18n: de-DE German Translation Updates
2025-06-20 09:55:37 +04:00
Tim Jaeryang Baek b5445b5db2 Merge pull request #15148 from jackthgu/none-dbconnect-warning-message
fix: Enhance database migration error logging
2025-06-20 09:55:02 +04:00
Taehong Gu c7ef6025af Provide more detailed information to the user upon database connection failure
modified:   backend/open_webui/internal/db.py
2025-06-20 14:13:43 +09:00
Classic298 f0dea9a7c4 Update translation.json 2025-06-19 23:27:14 +02:00
Keram Yasin f7c7d72857 Uyghur Translation Review 1. The translations are reviewed and corrected for three times. 2025-06-19 22:19:17 +02:00
Timothy Jaeryang Baek 81b8267e85 feat: odt file parse support 2025-06-19 18:39:00 +04:00
_00_ f939646364 i18n: en-US correction of num_keep parameter description
i18n: en-US correction of num_keep parameter description

Correction of num_keep parameter,
In actual description example is indicated that "last" x tokens will be retained, that is wrong, it have to say "first" x tokens.

Tokens to Keep on Context Refresh (num_keep): Retains part of the previous conversation. It's used when the n_ctx limit is reached. A new prompt will be constructed with the first n_keep characters of the original prompt plus the second half of the output to free up space for more conversation. Example: Keeping 50 first tokens helps the model remember the main topic when refreshing.

https://github.com/open-webui/open-webui/discussions/3794#discussioncomment-12691428
2025-06-19 14:18:15 +02:00
Tim Jaeryang Baek 4f8f349c8f Merge pull request #15142 from Classic298/fix-oauth
fix: OAuth authentication failure on second login due to stale session state
2025-06-19 15:56:26 +04:00
Classic298 bbdb098b87 Update auths.py 2025-06-19 13:44:03 +02:00
Keram 646b3ca7f6 Uyghur Translation Uploaded
1. All the file is translated to Uyghur for the first time.
2. The next step is to review all the translations.
2025-06-19 12:30:42 +02:00
Timothy Jaeryang Baek a196b9dc26 fix: direct tool servers not displaying 2025-06-19 14:03:36 +04:00
Tim Jaeryang Baek b08fc297cf Merge pull request #15138 from zhangtyzzz/exa_search_missing
fix: Resolve exa search not being processed correctly
2025-06-19 10:34:51 +04:00
zhangtyzzz 5f60b30320 add missed exa 2025-06-19 13:52:58 +08:00
Keram Yasin 088e54c4fa Uyghur Localization Files Added
1. Uyghur localization file is added.
2. Uyghur language code is added to language list.
2025-06-18 22:11:45 +02:00
Adam Tao baafdb752c feat(openai): Add stream_options to payload if api_version supports
Signed-off-by: Adam Tao <tcx4c70@gmail.com>
2025-06-18 21:17:09 +08:00
Tim Jaeryang Baek d6f13bee5e Merge pull request #15094 from silentoplayz/small-fix
fix: sanatizeResponseContent for stop token display
2025-06-18 16:57:27 +04:00
denispol 6555cf9d7a fix: enhance paste handling for mobile WebViews and large text 2025-06-18 14:50:16 +02:00
Timothy Jaeryang Baek 1547235d47 refac: chat input object localStorage -> sessionStorage 2025-06-18 16:02:29 +04:00
Classic298 b9ea6130c7 Update General.svelte 2025-06-18 13:10:02 +02:00
Classic298 3814bbf7b3 Update General.svelte 2025-06-18 13:09:21 +02:00
Tim Jaeryang Baek 34440f95dc Merge pull request #15093 from silentoplayz/fix-sidebar-drag-and-drop
fix: refine folder component's drag-&-drop error handling
2025-06-18 14:04:52 +04:00
Tim Jaeryang Baek 1abc3847ab Merge pull request #15108 from itk-dev/feature/telemetry-auth
feat: Added support for basic auth wiht OTEL exporter
2025-06-18 14:04:15 +04:00
Timothy Jaeryang Baek 6186bbf337 refac/fix: stt supported type 2025-06-18 14:01:14 +04:00
Jesper Kristensen 4119ab261e Added support for basic auth wiht OTEL exporter 2025-06-18 11:42:33 +02:00
Tim Jaeryang Baek e79f31b9c4 Merge pull request #15029 from Classic298/correct-markdown-rendering-model-descriptions
fix: Correct markdown rendering model descriptions
2025-06-18 12:50:31 +04:00
Tim Jaeryang Baek 1d179399f3 Merge pull request #15069 from d2a-pnagel/codex/repareer-fouten-in-nl-vertaalbestand
i18n: Fix errors in Dutch translations
2025-06-18 12:49:59 +04:00
Tim Jaeryang Baek 294d6ce060 Merge pull request #15101 from itk-dev/feature/high-contrast-mode-tools-section
FEAT: high contrast mode tools section
2025-06-18 12:34:24 +04:00
Sine Jespersen 3ff5db229b accessibility: labels and placeholder contrast 2025-06-18 09:50:04 +02:00
Sine Jespersen 27f7df8ad6 accessibility: placeholder contrast on sensitiveinput 2025-06-18 09:50:04 +02:00
Sine Jespersen bd818b4524 outline on switch button 2025-06-18 09:50:04 +02:00
Sine Jespersen 99b153a132 add aria label to button 2025-06-18 09:50:04 +02:00
Sine Jespersen d837e6e579 decorative svgs hidden from screen reader 2025-06-18 09:50:04 +02:00
Sine Jespersen 226a7578c6 labels for inputs 2025-06-18 09:50:04 +02:00
Sine Jespersen f36f943a54 screen readers: label button, and remove decorative svg 2025-06-18 09:50:04 +02:00
Sine Jespersen 3cdefff0d2 change div to h1, as it is the modal header 2025-06-18 09:50:04 +02:00
Tim Jaeryang Baek e05560a49d Merge pull request #15072 from PierreMesure/better-swedish-translation-1
Better swedish translation 1
2025-06-18 10:59:43 +04:00
Tim Jaeryang Baek 05aaefd8a5 Merge pull request #15085 from qingchunnh/Update_zh-CN-25618
i18n: Update & Improve zh-CN
2025-06-18 10:59:09 +04:00
Tim Jaeryang Baek 58b5d2b727 Merge pull request #15097 from silentoplayz/index-update
Refactor: Add missing Type definitions
2025-06-18 10:55:44 +04:00
Timothy Jaeryang Baek 0134b5eca4 fix: action reserved user param 2025-06-18 10:50:49 +04:00
Silentoplayz 3be4fb25cd fix: add ? 2025-06-18 02:01:21 -04:00
Silentoplayz d66285f926 chore: add toolIds type 2025-06-18 01:55:43 -04:00
Silentoplayz 35be40c932 chore: add more Settings types in stores 2025-06-18 01:48:34 -04:00
Silentoplayz 898b65a653 fix: sanatizeResponseContent for stop token display 2025-06-18 00:12:11 -04:00
Silentoplayz b120637127 fix: refine folder component's drag-&-drop error handling 2025-06-17 23:27:45 -04:00
qingchun 70968a6caf i18n: Update & Improve zh-CN 2025-06-18 01:17:41 +08:00
Pierre 8a169d5c8e Update Swedish translation 2025-06-17 19:09:38 +02:00
d2a-pnagel 5f82631ae6 Update translation.json 2025-06-17 14:23:53 +02:00
d2a-pnagel fdeded4a20 Fix obvious typos in Dutch translation 2025-06-17 14:22:46 +02:00
Tim Jaeryang Baek 3b70841a7d Merge pull request #15049 from silentoplayz/fixopeninnewtab
fix: regression for "open in new tab"
2025-06-17 15:27:11 +04:00
Tim Jaeryang Baek 23c679d8db Merge pull request #15065 from aleixdorca/dev
i18n: Update Catalan translation.json
2025-06-17 14:25:53 +04:00
Tim Jaeryang Baek 8a587f9f84 Merge pull request #15066 from bogdanr/dev
i18n: Added a few Romanian translations
2025-06-17 14:25:44 +04:00
Timothy Jaeryang Baek fb26be7dd6 fix: confirm dialog input reset on show 2025-06-17 14:24:23 +04:00
Aleix Dorca 8908e834ac Update Catalan translation.json 2025-06-17 12:07:06 +02:00
Bogdan Radulescu 098091c95e Added a few Romanian translations 2025-06-17 12:57:09 +03:00
Tim Jaeryang Baek e866fba27c Merge pull request #15059 from d2a-raudenaerde/main
i18n Fix a incorrect translation of dutch ("Analyzing" -> from "Analysiseren" to  "Analyseren")
2025-06-17 13:23:54 +04:00
Classic298 872b0ac943 Update General.svelte 2025-06-17 11:23:24 +02:00
Classic298 22b02af703 Update General.svelte 2025-06-17 11:22:34 +02:00
Classic298 7de0e49dc6 Update Controls.svelte 2025-06-17 11:21:24 +02:00
Classic298 c330f06542 Merge branch 'dev' into system-prompt-high-contrast 2025-06-17 11:10:03 +02:00
Rob Audenaerde 6a613c2dd6 Update translation.json 2025-06-17 10:44:23 +02:00
Tim Jaeryang Baek 4254469a33 Merge pull request #15025 from Mte90/patch-1
Italian translation improved
2025-06-17 12:23:28 +04:00
Tim Jaeryang Baek 52cffef16e Merge pull request #15028 from Classic298/system-prompt-resizing
feat: System prompt input field resizable
2025-06-17 12:23:06 +04:00
Tim Jaeryang Baek d50449ea71 Merge pull request #15035 from decent-engineer-decent-datascientist/dev
fix: Resolve UnicodeEncodeError for non-ASCII usernames in forwarded headers
2025-06-17 12:22:02 +04:00
Timothy Jaeryang Baek cd975a1047 Update pyproject.toml 2025-06-17 12:17:02 +04:00
Silentoplayz 354dd3caa9 fix: regression for "open in new tab" 2025-06-16 23:27:16 -04:00
Classic298 c66576805b Merge branch 'open-webui:main' into fix-postgresql-null-value-search 2025-06-16 21:11:30 +02:00
Classic298 beb1f3f7fd Merge branch 'open-webui:main' into system-prompt-resizing 2025-06-16 21:11:21 +02:00
Classic298 c8c4e1642f Merge branch 'open-webui:main' into correct-markdown-rendering-model-descriptions 2025-06-16 21:11:18 +02:00
Classic298 135b57ca8e Update Placeholder.svelte 2025-06-16 20:28:50 +02:00
priten 8a63a601de Fix typo on ollama patch 2025-06-16 12:35:09 -05:00
priten f7920df870 Fix non-ascii error issue on ENABLE_FORWARD_USER_INFO_HEADERS 2025-06-16 12:33:11 -05:00
Classic298 5261d3d31e Update General.svelte 2025-06-16 19:26:57 +02:00
Classic298 088243349d Update Controls.svelte 2025-06-16 19:26:33 +02:00
Classic298 9d37350b97 Update Markdown.svelte 2025-06-16 18:08:20 +02:00
Classic298 4f9ce28a15 Update ChatPlaceholder.svelte 2025-06-16 18:07:30 +02:00
Classic298 9f936a0c19 Update General.svelte 2025-06-16 17:48:45 +02:00
Classic298 85724940ee Update Controls.svelte 2025-06-16 17:48:00 +02:00
Daniele Scasciafratte a7772b4b35 Italian translation improved 2025-06-16 17:09:29 +02:00
Tim Jaeryang Baek b5f4c85bb1 Merge pull request #15014 from open-webui/dev
0.6.15
2025-06-16 18:34:32 +04:00
Timothy Jaeryang Baek 340d9820b8 refac 2025-06-16 18:33:45 +04:00
Timothy Jaeryang Baek 597709c64c doc: changelog 2025-06-16 18:30:51 +04:00
Timothy Jaeryang Baek 84fd41e6ef doc: changelog 2025-06-16 18:29:38 +04:00
Timothy Jaeryang Baek 3917b04ed3 doc: changelog 2025-06-16 18:27:18 +04:00
Timothy Jaeryang Baek b6ea043d18 chore: format 2025-06-16 17:59:07 +04:00
Timothy Jaeryang Baek b082a7edbe enh: allow iframe in content 2025-06-16 17:43:17 +04:00
Timothy Jaeryang Baek 72df23ed79 refac 2025-06-16 17:24:55 +04:00
Timothy Jaeryang Baek 11476eb6ee chore: format 2025-06-16 17:21:22 +04:00
Tim Jaeryang Baek 7096baff1b Merge pull request #15021 from rgaricano/dev
i18n: es-ES Translation v.0.6.15
2025-06-16 17:19:44 +04:00
Tim Jaeryang Baek 2885ed9648 Merge pull request #15019 from itk-dev/feature/high-contrast-mode-tools-section
Feature/high contrast mode tools section
2025-06-16 17:19:30 +04:00
Timothy Jaeryang Baek ab877e1d7e refac 2025-06-16 17:18:43 +04:00
_00_ b13a824047 UPD: es-ES Translation v.0.6.15
UPD: es-ES Translation v.0.6.15
2025-06-16 15:06:33 +02:00
Timothy Jaeryang Baek 9cbe2644b1 refac 2025-06-16 16:59:47 +04:00
Timothy Jaeryang Baek 6c54ca552a feat: global image compression 2025-06-16 16:52:57 +04:00
Timothy Jaeryang Baek 2949be4f27 refac 2025-06-16 16:33:48 +04:00
Timothy Jaeryang Baek 8e139b04f0 refac: iframe include allow-downloads by default 2025-06-16 16:24:51 +04:00
Timothy Jaeryang Baek 76fe344eb8 refac 2025-06-16 16:21:57 +04:00
Timothy Jaeryang BaekandWilliamGates f3cae94028 fix: bypass webloader
Co-Authored-By: WilliamGates <3852641+williamgateszhao@users.noreply.github.com>
2025-06-16 16:17:52 +04:00
Timothy Jaeryang BaekandBryan Berns 7a1afa9c66 feat: custom stt content type
Co-Authored-By: Bryan Berns <berns@uwalumni.com>
2025-06-16 16:13:40 +04:00
Sine Jespersen 437b7c5171 add aria label to button without label for accessibility 2025-06-16 13:33:45 +02:00
Sine Jespersen 5211602c2f add text high contrast mode for accessibility 2025-06-16 13:33:25 +02:00
Timothy Jaeryang Baek 6a5aac43df refac 2025-06-16 15:29:40 +04:00
Tim Jaeryang Baek d6664369a1 Merge pull request #14886 from Constantinople-AI/fix-no-arg-mcp-call
Isolated fix to single section
2025-06-16 15:28:48 +04:00
Timothy Jaeryang Baek bc7defbdd0 refac: allow editing own email 2025-06-16 15:23:03 +04:00
Timothy Jaeryang Baek 99e18d49dc enh: system prompt user permission 2025-06-16 15:12:32 +04:00
Timothy Jaeryang Baek 1af128fb24 chore 2025-06-16 15:05:35 +04:00
Tim Jaeryang Baek b8103807cf Merge pull request #15018 from itk-dev/feature/interface-tab-settings-modal-accessibility
Feat: interface tab settings modal accessibility
2025-06-16 14:25:01 +04:00
Tim Jaeryang Baek d4f67bb266 Merge pull request #15017 from Bek85/i18n-uzbek
i18n: Add Uzbek (Cyrillic and Latin) i18n support
2025-06-16 14:24:30 +04:00
Timothy Jaeryang Baek f3deb8b8de refac 2025-06-16 14:01:03 +04:00
Timothy Jaeryang Baek 7753f57d42 chore: format 2025-06-16 13:48:50 +04:00
Timothy Jaeryang Baek 26d069080a refac 2025-06-16 13:45:36 +04:00
Timothy Jaeryang Baek d802c718a4 refac: max file count behaviour 2025-06-16 13:28:31 +04:00
Bekzod Akbarov ace2345a69 Add Uzbek (Cyrillic and Latin) i18n support
- Added `uz-Cyrl-UZ` and `uz-Latn-UZ` folders with `translation.json` files
- Updated `languages.json` to include both Uzbek language variants
2025-06-16 14:25:22 +05:00
Timothy Jaeryang Baek 59324a82e0 refac 2025-06-16 13:19:00 +04:00
Timothy Jaeryang Baek e46e87889e refac: pass through ollama error message 2025-06-16 13:03:56 +04:00
Sine Jespersen 6fcdd11fb2 add labels to image compression inputs 2025-06-16 10:56:54 +02:00
Sine Jespersen 047d8cec5d aria-labelledby on buttons to connect to labels 2025-06-16 10:51:12 +02:00
Tim Jaeryang Baek 61d8d2f2cb Merge pull request #14197 from jk-f5/otel_metrics
feat: Add OpenTelemetry Metrics Support via OTLP Exporter
2025-06-16 12:49:41 +04:00
Timothy Jaeryang Baek 51e05988bd refac 2025-06-16 12:46:04 +04:00
Tim Jaeryang Baek b37907671e Merge pull request #15010 from itk-dev/feature/outline-hidden-depend-on-high-contrast-mode
Feat: outline hidden depend on high contrast mode
2025-06-16 12:43:33 +04:00
Tim Jaeryang Baek 0e245c0afc Merge pull request #15011 from itk-dev/feature/outline-hidden-depend-on-high-contrast-mode-selects-general-tab
outline-hidden depend on highcontrastmode in selects
2025-06-16 12:42:54 +04:00
Timothy Jaeryang Baek b748d5edee fix: direct tool servers permissions 2025-06-16 12:42:16 +04:00
Timothy Jaeryang Baek debe3c752f chore: python-pptx bump 2025-06-16 12:39:42 +04:00
Timothy Jaeryang Baek ea578af45f refac: use first user message as title instead of 'new chat' 2025-06-16 12:37:41 +04:00
Timothy Jaeryang Baek 8258dfb5af enh: enable deepgram smart_format 2025-06-16 12:34:01 +04:00
Sine Jespersen 1909505466 make header h1 instead of div 2025-06-16 10:33:48 +02:00
Timothy Jaeryang Baek f98ea78964 refac: remove user & access_control field from files 2025-06-16 12:31:52 +04:00
Sine Jespersen f1e38fa376 outline-hidden depend on highcontrastmode in selects 2025-06-16 10:26:54 +02:00
Timothy Jaeryang Baek 090234735a Update apple-touch-icon.png 2025-06-16 12:13:51 +04:00
Timothy Jaeryang Baek afca606063 refac: banner scrollbar 2025-06-16 12:06:33 +04:00
Sine Jespersen 0d473a456a maje outline-hidden depend on highContrastMode due to accessibility 2025-06-16 10:05:34 +02:00
Timothy Jaeryang Baek 4a2428123b refac 2025-06-16 12:04:31 +04:00
Timothy Jaeryang Baek ba6fc385bb refac: styling 2025-06-16 12:04:15 +04:00
Timothy Jaeryang Baek 4db0da2b75 refac 2025-06-16 12:02:34 +04:00
Timothy Jaeryang Baek 5ab95cbc3a refac: remove redundant overlay with temp chat 2025-06-16 11:57:56 +04:00
Timothy Jaeryang BaekandClassic298 3b0da53457 feat: load-url search param added
Co-Authored-By: Classic298 <27028174+Classic298@users.noreply.github.com>
2025-06-16 11:55:23 +04:00
Tim Jaeryang Baek 5d90e31ab6 Merge pull request #15009 from itk-dev/feature/password-button-accessibility-again
Feat: password button accessibility again
2025-06-16 11:52:39 +04:00
Timothy Jaeryang Baek dc3055873b refac: display full model name on hover 2025-06-16 11:50:26 +04:00
Timothy Jaeryang Baek f5d250820b refac 2025-06-16 11:46:02 +04:00
Timothy Jaeryang Baek 8892c14984 refac 2025-06-16 11:44:07 +04:00
Sine Jespersen 75883dc835 outline-hidden depend on high contrast mode 2025-06-16 09:43:47 +02:00
Sine Jespersen d1677635a5 add label visible to screen readers to sensitive input 2025-06-16 09:41:29 +02:00
Sine Jespersen 400c4c3d12 add aria pressed to toggle visibility button, to show current state 2025-06-16 09:40:41 +02:00
Timothy Jaeryang Baek a47e3b429e fix/refac: enforce multiple models permission to imported chats 2025-06-16 11:40:13 +04:00
Timothy Jaeryang Baek 805e980ae5 fix: notes permissions issue 2025-06-16 11:35:01 +04:00
Timothy Jaeryang Baek b50cfd0940 refac 2025-06-16 11:14:24 +04:00
Sine Jespersen 53d044f8a2 add aria label to button with no other label 2025-06-16 09:12:12 +02:00
Sine Jespersen 0e95af01ea remove decorative svg from screen readers 2025-06-16 09:11:50 +02:00
Tim Jaeryang Baek c5b48ec551 Merge pull request #14992 from sreesdas/dev
Fix: Added support for multiple pages in external document loader
2025-06-16 11:01:33 +04:00
Timothy Jaeryang Baek e0ffe234ba refac: styling 2025-06-16 10:58:04 +04:00
Timothy Jaeryang Baek e0917e9f38 refac: new chat button moved to the leftside 2025-06-16 10:54:50 +04:00
Timothy Jaeryang Baek 177f2902db refac 2025-06-16 10:51:30 +04:00
Timothy Jaeryang Baek 423a35782b refac: usage event handling 2025-06-16 10:42:34 +04:00
Tim Jaeryang Baek deaa7133a2 Merge pull request #14903 from vaclcer/vaclavs-docling-picture-describe-fix
fix: Docling - serialize picture description parameters
2025-06-16 09:58:05 +04:00
Tim Jaeryang Baek 37658e06a2 Merge pull request #14857 from Classic298/feat/code-interpreter-url-param
feat: Enable Code Interpreter using URL Parameter
2025-06-16 09:57:25 +04:00
Tim Jaeryang Baek 1f7a93b0fb Merge pull request #14937 from silentoplayz/situational-keyboard-shortcuts
feat: Add New Keyboard Shortcuts with asterisk and tooltip to imply situational
2025-06-16 09:54:05 +04:00
Tim Jaeryang Baek 866c61bac6 Merge pull request #14982 from silentoplayz/fix-drag
fix: Replace Inline SVGs with Icon Components and Convert Links to Buttons
2025-06-16 09:53:17 +04:00
Tim Jaeryang Baek c2b4b76a90 Merge pull request #14944 from silentoplayz/sort-feedbacks
feat: Implement Client-Side Sorting for Feedback History Table
2025-06-16 09:48:17 +04:00
Tim Jaeryang Baek 5a4a293a93 Merge branch 'dev' into sort-feedbacks 2025-06-16 09:47:51 +04:00
Timothy Jaeryang Baek c060e70089 refac: styling 2025-06-16 09:43:34 +04:00
Timothy Jaeryang Baek e34f11f5cc refac: styling 2025-06-16 09:40:40 +04:00
Timothy Jaeryang Baek 3e55c7f333 refac 2025-06-16 09:36:29 +04:00
Tim Jaeryang Baek b8a4824305 Merge pull request #14985 from Classic298/multi-model-chat-highlighting
Feat: Improve selected message highlighting for multi-model chats
2025-06-16 09:21:05 +04:00
Tim Jaeryang Baek 2251a50451 Merge pull request #14990 from SadmL/patch-1
[i18n] Russian locale update
2025-06-16 09:18:33 +04:00
sree 62bfe73964 Fix: Added support for multiple pages in external document loader, added filename in api request header 2025-06-15 19:59:05 +05:30
Link [Связной] 2eeffc8c59 [i18n] Russian locale update 2025-06-15 13:54:49 +03:00
Silentoplayz 6e8c6e269e fix: select-none w-full 2025-06-14 18:26:27 -04:00
Classic298 14cfd4599c Improve selected message highlight (#12) 2025-06-14 21:49:33 +02:00
Silentoplayz 4fd7bfe19b refac: Icon files for UserMenu 2025-06-14 14:31:20 -04:00
Silentoplayz 9ccb9a87e6 fix: Prevent draggable links for Admin Panel & Playground in UserMenu 2025-06-14 13:56:30 -04:00
Tim Jaeryang Baek 53060a44ae Merge pull request #14941 from silentoplayz/chat-bubble-timestamp-on-mouse-hover
Feat: Show timestamp on hover of user message in Chat Bubble UI mode
2025-06-14 18:06:47 +04:00
Tim Jaeryang Baek 4c7c5dd860 Merge pull request #14911 from ayanahye/feedback-evaluation-modal
Feat: add modal to display more evaluation information in Feedback History
2025-06-14 18:01:05 +04:00
Tim Jaeryang Baek 69be2a1bc9 Merge pull request #14949 from ayanahye/leaderboard-evaluation-additions
Feat: add initial modal for the leaderboard
2025-06-14 18:00:17 +04:00
Tim Jaeryang Baek 42b8463fcc Merge pull request #14959 from itk-dev/feature/accessibility-edit-connection-modal
Feature/accessibility edit connection modal
2025-06-14 17:58:38 +04:00
Tim Jaeryang Baek 35df44d07e Merge pull request #14971 from qingchunnh/Update_zh-CN-25614
i18n: Update zh-CN
2025-06-14 16:53:16 +04:00
qingchun 652e1a6b19 i18n: Update zh-CN 2025-06-14 02:34:08 +08:00
Sine Jespersen 78fcfbb328 hide decorative svg 2025-06-13 14:43:03 +02:00
Sine Jespersen 33a3aed698 remove repeated classes 2025-06-13 14:42:55 +02:00
Sine Jespersen d0a114869e make taglist a list and add aria-labels 2025-06-13 14:10:29 +02:00
Sine Jespersen 7d8fb116b6 Use <ul and <li because the items are a list 2025-06-13 14:10:29 +02:00
Sine Jespersen 6f73561f7a remove decorative svg from screen readers 2025-06-13 14:10:29 +02:00
Sine Jespersen a710ea1f07 add aria label to delete button 2025-06-13 14:10:29 +02:00
Sine Jespersen b95bc727df add label for screen readers for model id input 2025-06-13 14:10:29 +02:00
Sine Jespersen ba9e9e8095 add aria label to button with no label 2025-06-13 14:10:29 +02:00
Sine Jespersen 47c65bac6c remove decorative svg from screen readers 2025-06-13 14:10:29 +02:00
Sine Jespersen 85018dd106 high contrast mode on placeholder 2025-06-13 14:10:29 +02:00
Sine Jespersen 669a9f0777 highContrastMode (for accessibility) on text color in add connection modal 2025-06-13 14:10:28 +02:00
Sine Jespersen 1160a03644 change label for url to label instead of div 2025-06-13 14:10:28 +02:00
Sine Jespersen 843810cc9f add aria hidden to decorative svg 2025-06-13 14:10:28 +02:00
Sine Jespersen 8b5f5bb327 add label to verify connection button 2025-06-13 14:10:28 +02:00
Sine Jespersen 15efb07961 add label to toggle connection button in configure connect modal 2025-06-13 14:10:28 +02:00
Sine Jespersen a1034a4ca3 correct classname to inputclassname as prop is named this 2025-06-13 14:10:28 +02:00
Tim Jaeryang Baek 4978dd9085 Merge pull request #14943 from jk-f5/fix/oauth
Fix double‐slash (//auth) in OAuth callback redirect
2025-06-13 16:08:28 +04:00
Tim Jaeryang Baek 0b8f86d27b Merge pull request #14946 from t-rekttt/patch-1
fix: Page number 1 not displaying in citation references
2025-06-13 16:06:07 +04:00
Tim Jaeryang Baek 6918eb44ac Merge pull request #14953 from itk-dev/feature/aria-label-on-cog-button
Feat: aria label on cog button
2025-06-13 16:04:55 +04:00
Tim Jaeryang Baek 91af2698be Merge pull request #14958 from Ithanil/disable_console_log
feat: Disable console.log/debug in production
2025-06-13 16:04:13 +04:00
Jan Kessler 4681b06206 also disable console.debug 2025-06-13 13:54:40 +02:00
Jan Kessler aecbc93c16 disable console.log entirely 2025-06-13 13:39:16 +02:00
Classic298 033e5c1e00 Update chats.py 2025-06-13 13:07:41 +02:00
Classic298 e50cde80f3 Fix search unicode error (#11) 2025-06-13 13:05:33 +02:00
Sine Jespersen 0313ac0166 aria-hidden true on decorative svg 2025-06-13 10:46:39 +02:00
Sine Jespersen 87a837f850 add aria label to cog button in connections tab in settings modal 2025-06-13 10:46:27 +02:00
Ayana H 4bb41d3ead Merge branch 'dev' into leaderboard-evaluation-additions 2025-06-12 22:41:20 -07:00
ayana edf927516e Add initial modal for the leadderboard as well 2025-06-12 22:28:06 -07:00
T-Rekt 190400d1e8 Fix: page number edge case
Fix when document?.metadata?.page == 0 (page 1), the page number will not be displayed
2025-06-13 04:49:17 +07:00
Silentoplayz 380a31fbac Feat: Implement Client-Side Sorting for Feedback History Table 2025-06-12 16:13:33 -04:00
Jason Kidd fced3efd98 fix: Trailing slash was never removed from request.base_url because it's not a string but rather a starlette.datastructures.URL 2025-06-12 12:29:08 -07:00
Silentoplayz d92d555c03 Feat: Show user message timestamps on hover in Chat Bubble UI mode 2025-06-12 15:13:24 -04:00
ayana 880323a4e4 update based on requested changes 2025-06-12 10:53:55 -07:00
Tim Jaeryang Baek 8a2723a7a6 Merge pull request #14922 from itk-dev/feature/high-contrast-mode-in-connections-tab-in-settings-modal
Feat: high contrast mode in connections tab in settings modal
2025-06-12 20:40:01 +04:00
Tim Jaeryang Baek 2ff889dce0 Merge pull request #14933 from itk-dev/feature/accessibility-add-edit-connection-modal
Feat: accessibility add edit connection modal
2025-06-12 20:38:35 +04:00
Tim Jaeryang Baek 6c677e4b28 Merge pull request #14868 from ayanahye/evaluation-additions
Feat: add ability to sort leaderboard
2025-06-12 20:32:47 +04:00
Silentoplayz a98661735f feat: Add New Keyboard Shortcuts with asterisk and tooltip to imply situational 2025-06-12 09:47:17 -04:00
Sine Jespersen b446bea6d1 hide decorative svg from screen readers 2025-06-12 14:37:01 +02:00
Sine Jespersen 6faa9db4a7 add aria-label to button without label 2025-06-12 14:36:44 +02:00
Sine Jespersen 2b276583c3 change div to h1, because it is the main header in the modal 2025-06-12 14:34:26 +02:00
Tim Jaeryang Baek 80a5aa7259 Merge pull request #14923 from flex-mincheol/feature/i18n-ko-kr
i18n: Update ko-KR (Korean) translation.json
2025-06-12 13:36:28 +04:00
Sine Jespersen 9d76f30f9b high contrast mode on text in connections tab on settings modal 2025-06-12 11:01:02 +02:00
Tim Jaeryang Baek 09bbcee391 Merge pull request #14893 from tcx4c70/fix/log-format-error
fix(tools): not all arguments converted during string formatting
2025-06-12 12:30:31 +04:00
Timothy Jaeryang Baek c2aa5b7399 chore: youtube-transcript-api bump 2025-06-12 12:25:28 +04:00
Timothy Jaeryang Baek 9b9d5d84f4 fix: trusted header email case sensitive issue 2025-06-12 12:22:15 +04:00
flex-mincheol 389dbd1983 i18n: Update ko-KR (Korean) translation.json 2025-06-12 17:08:43 +09:00
ayana 2f8c0b6b26 Feat: add modal to display for evaluation information in Feedbacks 2025-06-11 19:34:07 -07:00
ayana 464c67eb46 use different notation 2025-06-11 18:27:13 -07:00
Vaclav Cerny 4bbc32efa6 fix: serialize picture description parameters to JSON in DoclingLoader 2025-06-11 20:00:25 +02:00
Tim Jaeryang Baek c27d8a4a28 Merge pull request #14900 from silentoplayz/fix-references
fix: prevent redundant documents in expanded citation list
2025-06-11 21:13:30 +04:00
Timothy Jaeryang Baek 371bdd7afa refac 2025-06-11 20:40:19 +04:00
Tim Jaeryang Baek 0d00971dad Merge pull request #14898 from jackthgu/ldap-group-sync
feat: Configure LDAP group synchronization with Open WebUI
2025-06-11 20:23:53 +04:00
Tim Jaeryang Baek 80c359c24e Merge pull request #14888 from silentoplayz/i18n-fix2
chore: consistent apostrophe usage in components and i18n files
2025-06-11 20:19:32 +04:00
Timothy Jaeryang Baek 6076afd5f5 fix: url param issue
#14901
2025-06-11 20:16:53 +04:00
Silentoplayz 8e60174e0d fix: prevent redundant documents in expanded citation list 2025-06-11 11:37:46 -04:00
Taehong Gu ba591d8c41 Configure LDAP group synchronization with Open WebUI 2025-06-11 23:55:31 +09:00
Tim Jaeryang Baek 387c2377ba Merge pull request #14890 from itk-dev/feature/hide-decorative-svgs
Feat: aria hidden on svg/icons
2025-06-11 17:39:20 +04:00
Adam Tao 809d91a3df fix(tools): not all arguments converted during string formatting
Signed-off-by: Adam Tao <tcx4c70@gmail.com>
2025-06-11 21:36:36 +08:00
Sine Jespersen 99ff1684d0 aria hidden on svg/icons 2025-06-11 14:47:28 +02:00
Tim Jaeryang Baek 9a1d2a1844 Merge pull request #14878 from itk-dev/feature/settings-modal-tab-list
Feature: settings modal tab list
2025-06-11 16:36:06 +04:00
Sine Jespersen 5dd99a0ea7 add aria-controls and ids to connect tabs to content 2025-06-11 13:45:47 +02:00
Sine Jespersen aee60add1d add aria-selected to tabs 2025-06-11 13:42:50 +02:00
Sine Jespersen 6af91325e2 add roles tablist and tab to tabs in settings modal 2025-06-11 13:41:26 +02:00
Silentoplayz a3f3f58e54 chore: consistent apostrophe usage in components and i18n files 2025-06-11 07:15:44 -04:00
Timothy Jaeryang Baek bc5f7e31ac refac 2025-06-11 14:43:56 +04:00
Timothy Jaeryang Baek c70a037692 refac 2025-06-11 14:43:07 +04:00
Tim Jaeryang Baek bebbc24d27 Merge pull request #14860 from Classic298/patch-2
i18n: Add german translation for new model pinning feature
2025-06-11 14:15:48 +04:00
Tim Jaeryang Baek 4e61bdd109 Merge pull request #14858 from Kylapaallikko/dev
i18n: Update fi-FI translation
2025-06-11 14:15:38 +04:00
2underscores 88c8ea883c Isolated fix to single section 2025-06-11 19:58:55 +10:00
Timothy Jaeryang Baek 9123c57c8a refac 2025-06-11 12:12:29 +04:00
ayana af285297bc Feat: add ability to sort leaderboard 2025-06-10 21:09:25 -07:00
Classic298 5c156c854b Update translation.json 2025-06-10 21:19:24 +02:00
google-labs-jules[bot] 70333c4dcf feat: Enable Code Interpreter via URL parameter
Adds the ability to enable the Code Interpreter feature by appending
`code-interpreter=true` to the chat URL.

The `Chat.svelte` component's `initNewChat` function has been updated
to parse this URL parameter. If present, it sets the
`codeInterpreterEnabled` state variable to true.

This change leverages the existing mechanisms for:
- Saving and restoring `codeInterpreterEnabled` state to/from localStorage.
- Displaying and toggling the Code Interpreter state via the existing UI
  button in the `MessageInput` component.

No new UI elements were added as the existing UI correctly reflects
the feature's state.
2025-06-10 18:56:49 +00:00
Kylapaallikko 1ca70b886b Update fi-FI translation.json 2025-06-10 21:51:29 +03:00
Jason Kidd 210dc746f0 feat: Add OpenTelemetry Metrics Support via OTLP Exporter 2025-06-10 10:52:10 -07:00
Tim Jaeryang Baek 63256136ef Merge pull request #14675 from open-webui/dev
0.6.14
2025-06-10 18:17:50 +04:00
Timothy Jaeryang Baek fd8cdb383c doc: changelog 2025-06-10 18:10:20 +04:00
Timothy Jaeryang Baek ab42a192e6 refac: styling 2025-06-10 17:54:08 +04:00
Timothy Jaeryang Baek adab4dbf21 chore: bump 2025-06-10 17:50:32 +04:00
Tim Jaeryang Baek c14160d73a Merge pull request #14845 from itk-dev/feature/accessibility-aria-label-close-button-and-aria-hidden-svg
Feat: accessibility aria label close button and aria hidden svg
2025-06-10 17:37:31 +04:00
Timothy Jaeryang Baek a7330d97da chore: format 2025-06-10 16:53:37 +04:00
Timothy Jaeryang Baek b39d33cefb refac 2025-06-10 16:52:37 +04:00
Timothy Jaeryang Baek d2e7feea86 refac 2025-06-10 16:42:26 +04:00
Timothy Jaeryang Baek a018140c7d refac: admin settings models 2025-06-10 16:41:39 +04:00
Timothy Jaeryang Baek 9931ccef1e refac 2025-06-10 16:20:57 +04:00
Sine Jespersen 56b012e3a6 danish translation added 2025-06-10 14:06:14 +02:00
Sine Jespersen c9820d65a6 add aria-hidden to svg, as this is decorative and only cause confusion for users with assistive tech 2025-06-10 13:59:27 +02:00
Sine Jespersen 9c1c11cad0 use xmark icon instead of inline svg 2025-06-10 13:58:58 +02:00
Sine Jespersen 8b5ecfb2fe Add aria label to close button 2025-06-10 13:52:24 +02:00
Tim Jaeryang Baek 3e81705dd3 Merge pull request #14837 from itk-dev/feature/menu-link-anchor
Feat: menu link anchor
2025-06-10 15:10:58 +04:00
Tim Jaeryang Baek 5168d612dc Merge pull request #14838 from Classic298/Classic298-patch-1
i18n: Small german translation update
2025-06-10 15:09:16 +04:00
Classic298 e67ab489aa Merge branch 'dev' into Classic298-patch-1 2025-06-10 13:07:24 +02:00
Classic298 c11cc58b00 Small german translation update 2025-06-10 12:54:17 +02:00
Sine Jespersen 3288b82175 change admin settings link to anchor instead of button 2025-06-10 12:49:18 +02:00
Timothy Jaeryang Baek f67f86f06f refac: styling 2025-06-10 14:33:49 +04:00
Timothy Jaeryang Baek c4cd2b55f0 refac: styling 2025-06-10 14:13:52 +04:00
Timothy Jaeryang Baek bacf39902b refac: styling 2025-06-10 14:02:43 +04:00
Tim Jaeryang Baek aeb81767a9 Merge pull request #14834 from SadmL/patch-1
[i18n] Russian locale update
2025-06-10 13:57:23 +04:00
Timothy Jaeryang Baek 884282c4ed refac: styling 2025-06-10 13:57:13 +04:00
Timothy Jaeryang Baek f55579ce25 refac 2025-06-10 13:55:25 +04:00
Link [Связной] 02f5bfba73 [i18n] Russian locale update 2025-06-10 12:54:31 +03:00
Tim Jaeryang Baek f29c9d8dcf Merge pull request #14833 from open-webui/dependabot/pip/pip-512858e340
build(deps): bump requests from 2.32.3 to 2.32.4 in the pip group across 1 directory
2025-06-10 13:45:47 +04:00
Timothy Jaeryang Baek 4c65728443 chore: format 2025-06-10 13:43:18 +04:00
Timothy Jaeryang Baek b36b480b43 refac 2025-06-10 13:40:36 +04:00
Tim Jaeryang Baek b8450ecf01 Merge pull request #14832 from itk-dev/feature/high-contrast-mode-and-label-menu-search-bar
FEAT: high contrast mode and label menu search bar
2025-06-10 13:40:14 +04:00
Timothy Jaeryang Baek 464b086ec5 refac 2025-06-10 13:39:41 +04:00
dependabot[bot] fc83842ab2 build(deps): bump requests in the pip group across 1 directory
Bumps the pip group with 1 update in the / directory: [requests](https://github.com/psf/requests).


Updates `requests` from 2.32.3 to 2.32.4
- [Release notes](https://github.com/psf/requests/releases)
- [Changelog](https://github.com/psf/requests/blob/main/HISTORY.md)
- [Commits](https://github.com/psf/requests/compare/v2.32.3...v2.32.4)

---
updated-dependencies:
- dependency-name: requests
  dependency-version: 2.32.4
  dependency-type: direct:production
  dependency-group: pip
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-10 09:38:48 +00:00
Tim Jaeryang Baek 7b248fc1f4 Merge pull request #14830 from open-webui/dependabot/pip/backend/pip-512858e340
build(deps): bump requests from 2.32.3 to 2.32.4 in /backend in the pip group across 1 directory
2025-06-10 13:37:49 +04:00
Timothy Jaeryang Baek a28bec865b refac 2025-06-10 13:16:44 +04:00
Timothy Jaeryang Baek b7a91b1963 refac: ollama response 2025-06-10 13:10:31 +04:00
Timothy Jaeryang Baek 2ccc441b41 refac 2025-06-10 13:01:45 +04:00
dependabot[bot] 106494aa79 build(deps): bump requests
Bumps the pip group with 1 update in the /backend directory: [requests](https://github.com/psf/requests).


Updates `requests` from 2.32.3 to 2.32.4
- [Release notes](https://github.com/psf/requests/releases)
- [Changelog](https://github.com/psf/requests/blob/main/HISTORY.md)
- [Commits](https://github.com/psf/requests/compare/v2.32.3...v2.32.4)

---
updated-dependencies:
- dependency-name: requests
  dependency-version: 2.32.4
  dependency-type: direct:production
  dependency-group: pip
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-10 08:56:03 +00:00
Timothy Jaeryang Baek 0b336fb6f1 refac 2025-06-10 12:53:18 +04:00
Timothy Jaeryang Baek 8f68b25510 enh/refac: ollama advanced params 2025-06-10 12:48:34 +04:00
Sine Jespersen 7fd5304e76 add assistive technology accessible label for search input in settings modal 2025-06-10 10:27:06 +02:00
Sine Jespersen af0f2a678e highContrastMode on search input in settingsmodal for accessibility 2025-06-10 10:25:40 +02:00
Timothy Jaeryang Baek ad80643774 refac: styling 2025-06-10 12:15:47 +04:00
Tim Jaeryang Baek 6e11bf73a7 Merge pull request #14827 from itk-dev/feature/high-contrast-mode-menu
FEAT: high contrast mode menu
2025-06-10 11:49:47 +04:00
Sine Jespersen ce45939463 add highContrastMode to menu items/tabs to improve contrast 2025-06-10 09:39:10 +02:00
Timothy Jaeryang Baek d430fe9551 refac 2025-06-10 11:30:54 +04:00
Timothy Jaeryang Baek 7f488b3754 feat: experimental pgvector pgcrypto support 2025-06-09 18:14:33 +04:00
Tim Jaeryang Baek 6d4f449085 Merge pull request #14775 from janaki-sasidhar/fix/insecure-user-switching-when-trusted-email-header
fix: verify trusted email header matches active user session
2025-06-09 17:44:30 +04:00
Timothy Jaeryang Baek 7953aa5763 refac 2025-06-09 17:43:22 +04:00
Tim Jaeryang Baek 95f54f5778 Merge pull request #14811 from SadmL/patch-1
[i18n] Russian locale update
2025-06-09 17:40:35 +04:00
Timothy Jaeryang Baek 51fc98bed8 refac 2025-06-09 17:24:03 +04:00
Timothy Jaeryang Baek ea8dc333ee refac/fix: multi-replica tasks 2025-06-09 17:21:10 +04:00
Link [Связной] 5447ac2de6 [i18n] Russian locale update 2025-06-09 15:43:17 +03:00
Timothy Jaeryang Baek 5fae7360fb refac: hide profile image in mobile 2025-06-09 16:35:20 +04:00
Timothy Jaeryang Baek db3c26ab7a refac: async redis 2025-06-09 15:37:05 +04:00
Timothy Jaeryang Baek 8da1ce358f refac: model selector styling 2025-06-09 11:22:31 +04:00
Timothy Jaeryang Baek ce9455b9a2 refac: styling 2025-06-09 10:43:47 +04:00
Tim Jaeryang Baek 36e5e51510 Merge pull request #14797 from qingchunnh/Update_zh-CN-2569-2
i18n: Update zh-CN
2025-06-09 10:13:28 +04:00
qingchun 7f39097879 i18n: Update zh-CN 2025-06-09 13:04:54 +08:00
Timothy Jaeryang Baek 9409083d0b refac 2025-06-09 02:16:23 +04:00
Timothy Jaeryang Baek 2573882b88 refac 2025-06-09 02:13:25 +04:00
Timothy Jaeryang Baek d1be1de013 fix: typo 2025-06-09 01:50:10 +04:00
Timothy Jaeryang Baek 7c03601425 chore: bump 2025-06-09 01:49:01 +04:00
Timothy Jaeryang Baek 053ab2838b chore: format 2025-06-09 01:48:29 +04:00
Timothy Jaeryang Baek 602bb55434 refac: styling 2025-06-09 01:45:41 +04:00
Timothy Jaeryang Baek 19c09d216d revert 2025-06-09 01:36:32 +04:00
Tim Jaeryang Baek 7a11fd2a4d Merge pull request #14793 from qingchunnh/Update_zh-CN-2569
i18n: Update & Improve zh-CN
2025-06-09 01:31:51 +04:00
Tim Jaeryang Baek c04569fdd4 Merge pull request #14791 from aindriu80/my-clean-fix
i18n: Updated Irish translation
2025-06-09 01:31:44 +04:00
Timothy Jaeryang Baek b0da2c998b refac 2025-06-09 01:27:50 +04:00
Timothy Jaeryang Baek 86551de1f7 refac 2025-06-09 01:26:32 +04:00
Timothy Jaeryang Baek 2f338b6ae7 refac: styling 2025-06-09 01:25:42 +04:00
Timothy Jaeryang Baek a64667ca8d feat: pinned models 2025-06-09 01:24:11 +04:00
Timothy Jaeryang Baek 3448362e00 refac: styling 2025-06-09 01:16:08 +04:00
Timothy Jaeryang Baek 9cbe39718d refac 2025-06-09 01:14:40 +04:00
Timothy Jaeryang Baek 7969d8e8f3 refac 2025-06-09 00:53:16 +04:00
Timothy Jaeryang Baek c59b4cf379 chore: bump bits ui 2025-06-09 00:37:38 +04:00
Timothy Jaeryang Baek 5e91c2c1fe refac: styling 2025-06-09 00:33:41 +04:00
qingchun b66d86cb74 i18n: Update & Improve zh-CN 2025-06-09 03:24:43 +08:00
Aindriú Mac Giolla Eoin ec9e3ef2c3 Merge branch 'dev' into my-clean-fix 2025-06-08 19:32:34 +01:00
Aindriú Mac Giolla Eoin a3ed5045d3 i18n fix commit issue 2025-06-08 19:28:35 +01:00
Timothy Jaeryang Baek 7f75acff96 chore: format 2025-06-08 22:08:25 +04:00
Timothy Jaeryang Baek a944e1f526 fix: feedback score overflow styling 2025-06-08 21:29:05 +04:00
Timothy Jaeryang Baek 3a0696e1b0 refac 2025-06-08 21:21:51 +04:00
Timothy Jaeryang Baek d8d8380a78 refac/fix: multi-replica stop task (response) 2025-06-08 21:20:30 +04:00
Timothy Jaeryang Baek 0c57980e72 refac: tasks 2025-06-08 20:58:31 +04:00
Timothy Jaeryang Baek 51fe33395b refac 2025-06-08 20:27:08 +04:00
Timothy Jaeryang Baek 4fe45d4430 refac/security: python code format endpoint 2025-06-08 20:26:07 +04:00
Timothy Jaeryang Baek 87e5aee106 refac 2025-06-08 20:05:26 +04:00
Timothy Jaeryang Baek 0cd400f5ee refac: docling picture describe params 2025-06-08 20:02:14 +04:00
Timothy Jaeryang Baek b9c64d0936 refac: styling 2025-06-08 19:22:41 +04:00
Tim Jaeryang Baek 6cb519ca0e Merge pull request #14774 from rragundez/images-from-db
fix: Store and load code interpreter generated images from a central location (DB and/or cloud storage)
2025-06-08 19:09:28 +04:00
Tim Jaeryang Baek 6bf393a480 Merge pull request #14787 from vaclcer/vaclavs-custom-docling
feat: Customize Docling's "Describe Pictures" feature
2025-06-08 19:02:36 +04:00
Tim Jaeryang Baek ac4513a773 Merge pull request #14783 from mrexodia/local-dev-fixes
fix: Improve local development setup
2025-06-08 18:59:38 +04:00
Tim Jaeryang Baek dae4a7088e Merge pull request #14780 from qingchunnh/Update_zh-CN-2568
i18n: Update zh-CN
2025-06-08 18:40:44 +04:00
Tim Jaeryang Baek 50d9a2ac58 Merge pull request #14781 from lucyknada/patch-2
fix: fix #14752 and add manual transcription retrieval
2025-06-08 18:40:28 +04:00
Tim Jaeryang Baek 0b1c81791e Merge pull request #14779 from GewoonJaap/feature/audio-html-component
Add Audio HTML token support
2025-06-08 18:39:50 +04:00
Vaclav Cerny 99f05561f8 Add configuration options for picture description modes and update related components 2025-06-08 16:30:26 +02:00
Duncan Ogilvie 5e73fd569d Improve local development setup 2025-06-08 15:17:08 +02:00
Duncan Ogilvie 7010393a41 Change default CORS_ALLOW_ORIGIN to '*'
The local development setup defaults do not actually work currently.
2025-06-08 15:16:51 +02:00
Duncan Ogilvie 41220b379f Exclude the 'backend' folder from the Vite watcher.
This avoids unnecessary reloads when working on the backend.
2025-06-08 14:58:56 +02:00
lucy b0965a8184 fixes #14752 and adds manual transcription option 2025-06-08 14:26:24 +02:00
qingchun 76d1690a0c i18n: Update zh-CN 2025-06-08 19:56:11 +08:00
Jaap d269c4b8e6 Audio token 2025-06-08 12:08:07 +02:00
Tim Jaeryang Baek 2b9c466bed Merge pull request #14765 from diwakar-s-maurya/patch-10
fix: save Document settings only when Save button is pressed
2025-06-08 13:05:51 +04:00
Tim Jaeryang Baek 1facc4485a Merge pull request #14766 from EntropyYue/feature/secret-key-file
feat: add support for WEBUI_SECRET_KEY_FILE
2025-06-08 13:05:29 +04:00
Tim Jaeryang Baek de3e474224 Merge pull request #14769 from RGSS3/main
The translation might be confusing, in some cases for example tooltip…
2025-06-08 13:04:50 +04:00
Tim Jaeryang Baek cc563d9763 Merge pull request #14770 from silentoplayz/small-fix
fix: remove newline from Pending User Overlay Title input field
2025-06-08 13:04:19 +04:00
Tim Jaeryang Baek ffe7bdd213 Merge pull request #14772 from silentoplayz/fix-save-for-LDAP
Fix: Ensure Consistent LDAP Settings Save
2025-06-08 13:03:17 +04:00
sasidhar 6860dec08f fix: properly sign out user on trusted email mismatch
When using trusted email header authentication, properly sign out the user
when the logged-in user's email doesn't match the trusted email header value.
This ensures proper session cleanup when the OAuth server changes the
authenticated user.

- Add response parameter to get_current_user function
- Delete JWT token cookie on email mismatch
- Delete OAuth token cookie if present
- Force re-authentication with 401 error
2025-06-08 14:26:55 +05:30
sasidhar 61f49ff580 fix: ensure trusted email header matches logged-in user
When using trusted email header authentication, verify that the logged-in user's
email matches the value in the header. This prevents session conflicts when the
OAuth server changes the authenticated user.

- Move trusted email verification after user existence check
- Raise 401 if email mismatch is detected
- Only perform verification when WEBUI_AUTH_TRUSTED_EMAIL_HEADER is enabled
2025-06-08 14:16:10 +05:30
Rodrigo Agundez 41e28a9f6f Use standardized procedure to save and load images 2025-06-08 16:26:58 +08:00
Rodrigo Agundez 168997a58e Return tuple consistently and strip potential left string 2025-06-08 16:26:08 +08:00
Silentoplayz a6c7ddcb28 fix: Hidden delete button for playground chat messages 2025-06-08 04:16:41 -04:00
Silentoplayz 67e930e1d2 Fix: Ensure Consistent LDAP Settings Save 2025-06-08 02:17:41 -04:00
Silentoplayz 92c7549b06 fix: remove newline 2025-06-08 01:50:11 -04:00
seiran e97394958e Merge branch 'dev' into main 2025-06-08 05:58:24 +08:00
Diwakar Singh Maurya d600e60776 fix: save Document settings only when Save button is pressed 2025-06-07 15:39:13 +00:00
EntropyYue fd4c42b42e feat: add support for WEBUI_SECRET_KEY_FILE 2025-06-07 23:14:21 +08:00
Diwakar Singh Maurya 871efb4ad9 feat: add langchain markdown document splitter 2025-06-07 06:02:53 +00:00
Tim Jaeryang Baek da5fbc0e39 Merge pull request #14743 from PeterDaveHello/Improve-zh-TW-locale
i18n: improve zh-TW Traditional Chinese locale
2025-06-06 21:26:57 +04:00
Tim Jaeryang Baek 1ad4f7a244 Merge pull request #14736 from SadmL/patch-1
[i18n] Russian locale update
2025-06-06 17:46:14 +04:00
Link [Связной] c0f65ec28a [i18n] Russian locale update 2025-06-06 16:28:36 +03:00
Sine Jespersen 6fd91e58cf add highContrastMode to type Settings 2025-06-06 13:38:35 +02:00
Timothy Jaeryang Baek b433d30c26 chore: format 2025-06-06 15:27:05 +04:00
Tim Jaeryang Baek 1ff7cb7d2f Merge pull request #14720 from itk-dev/feature/accessibible-chat-buttons
Feat: accessibible chat buttons
2025-06-06 12:55:46 +04:00
Sine Jespersen 9c38183689 reindent xmlns 2025-06-06 10:52:13 +02:00
Sine Jespersen fd97882fca add aria labels to buttons that only contains decorative svgs 2025-06-06 10:46:50 +02:00
Sine Jespersen 2b4d4d2770 remove aria-label from tooltip, as tippy handles this out of the box 2025-06-06 10:32:29 +02:00
Sine Jespersen dc60bd2816 add aria-hidden to svg, as these are decorative 2025-06-06 10:32:01 +02:00
Timothy Jaeryang Baek 0b84b22b4e refac: chat item key 2025-06-05 22:38:46 +04:00
Timothy Jaeryang Baek 870c23f69d refac 2025-06-05 22:37:14 +04:00
Timothy Jaeryang Baek 12cfb7b0f0 refac: admin evaluation pages 2025-06-05 21:43:43 +04:00
Tim Jaeryang Baek 37f1d47a06 Merge pull request #14683 from headwAI-GmbH/more-ideomatic-matrix
chore: More ideomatic docker-build matrix
2025-06-05 20:48:31 +04:00
Tim Jaeryang Baek 14a890a0a5 Merge pull request #14687 from SadmL/patch-2
[i18n] Russian locale update
2025-06-05 20:43:37 +04:00
Tim Jaeryang Baek f155abc63c Merge pull request #14700 from silentoplayz/better-settings-search
feat: Enhance Settings Modal Search & Admin Settings Button Placement
2025-06-05 20:43:19 +04:00
Tim Jaeryang Baek f6f3360ef7 Merge pull request #14695 from TiancongLx/dev
i18n: update zh-TW
2025-06-05 20:42:04 +04:00
Tim Jaeryang Baek 99fe23ef66 Merge pull request #14694 from Bouby308/patch-1
fix: return json for successful userSignOut
2025-06-05 20:40:10 +04:00
Tim Jaeryang Baek 92c5773682 Merge pull request #14696 from ShirasawaSama/fix_old_ios_device_voice_recording
fix: fix old iOS device voice recording
2025-06-05 20:38:55 +04:00
Silentoplayz 3c5ae1f62e chore: Also define the standard property 'appearance' for compatibilitycss(vendorPrefix) 2025-06-05 12:02:31 -04:00
Silentoplayz 543ed432b1 Update SettingsModal.svelte 2025-06-05 11:42:05 -04:00
Rodrigo AgundezandKG bb09245792 Inject code to block imports from blacklisted modules
Co-authored-by: KG <kahghi@users.noreply.github.com>
2025-06-05 23:21:37 +08:00
Rodrigo AgundezandKG 6dd969129d Add option to blacklist modules in code interpreter
Co-authored-by: KG <kahghi@users.noreply.github.com>
2025-06-05 23:21:04 +08:00
Peter Dave Hello a8b61204b4 i18n: improve zh-TW Traditional Chinese locale 2025-06-05 23:00:32 +08:00
Silentoplayz ec0124b521 Update SettingsModal.svelte 2025-06-05 10:48:33 -04:00
Shirasawa 07b2ecf6aa fix: fix old iOS device voice recording 2025-06-05 20:22:20 +08:00
Tiancong Li e7b86cc852 i18n: update zh-TW 2025-06-05 19:58:29 +08:00
Bouby308 eb3c080cce fix: return json for successful userSignOut
return json when signout request is successful so that frontend can get redirect url with res?.redirect_url on signout
2025-06-05 19:20:27 +08:00
Link [Связной] b3e328ed04 Update translation.json 2025-06-05 12:02:46 +03:00
Link [Связной] 6c8202d87d Update translation.json 2025-06-05 12:01:59 +03:00
Link [Связной] 936c3efe5e [i18n] Russian locale update 2025-06-05 11:55:46 +03:00
guenhter 0fe82d119f chore: make usage of matrix more ideomatic 2025-06-05 06:55:10 +02:00
Olivier Lacroix 4f982e244f Ensure tool callable can be used by genai directly 2025-06-05 10:58:22 +10:00
Timothy Jaeryang Baek 5e35aab292 chore: format 2025-06-05 01:12:28 +04:00
Timothy Jaeryang Baek f17bfb3adb refac 2025-06-05 01:11:31 +04:00
Timothy Jaeryang Baek 00e4391a6f refac 2025-06-05 01:05:33 +04:00
Timothy Jaeryang Baek 93236cead0 refac 2025-06-05 00:46:39 +04:00
Timothy Jaeryang Baek ed1db62b12 refac 2025-06-05 00:44:15 +04:00
Timothy Jaeryang Baek ab36b8aeae refac: embeddings endpoint 2025-06-05 00:37:31 +04:00
Timothy Jaeryang Baek b02a3da4da refac: styling 2025-06-05 00:32:48 +04:00
Tim Jaeryang Baek 7c4f261aa2 Merge pull request #14616 from Davixk/feat/new-perplexity-options
feat: add Perplexity AI model and search context usage configuration options
2025-06-05 00:28:00 +04:00
Tim Jaeryang Baek 14e158fde9 Merge pull request #14667 from hdnh2006/main
feat: OpenAI-compatible `/api/embeddings` endpoint with provider-agnostic OpenWebUI architecture
2025-06-05 00:25:56 +04:00
Tim Jaeryang Baek f50755ccd9 Merge pull request #14659 from vaclcer/vaclavs-picture-description
**fix**: do_picture_description feature when using Docling as an external document parser
2025-06-05 00:24:10 +04:00
henry 04da2b05f6 convert embedding function name to be more consistence 2025-06-04 18:24:27 +02:00
Vaclav Cerny 9772c18b20 fix(loader): remove deprecated picture description configuration 2025-06-04 17:21:44 +02:00
henry cc12e9e1a3 Ollama embeddings adapted to pydantic 2025-06-04 17:06:38 +02:00
henry 3ddebefca2 openai embeddings function modified 2025-06-04 16:13:53 +02:00
henry 8f6c3f46d6 payload and response modifed for compatibility 2025-06-04 16:11:40 +02:00
henry e0769c6a1f new embedding.py added for handling openai and ollama embedding 2025-06-04 16:09:39 +02:00
Henry 415114504f Merge branch 'open-webui:main' into main 2025-06-04 15:37:33 +02:00
Tim Jaeryang Baek 94356dc3ca Merge pull request #14662 from SadmL/patch-2
[i18n] Russian locale update
2025-06-04 17:01:06 +04:00
Link [Связной] 7f04045697 [i18n] Russian locale update 2025-06-04 15:58:15 +03:00
Vaclav Cerny 0b3719cc65 This commit fixes do_picture_description feature when using Docling as an external document parser.
Merge branch 'dev' into vaclavs-picture-description
2025-06-04 14:29:41 +02:00
Vaclav Cerny c71236ba07 feat(loader): enhance picture description prompt for improved detail and clarity 2025-06-04 14:25:31 +02:00
Vaclav Cerny c4278f4784 fix description vs classification mismatch 2025-06-04 14:13:00 +02:00
Timothy Jaeryang Baek bbafeca495 refac: reserved __user__ param format 2025-06-04 15:53:07 +04:00
Tim Jaeryang Baek dcb71c2292 Merge pull request #14644 from Davixk/feat/enforce-lf-eol
refac: enforce consistent line endings across all platforms
2025-06-04 15:44:40 +04:00
Timothy Jaeryang Baek 91e826cc27 refac 2025-06-04 15:43:23 +04:00
Timothy Jaeryang BaekandRodrigo Agundez 9964ad0a5b refac: auth cache dir
Co-Authored-By: Rodrigo Agundez <rragundez@users.noreply.github.com>
2025-06-04 15:21:08 +04:00
Vaclav Cerny 8644e81a1c feat(loader): add picture description configuration for DoclingLoader 2025-06-04 12:34:39 +02:00
Tim Jaeryang Baek 7ef2345ba6 Merge pull request #14642 from ayanahye/fix-chat-title-error-message
fix: error message on chat title edit
2025-06-04 14:09:20 +04:00
Tim Jaeryang Baek 00b16cbaf2 Merge pull request #14645 from qingchunnh/Update_zh-CN-2564
i18n: Update & Improve zh-CN
2025-06-04 14:08:43 +04:00
Tim Jaeryang Baek 2b926a373c Merge pull request #14639 from silentoplayz/add-clone-to-cloned-prompts
fix: append `(Clone)` to title and `-clone` to id when cloning a workspace prompt
2025-06-04 14:08:30 +04:00
Tim Jaeryang Baek 5df4f98fc3 Merge pull request #14640 from silentoplayz/locales-is-undefined-typeerror-fix
fix: Uncaught (in promise) TypeError: locales is undefined
2025-06-04 14:07:38 +04:00
qingchun 717d58116a i18n: Improve zh-CN 2025-06-04 12:46:14 +08:00
qingchun 3e02618278 i18n: Update & Improve zh-CN 2025-06-04 12:33:07 +08:00
Dave 5fb5daf07f refac: standardize formatting in .gitattributes for consistency 2025-06-04 06:32:06 +02:00
Dave 059327cd19 refac: update .gitattributes and .prettierrc for consistent line endings 2025-06-04 06:27:25 +02:00
Ayana H fd53c737f6 update spacing 2025-06-03 19:49:52 -07:00
ayana dc05ccb73f fix: error message on chat title edit 2025-06-03 19:27:28 -07:00
Timothy Jaeryang Baek aeafcc7903 refac: styling 2025-06-04 02:51:42 +04:00
Silentoplayz 86111ad44a fix: Uncaught (in promise) TypeError: locales is undefined in Collapsible.svelte 2025-06-03 18:50:16 -04:00
Timothy Jaeryang Baek ec81e98fa2 refac 2025-06-04 02:46:30 +04:00
Timothy Jaeryang Baek fe69189dc9 refac: styling 2025-06-04 02:36:31 +04:00
Silentoplayz 5b4f81ce11 Update PromptEditor.svelte 2025-06-03 17:25:33 -04:00
Silentoplayz 6ebf522a27 Update PromptEditor.svelte 2025-06-03 17:24:54 -04:00
Silentoplayz 2a6d2392a0 fix: append (Clone) to cloned prompt title and clone to prompt id 2025-06-03 17:19:24 -04:00
Timothy Jaeryang Baek 25157ca6ba chore: format 2025-06-03 22:51:59 +04:00
Timothy Jaeryang Baek c01e29c47f refac: model tools selection not working 2025-06-03 22:04:13 +04:00
Tim Jaeryang Baek bb689b9480 Merge pull request #14630 from Yu-QX/main
fix: Rendering problem with Chinese quotation mark in **bold** & *italic* (after Chinese parentheses issue).
2025-06-03 21:59:12 +04:00
Timothy Jaeryang Baek db836c53f6 refac 2025-06-03 21:57:18 +04:00
Timothy Jaeryang Baek 7ca35068a6 refac: styling 2025-06-03 21:55:40 +04:00
Timothy Jaeryang Baek bbedc50f00 refac 2025-06-03 21:52:58 +04:00
Timothy Jaeryang Baek d2b5a1614a refac 2025-06-03 21:52:25 +04:00
Timothy Jaeryang Baek abae0a837f refac: styling 2025-06-03 21:51:12 +04:00
YuQX 4eef6f432f fix: Rendering problem with Chinese quotation mark in **bold** & *italic* (after Chinese parentheses issue). 2025-06-03 23:30:51 +08:00
Timothy Jaeryang Baek 0a1ff4a8fa refac 2025-06-03 18:49:42 +04:00
Timothy Jaeryang Baek 931e5ffa6e refac 2025-06-03 18:49:03 +04:00
Timothy Jaeryang Baek 185249623b feat: follow ups backend integration 2025-06-03 18:47:49 +04:00
YuQX ca894df2bd Merge pull request #2 from open-webui/main
Upgrade to v0.6.13
2025-06-03 22:36:28 +08:00
Timothy Jaeryang Baek 9e49fbc8bf feat: follow ups 2025-06-03 18:07:29 +04:00
Timothy Jaeryang Baek f8b941fb96 refac 2025-06-03 17:24:31 +04:00
Timothy Jaeryang Baek 2fee89dfa6 doc: readme 2025-06-03 16:49:24 +04:00
Timothy Jaeryang Baek 4d364e2967 refac: remove msg from known type 2025-06-03 16:27:28 +04:00
Tim Jaeryang Baek 8bdefa5481 Merge pull request #14592 from ER-EPR/main
Feat: Add a new docker tag cu126 to maintain support for NVIDIA cuda capability 7.0 and below (e.g. V100, GTX1080)
2025-06-03 12:48:15 +04:00
Tim Jaeryang Baek 34a8b95cc3 Merge pull request #14622 from Zyfax/patch-2
fix: remove trailing slash form test connection api url
2025-06-03 12:43:31 +04:00
Zyfax e353f15a3c fix: remove ending slash form test connection url
Removes the ending slash when testing openai and ollama api url.
2025-06-03 09:11:01 +02:00
Dave 77b357c73b fix: update label for search context usage to clarify its purpose 2025-06-03 00:27:07 +02:00
Dave 96e9bfe0e5 feat: add Perplexity model and search context usage configuration options 2025-06-03 00:19:08 +02:00
Tim Jaeryang Baek 3c32d2cada Merge pull request #14539 from PVBLIC-F/refac/mistral
perf mistral.py Enhance for Overall Speed and Efficiency
2025-06-02 23:52:59 +04:00
Timothy Jaeryang Baek 3da1802eec fix: message delete issue 2025-06-02 23:25:38 +04:00
Tim Jaeryang Baek 0ebe35c571 Merge pull request #14532 from PVBLIC-F/refac/pinecone
perf pinecone.py Improve Performance and Maintainability Using Current Best Practices
2025-06-02 23:12:48 +04:00
Tim Jaeryang Baek 29d6eb642f Merge pull request #14555 from diwakar-s-maurya/patch-8
feat: Add route for each tab in Settings
2025-06-02 22:36:36 +04:00
Tim Jaeryang Baek 7536a94585 Merge pull request #14552 from expruc/he-tr
i18n: updated hebrew locale
2025-06-02 21:42:45 +04:00
Tim Jaeryang Baek 89dcce0df2 Merge pull request #14553 from PeterDaveHello/Improve-zh-TW-locale
i18n: update and improve zh-TW Traditional Chinese locale
2025-06-02 21:40:01 +04:00
Tim Jaeryang Baek 6e2bffee69 Merge pull request #14566 from open-webui/dependabot/pip/backend/dev/uvicorn-standard--0.34.2
build(deps): bump uvicorn[standard] from 0.34.0 to 0.34.2 in /backend
2025-06-02 21:38:39 +04:00
Tim Jaeryang Baek 84c06aa582 Merge pull request #14565 from open-webui/dependabot/pip/backend/dev/pillow-11.2.1
build(deps): bump pillow from 11.1.0 to 11.2.1 in /backend
2025-06-02 21:37:45 +04:00
Tim Jaeryang Baek 281b74f4b4 Merge pull request #14563 from open-webui/dependabot/pip/backend/dev/azure-ai-documentintelligence-1.0.2
build(deps): bump azure-ai-documentintelligence from 1.0.0 to 1.0.2 in /backend
2025-06-02 21:37:37 +04:00
Tim Jaeryang Baek 816e35bd79 Merge pull request #14562 from open-webui/dependabot/pip/backend/dev/validators-0.35.0
build(deps): bump validators from 0.34.0 to 0.35.0 in /backend
2025-06-02 21:37:28 +04:00
Tim Jaeryang Baek 0469120709 Merge pull request #14573 from open-webui/dependabot/npm_and_yarn/dev/katex-0.16.22
build(deps): bump katex from 0.16.21 to 0.16.22
2025-06-02 21:37:19 +04:00
ER-EPR bf3897ec40 Merge pull request #1 from ER-EPR/ER-EPR-cuda126-1
Add cuda126 image in docker-build.yaml
2025-06-02 17:45:39 +08:00
ER-EPR d649db4ef7 Add cuda126 image in docker-build.yaml
add USE_CUDA_VER=cu126 version to support cuda capability 7.0 and below
2025-06-02 17:44:17 +08:00
dependabot[bot] 28db03e4f9 build(deps): bump katex from 0.16.21 to 0.16.22
Bumps [katex](https://github.com/KaTeX/KaTeX) from 0.16.21 to 0.16.22.
- [Release notes](https://github.com/KaTeX/KaTeX/releases)
- [Changelog](https://github.com/KaTeX/KaTeX/blob/main/CHANGELOG.md)
- [Commits](https://github.com/KaTeX/KaTeX/compare/v0.16.21...v0.16.22)

---
updated-dependencies:
- dependency-name: katex
  dependency-version: 0.16.22
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-01 02:49:05 +00:00
dependabot[bot] 26549244fc build(deps): bump uvicorn[standard] from 0.34.0 to 0.34.2 in /backend
Bumps [uvicorn[standard]](https://github.com/encode/uvicorn) from 0.34.0 to 0.34.2.
- [Release notes](https://github.com/encode/uvicorn/releases)
- [Changelog](https://github.com/encode/uvicorn/blob/master/docs/release-notes.md)
- [Commits](https://github.com/encode/uvicorn/compare/0.34.0...0.34.2)

---
updated-dependencies:
- dependency-name: uvicorn[standard]
  dependency-version: 0.34.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-01 02:28:37 +00:00
dependabot[bot] 3ccd2364ba build(deps): bump pillow from 11.1.0 to 11.2.1 in /backend
Bumps [pillow](https://github.com/python-pillow/Pillow) from 11.1.0 to 11.2.1.
- [Release notes](https://github.com/python-pillow/Pillow/releases)
- [Changelog](https://github.com/python-pillow/Pillow/blob/main/CHANGES.rst)
- [Commits](https://github.com/python-pillow/Pillow/compare/11.1.0...11.2.1)

---
updated-dependencies:
- dependency-name: pillow
  dependency-version: 11.2.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-01 02:28:34 +00:00
dependabot[bot] 1131a61b8e build(deps): bump azure-ai-documentintelligence in /backend
Bumps [azure-ai-documentintelligence](https://github.com/Azure/azure-sdk-for-python) from 1.0.0 to 1.0.2.
- [Release notes](https://github.com/Azure/azure-sdk-for-python/releases)
- [Changelog](https://github.com/Azure/azure-sdk-for-python/blob/main/doc/esrp_release.md)
- [Commits](https://github.com/Azure/azure-sdk-for-python/compare/azure-ai-documentintelligence_1.0.0...azure-ai-documentintelligence_1.0.2)

---
updated-dependencies:
- dependency-name: azure-ai-documentintelligence
  dependency-version: 1.0.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-01 02:28:27 +00:00
dependabot[bot] 2f3d4622db build(deps): bump validators from 0.34.0 to 0.35.0 in /backend
Bumps [validators](https://github.com/python-validators/validators) from 0.34.0 to 0.35.0.
- [Release notes](https://github.com/python-validators/validators/releases)
- [Changelog](https://github.com/python-validators/validators/blob/master/CHANGES.md)
- [Commits](https://github.com/python-validators/validators/compare/0.34.0...0.35.0)

---
updated-dependencies:
- dependency-name: validators
  dependency-version: 0.35.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-01 02:28:18 +00:00
Peter Dave Hello 96d8a52fe3 i18n: update and improve zh-TW Traditional Chinese locale 2025-06-01 04:08:33 +08:00
Diwakar Singh Maurya 1ddc784af9 feat: Add route for each tab in Settings 2025-05-31 19:18:05 +00:00
expruc 185224e4e1 Locale: updated he translation 2025-05-31 21:37:42 +03:00
Timothy Jaeryang Baek 2c15f8e676 refac 2025-05-31 15:07:28 +04:00
Timothy Jaeryang Baek 4e82c44f3e refac 2025-05-31 15:04:38 +04:00
Timothy Jaeryang Baek e41e375aab refac: role update ui 2025-05-31 15:00:27 +04:00
Tim Jaeryang Baek 343dac91c9 Merge pull request #14522 from SadmL/patch-2
[i18n] Russian locale update
2025-05-31 14:42:52 +04:00
PVBLIC Foundation cf3635ba25 Update mistral.py
1. Intelligent Error Handling
Added _is_retryable_error() method to distinguish retryable vs non-retryable errors
Prevents unnecessary retries on client errors (4xx) that won't succeed
Caps retry delay at 30 seconds to prevent excessive waiting
2. Optimized Timeout Configuration
Upload: Capped at 2 minutes (was using full 5-minute timeout)
URL requests: 30 seconds (should be fast)
OCR processing: Full timeout (can take time)
Cleanup: 30 seconds (should be quick)
3. Enhanced Connection Pool
Increased connection limits: 20 total, 10 per host
Longer DNS cache TTL (10 minutes vs 5 minutes)
Increased keepalive timeout (60s vs 30s)
Added async DNS resolver for better performance
Granular timeout controls (connect, read, total)
4. Concurrency Control for Batch Processing
Added semaphore-based concurrency control (default: 5 concurrent)
Prevents API overwhelming while maintaining throughput
Configurable concurrency limit per workload
5. Memory Efficient Result Processing
Early exit for empty content validation
Better error metadata for debugging
Added content length tracking
Streamlined page processing logic
6. General Performance Improvements
Better error logging with truncated responses
Optimized metadata creation
Improved debug logging efficiency
2025-05-30 20:06:29 -07:00
PVBLIC Foundation 66bde32623 Update pinecone.py 2025-05-30 18:47:23 -07:00
sanae artoria cbc514e681 The translation might be confusing, in some cases for example tooltips over a radio button 2025-05-31 01:13:55 +08:00
PVBLIC Foundation 4ecf2a8685 Update pinecone.py
May 2025 Latest Pinecone Best Practices
2025-05-30 09:33:57 -07:00
Link [Связной] 5555d889d5 Update translation.json 2025-05-30 11:55:22 +03:00
Link [Связной] 7dde16d0ac [i18n] Russian locale update 2025-05-30 11:52:33 +03:00
Tim Jaeryang Baek 53764fe648 Merge pull request #14486 from open-webui/dev
0.6.13
2025-05-30 01:37:21 +04:00
Timothy Jaeryang Baek 235489cfc5 refac 2025-05-30 01:33:49 +04:00
Timothy Jaeryang Baek b2befd486f refac 2025-05-30 01:24:54 +04:00
Timothy Jaeryang Baek 9306ae5972 refac 2025-05-30 01:19:56 +04:00
Timothy Jaeryang Baek 036ce12dd9 doc: changelog 2025-05-30 01:14:38 +04:00
Tim Jaeryang Baek d6c3f93cfd Merge pull request #14509 from r0mdau/fix/audio-format
fix: only trust codec_name for audio conversion
2025-05-30 01:12:22 +04:00
Romain Dauby b12a493fe5 fix: only trust codec_name for audio conversion
Some files have .wav extension with incompatible OpenAI codec
2025-05-29 16:57:23 -04:00
Timothy Jaeryang Baek f4827f0c18 chore: format 2025-05-30 00:35:59 +04:00
Timothy Jaeryang Baek e1e2c096e2 refac: PLEASE follow existing convention 2025-05-30 00:34:18 +04:00
Tim Jaeryang Baek ff353578db Merge pull request #14370 from daw/feat/add-azure-openai-embeddings-option
feat:Add Azure OpenAI embedding support
2025-05-30 00:18:55 +04:00
Timothy Jaeryang Baek be989f3645 refac: better memory error handling 2025-05-30 00:12:28 +04:00
Timothy Jaeryang Baek 4c45d67677 refac/fix: memory 2025-05-30 00:10:52 +04:00
Timothy Jaeryang Baek 59768e34f4 refac 2025-05-30 00:04:13 +04:00
Timothy Jaeryang Baek 4371d2c5a5 enh: better custom param handling 2025-05-29 23:32:14 +04:00
Timothy Jaeryang Baek 9be22cb637 fix: prompt access control 2025-05-29 23:21:56 +04:00
Tim Jaeryang Baek f95f51530b Merge pull request #14488 from TiancongLx/dev
i18n: update zh-TW
2025-05-29 13:35:10 +04:00
Tiancong Li c3e8e1cbf1 i18n: update zh-TW 2025-05-29 17:18:27 +08:00
Tim Jaeryang Baek 2327c32a74 Merge pull request #14485 from jackthgu/update-korean-translation
i18n: Korean locale update
2025-05-29 13:16:00 +04:00
Taehong Gu a214e63cab Update Korean translation - Documentation 2025-05-29 18:12:54 +09:00
Tim Jaeryang Baek f1507f2458 Merge pull request #14472 from Davixk/fix/chat-loading-error
fix: Chat page fails to load on undefined message
2025-05-29 13:07:08 +04:00
Timothy Jaeryang Baek 255367934b doc: typo 2025-05-29 13:02:12 +04:00
Tim Jaeryang Baek bd4e010c76 Merge pull request #14477 from qingchunnh/Update_zh-CN-25529
i18n: Update & Improve zh-CN
2025-05-29 13:00:14 +04:00
Tim Jaeryang Baek 155518e788 Merge pull request #14478 from Kylapaallikko/dev
i18n: Update fi-FI translation
2025-05-29 12:59:52 +04:00
Timothy Jaeryang Baek d43bbcae28 refac/fix: open webui params handling 2025-05-29 12:57:58 +04:00
Kylapaallikko 309380b098 Update fi-FI translation.json 2025-05-29 09:18:42 +03:00
qingchun 2c723e1f28 i18n: Update & Improve zh-CN 2025-05-29 13:40:39 +08:00
Dave 01eedd36bc Handles undefined message case in message list creation
Prevents potential errors by returning an empty list if the
specified message ID does not exist in the history. This
enhancement ensures robustness in scenarios where a message
ID may be missing, avoiding further processing and potential
exceptions.
2025-05-29 06:19:13 +02:00
Tim Jaeryang Baek ba0088f39b Merge pull request #14441 from open-webui/dev
0.6.12
2025-05-29 03:59:24 +04:00
Timothy Jaeryang Baek 661625f362 doc: changelog 2025-05-29 03:56:42 +04:00
Timothy Jaeryang Baek 30b7ab3591 refac 2025-05-29 03:48:07 +04:00
Timothy Jaeryang Baek 7dc7d5c028 refac: PLEASE FOLLOW EXISTING CONVENTION 2025-05-29 03:47:02 +04:00
Timothy Jaeryang Baek 2c31f5c725 doc: readme 2025-05-29 03:41:38 +04:00
Timothy Jaeryang Baek a6864db8ec chore: format 2025-05-29 03:37:58 +04:00
Timothy Jaeryang Baek 21f85e63bf refac: styling 2025-05-29 03:37:13 +04:00
Timothy Jaeryang Baek 9220afe7b3 feat: custom advanced params 2025-05-29 03:33:11 +04:00
Timothy Jaeryang Baek bb4115fa0e refac: allow all params 2025-05-29 02:56:37 +04:00
Timothy Jaeryang Baek 551597b9cc chore: format 2025-05-29 02:36:33 +04:00
Timothy Jaeryang Baek cb4299eb98 refac 2025-05-29 02:33:40 +04:00
Tim Jaeryang Baek 042c37ea34 Merge pull request #14311 from Hisma/marker-api-content-extraction
feat: Marker api content extraction support
2025-05-29 02:21:13 +04:00
Timothy Jaeryang Baek 85a384fab5 enh: load tool by url 2025-05-29 02:08:54 +04:00
Timothy Jaeryang Baek 4461122a0e fix: /api/v1/retrieval/query/collection endpoint 2025-05-28 18:45:47 +04:00
YuQX 24be96a6d4 Merge pull request #1 from open-webui/main
Upgrade to v0.6.11
2025-05-28 22:24:33 +08:00
Tim Jaeryang Baek c9f983f644 Merge pull request #14445 from NotYuSheng/chore/duplicate-css-elements
chore: removed duplicate css elements
2025-05-28 17:58:59 +04:00
Timothy Jaeryang Baek cc6cbf53e8 refac 2025-05-28 17:36:05 +04:00
Timothy Jaeryang Baek 4279762ea4 refac 2025-05-28 16:36:23 +04:00
notyusheng efedb7ab1f chore: removed duplicate css elements 2025-05-28 08:31:11 -04:00
Timothy Jaeryang Baek 32135a29bb fix: image preview/download 2025-05-28 15:57:40 +04:00
Timothy Jaeryang Baek 8a74bdce37 fix: message input issue 2025-05-28 15:36:04 +04:00
Timothy Jaeryang Baek 3d9a430927 refac: styling 2025-05-28 14:28:39 +04:00
Timothy Jaeryang Baek a27a095884 refac 2025-05-28 14:27:34 +04:00
Tim Jaeryang Baek 596298fa46 Merge pull request #14405 from SadmL/patch-2
[i18n] Russian locale update
2025-05-28 14:23:33 +04:00
Timothy Jaeryang Baek bf7a18a0f8 refac 2025-05-28 02:10:54 +04:00
Timothy Jaeryang Baek d81886e315 refac 2025-05-28 01:42:42 +04:00
Timothy Jaeryang Baek 7effb04782 refac 2025-05-28 01:41:49 +04:00
Timothy Jaeryang Baek f5fefb49d5 refac 2025-05-28 01:38:24 +04:00
Timothy Jaeryang Baek e4a53e0a3c refac 2025-05-28 01:34:53 +04:00
Link [Связной] e861ba5520 [i18n] Russian locale update 2025-05-27 16:28:09 +03:00
Tim Jaeryang Baek 100a764293 Merge pull request #14402 from torisetxd/parallelized-model-fetching
perf: Parallelize base model fetching
2025-05-27 16:56:44 +04:00
Timothy Jaeryang Baek 1d216b82ba refac 2025-05-27 16:48:17 +04:00
toriset 9eccce2444 Added proper type hints to new functions
Forgot about that...
2025-05-27 15:44:20 +03:00
toriset 27de981246 Parallelize base model fetching 2025-05-27 15:35:16 +03:00
Timothy Jaeryang Baek 40bea00e3d refac 2025-05-27 16:06:00 +04:00
Timothy Jaeryang Baek 8609471eeb Revert "Merge pull request #14379 from aleixdorca/dev"
This reverts commit cb135c370a, reversing
changes made to 8cf578d9fd.
2025-05-27 16:00:19 +04:00
Tim Jaeryang Baek cb135c370a Merge pull request #14379 from aleixdorca/dev
i18n: Update Catalan translation.json
2025-05-27 15:59:37 +04:00
Tim Jaeryang Baek 8cf578d9fd Merge pull request #14368 from qingchunnh/Update_zh-CN-25527
i18n: Update & Improve zh-CN
2025-05-27 15:59:29 +04:00
Tim Jaeryang Baek 1aac41d6c3 Merge pull request #14400 from torisetxd/capabilities-fix
fix: Improve mobile styling for capabilities in workspace models
2025-05-27 15:59:16 +04:00
toriset 20403c9df7 Match capabilities styling to other settings in workspace models 2025-05-27 14:39:21 +03:00
Timothy Jaeryang Baek b944acd3ff refac: function cache 2025-05-27 14:39:35 +04:00
Tim Jaeryang Baek 1ec9e42c1e Merge pull request #14383 from TiancongLx/dev
i18n: complete and improve zh-TW
2025-05-27 13:56:51 +04:00
Tim Jaeryang Baek 5989e9bf75 Merge pull request #14387 from Zyfax/patch-1
fix: tts read aloud
2025-05-27 13:56:40 +04:00
Tim Jaeryang Baek 75b2e4a659 Merge pull request #14392 from hurxxxx/fix/improve-loading-functions
perf: Prevent duplicate function loads with caching helper and refactor
2025-05-27 13:56:13 +04:00
Gunwoo Hur 14c3d0c2d1 Prevent duplicate function module loads with caching helper and refactor 2025-05-27 18:08:58 +09:00
Tim Jaeryang Baek 737dc7797c Merge pull request #14390 from open-webui/dev
Dev
2025-05-27 12:59:28 +04:00
Timothy Jaeryang Baek eb14bfbcdf refac: bug report template 2025-05-27 12:59:01 +04:00
Zyfax 4995bfe571 fix: tts read aloud
Change: const content = removeAllDetails(content); to const content = removeAllDetails(message.content);

Error: Uncaught (in promise) ReferenceError: can't access lexical declaration 'z' before initialization.
ResponseMessage.svelte:215:42
2025-05-27 10:52:26 +02:00
Tiancong Li 098ec43413 i18n: update zh-TW 2025-05-27 16:16:09 +08:00
Aleix Dorca fcb03df9ce Update catalan translation.json 2025-05-27 09:02:42 +02:00
Hisma 19bb3589ee fix: add Datalab Marker API to Content Extraction Engine Dropdown 2025-05-27 02:24:53 -04:00
Hisma e12a79c0e2 fix: handle json output format correctly 2025-05-27 01:12:03 -04:00
Hisma a9405cc101 feat: Marker api content extraction support 2025-05-27 00:44:07 -04:00
qingchun 7988e0c6e2 i18n: Improve zh-CN 2025-05-27 11:25:12 +08:00
qingchun 14bf579e9c i18n: Fix zh-CN 2025-05-27 10:25:02 +08:00
qingchun fc2202ad76 i18n: Update zh-CN 2025-05-27 09:19:35 +08:00
Timothy Jaeryang Baek a2afd6f645 refac 2025-05-27 03:06:01 +04:00
Tim Jaeryang Baek b8e16211b9 Merge pull request #14364 from open-webui/dev
fix: image generation
2025-05-27 02:48:45 +04:00
Timothy Jaeryang Baek efb54aa2e4 fix: image generation 2025-05-27 02:48:22 +04:00
Tim Jaeryang Baek 9faa4c6a4c Merge pull request #14194 from open-webui/dev
0.6.11
2025-05-27 02:27:09 +04:00
Timothy Jaeryang Baek 5c74e56bd0 chore: format 2025-05-27 02:18:43 +04:00
Timothy Jaeryang Baek 8609fe7ab4 i18n: french 2025-05-27 02:18:02 +04:00
Tim Jaeryang Baek 1cb8fa0f03 Merge pull request #14362 from PVBLIC-F/fix/chat-engagement-critical
Fix/chat engagement critical
2025-05-27 02:17:34 +04:00
Timothy Jaeryang Baek a681d5c60e doc: changelog 2025-05-27 02:16:43 +04:00
cheadings71 256034e285 Update misc.py
Before fix: Chat engagement failed with TypeError and KeyError
After fix: Chat works smoothly with automatic title generation and proper history
2025-05-26 14:55:48 -07:00
cheadings71 d414662d23 fix: resolve chat engagement TypeError - Fix get_message_list() to return [] instead of None - Fix middleware to use correct metadata message_id - Add safe fallback for missing role field - Ensure assistant messages include role field 2025-05-26 14:35:09 -07:00
Timothy Jaeryang Baek 940a437631 refac 2025-05-27 01:16:11 +04:00
Timothy Jaeryang Baek aaff204e7b refac 2025-05-27 00:56:59 +04:00
Timothy Jaeryang Baek e68a3d28b5 refac 2025-05-27 00:40:55 +04:00
Timothy Jaeryang Baek 090619c27c refac 2025-05-27 00:29:16 +04:00
Timothy Jaeryang Baek 9c120affb6 chore: format 2025-05-27 00:21:23 +04:00
Timothy Jaeryang Baek 2c7ccc69fe enh: allow custom openapi json url 2025-05-27 00:20:47 +04:00
Timothy Jaeryang Baek a38e44e870 enh: external tool server custom name/description support 2025-05-27 00:10:33 +04:00
Timothy Jaeryang Baek 20f54616e7 chore: format 2025-05-26 23:55:39 +04:00
Timothy Jaeryang Baek b4caad928e feat: load function from url 2025-05-26 23:52:22 +04:00
Timothy Jaeryang Baek 7d756982c2 refac 2025-05-26 23:22:37 +04:00
Tim Jaeryang Baek 6062174602 Merge pull request #14228 from suleimanelkhoury/s3-tags-allowed-characters
fix: S3 allowed characters in Tags.
2025-05-26 22:43:21 +04:00
Timothy Jaeryang Baek 9a5febc2d1 feat: copy model link 2025-05-26 22:29:03 +04:00
Timothy Jaeryang Baek 2d5b82df8c enh: include sources field in non-streaming response 2025-05-26 22:22:37 +04:00
Timothy Jaeryang Baek ffa51ece0c refac: pinned chat endpoint 2025-05-26 22:15:21 +04:00
Timothy Jaeryang BaekandClassic298 a0d034fa7c feat: Bypass file creation with CTRL + SHIFT + V
Co-Authored-By: Classic298 <27028174+Classic298@users.noreply.github.com>
2025-05-26 22:10:35 +04:00
Timothy Jaeryang Baek fc5dfd3536 refac 2025-05-26 22:02:40 +04:00
Tim Jaeryang Baek 5d7c89964c Merge pull request #14314 from fl0w1nd/dev
fix: Correctly handle toggle filters to prevent unintended activation
2025-05-26 21:58:57 +04:00
Timothy Jaeryang Baek caa5ad44d4 refac 2025-05-26 19:53:29 +04:00
Tim Jaeryang Baek 040f29d058 Merge pull request #14316 from Yu-QX/main
fix: Handle special cases of LLM output not following markdown syntax
2025-05-26 19:30:48 +04:00
Yu QX bb62bd9c1f Extracting the traversal. 2025-05-26 21:04:08 +08:00
Yu QX b9f74a66b2 Seperate into processChineseContent 2025-05-26 20:43:21 +08:00
Yu QX 254dd4246e Integrate file into index.ts. 2025-05-26 19:35:28 +08:00
Timothy Jaeryang BaekandJason Baker 4da75a9e78 feat: GZip, Brotli, ZStd compression middleware support
Co-Authored-By: Jason Baker <jason.th.baker@gmail.com>
2025-05-26 14:18:29 +04:00
Yu QX 4483fa95b8 Perfect the logic of case 1.1.x 2025-05-26 17:25:33 +08:00
Tim Jaeryang Baek c157e74f0c Merge pull request #14335 from open-webui/main
dev
2025-05-26 13:02:08 +04:00
Tim Jaeryang Baek ada9655f33 Merge pull request #14317 from SadmL/patch-3
[i18n] Russian locale update
2025-05-26 13:01:39 +04:00
Tim Jaeryang Baek 9de92687f6 Merge pull request #14328 from ShirasawaSama/fix_path_leakage_caused_by_file_upload
fix: Fix path leakage caused by file upload
2025-05-26 12:57:21 +04:00
Tim Jaeryang Baek 31dbafd62e Merge pull request #14301 from qingchunnh/Update_zh-CN-25525
i18n: Update & Improve zh-CN
2025-05-26 12:56:14 +04:00
Shirasawa 0dc29a220f fix: Fix path leakage caused by file upload 2025-05-26 12:20:00 +08:00
Link [Связной] 7db9f7b8b6 Update translation.json 2025-05-25 14:53:03 +03:00
Link [Связной] dc50fa3744 [i18n] Russian locale update 2025-05-25 14:42:42 +03:00
YuQX 8ef7938c96 fix: Handle special cases of LLM output not following markdown syntax (20250525, formated) This commit introduces a utility function to address rendering issues in LLM output, particularly for Chinese characters and parentheses. The function ensures minimal modification of the original text while fixing markdown parsing problems. Changes include: - Added in for handling specific cases. - Updated in to incorporate the new utility. The fix ensures proper rendering of bold/italic text containing Chinese parentheses, improving readability for non-English content. 2025-05-25 19:37:43 +08:00
fl0w1nd 332043c38b fix: Correctly handle toggle filters to prevent unintended activation 2025-05-25 17:59:31 +08:00
YuQX 49fe137553 fix: Handle special cases of LLM output not following markdown syntax 20250525
This commit introduces a utility function  to address rendering issues in LLM output, particularly for Chinese characters and parentheses. The function ensures minimal modification of the original text while fixing markdown parsing problems.

Changes include:
- Added  in  for handling specific cases.
- Updated  in  to incorporate the new utility.

The fix ensures proper rendering of bold/italic text containing Chinese parentheses, improving readability for non-English content.
2025-05-25 13:58:32 +08:00
qingchun 6a93b54e8e i18n: Update & Improve zh-CN 2025-05-25 08:02:47 +08:00
Timothy Jaeryang Baek 8598fc9608 chore: format 2025-05-25 02:33:52 +04:00
Timothy Jaeryang Baek 5885d8e275 enh: ability to hide workspace models 2025-05-25 02:32:32 +04:00
Timothy Jaeryang Baek f9d374810d refac 2025-05-25 02:21:03 +04:00
Timothy Jaeryang Baek 358ad8ef20 enh: import/export default prompt suggestions 2025-05-25 02:17:51 +04:00
Timothy Jaeryang Baek 33f2fdb91e refac: styling 2025-05-25 02:07:13 +04:00
Timothy Jaeryang Baek e65ac97c45 refac 2025-05-25 01:59:48 +04:00
Timothy Jaeryang Baek b2b1a21299 refac 2025-05-25 01:48:37 +04:00
Timothy Jaeryang Baek 75208935d7 refac: user chat list modal 2025-05-25 01:44:53 +04:00
Timothy Jaeryang Baek 44e7e09784 refac 2025-05-25 01:28:35 +04:00
Timothy Jaeryang Baekandsilentoplayz 1d53754ea7 refac: chat list modal
Co-Authored-By: silentoplayz <50341825+silentoplayz@users.noreply.github.com>
2025-05-25 01:26:12 +04:00
Timothy Jaeryang Baek 6e8ca96799 enh: archived chats modal 2025-05-25 01:23:12 +04:00
Timothy Jaeryang Baek 5db7d8acd1 refac 2025-05-25 00:58:55 +04:00
Timothy Jaeryang Baek 0fe00ab2c3 refac 2025-05-25 00:52:53 +04:00
Timothy Jaeryang Baek 9b000d82ec refac 2025-05-25 00:51:44 +04:00
Timothy Jaeryang Baek 7e6f1f8848 enh: archived chats modal 2025-05-25 00:48:30 +04:00
Timothy Jaeryang Baek 31e2686ae6 feat: /sync functions endpoint 2025-05-24 23:39:19 +04:00
Timothy Jaeryang Baek cce5f024bd feat: WEBUI_AUTH_TRUSTED_GROUPS_HEADER 2025-05-24 23:17:12 +04:00
Tim Jaeryang Baek 17263a294f Merge pull request #14284 from diwakar-s-maurya/patch-7
feat: add support for table formatting in knowledge collection
2025-05-24 23:02:46 +04:00
Tim Jaeryang Baek 6dbab870e5 Merge pull request #14263 from qingchunnh/Update_zh-CN-25524
i18n: Update & Improve zh-CN
2025-05-24 22:59:47 +04:00
Timothy Jaeryang Baek 9598532270 enh/chore: bug report template 2025-05-24 22:56:54 +04:00
Diwakar Singh Maurya e31a7bcc4c feat: add support for table formatting in knowledge collection 2025-05-24 16:21:55 +00:00
Timothy Jaeryang Baek 261328077d refac 2025-05-24 17:20:40 +04:00
Timothy Jaeryang Baek aebbc2593b refac: scrollbar thumb styling 2025-05-24 17:19:47 +04:00
Tim Jaeryang Baek 8261f0e17e Merge pull request #14276 from Classic298/memories
feat: Make Edit/Add Memory Modal larger, and the text input box size-adjustable
2025-05-24 16:01:07 +04:00
Tim Jaeryang Baek 37d2aed26e Merge pull request #14268 from TiancongLx/dev
i18n: update zh-TW
2025-05-24 15:42:50 +04:00
Classic298 c377ff80c9 Update EditMemoryModal.svelte 2025-05-24 13:33:56 +02:00
Classic298 9bd031dae3 Update AddMemoryModal.svelte 2025-05-24 13:33:49 +02:00
Tiancong Li bc7853d6a4 i18n: update zh-TW 2025-05-24 13:38:32 +08:00
qingchun adefc811c7 i18n: Update & Improve zh-CN 2025-05-24 09:18:18 +08:00
qingchun 3c828f7828 i18n: Update & Improve zh-CN 2025-05-24 08:30:09 +08:00
Timothy Jaeryang Baek da75d0ca1e chore: format 2025-05-24 02:13:54 +04:00
Timothy Jaeryang Baek 51ab02f3af chore: format 2025-05-24 02:13:46 +04:00
Timothy Jaeryang Baek 29d08c2a16 refac: styling 2025-05-24 02:12:44 +04:00
Timothy Jaeryang Baek abcf59ab2b refac: styling 2025-05-24 01:56:02 +04:00
Timothy Jaeryang Baek ffa5db4fec refac: styling 2025-05-24 01:45:55 +04:00
Timothy Jaeryang Baek f95e52a6a6 refac: wording 2025-05-24 01:40:01 +04:00
Timothy Jaeryang Baek 17aca908c5 refac: styling 2025-05-24 01:36:53 +04:00
Tim Jaeryang Baek e663b90a9f Merge pull request #14069 from Ithanil/bm25_weight
feat: Configurable weight for BM25Retriever during hybrid search
2025-05-24 01:13:03 +04:00
Timothy Jaeryang Baek bf97dbe052 refac 2025-05-24 01:12:11 +04:00
Timothy Jaeryang Baek 80eaa24ca8 enh: provide source name in context if available 2025-05-24 00:52:09 +04:00
Timothy Jaeryang Baek 8b5e89eada chore: format 2025-05-24 00:43:38 +04:00
Timothy Jaeryang Baek a46765fd0e refac 2025-05-24 00:43:02 +04:00
Timothy Jaeryang Baek baaa285534 feat: user stt language 2025-05-24 00:36:30 +04:00
Jan Kessler a90d3f3268 add missing locale string 2025-05-23 22:30:04 +02:00
Jan Kessler e70dd33233 rename BM25_WEIGHT -> HYBRID_BM25_WEIGHT 2025-05-23 22:06:44 +02:00
Tim Jaeryang Baek 9946dc7b5f Merge pull request #14252 from Kylapaallikko/dev
i18n: Update fi-FI translation
2025-05-23 23:53:40 +04:00
Timothy Jaeryang Baek 88a296b989 refac: chat reset input behaviour 2025-05-23 23:52:10 +04:00
Kylapaallikko bf12946ac9 Update fi-FI translation.json 2025-05-23 22:12:00 +03:00
Tim Jaeryang Baek 99f88082ca Merge pull request #14248 from diwakar-s-maurya/patch-3
feat: Show tool call parameters along tool ouput
2025-05-23 22:55:08 +04:00
Timothy Jaeryang Baek 6636207e0c refac 2025-05-23 22:10:57 +04:00
Tim Jaeryang Baek c8f1bdf928 Merge pull request #14245 from PVBLIC-F/dev
perf Update mistral.py
2025-05-23 21:57:16 +04:00
Timothy Jaeryang Baek 3f2025dc6e enh: always process file with external document loader 2025-05-23 21:55:09 +04:00
Diwakar Singh Maurya 549237fb05 Show tool call parameters along tool ouput 2025-05-23 17:42:05 +00:00
Timothy Jaeryang Baek 797c7d4e56 refac: allow min_p for openai requests 2025-05-23 21:28:02 +04:00
PVBLIC Foundation bf193dfb5d Update mistral.py 2025-05-23 10:00:19 -07:00
Timothy Jaeryang Baek 30d15c1b4b chore: format 2025-05-23 20:44:41 +04:00
Timothy Jaeryang Baek b9afef85c4 refac: advanced params 2025-05-23 20:44:19 +04:00
Timothy Jaeryang Baek ed0bebc7ef enh: granular audio playback speed 2025-05-23 20:10:16 +04:00
Timothy Jaeryang Baek f75ac5cc01 refac: styling 2025-05-23 20:06:05 +04:00
Timothy Jaeryang Baek 1cf21d3fa2 feat: ollama unload model 2025-05-23 19:45:29 +04:00
Timothy Jaeryang Baek 19b69fcb66 chore: format 2025-05-23 19:22:08 +04:00
Timothy Jaeryang Baek e15ff8d9f6 refac 2025-05-23 19:15:57 +04:00
Timothy Jaeryang Baek 0e6f09a0a9 enh: ollama loaded model display 2025-05-23 19:13:18 +04:00
Timothy Jaeryang Baek a50a8e2ef9 refac: ollama ps 2025-05-23 18:47:50 +04:00
Timothy Jaeryang Baek 65d997a6c5 refac 2025-05-23 18:22:57 +04:00
Timothy Jaeryang Baek 62fbd6c91c refac 2025-05-23 18:17:35 +04:00
Timothy Jaeryang Baek 03288d9fca refac: redesign 2025-05-23 17:30:17 +04:00
Tim Jaeryang Baek 06aa59e2a5 Merge pull request #14219 from Yu-QX/main
Fix: Solve equation problems with Chinese symbols
2025-05-23 17:19:29 +04:00
Timothy Jaeryang Baek 02000b1a6c refac: include help items in user menu 2025-05-23 16:49:11 +04:00
Timothy Jaeryang Baek dd8e742c2d refac: tts details tags 2025-05-23 13:40:33 +04:00
Tim Jaeryang Baek 55a6fae9db Merge pull request #14210 from ShrootBuck/main
refac: improve prompt formatting for selected text in FloatingButtons
2025-05-23 13:14:27 +04:00
Tim Jaeryang Baek bdcd04a78c Merge pull request #14212 from qingchunnh/Update_zh-CN-25523
i18n: Update & Improve zh-CN
2025-05-23 13:13:19 +04:00
Suleiman Elkhoury e10d6ad79c Fix S3 allowed characters in Tags.
In Amazon S3 storage, only the following charaters allowed in Tagging "letters (a-z, A-Z), numbers (0-9), and spaces representable in UTF-8, and the following characters: + - = . _ : / @". Added a sanitizer function to clear tags before the put request.
2025-05-23 11:09:40 +02:00
Tim Jaeryang Baek 9c4ce0273f Merge pull request #14215 from qingchunnh/Update_zh-TW-25523
i18n: Update zh-TW
2025-05-23 13:09:23 +04:00
qingchun 3d2a26fe0b i18n: Update & Improve zh-CN 2025-05-23 16:54:20 +08:00
qingchun aa6648ba8a i18n: Update & Improve zh-CN 2025-05-23 15:46:03 +08:00
YuQX ec97fdf5d6 Fix: Solve equation problems with Chinese symbols by adding more symbols to the ALLOWED_SURROUNDING_CHARS 2025-05-23 15:06:37 +08:00
qingchun 4025c96d0f i18n: Update zh-TW 2025-05-23 12:39:45 +08:00
qingchun ed9a2c295c i18n: Update & Improve zh-CN 2025-05-23 12:34:30 +08:00
qingchun 28e44452dd i18n: Update & Improve zh-CN 2025-05-23 09:51:05 +08:00
Zayd Krunz 1d382b83c8 refac: improve prompt formatting for selected text in FloatingButtons 2025-05-23 01:16:47 +00:00
Timothy Jaeryang Baek a2f12db8d9 refac: memories 2025-05-23 03:26:14 +04:00
Timothy Jaeryang Baek 7071a03419 chore: format 2025-05-23 02:54:44 +04:00
Timothy Jaeryang Baek 7a593b63b2 fix: image generation with allowed file extensions 2025-05-23 02:53:08 +04:00
Timothy Jaeryang Baek 4c55ad71a7 fix: local/external models 2025-05-23 02:48:31 +04:00
Timothy Jaeryang Baek 1f632d3570 fix: remove leading dot for file extension check 2025-05-23 02:39:19 +04:00
2eca6f6414 feat: bypass web loader in web search
Co-Authored-By: Perry Li <peiyaoli@mail.nankai.edu.cn>
Co-Authored-By: WilliamGates <3852641+williamgateszhao@users.noreply.github.com>
2025-05-23 02:30:35 +04:00
Timothy Jaeryang Baek 0b7f927983 enh: sortable banners 2025-05-23 02:02:11 +04:00
Timothy Jaeryang Baek a68ec8822a enh: filter md image for task generations 2025-05-23 01:33:08 +04:00
Timothy Jaeryang BaekandTornike Gurgenidze aac25eac9e refac: reranker
Co-Authored-By: Tornike Gurgenidze <togurg14@freeuni.edu.ge>
2025-05-23 01:29:48 +04:00
Timothy Jaeryang Baek f2d2cae8fb enh: help menu 2025-05-23 01:16:08 +04:00
Timothy Jaeryang Baek 71288ea3f2 refac 2025-05-23 01:11:44 +04:00
Timothy Jaeryang Baek cbe84d7139 enh: token expiry buffer 2025-05-23 01:08:28 +04:00
Timothy Jaeryang Baek d4c3cfbaba refac: oauth redirect url to use WEBUI_URL 2025-05-23 01:03:28 +04:00
Tim Jaeryang Baek 336d0b45ad Merge pull request #14165 from Zyfax/patch-1
fix: image model list
2025-05-22 15:37:00 +04:00
Tim Jaeryang Baek da4aa5f08b Merge pull request #14152 from U8F69/fix_user_auth
fix(auth): correctly use password hash when duplicate email records exist
2025-05-22 14:58:10 +04:00
Zyfax 7489bc6126 fix: image model list
OpenAI image model added:
gpt-image-1

Gemini image model renamed:
id: imagen-3-0-generate-002 to imagen-3.0-generate-002
2025-05-22 11:07:46 +02:00
Tim Jaeryang Baek 4519e4eed1 Merge pull request #14158 from itk-dev/feature/4347-danishtranslations
i18n: Updated danish translations
2025-05-22 12:47:22 +04:00
Timothy Jaeryang Baek 787987d2f0 refac/fix: optional tool param type 2025-05-22 12:41:00 +04:00
Tim Jaeryang Baek 17ed331a0a Merge pull request #14126 from SadmL/patch-2
[i18n] Russian locale update
2025-05-22 12:38:01 +04:00
Tim Jaeryang Baek b339fd6fed Merge pull request #14136 from Vombit/main
[i18n] Fixed a typo in the translation
2025-05-22 12:37:31 +04:00
Tim Jaeryang Baek 0eda03bd3c Merge pull request #14147 from PVBLIC-F/dev
perf Update pinecone.py
2025-05-22 12:19:14 +04:00
Tim Jaeryang Baek a935e375f3 Merge pull request #14149 from silentoplayz/i18n-fix
fix: $i18n.t fix
2025-05-22 11:49:09 +04:00
U8F69 dd6124a84f fix(auth): fix invalid password use in auth 2025-05-22 11:03:43 +08:00
Silentoplayz 558b2b1016 Update General.svelte 2025-05-21 20:41:19 -04:00
PVBLIC Foundation 86e24bb4aa Update pinecone.py
I've improved the pinecone.py file by:
Updated from the deprecated PineconeGRPC client to the newer Pinecone client
Modified the client initialization code to match the new API requirements
Added better response handling with getattr() to safely access attributes from response objects
Removed the streaming_upsert method which is not available in the newer client
Added safer attribute access with fallbacks throughout the code
Updated the close method to reflect that the newer client doesn't need explicit closing
These changes ensure the code is compatible with the latest Pinecone Python SDK and will be more robust against future changes. The key improvement is migrating away from the deprecated gRPC client which will eventually stop working.
2025-05-21 15:28:42 -07:00
Vombit ff46fa5b33 fix: fixed a typo in the translation 2025-05-21 21:15:42 +05:00
lilosti bd40cceb2d Updated danish translations 2025-05-21 14:52:33 +02:00
Link [Связной] 9201fd4652 [i18n] Russian locale update 2025-05-21 14:48:57 +03:00
Tim Jaeryang Baek 65a44a3900 Merge pull request #14099 from Classic298/ShiftHoldingFix
fix: Holding Shift Bug fixed
2025-05-21 13:46:58 +04:00
Tim Jaeryang Baek d2e426990b Merge pull request #14110 from qingchunnh/Update_zh-CN-25521
i18n: update and improve zh-CN
2025-05-21 13:43:00 +04:00
qingchun ed0538e643 i18n: update and improve zh-CN 2025-05-21 07:34:07 +08:00
Timothy Jaeryang Baek e3e7eb96f6 refac 2025-05-20 23:55:11 +04:00
Tim Jaeryang Baek a45f2cd0a9 Merge pull request #14104 from bassettb/bryan/parse-db-user-chars
fix: handle escaped characters in DATABASE_URL username
2025-05-20 23:51:41 +04:00
Timothy Jaeryang Baek 6121d4696a enh: search input arrow controls 2025-05-20 23:47:41 +04:00
Timothy Jaeryang Baek b78354100f refac 2025-05-20 23:33:41 +04:00
Timothy Jaeryang Baek 512345f40e refac: chat transition 2025-05-20 23:33:23 +04:00
Bryan Bassett 12896fb728 use unquote_user in peewee 3.17.10 2025-05-20 15:18:32 -04:00
DrMelone 8b80555adf fix attempt #1 2025-05-20 20:03:21 +02:00
Tim Jaeryang Baek 4e4b5ab83b Merge pull request #14096 from silentoplayz/add-clear-button-to-remaining-pages
feat: Add Clear button to Functions and Models pages search bar
2025-05-20 21:30:07 +04:00
Silentoplayz 03eeb75bfe feat: Add Clear button for Function and admin Model pages 2025-05-20 12:21:32 -04:00
Timothy Jaeryang Baek 74ace200fe fix/refac: functions multi-replica issue 2025-05-20 20:20:27 +04:00
Tim Jaeryang Baek 2b5c015bcb Merge pull request #14093 from open-webui/main
dev
2025-05-20 19:48:26 +04:00
Timothy Jaeryang Baek 82716f3789 refac 2025-05-20 19:39:18 +04:00
Timothy Jaeryang Baek a8a9b35153 refac: file item modal 2025-05-20 19:29:31 +04:00
Tim Jaeryang Baek d3c7628092 Merge pull request #14059 from sreesdas/main
fix: resolve issue where external document loader was not invoked
2025-05-20 17:43:06 +04:00
Tim Jaeryang Baek 86f1aee20b Merge pull request #14054 from silentoplayz/delete-lightblub
fix: Correct typo in LightBulb component name and imports & delete lightblub.svelte (duplicate of lightbulb.svelte)
2025-05-20 17:25:42 +04:00
Tim Jaeryang Baek f9a66afe76 Merge pull request #14072 from torisetxd/alignment-fix
fix: Alignment issues with Text in Buttons in UI
2025-05-20 17:24:49 +04:00
Tim Jaeryang Baek fac5884d8c Merge pull request #14073 from tth37/fix_default_web_loader_verify_ssl
fix: Default web loader fail silently when `verify_ssl=False`
2025-05-20 17:24:22 +04:00
Tim Jaeryang Baek 6035664048 Merge pull request #14051 from Classic298/german-translation
i18n: German translation overhaul
2025-05-20 17:23:55 +04:00
Tim Jaeryang Baek 75a31e116a Merge pull request #14049 from mjmendo/web_search_optional_fields
fix: web search optional fields
2025-05-20 17:22:52 +04:00
tth37 78befd5a2f fix: Default web loader fail when verify_ssl=False 2025-05-20 19:44:18 +08:00
toriset b735042ecc Fix alignment issues with Buttons in UI 2025-05-20 14:33:04 +03:00
Jan Kessler 308d8ac04a make bm25_weight a regular parameter of query_doc.. / get_sources_from_files functions 2025-05-20 11:46:32 +02:00
Jan Kessler b5ddaf6417 make weight for bm25 retriever in hybrid search ui-configurable 2025-05-20 10:39:31 +02:00
Classic298 8d94a59f27 task models 2025-05-20 08:39:36 +02:00
Classic298 479b5ad820 fixes after feedback 2025-05-20 08:31:49 +02:00
sree f408b08965 minor bug fix for external document loader not working 2025-05-20 11:10:23 +05:30
Derek Wischusen 42be1f956a Add Azure OpenAI embedding support 2025-05-19 22:58:04 -04:00
Silentoplayz ab9486603c chore: fix on-hover visual 2025-05-19 22:42:15 -04:00
Silentoplayz 58b309149a chore: adjustment 2025-05-19 17:21:43 -04:00
Silentoplayz 83ed2d0661 chore: delete lightblub.svelte 2025-05-19 17:12:36 -04:00
DrMelone 4b538047dd various i18n fixes
typos, incomplete sentences fixed, wrong translations, inconsistencies fixed
2025-05-19 19:35:08 +02:00
DrMelone fd2ff5e777 i18n german update 1 2025-05-19 19:28:12 +02:00
Marcelo Mendoza 897727c7eb Merge branch 'open-webui:main' into web_search_optional_fields 2025-05-19 17:25:14 +02:00
Marcelo Mendoza d6ad96affb fix: use get method for title and snippet in search results 2025-05-19 17:24:47 +02:00
Tim Jaeryang Baek 5613cda5cf Merge pull request #14048 from open-webui/main
dev
2025-05-19 19:17:39 +04:00
Timothy Jaeryang Baek ed0659aca6 refac 2025-05-19 19:17:08 +04:00
Timothy Jaeryang Baek fd91b277a4 refac: pypi 2025-05-19 14:48:55 +04:00
Timothy Jaeryang Baek d2758c7fa2 refac: pypi 2025-05-19 14:35:51 +04:00
Tim Jaeryang Baek ef3232a24f Merge pull request #14023 from TiancongLx/dev
i18n: update zh-TW
2025-05-19 14:22:31 +04:00
Tim Jaeryang Baek 438eb90671 Merge pull request #14024 from hurxxxx/i18n/update-ko-KR
i18n: update ko-KR
2025-05-19 14:22:13 +04:00
Tim Jaeryang Baek c9bbfaee9d Merge pull request #14033 from SadmL/patch-2
[i18n] Russian locale update
2025-05-19 14:22:03 +04:00
Link [Связной] 1d0921de0d [i18n] Russian locale update 2025-05-19 11:24:08 +03:00
hurxxxx c1066d7e08 i18n: update ko-KR 2025-05-19 13:28:45 +09:00
Tiancong Li 23991c32c5 i18n: update zh-TW 2025-05-19 12:09:07 +08:00
Tim Jaeryang Baek e6afa69f59 Merge pull request #13887 from open-webui/dev
0.6.10
2025-05-19 05:34:23 +04:00
Timothy Jaeryang Baek 2b1c3b5208 Update CHANGELOG.md 2025-05-19 05:27:29 +04:00
Timothy Jaeryang Baek b905b73935 Update CHANGELOG.md 2025-05-19 05:24:03 +04:00
Timothy Jaeryang Baek fa5cd59ef8 doc: changelog 2025-05-19 05:20:57 +04:00
Timothy Jaeryang Baek 030c20feff refac 2025-05-19 05:12:53 +04:00
Tim Jaeryang Baek 8fb7b980a2 Merge pull request #14019 from silentoplayz/clear
feat: Improve remaining export buttons with total item count
2025-05-19 04:33:34 +04:00
Timothy Jaeryang Baek eafb8e014a chore: format 2025-05-19 04:32:00 +04:00
Timothy Jaeryang Baek 2ab5aa4d34 refac: azure openai 2025-05-19 04:31:04 +04:00
Silentoplayz 5cb1475e29 fix: apply change to functions and models pages 2025-05-18 20:12:53 -04:00
Timothy Jaeryang Baek 2f2a7ee0b0 chore: format 2025-05-19 03:58:42 +04:00
Timothy Jaeryang Baek 2e56b1f13d refac 2025-05-19 03:55:56 +04:00
Timothy Jaeryang Baek caeb822cdc feat: azure openai support 2025-05-19 03:40:32 +04:00
Timothy Jaeryang Baek 47f8b3500b feat: azure openai connections ui 2025-05-19 03:29:14 +04:00
Timothy Jaeryang Baek 73e64fe7fb refac: audio upload handling 2025-05-19 02:52:48 +04:00
Timothy Jaeryang Baekandsilentoplayz aee9d05916 enh: workspace search clear button
Co-Authored-By: silentoplayz <50341825+silentoplayz@users.noreply.github.com>
2025-05-19 02:36:15 +04:00
Timothy Jaeryang Baek 3af289d196 refac 2025-05-19 02:32:06 +04:00
Timothy Jaeryang Baek 9f9fb3403d refac 2025-05-19 02:07:41 +04:00
Timothy Jaeryang Baek c6580a2a02 refac 2025-05-19 02:01:07 +04:00
Timothy Jaeryang Baek 417ba4980a chore: format 2025-05-19 01:55:02 +04:00
Timothy Jaeryang Baek 0c6f22b20b refac 2025-05-19 01:54:26 +04:00
Timothy Jaeryang Baek 96c7a0ac3c refac 2025-05-19 01:52:40 +04:00
Timothy Jaeryang Baek e895c33baa refac 2025-05-19 01:47:12 +04:00
Timothy Jaeryang Baek fbfd3ff134 refac 2025-05-19 01:44:27 +04:00
Timothy Jaeryang Baek 5a40feea1c enh: search shortcut 2025-05-19 01:40:51 +04:00
Timothy Jaeryang Baek 1f5860c4ab refac 2025-05-19 01:40:10 +04:00
Timothy Jaeryang Baek d069d1264d feat: search modal 2025-05-19 01:39:33 +04:00
Tim Jaeryang Baek 4f4811adb1 Merge pull request #14017 from athoik/title_tags
feat: read max_tokens from model config with fallback to 1000 for title and tag generation
2025-05-19 00:43:05 +04:00
Timothy Jaeryang Baek 05b79ce221 refac: model selection 2025-05-19 00:41:49 +04:00
Timothy Jaeryang Baekandsilentoplayz 18d6311951 feat: export count
Co-Authored-By: silentoplayz <50341825+silentoplayz@users.noreply.github.com>
2025-05-19 00:15:07 +04:00
Timothy Jaeryang Baek 8f4104fb7a refac 2025-05-19 00:13:03 +04:00
Tim Jaeryang Baek 349b0ca0b7 Merge pull request #14002 from silentoplayz/search-by-user-or-email
feat: enhance search with username/email search across workspace pages
2025-05-19 00:12:11 +04:00
Tim Jaeryang Baek 2dde4102ad Merge pull request #14009 from TiancongLx/dev
i18n: update and improve zh-TW
2025-05-19 00:11:32 +04:00
Athanasios Oikonomou eabdd4a140 feat: read max_tokens from model config with fallback to 1000 for title and tag generation
Improves title and tag generation by using the max_tokens value from the model configuration when available, with a fallback to the previous default of 1000.

This change is necessary for models like Gemini Pro that generate longer responses and require a higher token limit to successfully generate titles or tags.
2025-05-18 22:45:33 +03:00
Tiancong Li 4667328d40 i18n: update zh-TW 2025-05-18 21:16:16 +08:00
Tim Jaeryang Baek 56740ab8d6 Merge pull request #13996 from Davixk/update-IT-translation
i18n: add and improve Italian translations
2025-05-18 15:04:54 +04:00
Silentoplayz 95badda1c2 feat: username and email search in workspace pages 2025-05-18 03:23:38 -04:00
Dave ebf2e4141e Merge remote-tracking branch 'upstream/dev' into update-IT-translation 2025-05-18 04:46:41 +02:00
Dave a8813c1a69 i18n: Update and Improve Italian translations 2025-05-18 04:36:12 +02:00
Tim Jaeryang Baek fbcc80485b Merge pull request #13972 from jarrod-lowe/otel-attrs
feat: Add user details to opentelemetry spans
2025-05-17 21:54:56 +04:00
Timothy Jaeryang Baek 765acc4cad chore: dep bump 2025-05-17 21:53:31 +04:00
Timothy Jaeryang Baek 9801a2d23f refac 2025-05-17 03:14:26 +04:00
Timothy Jaeryang Baek 424f6f71cf chore: duckduckgo-search bump 2025-05-17 03:03:07 +04:00
Timothy Jaeryang Baek b280f828b0 enh: very long audio transcription 2025-05-17 02:51:28 +04:00
Timothy Jaeryang Baek d54e588ec3 refac 2025-05-17 02:26:28 +04:00
Timothy Jaeryang Baek 08e4c163ea feat: local/external connections 2025-05-17 01:47:48 +04:00
Timothy Jaeryang Baek 000906cd99 chore: format 2025-05-17 01:13:36 +04:00
Timothy Jaeryang Baek c3acb30bb1 feat: model file upload capability 2025-05-17 01:13:13 +04:00
Jarrod Lowe df853246f3 Add user details to otel span 2025-05-17 09:11:26 +12:00
Timothy Jaeryang Baek 6692fb2181 chore: format 2025-05-17 01:00:37 +04:00
Timothy Jaeryang Baek b14398481d enh: model capabilities 2025-05-17 00:59:00 +04:00
Timothy Jaeryang Baek 7df6d7f325 refac/fix: signout redirect flow 2025-05-17 00:38:39 +04:00
Timothy Jaeryang Baek fcb93f3155 feat: artifacts download button 2025-05-17 00:27:20 +04:00
Timothy Jaeryang Baek 02e5bbc903 refac 2025-05-17 00:19:08 +04:00
Timothy Jaeryang Baek 359bcb837d refac 2025-05-17 00:08:03 +04:00
Timothy Jaeryang Baek ecae944fd0 refac 2025-05-16 23:59:24 +04:00
Timothy Jaeryang Baek 05f284d4a6 refac 2025-05-16 23:41:51 +04:00
Timothy Jaeryang Baek ea723374c7 refac 2025-05-16 23:36:33 +04:00
Timothy Jaeryang Baek 1f38350128 feat: toggle filter middleware 2025-05-16 23:33:02 +04:00
Timothy Jaeryang Baek e37433f2b1 refac 2025-05-16 23:21:08 +04:00
Timothy Jaeryang Baek b61efcf54a feat: toggle filter 2025-05-16 22:59:02 +04:00
Timothy Jaeryang Baek 32b386e8d0 refac 2025-05-16 22:44:47 +04:00
Timothy Jaeryang Baek 7bbeaec93b revert 2025-05-16 22:43:42 +04:00
Timothy Jaeryang Baek 34ef249a10 revert: remove reasoning capability 2025-05-16 22:24:23 +04:00
Timothy Jaeryang Baek 363ac35c09 refac 2025-05-16 22:17:47 +04:00
Tim Jaeryang Baek 50bc6d9b12 Merge pull request #13919 from dongfangzan/main
feat: add switching thinking/non-thinking modes to models
2025-05-16 22:08:04 +04:00
Timothy Jaeryang Baek 27bb5f5369 fix/refac: tts remove formattings 2025-05-16 21:51:29 +04:00
Timothy Jaeryang Baek 54dc24986f feat: preview html 2025-05-16 21:47:43 +04:00
Timothy Jaeryang Baek 07b5e84221 refac 2025-05-16 21:29:50 +04:00
Timothy Jaeryang Baek 2bd7db12a2 enh: ALLOWED_FILE_EXTENSIONS ui 2025-05-16 21:05:52 +04:00
Timothy Jaeryang Baek 528a2cf96a refac 2025-05-16 18:19:28 +04:00
Timothy Jaeryang Baek 2aa5e73728 refac: filter details 2025-05-16 18:01:55 +04:00
Timothy Jaeryang Baek dec9dd1ac0 refac: web search queries 2025-05-16 17:11:42 +04:00
Timothy Jaeryang Baek bb06df80a2 refac 2025-05-16 17:07:52 +04:00
Timothy Jaeryang Baek 0b761776c4 enh: high contrast mode 2025-05-16 17:06:21 +04:00
Timothy Jaeryang Baek 2450f2c5d8 chore: format 2025-05-16 16:57:30 +04:00
Timothy Jaeryang Baek 62180b15e1 refac: command arrow up/down behaviour 2025-05-16 15:39:26 +04:00
Tim Jaeryang Baek c201b4d689 Merge pull request #13931 from itk-dev/feature/better-frontend-error
WIP: Minor clean up of console log usage in the front end
2025-05-16 13:33:16 +04:00
Tim Jaeryang Baek bc66755fb0 Merge pull request #13950 from uryukikutani/feature/screen-reader
fix: made the chat response messages readable with a screen reader
2025-05-16 13:32:40 +04:00
Tim Jaeryang Baek e74c06d2f5 Merge pull request #13929 from mini-777/feat--Korean-translations-for-clarity-and-accuracy
i18n : korean translations for clarity and accuracy
2025-05-16 13:32:10 +04:00
uryukikutani 53dcaa63d4 made the chat response messages readable with a screen reader 2025-05-16 17:41:56 +09:00
Tim Jaeryang Baek 186a89bcba Merge pull request #13937 from tanerim/patch-1
Update translation.json
2025-05-16 10:23:47 +04:00
dongfangzan 351f0186b9 fix: typo 2025-05-16 08:05:49 +08:00
Taner Sezer 21e96247a9 Update translation.json
Added new translations
2025-05-16 01:19:53 +03:00
Jesper Kristensen 84e0605835 Cleaning up usage of console log in front end 2025-05-15 21:53:07 +02:00
Jesper Kristensen 20c1c3f66b Changed console logging of errors to use console error logging 2025-05-15 21:35:07 +02:00
mini-777 2a4fe1335e i18n: update Korean translations with new entries and corrections 2025-05-16 02:45:19 +09:00
mini-777 e24e22debe Merge commit 'e4179aa114fecc8d35ba48f091ce7c9abcc96065' into HEAD 2025-05-16 02:10:01 +09:00
mini-777 208b86ae53 feat: add new notes feature with editor and list components, and update routing for notes management 2025-05-16 02:06:36 +09:00
Tim Jaeryang Baek e4179aa114 Merge pull request #13922 from TiancongLx/dev
i18n: update zh-TW
2025-05-15 19:02:37 +04:00
Tiancong Li df5ef8e553 i18n: update zh-TW 2025-05-15 21:15:22 +08:00
dongfangzan 9b93b81f84 feat: add switching thinking/non-thinking modes to models 2025-05-15 19:56:10 +08:00
dongfangzan a7c90925ef test contribute 2025-05-15 19:31:40 +08:00
Tim Jaeryang Baek ca2cfb5bad Merge pull request #13899 from loitragg/apply-multitenant-for-qdrant
perf: implement multi-tenancy for Qdrant vector store client
2025-05-15 13:37:36 +04:00
Kiet Trinh 418ac1a8da refac: Rename Qdrant multi-tenancy variable for improved clarity and consistency 2025-05-15 09:09:24 +00:00
Tim Jaeryang Baek db24af6356 Merge pull request #13902 from yo-nagase/dev
i18n: Update Japanese translation
2025-05-15 12:59:17 +04:00
Timothy Jaeryang Baek 72b2555953 refac 2025-05-15 12:58:44 +04:00
Kiet Trinh 485bd7666c fix: Update Qdrant multi-tenancy variable name for consistency in configuration 2025-05-15 08:02:58 +00:00
Nagase Yoshimasa 6eb772c741 i18n: Update Japanese translation 2025-05-15 15:41:04 +09:00
LoiTra 184d8dfd7e feat: Implement Qdrant multi-tenancy support with collection management and tenant isolation 2025-05-15 11:28:06 +07:00
Timothy Jaeryang Baek 0a8cecfbfa feat: response watermark 2025-05-14 23:53:28 +04:00
Timothy Jaeryang Baek b143c71da2 refac: AIOHTTP_CLIENT_SESSION_SSL 2025-05-14 23:33:52 +04:00
Timothy Jaeryang Baek 04287eb6d6 refac 2025-05-14 23:27:34 +04:00
Timothy Jaeryang Baek 39b06dc3b6 refac 2025-05-14 23:13:06 +04:00
Tim Jaeryang Baek e358a66fb1 Merge pull request #13852 from fizyr-forks/self-signed-cert
feat: Add option to disable certificate validation for LDAP certificates.
2025-05-14 23:12:24 +04:00
Timothy Jaeryang Baek a6624a4b16 feat: RAG_ALLOWED_FILE_EXTENSIONS 2025-05-14 23:06:33 +04:00
Timothy Jaeryang Baek 7e241a3904 refac 2025-05-14 22:48:23 +04:00
Timothy Jaeryang Baek 42382b5167 fix 2025-05-14 22:46:01 +04:00
Timothy Jaeryang Baek 8732b64b6b feat: external document loader support 2025-05-14 22:28:40 +04:00
Timothy Jaeryang Baek 32ea31144e refac: file upload handling behaviour 2025-05-14 22:00:42 +04:00
Timothy Jaeryang Baek b80f0751f6 refac 2025-05-14 21:59:17 +04:00
Timothy Jaeryang Baek 0280ee30c7 chore: format 2025-05-14 21:56:22 +04:00
Timothy Jaeryang Baek 8fe4f789d4 fix 2025-05-14 21:53:46 +04:00
Timothy Jaeryang Baek dacf7e015d i18n: revert 2025-05-14 21:52:42 +04:00
Timothy Jaeryang Baek ac83568194 chore: format 2025-05-14 21:40:51 +04:00
Timothy Jaeryang Baek 10108fcd04 chore: format 2025-05-14 21:39:50 +04:00
Timothy Jaeryang Baek 74ccec2812 refac 2025-05-14 21:39:17 +04:00
Tim Jaeryang Baek 29ae231ee0 Merge pull request #13889 from Classic298/pendingtext
feat: Custom Title and Text for Account Activation Screen for pending users
2025-05-14 21:28:16 +04:00
Timothy Jaeryang Baek de70d0cb64 feat: docling do picture description support 2025-05-14 21:26:49 +04:00
DrMelone 3974e53cc3 i18n 2025-05-14 19:11:53 +02:00
DrMelone 92140118f2 revert i18n 2025-05-14 19:07:45 +02:00
DrMelone e9eb661b83 fix + i18n 2025-05-14 18:58:54 +02:00
DrMelone bdb3778f8b fix 2025-05-14 18:10:23 +02:00
Timothy Jaeryang Baek 6e8481e157 fix: evaluation feedback endpoints 2025-05-14 20:08:49 +04:00
DrMelone 143cb4cc34 feat custom text and title 2025-05-14 17:58:20 +02:00
Tim Jaeryang Baek 2217fc4fd9 Merge pull request #13883 from alpha-pet/fix-workspace-model-unintended-toggle
Fix: Unintentional toggling of model status on the workspace page.
2025-05-14 18:09:16 +04:00
Tim Jaeryang Baek 8029bf0dca Merge pull request #13881 from hwzhuhao/feat/vector-db
feat: Add vector type and vector factory class for vector database integration
2025-05-14 17:41:39 +04:00
Thomas Rehn 6b6ce36c96 fix: add unique key to model list to avoid state confusion when filtering 2025-05-14 15:33:32 +02:00
hwzhuhao 6f869ded43 feat:Add vector type and vector factory class for vector database integration 2025-05-14 21:30:50 +08:00
Tim Jaeryang Baek 4959e91fa7 Merge pull request #13832 from Ithanil/svelte_versioning
feat: Handle frontend updates according to Svelte docs
2025-05-14 17:19:32 +04:00
Tim Jaeryang Baek f77a076c16 Merge pull request #13882 from itk-dev/feature/enable_direct_connections
chore: Added missing "enable_direct_connections" to config type
2025-05-14 17:18:04 +04:00
Jesper Kristensen 8b194bc781 Added missing "enable_direct_connections" to config type 2025-05-14 14:53:36 +02:00
Tim Jaeryang Baek bd7d2978f1 Merge pull request #13846 from hary777/patch-2
i18n update translation.json fix typo "amin" in cs-CZ
2025-05-14 15:27:40 +04:00
Tim Jaeryang Baek c7e3ae9f39 Merge pull request #13812 from hwzhuhao/refactor/log
refactor: replace print statements with logging
2025-05-14 15:27:13 +04:00
Timothy Jaeryang Baek 92a8ea743a refac: form data edge case 2025-05-14 15:22:38 +04:00
Timothy Jaeryang Baek 3c4accaeb3 refac/security: ollama model upload path traversal 2025-05-14 15:17:07 +04:00
Timothy Jaeryang Baek a9c22bffc9 refac/security: pipelines upload path traversal 2025-05-14 15:15:05 +04:00
Tim Jaeryang Baek d89e37cc7c Merge pull request #13866 from itk-dev/feature/contrast-chat-input
feat: Contrast enhancements
2025-05-14 15:12:00 +04:00
Sine Jespersen 8eacd26573 add placeholder dark mode 2025-05-14 12:50:47 +02:00
Sine Jespersen 90bcf45b7b placeholder contrast changed 2025-05-14 10:24:09 +02:00
Yeyeong Cho bbb4e75df8 Merge pull request #2 from mini-777/i18n-ko-rewriting
feat(openwebui) : improve Korean translations for better readability Fixes mini-777/8dobibim#2
2025-05-14 17:23:48 +09:00
Sine Jespersen b052b5cced remove whitespace 2025-05-14 10:15:31 +02:00
Sine Jespersen 5db783d67b improve suggestions contrast 2025-05-14 09:52:44 +02:00
Sine Jespersen 43d0db5eea model selector contrast 2025-05-14 09:52:10 +02:00
Sine Jespersen 41f64da0d6 update message input contrast 2025-05-14 09:51:38 +02:00
Sine Jespersen 5cd0b99ae0 update toast contrast 2025-05-14 09:51:07 +02:00
mini-777 571d078a82 fix: improve Korean translations for better readability (fixes mini-777/8dobibim#2) 2025-05-14 15:37:59 +09:00
mini-777 f72fa9b269 fix: update Korean translations for clarity and accuracy 2025-05-14 15:28:46 +09:00
Hans Gaiser f63e76e653 Add option to disable certificate validation. 2025-05-13 17:38:06 +02:00
hary777 66d73cb1de Update translation.json fix typo
Fix typo "amin". I can not look at this anymore.
2025-05-13 15:24:48 +02:00
Tim Jaeryang Baek 950f3cdcc0 Merge pull request #13838 from TiancongLx/dev
i18n: update, complete, and optimize zh-TW
2025-05-13 16:53:52 +04:00
Tim Jaeryang Baek f907e7c979 Merge pull request #13836 from itk-dev/feature/build-watch
build: Added build:watch command to package.json
2025-05-13 16:53:24 +04:00
Jesper Kristensen 24c1b8911c Added build:watch script/command to package.json
Should make it eaiser to automatically build the front-end when
doing local development in docker node containers.
2025-05-13 10:36:48 +02:00
Tiancong Li b41fc1d2f4 i18n: update zh-TW 2025-05-13 16:30:59 +08:00
Jan Kessler dfd4037132 better handling of frontend updates, following svelte docs 2025-05-13 09:51:51 +02:00
Timothy Jaeryang Baek a904f874ee fix: admin signup logic 2025-05-12 20:03:40 +04:00
hwzhuhao fb2950201a refactor: replace print statements with logging 2025-05-12 22:18:47 +08:00
Timothy Jaeryang Baek e9b799676b chore: format 2025-05-12 17:34:33 +04:00
Tim Jaeryang Baek c7f743ddb7 Merge pull request #13810 from phpia/phpia-patch-1
i18n es
2025-05-12 17:31:18 +04:00
phpia 16a9210321 Update translation for mantain prompt
This pull request updates the Spanish localization by replacing all instances of "indicador"/"indicadores" with the more accurate terms "prompt"/"prompts" throughout the UI, documentation, and messages. 

Reason and Rationale: 

In the context of AI and Large Language Models (LLMs), the words "prompt" and "prompts" have become standardized terms worldwide, including within the Spanish-speaking technical community. While "indicador" is a valid literal translation of "prompt" in general Spanish, in generative AI contexts it is potentially confusing, since "indicador" more commonly refers to a gauge, dashboard indicator, or measuring device. This can cause misunderstanding for users, especially those familiar with AI concepts, who expect the term "prompt" to refer to the input text given to a model.
2025-05-12 14:59:15 +02:00
Sung Min c049837a11 Merge pull request #1 from mini-777/i18n-ko-missing
feat(openwebui) : add missing Korean translation strings Fixes mini-777/8dobibim#1
2025-05-12 20:55:14 +09:00
Tim Jaeryang Baek ca34ba318e Merge pull request #13801 from aleixdorca/dev
i18n: Update catalan translation.json
2025-05-12 14:56:19 +04:00
Tim Jaeryang Baek 0d8e24320e Merge pull request #13794 from Lutherwaves/dev
i18n: Bulgarian translations
2025-05-12 14:55:10 +04:00
Aleix Dorca b7ef9a03ac Update catalan translation.json 2025-05-12 08:56:52 +02:00
Tim Jaeryang Baek 4ce1e88750 Merge pull request #13795 from open-webui/dev
refac
2025-05-12 01:10:34 +04:00
Timothy Jaeryang Baek f32b042a00 refac 2025-05-12 01:08:49 +04:00
lutherwaves 792beb095e i18n: Bulgarian translations 2025-05-11 23:58:31 +03:00
Timothy Jaeryang Baek 4c06683e95 refac: Vazirmatn 2025-05-12 00:05:04 +04:00
Tim Jaeryang Baek 0cef844168 Merge pull request #13758 from open-webui/dev
0.6.9
2025-05-10 23:04:48 +04:00
Timothy Jaeryang Baek c56d6963b0 doc: changelog 2025-05-10 23:01:48 +04:00
Timothy Jaeryang Baek 92e2cfdd2f refac 2025-05-10 23:00:53 +04:00
Timothy Jaeryang Baek 3ad5a7d97c chore: bump 2025-05-10 22:48:26 +04:00
Timothy Jaeryang Baek 867fddb821 chore: format 2025-05-10 22:45:58 +04:00
Timothy Jaeryang Baek de69c08a1d enh: alert private task model 2025-05-10 22:43:53 +04:00
Timothy Jaeryang Baek 30cc51ec82 feat: edit message files 2025-05-10 22:27:39 +04:00
Timothy Jaeryang Baek ba69c751ca refac: temporary chat behaviour 2025-05-10 22:12:36 +04:00
Timothy Jaeryang Baek be85f7cc35 fix: confirm dialog focus 2025-05-10 22:09:05 +04:00
Tim Jaeryang Baek 4191ec692e Merge pull request #13751 from open-webui/dev
fix: external reranker
2025-05-10 19:33:59 +04:00
Timothy Jaeryang Baek 6b5f99bf66 fix: external reranker 2025-05-10 19:33:34 +04:00
Tim Jaeryang Baek ef301aa16b Merge pull request #13707 from open-webui/dev
0.6.8
2025-05-10 19:31:52 +04:00
Timothy Jaeryang Baek 32b27749ef Update CHANGELOG.md 2025-05-10 19:29:02 +04:00
Timothy Jaeryang Baek b81a5833c7 doc: changelog 2025-05-10 19:25:42 +04:00
Timothy Jaeryang Baek 6f635d8b7d refac 2025-05-10 19:16:09 +04:00
Timothy Jaeryang Baek 549989e9ec refac 2025-05-10 19:04:40 +04:00
Timothy Jaeryang Baek 1c665592e4 chore: bump 2025-05-10 19:01:12 +04:00
Timothy Jaeryang Baek c61790b355 chore: format 2025-05-10 19:00:01 +04:00
Timothy Jaeryang Baek 3a79840a87 feat: unstylized pdf export 2025-05-10 18:59:04 +04:00
Timothy Jaeryang Baek a515a5df1a refac 2025-05-10 18:38:30 +04:00
Timothy Jaeryang Baek ba72d4625f refac 2025-05-10 18:36:45 +04:00
Timothy Jaeryang Baek 3dc34c2402 feat: external reranker settings ui 2025-05-10 18:33:52 +04:00
Timothy Jaeryang Baek be912f1529 refac 2025-05-10 18:29:04 +04:00
Timothy Jaeryang BaekandBrendan Campbell d5fd3b3600 feat: external reranker
Co-Authored-By: Brendan Campbell <20541191+bcambs09@users.noreply.github.com>
2025-05-10 18:25:20 +04:00
Timothy Jaeryang BaekandMabeck 34ec10a78c refac: web search performance
Co-Authored-By: Mabeck <64421281+mmabeck@users.noreply.github.com>
2025-05-10 17:54:41 +04:00
Timothy Jaeryang Baek 015ac2f532 refac: user list loading indicator 2025-05-10 17:23:17 +04:00
Tim Jaeryang Baek 030b6b26af Merge pull request #13735 from Classic298/dev
Fix: Markdown in Knowledgebases rendered correctly now
2025-05-10 17:13:16 +04:00
Tim Jaeryang Baek 6d1c18e118 Merge pull request #13736 from taylorwilsdon/ideas/13702
enh: Improve visibility of overflow / hidden tools when more than 3 are present
2025-05-10 17:10:45 +04:00
Tim Jaeryang Baek 54dda08d39 Merge pull request #13712 from PVBLIC-F/dev
perf - Pinecone.py
2025-05-10 17:10:08 +04:00
Tim Jaeryang Baek 7bfe4c6a43 Merge pull request #13738 from dataway/dev
fix: allow non-root jupyter base_url
2025-05-10 17:09:09 +04:00
Timothy Jaeryang Baek cc1dc6cb78 refac: config 2025-05-10 17:08:09 +04:00
PVBLIC Foundation 3f58a17e47 Update pinecone.py
•	Removed the unused Pinecone REST‐client import; we now only import ServerlessSpec and the gRPC client.
	•	Enhanced close()
	•	Call self.client.close() to explicitly shut down the underlying gRPC channel.
	•	Log success or a warning on failure.
	•	Still tear down the thread‐pool executor afterward.
	•	Context‐manager support
	•	Added __enter__()/__exit__() so you can do:

with PineconeClient() as client:
    client.insert(...)
# automatically calls client.close()
2025-05-10 06:07:27 -07:00
Tim Jaeryang Baek 3e1be8b958 Merge pull request #13746 from ghscience/dev
i18n Updated Italian Language Translation Added missing translations
2025-05-10 17:05:49 +04:00
gh d704b8b686 i18n Updated Italian Language Translation 2025-05-10 09:53:52 +00:00
PVBLIC Foundation 12c2138982 Update pinecone.py
Refactor and added debug
2025-05-09 18:15:22 -07:00
Anthony Uk 337ece4d50 code_interpreter.py: allow non-root jupyter base_url 2025-05-09 20:32:01 +02:00
Taylor Wilsdon 1299c11650 only show tools toggle if overflow present 2025-05-09 10:30:45 -04:00
Taylor Wilsdon 628a10e34a make the collapse toggle more subtle & compact 2025-05-09 10:26:48 -04:00
Classic298 513895c2d5 Update KnowledgeBase.svelte 2025-05-09 16:24:07 +02:00
Taylor Wilsdon ccbc46fbbe implement visible scrollbar for tools overflow, add show all toggle 2025-05-09 10:20:17 -04:00
Tim Jaeryang Baek 8acc1ab425 Merge pull request #13731 from tth37/fix_duplicate_web_search_urls
fix: Duplicate web search urls
2025-05-09 16:43:11 +04:00
Tim Jaeryang Baek 616e9e2760 Merge pull request #13730 from Classic298/dev
feat: Increase default users per page in admin panel to 30
2025-05-09 16:42:29 +04:00
tth37 c95a65a4bd fix: Duplicate web search urls 2025-05-09 20:06:35 +08:00
Classic298 220d35ebcb Increase users shown per page to 30 2025-05-09 13:00:42 +02:00
Classic298 947f5e840e Increased PAGE_ITEM_COUNT to 30 2025-05-09 12:59:50 +02:00
Timothy Jaeryang Baek ebf5cb22ab refac: socket 2025-05-09 14:23:16 +04:00
Timothy Jaeryang Baek d84a3c0bf4 revert: websocket 2025-05-09 14:20:43 +04:00
Timothy Jaeryang Baek 9143716463 refac: chat pdf rendering 2025-05-09 13:56:53 +04:00
Yeyeong Cho 13e7a3dee9 fix:add missing Korean translation strings Fixes mini-777/8dobibim#1 2025-05-09 17:54:50 +09:00
Timothy Jaeryang Baek 9657474a8d refac: websocket 2025-05-09 12:45:53 +04:00
PVBLIC Foundation b38711a581 Update pinecone.py 2025-05-08 16:02:47 -07:00
PVBLIC Foundation 04b9065f08 Update pinecone.py
Now supports batched insert, upsert, and delete operations using a default batch size of 100, reducing API strain and improving throughput. All blocking calls to the Pinecone API are wrapped in asyncio.to_thread(...), ensuring async safety and preventing event loop blocking. The implementation includes zero-vector handling for efficient metadata-only queries, normalized cosine distance scores for accurate ranking, and protections against empty input operations. Logs for batch durations have been streamlined to minimize noise, while preserving key info-level success logs.
2025-05-08 15:53:30 -07:00
Timothy Jaeryang Baek 827326e1a2 refac: audio transcription issue 2025-05-08 22:57:48 +04:00
Timothy Jaeryang Baek bfa5550cc3 refac: openai already supports webm audio 2025-05-08 22:44:32 +04:00
Tim Jaeryang Baek e60fa7927d Merge pull request #13604 from Acters/dev
feat: Improve Default Code Interpreter Prompt
2025-05-08 22:14:29 +04:00
Tim Jaeryang Baek 1fea4f794f Merge pull request #13670 from HarrisonConsulting/fix/milvus-standalone-index
fix: enhance MilvusClient with dynamic index type and improved logging
2025-05-08 22:08:23 +04:00
Timothy Jaeryang Baek 7c8597765f refac/fix: ldap issue 2025-05-08 22:06:44 +04:00
Timothy Jaeryang Baek 08b6ea0e17 refac 2025-05-08 20:47:41 +04:00
Tim Jaeryang Baek 68f5ff540c Merge pull request #13695 from itk-dev/8908-focus-trap-modal
feat: focus trap modal
2025-05-08 20:44:36 +04:00
Tim Jaeryang Baek 9509528deb Merge pull request #13703 from hurxxxx/fix/onedrive-org-picker-error
fix: Add SharePoint tenant ID to OneDrive settings
2025-05-08 20:41:06 +04:00
hurxxxx 4ca0ea1e55 Add SharePoint tenant ID to OneDrive settings 2025-05-09 00:35:23 +09:00
Sine Jespersen 3886dee1ce add whitespace and semi colon 2025-05-08 13:29:24 +02:00
Sine Jespersen 305435b4ef use npm package focus-trap instead of npm package focus-trap-svelte 2025-05-08 13:22:47 +02:00
Matt Harrison 4abe2cfa7b fix: Specified "HNSW" for backwards compatibility prefering cluster environments over standalone. 2025-05-08 07:21:34 -04:00
Tim Jaeryang Baek a6d6c928d9 Merge pull request #13567 from tth37/fix_katex_chinese_delimiters
fix(katex): Allow CJK characters adjacent to math delimiters
2025-05-08 14:41:16 +04:00
Timothy Jaeryang Baek af518cacbf refac: filter out images 2025-05-08 12:43:13 +04:00
Tim Jaeryang Baek 54e4c1e0cc Merge pull request #13675 from feddersen-group/task/remove_files_from_tasks
perf: remove files from tasks
2025-05-08 12:38:39 +04:00
Tim Jaeryang Baek f6b20ca109 Merge pull request #13677 from silentoplayz/dev
refactor: Improve Default Title Generation Prompt Template for robust JSON output
2025-05-08 12:38:05 +04:00
silentoplayz 994f17b430 Update config.py
Update DEFAULT_TITLE_GENERATION_PROMPT_TEMPLATE
2025-05-08 07:31:51 +00:00
Alexander Grimm a655bb5a63 skip checks in title gen 2025-05-08 06:55:43 +00:00
Alexander Grimm cf5201aada remove files from message for tasks 2025-05-08 06:50:21 +00:00
Matt Harrison 2df9f7fb4d fix: remove import for os module in milvus.py 2025-05-08 00:28:24 -04:00
Matt Harrison 731251d11a refac: streamline Milvus index type handling using configuration options 2025-05-07 23:39:56 -04:00
Matt Harrison 2b8be86f23 feat: add Milvus configuration options for index and metric types 2025-05-07 23:32:58 -04:00
Matt Harrison 5e46c27806 refac: enhance MilvusClient with dynamic index type and improved logging 2025-05-07 21:51:28 -04:00
Timothy Jaeryang Baek b34401a087 refac/fix: notes hide hidden models 2025-05-07 23:55:20 +04:00
Timothy Jaeryang Baek 61a9d9927d refac/fix: message input rtl issue 2025-05-07 23:14:42 +04:00
Timothy Jaeryang Baek f761d2cf90 refac: allow html video controls 2025-05-07 23:06:00 +04:00
Timothy Jaeryang Baek 0f40cb0de4 feat: Vazirmatn font for Persian and Arabic languages 2025-05-07 22:31:59 +04:00
Timothy Jaeryang Baek b50dcb1862 refac: remove duplicate urls 2025-05-07 22:25:18 +04:00
Tim Jaeryang Baek b9f55a44b7 Merge pull request #13346 from dh4340/patch-1
Fix: Use dynamic Python command to run uvicorn and support pyenv setups.
2025-05-07 22:08:35 +04:00
Tim Jaeryang Baek f1bb43707e Merge pull request #13566 from itk-dev/8908-accessibility-enhancements
feat: Accessibility enhancements
2025-05-07 22:07:57 +04:00
Tim Jaeryang Baek 519bb64238 Merge pull request #13623 from panda44312/patch-15
i18n: zh-cn
2025-05-07 22:06:54 +04:00
Tim Jaeryang Baek 410af53eca Merge pull request #13581 from kaytwo/refreshOauthPfp
feat: refresh oauth profile picture
2025-05-07 22:06:43 +04:00
Tim Jaeryang Baek c2de7efa2f Merge pull request #13637 from mMabeck/my-dev
i18n: Update Danish translations (da-DK)
2025-05-07 22:02:29 +04:00
Tim Jaeryang Baek 6424c8b644 Merge pull request #13643 from suleimanelkhoury/S3-tagging-boto3
fix: Add S3_ENABLE_TAGGING env variable and fix S3_USE_ACCELERATE_ENDPOINT Type Error
2025-05-07 22:02:14 +04:00
Sine Jespersen bd2021e8e1 add aria modal true and role dialog to modal 2025-05-07 15:17:11 +02:00
Suleiman Elkhoury ccc64ac6b7 Update provider.py
Add S3_ENABLE_TAGGING to add tags optionally based on the env file.
2025-05-07 14:48:49 +02:00
Suleiman Elkhoury 7620a6bf8c Update config.py
Fixed S3_USE_ACCELERATE_ENDPOINT variable from "False" to "false" since boto3 expects a Python bool, not a string.
Added S3_ENABLE_TAGGING env variable to avoid incompatiblilty with Storages that does not support tagging (like Cloudflare R2)
2025-05-07 14:46:58 +02:00
Sine Jespersen 54b30e0b7f use svelte-focus-trap in modal 2025-05-07 14:21:56 +02:00
Sine Jespersen f56983632f add npm package svelte-focus-trap 2025-05-07 14:20:06 +02:00
Mabeck 3a553f7208 Updated danish translations 2025-05-07 14:06:30 +02:00
Panda 571e4c9600 Merge branch 'dev' into patch-15 2025-05-07 11:18:13 +02:00
Timothy Jaeryang Baek a38f3c7617 refac 2025-05-07 13:10:24 +04:00
Timothy Jaeryang Baek b1fb298bee enh: contributions stats script 2025-05-07 13:09:41 +04:00
Timothy Jaeryang Baek 176a7fc986 refac 2025-05-07 12:52:55 +04:00
Tim Jaeryang Baek efd50c3c5a Merge pull request #13607 from ssakar/dev
feat: add presence_penalty parameter to openai (Qwen3)
2025-05-07 12:48:23 +04:00
Sine Jespersen 263086aa91 break and indent divs 2025-05-07 09:21:04 +02:00
Tim Jaeryang Baek a3bb7df610 Merge pull request #13530 from open-webui/dev
0.6.7
2025-05-07 03:08:25 +04:00
Timothy Jaeryang Baek 3a6a05cef1 doc: changelog 2025-05-07 03:05:49 +04:00
Serkan Sakar d62353c140 enh: add presence_penalty parameter to openai 2025-05-07 00:56:20 +02:00
Timothy Jaeryang Baek 803b39b00c refac 2025-05-07 02:45:00 +04:00
Timothy Jaeryang Baek ed5de96d1d fix: tool servers ui settings can be bypassed with search 2025-05-07 02:41:33 +04:00
Timothy Jaeryang Baek c667d27c38 refac 2025-05-07 02:37:23 +04:00
acters e088f1ef23 Merge branch 'open-webui:dev' into dev 2025-05-06 16:17:54 -06:00
Timothy Jaeryang Baek ad1486c8bd refac 2025-05-07 02:06:18 +04:00
Timothy Jaeryang Baek 6359cb55fe chore: format 2025-05-07 02:01:03 +04:00
Timothy Jaeryang Baek 96618967bd refac 2025-05-07 02:00:10 +04:00
Tim Jaeryang Baek 19bf7289c1 Merge pull request #13603 from TiancongLx/dev
i18n: complete empty values and **fix a typo** in zh-TW
2025-05-07 01:53:16 +04:00
Timothy Jaeryang Baek 369f461691 refac 2025-05-07 01:48:54 +04:00
acters 9c760ace80 Improve Default Code Interpreter Prompt
The change to the default "Code Interpreter" prompt makes it more clear to the LLM to not wrap the code in markdown's code block formatting. This improves the reliability of having code generated in the XML tags that will run properly.
2025-05-06 15:36:17 -06:00
Tiancong Li 45e9d05240 i18n: update zh-TW 2025-05-07 05:16:44 +08:00
Timothy Jaeryang Baek ace3303ff5 enh: TOOL_SERVER_CONNECTIONS env var support 2025-05-07 01:11:28 +04:00
Timothy Jaeryang Baek 62e57a4cf5 refac: auth endpoint 2025-05-07 00:54:53 +04:00
Tim Jaeryang Baek 2a4dfc02a2 Merge pull request #13540 from NoMoreFood/dev
feat: Azure TTS Allow Base URL
2025-05-07 00:49:57 +04:00
Tim Jaeryang Baek e0e151f4f4 Merge pull request #13583 from torisetxd/torisetxd-image-patch
Fix: Fix Image parameter for Automatic1111 `/generations` endpoint
2025-05-07 00:45:08 +04:00
Tim Jaeryang Baek ea07e242f5 Merge pull request #13528 from Classic298/dev
feat: Enhance YouTube Transcription Loader for multi-language support
2025-05-07 00:44:45 +04:00
Timothy Jaeryang Baek 53f2ab1676 chore: wording 2025-05-06 20:15:09 +04:00
Chris Kanich 500f4d73e1 refresh oauth profile picture 2025-05-06 11:00:35 -05:00
toriset 6c957f2407 Fixed mis-use of set_image_model in automatic1111 Image generation
A custom model parameter would always throw an error.. :/
2025-05-06 18:17:52 +03:00
Classic298 1dcbec71ec Update youtube.py 2025-05-06 17:14:00 +02:00
Classic298 87dcbd198c Update youtube.py 2025-05-06 17:11:03 +02:00
Classic298 d7927506f1 Update youtube.py 2025-05-06 17:06:21 +02:00
Classic298 f65dc715f9 Update youtube.py 2025-05-06 16:30:18 +02:00
Classic298 c69278c13c Update youtube.py 2025-05-06 16:24:27 +02:00
Classic298 a129e0954e Update youtube.py 2025-05-06 16:22:40 +02:00
Classic298 5e1cb76b93 Update youtube.py 2025-05-06 16:16:58 +02:00
tth37 8e14372dd8 enh: Support more languages 2025-05-06 21:26:09 +08:00
Sine Jespersen 9d8d4393a6 remove alt text from logo, as this is decorative and provides no value for assistive tech users 2025-05-06 14:37:35 +02:00
Sine Jespersen c4a3a9c616 fix text-contrast from 3.71 to 7.55 on dark mode 2025-05-06 14:37:05 +02:00
Sine Jespersen 8c6f4f6579 change divs to labels, as this is what they semantically are 2025-05-06 14:33:26 +02:00
Sine Jespersen 86ce111edd Add aria-labelledby to icon-button to make assistive tech able to give info on what user can expect from button 2025-05-06 14:30:01 +02:00
Sine Jespersen 0889dd3a51 add close label to close button in changelog modal, only accessible for screen readers 2025-05-06 14:24:19 +02:00
tth37 de182ddec2 fix(katex): Allow Chinese characters adjacent to math delimiters 2025-05-06 18:38:42 +08:00
Panda bfdcc12cdc i18n: zh-cn 2025-05-06 08:30:28 +02:00
Tim Jaeryang Baek ed210517c2 Merge pull request #13533 from TheRauch1/main
feat: handle user OAuth groups as list or string
2025-05-06 09:57:14 +04:00
Tim Jaeryang Baek 7b5247810c Merge pull request #13542 from tuzkiyoung/main
fix: support gpt-image-1 with correct parameter
2025-05-06 09:56:48 +04:00
杨方亮 9cc00afc6b fix: support gpt-image-1 with correct parameter 2025-05-06 10:17:22 +08:00
Bryan Berns 5c055b73a3 Add Custom Azure TTS URL - Formatting 2025-05-05 22:17:18 -04:00
Bryan Berns 853adad894 Add Custom Azure TTS URL - Fix Spaces 2025-05-05 22:12:32 -04:00
Bryan Berns 5aabe21cbe Add Custom Azure TTS URL 2025-05-05 22:08:48 -04:00
Timothy Jaeryang Baek e63b8b3879 refac 2025-05-06 00:46:32 +04:00
Tim Jaeryang Baek 0a8199a056 Merge pull request #13522 from salbahra/bug/fix-missing-api-config
fix: Correct missing `api_config` for embedding Ollama proxy endpoints
2025-05-05 23:49:40 +04:00
Timothy Jaeryang Baek 7b36466c1c refac: audio transcribe supported filetype 2025-05-05 23:42:56 +04:00
Timothy Jaeryang Baek 27da31dc83 fix: tikaloader extract images 2025-05-05 23:40:34 +04:00
therauch1 c36acd46cc enh: handle user OAuth groups as list or string 2025-05-05 21:38:31 +02:00
Tim Jaeryang Baek 413297b5c7 Merge pull request #13532 from Classic298/main
i18n: Update de-DE translation
2025-05-05 23:37:54 +04:00
Classic298 f94965909c Update translation.json 2025-05-05 21:33:45 +02:00
Timothy Jaeryang Baek 982c84c8ac fix: non admin user notes 2025-05-05 23:31:37 +04:00
Timothy Jaeryang Baek 1f45b9df2e fix: notes non admin issue 2025-05-05 23:29:40 +04:00
Tim Jaeryang Baek 3dd2e80dec Merge pull request #13525 from Kylapaallikko/dev
i18n: Update fi-FI translation
2025-05-05 23:28:04 +04:00
Classic298 67a612fe24 Update youtube.py 2025-05-05 20:40:48 +02:00
Classic298 791dd24ace Update youtube.py 2025-05-05 20:08:25 +02:00
Classic298 9cf3381381 Update youtube.py 2025-05-05 20:07:52 +02:00
Classic298 b0d74a59f1 Update youtube.py 2025-05-05 20:07:37 +02:00
Classic298 1a30b3746e Update youtube.py 2025-05-05 20:03:00 +02:00
Classic298 0a3817ed86 Update youtube.py 2025-05-05 20:00:10 +02:00
Classic298 0a845db8ec Update youtube.py 2025-05-05 19:57:21 +02:00
Classic298 7680ac2517 Update youtube.py 2025-05-05 19:57:06 +02:00
Kylapaallikko b3eb3b3c05 Update fi-FI translation.json 2025-05-05 20:21:23 +03:00
Samer Albahra d5e866d839 Fix missing api_config for embedding Ollama proxy endpoints 2025-05-05 12:33:07 -04:00
Timothy Jaeryang Baek db0a0b395f fix: oauth 2025-05-05 19:38:36 +04:00
Tim Jaeryang Baek 23b9354cf6 Merge pull request #12932 from open-webui/dev
0.6.6
2025-05-05 17:58:52 +04:00
Timothy Jaeryang Baek 4fa2e136bf refac: styling 2025-05-05 17:58:40 +04:00
Timothy Jaeryang Baek 512d475ecc refac: api usage 2025-05-05 17:43:51 +04:00
Timothy Jaeryang Baek 56cefde161 doc: changelog 2025-05-05 17:13:09 +04:00
Timothy Jaeryang Baek f78811b253 chore: bump 2025-05-05 16:49:21 +04:00
Timothy Jaeryang Baek e6ca7a414b refac 2025-05-05 16:41:51 +04:00
Tim Jaeryang Baek 367f6fb8f1 Merge pull request #13498 from omahs/main
fix: typos
2025-05-05 16:27:33 +04:00
omahs 37e0313c83 Merge branch 'dev' into main 2025-05-05 14:26:40 +02:00
Timothy Jaeryang Baek 267889809d chore: format 2025-05-05 16:19:05 +04:00
Timothy Jaeryang Baek 33c206c15c enh: include profile image during user import 2025-05-05 16:17:20 +04:00
omahs 863f227be9 fix: typos 2025-05-05 14:14:59 +02:00
Timothy Jaeryang Baek abdde10b74 refac 2025-05-05 16:12:41 +04:00
Timothy Jaeryang Baek 26e9cd0b4b enh: user role update confirm dialog 2025-05-05 16:10:36 +04:00
Timothy Jaeryang Baek 457a0f318a refac 2025-05-05 16:05:39 +04:00
Timothy Jaeryang Baek 3771d27a52 refac 2025-05-05 15:50:09 +04:00
Timothy Jaeryang Baek d16ff54895 refac 2025-05-05 15:46:55 +04:00
Timothy Jaeryang Baek de0f52b686 doc: readme 2025-05-05 15:42:14 +04:00
Timothy Jaeryang Baek eec7a2afa1 refac 2025-05-05 11:25:00 +04:00
Timothy Jaeryang Baek 9e1400f055 enh: generate title 2025-05-05 10:36:01 +04:00
Timothy Jaeryang Baek aa374823ce refac 2025-05-05 10:16:34 +04:00
Timothy Jaeryang Baek a20a920ca4 refac: styling 2025-05-05 09:41:25 +04:00
Timothy Jaeryang Baek 827fd6606c refac 2025-05-05 09:36:34 +04:00
Timothy Jaeryang Baek deb1f6b6d7 feat: ai enhanced notes 2025-05-05 09:22:54 +04:00
Tim Jaeryang Baek 0f1df43bb2 Merge pull request #13485 from TiancongLx/dev
i18n: update zh-TW
2025-05-05 08:25:24 +04:00
Tiancong Li 62a5b14642 i18n: update zh-TW 2025-05-05 10:15:20 +08:00
Timothy Jaeryang Baek 9faf2d9849 refac: styling 2025-05-05 01:51:51 +04:00
Timothy Jaeryang Baek f24d0085c2 refac 2025-05-05 01:45:55 +04:00
Timothy Jaeryang Baek 463ac99e39 enh: note versioning 2025-05-05 01:35:43 +04:00
Timothy Jaeryang Baek e8b23ef0c1 refac 2025-05-05 00:05:06 +04:00
Timothy Jaeryang Baek a6a7ce6b68 enh: import md notes 2025-05-04 23:04:15 +04:00
Timothy Jaeryang Baek 8514669053 refac 2025-05-04 18:59:36 +04:00
Timothy Jaeryang Baek 291834ecbe chore: format 2025-05-04 18:32:56 +04:00
Timothy Jaeryang Baek 360b5b303a enh: note editor 2025-05-04 18:32:21 +04:00
Timothy Jaeryang Baek d8e9b000e3 enh: upload audio 2025-05-04 18:26:33 +04:00
Timothy Jaeryang Baek 62366ad020 refac: audio recording 2025-05-04 18:17:35 +04:00
Timothy Jaeryang Baek 2946f5fcce refac: styling 2025-05-04 17:33:15 +04:00
Timothy Jaeryang Baek f2cf21d64c enh: file item display audio 2025-05-04 17:29:59 +04:00
Timothy Jaeryang Baek 6475ae3b11 refac 2025-05-04 17:23:42 +04:00
Timothy Jaeryang Baek 84a05bec7b enh: notes user group permission 2025-05-04 17:22:51 +04:00
Timothy Jaeryang Baek f716968009 enh: delete notes 2025-05-04 17:01:12 +04:00
Timothy Jaeryang Baek 18149483f2 refac: note pdf export 2025-05-04 16:59:41 +04:00
Timothy Jaeryang Baek 175bbe27e9 refac 2025-05-04 16:53:48 +04:00
Tim Jaeryang Baek d1eaa9d4ee Merge pull request #13478 from SadmL/dev
[i18n] Russian locale update
2025-05-04 02:24:38 -07:00
SadmL 9b133a2245 misspell fix 2025-05-04 12:05:21 +03:00
SadmL 0e51b75654 Russian locale update 2025-05-04 11:59:13 +03:00
Timothy Jaeryang Baek 1cf9843468 refac 2025-05-04 12:02:10 +04:00
Timothy Jaeryang Baek dd42a10aa1 refac 2025-05-04 12:01:20 +04:00
Tim Jaeryang Baek 60c0aa36a2 Merge pull request #13470 from TiancongLx/dev
i18n: update zh-TW
2025-05-04 00:41:02 -07:00
Tiancong Li a7cbfeec9f i18n: update zh-TW 2025-05-04 11:58:20 +08:00
Timothy Jaeryang Baek c5c98591f9 refac 2025-05-04 00:20:30 +04:00
Timothy Jaeryang Baek 59e52add63 chore: bump 2025-05-04 00:15:46 +04:00
Timothy Jaeryang Baek 9c4a931d22 feat: capture display media audio 2025-05-04 00:14:19 +04:00
Timothy Jaeryang Baek 4cfb99248d chore: format 2025-05-03 23:48:24 +04:00
Timothy Jaeryang Baek aefd5d9557 chore: format 2025-05-03 23:48:12 +04:00
Timothy Jaeryang Baek 0f14f34dbe refac 2025-05-03 23:47:41 +04:00
Timothy Jaeryang Baek d74809853d enh: note files 2025-05-03 23:29:19 +04:00
Timothy Jaeryang Baek 23da63b06a refac: styling 2025-05-03 23:08:28 +04:00
Timothy Jaeryang Baek 116b61c53b refac 2025-05-03 23:06:46 +04:00
Timothy Jaeryang Baek f43cb87556 refac 2025-05-03 23:05:25 +04:00
Tim Jaeryang Baek 1af45435eb Merge pull request #13459 from open-webui/notes
feat: notes
2025-05-03 11:54:04 -07:00
Timothy Jaeryang Baek 4acd278624 refac 2025-05-03 22:53:23 +04:00
Timothy Jaeryang Baek 1d82a1c8c4 feat: note editor 2025-05-03 21:18:19 +04:00
Timothy Jaeryang Baek 52d32e8bf2 refac: styling 2025-05-03 21:02:21 +04:00
Timothy Jaeryang Baek a74297ed47 refac: styling 2025-05-03 18:56:27 +04:00
Timothy Jaeryang Baek 7de6112c5b feat: note list ui 2025-05-03 18:52:13 +04:00
Tim Jaeryang Baek bfacab5a0c Merge pull request #13447 from athoik/dev
feat(ocr): add support for Docling OCR engine and language configuration
2025-05-03 07:18:05 -07:00
Tim Jaeryang Baek bbc1ae0942 Merge pull request #13441 from salbahra/bug/fix-prefix-for-embedding-model
fix: Incorrect model reference with Ollama proxy when prefixes are used
2025-05-03 07:17:40 -07:00
Timothy Jaeryang Baek 7fee84c06e feat: notes 2025-05-03 18:16:32 +04:00
Athanasios Oikonomou 437804a2f8 fix: update validation logic for Docling OCR engine and language requirements
Both Docling OCR Engine and Language(s) must be provided or both left empty.
2025-05-03 08:12:58 +03:00
Athanasios Oikonomou 4801430ad2 fix: correct condition for Docling OCR engine and language validation
Both must have value or both must be empty.
2025-05-03 08:02:00 +03:00
Athanasios Oikonomou 657162e96d feat(ocr): add support for Docling OCR engine and language configuration
This commit adds support for configuring the OCR engine and language(s) for Docling.
Configuration can be set via the environment variables `DOCLING_OCR_ENGINE` and `DOCLING_OCR_LANG`, or through the UI.

Fixes #13133
2025-05-03 00:32:06 +03:00
Samer Albahra cafba3eaff Fix model reference with Ollama proxy when prefixes are used 2025-05-02 11:30:59 -04:00
Tim Jaeryang Baek 7d184c3a14 Merge pull request #13085 from ayan4m1/fix/tika-image-ocr
fix: pass extractInlineImages header to Tika if PDF_EXTRACT_IMAGES is true
2025-05-02 03:47:51 -07:00
Timothy Jaeryang Baek 754f631a07 feat: OAUTH_BLOCKED_GROUPS support 2025-05-02 14:47:02 +04:00
Timothy Jaeryang Baek c1ddb00377 refac: styling 2025-05-02 14:19:53 +04:00
Timothy Jaeryang Baek 74f160b849 refac 2025-05-02 14:16:11 +04:00
Tim Jaeryang Baek 11d599c55d Merge pull request #13111 from tth37/perf_multi_thread_web_searching
perf: Concurrent processing for web search queries
2025-05-02 03:15:49 -07:00
Tim Jaeryang Baek 7b014e44ee Merge pull request #13376 from Thaniel94/add-whisper-language-constraint
feat: Added WHISPER_LANGUAGE env variable
2025-05-02 03:08:00 -07:00
Timothy Jaeryang Baek 4a0b2a3afc chore: bump 2025-05-02 14:06:57 +04:00
Tim Jaeryang Baek 2d5643fb17 Merge pull request #13405 from yak1ex/fix_multiple_actions_in_an_action_module_do_not_work_as_intended
fix: Multiple actions in an action module do not work as intended.
2025-05-02 03:06:05 -07:00
Tim Jaeryang Baek d522914774 Merge pull request #13413 from hurxxxx/fix/eslint-version-mismatch
fix: eslint version mismatch
2025-05-02 03:05:30 -07:00
Tim Jaeryang Baek 3013ce2d1a Merge pull request #13415 from TiancongLx/dev
i18n: update zh-TW
2025-05-02 03:04:39 -07:00
Timothy Jaeryang Baek 6d81eef425 refac: reasoning detection 2025-05-02 14:04:12 +04:00
Timothy Jaeryang Baek c258272709 refac 2025-05-02 13:37:41 +04:00
Tim Jaeryang Baek a0981e01a0 Merge pull request #13417 from flefevre/dev-i8n-french
i18n french
2025-05-02 02:28:11 -07:00
hurxxxx 10243b0fac chore: bump @typescript-eslint/eslint-plugin to 8.31.1 2025-05-02 09:44:51 +09:00
nathaniel cc14aacaaa Improvements to parsing of WHISPER_LANGUAGE environment variable (Setting as empty string now equivalent to unsetting/removing WHISPER_LANGUAGE). Resolved crash caused when unset 2025-05-01 22:03:49 +01:00
nathaniel ef7acfbf3d WHISPER_LANGUAGE no longer a "PersistentConfig" variable (Was not appropriate with how WHISPER_LANGUAGE is currently configured). 2025-05-01 21:33:57 +01:00
flefevre fb8a7e8549 Merge branch 'dev' into dev-i8n-french 2025-05-01 20:17:50 +02:00
Francois LE FEVRE 754a3c4cb9 last elements to translate into french 2025-05-01 20:07:56 +02:00
Tiancong Li 725761ad6b i18n: update zh-TW 2025-05-01 22:56:47 +08:00
Francois LE FEVRE 57cb030a24 seconde review on french translation 2025-05-01 15:59:52 +02:00
Francois LE FEVRE 12626107b6 first elements translated in french 2025-05-01 15:46:58 +02:00
hurxxxx 72f985aba7 fix: align eslint plugin and parser versions 2025-05-01 22:39:29 +09:00
Timothy Jaeryang Baek 5d5e351937 feat: notes scaffolding 2025-05-01 16:39:36 +04:00
Tim Jaeryang Baek 2062d3434e Merge pull request #13385 from open-webui/dependabot/npm_and_yarn/dev/typescript-eslint/parser-8.31.1
build(deps-dev): bump @typescript-eslint/parser from 6.21.0 to 8.31.1
2025-05-01 04:59:00 -07:00
Timothy Jaeryang Baek f26cafd984 chore: langchain bump 2025-05-01 15:58:45 +04:00
Tim Jaeryang Baek aca82d64a0 Merge pull request #13391 from open-webui/dependabot/pip/backend/dev/rapidocr-onnxruntime-1.4.4
build(deps): bump rapidocr-onnxruntime from 1.3.24 to 1.4.4 in /backend
2025-05-01 04:56:32 -07:00
Tim Jaeryang Baek 3715919a6b Merge pull request #13389 from open-webui/dependabot/npm_and_yarn/dev/codemirror-lang-hcl-0.1.0
build(deps): bump codemirror-lang-hcl from 0.0.0-beta.2 to 0.1.0
2025-05-01 04:55:52 -07:00
Tim Jaeryang Baek 0e3b9f9da1 Merge pull request #13388 from open-webui/dependabot/npm_and_yarn/dev/prosemirror-schema-list-1.5.1
build(deps): bump prosemirror-schema-list from 1.4.1 to 1.5.1
2025-05-01 04:55:45 -07:00
Tim Jaeryang Baek f10e7ac81b Merge pull request #13387 from open-webui/dependabot/npm_and_yarn/dev/tiptap/core-2.11.9
build(deps): bump @tiptap/core from 2.10.0 to 2.11.9
2025-05-01 04:55:36 -07:00
Tim Jaeryang Baek 61580e9490 Merge pull request #13404 from NoMoreFood/dev
fix: Use SHA256 For Query Result Computation
2025-05-01 04:55:16 -07:00
Tim Jaeryang Baek 6f2d61dc4f Merge pull request #13398 from hurxxxx/i18n-korean
i18n: Update Korean translation.json and parsed messages
2025-05-01 04:48:56 -07:00
Bryan Berns 32257089f9 Use SHA256 For Query Result Computation 2025-05-01 03:56:20 -04:00
Yak! b5cfa2b992 fix: Multiple actions in an action module do not work as intended. 2025-05-01 16:41:06 +09:00
dependabot[bot] c826717343 build(deps): bump @tiptap/core from 2.10.0 to 2.11.9
Bumps [@tiptap/core](https://github.com/ueberdosis/tiptap/tree/HEAD/packages/core) from 2.10.0 to 2.11.9.
- [Release notes](https://github.com/ueberdosis/tiptap/releases)
- [Changelog](https://github.com/ueberdosis/tiptap/blob/@tiptap/core@2.11.9/packages/core/CHANGELOG.md)
- [Commits](https://github.com/ueberdosis/tiptap/commits/@tiptap/core@2.11.9/packages/core)

---
updated-dependencies:
- dependency-name: "@tiptap/core"
  dependency-version: 2.11.9
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-01 05:44:35 +00:00
Tim Jaeryang Baek 35a64b55a1 Merge pull request #13386 from open-webui/dependabot/npm_and_yarn/dev/tiptap/extension-code-block-lowlight-2.11.9
build(deps): bump @tiptap/extension-code-block-lowlight from 2.10.0 to 2.11.9
2025-04-30 22:43:30 -07:00
Tim Jaeryang Baek 9695e08411 Merge pull request #13392 from open-webui/dependabot/pip/backend/dev/pytest-approx-eq-8.3.5
build(deps): update pytest requirement from ~=8.3.2 to ~=8.3.5 in /backend
2025-04-30 22:43:09 -07:00
Tim Jaeryang Baek c6d76acf76 Merge pull request #13393 from open-webui/dependabot/pip/backend/dev/elasticsearch-9.0.1
build(deps): bump elasticsearch from 8.17.1 to 9.0.1 in /backend
2025-04-30 22:42:55 -07:00
Tim Jaeryang Baek b6a76dc026 Merge pull request #13394 from open-webui/dependabot/pip/backend/dev/loguru-0.7.3
build(deps): bump loguru from 0.7.2 to 0.7.3 in /backend
2025-04-30 22:42:45 -07:00
Timothy Jaeryang Baek 079af5fffe fix: send webhook notification when user is not active 2025-05-01 09:41:07 +04:00
Tim Jaeryang Baek e57f2c928a Merge pull request #13379 from NoMoreFood/dev
feat: Azure STT Allow Base URL & Max Speaker Setting
2025-04-30 22:26:18 -07:00
hurxxxx 513d5c8bf5 Fix typos 2025-05-01 13:21:15 +09:00
hurxxxx d72e41c563 Consolidate duplicate messages into a single one 2025-05-01 13:16:47 +09:00
hurxxxx 1ca5d532b8 chore(i18n): update parsed i18n data and Korean translations 2025-05-01 13:00:27 +09:00
hurxxxx f46c7d53b7 update Korean translation 2025-05-01 12:55:07 +09:00
hurxxxx a7cbf62c68 update Korean translation 2025-05-01 12:43:19 +09:00
dependabot[bot] 4eaaeb1888 build(deps): bump loguru from 0.7.2 to 0.7.3 in /backend
Bumps [loguru](https://github.com/Delgan/loguru) from 0.7.2 to 0.7.3.
- [Release notes](https://github.com/Delgan/loguru/releases)
- [Changelog](https://github.com/Delgan/loguru/blob/master/CHANGELOG.rst)
- [Commits](https://github.com/Delgan/loguru/compare/0.7.2...0.7.3)

---
updated-dependencies:
- dependency-name: loguru
  dependency-version: 0.7.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-01 02:54:59 +00:00
dependabot[bot] 753d530cd0 build(deps): bump elasticsearch from 8.17.1 to 9.0.1 in /backend
Bumps [elasticsearch](https://github.com/elastic/elasticsearch-py) from 8.17.1 to 9.0.1.
- [Release notes](https://github.com/elastic/elasticsearch-py/releases)
- [Commits](https://github.com/elastic/elasticsearch-py/compare/v8.17.1...v9.0.1)

---
updated-dependencies:
- dependency-name: elasticsearch
  dependency-version: 9.0.1
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-01 02:54:56 +00:00
dependabot[bot] 10f55d39d2 build(deps): update pytest requirement in /backend
Updates the requirements on [pytest](https://github.com/pytest-dev/pytest) to permit the latest version.
- [Release notes](https://github.com/pytest-dev/pytest/releases)
- [Changelog](https://github.com/pytest-dev/pytest/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/pytest-dev/pytest/compare/8.3.2...8.3.5)

---
updated-dependencies:
- dependency-name: pytest
  dependency-version: 8.3.5
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-01 02:54:52 +00:00
dependabot[bot] 0b84863d76 build(deps): bump rapidocr-onnxruntime from 1.3.24 to 1.4.4 in /backend
Bumps [rapidocr-onnxruntime](https://github.com/RapidAI/RapidOCR) from 1.3.24 to 1.4.4.
- [Release notes](https://github.com/RapidAI/RapidOCR/releases)
- [Commits](https://github.com/RapidAI/RapidOCR/compare/v1.3.24...v1.4.4)

---
updated-dependencies:
- dependency-name: rapidocr-onnxruntime
  dependency-version: 1.4.4
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-01 02:54:49 +00:00
dependabot[bot] efc163fede build(deps): bump codemirror-lang-hcl from 0.0.0-beta.2 to 0.1.0
Bumps codemirror-lang-hcl from 0.0.0-beta.2 to 0.1.0.

---
updated-dependencies:
- dependency-name: codemirror-lang-hcl
  dependency-version: 0.1.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-01 02:53:31 +00:00
dependabot[bot] dc5107a473 build(deps): bump prosemirror-schema-list from 1.4.1 to 1.5.1
Bumps [prosemirror-schema-list](https://github.com/prosemirror/prosemirror-schema-list) from 1.4.1 to 1.5.1.
- [Changelog](https://github.com/ProseMirror/prosemirror-schema-list/blob/master/CHANGELOG.md)
- [Commits](https://github.com/prosemirror/prosemirror-schema-list/compare/1.4.1...1.5.1)

---
updated-dependencies:
- dependency-name: prosemirror-schema-list
  dependency-version: 1.5.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-01 02:53:26 +00:00
dependabot[bot] c5fd40d951 build(deps): bump @tiptap/extension-code-block-lowlight
Bumps [@tiptap/extension-code-block-lowlight](https://github.com/ueberdosis/tiptap/tree/HEAD/packages/extension-code-block-lowlight) from 2.10.0 to 2.11.9.
- [Release notes](https://github.com/ueberdosis/tiptap/releases)
- [Changelog](https://github.com/ueberdosis/tiptap/blob/@tiptap/extension-code-block-lowlight@2.11.9/packages/extension-code-block-lowlight/CHANGELOG.md)
- [Commits](https://github.com/ueberdosis/tiptap/commits/@tiptap/extension-code-block-lowlight@2.11.9/packages/extension-code-block-lowlight)

---
updated-dependencies:
- dependency-name: "@tiptap/extension-code-block-lowlight"
  dependency-version: 2.11.9
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-01 02:53:05 +00:00
dependabot[bot] 317032aa5c build(deps-dev): bump @typescript-eslint/parser from 6.21.0 to 8.31.1
Bumps [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) from 6.21.0 to 8.31.1.
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/parser/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.31.1/packages/parser)

---
updated-dependencies:
- dependency-name: "@typescript-eslint/parser"
  dependency-version: 8.31.1
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-01 02:52:50 +00:00
Tim Jaeryang Baek f3b38e9777 Merge pull request #13372 from therealmichaelberna/main
**fix** convert_function_to_pydantic_model returning empty descriptions
2025-04-30 13:36:22 -07:00
Tim Jaeryang Baek 336d20fa33 Merge pull request #13364 from NovoNordisk-OpenSource/websocket-behind-auth
refactor: hiding websocket initalization behind authentication
2025-04-30 13:35:27 -07:00
Timothy Jaeryang Baek dadfa5f043 refac: HTML_FILE_ID handling 2025-05-01 00:32:29 +04:00
Timothy Jaeryang Baek e52e45d613 refac: collapsible 2025-04-30 22:26:07 +04:00
Michael Berna 4a6b964a51 Fixed convert_function_to_pydantic_model
convert_function_to_pydantic_model was returning empty function descriptions. I have renamed the variables more precisely which helps to avoid confusion and also resolved this issue which was caused by a variable naming conflict.
2025-04-30 14:22:38 -04:00
Timothy Jaeryang Baek 41904f21e4 refac 2025-04-30 19:41:16 +04:00
Timothy Jaeryang Baek ef2aeb7c0e fix: only allow admin uploaded html to be rendered as html 2025-04-30 19:34:53 +04:00
shcodenick 4c81b6ebc5 hiding websocket initalization behind authentication 2025-04-30 15:05:57 +02:00
Timothy Jaeryang Baek 783d409b1d refac 2025-04-30 16:58:38 +04:00
Timothy Jaeryang Baek c1fa989cb8 refac: styling 2025-04-30 16:54:51 +04:00
Bryan Berns 6c8a9d000e Azure STT Allow Base URL & Max Speaker Setting 2025-04-30 08:51:01 -04:00
Timothy Jaeryang Baek f9d238e850 refac: user list sub-standard code 2025-04-30 16:49:41 +04:00
Tim Jaeryang Baek db06a925fe Merge pull request #13267 from AnapatChaiwongse/dev
feat: implement pagination for /admin/users
2025-04-30 05:04:14 -07:00
Timothy Jaeryang Baek eb68b21e0f refac 2025-04-30 16:01:23 +04:00
Tim Jaeryang Baek e822984162 Merge pull request #13358 from feddersen-group/fix/load_ext_dependencies_on_startup
fix: install external requirements of tools and functions to prevent deactivation
2025-04-30 04:59:56 -07:00
Alexander Grimm e80ed32aeb fix: install external requirements of admin functions and tools on startups 2025-04-30 11:14:38 +00:00
Tim Jaeryang Baek c9d3bb35c3 Merge pull request #13342 from feddersen-group/feat/embedding_dim
feat: allow pgvector to use large embedding models
2025-04-30 02:41:26 -07:00
Alexander Grimm da9966aca1 ~ truncate vectors for pgvector if too big 2025-04-30 05:35:17 +00:00
Tim Jaeryang Baek 4ee5dd58b7 Merge pull request #13177 from tth37/fix_firecrawl_loader_default_mode
fix: FireCrawlLoader default mode to scrape
2025-04-29 08:39:06 -07:00
Tim Jaeryang Baek e87f2669fa Merge pull request #13191 from tth37/feat_firecrawl_search_engine
feat: Add Firecrawl search engine
2025-04-29 08:38:28 -07:00
Tim Jaeryang Baek 390263742c Merge pull request #13273 from spammenotinoz/dev
**FIX** Update Knowledge ReIndexing to continue upon missing\corrupt collections
2025-04-29 08:37:55 -07:00
Tim Jaeryang Baek 8f4be09dd2 Merge pull request #13315 from aleixdorca/dev
i18n: Update Catalan translation.json
2025-04-29 08:35:28 -07:00
Tim Jaeryang Baek 7b863465a9 Merge pull request #13311 from stephen304/yacy-support
feat: Yacy search support
2025-04-29 08:35:10 -07:00
Timothy Jaeryang Baek 6ae2d2c4ea refac: gpt-image-1 support 2025-04-29 19:34:00 +04:00
Timothy Jaeryang Baek addf44b21f refac 2025-04-29 19:27:54 +04:00
D 8f31b6d74a Fix: Use dynamic Python command to run uvicorn and support pyenv setups.
Updated the uvicorn startup command to dynamically detect and use either 'python3' or 'python',
ensuring compatibility across different environments and pyenv setups.
This avoids issues where only one of the commands is available.
2025-04-29 09:14:23 +01:00
Aleix Dorca bc88a9bc8b Update Catalan translation.json 2025-04-29 07:04:16 +02:00
Tim Jaeryang Baek f7f3ae7cc9 Merge pull request #13306 from tarmst/remove-blocking-websocket-awaits
Perf: Chat Websocket Sequential Awaits
2025-04-28 11:17:50 -07:00
Tim Jaeryang Baek 5a1c4dbe1b Merge pull request #13279 from panda44312/patch-14
i18n: zh-cn
2025-04-28 11:17:24 -07:00
Panda 63e4d1ea41 i18n: fix 2025-04-28 18:22:36 +02:00
tth37 cf2d7de873 refac: Implicit asyncio create_task 2025-04-29 00:20:55 +08:00
tarmst c9a9e05bb4 Fix websocket sequential awaits 2025-04-28 15:17:34 +00:00
Timothy Jaeryang Baek 3ac74a4fcb refac: styling 2025-04-28 18:21:48 +04:00
Timothy Jaeryang Baek 63fe426a52 chore: format 2025-04-28 17:46:05 +04:00
Timothy Jaeryang Baek ee8de1ee0b refac: disable jwt display for non-admin users 2025-04-28 17:45:17 +04:00
Timothy Jaeryang Baek 791c19acf7 enh: unicode char support for user import 2025-04-28 17:26:19 +04:00
Tim Jaeryang Baek a9999833d2 Merge pull request #13185 from Chayud-Ac/feature-thai-i18n
fix(i18n): improve Thai translations
2025-04-28 06:16:20 -07:00
Tim Jaeryang Baek c3da9f3026 Merge pull request #13246 from jmtatsch/fix-video-treated-as-text
Fix uploading video files
2025-04-28 06:15:40 -07:00
Tim Jaeryang Baek 2f5abf7374 Merge pull request #13187 from Mister-Hope/patch-1
feat: update default CUDA version to 12.8, close #13186
2025-04-28 06:15:01 -07:00
Tim Jaeryang Baek dedc45183f Merge pull request #13258 from choket/dev
Fix: Fixed a bug that caused user notification webhooks to never be sent
2025-04-28 06:14:24 -07:00
Tim Jaeryang Baek 6d59a5f809 Merge pull request #13253 from noexcs/dev
fix: The value of the 'Access-Control-Allow-Origin' header in the res…
2025-04-28 06:12:53 -07:00
Timothy Jaeryang Baek 10855cf5ed refac 2025-04-28 17:11:28 +04:00
Timothy Jaeryang Baek 9398c843d3 refac 2025-04-28 17:08:07 +04:00
Timothy Jaeryang Baek 45ad6776de fix: token cookie issue 2025-04-28 17:04:13 +04:00
Tim Jaeryang Baek fb1c101dc6 Merge pull request #13269 from jarrod-lowe/s3-tagging
feat: Add tags to S3 uploaded files
2025-04-28 05:57:08 -07:00
Tim Jaeryang Baek a0273aaea8 Merge pull request #13271 from schulti/patch-1
Update de-DE translation.json
2025-04-28 05:56:22 -07:00
Tim Jaeryang Baek 146a08c68f Merge branch 'dev' into patch-1 2025-04-28 05:49:50 -07:00
Timothy Jaeryang Baek 7d0a78a43a refac: aiohttp trust_env=True 2025-04-28 16:47:34 +04:00
Timothy Jaeryang Baek 9bfb4ae838 fix: ollama proxy "format" 2025-04-28 16:45:23 +04:00
Panda ec496de593 i18n: zh-cn 2025-04-28 11:05:06 +02:00
spammenotinoz 12fd7c8cb9 Update knowledge.py
Improve Error Handling ReIndexing Knowledge Database
2025-04-28 16:39:20 +10:00
Stefan Schultheis, OE1SCS d9186e138b Update de-DE translation.json
added a few translations and corrected some typos
2025-04-28 08:21:21 +02:00
Jarrod Lowe 141810039f Add tags to S3 uploaded files 2025-04-28 16:56:56 +12:00
KaeOhhhh 2112067054 Support pagination in /admin/users API
Update Users.svelte

Update index.ts

Update users.py

Update users.py

Update UserList.svelte
2025-04-28 11:29:42 +07:00
root 8a9de0a62f Fix bug that never triggers user notification webhooks 2025-04-27 21:31:34 +02:00
noexcs 4b9b24ae78 fix: The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' when the request's credentials mode is 'include'. 2025-04-27 21:29:13 +08:00
jmtatsch 056a042ef2 Update files.py
fix formatting
2025-04-27 11:18:27 +02:00
jmtatsch c51a91583e Dont attempt to infer charset for video files 2025-04-27 10:59:35 +02:00
nathaniel 1efa708f83 Added WHISPER_LANGUAGE env variable. If set to a country's ISO2, constrains Whisper's stt to that language. Detects language as normal if unset 2025-04-27 05:58:06 +01:00
Stephen Smith ea16426a8d Remove unused kwargs in yacy, update comments. 2025-04-27 00:41:46 -04:00
Stephen Smith f9b9217e98 Set Yacy search to text 2025-04-26 23:13:31 -04:00
Stephen Smith e6d43d70f3 Don't request nav and pass count to Yacy 2025-04-26 23:08:16 -04:00
Stephen Smith 240d91d38d Add yacy config for user/pass, automatically add yacy json api path 2025-04-26 22:28:30 -04:00
Stephen Smith 0f73b96616 first pass at yacy support copied from searxng 2025-04-26 14:07:13 -04:00
Timothy Jaeryang Baek 1349bc47b1 fix: chat message input ui styling 2025-04-25 14:21:58 +09:00
tth37 92dbeb1939 feat: Add Firecrawl search engine 2025-04-24 14:57:28 +08:00
Mister-Hope 5f69424215 Update Dockerfile 2025-04-24 13:59:57 +08:00
Chayud 86b9f252c6 fix(i18n): improve Thai translations 2025-04-24 10:35:02 +07:00
Timothy Jaeryang Baek 0b5514f376 refac 2025-04-24 03:16:46 +09:00
Timothy Jaeryang Baek 28ec3069de enh: exp token check 2025-04-24 03:12:22 +09:00
tth37 8f7195ceda fix: FireCrawlLoader default mode to scrape 2025-04-24 01:17:35 +08:00
Tim Jaeryang Baek 91e758f3ec Merge pull request #13165 from feddersen-group/perf/parallel_knowledge_search
perf: all knowledge searches in parallel in non-hybrid mode
2025-04-23 10:01:06 -07:00
Tim Jaeryang Baek d0a1a0f16b Merge pull request #13119 from jarrod-lowe/suggestions
feat: Set default prompt suggestions from environment variable DEFAULT_PROMPT_SUGGESTIONS
2025-04-23 09:55:57 -07:00
Timothy Jaeryang BaekandDrZoidberg09 732d7aee70 enh: sentence transformers env vars
Co-Authored-By: DrZoidberg09 <96449693+drzoidberg09@users.noreply.github.com>
2025-04-24 01:55:18 +09:00
Timothy Jaeryang Baek 3ec6652f99 chore: dep bump 2025-04-24 01:48:07 +09:00
Tim Jaeryang Baek cad9b292bf Merge pull request #13175 from PeterDaveHello/Improve-zh-TW-locale
i18n: improve zh-TW Traditional Chinese locale
2025-04-23 09:41:23 -07:00
Timothy Jaeryang Baek 09874ab83d fix: FireCrawlLoader 2025-04-24 01:40:34 +09:00
Peter Dave Hello c798adb04c i18n: improve zh-TW Traditional Chinese locale 2025-04-24 00:37:15 +08:00
Timothy Jaeryang Baek 7baca2b483 fix: ollama url model download issue 2025-04-24 01:26:19 +09:00
Tim Jaeryang Baek 2b10b378b6 Merge pull request #13170 from TiancongLx/dev
i18n: update zh-TW
2025-04-23 08:22:27 -07:00
Tim Jaeryang Baek 2652b91f5a Merge pull request #13166 from oleguldberg/update-danish-translation
i18n - Add to Danish Translation
2025-04-23 08:22:17 -07:00
Tiancong Li afdba443f4 i18n: update zh-TW 2025-04-23 21:28:46 +08:00
Ole Guldberg ba4f399ace Merge branch 'update-danish-translation' of https://github.com/oleguldberg/open-webui into update-danish-translation 2025-04-23 14:51:01 +02:00
Ole Guldberg 176ff56e85 Merge branch 'update-danish-translation' of https://github.com/oleguldberg/open-webui into update-danish-translation 2025-04-23 12:30:19 +02:00
Ole Guldberg f2765bee72 More Danish Translation
i18n - add to Danish translation. Made agains -dev.
2025-04-23 12:25:32 +02:00
Ole Guldberg 497a7b4ded More Danish Translation
i18n - add to Danish translation. Made agains -dev.
2025-04-23 12:06:17 +02:00
Alexander Grimm d182155fac ~ call knowledge searches in parallel in non-hybrid mode 2025-04-23 09:20:51 +00:00
Timothy Jaeryang Baek 5030041683 chore: format 2025-04-23 16:05:15 +09:00
Tim Jaeryang Baek faa3cac0e4 Merge pull request #13107 from tth37/fix_tavily_max_results
fix: `max_results` in Tavily search handler
2025-04-22 23:47:36 -07:00
Tim Jaeryang Baek d3e516934c Merge pull request #13098 from athoik/dev
feat: Add abstract base class for vector database integration
2025-04-22 23:24:08 -07:00
Timothy Jaeryang Baek 2f7b5acdf8 enh: chat share & export permissions 2025-04-23 14:43:33 +09:00
Tim Jaeryang Baek 7937ed3ee1 Merge pull request #13144 from PeterDaveHello/FixI18nInMultiResponseMessages
i18n: Add missing i18n locale for "Merged Response" string
2025-04-22 20:38:37 -07:00
Tim Jaeryang Baek f0a129482e Merge pull request #13145 from PeterDaveHello/Update-zh-TW-locale
i18n: improve zh-TW Traditional Chinese locale
2025-04-22 20:37:47 -07:00
Ole Guldberg 0e0ee3ca9c More Danish Translation 2025-04-22 21:32:21 +02:00
Ole Guldberg 26a2187e6e More Danish translation 2025-04-22 21:30:05 +02:00
Ole Guldberg 2b4e946f70 More danish 2025-04-22 21:10:51 +02:00
Ole Guldberg 17a9d11435 More dansh 2025-04-22 21:00:36 +02:00
Ole Guldberg a21ac3cc25 More danish 2025-04-22 20:54:33 +02:00
Peter Dave Hello 4eb6985c1b i18n: improve zh-TW Traditional Chinese locale 2025-04-23 02:03:07 +08:00
Peter Dave Hello 5f81755b86 i18n: Add missing i18n locale for "Merged Response" string 2025-04-23 02:01:18 +08:00
Ole Guldberg 4340431753 More danish translation 2025-04-22 18:10:43 +02:00
Timothy Jaeryang Baek f2314596ba enh: failed login attempts audit log 2025-04-23 00:06:44 +09:00
Timothy Jaeryang Baek 43efff0fe6 refac 2025-04-22 23:22:50 +09:00
Ole Guldberg 4e8cd688bf beginning of update 2025-04-22 16:05:13 +02:00
Jarrod Lowe af5efe1580 Set default prompt suggestions from environment variable 2025-04-22 11:39:01 +12:00
tth37 5b9c1de3d1 fix: Translatable 'Searching the web' 2025-04-22 01:24:30 +08:00
tth37 4b451b984b perf: Multi-thread web searching 2025-04-21 22:18:05 +08:00
tth37 bc315bd530 fix: max_results in Tavily search api 2025-04-21 20:59:47 +08:00
Athanasios Oikonomou 1e291aff25 feat: Add abstract base class for vector database integration
- Created `VectorDBBase` as an abstract base class to standardize vector database operations.
- Added required methods for common vector database operations: `has_collection`, `delete_collection`, `insert`, `upsert`, `search`, `query`, `get`, `delete`, `reset`.
- The base class can now be extended by any vector database implementation (e.g., Qdrant, Pinecone) to ensure a consistent API across different database systems.
2025-04-21 08:27:27 +03:00
Tim Jaeryang Baek 913f8a15f9 Merge pull request #13073 from athoik/dev
feat(vector-db): add support for Pinecone client
2025-04-20 14:42:36 -07:00
Tim Jaeryang Baek 7f85436a94 Merge pull request #13089 from izumi0004/fix/tool_calls
fix: handling tool calls in streamed responses
2025-04-20 14:08:09 -07:00
izumi0004 99ce85f20f fix: handling tool calls in streamed responses 2025-04-21 00:29:52 +08:00
ayan4m1 039dec6820 fix: pass header to Tika if PDF_EXTRACT_IMAGES is true 2025-04-20 17:36:40 +02:00
Athanasios Oikonomou e000c56ef7 feat(vector-db): add support for Pinecone client
Adds Pinecone as a supported vector database option.

- Implements `PineconeClient` with support for common operations: `add`, `query`, `delete`, `reset`.
- Emulates namespace support using metadata filtering (`collection_name` prefix).
- Dynamically configures Pinecone via the following env vars:
  - `PINECONE_API_KEY`
  - `PINECONE_ENVIRONMENT`
  - `PINECONE_INDEX_NAME`
  - `PINECONE_DIMENSION`
  - `PINECONE_METRIC`
  - `PINECONE_CLOUD`
- Integrates cleanly with the vector DB abstraction layer.
- Includes markdown documentation under `docs/getting-started/env-configuration.md`.

BREAKING CHANGE: None
2025-04-20 11:08:51 +03:00
Tim Jaeryang Baek af02708b4c Merge pull request #13072 from grandtele/fix/openai_o_series_handler
* fix: Refactor openai handler to support all o-series models universally
2025-04-20 00:42:39 -07:00
grand 5a8e417590 * refactor openai handler to support all o-series models universally 2025-04-20 09:14:27 +02:00
Timothy Jaeryang Baek 1a6e4c2948 refac 2025-04-19 23:40:31 -07:00
Timothy Jaeryang Baek 6fd082d55f enh: html token (allow yt embed) 2025-04-19 23:38:08 -07:00
Timothy Jaeryang Baek f4de3b4c45 enh: notificationSoundAlways option 2025-04-19 23:13:19 -07:00
Tim Jaeryang Baek 8fcad1df4e Merge pull request #13062 from open-webui/cla
chore: cla
2025-04-19 11:33:02 -07:00
Tim Jaeryang Baek 08a35417f2 Merge branch 'dev' into cla 2025-04-19 11:32:49 -07:00
Timothy Jaeryang Baek f7906790e1 refac 2025-04-19 11:32:16 -07:00
Timothy Jaeryang Baek 852d9dcbe9 chore: cla 2025-04-19 11:30:59 -07:00
Timothy Jaeryang Baek 48637893b1 fix: scroll on branch change w/ multi response chat 2025-04-19 04:09:46 -07:00
Timothy Jaeryang Baek 463d7fb628 refac: openapi to tool spec 2025-04-19 03:46:06 -07:00
Timothy Jaeryang Baek 332f8579d7 doc: readme 2025-04-19 02:55:02 -07:00
Timothy Jaeryang Baek ad8c8da438 refac 2025-04-19 02:47:48 -07:00
Timothy Jaeryang Baek f0447b24ab refac: wording 2025-04-19 01:51:40 -07:00
Timothy Jaeryang Baek 63a887151f chore: pr template 2025-04-19 00:47:52 -07:00
Timothy Jaeryang Baek 0aea894e2d Create CONTRIBUTOR_LICENSE_AGREEMENT.md 2025-04-19 00:41:08 -07:00
Timothy Jaeryang Baek 8507c47459 refac: WEBUI_AUTH_SIGNOUT_REDIRECT_URL 2025-04-18 23:58:37 -07:00
Tim Jaeryang Baek 221303eec5 Merge pull request #12592 from jarrod-lowe/main
feat: Redirect on sign out
2025-04-18 23:56:38 -07:00
Tim Jaeryang Baek 87844a8042 Merge pull request #12822 from tth37/feat_external_search_loader
feat: Support for Self-Hosted/External Web Search/Loader Engines
2025-04-18 23:51:27 -07:00
Timothy Jaeryang Baek 1c18282d19 fix 2025-04-18 18:21:49 -07:00
Tim Jaeryang Baek 6101067ea4 Merge pull request #12974 from holmlund/fix/prompts-international
fix: improve international character handling in prompt commands
2025-04-18 18:19:12 -07:00
Tim Jaeryang Baek d2f3c82fd6 Merge pull request #13031 from gaby/anyio-workers
feat: Add support for configuring FastAPI/AnyIO Thread Pool
2025-04-18 17:53:35 -07:00
Tim Jaeryang Baek 27936014b0 Merge pull request #13039 from taylorwilsdon/jit_oauth_groups
feat: Just-in-Time Group Creation for OAuth Groups
2025-04-18 17:53:04 -07:00
Timothy Jaeryang Baek 8b9527d887 refac 2025-04-18 17:14:09 -07:00
Timothy Jaeryang Baek a5476ef7d8 refac 2025-04-18 17:10:44 -07:00
Juan Calderon-Perez 62f87c2e38 Use default AnyIO pool size 2025-04-18 19:21:54 -04:00
Timothy Jaeryang Baek 60d84a3aae chore: license "branding" clause 2025-04-18 14:49:20 -07:00
Tim Jaeryang Baek 3e7bce0921 Merge pull request #13036 from erandow/translate-persian-texts
feat: translate all texts to persian
2025-04-18 12:32:17 -07:00
Taylor Wilsdon 1dbf4d0461 Add ENABLE_OAUTH_GROUP_CREATION flag for JIT group creation with OAuth group updates enabled 2025-04-18 10:17:08 -07:00
Taylor Wilsdon a67e5f725f Merge branch 'dev' of github.com:taylorwilsdon/open-webui into dev 2025-04-18 10:04:42 -07:00
Erfan Asadi 527c5e73dc fix: revert اولاما to ollama after feedback 2025-04-18 18:56:01 +03:30
Erfan Asadi 2ecfd3dbed Merge branch 'dev' of https://github.com/erandow/open-webui into translate-persian-texts 2025-04-18 18:55:14 +03:30
Erfan Asadi 5989b6ca12 Merge branch 'main' of https://github.com/erandow/open-webui into translate-persian-texts 2025-04-18 18:54:35 +03:30
Erfan Asadi e3af831db1 feat: translate the remaining texts in persian 2025-04-18 18:38:06 +03:30
Juan Calderon-Perez f77f6cd1e2 Update imports to match anyio docs 2025-04-18 09:30:34 -04:00
Juan Calderon-Perez e19554e9a8 Simplify import 2025-04-18 09:26:57 -04:00
Juan Calderon-Perez 19b3d88737 Add support for configuring FastAPI/AnyIO Thread Pool Size 2025-04-18 09:22:23 -04:00
Timothy Jaeryang Baek 0d09e46281 refac 2025-04-18 05:32:11 -07:00
Timothy Jaeryang Baek 961869a60c refac 2025-04-18 05:30:50 -07:00
Timothy Jaeryang Baek b8fb4e528d enh: toast close button 2025-04-18 04:33:18 -07:00
Tim Jaeryang Baek 794360addc Merge pull request #12997 from Elmolesto/feat/knowledge-lazy-load-content
feat: Lazy load file content on click
2025-04-18 04:04:29 -07:00
Tim Jaeryang Baek fa8132903e Merge pull request #12904 from hurxxxx/feat/onedrive-orgs
feat: support Organization and School accounts in OneDrive
2025-04-18 04:03:54 -07:00
Timothy Jaeryang Baek a86a932b46 refac: deprecate event dispatcher 2025-04-18 02:38:53 -07:00
Felipe Hernandez 14fa7d16f9 feat: Cache file content 2025-04-18 11:38:15 +02:00
Timothy Jaeryang Baek 64a98b2e0e refac: manifest orientation "any" 2025-04-18 02:24:44 -07:00
Timothy Jaeryang Baek 8f67ffabbd fix: model editor system not being preserved 2025-04-18 02:23:25 -07:00
Timothy Jaeryang Baek a22be555cb refac 2025-04-18 01:26:42 -07:00
Timothy Jaeryang Baek eb80719cf0 feat: AIOHTTP_CLIENT_SESSION_SSL 2025-04-17 22:11:42 -07:00
Tim Jaeryang Baek 840dd9c89a Merge pull request #12971 from tth37/fix_source_indexing
fix: Align backend <source> tag indexing with frontend citation grouping
2025-04-17 22:08:07 -07:00
Tim Jaeryang Baek 30e84adb51 Merge pull request #12883 from Classic298/patch-1
fix: UVICORN_WORKERS __init__.py pass application as string
2025-04-17 22:07:21 -07:00
Tim Jaeryang Baek 414ebc87ee Merge pull request #12894 from Classic298/patch-2
security/fix: prevent email and password changes to the primary admin account
2025-04-17 22:06:54 -07:00
Tim Jaeryang Baek 165eb50c22 Merge pull request #12958 from Youggls/fix_search_web
fix: use run_in_threadpool for search_web to prevent blocking
2025-04-17 22:03:31 -07:00
Tim Jaeryang Baek 477ac7de86 Merge pull request #12968 from alpha-pet/feat-openapi-parameter-description
feat: support OpenAPI parameter description and enum values in tool spec
2025-04-17 22:02:50 -07:00
Timothy Jaeryang Baek 824238a620 enh: AIOHTTP_CLIENT_SESSION_TOOL_SERVER_SSL 2025-04-17 22:02:16 -07:00
Felipe Hernandez 88f97372d9 fix: Show content on upload file 2025-04-18 02:01:47 +02:00
Felipe Hernandez 0577dc2990 feat: Lazy load file content on click 2025-04-18 01:40:30 +02:00
Tim Jaeryang Baek 3b20c57ea8 Merge pull request #12956 from lukasz-pekala/feat/improve-polish-translation
fix: update Polish translations for clarity and consistency
2025-04-17 12:38:14 -07:00
Tim Jaeryang Baek 43b9bf17dc Merge pull request #12966 from damianmr/dev
i18n: Update es-ES (Spanish) translation.json
2025-04-17 12:38:03 -07:00
Tim Jaeryang Baek 246ff83864 Merge pull request #12981 from knight-upstage/dev
i18n: Update ko-KR (Korean) translation.json
2025-04-17 12:37:41 -07:00
knight-upstage cdf26d8189 i18n: Update ko-KR (Korean) translation.json 2025-04-17 09:57:22 -07:00
tth37 79bde6fa47 fix: Align backend <source> tag indexing with frontend citation grouping 2025-04-17 22:27:22 +08:00
Thomas Rehn 3dcbf1acf5 feat: support OpenAPI parameter description and enum values in tool spec 2025-04-17 15:48:55 +02:00
Damian Mendez Romera b1629d8660 i18n: Update es-ES (Spanish) translation.json 2025-04-17 13:58:00 +01:00
David Holmlund 9f8b94e45f fix: improve international character handling in prompt commands 2025-04-17 14:56:57 +02:00
Youggls 9669cd3454 fix: use run_in_threadpool for search_web to prevent blocking
Used fastapi's run_in_threadpool function to execute the search_web function,
preventing the synchronous function from blocking the entire web search process.
2025-04-17 17:23:20 +08:00
Łukasz Pękala c4edcdb12a fix: update Polish translations for clarity and consistency 2025-04-17 10:56:23 +02:00
Timothy Jaeryang Baek a6ccc48d91 refac: textarea component 2025-04-17 00:35:39 -07:00
Tim Jaeryang Baek b6caa95fac Merge pull request #12947 from gaby/qdrant-grpc
feat: Add gRPC support to Qdrant
2025-04-16 23:08:50 -07:00
Juan Calderon-Perez 6188c0c5b7 Add suport for Qdrant GRPC 2025-04-17 01:13:49 -04:00
Jarrod Lowe dfe50b4e56 Merge branch 'open-webui:main' into main 2025-04-17 15:46:24 +12:00
Taylor Wilsdon 54c6e64eec Merge branch 'dev' of github.com:taylorwilsdon/open-webui into dev 2025-04-16 13:01:04 -07:00
Tim Jaeryang Baek 465e879a78 Merge pull request #12917 from rimvel/patch-1
Update translation.json
2025-04-16 08:05:44 -07:00
Tim Jaeryang Baek 93fd5185f9 Merge pull request #12921 from aleixdorca/dev
i18n: Update catalan translation.json
2025-04-16 08:05:14 -07:00
Tim Jaeryang Baek 4ebee592f2 Merge pull request #12925 from JaworIwanowRoosi/main
fix: typos in German translation
2025-04-16 08:05:05 -07:00
JaworIwanowRoosi 6076476a74 fixed: typos in German translation 2025-04-16 14:47:15 +02:00
Aleix Dorca 59918b0f54 Update catalan translation.json 2025-04-16 11:20:03 +02:00
Rimvydas bb1dea72d4 Update translation.json
Fixed typo from "Tęstti" to "Tęsti" (continue).
2025-04-16 09:59:20 +03:00
Tim Jaeryang Baek 264a87f467 Merge pull request #12901 from Kylapaallikko/dev
i18n: Update Finnish Translation
2025-04-15 19:03:52 -07:00
Tim Jaeryang Baek 07347f518a Merge pull request #12896 from gaby/fix-12893
fix: Optional content from files routes
2025-04-15 15:54:29 -07:00
hurxxxx 84f5a52923 chore: clean up unnecessary code 2025-04-16 02:38:20 +09:00
Kylapaallikko 8a50d50038 Update translation.json 2025-04-15 19:44:20 +03:00
hurxxxx 41ef596472 Merge remote-tracking branch 'upstream/main' into feat/onedrive-orgs 2025-04-16 01:41:24 +09:00
Juan Calderon-Perez b4d0d840d1 Fix formatting of qdrant.py 2025-04-15 08:56:51 -04:00
Juan Calderon-Perez b1ef53873f Check if content is present before removing 2025-04-15 08:53:05 -04:00
Classic298 4f14b17c34 Update users.py 2025-04-15 13:50:52 +02:00
Classic298 36b2052fb0 Update __init__.py 2025-04-15 09:55:35 +02:00
Timothy Jaeryang Baek 12c7ecf0f4 refac: styling 2025-04-14 22:34:38 -07:00
Tim Jaeryang Baek e5cbef0baa Merge pull request #12868 from athoik/dev
feat: add QDRANT_ON_DISK configuration option for Qdrant integration
2025-04-14 21:31:46 -07:00
Tim Jaeryang Baek 44e2785495 Merge pull request #12859 from aindriu80/main
i18n: Updated Irish translation
2025-04-14 21:30:21 -07:00
Tim Jaeryang Baek f96c610e21 Merge pull request #12847 from panda44312/patch-13
i18n: zh-cn fix
2025-04-14 21:30:17 -07:00
Taylor Wilsdon 0fc5699533 Merge branch 'dev' of github.com:taylorwilsdon/open-webui into dev 2025-04-14 19:34:12 -04:00
Athanasios Oikonomou 575c12f80c feat: add QDRANT_ON_DISK configuration option for Qdrant integration
This commit will allow configuring the on_disk client parameter, to reduce the memory usage.
https://qdrant.tech/documentation/concepts/storage/?q=mmap#configuring-memmap-storage
Default is false, keeping vectors in memory.
2025-04-15 01:40:57 +03:00
Tim Jaeryang Baek 7e0e6177e9 Merge pull request #12865 from TiancongLx/dev
i18n: update zh-TW
2025-04-14 13:53:24 -07:00
Tiancong Li 0d388b4e54 i18n: update zh-TW 2025-04-15 03:33:30 +08:00
Aindriú Mac Giolla Eoin a3d950872c Updated Irish translations - April 2025-04-14 17:14:02 +01:00
hurxxxx 55d077b52a refactor 2025-04-15 00:35:18 +09:00
hurxxxx 2d7062fc99 fix: onedrive orgs selection 2025-04-15 00:27:59 +09:00
hurxxxx 5fd794612e add onedrive sub menu 2025-04-14 22:57:32 +09:00
Panda 21206bf4b9 i18n: zh-cn fix 2025-04-14 14:26:10 +02:00
tth37 dcb3f18e1e refac: styling 2025-04-14 18:56:47 +08:00
tth37 85f8e91288 feat: Allow admin editing external search/loader settings 2025-04-14 18:19:26 +08:00
tth37 008fec80c1 fix: Update external search/loader method to POST 2025-04-14 18:17:27 +08:00
Tim Jaeryang Baek 07d8460126 Merge pull request #12809 from open-webui/dev
0.6.5
2025-04-14 02:13:21 -07:00
Timothy Jaeryang Baek e4c7417522 refac 2025-04-14 02:12:58 -07:00
Timothy Jaeryang Baek 91523bbb07 chore: bump 2025-04-14 02:08:11 -07:00
Timothy Jaeryang Baek 49bf3207e8 chore: format 2025-04-14 02:00:52 -07:00
Timothy Jaeryang Baek ec730b08c5 chore: format 2025-04-14 02:00:46 -07:00
Timothy Jaeryang Baek 815d18b57e revert 2025-04-14 01:57:23 -07:00
Timothy Jaeryang Baek 8d33b5e063 refac 2025-04-14 01:56:52 -07:00
Timothy Jaeryang BaekandSebastian Whincop aa8db40376 enh: copy formatted option
Co-Authored-By: Sebastian Whincop <123417897+macjedi42@users.noreply.github.com>
2025-04-14 01:56:15 -07:00
Timothy Jaeryang Baek 95fca7b700 refac 2025-04-14 01:41:58 -07:00
Timothy Jaeryang Baek 87aa01a5c2 refac: styling 2025-04-14 01:40:29 -07:00
Timothy Jaeryang Baek d932fc555d enh: tts/stt user permissions 2025-04-14 01:40:22 -07:00
Timothy Jaeryang Baek ec3b8fab5b refac 2025-04-14 01:08:16 -07:00
Timothy Jaeryang Baek 403295600f refac: audio tts voice list 2025-04-14 00:20:51 -07:00
Tim Jaeryang Baek ceffac87b6 Merge pull request #12835 from weisser-dev/dev
Fix: Robust Handling of LDAP Email Attribute Types
2025-04-14 00:15:51 -07:00
Timothy Jaeryang Baek e7332fd6fe refac 2025-04-13 23:39:38 -07:00
Tim Jaeryang Baek 60596c362a Merge pull request #12815 from TomBayne/enh/vad_filter
feat/Enable vad_filter to improve quality of transcription in faster-whisper
2025-04-13 23:27:03 -07:00
Erik c749083935 Update auths.py 2025-04-14 08:12:37 +02:00
Timothy Jaeryang Baek 70718dda90 refac 2025-04-13 22:31:43 -07:00
Timothy Jaeryang Baek 2fcf5d8578 refac 2025-04-13 17:28:09 -07:00
Tim Jaeryang Baek bc2b5d3cc8 Merge pull request #12816 from SadmL/dev
[i18n] Russian locale update
2025-04-13 14:11:16 -07:00
Tim Jaeryang Baek 370f934158 Merge pull request #12819 from athoik/dev
fix: Properly handle ldap3.abstract.attribute.Attribute value
2025-04-13 14:09:56 -07:00
tth37 22f0365cef format 2025-04-14 02:05:58 +08:00
tth37 839ba22c90 feat: Backend for Self-Hosted/External Web Search/Loader Engines 2025-04-14 01:49:05 +08:00
Athanasios Oikonomou c70fedb626 Properly handle ldap3.abstract.attribute.Attribute value
This commit fixes an issue created on commit 2e7a01f30a.

Ldap object is type <class 'ldap3.abstract.attribute.Attribute'>.
So we need to return the value and verify if it's null, string or list.

Fixes #12813
2025-04-13 18:00:42 +03:00
SadmL 08bcfdb8cc [i18n] Russian locale update 2025-04-13 15:37:18 +03:00
Tom 24367d459b Enable vad_filter to improve quality of transcription in faster-whisper model. 2025-04-13 13:03:57 +01:00
Tim Jaeryang Baek 8158609c93 Merge pull request #12810 from tth37/i18n_small_fix
i18n: update zh-CN
2025-04-12 23:15:54 -07:00
tth37 70d9405e64 i18n: update zh-CN 2025-04-13 14:14:34 +08:00
Timothy Jaeryang Baek aaefba2777 chore: bump 2025-04-12 23:06:40 -07:00
Tim Jaeryang Baek aca37f592d Merge pull request #12807 from open-webui/dev
0.6.4
2025-04-12 23:01:19 -07:00
Timothy Jaeryang Baek 888b468576 fix 2025-04-12 23:00:34 -07:00
Timothy Jaeryang Baek 9348c05f1a doc: changelog 2025-04-12 22:57:13 -07:00
Timothy Jaeryang Baek 4dafbbccfc fix: rag template display issue 2025-04-12 22:55:24 -07:00
Jarrod Lowe a8bbaa61bd Merge branch 'dev' into main 2025-04-11 22:14:41 +12:00
Taylor Wilsdon 708fcdc6b7 Merge branch 'dev' of github.com:taylorwilsdon/open-webui into dev 2025-04-10 06:32:17 -04:00
Jarrod Lowe b4465987f9 Environment variable to redirect on logout 2025-04-08 13:39:00 +12:00
Taylor Wilsdon 66bf6d88bc Merge branch 'dev' of github.com:taylorwilsdon/open-webui into dev 2025-04-07 10:17:57 -04:00
Taylor Wilsdon 0a2747bc6a Merge branch 'dev' of github.com:taylorwilsdon/open-webui into dev 2025-04-06 13:01:39 -04:00
hdnh2006 0afe972bc6 embeddings function added 100% OpenAI compatible 2025-02-14 17:57:47 +01:00
730 changed files with 138879 additions and 44390 deletions
+9
View File
@@ -7,6 +7,15 @@ OPENAI_API_KEY=''
# AUTOMATIC1111_BASE_URL="http://localhost:7860"
# For production, you should only need one host as
# fastapi serves the svelte-kit built frontend and backend from the same host and port.
# To test with CORS locally, you can set something like
# CORS_ALLOW_ORIGIN='http://localhost:5173;http://localhost:8080'
CORS_ALLOW_ORIGIN='*'
# For production you should set this to match the proxy configuration (127.0.0.1)
FORWARDED_ALLOW_IPS='*'
# DO NOT TRACK
SCARF_NO_ANALYTICS=true
DO_NOT_TRACK=true
+49 -1
View File
@@ -1 +1,49 @@
*.sh text eol=lf
# TypeScript
*.ts text eol=lf
*.tsx text eol=lf
# JavaScript
*.js text eol=lf
*.jsx text eol=lf
*.mjs text eol=lf
*.cjs text eol=lf
# Svelte
*.svelte text eol=lf
# HTML/CSS
*.html text eol=lf
*.css text eol=lf
*.scss text eol=lf
*.less text eol=lf
# Config files and JSON
*.json text eol=lf
*.jsonc text eol=lf
*.yml text eol=lf
*.yaml text eol=lf
*.toml text eol=lf
# Shell scripts
*.sh text eol=lf
# Markdown & docs
*.md text eol=lf
*.mdx text eol=lf
*.txt text eol=lf
# Git-related
.gitattributes text eol=lf
.gitignore text eol=lf
# Prettier and other dotfiles
.prettierrc text eol=lf
.prettierignore text eol=lf
.eslintrc text eol=lf
.eslintignore text eol=lf
.stylelintrc text eol=lf
.editorconfig text eol=lf
# Misc
*.env text eol=lf
*.lock text eol=lf
+43 -13
View File
@@ -11,7 +11,9 @@ body:
## Important Notes
- **Before submitting a bug report**: Please check the [Issues](https://github.com/open-webui/open-webui/issues) or [Discussions](https://github.com/open-webui/open-webui/discussions) sections to see if a similar issue has already been reported. If unsure, start a discussion first, as this helps us efficiently focus on improving the project.
- **Before submitting a bug report**: Please check the [Issues](https://github.com/open-webui/open-webui/issues) and [Discussions](https://github.com/open-webui/open-webui/discussions) sections to see if a similar issue has already been reported. If unsure, start a discussion first, as this helps us efficiently focus on improving the project. Duplicates may be closed without notice. **Please search for existing issues AND discussions. No matter open or closed.**
- Check for opened, **but also for (recently) CLOSED issues** as the issue you are trying to report **might already have been fixed on the dev branch!**
- **Respectful collaboration**: Open WebUI is a volunteer-driven project with a single maintainer and contributors who also have full-time jobs. Please be constructive and respectful in your communication.
@@ -19,13 +21,19 @@ body:
- **Bug Reproducibility**: If a bug cannot be reproduced using a `:main` or `:dev` Docker setup or with `pip install` on Python 3.11, community assistance may be required. In such cases, we will move it to the "[Issues](https://github.com/open-webui/open-webui/discussions/categories/issues)" Discussions section. Your help is appreciated!
- **Scope**: If you want to report a SECURITY VULNERABILITY, then do so through our [GitHub security page](https://github.com/open-webui/open-webui/security).
- type: checkboxes
id: issue-check
attributes:
label: Check Existing Issues
description: Confirm that you’ve checked for existing reports before submitting a new one.
options:
- label: I have searched the existing issues and discussions.
- label: I have searched for any existing and/or related issues.
required: true
- label: I have searched for any existing and/or related discussions.
required: true
- label: I have also searched in the CLOSED issues AND CLOSED discussions and found no related items (your issue might already be addressed on the development branch!).
required: true
- label: I am using the latest version of Open WebUI.
required: true
@@ -47,7 +55,7 @@ body:
id: open-webui-version
attributes:
label: Open WebUI Version
description: Specify the version (e.g., v0.3.11)
description: Specify the version (e.g., v0.6.26)
validations:
required: true
@@ -63,7 +71,7 @@ body:
id: operating-system
attributes:
label: Operating System
description: Specify the OS (e.g., Windows 10, macOS Sonoma, Ubuntu 22.04)
description: Specify the OS (e.g., Windows 10, macOS Sonoma, Ubuntu 22.04, Debian 12)
validations:
required: true
@@ -89,9 +97,20 @@ body:
required: true
- label: I have included the Docker container logs.
required: true
- label: I have listed steps to reproduce the bug in detail.
- label: I have **provided every relevant configuration, setting, and environment variable used in my setup.**
required: true
- label: I have clearly **listed every relevant configuration, custom setting, environment variable, and command-line option that influences my setup** (such as Docker Compose overrides, .env values, browser settings, authentication configurations, etc).
required: true
- label: |
I have documented **step-by-step reproduction instructions that are precise, sequential, and leave nothing to interpretation**. My steps:
- Start with the initial platform/version/OS and dependencies used,
- Specify exact install/launch/configure commands,
- List URLs visited, user input (incl. example values/emails/passwords if needed),
- Describe all options and toggles enabled or changed,
- Include any files or environmental changes,
- Identify the expected and actual result at each stage,
- Ensure any reasonably skilled user can follow and hit the same issue.
required: true
- type: textarea
id: expected-behavior
attributes:
@@ -112,15 +131,26 @@ body:
id: reproduction-steps
attributes:
label: Steps to Reproduce
description: Providing clear, step-by-step instructions helps us reproduce and fix the issue faster. If we can't reproduce it, we can't fix it.
description: |
Please provide a **very detailed, step-by-step guide** to reproduce the issue. Your instructions should be so clear and precise that anyone can follow them without guesswork. Include every relevant detail—settings, configuration options, exact commands used, values entered, and any prerequisites or environment variables.
**If full reproduction steps and all relevant settings are not provided, your issue may not be addressed.**
**If your steps to reproduction are incomplete, lacking detail or not reproducible, your issue can not be addressed.**
placeholder: |
1. Go to '...'
2. Click on '...'
3. Scroll down to '...'
4. See the error message '...'
Example (include every detail):
1. Start with a clean Ubuntu 22.04 install.
2. Install Docker v24.0.5 and start the service.
3. Clone the Open WebUI repo (git clone ...).
4. Use the Docker Compose file without modifications.
5. Open browser Chrome 115.0 in incognito mode.
6. Go to http://localhost:8080 and log in with user "test@example.com".
7. Set the language to "English" and theme to "Dark".
8. Attempt to connect to Ollama at "http://localhost:11434".
9. Observe that the error message "Connection refused" appears at the top right.
Please list each step carefully and include all relevant configuration, settings, and options.
validations:
required: true
- type: textarea
id: logs-screenshots
attributes:
@@ -142,5 +172,5 @@ body:
attributes:
value: |
## Note
If the bug report is incomplete or does not follow instructions, it may not be addressed. Ensure that you've followed all the **README.md** and **troubleshooting.md** guidelines, and provide all necessary information for us to reproduce the issue.
**If the bug report is incomplete, does not follow instructions or is lacking details it may not be addressed.** Ensure that you've followed all the **README.md** and **troubleshooting.md** guidelines, and provide all necessary information for us to reproduce the issue.
Thank you for contributing to Open WebUI!
+23 -5
View File
@@ -8,10 +8,21 @@ body:
value: |
## Important Notes
### Before submitting
Please check the [Issues](https://github.com/open-webui/open-webui/issues) or [Discussions](https://github.com/open-webui/open-webui/discussions) to see if a similar request has been posted.
Please check the **open AND closed** [Issues](https://github.com/open-webui/open-webui/issues) AND [Discussions](https://github.com/open-webui/open-webui/discussions) to see if a similar request has been posted.
It's likely we're already tracking it! If you’re unsure, start a discussion post first.
This will help us efficiently focus on improving the project.
#### Scope
If your feature request is likely to take more than a quick coding session to implement, test and verify, then open it in the **Ideas** section of the [Discussions](https://github.com/open-webui/open-webui/discussions) instead.
**We will close and force move your feature request to the Ideas section, if we believe your feature request is not trivial/quick to implement.**
This is to ensure the issues tab is used only for issues, quickly addressable feature requests and tracking tickets by the maintainers.
Other feature requests belong in the **Ideas** section of the [Discussions](https://github.com/open-webui/open-webui/discussions).
If your feature request might impact others in the community, definitely open a discussion instead and evaluate whether and how to implement it.
This will help us efficiently focus on improving the project.
### Collaborate respectfully
We value a **constructive attitude**, so please be mindful of your communication. If negativity is part of your approach, our capacity to engage may be limited. We're here to help if you're **open to learning** and **communicating positively**.
@@ -22,7 +33,6 @@ body:
We appreciate your time and ask that you **respect ours**.
### Contributing
If you encounter an issue, we highly encourage you to submit a pull request or fork the project. We actively work to prevent contributor burnout to maintain the quality and continuity of Open WebUI.
@@ -35,14 +45,22 @@ body:
label: Check Existing Issues
description: Please confirm that you've checked for existing similar requests
options:
- label: I have searched the existing issues and discussions.
- label: I have searched for all existing **open AND closed** issues and discussions for similar requests. I have found none that is comparable to my request.
required: true
- type: checkboxes
id: feature-scope
attributes:
label: Verify Feature Scope
description: Please confirm the feature's scope is within the described scope
options:
- label: I have read through and understood the scope definition for feature requests in the Issues section. I believe my feature request meets the definition and belongs in the Issues section instead of the Discussions.
required: true
- type: textarea
id: problem-description
attributes:
label: Problem Description
description: Is your feature request related to a problem? Please provide a clear and concise description of what the problem is.
placeholder: "Ex. I'm always frustrated when..."
placeholder: "Ex. I'm always frustrated when... / Not related to a problem"
validations:
required: true
- type: textarea
-6
View File
@@ -12,12 +12,6 @@ updates:
interval: monthly
target-branch: 'dev'
- package-ecosystem: npm
directory: '/'
schedule:
interval: monthly
target-branch: 'dev'
- package-ecosystem: 'github-actions'
directory: '/'
schedule:
+16 -6
View File
@@ -1,17 +1,20 @@
# Pull Request Checklist
### Note to first-time contributors: Please open a discussion post in [Discussions](https://github.com/open-webui/open-webui/discussions) and describe your changes before submitting a pull request.
### Note to first-time contributors: Please open a discussion post in [Discussions](https://github.com/open-webui/open-webui/discussions) to discuss your idea/fix with the community before creating a pull request, and describe your changes before submitting a pull request.
This is to ensure large feature PRs are discussed with the community first, before starting work on it. If the community does not want this feature or it is not relevant for Open WebUI as a project, it can be identified in the discussion before working on the feature and submitting the PR.
**Before submitting, make sure you've checked the following:**
- [ ] **Target branch:** Please verify that the pull request targets the `dev` branch.
- [ ] **Description:** Provide a concise description of the changes made in this pull request.
- [ ] **Target branch:** Verify that the pull request targets the `dev` branch. **Not targeting the `dev` branch will lead to immediate closure of the PR.**
- [ ] **Description:** Provide a concise description of the changes made in this pull request down below.
- [ ] **Changelog:** Ensure a changelog entry following the format of [Keep a Changelog](https://keepachangelog.com/) is added at the bottom of the PR description.
- [ ] **Documentation:** Have you updated relevant documentation [Open WebUI Docs](https://github.com/open-webui/docs), or other documentation sources?
- [ ] **Documentation:** If necessary, update relevant documentation [Open WebUI Docs](https://github.com/open-webui/docs) like environment variables, the tutorials, or other documentation sources.
- [ ] **Dependencies:** Are there any new dependencies? Have you updated the dependency versions in the documentation?
- [ ] **Testing:** Have you written and run sufficient tests to validate the changes?
- [ ] **Testing:** Perform manual tests to **verify the implemented fix/feature works as intended AND does not break any other functionality**. Take this as an opportunity to **make screenshots of the feature/fix and include it in the PR description**.
- [ ] **Agentic AI Code:** Confirm this Pull Request is **not written by any AI Agent** or has at least **gone through additional human review AND manual testing**. If any AI Agent is the co-author of this PR, it may lead to immediate closure of the PR.
- [ ] **Code review:** Have you performed a self-review of your code, addressing any coding standard issues and ensuring adherence to the project's coding standards?
- [ ] **Prefix:** To clearly categorize this pull request, prefix the pull request title using one of the following:
- [ ] **Title Prefix:** To clearly categorize this pull request, prefix the pull request title using one of the following:
- **BREAKING CHANGE**: Significant changes that may affect compatibility
- **build**: Changes that affect the build system or external dependencies
- **ci**: Changes to our continuous integration processes or workflows
@@ -70,3 +73,10 @@
### Screenshots or Videos
- [Attach any relevant screenshots or videos demonstrating the changes]
### Contributor License Agreement
By submitting this pull request, I confirm that I have read and fully agree to the [Contributor License Agreement (CLA)](https://github.com/open-webui/open-webui/blob/main/CONTRIBUTOR_LICENSE_AGREEMENT), and I am providing my contributions under its terms.
> [!NOTE]
> Deleting the CLA section will lead to immediate closure of your PR and it will not be merged in.
+3 -3
View File
@@ -11,7 +11,7 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: Check for changes in package.json
run: |
@@ -36,7 +36,7 @@ jobs:
echo "::set-output name=content::$CHANGELOG_ESCAPED"
- name: Create GitHub release
uses: actions/github-script@v7
uses: actions/github-script@v8
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
@@ -61,7 +61,7 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Trigger Docker build workflow
uses: actions/github-script@v7
uses: actions/github-script@v8
with:
script: |
github.rest.actions.createWorkflowDispatch({
+1 -1
View File
@@ -27,7 +27,7 @@ jobs:
HF_TOKEN: ${{ secrets.HF_TOKEN }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
lfs: true
+348 -19
View File
@@ -14,16 +14,18 @@ env:
jobs:
build-main-image:
runs-on: ${{ matrix.platform == 'linux/arm64' && 'ubuntu-24.04-arm' || 'ubuntu-latest' }}
runs-on: ${{ matrix.runner }}
permissions:
contents: read
packages: write
strategy:
fail-fast: false
matrix:
platform:
- linux/amd64
- linux/arm64
include:
- platform: linux/amd64
runner: ubuntu-latest
- platform: linux/arm64
runner: ubuntu-24.04-arm
steps:
# GitHub Packages requires the entire repository name to be in lowercase
@@ -41,7 +43,7 @@ jobs:
echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
@@ -111,16 +113,18 @@ jobs:
retention-days: 1
build-cuda-image:
runs-on: ${{ matrix.platform == 'linux/arm64' && 'ubuntu-24.04-arm' || 'ubuntu-latest' }}
runs-on: ${{ matrix.runner }}
permissions:
contents: read
packages: write
strategy:
fail-fast: false
matrix:
platform:
- linux/amd64
- linux/arm64
include:
- platform: linux/amd64
runner: ubuntu-latest
- platform: linux/arm64
runner: ubuntu-24.04-arm
steps:
# GitHub Packages requires the entire repository name to be in lowercase
@@ -137,8 +141,11 @@ jobs:
platform=${{ matrix.platform }}
echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
- name: Delete huge unnecessary tools folder
run: rm -rf /opt/hostedtoolcache
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
@@ -210,17 +217,125 @@ jobs:
if-no-files-found: error
retention-days: 1
build-ollama-image:
runs-on: ${{ matrix.platform == 'linux/arm64' && 'ubuntu-24.04-arm' || 'ubuntu-latest' }}
build-cuda126-image:
runs-on: ${{ matrix.runner }}
permissions:
contents: read
packages: write
strategy:
fail-fast: false
matrix:
platform:
- linux/amd64
- linux/arm64
include:
- platform: linux/amd64
runner: ubuntu-latest
- platform: linux/arm64
runner: ubuntu-24.04-arm
steps:
# GitHub Packages requires the entire repository name to be in lowercase
# although the repository owner has a lowercase username, this prevents some people from running actions after forking
- name: Set repository and image name to lowercase
run: |
echo "IMAGE_NAME=${IMAGE_NAME,,}" >>${GITHUB_ENV}
echo "FULL_IMAGE_NAME=ghcr.io/${IMAGE_NAME,,}" >>${GITHUB_ENV}
env:
IMAGE_NAME: '${{ github.repository }}'
- name: Prepare
run: |
platform=${{ matrix.platform }}
echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
- name: Delete huge unnecessary tools folder
run: rm -rf /opt/hostedtoolcache
- name: Checkout repository
uses: actions/checkout@v5
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to the Container registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata for Docker images (cuda126 tag)
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.FULL_IMAGE_NAME }}
tags: |
type=ref,event=branch
type=ref,event=tag
type=sha,prefix=git-
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=raw,enable=${{ github.ref == 'refs/heads/main' }},prefix=,suffix=,value=cuda126
flavor: |
latest=${{ github.ref == 'refs/heads/main' }}
suffix=-cuda126,onlatest=true
- name: Extract metadata for Docker cache
id: cache-meta
uses: docker/metadata-action@v5
with:
images: ${{ env.FULL_IMAGE_NAME }}
tags: |
type=ref,event=branch
${{ github.ref_type == 'tag' && 'type=raw,value=main' || '' }}
flavor: |
prefix=cache-cuda126-${{ matrix.platform }}-
latest=false
- name: Build Docker image (cuda126)
uses: docker/build-push-action@v5
id: build
with:
context: .
push: true
platforms: ${{ matrix.platform }}
labels: ${{ steps.meta.outputs.labels }}
outputs: type=image,name=${{ env.FULL_IMAGE_NAME }},push-by-digest=true,name-canonical=true,push=true
cache-from: type=registry,ref=${{ steps.cache-meta.outputs.tags }}
cache-to: type=registry,ref=${{ steps.cache-meta.outputs.tags }},mode=max
build-args: |
BUILD_HASH=${{ github.sha }}
USE_CUDA=true
USE_CUDA_VER=cu126
- name: Export digest
run: |
mkdir -p /tmp/digests
digest="${{ steps.build.outputs.digest }}"
touch "/tmp/digests/${digest#sha256:}"
- name: Upload digest
uses: actions/upload-artifact@v4
with:
name: digests-cuda126-${{ env.PLATFORM_PAIR }}
path: /tmp/digests/*
if-no-files-found: error
retention-days: 1
build-ollama-image:
runs-on: ${{ matrix.runner }}
permissions:
contents: read
packages: write
strategy:
fail-fast: false
matrix:
include:
- platform: linux/amd64
runner: ubuntu-latest
- platform: linux/arm64
runner: ubuntu-24.04-arm
steps:
# GitHub Packages requires the entire repository name to be in lowercase
@@ -238,7 +353,7 @@ jobs:
echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
@@ -310,6 +425,108 @@ jobs:
if-no-files-found: error
retention-days: 1
build-slim-image:
runs-on: ${{ matrix.runner }}
permissions:
contents: read
packages: write
strategy:
fail-fast: false
matrix:
include:
- platform: linux/amd64
runner: ubuntu-latest
- platform: linux/arm64
runner: ubuntu-24.04-arm
steps:
# GitHub Packages requires the entire repository name to be in lowercase
# although the repository owner has a lowercase username, this prevents some people from running actions after forking
- name: Set repository and image name to lowercase
run: |
echo "IMAGE_NAME=${IMAGE_NAME,,}" >>${GITHUB_ENV}
echo "FULL_IMAGE_NAME=ghcr.io/${IMAGE_NAME,,}" >>${GITHUB_ENV}
env:
IMAGE_NAME: '${{ github.repository }}'
- name: Prepare
run: |
platform=${{ matrix.platform }}
echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
- name: Checkout repository
uses: actions/checkout@v5
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to the Container registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata for Docker images (slim tag)
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.FULL_IMAGE_NAME }}
tags: |
type=ref,event=branch
type=ref,event=tag
type=sha,prefix=git-
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=raw,enable=${{ github.ref == 'refs/heads/main' }},prefix=,suffix=,value=slim
flavor: |
latest=${{ github.ref == 'refs/heads/main' }}
suffix=-slim,onlatest=true
- name: Extract metadata for Docker cache
id: cache-meta
uses: docker/metadata-action@v5
with:
images: ${{ env.FULL_IMAGE_NAME }}
tags: |
type=ref,event=branch
${{ github.ref_type == 'tag' && 'type=raw,value=main' || '' }}
flavor: |
prefix=cache-slim-${{ matrix.platform }}-
latest=false
- name: Build Docker image (slim)
uses: docker/build-push-action@v5
id: build
with:
context: .
push: true
platforms: ${{ matrix.platform }}
labels: ${{ steps.meta.outputs.labels }}
outputs: type=image,name=${{ env.FULL_IMAGE_NAME }},push-by-digest=true,name-canonical=true,push=true
cache-from: type=registry,ref=${{ steps.cache-meta.outputs.tags }}
cache-to: type=registry,ref=${{ steps.cache-meta.outputs.tags }},mode=max
build-args: |
BUILD_HASH=${{ github.sha }}
USE_SLIM=true
- name: Export digest
run: |
mkdir -p /tmp/digests
digest="${{ steps.build.outputs.digest }}"
touch "/tmp/digests/${digest#sha256:}"
- name: Upload digest
uses: actions/upload-artifact@v4
with:
name: digests-slim-${{ env.PLATFORM_PAIR }}
path: /tmp/digests/*
if-no-files-found: error
retention-days: 1
merge-main-images:
runs-on: ubuntu-latest
needs: [build-main-image]
@@ -324,7 +541,7 @@ jobs:
IMAGE_NAME: '${{ github.repository }}'
- name: Download digests
uses: actions/download-artifact@v4
uses: actions/download-artifact@v5
with:
pattern: digests-main-*
path: /tmp/digests
@@ -378,7 +595,7 @@ jobs:
IMAGE_NAME: '${{ github.repository }}'
- name: Download digests
uses: actions/download-artifact@v4
uses: actions/download-artifact@v5
with:
pattern: digests-cuda-*
path: /tmp/digests
@@ -420,6 +637,62 @@ jobs:
run: |
docker buildx imagetools inspect ${{ env.FULL_IMAGE_NAME }}:${{ steps.meta.outputs.version }}
merge-cuda126-images:
runs-on: ubuntu-latest
needs: [build-cuda126-image]
steps:
# GitHub Packages requires the entire repository name to be in lowercase
# although the repository owner has a lowercase username, this prevents some people from running actions after forking
- name: Set repository and image name to lowercase
run: |
echo "IMAGE_NAME=${IMAGE_NAME,,}" >>${GITHUB_ENV}
echo "FULL_IMAGE_NAME=ghcr.io/${IMAGE_NAME,,}" >>${GITHUB_ENV}
env:
IMAGE_NAME: '${{ github.repository }}'
- name: Download digests
uses: actions/download-artifact@v5
with:
pattern: digests-cuda126-*
path: /tmp/digests
merge-multiple: true
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to the Container registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata for Docker images (default latest tag)
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.FULL_IMAGE_NAME }}
tags: |
type=ref,event=branch
type=ref,event=tag
type=sha,prefix=git-
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=raw,enable=${{ github.ref == 'refs/heads/main' }},prefix=,suffix=,value=cuda126
flavor: |
latest=${{ github.ref == 'refs/heads/main' }}
suffix=-cuda126,onlatest=true
- name: Create manifest list and push
working-directory: /tmp/digests
run: |
docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
$(printf '${{ env.FULL_IMAGE_NAME }}@sha256:%s ' *)
- name: Inspect image
run: |
docker buildx imagetools inspect ${{ env.FULL_IMAGE_NAME }}:${{ steps.meta.outputs.version }}
merge-ollama-images:
runs-on: ubuntu-latest
needs: [build-ollama-image]
@@ -434,7 +707,7 @@ jobs:
IMAGE_NAME: '${{ github.repository }}'
- name: Download digests
uses: actions/download-artifact@v4
uses: actions/download-artifact@v5
with:
pattern: digests-ollama-*
path: /tmp/digests
@@ -475,3 +748,59 @@ jobs:
- name: Inspect image
run: |
docker buildx imagetools inspect ${{ env.FULL_IMAGE_NAME }}:${{ steps.meta.outputs.version }}
merge-slim-images:
runs-on: ubuntu-latest
needs: [build-slim-image]
steps:
# GitHub Packages requires the entire repository name to be in lowercase
# although the repository owner has a lowercase username, this prevents some people from running actions after forking
- name: Set repository and image name to lowercase
run: |
echo "IMAGE_NAME=${IMAGE_NAME,,}" >>${GITHUB_ENV}
echo "FULL_IMAGE_NAME=ghcr.io/${IMAGE_NAME,,}" >>${GITHUB_ENV}
env:
IMAGE_NAME: '${{ github.repository }}'
- name: Download digests
uses: actions/download-artifact@v5
with:
pattern: digests-slim-*
path: /tmp/digests
merge-multiple: true
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to the Container registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata for Docker images (default slim tag)
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.FULL_IMAGE_NAME }}
tags: |
type=ref,event=branch
type=ref,event=tag
type=sha,prefix=git-
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=raw,enable=${{ github.ref == 'refs/heads/main' }},prefix=,suffix=,value=slim
flavor: |
latest=${{ github.ref == 'refs/heads/main' }}
suffix=-slim,onlatest=true
- name: Create manifest list and push
working-directory: /tmp/digests
run: |
docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
$(printf '${{ env.FULL_IMAGE_NAME }}@sha256:%s ' *)
- name: Inspect image
run: |
docker buildx imagetools inspect ${{ env.FULL_IMAGE_NAME }}:${{ steps.meta.outputs.version }}
+2 -2
View File
@@ -30,10 +30,10 @@ jobs:
- 3.12.x
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- name: Set up Python
uses: actions/setup-python@v5
uses: actions/setup-python@v6
with:
python-version: '${{ matrix.python-version }}'
+6 -6
View File
@@ -24,15 +24,15 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: Setup Node.js
uses: actions/setup-node@v4
uses: actions/setup-node@v5
with:
node-version: '22'
- name: Install Dependencies
run: npm install
run: npm install --force
- name: Format Frontend
run: npm run format
@@ -51,15 +51,15 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: Setup Node.js
uses: actions/setup-node@v4
uses: actions/setup-node@v5
with:
node-version: '22'
- name: Install Dependencies
run: npm ci
run: npm ci --force
- name: Run vitest
run: npm run test:frontend
+7 -3
View File
@@ -16,11 +16,15 @@ jobs:
id-token: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
- uses: actions/setup-node@v4
uses: actions/checkout@v5
with:
fetch-depth: 0
- name: Install Git
run: sudo apt-get update && sudo apt-get install -y git
- uses: actions/setup-node@v5
with:
node-version: 22
- uses: actions/setup-python@v5
- uses: actions/setup-python@v6
with:
python-version: 3.11
- name: Build
+4 -1
View File
@@ -1,3 +1,5 @@
x.py
yarn.lock
.DS_Store
node_modules
/build
@@ -12,7 +14,8 @@ vite.config.ts.timestamp-*
__pycache__/
*.py[cod]
*$py.class
.nvmrc
CLAUDE.md
# C extensions
*.so
+2 -1
View File
@@ -5,5 +5,6 @@
"printWidth": 100,
"plugins": ["prettier-plugin-svelte"],
"pluginSearchDirs": ["."],
"overrides": [{ "files": "*.svelte", "options": { "parser": "svelte" } }]
"overrides": [{ "files": "*.svelte", "options": { "parser": "svelte" } }],
"endOfLine": "lf"
}
+1353
View File
File diff suppressed because it is too large Load Diff
+3 -3
View File
@@ -2,13 +2,13 @@
## Our Pledge
As members, contributors, and leaders of this community, we pledge to make participation in our open-source project a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socioeconomic status, nationality, personal appearance, race, religion, or sexual identity and orientation.
As members, contributors, and leaders of this community, we pledge to make participation in our project a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socioeconomic status, nationality, personal appearance, race, religion, or sexual identity and orientation.
We are committed to creating and maintaining an open, respectful, and professional environment where positive contributions and meaningful discussions can flourish. By participating in this project, you agree to uphold these values and align your behavior to the standards outlined in this Code of Conduct.
## Why These Standards Are Important
Open-source projects rely on a community of volunteers dedicating their time, expertise, and effort toward a shared goal. These projects are inherently collaborative but also fragile, as the success of the project depends on the goodwill, energy, and productivity of those involved.
Projects rely on a community of volunteers dedicating their time, expertise, and effort toward a shared goal. These projects are inherently collaborative but also fragile, as the success of the project depends on the goodwill, energy, and productivity of those involved.
Maintaining a positive and respectful environment is essential to safeguarding the integrity of this project and protecting contributors' efforts. Behavior that disrupts this atmosphere—whether through hostility, entitlement, or unprofessional conduct—can severely harm the morale and productivity of the community. **Strict enforcement of these standards ensures a safe and supportive space for meaningful collaboration.**
@@ -79,7 +79,7 @@ This approach ensures that disruptive behaviors are addressed swiftly and decisi
## Why Zero Tolerance Is Necessary
Open-source projects thrive on collaboration, goodwill, and mutual respect. Toxic behaviors—such as entitlement, hostility, or persistent negativity—threaten not just individual contributors but the health of the project as a whole. Allowing such behaviors to persist robs contributors of their time, energy, and enthusiasm for the work they do.
Projects thrive on collaboration, goodwill, and mutual respect. Toxic behaviors—such as entitlement, hostility, or persistent negativity—threaten not just individual contributors but the health of the project as a whole. Allowing such behaviors to persist robs contributors of their time, energy, and enthusiasm for the work they do.
By enforcing a zero-tolerance policy, we ensure that the community remains a safe, welcoming space for all participants. These measures are not about harshness—they are about protecting contributors and fostering a productive environment where innovation can thrive.
+7
View File
@@ -0,0 +1,7 @@
# Open WebUI Contributor License Agreement
By submitting my contributions to Open WebUI, I grant Open WebUI full freedom to use my work in any way they choose, under any terms they like, both now and in the future. This approach helps ensure the project remains unified, flexible, and easy to maintain, while empowering Open WebUI to respond quickly to the needs of its users and the wider community.
Taking part in this process means my work can be seamlessly integrated and combined with others, ensuring longevity and adaptability for everyone who benefits from the Open WebUI project. This collaborative approach strengthens the project’s future and helps guarantee that improvements can always be shared and distributed in the most effective way possible.
**_To the fullest extent permitted by law, my contributions are provided on an “as is” basis, with no warranties or guarantees of any kind, and I disclaim any liability for any issues or damages arising from their use or incorporation into the project, regardless of the type of legal claim._**
-64
View File
@@ -1,64 +0,0 @@
# Run with
# caddy run --envfile ./example.env --config ./Caddyfile.localhost
#
# This is configured for
# - Automatic HTTPS (even for localhost)
# - Reverse Proxying to Ollama API Base URL (http://localhost:11434/api)
# - CORS
# - HTTP Basic Auth API Tokens (uncomment basicauth section)
# CORS Preflight (OPTIONS) + Request (GET, POST, PATCH, PUT, DELETE)
(cors-api) {
@match-cors-api-preflight method OPTIONS
handle @match-cors-api-preflight {
header {
Access-Control-Allow-Origin "{http.request.header.origin}"
Access-Control-Allow-Methods "GET, POST, PUT, PATCH, DELETE, OPTIONS"
Access-Control-Allow-Headers "Origin, Accept, Authorization, Content-Type, X-Requested-With"
Access-Control-Allow-Credentials "true"
Access-Control-Max-Age "3600"
defer
}
respond "" 204
}
@match-cors-api-request {
not {
header Origin "{http.request.scheme}://{http.request.host}"
}
header Origin "{http.request.header.origin}"
}
handle @match-cors-api-request {
header {
Access-Control-Allow-Origin "{http.request.header.origin}"
Access-Control-Allow-Methods "GET, POST, PUT, PATCH, DELETE, OPTIONS"
Access-Control-Allow-Headers "Origin, Accept, Authorization, Content-Type, X-Requested-With"
Access-Control-Allow-Credentials "true"
Access-Control-Max-Age "3600"
defer
}
}
}
# replace localhost with example.com or whatever
localhost {
## HTTP Basic Auth
## (uncomment to enable)
# basicauth {
# # see .example.env for how to generate tokens
# {env.OLLAMA_API_ID} {env.OLLAMA_API_TOKEN_DIGEST}
# }
handle /api/* {
# Comment to disable CORS
import cors-api
reverse_proxy localhost:11434
}
# Same-Origin Static Web Server
file_server {
root ./build/
}
}
+42 -27
View File
@@ -3,8 +3,10 @@
# use build args in the docker build command with --build-arg="BUILDARG=true"
ARG USE_CUDA=false
ARG USE_OLLAMA=false
ARG USE_SLIM=false
ARG USE_PERMISSION_HARDENING=false
# Tested with cu117 for CUDA 11 and cu121 for CUDA 12 (default)
ARG USE_CUDA_VER=cu121
ARG USE_CUDA_VER=cu128
# any sentence transformer model; models to use can be found at https://huggingface.co/models?library=sentence-transformers
# Leaderboard: https://huggingface.co/spaces/mteb/leaderboard
# for better performance and multilangauge support use "intfloat/multilingual-e5-large" (~2.5GB) or "intfloat/multilingual-e5-base" (~1.5GB)
@@ -24,10 +26,16 @@ ARG GID=0
FROM --platform=$BUILDPLATFORM node:22-alpine3.20 AS build
ARG BUILD_HASH
# Set Node.js options (heap limit Allocation failed - JavaScript heap out of memory)
# ENV NODE_OPTIONS="--max-old-space-size=4096"
WORKDIR /app
# to store git revision in build
RUN apk add --no-cache git
COPY package.json package-lock.json ./
RUN npm ci
RUN npm ci --force
COPY . .
ENV APP_BUILD_HASH=${BUILD_HASH}
@@ -40,6 +48,8 @@ FROM python:3.11-slim-bookworm AS base
ARG USE_CUDA
ARG USE_OLLAMA
ARG USE_CUDA_VER
ARG USE_SLIM
ARG USE_PERMISSION_HARDENING
ARG USE_EMBEDDING_MODEL
ARG USE_RERANKING_MODEL
ARG UID
@@ -51,6 +61,7 @@ ENV ENV=prod \
# pass build args to the build
USE_OLLAMA_DOCKER=${USE_OLLAMA} \
USE_CUDA_DOCKER=${USE_CUDA} \
USE_SLIM_DOCKER=${USE_SLIM} \
USE_CUDA_DOCKER_VER=${USE_CUDA_VER} \
USE_EMBEDDING_MODEL_DOCKER=${USE_EMBEDDING_MODEL} \
USE_RERANKING_MODEL_DOCKER=${USE_RERANKING_MODEL}
@@ -105,29 +116,13 @@ RUN echo -n 00000000-0000-0000-0000-000000000000 > $HOME/.cache/chroma/telemetry
# Make sure the user has access to the app and root directory
RUN chown -R $UID:$GID /app $HOME
RUN if [ "$USE_OLLAMA" = "true" ]; then \
apt-get update && \
# Install pandoc and netcat
apt-get install -y --no-install-recommends git build-essential pandoc netcat-openbsd curl && \
apt-get install -y --no-install-recommends gcc python3-dev && \
# for RAG OCR
apt-get install -y --no-install-recommends ffmpeg libsm6 libxext6 && \
# install helper tools
apt-get install -y --no-install-recommends curl jq && \
# install ollama
curl -fsSL https://ollama.com/install.sh | sh && \
# cleanup
rm -rf /var/lib/apt/lists/*; \
else \
apt-get update && \
# Install pandoc, netcat and gcc
apt-get install -y --no-install-recommends git build-essential pandoc gcc netcat-openbsd curl jq && \
apt-get install -y --no-install-recommends gcc python3-dev && \
# for RAG OCR
apt-get install -y --no-install-recommends ffmpeg libsm6 libxext6 && \
# cleanup
rm -rf /var/lib/apt/lists/*; \
fi
# Install common system dependencies
RUN apt-get update && \
apt-get install -y --no-install-recommends \
git build-essential pandoc gcc netcat-openbsd curl jq \
python3-dev \
ffmpeg libsm6 libxext6 \
&& rm -rf /var/lib/apt/lists/*
# install python dependencies
COPY --chown=$UID:$GID ./backend/requirements.txt ./requirements.txt
@@ -143,13 +138,22 @@ RUN pip3 install --no-cache-dir uv && \
else \
pip3 install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cpu --no-cache-dir && \
uv pip install --system -r requirements.txt --no-cache-dir && \
if [ "$USE_SLIM" != "true" ]; then \
python -c "import os; from sentence_transformers import SentenceTransformer; SentenceTransformer(os.environ['RAG_EMBEDDING_MODEL'], device='cpu')" && \
python -c "import os; from faster_whisper import WhisperModel; WhisperModel(os.environ['WHISPER_MODEL'], device='cpu', compute_type='int8', download_root=os.environ['WHISPER_MODEL_DIR'])"; \
python -c "import os; import tiktoken; tiktoken.get_encoding(os.environ['TIKTOKEN_ENCODING_NAME'])"; \
fi; \
chown -R $UID:$GID /app/backend/data/
fi; \
mkdir -p /app/backend/data && chown -R $UID:$GID /app/backend/data/ && \
rm -rf /var/lib/apt/lists/*;
# Install Ollama if requested
RUN if [ "$USE_OLLAMA" = "true" ]; then \
date +%s > /tmp/ollama_build_hash && \
echo "Cache broken at timestamp: `cat /tmp/ollama_build_hash`" && \
curl -fsSL https://ollama.com/install.sh | sh && \
rm -rf /var/lib/apt/lists/*; \
fi
# copy embedding weight from build
# RUN mkdir -p /root/.cache/chroma/onnx_models/all-MiniLM-L6-v2
@@ -167,6 +171,17 @@ EXPOSE 8080
HEALTHCHECK CMD curl --silent --fail http://localhost:${PORT:-8080}/health | jq -ne 'input.status == true' || exit 1
# Minimal, atomic permission hardening for OpenShift (arbitrary UID):
# - Group 0 owns /app and /root
# - Directories are group-writable and have SGID so new files inherit GID 0
RUN if [ "$USE_PERMISSION_HARDENING" = "true" ]; then \
set -eux; \
chgrp -R 0 /app /root || true; \
chmod -R g+rwX /app /root || true; \
find /app -type d -exec chmod g+s {} + || true; \
find /root -type d -exec chmod g+s {} + || true; \
fi
USER $UID:$GID
ARG BUILD_HASH
+7 -1
View File
@@ -1,4 +1,4 @@
Copyright (c) 2023-2025 Timothy Jaeryang Baek
Copyright (c) 2023-2025 Timothy Jaeryang Baek (Open WebUI)
All rights reserved.
Redistribution and use in source and binary forms, with or without
@@ -15,6 +15,12 @@ modification, are permitted provided that the following conditions are met:
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
4. Notwithstanding any other provision of this License, and as a material condition of the rights granted herein, licensees are strictly prohibited from altering, removing, obscuring, or replacing any "Open WebUI" branding, including but not limited to the name, logo, or any visual, textual, or symbolic identifiers that distinguish the software and its interfaces, in any deployment or distribution, regardless of the number of users, except as explicitly set forth in Clauses 5 and 6 below.
5. The branding restriction enumerated in Clause 4 shall not apply in the following limited circumstances: (i) deployments or distributions where the total number of end users (defined as individual natural persons with direct access to the application) does not exceed fifty (50) within any rolling thirty (30) day period; (ii) cases in which the licensee is an official contributor to the codebase—with a substantive code change successfully merged into the main branch of the official codebase maintained by the copyright holder—who has obtained specific prior written permission for branding adjustment from the copyright holder; or (iii) where the licensee has obtained a duly executed enterprise license expressly permitting such modification. For all other cases, any removal or alteration of the "Open WebUI" branding shall constitute a material breach of license.
6. All code, modifications, or derivative works incorporated into this project prior to the incorporation of this branding clause remain licensed under the BSD 3-Clause License, and prior contributors retain all BSD-3 rights therein; if any such contributor requests the removal of their BSD-3-licensed code, the copyright holder will do so, and any replacement code will be licensed under the project's primary license then in effect. By contributing after this clause's adoption, you agree to the project's Contributor License Agreement (CLA) and to these updated terms for all new contributions.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+53
View File
@@ -0,0 +1,53 @@
All code and materials created before commit `60d84a3aae9802339705826e9095e272e3c83623` are subject to the following copyright and license:
Copyright (c) 2023-2025 Timothy Jaeryang Baek
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
All code and materials created before commit `a76068d69cd59568b920dfab85dc573dbbb8f131` are subject to the following copyright and license:
MIT License
Copyright (c) 2023 Timothy Jaeryang Baek
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+11
View File
@@ -0,0 +1,11 @@
# Open WebUI Multi-License Notice
This repository contains code governed by multiple licenses based on the date and origin of contribution:
1. All code committed prior to commit a76068d69cd59568b920dfab85dc573dbbb8f131 is licensed under the MIT License (see LICENSE_HISTORY).
2. All code committed from commit a76068d69cd59568b920dfab85dc573dbbb8f131 up to and including commit 60d84a3aae9802339705826e9095e272e3c83623 is licensed under the BSD 3-Clause License (see LICENSE_HISTORY).
3. All code contributed or modified after commit 60d84a3aae9802339705826e9095e272e3c83623 is licensed under the Open WebUI License (see LICENSE).
For details on which commits are covered by which license, refer to LICENSE_HISTORY.
+26 -9
View File
@@ -7,16 +7,17 @@
![GitHub language count](https://img.shields.io/github/languages/count/open-webui/open-webui)
![GitHub top language](https://img.shields.io/github/languages/top/open-webui/open-webui)
![GitHub last commit](https://img.shields.io/github/last-commit/open-webui/open-webui?color=red)
![Hits](https://hits.seeyoufarm.com/api/count/incr/badge.svg?url=https%3A%2F%2Fgithub.com%2Follama-webui%2Follama-wbui&count_bg=%2379C83D&title_bg=%23555555&icon=&icon_color=%23E7E7E7&title=hits&edge_flat=false)
[![Discord](https://img.shields.io/badge/Discord-Open_WebUI-blue?logo=discord&logoColor=white)](https://discord.gg/5rJgQTnV4s)
[![](https://img.shields.io/static/v1?label=Sponsor&message=%E2%9D%A4&logo=GitHub&color=%23fe8e86)](https://github.com/sponsors/tjbck)
**Open WebUI is an [extensible](https://docs.openwebui.com/features/plugin/), feature-rich, and user-friendly self-hosted AI platform designed to operate entirely offline.** It supports various LLM runners like **Ollama** and **OpenAI-compatible APIs**, with **built-in inference engine** for RAG, making it a **powerful AI deployment solution**.
Passionate about open-source AI? [Join our team →](https://careers.openwebui.com/)
![Open WebUI Demo](./demo.gif)
> [!TIP]
> **Looking for an [Enterprise Plan](https://docs.openwebui.com/enterprise)?** – **[Speak with Our Sales Team Today!](mailto:sales@openwebui.com)**
> **Looking for an [Enterprise Plan](https://docs.openwebui.com/enterprise)?** – **[Speak with Our Sales Team Today!](https://docs.openwebui.com/enterprise)**
>
> Get **enhanced capabilities**, including **custom theming and branding**, **Service Level Agreement (SLA) support**, **Long-Term Support (LTS) versions**, and **more!**
@@ -36,24 +37,38 @@ For more information, be sure to check out our [Open WebUI Documentation](https:
- ✒️🔢 **Full Markdown and LaTeX Support**: Elevate your LLM experience with comprehensive Markdown and LaTeX capabilities for enriched interaction.
- 🎤📹 **Hands-Free Voice/Video Call**: Experience seamless communication with integrated hands-free voice and video call features, allowing for a more dynamic and interactive chat environment.
- 🎤📹 **Hands-Free Voice/Video Call**: Experience seamless communication with integrated hands-free voice and video call features using multiple Speech-to-Text providers (Local Whisper, OpenAI, Deepgram, Azure) and Text-to-Speech engines (Azure, ElevenLabs, OpenAI, Transformers, WebAPI), allowing for dynamic and interactive chat environments.
- 🛠️ **Model Builder**: Easily create Ollama models via the Web UI. Create and add custom characters/agents, customize chat elements, and import models effortlessly through [Open WebUI Community](https://openwebui.com/) integration.
- 🐍 **Native Python Function Calling Tool**: Enhance your LLMs with built-in code editor support in the tools workspace. Bring Your Own Function (BYOF) by simply adding your pure Python functions, enabling seamless integration with LLMs.
- 📚 **Local RAG Integration**: Dive into the future of chat interactions with groundbreaking Retrieval Augmented Generation (RAG) support. This feature seamlessly integrates document interactions into your chat experience. You can load documents directly into the chat or add files to your document library, effortlessly accessing them using the `#` command before a query.
- 💾 **Persistent Artifact Storage**: Built-in key-value storage API for artifacts, enabling features like journals, trackers, leaderboards, and collaborative tools with both personal and shared data scopes across sessions.
- 🔍 **Web Search for RAG**: Perform web searches using providers like `SearXNG`, `Google PSE`, `Brave Search`, `serpstack`, `serper`, `Serply`, `DuckDuckGo`, `TavilySearch`, `SearchApi` and `Bing` and inject the results directly into your chat experience.
- 📚 **Local RAG Integration**: Dive into the future of chat interactions with groundbreaking Retrieval Augmented Generation (RAG) support using your choice of 9 vector databases and multiple content extraction engines (Tika, Docling, Document Intelligence, Mistral OCR, External loaders). Load documents directly into chat or add files to your document library, effortlessly accessing them using the `#` command before a query.
- 🔍 **Web Search for RAG**: Perform web searches using 15+ providers including `SearXNG`, `Google PSE`, `Brave Search`, `Kagi`, `Mojeek`, `Tavily`, `Perplexity`, `serpstack`, `serper`, `Serply`, `DuckDuckGo`, `SearchApi`, `SerpApi`, `Bing`, `Jina`, `Exa`, `Sougou`, `Azure AI Search`, and `Ollama Cloud`, injecting results directly into your chat experience.
- 🌐 **Web Browsing Capability**: Seamlessly integrate websites into your chat experience using the `#` command followed by a URL. This feature allows you to incorporate web content directly into your conversations, enhancing the richness and depth of your interactions.
- 🎨 **Image Generation Integration**: Seamlessly incorporate image generation capabilities using options such as AUTOMATIC1111 API or ComfyUI (local), and OpenAI's DALL-E (external), enriching your chat experience with dynamic visual content.
- 🎨 **Image Generation & Editing Integration**: Create and edit images using multiple engines including OpenAI's DALL-E, Gemini, ComfyUI (local), and AUTOMATIC1111 (local), with support for both generation and prompt-based editing workflows.
- ⚙️ **Many Models Conversations**: Effortlessly engage with various models simultaneously, harnessing their unique strengths for optimal responses. Enhance your experience by leveraging a diverse set of models in parallel.
- 🔐 **Role-Based Access Control (RBAC)**: Ensure secure access with restricted permissions; only authorized individuals can access your Ollama, and exclusive model creation/pulling rights are reserved for administrators.
- 🗄️ **Flexible Database & Storage Options**: Choose from SQLite (with optional encryption), PostgreSQL, or configure cloud storage backends (S3, Google Cloud Storage, Azure Blob Storage) for scalable deployments.
- 🔍 **Advanced Vector Database Support**: Select from 9 vector database options including ChromaDB, PGVector, Qdrant, Milvus, Elasticsearch, OpenSearch, Pinecone, S3Vector, and Oracle 23ai for optimal RAG performance.
- 🔐 **Enterprise Authentication**: Full support for LDAP/Active Directory integration, SCIM 2.0 automated provisioning, and SSO via trusted headers alongside OAuth providers. Enterprise-grade user and group provisioning through SCIM 2.0 protocol, enabling seamless integration with identity providers like Okta, Azure AD, and Google Workspace for automated user lifecycle management.
- ☁️ **Cloud-Native Integration**: Native support for Google Drive and OneDrive/SharePoint file picking, enabling seamless document import from enterprise cloud storage.
- 📊 **Production Observability**: Built-in OpenTelemetry support for traces, metrics, and logs, enabling comprehensive monitoring with your existing observability stack.
- ⚖️ **Horizontal Scalability**: Redis-backed session management and WebSocket support for multi-worker and multi-node deployments behind load balancers.
- 🌐🌍 **Multilingual Support**: Experience Open WebUI in your preferred language with our internationalization (i18n) support. Join us in expanding our supported languages! We're actively seeking contributors!
- 🧩 **Pipelines, Open WebUI Plugin Support**: Seamlessly integrate custom logic and Python libraries into Open WebUI using [Pipelines Plugin Framework](https://github.com/open-webui/pipelines). Launch your Pipelines instance, set the OpenAI URL to the Pipelines URL, and explore endless possibilities. [Examples](https://github.com/open-webui/pipelines/tree/main/examples) include **Function Calling**, User **Rate Limiting** to control access, **Usage Monitoring** with tools like Langfuse, **Live Translation with LibreTranslate** for multilingual support, **Toxic Message Filtering** and much more.
@@ -62,9 +77,9 @@ For more information, be sure to check out our [Open WebUI Documentation](https:
Want to learn more about Open WebUI's features? Check out our [Open WebUI documentation](https://docs.openwebui.com/features) for a comprehensive overview!
## 🔗 Also Check Out Open WebUI Community!
---
Don't forget to explore our sibling project, [Open WebUI Community](https://openwebui.com/), where you can discover, download, and explore customized Modelfiles. Open WebUI Community offers a wide range of exciting possibilities for enhancing your chat interactions with Open WebUI! 🚀
We are incredibly grateful for the generous support of our sponsors. Their contributions help us to maintain and improve our project, ensuring we can continue to deliver quality work to our community. Thank you!
## How to Install 🚀
@@ -155,6 +170,8 @@ After installation, you can access Open WebUI at [http://localhost:3000](http://
We offer various installation alternatives, including non-Docker native installation methods, Docker Compose, Kustomize, and Helm. Visit our [Open WebUI Documentation](https://docs.openwebui.com/getting-started/) or join our [Discord community](https://discord.gg/5rJgQTnV4s) for comprehensive guidance.
Look at the [Local Development Guide](https://docs.openwebui.com/getting-started/advanced-topics/development) for instructions on setting up a local development environment.
### Troubleshooting
Encountering connection issues? Our [Open WebUI Documentation](https://docs.openwebui.com/troubleshooting/) has got you covered. For further assistance and to join our vibrant community, visit the [Open WebUI Discord](https://discord.gg/5rJgQTnV4s).
@@ -206,7 +223,7 @@ Discover upcoming features on our roadmap in the [Open WebUI Documentation](http
## License 📜
This project is licensed under the [BSD-3-Clause License](LICENSE) - see the [LICENSE](LICENSE) file for details. 📄
This project contains code under multiple licenses. The current codebase includes components licensed under the Open WebUI License with an additional requirement to preserve the "Open WebUI" branding, as well as prior contributions under their respective original licenses. For a detailed record of license changes and the applicable terms for each section of the code, please refer to [LICENSE_HISTORY](./LICENSE_HISTORY). For complete and updated licensing details, please see the [LICENSE](./LICENSE) and [LICENSE_HISTORY](./LICENSE_HISTORY) files.
## Support 💬
+2 -1
View File
@@ -1,2 +1,3 @@
export CORS_ALLOW_ORIGIN="http://localhost:5173;http://localhost:8080"
PORT="${PORT:-8080}"
uvicorn open_webui.main:app --port $PORT --host 0.0.0.0 --forwarded-allow-ips '*' --reload
uvicorn open_webui.main:app --port $PORT --host 0.0.0.0 --forwarded-allow-ips '*' --reload
+1 -1
View File
@@ -76,7 +76,7 @@ def serve(
from open_webui.env import UVICORN_WORKERS # Import the workers setting
uvicorn.run(
open_webui.main.app,
"open_webui.main:app",
host=host,
port=port,
forwarded_allow_ips="*",
+1 -1
View File
@@ -10,7 +10,7 @@ script_location = migrations
# sys.path path, will be prepended to sys.path if present.
# defaults to the current working directory.
prepend_sys_path = .
prepend_sys_path = ..
# timezone to use when rendering the date within the migration file
# as well as the filename.
+1366 -163
View File
File diff suppressed because it is too large Load Diff
+7 -1
View File
@@ -38,13 +38,14 @@ class ERROR_MESSAGES(str, Enum):
ID_TAKEN = "Uh-oh! This id is already registered. Please choose another id string."
MODEL_ID_TAKEN = "Uh-oh! This model id is already registered. Please choose another model id string."
NAME_TAG_TAKEN = "Uh-oh! This name tag is already registered. Please choose another name tag string."
MODEL_ID_TOO_LONG = "The model id is too long. Please make sure your model id is less than 256 characters long."
INVALID_TOKEN = (
"Your session has expired or the token is invalid. Please sign in again."
)
INVALID_CRED = "The email or password provided is incorrect. Please check for typos and try logging in again."
INVALID_EMAIL_FORMAT = "The email format you entered is invalid. Please double-check and make sure you're using a valid email address (e.g., yourname@example.com)."
INVALID_PASSWORD = (
INCORRECT_PASSWORD = (
"The password provided is incorrect. Please check for typos and try again."
)
INVALID_TRUSTED_HEADER = "Your provider has not provided a trusted header. Please contact your administrator for assistance."
@@ -104,6 +105,10 @@ class ERROR_MESSAGES(str, Enum):
)
FILE_NOT_PROCESSED = "Extracted content is not available for this file. Please ensure that the file is processed before proceeding."
INVALID_PASSWORD = lambda err="": (
err if err else "The password does not meet the required validation criteria."
)
class TASKS(str, Enum):
def __str__(self) -> str:
@@ -111,6 +116,7 @@ class TASKS(str, Enum):
DEFAULT = lambda task="": f"{task if task else 'generation'}"
TITLE_GENERATION = "title_generation"
FOLLOW_UP_GENERATION = "follow_up_generation"
TAGS_GENERATION = "tags_generation"
EMOJI_GENERATION = "emoji_generation"
QUERY_GENERATION = "query_generation"
+400 -14
View File
@@ -5,7 +5,11 @@ import os
import pkgutil
import sys
import shutil
from uuid import uuid4
from pathlib import Path
from cryptography.hazmat.primitives import serialization
import re
import markdown
from bs4 import BeautifulSoup
@@ -15,14 +19,17 @@ from open_webui.constants import ERROR_MESSAGES
# Load .env file
####################################
OPEN_WEBUI_DIR = Path(__file__).parent # the path containing this file
print(OPEN_WEBUI_DIR)
# Use .resolve() to get the canonical path, removing any '..' or '.' components
ENV_FILE_PATH = Path(__file__).resolve()
BACKEND_DIR = OPEN_WEBUI_DIR.parent # the path containing this file
BASE_DIR = BACKEND_DIR.parent # the path containing the backend/
# OPEN_WEBUI_DIR should be the directory where env.py resides (open_webui/)
OPEN_WEBUI_DIR = ENV_FILE_PATH.parent
print(BACKEND_DIR)
print(BASE_DIR)
# BACKEND_DIR is the parent of OPEN_WEBUI_DIR (backend/)
BACKEND_DIR = OPEN_WEBUI_DIR.parent
# BASE_DIR is the parent of BACKEND_DIR (open-webui-dev/)
BASE_DIR = BACKEND_DIR.parent
try:
from dotenv import find_dotenv, load_dotenv
@@ -132,6 +139,10 @@ else:
VERSION = PACKAGE_DATA["version"]
DEPLOYMENT_ID = os.environ.get("DEPLOYMENT_ID", "")
INSTANCE_ID = os.environ.get("INSTANCE_ID", str(uuid4()))
# Function to parse each section
def parse_section(section):
items = []
@@ -197,6 +208,7 @@ CHANGELOG = changelog_json
SAFE_MODE = os.environ.get("SAFE_MODE", "false").lower() == "true"
####################################
# ENABLE_FORWARD_USER_INFO_HEADERS
####################################
@@ -205,6 +217,11 @@ ENABLE_FORWARD_USER_INFO_HEADERS = (
os.environ.get("ENABLE_FORWARD_USER_INFO_HEADERS", "False").lower() == "true"
)
# Experimental feature, may be removed in future
ENABLE_STAR_SESSIONS_MIDDLEWARE = (
os.environ.get("ENABLE_STAR_SESSIONS_MIDDLEWARE", "False").lower() == "true"
)
####################################
# WEBUI_BUILD_HASH
####################################
@@ -264,21 +281,43 @@ else:
DATABASE_URL = os.environ.get("DATABASE_URL", f"sqlite:///{DATA_DIR}/webui.db")
DATABASE_TYPE = os.environ.get("DATABASE_TYPE")
DATABASE_USER = os.environ.get("DATABASE_USER")
DATABASE_PASSWORD = os.environ.get("DATABASE_PASSWORD")
DATABASE_CRED = ""
if DATABASE_USER:
DATABASE_CRED += f"{DATABASE_USER}"
if DATABASE_PASSWORD:
DATABASE_CRED += f":{DATABASE_PASSWORD}"
DB_VARS = {
"db_type": DATABASE_TYPE,
"db_cred": DATABASE_CRED,
"db_host": os.environ.get("DATABASE_HOST"),
"db_port": os.environ.get("DATABASE_PORT"),
"db_name": os.environ.get("DATABASE_NAME"),
}
if all(DB_VARS.values()):
DATABASE_URL = f"{DB_VARS['db_type']}://{DB_VARS['db_cred']}@{DB_VARS['db_host']}:{DB_VARS['db_port']}/{DB_VARS['db_name']}"
elif DATABASE_TYPE == "sqlite+sqlcipher" and not os.environ.get("DATABASE_URL"):
# Handle SQLCipher with local file when DATABASE_URL wasn't explicitly set
DATABASE_URL = f"sqlite+sqlcipher:///{DATA_DIR}/webui.db"
# Replace the postgres:// with postgresql://
if "postgres://" in DATABASE_URL:
DATABASE_URL = DATABASE_URL.replace("postgres://", "postgresql://")
DATABASE_SCHEMA = os.environ.get("DATABASE_SCHEMA", None)
DATABASE_POOL_SIZE = os.environ.get("DATABASE_POOL_SIZE", 0)
DATABASE_POOL_SIZE = os.environ.get("DATABASE_POOL_SIZE", None)
if DATABASE_POOL_SIZE == "":
DATABASE_POOL_SIZE = 0
else:
if DATABASE_POOL_SIZE != None:
try:
DATABASE_POOL_SIZE = int(DATABASE_POOL_SIZE)
except Exception:
DATABASE_POOL_SIZE = 0
DATABASE_POOL_SIZE = None
DATABASE_POOL_MAX_OVERFLOW = os.environ.get("DATABASE_POOL_MAX_OVERFLOW", 0)
@@ -310,6 +349,21 @@ else:
except Exception:
DATABASE_POOL_RECYCLE = 3600
DATABASE_ENABLE_SQLITE_WAL = (
os.environ.get("DATABASE_ENABLE_SQLITE_WAL", "False").lower() == "true"
)
DATABASE_USER_ACTIVE_STATUS_UPDATE_INTERVAL = os.environ.get(
"DATABASE_USER_ACTIVE_STATUS_UPDATE_INTERVAL", None
)
if DATABASE_USER_ACTIVE_STATUS_UPDATE_INTERVAL is not None:
try:
DATABASE_USER_ACTIVE_STATUS_UPDATE_INTERVAL = float(
DATABASE_USER_ACTIVE_STATUS_UPDATE_INTERVAL
)
except Exception:
DATABASE_USER_ACTIVE_STATUS_UPDATE_INTERVAL = 0.0
RESET_CONFIG_ON_START = (
os.environ.get("RESET_CONFIG_ON_START", "False").lower() == "true"
)
@@ -318,14 +372,29 @@ ENABLE_REALTIME_CHAT_SAVE = (
os.environ.get("ENABLE_REALTIME_CHAT_SAVE", "False").lower() == "true"
)
ENABLE_QUERIES_CACHE = os.environ.get("ENABLE_QUERIES_CACHE", "False").lower() == "true"
####################################
# REDIS
####################################
REDIS_URL = os.environ.get("REDIS_URL", "")
REDIS_CLUSTER = os.environ.get("REDIS_CLUSTER", "False").lower() == "true"
REDIS_KEY_PREFIX = os.environ.get("REDIS_KEY_PREFIX", "open-webui")
REDIS_SENTINEL_HOSTS = os.environ.get("REDIS_SENTINEL_HOSTS", "")
REDIS_SENTINEL_PORT = os.environ.get("REDIS_SENTINEL_PORT", "26379")
# Maximum number of retries for Redis operations when using Sentinel fail-over
REDIS_SENTINEL_MAX_RETRY_COUNT = os.environ.get("REDIS_SENTINEL_MAX_RETRY_COUNT", "2")
try:
REDIS_SENTINEL_MAX_RETRY_COUNT = int(REDIS_SENTINEL_MAX_RETRY_COUNT)
if REDIS_SENTINEL_MAX_RETRY_COUNT < 1:
REDIS_SENTINEL_MAX_RETRY_COUNT = 2
except ValueError:
REDIS_SENTINEL_MAX_RETRY_COUNT = 2
####################################
# UVICORN WORKERS
####################################
@@ -345,15 +414,42 @@ except ValueError:
####################################
WEBUI_AUTH = os.environ.get("WEBUI_AUTH", "True").lower() == "true"
ENABLE_INITIAL_ADMIN_SIGNUP = (
os.environ.get("ENABLE_INITIAL_ADMIN_SIGNUP", "False").lower() == "true"
)
ENABLE_SIGNUP_PASSWORD_CONFIRMATION = (
os.environ.get("ENABLE_SIGNUP_PASSWORD_CONFIRMATION", "False").lower() == "true"
)
WEBUI_AUTH_TRUSTED_EMAIL_HEADER = os.environ.get(
"WEBUI_AUTH_TRUSTED_EMAIL_HEADER", None
)
WEBUI_AUTH_TRUSTED_NAME_HEADER = os.environ.get("WEBUI_AUTH_TRUSTED_NAME_HEADER", None)
WEBUI_AUTH_TRUSTED_GROUPS_HEADER = os.environ.get(
"WEBUI_AUTH_TRUSTED_GROUPS_HEADER", None
)
ENABLE_PASSWORD_VALIDATION = (
os.environ.get("ENABLE_PASSWORD_VALIDATION", "False").lower() == "true"
)
PASSWORD_VALIDATION_REGEX_PATTERN = os.environ.get(
"PASSWORD_VALIDATION_REGEX_PATTERN",
"^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[^\w\s]).{8,}$",
)
PASSWORD_VALIDATION_REGEX_PATTERN = re.compile(PASSWORD_VALIDATION_REGEX_PATTERN)
BYPASS_MODEL_ACCESS_CONTROL = (
os.environ.get("BYPASS_MODEL_ACCESS_CONTROL", "False").lower() == "true"
)
WEBUI_AUTH_SIGNOUT_REDIRECT_URL = os.environ.get(
"WEBUI_AUTH_SIGNOUT_REDIRECT_URL", None
)
####################################
# WEBUI_SECRET_KEY
####################################
@@ -386,18 +482,186 @@ WEBUI_AUTH_COOKIE_SECURE = (
if WEBUI_AUTH and WEBUI_SECRET_KEY == "":
raise ValueError(ERROR_MESSAGES.ENV_VAR_NOT_FOUND)
ENABLE_COMPRESSION_MIDDLEWARE = (
os.environ.get("ENABLE_COMPRESSION_MIDDLEWARE", "True").lower() == "true"
)
####################################
# OAUTH Configuration
####################################
ENABLE_OAUTH_EMAIL_FALLBACK = (
os.environ.get("ENABLE_OAUTH_EMAIL_FALLBACK", "False").lower() == "true"
)
ENABLE_OAUTH_ID_TOKEN_COOKIE = (
os.environ.get("ENABLE_OAUTH_ID_TOKEN_COOKIE", "True").lower() == "true"
)
OAUTH_CLIENT_INFO_ENCRYPTION_KEY = os.environ.get(
"OAUTH_CLIENT_INFO_ENCRYPTION_KEY", WEBUI_SECRET_KEY
)
OAUTH_SESSION_TOKEN_ENCRYPTION_KEY = os.environ.get(
"OAUTH_SESSION_TOKEN_ENCRYPTION_KEY", WEBUI_SECRET_KEY
)
####################################
# SCIM Configuration
####################################
ENABLE_SCIM = (
os.environ.get("ENABLE_SCIM", os.environ.get("SCIM_ENABLED", "False")).lower()
== "true"
)
SCIM_TOKEN = os.environ.get("SCIM_TOKEN", "")
####################################
# LICENSE_KEY
####################################
LICENSE_KEY = os.environ.get("LICENSE_KEY", "")
LICENSE_BLOB = None
LICENSE_BLOB_PATH = os.environ.get("LICENSE_BLOB_PATH", DATA_DIR / "l.data")
if LICENSE_BLOB_PATH and os.path.exists(LICENSE_BLOB_PATH):
with open(LICENSE_BLOB_PATH, "rb") as f:
LICENSE_BLOB = f.read()
LICENSE_PUBLIC_KEY = os.environ.get("LICENSE_PUBLIC_KEY", "")
pk = None
if LICENSE_PUBLIC_KEY:
pk = serialization.load_pem_public_key(
f"""
-----BEGIN PUBLIC KEY-----
{LICENSE_PUBLIC_KEY}
-----END PUBLIC KEY-----
""".encode(
"utf-8"
)
)
####################################
# MODELS
####################################
MODELS_CACHE_TTL = os.environ.get("MODELS_CACHE_TTL", "1")
if MODELS_CACHE_TTL == "":
MODELS_CACHE_TTL = None
else:
try:
MODELS_CACHE_TTL = int(MODELS_CACHE_TTL)
except Exception:
MODELS_CACHE_TTL = 1
####################################
# CHAT
####################################
ENABLE_CHAT_RESPONSE_BASE64_IMAGE_URL_CONVERSION = (
os.environ.get("ENABLE_CHAT_RESPONSE_BASE64_IMAGE_URL_CONVERSION", "False").lower()
== "true"
)
CHAT_RESPONSE_STREAM_DELTA_CHUNK_SIZE = os.environ.get(
"CHAT_RESPONSE_STREAM_DELTA_CHUNK_SIZE", "1"
)
if CHAT_RESPONSE_STREAM_DELTA_CHUNK_SIZE == "":
CHAT_RESPONSE_STREAM_DELTA_CHUNK_SIZE = 1
else:
try:
CHAT_RESPONSE_STREAM_DELTA_CHUNK_SIZE = int(
CHAT_RESPONSE_STREAM_DELTA_CHUNK_SIZE
)
except Exception:
CHAT_RESPONSE_STREAM_DELTA_CHUNK_SIZE = 1
CHAT_RESPONSE_MAX_TOOL_CALL_RETRIES = os.environ.get(
"CHAT_RESPONSE_MAX_TOOL_CALL_RETRIES", "30"
)
if CHAT_RESPONSE_MAX_TOOL_CALL_RETRIES == "":
CHAT_RESPONSE_MAX_TOOL_CALL_RETRIES = 30
else:
try:
CHAT_RESPONSE_MAX_TOOL_CALL_RETRIES = int(CHAT_RESPONSE_MAX_TOOL_CALL_RETRIES)
except Exception:
CHAT_RESPONSE_MAX_TOOL_CALL_RETRIES = 30
CHAT_STREAM_RESPONSE_CHUNK_MAX_BUFFER_SIZE = os.environ.get(
"CHAT_STREAM_RESPONSE_CHUNK_MAX_BUFFER_SIZE", ""
)
if CHAT_STREAM_RESPONSE_CHUNK_MAX_BUFFER_SIZE == "":
CHAT_STREAM_RESPONSE_CHUNK_MAX_BUFFER_SIZE = None
else:
try:
CHAT_STREAM_RESPONSE_CHUNK_MAX_BUFFER_SIZE = int(
CHAT_STREAM_RESPONSE_CHUNK_MAX_BUFFER_SIZE
)
except Exception:
CHAT_STREAM_RESPONSE_CHUNK_MAX_BUFFER_SIZE = None
####################################
# WEBSOCKET SUPPORT
####################################
ENABLE_WEBSOCKET_SUPPORT = (
os.environ.get("ENABLE_WEBSOCKET_SUPPORT", "True").lower() == "true"
)
WEBSOCKET_MANAGER = os.environ.get("WEBSOCKET_MANAGER", "")
WEBSOCKET_REDIS_OPTIONS = os.environ.get("WEBSOCKET_REDIS_OPTIONS", "")
if WEBSOCKET_REDIS_OPTIONS == "":
log.debug("No WEBSOCKET_REDIS_OPTIONS provided, defaulting to None")
WEBSOCKET_REDIS_OPTIONS = None
else:
try:
WEBSOCKET_REDIS_OPTIONS = json.loads(WEBSOCKET_REDIS_OPTIONS)
except Exception:
log.warning("Invalid WEBSOCKET_REDIS_OPTIONS, defaulting to None")
WEBSOCKET_REDIS_OPTIONS = None
WEBSOCKET_REDIS_URL = os.environ.get("WEBSOCKET_REDIS_URL", REDIS_URL)
WEBSOCKET_REDIS_LOCK_TIMEOUT = os.environ.get("WEBSOCKET_REDIS_LOCK_TIMEOUT", 60)
WEBSOCKET_REDIS_CLUSTER = (
os.environ.get("WEBSOCKET_REDIS_CLUSTER", str(REDIS_CLUSTER)).lower() == "true"
)
websocket_redis_lock_timeout = os.environ.get("WEBSOCKET_REDIS_LOCK_TIMEOUT", "60")
try:
WEBSOCKET_REDIS_LOCK_TIMEOUT = int(websocket_redis_lock_timeout)
except ValueError:
WEBSOCKET_REDIS_LOCK_TIMEOUT = 60
WEBSOCKET_SENTINEL_HOSTS = os.environ.get("WEBSOCKET_SENTINEL_HOSTS", "")
WEBSOCKET_SENTINEL_PORT = os.environ.get("WEBSOCKET_SENTINEL_PORT", "26379")
WEBSOCKET_SERVER_LOGGING = (
os.environ.get("WEBSOCKET_SERVER_LOGGING", "False").lower() == "true"
)
WEBSOCKET_SERVER_ENGINEIO_LOGGING = (
os.environ.get("WEBSOCKET_SERVER_LOGGING", "False").lower() == "true"
)
WEBSOCKET_SERVER_PING_TIMEOUT = os.environ.get("WEBSOCKET_SERVER_PING_TIMEOUT", "20")
try:
WEBSOCKET_SERVER_PING_TIMEOUT = int(WEBSOCKET_SERVER_PING_TIMEOUT)
except ValueError:
WEBSOCKET_SERVER_PING_TIMEOUT = 20
WEBSOCKET_SERVER_PING_INTERVAL = os.environ.get("WEBSOCKET_SERVER_PING_INTERVAL", "25")
try:
WEBSOCKET_SERVER_PING_INTERVAL = int(WEBSOCKET_SERVER_PING_INTERVAL)
except ValueError:
WEBSOCKET_SERVER_PING_INTERVAL = 25
AIOHTTP_CLIENT_TIMEOUT = os.environ.get("AIOHTTP_CLIENT_TIMEOUT", "")
@@ -409,6 +673,11 @@ else:
except Exception:
AIOHTTP_CLIENT_TIMEOUT = 300
AIOHTTP_CLIENT_SESSION_SSL = (
os.environ.get("AIOHTTP_CLIENT_SESSION_SSL", "True").lower() == "true"
)
AIOHTTP_CLIENT_TIMEOUT_MODEL_LIST = os.environ.get(
"AIOHTTP_CLIENT_TIMEOUT_MODEL_LIST",
os.environ.get("AIOHTTP_CLIENT_TIMEOUT_OPENAI_MODEL_LIST", "10"),
@@ -437,22 +706,86 @@ else:
except Exception:
AIOHTTP_CLIENT_TIMEOUT_TOOL_SERVER_DATA = 10
AIOHTTP_CLIENT_SESSION_TOOL_SERVER_SSL = (
os.environ.get("AIOHTTP_CLIENT_SESSION_TOOL_SERVER_SSL", "True").lower() == "true"
)
####################################
# SENTENCE TRANSFORMERS
####################################
SENTENCE_TRANSFORMERS_BACKEND = os.environ.get("SENTENCE_TRANSFORMERS_BACKEND", "")
if SENTENCE_TRANSFORMERS_BACKEND == "":
SENTENCE_TRANSFORMERS_BACKEND = "torch"
SENTENCE_TRANSFORMERS_MODEL_KWARGS = os.environ.get(
"SENTENCE_TRANSFORMERS_MODEL_KWARGS", ""
)
if SENTENCE_TRANSFORMERS_MODEL_KWARGS == "":
SENTENCE_TRANSFORMERS_MODEL_KWARGS = None
else:
try:
SENTENCE_TRANSFORMERS_MODEL_KWARGS = json.loads(
SENTENCE_TRANSFORMERS_MODEL_KWARGS
)
except Exception:
SENTENCE_TRANSFORMERS_MODEL_KWARGS = None
SENTENCE_TRANSFORMERS_CROSS_ENCODER_BACKEND = os.environ.get(
"SENTENCE_TRANSFORMERS_CROSS_ENCODER_BACKEND", ""
)
if SENTENCE_TRANSFORMERS_CROSS_ENCODER_BACKEND == "":
SENTENCE_TRANSFORMERS_CROSS_ENCODER_BACKEND = "torch"
SENTENCE_TRANSFORMERS_CROSS_ENCODER_MODEL_KWARGS = os.environ.get(
"SENTENCE_TRANSFORMERS_CROSS_ENCODER_MODEL_KWARGS", ""
)
if SENTENCE_TRANSFORMERS_CROSS_ENCODER_MODEL_KWARGS == "":
SENTENCE_TRANSFORMERS_CROSS_ENCODER_MODEL_KWARGS = None
else:
try:
SENTENCE_TRANSFORMERS_CROSS_ENCODER_MODEL_KWARGS = json.loads(
SENTENCE_TRANSFORMERS_CROSS_ENCODER_MODEL_KWARGS
)
except Exception:
SENTENCE_TRANSFORMERS_CROSS_ENCODER_MODEL_KWARGS = None
####################################
# OFFLINE_MODE
####################################
ENABLE_VERSION_UPDATE_CHECK = (
os.environ.get("ENABLE_VERSION_UPDATE_CHECK", "true").lower() == "true"
)
OFFLINE_MODE = os.environ.get("OFFLINE_MODE", "false").lower() == "true"
if OFFLINE_MODE:
os.environ["HF_HUB_OFFLINE"] = "1"
ENABLE_VERSION_UPDATE_CHECK = False
####################################
# AUDIT LOGGING
####################################
# Where to store log file
AUDIT_LOGS_FILE_PATH = f"{DATA_DIR}/audit.log"
# Defaults to the DATA_DIR/audit.log. To set AUDIT_LOGS_FILE_PATH you need to
# provide the whole path, like: /app/audit.log
AUDIT_LOGS_FILE_PATH = os.getenv("AUDIT_LOGS_FILE_PATH", f"{DATA_DIR}/audit.log")
# Maximum size of a file before rotating into a new log file
AUDIT_LOG_FILE_ROTATION_SIZE = os.getenv("AUDIT_LOG_FILE_ROTATION_SIZE", "10MB")
# Comma separated list of logger names to use for audit logging
# Default is "uvicorn.access" which is the access log for Uvicorn
# You can add more logger names to this list if you want to capture more logs
AUDIT_UVICORN_LOGGER_NAMES = os.getenv(
"AUDIT_UVICORN_LOGGER_NAMES", "uvicorn.access"
).split(",")
# METADATA | REQUEST | REQUEST_RESPONSE
AUDIT_LOG_LEVEL = os.getenv("AUDIT_LOG_LEVEL", "NONE").upper()
try:
@@ -467,14 +800,40 @@ AUDIT_EXCLUDED_PATHS = os.getenv("AUDIT_EXCLUDED_PATHS", "/chats,/chat,/folders"
AUDIT_EXCLUDED_PATHS = [path.strip() for path in AUDIT_EXCLUDED_PATHS]
AUDIT_EXCLUDED_PATHS = [path.lstrip("/") for path in AUDIT_EXCLUDED_PATHS]
####################################
# OPENTELEMETRY
####################################
ENABLE_OTEL = os.environ.get("ENABLE_OTEL", "False").lower() == "true"
ENABLE_OTEL_TRACES = os.environ.get("ENABLE_OTEL_TRACES", "False").lower() == "true"
ENABLE_OTEL_METRICS = os.environ.get("ENABLE_OTEL_METRICS", "False").lower() == "true"
ENABLE_OTEL_LOGS = os.environ.get("ENABLE_OTEL_LOGS", "False").lower() == "true"
OTEL_EXPORTER_OTLP_ENDPOINT = os.environ.get(
"OTEL_EXPORTER_OTLP_ENDPOINT", "http://localhost:4317"
)
OTEL_METRICS_EXPORTER_OTLP_ENDPOINT = os.environ.get(
"OTEL_METRICS_EXPORTER_OTLP_ENDPOINT", OTEL_EXPORTER_OTLP_ENDPOINT
)
OTEL_LOGS_EXPORTER_OTLP_ENDPOINT = os.environ.get(
"OTEL_LOGS_EXPORTER_OTLP_ENDPOINT", OTEL_EXPORTER_OTLP_ENDPOINT
)
OTEL_EXPORTER_OTLP_INSECURE = (
os.environ.get("OTEL_EXPORTER_OTLP_INSECURE", "False").lower() == "true"
)
OTEL_METRICS_EXPORTER_OTLP_INSECURE = (
os.environ.get(
"OTEL_METRICS_EXPORTER_OTLP_INSECURE", str(OTEL_EXPORTER_OTLP_INSECURE)
).lower()
== "true"
)
OTEL_LOGS_EXPORTER_OTLP_INSECURE = (
os.environ.get(
"OTEL_LOGS_EXPORTER_OTLP_INSECURE", str(OTEL_EXPORTER_OTLP_INSECURE)
).lower()
== "true"
)
OTEL_SERVICE_NAME = os.environ.get("OTEL_SERVICE_NAME", "open-webui")
OTEL_RESOURCE_ATTRIBUTES = os.environ.get(
"OTEL_RESOURCE_ATTRIBUTES", ""
@@ -482,6 +841,33 @@ OTEL_RESOURCE_ATTRIBUTES = os.environ.get(
OTEL_TRACES_SAMPLER = os.environ.get(
"OTEL_TRACES_SAMPLER", "parentbased_always_on"
).lower()
OTEL_BASIC_AUTH_USERNAME = os.environ.get("OTEL_BASIC_AUTH_USERNAME", "")
OTEL_BASIC_AUTH_PASSWORD = os.environ.get("OTEL_BASIC_AUTH_PASSWORD", "")
OTEL_METRICS_BASIC_AUTH_USERNAME = os.environ.get(
"OTEL_METRICS_BASIC_AUTH_USERNAME", OTEL_BASIC_AUTH_USERNAME
)
OTEL_METRICS_BASIC_AUTH_PASSWORD = os.environ.get(
"OTEL_METRICS_BASIC_AUTH_PASSWORD", OTEL_BASIC_AUTH_PASSWORD
)
OTEL_LOGS_BASIC_AUTH_USERNAME = os.environ.get(
"OTEL_LOGS_BASIC_AUTH_USERNAME", OTEL_BASIC_AUTH_USERNAME
)
OTEL_LOGS_BASIC_AUTH_PASSWORD = os.environ.get(
"OTEL_LOGS_BASIC_AUTH_PASSWORD", OTEL_BASIC_AUTH_PASSWORD
)
OTEL_OTLP_SPAN_EXPORTER = os.environ.get(
"OTEL_OTLP_SPAN_EXPORTER", "grpc"
).lower() # grpc or http
OTEL_METRICS_OTLP_SPAN_EXPORTER = os.environ.get(
"OTEL_METRICS_OTLP_SPAN_EXPORTER", OTEL_OTLP_SPAN_EXPORTER
).lower() # grpc or http
OTEL_LOGS_OTLP_SPAN_EXPORTER = os.environ.get(
"OTEL_LOGS_OTLP_SPAN_EXPORTER", OTEL_OTLP_SPAN_EXPORTER
).lower() # grpc or http
####################################
# TOOLS/FUNCTIONS PIP OPTIONS
+92 -61
View File
@@ -19,16 +19,21 @@ from fastapi import (
from starlette.responses import Response, StreamingResponse
from open_webui.constants import ERROR_MESSAGES
from open_webui.socket.main import (
get_event_call,
get_event_emitter,
)
from open_webui.models.users import UserModel
from open_webui.models.functions import Functions
from open_webui.models.models import Models
from open_webui.utils.plugin import load_function_module_by_id
from open_webui.utils.plugin import (
load_function_module_by_id,
get_function_module_from_cache,
)
from open_webui.utils.tools import get_tools
from open_webui.utils.access_control import has_access
@@ -43,7 +48,7 @@ from open_webui.utils.misc import (
)
from open_webui.utils.payload import (
apply_model_params_to_body_openai,
apply_model_system_prompt_to_body,
apply_system_prompt_to_body,
)
@@ -53,16 +58,23 @@ log.setLevel(SRC_LOG_LEVELS["MAIN"])
def get_function_module_by_id(request: Request, pipe_id: str):
# Check if function is already loaded
if pipe_id not in request.app.state.FUNCTIONS:
function_module, _, _ = load_function_module_by_id(pipe_id)
request.app.state.FUNCTIONS[pipe_id] = function_module
else:
function_module = request.app.state.FUNCTIONS[pipe_id]
function_module, _, _ = get_function_module_from_cache(request, pipe_id)
if hasattr(function_module, "valves") and hasattr(function_module, "Valves"):
Valves = function_module.Valves
valves = Functions.get_function_valves_by_id(pipe_id)
function_module.valves = function_module.Valves(**(valves if valves else {}))
if valves:
try:
function_module.valves = Valves(
**{k: v for k, v in valves.items() if v is not None}
)
except Exception as e:
log.exception(f"Error loading valves for function {pipe_id}: {e}")
raise e
else:
function_module.valves = Valves()
return function_module
@@ -71,65 +83,75 @@ async def get_function_models(request):
pipe_models = []
for pipe in pipes:
function_module = get_function_module_by_id(request, pipe.id)
try:
function_module = get_function_module_by_id(request, pipe.id)
# Check if function is a manifold
if hasattr(function_module, "pipes"):
sub_pipes = []
has_user_valves = False
if hasattr(function_module, "UserValves"):
has_user_valves = True
# Handle pipes being a list, sync function, or async function
try:
if callable(function_module.pipes):
if asyncio.iscoroutinefunction(function_module.pipes):
sub_pipes = await function_module.pipes()
else:
sub_pipes = function_module.pipes()
else:
sub_pipes = function_module.pipes
except Exception as e:
log.exception(e)
# Check if function is a manifold
if hasattr(function_module, "pipes"):
sub_pipes = []
log.debug(
f"get_function_models: function '{pipe.id}' is a manifold of {sub_pipes}"
)
# Handle pipes being a list, sync function, or async function
try:
if callable(function_module.pipes):
if asyncio.iscoroutinefunction(function_module.pipes):
sub_pipes = await function_module.pipes()
else:
sub_pipes = function_module.pipes()
else:
sub_pipes = function_module.pipes
except Exception as e:
log.exception(e)
sub_pipes = []
for p in sub_pipes:
sub_pipe_id = f'{pipe.id}.{p["id"]}'
sub_pipe_name = p["name"]
log.debug(
f"get_function_models: function '{pipe.id}' is a manifold of {sub_pipes}"
)
if hasattr(function_module, "name"):
sub_pipe_name = f"{function_module.name}{sub_pipe_name}"
for p in sub_pipes:
sub_pipe_id = f'{pipe.id}.{p["id"]}'
sub_pipe_name = p["name"]
pipe_flag = {"type": pipe.type}
if hasattr(function_module, "name"):
sub_pipe_name = f"{function_module.name}{sub_pipe_name}"
pipe_flag = {"type": pipe.type}
pipe_models.append(
{
"id": sub_pipe_id,
"name": sub_pipe_name,
"object": "model",
"created": pipe.created_at,
"owned_by": "openai",
"pipe": pipe_flag,
"has_user_valves": has_user_valves,
}
)
else:
pipe_flag = {"type": "pipe"}
log.debug(
f"get_function_models: function '{pipe.id}' is a single pipe {{ 'id': {pipe.id}, 'name': {pipe.name} }}"
)
pipe_models.append(
{
"id": sub_pipe_id,
"name": sub_pipe_name,
"id": pipe.id,
"name": pipe.name,
"object": "model",
"created": pipe.created_at,
"owned_by": "openai",
"pipe": pipe_flag,
"has_user_valves": has_user_valves,
}
)
else:
pipe_flag = {"type": "pipe"}
log.debug(
f"get_function_models: function '{pipe.id}' is a single pipe {{ 'id': {pipe.id}, 'name': {pipe.name} }}"
)
pipe_models.append(
{
"id": pipe.id,
"name": pipe.name,
"object": "model",
"created": pipe.created_at,
"owned_by": "openai",
"pipe": pipe_flag,
}
)
except Exception as e:
log.exception(e)
continue
return pipe_models
@@ -220,6 +242,16 @@ async def generate_function_chat_completion(
__task__ = metadata.get("task", None)
__task_body__ = metadata.get("task_body", None)
oauth_token = None
try:
if request.cookies.get("oauth_session_id", None):
oauth_token = await request.app.state.oauth_manager.get_oauth_token(
user.id,
request.cookies.get("oauth_session_id", None),
)
except Exception as e:
log.error(f"Error getting OAuth token: {e}")
extra_params = {
"__event_emitter__": __event_emitter__,
"__event_call__": __event_call__,
@@ -229,16 +261,12 @@ async def generate_function_chat_completion(
"__task__": __task__,
"__task_body__": __task_body__,
"__files__": files,
"__user__": {
"id": user.id,
"email": user.email,
"name": user.name,
"role": user.role,
},
"__user__": user.model_dump() if isinstance(user, UserModel) else {},
"__metadata__": metadata,
"__oauth_token__": oauth_token,
"__request__": request,
}
extra_params["__tools__"] = get_tools(
extra_params["__tools__"] = await get_tools(
request,
tool_ids,
user,
@@ -255,8 +283,11 @@ async def generate_function_chat_completion(
form_data["model"] = model_info.base_model_id
params = model_info.params.model_dump()
form_data = apply_model_params_to_body_openai(params, form_data)
form_data = apply_model_system_prompt_to_body(params, form_data, metadata, user)
if params:
system = params.pop("system", None)
form_data = apply_model_params_to_body_openai(params, form_data)
form_data = apply_system_prompt_to_body(system, form_data, metadata, user)
pipe_id = get_pipe_id(form_data)
function_module = get_function_module_by_id(request, pipe_id)
+63 -15
View File
@@ -1,3 +1,4 @@
import os
import json
import logging
from contextlib import contextmanager
@@ -13,9 +14,10 @@ from open_webui.env import (
DATABASE_POOL_RECYCLE,
DATABASE_POOL_SIZE,
DATABASE_POOL_TIMEOUT,
DATABASE_ENABLE_SQLITE_WAL,
)
from peewee_migrate import Router
from sqlalchemy import Dialect, create_engine, MetaData, types
from sqlalchemy import Dialect, create_engine, MetaData, event, types
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import scoped_session, sessionmaker
from sqlalchemy.pool import QueuePool, NullPool
@@ -62,6 +64,9 @@ def handle_peewee_migration(DATABASE_URL):
except Exception as e:
log.error(f"Failed to initialize the database connection: {e}")
log.warning(
"Hint: If your database password contains special characters, you may need to URL-encode it."
)
raise
finally:
# Properly closing the database connection
@@ -76,25 +81,68 @@ handle_peewee_migration(DATABASE_URL)
SQLALCHEMY_DATABASE_URL = DATABASE_URL
if "sqlite" in SQLALCHEMY_DATABASE_URL:
# Handle SQLCipher URLs
if SQLALCHEMY_DATABASE_URL.startswith("sqlite+sqlcipher://"):
database_password = os.environ.get("DATABASE_PASSWORD")
if not database_password or database_password.strip() == "":
raise ValueError(
"DATABASE_PASSWORD is required when using sqlite+sqlcipher:// URLs"
)
# Extract database path from SQLCipher URL
db_path = SQLALCHEMY_DATABASE_URL.replace("sqlite+sqlcipher://", "")
if db_path.startswith("/"):
db_path = db_path[1:] # Remove leading slash for relative paths
# Create a custom creator function that uses sqlcipher3
def create_sqlcipher_connection():
import sqlcipher3
conn = sqlcipher3.connect(db_path, check_same_thread=False)
conn.execute(f"PRAGMA key = '{database_password}'")
return conn
engine = create_engine(
"sqlite://", # Dummy URL since we're using creator
creator=create_sqlcipher_connection,
echo=False,
)
log.info("Connected to encrypted SQLite database using SQLCipher")
elif "sqlite" in SQLALCHEMY_DATABASE_URL:
engine = create_engine(
SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False}
)
def on_connect(dbapi_connection, connection_record):
cursor = dbapi_connection.cursor()
if DATABASE_ENABLE_SQLITE_WAL:
cursor.execute("PRAGMA journal_mode=WAL")
else:
cursor.execute("PRAGMA journal_mode=DELETE")
cursor.close()
event.listen(engine, "connect", on_connect)
else:
if DATABASE_POOL_SIZE > 0:
engine = create_engine(
SQLALCHEMY_DATABASE_URL,
pool_size=DATABASE_POOL_SIZE,
max_overflow=DATABASE_POOL_MAX_OVERFLOW,
pool_timeout=DATABASE_POOL_TIMEOUT,
pool_recycle=DATABASE_POOL_RECYCLE,
pool_pre_ping=True,
poolclass=QueuePool,
)
if isinstance(DATABASE_POOL_SIZE, int):
if DATABASE_POOL_SIZE > 0:
engine = create_engine(
SQLALCHEMY_DATABASE_URL,
pool_size=DATABASE_POOL_SIZE,
max_overflow=DATABASE_POOL_MAX_OVERFLOW,
pool_timeout=DATABASE_POOL_TIMEOUT,
pool_recycle=DATABASE_POOL_RECYCLE,
pool_pre_ping=True,
poolclass=QueuePool,
)
else:
engine = create_engine(
SQLALCHEMY_DATABASE_URL, pool_pre_ping=True, poolclass=NullPool
)
else:
engine = create_engine(
SQLALCHEMY_DATABASE_URL, pool_pre_ping=True, poolclass=NullPool
)
engine = create_engine(SQLALCHEMY_DATABASE_URL, pool_pre_ping=True)
SessionLocal = sessionmaker(
+40 -16
View File
@@ -1,4 +1,5 @@
import logging
import os
from contextvars import ContextVar
from open_webui.env import SRC_LOG_LEVELS
@@ -43,24 +44,47 @@ class ReconnectingPostgresqlDatabase(CustomReconnectMixin, PostgresqlDatabase):
def register_connection(db_url):
db = connect(db_url, unquote_password=True)
if isinstance(db, PostgresqlDatabase):
# Enable autoconnect for SQLite databases, managed by Peewee
# Check if using SQLCipher protocol
if db_url.startswith("sqlite+sqlcipher://"):
database_password = os.environ.get("DATABASE_PASSWORD")
if not database_password or database_password.strip() == "":
raise ValueError(
"DATABASE_PASSWORD is required when using sqlite+sqlcipher:// URLs"
)
from playhouse.sqlcipher_ext import SqlCipherDatabase
# Parse the database path from SQLCipher URL
# Convert sqlite+sqlcipher:///path/to/db.sqlite to /path/to/db.sqlite
db_path = db_url.replace("sqlite+sqlcipher://", "")
if db_path.startswith("/"):
db_path = db_path[1:] # Remove leading slash for relative paths
# Use Peewee's native SqlCipherDatabase with encryption
db = SqlCipherDatabase(db_path, passphrase=database_password)
db.autoconnect = True
db.reuse_if_open = True
log.info("Connected to PostgreSQL database")
log.info("Connected to encrypted SQLite database using SQLCipher")
# Get the connection details
connection = parse(db_url, unquote_password=True)
# Use our custom database class that supports reconnection
db = ReconnectingPostgresqlDatabase(**connection)
db.connect(reuse_if_open=True)
elif isinstance(db, SqliteDatabase):
# Enable autoconnect for SQLite databases, managed by Peewee
db.autoconnect = True
db.reuse_if_open = True
log.info("Connected to SQLite database")
else:
raise ValueError("Unsupported database connection")
# Standard database connection (existing logic)
db = connect(db_url, unquote_user=True, unquote_password=True)
if isinstance(db, PostgresqlDatabase):
# Enable autoconnect for SQLite databases, managed by Peewee
db.autoconnect = True
db.reuse_if_open = True
log.info("Connected to PostgreSQL database")
# Get the connection details
connection = parse(db_url, unquote_user=True, unquote_password=True)
# Use our custom database class that supports reconnection
db = ReconnectingPostgresqlDatabase(**connection)
db.connect(reuse_if_open=True)
elif isinstance(db, SqliteDatabase):
# Enable autoconnect for SQLite databases, managed by Peewee
db.autoconnect = True
db.reuse_if_open = True
log.info("Connected to SQLite database")
else:
raise ValueError("Unsupported database connection")
return db
+973 -156
View File
File diff suppressed because it is too large Load Diff
+34 -7
View File
@@ -2,8 +2,8 @@ from logging.config import fileConfig
from alembic import context
from open_webui.models.auths import Auth
from open_webui.env import DATABASE_URL
from sqlalchemy import engine_from_config, pool
from open_webui.env import DATABASE_URL, DATABASE_PASSWORD
from sqlalchemy import engine_from_config, pool, create_engine
# this is the Alembic Config object, which provides
# access to the values within the .ini file in use.
@@ -62,11 +62,38 @@ def run_migrations_online() -> None:
and associate a connection with the context.
"""
connectable = engine_from_config(
config.get_section(config.config_ini_section, {}),
prefix="sqlalchemy.",
poolclass=pool.NullPool,
)
# Handle SQLCipher URLs
if DB_URL and DB_URL.startswith("sqlite+sqlcipher://"):
if not DATABASE_PASSWORD or DATABASE_PASSWORD.strip() == "":
raise ValueError(
"DATABASE_PASSWORD is required when using sqlite+sqlcipher:// URLs"
)
# Extract database path from SQLCipher URL
db_path = DB_URL.replace("sqlite+sqlcipher://", "")
if db_path.startswith("/"):
db_path = db_path[1:] # Remove leading slash for relative paths
# Create a custom creator function that uses sqlcipher3
def create_sqlcipher_connection():
import sqlcipher3
conn = sqlcipher3.connect(db_path, check_same_thread=False)
conn.execute(f"PRAGMA key = '{DATABASE_PASSWORD}'")
return conn
connectable = create_engine(
"sqlite://", # Dummy URL since we're using creator
creator=create_sqlcipher_connection,
echo=False,
)
else:
# Standard database connection (existing logic)
connectable = engine_from_config(
config.get_section(config.config_ini_section, {}),
prefix="sqlalchemy.",
poolclass=pool.NullPool,
)
with connectable.connect() as connection:
context.configure(connection=connection, target_metadata=target_metadata)
@@ -0,0 +1,46 @@
"""Add indexes
Revision ID: 018012973d35
Revises: d31026856c01
Create Date: 2025-08-13 03:00:00.000000
"""
from alembic import op
import sqlalchemy as sa
revision = "018012973d35"
down_revision = "d31026856c01"
branch_labels = None
depends_on = None
def upgrade():
# Chat table indexes
op.create_index("folder_id_idx", "chat", ["folder_id"])
op.create_index("user_id_pinned_idx", "chat", ["user_id", "pinned"])
op.create_index("user_id_archived_idx", "chat", ["user_id", "archived"])
op.create_index("updated_at_user_id_idx", "chat", ["updated_at", "user_id"])
op.create_index("folder_id_user_id_idx", "chat", ["folder_id", "user_id"])
# Tag table index
op.create_index("user_id_idx", "tag", ["user_id"])
# Function table index
op.create_index("is_global_idx", "function", ["is_global"])
def downgrade():
# Chat table indexes
op.drop_index("folder_id_idx", table_name="chat")
op.drop_index("user_id_pinned_idx", table_name="chat")
op.drop_index("user_id_archived_idx", table_name="chat")
op.drop_index("updated_at_user_id_idx", table_name="chat")
op.drop_index("folder_id_user_id_idx", table_name="chat")
# Tag table index
op.drop_index("user_id_idx", table_name="tag")
# Function table index
op.drop_index("is_global_idx", table_name="function")
@@ -0,0 +1,103 @@
"""Update messages and channel member table
Revision ID: 2f1211949ecc
Revises: 37f288994c47
Create Date: 2025-11-27 03:07:56.200231
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
import open_webui.internal.db
# revision identifiers, used by Alembic.
revision: str = "2f1211949ecc"
down_revision: Union[str, None] = "37f288994c47"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
# New columns to be added to channel_member table
op.add_column("channel_member", sa.Column("status", sa.Text(), nullable=True))
op.add_column(
"channel_member",
sa.Column(
"is_active",
sa.Boolean(),
nullable=False,
default=True,
server_default=sa.sql.expression.true(),
),
)
op.add_column(
"channel_member",
sa.Column(
"is_channel_muted",
sa.Boolean(),
nullable=False,
default=False,
server_default=sa.sql.expression.false(),
),
)
op.add_column(
"channel_member",
sa.Column(
"is_channel_pinned",
sa.Boolean(),
nullable=False,
default=False,
server_default=sa.sql.expression.false(),
),
)
op.add_column("channel_member", sa.Column("data", sa.JSON(), nullable=True))
op.add_column("channel_member", sa.Column("meta", sa.JSON(), nullable=True))
op.add_column(
"channel_member", sa.Column("joined_at", sa.BigInteger(), nullable=False)
)
op.add_column(
"channel_member", sa.Column("left_at", sa.BigInteger(), nullable=True)
)
op.add_column(
"channel_member", sa.Column("last_read_at", sa.BigInteger(), nullable=True)
)
op.add_column(
"channel_member", sa.Column("updated_at", sa.BigInteger(), nullable=True)
)
# New columns to be added to message table
op.add_column(
"message",
sa.Column(
"is_pinned",
sa.Boolean(),
nullable=False,
default=False,
server_default=sa.sql.expression.false(),
),
)
op.add_column("message", sa.Column("pinned_at", sa.BigInteger(), nullable=True))
op.add_column("message", sa.Column("pinned_by", sa.Text(), nullable=True))
def downgrade() -> None:
op.drop_column("channel_member", "updated_at")
op.drop_column("channel_member", "last_read_at")
op.drop_column("channel_member", "meta")
op.drop_column("channel_member", "data")
op.drop_column("channel_member", "is_channel_pinned")
op.drop_column("channel_member", "is_channel_muted")
op.drop_column("message", "pinned_by")
op.drop_column("message", "pinned_at")
op.drop_column("message", "is_pinned")
@@ -0,0 +1,146 @@
"""add_group_member_table
Revision ID: 37f288994c47
Revises: a5c220713937
Create Date: 2025-11-17 03:45:25.123939
"""
import uuid
import time
import json
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision: str = "37f288994c47"
down_revision: Union[str, None] = "a5c220713937"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
# 1. Create new table
op.create_table(
"group_member",
sa.Column("id", sa.Text(), primary_key=True, unique=True, nullable=False),
sa.Column(
"group_id",
sa.Text(),
sa.ForeignKey("group.id", ondelete="CASCADE"),
nullable=False,
),
sa.Column(
"user_id",
sa.Text(),
sa.ForeignKey("user.id", ondelete="CASCADE"),
nullable=False,
),
sa.Column("created_at", sa.BigInteger(), nullable=True),
sa.Column("updated_at", sa.BigInteger(), nullable=True),
sa.UniqueConstraint("group_id", "user_id", name="uq_group_member_group_user"),
)
connection = op.get_bind()
# 2. Read existing group with user_ids JSON column
group_table = sa.Table(
"group",
sa.MetaData(),
sa.Column("id", sa.Text()),
sa.Column("user_ids", sa.JSON()), # JSON stored as text in SQLite + PG
)
results = connection.execute(
sa.select(group_table.c.id, group_table.c.user_ids)
).fetchall()
print(results)
# 3. Insert members into group_member table
gm_table = sa.Table(
"group_member",
sa.MetaData(),
sa.Column("id", sa.Text()),
sa.Column("group_id", sa.Text()),
sa.Column("user_id", sa.Text()),
sa.Column("created_at", sa.BigInteger()),
sa.Column("updated_at", sa.BigInteger()),
)
now = int(time.time())
for group_id, user_ids in results:
if not user_ids:
continue
if isinstance(user_ids, str):
try:
user_ids = json.loads(user_ids)
except Exception:
continue # skip invalid JSON
if not isinstance(user_ids, list):
continue
rows = [
{
"id": str(uuid.uuid4()),
"group_id": group_id,
"user_id": uid,
"created_at": now,
"updated_at": now,
}
for uid in user_ids
]
if rows:
connection.execute(gm_table.insert(), rows)
# 4. Optionally drop the old column
with op.batch_alter_table("group") as batch:
batch.drop_column("user_ids")
def downgrade():
# Reverse: restore user_ids column
with op.batch_alter_table("group") as batch:
batch.add_column(sa.Column("user_ids", sa.JSON()))
connection = op.get_bind()
gm_table = sa.Table(
"group_member",
sa.MetaData(),
sa.Column("group_id", sa.Text()),
sa.Column("user_id", sa.Text()),
sa.Column("created_at", sa.BigInteger()),
sa.Column("updated_at", sa.BigInteger()),
)
group_table = sa.Table(
"group",
sa.MetaData(),
sa.Column("id", sa.Text()),
sa.Column("user_ids", sa.JSON()),
)
# Build JSON arrays again
results = connection.execute(sa.select(group_table.c.id)).fetchall()
for (group_id,) in results:
members = connection.execute(
sa.select(gm_table.c.user_id).where(gm_table.c.group_id == group_id)
).fetchall()
member_ids = [m[0] for m in members]
connection.execute(
group_table.update()
.where(group_table.c.id == group_id)
.values(user_ids=member_ids)
)
# Drop the new table
op.drop_table("group_member")
@@ -0,0 +1,80 @@
"""Add oauth_session table
Revision ID: 38d63c18f30f
Revises: 3af16a1c9fb6
Create Date: 2025-09-08 14:19:59.583921
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision: str = "38d63c18f30f"
down_revision: Union[str, None] = "3af16a1c9fb6"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
# Ensure 'id' column in 'user' table is unique and primary key (ForeignKey constraint)
inspector = sa.inspect(op.get_bind())
columns = inspector.get_columns("user")
pk_columns = inspector.get_pk_constraint("user")["constrained_columns"]
id_column = next((col for col in columns if col["name"] == "id"), None)
if id_column and not id_column.get("unique", False):
unique_constraints = inspector.get_unique_constraints("user")
unique_columns = {tuple(u["column_names"]) for u in unique_constraints}
with op.batch_alter_table("user") as batch_op:
# If primary key is wrong, drop it
if pk_columns and pk_columns != ["id"]:
batch_op.drop_constraint(
inspector.get_pk_constraint("user")["name"], type_="primary"
)
# Add unique constraint if missing
if ("id",) not in unique_columns:
batch_op.create_unique_constraint("uq_user_id", ["id"])
# Re-create correct primary key
batch_op.create_primary_key("pk_user_id", ["id"])
# Create oauth_session table
op.create_table(
"oauth_session",
sa.Column("id", sa.Text(), primary_key=True, nullable=False, unique=True),
sa.Column(
"user_id",
sa.Text(),
sa.ForeignKey("user.id", ondelete="CASCADE"),
nullable=False,
),
sa.Column("provider", sa.Text(), nullable=False),
sa.Column("token", sa.Text(), nullable=False),
sa.Column("expires_at", sa.BigInteger(), nullable=False),
sa.Column("created_at", sa.BigInteger(), nullable=False),
sa.Column("updated_at", sa.BigInteger(), nullable=False),
)
# Create indexes for better performance
op.create_index("idx_oauth_session_user_id", "oauth_session", ["user_id"])
op.create_index("idx_oauth_session_expires_at", "oauth_session", ["expires_at"])
op.create_index(
"idx_oauth_session_user_provider", "oauth_session", ["user_id", "provider"]
)
def downgrade() -> None:
# Drop indexes first
op.drop_index("idx_oauth_session_user_provider", table_name="oauth_session")
op.drop_index("idx_oauth_session_expires_at", table_name="oauth_session")
op.drop_index("idx_oauth_session_user_id", table_name="oauth_session")
# Drop the table
op.drop_table("oauth_session")
@@ -0,0 +1,32 @@
"""update user table
Revision ID: 3af16a1c9fb6
Revises: 018012973d35
Create Date: 2025-08-21 02:07:18.078283
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision: str = "3af16a1c9fb6"
down_revision: Union[str, None] = "018012973d35"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
op.add_column("user", sa.Column("username", sa.String(length=50), nullable=True))
op.add_column("user", sa.Column("bio", sa.Text(), nullable=True))
op.add_column("user", sa.Column("gender", sa.Text(), nullable=True))
op.add_column("user", sa.Column("date_of_birth", sa.Date(), nullable=True))
def downgrade() -> None:
op.drop_column("user", "username")
op.drop_column("user", "bio")
op.drop_column("user", "gender")
op.drop_column("user", "date_of_birth")
@@ -0,0 +1,169 @@
"""Add knowledge_file table
Revision ID: 3e0e00844bb0
Revises: 90ef40d4714e
Create Date: 2025-12-02 06:54:19.401334
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
from sqlalchemy import inspect
import open_webui.internal.db
import time
import json
import uuid
# revision identifiers, used by Alembic.
revision: str = "3e0e00844bb0"
down_revision: Union[str, None] = "90ef40d4714e"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
op.create_table(
"knowledge_file",
sa.Column("id", sa.Text(), primary_key=True),
sa.Column("user_id", sa.Text(), nullable=False),
sa.Column(
"knowledge_id",
sa.Text(),
sa.ForeignKey("knowledge.id", ondelete="CASCADE"),
nullable=False,
),
sa.Column(
"file_id",
sa.Text(),
sa.ForeignKey("file.id", ondelete="CASCADE"),
nullable=False,
),
sa.Column("created_at", sa.BigInteger(), nullable=False),
sa.Column("updated_at", sa.BigInteger(), nullable=False),
# indexes
sa.Index("ix_knowledge_file_knowledge_id", "knowledge_id"),
sa.Index("ix_knowledge_file_file_id", "file_id"),
sa.Index("ix_knowledge_file_user_id", "user_id"),
# unique constraints
sa.UniqueConstraint(
"knowledge_id", "file_id", name="uq_knowledge_file_knowledge_file"
), # prevent duplicate entries
)
connection = op.get_bind()
# 2. Read existing group with user_ids JSON column
knowledge_table = sa.Table(
"knowledge",
sa.MetaData(),
sa.Column("id", sa.Text()),
sa.Column("user_id", sa.Text()),
sa.Column("data", sa.JSON()), # JSON stored as text in SQLite + PG
)
results = connection.execute(
sa.select(
knowledge_table.c.id, knowledge_table.c.user_id, knowledge_table.c.data
)
).fetchall()
# 3. Insert members into group_member table
kf_table = sa.Table(
"knowledge_file",
sa.MetaData(),
sa.Column("id", sa.Text()),
sa.Column("user_id", sa.Text()),
sa.Column("knowledge_id", sa.Text()),
sa.Column("file_id", sa.Text()),
sa.Column("created_at", sa.BigInteger()),
sa.Column("updated_at", sa.BigInteger()),
)
file_table = sa.Table(
"file",
sa.MetaData(),
sa.Column("id", sa.Text()),
)
now = int(time.time())
for knowledge_id, user_id, data in results:
if not data:
continue
if isinstance(data, str):
try:
data = json.loads(data)
except Exception:
continue # skip invalid JSON
if not isinstance(data, dict):
continue
file_ids = data.get("file_ids", [])
for file_id in file_ids:
file_exists = connection.execute(
sa.select(file_table.c.id).where(file_table.c.id == file_id)
).fetchone()
if not file_exists:
continue # skip non-existing files
row = {
"id": str(uuid.uuid4()),
"user_id": user_id,
"knowledge_id": knowledge_id,
"file_id": file_id,
"created_at": now,
"updated_at": now,
}
connection.execute(kf_table.insert().values(**row))
with op.batch_alter_table("knowledge") as batch:
batch.drop_column("data")
def downgrade() -> None:
# 1. Add back the old data column
op.add_column("knowledge", sa.Column("data", sa.JSON(), nullable=True))
connection = op.get_bind()
# 2. Read knowledge_file entries and reconstruct data JSON
knowledge_table = sa.Table(
"knowledge",
sa.MetaData(),
sa.Column("id", sa.Text()),
sa.Column("data", sa.JSON()),
)
kf_table = sa.Table(
"knowledge_file",
sa.MetaData(),
sa.Column("id", sa.Text()),
sa.Column("knowledge_id", sa.Text()),
sa.Column("file_id", sa.Text()),
)
results = connection.execute(sa.select(knowledge_table.c.id)).fetchall()
for (knowledge_id,) in results:
file_ids = connection.execute(
sa.select(kf_table.c.file_id).where(kf_table.c.knowledge_id == knowledge_id)
).fetchall()
file_ids_list = [fid for (fid,) in file_ids]
data_json = {"file_ids": file_ids_list}
connection.execute(
knowledge_table.update()
.where(knowledge_table.c.id == knowledge_id)
.values(data=data_json)
)
# 3. Drop the knowledge_file table
op.drop_table("knowledge_file")
@@ -0,0 +1,81 @@
"""Update channel and channel members table
Revision ID: 90ef40d4714e
Revises: b10670c03dd5
Create Date: 2025-11-30 06:33:38.790341
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
import open_webui.internal.db
# revision identifiers, used by Alembic.
revision: str = "90ef40d4714e"
down_revision: Union[str, None] = "b10670c03dd5"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
# Update 'channel' table
op.add_column("channel", sa.Column("is_private", sa.Boolean(), nullable=True))
op.add_column("channel", sa.Column("archived_at", sa.BigInteger(), nullable=True))
op.add_column("channel", sa.Column("archived_by", sa.Text(), nullable=True))
op.add_column("channel", sa.Column("deleted_at", sa.BigInteger(), nullable=True))
op.add_column("channel", sa.Column("deleted_by", sa.Text(), nullable=True))
op.add_column("channel", sa.Column("updated_by", sa.Text(), nullable=True))
# Update 'channel_member' table
op.add_column("channel_member", sa.Column("role", sa.Text(), nullable=True))
op.add_column("channel_member", sa.Column("invited_by", sa.Text(), nullable=True))
op.add_column(
"channel_member", sa.Column("invited_at", sa.BigInteger(), nullable=True)
)
# Create 'channel_webhook' table
op.create_table(
"channel_webhook",
sa.Column("id", sa.Text(), primary_key=True, unique=True, nullable=False),
sa.Column("user_id", sa.Text(), nullable=False),
sa.Column(
"channel_id",
sa.Text(),
sa.ForeignKey("channel.id", ondelete="CASCADE"),
nullable=False,
),
sa.Column("name", sa.Text(), nullable=False),
sa.Column("profile_image_url", sa.Text(), nullable=True),
sa.Column("token", sa.Text(), nullable=False),
sa.Column("last_used_at", sa.BigInteger(), nullable=True),
sa.Column("created_at", sa.BigInteger(), nullable=False),
sa.Column("updated_at", sa.BigInteger(), nullable=False),
)
pass
def downgrade() -> None:
# Downgrade 'channel' table
op.drop_column("channel", "is_private")
op.drop_column("channel", "archived_at")
op.drop_column("channel", "archived_by")
op.drop_column("channel", "deleted_at")
op.drop_column("channel", "deleted_by")
op.drop_column("channel", "updated_by")
# Downgrade 'channel_member' table
op.drop_column("channel_member", "role")
op.drop_column("channel_member", "invited_by")
op.drop_column("channel_member", "invited_at")
# Drop 'channel_webhook' table
op.drop_table("channel_webhook")
pass
@@ -0,0 +1,33 @@
"""Add note table
Revision ID: 9f0c9cd09105
Revises: 3781e22d8b01
Create Date: 2025-05-03 03:00:00.000000
"""
from alembic import op
import sqlalchemy as sa
revision = "9f0c9cd09105"
down_revision = "3781e22d8b01"
branch_labels = None
depends_on = None
def upgrade():
op.create_table(
"note",
sa.Column("id", sa.Text(), nullable=False, primary_key=True, unique=True),
sa.Column("user_id", sa.Text(), nullable=True),
sa.Column("title", sa.Text(), nullable=True),
sa.Column("data", sa.JSON(), nullable=True),
sa.Column("meta", sa.JSON(), nullable=True),
sa.Column("access_control", sa.JSON(), nullable=True),
sa.Column("created_at", sa.BigInteger(), nullable=True),
sa.Column("updated_at", sa.BigInteger(), nullable=True),
)
def downgrade():
op.drop_table("note")
@@ -0,0 +1,34 @@
"""Add reply_to_id column to message
Revision ID: a5c220713937
Revises: 38d63c18f30f
Create Date: 2025-09-27 02:24:18.058455
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision: str = "a5c220713937"
down_revision: Union[str, None] = "38d63c18f30f"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
# Add 'reply_to_id' column to the 'message' table for replying to messages
op.add_column(
"message",
sa.Column("reply_to_id", sa.Text(), nullable=True),
)
pass
def downgrade() -> None:
# Remove 'reply_to_id' column from the 'message' table
op.drop_column("message", "reply_to_id")
pass
@@ -0,0 +1,251 @@
"""Update user table
Revision ID: b10670c03dd5
Revises: 2f1211949ecc
Create Date: 2025-11-28 04:55:31.737538
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
import open_webui.internal.db
import json
import time
# revision identifiers, used by Alembic.
revision: str = "b10670c03dd5"
down_revision: Union[str, None] = "2f1211949ecc"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def _drop_sqlite_indexes_for_column(table_name, column_name, conn):
"""
SQLite requires manual removal of any indexes referencing a column
before ALTER TABLE ... DROP COLUMN can succeed.
"""
indexes = conn.execute(sa.text(f"PRAGMA index_list('{table_name}')")).fetchall()
for idx in indexes:
index_name = idx[1] # index name
# Get indexed columns
idx_info = conn.execute(
sa.text(f"PRAGMA index_info('{index_name}')")
).fetchall()
indexed_cols = [row[2] for row in idx_info] # col names
if column_name in indexed_cols:
conn.execute(sa.text(f"DROP INDEX IF EXISTS {index_name}"))
def _convert_column_to_json(table: str, column: str):
conn = op.get_bind()
dialect = conn.dialect.name
# SQLite cannot ALTER COLUMN → must recreate column
if dialect == "sqlite":
# 1. Add temporary column
op.add_column(table, sa.Column(f"{column}_json", sa.JSON(), nullable=True))
# 2. Load old data
rows = conn.execute(sa.text(f'SELECT id, {column} FROM "{table}"')).fetchall()
for row in rows:
uid, raw = row
if raw is None:
parsed = None
else:
try:
parsed = json.loads(raw)
except Exception:
parsed = None # fallback safe behavior
conn.execute(
sa.text(f'UPDATE "{table}" SET {column}_json = :val WHERE id = :id'),
{"val": json.dumps(parsed) if parsed else None, "id": uid},
)
# 3. Drop old TEXT column
op.drop_column(table, column)
# 4. Rename new JSON column → original name
op.alter_column(table, f"{column}_json", new_column_name=column)
else:
# PostgreSQL supports direct CAST
op.alter_column(
table,
column,
type_=sa.JSON(),
postgresql_using=f"{column}::json",
)
def _convert_column_to_text(table: str, column: str):
conn = op.get_bind()
dialect = conn.dialect.name
if dialect == "sqlite":
op.add_column(table, sa.Column(f"{column}_text", sa.Text(), nullable=True))
rows = conn.execute(sa.text(f'SELECT id, {column} FROM "{table}"')).fetchall()
for uid, raw in rows:
conn.execute(
sa.text(f'UPDATE "{table}" SET {column}_text = :val WHERE id = :id'),
{"val": json.dumps(raw) if raw else None, "id": uid},
)
op.drop_column(table, column)
op.alter_column(table, f"{column}_text", new_column_name=column)
else:
op.alter_column(
table,
column,
type_=sa.Text(),
postgresql_using=f"to_json({column})::text",
)
def upgrade() -> None:
op.add_column(
"user", sa.Column("profile_banner_image_url", sa.Text(), nullable=True)
)
op.add_column("user", sa.Column("timezone", sa.String(), nullable=True))
op.add_column("user", sa.Column("presence_state", sa.String(), nullable=True))
op.add_column("user", sa.Column("status_emoji", sa.String(), nullable=True))
op.add_column("user", sa.Column("status_message", sa.Text(), nullable=True))
op.add_column(
"user", sa.Column("status_expires_at", sa.BigInteger(), nullable=True)
)
op.add_column("user", sa.Column("oauth", sa.JSON(), nullable=True))
# Convert info (TEXT/JSONField) → JSON
_convert_column_to_json("user", "info")
# Convert settings (TEXT/JSONField) → JSON
_convert_column_to_json("user", "settings")
op.create_table(
"api_key",
sa.Column("id", sa.Text(), primary_key=True, unique=True),
sa.Column("user_id", sa.Text(), sa.ForeignKey("user.id", ondelete="CASCADE")),
sa.Column("key", sa.Text(), unique=True, nullable=False),
sa.Column("data", sa.JSON(), nullable=True),
sa.Column("expires_at", sa.BigInteger(), nullable=True),
sa.Column("last_used_at", sa.BigInteger(), nullable=True),
sa.Column("created_at", sa.BigInteger(), nullable=False),
sa.Column("updated_at", sa.BigInteger(), nullable=False),
)
conn = op.get_bind()
users = conn.execute(
sa.text('SELECT id, oauth_sub FROM "user" WHERE oauth_sub IS NOT NULL')
).fetchall()
for uid, oauth_sub in users:
if oauth_sub:
# Example formats supported:
# provider@sub
# plain sub (stored as {"oidc": {"sub": sub}})
if "@" in oauth_sub:
provider, sub = oauth_sub.split("@", 1)
else:
provider, sub = "oidc", oauth_sub
oauth_json = json.dumps({provider: {"sub": sub}})
conn.execute(
sa.text('UPDATE "user" SET oauth = :oauth WHERE id = :id'),
{"oauth": oauth_json, "id": uid},
)
users_with_keys = conn.execute(
sa.text('SELECT id, api_key FROM "user" WHERE api_key IS NOT NULL')
).fetchall()
now = int(time.time())
for uid, api_key in users_with_keys:
if api_key:
conn.execute(
sa.text(
"""
INSERT INTO api_key (id, user_id, key, created_at, updated_at)
VALUES (:id, :user_id, :key, :created_at, :updated_at)
"""
),
{
"id": f"key_{uid}",
"user_id": uid,
"key": api_key,
"created_at": now,
"updated_at": now,
},
)
if conn.dialect.name == "sqlite":
_drop_sqlite_indexes_for_column("user", "api_key", conn)
_drop_sqlite_indexes_for_column("user", "oauth_sub", conn)
with op.batch_alter_table("user") as batch_op:
batch_op.drop_column("api_key")
batch_op.drop_column("oauth_sub")
def downgrade() -> None:
# --- 1. Restore old oauth_sub column ---
op.add_column("user", sa.Column("oauth_sub", sa.Text(), nullable=True))
conn = op.get_bind()
users = conn.execute(
sa.text('SELECT id, oauth FROM "user" WHERE oauth IS NOT NULL')
).fetchall()
for uid, oauth in users:
try:
data = json.loads(oauth)
provider = list(data.keys())[0]
sub = data[provider].get("sub")
oauth_sub = f"{provider}@{sub}"
except Exception:
oauth_sub = None
conn.execute(
sa.text('UPDATE "user" SET oauth_sub = :oauth_sub WHERE id = :id'),
{"oauth_sub": oauth_sub, "id": uid},
)
op.drop_column("user", "oauth")
# --- 2. Restore api_key field ---
op.add_column("user", sa.Column("api_key", sa.String(), nullable=True))
# Restore values from api_key
keys = conn.execute(sa.text("SELECT user_id, key FROM api_key")).fetchall()
for uid, key in keys:
conn.execute(
sa.text('UPDATE "user" SET api_key = :key WHERE id = :id'),
{"key": key, "id": uid},
)
# Drop new table
op.drop_table("api_key")
with op.batch_alter_table("user") as batch_op:
batch_op.drop_column("profile_banner_image_url")
batch_op.drop_column("timezone")
batch_op.drop_column("presence_state")
batch_op.drop_column("status_emoji")
batch_op.drop_column("status_message")
batch_op.drop_column("status_expires_at")
# Convert info (JSON) → TEXT
_convert_column_to_text("user", "info")
# Convert settings (JSON) → TEXT
_convert_column_to_text("user", "settings")
@@ -0,0 +1,23 @@
"""Update folder table data
Revision ID: d31026856c01
Revises: 9f0c9cd09105
Create Date: 2025-07-13 03:00:00.000000
"""
from alembic import op
import sqlalchemy as sa
revision = "d31026856c01"
down_revision = "9f0c9cd09105"
branch_labels = None
depends_on = None
def upgrade():
op.add_column("folder", sa.Column("data", sa.JSON(), nullable=True))
def downgrade():
op.drop_column("folder", "data")
+17 -25
View File
@@ -3,11 +3,10 @@ import uuid
from typing import Optional
from open_webui.internal.db import Base, get_db
from open_webui.models.users import UserModel, Users
from open_webui.models.users import UserModel, UserProfileImageResponse, Users
from open_webui.env import SRC_LOG_LEVELS
from pydantic import BaseModel
from sqlalchemy import Boolean, Column, String, Text
from open_webui.utils.auth import verify_password
log = logging.getLogger(__name__)
log.setLevel(SRC_LOG_LEVELS["MODELS"])
@@ -20,7 +19,7 @@ log.setLevel(SRC_LOG_LEVELS["MODELS"])
class Auth(Base):
__tablename__ = "auth"
id = Column(String, primary_key=True)
id = Column(String, primary_key=True, unique=True)
email = Column(String)
password = Column(Text)
active = Column(Boolean)
@@ -47,15 +46,7 @@ class ApiKey(BaseModel):
api_key: Optional[str] = None
class UserResponse(BaseModel):
id: str
email: str
name: str
role: str
profile_image_url: str
class SigninResponse(Token, UserResponse):
class SigninResponse(Token, UserProfileImageResponse):
pass
@@ -73,11 +64,6 @@ class ProfileImageUrlForm(BaseModel):
profile_image_url: str
class UpdateProfileForm(BaseModel):
profile_image_url: str
name: str
class UpdatePasswordForm(BaseModel):
password: str
new_password: str
@@ -102,7 +88,7 @@ class AuthsTable:
name: str,
profile_image_url: str = "/user.png",
role: str = "pending",
oauth_sub: Optional[str] = None,
oauth: Optional[dict] = None,
) -> Optional[UserModel]:
with get_db() as db:
log.info("insert_new_auth")
@@ -116,7 +102,7 @@ class AuthsTable:
db.add(result)
user = Users.insert_new_user(
id, name, email, profile_image_url, role, oauth_sub
id, name, email, profile_image_url, role, oauth=oauth
)
db.commit()
@@ -127,14 +113,20 @@ class AuthsTable:
else:
return None
def authenticate_user(self, email: str, password: str) -> Optional[UserModel]:
def authenticate_user(
self, email: str, verify_password: callable
) -> Optional[UserModel]:
log.info(f"authenticate_user: {email}")
user = Users.get_user_by_email(email)
if not user:
return None
try:
with get_db() as db:
auth = db.query(Auth).filter_by(email=email, active=True).first()
auth = db.query(Auth).filter_by(id=user.id, active=True).first()
if auth:
if verify_password(password, auth.password):
user = Users.get_user_by_id(auth.id)
if verify_password(auth.password):
return user
else:
return None
@@ -155,8 +147,8 @@ class AuthsTable:
except Exception:
return False
def authenticate_user_by_trusted_header(self, email: str) -> Optional[UserModel]:
log.info(f"authenticate_user_by_trusted_header: {email}")
def authenticate_user_by_email(self, email: str) -> Optional[UserModel]:
log.info(f"authenticate_user_by_email: {email}")
try:
with get_db() as db:
auth = db.query(Auth).filter_by(email=email, active=True).first()
+556 -19
View File
@@ -4,10 +4,13 @@ import uuid
from typing import Optional
from open_webui.internal.db import Base, get_db
from open_webui.utils.access_control import has_access
from open_webui.models.groups import Groups
from pydantic import BaseModel, ConfigDict
from sqlalchemy import BigInteger, Boolean, Column, String, Text, JSON
from sqlalchemy.dialects.postgresql import JSONB
from sqlalchemy import BigInteger, Boolean, Column, String, Text, JSON, case, cast
from sqlalchemy import or_, func, select, and_, text
from sqlalchemy.sql import exists
@@ -19,19 +22,30 @@ from sqlalchemy.sql import exists
class Channel(Base):
__tablename__ = "channel"
id = Column(Text, primary_key=True)
id = Column(Text, primary_key=True, unique=True)
user_id = Column(Text)
type = Column(Text, nullable=True)
name = Column(Text)
description = Column(Text, nullable=True)
# Used to indicate if the channel is private (for 'group' type channels)
is_private = Column(Boolean, nullable=True)
data = Column(JSON, nullable=True)
meta = Column(JSON, nullable=True)
access_control = Column(JSON, nullable=True)
created_at = Column(BigInteger)
updated_at = Column(BigInteger)
updated_by = Column(Text, nullable=True)
archived_at = Column(BigInteger, nullable=True)
archived_by = Column(Text, nullable=True)
deleted_at = Column(BigInteger, nullable=True)
deleted_by = Column(Text, nullable=True)
class ChannelModel(BaseModel):
@@ -39,17 +53,122 @@ class ChannelModel(BaseModel):
id: str
user_id: str
type: Optional[str] = None
name: str
description: Optional[str] = None
is_private: Optional[bool] = None
data: Optional[dict] = None
meta: Optional[dict] = None
access_control: Optional[dict] = None
created_at: int # timestamp in epoch
updated_at: int # timestamp in epoch
created_at: int # timestamp in epoch (time_ns)
updated_at: int # timestamp in epoch (time_ns)
updated_by: Optional[str] = None
archived_at: Optional[int] = None # timestamp in epoch (time_ns)
archived_by: Optional[str] = None
deleted_at: Optional[int] = None # timestamp in epoch (time_ns)
deleted_by: Optional[str] = None
class ChannelMember(Base):
__tablename__ = "channel_member"
id = Column(Text, primary_key=True, unique=True)
channel_id = Column(Text, nullable=False)
user_id = Column(Text, nullable=False)
role = Column(Text, nullable=True)
status = Column(Text, nullable=True)
is_active = Column(Boolean, nullable=False, default=True)
is_channel_muted = Column(Boolean, nullable=False, default=False)
is_channel_pinned = Column(Boolean, nullable=False, default=False)
data = Column(JSON, nullable=True)
meta = Column(JSON, nullable=True)
invited_at = Column(BigInteger, nullable=True)
invited_by = Column(Text, nullable=True)
joined_at = Column(BigInteger)
left_at = Column(BigInteger, nullable=True)
last_read_at = Column(BigInteger, nullable=True)
created_at = Column(BigInteger)
updated_at = Column(BigInteger)
class ChannelMemberModel(BaseModel):
model_config = ConfigDict(from_attributes=True)
id: str
channel_id: str
user_id: str
role: Optional[str] = None
status: Optional[str] = None
is_active: bool = True
is_channel_muted: bool = False
is_channel_pinned: bool = False
data: Optional[dict] = None
meta: Optional[dict] = None
invited_at: Optional[int] = None # timestamp in epoch (time_ns)
invited_by: Optional[str] = None
joined_at: Optional[int] = None # timestamp in epoch (time_ns)
left_at: Optional[int] = None # timestamp in epoch (time_ns)
last_read_at: Optional[int] = None # timestamp in epoch (time_ns)
created_at: Optional[int] = None # timestamp in epoch (time_ns)
updated_at: Optional[int] = None # timestamp in epoch (time_ns)
class ChannelWebhook(Base):
__tablename__ = "channel_webhook"
id = Column(Text, primary_key=True, unique=True)
channel_id = Column(Text, nullable=False)
user_id = Column(Text, nullable=False)
name = Column(Text, nullable=False)
profile_image_url = Column(Text, nullable=True)
token = Column(Text, nullable=False)
last_used_at = Column(BigInteger, nullable=True)
created_at = Column(BigInteger, nullable=False)
updated_at = Column(BigInteger, nullable=False)
class ChannelWebhookModel(BaseModel):
model_config = ConfigDict(from_attributes=True)
id: str
channel_id: str
user_id: str
name: str
profile_image_url: Optional[str] = None
token: str
last_used_at: Optional[int] = None # timestamp in epoch (time_ns)
created_at: int # timestamp in epoch (time_ns)
updated_at: int # timestamp in epoch (time_ns)
####################
@@ -57,23 +176,95 @@ class ChannelModel(BaseModel):
####################
class ChannelResponse(ChannelModel):
is_manager: bool = False
write_access: bool = False
user_count: Optional[int] = None
class ChannelForm(BaseModel):
name: str
name: str = ""
description: Optional[str] = None
is_private: Optional[bool] = None
data: Optional[dict] = None
meta: Optional[dict] = None
access_control: Optional[dict] = None
group_ids: Optional[list[str]] = None
user_ids: Optional[list[str]] = None
class CreateChannelForm(ChannelForm):
type: Optional[str] = None
class ChannelTable:
def _collect_unique_user_ids(
self,
invited_by: str,
user_ids: Optional[list[str]] = None,
group_ids: Optional[list[str]] = None,
) -> set[str]:
"""
Collect unique user ids from:
- invited_by
- user_ids
- each group in group_ids
Returns a set for efficient SQL diffing.
"""
users = set(user_ids or [])
users.add(invited_by)
for group_id in group_ids or []:
users.update(Groups.get_group_user_ids_by_id(group_id))
return users
def _create_membership_models(
self,
channel_id: str,
invited_by: str,
user_ids: set[str],
) -> list[ChannelMember]:
"""
Takes a set of NEW user IDs (already filtered to exclude existing members).
Returns ORM ChannelMember objects to be added.
"""
now = int(time.time_ns())
memberships = []
for uid in user_ids:
model = ChannelMemberModel(
**{
"id": str(uuid.uuid4()),
"channel_id": channel_id,
"user_id": uid,
"status": "joined",
"is_active": True,
"is_channel_muted": False,
"is_channel_pinned": False,
"invited_at": now,
"invited_by": invited_by,
"joined_at": now,
"left_at": None,
"last_read_at": now,
"created_at": now,
"updated_at": now,
}
)
memberships.append(ChannelMember(**model.model_dump()))
return memberships
def insert_new_channel(
self, type: Optional[str], form_data: ChannelForm, user_id: str
self, form_data: CreateChannelForm, user_id: str
) -> Optional[ChannelModel]:
with get_db() as db:
channel = ChannelModel(
**{
**form_data.model_dump(),
"type": type,
"type": form_data.type if form_data.type else None,
"name": form_data.name.lower(),
"id": str(uuid.uuid4()),
"user_id": user_id,
@@ -81,9 +272,21 @@ class ChannelTable:
"updated_at": int(time.time_ns()),
}
)
new_channel = Channel(**channel.model_dump())
if form_data.type in ["group", "dm"]:
users = self._collect_unique_user_ids(
invited_by=user_id,
user_ids=form_data.user_ids,
group_ids=form_data.group_ids,
)
memberships = self._create_membership_models(
channel_id=new_channel.id,
invited_by=user_id,
user_ids=users,
)
db.add_all(memberships)
db.add(new_channel)
db.commit()
return channel
@@ -93,16 +296,346 @@ class ChannelTable:
channels = db.query(Channel).all()
return [ChannelModel.model_validate(channel) for channel in channels]
def get_channels_by_user_id(
self, user_id: str, permission: str = "read"
) -> list[ChannelModel]:
channels = self.get_channels()
return [
channel
for channel in channels
if channel.user_id == user_id
or has_access(user_id, permission, channel.access_control)
]
def _has_permission(self, db, query, filter: dict, permission: str = "read"):
group_ids = filter.get("group_ids", [])
user_id = filter.get("user_id")
dialect_name = db.bind.dialect.name
# Public access
conditions = []
if group_ids or user_id:
conditions.extend(
[
Channel.access_control.is_(None),
cast(Channel.access_control, String) == "null",
]
)
# User-level permission
if user_id:
conditions.append(Channel.user_id == user_id)
# Group-level permission
if group_ids:
group_conditions = []
for gid in group_ids:
if dialect_name == "sqlite":
group_conditions.append(
Channel.access_control[permission]["group_ids"].contains([gid])
)
elif dialect_name == "postgresql":
group_conditions.append(
cast(
Channel.access_control[permission]["group_ids"],
JSONB,
).contains([gid])
)
conditions.append(or_(*group_conditions))
if conditions:
query = query.filter(or_(*conditions))
return query
def get_channels_by_user_id(self, user_id: str) -> list[ChannelModel]:
with get_db() as db:
user_group_ids = [
group.id for group in Groups.get_groups_by_member_id(user_id)
]
membership_channels = (
db.query(Channel)
.join(ChannelMember, Channel.id == ChannelMember.channel_id)
.filter(
Channel.deleted_at.is_(None),
Channel.archived_at.is_(None),
Channel.type.in_(["group", "dm"]),
ChannelMember.user_id == user_id,
ChannelMember.is_active.is_(True),
)
.all()
)
query = db.query(Channel).filter(
Channel.deleted_at.is_(None),
Channel.archived_at.is_(None),
or_(
Channel.type.is_(None), # True NULL/None
Channel.type == "", # Empty string
and_(Channel.type != "group", Channel.type != "dm"),
),
)
query = self._has_permission(
db, query, {"user_id": user_id, "group_ids": user_group_ids}
)
standard_channels = query.all()
all_channels = membership_channels + standard_channels
return [ChannelModel.model_validate(c) for c in all_channels]
def get_dm_channel_by_user_ids(self, user_ids: list[str]) -> Optional[ChannelModel]:
with get_db() as db:
# Ensure uniqueness in case a list with duplicates is passed
unique_user_ids = list(set(user_ids))
match_count = func.sum(
case(
(ChannelMember.user_id.in_(unique_user_ids), 1),
else_=0,
)
)
subquery = (
db.query(ChannelMember.channel_id)
.group_by(ChannelMember.channel_id)
# 1. Channel must have exactly len(user_ids) members
.having(func.count(ChannelMember.user_id) == len(unique_user_ids))
# 2. All those members must be in unique_user_ids
.having(match_count == len(unique_user_ids))
.subquery()
)
channel = (
db.query(Channel)
.filter(
Channel.id.in_(subquery),
Channel.type == "dm",
)
.first()
)
return ChannelModel.model_validate(channel) if channel else None
def add_members_to_channel(
self,
channel_id: str,
invited_by: str,
user_ids: Optional[list[str]] = None,
group_ids: Optional[list[str]] = None,
) -> list[ChannelMemberModel]:
with get_db() as db:
# 1. Collect all user_ids including groups + inviter
requested_users = self._collect_unique_user_ids(
invited_by, user_ids, group_ids
)
existing_users = {
row.user_id
for row in db.query(ChannelMember.user_id)
.filter(ChannelMember.channel_id == channel_id)
.all()
}
new_user_ids = requested_users - existing_users
if not new_user_ids:
return [] # Nothing to add
new_memberships = self._create_membership_models(
channel_id, invited_by, new_user_ids
)
db.add_all(new_memberships)
db.commit()
return [
ChannelMemberModel.model_validate(membership)
for membership in new_memberships
]
def remove_members_from_channel(
self,
channel_id: str,
user_ids: list[str],
) -> int:
with get_db() as db:
result = (
db.query(ChannelMember)
.filter(
ChannelMember.channel_id == channel_id,
ChannelMember.user_id.in_(user_ids),
)
.delete(synchronize_session=False)
)
db.commit()
return result # number of rows deleted
def is_user_channel_manager(self, channel_id: str, user_id: str) -> bool:
with get_db() as db:
# Check if the user is the creator of the channel
# or has a 'manager' role in ChannelMember
channel = db.query(Channel).filter(Channel.id == channel_id).first()
if channel and channel.user_id == user_id:
return True
membership = (
db.query(ChannelMember)
.filter(
ChannelMember.channel_id == channel_id,
ChannelMember.user_id == user_id,
ChannelMember.role == "manager",
)
.first()
)
return membership is not None
def join_channel(
self, channel_id: str, user_id: str
) -> Optional[ChannelMemberModel]:
with get_db() as db:
# Check if the membership already exists
existing_membership = (
db.query(ChannelMember)
.filter(
ChannelMember.channel_id == channel_id,
ChannelMember.user_id == user_id,
)
.first()
)
if existing_membership:
return ChannelMemberModel.model_validate(existing_membership)
# Create new membership
channel_member = ChannelMemberModel(
**{
"id": str(uuid.uuid4()),
"channel_id": channel_id,
"user_id": user_id,
"status": "joined",
"is_active": True,
"is_channel_muted": False,
"is_channel_pinned": False,
"joined_at": int(time.time_ns()),
"left_at": None,
"last_read_at": int(time.time_ns()),
"created_at": int(time.time_ns()),
"updated_at": int(time.time_ns()),
}
)
new_membership = ChannelMember(**channel_member.model_dump())
db.add(new_membership)
db.commit()
return channel_member
def leave_channel(self, channel_id: str, user_id: str) -> bool:
with get_db() as db:
membership = (
db.query(ChannelMember)
.filter(
ChannelMember.channel_id == channel_id,
ChannelMember.user_id == user_id,
)
.first()
)
if not membership:
return False
membership.status = "left"
membership.is_active = False
membership.left_at = int(time.time_ns())
membership.updated_at = int(time.time_ns())
db.commit()
return True
def get_member_by_channel_and_user_id(
self, channel_id: str, user_id: str
) -> Optional[ChannelMemberModel]:
with get_db() as db:
membership = (
db.query(ChannelMember)
.filter(
ChannelMember.channel_id == channel_id,
ChannelMember.user_id == user_id,
)
.first()
)
return ChannelMemberModel.model_validate(membership) if membership else None
def get_members_by_channel_id(self, channel_id: str) -> list[ChannelMemberModel]:
with get_db() as db:
memberships = (
db.query(ChannelMember)
.filter(ChannelMember.channel_id == channel_id)
.all()
)
return [
ChannelMemberModel.model_validate(membership)
for membership in memberships
]
def pin_channel(self, channel_id: str, user_id: str, is_pinned: bool) -> bool:
with get_db() as db:
membership = (
db.query(ChannelMember)
.filter(
ChannelMember.channel_id == channel_id,
ChannelMember.user_id == user_id,
)
.first()
)
if not membership:
return False
membership.is_channel_pinned = is_pinned
membership.updated_at = int(time.time_ns())
db.commit()
return True
def update_member_last_read_at(self, channel_id: str, user_id: str) -> bool:
with get_db() as db:
membership = (
db.query(ChannelMember)
.filter(
ChannelMember.channel_id == channel_id,
ChannelMember.user_id == user_id,
)
.first()
)
if not membership:
return False
membership.last_read_at = int(time.time_ns())
membership.updated_at = int(time.time_ns())
db.commit()
return True
def update_member_active_status(
self, channel_id: str, user_id: str, is_active: bool
) -> bool:
with get_db() as db:
membership = (
db.query(ChannelMember)
.filter(
ChannelMember.channel_id == channel_id,
ChannelMember.user_id == user_id,
)
.first()
)
if not membership:
return False
membership.is_active = is_active
membership.updated_at = int(time.time_ns())
db.commit()
return True
def is_user_channel_member(self, channel_id: str, user_id: str) -> bool:
with get_db() as db:
membership = (
db.query(ChannelMember)
.filter(
ChannelMember.channel_id == channel_id,
ChannelMember.user_id == user_id,
)
.first()
)
return membership is not None
def get_channel_by_id(self, id: str) -> Optional[ChannelModel]:
with get_db() as db:
@@ -118,8 +651,12 @@ class ChannelTable:
return None
channel.name = form_data.name
channel.description = form_data.description
channel.is_private = form_data.is_private
channel.data = form_data.data
channel.meta = form_data.meta
channel.access_control = form_data.access_control
channel.updated_at = int(time.time_ns())
+352 -90
View File
@@ -6,12 +6,14 @@ from typing import Optional
from open_webui.internal.db import Base, get_db
from open_webui.models.tags import TagModel, Tag, Tags
from open_webui.models.folders import Folders
from open_webui.env import SRC_LOG_LEVELS
from pydantic import BaseModel, ConfigDict
from sqlalchemy import BigInteger, Boolean, Column, String, Text, JSON
from sqlalchemy import BigInteger, Boolean, Column, String, Text, JSON, Index
from sqlalchemy import or_, func, select, and_, text
from sqlalchemy.sql import exists
from sqlalchemy.sql.expression import bindparam
####################
# Chat DB Schema
@@ -24,7 +26,7 @@ log.setLevel(SRC_LOG_LEVELS["MODELS"])
class Chat(Base):
__tablename__ = "chat"
id = Column(String, primary_key=True)
id = Column(String, primary_key=True, unique=True)
user_id = Column(String)
title = Column(Text)
chat = Column(JSON)
@@ -39,6 +41,20 @@ class Chat(Base):
meta = Column(JSON, server_default="{}")
folder_id = Column(Text, nullable=True)
__table_args__ = (
# Performance indexes for common queries
# WHERE folder_id = ...
Index("folder_id_idx", "folder_id"),
# WHERE user_id = ... AND pinned = ...
Index("user_id_pinned_idx", "user_id", "pinned"),
# WHERE user_id = ... AND archived = ...
Index("user_id_archived_idx", "user_id", "archived"),
# WHERE user_id = ... ORDER BY updated_at DESC
Index("updated_at_user_id_idx", "updated_at", "user_id"),
# WHERE folder_id = ... AND user_id = ...
Index("folder_id_user_id_idx", "folder_id", "user_id"),
)
class ChatModel(BaseModel):
model_config = ConfigDict(from_attributes=True)
@@ -66,12 +82,18 @@ class ChatModel(BaseModel):
class ChatForm(BaseModel):
chat: dict
folder_id: Optional[str] = None
class ChatImportForm(ChatForm):
meta: Optional[dict] = {}
pinned: Optional[bool] = False
folder_id: Optional[str] = None
created_at: Optional[int] = None
updated_at: Optional[int] = None
class ChatsImportForm(BaseModel):
chats: list[ChatImportForm]
class ChatTitleMessagesForm(BaseModel):
@@ -105,6 +127,43 @@ class ChatTitleIdResponse(BaseModel):
class ChatTable:
def _clean_null_bytes(self, obj):
"""
Recursively remove actual null bytes (\x00) and unicode escape \\u0000
from strings inside dict/list structures.
Safe for JSON objects.
"""
if isinstance(obj, str):
return obj.replace("\x00", "").replace("\u0000", "")
elif isinstance(obj, dict):
return {k: self._clean_null_bytes(v) for k, v in obj.items()}
elif isinstance(obj, list):
return [self._clean_null_bytes(v) for v in obj]
return obj
def _sanitize_chat_row(self, chat_item):
"""
Clean a Chat SQLAlchemy model's title + chat JSON,
and return True if anything changed.
"""
changed = False
# Clean title
if chat_item.title:
cleaned = self._clean_null_bytes(chat_item.title)
if cleaned != chat_item.title:
chat_item.title = cleaned
changed = True
# Clean JSON
if chat_item.chat:
cleaned = self._clean_null_bytes(chat_item.chat)
if cleaned != chat_item.chat:
chat_item.chat = cleaned
changed = True
return changed
def insert_new_chat(self, user_id: str, form_data: ChatForm) -> Optional[ChatModel]:
with get_db() as db:
id = str(uuid.uuid4())
@@ -112,59 +171,76 @@ class ChatTable:
**{
"id": id,
"user_id": user_id,
"title": (
"title": self._clean_null_bytes(
form_data.chat["title"]
if "title" in form_data.chat
else "New Chat"
),
"chat": form_data.chat,
"created_at": int(time.time()),
"updated_at": int(time.time()),
}
)
result = Chat(**chat.model_dump())
db.add(result)
db.commit()
db.refresh(result)
return ChatModel.model_validate(result) if result else None
def import_chat(
self, user_id: str, form_data: ChatImportForm
) -> Optional[ChatModel]:
with get_db() as db:
id = str(uuid.uuid4())
chat = ChatModel(
**{
"id": id,
"user_id": user_id,
"title": (
form_data.chat["title"]
if "title" in form_data.chat
else "New Chat"
),
"chat": form_data.chat,
"meta": form_data.meta,
"pinned": form_data.pinned,
"chat": self._clean_null_bytes(form_data.chat),
"folder_id": form_data.folder_id,
"created_at": int(time.time()),
"updated_at": int(time.time()),
}
)
result = Chat(**chat.model_dump())
db.add(result)
chat_item = Chat(**chat.model_dump())
db.add(chat_item)
db.commit()
db.refresh(result)
return ChatModel.model_validate(result) if result else None
db.refresh(chat_item)
return ChatModel.model_validate(chat_item) if chat_item else None
def _chat_import_form_to_chat_model(
self, user_id: str, form_data: ChatImportForm
) -> ChatModel:
id = str(uuid.uuid4())
chat = ChatModel(
**{
"id": id,
"user_id": user_id,
"title": self._clean_null_bytes(
form_data.chat["title"] if "title" in form_data.chat else "New Chat"
),
"chat": self._clean_null_bytes(form_data.chat),
"meta": form_data.meta,
"pinned": form_data.pinned,
"folder_id": form_data.folder_id,
"created_at": (
form_data.created_at if form_data.created_at else int(time.time())
),
"updated_at": (
form_data.updated_at if form_data.updated_at else int(time.time())
),
}
)
return chat
def import_chats(
self, user_id: str, chat_import_forms: list[ChatImportForm]
) -> list[ChatModel]:
with get_db() as db:
chats = []
for form_data in chat_import_forms:
chat = self._chat_import_form_to_chat_model(user_id, form_data)
chats.append(Chat(**chat.model_dump()))
db.add_all(chats)
db.commit()
return [ChatModel.model_validate(chat) for chat in chats]
def update_chat_by_id(self, id: str, chat: dict) -> Optional[ChatModel]:
try:
with get_db() as db:
chat_item = db.get(Chat, id)
chat_item.chat = chat
chat_item.title = chat["title"] if "title" in chat else "New Chat"
chat_item.chat = self._clean_null_bytes(chat)
chat_item.title = (
self._clean_null_bytes(chat["title"])
if "title" in chat
else "New Chat"
)
chat_item.updated_at = int(time.time())
db.commit()
db.refresh(chat_item)
@@ -209,7 +285,7 @@ class ChatTable:
return chat.chat.get("title", "New Chat")
def get_messages_by_chat_id(self, id: str) -> Optional[dict]:
def get_messages_map_by_chat_id(self, id: str) -> Optional[dict]:
chat = self.get_chat_by_id(id)
if chat is None:
return None
@@ -232,6 +308,10 @@ class ChatTable:
if chat is None:
return None
# Sanitize message content for null characters before upserting
if isinstance(message.get("content"), str):
message["content"] = message["content"].replace("\x00", "")
chat = chat.chat
history = chat.get("history", {})
@@ -266,6 +346,27 @@ class ChatTable:
chat["history"] = history
return self.update_chat_by_id(id, chat)
def add_message_files_by_id_and_message_id(
self, id: str, message_id: str, files: list[dict]
) -> list[dict]:
chat = self.get_chat_by_id(id)
if chat is None:
return None
chat = chat.chat
history = chat.get("history", {})
message_files = []
if message_id in history.get("messages", {}):
message_files = history["messages"][message_id].get("files", [])
message_files = message_files + files
history["messages"][message_id]["files"] = message_files
chat["history"] = history
self.update_chat_by_id(id, chat)
return message_files
def insert_shared_chat_by_chat_id(self, chat_id: str) -> Optional[ChatModel]:
with get_db() as db:
# Get the existing chat to share
@@ -280,6 +381,9 @@ class ChatTable:
"user_id": f"shared-{chat_id}",
"title": chat.title,
"chat": chat.chat,
"meta": chat.meta,
"pinned": chat.pinned,
"folder_id": chat.folder_id,
"created_at": chat.created_at,
"updated_at": int(time.time()),
}
@@ -311,7 +415,9 @@ class ChatTable:
shared_chat.title = chat.title
shared_chat.chat = chat.chat
shared_chat.meta = chat.meta
shared_chat.pinned = chat.pinned
shared_chat.folder_id = chat.folder_id
shared_chat.updated_at = int(time.time())
db.commit()
db.refresh(shared_chat)
@@ -330,6 +436,15 @@ class ChatTable:
except Exception:
return False
def unarchive_all_chats_by_user_id(self, user_id: str) -> bool:
try:
with get_db() as db:
db.query(Chat).filter_by(user_id=user_id).update({"archived": False})
db.commit()
return True
except Exception:
return False
def update_chat_share_id_by_id(
self, id: str, share_id: Optional[str]
) -> Optional[ChatModel]:
@@ -360,6 +475,7 @@ class ChatTable:
with get_db() as db:
chat = db.get(Chat, id)
chat.archived = not chat.archived
chat.folder_id = None
chat.updated_at = int(time.time())
db.commit()
db.refresh(chat)
@@ -377,22 +493,50 @@ class ChatTable:
return False
def get_archived_chat_list_by_user_id(
self, user_id: str, skip: int = 0, limit: int = 50
self,
user_id: str,
filter: Optional[dict] = None,
skip: int = 0,
limit: int = 50,
) -> list[ChatModel]:
with get_db() as db:
all_chats = (
db.query(Chat)
.filter_by(user_id=user_id, archived=True)
.order_by(Chat.updated_at.desc())
# .limit(limit).offset(skip)
.all()
)
query = db.query(Chat).filter_by(user_id=user_id, archived=True)
if filter:
query_key = filter.get("query")
if query_key:
query = query.filter(Chat.title.ilike(f"%{query_key}%"))
order_by = filter.get("order_by")
direction = filter.get("direction")
if order_by and direction:
if not getattr(Chat, order_by, None):
raise ValueError("Invalid order_by field")
if direction.lower() == "asc":
query = query.order_by(getattr(Chat, order_by).asc())
elif direction.lower() == "desc":
query = query.order_by(getattr(Chat, order_by).desc())
else:
raise ValueError("Invalid direction for ordering")
else:
query = query.order_by(Chat.updated_at.desc())
if skip:
query = query.offset(skip)
if limit:
query = query.limit(limit)
all_chats = query.all()
return [ChatModel.model_validate(chat) for chat in all_chats]
def get_chat_list_by_user_id(
self,
user_id: str,
include_archived: bool = False,
filter: Optional[dict] = None,
skip: int = 0,
limit: int = 50,
) -> list[ChatModel]:
@@ -401,7 +545,23 @@ class ChatTable:
if not include_archived:
query = query.filter_by(archived=False)
query = query.order_by(Chat.updated_at.desc())
if filter:
query_key = filter.get("query")
if query_key:
query = query.filter(Chat.title.ilike(f"%{query_key}%"))
order_by = filter.get("order_by")
direction = filter.get("direction")
if order_by and direction and getattr(Chat, order_by):
if direction.lower() == "asc":
query = query.order_by(getattr(Chat, order_by).asc())
elif direction.lower() == "desc":
query = query.order_by(getattr(Chat, order_by).desc())
else:
raise ValueError("Invalid direction for ordering")
else:
query = query.order_by(Chat.updated_at.desc())
if skip:
query = query.offset(skip)
@@ -415,12 +575,19 @@ class ChatTable:
self,
user_id: str,
include_archived: bool = False,
include_folders: bool = False,
include_pinned: bool = False,
skip: Optional[int] = None,
limit: Optional[int] = None,
) -> list[ChatTitleIdResponse]:
with get_db() as db:
query = db.query(Chat).filter_by(user_id=user_id).filter_by(folder_id=None)
query = query.filter(or_(Chat.pinned == False, Chat.pinned == None))
query = db.query(Chat).filter_by(user_id=user_id)
if not include_folders:
query = query.filter_by(folder_id=None)
if not include_pinned:
query = query.filter(or_(Chat.pinned == False, Chat.pinned == None))
if not include_archived:
query = query.filter_by(archived=False)
@@ -436,7 +603,7 @@ class ChatTable:
all_chats = query.all()
# result has to be destrctured from sqlalchemy `row` and mapped to a dict since the `ChatModel`is not the returned dataclass.
# result has to be destructured from sqlalchemy `row` and mapped to a dict since the `ChatModel`is not the returned dataclass.
return [
ChatTitleIdResponse.model_validate(
{
@@ -465,8 +632,15 @@ class ChatTable:
def get_chat_by_id(self, id: str) -> Optional[ChatModel]:
try:
with get_db() as db:
chat = db.get(Chat, id)
return ChatModel.model_validate(chat)
chat_item = db.get(Chat, id)
if chat_item is None:
return None
if self._sanitize_chat_row(chat_item):
db.commit()
db.refresh(chat_item)
return ChatModel.model_validate(chat_item)
except Exception:
return None
@@ -539,10 +713,12 @@ class ChatTable:
"""
Filters chats based on a search query using Python, allowing pagination using skip and limit.
"""
search_text = search_text.lower().strip()
search_text = search_text.replace("\u0000", "").lower().strip()
if not search_text:
return self.get_chat_list_by_user_id(user_id, include_archived, skip, limit)
return self.get_chat_list_by_user_id(
user_id, include_archived, filter={}, skip=skip, limit=limit
)
search_text_words = search_text.split(" ")
@@ -553,8 +729,45 @@ class ChatTable:
if word.startswith("tag:")
]
# Extract folder names - handle spaces and case insensitivity
folders = Folders.search_folders_by_names(
user_id,
[
word.replace("folder:", "")
for word in search_text_words
if word.startswith("folder:")
],
)
folder_ids = [folder.id for folder in folders]
is_pinned = None
if "pinned:true" in search_text_words:
is_pinned = True
elif "pinned:false" in search_text_words:
is_pinned = False
is_archived = None
if "archived:true" in search_text_words:
is_archived = True
elif "archived:false" in search_text_words:
is_archived = False
is_shared = None
if "shared:true" in search_text_words:
is_shared = True
elif "shared:false" in search_text_words:
is_shared = False
search_text_words = [
word for word in search_text_words if not word.startswith("tag:")
word
for word in search_text_words
if (
not word.startswith("tag:")
and not word.startswith("folder:")
and not word.startswith("pinned:")
and not word.startswith("archived:")
and not word.startswith("shared:")
)
]
search_text = " ".join(search_text_words)
@@ -562,30 +775,41 @@ class ChatTable:
with get_db() as db:
query = db.query(Chat).filter(Chat.user_id == user_id)
if not include_archived:
if is_archived is not None:
query = query.filter(Chat.archived == is_archived)
elif not include_archived:
query = query.filter(Chat.archived == False)
if is_pinned is not None:
query = query.filter(Chat.pinned == is_pinned)
if is_shared is not None:
if is_shared:
query = query.filter(Chat.share_id.isnot(None))
else:
query = query.filter(Chat.share_id.is_(None))
if folder_ids:
query = query.filter(Chat.folder_id.in_(folder_ids))
query = query.order_by(Chat.updated_at.desc())
# Check if the database dialect is either 'sqlite' or 'postgresql'
dialect_name = db.bind.dialect.name
if dialect_name == "sqlite":
# SQLite case: using JSON1 extension for JSON searching
sqlite_content_sql = (
"EXISTS ("
" SELECT 1 "
" FROM json_each(Chat.chat, '$.messages') AS message "
" WHERE LOWER(message.value->>'content') LIKE '%' || :content_key || '%'"
")"
)
sqlite_content_clause = text(sqlite_content_sql)
query = query.filter(
(
Chat.title.ilike(
f"%{search_text}%"
) # Case-insensitive search in title
| text(
"""
EXISTS (
SELECT 1
FROM json_each(Chat.chat, '$.messages') AS message
WHERE LOWER(message.value->>'content') LIKE '%' || :search_text || '%'
)
"""
)
).params(search_text=search_text)
or_(
Chat.title.ilike(bindparam("title_key")), sqlite_content_clause
).params(title_key=f"%{search_text}%", content_key=search_text)
)
# Check if there are any tags to filter, it should have all the tags
@@ -619,23 +843,32 @@ class ChatTable:
)
elif dialect_name == "postgresql":
# PostgreSQL relies on proper JSON query for search
query = query.filter(
(
Chat.title.ilike(
f"%{search_text}%"
) # Case-insensitive search in title
| text(
"""
EXISTS (
SELECT 1
FROM json_array_elements(Chat.chat->'messages') AS message
WHERE LOWER(message->>'content') LIKE '%' || :search_text || '%'
)
"""
)
).params(search_text=search_text)
# PostgreSQL doesn't allow null bytes in text. We filter those out by checking
# the JSON representation for \u0000 before attempting text extraction
# Safety filter: JSON field must not contain \u0000
query = query.filter(text("Chat.chat::text NOT LIKE '%\\\\u0000%'"))
# Safety filter: title must not contain actual null bytes
query = query.filter(text("Chat.title::text NOT LIKE '%\\x00%'"))
postgres_content_sql = """
EXISTS (
SELECT 1
FROM json_array_elements(Chat.chat->'messages') AS message
WHERE json_typeof(message->'content') = 'string'
AND LOWER(message->>'content') LIKE '%' || :content_key || '%'
)
"""
postgres_content_clause = text(postgres_content_sql)
query = query.filter(
or_(
Chat.title.ilike(bindparam("title_key")),
postgres_content_clause,
)
).params(title_key=f"%{search_text}%", content_key=search_text.lower())
# Check if there are any tags to filter, it should have all the tags
if "none" in tag_ids:
@@ -680,7 +913,7 @@ class ChatTable:
return [ChatModel.model_validate(chat) for chat in all_chats]
def get_chats_by_folder_id_and_user_id(
self, folder_id: str, user_id: str
self, folder_id: str, user_id: str, skip: int = 0, limit: int = 60
) -> list[ChatModel]:
with get_db() as db:
query = db.query(Chat).filter_by(folder_id=folder_id, user_id=user_id)
@@ -689,6 +922,11 @@ class ChatTable:
query = query.order_by(Chat.updated_at.desc())
if skip:
query = query.offset(skip)
if limit:
query = query.limit(limit)
all_chats = query.all()
return [ChatModel.model_validate(chat) for chat in all_chats]
@@ -818,6 +1056,16 @@ class ChatTable:
return count
def count_chats_by_folder_id_and_user_id(self, folder_id: str, user_id: str) -> int:
with get_db() as db:
query = db.query(Chat).filter_by(user_id=user_id)
query = query.filter_by(folder_id=folder_id)
count = query.count()
log.info(f"Count of chats for folder '{folder_id}': {count}")
return count
def delete_tag_by_id_and_user_id_and_tag_name(
self, id: str, user_id: str, tag_name: str
) -> bool:
@@ -895,6 +1143,20 @@ class ChatTable:
except Exception:
return False
def move_chats_by_user_id_and_folder_id(
self, user_id: str, folder_id: str, new_folder_id: Optional[str]
) -> bool:
try:
with get_db() as db:
db.query(Chat).filter_by(user_id=user_id, folder_id=folder_id).update(
{"folder_id": new_folder_id}
)
db.commit()
return True
except Exception:
return False
def delete_shared_chats_by_user_id(self, user_id: str) -> bool:
try:
with get_db() as db:
+88 -2
View File
@@ -4,7 +4,7 @@ import uuid
from typing import Optional
from open_webui.internal.db import Base, get_db
from open_webui.models.chats import Chats
from open_webui.models.users import User
from open_webui.env import SRC_LOG_LEVELS
from pydantic import BaseModel, ConfigDict
@@ -21,7 +21,7 @@ log.setLevel(SRC_LOG_LEVELS["MODELS"])
class Feedback(Base):
__tablename__ = "feedback"
id = Column(Text, primary_key=True)
id = Column(Text, primary_key=True, unique=True)
user_id = Column(Text)
version = Column(BigInteger, default=0)
type = Column(Text)
@@ -92,6 +92,28 @@ class FeedbackForm(BaseModel):
model_config = ConfigDict(extra="allow")
class UserResponse(BaseModel):
id: str
name: str
email: str
role: str = "pending"
last_active_at: int # timestamp in epoch
updated_at: int # timestamp in epoch
created_at: int # timestamp in epoch
model_config = ConfigDict(from_attributes=True)
class FeedbackUserResponse(FeedbackResponse):
user: Optional[UserResponse] = None
class FeedbackListResponse(BaseModel):
items: list[FeedbackUserResponse]
total: int
class FeedbackTable:
def insert_new_feedback(
self, user_id: str, form_data: FeedbackForm
@@ -143,6 +165,70 @@ class FeedbackTable:
except Exception:
return None
def get_feedback_items(
self, filter: dict = {}, skip: int = 0, limit: int = 30
) -> FeedbackListResponse:
with get_db() as db:
query = db.query(Feedback, User).join(User, Feedback.user_id == User.id)
if filter:
order_by = filter.get("order_by")
direction = filter.get("direction")
if order_by == "username":
if direction == "asc":
query = query.order_by(User.name.asc())
else:
query = query.order_by(User.name.desc())
elif order_by == "model_id":
# it's stored in feedback.data['model_id']
if direction == "asc":
query = query.order_by(
Feedback.data["model_id"].as_string().asc()
)
else:
query = query.order_by(
Feedback.data["model_id"].as_string().desc()
)
elif order_by == "rating":
# it's stored in feedback.data['rating']
if direction == "asc":
query = query.order_by(
Feedback.data["rating"].as_string().asc()
)
else:
query = query.order_by(
Feedback.data["rating"].as_string().desc()
)
elif order_by == "updated_at":
if direction == "asc":
query = query.order_by(Feedback.updated_at.asc())
else:
query = query.order_by(Feedback.updated_at.desc())
else:
query = query.order_by(Feedback.created_at.desc())
# Count BEFORE pagination
total = query.count()
if skip:
query = query.offset(skip)
if limit:
query = query.limit(limit)
items = query.all()
feedbacks = []
for feedback, user in items:
feedback_model = FeedbackModel.model_validate(feedback)
user_model = UserResponse.model_validate(user)
feedbacks.append(
FeedbackUserResponse(**feedback_model.model_dump(), user=user_model)
)
return FeedbackListResponse(items=feedbacks, total=total)
def get_all_feedbacks(self) -> list[FeedbackModel]:
with get_db() as db:
return [
+56 -2
View File
@@ -17,7 +17,7 @@ log.setLevel(SRC_LOG_LEVELS["MODELS"])
class File(Base):
__tablename__ = "file"
id = Column(String, primary_key=True)
id = Column(String, primary_key=True, unique=True)
user_id = Column(String)
hash = Column(Text, nullable=True)
@@ -82,6 +82,7 @@ class FileModelResponse(BaseModel):
class FileMetadataResponse(BaseModel):
id: str
hash: Optional[str] = None
meta: dict
created_at: int # timestamp in epoch
updated_at: int # timestamp in epoch
@@ -97,6 +98,12 @@ class FileForm(BaseModel):
access_control: Optional[dict] = None
class FileUpdateForm(BaseModel):
hash: Optional[str] = None
data: Optional[dict] = None
meta: Optional[dict] = None
class FilesTable:
def insert_new_file(self, user_id: str, form_data: FileForm) -> Optional[FileModel]:
with get_db() as db:
@@ -130,12 +137,24 @@ class FilesTable:
except Exception:
return None
def get_file_by_id_and_user_id(self, id: str, user_id: str) -> Optional[FileModel]:
with get_db() as db:
try:
file = db.query(File).filter_by(id=id, user_id=user_id).first()
if file:
return FileModel.model_validate(file)
else:
return None
except Exception:
return None
def get_file_metadata_by_id(self, id: str) -> Optional[FileMetadataResponse]:
with get_db() as db:
try:
file = db.get(File, id)
return FileMetadataResponse(
id=file.id,
hash=file.hash,
meta=file.meta,
created_at=file.created_at,
updated_at=file.updated_at,
@@ -147,6 +166,15 @@ class FilesTable:
with get_db() as db:
return [FileModel.model_validate(file) for file in db.query(File).all()]
def check_access_by_user_id(self, id, user_id, permission="write") -> bool:
file = self.get_file_by_id(id)
if not file:
return False
if file.user_id == user_id:
return True
# Implement additional access control logic here as needed
return False
def get_files_by_ids(self, ids: list[str]) -> list[FileModel]:
with get_db() as db:
return [
@@ -162,11 +190,14 @@ class FilesTable:
return [
FileMetadataResponse(
id=file.id,
hash=file.hash,
meta=file.meta,
created_at=file.created_at,
updated_at=file.updated_at,
)
for file in db.query(File)
for file in db.query(
File.id, File.hash, File.meta, File.created_at, File.updated_at
)
.filter(File.id.in_(ids))
.order_by(File.updated_at.desc())
.all()
@@ -179,6 +210,29 @@ class FilesTable:
for file in db.query(File).filter_by(user_id=user_id).all()
]
def update_file_by_id(
self, id: str, form_data: FileUpdateForm
) -> Optional[FileModel]:
with get_db() as db:
try:
file = db.query(File).filter_by(id=id).first()
if form_data.hash is not None:
file.hash = form_data.hash
if form_data.data is not None:
file.data = {**(file.data if file.data else {}), **form_data.data}
if form_data.meta is not None:
file.meta = {**(file.meta if file.meta else {}), **form_data.meta}
file.updated_at = int(time.time())
db.commit()
return FileModel.model_validate(file)
except Exception as e:
log.exception(f"Error updating file completely by id: {e}")
return None
def update_file_hash_by_id(self, id: str, hash: str) -> Optional[FileModel]:
with get_db() as db:
try:
+116 -28
View File
@@ -2,14 +2,14 @@ import logging
import time
import uuid
from typing import Optional
import re
from pydantic import BaseModel, ConfigDict
from sqlalchemy import BigInteger, Column, Text, JSON, Boolean, func
from open_webui.internal.db import Base, get_db
from open_webui.models.chats import Chats
from open_webui.env import SRC_LOG_LEVELS
from pydantic import BaseModel, ConfigDict
from sqlalchemy import BigInteger, Column, Text, JSON, Boolean
from open_webui.utils.access_control import get_permissions
log = logging.getLogger(__name__)
@@ -23,12 +23,13 @@ log.setLevel(SRC_LOG_LEVELS["MODELS"])
class Folder(Base):
__tablename__ = "folder"
id = Column(Text, primary_key=True)
id = Column(Text, primary_key=True, unique=True)
parent_id = Column(Text, nullable=True)
user_id = Column(Text)
name = Column(Text)
items = Column(JSON, nullable=True)
meta = Column(JSON, nullable=True)
data = Column(JSON, nullable=True)
is_expanded = Column(Boolean, default=False)
created_at = Column(BigInteger)
updated_at = Column(BigInteger)
@@ -41,6 +42,7 @@ class FolderModel(BaseModel):
name: str
items: Optional[dict] = None
meta: Optional[dict] = None
data: Optional[dict] = None
is_expanded: bool = False
created_at: int
updated_at: int
@@ -48,6 +50,20 @@ class FolderModel(BaseModel):
model_config = ConfigDict(from_attributes=True)
class FolderMetadataResponse(BaseModel):
icon: Optional[str] = None
class FolderNameIdResponse(BaseModel):
id: str
name: str
meta: Optional[FolderMetadataResponse] = None
parent_id: Optional[str] = None
is_expanded: bool = False
created_at: int
updated_at: int
####################
# Forms
####################
@@ -55,12 +71,21 @@ class FolderModel(BaseModel):
class FolderForm(BaseModel):
name: str
data: Optional[dict] = None
meta: Optional[dict] = None
model_config = ConfigDict(extra="allow")
class FolderUpdateForm(BaseModel):
name: Optional[str] = None
data: Optional[dict] = None
meta: Optional[dict] = None
model_config = ConfigDict(extra="allow")
class FolderTable:
def insert_new_folder(
self, user_id: str, name: str, parent_id: Optional[str] = None
self, user_id: str, form_data: FolderForm, parent_id: Optional[str] = None
) -> Optional[FolderModel]:
with get_db() as db:
id = str(uuid.uuid4())
@@ -68,7 +93,7 @@ class FolderTable:
**{
"id": id,
"user_id": user_id,
"name": name,
**(form_data.model_dump(exclude_unset=True) or {}),
"parent_id": parent_id,
"created_at": int(time.time()),
"updated_at": int(time.time()),
@@ -103,7 +128,7 @@ class FolderTable:
def get_children_folders_by_id_and_user_id(
self, id: str, user_id: str
) -> Optional[FolderModel]:
) -> Optional[list[FolderModel]]:
try:
with get_db() as db:
folders = []
@@ -187,8 +212,8 @@ class FolderTable:
log.error(f"update_folder: {e}")
return
def update_folder_name_by_id_and_user_id(
self, id: str, user_id: str, name: str
def update_folder_by_id_and_user_id(
self, id: str, user_id: str, form_data: FolderUpdateForm
) -> Optional[FolderModel]:
try:
with get_db() as db:
@@ -197,18 +222,35 @@ class FolderTable:
if not folder:
return None
form_data = form_data.model_dump(exclude_unset=True)
existing_folder = (
db.query(Folder)
.filter_by(name=name, parent_id=folder.parent_id, user_id=user_id)
.filter_by(
name=form_data.get("name"),
parent_id=folder.parent_id,
user_id=user_id,
)
.first()
)
if existing_folder:
if existing_folder and existing_folder.id != id:
return None
folder.name = name
folder.updated_at = int(time.time())
folder.name = form_data.get("name", folder.name)
if "data" in form_data:
folder.data = {
**(folder.data or {}),
**form_data["data"],
}
if "meta" in form_data:
folder.meta = {
**(folder.meta or {}),
**form_data["meta"],
}
folder.updated_at = int(time.time())
db.commit()
return FolderModel.model_validate(folder)
@@ -236,18 +278,15 @@ class FolderTable:
log.error(f"update_folder: {e}")
return
def delete_folder_by_id_and_user_id(
self, id: str, user_id: str, delete_chats=True
) -> bool:
def delete_folder_by_id_and_user_id(self, id: str, user_id: str) -> list[str]:
try:
folder_ids = []
with get_db() as db:
folder = db.query(Folder).filter_by(id=id, user_id=user_id).first()
if not folder:
return False
return folder_ids
if delete_chats:
# Delete all chats in the folder
Chats.delete_chats_by_user_id_and_folder_id(user_id, folder.id)
folder_ids.append(folder.id)
# Delete all children folders
def delete_children(folder):
@@ -255,12 +294,9 @@ class FolderTable:
folder.id, user_id
)
for folder_child in folder_children:
if delete_chats:
Chats.delete_chats_by_user_id_and_folder_id(
user_id, folder_child.id
)
delete_children(folder_child)
folder_ids.append(folder_child.id)
folder = db.query(Folder).filter_by(id=folder_child.id).first()
db.delete(folder)
@@ -269,10 +305,62 @@ class FolderTable:
delete_children(folder)
db.delete(folder)
db.commit()
return True
return folder_ids
except Exception as e:
log.error(f"delete_folder: {e}")
return False
return []
def normalize_folder_name(self, name: str) -> str:
# Replace _ and space with a single space, lower case, collapse multiple spaces
name = re.sub(r"[\s_]+", " ", name)
return name.strip().lower()
def search_folders_by_names(
self, user_id: str, queries: list[str]
) -> list[FolderModel]:
"""
Search for folders for a user where the name matches any of the queries, treating _ and space as equivalent, case-insensitive.
"""
normalized_queries = [self.normalize_folder_name(q) for q in queries]
if not normalized_queries:
return []
results = {}
with get_db() as db:
folders = db.query(Folder).filter_by(user_id=user_id).all()
for folder in folders:
if self.normalize_folder_name(folder.name) in normalized_queries:
results[folder.id] = FolderModel.model_validate(folder)
# get children folders
children = self.get_children_folders_by_id_and_user_id(
folder.id, user_id
)
for child in children:
results[child.id] = child
# Return the results as a list
if not results:
return []
else:
results = list(results.values())
return results
def search_folders_by_name_contains(
self, user_id: str, query: str
) -> list[FolderModel]:
"""
Partial match: normalized name contains (as substring) the normalized query.
"""
normalized_query = self.normalize_folder_name(query)
results = []
with get_db() as db:
folders = db.query(Folder).filter_by(user_id=user_id).all()
for folder in folders:
norm_name = self.normalize_folder_name(folder.name)
if normalized_query in norm_name:
results.append(FolderModel.model_validate(folder))
return results
Folders = FolderTable()
+132 -11
View File
@@ -3,10 +3,10 @@ import time
from typing import Optional
from open_webui.internal.db import Base, JSONField, get_db
from open_webui.models.users import Users
from open_webui.models.users import Users, UserModel
from open_webui.env import SRC_LOG_LEVELS
from pydantic import BaseModel, ConfigDict
from sqlalchemy import BigInteger, Boolean, Column, String, Text
from sqlalchemy import BigInteger, Boolean, Column, String, Text, Index
log = logging.getLogger(__name__)
log.setLevel(SRC_LOG_LEVELS["MODELS"])
@@ -19,7 +19,7 @@ log.setLevel(SRC_LOG_LEVELS["MODELS"])
class Function(Base):
__tablename__ = "function"
id = Column(String, primary_key=True)
id = Column(String, primary_key=True, unique=True)
user_id = Column(String)
name = Column(Text)
type = Column(Text)
@@ -31,10 +31,13 @@ class Function(Base):
updated_at = Column(BigInteger)
created_at = Column(BigInteger)
__table_args__ = (Index("is_global_idx", "is_global"),)
class FunctionMeta(BaseModel):
description: Optional[str] = None
manifest: Optional[dict] = {}
model_config = ConfigDict(extra="allow")
class FunctionModel(BaseModel):
@@ -52,11 +55,31 @@ class FunctionModel(BaseModel):
model_config = ConfigDict(from_attributes=True)
class FunctionWithValvesModel(BaseModel):
id: str
user_id: str
name: str
type: str
content: str
meta: FunctionMeta
valves: Optional[dict] = None
is_active: bool = False
is_global: bool = False
updated_at: int # timestamp in epoch
created_at: int # timestamp in epoch
model_config = ConfigDict(from_attributes=True)
####################
# Forms
####################
class FunctionUserResponse(FunctionModel):
user: Optional[UserModel] = None
class FunctionResponse(BaseModel):
id: str
user_id: str
@@ -108,6 +131,54 @@ class FunctionsTable:
log.exception(f"Error creating a new function: {e}")
return None
def sync_functions(
self, user_id: str, functions: list[FunctionWithValvesModel]
) -> list[FunctionWithValvesModel]:
# Synchronize functions for a user by updating existing ones, inserting new ones, and removing those that are no longer present.
try:
with get_db() as db:
# Get existing functions
existing_functions = db.query(Function).all()
existing_ids = {func.id for func in existing_functions}
# Prepare a set of new function IDs
new_function_ids = {func.id for func in functions}
# Update or insert functions
for func in functions:
if func.id in existing_ids:
db.query(Function).filter_by(id=func.id).update(
{
**func.model_dump(),
"user_id": user_id,
"updated_at": int(time.time()),
}
)
else:
new_func = Function(
**{
**func.model_dump(),
"user_id": user_id,
"updated_at": int(time.time()),
}
)
db.add(new_func)
# Remove functions that are no longer present
for func in existing_functions:
if func.id not in new_function_ids:
db.delete(func)
db.commit()
return [
FunctionModel.model_validate(func)
for func in db.query(Function).all()
]
except Exception as e:
log.exception(f"Error syncing functions for user {user_id}: {e}")
return []
def get_function_by_id(self, id: str) -> Optional[FunctionModel]:
try:
with get_db() as db:
@@ -116,19 +187,48 @@ class FunctionsTable:
except Exception:
return None
def get_functions(self, active_only=False) -> list[FunctionModel]:
def get_functions(
self, active_only=False, include_valves=False
) -> list[FunctionModel | FunctionWithValvesModel]:
with get_db() as db:
if active_only:
functions = db.query(Function).filter_by(is_active=True).all()
else:
functions = db.query(Function).all()
if include_valves:
return [
FunctionModel.model_validate(function)
for function in db.query(Function).filter_by(is_active=True).all()
FunctionWithValvesModel.model_validate(function)
for function in functions
]
else:
return [
FunctionModel.model_validate(function)
for function in db.query(Function).all()
FunctionModel.model_validate(function) for function in functions
]
def get_function_list(self) -> list[FunctionUserResponse]:
with get_db() as db:
functions = db.query(Function).order_by(Function.updated_at.desc()).all()
user_ids = list(set(func.user_id for func in functions))
users = Users.get_users_by_user_ids(user_ids) if user_ids else []
users_dict = {user.id: user for user in users}
return [
FunctionUserResponse.model_validate(
{
**FunctionModel.model_validate(func).model_dump(),
"user": (
users_dict.get(func.user_id).model_dump()
if func.user_id in users_dict
else None
),
}
)
for func in functions
]
def get_functions_by_type(
self, type: str, active_only=False
) -> list[FunctionModel]:
@@ -187,6 +287,29 @@ class FunctionsTable:
except Exception:
return None
def update_function_metadata_by_id(
self, id: str, metadata: dict
) -> Optional[FunctionModel]:
with get_db() as db:
try:
function = db.get(Function, id)
if function:
if function.meta:
function.meta = {**function.meta, **metadata}
else:
function.meta = metadata
function.updated_at = int(time.time())
db.commit()
db.refresh(function)
return self.get_function_by_id(id)
else:
return None
except Exception as e:
log.exception(f"Error updating function metadata by id {id}: {e}")
return None
def get_user_valves_by_id_and_user_id(
self, id: str, user_id: str
) -> Optional[dict]:
@@ -202,9 +325,7 @@ class FunctionsTable:
return user_settings["functions"]["valves"].get(id, {})
except Exception as e:
log.exception(
f"Error getting user values by id {id} and user id {user_id}: {e}"
)
log.exception(f"Error getting user values by id {id} and user id {user_id}")
return None
def update_user_valves_by_id_and_user_id(
+362 -40
View File
@@ -11,7 +11,18 @@ from open_webui.models.files import FileMetadataResponse
from pydantic import BaseModel, ConfigDict
from sqlalchemy import BigInteger, Column, String, Text, JSON, func
from sqlalchemy import (
BigInteger,
Column,
String,
Text,
JSON,
and_,
func,
ForeignKey,
cast,
or_,
)
log = logging.getLogger(__name__)
@@ -35,14 +46,12 @@ class Group(Base):
meta = Column(JSON, nullable=True)
permissions = Column(JSON, nullable=True)
user_ids = Column(JSON, nullable=True)
created_at = Column(BigInteger)
updated_at = Column(BigInteger)
class GroupModel(BaseModel):
model_config = ConfigDict(from_attributes=True)
id: str
user_id: str
@@ -53,38 +62,62 @@ class GroupModel(BaseModel):
meta: Optional[dict] = None
permissions: Optional[dict] = None
user_ids: list[str] = []
created_at: int # timestamp in epoch
updated_at: int # timestamp in epoch
model_config = ConfigDict(from_attributes=True)
class GroupMember(Base):
__tablename__ = "group_member"
id = Column(Text, unique=True, primary_key=True)
group_id = Column(
Text,
ForeignKey("group.id", ondelete="CASCADE"),
nullable=False,
)
user_id = Column(Text, nullable=False)
created_at = Column(BigInteger, nullable=True)
updated_at = Column(BigInteger, nullable=True)
class GroupMemberModel(BaseModel):
id: str
group_id: str
user_id: str
created_at: Optional[int] = None # timestamp in epoch
updated_at: Optional[int] = None # timestamp in epoch
####################
# Forms
####################
class GroupResponse(BaseModel):
id: str
user_id: str
name: str
description: str
permissions: Optional[dict] = None
data: Optional[dict] = None
meta: Optional[dict] = None
user_ids: list[str] = []
created_at: int # timestamp in epoch
updated_at: int # timestamp in epoch
class GroupResponse(GroupModel):
member_count: Optional[int] = None
class GroupForm(BaseModel):
name: str
description: str
permissions: Optional[dict] = None
data: Optional[dict] = None
class UserIdsForm(BaseModel):
user_ids: Optional[list[str]] = None
class GroupUpdateForm(GroupForm):
user_ids: Optional[list[str]] = None
pass
class GroupListResponse(BaseModel):
items: list[GroupResponse] = []
total: int = 0
class GroupTable:
@@ -115,24 +148,94 @@ class GroupTable:
except Exception:
return None
def get_groups(self) -> list[GroupModel]:
def get_all_groups(self) -> list[GroupModel]:
with get_db() as db:
groups = db.query(Group).order_by(Group.updated_at.desc()).all()
return [GroupModel.model_validate(group) for group in groups]
def get_groups(self, filter) -> list[GroupResponse]:
with get_db() as db:
query = db.query(Group)
if filter:
if "query" in filter:
query = query.filter(Group.name.ilike(f"%{filter['query']}%"))
if "member_id" in filter:
query = query.join(
GroupMember, GroupMember.group_id == Group.id
).filter(GroupMember.user_id == filter["member_id"])
if "share" in filter:
share_value = filter["share"]
json_share = Group.data["config"]["share"].as_boolean()
if share_value:
query = query.filter(
or_(
Group.data.is_(None),
json_share.is_(None),
json_share == True,
)
)
else:
query = query.filter(
and_(Group.data.isnot(None), json_share == False)
)
groups = query.order_by(Group.updated_at.desc()).all()
return [
GroupModel.model_validate(group)
for group in db.query(Group).order_by(Group.updated_at.desc()).all()
GroupResponse.model_validate(
{
**GroupModel.model_validate(group).model_dump(),
"member_count": self.get_group_member_count_by_id(group.id),
}
)
for group in groups
]
def search_groups(
self, filter: Optional[dict] = None, skip: int = 0, limit: int = 30
) -> GroupListResponse:
with get_db() as db:
query = db.query(Group)
if filter:
if "query" in filter:
query = query.filter(Group.name.ilike(f"%{filter['query']}%"))
if "member_id" in filter:
query = query.join(
GroupMember, GroupMember.group_id == Group.id
).filter(GroupMember.user_id == filter["member_id"])
if "share" in filter:
# 'share' is stored in data JSON, support both sqlite and postgres
share_value = filter["share"]
print("Filtering by share:", share_value)
query = query.filter(
Group.data.op("->>")("share") == str(share_value)
)
total = query.count()
query = query.order_by(Group.updated_at.desc())
groups = query.offset(skip).limit(limit).all()
return {
"items": [
GroupResponse.model_validate(
**GroupModel.model_validate(group).model_dump(),
member_count=self.get_group_member_count_by_id(group.id),
)
for group in groups
],
"total": total,
}
def get_groups_by_member_id(self, user_id: str) -> list[GroupModel]:
with get_db() as db:
return [
GroupModel.model_validate(group)
for group in db.query(Group)
.filter(
func.json_array_length(Group.user_ids) > 0
) # Ensure array exists
.filter(
Group.user_ids.cast(String).like(f'%"{user_id}"%')
) # String-based check
.join(GroupMember, GroupMember.group_id == Group.id)
.filter(GroupMember.user_id == user_id)
.order_by(Group.updated_at.desc())
.all()
]
@@ -145,12 +248,63 @@ class GroupTable:
except Exception:
return None
def get_group_user_ids_by_id(self, id: str) -> Optional[str]:
group = self.get_group_by_id(id)
if group:
return group.user_ids
else:
return None
def get_group_user_ids_by_id(self, id: str) -> Optional[list[str]]:
with get_db() as db:
members = (
db.query(GroupMember.user_id).filter(GroupMember.group_id == id).all()
)
if not members:
return None
return [m[0] for m in members]
def get_group_user_ids_by_ids(self, group_ids: list[str]) -> dict[str, list[str]]:
with get_db() as db:
members = (
db.query(GroupMember.group_id, GroupMember.user_id)
.filter(GroupMember.group_id.in_(group_ids))
.all()
)
group_user_ids: dict[str, list[str]] = {
group_id: [] for group_id in group_ids
}
for group_id, user_id in members:
group_user_ids[group_id].append(user_id)
return group_user_ids
def set_group_user_ids_by_id(self, group_id: str, user_ids: list[str]) -> None:
with get_db() as db:
# Delete existing members
db.query(GroupMember).filter(GroupMember.group_id == group_id).delete()
# Insert new members
now = int(time.time())
new_members = [
GroupMember(
id=str(uuid.uuid4()),
group_id=group_id,
user_id=user_id,
created_at=now,
updated_at=now,
)
for user_id in user_ids
]
db.add_all(new_members)
db.commit()
def get_group_member_count_by_id(self, id: str) -> int:
with get_db() as db:
count = (
db.query(func.count(GroupMember.user_id))
.filter(GroupMember.group_id == id)
.scalar()
)
return count if count else 0
def update_group_by_id(
self, id: str, form_data: GroupUpdateForm, overwrite: bool = False
@@ -191,21 +345,189 @@ class GroupTable:
def remove_user_from_all_groups(self, user_id: str) -> bool:
with get_db() as db:
try:
groups = self.get_groups_by_member_id(user_id)
# Find all groups the user belongs to
groups = (
db.query(Group)
.join(GroupMember, GroupMember.group_id == Group.id)
.filter(GroupMember.user_id == user_id)
.all()
)
# Remove the user from each group
for group in groups:
group.user_ids.remove(user_id)
db.query(Group).filter_by(id=group.id).update(
{
"user_ids": group.user_ids,
"updated_at": int(time.time()),
}
)
db.commit()
db.query(GroupMember).filter(
GroupMember.group_id == group.id, GroupMember.user_id == user_id
).delete()
db.query(Group).filter_by(id=group.id).update(
{"updated_at": int(time.time())}
)
db.commit()
return True
except Exception:
db.rollback()
return False
def create_groups_by_group_names(
self, user_id: str, group_names: list[str]
) -> list[GroupModel]:
# check for existing groups
existing_groups = self.get_all_groups()
existing_group_names = {group.name for group in existing_groups}
new_groups = []
with get_db() as db:
for group_name in group_names:
if group_name not in existing_group_names:
new_group = GroupModel(
id=str(uuid.uuid4()),
user_id=user_id,
name=group_name,
description="",
created_at=int(time.time()),
updated_at=int(time.time()),
)
try:
result = Group(**new_group.model_dump())
db.add(result)
db.commit()
db.refresh(result)
new_groups.append(GroupModel.model_validate(result))
except Exception as e:
log.exception(e)
continue
return new_groups
def sync_groups_by_group_names(self, user_id: str, group_names: list[str]) -> bool:
with get_db() as db:
try:
now = int(time.time())
# 1. Groups that SHOULD contain the user
target_groups = (
db.query(Group).filter(Group.name.in_(group_names)).all()
)
target_group_ids = {g.id for g in target_groups}
# 2. Groups the user is CURRENTLY in
existing_group_ids = {
g.id
for g in db.query(Group)
.join(GroupMember, GroupMember.group_id == Group.id)
.filter(GroupMember.user_id == user_id)
.all()
}
# 3. Determine adds + removals
groups_to_add = target_group_ids - existing_group_ids
groups_to_remove = existing_group_ids - target_group_ids
# 4. Remove in one bulk delete
if groups_to_remove:
db.query(GroupMember).filter(
GroupMember.user_id == user_id,
GroupMember.group_id.in_(groups_to_remove),
).delete(synchronize_session=False)
db.query(Group).filter(Group.id.in_(groups_to_remove)).update(
{"updated_at": now}, synchronize_session=False
)
# 5. Bulk insert missing memberships
for group_id in groups_to_add:
db.add(
GroupMember(
id=str(uuid.uuid4()),
group_id=group_id,
user_id=user_id,
created_at=now,
updated_at=now,
)
)
if groups_to_add:
db.query(Group).filter(Group.id.in_(groups_to_add)).update(
{"updated_at": now}, synchronize_session=False
)
db.commit()
return True
except Exception as e:
log.exception(e)
db.rollback()
return False
def add_users_to_group(
self, id: str, user_ids: Optional[list[str]] = None
) -> Optional[GroupModel]:
try:
with get_db() as db:
group = db.query(Group).filter_by(id=id).first()
if not group:
return None
now = int(time.time())
for user_id in user_ids or []:
try:
db.add(
GroupMember(
id=str(uuid.uuid4()),
group_id=id,
user_id=user_id,
created_at=now,
updated_at=now,
)
)
db.flush() # Detect unique constraint violation early
except Exception:
db.rollback() # Clear failed INSERT
db.begin() # Start a new transaction
continue # Duplicate → ignore
group.updated_at = now
db.commit()
db.refresh(group)
return GroupModel.model_validate(group)
except Exception as e:
log.exception(e)
return None
def remove_users_from_group(
self, id: str, user_ids: Optional[list[str]] = None
) -> Optional[GroupModel]:
try:
with get_db() as db:
group = db.query(Group).filter_by(id=id).first()
if not group:
return None
if not user_ids:
return GroupModel.model_validate(group)
# Remove each user from group_member
for user_id in user_ids:
db.query(GroupMember).filter(
GroupMember.group_id == id, GroupMember.user_id == user_id
).delete()
# Update group timestamp
group.updated_at = int(time.time())
db.commit()
db.refresh(group)
return GroupModel.model_validate(group)
except Exception as e:
log.exception(e)
return None
Groups = GroupTable()
+162 -13
View File
@@ -7,12 +7,21 @@ import uuid
from open_webui.internal.db import Base, get_db
from open_webui.env import SRC_LOG_LEVELS
from open_webui.models.files import FileMetadataResponse
from open_webui.models.files import File, FileModel, FileMetadataResponse
from open_webui.models.groups import Groups
from open_webui.models.users import Users, UserResponse
from pydantic import BaseModel, ConfigDict
from sqlalchemy import BigInteger, Column, String, Text, JSON
from sqlalchemy import (
BigInteger,
Column,
ForeignKey,
String,
Text,
JSON,
UniqueConstraint,
)
from open_webui.utils.access_control import has_access
@@ -33,9 +42,7 @@ class Knowledge(Base):
name = Column(Text)
description = Column(Text)
data = Column(JSON, nullable=True)
meta = Column(JSON, nullable=True)
access_control = Column(JSON, nullable=True) # Controls data access levels.
# Defines access control rules for this entry.
# - `None`: Public access, available to all users with the "user" role.
@@ -66,7 +73,6 @@ class KnowledgeModel(BaseModel):
name: str
description: str
data: Optional[dict] = None
meta: Optional[dict] = None
access_control: Optional[dict] = None
@@ -75,11 +81,42 @@ class KnowledgeModel(BaseModel):
updated_at: int # timestamp in epoch
class KnowledgeFile(Base):
__tablename__ = "knowledge_file"
id = Column(Text, unique=True, primary_key=True)
knowledge_id = Column(
Text, ForeignKey("knowledge.id", ondelete="CASCADE"), nullable=False
)
file_id = Column(Text, ForeignKey("file.id", ondelete="CASCADE"), nullable=False)
user_id = Column(Text, nullable=False)
created_at = Column(BigInteger, nullable=False)
updated_at = Column(BigInteger, nullable=False)
__table_args__ = (
UniqueConstraint(
"knowledge_id", "file_id", name="uq_knowledge_file_knowledge_file"
),
)
class KnowledgeFileModel(BaseModel):
id: str
knowledge_id: str
file_id: str
user_id: str
created_at: int # timestamp in epoch
updated_at: int # timestamp in epoch
model_config = ConfigDict(from_attributes=True)
####################
# Forms
####################
class KnowledgeUserModel(KnowledgeModel):
user: Optional[UserResponse] = None
@@ -95,7 +132,6 @@ class KnowledgeUserResponse(KnowledgeUserModel):
class KnowledgeForm(BaseModel):
name: str
description: str
data: Optional[dict] = None
access_control: Optional[dict] = None
@@ -128,11 +164,18 @@ class KnowledgeTable:
def get_knowledge_bases(self) -> list[KnowledgeUserModel]:
with get_db() as db:
knowledge_bases = []
for knowledge in (
all_knowledge = (
db.query(Knowledge).order_by(Knowledge.updated_at.desc()).all()
):
user = Users.get_user_by_id(knowledge.user_id)
)
user_ids = list(set(knowledge.user_id for knowledge in all_knowledge))
users = Users.get_users_by_user_ids(user_ids) if user_ids else []
users_dict = {user.id: user for user in users}
knowledge_bases = []
for knowledge in all_knowledge:
user = users_dict.get(knowledge.user_id)
knowledge_bases.append(
KnowledgeUserModel.model_validate(
{
@@ -143,15 +186,27 @@ class KnowledgeTable:
)
return knowledge_bases
def check_access_by_user_id(self, id, user_id, permission="write") -> bool:
knowledge = self.get_knowledge_by_id(id)
if not knowledge:
return False
if knowledge.user_id == user_id:
return True
user_group_ids = {group.id for group in Groups.get_groups_by_member_id(user_id)}
return has_access(user_id, permission, knowledge.access_control, user_group_ids)
def get_knowledge_bases_by_user_id(
self, user_id: str, permission: str = "write"
) -> list[KnowledgeUserModel]:
knowledge_bases = self.get_knowledge_bases()
user_group_ids = {group.id for group in Groups.get_groups_by_member_id(user_id)}
return [
knowledge_base
for knowledge_base in knowledge_bases
if knowledge_base.user_id == user_id
or has_access(user_id, permission, knowledge_base.access_control)
or has_access(
user_id, permission, knowledge_base.access_control, user_group_ids
)
]
def get_knowledge_by_id(self, id: str) -> Optional[KnowledgeModel]:
@@ -162,6 +217,100 @@ class KnowledgeTable:
except Exception:
return None
def get_knowledges_by_file_id(self, file_id: str) -> list[KnowledgeModel]:
try:
with get_db() as db:
knowledges = (
db.query(Knowledge)
.join(KnowledgeFile, Knowledge.id == KnowledgeFile.knowledge_id)
.filter(KnowledgeFile.file_id == file_id)
.all()
)
return [
KnowledgeModel.model_validate(knowledge) for knowledge in knowledges
]
except Exception:
return []
def get_files_by_id(self, knowledge_id: str) -> list[FileModel]:
try:
with get_db() as db:
files = (
db.query(File)
.join(KnowledgeFile, File.id == KnowledgeFile.file_id)
.filter(KnowledgeFile.knowledge_id == knowledge_id)
.all()
)
return [FileModel.model_validate(file) for file in files]
except Exception:
return []
def get_file_metadatas_by_id(self, knowledge_id: str) -> list[FileMetadataResponse]:
try:
with get_db() as db:
files = self.get_files_by_id(knowledge_id)
return [FileMetadataResponse(**file.model_dump()) for file in files]
except Exception:
return []
def add_file_to_knowledge_by_id(
self, knowledge_id: str, file_id: str, user_id: str
) -> Optional[KnowledgeFileModel]:
with get_db() as db:
knowledge_file = KnowledgeFileModel(
**{
"id": str(uuid.uuid4()),
"knowledge_id": knowledge_id,
"file_id": file_id,
"user_id": user_id,
"created_at": int(time.time()),
"updated_at": int(time.time()),
}
)
try:
result = KnowledgeFile(**knowledge_file.model_dump())
db.add(result)
db.commit()
db.refresh(result)
if result:
return KnowledgeFileModel.model_validate(result)
else:
return None
except Exception:
return None
def remove_file_from_knowledge_by_id(self, knowledge_id: str, file_id: str) -> bool:
try:
with get_db() as db:
db.query(KnowledgeFile).filter_by(
knowledge_id=knowledge_id, file_id=file_id
).delete()
db.commit()
return True
except Exception:
return False
def reset_knowledge_by_id(self, id: str) -> Optional[KnowledgeModel]:
try:
with get_db() as db:
# Delete all knowledge_file entries for this knowledge_id
db.query(KnowledgeFile).filter_by(knowledge_id=id).delete()
db.commit()
# Update the knowledge entry's updated_at timestamp
db.query(Knowledge).filter_by(id=id).update(
{
"updated_at": int(time.time()),
}
)
db.commit()
return self.get_knowledge_by_id(id=id)
except Exception as e:
log.exception(e)
return None
def update_knowledge_by_id(
self, id: str, form_data: KnowledgeForm, overwrite: bool = False
) -> Optional[KnowledgeModel]:
+14 -5
View File
@@ -14,7 +14,7 @@ from sqlalchemy import BigInteger, Column, String, Text
class Memory(Base):
__tablename__ = "memory"
id = Column(String, primary_key=True)
id = Column(String, primary_key=True, unique=True)
user_id = Column(String)
content = Column(Text)
updated_at = Column(BigInteger)
@@ -71,9 +71,13 @@ class MemoriesTable:
) -> Optional[MemoryModel]:
with get_db() as db:
try:
db.query(Memory).filter_by(id=id, user_id=user_id).update(
{"content": content, "updated_at": int(time.time())}
)
memory = db.get(Memory, id)
if not memory or memory.user_id != user_id:
return None
memory.content = content
memory.updated_at = int(time.time())
db.commit()
return self.get_memory_by_id(id)
except Exception:
@@ -127,7 +131,12 @@ class MemoriesTable:
def delete_memory_by_id_and_user_id(self, id: str, user_id: str) -> bool:
with get_db() as db:
try:
db.query(Memory).filter_by(id=id, user_id=user_id).delete()
memory = db.get(Memory, id)
if not memory or memory.user_id != user_id:
return None
# Delete the memory
db.delete(memory)
db.commit()
return True
+208 -25
View File
@@ -5,6 +5,8 @@ from typing import Optional
from open_webui.internal.db import Base, get_db
from open_webui.models.tags import TagModel, Tag, Tags
from open_webui.models.users import Users, User, UserNameResponse
from open_webui.models.channels import Channels, ChannelMember
from pydantic import BaseModel, ConfigDict
@@ -19,7 +21,7 @@ from sqlalchemy.sql import exists
class MessageReaction(Base):
__tablename__ = "message_reaction"
id = Column(Text, primary_key=True)
id = Column(Text, primary_key=True, unique=True)
user_id = Column(Text)
message_id = Column(Text)
name = Column(Text)
@@ -38,13 +40,19 @@ class MessageReactionModel(BaseModel):
class Message(Base):
__tablename__ = "message"
id = Column(Text, primary_key=True)
id = Column(Text, primary_key=True, unique=True)
user_id = Column(Text)
channel_id = Column(Text, nullable=True)
reply_to_id = Column(Text, nullable=True)
parent_id = Column(Text, nullable=True)
# Pins
is_pinned = Column(Boolean, nullable=False, default=False)
pinned_at = Column(BigInteger, nullable=True)
pinned_by = Column(Text, nullable=True)
content = Column(Text)
data = Column(JSON, nullable=True)
meta = Column(JSON, nullable=True)
@@ -60,14 +68,20 @@ class MessageModel(BaseModel):
user_id: str
channel_id: Optional[str] = None
reply_to_id: Optional[str] = None
parent_id: Optional[str] = None
# Pins
is_pinned: bool = False
pinned_by: Optional[str] = None
pinned_at: Optional[int] = None # timestamp in epoch (time_ns)
content: str
data: Optional[dict] = None
meta: Optional[dict] = None
created_at: int # timestamp in epoch
updated_at: int # timestamp in epoch
created_at: int # timestamp in epoch (time_ns)
updated_at: int # timestamp in epoch (time_ns)
####################
@@ -76,7 +90,9 @@ class MessageModel(BaseModel):
class MessageForm(BaseModel):
temp_id: Optional[str] = None
content: str
reply_to_id: Optional[str] = None
parent_id: Optional[str] = None
data: Optional[dict] = None
meta: Optional[dict] = None
@@ -84,11 +100,23 @@ class MessageForm(BaseModel):
class Reactions(BaseModel):
name: str
user_ids: list[str]
users: list[dict]
count: int
class MessageResponse(MessageModel):
class MessageUserResponse(MessageModel):
user: Optional[UserNameResponse] = None
class MessageReplyToResponse(MessageUserResponse):
reply_to_message: Optional[MessageUserResponse] = None
class MessageWithReactionsResponse(MessageUserResponse):
reactions: list[Reactions]
class MessageResponse(MessageReplyToResponse):
latest_reply_at: Optional[int]
reply_count: int
reactions: list[Reactions]
@@ -99,15 +127,21 @@ class MessageTable:
self, form_data: MessageForm, channel_id: str, user_id: str
) -> Optional[MessageModel]:
with get_db() as db:
id = str(uuid.uuid4())
channel_member = Channels.join_channel(channel_id, user_id)
id = str(uuid.uuid4())
ts = int(time.time_ns())
message = MessageModel(
**{
"id": id,
"user_id": user_id,
"channel_id": channel_id,
"reply_to_id": form_data.reply_to_id,
"parent_id": form_data.parent_id,
"is_pinned": False,
"pinned_at": None,
"pinned_by": None,
"content": form_data.content,
"data": form_data.data,
"meta": form_data.meta,
@@ -115,8 +149,8 @@ class MessageTable:
"updated_at": ts,
}
)
result = Message(**message.model_dump())
db.add(result)
db.commit()
db.refresh(result)
@@ -128,19 +162,32 @@ class MessageTable:
if not message:
return None
reactions = self.get_reactions_by_message_id(id)
replies = self.get_replies_by_message_id(id)
reply_to_message = (
self.get_message_by_id(message.reply_to_id)
if message.reply_to_id
else None
)
return MessageResponse(
**{
reactions = self.get_reactions_by_message_id(id)
thread_replies = self.get_thread_replies_by_message_id(id)
user = Users.get_user_by_id(message.user_id)
return MessageResponse.model_validate(
{
**MessageModel.model_validate(message).model_dump(),
"latest_reply_at": replies[0].created_at if replies else None,
"reply_count": len(replies),
"user": user.model_dump() if user else None,
"reply_to_message": (
reply_to_message.model_dump() if reply_to_message else None
),
"latest_reply_at": (
thread_replies[0].created_at if thread_replies else None
),
"reply_count": len(thread_replies),
"reactions": reactions,
}
)
def get_replies_by_message_id(self, id: str) -> list[MessageModel]:
def get_thread_replies_by_message_id(self, id: str) -> list[MessageReplyToResponse]:
with get_db() as db:
all_messages = (
db.query(Message)
@@ -148,7 +195,27 @@ class MessageTable:
.order_by(Message.created_at.desc())
.all()
)
return [MessageModel.model_validate(message) for message in all_messages]
messages = []
for message in all_messages:
reply_to_message = (
self.get_message_by_id(message.reply_to_id)
if message.reply_to_id
else None
)
messages.append(
MessageReplyToResponse.model_validate(
{
**MessageModel.model_validate(message).model_dump(),
"reply_to_message": (
reply_to_message.model_dump()
if reply_to_message
else None
),
}
)
)
return messages
def get_reply_user_ids_by_message_id(self, id: str) -> list[str]:
with get_db() as db:
@@ -159,7 +226,7 @@ class MessageTable:
def get_messages_by_channel_id(
self, channel_id: str, skip: int = 0, limit: int = 50
) -> list[MessageModel]:
) -> list[MessageReplyToResponse]:
with get_db() as db:
all_messages = (
db.query(Message)
@@ -169,11 +236,31 @@ class MessageTable:
.limit(limit)
.all()
)
return [MessageModel.model_validate(message) for message in all_messages]
messages = []
for message in all_messages:
reply_to_message = (
self.get_message_by_id(message.reply_to_id)
if message.reply_to_id
else None
)
messages.append(
MessageReplyToResponse.model_validate(
{
**MessageModel.model_validate(message).model_dump(),
"reply_to_message": (
reply_to_message.model_dump()
if reply_to_message
else None
),
}
)
)
return messages
def get_messages_by_parent_id(
self, channel_id: str, parent_id: str, skip: int = 0, limit: int = 50
) -> list[MessageModel]:
) -> list[MessageReplyToResponse]:
with get_db() as db:
message = db.get(Message, parent_id)
@@ -193,6 +280,49 @@ class MessageTable:
if len(all_messages) < limit:
all_messages.append(message)
messages = []
for message in all_messages:
reply_to_message = (
self.get_message_by_id(message.reply_to_id)
if message.reply_to_id
else None
)
messages.append(
MessageReplyToResponse.model_validate(
{
**MessageModel.model_validate(message).model_dump(),
"reply_to_message": (
reply_to_message.model_dump()
if reply_to_message
else None
),
}
)
)
return messages
def get_last_message_by_channel_id(self, channel_id: str) -> Optional[MessageModel]:
with get_db() as db:
message = (
db.query(Message)
.filter_by(channel_id=channel_id)
.order_by(Message.created_at.desc())
.first()
)
return MessageModel.model_validate(message) if message else None
def get_pinned_messages_by_channel_id(
self, channel_id: str, skip: int = 0, limit: int = 50
) -> list[MessageModel]:
with get_db() as db:
all_messages = (
db.query(Message)
.filter_by(channel_id=channel_id, is_pinned=True)
.order_by(Message.pinned_at.desc())
.offset(skip)
.limit(limit)
.all()
)
return [MessageModel.model_validate(message) for message in all_messages]
def update_message_by_id(
@@ -201,17 +331,57 @@ class MessageTable:
with get_db() as db:
message = db.get(Message, id)
message.content = form_data.content
message.data = form_data.data
message.meta = form_data.meta
message.data = {
**(message.data if message.data else {}),
**(form_data.data if form_data.data else {}),
}
message.meta = {
**(message.meta if message.meta else {}),
**(form_data.meta if form_data.meta else {}),
}
message.updated_at = int(time.time_ns())
db.commit()
db.refresh(message)
return MessageModel.model_validate(message) if message else None
def update_is_pinned_by_id(
self, id: str, is_pinned: bool, pinned_by: Optional[str] = None
) -> Optional[MessageModel]:
with get_db() as db:
message = db.get(Message, id)
message.is_pinned = is_pinned
message.pinned_at = int(time.time_ns()) if is_pinned else None
message.pinned_by = pinned_by if is_pinned else None
db.commit()
db.refresh(message)
return MessageModel.model_validate(message) if message else None
def get_unread_message_count(
self, channel_id: str, user_id: str, last_read_at: Optional[int] = None
) -> int:
with get_db() as db:
query = db.query(Message).filter(
Message.channel_id == channel_id,
Message.parent_id == None, # only count top-level messages
Message.created_at > (last_read_at if last_read_at else 0),
)
if user_id:
query = query.filter(Message.user_id != user_id)
return query.count()
def add_reaction_to_message(
self, id: str, user_id: str, name: str
) -> Optional[MessageReactionModel]:
with get_db() as db:
# check for existing reaction
existing_reaction = (
db.query(MessageReaction)
.filter_by(message_id=id, user_id=user_id, name=name)
.first()
)
if existing_reaction:
return MessageReactionModel.model_validate(existing_reaction)
reaction_id = str(uuid.uuid4())
reaction = MessageReactionModel(
id=reaction_id,
@@ -228,17 +398,30 @@ class MessageTable:
def get_reactions_by_message_id(self, id: str) -> list[Reactions]:
with get_db() as db:
all_reactions = db.query(MessageReaction).filter_by(message_id=id).all()
# JOIN User so all user info is fetched in one query
results = (
db.query(MessageReaction, User)
.join(User, MessageReaction.user_id == User.id)
.filter(MessageReaction.message_id == id)
.all()
)
reactions = {}
for reaction in all_reactions:
for reaction, user in results:
if reaction.name not in reactions:
reactions[reaction.name] = {
"name": reaction.name,
"user_ids": [],
"users": [],
"count": 0,
}
reactions[reaction.name]["user_ids"].append(reaction.user_id)
reactions[reaction.name]["users"].append(
{
"id": user.id,
"name": user.name,
}
)
reactions[reaction.name]["count"] += 1
return [Reactions(**reaction) for reaction in reactions.values()]
+198 -11
View File
@@ -5,13 +5,16 @@ from typing import Optional
from open_webui.internal.db import Base, JSONField, get_db
from open_webui.env import SRC_LOG_LEVELS
from open_webui.models.users import Users, UserResponse
from open_webui.models.groups import Groups
from open_webui.models.users import User, UserModel, Users, UserResponse
from pydantic import BaseModel, ConfigDict
from sqlalchemy import or_, and_, func
from sqlalchemy import String, cast, or_, and_, func
from sqlalchemy.dialects import postgresql, sqlite
from sqlalchemy.dialects.postgresql import JSONB
from sqlalchemy import BigInteger, Column, Text, JSON, Boolean
@@ -52,7 +55,7 @@ class ModelMeta(BaseModel):
class Model(Base):
__tablename__ = "model"
id = Column(Text, primary_key=True)
id = Column(Text, primary_key=True, unique=True)
"""
The model's id as used in the API. If set to an existing model, it will override the model.
"""
@@ -132,6 +135,11 @@ class ModelResponse(ModelModel):
pass
class ModelListResponse(BaseModel):
items: list[ModelUserResponse]
total: int
class ModelForm(BaseModel):
id: str
base_model_id: Optional[str] = None
@@ -175,9 +183,16 @@ class ModelsTable:
def get_models(self) -> list[ModelUserResponse]:
with get_db() as db:
all_models = db.query(Model).filter(Model.base_model_id != None).all()
user_ids = list(set(model.user_id for model in all_models))
users = Users.get_users_by_user_ids(user_ids) if user_ids else []
users_dict = {user.id: user for user in users}
models = []
for model in db.query(Model).filter(Model.base_model_id != None).all():
user = Users.get_user_by_id(model.user_id)
for model in all_models:
user = users_dict.get(model.user_id)
models.append(
ModelUserResponse.model_validate(
{
@@ -199,13 +214,143 @@ class ModelsTable:
self, user_id: str, permission: str = "write"
) -> list[ModelUserResponse]:
models = self.get_models()
user_group_ids = {group.id for group in Groups.get_groups_by_member_id(user_id)}
return [
model
for model in models
if model.user_id == user_id
or has_access(user_id, permission, model.access_control)
or has_access(user_id, permission, model.access_control, user_group_ids)
]
def _has_permission(self, db, query, filter: dict, permission: str = "read"):
group_ids = filter.get("group_ids", [])
user_id = filter.get("user_id")
dialect_name = db.bind.dialect.name
# Public access
conditions = []
if group_ids or user_id:
conditions.extend(
[
Model.access_control.is_(None),
cast(Model.access_control, String) == "null",
]
)
# User-level permission
if user_id:
conditions.append(Model.user_id == user_id)
# Group-level permission
if group_ids:
group_conditions = []
for gid in group_ids:
if dialect_name == "sqlite":
group_conditions.append(
Model.access_control[permission]["group_ids"].contains([gid])
)
elif dialect_name == "postgresql":
group_conditions.append(
cast(
Model.access_control[permission]["group_ids"],
JSONB,
).contains([gid])
)
conditions.append(or_(*group_conditions))
if conditions:
query = query.filter(or_(*conditions))
return query
def search_models(
self, user_id: str, filter: dict = {}, skip: int = 0, limit: int = 30
) -> ModelListResponse:
with get_db() as db:
# Join GroupMember so we can order by group_id when requested
query = db.query(Model, User).outerjoin(User, User.id == Model.user_id)
query = query.filter(Model.base_model_id != None)
if filter:
query_key = filter.get("query")
if query_key:
query = query.filter(
or_(
Model.name.ilike(f"%{query_key}%"),
Model.base_model_id.ilike(f"%{query_key}%"),
)
)
view_option = filter.get("view_option")
if view_option == "created":
query = query.filter(Model.user_id == user_id)
elif view_option == "shared":
query = query.filter(Model.user_id != user_id)
# Apply access control filtering
query = self._has_permission(
db,
query,
filter,
permission="write",
)
tag = filter.get("tag")
if tag:
# TODO: This is a simple implementation and should be improved for performance
like_pattern = f'%"{tag.lower()}"%' # `"tag"` inside JSON array
meta_text = func.lower(cast(Model.meta, String))
query = query.filter(meta_text.like(like_pattern))
order_by = filter.get("order_by")
direction = filter.get("direction")
if order_by == "name":
if direction == "asc":
query = query.order_by(Model.name.asc())
else:
query = query.order_by(Model.name.desc())
elif order_by == "created_at":
if direction == "asc":
query = query.order_by(Model.created_at.asc())
else:
query = query.order_by(Model.created_at.desc())
elif order_by == "updated_at":
if direction == "asc":
query = query.order_by(Model.updated_at.asc())
else:
query = query.order_by(Model.updated_at.desc())
else:
query = query.order_by(Model.created_at.desc())
# Count BEFORE pagination
total = query.count()
if skip:
query = query.offset(skip)
if limit:
query = query.limit(limit)
items = query.all()
models = []
for model, user in items:
models.append(
ModelUserResponse(
**ModelModel.model_validate(model).model_dump(),
user=(
UserResponse(**UserModel.model_validate(user).model_dump())
if user
else None
),
)
)
return ModelListResponse(items=models, total=total)
def get_model_by_id(self, id: str) -> Optional[ModelModel]:
try:
with get_db() as db:
@@ -235,11 +380,9 @@ class ModelsTable:
try:
with get_db() as db:
# update only the fields that are present in the model
result = (
db.query(Model)
.filter_by(id=id)
.update(model.model_dump(exclude={"id"}))
)
data = model.model_dump(exclude={"id"})
result = db.query(Model).filter_by(id=id).update(data)
db.commit()
model = db.get(Model, id)
@@ -269,5 +412,49 @@ class ModelsTable:
except Exception:
return False
def sync_models(self, user_id: str, models: list[ModelModel]) -> list[ModelModel]:
try:
with get_db() as db:
# Get existing models
existing_models = db.query(Model).all()
existing_ids = {model.id for model in existing_models}
# Prepare a set of new model IDs
new_model_ids = {model.id for model in models}
# Update or insert models
for model in models:
if model.id in existing_ids:
db.query(Model).filter_by(id=model.id).update(
{
**model.model_dump(),
"user_id": user_id,
"updated_at": int(time.time()),
}
)
else:
new_model = Model(
**{
**model.model_dump(),
"user_id": user_id,
"updated_at": int(time.time()),
}
)
db.add(new_model)
# Remove models that are no longer present
for model in existing_models:
if model.id not in new_model_ids:
db.delete(model)
db.commit()
return [
ModelModel.model_validate(model) for model in db.query(Model).all()
]
except Exception as e:
log.exception(f"Error syncing models for user {user_id}: {e}")
return []
Models = ModelsTable()
+218
View File
@@ -0,0 +1,218 @@
import json
import time
import uuid
from typing import Optional
from functools import lru_cache
from open_webui.internal.db import Base, get_db
from open_webui.models.groups import Groups
from open_webui.utils.access_control import has_access
from open_webui.models.users import Users, UserResponse
from pydantic import BaseModel, ConfigDict
from sqlalchemy import BigInteger, Boolean, Column, String, Text, JSON
from sqlalchemy import or_, func, select, and_, text
from sqlalchemy.sql import exists
####################
# Note DB Schema
####################
class Note(Base):
__tablename__ = "note"
id = Column(Text, primary_key=True, unique=True)
user_id = Column(Text)
title = Column(Text)
data = Column(JSON, nullable=True)
meta = Column(JSON, nullable=True)
access_control = Column(JSON, nullable=True)
created_at = Column(BigInteger)
updated_at = Column(BigInteger)
class NoteModel(BaseModel):
model_config = ConfigDict(from_attributes=True)
id: str
user_id: str
title: str
data: Optional[dict] = None
meta: Optional[dict] = None
access_control: Optional[dict] = None
created_at: int # timestamp in epoch
updated_at: int # timestamp in epoch
####################
# Forms
####################
class NoteForm(BaseModel):
title: str
data: Optional[dict] = None
meta: Optional[dict] = None
access_control: Optional[dict] = None
class NoteUpdateForm(BaseModel):
title: Optional[str] = None
data: Optional[dict] = None
meta: Optional[dict] = None
access_control: Optional[dict] = None
class NoteUserResponse(NoteModel):
user: Optional[UserResponse] = None
class NoteTable:
def insert_new_note(
self,
form_data: NoteForm,
user_id: str,
) -> Optional[NoteModel]:
with get_db() as db:
note = NoteModel(
**{
"id": str(uuid.uuid4()),
"user_id": user_id,
**form_data.model_dump(),
"created_at": int(time.time_ns()),
"updated_at": int(time.time_ns()),
}
)
new_note = Note(**note.model_dump())
db.add(new_note)
db.commit()
return note
def get_notes(
self, skip: Optional[int] = None, limit: Optional[int] = None
) -> list[NoteModel]:
with get_db() as db:
query = db.query(Note).order_by(Note.updated_at.desc())
if skip is not None:
query = query.offset(skip)
if limit is not None:
query = query.limit(limit)
notes = query.all()
return [NoteModel.model_validate(note) for note in notes]
def get_notes_by_user_id(
self,
user_id: str,
skip: Optional[int] = None,
limit: Optional[int] = None,
) -> list[NoteModel]:
with get_db() as db:
query = db.query(Note).filter(Note.user_id == user_id)
query = query.order_by(Note.updated_at.desc())
if skip is not None:
query = query.offset(skip)
if limit is not None:
query = query.limit(limit)
notes = query.all()
return [NoteModel.model_validate(note) for note in notes]
def get_notes_by_permission(
self,
user_id: str,
permission: str = "write",
skip: Optional[int] = None,
limit: Optional[int] = None,
) -> list[NoteModel]:
with get_db() as db:
user_groups = Groups.get_groups_by_member_id(user_id)
user_group_ids = {group.id for group in user_groups}
# Order newest-first. We stream to keep memory usage low.
query = (
db.query(Note)
.order_by(Note.updated_at.desc())
.execution_options(stream_results=True)
.yield_per(256)
)
results: list[NoteModel] = []
n_skipped = 0
for note in query:
# Fast-pass #1: owner
if note.user_id == user_id:
permitted = True
# Fast-pass #2: public/open
elif note.access_control is None:
# Technically this should mean public access for both read and write, but we'll only do read for now
# We might want to change this behavior later
permitted = permission == "read"
else:
permitted = has_access(
user_id, permission, note.access_control, user_group_ids
)
if not permitted:
continue
# Apply skip AFTER permission filtering so it counts only accessible notes
if skip and n_skipped < skip:
n_skipped += 1
continue
results.append(NoteModel.model_validate(note))
if limit is not None and len(results) >= limit:
break
return results
def get_note_by_id(self, id: str) -> Optional[NoteModel]:
with get_db() as db:
note = db.query(Note).filter(Note.id == id).first()
return NoteModel.model_validate(note) if note else None
def update_note_by_id(
self, id: str, form_data: NoteUpdateForm
) -> Optional[NoteModel]:
with get_db() as db:
note = db.query(Note).filter(Note.id == id).first()
if not note:
return None
form_data = form_data.model_dump(exclude_unset=True)
if "title" in form_data:
note.title = form_data["title"]
if "data" in form_data:
note.data = {**note.data, **form_data["data"]}
if "meta" in form_data:
note.meta = {**note.meta, **form_data["meta"]}
if "access_control" in form_data:
note.access_control = form_data["access_control"]
note.updated_at = int(time.time_ns())
db.commit()
return NoteModel.model_validate(note) if note else None
def delete_note_by_id(self, id: str):
with get_db() as db:
db.query(Note).filter(Note.id == id).delete()
db.commit()
return True
Notes = NoteTable()
+277
View File
@@ -0,0 +1,277 @@
import time
import logging
import uuid
from typing import Optional, List
import base64
import hashlib
import json
from cryptography.fernet import Fernet
from open_webui.internal.db import Base, get_db
from open_webui.env import SRC_LOG_LEVELS, OAUTH_SESSION_TOKEN_ENCRYPTION_KEY
from pydantic import BaseModel, ConfigDict
from sqlalchemy import BigInteger, Column, String, Text, Index
log = logging.getLogger(__name__)
log.setLevel(SRC_LOG_LEVELS["MODELS"])
####################
# DB MODEL
####################
class OAuthSession(Base):
__tablename__ = "oauth_session"
id = Column(Text, primary_key=True, unique=True)
user_id = Column(Text, nullable=False)
provider = Column(Text, nullable=False)
token = Column(
Text, nullable=False
) # JSON with access_token, id_token, refresh_token
expires_at = Column(BigInteger, nullable=False)
created_at = Column(BigInteger, nullable=False)
updated_at = Column(BigInteger, nullable=False)
# Add indexes for better performance
__table_args__ = (
Index("idx_oauth_session_user_id", "user_id"),
Index("idx_oauth_session_expires_at", "expires_at"),
Index("idx_oauth_session_user_provider", "user_id", "provider"),
)
class OAuthSessionModel(BaseModel):
id: str
user_id: str
provider: str
token: dict
expires_at: int # timestamp in epoch
created_at: int # timestamp in epoch
updated_at: int # timestamp in epoch
model_config = ConfigDict(from_attributes=True)
####################
# Forms
####################
class OAuthSessionResponse(BaseModel):
id: str
user_id: str
provider: str
expires_at: int
class OAuthSessionTable:
def __init__(self):
self.encryption_key = OAUTH_SESSION_TOKEN_ENCRYPTION_KEY
if not self.encryption_key:
raise Exception("OAUTH_SESSION_TOKEN_ENCRYPTION_KEY is not set")
# check if encryption key is in the right format for Fernet (32 url-safe base64-encoded bytes)
if len(self.encryption_key) != 44:
key_bytes = hashlib.sha256(self.encryption_key.encode()).digest()
self.encryption_key = base64.urlsafe_b64encode(key_bytes)
else:
self.encryption_key = self.encryption_key.encode()
try:
self.fernet = Fernet(self.encryption_key)
except Exception as e:
log.error(f"Error initializing Fernet with provided key: {e}")
raise
def _encrypt_token(self, token) -> str:
"""Encrypt OAuth tokens for storage"""
try:
token_json = json.dumps(token)
encrypted = self.fernet.encrypt(token_json.encode()).decode()
return encrypted
except Exception as e:
log.error(f"Error encrypting tokens: {e}")
raise
def _decrypt_token(self, token: str):
"""Decrypt OAuth tokens from storage"""
try:
decrypted = self.fernet.decrypt(token.encode()).decode()
return json.loads(decrypted)
except Exception as e:
log.error(f"Error decrypting tokens: {e}")
raise
def create_session(
self,
user_id: str,
provider: str,
token: dict,
) -> Optional[OAuthSessionModel]:
"""Create a new OAuth session"""
try:
with get_db() as db:
current_time = int(time.time())
id = str(uuid.uuid4())
result = OAuthSession(
**{
"id": id,
"user_id": user_id,
"provider": provider,
"token": self._encrypt_token(token),
"expires_at": token.get("expires_at"),
"created_at": current_time,
"updated_at": current_time,
}
)
db.add(result)
db.commit()
db.refresh(result)
if result:
result.token = token # Return decrypted token
return OAuthSessionModel.model_validate(result)
else:
return None
except Exception as e:
log.error(f"Error creating OAuth session: {e}")
return None
def get_session_by_id(self, session_id: str) -> Optional[OAuthSessionModel]:
"""Get OAuth session by ID"""
try:
with get_db() as db:
session = db.query(OAuthSession).filter_by(id=session_id).first()
if session:
session.token = self._decrypt_token(session.token)
return OAuthSessionModel.model_validate(session)
return None
except Exception as e:
log.error(f"Error getting OAuth session by ID: {e}")
return None
def get_session_by_id_and_user_id(
self, session_id: str, user_id: str
) -> Optional[OAuthSessionModel]:
"""Get OAuth session by ID and user ID"""
try:
with get_db() as db:
session = (
db.query(OAuthSession)
.filter_by(id=session_id, user_id=user_id)
.first()
)
if session:
session.token = self._decrypt_token(session.token)
return OAuthSessionModel.model_validate(session)
return None
except Exception as e:
log.error(f"Error getting OAuth session by ID: {e}")
return None
def get_session_by_provider_and_user_id(
self, provider: str, user_id: str
) -> Optional[OAuthSessionModel]:
"""Get OAuth session by provider and user ID"""
try:
with get_db() as db:
session = (
db.query(OAuthSession)
.filter_by(provider=provider, user_id=user_id)
.first()
)
if session:
session.token = self._decrypt_token(session.token)
return OAuthSessionModel.model_validate(session)
return None
except Exception as e:
log.error(f"Error getting OAuth session by provider and user ID: {e}")
return None
def get_sessions_by_user_id(self, user_id: str) -> List[OAuthSessionModel]:
"""Get all OAuth sessions for a user"""
try:
with get_db() as db:
sessions = db.query(OAuthSession).filter_by(user_id=user_id).all()
results = []
for session in sessions:
session.token = self._decrypt_token(session.token)
results.append(OAuthSessionModel.model_validate(session))
return results
except Exception as e:
log.error(f"Error getting OAuth sessions by user ID: {e}")
return []
def update_session_by_id(
self, session_id: str, token: dict
) -> Optional[OAuthSessionModel]:
"""Update OAuth session tokens"""
try:
with get_db() as db:
current_time = int(time.time())
db.query(OAuthSession).filter_by(id=session_id).update(
{
"token": self._encrypt_token(token),
"expires_at": token.get("expires_at"),
"updated_at": current_time,
}
)
db.commit()
session = db.query(OAuthSession).filter_by(id=session_id).first()
if session:
session.token = self._decrypt_token(session.token)
return OAuthSessionModel.model_validate(session)
return None
except Exception as e:
log.error(f"Error updating OAuth session tokens: {e}")
return None
def delete_session_by_id(self, session_id: str) -> bool:
"""Delete an OAuth session"""
try:
with get_db() as db:
result = db.query(OAuthSession).filter_by(id=session_id).delete()
db.commit()
return result > 0
except Exception as e:
log.error(f"Error deleting OAuth session: {e}")
return False
def delete_sessions_by_user_id(self, user_id: str) -> bool:
"""Delete all OAuth sessions for a user"""
try:
with get_db() as db:
result = db.query(OAuthSession).filter_by(user_id=user_id).delete()
db.commit()
return True
except Exception as e:
log.error(f"Error deleting OAuth sessions by user ID: {e}")
return False
def delete_sessions_by_provider(self, provider: str) -> bool:
"""Delete all OAuth sessions for a provider"""
try:
with get_db() as db:
db.query(OAuthSession).filter_by(provider=provider).delete()
db.commit()
return True
except Exception as e:
log.error(f"Error deleting OAuth sessions by provider {provider}: {e}")
return False
OAuthSessions = OAuthSessionTable()
+12 -4
View File
@@ -2,6 +2,7 @@ import time
from typing import Optional
from open_webui.internal.db import Base, get_db
from open_webui.models.groups import Groups
from open_webui.models.users import Users, UserResponse
from pydantic import BaseModel, ConfigDict
@@ -103,10 +104,16 @@ class PromptsTable:
def get_prompts(self) -> list[PromptUserResponse]:
with get_db() as db:
prompts = []
all_prompts = db.query(Prompt).order_by(Prompt.timestamp.desc()).all()
for prompt in db.query(Prompt).order_by(Prompt.timestamp.desc()).all():
user = Users.get_user_by_id(prompt.user_id)
user_ids = list(set(prompt.user_id for prompt in all_prompts))
users = Users.get_users_by_user_ids(user_ids) if user_ids else []
users_dict = {user.id: user for user in users}
prompts = []
for prompt in all_prompts:
user = users_dict.get(prompt.user_id)
prompts.append(
PromptUserResponse.model_validate(
{
@@ -122,12 +129,13 @@ class PromptsTable:
self, user_id: str, permission: str = "write"
) -> list[PromptUserResponse]:
prompts = self.get_prompts()
user_group_ids = {group.id for group in Groups.get_groups_by_member_id(user_id)}
return [
prompt
for prompt in prompts
if prompt.user_id == user_id
or has_access(user_id, permission, prompt.access_control)
or has_access(user_id, permission, prompt.access_control, user_group_ids)
]
def update_prompt_by_command(
+6 -1
View File
@@ -8,7 +8,7 @@ from open_webui.internal.db import Base, get_db
from open_webui.env import SRC_LOG_LEVELS
from pydantic import BaseModel, ConfigDict
from sqlalchemy import BigInteger, Column, String, JSON, PrimaryKeyConstraint
from sqlalchemy import BigInteger, Column, String, JSON, PrimaryKeyConstraint, Index
log = logging.getLogger(__name__)
log.setLevel(SRC_LOG_LEVELS["MODELS"])
@@ -24,6 +24,11 @@ class Tag(Base):
user_id = Column(String)
meta = Column(JSON, nullable=True)
__table_args__ = (
PrimaryKeyConstraint("id", "user_id", name="pk_id_user_id"),
Index("user_id_idx", "user_id"),
)
# Unique constraint ensuring (id, user_id) is unique, not just the `id` column
__table_args__ = (PrimaryKeyConstraint("id", "user_id", name="pk_id_user_id"),)
+17 -5
View File
@@ -4,6 +4,8 @@ from typing import Optional
from open_webui.internal.db import Base, JSONField, get_db
from open_webui.models.users import Users, UserResponse
from open_webui.models.groups import Groups
from open_webui.env import SRC_LOG_LEVELS
from pydantic import BaseModel, ConfigDict
from sqlalchemy import BigInteger, Column, String, Text, JSON
@@ -22,7 +24,7 @@ log.setLevel(SRC_LOG_LEVELS["MODELS"])
class Tool(Base):
__tablename__ = "tool"
id = Column(String, primary_key=True)
id = Column(String, primary_key=True, unique=True)
user_id = Column(String)
name = Column(Text)
content = Column(Text)
@@ -93,6 +95,8 @@ class ToolResponse(BaseModel):
class ToolUserResponse(ToolResponse):
user: Optional[UserResponse] = None
model_config = ConfigDict(extra="allow")
class ToolForm(BaseModel):
id: str
@@ -144,9 +148,16 @@ class ToolsTable:
def get_tools(self) -> list[ToolUserModel]:
with get_db() as db:
all_tools = db.query(Tool).order_by(Tool.updated_at.desc()).all()
user_ids = list(set(tool.user_id for tool in all_tools))
users = Users.get_users_by_user_ids(user_ids) if user_ids else []
users_dict = {user.id: user for user in users}
tools = []
for tool in db.query(Tool).order_by(Tool.updated_at.desc()).all():
user = Users.get_user_by_id(tool.user_id)
for tool in all_tools:
user = users_dict.get(tool.user_id)
tools.append(
ToolUserModel.model_validate(
{
@@ -161,12 +172,13 @@ class ToolsTable:
self, user_id: str, permission: str = "write"
) -> list[ToolUserModel]:
tools = self.get_tools()
user_group_ids = {group.id for group in Groups.get_groups_by_member_id(user_id)}
return [
tool
for tool in tools
if tool.user_id == user_id
or has_access(user_id, permission, tool.access_control)
or has_access(user_id, permission, tool.access_control, user_group_ids)
]
def get_tool_valves_by_id(self, id: str) -> Optional[dict]:
@@ -175,7 +187,7 @@ class ToolsTable:
tool = db.get(Tool, id)
return tool.valves if tool.valves else {}
except Exception as e:
log.exception(f"Error getting tool valves by id {id}: {e}")
log.exception(f"Error getting tool valves by id {id}")
return None
def update_tool_valves_by_id(self, id: str, valves: dict) -> Optional[ToolValves]:
+446 -62
View File
@@ -4,60 +4,139 @@ from typing import Optional
from open_webui.internal.db import Base, JSONField, get_db
from open_webui.env import DATABASE_USER_ACTIVE_STATUS_UPDATE_INTERVAL
from open_webui.models.chats import Chats
from open_webui.models.groups import Groups
from open_webui.models.groups import Groups, GroupMember
from open_webui.models.channels import ChannelMember
from open_webui.utils.misc import throttle
from pydantic import BaseModel, ConfigDict
from sqlalchemy import BigInteger, Column, String, Text
from sqlalchemy import (
BigInteger,
JSON,
Column,
String,
Boolean,
Text,
Date,
exists,
select,
cast,
)
from sqlalchemy import or_, case
from sqlalchemy.dialects.postgresql import JSONB
import datetime
####################
# User DB Schema
####################
class User(Base):
__tablename__ = "user"
id = Column(String, primary_key=True)
name = Column(String)
email = Column(String)
role = Column(String)
profile_image_url = Column(Text)
last_active_at = Column(BigInteger)
updated_at = Column(BigInteger)
created_at = Column(BigInteger)
api_key = Column(String, nullable=True, unique=True)
settings = Column(JSONField, nullable=True)
info = Column(JSONField, nullable=True)
oauth_sub = Column(Text, unique=True)
class UserSettings(BaseModel):
ui: Optional[dict] = {}
model_config = ConfigDict(extra="allow")
pass
class User(Base):
__tablename__ = "user"
id = Column(String, primary_key=True, unique=True)
email = Column(String)
username = Column(String(50), nullable=True)
role = Column(String)
name = Column(String)
profile_image_url = Column(Text)
profile_banner_image_url = Column(Text, nullable=True)
bio = Column(Text, nullable=True)
gender = Column(Text, nullable=True)
date_of_birth = Column(Date, nullable=True)
timezone = Column(String, nullable=True)
presence_state = Column(String, nullable=True)
status_emoji = Column(String, nullable=True)
status_message = Column(Text, nullable=True)
status_expires_at = Column(BigInteger, nullable=True)
info = Column(JSON, nullable=True)
settings = Column(JSON, nullable=True)
oauth = Column(JSON, nullable=True)
last_active_at = Column(BigInteger)
updated_at = Column(BigInteger)
created_at = Column(BigInteger)
class UserModel(BaseModel):
id: str
name: str
email: str
username: Optional[str] = None
role: str = "pending"
name: str
profile_image_url: str
profile_banner_image_url: Optional[str] = None
bio: Optional[str] = None
gender: Optional[str] = None
date_of_birth: Optional[datetime.date] = None
timezone: Optional[str] = None
presence_state: Optional[str] = None
status_emoji: Optional[str] = None
status_message: Optional[str] = None
status_expires_at: Optional[int] = None
info: Optional[dict] = None
settings: Optional[UserSettings] = None
oauth: Optional[dict] = None
last_active_at: int # timestamp in epoch
updated_at: int # timestamp in epoch
created_at: int # timestamp in epoch
api_key: Optional[str] = None
settings: Optional[UserSettings] = None
info: Optional[dict] = None
model_config = ConfigDict(from_attributes=True)
oauth_sub: Optional[str] = None
class UserStatusModel(UserModel):
is_active: bool = False
model_config = ConfigDict(from_attributes=True)
class ApiKey(Base):
__tablename__ = "api_key"
id = Column(Text, primary_key=True, unique=True)
user_id = Column(Text, nullable=False)
key = Column(Text, unique=True, nullable=False)
data = Column(JSON, nullable=True)
expires_at = Column(BigInteger, nullable=True)
last_used_at = Column(BigInteger, nullable=True)
created_at = Column(BigInteger, nullable=False)
updated_at = Column(BigInteger, nullable=False)
class ApiKeyModel(BaseModel):
id: str
user_id: str
key: str
data: Optional[dict] = None
expires_at: Optional[int] = None
last_used_at: Optional[int] = None
created_at: int # timestamp in epoch
updated_at: int # timestamp in epoch
model_config = ConfigDict(from_attributes=True)
@@ -67,18 +146,78 @@ class UserModel(BaseModel):
####################
class UserResponse(BaseModel):
class UpdateProfileForm(BaseModel):
profile_image_url: str
name: str
bio: Optional[str] = None
gender: Optional[str] = None
date_of_birth: Optional[datetime.date] = None
class UserGroupIdsModel(UserModel):
group_ids: list[str] = []
class UserModelResponse(UserModel):
model_config = ConfigDict(extra="allow")
class UserListResponse(BaseModel):
users: list[UserModelResponse]
total: int
class UserGroupIdsListResponse(BaseModel):
users: list[UserGroupIdsModel]
total: int
class UserStatus(BaseModel):
status_emoji: Optional[str] = None
status_message: Optional[str] = None
status_expires_at: Optional[int] = None
class UserInfoResponse(UserStatus):
id: str
name: str
email: str
role: str
profile_image_url: str
class UserIdNameResponse(BaseModel):
id: str
name: str
class UserIdNameStatusResponse(UserStatus):
id: str
name: str
is_active: Optional[bool] = None
class UserInfoListResponse(BaseModel):
users: list[UserInfoResponse]
total: int
class UserIdNameListResponse(BaseModel):
users: list[UserIdNameResponse]
total: int
class UserNameResponse(BaseModel):
id: str
name: str
role: str
class UserResponse(UserNameResponse):
email: str
class UserProfileImageResponse(UserNameResponse):
email: str
profile_image_url: str
@@ -88,6 +227,7 @@ class UserRoleUpdateForm(BaseModel):
class UserUpdateForm(BaseModel):
role: str
name: str
email: str
profile_image_url: str
@@ -102,20 +242,20 @@ class UsersTable:
email: str,
profile_image_url: str = "/user.png",
role: str = "pending",
oauth_sub: Optional[str] = None,
oauth: Optional[dict] = None,
) -> Optional[UserModel]:
with get_db() as db:
user = UserModel(
**{
"id": id,
"name": name,
"email": email,
"name": name,
"role": role,
"profile_image_url": profile_image_url,
"last_active_at": int(time.time()),
"created_at": int(time.time()),
"updated_at": int(time.time()),
"oauth_sub": oauth_sub,
"oauth": oauth,
}
)
result = User(**user.model_dump())
@@ -138,8 +278,13 @@ class UsersTable:
def get_user_by_api_key(self, api_key: str) -> Optional[UserModel]:
try:
with get_db() as db:
user = db.query(User).filter_by(api_key=api_key).first()
return UserModel.model_validate(user)
user = (
db.query(User)
.join(ApiKey, User.id == ApiKey.user_id)
.filter(ApiKey.key == api_key)
.first()
)
return UserModel.model_validate(user) if user else None
except Exception:
return None
@@ -151,31 +296,173 @@ class UsersTable:
except Exception:
return None
def get_user_by_oauth_sub(self, sub: str) -> Optional[UserModel]:
def get_user_by_oauth_sub(self, provider: str, sub: str) -> Optional[UserModel]:
try:
with get_db() as db:
user = db.query(User).filter_by(oauth_sub=sub).first()
return UserModel.model_validate(user)
except Exception:
with get_db() as db: # type: Session
dialect_name = db.bind.dialect.name
query = db.query(User)
if dialect_name == "sqlite":
query = query.filter(User.oauth.contains({provider: {"sub": sub}}))
elif dialect_name == "postgresql":
query = query.filter(
User.oauth[provider].cast(JSONB)["sub"].astext == sub
)
user = query.first()
return UserModel.model_validate(user) if user else None
except Exception as e:
# You may want to log the exception here
return None
def get_users(
self, skip: Optional[int] = None, limit: Optional[int] = None
) -> list[UserModel]:
self,
filter: Optional[dict] = None,
skip: Optional[int] = None,
limit: Optional[int] = None,
) -> dict:
with get_db() as db:
# Join GroupMember so we can order by group_id when requested
query = db.query(User)
query = db.query(User).order_by(User.created_at.desc())
if filter:
query_key = filter.get("query")
if query_key:
query = query.filter(
or_(
User.name.ilike(f"%{query_key}%"),
User.email.ilike(f"%{query_key}%"),
)
)
if skip:
channel_id = filter.get("channel_id")
if channel_id:
query = query.filter(
exists(
select(ChannelMember.id).where(
ChannelMember.user_id == User.id,
ChannelMember.channel_id == channel_id,
)
)
)
user_ids = filter.get("user_ids")
group_ids = filter.get("group_ids")
if isinstance(user_ids, list) and isinstance(group_ids, list):
# If both are empty lists, return no users
if not user_ids and not group_ids:
return {"users": [], "total": 0}
if user_ids:
query = query.filter(User.id.in_(user_ids))
if group_ids:
query = query.filter(
exists(
select(GroupMember.id).where(
GroupMember.user_id == User.id,
GroupMember.group_id.in_(group_ids),
)
)
)
roles = filter.get("roles")
if roles:
include_roles = [role for role in roles if not role.startswith("!")]
exclude_roles = [role[1:] for role in roles if role.startswith("!")]
if include_roles:
query = query.filter(User.role.in_(include_roles))
if exclude_roles:
query = query.filter(~User.role.in_(exclude_roles))
order_by = filter.get("order_by")
direction = filter.get("direction")
if order_by and order_by.startswith("group_id:"):
group_id = order_by.split(":", 1)[1]
# Subquery that checks if the user belongs to the group
membership_exists = exists(
select(GroupMember.id).where(
GroupMember.user_id == User.id,
GroupMember.group_id == group_id,
)
)
# CASE: user in group → 1, user not in group → 0
group_sort = case((membership_exists, 1), else_=0)
if direction == "asc":
query = query.order_by(group_sort.asc(), User.name.asc())
else:
query = query.order_by(group_sort.desc(), User.name.asc())
elif order_by == "name":
if direction == "asc":
query = query.order_by(User.name.asc())
else:
query = query.order_by(User.name.desc())
elif order_by == "email":
if direction == "asc":
query = query.order_by(User.email.asc())
else:
query = query.order_by(User.email.desc())
elif order_by == "created_at":
if direction == "asc":
query = query.order_by(User.created_at.asc())
else:
query = query.order_by(User.created_at.desc())
elif order_by == "last_active_at":
if direction == "asc":
query = query.order_by(User.last_active_at.asc())
else:
query = query.order_by(User.last_active_at.desc())
elif order_by == "updated_at":
if direction == "asc":
query = query.order_by(User.updated_at.asc())
else:
query = query.order_by(User.updated_at.desc())
elif order_by == "role":
if direction == "asc":
query = query.order_by(User.role.asc())
else:
query = query.order_by(User.role.desc())
else:
query = query.order_by(User.created_at.desc())
# Count BEFORE pagination
total = query.count()
# correct pagination logic
if skip is not None:
query = query.offset(skip)
if limit:
if limit is not None:
query = query.limit(limit)
users = query.all()
return {
"users": [UserModel.model_validate(user) for user in users],
"total": total,
}
def get_users_by_group_id(self, group_id: str) -> list[UserModel]:
with get_db() as db:
users = (
db.query(User)
.join(GroupMember, User.id == GroupMember.user_id)
.filter(GroupMember.group_id == group_id)
.all()
)
return [UserModel.model_validate(user) for user in users]
def get_users_by_user_ids(self, user_ids: list[str]) -> list[UserModel]:
def get_users_by_user_ids(self, user_ids: list[str]) -> list[UserStatusModel]:
with get_db() as db:
users = db.query(User).filter(User.id.in_(user_ids)).all()
return [UserModel.model_validate(user) for user in users]
@@ -184,6 +471,10 @@ class UsersTable:
with get_db() as db:
return db.query(User).count()
def has_users(self) -> bool:
with get_db() as db:
return db.query(db.query(User).exists()).scalar()
def get_first_user(self) -> UserModel:
try:
with get_db() as db:
@@ -208,6 +499,15 @@ class UsersTable:
except Exception:
return None
def get_num_users_active_today(self) -> Optional[int]:
with get_db() as db:
current_timestamp = int(datetime.datetime.now().timestamp())
today_midnight_timestamp = current_timestamp - (current_timestamp % 86400)
query = db.query(User).filter(
User.last_active_at > today_midnight_timestamp
)
return query.count()
def update_user_role_by_id(self, id: str, role: str) -> Optional[UserModel]:
try:
with get_db() as db:
@@ -218,6 +518,21 @@ class UsersTable:
except Exception:
return None
def update_user_status_by_id(
self, id: str, form_data: UserStatus
) -> Optional[UserModel]:
try:
with get_db() as db:
db.query(User).filter_by(id=id).update(
{**form_data.model_dump(exclude_none=True)}
)
db.commit()
user = db.query(User).filter_by(id=id).first()
return UserModel.model_validate(user)
except Exception:
return None
def update_user_profile_image_url_by_id(
self, id: str, profile_image_url: str
) -> Optional[UserModel]:
@@ -233,7 +548,8 @@ class UsersTable:
except Exception:
return None
def update_user_last_active_by_id(self, id: str) -> Optional[UserModel]:
@throttle(DATABASE_USER_ACTIVE_STATUS_UPDATE_INTERVAL)
def update_last_active_by_id(self, id: str) -> Optional[UserModel]:
try:
with get_db() as db:
db.query(User).filter_by(id=id).update(
@@ -246,16 +562,35 @@ class UsersTable:
except Exception:
return None
def update_user_oauth_sub_by_id(
self, id: str, oauth_sub: str
def update_user_oauth_by_id(
self, id: str, provider: str, sub: str
) -> Optional[UserModel]:
"""
Update or insert an OAuth provider/sub pair into the user's oauth JSON field.
Example resulting structure:
{
"google": { "sub": "123" },
"github": { "sub": "abc" }
}
"""
try:
with get_db() as db:
db.query(User).filter_by(id=id).update({"oauth_sub": oauth_sub})
user = db.query(User).filter_by(id=id).first()
if not user:
return None
# Load existing oauth JSON or create empty
oauth = user.oauth or {}
# Update or insert provider entry
oauth[provider] = {"sub": sub}
# Persist updated JSON
db.query(User).filter_by(id=id).update({"oauth": oauth})
db.commit()
user = db.query(User).filter_by(id=id).first()
return UserModel.model_validate(user)
except Exception:
return None
@@ -268,7 +603,8 @@ class UsersTable:
user = db.query(User).filter_by(id=id).first()
return UserModel.model_validate(user)
# return UserModel(**user.dict())
except Exception:
except Exception as e:
print(e)
return None
def update_user_settings_by_id(self, id: str, updated: dict) -> Optional[UserModel]:
@@ -308,27 +644,75 @@ class UsersTable:
except Exception:
return False
def update_user_api_key_by_id(self, id: str, api_key: str) -> str:
try:
with get_db() as db:
result = db.query(User).filter_by(id=id).update({"api_key": api_key})
db.commit()
return True if result == 1 else False
except Exception:
return False
def get_user_api_key_by_id(self, id: str) -> Optional[str]:
try:
with get_db() as db:
user = db.query(User).filter_by(id=id).first()
return user.api_key
api_key = db.query(ApiKey).filter_by(user_id=id).first()
return api_key.key if api_key else None
except Exception:
return None
def update_user_api_key_by_id(self, id: str, api_key: str) -> bool:
try:
with get_db() as db:
db.query(ApiKey).filter_by(user_id=id).delete()
db.commit()
now = int(time.time())
new_api_key = ApiKey(
id=f"key_{id}",
user_id=id,
key=api_key,
created_at=now,
updated_at=now,
)
db.add(new_api_key)
db.commit()
return True
except Exception:
return False
def delete_user_api_key_by_id(self, id: str) -> bool:
try:
with get_db() as db:
db.query(ApiKey).filter_by(user_id=id).delete()
db.commit()
return True
except Exception:
return False
def get_valid_user_ids(self, user_ids: list[str]) -> list[str]:
with get_db() as db:
users = db.query(User).filter(User.id.in_(user_ids)).all()
return [user.id for user in users]
def get_super_admin_user(self) -> Optional[UserModel]:
with get_db() as db:
user = db.query(User).filter_by(role="admin").first()
if user:
return UserModel.model_validate(user)
else:
return None
def get_active_user_count(self) -> int:
with get_db() as db:
# Consider user active if last_active_at within the last 3 minutes
three_minutes_ago = int(time.time()) - 180
count = (
db.query(User).filter(User.last_active_at >= three_minutes_ago).count()
)
return count
def is_user_active(self, user_id: str) -> bool:
with get_db() as db:
user = db.query(User).filter_by(id=user_id).first()
if user and user.last_active_at:
# Consider user active if last_active_at within the last 3 minutes
three_minutes_ago = int(time.time()) - 180
return user.last_active_at >= three_minutes_ago
return False
Users = UsersTable()
@@ -0,0 +1,278 @@
import os
import time
import requests
import logging
import json
from typing import List, Optional
from langchain_core.documents import Document
from fastapi import HTTPException, status
log = logging.getLogger(__name__)
class DatalabMarkerLoader:
def __init__(
self,
file_path: str,
api_key: str,
api_base_url: str,
additional_config: Optional[str] = None,
use_llm: bool = False,
skip_cache: bool = False,
force_ocr: bool = False,
paginate: bool = False,
strip_existing_ocr: bool = False,
disable_image_extraction: bool = False,
format_lines: bool = False,
output_format: str = None,
):
self.file_path = file_path
self.api_key = api_key
self.api_base_url = api_base_url
self.additional_config = additional_config
self.use_llm = use_llm
self.skip_cache = skip_cache
self.force_ocr = force_ocr
self.paginate = paginate
self.strip_existing_ocr = strip_existing_ocr
self.disable_image_extraction = disable_image_extraction
self.format_lines = format_lines
self.output_format = output_format
def _get_mime_type(self, filename: str) -> str:
ext = filename.rsplit(".", 1)[-1].lower()
mime_map = {
"pdf": "application/pdf",
"xls": "application/vnd.ms-excel",
"xlsx": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
"ods": "application/vnd.oasis.opendocument.spreadsheet",
"doc": "application/msword",
"docx": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
"odt": "application/vnd.oasis.opendocument.text",
"ppt": "application/vnd.ms-powerpoint",
"pptx": "application/vnd.openxmlformats-officedocument.presentationml.presentation",
"odp": "application/vnd.oasis.opendocument.presentation",
"html": "text/html",
"epub": "application/epub+zip",
"png": "image/png",
"jpeg": "image/jpeg",
"jpg": "image/jpeg",
"webp": "image/webp",
"gif": "image/gif",
"tiff": "image/tiff",
}
return mime_map.get(ext, "application/octet-stream")
def check_marker_request_status(self, request_id: str) -> dict:
url = f"{self.api_base_url}/{request_id}"
headers = {"X-Api-Key": self.api_key}
try:
response = requests.get(url, headers=headers)
response.raise_for_status()
result = response.json()
log.info(f"Marker API status check for request {request_id}: {result}")
return result
except requests.HTTPError as e:
log.error(f"Error checking Marker request status: {e}")
raise HTTPException(
status.HTTP_502_BAD_GATEWAY,
detail=f"Failed to check Marker request: {e}",
)
except ValueError as e:
log.error(f"Invalid JSON checking Marker request: {e}")
raise HTTPException(
status.HTTP_502_BAD_GATEWAY, detail=f"Invalid JSON: {e}"
)
def load(self) -> List[Document]:
filename = os.path.basename(self.file_path)
mime_type = self._get_mime_type(filename)
headers = {"X-Api-Key": self.api_key}
form_data = {
"use_llm": str(self.use_llm).lower(),
"skip_cache": str(self.skip_cache).lower(),
"force_ocr": str(self.force_ocr).lower(),
"paginate": str(self.paginate).lower(),
"strip_existing_ocr": str(self.strip_existing_ocr).lower(),
"disable_image_extraction": str(self.disable_image_extraction).lower(),
"format_lines": str(self.format_lines).lower(),
"output_format": self.output_format,
}
if self.additional_config and self.additional_config.strip():
form_data["additional_config"] = self.additional_config
log.info(
f"Datalab Marker POST request parameters: {{'filename': '{filename}', 'mime_type': '{mime_type}', **{form_data}}}"
)
try:
with open(self.file_path, "rb") as f:
files = {"file": (filename, f, mime_type)}
response = requests.post(
f"{self.api_base_url}",
data=form_data,
files=files,
headers=headers,
)
response.raise_for_status()
result = response.json()
except FileNotFoundError:
raise HTTPException(
status.HTTP_404_NOT_FOUND, detail=f"File not found: {self.file_path}"
)
except requests.HTTPError as e:
raise HTTPException(
status.HTTP_400_BAD_REQUEST,
detail=f"Datalab Marker request failed: {e}",
)
except ValueError as e:
raise HTTPException(
status.HTTP_502_BAD_GATEWAY, detail=f"Invalid JSON response: {e}"
)
except Exception as e:
raise HTTPException(status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e))
if not result.get("success"):
raise HTTPException(
status.HTTP_400_BAD_REQUEST,
detail=f"Datalab Marker request failed: {result.get('error', 'Unknown error')}",
)
check_url = result.get("request_check_url")
request_id = result.get("request_id")
# Check if this is a direct response (self-hosted) or polling response (DataLab)
if check_url:
# DataLab polling pattern
for _ in range(300): # Up to 10 minutes
time.sleep(2)
try:
poll_response = requests.get(check_url, headers=headers)
poll_response.raise_for_status()
poll_result = poll_response.json()
except (requests.HTTPError, ValueError) as e:
raw_body = poll_response.text
log.error(f"Polling error: {e}, response body: {raw_body}")
raise HTTPException(
status.HTTP_502_BAD_GATEWAY, detail=f"Polling failed: {e}"
)
status_val = poll_result.get("status")
success_val = poll_result.get("success")
if status_val == "complete":
summary = {
k: poll_result.get(k)
for k in (
"status",
"output_format",
"success",
"error",
"page_count",
"total_cost",
)
}
log.info(
f"Marker processing completed successfully: {json.dumps(summary, indent=2)}"
)
break
if status_val == "failed" or success_val is False:
log.error(
f"Marker poll failed full response: {json.dumps(poll_result, indent=2)}"
)
error_msg = (
poll_result.get("error")
or "Marker returned failure without error message"
)
raise HTTPException(
status.HTTP_400_BAD_REQUEST,
detail=f"Marker processing failed: {error_msg}",
)
else:
raise HTTPException(
status.HTTP_504_GATEWAY_TIMEOUT,
detail="Marker processing timed out",
)
if not poll_result.get("success", False):
error_msg = poll_result.get("error") or "Unknown processing error"
raise HTTPException(
status.HTTP_400_BAD_REQUEST,
detail=f"Final processing failed: {error_msg}",
)
# DataLab format - content in format-specific fields
content_key = self.output_format.lower()
raw_content = poll_result.get(content_key)
final_result = poll_result
else:
# Self-hosted direct response - content in "output" field
if "output" in result:
log.info("Self-hosted Marker returned direct response without polling")
raw_content = result.get("output")
final_result = result
else:
available_fields = (
list(result.keys())
if isinstance(result, dict)
else "non-dict response"
)
raise HTTPException(
status.HTTP_502_BAD_GATEWAY,
detail=f"Custom Marker endpoint returned success but no 'output' field found. Available fields: {available_fields}. Expected either 'request_check_url' for polling or 'output' field for direct response.",
)
if self.output_format.lower() == "json":
full_text = json.dumps(raw_content, indent=2)
elif self.output_format.lower() in {"markdown", "html"}:
full_text = str(raw_content).strip()
else:
raise HTTPException(
status.HTTP_400_BAD_REQUEST,
detail=f"Unsupported output format: {self.output_format}",
)
if not full_text:
raise HTTPException(
status.HTTP_400_BAD_REQUEST,
detail="Marker returned empty content",
)
marker_output_dir = os.path.join("/app/backend/data/uploads", "marker_output")
os.makedirs(marker_output_dir, exist_ok=True)
file_ext_map = {"markdown": "md", "json": "json", "html": "html"}
file_ext = file_ext_map.get(self.output_format.lower(), "txt")
output_filename = f"{os.path.splitext(filename)[0]}.{file_ext}"
output_path = os.path.join(marker_output_dir, output_filename)
try:
with open(output_path, "w", encoding="utf-8") as f:
f.write(full_text)
log.info(f"Saved Marker output to: {output_path}")
except Exception as e:
log.warning(f"Failed to write marker output to disk: {e}")
metadata = {
"source": filename,
"output_format": final_result.get("output_format", self.output_format),
"page_count": final_result.get("page_count", 0),
"processed_with_llm": self.use_llm,
"request_id": request_id or "",
}
images = final_result.get("images", {})
if images:
metadata["image_count"] = len(images)
metadata["images"] = json.dumps(list(images.keys()))
for k, v in metadata.items():
if isinstance(v, (dict, list)):
metadata[k] = json.dumps(v)
elif v is None:
metadata[k] = ""
return [Document(page_content=full_text, metadata=metadata)]
@@ -0,0 +1,91 @@
import requests
import logging, os
from typing import Iterator, List, Union
from urllib.parse import quote
from langchain_core.document_loaders import BaseLoader
from langchain_core.documents import Document
from open_webui.utils.headers import include_user_info_headers
from open_webui.env import SRC_LOG_LEVELS
log = logging.getLogger(__name__)
log.setLevel(SRC_LOG_LEVELS["RAG"])
class ExternalDocumentLoader(BaseLoader):
def __init__(
self,
file_path,
url: str,
api_key: str,
mime_type=None,
user=None,
**kwargs,
) -> None:
self.url = url
self.api_key = api_key
self.file_path = file_path
self.mime_type = mime_type
self.user = user
def load(self) -> List[Document]:
with open(self.file_path, "rb") as f:
data = f.read()
headers = {}
if self.mime_type is not None:
headers["Content-Type"] = self.mime_type
if self.api_key is not None:
headers["Authorization"] = f"Bearer {self.api_key}"
try:
headers["X-Filename"] = quote(os.path.basename(self.file_path))
except:
pass
if self.user is not None:
headers = include_user_info_headers(headers, self.user)
url = self.url
if url.endswith("/"):
url = url[:-1]
try:
response = requests.put(f"{url}/process", data=data, headers=headers)
except Exception as e:
log.error(f"Error connecting to endpoint: {e}")
raise Exception(f"Error connecting to endpoint: {e}")
if response.ok:
response_data = response.json()
if response_data:
if isinstance(response_data, dict):
return [
Document(
page_content=response_data.get("page_content"),
metadata=response_data.get("metadata"),
)
]
elif isinstance(response_data, list):
documents = []
for document in response_data:
documents.append(
Document(
page_content=document.get("page_content"),
metadata=document.get("metadata"),
)
)
return documents
else:
raise Exception("Error loading document: Unable to parse content")
else:
raise Exception("Error loading document: No content returned")
else:
raise Exception(
f"Error loading document: {response.status_code} {response.text}"
)
@@ -0,0 +1,53 @@
import requests
import logging
from typing import Iterator, List, Union
from langchain_core.document_loaders import BaseLoader
from langchain_core.documents import Document
from open_webui.env import SRC_LOG_LEVELS
log = logging.getLogger(__name__)
log.setLevel(SRC_LOG_LEVELS["RAG"])
class ExternalWebLoader(BaseLoader):
def __init__(
self,
web_paths: Union[str, List[str]],
external_url: str,
external_api_key: str,
continue_on_failure: bool = True,
**kwargs,
) -> None:
self.external_url = external_url
self.external_api_key = external_api_key
self.urls = web_paths if isinstance(web_paths, list) else [web_paths]
self.continue_on_failure = continue_on_failure
def lazy_load(self) -> Iterator[Document]:
batch_size = 20
for i in range(0, len(self.urls), batch_size):
urls = self.urls[i : i + batch_size]
try:
response = requests.post(
self.external_url,
headers={
"User-Agent": "Open WebUI (https://github.com/open-webui/open-webui) External Web Loader",
"Authorization": f"Bearer {self.external_api_key}",
},
json={
"urls": urls,
},
)
response.raise_for_status()
results = response.json()
for result in results:
yield Document(
page_content=result.get("page_content", ""),
metadata=result.get("metadata", {}),
)
except Exception as e:
if self.continue_on_failure:
log.error(f"Error extracting content from batch {urls}: {e}")
else:
raise e
+138 -24
View File
@@ -2,7 +2,9 @@ import requests
import logging
import ftfy
import sys
import json
from azure.identity import DefaultAzureCredential
from langchain_community.document_loaders import (
AzureAIDocumentIntelligenceLoader,
BSHTMLLoader,
@@ -13,7 +15,7 @@ from langchain_community.document_loaders import (
TextLoader,
UnstructuredEPubLoader,
UnstructuredExcelLoader,
UnstructuredMarkdownLoader,
UnstructuredODTLoader,
UnstructuredPowerPointLoader,
UnstructuredRSTLoader,
UnstructuredXMLLoader,
@@ -21,7 +23,12 @@ from langchain_community.document_loaders import (
)
from langchain_core.documents import Document
from open_webui.retrieval.loaders.external_document import ExternalDocumentLoader
from open_webui.retrieval.loaders.mistral import MistralLoader
from open_webui.retrieval.loaders.datalab_marker import DatalabMarkerLoader
from open_webui.retrieval.loaders.mineru import MinerULoader
from open_webui.env import SRC_LOG_LEVELS, GLOBAL_LOG_LEVEL
@@ -72,7 +79,6 @@ known_source_ext = [
"swift",
"vue",
"svelte",
"msg",
"ex",
"exs",
"erl",
@@ -85,11 +91,13 @@ known_source_ext = [
class TikaLoader:
def __init__(self, url, file_path, mime_type=None):
def __init__(self, url, file_path, mime_type=None, extract_images=None):
self.url = url
self.file_path = file_path
self.mime_type = mime_type
self.extract_images = extract_images
def load(self) -> list[Document]:
with open(self.file_path, "rb") as f:
data = f.read()
@@ -99,6 +107,9 @@ class TikaLoader:
else:
headers = {}
if self.extract_images == True:
headers["X-Tika-PDFextractInlineImages"] = "true"
endpoint = self.url
if not endpoint.endswith("/"):
endpoint += "/"
@@ -121,13 +132,20 @@ class TikaLoader:
class DoclingLoader:
def __init__(self, url, file_path=None, mime_type=None):
def __init__(self, url, api_key=None, file_path=None, mime_type=None, params=None):
self.url = url.rstrip("/")
self.api_key = api_key
self.file_path = file_path
self.mime_type = mime_type
self.params = params or {}
def load(self) -> list[Document]:
with open(self.file_path, "rb") as f:
headers = {}
if self.api_key:
headers["Authorization"] = f"Bearer {self.api_key}"
files = {
"files": (
self.file_path,
@@ -136,14 +154,15 @@ class DoclingLoader:
)
}
params = {
"image_export_mode": "placeholder",
"table_mode": "accurate",
}
endpoint = f"{self.url}/v1alpha/convert/file"
r = requests.post(endpoint, files=files, data=params)
r = requests.post(
f"{self.url}/v1/convert/file",
files=files,
data={
"image_export_mode": "placeholder",
**self.params,
},
headers=headers,
)
if r.ok:
result = r.json()
document_data = result.get("document", {})
@@ -152,7 +171,6 @@ class DoclingLoader:
metadata = {"Content-Type": self.mime_type} if self.mime_type else {}
log.debug("Docling extracted text: %s", text)
return [Document(page_content=text, metadata=metadata)]
else:
error_msg = f"Error calling Docling API: {r.reason}"
@@ -169,6 +187,7 @@ class DoclingLoader:
class Loader:
def __init__(self, engine: str = "", **kwargs):
self.engine = engine
self.user = kwargs.get("user", None)
self.kwargs = kwargs
def load(
@@ -186,50 +205,141 @@ class Loader:
def _is_text_file(self, file_ext: str, file_content_type: str) -> bool:
return file_ext in known_source_ext or (
file_content_type and file_content_type.find("text/") >= 0
file_content_type
and file_content_type.find("text/") >= 0
# Avoid text/html files being detected as text
and not file_content_type.find("html") >= 0
)
def _get_loader(self, filename: str, file_content_type: str, file_path: str):
file_ext = filename.split(".")[-1].lower()
if self.engine == "tika" and self.kwargs.get("TIKA_SERVER_URL"):
if (
self.engine == "external"
and self.kwargs.get("EXTERNAL_DOCUMENT_LOADER_URL")
and self.kwargs.get("EXTERNAL_DOCUMENT_LOADER_API_KEY")
):
loader = ExternalDocumentLoader(
file_path=file_path,
url=self.kwargs.get("EXTERNAL_DOCUMENT_LOADER_URL"),
api_key=self.kwargs.get("EXTERNAL_DOCUMENT_LOADER_API_KEY"),
mime_type=file_content_type,
user=self.user,
)
elif self.engine == "tika" and self.kwargs.get("TIKA_SERVER_URL"):
if self._is_text_file(file_ext, file_content_type):
loader = TextLoader(file_path, autodetect_encoding=True)
else:
loader = TikaLoader(
url=self.kwargs.get("TIKA_SERVER_URL"),
file_path=file_path,
mime_type=file_content_type,
extract_images=self.kwargs.get("PDF_EXTRACT_IMAGES"),
)
elif (
self.engine == "datalab_marker"
and self.kwargs.get("DATALAB_MARKER_API_KEY")
and file_ext
in [
"pdf",
"xls",
"xlsx",
"ods",
"doc",
"docx",
"odt",
"ppt",
"pptx",
"odp",
"html",
"epub",
"png",
"jpeg",
"jpg",
"webp",
"gif",
"tiff",
]
):
api_base_url = self.kwargs.get("DATALAB_MARKER_API_BASE_URL", "")
if not api_base_url or api_base_url.strip() == "":
api_base_url = "https://www.datalab.to/api/v1/marker" # https://github.com/open-webui/open-webui/pull/16867#issuecomment-3218424349
loader = DatalabMarkerLoader(
file_path=file_path,
api_key=self.kwargs["DATALAB_MARKER_API_KEY"],
api_base_url=api_base_url,
additional_config=self.kwargs.get("DATALAB_MARKER_ADDITIONAL_CONFIG"),
use_llm=self.kwargs.get("DATALAB_MARKER_USE_LLM", False),
skip_cache=self.kwargs.get("DATALAB_MARKER_SKIP_CACHE", False),
force_ocr=self.kwargs.get("DATALAB_MARKER_FORCE_OCR", False),
paginate=self.kwargs.get("DATALAB_MARKER_PAGINATE", False),
strip_existing_ocr=self.kwargs.get(
"DATALAB_MARKER_STRIP_EXISTING_OCR", False
),
disable_image_extraction=self.kwargs.get(
"DATALAB_MARKER_DISABLE_IMAGE_EXTRACTION", False
),
format_lines=self.kwargs.get("DATALAB_MARKER_FORMAT_LINES", False),
output_format=self.kwargs.get(
"DATALAB_MARKER_OUTPUT_FORMAT", "markdown"
),
)
elif self.engine == "docling" and self.kwargs.get("DOCLING_SERVER_URL"):
if self._is_text_file(file_ext, file_content_type):
loader = TextLoader(file_path, autodetect_encoding=True)
else:
# Build params for DoclingLoader
params = self.kwargs.get("DOCLING_PARAMS", {})
if not isinstance(params, dict):
try:
params = json.loads(params)
except json.JSONDecodeError:
log.error("Invalid DOCLING_PARAMS format, expected JSON object")
params = {}
loader = DoclingLoader(
url=self.kwargs.get("DOCLING_SERVER_URL"),
api_key=self.kwargs.get("DOCLING_API_KEY", None),
file_path=file_path,
mime_type=file_content_type,
params=params,
)
elif (
self.engine == "document_intelligence"
and self.kwargs.get("DOCUMENT_INTELLIGENCE_ENDPOINT") != ""
and self.kwargs.get("DOCUMENT_INTELLIGENCE_KEY") != ""
and (
file_ext in ["pdf", "xls", "xlsx", "docx", "ppt", "pptx"]
file_ext in ["pdf", "docx", "ppt", "pptx"]
or file_content_type
in [
"application/vnd.ms-excel",
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
"application/vnd.ms-powerpoint",
"application/vnd.openxmlformats-officedocument.presentationml.presentation",
]
)
):
loader = AzureAIDocumentIntelligenceLoader(
if self.kwargs.get("DOCUMENT_INTELLIGENCE_KEY") != "":
loader = AzureAIDocumentIntelligenceLoader(
file_path=file_path,
api_endpoint=self.kwargs.get("DOCUMENT_INTELLIGENCE_ENDPOINT"),
api_key=self.kwargs.get("DOCUMENT_INTELLIGENCE_KEY"),
api_model=self.kwargs.get("DOCUMENT_INTELLIGENCE_MODEL"),
)
else:
loader = AzureAIDocumentIntelligenceLoader(
file_path=file_path,
api_endpoint=self.kwargs.get("DOCUMENT_INTELLIGENCE_ENDPOINT"),
azure_credential=DefaultAzureCredential(),
api_model=self.kwargs.get("DOCUMENT_INTELLIGENCE_MODEL"),
)
elif self.engine == "mineru" and file_ext in [
"pdf"
]: # MinerU currently only supports PDF
loader = MinerULoader(
file_path=file_path,
api_endpoint=self.kwargs.get("DOCUMENT_INTELLIGENCE_ENDPOINT"),
api_key=self.kwargs.get("DOCUMENT_INTELLIGENCE_KEY"),
api_mode=self.kwargs.get("MINERU_API_MODE", "local"),
api_url=self.kwargs.get("MINERU_API_URL", "http://localhost:8000"),
api_key=self.kwargs.get("MINERU_API_KEY", ""),
params=self.kwargs.get("MINERU_PARAMS", {}),
)
elif (
self.engine == "mistral_ocr"
@@ -238,7 +348,9 @@ class Loader:
in ["pdf"] # Mistral OCR currently only supports PDF and images
):
loader = MistralLoader(
api_key=self.kwargs.get("MISTRAL_OCR_API_KEY"), file_path=file_path
base_url=self.kwargs.get("MISTRAL_OCR_API_BASE_URL"),
api_key=self.kwargs.get("MISTRAL_OCR_API_KEY"),
file_path=file_path,
)
else:
if file_ext == "pdf":
@@ -275,6 +387,8 @@ class Loader:
loader = UnstructuredPowerPointLoader(file_path)
elif file_ext == "msg":
loader = OutlookMessageLoader(file_path)
elif file_ext == "odt":
loader = UnstructuredODTLoader(file_path)
elif self._is_text_file(file_ext, file_content_type):
loader = TextLoader(file_path, autodetect_encoding=True)
else:
@@ -0,0 +1,522 @@
import os
import time
import requests
import logging
import tempfile
import zipfile
from typing import List, Optional
from langchain_core.documents import Document
from fastapi import HTTPException, status
log = logging.getLogger(__name__)
class MinerULoader:
"""
MinerU document parser loader supporting both Cloud API and Local API modes.
Cloud API: Uses MinerU managed service with async task-based processing
Local API: Uses self-hosted MinerU API with synchronous processing
"""
def __init__(
self,
file_path: str,
api_mode: str = "local",
api_url: str = "http://localhost:8000",
api_key: str = "",
params: dict = None,
):
self.file_path = file_path
self.api_mode = api_mode.lower()
self.api_url = api_url.rstrip("/")
self.api_key = api_key
# Parse params dict with defaults
self.params = params or {}
self.enable_ocr = params.get("enable_ocr", False)
self.enable_formula = params.get("enable_formula", True)
self.enable_table = params.get("enable_table", True)
self.language = params.get("language", "en")
self.model_version = params.get("model_version", "pipeline")
self.page_ranges = self.params.pop("page_ranges", "")
# Validate API mode
if self.api_mode not in ["local", "cloud"]:
raise ValueError(
f"Invalid API mode: {self.api_mode}. Must be 'local' or 'cloud'"
)
# Validate Cloud API requirements
if self.api_mode == "cloud" and not self.api_key:
raise ValueError("API key is required for Cloud API mode")
def load(self) -> List[Document]:
"""
Main entry point for loading and parsing the document.
Routes to Cloud or Local API based on api_mode.
"""
try:
if self.api_mode == "cloud":
return self._load_cloud_api()
else:
return self._load_local_api()
except Exception as e:
log.error(f"Error loading document with MinerU: {e}")
raise
def _load_local_api(self) -> List[Document]:
"""
Load document using Local API (synchronous).
Posts file to /file_parse endpoint and gets immediate response.
"""
log.info(f"Using MinerU Local API at {self.api_url}")
filename = os.path.basename(self.file_path)
# Build form data for Local API
form_data = {
**self.params,
"return_md": "true",
}
# Page ranges (Local API uses start_page_id and end_page_id)
if self.page_ranges:
# For simplicity, if page_ranges is specified, log a warning
# Full page range parsing would require parsing the string
log.warning(
f"Page ranges '{self.page_ranges}' specified but Local API uses different format. "
"Consider using start_page_id/end_page_id parameters if needed."
)
try:
with open(self.file_path, "rb") as f:
files = {"files": (filename, f, "application/octet-stream")}
log.info(f"Sending file to MinerU Local API: {filename}")
log.debug(f"Local API parameters: {form_data}")
response = requests.post(
f"{self.api_url}/file_parse",
data=form_data,
files=files,
timeout=300, # 5 minute timeout for large documents
)
response.raise_for_status()
except FileNotFoundError:
raise HTTPException(
status.HTTP_404_NOT_FOUND, detail=f"File not found: {self.file_path}"
)
except requests.Timeout:
raise HTTPException(
status.HTTP_504_GATEWAY_TIMEOUT,
detail="MinerU Local API request timed out",
)
except requests.HTTPError as e:
error_detail = f"MinerU Local API request failed: {e}"
if e.response is not None:
try:
error_data = e.response.json()
error_detail += f" - {error_data}"
except:
error_detail += f" - {e.response.text}"
raise HTTPException(status.HTTP_400_BAD_REQUEST, detail=error_detail)
except Exception as e:
raise HTTPException(
status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"Error calling MinerU Local API: {str(e)}",
)
# Parse response
try:
result = response.json()
except ValueError as e:
raise HTTPException(
status.HTTP_502_BAD_GATEWAY,
detail=f"Invalid JSON response from MinerU Local API: {e}",
)
# Extract markdown content from response
if "results" not in result:
raise HTTPException(
status.HTTP_502_BAD_GATEWAY,
detail="MinerU Local API response missing 'results' field",
)
results = result["results"]
if not results:
raise HTTPException(
status.HTTP_400_BAD_REQUEST,
detail="MinerU returned empty results",
)
# Get the first (and typically only) result
file_result = list(results.values())[0]
markdown_content = file_result.get("md_content", "")
if not markdown_content:
raise HTTPException(
status.HTTP_400_BAD_REQUEST,
detail="MinerU returned empty markdown content",
)
log.info(f"Successfully parsed document with MinerU Local API: {filename}")
# Create metadata
metadata = {
"source": filename,
"api_mode": "local",
"backend": result.get("backend", "unknown"),
"version": result.get("version", "unknown"),
}
return [Document(page_content=markdown_content, metadata=metadata)]
def _load_cloud_api(self) -> List[Document]:
"""
Load document using Cloud API (asynchronous).
Uses batch upload endpoint to avoid need for public file URLs.
"""
log.info(f"Using MinerU Cloud API at {self.api_url}")
filename = os.path.basename(self.file_path)
# Step 1: Request presigned upload URL
batch_id, upload_url = self._request_upload_url(filename)
# Step 2: Upload file to presigned URL
self._upload_to_presigned_url(upload_url)
# Step 3: Poll for results
result = self._poll_batch_status(batch_id, filename)
# Step 4: Download and extract markdown from ZIP
markdown_content = self._download_and_extract_zip(
result["full_zip_url"], filename
)
log.info(f"Successfully parsed document with MinerU Cloud API: {filename}")
# Create metadata
metadata = {
"source": filename,
"api_mode": "cloud",
"batch_id": batch_id,
}
return [Document(page_content=markdown_content, metadata=metadata)]
def _request_upload_url(self, filename: str) -> tuple:
"""
Request presigned upload URL from Cloud API.
Returns (batch_id, upload_url).
"""
headers = {
"Authorization": f"Bearer {self.api_key}",
"Content-Type": "application/json",
}
# Build request body
request_body = {
**self.params,
"files": [
{
"name": filename,
"is_ocr": self.enable_ocr,
}
],
}
# Add page ranges if specified
if self.page_ranges:
request_body["files"][0]["page_ranges"] = self.page_ranges
log.info(f"Requesting upload URL for: {filename}")
log.debug(f"Cloud API request body: {request_body}")
try:
response = requests.post(
f"{self.api_url}/file-urls/batch",
headers=headers,
json=request_body,
timeout=30,
)
response.raise_for_status()
except requests.HTTPError as e:
error_detail = f"Failed to request upload URL: {e}"
if e.response is not None:
try:
error_data = e.response.json()
error_detail += f" - {error_data.get('msg', error_data)}"
except:
error_detail += f" - {e.response.text}"
raise HTTPException(status.HTTP_400_BAD_REQUEST, detail=error_detail)
except Exception as e:
raise HTTPException(
status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"Error requesting upload URL: {str(e)}",
)
try:
result = response.json()
except ValueError as e:
raise HTTPException(
status.HTTP_502_BAD_GATEWAY,
detail=f"Invalid JSON response: {e}",
)
# Check for API error response
if result.get("code") != 0:
raise HTTPException(
status.HTTP_400_BAD_REQUEST,
detail=f"MinerU Cloud API error: {result.get('msg', 'Unknown error')}",
)
data = result.get("data", {})
batch_id = data.get("batch_id")
file_urls = data.get("file_urls", [])
if not batch_id or not file_urls:
raise HTTPException(
status.HTTP_502_BAD_GATEWAY,
detail="MinerU Cloud API response missing batch_id or file_urls",
)
upload_url = file_urls[0]
log.info(f"Received upload URL for batch: {batch_id}")
return batch_id, upload_url
def _upload_to_presigned_url(self, upload_url: str) -> None:
"""
Upload file to presigned URL (no authentication needed).
"""
log.info(f"Uploading file to presigned URL")
try:
with open(self.file_path, "rb") as f:
response = requests.put(
upload_url,
data=f,
timeout=300, # 5 minute timeout for large files
)
response.raise_for_status()
except FileNotFoundError:
raise HTTPException(
status.HTTP_404_NOT_FOUND, detail=f"File not found: {self.file_path}"
)
except requests.Timeout:
raise HTTPException(
status.HTTP_504_GATEWAY_TIMEOUT,
detail="File upload to presigned URL timed out",
)
except requests.HTTPError as e:
raise HTTPException(
status.HTTP_400_BAD_REQUEST,
detail=f"Failed to upload file to presigned URL: {e}",
)
except Exception as e:
raise HTTPException(
status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"Error uploading file: {str(e)}",
)
log.info("File uploaded successfully")
def _poll_batch_status(self, batch_id: str, filename: str) -> dict:
"""
Poll batch status until completion.
Returns the result dict for the file.
"""
headers = {
"Authorization": f"Bearer {self.api_key}",
}
max_iterations = 300 # 10 minutes max (2 seconds per iteration)
poll_interval = 2 # seconds
log.info(f"Polling batch status: {batch_id}")
for iteration in range(max_iterations):
try:
response = requests.get(
f"{self.api_url}/extract-results/batch/{batch_id}",
headers=headers,
timeout=30,
)
response.raise_for_status()
except requests.HTTPError as e:
error_detail = f"Failed to poll batch status: {e}"
if e.response is not None:
try:
error_data = e.response.json()
error_detail += f" - {error_data.get('msg', error_data)}"
except:
error_detail += f" - {e.response.text}"
raise HTTPException(status.HTTP_400_BAD_REQUEST, detail=error_detail)
except Exception as e:
raise HTTPException(
status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"Error polling batch status: {str(e)}",
)
try:
result = response.json()
except ValueError as e:
raise HTTPException(
status.HTTP_502_BAD_GATEWAY,
detail=f"Invalid JSON response while polling: {e}",
)
# Check for API error response
if result.get("code") != 0:
raise HTTPException(
status.HTTP_400_BAD_REQUEST,
detail=f"MinerU Cloud API error: {result.get('msg', 'Unknown error')}",
)
data = result.get("data", {})
extract_result = data.get("extract_result", [])
# Find our file in the batch results
file_result = None
for item in extract_result:
if item.get("file_name") == filename:
file_result = item
break
if not file_result:
raise HTTPException(
status.HTTP_502_BAD_GATEWAY,
detail=f"File {filename} not found in batch results",
)
state = file_result.get("state")
if state == "done":
log.info(f"Processing complete for {filename}")
return file_result
elif state == "failed":
error_msg = file_result.get("err_msg", "Unknown error")
raise HTTPException(
status.HTTP_400_BAD_REQUEST,
detail=f"MinerU processing failed: {error_msg}",
)
elif state in ["waiting-file", "pending", "running", "converting"]:
# Still processing
if iteration % 10 == 0: # Log every 20 seconds
log.info(
f"Processing status: {state} (iteration {iteration + 1}/{max_iterations})"
)
time.sleep(poll_interval)
else:
log.warning(f"Unknown state: {state}")
time.sleep(poll_interval)
# Timeout
raise HTTPException(
status.HTTP_504_GATEWAY_TIMEOUT,
detail="MinerU processing timed out after 10 minutes",
)
def _download_and_extract_zip(self, zip_url: str, filename: str) -> str:
"""
Download ZIP file from CDN and extract markdown content.
Returns the markdown content as a string.
"""
log.info(f"Downloading results from: {zip_url}")
try:
response = requests.get(zip_url, timeout=60)
response.raise_for_status()
except requests.HTTPError as e:
raise HTTPException(
status.HTTP_400_BAD_REQUEST,
detail=f"Failed to download results ZIP: {e}",
)
except Exception as e:
raise HTTPException(
status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"Error downloading results: {str(e)}",
)
# Save ZIP to temporary file and extract
try:
with tempfile.NamedTemporaryFile(delete=False, suffix=".zip") as tmp_zip:
tmp_zip.write(response.content)
tmp_zip_path = tmp_zip.name
with tempfile.TemporaryDirectory() as tmp_dir:
# Extract ZIP
with zipfile.ZipFile(tmp_zip_path, "r") as zip_ref:
zip_ref.extractall(tmp_dir)
# Find markdown file - search recursively for any .md file
markdown_content = None
found_md_path = None
# First, list all files in the ZIP for debugging
all_files = []
for root, dirs, files in os.walk(tmp_dir):
for file in files:
full_path = os.path.join(root, file)
all_files.append(full_path)
# Look for any .md file
if file.endswith(".md"):
found_md_path = full_path
log.info(f"Found markdown file at: {full_path}")
try:
with open(full_path, "r", encoding="utf-8") as f:
markdown_content = f.read()
if (
markdown_content
): # Use the first non-empty markdown file
break
except Exception as e:
log.warning(f"Failed to read {full_path}: {e}")
if markdown_content:
break
if markdown_content is None:
log.error(f"Available files in ZIP: {all_files}")
# Try to provide more helpful error message
md_files = [f for f in all_files if f.endswith(".md")]
if md_files:
error_msg = (
f"Found .md files but couldn't read them: {md_files}"
)
else:
error_msg = (
f"No .md files found in ZIP. Available files: {all_files}"
)
raise HTTPException(
status.HTTP_502_BAD_GATEWAY,
detail=error_msg,
)
# Clean up temporary ZIP file
os.unlink(tmp_zip_path)
except zipfile.BadZipFile as e:
raise HTTPException(
status.HTTP_502_BAD_GATEWAY,
detail=f"Invalid ZIP file received: {e}",
)
except Exception as e:
raise HTTPException(
status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"Error extracting ZIP: {str(e)}",
)
if not markdown_content:
raise HTTPException(
status.HTTP_400_BAD_REQUEST,
detail="Extracted markdown content is empty",
)
log.info(
f"Successfully extracted markdown content ({len(markdown_content)} characters)"
)
return markdown_content
+626 -81
View File
@@ -1,8 +1,12 @@
import requests
import aiohttp
import asyncio
import logging
import os
import sys
import time
from typing import List, Dict, Any
from contextlib import asynccontextmanager
from langchain_core.documents import Document
from open_webui.env import SRC_LOG_LEVELS, GLOBAL_LOG_LEVEL
@@ -14,27 +18,87 @@ log.setLevel(SRC_LOG_LEVELS["RAG"])
class MistralLoader:
"""
Enhanced Mistral OCR loader with both sync and async support.
Loads documents by processing them through the Mistral OCR API.
Performance Optimizations:
- Differentiated timeouts for different operations
- Intelligent retry logic with exponential backoff
- Memory-efficient file streaming for large files
- Connection pooling and keepalive optimization
- Semaphore-based concurrency control for batch processing
- Enhanced error handling with retryable error classification
"""
BASE_API_URL = "https://api.mistral.ai/v1"
def __init__(self, api_key: str, file_path: str):
def __init__(
self,
base_url: str,
api_key: str,
file_path: str,
timeout: int = 300, # 5 minutes default
max_retries: int = 3,
enable_debug_logging: bool = False,
):
"""
Initializes the loader.
Initializes the loader with enhanced features.
Args:
api_key: Your Mistral API key.
file_path: The local path to the PDF file to process.
timeout: Request timeout in seconds.
max_retries: Maximum number of retry attempts.
enable_debug_logging: Enable detailed debug logs.
"""
if not api_key:
raise ValueError("API key cannot be empty.")
if not os.path.exists(file_path):
raise FileNotFoundError(f"File not found at {file_path}")
self.base_url = (
base_url.rstrip("/") if base_url else "https://api.mistral.ai/v1"
)
self.api_key = api_key
self.file_path = file_path
self.headers = {"Authorization": f"Bearer {self.api_key}"}
self.timeout = timeout
self.max_retries = max_retries
self.debug = enable_debug_logging
# PERFORMANCE OPTIMIZATION: Differentiated timeouts for different operations
# This prevents long-running OCR operations from affecting quick operations
# and improves user experience by failing fast on operations that should be quick
self.upload_timeout = min(
timeout, 120
) # Cap upload at 2 minutes - prevents hanging on large files
self.url_timeout = (
30 # URL requests should be fast - fail quickly if API is slow
)
self.ocr_timeout = (
timeout # OCR can take the full timeout - this is the heavy operation
)
self.cleanup_timeout = (
30 # Cleanup should be quick - don't hang on file deletion
)
# PERFORMANCE OPTIMIZATION: Pre-compute file info to avoid repeated filesystem calls
# This avoids multiple os.path.basename() and os.path.getsize() calls during processing
self.file_name = os.path.basename(file_path)
self.file_size = os.path.getsize(file_path)
# ENHANCEMENT: Added User-Agent for better API tracking and debugging
self.headers = {
"Authorization": f"Bearer {self.api_key}",
"User-Agent": "OpenWebUI-MistralLoader/2.0", # Helps API provider track usage
}
def _debug_log(self, message: str, *args) -> None:
"""
PERFORMANCE OPTIMIZATION: Conditional debug logging for performance.
Only processes debug messages when debug mode is enabled, avoiding
string formatting overhead in production environments.
"""
if self.debug:
log.debug(message, *args)
def _handle_response(self, response: requests.Response) -> Dict[str, Any]:
"""Checks response status and returns JSON content."""
@@ -54,24 +118,154 @@ class MistralLoader:
log.error(f"JSON decode error: {json_err} - Response: {response.text}")
raise # Re-raise after logging
def _upload_file(self) -> str:
"""Uploads the file to Mistral for OCR processing."""
log.info("Uploading file to Mistral API")
url = f"{self.BASE_API_URL}/files"
file_name = os.path.basename(self.file_path)
async def _handle_response_async(
self, response: aiohttp.ClientResponse
) -> Dict[str, Any]:
"""Async version of response handling with better error info."""
try:
with open(self.file_path, "rb") as f:
files = {"file": (file_name, f, "application/pdf")}
data = {"purpose": "ocr"}
response.raise_for_status()
upload_headers = self.headers.copy() # Avoid modifying self.headers
response = requests.post(
url, headers=upload_headers, files=files, data=data
# Check content type
content_type = response.headers.get("content-type", "")
if "application/json" not in content_type:
if response.status == 204:
return {}
text = await response.text()
raise ValueError(
f"Unexpected content type: {content_type}, body: {text[:200]}..."
)
response_data = self._handle_response(response)
return await response.json()
except aiohttp.ClientResponseError as e:
error_text = await response.text() if response else "No response"
log.error(f"HTTP {e.status}: {e.message} - Response: {error_text[:500]}")
raise
except aiohttp.ClientError as e:
log.error(f"Client error: {e}")
raise
except Exception as e:
log.error(f"Unexpected error processing response: {e}")
raise
def _is_retryable_error(self, error: Exception) -> bool:
"""
ENHANCEMENT: Intelligent error classification for retry logic.
Determines if an error is retryable based on its type and status code.
This prevents wasting time retrying errors that will never succeed
(like authentication errors) while ensuring transient errors are retried.
Retryable errors:
- Network connection errors (temporary network issues)
- Timeouts (server might be temporarily overloaded)
- Server errors (5xx status codes - server-side issues)
- Rate limiting (429 status - temporary throttling)
Non-retryable errors:
- Authentication errors (401, 403 - won't fix with retry)
- Bad request errors (400 - malformed request)
- Not found errors (404 - resource doesn't exist)
"""
if isinstance(error, requests.exceptions.ConnectionError):
return True # Network issues are usually temporary
if isinstance(error, requests.exceptions.Timeout):
return True # Timeouts might resolve on retry
if isinstance(error, requests.exceptions.HTTPError):
# Only retry on server errors (5xx) or rate limits (429)
if hasattr(error, "response") and error.response is not None:
status_code = error.response.status_code
return status_code >= 500 or status_code == 429
return False
if isinstance(
error, (aiohttp.ClientConnectionError, aiohttp.ServerTimeoutError)
):
return True # Async network/timeout errors are retryable
if isinstance(error, aiohttp.ClientResponseError):
return error.status >= 500 or error.status == 429
return False # All other errors are non-retryable
def _retry_request_sync(self, request_func, *args, **kwargs):
"""
ENHANCEMENT: Synchronous retry logic with intelligent error classification.
Uses exponential backoff with jitter to avoid thundering herd problems.
The wait time increases exponentially but is capped at 30 seconds to
prevent excessive delays. Only retries errors that are likely to succeed
on subsequent attempts.
"""
for attempt in range(self.max_retries):
try:
return request_func(*args, **kwargs)
except Exception as e:
if attempt == self.max_retries - 1 or not self._is_retryable_error(e):
raise
# PERFORMANCE OPTIMIZATION: Exponential backoff with cap
# Prevents overwhelming the server while ensuring reasonable retry delays
wait_time = min((2**attempt) + 0.5, 30) # Cap at 30 seconds
log.warning(
f"Retryable error (attempt {attempt + 1}/{self.max_retries}): {e}. "
f"Retrying in {wait_time}s..."
)
time.sleep(wait_time)
async def _retry_request_async(self, request_func, *args, **kwargs):
"""
ENHANCEMENT: Async retry logic with intelligent error classification.
Async version of retry logic that doesn't block the event loop during
wait periods. Uses the same exponential backoff strategy as sync version.
"""
for attempt in range(self.max_retries):
try:
return await request_func(*args, **kwargs)
except Exception as e:
if attempt == self.max_retries - 1 or not self._is_retryable_error(e):
raise
# PERFORMANCE OPTIMIZATION: Non-blocking exponential backoff
wait_time = min((2**attempt) + 0.5, 30) # Cap at 30 seconds
log.warning(
f"Retryable error (attempt {attempt + 1}/{self.max_retries}): {e}. "
f"Retrying in {wait_time}s..."
)
await asyncio.sleep(wait_time) # Non-blocking wait
def _upload_file(self) -> str:
"""
PERFORMANCE OPTIMIZATION: Enhanced file upload with streaming consideration.
Uploads the file to Mistral for OCR processing (sync version).
Uses context manager for file handling to ensure proper resource cleanup.
Although streaming is not enabled for this endpoint, the file is opened
in a context manager to minimize memory usage duration.
"""
log.info("Uploading file to Mistral API")
url = f"{self.base_url}/files"
def upload_request():
# MEMORY OPTIMIZATION: Use context manager to minimize file handle lifetime
# This ensures the file is closed immediately after reading, reducing memory usage
with open(self.file_path, "rb") as f:
files = {"file": (self.file_name, f, "application/pdf")}
data = {"purpose": "ocr"}
# NOTE: stream=False is required for this endpoint
# The Mistral API doesn't support chunked uploads for this endpoint
response = requests.post(
url,
headers=self.headers,
files=files,
data=data,
timeout=self.upload_timeout, # Use specialized upload timeout
stream=False, # Keep as False for this endpoint
)
return self._handle_response(response)
try:
response_data = self._retry_request_sync(upload_request)
file_id = response_data.get("id")
if not file_id:
raise ValueError("File ID not found in upload response.")
@@ -81,16 +275,66 @@ class MistralLoader:
log.error(f"Failed to upload file: {e}")
raise
async def _upload_file_async(self, session: aiohttp.ClientSession) -> str:
"""Async file upload with streaming for better memory efficiency."""
url = f"{self.base_url}/files"
async def upload_request():
# Create multipart writer for streaming upload
writer = aiohttp.MultipartWriter("form-data")
# Add purpose field
purpose_part = writer.append("ocr")
purpose_part.set_content_disposition("form-data", name="purpose")
# Add file part with streaming
file_part = writer.append_payload(
aiohttp.streams.FilePayload(
self.file_path,
filename=self.file_name,
content_type="application/pdf",
)
)
file_part.set_content_disposition(
"form-data", name="file", filename=self.file_name
)
self._debug_log(
f"Uploading file: {self.file_name} ({self.file_size:,} bytes)"
)
async with session.post(
url,
data=writer,
headers=self.headers,
timeout=aiohttp.ClientTimeout(total=self.upload_timeout),
) as response:
return await self._handle_response_async(response)
response_data = await self._retry_request_async(upload_request)
file_id = response_data.get("id")
if not file_id:
raise ValueError("File ID not found in upload response.")
log.info(f"File uploaded successfully. File ID: {file_id}")
return file_id
def _get_signed_url(self, file_id: str) -> str:
"""Retrieves a temporary signed URL for the uploaded file."""
"""Retrieves a temporary signed URL for the uploaded file (sync version)."""
log.info(f"Getting signed URL for file ID: {file_id}")
url = f"{self.BASE_API_URL}/files/{file_id}/url"
url = f"{self.base_url}/files/{file_id}/url"
params = {"expiry": 1}
signed_url_headers = {**self.headers, "Accept": "application/json"}
def url_request():
response = requests.get(
url, headers=signed_url_headers, params=params, timeout=self.url_timeout
)
return self._handle_response(response)
try:
response = requests.get(url, headers=signed_url_headers, params=params)
response_data = self._handle_response(response)
response_data = self._retry_request_sync(url_request)
signed_url = response_data.get("url")
if not signed_url:
raise ValueError("Signed URL not found in response.")
@@ -100,10 +344,38 @@ class MistralLoader:
log.error(f"Failed to get signed URL: {e}")
raise
async def _get_signed_url_async(
self, session: aiohttp.ClientSession, file_id: str
) -> str:
"""Async signed URL retrieval."""
url = f"{self.base_url}/files/{file_id}/url"
params = {"expiry": 1}
headers = {**self.headers, "Accept": "application/json"}
async def url_request():
self._debug_log(f"Getting signed URL for file ID: {file_id}")
async with session.get(
url,
headers=headers,
params=params,
timeout=aiohttp.ClientTimeout(total=self.url_timeout),
) as response:
return await self._handle_response_async(response)
response_data = await self._retry_request_async(url_request)
signed_url = response_data.get("url")
if not signed_url:
raise ValueError("Signed URL not found in response.")
self._debug_log("Signed URL received successfully")
return signed_url
def _process_ocr(self, signed_url: str) -> Dict[str, Any]:
"""Sends the signed URL to the OCR endpoint for processing."""
"""Sends the signed URL to the OCR endpoint for processing (sync version)."""
log.info("Processing OCR via Mistral API")
url = f"{self.BASE_API_URL}/ocr"
url = f"{self.base_url}/ocr"
ocr_headers = {
**self.headers,
"Content-Type": "application/json",
@@ -118,43 +390,218 @@ class MistralLoader:
"include_image_base64": False,
}
def ocr_request():
response = requests.post(
url, headers=ocr_headers, json=payload, timeout=self.ocr_timeout
)
return self._handle_response(response)
try:
response = requests.post(url, headers=ocr_headers, json=payload)
ocr_response = self._handle_response(response)
ocr_response = self._retry_request_sync(ocr_request)
log.info("OCR processing done.")
log.debug("OCR response: %s", ocr_response)
self._debug_log("OCR response: %s", ocr_response)
return ocr_response
except Exception as e:
log.error(f"Failed during OCR processing: {e}")
raise
async def _process_ocr_async(
self, session: aiohttp.ClientSession, signed_url: str
) -> Dict[str, Any]:
"""Async OCR processing with timing metrics."""
url = f"{self.base_url}/ocr"
headers = {
**self.headers,
"Content-Type": "application/json",
"Accept": "application/json",
}
payload = {
"model": "mistral-ocr-latest",
"document": {
"type": "document_url",
"document_url": signed_url,
},
"include_image_base64": False,
}
async def ocr_request():
log.info("Starting OCR processing via Mistral API")
start_time = time.time()
async with session.post(
url,
json=payload,
headers=headers,
timeout=aiohttp.ClientTimeout(total=self.ocr_timeout),
) as response:
ocr_response = await self._handle_response_async(response)
processing_time = time.time() - start_time
log.info(f"OCR processing completed in {processing_time:.2f}s")
return ocr_response
return await self._retry_request_async(ocr_request)
def _delete_file(self, file_id: str) -> None:
"""Deletes the file from Mistral storage."""
"""Deletes the file from Mistral storage (sync version)."""
log.info(f"Deleting uploaded file ID: {file_id}")
url = f"{self.BASE_API_URL}/files/{file_id}"
# No specific Accept header needed, default or Authorization is usually sufficient
url = f"{self.base_url}/files/{file_id}"
try:
response = requests.delete(url, headers=self.headers)
delete_response = self._handle_response(
response
) # Check status, ignore response body unless needed
log.info(
f"File deleted successfully: {delete_response}"
) # Log the response if available
response = requests.delete(
url, headers=self.headers, timeout=self.cleanup_timeout
)
delete_response = self._handle_response(response)
log.info(f"File deleted successfully: {delete_response}")
except Exception as e:
# Log error but don't necessarily halt execution if deletion fails
log.error(f"Failed to delete file ID {file_id}: {e}")
# Depending on requirements, you might choose to raise the error here
async def _delete_file_async(
self, session: aiohttp.ClientSession, file_id: str
) -> None:
"""Async file deletion with error tolerance."""
try:
async def delete_request():
self._debug_log(f"Deleting file ID: {file_id}")
async with session.delete(
url=f"{self.base_url}/files/{file_id}",
headers=self.headers,
timeout=aiohttp.ClientTimeout(
total=self.cleanup_timeout
), # Shorter timeout for cleanup
) as response:
return await self._handle_response_async(response)
await self._retry_request_async(delete_request)
self._debug_log(f"File {file_id} deleted successfully")
except Exception as e:
# Don't fail the entire process if cleanup fails
log.warning(f"Failed to delete file ID {file_id}: {e}")
@asynccontextmanager
async def _get_session(self):
"""Context manager for HTTP session with optimized settings."""
connector = aiohttp.TCPConnector(
limit=20, # Increased total connection limit for better throughput
limit_per_host=10, # Increased per-host limit for API endpoints
ttl_dns_cache=600, # Longer DNS cache TTL (10 minutes)
use_dns_cache=True,
keepalive_timeout=60, # Increased keepalive for connection reuse
enable_cleanup_closed=True,
force_close=False, # Allow connection reuse
resolver=aiohttp.AsyncResolver(), # Use async DNS resolver
)
timeout = aiohttp.ClientTimeout(
total=self.timeout,
connect=30, # Connection timeout
sock_read=60, # Socket read timeout
)
async with aiohttp.ClientSession(
connector=connector,
timeout=timeout,
headers={"User-Agent": "OpenWebUI-MistralLoader/2.0"},
raise_for_status=False, # We handle status codes manually
trust_env=True,
) as session:
yield session
def _process_results(self, ocr_response: Dict[str, Any]) -> List[Document]:
"""Process OCR results into Document objects with enhanced metadata and memory efficiency."""
pages_data = ocr_response.get("pages")
if not pages_data:
log.warning("No pages found in OCR response.")
return [
Document(
page_content="No text content found",
metadata={"error": "no_pages", "file_name": self.file_name},
)
]
documents = []
total_pages = len(pages_data)
skipped_pages = 0
# Process pages in a memory-efficient way
for page_data in pages_data:
page_content = page_data.get("markdown")
page_index = page_data.get("index") # API uses 0-based index
if page_content is None or page_index is None:
skipped_pages += 1
self._debug_log(
f"Skipping page due to missing 'markdown' or 'index'. Data keys: {list(page_data.keys())}"
)
continue
# Clean up content efficiently with early exit for empty content
if isinstance(page_content, str):
cleaned_content = page_content.strip()
else:
cleaned_content = str(page_content).strip()
if not cleaned_content:
skipped_pages += 1
self._debug_log(f"Skipping empty page {page_index}")
continue
# Create document with optimized metadata
documents.append(
Document(
page_content=cleaned_content,
metadata={
"page": page_index, # 0-based index from API
"page_label": page_index + 1, # 1-based label for convenience
"total_pages": total_pages,
"file_name": self.file_name,
"file_size": self.file_size,
"processing_engine": "mistral-ocr",
"content_length": len(cleaned_content),
},
)
)
if skipped_pages > 0:
log.info(
f"Processed {len(documents)} pages, skipped {skipped_pages} empty/invalid pages"
)
if not documents:
# Case where pages existed but none had valid markdown/index
log.warning(
"OCR response contained pages, but none had valid content/index."
)
return [
Document(
page_content="No valid text content found in document",
metadata={
"error": "no_valid_pages",
"total_pages": total_pages,
"file_name": self.file_name,
},
)
]
return documents
def load(self) -> List[Document]:
"""
Executes the full OCR workflow: upload, get URL, process OCR, delete file.
Synchronous version for backward compatibility.
Returns:
A list of Document objects, one for each page processed.
"""
file_id = None
start_time = time.time()
try:
# 1. Upload file
file_id = self._upload_file()
@@ -166,53 +613,30 @@ class MistralLoader:
ocr_response = self._process_ocr(signed_url)
# 4. Process results
pages_data = ocr_response.get("pages")
if not pages_data:
log.warning("No pages found in OCR response.")
return [Document(page_content="No text content found", metadata={})]
documents = self._process_results(ocr_response)
documents = []
total_pages = len(pages_data)
for page_data in pages_data:
page_content = page_data.get("markdown")
page_index = page_data.get("index") # API uses 0-based index
if page_content is not None and page_index is not None:
documents.append(
Document(
page_content=page_content,
metadata={
"page": page_index, # 0-based index from API
"page_label": page_index
+ 1, # 1-based label for convenience
"total_pages": total_pages,
# Add other relevant metadata from page_data if available/needed
# e.g., page_data.get('width'), page_data.get('height')
},
)
)
else:
log.warning(
f"Skipping page due to missing 'markdown' or 'index'. Data: {page_data}"
)
if not documents:
# Case where pages existed but none had valid markdown/index
log.warning(
"OCR response contained pages, but none had valid content/index."
)
return [
Document(
page_content="No text content found in valid pages", metadata={}
)
]
total_time = time.time() - start_time
log.info(
f"Sync OCR workflow completed in {total_time:.2f}s, produced {len(documents)} documents"
)
return documents
except Exception as e:
log.error(f"An error occurred during the loading process: {e}")
# Return an empty list or a specific error document on failure
return [Document(page_content=f"Error during processing: {e}", metadata={})]
total_time = time.time() - start_time
log.error(
f"An error occurred during the loading process after {total_time:.2f}s: {e}"
)
# Return an error document on failure
return [
Document(
page_content=f"Error during processing: {e}",
metadata={
"error": "processing_failed",
"file_name": self.file_name,
},
)
]
finally:
# 5. Delete file (attempt even if prior steps failed after upload)
if file_id:
@@ -223,3 +647,124 @@ class MistralLoader:
log.error(
f"Cleanup error: Could not delete file ID {file_id}. Reason: {del_e}"
)
async def load_async(self) -> List[Document]:
"""
Asynchronous OCR workflow execution with optimized performance.
Returns:
A list of Document objects, one for each page processed.
"""
file_id = None
start_time = time.time()
try:
async with self._get_session() as session:
# 1. Upload file with streaming
file_id = await self._upload_file_async(session)
# 2. Get signed URL
signed_url = await self._get_signed_url_async(session, file_id)
# 3. Process OCR
ocr_response = await self._process_ocr_async(session, signed_url)
# 4. Process results
documents = self._process_results(ocr_response)
total_time = time.time() - start_time
log.info(
f"Async OCR workflow completed in {total_time:.2f}s, produced {len(documents)} documents"
)
return documents
except Exception as e:
total_time = time.time() - start_time
log.error(f"Async OCR workflow failed after {total_time:.2f}s: {e}")
return [
Document(
page_content=f"Error during OCR processing: {e}",
metadata={
"error": "processing_failed",
"file_name": self.file_name,
},
)
]
finally:
# 5. Cleanup - always attempt file deletion
if file_id:
try:
async with self._get_session() as session:
await self._delete_file_async(session, file_id)
except Exception as cleanup_error:
log.error(f"Cleanup failed for file ID {file_id}: {cleanup_error}")
@staticmethod
async def load_multiple_async(
loaders: List["MistralLoader"],
max_concurrent: int = 5, # Limit concurrent requests
) -> List[List[Document]]:
"""
Process multiple files concurrently with controlled concurrency.
Args:
loaders: List of MistralLoader instances
max_concurrent: Maximum number of concurrent requests
Returns:
List of document lists, one for each loader
"""
if not loaders:
return []
log.info(
f"Starting concurrent processing of {len(loaders)} files with max {max_concurrent} concurrent"
)
start_time = time.time()
# Use semaphore to control concurrency
semaphore = asyncio.Semaphore(max_concurrent)
async def process_with_semaphore(loader: "MistralLoader") -> List[Document]:
async with semaphore:
return await loader.load_async()
# Process all files with controlled concurrency
tasks = [process_with_semaphore(loader) for loader in loaders]
results = await asyncio.gather(*tasks, return_exceptions=True)
# Handle any exceptions in results
processed_results = []
for i, result in enumerate(results):
if isinstance(result, Exception):
log.error(f"File {i} failed: {result}")
processed_results.append(
[
Document(
page_content=f"Error processing file: {result}",
metadata={
"error": "batch_processing_failed",
"file_index": i,
},
)
]
)
else:
processed_results.append(result)
# MONITORING: Log comprehensive batch processing statistics
total_time = time.time() - start_time
total_docs = sum(len(docs) for docs in processed_results)
success_count = sum(
1 for result in results if not isinstance(result, Exception)
)
failure_count = len(results) - success_count
log.info(
f"Batch processing completed in {total_time:.2f}s: "
f"{success_count} files succeeded, {failure_count} files failed, "
f"produced {total_docs} total documents"
)
return processed_results
+70 -21
View File
@@ -1,4 +1,5 @@
import logging
from xml.etree.ElementTree import ParseError
from typing import Any, Dict, Generator, List, Optional, Sequence, Union
from urllib.parse import parse_qs, urlparse
@@ -62,12 +63,17 @@ class YoutubeLoader:
_video_id = _parse_video_id(video_id)
self.video_id = _video_id if _video_id is not None else video_id
self._metadata = {"source": video_id}
self.language = language
self.proxy_url = proxy_url
# Ensure language is a list
if isinstance(language, str):
self.language = [language]
else:
self.language = language
self.language = list(language)
# Add English as fallback if not already in the list
if "en" not in self.language:
self.language.append("en")
def load(self) -> List[Document]:
"""Load YouTube transcripts into `Document` objects."""
@@ -77,6 +83,7 @@ class YoutubeLoader:
TranscriptsDisabled,
YouTubeTranscriptApi,
)
from youtube_transcript_api.proxies import GenericProxyConfig
except ImportError:
raise ImportError(
'Could not import "youtube_transcript_api" Python package. '
@@ -84,34 +91,76 @@ class YoutubeLoader:
)
if self.proxy_url:
youtube_proxies = {
"http": self.proxy_url,
"https": self.proxy_url,
}
# Don't log complete URL because it might contain secrets
youtube_proxies = GenericProxyConfig(
http_url=self.proxy_url, https_url=self.proxy_url
)
log.debug(f"Using proxy URL: {self.proxy_url[:14]}...")
else:
youtube_proxies = None
transcript_api = YouTubeTranscriptApi(proxy_config=youtube_proxies)
try:
transcript_list = YouTubeTranscriptApi.list_transcripts(
self.video_id, proxies=youtube_proxies
)
transcript_list = transcript_api.list(self.video_id)
except Exception as e:
log.exception("Loading YouTube transcript failed")
return []
try:
transcript = transcript_list.find_transcript(self.language)
except NoTranscriptFound:
transcript = transcript_list.find_transcript(["en"])
# Try each language in order of priority
for lang in self.language:
try:
transcript = transcript_list.find_transcript([lang])
if transcript.is_generated:
log.debug(f"Found generated transcript for language '{lang}'")
try:
transcript = transcript_list.find_manually_created_transcript(
[lang]
)
log.debug(f"Found manual transcript for language '{lang}'")
except NoTranscriptFound:
log.debug(
f"No manual transcript found for language '{lang}', using generated"
)
pass
transcript_pieces: List[Dict[str, Any]] = transcript.fetch()
log.debug(f"Found transcript for language '{lang}'")
try:
transcript_pieces: List[Dict[str, Any]] = transcript.fetch()
except ParseError:
log.debug(f"Empty or invalid transcript for language '{lang}'")
continue
transcript = " ".join(
map(
lambda transcript_piece: transcript_piece.text.strip(" "),
transcript_pieces,
)
if not transcript_pieces:
log.debug(f"Empty transcript for language '{lang}'")
continue
transcript_text = " ".join(
map(
lambda transcript_piece: (
transcript_piece.text.strip(" ")
if hasattr(transcript_piece, "text")
else ""
),
transcript_pieces,
)
)
return [Document(page_content=transcript_text, metadata=self._metadata)]
except NoTranscriptFound:
log.debug(f"No transcript found for language '{lang}'")
continue
except Exception as e:
log.info(f"Error finding transcript for language '{lang}'")
raise e
# If we get here, all languages failed
languages_tried = ", ".join(self.language)
log.warning(
f"No transcript found for any of the specified languages: {languages_tried}. Verify if the video has transcripts, add more languages if needed."
)
return [Document(page_content=transcript, metadata=self._metadata)]
raise NoTranscriptFound(self.video_id, self.language, list(transcript_list))
async def aload(self) -> Generator[Document, None, None]:
"""Asynchronously load YouTube transcripts into `Document` objects."""
import asyncio
loop = asyncio.get_event_loop()
return await loop.run_in_executor(None, self.load)
@@ -0,0 +1,8 @@
from abc import ABC, abstractmethod
from typing import Optional, List, Tuple
class BaseReranker(ABC):
@abstractmethod
def predict(self, sentences: List[Tuple[str, str]]) -> Optional[List[float]]:
pass
@@ -7,11 +7,13 @@ from colbert.modeling.checkpoint import Checkpoint
from open_webui.env import SRC_LOG_LEVELS
from open_webui.retrieval.models.base_reranker import BaseReranker
log = logging.getLogger(__name__)
log.setLevel(SRC_LOG_LEVELS["RAG"])
class ColBERT:
class ColBERT(BaseReranker):
def __init__(self, name, **kwargs) -> None:
log.info("ColBERT: Loading model", name)
self.device = "cuda" if torch.cuda.is_available() else "cpu"
@@ -0,0 +1,70 @@
import logging
import requests
from typing import Optional, List, Tuple
from urllib.parse import quote
from open_webui.env import ENABLE_FORWARD_USER_INFO_HEADERS, SRC_LOG_LEVELS
from open_webui.retrieval.models.base_reranker import BaseReranker
from open_webui.utils.headers import include_user_info_headers
log = logging.getLogger(__name__)
log.setLevel(SRC_LOG_LEVELS["RAG"])
class ExternalReranker(BaseReranker):
def __init__(
self,
api_key: str,
url: str = "http://localhost:8080/v1/rerank",
model: str = "reranker",
):
self.api_key = api_key
self.url = url
self.model = model
def predict(
self, sentences: List[Tuple[str, str]], user=None
) -> Optional[List[float]]:
query = sentences[0][0]
docs = [i[1] for i in sentences]
payload = {
"model": self.model,
"query": query,
"documents": docs,
"top_n": len(docs),
}
try:
log.info(f"ExternalReranker:predict:model {self.model}")
log.info(f"ExternalReranker:predict:query {query}")
headers = {
"Content-Type": "application/json",
"Authorization": f"Bearer {self.api_key}",
}
if ENABLE_FORWARD_USER_INFO_HEADERS and user:
headers = include_user_info_headers(headers, user)
r = requests.post(
f"{self.url}",
headers=headers,
json=payload,
)
r.raise_for_status()
data = r.json()
if "results" in data:
sorted_results = sorted(data["results"], key=lambda x: x["index"])
return [result["relevance_score"] for result in sorted_results]
else:
log.error("No results found in external reranking response")
return None
except Exception as e:
log.exception(f"Error in external reranking: {e}")
return None
File diff suppressed because it is too large Load Diff
@@ -1,26 +0,0 @@
from open_webui.config import VECTOR_DB
if VECTOR_DB == "milvus":
from open_webui.retrieval.vector.dbs.milvus import MilvusClient
VECTOR_DB_CLIENT = MilvusClient()
elif VECTOR_DB == "qdrant":
from open_webui.retrieval.vector.dbs.qdrant import QdrantClient
VECTOR_DB_CLIENT = QdrantClient()
elif VECTOR_DB == "opensearch":
from open_webui.retrieval.vector.dbs.opensearch import OpenSearchClient
VECTOR_DB_CLIENT = OpenSearchClient()
elif VECTOR_DB == "pgvector":
from open_webui.retrieval.vector.dbs.pgvector import PgvectorClient
VECTOR_DB_CLIENT = PgvectorClient()
elif VECTOR_DB == "elasticsearch":
from open_webui.retrieval.vector.dbs.elasticsearch import ElasticsearchClient
VECTOR_DB_CLIENT = ElasticsearchClient()
else:
from open_webui.retrieval.vector.dbs.chroma import ChromaClient
VECTOR_DB_CLIENT = ChromaClient()
@@ -5,7 +5,14 @@ from chromadb.utils.batch_utils import create_batches
from typing import Optional
from open_webui.retrieval.vector.main import VectorItem, SearchResult, GetResult
from open_webui.retrieval.vector.main import (
VectorDBBase,
VectorItem,
SearchResult,
GetResult,
)
from open_webui.retrieval.vector.utils import process_metadata
from open_webui.config import (
CHROMA_DATA_PATH,
CHROMA_HTTP_HOST,
@@ -23,7 +30,7 @@ log = logging.getLogger(__name__)
log.setLevel(SRC_LOG_LEVELS["RAG"])
class ChromaClient:
class ChromaClient(VectorDBBase):
def __init__(self):
settings_dict = {
"allow_reset": True,
@@ -139,7 +146,7 @@ class ChromaClient:
ids = [item["id"] for item in items]
documents = [item["text"] for item in items]
embeddings = [item["vector"] for item in items]
metadatas = [item["metadata"] for item in items]
metadatas = [process_metadata(item["metadata"]) for item in items]
for batch in create_batches(
api=self.client,
@@ -159,7 +166,7 @@ class ChromaClient:
ids = [item["id"] for item in items]
documents = [item["text"] for item in items]
embeddings = [item["vector"] for item in items]
metadatas = [item["metadata"] for item in items]
metadatas = [process_metadata(item["metadata"]) for item in items]
collection.upsert(
ids=ids, documents=documents, embeddings=embeddings, metadatas=metadatas
@@ -2,7 +2,14 @@ from elasticsearch import Elasticsearch, BadRequestError
from typing import Optional
import ssl
from elasticsearch.helpers import bulk, scan
from open_webui.retrieval.vector.main import VectorItem, SearchResult, GetResult
from open_webui.retrieval.vector.utils import process_metadata
from open_webui.retrieval.vector.main import (
VectorDBBase,
VectorItem,
SearchResult,
GetResult,
)
from open_webui.config import (
ELASTICSEARCH_URL,
ELASTICSEARCH_CA_CERTS,
@@ -15,7 +22,7 @@ from open_webui.config import (
)
class ElasticsearchClient:
class ElasticsearchClient(VectorDBBase):
"""
Important:
in order to reduce the number of indexes and since the embedding vector length is fixed, we avoid creating
@@ -238,7 +245,7 @@ class ElasticsearchClient:
"collection": collection_name,
"vector": item["vector"],
"text": item["text"],
"metadata": item["metadata"],
"metadata": process_metadata(item["metadata"]),
},
}
for item in batch
@@ -259,7 +266,7 @@ class ElasticsearchClient:
"collection": collection_name,
"vector": item["vector"],
"text": item["text"],
"metadata": item["metadata"],
"metadata": process_metadata(item["metadata"]),
},
"doc_as_upsert": True,
}
+163 -70
View File
@@ -1,14 +1,29 @@
from pymilvus import MilvusClient as Client
from pymilvus import FieldSchema, DataType
from pymilvus import connections, Collection
import json
import logging
from typing import Optional
from open_webui.retrieval.vector.main import VectorItem, SearchResult, GetResult
from open_webui.retrieval.vector.utils import process_metadata
from open_webui.retrieval.vector.main import (
VectorDBBase,
VectorItem,
SearchResult,
GetResult,
)
from open_webui.config import (
MILVUS_URI,
MILVUS_DB,
MILVUS_TOKEN,
MILVUS_INDEX_TYPE,
MILVUS_METRIC_TYPE,
MILVUS_HNSW_M,
MILVUS_HNSW_EFCONSTRUCTION,
MILVUS_IVF_FLAT_NLIST,
MILVUS_DISKANN_MAX_DEGREE,
MILVUS_DISKANN_SEARCH_LIST_SIZE,
)
from open_webui.env import SRC_LOG_LEVELS
@@ -16,7 +31,7 @@ log = logging.getLogger(__name__)
log.setLevel(SRC_LOG_LEVELS["RAG"])
class MilvusClient:
class MilvusClient(VectorDBBase):
def __init__(self):
self.collection_prefix = "open_webui"
if MILVUS_TOKEN is None:
@@ -28,7 +43,6 @@ class MilvusClient:
ids = []
documents = []
metadatas = []
for match in result:
_ids = []
_documents = []
@@ -37,11 +51,9 @@ class MilvusClient:
_ids.append(item.get("id"))
_documents.append(item.get("data", {}).get("text"))
_metadatas.append(item.get("metadata"))
ids.append(_ids)
documents.append(_documents)
metadatas.append(_metadatas)
return GetResult(
**{
"ids": ids,
@@ -55,13 +67,11 @@ class MilvusClient:
distances = []
documents = []
metadatas = []
for match in result:
_ids = []
_distances = []
_documents = []
_metadatas = []
for item in match:
_ids.append(item.get("id"))
# normalize milvus score from [-1, 1] to [0, 1] range
@@ -70,12 +80,10 @@ class MilvusClient:
_distances.append(_dist)
_documents.append(item.get("entity", {}).get("data", {}).get("text"))
_metadatas.append(item.get("entity", {}).get("metadata"))
ids.append(_ids)
distances.append(_distances)
documents.append(_documents)
metadatas.append(_metadatas)
return SearchResult(
**{
"ids": ids,
@@ -108,11 +116,45 @@ class MilvusClient:
)
index_params = self.client.prepare_index_params()
# Use configurations from config.py
index_type = MILVUS_INDEX_TYPE.upper()
metric_type = MILVUS_METRIC_TYPE.upper()
log.info(f"Using Milvus index type: {index_type}, metric type: {metric_type}")
index_creation_params = {}
if index_type == "HNSW":
index_creation_params = {
"M": MILVUS_HNSW_M,
"efConstruction": MILVUS_HNSW_EFCONSTRUCTION,
}
log.info(f"HNSW params: {index_creation_params}")
elif index_type == "IVF_FLAT":
index_creation_params = {"nlist": MILVUS_IVF_FLAT_NLIST}
log.info(f"IVF_FLAT params: {index_creation_params}")
elif index_type == "DISKANN":
index_creation_params = {
"max_degree": MILVUS_DISKANN_MAX_DEGREE,
"search_list_size": MILVUS_DISKANN_SEARCH_LIST_SIZE,
}
log.info(f"DISKANN params: {index_creation_params}")
elif index_type in ["FLAT", "AUTOINDEX"]:
log.info(f"Using {index_type} index with no specific build-time params.")
else:
log.warning(
f"Unsupported MILVUS_INDEX_TYPE: '{index_type}'. "
f"Supported types: HNSW, IVF_FLAT, DISKANN, FLAT, AUTOINDEX. "
f"Milvus will use its default for the collection if this type is not directly supported for index creation."
)
# For unsupported types, pass the type directly to Milvus; it might handle it or use a default.
# If Milvus errors out, the user needs to correct the MILVUS_INDEX_TYPE env var.
index_params.add_index(
field_name="vector",
index_type="HNSW",
metric_type="COSINE",
params={"M": 16, "efConstruction": 100},
index_type=index_type,
metric_type=metric_type,
params=index_creation_params,
)
self.client.create_collection(
@@ -120,6 +162,9 @@ class MilvusClient:
schema=schema,
index_params=index_params,
)
log.info(
f"Successfully created collection '{self.collection_prefix}_{collection_name}' with index type '{index_type}' and metric '{metric_type}'."
)
def has_collection(self, collection_name: str) -> bool:
# Check if the collection exists based on the collection name.
@@ -140,84 +185,81 @@ class MilvusClient:
) -> Optional[SearchResult]:
# Search for the nearest neighbor items based on the vectors and return 'limit' number of results.
collection_name = collection_name.replace("-", "_")
# For some index types like IVF_FLAT, search params like nprobe can be set.
# Example: search_params = {"nprobe": 10} if using IVF_FLAT
# For simplicity, not adding configurable search_params here, but could be extended.
result = self.client.search(
collection_name=f"{self.collection_prefix}_{collection_name}",
data=vectors,
limit=limit,
output_fields=["data", "metadata"],
# search_params=search_params # Potentially add later if needed
)
return self._result_to_search_result(result)
def query(self, collection_name: str, filter: dict, limit: Optional[int] = None):
# Construct the filter string for querying
def query(self, collection_name: str, filter: dict, limit: int = -1):
connections.connect(uri=MILVUS_URI, token=MILVUS_TOKEN, db_name=MILVUS_DB)
collection_name = collection_name.replace("-", "_")
if not self.has_collection(collection_name):
log.warning(
f"Query attempted on non-existent collection: {self.collection_prefix}_{collection_name}"
)
return None
filter_string = " && ".join(
[
f'metadata["{key}"] == {json.dumps(value)}'
for key, value in filter.items()
]
)
filter_expressions = []
for key, value in filter.items():
if isinstance(value, str):
filter_expressions.append(f'metadata["{key}"] == "{value}"')
else:
filter_expressions.append(f'metadata["{key}"] == {value}')
max_limit = 16383 # The maximum number of records per request
all_results = []
filter_string = " && ".join(filter_expressions)
if limit is None:
limit = float("inf") # Use infinity as a placeholder for no limit
# Initialize offset and remaining to handle pagination
offset = 0
remaining = limit
collection = Collection(f"{self.collection_prefix}_{collection_name}")
collection.load()
try:
# Loop until there are no more items to fetch or the desired limit is reached
while remaining > 0:
log.info(f"remaining: {remaining}")
current_fetch = min(
max_limit, remaining
) # Determine how many items to fetch in this iteration
log.info(
f"Querying collection {self.collection_prefix}_{collection_name} with filter: '{filter_string}', limit: {limit}"
)
results = self.client.query(
collection_name=f"{self.collection_prefix}_{collection_name}",
filter=filter_string,
output_fields=["*"],
limit=current_fetch,
offset=offset,
)
iterator = collection.query_iterator(
expr=filter_string,
output_fields=[
"id",
"data",
"metadata",
],
limit=limit if limit > 0 else -1,
)
if not results:
all_results = []
while True:
batch = iterator.next()
if not batch:
iterator.close()
break
all_results.extend(batch)
all_results.extend(results)
results_count = len(results)
remaining -= (
results_count # Decrease remaining by the number of items fetched
)
offset += results_count
log.debug(f"Total results from query: {len(all_results)}")
return self._result_to_get_result([all_results] if all_results else [[]])
# Break the loop if the results returned are less than the requested fetch count
if results_count < current_fetch:
break
log.debug(all_results)
return self._result_to_get_result([all_results])
except Exception as e:
log.exception(
f"Error querying collection {collection_name} with limit {limit}: {e}"
f"Error querying collection {self.collection_prefix}_{collection_name} with filter '{filter_string}' and limit {limit}: {e}"
)
return None
def get(self, collection_name: str) -> Optional[GetResult]:
# Get all the items in the collection.
# Get all the items in the collection. This can be very resource-intensive for large collections.
collection_name = collection_name.replace("-", "_")
result = self.client.query(
collection_name=f"{self.collection_prefix}_{collection_name}",
filter='id != ""',
log.warning(
f"Fetching ALL items from collection '{self.collection_prefix}_{collection_name}'. This might be slow for large collections."
)
return self._result_to_get_result([result])
# Using query with a trivial filter to get all items.
# This will use the paginated query logic.
return self.query(collection_name=collection_name, filter={}, limit=-1)
def insert(self, collection_name: str, items: list[VectorItem]):
# Insert the items into the collection, if the collection does not exist, it will be created.
@@ -225,10 +267,23 @@ class MilvusClient:
if not self.client.has_collection(
collection_name=f"{self.collection_prefix}_{collection_name}"
):
log.info(
f"Collection {self.collection_prefix}_{collection_name} does not exist. Creating now."
)
if not items:
log.error(
f"Cannot create collection {self.collection_prefix}_{collection_name} without items to determine dimension."
)
raise ValueError(
"Cannot create Milvus collection without items to determine vector dimension."
)
self._create_collection(
collection_name=collection_name, dimension=len(items[0]["vector"])
)
log.info(
f"Inserting {len(items)} items into collection {self.collection_prefix}_{collection_name}."
)
return self.client.insert(
collection_name=f"{self.collection_prefix}_{collection_name}",
data=[
@@ -236,7 +291,7 @@ class MilvusClient:
"id": item["id"],
"vector": item["vector"],
"data": {"text": item["text"]},
"metadata": item["metadata"],
"metadata": process_metadata(item["metadata"]),
}
for item in items
],
@@ -248,10 +303,23 @@ class MilvusClient:
if not self.client.has_collection(
collection_name=f"{self.collection_prefix}_{collection_name}"
):
log.info(
f"Collection {self.collection_prefix}_{collection_name} does not exist for upsert. Creating now."
)
if not items:
log.error(
f"Cannot create collection {self.collection_prefix}_{collection_name} for upsert without items to determine dimension."
)
raise ValueError(
"Cannot create Milvus collection for upsert without items to determine vector dimension."
)
self._create_collection(
collection_name=collection_name, dimension=len(items[0]["vector"])
)
log.info(
f"Upserting {len(items)} items into collection {self.collection_prefix}_{collection_name}."
)
return self.client.upsert(
collection_name=f"{self.collection_prefix}_{collection_name}",
data=[
@@ -259,7 +327,7 @@ class MilvusClient:
"id": item["id"],
"vector": item["vector"],
"data": {"text": item["text"]},
"metadata": item["metadata"],
"metadata": process_metadata(item["metadata"]),
}
for item in items
],
@@ -271,30 +339,55 @@ class MilvusClient:
ids: Optional[list[str]] = None,
filter: Optional[dict] = None,
):
# Delete the items from the collection based on the ids.
# Delete the items from the collection based on the ids or filter.
collection_name = collection_name.replace("-", "_")
if not self.has_collection(collection_name):
log.warning(
f"Delete attempted on non-existent collection: {self.collection_prefix}_{collection_name}"
)
return None
if ids:
log.info(
f"Deleting items by IDs from {self.collection_prefix}_{collection_name}. IDs: {ids}"
)
return self.client.delete(
collection_name=f"{self.collection_prefix}_{collection_name}",
ids=ids,
)
elif filter:
# Convert the filter dictionary to a string using JSON_CONTAINS.
filter_string = " && ".join(
[
f'metadata["{key}"] == {json.dumps(value)}'
for key, value in filter.items()
]
)
log.info(
f"Deleting items by filter from {self.collection_prefix}_{collection_name}. Filter: {filter_string}"
)
return self.client.delete(
collection_name=f"{self.collection_prefix}_{collection_name}",
filter=filter_string,
)
else:
log.warning(
f"Delete operation on {self.collection_prefix}_{collection_name} called without IDs or filter. No action taken."
)
return None
def reset(self):
# Resets the database. This will delete all collections and item entries.
# Resets the database. This will delete all collections and item entries that match the prefix.
log.warning(
f"Resetting Milvus: Deleting all collections with prefix '{self.collection_prefix}'."
)
collection_names = self.client.list_collections()
for collection_name in collection_names:
if collection_name.startswith(self.collection_prefix):
self.client.drop_collection(collection_name=collection_name)
deleted_collections = []
for collection_name_full in collection_names:
if collection_name_full.startswith(self.collection_prefix):
try:
self.client.drop_collection(collection_name=collection_name_full)
deleted_collections.append(collection_name_full)
log.info(f"Deleted collection: {collection_name_full}")
except Exception as e:
log.error(f"Error deleting collection {collection_name_full}: {e}")
log.info(f"Milvus reset complete. Deleted collections: {deleted_collections}")
@@ -0,0 +1,289 @@
import logging
from typing import Optional, Tuple, List, Dict, Any
from open_webui.config import (
MILVUS_URI,
MILVUS_TOKEN,
MILVUS_DB,
MILVUS_COLLECTION_PREFIX,
MILVUS_INDEX_TYPE,
MILVUS_METRIC_TYPE,
MILVUS_HNSW_M,
MILVUS_HNSW_EFCONSTRUCTION,
MILVUS_IVF_FLAT_NLIST,
)
from open_webui.env import SRC_LOG_LEVELS
from open_webui.retrieval.vector.main import (
GetResult,
SearchResult,
VectorDBBase,
VectorItem,
)
from pymilvus import (
connections,
utility,
Collection,
CollectionSchema,
FieldSchema,
DataType,
)
log = logging.getLogger(__name__)
log.setLevel(SRC_LOG_LEVELS["RAG"])
RESOURCE_ID_FIELD = "resource_id"
class MilvusClient(VectorDBBase):
def __init__(self):
# Milvus collection names can only contain numbers, letters, and underscores.
self.collection_prefix = MILVUS_COLLECTION_PREFIX.replace("-", "_")
connections.connect(
alias="default",
uri=MILVUS_URI,
token=MILVUS_TOKEN,
db_name=MILVUS_DB,
)
# Main collection types for multi-tenancy
self.MEMORY_COLLECTION = f"{self.collection_prefix}_memories"
self.KNOWLEDGE_COLLECTION = f"{self.collection_prefix}_knowledge"
self.FILE_COLLECTION = f"{self.collection_prefix}_files"
self.WEB_SEARCH_COLLECTION = f"{self.collection_prefix}_web_search"
self.HASH_BASED_COLLECTION = f"{self.collection_prefix}_hash_based"
self.shared_collections = [
self.MEMORY_COLLECTION,
self.KNOWLEDGE_COLLECTION,
self.FILE_COLLECTION,
self.WEB_SEARCH_COLLECTION,
self.HASH_BASED_COLLECTION,
]
def _get_collection_and_resource_id(self, collection_name: str) -> Tuple[str, str]:
"""
Maps the traditional collection name to multi-tenant collection and resource ID.
WARNING: This mapping relies on current Open WebUI naming conventions for
collection names. If Open WebUI changes how it generates collection names
(e.g., "user-memory-" prefix, "file-" prefix, web search patterns, or hash
formats), this mapping will break and route data to incorrect collections.
POTENTIALLY CAUSING HUGE DATA CORRUPTION, DATA CONSISTENCY ISSUES AND INCORRECT
DATA MAPPING INSIDE THE DATABASE.
"""
resource_id = collection_name
if collection_name.startswith("user-memory-"):
return self.MEMORY_COLLECTION, resource_id
elif collection_name.startswith("file-"):
return self.FILE_COLLECTION, resource_id
elif collection_name.startswith("web-search-"):
return self.WEB_SEARCH_COLLECTION, resource_id
elif len(collection_name) == 63 and all(
c in "0123456789abcdef" for c in collection_name
):
return self.HASH_BASED_COLLECTION, resource_id
else:
return self.KNOWLEDGE_COLLECTION, resource_id
def _create_shared_collection(self, mt_collection_name: str, dimension: int):
fields = [
FieldSchema(
name="id",
dtype=DataType.VARCHAR,
is_primary=True,
auto_id=False,
max_length=36,
),
FieldSchema(name="vector", dtype=DataType.FLOAT_VECTOR, dim=dimension),
FieldSchema(name="text", dtype=DataType.VARCHAR, max_length=65535),
FieldSchema(name="metadata", dtype=DataType.JSON),
FieldSchema(name=RESOURCE_ID_FIELD, dtype=DataType.VARCHAR, max_length=255),
]
schema = CollectionSchema(fields, "Shared collection for multi-tenancy")
collection = Collection(mt_collection_name, schema)
index_params = {
"metric_type": MILVUS_METRIC_TYPE,
"index_type": MILVUS_INDEX_TYPE,
"params": {},
}
if MILVUS_INDEX_TYPE == "HNSW":
index_params["params"] = {
"M": MILVUS_HNSW_M,
"efConstruction": MILVUS_HNSW_EFCONSTRUCTION,
}
elif MILVUS_INDEX_TYPE == "IVF_FLAT":
index_params["params"] = {"nlist": MILVUS_IVF_FLAT_NLIST}
collection.create_index("vector", index_params)
collection.create_index(RESOURCE_ID_FIELD)
log.info(f"Created shared collection: {mt_collection_name}")
return collection
def _ensure_collection(self, mt_collection_name: str, dimension: int):
if not utility.has_collection(mt_collection_name):
self._create_shared_collection(mt_collection_name, dimension)
def has_collection(self, collection_name: str) -> bool:
mt_collection, resource_id = self._get_collection_and_resource_id(
collection_name
)
if not utility.has_collection(mt_collection):
return False
collection = Collection(mt_collection)
collection.load()
res = collection.query(expr=f"{RESOURCE_ID_FIELD} == '{resource_id}'", limit=1)
return len(res) > 0
def upsert(self, collection_name: str, items: List[VectorItem]):
if not items:
return
mt_collection, resource_id = self._get_collection_and_resource_id(
collection_name
)
dimension = len(items[0]["vector"])
self._ensure_collection(mt_collection, dimension)
collection = Collection(mt_collection)
entities = [
{
"id": item["id"],
"vector": item["vector"],
"text": item["text"],
"metadata": item["metadata"],
RESOURCE_ID_FIELD: resource_id,
}
for item in items
]
collection.insert(entities)
def search(
self, collection_name: str, vectors: List[List[float]], limit: int
) -> Optional[SearchResult]:
if not vectors:
return None
mt_collection, resource_id = self._get_collection_and_resource_id(
collection_name
)
if not utility.has_collection(mt_collection):
return None
collection = Collection(mt_collection)
collection.load()
search_params = {"metric_type": MILVUS_METRIC_TYPE, "params": {}}
results = collection.search(
data=vectors,
anns_field="vector",
param=search_params,
limit=limit,
expr=f"{RESOURCE_ID_FIELD} == '{resource_id}'",
output_fields=["id", "text", "metadata"],
)
ids, documents, metadatas, distances = [], [], [], []
for hits in results:
batch_ids, batch_docs, batch_metadatas, batch_dists = [], [], [], []
for hit in hits:
batch_ids.append(hit.entity.get("id"))
batch_docs.append(hit.entity.get("text"))
batch_metadatas.append(hit.entity.get("metadata"))
batch_dists.append(hit.distance)
ids.append(batch_ids)
documents.append(batch_docs)
metadatas.append(batch_metadatas)
distances.append(batch_dists)
return SearchResult(
ids=ids, documents=documents, metadatas=metadatas, distances=distances
)
def delete(
self,
collection_name: str,
ids: Optional[List[str]] = None,
filter: Optional[Dict[str, Any]] = None,
):
mt_collection, resource_id = self._get_collection_and_resource_id(
collection_name
)
if not utility.has_collection(mt_collection):
return
collection = Collection(mt_collection)
# Build expression
expr = [f"{RESOURCE_ID_FIELD} == '{resource_id}'"]
if ids:
# Milvus expects a string list for 'in' operator
id_list_str = ", ".join([f"'{id_val}'" for id_val in ids])
expr.append(f"id in [{id_list_str}]")
if filter:
for key, value in filter.items():
expr.append(f"metadata['{key}'] == '{value}'")
collection.delete(" and ".join(expr))
def reset(self):
for collection_name in self.shared_collections:
if utility.has_collection(collection_name):
utility.drop_collection(collection_name)
def delete_collection(self, collection_name: str):
mt_collection, resource_id = self._get_collection_and_resource_id(
collection_name
)
if not utility.has_collection(mt_collection):
return
collection = Collection(mt_collection)
collection.delete(f"{RESOURCE_ID_FIELD} == '{resource_id}'")
def query(
self, collection_name: str, filter: Dict[str, Any], limit: Optional[int] = None
) -> Optional[GetResult]:
mt_collection, resource_id = self._get_collection_and_resource_id(
collection_name
)
if not utility.has_collection(mt_collection):
return None
collection = Collection(mt_collection)
collection.load()
expr = [f"{RESOURCE_ID_FIELD} == '{resource_id}'"]
if filter:
for key, value in filter.items():
if isinstance(value, str):
expr.append(f"metadata['{key}'] == '{value}'")
else:
expr.append(f"metadata['{key}'] == {value}")
iterator = collection.query_iterator(
expr=" and ".join(expr),
output_fields=["id", "text", "metadata"],
limit=limit if limit else -1,
)
all_results = []
while True:
batch = iterator.next()
if not batch:
iterator.close()
break
all_results.extend(batch)
ids = [res["id"] for res in all_results]
documents = [res["text"] for res in all_results]
metadatas = [res["metadata"] for res in all_results]
return GetResult(ids=[ids], documents=[documents], metadatas=[metadatas])
def get(self, collection_name: str) -> Optional[GetResult]:
return self.query(collection_name, filter={}, limit=None)
def insert(self, collection_name: str, items: List[VectorItem]):
return self.upsert(collection_name, items)
@@ -2,7 +2,13 @@ from opensearchpy import OpenSearch
from opensearchpy.helpers import bulk
from typing import Optional
from open_webui.retrieval.vector.main import VectorItem, SearchResult, GetResult
from open_webui.retrieval.vector.utils import process_metadata
from open_webui.retrieval.vector.main import (
VectorDBBase,
VectorItem,
SearchResult,
GetResult,
)
from open_webui.config import (
OPENSEARCH_URI,
OPENSEARCH_SSL,
@@ -12,7 +18,7 @@ from open_webui.config import (
)
class OpenSearchClient:
class OpenSearchClient(VectorDBBase):
def __init__(self):
self.index_prefix = "open_webui"
self.client = OpenSearch(
@@ -152,10 +158,10 @@ class OpenSearchClient:
for field, value in filter.items():
query_body["query"]["bool"]["filter"].append(
{"match": {"metadata." + str(field): value}}
{"term": {"metadata." + str(field) + ".keyword": value}}
)
size = limit if limit else 10
size = limit if limit else 10000
try:
result = self.client.search(
@@ -195,12 +201,13 @@ class OpenSearchClient:
"_source": {
"vector": item["vector"],
"text": item["text"],
"metadata": item["metadata"],
"metadata": process_metadata(item["metadata"]),
},
}
for item in batch
]
bulk(self.client, actions)
self.client.indices.refresh(self._get_index_name(collection_name))
def upsert(self, collection_name: str, items: list[VectorItem]):
self._create_index_if_not_exists(
@@ -216,13 +223,14 @@ class OpenSearchClient:
"doc": {
"vector": item["vector"],
"text": item["text"],
"metadata": item["metadata"],
"metadata": process_metadata(item["metadata"]),
},
"doc_as_upsert": True,
}
for item in batch
]
bulk(self.client, actions)
self.client.indices.refresh(self._get_index_name(collection_name))
def delete(
self,
@@ -246,11 +254,12 @@ class OpenSearchClient:
}
for field, value in filter.items():
query_body["query"]["bool"]["filter"].append(
{"match": {"metadata." + str(field): value}}
{"term": {"metadata." + str(field) + ".keyword": value}}
)
self.client.delete_by_query(
index=self._get_index_name(collection_name), body=query_body
)
self.client.indices.refresh(self._get_index_name(collection_name))
def reset(self):
indices = self.client.indices.get(index=f"{self.index_prefix}_*")
@@ -0,0 +1,943 @@
"""
Oracle 23ai Vector Database Client - Fixed Version
# .env
VECTOR_DB = "oracle23ai"
## DBCS or oracle 23ai free
ORACLE_DB_USE_WALLET = false
ORACLE_DB_USER = "DEMOUSER"
ORACLE_DB_PASSWORD = "Welcome123456"
ORACLE_DB_DSN = "localhost:1521/FREEPDB1"
## ADW or ATP
# ORACLE_DB_USE_WALLET = true
# ORACLE_DB_USER = "DEMOUSER"
# ORACLE_DB_PASSWORD = "Welcome123456"
# ORACLE_DB_DSN = "medium"
# ORACLE_DB_DSN = "(description= (retry_count=3)(retry_delay=3)(address=(protocol=tcps)(port=1522)(host=xx.oraclecloud.com))(connect_data=(service_name=yy.adb.oraclecloud.com))(security=(ssl_server_dn_match=no)))"
# ORACLE_WALLET_DIR = "/home/opc/adb_wallet"
# ORACLE_WALLET_PASSWORD = "Welcome1"
ORACLE_VECTOR_LENGTH = 768
ORACLE_DB_POOL_MIN = 2
ORACLE_DB_POOL_MAX = 10
ORACLE_DB_POOL_INCREMENT = 1
"""
from typing import Optional, List, Dict, Any, Union
from decimal import Decimal
import logging
import os
import threading
import time
import json
import array
import oracledb
from open_webui.retrieval.vector.main import (
VectorDBBase,
VectorItem,
SearchResult,
GetResult,
)
from open_webui.config import (
ORACLE_DB_USE_WALLET,
ORACLE_DB_USER,
ORACLE_DB_PASSWORD,
ORACLE_DB_DSN,
ORACLE_WALLET_DIR,
ORACLE_WALLET_PASSWORD,
ORACLE_VECTOR_LENGTH,
ORACLE_DB_POOL_MIN,
ORACLE_DB_POOL_MAX,
ORACLE_DB_POOL_INCREMENT,
)
from open_webui.env import SRC_LOG_LEVELS
log = logging.getLogger(__name__)
log.setLevel(SRC_LOG_LEVELS["RAG"])
class Oracle23aiClient(VectorDBBase):
"""
Oracle Vector Database Client for vector similarity search using Oracle Database 23ai.
This client provides an interface to store, retrieve, and search vector embeddings
in an Oracle database. It uses connection pooling for efficient database access
and supports vector similarity search operations.
Attributes:
pool: Connection pool for Oracle database connections
"""
def __init__(self) -> None:
"""
Initialize the Oracle23aiClient with a connection pool.
Creates a connection pool with configurable min/max connections, initializes
the database schema if needed, and sets up necessary tables and indexes.
Raises:
ValueError: If required configuration parameters are missing
Exception: If database initialization fails
"""
self.pool = None
try:
# Create the appropriate connection pool based on DB type
if ORACLE_DB_USE_WALLET:
self._create_adb_pool()
else: # DBCS
self._create_dbcs_pool()
dsn = ORACLE_DB_DSN
log.info(f"Creating Connection Pool [{ORACLE_DB_USER}:**@{dsn}]")
with self.get_connection() as connection:
log.info(f"Connection version: {connection.version}")
self._initialize_database(connection)
log.info("Oracle Vector Search initialization complete.")
except Exception as e:
log.exception(f"Error during Oracle Vector Search initialization: {e}")
raise
def _create_adb_pool(self) -> None:
"""
Create connection pool for Oracle Autonomous Database.
Uses wallet-based authentication.
"""
self.pool = oracledb.create_pool(
user=ORACLE_DB_USER,
password=ORACLE_DB_PASSWORD,
dsn=ORACLE_DB_DSN,
min=ORACLE_DB_POOL_MIN,
max=ORACLE_DB_POOL_MAX,
increment=ORACLE_DB_POOL_INCREMENT,
config_dir=ORACLE_WALLET_DIR,
wallet_location=ORACLE_WALLET_DIR,
wallet_password=ORACLE_WALLET_PASSWORD,
)
log.info("Created ADB connection pool with wallet authentication.")
def _create_dbcs_pool(self) -> None:
"""
Create connection pool for Oracle Database Cloud Service.
Uses basic authentication without wallet.
"""
self.pool = oracledb.create_pool(
user=ORACLE_DB_USER,
password=ORACLE_DB_PASSWORD,
dsn=ORACLE_DB_DSN,
min=ORACLE_DB_POOL_MIN,
max=ORACLE_DB_POOL_MAX,
increment=ORACLE_DB_POOL_INCREMENT,
)
log.info("Created DB connection pool with basic authentication.")
def get_connection(self):
"""
Acquire a connection from the connection pool with retry logic.
Returns:
connection: A database connection with output type handler configured
"""
max_retries = 3
for attempt in range(max_retries):
try:
connection = self.pool.acquire()
connection.outputtypehandler = self._output_type_handler
return connection
except oracledb.DatabaseError as e:
(error_obj,) = e.args
log.exception(
f"Connection attempt {attempt + 1} failed: {error_obj.message}"
)
if attempt < max_retries - 1:
wait_time = 2**attempt
log.info(f"Retrying in {wait_time} seconds...")
time.sleep(wait_time)
else:
raise
def start_health_monitor(self, interval_seconds: int = 60):
"""
Start a background thread to periodically check the health of the connection pool.
Args:
interval_seconds (int): Number of seconds between health checks
"""
def _monitor():
while True:
try:
log.info("[HealthCheck] Running periodic DB health check...")
self.ensure_connection()
log.info("[HealthCheck] Connection is healthy.")
except Exception as e:
log.exception(f"[HealthCheck] Connection health check failed: {e}")
time.sleep(interval_seconds)
thread = threading.Thread(target=_monitor, daemon=True)
thread.start()
log.info(f"Started DB health monitor every {interval_seconds} seconds.")
def _reconnect_pool(self):
"""
Attempt to reinitialize the connection pool if it's been closed or broken.
"""
try:
log.info("Attempting to reinitialize the Oracle connection pool...")
# Close existing pool if it exists
if self.pool:
try:
self.pool.close()
except Exception as close_error:
log.warning(f"Error closing existing pool: {close_error}")
# Re-create the appropriate connection pool based on DB type
if ORACLE_DB_USE_WALLET:
self._create_adb_pool()
else: # DBCS
self._create_dbcs_pool()
log.info("Connection pool reinitialized.")
except Exception as e:
log.exception(f"Failed to reinitialize the connection pool: {e}")
raise
def ensure_connection(self):
"""
Ensure the database connection is alive, reconnecting pool if needed.
"""
try:
with self.get_connection() as connection:
with connection.cursor() as cursor:
cursor.execute("SELECT 1 FROM dual")
except Exception as e:
log.exception(
f"Connection check failed: {e}, attempting to reconnect pool..."
)
self._reconnect_pool()
def _output_type_handler(self, cursor, metadata):
"""
Handle Oracle vector type conversion.
Args:
cursor: Oracle database cursor
metadata: Metadata for the column
Returns:
A variable with appropriate conversion for vector types
"""
if metadata.type_code is oracledb.DB_TYPE_VECTOR:
return cursor.var(
metadata.type_code, arraysize=cursor.arraysize, outconverter=list
)
def _initialize_database(self, connection) -> None:
"""
Initialize database schema, tables and indexes.
Creates the document_chunk table and necessary indexes if they don't exist.
Args:
connection: Oracle database connection
Raises:
Exception: If schema initialization fails
"""
with connection.cursor() as cursor:
try:
log.info("Creating Table document_chunk")
cursor.execute(
"""
BEGIN
EXECUTE IMMEDIATE '
CREATE TABLE IF NOT EXISTS document_chunk (
id VARCHAR2(255) PRIMARY KEY,
collection_name VARCHAR2(255) NOT NULL,
text CLOB,
vmetadata JSON,
vector vector(*, float32)
)
';
EXCEPTION
WHEN OTHERS THEN
IF SQLCODE != -955 THEN
RAISE;
END IF;
END;
"""
)
log.info("Creating Index document_chunk_collection_name_idx")
cursor.execute(
"""
BEGIN
EXECUTE IMMEDIATE '
CREATE INDEX IF NOT EXISTS document_chunk_collection_name_idx
ON document_chunk (collection_name)
';
EXCEPTION
WHEN OTHERS THEN
IF SQLCODE != -955 THEN
RAISE;
END IF;
END;
"""
)
log.info("Creating VECTOR INDEX document_chunk_vector_ivf_idx")
cursor.execute(
"""
BEGIN
EXECUTE IMMEDIATE '
CREATE VECTOR INDEX IF NOT EXISTS document_chunk_vector_ivf_idx
ON document_chunk(vector)
ORGANIZATION NEIGHBOR PARTITIONS
DISTANCE COSINE
WITH TARGET ACCURACY 95
PARAMETERS (TYPE IVF, NEIGHBOR PARTITIONS 100)
';
EXCEPTION
WHEN OTHERS THEN
IF SQLCODE != -955 THEN
RAISE;
END IF;
END;
"""
)
connection.commit()
log.info("Database initialization completed successfully.")
except Exception as e:
connection.rollback()
log.exception(f"Error during database initialization: {e}")
raise
def check_vector_length(self) -> None:
"""
Check vector length compatibility (placeholder).
This method would check if the configured vector length matches the database schema.
Currently implemented as a placeholder.
"""
pass
def _vector_to_blob(self, vector: List[float]) -> bytes:
"""
Convert a vector to Oracle BLOB format.
Args:
vector (List[float]): The vector to convert
Returns:
bytes: The vector in Oracle BLOB format
"""
return array.array("f", vector)
def adjust_vector_length(self, vector: List[float]) -> List[float]:
"""
Adjust vector to the expected length if needed.
Args:
vector (List[float]): The vector to adjust
Returns:
List[float]: The adjusted vector
"""
return vector
def _decimal_handler(self, obj):
"""
Handle Decimal objects for JSON serialization.
Args:
obj: Object to serialize
Returns:
float: Converted decimal value
Raises:
TypeError: If object is not JSON serializable
"""
if isinstance(obj, Decimal):
return float(obj)
raise TypeError(f"{obj} is not JSON serializable")
def _metadata_to_json(self, metadata: Dict) -> str:
"""
Convert metadata dictionary to JSON string.
Args:
metadata (Dict): Metadata dictionary
Returns:
str: JSON representation of metadata
"""
return json.dumps(metadata, default=self._decimal_handler) if metadata else "{}"
def _json_to_metadata(self, json_str: str) -> Dict:
"""
Convert JSON string to metadata dictionary.
Args:
json_str (str): JSON string
Returns:
Dict: Metadata dictionary
"""
return json.loads(json_str) if json_str else {}
def insert(self, collection_name: str, items: List[VectorItem]) -> None:
"""
Insert vector items into the database.
Args:
collection_name (str): Name of the collection
items (List[VectorItem]): List of vector items to insert
Raises:
Exception: If insertion fails
Example:
>>> client = Oracle23aiClient()
>>> items = [
... {"id": "1", "text": "Sample text", "vector": [0.1, 0.2, ...], "metadata": {"source": "doc1"}},
... {"id": "2", "text": "Another text", "vector": [0.3, 0.4, ...], "metadata": {"source": "doc2"}}
... ]
>>> client.insert("my_collection", items)
"""
log.info(f"Inserting {len(items)} items into collection '{collection_name}'.")
with self.get_connection() as connection:
try:
with connection.cursor() as cursor:
for item in items:
vector_blob = self._vector_to_blob(item["vector"])
metadata_json = self._metadata_to_json(item["metadata"])
cursor.execute(
"""
INSERT INTO document_chunk
(id, collection_name, text, vmetadata, vector)
VALUES (:id, :collection_name, :text, :metadata, :vector)
""",
{
"id": item["id"],
"collection_name": collection_name,
"text": item["text"],
"metadata": metadata_json,
"vector": vector_blob,
},
)
connection.commit()
log.info(
f"Successfully inserted {len(items)} items into collection '{collection_name}'."
)
except Exception as e:
connection.rollback()
log.exception(f"Error during insert: {e}")
raise
def upsert(self, collection_name: str, items: List[VectorItem]) -> None:
"""
Update or insert vector items into the database.
If an item with the same ID exists, it will be updated;
otherwise, it will be inserted.
Args:
collection_name (str): Name of the collection
items (List[VectorItem]): List of vector items to upsert
Raises:
Exception: If upsert operation fails
Example:
>>> client = Oracle23aiClient()
>>> items = [
... {"id": "1", "text": "Updated text", "vector": [0.1, 0.2, ...], "metadata": {"source": "doc1"}},
... {"id": "3", "text": "New item", "vector": [0.5, 0.6, ...], "metadata": {"source": "doc3"}}
... ]
>>> client.upsert("my_collection", items)
"""
log.info(f"Upserting {len(items)} items into collection '{collection_name}'.")
with self.get_connection() as connection:
try:
with connection.cursor() as cursor:
for item in items:
vector_blob = self._vector_to_blob(item["vector"])
metadata_json = self._metadata_to_json(item["metadata"])
cursor.execute(
"""
MERGE INTO document_chunk d
USING (SELECT :merge_id as id FROM dual) s
ON (d.id = s.id)
WHEN MATCHED THEN
UPDATE SET
collection_name = :upd_collection_name,
text = :upd_text,
vmetadata = :upd_metadata,
vector = :upd_vector
WHEN NOT MATCHED THEN
INSERT (id, collection_name, text, vmetadata, vector)
VALUES (:ins_id, :ins_collection_name, :ins_text, :ins_metadata, :ins_vector)
""",
{
"merge_id": item["id"],
"upd_collection_name": collection_name,
"upd_text": item["text"],
"upd_metadata": metadata_json,
"upd_vector": vector_blob,
"ins_id": item["id"],
"ins_collection_name": collection_name,
"ins_text": item["text"],
"ins_metadata": metadata_json,
"ins_vector": vector_blob,
},
)
connection.commit()
log.info(
f"Successfully upserted {len(items)} items into collection '{collection_name}'."
)
except Exception as e:
connection.rollback()
log.exception(f"Error during upsert: {e}")
raise
def search(
self, collection_name: str, vectors: List[List[Union[float, int]]], limit: int
) -> Optional[SearchResult]:
"""
Search for similar vectors in the database.
Performs vector similarity search using cosine distance.
Args:
collection_name (str): Name of the collection to search
vectors (List[List[Union[float, int]]]): Query vectors to find similar items for
limit (int): Maximum number of results to return per query
Returns:
Optional[SearchResult]: Search results containing ids, distances, documents, and metadata
Example:
>>> client = Oracle23aiClient()
>>> query_vector = [0.1, 0.2, 0.3, ...] # Must match VECTOR_LENGTH
>>> results = client.search("my_collection", [query_vector], limit=5)
>>> if results:
... log.info(f"Found {len(results.ids[0])} matches")
... for i, (id, dist) in enumerate(zip(results.ids[0], results.distances[0])):
... log.info(f"Match {i+1}: id={id}, distance={dist}")
"""
log.info(
f"Searching items from collection '{collection_name}' with limit {limit}."
)
try:
if not vectors:
log.warning("No vectors provided for search.")
return None
num_queries = len(vectors)
ids = [[] for _ in range(num_queries)]
distances = [[] for _ in range(num_queries)]
documents = [[] for _ in range(num_queries)]
metadatas = [[] for _ in range(num_queries)]
with self.get_connection() as connection:
with connection.cursor() as cursor:
for qid, vector in enumerate(vectors):
vector_blob = self._vector_to_blob(vector)
cursor.execute(
"""
SELECT dc.id, dc.text,
JSON_SERIALIZE(dc.vmetadata RETURNING VARCHAR2(4096)) as vmetadata,
VECTOR_DISTANCE(dc.vector, :query_vector, COSINE) as distance
FROM document_chunk dc
WHERE dc.collection_name = :collection_name
ORDER BY VECTOR_DISTANCE(dc.vector, :query_vector, COSINE)
FETCH APPROX FIRST :limit ROWS ONLY
""",
{
"query_vector": vector_blob,
"collection_name": collection_name,
"limit": limit,
},
)
results = cursor.fetchall()
for row in results:
ids[qid].append(row[0])
documents[qid].append(
row[1].read()
if isinstance(row[1], oracledb.LOB)
else str(row[1])
)
# 🔧 FIXED: Parse JSON metadata properly
metadata_str = (
row[2].read()
if isinstance(row[2], oracledb.LOB)
else row[2]
)
metadatas[qid].append(self._json_to_metadata(metadata_str))
distances[qid].append(float(row[3]))
log.info(
f"Search completed. Found {sum(len(ids[i]) for i in range(num_queries))} total results."
)
return SearchResult(
ids=ids, distances=distances, documents=documents, metadatas=metadatas
)
except Exception as e:
log.exception(f"Error during search: {e}")
return None
def query(
self, collection_name: str, filter: Dict, limit: Optional[int] = None
) -> Optional[GetResult]:
"""
Query items based on metadata filters.
Retrieves items that match specified metadata criteria.
Args:
collection_name (str): Name of the collection to query
filter (Dict[str, Any]): Metadata filters to apply
limit (Optional[int]): Maximum number of results to return
Returns:
Optional[GetResult]: Query results containing ids, documents, and metadata
Example:
>>> client = Oracle23aiClient()
>>> filter = {"source": "doc1", "category": "finance"}
>>> results = client.query("my_collection", filter, limit=20)
>>> if results:
... print(f"Found {len(results.ids[0])} matching documents")
"""
log.info(f"Querying items from collection '{collection_name}' with filters.")
try:
limit = limit or 100
query = """
SELECT id, text, JSON_SERIALIZE(vmetadata RETURNING VARCHAR2(4096)) as vmetadata
FROM document_chunk
WHERE collection_name = :collection_name
"""
params = {"collection_name": collection_name}
for i, (key, value) in enumerate(filter.items()):
param_name = f"value_{i}"
query += f" AND JSON_VALUE(vmetadata, '$.{key}' RETURNING VARCHAR2(4096)) = :{param_name}"
params[param_name] = str(value)
query += " FETCH FIRST :limit ROWS ONLY"
params["limit"] = limit
with self.get_connection() as connection:
with connection.cursor() as cursor:
cursor.execute(query, params)
results = cursor.fetchall()
if not results:
log.info("No results found for query.")
return None
ids = [[row[0] for row in results]]
documents = [
[
row[1].read() if isinstance(row[1], oracledb.LOB) else str(row[1])
for row in results
]
]
# 🔧 FIXED: Parse JSON metadata properly
metadatas = [
[
self._json_to_metadata(
row[2].read() if isinstance(row[2], oracledb.LOB) else row[2]
)
for row in results
]
]
log.info(f"Query completed. Found {len(results)} results.")
return GetResult(ids=ids, documents=documents, metadatas=metadatas)
except Exception as e:
log.exception(f"Error during query: {e}")
return None
def get(self, collection_name: str) -> Optional[GetResult]:
"""
Get all items in a collection.
Retrieves items from a specified collection up to the limit.
Args:
collection_name (str): Name of the collection to retrieve
limit (Optional[int]): Maximum number of items to retrieve
Returns:
Optional[GetResult]: Result containing ids, documents, and metadata
Example:
>>> client = Oracle23aiClient()
>>> results = client.get("my_collection", limit=50)
>>> if results:
... print(f"Retrieved {len(results.ids[0])} documents from collection")
"""
log.info(
f"Getting items from collection '{collection_name}' with limit {limit}."
)
try:
limit = 1000 # Hardcoded limit for get operation
with self.get_connection() as connection:
with connection.cursor() as cursor:
cursor.execute(
"""
SELECT /*+ MONITOR */ id, text, JSON_SERIALIZE(vmetadata RETURNING VARCHAR2(4096)) as vmetadata
FROM document_chunk
WHERE collection_name = :collection_name
FETCH FIRST :limit ROWS ONLY
""",
{"collection_name": collection_name, "limit": limit},
)
results = cursor.fetchall()
if not results:
log.info("No results found.")
return None
ids = [[row[0] for row in results]]
documents = [
[
row[1].read() if isinstance(row[1], oracledb.LOB) else str(row[1])
for row in results
]
]
# 🔧 FIXED: Parse JSON metadata properly
metadatas = [
[
self._json_to_metadata(
row[2].read() if isinstance(row[2], oracledb.LOB) else row[2]
)
for row in results
]
]
return GetResult(ids=ids, documents=documents, metadatas=metadatas)
except Exception as e:
log.exception(f"Error during get: {e}")
return None
def delete(
self,
collection_name: str,
ids: Optional[List[str]] = None,
filter: Optional[Dict[str, Any]] = None,
) -> None:
"""
Delete items from the database.
Deletes items from a collection based on IDs or metadata filters.
Args:
collection_name (str): Name of the collection to delete from
ids (Optional[List[str]]): Specific item IDs to delete
filter (Optional[Dict[str, Any]]): Metadata filters for deletion
Raises:
Exception: If deletion fails
Example:
>>> client = Oracle23aiClient()
>>> # Delete specific items by ID
>>> client.delete("my_collection", ids=["1", "3", "5"])
>>> # Or delete by metadata filter
>>> client.delete("my_collection", filter={"source": "deprecated_source"})
"""
log.info(f"Deleting items from collection '{collection_name}'.")
try:
query = (
"DELETE FROM document_chunk WHERE collection_name = :collection_name"
)
params = {"collection_name": collection_name}
if ids:
# 🔧 FIXED: Use proper parameterized query to prevent SQL injection
placeholders = ",".join([f":id_{i}" for i in range(len(ids))])
query += f" AND id IN ({placeholders})"
for i, id_val in enumerate(ids):
params[f"id_{i}"] = id_val
if filter:
for i, (key, value) in enumerate(filter.items()):
param_name = f"value_{i}"
query += f" AND JSON_VALUE(vmetadata, '$.{key}' RETURNING VARCHAR2(4096)) = :{param_name}"
params[param_name] = str(value)
with self.get_connection() as connection:
with connection.cursor() as cursor:
cursor.execute(query, params)
deleted = cursor.rowcount
connection.commit()
log.info(f"Deleted {deleted} items from collection '{collection_name}'.")
except Exception as e:
log.exception(f"Error during delete: {e}")
raise
def reset(self) -> None:
"""
Reset the database by deleting all items.
Deletes all items from the document_chunk table.
Raises:
Exception: If reset fails
Example:
>>> client = Oracle23aiClient()
>>> client.reset() # Warning: Removes all data!
"""
log.info("Resetting database - deleting all items.")
try:
with self.get_connection() as connection:
with connection.cursor() as cursor:
cursor.execute("DELETE FROM document_chunk")
deleted = cursor.rowcount
connection.commit()
log.info(
f"Reset complete. Deleted {deleted} items from 'document_chunk' table."
)
except Exception as e:
log.exception(f"Error during reset: {e}")
raise
def close(self) -> None:
"""
Close the database connection pool.
Properly closes the connection pool and releases all resources.
Example:
>>> client = Oracle23aiClient()
>>> # After finishing all operations
>>> client.close()
"""
try:
if hasattr(self, "pool") and self.pool:
self.pool.close()
log.info("Oracle Vector Search connection pool closed.")
except Exception as e:
log.exception(f"Error closing connection pool: {e}")
def has_collection(self, collection_name: str) -> bool:
"""
Check if a collection exists.
Args:
collection_name (str): Name of the collection to check
Returns:
bool: True if the collection exists, False otherwise
Example:
>>> client = Oracle23aiClient()
>>> if client.has_collection("my_collection"):
... print("Collection exists!")
... else:
... print("Collection does not exist.")
"""
try:
with self.get_connection() as connection:
with connection.cursor() as cursor:
cursor.execute(
"""
SELECT COUNT(*)
FROM document_chunk
WHERE collection_name = :collection_name
FETCH FIRST 1 ROWS ONLY
""",
{"collection_name": collection_name},
)
count = cursor.fetchone()[0]
return count > 0
except Exception as e:
log.exception(f"Error checking collection existence: {e}")
return False
def delete_collection(self, collection_name: str) -> None:
"""
Delete an entire collection.
Removes all items belonging to the specified collection.
Args:
collection_name (str): Name of the collection to delete
Example:
>>> client = Oracle23aiClient()
>>> client.delete_collection("obsolete_collection")
"""
log.info(f"Deleting collection '{collection_name}'.")
try:
with self.get_connection() as connection:
with connection.cursor() as cursor:
cursor.execute(
"""
DELETE FROM document_chunk
WHERE collection_name = :collection_name
""",
{"collection_name": collection_name},
)
deleted = cursor.rowcount
connection.commit()
log.info(
f"Collection '{collection_name}' deleted. Removed {deleted} items."
)
except Exception as e:
log.exception(f"Error deleting collection '{collection_name}': {e}")
raise
@@ -1,12 +1,16 @@
from typing import Optional, List, Dict, Any
from typing import Optional, List, Dict, Any, Tuple
import logging
import json
from sqlalchemy import (
func,
literal,
cast,
column,
create_engine,
Column,
Integer,
MetaData,
LargeBinary,
select,
text,
Text,
@@ -14,37 +18,76 @@ from sqlalchemy import (
values,
)
from sqlalchemy.sql import true
from sqlalchemy.pool import NullPool
from sqlalchemy.pool import NullPool, QueuePool
from sqlalchemy.orm import declarative_base, scoped_session, sessionmaker
from sqlalchemy.dialects.postgresql import JSONB, array
from pgvector.sqlalchemy import Vector
from pgvector.sqlalchemy import Vector, HALFVEC
from sqlalchemy.ext.mutable import MutableDict
from sqlalchemy.exc import NoSuchTableError
from open_webui.retrieval.vector.main import VectorItem, SearchResult, GetResult
from open_webui.config import PGVECTOR_DB_URL, PGVECTOR_INITIALIZE_MAX_VECTOR_LENGTH
from open_webui.retrieval.vector.utils import process_metadata
from open_webui.retrieval.vector.main import (
VectorDBBase,
VectorItem,
SearchResult,
GetResult,
)
from open_webui.config import (
PGVECTOR_DB_URL,
PGVECTOR_INITIALIZE_MAX_VECTOR_LENGTH,
PGVECTOR_CREATE_EXTENSION,
PGVECTOR_PGCRYPTO,
PGVECTOR_PGCRYPTO_KEY,
PGVECTOR_POOL_SIZE,
PGVECTOR_POOL_MAX_OVERFLOW,
PGVECTOR_POOL_TIMEOUT,
PGVECTOR_POOL_RECYCLE,
PGVECTOR_INDEX_METHOD,
PGVECTOR_HNSW_M,
PGVECTOR_HNSW_EF_CONSTRUCTION,
PGVECTOR_IVFFLAT_LISTS,
PGVECTOR_USE_HALFVEC,
)
from open_webui.env import SRC_LOG_LEVELS
VECTOR_LENGTH = PGVECTOR_INITIALIZE_MAX_VECTOR_LENGTH
USE_HALFVEC = PGVECTOR_USE_HALFVEC
VECTOR_TYPE_FACTORY = HALFVEC if USE_HALFVEC else Vector
VECTOR_OPCLASS = "halfvec_cosine_ops" if USE_HALFVEC else "vector_cosine_ops"
Base = declarative_base()
log = logging.getLogger(__name__)
log.setLevel(SRC_LOG_LEVELS["RAG"])
def pgcrypto_encrypt(val, key):
return func.pgp_sym_encrypt(val, literal(key))
def pgcrypto_decrypt(col, key, outtype="text"):
return func.cast(func.pgp_sym_decrypt(col, literal(key)), outtype)
class DocumentChunk(Base):
__tablename__ = "document_chunk"
id = Column(Text, primary_key=True)
vector = Column(Vector(dim=VECTOR_LENGTH), nullable=True)
vector = Column(VECTOR_TYPE_FACTORY(dim=VECTOR_LENGTH), nullable=True)
collection_name = Column(Text, nullable=False)
text = Column(Text, nullable=True)
vmetadata = Column(MutableDict.as_mutable(JSONB), nullable=True)
if PGVECTOR_PGCRYPTO:
text = Column(LargeBinary, nullable=True)
vmetadata = Column(LargeBinary, nullable=True)
else:
text = Column(Text, nullable=True)
vmetadata = Column(MutableDict.as_mutable(JSONB), nullable=True)
class PgvectorClient:
class PgvectorClient(VectorDBBase):
def __init__(self) -> None:
# if no pgvector uri, use the existing database connection
@@ -53,9 +96,24 @@ class PgvectorClient:
self.session = Session
else:
engine = create_engine(
PGVECTOR_DB_URL, pool_pre_ping=True, poolclass=NullPool
)
if isinstance(PGVECTOR_POOL_SIZE, int):
if PGVECTOR_POOL_SIZE > 0:
engine = create_engine(
PGVECTOR_DB_URL,
pool_size=PGVECTOR_POOL_SIZE,
max_overflow=PGVECTOR_POOL_MAX_OVERFLOW,
pool_timeout=PGVECTOR_POOL_TIMEOUT,
pool_recycle=PGVECTOR_POOL_RECYCLE,
pool_pre_ping=True,
poolclass=QueuePool,
)
else:
engine = create_engine(
PGVECTOR_DB_URL, pool_pre_ping=True, poolclass=NullPool
)
else:
engine = create_engine(PGVECTOR_DB_URL, pool_pre_ping=True)
SessionLocal = sessionmaker(
autocommit=False, autoflush=False, bind=engine, expire_on_commit=False
)
@@ -63,7 +121,41 @@ class PgvectorClient:
try:
# Ensure the pgvector extension is available
self.session.execute(text("CREATE EXTENSION IF NOT EXISTS vector;"))
# Use a conditional check to avoid permission issues on Azure PostgreSQL
if PGVECTOR_CREATE_EXTENSION:
self.session.execute(
text(
"""
DO $$
BEGIN
IF NOT EXISTS (SELECT 1 FROM pg_extension WHERE extname = 'vector') THEN
CREATE EXTENSION IF NOT EXISTS vector;
END IF;
END $$;
"""
)
)
if PGVECTOR_PGCRYPTO:
# Ensure the pgcrypto extension is available for encryption
# Use a conditional check to avoid permission issues on Azure PostgreSQL
self.session.execute(
text(
"""
DO $$
BEGIN
IF NOT EXISTS (SELECT 1 FROM pg_extension WHERE extname = 'pgcrypto') THEN
CREATE EXTENSION IF NOT EXISTS pgcrypto;
END IF;
END $$;
"""
)
)
if not PGVECTOR_PGCRYPTO_KEY:
raise ValueError(
"PGVECTOR_PGCRYPTO_KEY must be set when PGVECTOR_PGCRYPTO is enabled."
)
# Check vector length consistency
self.check_vector_length()
@@ -74,13 +166,9 @@ class PgvectorClient:
connection = self.session.connection()
Base.metadata.create_all(bind=connection)
# Create an index on the vector column if it doesn't exist
self.session.execute(
text(
"CREATE INDEX IF NOT EXISTS idx_document_chunk_vector "
"ON document_chunk USING ivfflat (vector vector_cosine_ops) WITH (lists = 100);"
)
)
index_method, index_options = self._vector_index_configuration()
self._ensure_vector_index(index_method, index_options)
self.session.execute(
text(
"CREATE INDEX IF NOT EXISTS idx_document_chunk_collection_name "
@@ -94,6 +182,78 @@ class PgvectorClient:
log.exception(f"Error during initialization: {e}")
raise
@staticmethod
def _extract_index_method(index_def: Optional[str]) -> Optional[str]:
if not index_def:
return None
try:
after_using = index_def.lower().split("using ", 1)[1]
return after_using.split()[0]
except (IndexError, AttributeError):
return None
def _vector_index_configuration(self) -> Tuple[str, str]:
if PGVECTOR_INDEX_METHOD:
index_method = PGVECTOR_INDEX_METHOD
log.info(
"Using vector index method '%s' from PGVECTOR_INDEX_METHOD.",
index_method,
)
elif USE_HALFVEC:
index_method = "hnsw"
log.info(
"VECTOR_LENGTH=%s exceeds 2000; using halfvec column type with hnsw index.",
VECTOR_LENGTH,
)
else:
index_method = "ivfflat"
if index_method == "hnsw":
index_options = f"WITH (m = {PGVECTOR_HNSW_M}, ef_construction = {PGVECTOR_HNSW_EF_CONSTRUCTION})"
else:
index_options = f"WITH (lists = {PGVECTOR_IVFFLAT_LISTS})"
return index_method, index_options
def _ensure_vector_index(self, index_method: str, index_options: str) -> None:
index_name = "idx_document_chunk_vector"
existing_index_def = self.session.execute(
text(
"""
SELECT indexdef
FROM pg_indexes
WHERE schemaname = current_schema()
AND tablename = 'document_chunk'
AND indexname = :index_name
"""
),
{"index_name": index_name},
).scalar()
existing_method = self._extract_index_method(existing_index_def)
if existing_method and existing_method != index_method:
raise RuntimeError(
f"Existing pgvector index '{index_name}' uses method '{existing_method}' but configuration now "
f"requires '{index_method}'. Automatic rebuild is disabled to prevent long-running maintenance. "
"Drop the index manually (optionally after tuning maintenance_work_mem/max_parallel_maintenance_workers) "
"and recreate it with the new method before restarting Open WebUI."
)
if not existing_index_def:
index_sql = (
f"CREATE INDEX IF NOT EXISTS {index_name} "
f"ON document_chunk USING {index_method} (vector {VECTOR_OPCLASS})"
)
if index_options:
index_sql = f"{index_sql} {index_options}"
self.session.execute(text(index_sql))
log.info(
"Ensured vector index '%s' using %s%s.",
index_name,
index_method,
f" {index_options}" if index_options else "",
)
def check_vector_length(self) -> None:
"""
Check if the VECTOR_LENGTH matches the existing vector column dimension in the database.
@@ -113,16 +273,19 @@ class PgvectorClient:
if "vector" in document_chunk_table.columns:
vector_column = document_chunk_table.columns["vector"]
vector_type = vector_column.type
if isinstance(vector_type, Vector):
db_vector_length = vector_type.dim
if db_vector_length != VECTOR_LENGTH:
raise Exception(
f"VECTOR_LENGTH {VECTOR_LENGTH} does not match existing vector column dimension {db_vector_length}. "
"Cannot change vector size after initialization without migrating the data."
)
else:
expected_type = HALFVEC if USE_HALFVEC else Vector
if not isinstance(vector_type, expected_type):
raise Exception(
"The 'vector' column exists but is not of type 'Vector'."
"The 'vector' column type does not match the expected type "
f"('{expected_type.__name__}') for VECTOR_LENGTH {VECTOR_LENGTH}."
)
db_vector_length = getattr(vector_type, "dim", None)
if db_vector_length is not None and db_vector_length != VECTOR_LENGTH:
raise Exception(
f"VECTOR_LENGTH {VECTOR_LENGTH} does not match existing vector column dimension {db_vector_length}. "
"Cannot change vector size after initialization without migrating the data."
)
else:
raise Exception(
@@ -136,29 +299,60 @@ class PgvectorClient:
# Pad the vector with zeros
vector += [0.0] * (VECTOR_LENGTH - current_length)
elif current_length > VECTOR_LENGTH:
raise Exception(
f"Vector length {current_length} not supported. Max length must be <= {VECTOR_LENGTH}"
)
# Truncate the vector to VECTOR_LENGTH
vector = vector[:VECTOR_LENGTH]
return vector
def insert(self, collection_name: str, items: List[VectorItem]) -> None:
try:
new_items = []
for item in items:
vector = self.adjust_vector_length(item["vector"])
new_chunk = DocumentChunk(
id=item["id"],
vector=vector,
collection_name=collection_name,
text=item["text"],
vmetadata=item["metadata"],
if PGVECTOR_PGCRYPTO:
for item in items:
vector = self.adjust_vector_length(item["vector"])
# Use raw SQL for BYTEA/pgcrypto
# Ensure metadata is converted to its JSON text representation
json_metadata = json.dumps(item["metadata"])
self.session.execute(
text(
"""
INSERT INTO document_chunk
(id, vector, collection_name, text, vmetadata)
VALUES (
:id, :vector, :collection_name,
pgp_sym_encrypt(:text, :key),
pgp_sym_encrypt(:metadata_text, :key)
)
ON CONFLICT (id) DO NOTHING
"""
),
{
"id": item["id"],
"vector": vector,
"collection_name": collection_name,
"text": item["text"],
"metadata_text": json_metadata,
"key": PGVECTOR_PGCRYPTO_KEY,
},
)
self.session.commit()
log.info(f"Encrypted & inserted {len(items)} into '{collection_name}'")
else:
new_items = []
for item in items:
vector = self.adjust_vector_length(item["vector"])
new_chunk = DocumentChunk(
id=item["id"],
vector=vector,
collection_name=collection_name,
text=item["text"],
vmetadata=process_metadata(item["metadata"]),
)
new_items.append(new_chunk)
self.session.bulk_save_objects(new_items)
self.session.commit()
log.info(
f"Inserted {len(new_items)} items into collection '{collection_name}'."
)
new_items.append(new_chunk)
self.session.bulk_save_objects(new_items)
self.session.commit()
log.info(
f"Inserted {len(new_items)} items into collection '{collection_name}'."
)
except Exception as e:
self.session.rollback()
log.exception(f"Error during insert: {e}")
@@ -166,33 +360,66 @@ class PgvectorClient:
def upsert(self, collection_name: str, items: List[VectorItem]) -> None:
try:
for item in items:
vector = self.adjust_vector_length(item["vector"])
existing = (
self.session.query(DocumentChunk)
.filter(DocumentChunk.id == item["id"])
.first()
if PGVECTOR_PGCRYPTO:
for item in items:
vector = self.adjust_vector_length(item["vector"])
json_metadata = json.dumps(item["metadata"])
self.session.execute(
text(
"""
INSERT INTO document_chunk
(id, vector, collection_name, text, vmetadata)
VALUES (
:id, :vector, :collection_name,
pgp_sym_encrypt(:text, :key),
pgp_sym_encrypt(:metadata_text, :key)
)
ON CONFLICT (id) DO UPDATE SET
vector = EXCLUDED.vector,
collection_name = EXCLUDED.collection_name,
text = EXCLUDED.text,
vmetadata = EXCLUDED.vmetadata
"""
),
{
"id": item["id"],
"vector": vector,
"collection_name": collection_name,
"text": item["text"],
"metadata_text": json_metadata,
"key": PGVECTOR_PGCRYPTO_KEY,
},
)
self.session.commit()
log.info(f"Encrypted & upserted {len(items)} into '{collection_name}'")
else:
for item in items:
vector = self.adjust_vector_length(item["vector"])
existing = (
self.session.query(DocumentChunk)
.filter(DocumentChunk.id == item["id"])
.first()
)
if existing:
existing.vector = vector
existing.text = item["text"]
existing.vmetadata = process_metadata(item["metadata"])
existing.collection_name = (
collection_name # Update collection_name if necessary
)
else:
new_chunk = DocumentChunk(
id=item["id"],
vector=vector,
collection_name=collection_name,
text=item["text"],
vmetadata=process_metadata(item["metadata"]),
)
self.session.add(new_chunk)
self.session.commit()
log.info(
f"Upserted {len(items)} items into collection '{collection_name}'."
)
if existing:
existing.vector = vector
existing.text = item["text"]
existing.vmetadata = item["metadata"]
existing.collection_name = (
collection_name # Update collection_name if necessary
)
else:
new_chunk = DocumentChunk(
id=item["id"],
vector=vector,
collection_name=collection_name,
text=item["text"],
vmetadata=item["metadata"],
)
self.session.add(new_chunk)
self.session.commit()
log.info(
f"Upserted {len(items)} items into collection '{collection_name}'."
)
except Exception as e:
self.session.rollback()
log.exception(f"Error during upsert: {e}")
@@ -213,11 +440,11 @@ class PgvectorClient:
num_queries = len(vectors)
def vector_expr(vector):
return cast(array(vector), Vector(VECTOR_LENGTH))
return cast(array(vector), VECTOR_TYPE_FACTORY(VECTOR_LENGTH))
# Create the values for query vectors
qid_col = column("qid", Integer)
q_vector_col = column("q_vector", Vector(VECTOR_LENGTH))
q_vector_col = column("q_vector", VECTOR_TYPE_FACTORY(VECTOR_LENGTH))
query_vectors = (
values(qid_col, q_vector_col)
.data(
@@ -226,16 +453,32 @@ class PgvectorClient:
.alias("query_vectors")
)
result_fields = [
DocumentChunk.id,
]
if PGVECTOR_PGCRYPTO:
result_fields.append(
pgcrypto_decrypt(
DocumentChunk.text, PGVECTOR_PGCRYPTO_KEY, Text
).label("text")
)
result_fields.append(
pgcrypto_decrypt(
DocumentChunk.vmetadata, PGVECTOR_PGCRYPTO_KEY, JSONB
).label("vmetadata")
)
else:
result_fields.append(DocumentChunk.text)
result_fields.append(DocumentChunk.vmetadata)
result_fields.append(
(DocumentChunk.vector.cosine_distance(query_vectors.c.q_vector)).label(
"distance"
)
)
# Build the lateral subquery for each query vector
subq = (
select(
DocumentChunk.id,
DocumentChunk.text,
DocumentChunk.vmetadata,
(
DocumentChunk.vector.cosine_distance(query_vectors.c.q_vector)
).label("distance"),
)
select(*result_fields)
.where(DocumentChunk.collection_name == collection_name)
.order_by(
(DocumentChunk.vector.cosine_distance(query_vectors.c.q_vector))
@@ -284,10 +527,12 @@ class PgvectorClient:
documents[qid].append(row.text)
metadatas[qid].append(row.vmetadata)
self.session.rollback() # read-only transaction
return SearchResult(
ids=ids, distances=distances, documents=documents, metadatas=metadatas
)
except Exception as e:
self.session.rollback()
log.exception(f"Error during search: {e}")
return None
@@ -295,17 +540,43 @@ class PgvectorClient:
self, collection_name: str, filter: Dict[str, Any], limit: Optional[int] = None
) -> Optional[GetResult]:
try:
query = self.session.query(DocumentChunk).filter(
DocumentChunk.collection_name == collection_name
)
if PGVECTOR_PGCRYPTO:
# Build where clause for vmetadata filter
where_clauses = [DocumentChunk.collection_name == collection_name]
for key, value in filter.items():
# decrypt then check key: JSON filter after decryption
where_clauses.append(
pgcrypto_decrypt(
DocumentChunk.vmetadata, PGVECTOR_PGCRYPTO_KEY, JSONB
)[key].astext
== str(value)
)
stmt = select(
DocumentChunk.id,
pgcrypto_decrypt(
DocumentChunk.text, PGVECTOR_PGCRYPTO_KEY, Text
).label("text"),
pgcrypto_decrypt(
DocumentChunk.vmetadata, PGVECTOR_PGCRYPTO_KEY, JSONB
).label("vmetadata"),
).where(*where_clauses)
if limit is not None:
stmt = stmt.limit(limit)
results = self.session.execute(stmt).all()
else:
query = self.session.query(DocumentChunk).filter(
DocumentChunk.collection_name == collection_name
)
for key, value in filter.items():
query = query.filter(DocumentChunk.vmetadata[key].astext == str(value))
for key, value in filter.items():
query = query.filter(
DocumentChunk.vmetadata[key].astext == str(value)
)
if limit is not None:
query = query.limit(limit)
if limit is not None:
query = query.limit(limit)
results = query.all()
results = query.all()
if not results:
return None
@@ -314,12 +585,14 @@ class PgvectorClient:
documents = [[result.text for result in results]]
metadatas = [[result.vmetadata for result in results]]
self.session.rollback() # read-only transaction
return GetResult(
ids=ids,
documents=documents,
metadatas=metadatas,
)
except Exception as e:
self.session.rollback()
log.exception(f"Error during query: {e}")
return None
@@ -327,23 +600,43 @@ class PgvectorClient:
self, collection_name: str, limit: Optional[int] = None
) -> Optional[GetResult]:
try:
query = self.session.query(DocumentChunk).filter(
DocumentChunk.collection_name == collection_name
)
if limit is not None:
query = query.limit(limit)
if PGVECTOR_PGCRYPTO:
stmt = select(
DocumentChunk.id,
pgcrypto_decrypt(
DocumentChunk.text, PGVECTOR_PGCRYPTO_KEY, Text
).label("text"),
pgcrypto_decrypt(
DocumentChunk.vmetadata, PGVECTOR_PGCRYPTO_KEY, JSONB
).label("vmetadata"),
).where(DocumentChunk.collection_name == collection_name)
if limit is not None:
stmt = stmt.limit(limit)
results = self.session.execute(stmt).all()
ids = [[row.id for row in results]]
documents = [[row.text for row in results]]
metadatas = [[row.vmetadata for row in results]]
else:
results = query.all()
query = self.session.query(DocumentChunk).filter(
DocumentChunk.collection_name == collection_name
)
if limit is not None:
query = query.limit(limit)
if not results:
return None
results = query.all()
ids = [[result.id for result in results]]
documents = [[result.text for result in results]]
metadatas = [[result.vmetadata for result in results]]
if not results:
return None
ids = [[result.id for result in results]]
documents = [[result.text for result in results]]
metadatas = [[result.vmetadata for result in results]]
self.session.rollback() # read-only transaction
return GetResult(ids=ids, documents=documents, metadatas=metadatas)
except Exception as e:
self.session.rollback()
log.exception(f"Error during get: {e}")
return None
@@ -354,17 +647,33 @@ class PgvectorClient:
filter: Optional[Dict[str, Any]] = None,
) -> None:
try:
query = self.session.query(DocumentChunk).filter(
DocumentChunk.collection_name == collection_name
)
if ids:
query = query.filter(DocumentChunk.id.in_(ids))
if filter:
for key, value in filter.items():
query = query.filter(
DocumentChunk.vmetadata[key].astext == str(value)
)
deleted = query.delete(synchronize_session=False)
if PGVECTOR_PGCRYPTO:
wheres = [DocumentChunk.collection_name == collection_name]
if ids:
wheres.append(DocumentChunk.id.in_(ids))
if filter:
for key, value in filter.items():
wheres.append(
pgcrypto_decrypt(
DocumentChunk.vmetadata, PGVECTOR_PGCRYPTO_KEY, JSONB
)[key].astext
== str(value)
)
stmt = DocumentChunk.__table__.delete().where(*wheres)
result = self.session.execute(stmt)
deleted = result.rowcount
else:
query = self.session.query(DocumentChunk).filter(
DocumentChunk.collection_name == collection_name
)
if ids:
query = query.filter(DocumentChunk.id.in_(ids))
if filter:
for key, value in filter.items():
query = query.filter(
DocumentChunk.vmetadata[key].astext == str(value)
)
deleted = query.delete(synchronize_session=False)
self.session.commit()
log.info(f"Deleted {deleted} items from collection '{collection_name}'.")
except Exception as e:
@@ -395,8 +704,10 @@ class PgvectorClient:
.first()
is not None
)
self.session.rollback() # read-only transaction
return exists
except Exception as e:
self.session.rollback()
log.exception(f"Error checking collection existence: {e}")
return False
@@ -0,0 +1,583 @@
from typing import Optional, List, Dict, Any, Union
import logging
import time # for measuring elapsed time
from pinecone import Pinecone, ServerlessSpec
# Add gRPC support for better performance (Pinecone best practice)
try:
from pinecone.grpc import PineconeGRPC
GRPC_AVAILABLE = True
except ImportError:
GRPC_AVAILABLE = False
import asyncio # for async upserts
import functools # for partial binding in async tasks
import concurrent.futures # for parallel batch upserts
import random # for jitter in retry backoff
from open_webui.retrieval.vector.main import (
VectorDBBase,
VectorItem,
SearchResult,
GetResult,
)
from open_webui.config import (
PINECONE_API_KEY,
PINECONE_ENVIRONMENT,
PINECONE_INDEX_NAME,
PINECONE_DIMENSION,
PINECONE_METRIC,
PINECONE_CLOUD,
)
from open_webui.env import SRC_LOG_LEVELS
from open_webui.retrieval.vector.utils import process_metadata
NO_LIMIT = 10000 # Reasonable limit to avoid overwhelming the system
BATCH_SIZE = 100 # Recommended batch size for Pinecone operations
log = logging.getLogger(__name__)
log.setLevel(SRC_LOG_LEVELS["RAG"])
class PineconeClient(VectorDBBase):
def __init__(self):
self.collection_prefix = "open-webui"
# Validate required configuration
self._validate_config()
# Store configuration values
self.api_key = PINECONE_API_KEY
self.environment = PINECONE_ENVIRONMENT
self.index_name = PINECONE_INDEX_NAME
self.dimension = PINECONE_DIMENSION
self.metric = PINECONE_METRIC
self.cloud = PINECONE_CLOUD
# Initialize Pinecone client for improved performance
if GRPC_AVAILABLE:
# Use gRPC client for better performance (Pinecone recommendation)
self.client = PineconeGRPC(
api_key=self.api_key,
pool_threads=20, # Improved connection pool size
timeout=30, # Reasonable timeout for operations
)
self.using_grpc = True
log.info("Using Pinecone gRPC client for optimal performance")
else:
# Fallback to HTTP client with enhanced connection pooling
self.client = Pinecone(
api_key=self.api_key,
pool_threads=20, # Improved connection pool size
timeout=30, # Reasonable timeout for operations
)
self.using_grpc = False
log.info("Using Pinecone HTTP client (gRPC not available)")
# Persistent executor for batch operations
self._executor = concurrent.futures.ThreadPoolExecutor(max_workers=5)
# Create index if it doesn't exist
self._initialize_index()
def _validate_config(self) -> None:
"""Validate that all required configuration variables are set."""
missing_vars = []
if not PINECONE_API_KEY:
missing_vars.append("PINECONE_API_KEY")
if not PINECONE_ENVIRONMENT:
missing_vars.append("PINECONE_ENVIRONMENT")
if not PINECONE_INDEX_NAME:
missing_vars.append("PINECONE_INDEX_NAME")
if not PINECONE_DIMENSION:
missing_vars.append("PINECONE_DIMENSION")
if not PINECONE_CLOUD:
missing_vars.append("PINECONE_CLOUD")
if missing_vars:
raise ValueError(
f"Required configuration missing: {', '.join(missing_vars)}"
)
def _initialize_index(self) -> None:
"""Initialize the Pinecone index."""
try:
# Check if index exists
if self.index_name not in self.client.list_indexes().names():
log.info(f"Creating Pinecone index '{self.index_name}'...")
self.client.create_index(
name=self.index_name,
dimension=self.dimension,
metric=self.metric,
spec=ServerlessSpec(cloud=self.cloud, region=self.environment),
)
log.info(f"Successfully created Pinecone index '{self.index_name}'")
else:
log.info(f"Using existing Pinecone index '{self.index_name}'")
# Connect to the index
self.index = self.client.Index(
self.index_name,
pool_threads=20, # Enhanced connection pool for index operations
)
except Exception as e:
log.error(f"Failed to initialize Pinecone index: {e}")
raise RuntimeError(f"Failed to initialize Pinecone index: {e}")
def _retry_pinecone_operation(self, operation_func, max_retries=3):
"""Retry Pinecone operations with exponential backoff for rate limits and network issues."""
for attempt in range(max_retries):
try:
return operation_func()
except Exception as e:
error_str = str(e).lower()
# Check if it's a retryable error (rate limits, network issues, timeouts)
is_retryable = any(
keyword in error_str
for keyword in [
"rate limit",
"quota",
"timeout",
"network",
"connection",
"unavailable",
"internal error",
"429",
"500",
"502",
"503",
"504",
]
)
if not is_retryable or attempt == max_retries - 1:
# Don't retry for non-retryable errors or on final attempt
raise
# Exponential backoff with jitter
delay = (2**attempt) + random.uniform(0, 1)
log.warning(
f"Pinecone operation failed (attempt {attempt + 1}/{max_retries}), "
f"retrying in {delay:.2f}s: {e}"
)
time.sleep(delay)
def _create_points(
self, items: List[VectorItem], collection_name_with_prefix: str
) -> List[Dict[str, Any]]:
"""Convert VectorItem objects to Pinecone point format."""
points = []
for item in items:
# Start with any existing metadata or an empty dict
metadata = item.get("metadata", {}).copy() if item.get("metadata") else {}
# Add text to metadata if available
if "text" in item:
metadata["text"] = item["text"]
# Always add collection_name to metadata for filtering
metadata["collection_name"] = collection_name_with_prefix
point = {
"id": item["id"],
"values": item["vector"],
"metadata": process_metadata(metadata),
}
points.append(point)
return points
def _get_collection_name_with_prefix(self, collection_name: str) -> str:
"""Get the collection name with prefix."""
return f"{self.collection_prefix}_{collection_name}"
def _normalize_distance(self, score: float) -> float:
"""Normalize distance score based on the metric used."""
if self.metric.lower() == "cosine":
# Cosine similarity ranges from -1 to 1, normalize to 0 to 1
return (score + 1.0) / 2.0
elif self.metric.lower() in ["euclidean", "dotproduct"]:
# These are already suitable for ranking (smaller is better for Euclidean)
return score
else:
# For other metrics, use as is
return score
def _result_to_get_result(self, matches: list) -> GetResult:
"""Convert Pinecone matches to GetResult format."""
ids = []
documents = []
metadatas = []
for match in matches:
metadata = getattr(match, "metadata", {}) or {}
ids.append(match.id if hasattr(match, "id") else match["id"])
documents.append(metadata.get("text", ""))
metadatas.append(metadata)
return GetResult(
**{
"ids": [ids],
"documents": [documents],
"metadatas": [metadatas],
}
)
def has_collection(self, collection_name: str) -> bool:
"""Check if a collection exists by searching for at least one item."""
collection_name_with_prefix = self._get_collection_name_with_prefix(
collection_name
)
try:
# Search for at least 1 item with this collection name in metadata
response = self.index.query(
vector=[0.0] * self.dimension, # dummy vector
top_k=1,
filter={"collection_name": collection_name_with_prefix},
include_metadata=False,
)
matches = getattr(response, "matches", []) or []
return len(matches) > 0
except Exception as e:
log.exception(
f"Error checking collection '{collection_name_with_prefix}': {e}"
)
return False
def delete_collection(self, collection_name: str) -> None:
"""Delete a collection by removing all vectors with the collection name in metadata."""
collection_name_with_prefix = self._get_collection_name_with_prefix(
collection_name
)
try:
self.index.delete(filter={"collection_name": collection_name_with_prefix})
log.info(
f"Collection '{collection_name_with_prefix}' deleted (all vectors removed)."
)
except Exception as e:
log.warning(
f"Failed to delete collection '{collection_name_with_prefix}': {e}"
)
raise
def insert(self, collection_name: str, items: List[VectorItem]) -> None:
"""Insert vectors into a collection."""
if not items:
log.warning("No items to insert")
return
start_time = time.time()
collection_name_with_prefix = self._get_collection_name_with_prefix(
collection_name
)
points = self._create_points(items, collection_name_with_prefix)
# Parallelize batch inserts for performance
executor = self._executor
futures = []
for i in range(0, len(points), BATCH_SIZE):
batch = points[i : i + BATCH_SIZE]
futures.append(executor.submit(self.index.upsert, vectors=batch))
for future in concurrent.futures.as_completed(futures):
try:
future.result()
except Exception as e:
log.error(f"Error inserting batch: {e}")
raise
elapsed = time.time() - start_time
log.debug(f"Insert of {len(points)} vectors took {elapsed:.2f} seconds")
log.info(
f"Successfully inserted {len(points)} vectors in parallel batches "
f"into '{collection_name_with_prefix}'"
)
def upsert(self, collection_name: str, items: List[VectorItem]) -> None:
"""Upsert (insert or update) vectors into a collection."""
if not items:
log.warning("No items to upsert")
return
start_time = time.time()
collection_name_with_prefix = self._get_collection_name_with_prefix(
collection_name
)
points = self._create_points(items, collection_name_with_prefix)
# Parallelize batch upserts for performance
executor = self._executor
futures = []
for i in range(0, len(points), BATCH_SIZE):
batch = points[i : i + BATCH_SIZE]
futures.append(executor.submit(self.index.upsert, vectors=batch))
for future in concurrent.futures.as_completed(futures):
try:
future.result()
except Exception as e:
log.error(f"Error upserting batch: {e}")
raise
elapsed = time.time() - start_time
log.debug(f"Upsert of {len(points)} vectors took {elapsed:.2f} seconds")
log.info(
f"Successfully upserted {len(points)} vectors in parallel batches "
f"into '{collection_name_with_prefix}'"
)
async def insert_async(self, collection_name: str, items: List[VectorItem]) -> None:
"""Async version of insert using asyncio and run_in_executor for improved performance."""
if not items:
log.warning("No items to insert")
return
collection_name_with_prefix = self._get_collection_name_with_prefix(
collection_name
)
points = self._create_points(items, collection_name_with_prefix)
# Create batches
batches = [
points[i : i + BATCH_SIZE] for i in range(0, len(points), BATCH_SIZE)
]
loop = asyncio.get_event_loop()
tasks = [
loop.run_in_executor(
None, functools.partial(self.index.upsert, vectors=batch)
)
for batch in batches
]
results = await asyncio.gather(*tasks, return_exceptions=True)
for result in results:
if isinstance(result, Exception):
log.error(f"Error in async insert batch: {result}")
raise result
log.info(
f"Successfully async inserted {len(points)} vectors in batches "
f"into '{collection_name_with_prefix}'"
)
async def upsert_async(self, collection_name: str, items: List[VectorItem]) -> None:
"""Async version of upsert using asyncio and run_in_executor for improved performance."""
if not items:
log.warning("No items to upsert")
return
collection_name_with_prefix = self._get_collection_name_with_prefix(
collection_name
)
points = self._create_points(items, collection_name_with_prefix)
# Create batches
batches = [
points[i : i + BATCH_SIZE] for i in range(0, len(points), BATCH_SIZE)
]
loop = asyncio.get_event_loop()
tasks = [
loop.run_in_executor(
None, functools.partial(self.index.upsert, vectors=batch)
)
for batch in batches
]
results = await asyncio.gather(*tasks, return_exceptions=True)
for result in results:
if isinstance(result, Exception):
log.error(f"Error in async upsert batch: {result}")
raise result
log.info(
f"Successfully async upserted {len(points)} vectors in batches "
f"into '{collection_name_with_prefix}'"
)
def search(
self, collection_name: str, vectors: List[List[Union[float, int]]], limit: int
) -> Optional[SearchResult]:
"""Search for similar vectors in a collection."""
if not vectors or not vectors[0]:
log.warning("No vectors provided for search")
return None
collection_name_with_prefix = self._get_collection_name_with_prefix(
collection_name
)
if limit is None or limit <= 0:
limit = NO_LIMIT
try:
# Search using the first vector (assuming this is the intended behavior)
query_vector = vectors[0]
# Perform the search
query_response = self.index.query(
vector=query_vector,
top_k=limit,
include_metadata=True,
filter={"collection_name": collection_name_with_prefix},
)
matches = getattr(query_response, "matches", []) or []
if not matches:
# Return empty result if no matches
return SearchResult(
ids=[[]],
documents=[[]],
metadatas=[[]],
distances=[[]],
)
# Convert to GetResult format
get_result = self._result_to_get_result(matches)
# Calculate normalized distances based on metric
distances = [
[
self._normalize_distance(getattr(match, "score", 0.0))
for match in matches
]
]
return SearchResult(
ids=get_result.ids,
documents=get_result.documents,
metadatas=get_result.metadatas,
distances=distances,
)
except Exception as e:
log.error(f"Error searching in '{collection_name_with_prefix}': {e}")
return None
def query(
self, collection_name: str, filter: Dict, limit: Optional[int] = None
) -> Optional[GetResult]:
"""Query vectors by metadata filter."""
collection_name_with_prefix = self._get_collection_name_with_prefix(
collection_name
)
if limit is None or limit <= 0:
limit = NO_LIMIT
try:
# Create a zero vector for the dimension as Pinecone requires a vector
zero_vector = [0.0] * self.dimension
# Combine user filter with collection_name
pinecone_filter = {"collection_name": collection_name_with_prefix}
if filter:
pinecone_filter.update(filter)
# Perform metadata-only query
query_response = self.index.query(
vector=zero_vector,
filter=pinecone_filter,
top_k=limit,
include_metadata=True,
)
matches = getattr(query_response, "matches", []) or []
return self._result_to_get_result(matches)
except Exception as e:
log.error(f"Error querying collection '{collection_name}': {e}")
return None
def get(self, collection_name: str) -> Optional[GetResult]:
"""Get all vectors in a collection."""
collection_name_with_prefix = self._get_collection_name_with_prefix(
collection_name
)
try:
# Use a zero vector for fetching all entries
zero_vector = [0.0] * self.dimension
# Add filter to only get vectors for this collection
query_response = self.index.query(
vector=zero_vector,
top_k=NO_LIMIT,
include_metadata=True,
filter={"collection_name": collection_name_with_prefix},
)
matches = getattr(query_response, "matches", []) or []
return self._result_to_get_result(matches)
except Exception as e:
log.error(f"Error getting collection '{collection_name}': {e}")
return None
def delete(
self,
collection_name: str,
ids: Optional[List[str]] = None,
filter: Optional[Dict] = None,
) -> None:
"""Delete vectors by IDs or filter."""
collection_name_with_prefix = self._get_collection_name_with_prefix(
collection_name
)
try:
if ids:
# Delete by IDs (in batches for large deletions)
for i in range(0, len(ids), BATCH_SIZE):
batch_ids = ids[i : i + BATCH_SIZE]
# Note: When deleting by ID, we can't filter by collection_name
# This is a limitation of Pinecone - be careful with ID uniqueness
self.index.delete(ids=batch_ids)
log.debug(
f"Deleted batch of {len(batch_ids)} vectors by ID "
f"from '{collection_name_with_prefix}'"
)
log.info(
f"Successfully deleted {len(ids)} vectors by ID "
f"from '{collection_name_with_prefix}'"
)
elif filter:
# Combine user filter with collection_name
pinecone_filter = {"collection_name": collection_name_with_prefix}
if filter:
pinecone_filter.update(filter)
# Delete by metadata filter
self.index.delete(filter=pinecone_filter)
log.info(
f"Successfully deleted vectors by filter from '{collection_name_with_prefix}'"
)
else:
log.warning("No ids or filter provided for delete operation")
except Exception as e:
log.error(f"Error deleting from collection '{collection_name}': {e}")
raise
def reset(self) -> None:
"""Reset the database by deleting all collections."""
try:
self.index.delete(delete_all=True)
log.info("All vectors successfully deleted from the index.")
except Exception as e:
log.error(f"Failed to reset Pinecone index: {e}")
raise
def close(self):
"""Shut down resources."""
try:
# The new Pinecone client doesn't need explicit closing
pass
except Exception as e:
log.warning(f"Failed to clean up Pinecone resources: {e}")
self._executor.shutdown(wait=True)
def __enter__(self):
"""Enter context manager."""
return self
def __exit__(self, exc_type, exc_val, exc_tb):
"""Exit context manager, ensuring resources are cleaned up."""
self.close()
@@ -1,12 +1,27 @@
from typing import Optional
import logging
from urllib.parse import urlparse
from qdrant_client import QdrantClient as Qclient
from qdrant_client.http.models import PointStruct
from qdrant_client.models import models
from open_webui.retrieval.vector.main import VectorItem, SearchResult, GetResult
from open_webui.config import QDRANT_URI, QDRANT_API_KEY
from open_webui.retrieval.vector.main import (
VectorDBBase,
VectorItem,
SearchResult,
GetResult,
)
from open_webui.config import (
QDRANT_URI,
QDRANT_API_KEY,
QDRANT_ON_DISK,
QDRANT_GRPC_PORT,
QDRANT_PREFER_GRPC,
QDRANT_COLLECTION_PREFIX,
QDRANT_TIMEOUT,
QDRANT_HNSW_M,
)
from open_webui.env import SRC_LOG_LEVELS
NO_LIMIT = 999999999
@@ -15,16 +30,41 @@ log = logging.getLogger(__name__)
log.setLevel(SRC_LOG_LEVELS["RAG"])
class QdrantClient:
class QdrantClient(VectorDBBase):
def __init__(self):
self.collection_prefix = "open-webui"
self.collection_prefix = QDRANT_COLLECTION_PREFIX
self.QDRANT_URI = QDRANT_URI
self.QDRANT_API_KEY = QDRANT_API_KEY
self.client = (
Qclient(url=self.QDRANT_URI, api_key=self.QDRANT_API_KEY)
if self.QDRANT_URI
else None
)
self.QDRANT_ON_DISK = QDRANT_ON_DISK
self.PREFER_GRPC = QDRANT_PREFER_GRPC
self.GRPC_PORT = QDRANT_GRPC_PORT
self.QDRANT_TIMEOUT = QDRANT_TIMEOUT
self.QDRANT_HNSW_M = QDRANT_HNSW_M
if not self.QDRANT_URI:
self.client = None
return
# Unified handling for either scheme
parsed = urlparse(self.QDRANT_URI)
host = parsed.hostname or self.QDRANT_URI
http_port = parsed.port or 6333 # default REST port
if self.PREFER_GRPC:
self.client = Qclient(
host=host,
port=http_port,
grpc_port=self.GRPC_PORT,
prefer_grpc=self.PREFER_GRPC,
api_key=self.QDRANT_API_KEY,
timeout=self.QDRANT_TIMEOUT,
)
else:
self.client = Qclient(
url=self.QDRANT_URI,
api_key=self.QDRANT_API_KEY,
timeout=QDRANT_TIMEOUT,
)
def _result_to_get_result(self, points) -> GetResult:
ids = []
@@ -50,10 +90,34 @@ class QdrantClient:
self.client.create_collection(
collection_name=collection_name_with_prefix,
vectors_config=models.VectorParams(
size=dimension, distance=models.Distance.COSINE
size=dimension,
distance=models.Distance.COSINE,
on_disk=self.QDRANT_ON_DISK,
),
hnsw_config=models.HnswConfigDiff(
m=self.QDRANT_HNSW_M,
),
)
# Create payload indexes for efficient filtering
self.client.create_payload_index(
collection_name=collection_name_with_prefix,
field_name="metadata.hash",
field_schema=models.KeywordIndexParams(
type=models.KeywordIndexType.KEYWORD,
is_tenant=False,
on_disk=self.QDRANT_ON_DISK,
),
)
self.client.create_payload_index(
collection_name=collection_name_with_prefix,
field_name="metadata.file_id",
field_schema=models.KeywordIndexParams(
type=models.KeywordIndexType.KEYWORD,
is_tenant=False,
on_disk=self.QDRANT_ON_DISK,
),
)
log.info(f"collection {collection_name_with_prefix} successfully created!")
def _create_collection_if_not_exists(self, collection_name, dimension):
@@ -119,23 +183,23 @@ class QdrantClient:
)
)
points = self.client.query_points(
points = self.client.scroll(
collection_name=f"{self.collection_prefix}_{collection_name}",
query_filter=models.Filter(should=field_conditions),
scroll_filter=models.Filter(should=field_conditions),
limit=limit,
)
return self._result_to_get_result(points.points)
return self._result_to_get_result(points[0])
except Exception as e:
log.exception(f"Error querying a collection '{collection_name}': {e}")
return None
def get(self, collection_name: str) -> Optional[GetResult]:
# Get all the items in the collection.
points = self.client.query_points(
points = self.client.scroll(
collection_name=f"{self.collection_prefix}_{collection_name}",
limit=NO_LIMIT, # otherwise qdrant would set limit to 10!
)
return self._result_to_get_result(points.points)
return self._result_to_get_result(points[0])
def insert(self, collection_name: str, items: list[VectorItem]):
# Insert the items into the collection, if the collection does not exist, it will be created.
@@ -0,0 +1,372 @@
import logging
from typing import Optional, Tuple, List, Dict, Any
from urllib.parse import urlparse
import grpc
from open_webui.config import (
QDRANT_API_KEY,
QDRANT_GRPC_PORT,
QDRANT_ON_DISK,
QDRANT_PREFER_GRPC,
QDRANT_URI,
QDRANT_COLLECTION_PREFIX,
QDRANT_TIMEOUT,
QDRANT_HNSW_M,
)
from open_webui.env import SRC_LOG_LEVELS
from open_webui.retrieval.vector.main import (
GetResult,
SearchResult,
VectorDBBase,
VectorItem,
)
from qdrant_client import QdrantClient as Qclient
from qdrant_client.http.exceptions import UnexpectedResponse
from qdrant_client.http.models import PointStruct
from qdrant_client.models import models
NO_LIMIT = 999999999
TENANT_ID_FIELD = "tenant_id"
DEFAULT_DIMENSION = 384
log = logging.getLogger(__name__)
log.setLevel(SRC_LOG_LEVELS["RAG"])
def _tenant_filter(tenant_id: str) -> models.FieldCondition:
return models.FieldCondition(
key=TENANT_ID_FIELD, match=models.MatchValue(value=tenant_id)
)
def _metadata_filter(key: str, value: Any) -> models.FieldCondition:
return models.FieldCondition(
key=f"metadata.{key}", match=models.MatchValue(value=value)
)
class QdrantClient(VectorDBBase):
def __init__(self):
self.collection_prefix = QDRANT_COLLECTION_PREFIX
self.QDRANT_URI = QDRANT_URI
self.QDRANT_API_KEY = QDRANT_API_KEY
self.QDRANT_ON_DISK = QDRANT_ON_DISK
self.PREFER_GRPC = QDRANT_PREFER_GRPC
self.GRPC_PORT = QDRANT_GRPC_PORT
self.QDRANT_TIMEOUT = QDRANT_TIMEOUT
self.QDRANT_HNSW_M = QDRANT_HNSW_M
if not self.QDRANT_URI:
raise ValueError(
"QDRANT_URI is not set. Please configure it in the environment variables."
)
# Unified handling for either scheme
parsed = urlparse(self.QDRANT_URI)
host = parsed.hostname or self.QDRANT_URI
http_port = parsed.port or 6333 # default REST port
self.client = (
Qclient(
host=host,
port=http_port,
grpc_port=self.GRPC_PORT,
prefer_grpc=self.PREFER_GRPC,
api_key=self.QDRANT_API_KEY,
timeout=self.QDRANT_TIMEOUT,
)
if self.PREFER_GRPC
else Qclient(
url=self.QDRANT_URI,
api_key=self.QDRANT_API_KEY,
timeout=self.QDRANT_TIMEOUT,
)
)
# Main collection types for multi-tenancy
self.MEMORY_COLLECTION = f"{self.collection_prefix}_memories"
self.KNOWLEDGE_COLLECTION = f"{self.collection_prefix}_knowledge"
self.FILE_COLLECTION = f"{self.collection_prefix}_files"
self.WEB_SEARCH_COLLECTION = f"{self.collection_prefix}_web-search"
self.HASH_BASED_COLLECTION = f"{self.collection_prefix}_hash-based"
def _result_to_get_result(self, points) -> GetResult:
ids, documents, metadatas = [], [], []
for point in points:
payload = point.payload
ids.append(point.id)
documents.append(payload["text"])
metadatas.append(payload["metadata"])
return GetResult(ids=[ids], documents=[documents], metadatas=[metadatas])
def _get_collection_and_tenant_id(self, collection_name: str) -> Tuple[str, str]:
"""
Maps the traditional collection name to multi-tenant collection and tenant ID.
Returns:
tuple: (collection_name, tenant_id)
WARNING: This mapping relies on current Open WebUI naming conventions for
collection names. If Open WebUI changes how it generates collection names
(e.g., "user-memory-" prefix, "file-" prefix, web search patterns, or hash
formats), this mapping will break and route data to incorrect collections.
POTENTIALLY CAUSING HUGE DATA CORRUPTION, DATA CONSISTENCY ISSUES AND INCORRECT
DATA MAPPING INSIDE THE DATABASE.
"""
# Check for user memory collections
tenant_id = collection_name
if collection_name.startswith("user-memory-"):
return self.MEMORY_COLLECTION, tenant_id
# Check for file collections
elif collection_name.startswith("file-"):
return self.FILE_COLLECTION, tenant_id
# Check for web search collections
elif collection_name.startswith("web-search-"):
return self.WEB_SEARCH_COLLECTION, tenant_id
# Handle hash-based collections (YouTube and web URLs)
elif len(collection_name) == 63 and all(
c in "0123456789abcdef" for c in collection_name
):
return self.HASH_BASED_COLLECTION, tenant_id
else:
return self.KNOWLEDGE_COLLECTION, tenant_id
def _create_multi_tenant_collection(
self, mt_collection_name: str, dimension: int = DEFAULT_DIMENSION
):
"""
Creates a collection with multi-tenancy configuration and payload indexes for tenant_id and metadata fields.
"""
self.client.create_collection(
collection_name=mt_collection_name,
vectors_config=models.VectorParams(
size=dimension,
distance=models.Distance.COSINE,
on_disk=self.QDRANT_ON_DISK,
),
# Disable global index building due to multitenancy
# For more details https://qdrant.tech/documentation/guides/multiple-partitions/#calibrate-performance
hnsw_config=models.HnswConfigDiff(
payload_m=self.QDRANT_HNSW_M,
m=0,
),
)
log.info(
f"Multi-tenant collection {mt_collection_name} created with dimension {dimension}!"
)
self.client.create_payload_index(
collection_name=mt_collection_name,
field_name=TENANT_ID_FIELD,
field_schema=models.KeywordIndexParams(
type=models.KeywordIndexType.KEYWORD,
is_tenant=True,
on_disk=self.QDRANT_ON_DISK,
),
)
for field in ("metadata.hash", "metadata.file_id"):
self.client.create_payload_index(
collection_name=mt_collection_name,
field_name=field,
field_schema=models.KeywordIndexParams(
type=models.KeywordIndexType.KEYWORD,
on_disk=self.QDRANT_ON_DISK,
),
)
def _create_points(
self, items: List[VectorItem], tenant_id: str
) -> List[PointStruct]:
"""
Create point structs from vector items with tenant ID.
"""
return [
PointStruct(
id=item["id"],
vector=item["vector"],
payload={
"text": item["text"],
"metadata": item["metadata"],
TENANT_ID_FIELD: tenant_id,
},
)
for item in items
]
def _ensure_collection(
self, mt_collection_name: str, dimension: int = DEFAULT_DIMENSION
):
"""
Ensure the collection exists and payload indexes are created for tenant_id and metadata fields.
"""
if not self.client.collection_exists(collection_name=mt_collection_name):
self._create_multi_tenant_collection(mt_collection_name, dimension)
def has_collection(self, collection_name: str) -> bool:
"""
Check if a logical collection exists by checking for any points with the tenant ID.
"""
if not self.client:
return False
mt_collection, tenant_id = self._get_collection_and_tenant_id(collection_name)
if not self.client.collection_exists(collection_name=mt_collection):
return False
tenant_filter = _tenant_filter(tenant_id)
count_result = self.client.count(
collection_name=mt_collection,
count_filter=models.Filter(must=[tenant_filter]),
)
return count_result.count > 0
def delete(
self,
collection_name: str,
ids: Optional[List[str]] = None,
filter: Optional[Dict[str, Any]] = None,
):
"""
Delete vectors by ID or filter from a collection with tenant isolation.
"""
if not self.client:
return None
mt_collection, tenant_id = self._get_collection_and_tenant_id(collection_name)
if not self.client.collection_exists(collection_name=mt_collection):
log.debug(f"Collection {mt_collection} doesn't exist, nothing to delete")
return None
must_conditions = [_tenant_filter(tenant_id)]
should_conditions = []
if ids:
should_conditions = [_metadata_filter("id", id_value) for id_value in ids]
elif filter:
must_conditions += [_metadata_filter(k, v) for k, v in filter.items()]
return self.client.delete(
collection_name=mt_collection,
points_selector=models.FilterSelector(
filter=models.Filter(must=must_conditions, should=should_conditions)
),
)
def search(
self, collection_name: str, vectors: List[List[float | int]], limit: int
) -> Optional[SearchResult]:
"""
Search for the nearest neighbor items based on the vectors with tenant isolation.
"""
if not self.client or not vectors:
return None
mt_collection, tenant_id = self._get_collection_and_tenant_id(collection_name)
if not self.client.collection_exists(collection_name=mt_collection):
log.debug(f"Collection {mt_collection} doesn't exist, search returns None")
return None
tenant_filter = _tenant_filter(tenant_id)
query_response = self.client.query_points(
collection_name=mt_collection,
query=vectors[0],
limit=limit,
query_filter=models.Filter(must=[tenant_filter]),
)
get_result = self._result_to_get_result(query_response.points)
return SearchResult(
ids=get_result.ids,
documents=get_result.documents,
metadatas=get_result.metadatas,
distances=[[(point.score + 1.0) / 2.0 for point in query_response.points]],
)
def query(
self, collection_name: str, filter: Dict[str, Any], limit: Optional[int] = None
):
"""
Query points with filters and tenant isolation.
"""
if not self.client:
return None
mt_collection, tenant_id = self._get_collection_and_tenant_id(collection_name)
if not self.client.collection_exists(collection_name=mt_collection):
log.debug(f"Collection {mt_collection} doesn't exist, query returns None")
return None
if limit is None:
limit = NO_LIMIT
tenant_filter = _tenant_filter(tenant_id)
field_conditions = [_metadata_filter(k, v) for k, v in filter.items()]
combined_filter = models.Filter(must=[tenant_filter, *field_conditions])
points = self.client.scroll(
collection_name=mt_collection,
scroll_filter=combined_filter,
limit=limit,
)
return self._result_to_get_result(points[0])
def get(self, collection_name: str) -> Optional[GetResult]:
"""
Get all items in a collection with tenant isolation.
"""
if not self.client:
return None
mt_collection, tenant_id = self._get_collection_and_tenant_id(collection_name)
if not self.client.collection_exists(collection_name=mt_collection):
log.debug(f"Collection {mt_collection} doesn't exist, get returns None")
return None
tenant_filter = _tenant_filter(tenant_id)
points = self.client.scroll(
collection_name=mt_collection,
scroll_filter=models.Filter(must=[tenant_filter]),
limit=NO_LIMIT,
)
return self._result_to_get_result(points[0])
def upsert(self, collection_name: str, items: List[VectorItem]):
"""
Upsert items with tenant ID.
"""
if not self.client or not items:
return None
mt_collection, tenant_id = self._get_collection_and_tenant_id(collection_name)
dimension = len(items[0]["vector"])
self._ensure_collection(mt_collection, dimension)
points = self._create_points(items, tenant_id)
self.client.upload_points(mt_collection, points)
return None
def insert(self, collection_name: str, items: List[VectorItem]):
"""
Insert items with tenant ID.
"""
return self.upsert(collection_name, items)
def reset(self):
"""
Reset the database by deleting all collections.
"""
if not self.client:
return None
for collection in self.client.get_collections().collections:
if collection.name.startswith(self.collection_prefix):
self.client.delete_collection(collection_name=collection.name)
def delete_collection(self, collection_name: str):
"""
Delete a collection.
"""
if not self.client:
return None
mt_collection, tenant_id = self._get_collection_and_tenant_id(collection_name)
if not self.client.collection_exists(collection_name=mt_collection):
log.debug(f"Collection {mt_collection} doesn't exist, nothing to delete")
return None
self.client.delete(
collection_name=mt_collection,
points_selector=models.FilterSelector(
filter=models.Filter(must=[_tenant_filter(tenant_id)])
),
)
@@ -0,0 +1,776 @@
from open_webui.retrieval.vector.utils import process_metadata
from open_webui.retrieval.vector.main import (
VectorDBBase,
VectorItem,
GetResult,
SearchResult,
)
from open_webui.config import S3_VECTOR_BUCKET_NAME, S3_VECTOR_REGION
from open_webui.env import SRC_LOG_LEVELS
from typing import List, Optional, Dict, Any, Union
import logging
import boto3
log = logging.getLogger(__name__)
log.setLevel(SRC_LOG_LEVELS["RAG"])
class S3VectorClient(VectorDBBase):
"""
AWS S3 Vector integration for Open WebUI Knowledge.
"""
def __init__(self):
self.bucket_name = S3_VECTOR_BUCKET_NAME
self.region = S3_VECTOR_REGION
# Simple validation - log warnings instead of raising exceptions
if not self.bucket_name:
log.warning("S3_VECTOR_BUCKET_NAME not set - S3Vector will not work")
if not self.region:
log.warning("S3_VECTOR_REGION not set - S3Vector will not work")
if self.bucket_name and self.region:
try:
self.client = boto3.client("s3vectors", region_name=self.region)
log.info(
f"S3Vector client initialized for bucket '{self.bucket_name}' in region '{self.region}'"
)
except Exception as e:
log.error(f"Failed to initialize S3Vector client: {e}")
self.client = None
else:
self.client = None
def _create_index(
self,
index_name: str,
dimension: int,
data_type: str = "float32",
distance_metric: str = "cosine",
) -> None:
"""
Create a new index in the S3 vector bucket for the given collection if it does not exist.
"""
if self.has_collection(index_name):
log.debug(f"Index '{index_name}' already exists, skipping creation")
return
try:
self.client.create_index(
vectorBucketName=self.bucket_name,
indexName=index_name,
dataType=data_type,
dimension=dimension,
distanceMetric=distance_metric,
)
log.info(
f"Created S3 index: {index_name} (dim={dimension}, type={data_type}, metric={distance_metric})"
)
except Exception as e:
log.error(f"Error creating S3 index '{index_name}': {e}")
raise
def _filter_metadata(
self, metadata: Dict[str, Any], item_id: str
) -> Dict[str, Any]:
"""
Filter vector metadata keys to comply with S3 Vector API limit of 10 keys maximum.
"""
if not isinstance(metadata, dict) or len(metadata) <= 10:
return metadata
# Keep only the first 10 keys, prioritizing important ones based on actual Open WebUI metadata
important_keys = [
"text", # The actual document content
"file_id", # File ID
"source", # Document source file
"title", # Document title
"page", # Page number
"total_pages", # Total pages in document
"embedding_config", # Embedding configuration
"created_by", # User who created it
"name", # Document name
"hash", # Content hash
]
filtered_metadata = {}
# First, add important keys if they exist
for key in important_keys:
if key in metadata:
filtered_metadata[key] = metadata[key]
if len(filtered_metadata) >= 10:
break
# If we still have room, add other keys
if len(filtered_metadata) < 10:
for key, value in metadata.items():
if key not in filtered_metadata:
filtered_metadata[key] = value
if len(filtered_metadata) >= 10:
break
log.warning(
f"Metadata for key '{item_id}' had {len(metadata)} keys, limited to 10 keys"
)
return filtered_metadata
def has_collection(self, collection_name: str) -> bool:
"""
Check if a vector index exists using direct lookup.
This avoids pagination issues with list_indexes() and is significantly faster.
"""
try:
self.client.get_index(
vectorBucketName=self.bucket_name, indexName=collection_name
)
return True
except Exception as e:
log.error(f"Error checking if index '{collection_name}' exists: {e}")
return False
def delete_collection(self, collection_name: str) -> None:
"""
Delete an entire S3 Vector index/collection.
"""
if not self.has_collection(collection_name):
log.warning(
f"Collection '{collection_name}' does not exist, nothing to delete"
)
return
try:
log.info(f"Deleting collection '{collection_name}'")
self.client.delete_index(
vectorBucketName=self.bucket_name, indexName=collection_name
)
log.info(f"Successfully deleted collection '{collection_name}'")
except Exception as e:
log.error(f"Error deleting collection '{collection_name}': {e}")
raise
def insert(self, collection_name: str, items: List[VectorItem]) -> None:
"""
Insert vector items into the S3 Vector index. Create index if it does not exist.
"""
if not items:
log.warning("No items to insert")
return
dimension = len(items[0]["vector"])
try:
if not self.has_collection(collection_name):
log.info(f"Index '{collection_name}' does not exist. Creating index.")
self._create_index(
index_name=collection_name,
dimension=dimension,
data_type="float32",
distance_metric="cosine",
)
# Prepare vectors for insertion
vectors = []
for item in items:
# Ensure vector data is in the correct format for S3 Vector API
vector_data = item["vector"]
if isinstance(vector_data, list):
# Convert list to float32 values as required by S3 Vector API
vector_data = [float(x) for x in vector_data]
# Prepare metadata, ensuring the text field is preserved
metadata = item.get("metadata", {}).copy()
# Add the text field to metadata so it's available for retrieval
metadata["text"] = item["text"]
# Convert metadata to string format for consistency
metadata = process_metadata(metadata)
# Filter metadata to comply with S3 Vector API limit of 10 keys
metadata = self._filter_metadata(metadata, item["id"])
vectors.append(
{
"key": item["id"],
"data": {"float32": vector_data},
"metadata": metadata,
}
)
# Insert vectors in batches of 500 (S3 Vector API limit)
batch_size = 500
for i in range(0, len(vectors), batch_size):
batch = vectors[i : i + batch_size]
self.client.put_vectors(
vectorBucketName=self.bucket_name,
indexName=collection_name,
vectors=batch,
)
log.info(
f"Inserted batch {i//batch_size + 1}: {len(batch)} vectors into index '{collection_name}'."
)
log.info(
f"Completed insertion of {len(vectors)} vectors into index '{collection_name}'."
)
except Exception as e:
log.error(f"Error inserting vectors: {e}")
raise
def upsert(self, collection_name: str, items: List[VectorItem]) -> None:
"""
Insert or update vector items in the S3 Vector index. Create index if it does not exist.
"""
if not items:
log.warning("No items to upsert")
return
dimension = len(items[0]["vector"])
log.info(f"Upsert dimension: {dimension}")
try:
if not self.has_collection(collection_name):
log.info(
f"Index '{collection_name}' does not exist. Creating index for upsert."
)
self._create_index(
index_name=collection_name,
dimension=dimension,
data_type="float32",
distance_metric="cosine",
)
# Prepare vectors for upsert
vectors = []
for item in items:
# Ensure vector data is in the correct format for S3 Vector API
vector_data = item["vector"]
if isinstance(vector_data, list):
# Convert list to float32 values as required by S3 Vector API
vector_data = [float(x) for x in vector_data]
# Prepare metadata, ensuring the text field is preserved
metadata = item.get("metadata", {}).copy()
# Add the text field to metadata so it's available for retrieval
metadata["text"] = item["text"]
# Convert metadata to string format for consistency
metadata = process_metadata(metadata)
# Filter metadata to comply with S3 Vector API limit of 10 keys
metadata = self._filter_metadata(metadata, item["id"])
vectors.append(
{
"key": item["id"],
"data": {"float32": vector_data},
"metadata": metadata,
}
)
# Upsert vectors in batches of 500 (S3 Vector API limit)
batch_size = 500
for i in range(0, len(vectors), batch_size):
batch = vectors[i : i + batch_size]
if i == 0: # Log sample info for first batch only
log.info(
f"Upserting batch 1: {len(batch)} vectors. First vector sample: key={batch[0]['key']}, data_type={type(batch[0]['data']['float32'])}, data_len={len(batch[0]['data']['float32'])}"
)
else:
log.info(
f"Upserting batch {i//batch_size + 1}: {len(batch)} vectors."
)
self.client.put_vectors(
vectorBucketName=self.bucket_name,
indexName=collection_name,
vectors=batch,
)
log.info(
f"Completed upsert of {len(vectors)} vectors into index '{collection_name}'."
)
except Exception as e:
log.error(f"Error upserting vectors: {e}")
raise
def search(
self, collection_name: str, vectors: List[List[Union[float, int]]], limit: int
) -> Optional[SearchResult]:
"""
Search for similar vectors in a collection using multiple query vectors.
"""
if not self.has_collection(collection_name):
log.warning(f"Collection '{collection_name}' does not exist")
return None
if not vectors:
log.warning("No query vectors provided")
return None
try:
log.info(
f"Searching collection '{collection_name}' with {len(vectors)} query vectors, limit={limit}"
)
# Initialize result lists
all_ids = []
all_documents = []
all_metadatas = []
all_distances = []
# Process each query vector
for i, query_vector in enumerate(vectors):
log.debug(f"Processing query vector {i+1}/{len(vectors)}")
# Prepare the query vector in S3 Vector format
query_vector_dict = {"float32": [float(x) for x in query_vector]}
# Call S3 Vector query API
response = self.client.query_vectors(
vectorBucketName=self.bucket_name,
indexName=collection_name,
topK=limit,
queryVector=query_vector_dict,
returnMetadata=True,
returnDistance=True,
)
# Process results for this query
query_ids = []
query_documents = []
query_metadatas = []
query_distances = []
result_vectors = response.get("vectors", [])
for vector in result_vectors:
vector_id = vector.get("key")
vector_metadata = vector.get("metadata", {})
vector_distance = vector.get("distance", 0.0)
# Extract document text from metadata
document_text = ""
if isinstance(vector_metadata, dict):
# Get the text field first (highest priority)
document_text = vector_metadata.get("text")
if not document_text:
# Fallback to other possible text fields
document_text = (
vector_metadata.get("content")
or vector_metadata.get("document")
or vector_id
)
else:
document_text = vector_id
query_ids.append(vector_id)
query_documents.append(document_text)
query_metadatas.append(vector_metadata)
query_distances.append(vector_distance)
# Add this query's results to the overall results
all_ids.append(query_ids)
all_documents.append(query_documents)
all_metadatas.append(query_metadatas)
all_distances.append(query_distances)
log.info(f"Search completed. Found results for {len(all_ids)} queries")
# Return SearchResult format
return SearchResult(
ids=all_ids if all_ids else None,
documents=all_documents if all_documents else None,
metadatas=all_metadatas if all_metadatas else None,
distances=all_distances if all_distances else None,
)
except Exception as e:
log.error(f"Error searching collection '{collection_name}': {str(e)}")
# Handle specific AWS exceptions
if hasattr(e, "response") and "Error" in e.response:
error_code = e.response["Error"]["Code"]
if error_code == "NotFoundException":
log.warning(f"Collection '{collection_name}' not found")
return None
elif error_code == "ValidationException":
log.error(f"Invalid query vector dimensions or parameters")
return None
elif error_code == "AccessDeniedException":
log.error(
f"Access denied for collection '{collection_name}'. Check permissions."
)
return None
raise
def query(
self, collection_name: str, filter: Dict, limit: Optional[int] = None
) -> Optional[GetResult]:
"""
Query vectors from a collection using metadata filter.
"""
if not self.has_collection(collection_name):
log.warning(f"Collection '{collection_name}' does not exist")
return GetResult(ids=[[]], documents=[[]], metadatas=[[]])
if not filter:
log.warning("No filter provided, returning all vectors")
return self.get(collection_name)
try:
log.info(f"Querying collection '{collection_name}' with filter: {filter}")
# For S3 Vector, we need to use list_vectors and then filter results
# Since S3 Vector may not support complex server-side filtering,
# we'll retrieve all vectors and filter client-side
# Get all vectors first
all_vectors_result = self.get(collection_name)
if not all_vectors_result or not all_vectors_result.ids:
log.warning("No vectors found in collection")
return GetResult(ids=[[]], documents=[[]], metadatas=[[]])
# Extract the lists from the result
all_ids = all_vectors_result.ids[0] if all_vectors_result.ids else []
all_documents = (
all_vectors_result.documents[0] if all_vectors_result.documents else []
)
all_metadatas = (
all_vectors_result.metadatas[0] if all_vectors_result.metadatas else []
)
# Apply client-side filtering
filtered_ids = []
filtered_documents = []
filtered_metadatas = []
for i, metadata in enumerate(all_metadatas):
if self._matches_filter(metadata, filter):
if i < len(all_ids):
filtered_ids.append(all_ids[i])
if i < len(all_documents):
filtered_documents.append(all_documents[i])
filtered_metadatas.append(metadata)
# Apply limit if specified
if limit and len(filtered_ids) >= limit:
break
log.info(
f"Filter applied: {len(filtered_ids)} vectors match out of {len(all_ids)} total"
)
# Return GetResult format
if filtered_ids:
return GetResult(
ids=[filtered_ids],
documents=[filtered_documents],
metadatas=[filtered_metadatas],
)
else:
return GetResult(ids=[[]], documents=[[]], metadatas=[[]])
except Exception as e:
log.error(f"Error querying collection '{collection_name}': {str(e)}")
# Handle specific AWS exceptions
if hasattr(e, "response") and "Error" in e.response:
error_code = e.response["Error"]["Code"]
if error_code == "NotFoundException":
log.warning(f"Collection '{collection_name}' not found")
return GetResult(ids=[[]], documents=[[]], metadatas=[[]])
elif error_code == "AccessDeniedException":
log.error(
f"Access denied for collection '{collection_name}'. Check permissions."
)
return GetResult(ids=[[]], documents=[[]], metadatas=[[]])
raise
def get(self, collection_name: str) -> Optional[GetResult]:
"""
Retrieve all vectors from a collection.
"""
if not self.has_collection(collection_name):
log.warning(f"Collection '{collection_name}' does not exist")
return GetResult(ids=[[]], documents=[[]], metadatas=[[]])
try:
log.info(f"Retrieving all vectors from collection '{collection_name}'")
# Initialize result lists
all_ids = []
all_documents = []
all_metadatas = []
# Handle pagination
next_token = None
while True:
# Prepare request parameters
request_params = {
"vectorBucketName": self.bucket_name,
"indexName": collection_name,
"returnData": False, # Don't include vector data (not needed for get)
"returnMetadata": True, # Include metadata
"maxResults": 500, # Use reasonable page size
}
if next_token:
request_params["nextToken"] = next_token
# Call S3 Vector API
response = self.client.list_vectors(**request_params)
# Process vectors in this page
vectors = response.get("vectors", [])
for vector in vectors:
vector_id = vector.get("key")
vector_data = vector.get("data", {})
vector_metadata = vector.get("metadata", {})
# Extract the actual vector array
vector_array = vector_data.get("float32", [])
# For documents, we try to extract text from metadata or use the vector ID
document_text = ""
if isinstance(vector_metadata, dict):
# Get the text field first (highest priority)
document_text = vector_metadata.get("text")
if not document_text:
# Fallback to other possible text fields
document_text = (
vector_metadata.get("content")
or vector_metadata.get("document")
or vector_id
)
# Log the actual content for debugging
log.debug(
f"Document text preview (first 200 chars): {str(document_text)[:200]}"
)
else:
document_text = vector_id
all_ids.append(vector_id)
all_documents.append(document_text)
all_metadatas.append(vector_metadata)
# Check if there are more pages
next_token = response.get("nextToken")
if not next_token:
break
log.info(
f"Retrieved {len(all_ids)} vectors from collection '{collection_name}'"
)
# Return in GetResult format
# The Open WebUI GetResult expects lists of lists, so we wrap each list
if all_ids:
return GetResult(
ids=[all_ids], documents=[all_documents], metadatas=[all_metadatas]
)
else:
return GetResult(ids=[[]], documents=[[]], metadatas=[[]])
except Exception as e:
log.error(
f"Error retrieving vectors from collection '{collection_name}': {str(e)}"
)
# Handle specific AWS exceptions
if hasattr(e, "response") and "Error" in e.response:
error_code = e.response["Error"]["Code"]
if error_code == "NotFoundException":
log.warning(f"Collection '{collection_name}' not found")
return GetResult(ids=[[]], documents=[[]], metadatas=[[]])
elif error_code == "AccessDeniedException":
log.error(
f"Access denied for collection '{collection_name}'. Check permissions."
)
return GetResult(ids=[[]], documents=[[]], metadatas=[[]])
raise
def delete(
self,
collection_name: str,
ids: Optional[List[str]] = None,
filter: Optional[Dict] = None,
) -> None:
"""
Delete vectors by ID or filter from a collection.
"""
if not self.has_collection(collection_name):
log.warning(
f"Collection '{collection_name}' does not exist, nothing to delete"
)
return
# Check if this is a knowledge collection (not file-specific)
is_knowledge_collection = not collection_name.startswith("file-")
try:
if ids:
# Delete by specific vector IDs/keys
log.info(
f"Deleting {len(ids)} vectors by IDs from collection '{collection_name}'"
)
self.client.delete_vectors(
vectorBucketName=self.bucket_name,
indexName=collection_name,
keys=ids,
)
log.info(f"Deleted {len(ids)} vectors from index '{collection_name}'")
elif filter:
# Handle filter-based deletion
log.info(
f"Deleting vectors by filter from collection '{collection_name}': {filter}"
)
# If this is a knowledge collection and we have a file_id filter,
# also clean up the corresponding file-specific collection
if is_knowledge_collection and "file_id" in filter:
file_id = filter["file_id"]
file_collection_name = f"file-{file_id}"
if self.has_collection(file_collection_name):
log.info(
f"Found related file-specific collection '{file_collection_name}', deleting it to prevent duplicates"
)
self.delete_collection(file_collection_name)
# For the main collection, implement query-then-delete
# First, query to get IDs matching the filter
query_result = self.query(collection_name, filter)
if query_result and query_result.ids and query_result.ids[0]:
matching_ids = query_result.ids[0]
log.info(
f"Found {len(matching_ids)} vectors matching filter, deleting them"
)
# Delete the matching vectors by ID
self.client.delete_vectors(
vectorBucketName=self.bucket_name,
indexName=collection_name,
keys=matching_ids,
)
log.info(
f"Deleted {len(matching_ids)} vectors from index '{collection_name}' using filter"
)
else:
log.warning("No vectors found matching the filter criteria")
else:
log.warning("No IDs or filter provided for deletion")
except Exception as e:
log.error(
f"Error deleting vectors from collection '{collection_name}': {e}"
)
raise
def reset(self) -> None:
"""
Reset/clear all vector data. For S3 Vector, this deletes all indexes.
"""
try:
log.warning(
"Reset called - this will delete all vector indexes in the S3 bucket"
)
# List all indexes
response = self.client.list_indexes(vectorBucketName=self.bucket_name)
indexes = response.get("indexes", [])
if not indexes:
log.warning("No indexes found to delete")
return
# Delete all indexes
deleted_count = 0
for index in indexes:
index_name = index.get("indexName")
if index_name:
try:
self.client.delete_index(
vectorBucketName=self.bucket_name, indexName=index_name
)
deleted_count += 1
log.info(f"Deleted index: {index_name}")
except Exception as e:
log.error(f"Error deleting index '{index_name}': {e}")
log.info(f"Reset completed: deleted {deleted_count} indexes")
except Exception as e:
log.error(f"Error during reset: {e}")
raise
def _matches_filter(self, metadata: Dict[str, Any], filter: Dict[str, Any]) -> bool:
"""
Check if metadata matches the given filter conditions.
"""
if not isinstance(metadata, dict) or not isinstance(filter, dict):
return False
# Check each filter condition
for key, expected_value in filter.items():
# Handle special operators
if key.startswith("$"):
if key == "$and":
# All conditions must match
if not isinstance(expected_value, list):
continue
for condition in expected_value:
if not self._matches_filter(metadata, condition):
return False
elif key == "$or":
# At least one condition must match
if not isinstance(expected_value, list):
continue
any_match = False
for condition in expected_value:
if self._matches_filter(metadata, condition):
any_match = True
break
if not any_match:
return False
continue
# Get the actual value from metadata
actual_value = metadata.get(key)
# Handle different types of expected values
if isinstance(expected_value, dict):
# Handle comparison operators
for op, op_value in expected_value.items():
if op == "$eq":
if actual_value != op_value:
return False
elif op == "$ne":
if actual_value == op_value:
return False
elif op == "$in":
if (
not isinstance(op_value, list)
or actual_value not in op_value
):
return False
elif op == "$nin":
if isinstance(op_value, list) and actual_value in op_value:
return False
elif op == "$exists":
if bool(op_value) != (key in metadata):
return False
# Add more operators as needed
else:
# Simple equality check
if actual_value != expected_value:
return False
return True
@@ -0,0 +1,340 @@
import weaviate
import re
import uuid
from typing import Any, Dict, List, Optional, Union
from open_webui.retrieval.vector.main import (
VectorDBBase,
VectorItem,
SearchResult,
GetResult,
)
from open_webui.retrieval.vector.utils import process_metadata
from open_webui.config import (
WEAVIATE_HTTP_HOST,
WEAVIATE_HTTP_PORT,
WEAVIATE_GRPC_PORT,
WEAVIATE_API_KEY,
)
def _convert_uuids_to_strings(obj: Any) -> Any:
"""
Recursively convert UUID objects to strings in nested data structures.
This function handles:
- UUID objects -> string
- Dictionaries with UUID values
- Lists/Tuples with UUID values
- Nested combinations of the above
Args:
obj: Any object that might contain UUIDs
Returns:
The same object structure with UUIDs converted to strings
"""
if isinstance(obj, uuid.UUID):
return str(obj)
elif isinstance(obj, dict):
return {key: _convert_uuids_to_strings(value) for key, value in obj.items()}
elif isinstance(obj, (list, tuple)):
return type(obj)(_convert_uuids_to_strings(item) for item in obj)
elif isinstance(obj, (str, int, float, bool, type(None))):
return obj
else:
return obj
class WeaviateClient(VectorDBBase):
def __init__(self):
self.url = WEAVIATE_HTTP_HOST
try:
# Build connection parameters
connection_params = {
"host": WEAVIATE_HTTP_HOST,
"port": WEAVIATE_HTTP_PORT,
"grpc_port": WEAVIATE_GRPC_PORT,
}
# Only add auth_credentials if WEAVIATE_API_KEY exists and is not empty
if WEAVIATE_API_KEY:
connection_params["auth_credentials"] = (
weaviate.classes.init.Auth.api_key(WEAVIATE_API_KEY)
)
self.client = weaviate.connect_to_local(**connection_params)
self.client.connect()
except Exception as e:
raise ConnectionError(f"Failed to connect to Weaviate: {e}") from e
def _sanitize_collection_name(self, collection_name: str) -> str:
"""Sanitize collection name to be a valid Weaviate class name."""
if not isinstance(collection_name, str) or not collection_name.strip():
raise ValueError("Collection name must be a non-empty string")
# Requirements for a valid Weaviate class name:
# The collection name must begin with a capital letter.
# The name can only contain letters, numbers, and the underscore (_) character. Spaces are not allowed.
# Replace hyphens with underscores and keep only alphanumeric characters
name = re.sub(r"[^a-zA-Z0-9_]", "", collection_name.replace("-", "_"))
name = name.strip("_")
if not name:
raise ValueError(
"Could not sanitize collection name to be a valid Weaviate class name"
)
# Ensure it starts with a letter and is capitalized
if not name[0].isalpha():
name = "C" + name
return name[0].upper() + name[1:]
def has_collection(self, collection_name: str) -> bool:
sane_collection_name = self._sanitize_collection_name(collection_name)
return self.client.collections.exists(sane_collection_name)
def delete_collection(self, collection_name: str) -> None:
sane_collection_name = self._sanitize_collection_name(collection_name)
if self.client.collections.exists(sane_collection_name):
self.client.collections.delete(sane_collection_name)
def _create_collection(self, collection_name: str) -> None:
self.client.collections.create(
name=collection_name,
vector_config=weaviate.classes.config.Configure.Vectors.self_provided(),
properties=[
weaviate.classes.config.Property(
name="text", data_type=weaviate.classes.config.DataType.TEXT
),
],
)
def insert(self, collection_name: str, items: List[VectorItem]) -> None:
sane_collection_name = self._sanitize_collection_name(collection_name)
if not self.client.collections.exists(sane_collection_name):
self._create_collection(sane_collection_name)
collection = self.client.collections.get(sane_collection_name)
with collection.batch.fixed_size(batch_size=100) as batch:
for item in items:
item_uuid = str(uuid.uuid4()) if not item["id"] else str(item["id"])
properties = {"text": item["text"]}
if item["metadata"]:
clean_metadata = _convert_uuids_to_strings(
process_metadata(item["metadata"])
)
clean_metadata.pop("text", None)
properties.update(clean_metadata)
batch.add_object(
properties=properties, uuid=item_uuid, vector=item["vector"]
)
def upsert(self, collection_name: str, items: List[VectorItem]) -> None:
sane_collection_name = self._sanitize_collection_name(collection_name)
if not self.client.collections.exists(sane_collection_name):
self._create_collection(sane_collection_name)
collection = self.client.collections.get(sane_collection_name)
with collection.batch.fixed_size(batch_size=100) as batch:
for item in items:
item_uuid = str(item["id"]) if item["id"] else None
properties = {"text": item["text"]}
if item["metadata"]:
clean_metadata = _convert_uuids_to_strings(
process_metadata(item["metadata"])
)
clean_metadata.pop("text", None)
properties.update(clean_metadata)
batch.add_object(
properties=properties, uuid=item_uuid, vector=item["vector"]
)
def search(
self, collection_name: str, vectors: List[List[Union[float, int]]], limit: int
) -> Optional[SearchResult]:
sane_collection_name = self._sanitize_collection_name(collection_name)
if not self.client.collections.exists(sane_collection_name):
return None
collection = self.client.collections.get(sane_collection_name)
result_ids, result_documents, result_metadatas, result_distances = (
[],
[],
[],
[],
)
for vector_embedding in vectors:
try:
response = collection.query.near_vector(
near_vector=vector_embedding,
limit=limit,
return_metadata=weaviate.classes.query.MetadataQuery(distance=True),
)
ids = [str(obj.uuid) for obj in response.objects]
documents = []
metadatas = []
distances = []
for obj in response.objects:
properties = dict(obj.properties) if obj.properties else {}
documents.append(properties.pop("text", ""))
metadatas.append(_convert_uuids_to_strings(properties))
# Weaviate has cosine distance, 2 (worst) -> 0 (best). Re-ordering to 0 -> 1
raw_distances = [
(
obj.metadata.distance
if obj.metadata and obj.metadata.distance
else 2.0
)
for obj in response.objects
]
distances = [(2 - dist) / 2 for dist in raw_distances]
result_ids.append(ids)
result_documents.append(documents)
result_metadatas.append(metadatas)
result_distances.append(distances)
except Exception:
result_ids.append([])
result_documents.append([])
result_metadatas.append([])
result_distances.append([])
return SearchResult(
**{
"ids": result_ids,
"documents": result_documents,
"metadatas": result_metadatas,
"distances": result_distances,
}
)
def query(
self, collection_name: str, filter: Dict, limit: Optional[int] = None
) -> Optional[GetResult]:
sane_collection_name = self._sanitize_collection_name(collection_name)
if not self.client.collections.exists(sane_collection_name):
return None
collection = self.client.collections.get(sane_collection_name)
weaviate_filter = None
if filter:
for key, value in filter.items():
prop_filter = weaviate.classes.query.Filter.by_property(name=key).equal(
value
)
weaviate_filter = (
prop_filter
if weaviate_filter is None
else weaviate.classes.query.Filter.all_of(
[weaviate_filter, prop_filter]
)
)
try:
response = collection.query.fetch_objects(
filters=weaviate_filter, limit=limit
)
ids = [str(obj.uuid) for obj in response.objects]
documents = []
metadatas = []
for obj in response.objects:
properties = dict(obj.properties) if obj.properties else {}
documents.append(properties.pop("text", ""))
metadatas.append(_convert_uuids_to_strings(properties))
return GetResult(
**{
"ids": [ids],
"documents": [documents],
"metadatas": [metadatas],
}
)
except Exception:
return None
def get(self, collection_name: str) -> Optional[GetResult]:
sane_collection_name = self._sanitize_collection_name(collection_name)
if not self.client.collections.exists(sane_collection_name):
return None
collection = self.client.collections.get(sane_collection_name)
ids, documents, metadatas = [], [], []
try:
for item in collection.iterator():
ids.append(str(item.uuid))
properties = dict(item.properties) if item.properties else {}
documents.append(properties.pop("text", ""))
metadatas.append(_convert_uuids_to_strings(properties))
if not ids:
return None
return GetResult(
**{
"ids": [ids],
"documents": [documents],
"metadatas": [metadatas],
}
)
except Exception:
return None
def delete(
self,
collection_name: str,
ids: Optional[List[str]] = None,
filter: Optional[Dict] = None,
) -> None:
sane_collection_name = self._sanitize_collection_name(collection_name)
if not self.client.collections.exists(sane_collection_name):
return
collection = self.client.collections.get(sane_collection_name)
try:
if ids:
for item_id in ids:
collection.data.delete_by_id(uuid=item_id)
elif filter:
weaviate_filter = None
for key, value in filter.items():
prop_filter = weaviate.classes.query.Filter.by_property(
name=key
).equal(value)
weaviate_filter = (
prop_filter
if weaviate_filter is None
else weaviate.classes.query.Filter.all_of(
[weaviate_filter, prop_filter]
)
)
if weaviate_filter:
collection.data.delete_many(where=weaviate_filter)
except Exception:
pass
def reset(self) -> None:
try:
for collection_name in self.client.collections.list_all().keys():
self.client.collections.delete(collection_name)
except Exception:
pass
@@ -0,0 +1,78 @@
from open_webui.retrieval.vector.main import VectorDBBase
from open_webui.retrieval.vector.type import VectorType
from open_webui.config import (
VECTOR_DB,
ENABLE_QDRANT_MULTITENANCY_MODE,
ENABLE_MILVUS_MULTITENANCY_MODE,
)
class Vector:
@staticmethod
def get_vector(vector_type: str) -> VectorDBBase:
"""
get vector db instance by vector type
"""
match vector_type:
case VectorType.MILVUS:
if ENABLE_MILVUS_MULTITENANCY_MODE:
from open_webui.retrieval.vector.dbs.milvus_multitenancy import (
MilvusClient,
)
return MilvusClient()
else:
from open_webui.retrieval.vector.dbs.milvus import MilvusClient
return MilvusClient()
case VectorType.QDRANT:
if ENABLE_QDRANT_MULTITENANCY_MODE:
from open_webui.retrieval.vector.dbs.qdrant_multitenancy import (
QdrantClient,
)
return QdrantClient()
else:
from open_webui.retrieval.vector.dbs.qdrant import QdrantClient
return QdrantClient()
case VectorType.PINECONE:
from open_webui.retrieval.vector.dbs.pinecone import PineconeClient
return PineconeClient()
case VectorType.S3VECTOR:
from open_webui.retrieval.vector.dbs.s3vector import S3VectorClient
return S3VectorClient()
case VectorType.OPENSEARCH:
from open_webui.retrieval.vector.dbs.opensearch import OpenSearchClient
return OpenSearchClient()
case VectorType.PGVECTOR:
from open_webui.retrieval.vector.dbs.pgvector import PgvectorClient
return PgvectorClient()
case VectorType.ELASTICSEARCH:
from open_webui.retrieval.vector.dbs.elasticsearch import (
ElasticsearchClient,
)
return ElasticsearchClient()
case VectorType.CHROMA:
from open_webui.retrieval.vector.dbs.chroma import ChromaClient
return ChromaClient()
case VectorType.ORACLE23AI:
from open_webui.retrieval.vector.dbs.oracle23ai import Oracle23aiClient
return Oracle23aiClient()
case VectorType.WEAVIATE:
from open_webui.retrieval.vector.dbs.weaviate import WeaviateClient
return WeaviateClient()
case _:
raise ValueError(f"Unsupported vector type: {vector_type}")
VECTOR_DB_CLIENT = Vector.get_vector(VECTOR_DB)
+68 -1
View File
@@ -1,5 +1,6 @@
from pydantic import BaseModel
from typing import Optional, List, Any
from abc import ABC, abstractmethod
from typing import Any, Dict, List, Optional, Union
class VectorItem(BaseModel):
@@ -17,3 +18,69 @@ class GetResult(BaseModel):
class SearchResult(GetResult):
distances: Optional[List[List[float | int]]]
class VectorDBBase(ABC):
"""
Abstract base class for all vector database backends.
Implementations of this class provide methods for collection management,
vector insertion, deletion, similarity search, and metadata filtering.
Any custom vector database integration must inherit from this class and
implement all abstract methods.
"""
@abstractmethod
def has_collection(self, collection_name: str) -> bool:
"""Check if the collection exists in the vector DB."""
pass
@abstractmethod
def delete_collection(self, collection_name: str) -> None:
"""Delete a collection from the vector DB."""
pass
@abstractmethod
def insert(self, collection_name: str, items: List[VectorItem]) -> None:
"""Insert a list of vector items into a collection."""
pass
@abstractmethod
def upsert(self, collection_name: str, items: List[VectorItem]) -> None:
"""Insert or update vector items in a collection."""
pass
@abstractmethod
def search(
self, collection_name: str, vectors: List[List[Union[float, int]]], limit: int
) -> Optional[SearchResult]:
"""Search for similar vectors in a collection."""
pass
@abstractmethod
def query(
self, collection_name: str, filter: Dict, limit: Optional[int] = None
) -> Optional[GetResult]:
"""Query vectors from a collection using metadata filter."""
pass
@abstractmethod
def get(self, collection_name: str) -> Optional[GetResult]:
"""Retrieve all vectors from a collection."""
pass
@abstractmethod
def delete(
self,
collection_name: str,
ids: Optional[List[str]] = None,
filter: Optional[Dict] = None,
) -> None:
"""Delete vectors by ID or filter from a collection."""
pass
@abstractmethod
def reset(self) -> None:
"""Reset the vector database by removing all collections or those matching a condition."""
pass
@@ -0,0 +1,14 @@
from enum import StrEnum
class VectorType(StrEnum):
MILVUS = "milvus"
QDRANT = "qdrant"
CHROMA = "chroma"
PINECONE = "pinecone"
ELASTICSEARCH = "elasticsearch"
OPENSEARCH = "opensearch"
PGVECTOR = "pgvector"
ORACLE23AI = "oracle23ai"
S3VECTOR = "s3vector"
WEAVIATE = "weaviate"
@@ -0,0 +1,28 @@
from datetime import datetime
KEYS_TO_EXCLUDE = ["content", "pages", "tables", "paragraphs", "sections", "figures"]
def filter_metadata(metadata: dict[str, any]) -> dict[str, any]:
metadata = {
key: value for key, value in metadata.items() if key not in KEYS_TO_EXCLUDE
}
return metadata
def process_metadata(
metadata: dict[str, any],
) -> dict[str, any]:
for key, value in metadata.items():
# Remove large fields
if key in KEYS_TO_EXCLUDE:
del metadata[key]
# Convert non-serializable fields to strings
if (
isinstance(value, datetime)
or isinstance(value, list)
or isinstance(value, dict)
):
metadata[key] = str(value)
return metadata
+128
View File
@@ -0,0 +1,128 @@
import logging
from typing import Optional
from open_webui.retrieval.web.main import SearchResult, get_filtered_results
from open_webui.env import SRC_LOG_LEVELS
log = logging.getLogger(__name__)
log.setLevel(SRC_LOG_LEVELS["RAG"])
"""
Azure AI Search integration for Open WebUI.
Documentation: https://learn.microsoft.com/en-us/python/api/overview/azure/search-documents-readme?view=azure-python
Required package: azure-search-documents
Install: pip install azure-search-documents
"""
def search_azure(
api_key: str,
endpoint: str,
index_name: str,
query: str,
count: int,
filter_list: Optional[list[str]] = None,
) -> list[SearchResult]:
"""
Search using Azure AI Search.
Args:
api_key: Azure Search API key (query key or admin key)
endpoint: Azure Search service endpoint (e.g., https://myservice.search.windows.net)
index_name: Name of the search index to query
query: Search query string
count: Number of results to return
filter_list: Optional list of domains to filter results
Returns:
List of SearchResult objects with link, title, and snippet
"""
try:
from azure.core.credentials import AzureKeyCredential
from azure.search.documents import SearchClient
except ImportError:
log.error(
"azure-search-documents package is not installed. "
"Install it with: pip install azure-search-documents"
)
raise ImportError(
"azure-search-documents is required for Azure AI Search. "
"Install it with: pip install azure-search-documents"
)
try:
# Create search client with API key authentication
credential = AzureKeyCredential(api_key)
search_client = SearchClient(
endpoint=endpoint, index_name=index_name, credential=credential
)
# Perform the search
results = search_client.search(search_text=query, top=count)
# Convert results to list and extract fields
search_results = []
for result in results:
# Azure AI Search returns documents with custom schemas
# We need to extract common fields that might represent URL, title, and content
# Common field names to look for:
result_dict = dict(result)
# Try to find URL field (common names)
link = (
result_dict.get("url")
or result_dict.get("link")
or result_dict.get("uri")
or result_dict.get("metadata_storage_path")
or ""
)
# Try to find title field (common names)
title = (
result_dict.get("title")
or result_dict.get("name")
or result_dict.get("metadata_title")
or result_dict.get("metadata_storage_name")
or None
)
# Try to find content/snippet field (common names)
snippet = (
result_dict.get("content")
or result_dict.get("snippet")
or result_dict.get("description")
or result_dict.get("summary")
or result_dict.get("text")
or None
)
# Truncate snippet if too long
if snippet and len(snippet) > 500:
snippet = snippet[:497] + "..."
if link: # Only add if we found a valid link
search_results.append(
{
"link": link,
"title": title,
"snippet": snippet,
}
)
# Apply domain filtering if specified
if filter_list:
search_results = get_filtered_results(search_results, filter_list)
# Convert to SearchResult objects
return [
SearchResult(
link=result["link"],
title=result.get("title"),
snippet=result.get("snippet"),
)
for result in search_results
]
except Exception as ex:
log.error(f"Azure AI Search error: {ex}")
raise ex
+3 -1
View File
@@ -36,7 +36,9 @@ def search_brave(
return [
SearchResult(
link=result["url"], title=result.get("title"), snippet=result.get("snippet")
link=result["url"],
title=result.get("title"),
snippet=result.get("description"),
)
for result in results[:count]
]
@@ -2,8 +2,8 @@ import logging
from typing import Optional
from open_webui.retrieval.web.main import SearchResult, get_filtered_results
from duckduckgo_search import DDGS
from duckduckgo_search.exceptions import RatelimitException
from ddgs import DDGS
from ddgs.exceptions import RatelimitException
from open_webui.env import SRC_LOG_LEVELS
log = logging.getLogger(__name__)
@@ -11,7 +11,10 @@ log.setLevel(SRC_LOG_LEVELS["RAG"])
def search_duckduckgo(
query: str, count: int, filter_list: Optional[list[str]] = None
query: str,
count: int,
filter_list: Optional[list[str]] = None,
concurrent_requests: Optional[int] = None,
) -> list[SearchResult]:
"""
Search using DuckDuckGo's Search API and return the results as a list of SearchResult objects.
@@ -25,6 +28,9 @@ def search_duckduckgo(
# Use the DDGS context manager to create a DDGS object
search_results = []
with DDGS() as ddgs:
if concurrent_requests:
ddgs.threads = concurrent_requests
# Use the ddgs.text() method to perform the search
try:
search_results = ddgs.text(
@@ -0,0 +1,62 @@
import logging
from typing import Optional, List
import requests
from fastapi import Request
from open_webui.env import SRC_LOG_LEVELS
from open_webui.retrieval.web.main import SearchResult, get_filtered_results
from open_webui.utils.headers import include_user_info_headers
log = logging.getLogger(__name__)
log.setLevel(SRC_LOG_LEVELS["RAG"])
def search_external(
request: Request,
external_url: str,
external_api_key: str,
query: str,
count: int,
filter_list: Optional[List[str]] = None,
user=None,
) -> List[SearchResult]:
try:
headers = {
"User-Agent": "Open WebUI (https://github.com/open-webui/open-webui) RAG Bot",
"Authorization": f"Bearer {external_api_key}",
}
headers = include_user_info_headers(headers, user)
chat_id = getattr(request.state, "chat_id", None)
if chat_id:
headers["X-OpenWebUI-Chat-Id"] = str(chat_id)
response = requests.post(
external_url,
headers=headers,
json={
"query": query,
"count": count,
},
)
response.raise_for_status()
results = response.json()
if filter_list:
results = get_filtered_results(results, filter_list)
results = [
SearchResult(
link=result.get("link"),
title=result.get("title"),
snippet=result.get("snippet"),
)
for result in results[:count]
]
log.info(f"External search results: {results}")
return results
except Exception as e:
log.error(f"Error in External search: {e}")
return []
@@ -0,0 +1,41 @@
import logging
from typing import Optional, List
from open_webui.retrieval.web.main import SearchResult, get_filtered_results
from open_webui.env import SRC_LOG_LEVELS
log = logging.getLogger(__name__)
log.setLevel(SRC_LOG_LEVELS["RAG"])
def search_firecrawl(
firecrawl_url: str,
firecrawl_api_key: str,
query: str,
count: int,
filter_list: Optional[List[str]] = None,
) -> List[SearchResult]:
try:
from firecrawl import FirecrawlApp
firecrawl = FirecrawlApp(api_key=firecrawl_api_key, api_url=firecrawl_url)
response = firecrawl.search(
query=query, limit=count, ignore_invalid_urls=True, timeout=count * 3
)
results = response.web
if filter_list:
results = get_filtered_results(results, filter_list)
results = [
SearchResult(
link=result.url,
title=result.title,
snippet=result.description,
)
for result in results[:count]
]
log.info(f"External search results: {results}")
return results
except Exception as e:
log.error(f"Error in External search: {e}")
return []
@@ -15,6 +15,7 @@ def search_google_pse(
query: str,
count: int,
filter_list: Optional[list[str]] = None,
referer: Optional[str] = None,
) -> list[SearchResult]:
"""Search using Google's Programmable Search Engine API and return the results as a list of SearchResult objects.
Handles pagination for counts greater than 10.
@@ -30,7 +31,11 @@ def search_google_pse(
list[SearchResult]: A list of SearchResult objects.
"""
url = "https://www.googleapis.com/customsearch/v1"
headers = {"Content-Type": "application/json"}
if referer:
headers["Referer"] = referer
all_results = []
start_index = 1 # Google PSE start parameter is 1-based
+22 -2
View File
@@ -5,18 +5,38 @@ from urllib.parse import urlparse
from pydantic import BaseModel
from open_webui.retrieval.web.utils import resolve_hostname
from open_webui.utils.misc import is_string_allowed
def get_filtered_results(results, filter_list):
if not filter_list:
return results
filtered_results = []
for result in results:
url = result.get("url") or result.get("link", "")
url = result.get("url") or result.get("link", "") or result.get("href", "")
if not validators.url(url):
continue
domain = urlparse(url).netloc
if any(domain.endswith(filtered_domain) for filtered_domain in filter_list):
if not domain:
continue
hostnames = [domain]
try:
ipv4_addresses, ipv6_addresses = resolve_hostname(domain)
hostnames.extend(ipv4_addresses)
hostnames.extend(ipv6_addresses)
except Exception:
pass
if is_string_allowed(hostnames, filter_list):
filtered_results.append(result)
continue
return filtered_results
@@ -0,0 +1,51 @@
import logging
from dataclasses import dataclass
from typing import Optional
import requests
from open_webui.env import SRC_LOG_LEVELS
from open_webui.retrieval.web.main import SearchResult
log = logging.getLogger(__name__)
log.setLevel(SRC_LOG_LEVELS["RAG"])
def search_ollama_cloud(
url: str,
api_key: str,
query: str,
count: int,
filter_list: Optional[list[str]] = None,
) -> list[SearchResult]:
"""Search using Ollama Search API and return the results as a list of SearchResult objects.
Args:
api_key (str): A Ollama Search API key
query (str): The query to search for
count (int): Number of results to return
filter_list (Optional[list[str]]): List of domains to filter results by
"""
log.info(f"Searching with Ollama for query: {query}")
headers = {"Authorization": f"Bearer {api_key}", "Content-Type": "application/json"}
payload = {"query": query, "max_results": count}
try:
response = requests.post(f"{url}/api/web_search", headers=headers, json=payload)
response.raise_for_status()
data = response.json()
results = data.get("results", [])
log.info(f"Found {len(results)} results")
return [
SearchResult(
link=result.get("url", ""),
title=result.get("title", ""),
snippet=result.get("content", ""),
)
for result in results
]
except Exception as e:
log.error(f"Error searching Ollama: {e}")
return []
+20 -2
View File
@@ -1,10 +1,20 @@
import logging
from typing import Optional, List
from typing import Optional, Literal
import requests
from open_webui.retrieval.web.main import SearchResult, get_filtered_results
from open_webui.env import SRC_LOG_LEVELS
MODELS = Literal[
"sonar",
"sonar-pro",
"sonar-reasoning",
"sonar-reasoning-pro",
"sonar-deep-research",
]
SEARCH_CONTEXT_USAGE_LEVELS = Literal["low", "medium", "high"]
log = logging.getLogger(__name__)
log.setLevel(SRC_LOG_LEVELS["RAG"])
@@ -14,6 +24,8 @@ def search_perplexity(
query: str,
count: int,
filter_list: Optional[list[str]] = None,
model: MODELS = "sonar",
search_context_usage: SEARCH_CONTEXT_USAGE_LEVELS = "medium",
) -> list[SearchResult]:
"""Search using Perplexity API and return the results as a list of SearchResult objects.
@@ -21,6 +33,9 @@ def search_perplexity(
api_key (str): A Perplexity API key
query (str): The query to search for
count (int): Maximum number of results to return
filter_list (Optional[list[str]]): List of domains to filter results
model (str): The Perplexity model to use (sonar, sonar-pro)
search_context_usage (str): Search context usage level (low, medium, high)
"""
@@ -33,7 +48,7 @@ def search_perplexity(
# Create payload for the API call
payload = {
"model": "sonar",
"model": model,
"messages": [
{
"role": "system",
@@ -43,6 +58,9 @@ def search_perplexity(
],
"temperature": 0.2, # Lower temperature for more factual responses
"stream": False,
"web_search_options": {
"search_context_usage": search_context_usage,
},
}
headers = {

Some files were not shown because too many files have changed in this diff Show More