Compare commits

...

3485 commits

Author SHA1 Message Date
Tim Baek
6f1486ffd0
Merge pull request #19466 from open-webui/dev
Some checks failed
Python CI / Format Backend (push) Has been cancelled
Frontend Build / Format & Build Frontend (push) Has been cancelled
Frontend Build / Frontend Unit Tests (push) Has been cancelled
Release / release (push) Has been cancelled
Deploy to HuggingFace Spaces / check-secret (push) Has been cancelled
Create and publish Docker images with specific build args / build-main-image (linux/amd64, ubuntu-latest) (push) Has been cancelled
Create and publish Docker images with specific build args / build-main-image (linux/arm64, ubuntu-24.04-arm) (push) Has been cancelled
Create and publish Docker images with specific build args / build-cuda-image (linux/amd64, ubuntu-latest) (push) Has been cancelled
Create and publish Docker images with specific build args / build-cuda-image (linux/arm64, ubuntu-24.04-arm) (push) Has been cancelled
Create and publish Docker images with specific build args / build-cuda126-image (linux/amd64, ubuntu-latest) (push) Has been cancelled
Create and publish Docker images with specific build args / build-cuda126-image (linux/arm64, ubuntu-24.04-arm) (push) Has been cancelled
Create and publish Docker images with specific build args / build-ollama-image (linux/amd64, ubuntu-latest) (push) Has been cancelled
Create and publish Docker images with specific build args / build-ollama-image (linux/arm64, ubuntu-24.04-arm) (push) Has been cancelled
Create and publish Docker images with specific build args / build-slim-image (linux/amd64, ubuntu-latest) (push) Has been cancelled
Create and publish Docker images with specific build args / build-slim-image (linux/arm64, ubuntu-24.04-arm) (push) Has been cancelled
Release to PyPI / release (push) Has been cancelled
Deploy to HuggingFace Spaces / deploy (push) Has been cancelled
Create and publish Docker images with specific build args / merge-main-images (push) Has been cancelled
Create and publish Docker images with specific build args / merge-cuda-images (push) Has been cancelled
Create and publish Docker images with specific build args / merge-cuda126-images (push) Has been cancelled
Create and publish Docker images with specific build args / merge-ollama-images (push) Has been cancelled
Create and publish Docker images with specific build args / merge-slim-images (push) Has been cancelled
0.6.41
2025-12-02 17:28:46 -05:00
Timothy Jaeryang Baek
73f7e91dec chore: format
Some checks are pending
Deploy to HuggingFace Spaces / check-secret (push) Waiting to run
Deploy to HuggingFace Spaces / deploy (push) Blocked by required conditions
Create and publish Docker images with specific build args / build-main-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-main-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / merge-main-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda126-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-ollama-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-slim-images (push) Blocked by required conditions
Python CI / Format Backend (push) Waiting to run
Frontend Build / Format & Build Frontend (push) Waiting to run
Frontend Build / Frontend Unit Tests (push) Waiting to run
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
Classic298
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
Classic298
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
Some checks are pending
Deploy to HuggingFace Spaces / check-secret (push) Waiting to run
Deploy to HuggingFace Spaces / deploy (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda126-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / build-main-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-main-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / merge-main-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-ollama-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-slim-images (push) Blocked by required conditions
Python CI / Format Backend (push) Waiting to run
Frontend Build / Format & Build Frontend (push) Waiting to run
Frontend Build / Frontend Unit Tests (push) Waiting to run
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
Poccia
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
Classic298
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
Some checks are pending
Deploy to HuggingFace Spaces / check-secret (push) Waiting to run
Deploy to HuggingFace Spaces / deploy (push) Blocked by required conditions
Create and publish Docker images with specific build args / build-main-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-main-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / merge-cuda126-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / build-cuda-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / merge-main-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-ollama-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-slim-images (push) Blocked by required conditions
Python CI / Format Backend (push) Waiting to run
Frontend Build / Format & Build Frontend (push) Waiting to run
Frontend Build / Frontend Unit Tests (push) Waiting to run
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
Some checks failed
Python CI / Format Backend (push) Has been cancelled
Frontend Build / Format & Build Frontend (push) Has been cancelled
Frontend Build / Frontend Unit Tests (push) Has been cancelled
Deploy to HuggingFace Spaces / check-secret (push) Has been cancelled
Create and publish Docker images with specific build args / build-main-image (linux/amd64, ubuntu-latest) (push) Has been cancelled
Create and publish Docker images with specific build args / build-main-image (linux/arm64, ubuntu-24.04-arm) (push) Has been cancelled
Create and publish Docker images with specific build args / build-cuda-image (linux/amd64, ubuntu-latest) (push) Has been cancelled
Create and publish Docker images with specific build args / build-cuda-image (linux/arm64, ubuntu-24.04-arm) (push) Has been cancelled
Create and publish Docker images with specific build args / build-cuda126-image (linux/amd64, ubuntu-latest) (push) Has been cancelled
Create and publish Docker images with specific build args / build-cuda126-image (linux/arm64, ubuntu-24.04-arm) (push) Has been cancelled
Create and publish Docker images with specific build args / build-ollama-image (linux/amd64, ubuntu-latest) (push) Has been cancelled
Create and publish Docker images with specific build args / build-ollama-image (linux/arm64, ubuntu-24.04-arm) (push) Has been cancelled
Create and publish Docker images with specific build args / build-slim-image (linux/amd64, ubuntu-latest) (push) Has been cancelled
Create and publish Docker images with specific build args / build-slim-image (linux/arm64, ubuntu-24.04-arm) (push) Has been cancelled
Deploy to HuggingFace Spaces / deploy (push) Has been cancelled
Create and publish Docker images with specific build args / merge-main-images (push) Has been cancelled
Create and publish Docker images with specific build args / merge-cuda-images (push) Has been cancelled
Create and publish Docker images with specific build args / merge-cuda126-images (push) Has been cancelled
Create and publish Docker images with specific build args / merge-ollama-images (push) Has been cancelled
Create and publish Docker images with specific build args / merge-slim-images (push) Has been cancelled
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
Some checks are pending
Python CI / Format Backend (push) Waiting to run
Deploy to HuggingFace Spaces / check-secret (push) Waiting to run
Deploy to HuggingFace Spaces / deploy (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-ollama-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-slim-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / build-main-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-main-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / merge-main-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda126-images (push) Blocked by required conditions
Frontend Build / Format & Build Frontend (push) Waiting to run
Frontend Build / Frontend Unit Tests (push) Waiting to run
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
Some checks are pending
Deploy to HuggingFace Spaces / check-secret (push) Waiting to run
Deploy to HuggingFace Spaces / deploy (push) Blocked by required conditions
Create and publish Docker images with specific build args / build-ollama-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / merge-cuda126-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-ollama-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-slim-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / build-main-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-main-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / merge-main-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda-images (push) Blocked by required conditions
Python CI / Format Backend (push) Waiting to run
Frontend Build / Format & Build Frontend (push) Waiting to run
Frontend Build / Frontend Unit Tests (push) Waiting to run
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
Some checks are pending
Deploy to HuggingFace Spaces / check-secret (push) Waiting to run
Deploy to HuggingFace Spaces / deploy (push) Blocked by required conditions
Create and publish Docker images with specific build args / build-main-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-main-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / merge-main-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda126-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-ollama-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-slim-images (push) Blocked by required conditions
Frontend Build / Format & Build Frontend (push) Waiting to run
Frontend Build / Frontend Unit Tests (push) Waiting to run
2025-11-29 01:05:39 -05:00
Timothy Jaeryang Baek
6c53bf7175 refac: styling
Some checks are pending
Deploy to HuggingFace Spaces / check-secret (push) Waiting to run
Deploy to HuggingFace Spaces / deploy (push) Blocked by required conditions
Create and publish Docker images with specific build args / build-main-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-main-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / merge-main-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda126-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-ollama-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-slim-images (push) Blocked by required conditions
Python CI / Format Backend (push) Waiting to run
Frontend Build / Format & Build Frontend (push) Waiting to run
Frontend Build / Frontend Unit Tests (push) Waiting to run
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
Some checks are pending
Deploy to HuggingFace Spaces / check-secret (push) Waiting to run
Deploy to HuggingFace Spaces / deploy (push) Blocked by required conditions
Create and publish Docker images with specific build args / build-main-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-main-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / merge-main-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda126-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-ollama-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-slim-images (push) Blocked by required conditions
Python CI / Format Backend (push) Waiting to run
Frontend Build / Format & Build Frontend (push) Waiting to run
Frontend Build / Frontend Unit Tests (push) Waiting to run
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
Some checks are pending
Deploy to HuggingFace Spaces / check-secret (push) Waiting to run
Deploy to HuggingFace Spaces / deploy (push) Blocked by required conditions
Create and publish Docker images with specific build args / build-main-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-main-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / merge-main-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda126-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-ollama-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-slim-images (push) Blocked by required conditions
Python CI / Format Backend (push) Waiting to run
Frontend Build / Format & Build Frontend (push) Waiting to run
Frontend Build / Frontend Unit Tests (push) Waiting to run
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)
Some checks are pending
Deploy to HuggingFace Spaces / check-secret (push) Waiting to run
Deploy to HuggingFace Spaces / deploy (push) Blocked by required conditions
Create and publish Docker images with specific build args / build-main-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-main-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / merge-main-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda126-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-ollama-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-slim-images (push) Blocked by required conditions
Frontend Build / Format & Build Frontend (push) Waiting to run
Frontend Build / Frontend Unit Tests (push) Waiting to run
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
Some checks are pending
Deploy to HuggingFace Spaces / check-secret (push) Waiting to run
Deploy to HuggingFace Spaces / deploy (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-main-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / build-main-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-main-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / merge-cuda-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / build-cuda-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / merge-cuda126-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-ollama-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-slim-images (push) Blocked by required conditions
Python CI / Format Backend (push) Waiting to run
Frontend Build / Format & Build Frontend (push) Waiting to run
Frontend Build / Frontend Unit Tests (push) Waiting to run
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 Baek
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
Some checks are pending
Deploy to HuggingFace Spaces / check-secret (push) Waiting to run
Deploy to HuggingFace Spaces / deploy (push) Blocked by required conditions
Create and publish Docker images with specific build args / build-main-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / merge-main-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda126-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-ollama-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-slim-images (push) Blocked by required conditions
Python CI / Format Backend (push) Waiting to run
Frontend Build / Frontend Unit Tests (push) Waiting to run
Frontend Build / Format & Build Frontend (push) Waiting to run
Create and publish Docker images with specific build args / build-main-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
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
Some checks failed
Frontend Build / Format & Build Frontend (push) Has been cancelled
Frontend Build / Frontend Unit Tests (push) Has been cancelled
Deploy to HuggingFace Spaces / check-secret (push) Has been cancelled
Create and publish Docker images with specific build args / build-main-image (linux/amd64, ubuntu-latest) (push) Has been cancelled
Create and publish Docker images with specific build args / build-main-image (linux/arm64, ubuntu-24.04-arm) (push) Has been cancelled
Create and publish Docker images with specific build args / build-cuda-image (linux/amd64, ubuntu-latest) (push) Has been cancelled
Create and publish Docker images with specific build args / build-cuda-image (linux/arm64, ubuntu-24.04-arm) (push) Has been cancelled
Create and publish Docker images with specific build args / build-cuda126-image (linux/amd64, ubuntu-latest) (push) Has been cancelled
Create and publish Docker images with specific build args / build-cuda126-image (linux/arm64, ubuntu-24.04-arm) (push) Has been cancelled
Create and publish Docker images with specific build args / build-ollama-image (linux/amd64, ubuntu-latest) (push) Has been cancelled
Create and publish Docker images with specific build args / build-ollama-image (linux/arm64, ubuntu-24.04-arm) (push) Has been cancelled
Create and publish Docker images with specific build args / build-slim-image (linux/amd64, ubuntu-latest) (push) Has been cancelled
Create and publish Docker images with specific build args / build-slim-image (linux/arm64, ubuntu-24.04-arm) (push) Has been cancelled
Python CI / Format Backend (push) Has been cancelled
Deploy to HuggingFace Spaces / deploy (push) Has been cancelled
Create and publish Docker images with specific build args / merge-main-images (push) Has been cancelled
Create and publish Docker images with specific build args / merge-cuda-images (push) Has been cancelled
Create and publish Docker images with specific build args / merge-cuda126-images (push) Has been cancelled
Create and publish Docker images with specific build args / merge-ollama-images (push) Has been cancelled
Create and publish Docker images with specific build args / merge-slim-images (push) Has been cancelled
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
Some checks are pending
Deploy to HuggingFace Spaces / check-secret (push) Waiting to run
Deploy to HuggingFace Spaces / deploy (push) Blocked by required conditions
Create and publish Docker images with specific build args / build-main-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-main-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / merge-main-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda126-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-ollama-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-slim-images (push) Blocked by required conditions
Python CI / Format Backend (push) Waiting to run
2025-11-25 08:00:30 -05:00
Aleix Dorca
1bfe2c92ba
Merge pull request #19464 from aleixdorca/dev
Some checks are pending
Deploy to HuggingFace Spaces / check-secret (push) Waiting to run
Deploy to HuggingFace Spaces / deploy (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-slim-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / build-main-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-main-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / merge-main-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda126-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-ollama-images (push) Blocked by required conditions
Python CI / Format Backend (push) Waiting to run
Frontend Build / Frontend Unit Tests (push) Waiting to run
Frontend Build / Format & Build Frontend (push) Waiting to run
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
Some checks failed
Release to PyPI / release (push) Has been cancelled
Release / release (push) Has been cancelled
Deploy to HuggingFace Spaces / check-secret (push) Has been cancelled
Create and publish Docker images with specific build args / build-main-image (linux/amd64, ubuntu-latest) (push) Has been cancelled
Create and publish Docker images with specific build args / build-main-image (linux/arm64, ubuntu-24.04-arm) (push) Has been cancelled
Create and publish Docker images with specific build args / build-cuda-image (linux/amd64, ubuntu-latest) (push) Has been cancelled
Create and publish Docker images with specific build args / build-cuda-image (linux/arm64, ubuntu-24.04-arm) (push) Has been cancelled
Create and publish Docker images with specific build args / build-cuda126-image (linux/amd64, ubuntu-latest) (push) Has been cancelled
Create and publish Docker images with specific build args / build-cuda126-image (linux/arm64, ubuntu-24.04-arm) (push) Has been cancelled
Create and publish Docker images with specific build args / build-ollama-image (linux/amd64, ubuntu-latest) (push) Has been cancelled
Create and publish Docker images with specific build args / build-ollama-image (linux/arm64, ubuntu-24.04-arm) (push) Has been cancelled
Create and publish Docker images with specific build args / build-slim-image (linux/amd64, ubuntu-latest) (push) Has been cancelled
Create and publish Docker images with specific build args / build-slim-image (linux/arm64, ubuntu-24.04-arm) (push) Has been cancelled
Python CI / Format Backend (push) Has been cancelled
Frontend Build / Format & Build Frontend (push) Has been cancelled
Frontend Build / Frontend Unit Tests (push) Has been cancelled
Deploy to HuggingFace Spaces / deploy (push) Has been cancelled
Create and publish Docker images with specific build args / merge-main-images (push) Has been cancelled
Create and publish Docker images with specific build args / merge-cuda-images (push) Has been cancelled
Create and publish Docker images with specific build args / merge-cuda126-images (push) Has been cancelled
Create and publish Docker images with specific build args / merge-ollama-images (push) Has been cancelled
Create and publish Docker images with specific build args / merge-slim-images (push) Has been cancelled
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 Baek
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
Some checks are pending
Deploy to HuggingFace Spaces / check-secret (push) Waiting to run
Deploy to HuggingFace Spaces / deploy (push) Blocked by required conditions
Create and publish Docker images with specific build args / build-main-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-main-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / merge-main-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda126-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-ollama-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-slim-images (push) Blocked by required conditions
Python CI / Format Backend (push) Waiting to run
Frontend Build / Format & Build Frontend (push) Waiting to run
Frontend Build / Frontend Unit Tests (push) Waiting to run
2025-11-24 16:01:13 -05:00
Classic298
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
Some checks are pending
Release / release (push) Waiting to run
Deploy to HuggingFace Spaces / check-secret (push) Waiting to run
Deploy to HuggingFace Spaces / deploy (push) Blocked by required conditions
Create and publish Docker images with specific build args / build-main-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-main-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / merge-main-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda126-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-ollama-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-slim-images (push) Blocked by required conditions
Python CI / Format Backend (push) Waiting to run
Frontend Build / Format & Build Frontend (push) Waiting to run
Frontend Build / Frontend Unit Tests (push) Waiting to run
Release to PyPI / release (push) Waiting to run
0.6.38
2025-11-24 07:00:31 -05:00
Timothy Jaeryang Baek
0f8729dea2 refac
Some checks are pending
Deploy to HuggingFace Spaces / check-secret (push) Waiting to run
Deploy to HuggingFace Spaces / deploy (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda126-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / build-main-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-main-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / merge-main-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-ollama-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-slim-images (push) Blocked by required conditions
Python CI / Format Backend (push) Waiting to run
2025-11-24 06:42:12 -05:00
Timothy Jaeryang Baek
7ad549b4fb Update docker-build.yaml
Some checks are pending
Deploy to HuggingFace Spaces / check-secret (push) Waiting to run
Deploy to HuggingFace Spaces / deploy (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-main-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda126-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-ollama-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-slim-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / build-main-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-main-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Python CI / Format Backend (push) Waiting to run
Frontend Build / Format & Build Frontend (push) Waiting to run
Frontend Build / Frontend Unit Tests (push) Waiting to run
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
Some checks are pending
Release / release (push) Waiting to run
Deploy to HuggingFace Spaces / check-secret (push) Waiting to run
Deploy to HuggingFace Spaces / deploy (push) Blocked by required conditions
Create and publish Docker images with specific build args / build-main-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-main-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / merge-main-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda126-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-ollama-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-slim-images (push) Blocked by required conditions
Python CI / Format Backend (push) Waiting to run
Frontend Build / Format & Build Frontend (push) Waiting to run
Frontend Build / Frontend Unit Tests (push) Waiting to run
Release to PyPI / release (push) Waiting to run
chore: format
2025-11-23 22:16:33 -05:00
Timothy Jaeryang Baek
819668b42d chore: format
Some checks are pending
Deploy to HuggingFace Spaces / check-secret (push) Waiting to run
Deploy to HuggingFace Spaces / deploy (push) Blocked by required conditions
Create and publish Docker images with specific build args / build-main-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-main-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / merge-main-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda126-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-ollama-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-slim-images (push) Blocked by required conditions
Python CI / Format Backend (push) Waiting to run
Frontend Build / Format & Build Frontend (push) Waiting to run
Frontend Build / Frontend Unit Tests (push) Waiting to run
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 Baek
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)
Some checks are pending
Deploy to HuggingFace Spaces / check-secret (push) Waiting to run
Deploy to HuggingFace Spaces / deploy (push) Blocked by required conditions
Create and publish Docker images with specific build args / build-main-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-main-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / merge-main-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda126-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-ollama-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-slim-images (push) Blocked by required conditions
Python CI / Format Backend (push) Waiting to run
Frontend Build / Format & Build Frontend (push) Waiting to run
Frontend Build / Frontend Unit Tests (push) Waiting to run
* 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 Baek
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
Some checks are pending
Deploy to HuggingFace Spaces / check-secret (push) Waiting to run
Deploy to HuggingFace Spaces / deploy (push) Blocked by required conditions
Create and publish Docker images with specific build args / build-main-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-main-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / merge-main-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda126-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-ollama-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-slim-images (push) Blocked by required conditions
Python CI / Format Backend (push) Waiting to run
Frontend Build / Format & Build Frontend (push) Waiting to run
Frontend Build / Frontend Unit Tests (push) Waiting to run
2025-11-22 21:33:14 -05:00
Classic298
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
Some checks are pending
Create and publish Docker images with specific build args / build-slim-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / merge-cuda-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda126-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-ollama-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-slim-images (push) Blocked by required conditions
Python CI / Format Backend (push) Waiting to run
Deploy to HuggingFace Spaces / check-secret (push) Waiting to run
Deploy to HuggingFace Spaces / deploy (push) Blocked by required conditions
Create and publish Docker images with specific build args / build-main-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-main-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / merge-main-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / build-cuda-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Frontend Build / Format & Build Frontend (push) Waiting to run
Frontend Build / Frontend Unit Tests (push) Waiting to run
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 Baek
b88f829dbb enh: clone system models
Some checks are pending
Deploy to HuggingFace Spaces / check-secret (push) Waiting to run
Deploy to HuggingFace Spaces / deploy (push) Blocked by required conditions
Create and publish Docker images with specific build args / build-main-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-main-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / merge-main-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda126-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-ollama-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-slim-images (push) Blocked by required conditions
Frontend Build / Format & Build Frontend (push) Waiting to run
Frontend Build / Frontend Unit Tests (push) Waiting to run
Co-Authored-By: G30 <50341825+silentoplayz@users.noreply.github.com>
2025-11-21 05:28:55 -05:00
Timothy Jaeryang Baek
2b58191b82 refac
Some checks are pending
Deploy to HuggingFace Spaces / check-secret (push) Waiting to run
Deploy to HuggingFace Spaces / deploy (push) Blocked by required conditions
Create and publish Docker images with specific build args / build-main-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-main-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / merge-main-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda126-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-ollama-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-slim-images (push) Blocked by required conditions
Python CI / Format Backend (push) Waiting to run
Frontend Build / Format & Build Frontend (push) Waiting to run
Frontend Build / Frontend Unit Tests (push) Waiting to run
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 Baek
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
Some checks are pending
Deploy to HuggingFace Spaces / check-secret (push) Waiting to run
Deploy to HuggingFace Spaces / deploy (push) Blocked by required conditions
Create and publish Docker images with specific build args / build-main-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-main-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / merge-main-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda126-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-ollama-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-slim-images (push) Blocked by required conditions
Python CI / Format Backend (push) Waiting to run
Frontend Build / Format & Build Frontend (push) Waiting to run
Frontend Build / Frontend Unit Tests (push) Waiting to run
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
Classic298
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
Classic298
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 Baek
4c28f19bdd enh/pref: convert markdown base64 images to urls
Some checks are pending
Deploy to HuggingFace Spaces / check-secret (push) Waiting to run
Deploy to HuggingFace Spaces / deploy (push) Blocked by required conditions
Create and publish Docker images with specific build args / build-main-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-main-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / merge-main-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda126-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-ollama-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-slim-images (push) Blocked by required conditions
Python CI / Format Backend (push) Waiting to run
Frontend Build / Frontend Unit Tests (push) Waiting to run
Frontend Build / Format & Build Frontend (push) Waiting to run
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)
Some checks are pending
Deploy to HuggingFace Spaces / check-secret (push) Waiting to run
Deploy to HuggingFace Spaces / deploy (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-ollama-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / build-main-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-main-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / merge-main-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda126-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-slim-images (push) Blocked by required conditions
Python CI / Format Backend (push) Waiting to run
Frontend Build / Format & Build Frontend (push) Waiting to run
Frontend Build / Frontend Unit Tests (push) Waiting to run
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
Some checks are pending
Deploy to HuggingFace Spaces / check-secret (push) Waiting to run
Deploy to HuggingFace Spaces / deploy (push) Blocked by required conditions
Create and publish Docker images with specific build args / build-main-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / merge-slim-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / build-main-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / merge-main-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda126-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-ollama-images (push) Blocked by required conditions
Python CI / Format Backend (push) Waiting to run
Frontend Build / Format & Build Frontend (push) Waiting to run
Frontend Build / Frontend Unit Tests (push) Waiting to run
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 Baek
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
Some checks are pending
Deploy to HuggingFace Spaces / check-secret (push) Waiting to run
Deploy to HuggingFace Spaces / deploy (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-ollama-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / build-main-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-main-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / merge-main-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda126-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-slim-images (push) Blocked by required conditions
Python CI / Format Backend (push) Waiting to run
Frontend Build / Format & Build Frontend (push) Waiting to run
Frontend Build / Frontend Unit Tests (push) Waiting to run
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 Baek
3d50d5ff77 refac
Co-Authored-By: G30 <50341825+silentoplayz@users.noreply.github.com>
2025-11-19 03:21:28 -05:00
Timothy Jaeryang Baek
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 Baek
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
Some checks are pending
Deploy to HuggingFace Spaces / check-secret (push) Waiting to run
Deploy to HuggingFace Spaces / deploy (push) Blocked by required conditions
Create and publish Docker images with specific build args / build-main-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / merge-slim-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / build-main-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / merge-main-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda126-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-ollama-images (push) Blocked by required conditions
Python CI / Format Backend (push) Waiting to run
Frontend Build / Format & Build Frontend (push) Waiting to run
Frontend Build / Frontend Unit Tests (push) Waiting to run
2025-11-18 18:37:57 -05:00
Jacob Leksan
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
Some checks are pending
Deploy to HuggingFace Spaces / check-secret (push) Waiting to run
Deploy to HuggingFace Spaces / deploy (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-ollama-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-slim-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / build-main-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-main-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / merge-main-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda126-images (push) Blocked by required conditions
Python CI / Format Backend (push) Waiting to run
Frontend Build / Format & Build Frontend (push) Waiting to run
Frontend Build / Frontend Unit Tests (push) Waiting to run
2025-11-18 04:42:09 -05:00
Timothy Jaeryang Baek
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
lazariv
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
Classic298
b4bc71d1bd
feat: add granular import/export permissions for workspace items (#19242)
Some checks are pending
Deploy to HuggingFace Spaces / check-secret (push) Waiting to run
Deploy to HuggingFace Spaces / deploy (push) Blocked by required conditions
Create and publish Docker images with specific build args / build-main-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-main-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / merge-slim-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / build-cuda126-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / merge-main-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda126-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-ollama-images (push) Blocked by required conditions
Python CI / Format Backend (push) Waiting to run
Frontend Build / Format & Build Frontend (push) Waiting to run
Frontend Build / Frontend Unit Tests (push) Waiting to run
* 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
Some checks are pending
Python CI / Format Backend (push) Waiting to run
Deploy to HuggingFace Spaces / check-secret (push) Waiting to run
Deploy to HuggingFace Spaces / deploy (push) Blocked by required conditions
Create and publish Docker images with specific build args / build-main-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-main-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / merge-main-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda126-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-ollama-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-slim-images (push) Blocked by required conditions
Frontend Build / Format & Build Frontend (push) Waiting to run
Frontend Build / Frontend Unit Tests (push) Waiting to run
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
Some checks are pending
Deploy to HuggingFace Spaces / check-secret (push) Waiting to run
Deploy to HuggingFace Spaces / deploy (push) Blocked by required conditions
Create and publish Docker images with specific build args / build-main-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-main-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / merge-main-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda126-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-ollama-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-slim-images (push) Blocked by required conditions
Frontend Build / Format & Build Frontend (push) Waiting to run
Frontend Build / Frontend Unit Tests (push) Waiting to run
2025-11-16 19:17:08 -05:00
Timothy Jaeryang Baek
7c2bed2c73 fix: UserValves contamination between multiple tools
Some checks are pending
Deploy to HuggingFace Spaces / check-secret (push) Waiting to run
Deploy to HuggingFace Spaces / deploy (push) Blocked by required conditions
Create and publish Docker images with specific build args / build-main-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-main-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / merge-main-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda126-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-ollama-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-slim-images (push) Blocked by required conditions
Python CI / Format Backend (push) Waiting to run
Frontend Build / Format & Build Frontend (push) Waiting to run
Frontend Build / Frontend Unit Tests (push) Waiting to run
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
Some checks are pending
Deploy to HuggingFace Spaces / check-secret (push) Waiting to run
Deploy to HuggingFace Spaces / deploy (push) Blocked by required conditions
Create and publish Docker images with specific build args / build-main-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-main-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / merge-main-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / build-cuda-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / merge-cuda-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda126-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-ollama-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-slim-images (push) Blocked by required conditions
Python CI / Format Backend (push) Waiting to run
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
Some checks are pending
Deploy to HuggingFace Spaces / check-secret (push) Waiting to run
Deploy to HuggingFace Spaces / deploy (push) Blocked by required conditions
Create and publish Docker images with specific build args / build-main-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-main-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / merge-main-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda126-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-ollama-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-slim-images (push) Blocked by required conditions
Python CI / Format Backend (push) Waiting to run
Frontend Build / Frontend Unit Tests (push) Waiting to run
Frontend Build / Format & Build Frontend (push) Waiting to run
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
Some checks failed
Deploy to HuggingFace Spaces / check-secret (push) Has been cancelled
Create and publish Docker images with specific build args / build-main-image (linux/amd64, ubuntu-latest) (push) Has been cancelled
Create and publish Docker images with specific build args / build-main-image (linux/arm64, ubuntu-24.04-arm) (push) Has been cancelled
Create and publish Docker images with specific build args / build-cuda-image (linux/amd64, ubuntu-latest) (push) Has been cancelled
Create and publish Docker images with specific build args / build-cuda-image (linux/arm64, ubuntu-24.04-arm) (push) Has been cancelled
Create and publish Docker images with specific build args / build-cuda126-image (linux/amd64, ubuntu-latest) (push) Has been cancelled
Create and publish Docker images with specific build args / build-cuda126-image (linux/arm64, ubuntu-24.04-arm) (push) Has been cancelled
Create and publish Docker images with specific build args / build-ollama-image (linux/amd64, ubuntu-latest) (push) Has been cancelled
Create and publish Docker images with specific build args / build-ollama-image (linux/arm64, ubuntu-24.04-arm) (push) Has been cancelled
Create and publish Docker images with specific build args / build-slim-image (linux/amd64, ubuntu-latest) (push) Has been cancelled
Create and publish Docker images with specific build args / build-slim-image (linux/arm64, ubuntu-24.04-arm) (push) Has been cancelled
Python CI / Format Backend (push) Has been cancelled
Frontend Build / Format & Build Frontend (push) Has been cancelled
Frontend Build / Frontend Unit Tests (push) Has been cancelled
Deploy to HuggingFace Spaces / deploy (push) Has been cancelled
Create and publish Docker images with specific build args / merge-main-images (push) Has been cancelled
Create and publish Docker images with specific build args / merge-cuda-images (push) Has been cancelled
Create and publish Docker images with specific build args / merge-cuda126-images (push) Has been cancelled
Create and publish Docker images with specific build args / merge-ollama-images (push) Has been cancelled
Create and publish Docker images with specific build args / merge-slim-images (push) Has been cancelled
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ê
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
Some checks are pending
Deploy to HuggingFace Spaces / check-secret (push) Waiting to run
Deploy to HuggingFace Spaces / deploy (push) Blocked by required conditions
Create and publish Docker images with specific build args / build-main-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-main-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / merge-main-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda126-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-ollama-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-slim-images (push) Blocked by required conditions
Python CI / Format Backend (push) Waiting to run
Frontend Build / Frontend Unit Tests (push) Waiting to run
Frontend Build / Format & Build Frontend (push) Waiting to run
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
Some checks are pending
Deploy to HuggingFace Spaces / check-secret (push) Waiting to run
Deploy to HuggingFace Spaces / deploy (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-ollama-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / build-main-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-main-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / merge-main-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda126-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-slim-images (push) Blocked by required conditions
Python CI / Format Backend (push) Waiting to run
Frontend Build / Format & Build Frontend (push) Waiting to run
Frontend Build / Frontend Unit Tests (push) Waiting to run
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
Classic298
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)
Some checks are pending
Deploy to HuggingFace Spaces / check-secret (push) Waiting to run
Deploy to HuggingFace Spaces / deploy (push) Blocked by required conditions
Create and publish Docker images with specific build args / build-main-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / merge-slim-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / build-main-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / merge-main-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda126-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-ollama-images (push) Blocked by required conditions
Python CI / Format Backend (push) Waiting to run
2025-11-12 15:05:17 -05:00
Classic298
6eea0d40ab
Feat: optionally disable password login endpoints (#19113)
Some checks are pending
Deploy to HuggingFace Spaces / check-secret (push) Waiting to run
Deploy to HuggingFace Spaces / deploy (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-slim-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / build-main-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-main-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / merge-main-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda126-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-ollama-images (push) Blocked by required conditions
Python CI / Format Backend (push) Waiting to run
* 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)
Some checks failed
Deploy to HuggingFace Spaces / check-secret (push) Waiting to run
Deploy to HuggingFace Spaces / deploy (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-slim-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / build-main-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-main-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / merge-main-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda126-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-ollama-images (push) Blocked by required conditions
Python CI / Format Backend (push) Waiting to run
Frontend Build / Format & Build Frontend (push) Has been cancelled
Frontend Build / Frontend Unit Tests (push) Has been cancelled
* 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
Some checks are pending
Deploy to HuggingFace Spaces / check-secret (push) Waiting to run
Deploy to HuggingFace Spaces / deploy (push) Blocked by required conditions
Create and publish Docker images with specific build args / build-main-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-main-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / merge-main-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda126-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-ollama-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-slim-images (push) Blocked by required conditions
Python CI / Format Backend (push) Waiting to run
Frontend Build / Format & Build Frontend (push) Waiting to run
Frontend Build / Frontend Unit Tests (push) Waiting to run
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
Some checks are pending
Python CI / Format Backend (push) Waiting to run
Deploy to HuggingFace Spaces / check-secret (push) Waiting to run
Deploy to HuggingFace Spaces / deploy (push) Blocked by required conditions
Create and publish Docker images with specific build args / build-main-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-main-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / merge-main-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda126-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-ollama-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-slim-images (push) Blocked by required conditions
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
Some checks failed
Deploy to HuggingFace Spaces / check-secret (push) Waiting to run
Deploy to HuggingFace Spaces / deploy (push) Blocked by required conditions
Create and publish Docker images with specific build args / build-main-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-main-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / merge-main-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda126-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-ollama-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-slim-images (push) Blocked by required conditions
Python CI / Format Backend (push) Waiting to run
Frontend Build / Format & Build Frontend (push) Has been cancelled
Frontend Build / Frontend Unit Tests (push) Has been cancelled
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 Baek
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 Baek
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
Some checks are pending
Deploy to HuggingFace Spaces / check-secret (push) Waiting to run
Deploy to HuggingFace Spaces / deploy (push) Blocked by required conditions
Create and publish Docker images with specific build args / build-main-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-main-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / merge-main-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda126-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-ollama-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-slim-images (push) Blocked by required conditions
Python CI / Format Backend (push) Waiting to run
Frontend Build / Frontend Unit Tests (push) Waiting to run
Frontend Build / Format & Build Frontend (push) Waiting to run
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 Skalicky
e72a7e4eca
perf Optimize Socket Emits Using User Rooms (#18996)
Some checks are pending
Deploy to HuggingFace Spaces / check-secret (push) Waiting to run
Deploy to HuggingFace Spaces / deploy (push) Blocked by required conditions
Create and publish Docker images with specific build args / build-main-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-main-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / merge-main-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda126-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-ollama-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-slim-images (push) Blocked by required conditions
Python CI / Format Backend (push) Waiting to run
Frontend Build / Format & Build Frontend (push) Waiting to run
Frontend Build / Frontend Unit Tests (push) Waiting to run
* 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
Some checks failed
Frontend Build / Format & Build Frontend (push) Has been cancelled
Frontend Build / Frontend Unit Tests (push) Has been cancelled
Deploy to HuggingFace Spaces / check-secret (push) Has been cancelled
Release to PyPI / release (push) Has been cancelled
Release / release (push) Has been cancelled
Create and publish Docker images with specific build args / build-main-image (linux/amd64, ubuntu-latest) (push) Has been cancelled
Create and publish Docker images with specific build args / build-main-image (linux/arm64, ubuntu-24.04-arm) (push) Has been cancelled
Create and publish Docker images with specific build args / build-cuda-image (linux/amd64, ubuntu-latest) (push) Has been cancelled
Create and publish Docker images with specific build args / build-cuda-image (linux/arm64, ubuntu-24.04-arm) (push) Has been cancelled
Create and publish Docker images with specific build args / build-cuda126-image (linux/amd64, ubuntu-latest) (push) Has been cancelled
Create and publish Docker images with specific build args / build-cuda126-image (linux/arm64, ubuntu-24.04-arm) (push) Has been cancelled
Create and publish Docker images with specific build args / build-ollama-image (linux/amd64, ubuntu-latest) (push) Has been cancelled
Create and publish Docker images with specific build args / build-ollama-image (linux/arm64, ubuntu-24.04-arm) (push) Has been cancelled
Create and publish Docker images with specific build args / build-slim-image (linux/amd64, ubuntu-latest) (push) Has been cancelled
Create and publish Docker images with specific build args / build-slim-image (linux/arm64, ubuntu-24.04-arm) (push) Has been cancelled
Python CI / Format Backend (push) Has been cancelled
Deploy to HuggingFace Spaces / deploy (push) Has been cancelled
Create and publish Docker images with specific build args / merge-main-images (push) Has been cancelled
Create and publish Docker images with specific build args / merge-cuda-images (push) Has been cancelled
Create and publish Docker images with specific build args / merge-cuda126-images (push) Has been cancelled
Create and publish Docker images with specific build args / merge-ollama-images (push) Has been cancelled
Create and publish Docker images with specific build args / merge-slim-images (push) Has been cancelled
0.6.36
2025-11-06 16:45:23 -05:00
Timothy Jaeryang Baek
49d57ae82b chore: format
Some checks are pending
Deploy to HuggingFace Spaces / check-secret (push) Waiting to run
Deploy to HuggingFace Spaces / deploy (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-ollama-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / build-main-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-main-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / merge-main-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda126-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-slim-images (push) Blocked by required conditions
Python CI / Format Backend (push) Waiting to run
Frontend Build / Format & Build Frontend (push) Waiting to run
Frontend Build / Frontend Unit Tests (push) Waiting to run
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 Baek
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
Some checks are pending
Release / release (push) Waiting to run
Deploy to HuggingFace Spaces / check-secret (push) Waiting to run
Deploy to HuggingFace Spaces / deploy (push) Blocked by required conditions
Create and publish Docker images with specific build args / build-main-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-main-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / merge-main-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda126-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-ollama-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-slim-images (push) Blocked by required conditions
Python CI / Format Backend (push) Waiting to run
Frontend Build / Format & Build Frontend (push) Waiting to run
Frontend Build / Frontend Unit Tests (push) Waiting to run
Release to PyPI / release (push) Waiting to run
0.6.35
2025-11-06 13:40:46 -05:00
Timothy Jaeryang Baek
d5fe0f6067 refac
Some checks are pending
Deploy to HuggingFace Spaces / check-secret (push) Waiting to run
Deploy to HuggingFace Spaces / deploy (push) Blocked by required conditions
Create and publish Docker images with specific build args / build-main-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-main-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / merge-main-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda126-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-ollama-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-slim-images (push) Blocked by required conditions
Python CI / Format Backend (push) Waiting to run
Frontend Build / Format & Build Frontend (push) Waiting to run
Frontend Build / Frontend Unit Tests (push) Waiting to run
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
Some checks are pending
Deploy to HuggingFace Spaces / check-secret (push) Waiting to run
Deploy to HuggingFace Spaces / deploy (push) Blocked by required conditions
Create and publish Docker images with specific build args / build-main-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-main-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / merge-main-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda126-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-ollama-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-slim-images (push) Blocked by required conditions
Python CI / Format Backend (push) Waiting to run
Frontend Build / Format & Build Frontend (push) Waiting to run
Frontend Build / Frontend Unit Tests (push) Waiting to run
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 Baek
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 Baek
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 Baek
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
Some checks are pending
Deploy to HuggingFace Spaces / check-secret (push) Waiting to run
Deploy to HuggingFace Spaces / deploy (push) Blocked by required conditions
Create and publish Docker images with specific build args / build-main-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-main-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / merge-main-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda126-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-ollama-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-slim-images (push) Blocked by required conditions
Frontend Build / Format & Build Frontend (push) Waiting to run
Frontend Build / Frontend Unit Tests (push) Waiting to run
2025-11-05 14:43:25 -05:00
Timothy Jaeryang Baek
cdf90222c7 refac
Some checks are pending
Deploy to HuggingFace Spaces / check-secret (push) Waiting to run
Deploy to HuggingFace Spaces / deploy (push) Blocked by required conditions
Create and publish Docker images with specific build args / build-main-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-main-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / merge-main-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda126-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-ollama-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-slim-images (push) Blocked by required conditions
Python CI / Format Backend (push) Waiting to run
Frontend Build / Frontend Unit Tests (push) Waiting to run
Frontend Build / Format & Build Frontend (push) Waiting to run
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
Some checks are pending
Deploy to HuggingFace Spaces / check-secret (push) Waiting to run
Deploy to HuggingFace Spaces / deploy (push) Blocked by required conditions
Create and publish Docker images with specific build args / build-main-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-main-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / merge-main-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda126-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-ollama-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-slim-images (push) Blocked by required conditions
Python CI / Format Backend (push) Waiting to run
Frontend Build / Format & Build Frontend (push) Waiting to run
Frontend Build / Frontend Unit Tests (push) Waiting to run
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
Some checks are pending
Deploy to HuggingFace Spaces / check-secret (push) Waiting to run
Deploy to HuggingFace Spaces / deploy (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-slim-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / build-main-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-main-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / merge-main-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda126-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-ollama-images (push) Blocked by required conditions
Python CI / Format Backend (push) Waiting to run
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
Some checks are pending
Deploy to HuggingFace Spaces / check-secret (push) Waiting to run
Deploy to HuggingFace Spaces / deploy (push) Blocked by required conditions
Create and publish Docker images with specific build args / build-main-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-main-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / merge-slim-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / build-cuda126-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / merge-main-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda126-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-ollama-images (push) Blocked by required conditions
Python CI / Format Backend (push) Waiting to run
Frontend Build / Format & Build Frontend (push) Waiting to run
Frontend Build / Frontend Unit Tests (push) Waiting to run
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
Some checks are pending
Deploy to HuggingFace Spaces / check-secret (push) Waiting to run
Deploy to HuggingFace Spaces / deploy (push) Blocked by required conditions
Create and publish Docker images with specific build args / build-main-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-main-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / merge-main-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda126-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-ollama-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-slim-images (push) Blocked by required conditions
Frontend Build / Format & Build Frontend (push) Waiting to run
Frontend Build / Frontend Unit Tests (push) Waiting to run
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
Some checks failed
Deploy to HuggingFace Spaces / check-secret (push) Waiting to run
Deploy to HuggingFace Spaces / deploy (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-ollama-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-slim-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / build-main-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-main-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / merge-main-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda126-images (push) Blocked by required conditions
Frontend Build / Format & Build Frontend (push) Waiting to run
Frontend Build / Frontend Unit Tests (push) Waiting to run
Python CI / Format Backend (push) Has been cancelled
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
Some checks are pending
Deploy to HuggingFace Spaces / check-secret (push) Waiting to run
Deploy to HuggingFace Spaces / deploy (push) Blocked by required conditions
Create and publish Docker images with specific build args / build-main-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-main-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / merge-main-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / build-cuda-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / merge-cuda-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda126-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-ollama-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-slim-images (push) Blocked by required conditions
Frontend Build / Format & Build Frontend (push) Waiting to run
Frontend Build / Frontend Unit Tests (push) Waiting to run
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
Some checks are pending
Deploy to HuggingFace Spaces / check-secret (push) Waiting to run
Deploy to HuggingFace Spaces / deploy (push) Blocked by required conditions
Create and publish Docker images with specific build args / build-main-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-main-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / merge-cuda126-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / build-cuda-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / merge-main-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-ollama-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-slim-images (push) Blocked by required conditions
Python CI / Format Backend (push) Waiting to run
Frontend Build / Frontend Unit Tests (push) Waiting to run
Frontend Build / Format & Build Frontend (push) Waiting to run
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
Some checks are pending
Deploy to HuggingFace Spaces / check-secret (push) Waiting to run
Deploy to HuggingFace Spaces / deploy (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda126-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / build-main-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-main-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / merge-main-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-ollama-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-slim-images (push) Blocked by required conditions
Python CI / Format Backend (push) Waiting to run
Frontend Build / Format & Build Frontend (push) Waiting to run
Frontend Build / Frontend Unit Tests (push) Waiting to run
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
Some checks are pending
Deploy to HuggingFace Spaces / check-secret (push) Waiting to run
Deploy to HuggingFace Spaces / deploy (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-ollama-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-slim-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / build-main-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-main-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / merge-cuda126-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / build-cuda-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / merge-main-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda-images (push) Blocked by required conditions
Python CI / Format Backend (push) Waiting to run
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
Some checks failed
Deploy to HuggingFace Spaces / check-secret (push) Waiting to run
Deploy to HuggingFace Spaces / deploy (push) Blocked by required conditions
Create and publish Docker images with specific build args / build-main-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-main-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / merge-main-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda126-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-ollama-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-slim-images (push) Blocked by required conditions
Python CI / Format Backend (push) Waiting to run
Frontend Build / Format & Build Frontend (push) Has been cancelled
Frontend Build / Frontend Unit Tests (push) Has been cancelled
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
Some checks failed
Deploy to HuggingFace Spaces / check-secret (push) Has been cancelled
Create and publish Docker images with specific build args / build-main-image (linux/amd64, ubuntu-latest) (push) Has been cancelled
Create and publish Docker images with specific build args / build-main-image (linux/arm64, ubuntu-24.04-arm) (push) Has been cancelled
Create and publish Docker images with specific build args / build-cuda-image (linux/amd64, ubuntu-latest) (push) Has been cancelled
Create and publish Docker images with specific build args / build-cuda-image (linux/arm64, ubuntu-24.04-arm) (push) Has been cancelled
Create and publish Docker images with specific build args / build-cuda126-image (linux/amd64, ubuntu-latest) (push) Has been cancelled
Create and publish Docker images with specific build args / build-cuda126-image (linux/arm64, ubuntu-24.04-arm) (push) Has been cancelled
Create and publish Docker images with specific build args / build-ollama-image (linux/amd64, ubuntu-latest) (push) Has been cancelled
Create and publish Docker images with specific build args / build-ollama-image (linux/arm64, ubuntu-24.04-arm) (push) Has been cancelled
Create and publish Docker images with specific build args / build-slim-image (linux/amd64, ubuntu-latest) (push) Has been cancelled
Create and publish Docker images with specific build args / build-slim-image (linux/arm64, ubuntu-24.04-arm) (push) Has been cancelled
Frontend Build / Format & Build Frontend (push) Has been cancelled
Frontend Build / Frontend Unit Tests (push) Has been cancelled
Deploy to HuggingFace Spaces / deploy (push) Has been cancelled
Create and publish Docker images with specific build args / merge-main-images (push) Has been cancelled
Create and publish Docker images with specific build args / merge-cuda-images (push) Has been cancelled
Create and publish Docker images with specific build args / merge-cuda126-images (push) Has been cancelled
Create and publish Docker images with specific build args / merge-ollama-images (push) Has been cancelled
Create and publish Docker images with specific build args / merge-slim-images (push) Has been cancelled
2025-10-28 17:31:38 -07:00
Timothy Jaeryang Baek
a0068c4a17 refac
Some checks are pending
Deploy to HuggingFace Spaces / check-secret (push) Waiting to run
Deploy to HuggingFace Spaces / deploy (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / build-main-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-main-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / merge-main-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / build-cuda-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / merge-cuda126-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-ollama-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-slim-images (push) Blocked by required conditions
Frontend Build / Frontend Unit Tests (push) Waiting to run
Frontend Build / Format & Build Frontend (push) Waiting to run
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
Some checks failed
Deploy to HuggingFace Spaces / check-secret (push) Waiting to run
Deploy to HuggingFace Spaces / deploy (push) Blocked by required conditions
Create and publish Docker images with specific build args / build-main-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-main-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / merge-main-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda126-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-ollama-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-slim-images (push) Blocked by required conditions
Frontend Build / Format & Build Frontend (push) Waiting to run
Frontend Build / Frontend Unit Tests (push) Waiting to run
Python CI / Format Backend (push) Has been cancelled
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
Some checks are pending
Deploy to HuggingFace Spaces / check-secret (push) Waiting to run
Deploy to HuggingFace Spaces / deploy (push) Blocked by required conditions
Create and publish Docker images with specific build args / build-main-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / merge-slim-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / build-main-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / merge-main-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda126-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-ollama-images (push) Blocked by required conditions
Python CI / Format Backend (push) Waiting to run
Frontend Build / Format & Build Frontend (push) Waiting to run
Frontend Build / Frontend Unit Tests (push) Waiting to run
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
Some checks are pending
Deploy to HuggingFace Spaces / check-secret (push) Waiting to run
Deploy to HuggingFace Spaces / deploy (push) Blocked by required conditions
Create and publish Docker images with specific build args / build-main-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-main-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / merge-main-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / build-cuda-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / merge-cuda-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda126-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-ollama-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-slim-images (push) Blocked by required conditions
Frontend Build / Frontend Unit Tests (push) Waiting to run
Frontend Build / Format & Build Frontend (push) Waiting to run
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
Some checks are pending
Deploy to HuggingFace Spaces / check-secret (push) Waiting to run
Deploy to HuggingFace Spaces / deploy (push) Blocked by required conditions
Create and publish Docker images with specific build args / build-main-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-main-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / merge-main-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda126-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-ollama-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-slim-images (push) Blocked by required conditions
Python CI / Format Backend (push) Waiting to run
Frontend Build / Format & Build Frontend (push) Waiting to run
Frontend Build / Frontend Unit Tests (push) Waiting to run
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
Some checks are pending
Deploy to HuggingFace Spaces / check-secret (push) Waiting to run
Deploy to HuggingFace Spaces / deploy (push) Blocked by required conditions
Create and publish Docker images with specific build args / build-main-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-main-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / merge-main-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda126-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-ollama-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-slim-images (push) Blocked by required conditions
Python CI / Format Backend (push) Waiting to run
Frontend Build / Frontend Unit Tests (push) Waiting to run
Frontend Build / Format & Build Frontend (push) Waiting to run
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
Some checks failed
Deploy to HuggingFace Spaces / check-secret (push) Has been cancelled
Create and publish Docker images with specific build args / build-main-image (linux/amd64, ubuntu-latest) (push) Has been cancelled
Create and publish Docker images with specific build args / build-main-image (linux/arm64, ubuntu-24.04-arm) (push) Has been cancelled
Create and publish Docker images with specific build args / build-cuda-image (linux/amd64, ubuntu-latest) (push) Has been cancelled
Create and publish Docker images with specific build args / build-cuda-image (linux/arm64, ubuntu-24.04-arm) (push) Has been cancelled
Create and publish Docker images with specific build args / build-cuda126-image (linux/amd64, ubuntu-latest) (push) Has been cancelled
Create and publish Docker images with specific build args / build-cuda126-image (linux/arm64, ubuntu-24.04-arm) (push) Has been cancelled
Create and publish Docker images with specific build args / build-ollama-image (linux/amd64, ubuntu-latest) (push) Has been cancelled
Create and publish Docker images with specific build args / build-ollama-image (linux/arm64, ubuntu-24.04-arm) (push) Has been cancelled
Create and publish Docker images with specific build args / build-slim-image (linux/amd64, ubuntu-latest) (push) Has been cancelled
Create and publish Docker images with specific build args / build-slim-image (linux/arm64, ubuntu-24.04-arm) (push) Has been cancelled
Frontend Build / Format & Build Frontend (push) Has been cancelled
Frontend Build / Frontend Unit Tests (push) Has been cancelled
Deploy to HuggingFace Spaces / deploy (push) Has been cancelled
Create and publish Docker images with specific build args / merge-main-images (push) Has been cancelled
Create and publish Docker images with specific build args / merge-cuda-images (push) Has been cancelled
Create and publish Docker images with specific build args / merge-cuda126-images (push) Has been cancelled
Create and publish Docker images with specific build args / merge-ollama-images (push) Has been cancelled
Create and publish Docker images with specific build args / merge-slim-images (push) Has been cancelled
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 Baek
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
EntropyYue
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
Tim Jaeryang Baek
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
Tim Jaeryang Baek
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 Baek
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 Baek
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 Baek
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 Baek
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 Baek
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 bb6864dd12 (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 Baek
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 Baek
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 Baek
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 Baek
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 Baek
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 Baek
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 Baek
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 Baek
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 Baek
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 Baek
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 Baek
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
Dieu
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 Baek
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 Baek
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 Baek
6176dba3c9 refac
Co-Authored-By: Classic298 <27028174+Classic298@users.noreply.github.com>
2025-07-13 01:50:01 +04:00
Timothy Jaeryang Baek
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 Baek
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 Baek
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 Baek
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 Baek
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
Timothy Jaeryang Baek
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 Baek
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 Baek
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 Baek
f3cae94028 fix: bypass webloader
Co-Authored-By: WilliamGates <3852641+williamgateszhao@users.noreply.github.com>
2025-06-16 16:17:52 +04:00
Timothy Jaeryang Baek
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 Baek
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 Agundez
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 Agundez
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 Baek
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
YuQX
24be96a6d4
Merge pull request #1 from open-webui/main
Upgrade to v0.6.11
2025-05-28 22:24:33 +08:00
Derek Wischusen
42be1f956a Add Azure OpenAI embedding support 2025-05-19 22:58:04 -04:00
hdnh2006
0afe972bc6 embeddings function added 100% OpenAI compatible 2025-02-14 17:57:47 +01:00
680 changed files with 116839 additions and 40522 deletions

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

48
.gitattributes vendored
View file

@ -1 +1,49 @@
# 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

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 youve 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
@ -126,6 +134,7 @@ body:
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: |
Example (include every detail):
@ -163,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!

View file

@ -8,8 +8,19 @@ 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 youre unsure, start a discussion post first.
#### 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
@ -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

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:

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
@ -73,4 +76,7 @@
### Contributor License Agreement
By submitting this pull request, I confirm that I have read and fully agree to the [Contributor License Agreement (CLA)](/CONTRIBUTOR_LICENSE_AGREEMENT), and I am providing my contributions under its terms.
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.

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({

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

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 }}

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 }}'

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

View file

@ -16,15 +16,15 @@ jobs:
id-token: write
steps:
- name: Checkout repository
uses: actions/checkout@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@v4
- 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

5
.gitignore vendored
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

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"
}

File diff suppressed because it is too large Load diff

View file

@ -3,6 +3,8 @@
# 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=cu128
# any sentence transformer model; models to use can be found at https://huggingface.co/models?library=sentence-transformers
@ -24,13 +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}
@ -43,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
@ -54,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}
@ -108,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
@ -146,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
@ -170,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

53
LICENSE_HISTORY Normal file
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
LICENSE_NOTICE Normal file
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.

View file

@ -12,10 +12,12 @@
**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!**
@ -35,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.
@ -61,33 +77,6 @@ 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!
## Sponsors 🙌
#### Emerald
<table>
<tr>
<td>
<a href="https://n8n.io/" target="_blank">
<img src="https://docs.openwebui.com/sponsors/logos/n8n.png" alt="n8n" style="width: 8rem; height: 8rem; border-radius: .75rem;" />
</a>
</td>
<td>
N8N • Does your interface have a backend yet?<br>Try <a href="https://n8n.io/">n8n</a>
</td>
</tr>
<tr>
<td>
<a href="https://warp.dev/open-webui" target="_blank">
<img src="https://docs.openwebui.com/sponsors/logos/warp.png" alt="n8n" style="width: 8rem; height: 8rem; border-radius: .75rem;" />
</a>
</td>
<td>
Wrap • The intelligent terminal for developers
</td>
</tr>
</table>
---
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!
@ -181,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).
@ -232,7 +223,7 @@ Discover upcoming features on our roadmap in the [Open WebUI Documentation](http
## License 📜
This project is licensed under the [Open WebUI License](LICENSE), a revised BSD-3-Clause license. You receive all the same rights as the classic BSD-3 license: you can use, modify, and distribute the software, including in proprietary and commercial products, with minimal restrictions. The only additional requirement is to preserve the "Open WebUI" branding, as detailed in the LICENSE file. For full terms, see the [LICENSE](LICENSE) document. 📄
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 💬

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

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.

File diff suppressed because it is too large Load diff

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"

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,6 +414,14 @@ 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
)
@ -354,6 +431,17 @@ WEBUI_AUTH_TRUSTED_GROUPS_HEADER = os.environ.get(
)
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"
)
@ -394,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", "")
@ -504,19 +760,32 @@ else:
# 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:
@ -537,9 +806,34 @@ AUDIT_EXCLUDED_PATHS = [path.lstrip("/") for path in AUDIT_EXCLUDED_PATHS]
####################################
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", ""
@ -547,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

View file

@ -19,12 +19,14 @@ 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
@ -46,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,
)
@ -59,8 +61,20 @@ def get_function_module_by_id(request: Request, pipe_id: str):
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
@ -69,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
@ -218,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__,
@ -227,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,
@ -253,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)

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(

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_user=True, 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_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")
# 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

File diff suppressed because it is too large Load diff

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)

View file

@ -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")

View file

@ -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")

View file

@ -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")

View file

@ -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")

View file

@ -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")

View file

@ -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")

View 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

View file

@ -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

View file

@ -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")

View file

@ -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")

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,7 +113,9 @@ 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)
@ -138,7 +126,7 @@ class AuthsTable:
with get_db() as db:
auth = db.query(Auth).filter_by(id=user.id, active=True).first()
if auth:
if verify_password(password, auth.password):
if verify_password(auth.password):
return user
else:
return None

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())

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)
@ -395,7 +511,10 @@ class ChatTable:
order_by = filter.get("order_by")
direction = filter.get("direction")
if order_by and direction and getattr(Chat, order_by):
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":
@ -456,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)
@ -506,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
@ -580,7 +713,7 @@ 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(
@ -596,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)
@ -605,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
@ -662,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:
@ -723,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)
@ -732,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]
@ -861,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:
@ -938,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:

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 [

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:

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()

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
@ -109,8 +132,8 @@ class FunctionsTable:
return None
def sync_functions(
self, user_id: str, functions: list[FunctionModel]
) -> list[FunctionModel]:
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:
@ -164,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]:
@ -235,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]:
@ -250,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(

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,59 +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(GroupMember).filter(
GroupMember.group_id == group.id, GroupMember.user_id == user_id
).delete()
db.query(Group).filter_by(id=group.id).update(
{
"user_ids": group.user_ids,
"updated_at": int(time.time()),
}
{"updated_at": int(time.time())}
)
db.commit()
return True
except Exception:
return False
def sync_user_groups_by_group_names(
self, user_id: str, group_names: list[str]
) -> bool:
with get_db() as db:
try:
groups = db.query(Group).filter(Group.name.in_(group_names)).all()
group_ids = [group.id for group in groups]
# Remove user from groups not in the new list
existing_groups = self.get_groups_by_member_id(user_id)
for group in existing_groups:
if group.id not in group_ids:
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()),
}
)
# Add user to new groups
for group in groups:
if user_id not in group.user_ids:
group.user_ids.append(user_id)
db.query(Group).filter_by(id=group.id).update(
{
"user_ids": group.user_ids,
"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()

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]:

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

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()]

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()

View file

@ -2,8 +2,10 @@ 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
@ -21,7 +23,7 @@ from sqlalchemy.sql import exists
class Note(Base):
__tablename__ = "note"
id = Column(Text, primary_key=True)
id = Column(Text, primary_key=True, unique=True)
user_id = Column(Text)
title = Column(Text)
@ -62,6 +64,13 @@ class NoteForm(BaseModel):
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
@ -89,37 +98,111 @@ class NoteTable:
db.commit()
return note
def get_notes(self) -> list[NoteModel]:
def get_notes(
self, skip: Optional[int] = None, limit: Optional[int] = None
) -> list[NoteModel]:
with get_db() as db:
notes = db.query(Note).order_by(Note.updated_at.desc()).all()
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, permission: str = "write"
self,
user_id: str,
skip: Optional[int] = None,
limit: Optional[int] = None,
) -> list[NoteModel]:
notes = self.get_notes()
return [
note
for note in notes
if note.user_id == user_id
or has_access(user_id, permission, note.access_control)
]
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: NoteForm) -> Optional[NoteModel]:
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
note.title = form_data.title
note.data = form_data.data
note.meta = form_data.meta
note.access_control = form_data.access_control
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()

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()

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(

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"),)

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]:

View file

@ -4,62 +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 or_
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)
@ -69,23 +146,78 @@ class UserModel(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[UserModel]
users: list[UserModelResponse]
total: int
class UserResponse(BaseModel):
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
@ -95,6 +227,7 @@ class UserRoleUpdateForm(BaseModel):
class UserUpdateForm(BaseModel):
role: str
name: str
email: str
profile_image_url: str
@ -109,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())
@ -145,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
@ -158,12 +296,23 @@ 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(
@ -171,8 +320,9 @@ class UsersTable:
filter: Optional[dict] = None,
skip: Optional[int] = None,
limit: Optional[int] = None,
) -> UserListResponse:
) -> dict:
with get_db() as db:
# Join GroupMember so we can order by group_id when requested
query = db.query(User)
if filter:
@ -185,14 +335,76 @@ class UsersTable:
)
)
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 == "name":
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())
@ -225,18 +437,32 @@ class UsersTable:
else:
query = query.order_by(User.created_at.desc())
if skip:
# 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": db.query(User).count(),
"total": total,
}
def get_users_by_user_ids(self, user_ids: list[str]) -> list[UserModel]:
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[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]
@ -245,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:
@ -269,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:
@ -279,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]:
@ -294,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(
@ -307,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
@ -329,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]:
@ -369,23 +644,45 @@ 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()
@ -399,5 +696,23 @@ class UsersTable:
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()

View file

@ -15,24 +15,28 @@ class DatalabMarkerLoader:
self,
file_path: str,
api_key: str,
langs: Optional[str] = None,
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.langs = langs
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:
@ -60,7 +64,7 @@ class DatalabMarkerLoader:
return mime_map.get(ext, "application/octet-stream")
def check_marker_request_status(self, request_id: str) -> dict:
url = f"https://www.datalab.to/api/v1/marker/{request_id}"
url = f"{self.api_base_url}/{request_id}"
headers = {"X-Api-Key": self.api_key}
try:
response = requests.get(url, headers=headers)
@ -81,22 +85,24 @@ class DatalabMarkerLoader:
)
def load(self) -> List[Document]:
url = "https://www.datalab.to/api/v1/marker"
filename = os.path.basename(self.file_path)
mime_type = self._get_mime_type(filename)
headers = {"X-Api-Key": self.api_key}
form_data = {
"langs": self.langs,
"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}}}"
)
@ -105,7 +111,10 @@ class DatalabMarkerLoader:
with open(self.file_path, "rb") as f:
files = {"file": (filename, f, mime_type)}
response = requests.post(
url, data=form_data, files=files, headers=headers
f"{self.api_base_url}",
data=form_data,
files=files,
headers=headers,
)
response.raise_for_status()
result = response.json()
@ -133,74 +142,92 @@ class DatalabMarkerLoader:
check_url = result.get("request_check_url")
request_id = result.get("request_id")
if not check_url:
raise HTTPException(
status.HTTP_502_BAD_GATEWAY, detail="No request_check_url returned."
)
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",
# 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}"
)
}
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"
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"Marker processing failed: {error_msg}",
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:
raise HTTPException(
status.HTTP_504_GATEWAY_TIMEOUT, detail="Marker processing timed out"
)
# 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 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}",
)
content_key = self.output_format.lower()
raw_content = poll_result.get(content_key)
if content_key == "json":
if self.output_format.lower() == "json":
full_text = json.dumps(raw_content, indent=2)
elif content_key in {"markdown", "html"}:
elif self.output_format.lower() in {"markdown", "html"}:
full_text = str(raw_content).strip()
else:
raise HTTPException(
@ -211,14 +238,14 @@ class DatalabMarkerLoader:
if not full_text:
raise HTTPException(
status.HTTP_400_BAD_REQUEST,
detail="Datalab Marker returned empty content",
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(content_key, "txt")
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)
@ -231,13 +258,13 @@ class DatalabMarkerLoader:
metadata = {
"source": filename,
"output_format": poll_result.get("output_format", self.output_format),
"page_count": poll_result.get("page_count", 0),
"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 = poll_result.get("images", {})
images = final_result.get("images", {})
if images:
metadata["image_count"] = len(images)
metadata["images"] = json.dumps(list(images.keys()))

View file

@ -1,9 +1,11 @@
import requests
import logging
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__)
@ -17,6 +19,7 @@ class ExternalDocumentLoader(BaseLoader):
url: str,
api_key: str,
mime_type=None,
user=None,
**kwargs,
) -> None:
self.url = url
@ -25,7 +28,9 @@ class ExternalDocumentLoader(BaseLoader):
self.file_path = file_path
self.mime_type = mime_type
def load(self) -> list[Document]:
self.user = user
def load(self) -> List[Document]:
with open(self.file_path, "rb") as f:
data = f.read()
@ -36,23 +41,51 @@ class ExternalDocumentLoader(BaseLoader):
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]
r = requests.put(f"{url}/process", data=data, headers=headers)
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 r.ok:
res = r.json()
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")
if res:
return [
Document(
page_content=res.get("page_content"),
metadata=res.get("metadata"),
)
]
else:
raise Exception("Error loading document: No content returned")
else:
raise Exception(f"Error loading document: {r.status_code} {r.text}")
raise Exception(
f"Error loading document: {response.status_code} {response.text}"
)

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,
@ -25,6 +27,7 @@ from open_webui.retrieval.loaders.external_document import ExternalDocumentLoade
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
@ -76,7 +79,6 @@ known_source_ext = [
"swift",
"vue",
"svelte",
"msg",
"ex",
"exs",
"erl",
@ -130,8 +132,9 @@ class TikaLoader:
class DoclingLoader:
def __init__(self, url, file_path=None, mime_type=None, params=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
@ -139,6 +142,10 @@ class DoclingLoader:
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,
@ -147,28 +154,15 @@ class DoclingLoader:
)
}
params = {
"image_export_mode": "placeholder",
"table_mode": "accurate",
}
if self.params:
if self.params.get("do_picture_classification"):
params["do_picture_classification"] = self.params.get(
"do_picture_classification"
)
if self.params.get("ocr_engine") and self.params.get("ocr_lang"):
params["ocr_engine"] = self.params.get("ocr_engine")
params["ocr_lang"] = [
lang.strip()
for lang in self.params.get("ocr_lang").split(",")
if lang.strip()
]
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", {})
@ -177,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}"
@ -194,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(
@ -211,7 +205,10 @@ 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):
@ -227,6 +224,7 @@ class Loader:
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):
@ -235,7 +233,6 @@ class Loader:
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 (
@ -263,10 +260,15 @@ class Loader:
"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"],
langs=self.kwargs.get("DATALAB_MARKER_LANGS"),
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),
@ -277,6 +279,7 @@ class Loader:
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"
),
@ -285,38 +288,58 @@ class Loader:
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={
"ocr_engine": self.kwargs.get("DOCLING_OCR_ENGINE"),
"ocr_lang": self.kwargs.get("DOCLING_OCR_LANG"),
"do_picture_classification": self.kwargs.get(
"DOCLING_DO_PICTURE_DESCRIPTION"
),
},
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"
@ -325,16 +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
)
elif (
self.engine == "external"
and self.kwargs.get("MISTRAL_OCR_API_KEY") != ""
and file_ext
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":
@ -371,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:

View file

@ -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

View file

@ -20,12 +20,19 @@ class MistralLoader:
"""
Enhanced Mistral OCR loader with both sync and async support.
Loads documents by processing them through the Mistral OCR API.
"""
BASE_API_URL = "https://api.mistral.ai/v1"
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
"""
def __init__(
self,
base_url: str,
api_key: str,
file_path: str,
timeout: int = 300, # 5 minutes default
@ -47,23 +54,49 @@ class MistralLoader:
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.timeout = timeout
self.max_retries = max_retries
self.debug = enable_debug_logging
# Pre-compute file info for performance
# 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",
"User-Agent": "OpenWebUI-MistralLoader/2.0", # Helps API provider track usage
}
def _debug_log(self, message: str, *args) -> None:
"""Conditional debug logging for performance."""
"""
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)
@ -115,53 +148,118 @@ class MistralLoader:
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):
"""Synchronous retry logic with exponential backoff."""
"""
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 (requests.exceptions.RequestException, Exception) as e:
if attempt == self.max_retries - 1:
except Exception as e:
if attempt == self.max_retries - 1 or not self._is_retryable_error(e):
raise
wait_time = (2**attempt) + 0.5
# 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"Request failed (attempt {attempt + 1}/{self.max_retries}): {e}. Retrying in {wait_time}s..."
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):
"""Async retry logic with exponential backoff."""
"""
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 (aiohttp.ClientError, asyncio.TimeoutError) as e:
if attempt == self.max_retries - 1:
except Exception as e:
if attempt == self.max_retries - 1 or not self._is_retryable_error(e):
raise
wait_time = (2**attempt) + 0.5
# PERFORMANCE OPTIMIZATION: Non-blocking exponential backoff
wait_time = min((2**attempt) + 0.5, 30) # Cap at 30 seconds
log.warning(
f"Request failed (attempt {attempt + 1}/{self.max_retries}): {e}. Retrying in {wait_time}s..."
f"Retryable error (attempt {attempt + 1}/{self.max_retries}): {e}. "
f"Retrying in {wait_time}s..."
)
await asyncio.sleep(wait_time)
await asyncio.sleep(wait_time) # Non-blocking wait
def _upload_file(self) -> str:
"""Uploads the file to Mistral for OCR processing (sync version)."""
"""
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_API_URL}/files"
file_name = os.path.basename(self.file_path)
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": (file_name, f, "application/pdf")}
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.timeout,
timeout=self.upload_timeout, # Use specialized upload timeout
stream=False, # Keep as False for this endpoint
)
return self._handle_response(response)
@ -179,7 +277,7 @@ class MistralLoader:
async def _upload_file_async(self, session: aiohttp.ClientSession) -> str:
"""Async file upload with streaming for better memory efficiency."""
url = f"{self.BASE_API_URL}/files"
url = f"{self.base_url}/files"
async def upload_request():
# Create multipart writer for streaming upload
@ -209,7 +307,7 @@ class MistralLoader:
url,
data=writer,
headers=self.headers,
timeout=aiohttp.ClientTimeout(total=self.timeout),
timeout=aiohttp.ClientTimeout(total=self.upload_timeout),
) as response:
return await self._handle_response_async(response)
@ -225,13 +323,13 @@ class MistralLoader:
def _get_signed_url(self, file_id: str) -> str:
"""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.timeout
url, headers=signed_url_headers, params=params, timeout=self.url_timeout
)
return self._handle_response(response)
@ -250,7 +348,7 @@ class MistralLoader:
self, session: aiohttp.ClientSession, file_id: str
) -> str:
"""Async signed URL retrieval."""
url = f"{self.BASE_API_URL}/files/{file_id}/url"
url = f"{self.base_url}/files/{file_id}/url"
params = {"expiry": 1}
headers = {**self.headers, "Accept": "application/json"}
@ -261,7 +359,7 @@ class MistralLoader:
url,
headers=headers,
params=params,
timeout=aiohttp.ClientTimeout(total=self.timeout),
timeout=aiohttp.ClientTimeout(total=self.url_timeout),
) as response:
return await self._handle_response_async(response)
@ -277,7 +375,7 @@ class MistralLoader:
def _process_ocr(self, signed_url: str) -> Dict[str, Any]:
"""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",
@ -294,7 +392,7 @@ class MistralLoader:
def ocr_request():
response = requests.post(
url, headers=ocr_headers, json=payload, timeout=self.timeout
url, headers=ocr_headers, json=payload, timeout=self.ocr_timeout
)
return self._handle_response(response)
@ -311,7 +409,7 @@ class MistralLoader:
self, session: aiohttp.ClientSession, signed_url: str
) -> Dict[str, Any]:
"""Async OCR processing with timing metrics."""
url = f"{self.BASE_API_URL}/ocr"
url = f"{self.base_url}/ocr"
headers = {
**self.headers,
@ -336,7 +434,7 @@ class MistralLoader:
url,
json=payload,
headers=headers,
timeout=aiohttp.ClientTimeout(total=self.timeout),
timeout=aiohttp.ClientTimeout(total=self.ocr_timeout),
) as response:
ocr_response = await self._handle_response_async(response)
@ -350,10 +448,12 @@ class MistralLoader:
def _delete_file(self, file_id: str) -> None:
"""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}"
url = f"{self.base_url}/files/{file_id}"
try:
response = requests.delete(url, headers=self.headers, timeout=30)
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:
@ -369,10 +469,10 @@ class MistralLoader:
async def delete_request():
self._debug_log(f"Deleting file ID: {file_id}")
async with session.delete(
url=f"{self.BASE_API_URL}/files/{file_id}",
url=f"{self.base_url}/files/{file_id}",
headers=self.headers,
timeout=aiohttp.ClientTimeout(
total=30
total=self.cleanup_timeout
), # Shorter timeout for cleanup
) as response:
return await self._handle_response_async(response)
@ -388,29 +488,40 @@ class MistralLoader:
async def _get_session(self):
"""Context manager for HTTP session with optimized settings."""
connector = aiohttp.TCPConnector(
limit=10, # Total connection limit
limit_per_host=5, # Per-host connection limit
ttl_dns_cache=300, # DNS cache TTL
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=30,
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=aiohttp.ClientTimeout(total=self.timeout),
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."""
"""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"}
page_content="No text content found",
metadata={"error": "no_pages", "file_name": self.file_name},
)
]
@ -418,41 +529,44 @@ class MistralLoader:
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 not None and page_index is not None:
# Clean up content efficiently
cleaned_content = (
page_content.strip()
if isinstance(page_content, str)
else str(page_content)
)
if cleaned_content: # Only add non-empty pages
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",
},
)
)
else:
skipped_pages += 1
self._debug_log(f"Skipping empty page {page_index}")
else:
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: {page_data}"
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(
@ -467,7 +581,11 @@ class MistralLoader:
return [
Document(
page_content="No valid text content found in document",
metadata={"error": "no_valid_pages", "total_pages": total_pages},
metadata={
"error": "no_valid_pages",
"total_pages": total_pages,
"file_name": self.file_name,
},
)
]
@ -585,12 +703,14 @@ class MistralLoader:
@staticmethod
async def load_multiple_async(
loaders: List["MistralLoader"],
max_concurrent: int = 5, # Limit concurrent requests
) -> List[List[Document]]:
"""
Process multiple files concurrently for maximum performance.
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
@ -598,11 +718,20 @@ class MistralLoader:
if not loaders:
return []
log.info(f"Starting concurrent processing of {len(loaders)} files")
log.info(
f"Starting concurrent processing of {len(loaders)} files with max {max_concurrent} concurrent"
)
start_time = time.time()
# Process all files concurrently
tasks = [loader.load_async() for loader in loaders]
# 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
@ -624,10 +753,18 @@ class MistralLoader:
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, produced {total_docs} total documents"
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

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
@ -82,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. '
@ -89,19 +91,16 @@ 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 []
@ -110,11 +109,37 @@ class YoutubeLoader:
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
log.debug(f"Found transcript for language '{lang}'")
transcript_pieces: List[Dict[str, Any]] = transcript.fetch()
try:
transcript_pieces: List[Dict[str, Any]] = transcript.fetch()
except ParseError:
log.debug(f"Empty or invalid transcript for language '{lang}'")
continue
if not transcript_pieces:
log.debug(f"Empty transcript for language '{lang}'")
continue
transcript_text = " ".join(
map(
lambda transcript_piece: transcript_piece.text.strip(" "),
lambda transcript_piece: (
transcript_piece.text.strip(" ")
if hasattr(transcript_piece, "text")
else ""
),
transcript_pieces,
)
)
@ -131,6 +156,11 @@ class YoutubeLoader:
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."
)
raise NoTranscriptFound(
f"No transcript found for any supported language. Verify if the video has transcripts, add more languages if needed."
)
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)

View file

@ -1,9 +1,12 @@
import logging
import requests
from typing import Optional, List, Tuple
from urllib.parse import quote
from open_webui.env import SRC_LOG_LEVELS
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__)
@ -21,7 +24,9 @@ class ExternalReranker(BaseReranker):
self.url = url
self.model = model
def predict(self, sentences: List[Tuple[str, str]]) -> Optional[List[float]]:
def predict(
self, sentences: List[Tuple[str, str]], user=None
) -> Optional[List[float]]:
query = sentences[0][0]
docs = [i[1] for i in sentences]
@ -36,12 +41,17 @@ class ExternalReranker(BaseReranker):
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={
"Content-Type": "application/json",
"Authorization": f"Bearer {self.api_key}",
},
headers=headers,
json=payload,
)

File diff suppressed because it is too large Load diff

View file

@ -11,6 +11,8 @@ from open_webui.retrieval.vector.main import (
SearchResult,
GetResult,
)
from open_webui.retrieval.vector.utils import process_metadata
from open_webui.config import (
CHROMA_DATA_PATH,
CHROMA_HTTP_HOST,
@ -144,7 +146,7 @@ class ChromaClient(VectorDBBase):
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,
@ -164,7 +166,7 @@ class ChromaClient(VectorDBBase):
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

View file

@ -2,6 +2,8 @@ from elasticsearch import Elasticsearch, BadRequestError
from typing import Optional
import ssl
from elasticsearch.helpers import bulk, scan
from open_webui.retrieval.vector.utils import process_metadata
from open_webui.retrieval.vector.main import (
VectorDBBase,
VectorItem,
@ -243,7 +245,7 @@ class ElasticsearchClient(VectorDBBase):
"collection": collection_name,
"vector": item["vector"],
"text": item["text"],
"metadata": item["metadata"],
"metadata": process_metadata(item["metadata"]),
},
}
for item in batch
@ -264,7 +266,7 @@ class ElasticsearchClient(VectorDBBase):
"collection": collection_name,
"vector": item["vector"],
"text": item["text"],
"metadata": item["metadata"],
"metadata": process_metadata(item["metadata"]),
},
"doc_as_upsert": True,
}

View file

@ -1,8 +1,12 @@
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.utils import process_metadata
from open_webui.retrieval.vector.main import (
VectorDBBase,
VectorItem,
@ -18,6 +22,8 @@ from open_webui.config import (
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
@ -127,12 +133,18 @@ class MilvusClient(VectorDBBase):
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, FLAT, AUTOINDEX. "
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.
@ -185,86 +197,54 @@ class MilvusClient(VectorDBBase):
)
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()
]
)
max_limit = 16383 # The maximum number of records per request
all_results = []
if limit is None:
# Milvus default limit for query if not specified is 16384, but docs mention iteration.
# Let's set a practical high number if "all" is intended, or handle true pagination.
# For now, if limit is None, we'll fetch in batches up to a very large number.
# This part could be refined based on expected use cases for "get all".
# For this function signature, None implies "as many as possible" up to Milvus limits.
limit = (
16384 * 10
) # A large number to signify fetching many, will be capped by actual data or max_limit per call.
log.info(
f"Limit not specified for query, fetching up to {limit} results in batches."
)
# Initialize offset and remaining to handle pagination
offset = 0
remaining = limit
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}')
filter_string = " && ".join(filter_expressions)
collection = Collection(f"{self.collection_prefix}_{collection_name}")
collection.load()
try:
log.info(
f"Querying collection {self.collection_prefix}_{collection_name} with filter: '{filter_string}', limit: {limit}"
)
# Loop until there are no more items to fetch or the desired limit is reached
while remaining > 0:
current_fetch = min(
max_limit, remaining if isinstance(remaining, int) else max_limit
)
log.debug(
f"Querying with offset: {offset}, current_fetch: {current_fetch}"
)
results = self.client.query(
collection_name=f"{self.collection_prefix}_{collection_name}",
filter=filter_string,
output_fields=[
"id",
"data",
"metadata",
], # Explicitly list needed fields. Vector not usually needed in query.
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:
log.debug("No more results from query.")
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)
log.debug(f"Fetched {results_count} results in this batch.")
log.debug(f"Total results from query: {len(all_results)}")
return self._result_to_get_result([all_results] if all_results else [[]])
if isinstance(remaining, int):
remaining -= results_count
offset += results_count
# Break the loop if the results returned are less than the requested fetch count (means end of data)
if results_count < current_fetch:
log.debug(
"Fetched less than requested, assuming end of results for this query."
)
break
log.info(f"Total results from query: {len(all_results)}")
return self._result_to_get_result([all_results])
except Exception as e:
log.exception(
f"Error querying collection {self.collection_prefix}_{collection_name} with filter '{filter_string}' and limit {limit}: {e}"
@ -279,7 +259,7 @@ class MilvusClient(VectorDBBase):
)
# 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=None)
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.
@ -311,7 +291,7 @@ class MilvusClient(VectorDBBase):
"id": item["id"],
"vector": item["vector"],
"data": {"text": item["text"]},
"metadata": item["metadata"],
"metadata": process_metadata(item["metadata"]),
}
for item in items
],
@ -347,7 +327,7 @@ class MilvusClient(VectorDBBase):
"id": item["id"],
"vector": item["vector"],
"data": {"text": item["text"]},
"metadata": item["metadata"],
"metadata": process_metadata(item["metadata"]),
}
for item in items
],

View file

@ -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)

View file

@ -2,6 +2,7 @@ from opensearchpy import OpenSearch
from opensearchpy.helpers import bulk
from typing import Optional
from open_webui.retrieval.vector.utils import process_metadata
from open_webui.retrieval.vector.main import (
VectorDBBase,
VectorItem,
@ -157,10 +158,10 @@ class OpenSearchClient(VectorDBBase):
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(
@ -200,12 +201,13 @@ class OpenSearchClient(VectorDBBase):
"_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(
@ -221,13 +223,14 @@ class OpenSearchClient(VectorDBBase):
"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,
@ -251,11 +254,12 @@ class OpenSearchClient(VectorDBBase):
}
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}_*")

View file

@ -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

View file

@ -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,39 +18,73 @@ 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.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
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(VectorDBBase):
@ -58,9 +96,24 @@ class PgvectorClient(VectorDBBase):
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
)
@ -68,7 +121,41 @@ class PgvectorClient(VectorDBBase):
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()
@ -79,13 +166,9 @@ class PgvectorClient(VectorDBBase):
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 "
@ -99,6 +182,78 @@ class PgvectorClient(VectorDBBase):
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.
@ -118,16 +273,19 @@ class PgvectorClient(VectorDBBase):
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(
@ -147,22 +305,54 @@ class PgvectorClient(VectorDBBase):
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}")
@ -170,33 +360,66 @@ class PgvectorClient(VectorDBBase):
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}")
@ -217,11 +440,11 @@ class PgvectorClient(VectorDBBase):
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(
@ -230,16 +453,32 @@ class PgvectorClient(VectorDBBase):
.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))
@ -288,10 +527,12 @@ class PgvectorClient(VectorDBBase):
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
@ -299,17 +540,43 @@ class PgvectorClient(VectorDBBase):
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
@ -318,12 +585,14 @@ class PgvectorClient(VectorDBBase):
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
@ -331,23 +600,43 @@ class PgvectorClient(VectorDBBase):
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
@ -358,17 +647,33 @@ class PgvectorClient(VectorDBBase):
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:
@ -399,8 +704,10 @@ class PgvectorClient(VectorDBBase):
.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

View file

@ -3,10 +3,19 @@ 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,
@ -23,6 +32,8 @@ from open_webui.config import (
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
@ -47,7 +58,24 @@ class PineconeClient(VectorDBBase):
self.cloud = PINECONE_CLOUD
# Initialize Pinecone client for improved performance
self.client = Pinecone(api_key=self.api_key)
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)
@ -91,12 +119,53 @@ class PineconeClient(VectorDBBase):
log.info(f"Using existing Pinecone index '{self.index_name}'")
# Connect to the index
self.index = self.client.Index(self.index_name)
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]]:
@ -116,7 +185,7 @@ class PineconeClient(VectorDBBase):
point = {
"id": item["id"],
"values": item["vector"],
"metadata": metadata,
"metadata": process_metadata(metadata),
}
points.append(point)
return points
@ -223,7 +292,8 @@ class PineconeClient(VectorDBBase):
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 into '{collection_name_with_prefix}'"
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:
@ -254,7 +324,8 @@ class PineconeClient(VectorDBBase):
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 into '{collection_name_with_prefix}'"
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:
@ -285,7 +356,8 @@ class PineconeClient(VectorDBBase):
log.error(f"Error in async insert batch: {result}")
raise result
log.info(
f"Successfully async inserted {len(points)} vectors in batches into '{collection_name_with_prefix}'"
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:
@ -316,7 +388,8 @@ class PineconeClient(VectorDBBase):
log.error(f"Error in async upsert batch: {result}")
raise result
log.info(
f"Successfully async upserted {len(points)} vectors in batches into '{collection_name_with_prefix}'"
f"Successfully async upserted {len(points)} vectors in batches "
f"into '{collection_name_with_prefix}'"
)
def search(
@ -457,10 +530,12 @@ class PineconeClient(VectorDBBase):
# 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 from '{collection_name_with_prefix}'"
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 from '{collection_name_with_prefix}'"
f"Successfully deleted {len(ids)} vectors by ID "
f"from '{collection_name_with_prefix}'"
)
elif filter:

View file

@ -18,6 +18,9 @@ from open_webui.config import (
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
@ -29,12 +32,14 @@ log.setLevel(SRC_LOG_LEVELS["RAG"])
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.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
@ -52,9 +57,14 @@ class QdrantClient(VectorDBBase):
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)
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 = []
@ -84,8 +94,30 @@ class QdrantClient(VectorDBBase):
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):
@ -151,23 +183,23 @@ class QdrantClient(VectorDBBase):
)
)
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.

View file

@ -1,5 +1,5 @@
import logging
from typing import Optional, Tuple
from typing import Optional, Tuple, List, Dict, Any
from urllib.parse import urlparse
import grpc
@ -9,6 +9,9 @@ from open_webui.config import (
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 (
@ -23,39 +26,62 @@ 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 = "open-webui"
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:
self.client = None
return
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
if self.PREFER_GRPC:
self.client = Qclient(
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)
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"
@ -65,23 +91,13 @@ class QdrantClient(VectorDBBase):
self.HASH_BASED_COLLECTION = f"{self.collection_prefix}_hash-based"
def _result_to_get_result(self, points) -> GetResult:
ids = []
documents = []
metadatas = []
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],
}
)
return GetResult(ids=[ids], documents=[documents], metadatas=[metadatas])
def _get_collection_and_tenant_id(self, collection_name: str) -> Tuple[str, str]:
"""
@ -89,6 +105,13 @@ class QdrantClient(VectorDBBase):
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
@ -113,143 +136,53 @@ class QdrantClient(VectorDBBase):
else:
return self.KNOWLEDGE_COLLECTION, tenant_id
def _extract_error_message(self, exception):
"""
Extract error message from either HTTP or gRPC exceptions
Returns:
tuple: (status_code, error_message)
"""
# Check if it's an HTTP exception
if isinstance(exception, UnexpectedResponse):
try:
error_data = exception.structured()
error_msg = error_data.get("status", {}).get("error", "")
return exception.status_code, error_msg
except Exception as inner_e:
log.error(f"Failed to parse HTTP error: {inner_e}")
return exception.status_code, str(exception)
# Check if it's a gRPC exception
elif isinstance(exception, grpc.RpcError):
# Extract status code from gRPC error
status_code = None
if hasattr(exception, "code") and callable(exception.code):
status_code = exception.code().value[0]
# Extract error message
error_msg = str(exception)
if "details =" in error_msg:
# Parse the details line which contains the actual error message
try:
details_line = [
line.strip()
for line in error_msg.split("\n")
if "details =" in line
][0]
error_msg = details_line.split("details =")[1].strip(' "')
except (IndexError, AttributeError):
# Fall back to full message if parsing fails
pass
return status_code, error_msg
# For any other type of exception
return None, str(exception)
def _is_collection_not_found_error(self, exception):
"""
Check if the exception is due to collection not found, supporting both HTTP and gRPC
"""
status_code, error_msg = self._extract_error_message(exception)
# HTTP error (404)
if (
status_code == 404
and "Collection" in error_msg
and "doesn't exist" in error_msg
):
return True
# gRPC error (NOT_FOUND status)
if (
isinstance(exception, grpc.RpcError)
and exception.code() == grpc.StatusCode.NOT_FOUND
):
return True
return False
def _is_dimension_mismatch_error(self, exception):
"""
Check if the exception is due to dimension mismatch, supporting both HTTP and gRPC
"""
status_code, error_msg = self._extract_error_message(exception)
# Common patterns in both HTTP and gRPC
return (
"Vector dimension error" in error_msg
or "dimensions mismatch" in error_msg
or "invalid vector size" in error_msg
)
def _create_multi_tenant_collection_if_not_exists(
self, mt_collection_name: str, dimension: int = 384
def _create_multi_tenant_collection(
self, mt_collection_name: str, dimension: int = DEFAULT_DIMENSION
):
"""
Creates a collection with multi-tenancy configuration if it doesn't exist.
Default dimension is set to 384 which corresponds to 'sentence-transformers/all-MiniLM-L6-v2'.
When creating collections dynamically (insert/upsert), the actual vector dimensions will be used.
Creates a collection with multi-tenancy configuration and payload indexes for tenant_id and metadata fields.
"""
try:
# Try to create the collection directly - will fail if it already exists
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,
),
hnsw_config=models.HnswConfigDiff(
payload_m=16, # Enable per-tenant indexing
m=0,
on_disk=self.QDRANT_ON_DISK,
),
)
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}!"
)
# Create tenant ID payload index
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="tenant_id",
field_name=field,
field_schema=models.KeywordIndexParams(
type=models.KeywordIndexType.KEYWORD,
is_tenant=True,
on_disk=self.QDRANT_ON_DISK,
),
wait=True,
)
log.info(
f"Multi-tenant collection {mt_collection_name} created with dimension {dimension}!"
)
except (UnexpectedResponse, grpc.RpcError) as e:
# Check for the specific error indicating collection already exists
status_code, error_msg = self._extract_error_message(e)
# HTTP status code 409 or gRPC ALREADY_EXISTS
if (isinstance(e, UnexpectedResponse) and status_code == 409) or (
isinstance(e, grpc.RpcError)
and e.code() == grpc.StatusCode.ALREADY_EXISTS
):
if "already exists" in error_msg:
log.debug(f"Collection {mt_collection_name} already exists")
return
# If it's not an already exists error, re-raise
raise e
except Exception as e:
raise e
def _create_points(self, items: list[VectorItem], tenant_id: str):
def _create_points(
self, items: List[VectorItem], tenant_id: str
) -> List[PointStruct]:
"""
Create point structs from vector items with tenant ID.
"""
@ -260,56 +193,42 @@ class QdrantClient(VectorDBBase):
payload={
"text": item["text"],
"metadata": item["metadata"],
"tenant_id": tenant_id,
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
# Map to multi-tenant collection and tenant ID
mt_collection, tenant_id = self._get_collection_and_tenant_id(collection_name)
# Create tenant filter
tenant_filter = models.FieldCondition(
key="tenant_id", match=models.MatchValue(value=tenant_id)
)
try:
# Try directly querying - most of the time collection should exist
response = self.client.query_points(
collection_name=mt_collection,
query_filter=models.Filter(must=[tenant_filter]),
limit=1,
)
# Collection exists with this tenant ID if there are points
return len(response.points) > 0
except (UnexpectedResponse, grpc.RpcError) as e:
if self._is_collection_not_found_error(e):
log.debug(f"Collection {mt_collection} doesn't exist")
return False
else:
# For other API errors, log and return False
_, error_msg = self._extract_error_message(e)
log.warning(f"Unexpected Qdrant error: {error_msg}")
return False
except Exception as e:
# For any other errors, log and return False
log.debug(f"Error checking collection {mt_collection}: {e}")
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] = None,
ids: Optional[List[str]] = None,
filter: Optional[Dict[str, Any]] = None,
):
"""
Delete vectors by ID or filter from a collection with tenant isolation.
@ -317,189 +236,76 @@ class QdrantClient(VectorDBBase):
if not self.client:
return None
# Map to multi-tenant collection and tenant ID
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
# Create tenant filter
tenant_filter = models.FieldCondition(
key="tenant_id", match=models.MatchValue(value=tenant_id)
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)
),
)
must_conditions = [tenant_filter]
should_conditions = []
if ids:
for id_value in ids:
should_conditions.append(
models.FieldCondition(
key="metadata.id",
match=models.MatchValue(value=id_value),
),
)
elif filter:
for key, value in filter.items():
must_conditions.append(
models.FieldCondition(
key=f"metadata.{key}",
match=models.MatchValue(value=value),
),
)
try:
# Try to delete directly - most of the time collection should exist
update_result = self.client.delete(
collection_name=mt_collection,
points_selector=models.FilterSelector(
filter=models.Filter(must=must_conditions, should=should_conditions)
),
)
return update_result
except (UnexpectedResponse, grpc.RpcError) as e:
if self._is_collection_not_found_error(e):
log.debug(
f"Collection {mt_collection} doesn't exist, nothing to delete"
)
return None
else:
# For other API errors, log and re-raise
_, error_msg = self._extract_error_message(e)
log.warning(f"Unexpected Qdrant error: {error_msg}")
raise
except Exception as e:
# For non-Qdrant exceptions, re-raise
raise
def search(
self, collection_name: str, vectors: list[list[float | int]], limit: int
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:
if not self.client or not vectors:
return None
# Map to multi-tenant collection and tenant ID
mt_collection, tenant_id = self._get_collection_and_tenant_id(collection_name)
# Get the vector dimension from the query vector
dimension = len(vectors[0]) if vectors and len(vectors) > 0 else None
try:
# Try the search operation directly - most of the time collection should exist
# Create tenant filter
tenant_filter = models.FieldCondition(
key="tenant_id", match=models.MatchValue(value=tenant_id)
)
# Ensure vector dimensions match the collection
collection_dim = self.client.get_collection(
mt_collection
).config.params.vectors.size
if collection_dim != dimension:
if collection_dim < dimension:
vectors = [vector[:collection_dim] for vector in vectors]
else:
vectors = [
vector + [0] * (collection_dim - dimension)
for vector in vectors
]
# Search with tenant filter
prefetch_query = models.Prefetch(
filter=models.Filter(must=[tenant_filter]),
limit=NO_LIMIT,
)
query_response = self.client.query_points(
collection_name=mt_collection,
query=vectors[0],
prefetch=prefetch_query,
limit=limit,
)
get_result = self._result_to_get_result(query_response.points)
return SearchResult(
ids=get_result.ids,
documents=get_result.documents,
metadatas=get_result.metadatas,
# qdrant distance is [-1, 1], normalize to [0, 1]
distances=[
[(point.score + 1.0) / 2.0 for point in query_response.points]
],
)
except (UnexpectedResponse, grpc.RpcError) as e:
if self._is_collection_not_found_error(e):
log.debug(
f"Collection {mt_collection} doesn't exist, search returns None"
)
return None
else:
# For other API errors, log and re-raise
_, error_msg = self._extract_error_message(e)
log.warning(f"Unexpected Qdrant error during search: {error_msg}")
raise
except Exception as e:
# For non-Qdrant exceptions, log and return None
log.exception(f"Error searching collection '{collection_name}': {e}")
if not self.client.collection_exists(collection_name=mt_collection):
log.debug(f"Collection {mt_collection} doesn't exist, search returns None")
return None
def query(self, collection_name: str, filter: dict, limit: Optional[int] = 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
# Map to multi-tenant collection and tenant ID
mt_collection, tenant_id = self._get_collection_and_tenant_id(collection_name)
# Set default limit if not provided
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
# Create tenant filter
tenant_filter = models.FieldCondition(
key="tenant_id", match=models.MatchValue(value=tenant_id)
)
# Create metadata filters
field_conditions = []
for key, value in filter.items():
field_conditions.append(
models.FieldCondition(
key=f"metadata.{key}", match=models.MatchValue(value=value)
)
)
# Combine tenant filter with metadata filters
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])
try:
# Try the query directly - most of the time collection should exist
points = self.client.query_points(
collection_name=mt_collection,
query_filter=combined_filter,
limit=limit,
)
return self._result_to_get_result(points.points)
except (UnexpectedResponse, grpc.RpcError) as e:
if self._is_collection_not_found_error(e):
log.debug(
f"Collection {mt_collection} doesn't exist, query returns None"
)
return None
else:
# For other API errors, log and re-raise
_, error_msg = self._extract_error_message(e)
log.warning(f"Unexpected Qdrant error during query: {error_msg}")
raise
except Exception as e:
# For non-Qdrant exceptions, log and re-raise
log.exception(f"Error querying collection '{collection_name}': {e}")
return None
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]:
"""
@ -507,169 +313,36 @@ class QdrantClient(VectorDBBase):
"""
if not self.client:
return None
# Map to multi-tenant collection and tenant ID
mt_collection, tenant_id = self._get_collection_and_tenant_id(collection_name)
# Create tenant filter
tenant_filter = models.FieldCondition(
key="tenant_id", match=models.MatchValue(value=tenant_id)
)
try:
# Try to get points directly - most of the time collection should exist
points = self.client.query_points(
collection_name=mt_collection,
query_filter=models.Filter(must=[tenant_filter]),
limit=NO_LIMIT,
)
return self._result_to_get_result(points.points)
except (UnexpectedResponse, grpc.RpcError) as e:
if self._is_collection_not_found_error(e):
log.debug(f"Collection {mt_collection} doesn't exist, get returns None")
return None
else:
# For other API errors, log and re-raise
_, error_msg = self._extract_error_message(e)
log.warning(f"Unexpected Qdrant error during get: {error_msg}")
raise
except Exception as e:
# For non-Qdrant exceptions, log and return None
log.exception(f"Error getting collection '{collection_name}': {e}")
if not self.client.collection_exists(collection_name=mt_collection):
log.debug(f"Collection {mt_collection} doesn't exist, get returns None")
return None
def _handle_operation_with_error_retry(
self, operation_name, mt_collection, points, dimension
):
"""
Private helper to handle common error cases for insert and upsert operations.
Args:
operation_name: 'insert' or 'upsert'
mt_collection: The multi-tenant collection name
points: The vector points to insert/upsert
dimension: The dimension of the vectors
Returns:
The operation result (for upsert) or None (for insert)
"""
try:
if operation_name == "insert":
self.client.upload_points(mt_collection, points)
return None
else: # upsert
return self.client.upsert(mt_collection, points)
except (UnexpectedResponse, grpc.RpcError) as e:
# Handle collection not found
if self._is_collection_not_found_error(e):
log.info(
f"Collection {mt_collection} doesn't exist. Creating it with dimension {dimension}."
)
# Create collection with correct dimensions from our vectors
self._create_multi_tenant_collection_if_not_exists(
mt_collection_name=mt_collection, dimension=dimension
)
# Try operation again - no need for dimension adjustment since we just created with correct dimensions
if operation_name == "insert":
self.client.upload_points(mt_collection, points)
return None
else: # upsert
return self.client.upsert(mt_collection, points)
# Handle dimension mismatch
elif self._is_dimension_mismatch_error(e):
# For dimension errors, the collection must exist, so get its configuration
mt_collection_info = self.client.get_collection(mt_collection)
existing_size = mt_collection_info.config.params.vectors.size
log.info(
f"Dimension mismatch: Collection {mt_collection} expects {existing_size}, got {dimension}"
)
if existing_size < dimension:
# Truncate vectors to fit
log.info(
f"Truncating vectors from {dimension} to {existing_size} dimensions"
)
points = [
PointStruct(
id=point.id,
vector=point.vector[:existing_size],
payload=point.payload,
)
for point in points
]
elif existing_size > dimension:
# Pad vectors with zeros
log.info(
f"Padding vectors from {dimension} to {existing_size} dimensions with zeros"
)
points = [
PointStruct(
id=point.id,
vector=point.vector
+ [0] * (existing_size - len(point.vector)),
payload=point.payload,
)
for point in points
]
# Try operation again with adjusted dimensions
if operation_name == "insert":
self.client.upload_points(mt_collection, points)
return None
else: # upsert
return self.client.upsert(mt_collection, points)
else:
# Not a known error we can handle, log and re-raise
_, error_msg = self._extract_error_message(e)
log.warning(f"Unhandled Qdrant error: {error_msg}")
raise
except Exception as e:
# For non-Qdrant exceptions, re-raise
raise
def insert(self, collection_name: str, items: list[VectorItem]):
"""
Insert items with tenant ID.
"""
if not self.client or not items:
return None
# Map to multi-tenant collection and tenant ID
mt_collection, tenant_id = self._get_collection_and_tenant_id(collection_name)
# Get dimensions from the actual vectors
dimension = len(items[0]["vector"]) if items else None
# Create points with tenant ID
points = self._create_points(items, tenant_id)
# Handle the operation with error retry
return self._handle_operation_with_error_retry(
"insert", mt_collection, points, dimension
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]):
def upsert(self, collection_name: str, items: List[VectorItem]):
"""
Upsert items with tenant ID.
"""
if not self.client or not items:
return None
# Map to multi-tenant collection and tenant ID
mt_collection, tenant_id = self._get_collection_and_tenant_id(collection_name)
# Get dimensions from the actual vectors
dimension = len(items[0]["vector"]) if items else None
# Create points with tenant ID
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
# Handle the operation with error retry
return self._handle_operation_with_error_retry(
"upsert", mt_collection, points, dimension
)
def insert(self, collection_name: str, items: List[VectorItem]):
"""
Insert items with tenant ID.
"""
return self.upsert(collection_name, items)
def reset(self):
"""
@ -677,11 +350,9 @@ class QdrantClient(VectorDBBase):
"""
if not self.client:
return None
collection_names = self.client.get_collections().collections
for collection_name in collection_names:
if collection_name.name.startswith(self.collection_prefix):
self.client.delete_collection(collection_name=collection_name.name)
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):
"""
@ -689,24 +360,13 @@ class QdrantClient(VectorDBBase):
"""
if not self.client:
return None
# Map to multi-tenant collection and tenant ID
mt_collection, tenant_id = self._get_collection_and_tenant_id(collection_name)
tenant_filter = models.FieldCondition(
key="tenant_id", match=models.MatchValue(value=tenant_id)
)
field_conditions = [tenant_filter]
update_result = self.client.delete(
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=field_conditions)
filter=models.Filter(must=[_tenant_filter(tenant_id)])
),
)
if self.client.get_collection(mt_collection).points_count == 0:
self.client.delete_collection(mt_collection)
return update_result

View file

@ -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

View file

@ -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

View file

@ -1,6 +1,10 @@
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
from open_webui.config import (
VECTOR_DB,
ENABLE_QDRANT_MULTITENANCY_MODE,
ENABLE_MILVUS_MULTITENANCY_MODE,
)
class Vector:
@ -12,9 +16,16 @@ class Vector:
"""
match vector_type:
case VectorType.MILVUS:
from open_webui.retrieval.vector.dbs.milvus import MilvusClient
if ENABLE_MILVUS_MULTITENANCY_MODE:
from open_webui.retrieval.vector.dbs.milvus_multitenancy import (
MilvusClient,
)
return 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 (
@ -30,6 +41,10 @@ class Vector:
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
@ -48,6 +63,14 @@ class Vector:
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}")

View file

@ -9,3 +9,6 @@ class VectorType(StrEnum):
ELASTICSEARCH = "elasticsearch"
OPENSEARCH = "opensearch"
PGVECTOR = "pgvector"
ORACLE23AI = "oracle23ai"
S3VECTOR = "s3vector"
WEAVIATE = "weaviate"

View file

@ -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

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

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]
]

View file

@ -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(

View file

@ -2,27 +2,42 @@ import logging
from typing import Optional, List
import requests
from open_webui.retrieval.web.main import SearchResult, get_filtered_results
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={
"User-Agent": "Open WebUI (https://github.com/open-webui/open-webui) RAG Bot",
"Authorization": f"Bearer {external_api_key}",
},
headers=headers,
json={
"query": query,
"count": count,

View file

@ -1,11 +1,10 @@
import logging
from typing import Optional, List
from urllib.parse import urljoin
import requests
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"])
@ -18,27 +17,20 @@ def search_firecrawl(
filter_list: Optional[List[str]] = None,
) -> List[SearchResult]:
try:
firecrawl_search_url = urljoin(firecrawl_url, "/v1/search")
response = requests.post(
firecrawl_search_url,
headers={
"User-Agent": "Open WebUI (https://github.com/open-webui/open-webui) RAG Bot",
"Authorization": f"Bearer {firecrawl_api_key}",
},
json={
"query": query,
"limit": count,
},
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
)
response.raise_for_status()
results = response.json().get("data", [])
results = response.web
if filter_list:
results = get_filtered_results(results, filter_list)
results = [
SearchResult(
link=result.get("url"),
title=result.get("title"),
snippet=result.get("description"),
link=result.url,
title=result.title,
snippet=result.description,
)
for result in results[:count]
]

View file

@ -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

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

View file

@ -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 []

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 = {

View file

@ -0,0 +1,76 @@
import logging
from typing import Optional, Literal
import requests
from open_webui.retrieval.web.main import SearchResult, get_filtered_results
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"])
def search_perplexity_search(
api_key: str,
query: str,
count: int,
filter_list: Optional[list[str]] = None,
api_url: str = "https://api.perplexity.ai/search",
user=None,
) -> list[SearchResult]:
"""Search using Perplexity API and return the results as a list of SearchResult objects.
Args:
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
api_url (str): Custom API URL (defaults to https://api.perplexity.ai/search)
user: Optional user object for forwarding user info headers
"""
# Handle PersistentConfig object
if hasattr(api_key, "__str__"):
api_key = str(api_key)
if hasattr(api_url, "__str__"):
api_url = str(api_url)
try:
url = api_url
# Create payload for the API call
payload = {
"query": query,
"max_results": count,
}
headers = {
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json",
}
# Forward user info headers if user is provided
if user is not None:
headers = include_user_info_headers(headers, user)
# Make the API request
response = requests.request("POST", url, json=payload, headers=headers)
# Parse the JSON response
json_response = response.json()
# Extract citations from the response
results = json_response.get("results", [])
return [
SearchResult(
link=result["url"], title=result["title"], snippet=result["snippet"]
)
for result in results
]
except Exception as e:
log.error(f"Error searching with Perplexity Search API: {e}")
return []

View file

@ -4,7 +4,6 @@ import socket
import ssl
import urllib.parse
import urllib.request
from collections import defaultdict
from datetime import datetime, time, timedelta
from typing import (
Any,
@ -17,13 +16,15 @@ from typing import (
Union,
Literal,
)
from fastapi.concurrency import run_in_threadpool
import aiohttp
import certifi
import validators
from langchain_community.document_loaders import PlaywrightURLLoader, WebBaseLoader
from langchain_community.document_loaders.firecrawl import FireCrawlLoader
from langchain_community.document_loaders.base import BaseLoader
from langchain_core.documents import Document
from open_webui.retrieval.loaders.tavily import TavilyLoader
from open_webui.retrieval.loaders.external_web import ExternalWebLoader
from open_webui.constants import ERROR_MESSAGES
@ -38,17 +39,46 @@ from open_webui.config import (
TAVILY_EXTRACT_DEPTH,
EXTERNAL_WEB_LOADER_URL,
EXTERNAL_WEB_LOADER_API_KEY,
WEB_FETCH_FILTER_LIST,
)
from open_webui.env import SRC_LOG_LEVELS, AIOHTTP_CLIENT_SESSION_SSL
from open_webui.env import SRC_LOG_LEVELS
from open_webui.utils.misc import is_string_allowed
log = logging.getLogger(__name__)
log.setLevel(SRC_LOG_LEVELS["RAG"])
def resolve_hostname(hostname):
# Get address information
addr_info = socket.getaddrinfo(hostname, None)
# Extract IP addresses from address information
ipv4_addresses = [info[4][0] for info in addr_info if info[0] == socket.AF_INET]
ipv6_addresses = [info[4][0] for info in addr_info if info[0] == socket.AF_INET6]
return ipv4_addresses, ipv6_addresses
def validate_url(url: Union[str, Sequence[str]]):
if isinstance(url, str):
if isinstance(validators.url(url), validators.ValidationError):
raise ValueError(ERROR_MESSAGES.INVALID_URL)
parsed_url = urllib.parse.urlparse(url)
# Protocol validation - only allow http/https
if parsed_url.scheme not in ["http", "https"]:
log.warning(
f"Blocked non-HTTP(S) protocol: {parsed_url.scheme} in URL: {url}"
)
raise ValueError(ERROR_MESSAGES.INVALID_URL)
# Blocklist check using unified filtering logic
if WEB_FETCH_FILTER_LIST:
if not is_string_allowed(url, WEB_FETCH_FILTER_LIST):
log.warning(f"URL blocked by filter list: {url}")
raise ValueError(ERROR_MESSAGES.INVALID_URL)
if not ENABLE_RAG_LOCAL_WEB_FETCH:
# Local web fetch is disabled, filter out any URLs that resolve to private IP addresses
parsed_url = urllib.parse.urlparse(url)
@ -75,22 +105,12 @@ def safe_validate_urls(url: Sequence[str]) -> Sequence[str]:
try:
if validate_url(u):
valid_urls.append(u)
except ValueError:
except Exception as e:
log.debug(f"Invalid URL {u}: {str(e)}")
continue
return valid_urls
def resolve_hostname(hostname):
# Get address information
addr_info = socket.getaddrinfo(hostname, None)
# Extract IP addresses from address information
ipv4_addresses = [info[4][0] for info in addr_info if info[0] == socket.AF_INET]
ipv6_addresses = [info[4][0] for info in addr_info if info[0] == socket.AF_INET6]
return ipv4_addresses, ipv6_addresses
def extract_metadata(soup, url):
metadata = {"source": url}
if title := soup.find("title"):
@ -141,13 +161,13 @@ class RateLimitMixin:
class URLProcessingMixin:
def _verify_ssl_cert(self, url: str) -> bool:
async def _verify_ssl_cert(self, url: str) -> bool:
"""Verify SSL certificate for a URL."""
return verify_ssl_cert(url)
return await run_in_threadpool(verify_ssl_cert, url)
async def _safe_process_url(self, url: str) -> bool:
"""Perform safety checks before processing a URL."""
if self.verify_ssl and not self._verify_ssl_cert(url):
if self.verify_ssl and not await self._verify_ssl_cert(url):
raise ValueError(f"SSL certificate verification failed for {url}")
await self._wait_for_rate_limit()
return True
@ -188,13 +208,12 @@ class SafeFireCrawlLoader(BaseLoader, RateLimitMixin, URLProcessingMixin):
(uses FIRE_CRAWL_API_KEY environment variable if not provided).
api_url: Base URL for FireCrawl API. Defaults to official API endpoint.
mode: Operation mode selection:
- 'crawl': Website crawling mode (default)
- 'scrape': Direct page scraping
- 'crawl': Website crawling mode
- 'scrape': Direct page scraping (default)
- 'map': Site map generation
proxy: Proxy override settings for the FireCrawl API.
params: The parameters to pass to the Firecrawl API.
Examples include crawlerOptions.
For more details, visit: https://github.com/mendableai/firecrawl-py
For more details, visit: https://docs.firecrawl.dev/sdks/python#batch-scrape
"""
proxy_server = proxy.get("server") if proxy else None
if trust_env and not proxy_server:
@ -214,50 +233,88 @@ class SafeFireCrawlLoader(BaseLoader, RateLimitMixin, URLProcessingMixin):
self.api_key = api_key
self.api_url = api_url
self.mode = mode
self.params = params
self.params = params or {}
def lazy_load(self) -> Iterator[Document]:
"""Load documents concurrently using FireCrawl."""
for url in self.web_paths:
try:
self._safe_process_url_sync(url)
loader = FireCrawlLoader(
url=url,
api_key=self.api_key,
api_url=self.api_url,
mode=self.mode,
params=self.params,
"""Load documents using FireCrawl batch_scrape."""
log.debug(
"Starting FireCrawl batch scrape for %d URLs, mode: %s, params: %s",
len(self.web_paths),
self.mode,
self.params,
)
try:
from firecrawl import FirecrawlApp
firecrawl = FirecrawlApp(api_key=self.api_key, api_url=self.api_url)
result = firecrawl.batch_scrape(
self.web_paths,
formats=["markdown"],
skip_tls_verification=not self.verify_ssl,
ignore_invalid_urls=True,
remove_base64_images=True,
max_age=300000, # 5 minutes https://docs.firecrawl.dev/features/fast-scraping#common-maxage-values
wait_timeout=len(self.web_paths) * 3,
**self.params,
)
if result.status != "completed":
raise RuntimeError(
f"FireCrawl batch scrape did not complete successfully. result: {result}"
)
for document in loader.lazy_load():
if not document.metadata.get("source"):
document.metadata["source"] = document.metadata.get("sourceURL")
yield document
except Exception as e:
if self.continue_on_failure:
log.exception(f"Error loading {url}: {e}")
continue
for data in result.data:
metadata = data.metadata or {}
yield Document(
page_content=data.markdown or "",
metadata={"source": metadata.url or metadata.source_url or ""},
)
except Exception as e:
if self.continue_on_failure:
log.exception(f"Error extracting content from URLs: {e}")
else:
raise e
async def alazy_load(self):
"""Async version of lazy_load."""
for url in self.web_paths:
try:
await self._safe_process_url(url)
loader = FireCrawlLoader(
url=url,
api_key=self.api_key,
api_url=self.api_url,
mode=self.mode,
params=self.params,
log.debug(
"Starting FireCrawl batch scrape for %d URLs, mode: %s, params: %s",
len(self.web_paths),
self.mode,
self.params,
)
try:
from firecrawl import FirecrawlApp
firecrawl = FirecrawlApp(api_key=self.api_key, api_url=self.api_url)
result = firecrawl.batch_scrape(
self.web_paths,
formats=["markdown"],
skip_tls_verification=not self.verify_ssl,
ignore_invalid_urls=True,
remove_base64_images=True,
max_age=300000, # 5 minutes https://docs.firecrawl.dev/features/fast-scraping#common-maxage-values
wait_timeout=len(self.web_paths) * 3,
**self.params,
)
if result.status != "completed":
raise RuntimeError(
f"FireCrawl batch scrape did not complete successfully. result: {result}"
)
async for document in loader.alazy_load():
if not document.metadata.get("source"):
document.metadata["source"] = document.metadata.get("sourceURL")
yield document
except Exception as e:
if self.continue_on_failure:
log.exception(f"Error loading {url}: {e}")
continue
for data in result.data:
metadata = data.metadata or {}
yield Document(
page_content=data.markdown or "",
metadata={"source": metadata.url or metadata.source_url or ""},
)
except Exception as e:
if self.continue_on_failure:
log.exception(f"Error extracting content from URLs: {e}")
else:
raise e
@ -517,6 +574,7 @@ class SafeWebBaseLoader(WebBaseLoader):
async with session.get(
url,
**(self.requests_kwargs | kwargs),
allow_redirects=False,
) as response:
if self.raise_for_status:
response.raise_for_status()
@ -602,6 +660,10 @@ def get_web_loader(
# Check if the URLs are valid
safe_urls = safe_validate_urls([urls] if isinstance(urls, str) else urls)
if not safe_urls:
log.warning(f"All provided URLs were blocked or invalid: {urls}")
raise ValueError(ERROR_MESSAGES.INVALID_URL)
web_loader_args = {
"web_paths": safe_urls,
"verify_ssl": verify_ssl,
@ -614,7 +676,7 @@ def get_web_loader(
WebLoaderClass = SafeWebBaseLoader
if WEB_LOADER_ENGINE.value == "playwright":
WebLoaderClass = SafePlaywrightURLLoader
web_loader_args["playwright_timeout"] = PLAYWRIGHT_TIMEOUT.value * 1000
web_loader_args["playwright_timeout"] = PLAYWRIGHT_TIMEOUT.value
if PLAYWRIGHT_WS_URL.value:
web_loader_args["playwright_ws_url"] = PLAYWRIGHT_WS_URL.value

View file

@ -3,14 +3,15 @@ import json
import logging
import os
import uuid
import html
import base64
from functools import lru_cache
from pathlib import Path
from pydub import AudioSegment
from pydub.silence import split_on_silence
from concurrent.futures import ThreadPoolExecutor
from typing import Optional
from fnmatch import fnmatch
import aiohttp
import aiofiles
import requests
@ -33,18 +34,20 @@ from pydantic import BaseModel
from open_webui.utils.auth import get_admin_user, get_verified_user
from open_webui.utils.headers import include_user_info_headers
from open_webui.config import (
WHISPER_MODEL_AUTO_UPDATE,
WHISPER_MODEL_DIR,
CACHE_DIR,
WHISPER_LANGUAGE,
ELEVENLABS_API_BASE_URL,
)
from open_webui.constants import ERROR_MESSAGES
from open_webui.env import (
ENV,
AIOHTTP_CLIENT_SESSION_SSL,
AIOHTTP_CLIENT_TIMEOUT,
ENV,
SRC_LOG_LEVELS,
DEVICE_TYPE,
ENABLE_FORWARD_USER_INFO_HEADERS,
@ -96,12 +99,9 @@ def is_audio_conversion_required(file_path):
# File is AAC/mp4a audio, recommend mp3 conversion
return True
# If the codec name or file extension is in the supported formats
if (
codec_name in SUPPORTED_FORMATS
or os.path.splitext(file_path)[1][1:].lower() in SUPPORTED_FORMATS
):
return False # Already supported
# If the codec name is in the supported formats
if codec_name in SUPPORTED_FORMATS:
return False
return True
except Exception as e:
@ -156,6 +156,7 @@ def set_faster_whisper_model(model: str, auto_update: bool = False):
class TTSConfigForm(BaseModel):
OPENAI_API_BASE_URL: str
OPENAI_API_KEY: str
OPENAI_PARAMS: Optional[dict] = None
API_KEY: str
ENGINE: str
MODEL: str
@ -171,6 +172,7 @@ class STTConfigForm(BaseModel):
OPENAI_API_KEY: str
ENGINE: str
MODEL: str
SUPPORTED_CONTENT_TYPES: list[str] = []
WHISPER_MODEL: str
DEEPGRAM_API_KEY: str
AZURE_API_KEY: str
@ -178,6 +180,9 @@ class STTConfigForm(BaseModel):
AZURE_LOCALES: str
AZURE_BASE_URL: str
AZURE_MAX_SPEAKERS: str
MISTRAL_API_KEY: str
MISTRAL_API_BASE_URL: str
MISTRAL_USE_CHAT_COMPLETIONS: bool
class AudioConfigUpdateForm(BaseModel):
@ -191,6 +196,7 @@ async def get_audio_config(request: Request, user=Depends(get_admin_user)):
"tts": {
"OPENAI_API_BASE_URL": request.app.state.config.TTS_OPENAI_API_BASE_URL,
"OPENAI_API_KEY": request.app.state.config.TTS_OPENAI_API_KEY,
"OPENAI_PARAMS": request.app.state.config.TTS_OPENAI_PARAMS,
"API_KEY": request.app.state.config.TTS_API_KEY,
"ENGINE": request.app.state.config.TTS_ENGINE,
"MODEL": request.app.state.config.TTS_MODEL,
@ -205,6 +211,7 @@ async def get_audio_config(request: Request, user=Depends(get_admin_user)):
"OPENAI_API_KEY": request.app.state.config.STT_OPENAI_API_KEY,
"ENGINE": request.app.state.config.STT_ENGINE,
"MODEL": request.app.state.config.STT_MODEL,
"SUPPORTED_CONTENT_TYPES": request.app.state.config.STT_SUPPORTED_CONTENT_TYPES,
"WHISPER_MODEL": request.app.state.config.WHISPER_MODEL,
"DEEPGRAM_API_KEY": request.app.state.config.DEEPGRAM_API_KEY,
"AZURE_API_KEY": request.app.state.config.AUDIO_STT_AZURE_API_KEY,
@ -212,6 +219,9 @@ async def get_audio_config(request: Request, user=Depends(get_admin_user)):
"AZURE_LOCALES": request.app.state.config.AUDIO_STT_AZURE_LOCALES,
"AZURE_BASE_URL": request.app.state.config.AUDIO_STT_AZURE_BASE_URL,
"AZURE_MAX_SPEAKERS": request.app.state.config.AUDIO_STT_AZURE_MAX_SPEAKERS,
"MISTRAL_API_KEY": request.app.state.config.AUDIO_STT_MISTRAL_API_KEY,
"MISTRAL_API_BASE_URL": request.app.state.config.AUDIO_STT_MISTRAL_API_BASE_URL,
"MISTRAL_USE_CHAT_COMPLETIONS": request.app.state.config.AUDIO_STT_MISTRAL_USE_CHAT_COMPLETIONS,
},
}
@ -222,6 +232,7 @@ async def update_audio_config(
):
request.app.state.config.TTS_OPENAI_API_BASE_URL = form_data.tts.OPENAI_API_BASE_URL
request.app.state.config.TTS_OPENAI_API_KEY = form_data.tts.OPENAI_API_KEY
request.app.state.config.TTS_OPENAI_PARAMS = form_data.tts.OPENAI_PARAMS
request.app.state.config.TTS_API_KEY = form_data.tts.API_KEY
request.app.state.config.TTS_ENGINE = form_data.tts.ENGINE
request.app.state.config.TTS_MODEL = form_data.tts.MODEL
@ -239,6 +250,10 @@ async def update_audio_config(
request.app.state.config.STT_OPENAI_API_KEY = form_data.stt.OPENAI_API_KEY
request.app.state.config.STT_ENGINE = form_data.stt.ENGINE
request.app.state.config.STT_MODEL = form_data.stt.MODEL
request.app.state.config.STT_SUPPORTED_CONTENT_TYPES = (
form_data.stt.SUPPORTED_CONTENT_TYPES
)
request.app.state.config.WHISPER_MODEL = form_data.stt.WHISPER_MODEL
request.app.state.config.DEEPGRAM_API_KEY = form_data.stt.DEEPGRAM_API_KEY
request.app.state.config.AUDIO_STT_AZURE_API_KEY = form_data.stt.AZURE_API_KEY
@ -248,20 +263,30 @@ async def update_audio_config(
request.app.state.config.AUDIO_STT_AZURE_MAX_SPEAKERS = (
form_data.stt.AZURE_MAX_SPEAKERS
)
request.app.state.config.AUDIO_STT_MISTRAL_API_KEY = form_data.stt.MISTRAL_API_KEY
request.app.state.config.AUDIO_STT_MISTRAL_API_BASE_URL = (
form_data.stt.MISTRAL_API_BASE_URL
)
request.app.state.config.AUDIO_STT_MISTRAL_USE_CHAT_COMPLETIONS = (
form_data.stt.MISTRAL_USE_CHAT_COMPLETIONS
)
if request.app.state.config.STT_ENGINE == "":
request.app.state.faster_whisper_model = set_faster_whisper_model(
form_data.stt.WHISPER_MODEL, WHISPER_MODEL_AUTO_UPDATE
)
else:
request.app.state.faster_whisper_model = None
return {
"tts": {
"OPENAI_API_BASE_URL": request.app.state.config.TTS_OPENAI_API_BASE_URL,
"OPENAI_API_KEY": request.app.state.config.TTS_OPENAI_API_KEY,
"API_KEY": request.app.state.config.TTS_API_KEY,
"ENGINE": request.app.state.config.TTS_ENGINE,
"MODEL": request.app.state.config.TTS_MODEL,
"VOICE": request.app.state.config.TTS_VOICE,
"OPENAI_API_BASE_URL": request.app.state.config.TTS_OPENAI_API_BASE_URL,
"OPENAI_API_KEY": request.app.state.config.TTS_OPENAI_API_KEY,
"OPENAI_PARAMS": request.app.state.config.TTS_OPENAI_PARAMS,
"API_KEY": request.app.state.config.TTS_API_KEY,
"SPLIT_ON": request.app.state.config.TTS_SPLIT_ON,
"AZURE_SPEECH_REGION": request.app.state.config.TTS_AZURE_SPEECH_REGION,
"AZURE_SPEECH_BASE_URL": request.app.state.config.TTS_AZURE_SPEECH_BASE_URL,
@ -272,6 +297,7 @@ async def update_audio_config(
"OPENAI_API_KEY": request.app.state.config.STT_OPENAI_API_KEY,
"ENGINE": request.app.state.config.STT_ENGINE,
"MODEL": request.app.state.config.STT_MODEL,
"SUPPORTED_CONTENT_TYPES": request.app.state.config.STT_SUPPORTED_CONTENT_TYPES,
"WHISPER_MODEL": request.app.state.config.WHISPER_MODEL,
"DEEPGRAM_API_KEY": request.app.state.config.DEEPGRAM_API_KEY,
"AZURE_API_KEY": request.app.state.config.AUDIO_STT_AZURE_API_KEY,
@ -279,6 +305,9 @@ async def update_audio_config(
"AZURE_LOCALES": request.app.state.config.AUDIO_STT_AZURE_LOCALES,
"AZURE_BASE_URL": request.app.state.config.AUDIO_STT_AZURE_BASE_URL,
"AZURE_MAX_SPEAKERS": request.app.state.config.AUDIO_STT_AZURE_MAX_SPEAKERS,
"MISTRAL_API_KEY": request.app.state.config.AUDIO_STT_MISTRAL_API_KEY,
"MISTRAL_API_BASE_URL": request.app.state.config.AUDIO_STT_MISTRAL_API_BASE_URL,
"MISTRAL_USE_CHAT_COMPLETIONS": request.app.state.config.AUDIO_STT_MISTRAL_USE_CHAT_COMPLETIONS,
},
}
@ -321,6 +350,7 @@ async def speech(request: Request, user=Depends(get_verified_user)):
log.exception(e)
raise HTTPException(status_code=400, detail="Invalid JSON payload")
r = None
if request.app.state.config.TTS_ENGINE == "openai":
payload["model"] = request.app.state.config.TTS_MODEL
@ -329,32 +359,32 @@ async def speech(request: Request, user=Depends(get_verified_user)):
async with aiohttp.ClientSession(
timeout=timeout, trust_env=True
) as session:
async with session.post(
payload = {
**payload,
**(request.app.state.config.TTS_OPENAI_PARAMS or {}),
}
headers = {
"Content-Type": "application/json",
"Authorization": f"Bearer {request.app.state.config.TTS_OPENAI_API_KEY}",
}
if ENABLE_FORWARD_USER_INFO_HEADERS:
headers = include_user_info_headers(headers, user)
r = await session.post(
url=f"{request.app.state.config.TTS_OPENAI_API_BASE_URL}/audio/speech",
json=payload,
headers={
"Content-Type": "application/json",
"Authorization": f"Bearer {request.app.state.config.TTS_OPENAI_API_KEY}",
**(
{
"X-OpenWebUI-User-Name": user.name,
"X-OpenWebUI-User-Id": user.id,
"X-OpenWebUI-User-Email": user.email,
"X-OpenWebUI-User-Role": user.role,
}
if ENABLE_FORWARD_USER_INFO_HEADERS
else {}
),
},
headers=headers,
ssl=AIOHTTP_CLIENT_SESSION_SSL,
) as r:
r.raise_for_status()
)
async with aiofiles.open(file_path, "wb") as f:
await f.write(await r.read())
r.raise_for_status()
async with aiofiles.open(file_body_path, "w") as f:
await f.write(json.dumps(payload))
async with aiofiles.open(file_path, "wb") as f:
await f.write(await r.read())
async with aiofiles.open(file_body_path, "w") as f:
await f.write(json.dumps(payload))
return FileResponse(file_path)
@ -362,18 +392,22 @@ async def speech(request: Request, user=Depends(get_verified_user)):
log.exception(e)
detail = None
try:
if r.status != 200:
res = await r.json()
status_code = 500
detail = f"Open WebUI: Server Connection Error"
if r is not None:
status_code = r.status
try:
res = await r.json()
if "error" in res:
detail = f"External: {res['error'].get('message', '')}"
except Exception:
detail = f"External: {e}"
detail = f"External: {res['error']}"
except Exception:
detail = f"External: {e}"
raise HTTPException(
status_code=getattr(r, "status", 500) if r else 500,
detail=detail if detail else "Open WebUI: Server Connection Error",
status_code=status_code,
detail=detail,
)
elif request.app.state.config.TTS_ENGINE == "elevenlabs":
@ -391,7 +425,7 @@ async def speech(request: Request, user=Depends(get_verified_user)):
timeout=timeout, trust_env=True
) as session:
async with session.post(
f"https://api.elevenlabs.io/v1/text-to-speech/{voice_id}",
f"{ELEVENLABS_API_BASE_URL}/v1/text-to-speech/{voice_id}",
json={
"text": payload["input"],
"model_id": request.app.state.config.TTS_MODEL,
@ -446,7 +480,7 @@ async def speech(request: Request, user=Depends(get_verified_user)):
try:
data = f"""<speak version="1.0" xmlns="http://www.w3.org/2001/10/synthesis" xml:lang="{locale}">
<voice name="{language}">{payload["input"]}</voice>
<voice name="{language}">{html.escape(payload["input"])}</voice>
</speak>"""
timeout = aiohttp.ClientTimeout(total=AIOHTTP_CLIENT_TIMEOUT)
async with aiohttp.ClientSession(
@ -530,13 +564,18 @@ async def speech(request: Request, user=Depends(get_verified_user)):
return FileResponse(file_path)
def transcription_handler(request, file_path, metadata):
def transcription_handler(request, file_path, metadata, user=None):
filename = os.path.basename(file_path)
file_dir = os.path.dirname(file_path)
id = filename.split(".")[0]
metadata = metadata or {}
languages = [
metadata.get("language", None) if not WHISPER_LANGUAGE else WHISPER_LANGUAGE,
None, # Always fallback to None in case transcription fails
]
if request.app.state.config.STT_ENGINE == "":
if request.app.state.faster_whisper_model is None:
request.app.state.faster_whisper_model = set_faster_whisper_model(
@ -548,7 +587,7 @@ def transcription_handler(request, file_path, metadata):
file_path,
beam_size=5,
vad_filter=request.app.state.config.WHISPER_VAD_FILTER,
language=metadata.get("language") or WHISPER_LANGUAGE,
language=languages[0],
)
log.info(
"Detected language '%s' with probability %f"
@ -568,21 +607,30 @@ def transcription_handler(request, file_path, metadata):
elif request.app.state.config.STT_ENGINE == "openai":
r = None
try:
r = requests.post(
url=f"{request.app.state.config.STT_OPENAI_API_BASE_URL}/audio/transcriptions",
headers={
"Authorization": f"Bearer {request.app.state.config.STT_OPENAI_API_KEY}"
},
files={"file": (filename, open(file_path, "rb"))},
data={
for language in languages:
payload = {
"model": request.app.state.config.STT_MODEL,
**(
{"language": metadata.get("language")}
if metadata.get("language")
else {}
),
},
)
}
if language:
payload["language"] = language
headers = {
"Authorization": f"Bearer {request.app.state.config.STT_OPENAI_API_KEY}"
}
if user and ENABLE_FORWARD_USER_INFO_HEADERS:
headers = include_user_info_headers(headers, user)
r = requests.post(
url=f"{request.app.state.config.STT_OPENAI_API_BASE_URL}/audio/transcriptions",
headers=headers,
files={"file": (filename, open(file_path, "rb"))},
data=payload,
)
if r.status_code == 200:
# Successful transcription
break
r.raise_for_status()
data = r.json()
@ -624,18 +672,26 @@ def transcription_handler(request, file_path, metadata):
"Content-Type": mime,
}
# Add model if specified
params = {}
if request.app.state.config.STT_MODEL:
params["model"] = request.app.state.config.STT_MODEL
for language in languages:
params = {}
if request.app.state.config.STT_MODEL:
params["model"] = request.app.state.config.STT_MODEL
if language:
params["language"] = language
# Make request to Deepgram API
r = requests.post(
"https://api.deepgram.com/v1/listen?smart_format=true",
headers=headers,
params=params,
data=file_data,
)
if r.status_code == 200:
# Successful transcription
break
# Make request to Deepgram API
r = requests.post(
"https://api.deepgram.com/v1/listen",
headers=headers,
params=params,
data=file_data,
)
r.raise_for_status()
response_data = r.json()
@ -788,8 +844,190 @@ def transcription_handler(request, file_path, metadata):
detail=detail if detail else "Open WebUI: Server Connection Error",
)
elif request.app.state.config.STT_ENGINE == "mistral":
# Check file exists
if not os.path.exists(file_path):
raise HTTPException(status_code=400, detail="Audio file not found")
def transcribe(request: Request, file_path: str, metadata: Optional[dict] = None):
# Check file size
file_size = os.path.getsize(file_path)
if file_size > MAX_FILE_SIZE:
raise HTTPException(
status_code=400,
detail=f"File size exceeds limit of {MAX_FILE_SIZE_MB}MB",
)
api_key = request.app.state.config.AUDIO_STT_MISTRAL_API_KEY
api_base_url = (
request.app.state.config.AUDIO_STT_MISTRAL_API_BASE_URL
or "https://api.mistral.ai/v1"
)
use_chat_completions = (
request.app.state.config.AUDIO_STT_MISTRAL_USE_CHAT_COMPLETIONS
)
if not api_key:
raise HTTPException(
status_code=400,
detail="Mistral API key is required for Mistral STT",
)
r = None
try:
# Use voxtral-mini-latest as the default model for transcription
model = request.app.state.config.STT_MODEL or "voxtral-mini-latest"
log.info(
f"Mistral STT - model: {model}, "
f"method: {'chat_completions' if use_chat_completions else 'transcriptions'}"
)
if use_chat_completions:
# Use chat completions API with audio input
# This method requires mp3 or wav format
audio_file_to_use = file_path
if is_audio_conversion_required(file_path):
log.debug("Converting audio to mp3 for chat completions API")
converted_path = convert_audio_to_mp3(file_path)
if converted_path:
audio_file_to_use = converted_path
else:
log.error("Audio conversion failed")
raise HTTPException(
status_code=500,
detail="Audio conversion failed. Chat completions API requires mp3 or wav format.",
)
# Read and encode audio file as base64
with open(audio_file_to_use, "rb") as audio_file:
audio_base64 = base64.b64encode(audio_file.read()).decode("utf-8")
# Prepare chat completions request
url = f"{api_base_url}/chat/completions"
# Add language instruction if specified
language = metadata.get("language", None) if metadata else None
if language:
text_instruction = f"Transcribe this audio exactly as spoken in {language}. Do not translate it."
else:
text_instruction = "Transcribe this audio exactly as spoken in its original language. Do not translate it to another language."
payload = {
"model": model,
"messages": [
{
"role": "user",
"content": [
{
"type": "input_audio",
"input_audio": audio_base64,
},
{"type": "text", "text": text_instruction},
],
}
],
}
r = requests.post(
url=url,
json=payload,
headers={
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json",
},
)
r.raise_for_status()
response = r.json()
# Extract transcript from chat completion response
transcript = (
response.get("choices", [{}])[0]
.get("message", {})
.get("content", "")
.strip()
)
if not transcript:
raise ValueError("Empty transcript in response")
data = {"text": transcript}
else:
# Use dedicated transcriptions API
url = f"{api_base_url}/audio/transcriptions"
# Determine the MIME type
mime_type, _ = mimetypes.guess_type(file_path)
if not mime_type:
mime_type = "audio/webm"
# Use context manager to ensure file is properly closed
with open(file_path, "rb") as audio_file:
files = {"file": (filename, audio_file, mime_type)}
data_form = {"model": model}
# Add language if specified in metadata
language = metadata.get("language", None) if metadata else None
if language:
data_form["language"] = language
r = requests.post(
url=url,
files=files,
data=data_form,
headers={
"Authorization": f"Bearer {api_key}",
},
)
r.raise_for_status()
response = r.json()
# Extract transcript from response
transcript = response.get("text", "").strip()
if not transcript:
raise ValueError("Empty transcript in response")
data = {"text": transcript}
# Save transcript to json file (consistent with other providers)
transcript_file = f"{file_dir}/{id}.json"
with open(transcript_file, "w") as f:
json.dump(data, f)
log.debug(data)
return data
except ValueError as e:
log.exception("Error parsing Mistral response")
raise HTTPException(
status_code=500,
detail=f"Failed to parse Mistral response: {str(e)}",
)
except requests.exceptions.RequestException as e:
log.exception(e)
detail = None
try:
if r is not None and r.status_code != 200:
res = r.json()
if "error" in res:
detail = f"External: {res['error'].get('message', '')}"
else:
detail = f"External: {r.text}"
except Exception:
detail = f"External: {e}"
raise HTTPException(
status_code=getattr(r, "status_code", 500) if r else 500,
detail=detail if detail else "Open WebUI: Server Connection Error",
)
def transcribe(
request: Request, file_path: str, metadata: Optional[dict] = None, user=None
):
log.info(f"transcribe: {file_path} {metadata}")
if is_audio_conversion_required(file_path):
@ -816,7 +1054,9 @@ def transcribe(request: Request, file_path: str, metadata: Optional[dict] = None
with ThreadPoolExecutor() as executor:
# Submit tasks for each chunk_path
futures = [
executor.submit(transcription_handler, request, chunk_path, metadata)
executor.submit(
transcription_handler, request, chunk_path, metadata, user
)
for chunk_path in chunk_paths
]
# Gather results as they complete
@ -913,10 +1153,18 @@ def transcription(
):
log.info(f"file.content_type: {file.content_type}")
SUPPORTED_CONTENT_TYPES = {"video/webm"} # Extend if you add more video types!
if not (
file.content_type.startswith("audio/")
or file.content_type in SUPPORTED_CONTENT_TYPES
stt_supported_content_types = getattr(
request.app.state.config, "STT_SUPPORTED_CONTENT_TYPES", []
)
if not any(
fnmatch(file.content_type, content_type)
for content_type in (
stt_supported_content_types
if stt_supported_content_types
and any(t.strip() for t in stt_supported_content_types)
else ["audio/*", "video/webm"]
)
):
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
@ -943,7 +1191,7 @@ def transcription(
if language:
metadata = {"language": language}
result = transcribe(request, file_path, metadata)
result = transcribe(request, file_path, metadata, user)
return {
**result,
@ -989,7 +1237,7 @@ def get_available_models(request: Request) -> list[dict]:
elif request.app.state.config.TTS_ENGINE == "elevenlabs":
try:
response = requests.get(
"https://api.elevenlabs.io/v1/models",
f"{ELEVENLABS_API_BASE_URL}/v1/models",
headers={
"xi-api-key": request.app.state.config.TTS_API_KEY,
"Content-Type": "application/json",
@ -1093,7 +1341,7 @@ def get_elevenlabs_voices(api_key: str) -> dict:
try:
# TODO: Add retries
response = requests.get(
"https://api.elevenlabs.io/v1/voices",
f"{ELEVENLABS_API_BASE_URL}/v1/voices",
headers={
"xi-api-key": api_key,
"Content-Type": "application/json",

View file

@ -4,6 +4,8 @@ import time
import datetime
import logging
from aiohttp import ClientSession
import urllib
from open_webui.models.auths import (
AddUserForm,
@ -15,11 +17,15 @@ from open_webui.models.auths import (
SigninResponse,
SignupForm,
UpdatePasswordForm,
UpdateProfileForm,
UserResponse,
)
from open_webui.models.users import Users
from open_webui.models.users import (
UserProfileImageResponse,
Users,
UpdateProfileForm,
UserStatus,
)
from open_webui.models.groups import Groups
from open_webui.models.oauth_sessions import OAuthSessions
from open_webui.constants import ERROR_MESSAGES, WEBHOOK_MESSAGES
from open_webui.env import (
@ -30,16 +36,25 @@ from open_webui.env import (
WEBUI_AUTH_COOKIE_SAME_SITE,
WEBUI_AUTH_COOKIE_SECURE,
WEBUI_AUTH_SIGNOUT_REDIRECT_URL,
ENABLE_INITIAL_ADMIN_SIGNUP,
SRC_LOG_LEVELS,
)
from fastapi import APIRouter, Depends, HTTPException, Request, status
from fastapi.responses import RedirectResponse, Response, JSONResponse
from open_webui.config import OPENID_PROVIDER_URL, ENABLE_OAUTH_SIGNUP, ENABLE_LDAP
from open_webui.config import (
OPENID_PROVIDER_URL,
ENABLE_OAUTH_SIGNUP,
ENABLE_LDAP,
ENABLE_PASSWORD_AUTH,
)
from pydantic import BaseModel
from open_webui.utils.misc import parse_duration, validate_email_format
from open_webui.utils.auth import (
validate_password,
verify_password,
decode_token,
invalidate_token,
create_api_key,
create_token,
get_admin_user,
@ -49,32 +64,46 @@ from open_webui.utils.auth import (
get_http_authorization_cred,
)
from open_webui.utils.webhook import post_webhook
from open_webui.utils.access_control import get_permissions
from open_webui.utils.access_control import get_permissions, has_permission
from open_webui.utils.groups import apply_default_group_assignment
from open_webui.utils.redis import get_redis_client
from open_webui.utils.rate_limit import RateLimiter
from typing import Optional, List
from ssl import CERT_NONE, CERT_REQUIRED, PROTOCOL_TLS
if ENABLE_LDAP.value:
from ldap3 import Server, Connection, NONE, Tls
from ldap3.utils.conv import escape_filter_chars
from ldap3 import Server, Connection, NONE, Tls
from ldap3.utils.conv import escape_filter_chars
router = APIRouter()
log = logging.getLogger(__name__)
log.setLevel(SRC_LOG_LEVELS["MAIN"])
signin_rate_limiter = RateLimiter(
redis_client=get_redis_client(), limit=5 * 3, window=60 * 3
)
############################
# GetSessionUser
############################
class SessionUserResponse(Token, UserResponse):
class SessionUserResponse(Token, UserProfileImageResponse):
expires_at: Optional[int] = None
permissions: Optional[dict] = None
@router.get("/", response_model=SessionUserResponse)
class SessionUserInfoResponse(SessionUserResponse, UserStatus):
bio: Optional[str] = None
gender: Optional[str] = None
date_of_birth: Optional[datetime.date] = None
@router.get("/", response_model=SessionUserInfoResponse)
async def get_session_user(
request: Request, response: Response, user=Depends(get_current_user)
):
@ -122,6 +151,12 @@ async def get_session_user(
"name": user.name,
"role": user.role,
"profile_image_url": user.profile_image_url,
"bio": user.bio,
"gender": user.gender,
"date_of_birth": user.date_of_birth,
"status_emoji": user.status_emoji,
"status_message": user.status_message,
"status_expires_at": user.status_expires_at,
"permissions": user_permissions,
}
@ -131,14 +166,14 @@ async def get_session_user(
############################
@router.post("/update/profile", response_model=UserResponse)
@router.post("/update/profile", response_model=UserProfileImageResponse)
async def update_profile(
form_data: UpdateProfileForm, session_user=Depends(get_verified_user)
):
if session_user:
user = Users.update_user_by_id(
session_user.id,
{"profile_image_url": form_data.profile_image_url, "name": form_data.name},
form_data.model_dump(),
)
if user:
return user
@ -160,13 +195,19 @@ async def update_password(
if WEBUI_AUTH_TRUSTED_EMAIL_HEADER:
raise HTTPException(400, detail=ERROR_MESSAGES.ACTION_PROHIBITED)
if session_user:
user = Auths.authenticate_user(session_user.email, form_data.password)
user = Auths.authenticate_user(
session_user.email, lambda pw: verify_password(form_data.password, pw)
)
if user:
try:
validate_password(form_data.password)
except Exception as e:
raise HTTPException(400, detail=str(e))
hashed = get_password_hash(form_data.new_password)
return Auths.update_user_password_by_id(user.id, hashed)
else:
raise HTTPException(400, detail=ERROR_MESSAGES.INVALID_PASSWORD)
raise HTTPException(400, detail=ERROR_MESSAGES.INCORRECT_PASSWORD)
else:
raise HTTPException(400, detail=ERROR_MESSAGES.INVALID_CRED)
@ -176,7 +217,17 @@ async def update_password(
############################
@router.post("/ldap", response_model=SessionUserResponse)
async def ldap_auth(request: Request, response: Response, form_data: LdapForm):
ENABLE_LDAP = request.app.state.config.ENABLE_LDAP
# Security checks FIRST - before loading any config
if not request.app.state.config.ENABLE_LDAP:
raise HTTPException(400, detail="LDAP authentication is not enabled")
if not ENABLE_PASSWORD_AUTH:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail=ERROR_MESSAGES.ACTION_PROHIBITED,
)
# NOW load LDAP config variables
LDAP_SERVER_LABEL = request.app.state.config.LDAP_SERVER_LABEL
LDAP_SERVER_HOST = request.app.state.config.LDAP_SERVER_HOST
LDAP_SERVER_PORT = request.app.state.config.LDAP_SERVER_PORT
@ -197,9 +248,6 @@ async def ldap_auth(request: Request, response: Response, form_data: LdapForm):
else "ALL"
)
if not ENABLE_LDAP:
raise HTTPException(400, detail="LDAP authentication is not enabled")
try:
tls = Tls(
validate=LDAP_VALIDATE_CERT,
@ -229,14 +277,30 @@ async def ldap_auth(request: Request, response: Response, form_data: LdapForm):
if not connection_app.bind():
raise HTTPException(400, detail="Application account bind failed")
ENABLE_LDAP_GROUP_MANAGEMENT = (
request.app.state.config.ENABLE_LDAP_GROUP_MANAGEMENT
)
ENABLE_LDAP_GROUP_CREATION = request.app.state.config.ENABLE_LDAP_GROUP_CREATION
LDAP_ATTRIBUTE_FOR_GROUPS = request.app.state.config.LDAP_ATTRIBUTE_FOR_GROUPS
search_attributes = [
f"{LDAP_ATTRIBUTE_FOR_USERNAME}",
f"{LDAP_ATTRIBUTE_FOR_MAIL}",
"cn",
]
if ENABLE_LDAP_GROUP_MANAGEMENT:
search_attributes.append(f"{LDAP_ATTRIBUTE_FOR_GROUPS}")
log.info(
f"LDAP Group Management enabled. Adding {LDAP_ATTRIBUTE_FOR_GROUPS} to search attributes"
)
log.info(f"LDAP search attributes: {search_attributes}")
search_success = connection_app.search(
search_base=LDAP_SEARCH_BASE,
search_filter=f"(&({LDAP_ATTRIBUTE_FOR_USERNAME}={escape_filter_chars(form_data.user.lower())}){LDAP_SEARCH_FILTERS})",
attributes=[
f"{LDAP_ATTRIBUTE_FOR_USERNAME}",
f"{LDAP_ATTRIBUTE_FOR_MAIL}",
"cn",
],
attributes=search_attributes,
)
if not search_success or not connection_app.entries:
@ -259,6 +323,69 @@ async def ldap_auth(request: Request, response: Response, form_data: LdapForm):
cn = str(entry["cn"])
user_dn = entry.entry_dn
user_groups = []
if ENABLE_LDAP_GROUP_MANAGEMENT and LDAP_ATTRIBUTE_FOR_GROUPS in entry:
group_dns = entry[LDAP_ATTRIBUTE_FOR_GROUPS]
log.info(f"LDAP raw group DNs for user {username}: {group_dns}")
if group_dns:
log.info(f"LDAP group_dns original: {group_dns}")
log.info(f"LDAP group_dns type: {type(group_dns)}")
log.info(f"LDAP group_dns length: {len(group_dns)}")
if hasattr(group_dns, "value"):
group_dns = group_dns.value
log.info(f"Extracted .value property: {group_dns}")
elif hasattr(group_dns, "__iter__") and not isinstance(
group_dns, (str, bytes)
):
group_dns = list(group_dns)
log.info(f"Converted to list: {group_dns}")
if isinstance(group_dns, list):
group_dns = [str(item) for item in group_dns]
else:
group_dns = [str(group_dns)]
log.info(
f"LDAP group_dns after processing - type: {type(group_dns)}, length: {len(group_dns)}"
)
for group_idx, group_dn in enumerate(group_dns):
group_dn = str(group_dn)
log.info(f"Processing group DN #{group_idx + 1}: {group_dn}")
try:
group_cn = None
for item in group_dn.split(","):
item = item.strip()
if item.upper().startswith("CN="):
group_cn = item[3:]
break
if group_cn:
user_groups.append(group_cn)
else:
log.warning(
f"Could not extract CN from group DN: {group_dn}"
)
except Exception as e:
log.warning(
f"Failed to extract group name from DN {group_dn}: {e}"
)
log.info(
f"LDAP groups for user {username}: {user_groups} (total: {len(user_groups)})"
)
else:
log.info(f"No groups found for user {username}")
elif ENABLE_LDAP_GROUP_MANAGEMENT:
log.warning(
f"LDAP Group Management enabled but {LDAP_ATTRIBUTE_FOR_GROUPS} attribute not found in user entry"
)
if username == form_data.user.lower():
connection_user = Connection(
server,
@ -273,11 +400,9 @@ async def ldap_auth(request: Request, response: Response, form_data: LdapForm):
user = Users.get_user_by_email(email)
if not user:
try:
user_count = Users.get_num_users()
role = (
"admin"
if user_count == 0
if not Users.has_users()
else request.app.state.config.DEFAULT_USER_ROLE
)
@ -293,6 +418,11 @@ async def ldap_auth(request: Request, response: Response, form_data: LdapForm):
500, detail=ERROR_MESSAGES.CREATE_USER_ERROR
)
apply_default_group_assignment(
request.app.state.config.DEFAULT_GROUP_ID,
user.id,
)
except HTTPException:
raise
except Exception as err:
@ -334,6 +464,21 @@ async def ldap_auth(request: Request, response: Response, form_data: LdapForm):
user.id, request.app.state.config.USER_PERMISSIONS
)
if (
user.role != "admin"
and ENABLE_LDAP_GROUP_MANAGEMENT
and user_groups
):
if ENABLE_LDAP_GROUP_CREATION:
Groups.create_groups_by_group_names(user.id, user_groups)
try:
Groups.sync_groups_by_group_names(user.id, user_groups)
log.info(
f"Successfully synced groups for user {user.id}: {user_groups}"
)
except Exception as e:
log.error(f"Failed to sync groups for user {user.id}: {e}")
return {
"token": token,
"token_type": "Bearer",
@ -361,6 +506,12 @@ async def ldap_auth(request: Request, response: Response, form_data: LdapForm):
@router.post("/signin", response_model=SessionUserResponse)
async def signin(request: Request, response: Response, form_data: SigninForm):
if not ENABLE_PASSWORD_AUTH:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail=ERROR_MESSAGES.ACTION_PROHIBITED,
)
if WEBUI_AUTH_TRUSTED_EMAIL_HEADER:
if WEBUI_AUTH_TRUSTED_EMAIL_HEADER not in request.headers:
raise HTTPException(400, detail=ERROR_MESSAGES.INVALID_TRUSTED_HEADER)
@ -370,6 +521,10 @@ async def signin(request: Request, response: Response, form_data: SigninForm):
if WEBUI_AUTH_TRUSTED_NAME_HEADER:
name = request.headers.get(WEBUI_AUTH_TRUSTED_NAME_HEADER, email)
try:
name = urllib.parse.unquote(name, encoding="utf-8")
except Exception as e:
pass
if not Users.get_user_by_email(email.lower()):
await signup(
@ -386,16 +541,18 @@ async def signin(request: Request, response: Response, form_data: SigninForm):
group_names = [name.strip() for name in group_names if name.strip()]
if group_names:
Groups.sync_user_groups_by_group_names(user.id, group_names)
Groups.sync_groups_by_group_names(user.id, group_names)
elif WEBUI_AUTH == False:
admin_email = "admin@localhost"
admin_password = "admin"
if Users.get_user_by_email(admin_email.lower()):
user = Auths.authenticate_user(admin_email.lower(), admin_password)
user = Auths.authenticate_user(
admin_email.lower(), lambda pw: verify_password(admin_password, pw)
)
else:
if Users.get_num_users() != 0:
if Users.has_users():
raise HTTPException(400, detail=ERROR_MESSAGES.EXISTING_USERS)
await signup(
@ -404,9 +561,28 @@ async def signin(request: Request, response: Response, form_data: SigninForm):
SignupForm(email=admin_email, password=admin_password, name="User"),
)
user = Auths.authenticate_user(admin_email.lower(), admin_password)
user = Auths.authenticate_user(
admin_email.lower(), lambda pw: verify_password(admin_password, pw)
)
else:
user = Auths.authenticate_user(form_data.email.lower(), form_data.password)
if signin_rate_limiter.is_limited(form_data.email.lower()):
raise HTTPException(
status_code=status.HTTP_429_TOO_MANY_REQUESTS,
detail=ERROR_MESSAGES.RATE_LIMIT_EXCEEDED,
)
password_bytes = form_data.password.encode("utf-8")
if len(password_bytes) > 72:
# TODO: Implement other hashing algorithms that support longer passwords
log.info("Password too long, truncating to 72 bytes for bcrypt")
password_bytes = password_bytes[:72]
# decode safely — ignore incomplete UTF-8 sequences
form_data.password = password_bytes.decode("utf-8", errors="ignore")
user = Auths.authenticate_user(
form_data.email.lower(), lambda pw: verify_password(form_data.password, pw)
)
if user:
@ -462,22 +638,23 @@ async def signin(request: Request, response: Response, form_data: SigninForm):
@router.post("/signup", response_model=SessionUserResponse)
async def signup(request: Request, response: Response, form_data: SignupForm):
has_users = Users.has_users()
if WEBUI_AUTH:
if (
not request.app.state.config.ENABLE_SIGNUP
or not request.app.state.config.ENABLE_LOGIN_FORM
):
raise HTTPException(
status.HTTP_403_FORBIDDEN, detail=ERROR_MESSAGES.ACCESS_PROHIBITED
)
if has_users or not ENABLE_INITIAL_ADMIN_SIGNUP:
raise HTTPException(
status.HTTP_403_FORBIDDEN, detail=ERROR_MESSAGES.ACCESS_PROHIBITED
)
else:
if Users.get_num_users() != 0:
if has_users:
raise HTTPException(
status.HTTP_403_FORBIDDEN, detail=ERROR_MESSAGES.ACCESS_PROHIBITED
)
user_count = Users.get_num_users()
if not validate_email_format(form_data.email.lower()):
raise HTTPException(
status.HTTP_400_BAD_REQUEST, detail=ERROR_MESSAGES.INVALID_EMAIL_FORMAT
@ -487,18 +664,14 @@ async def signup(request: Request, response: Response, form_data: SignupForm):
raise HTTPException(400, detail=ERROR_MESSAGES.EMAIL_TAKEN)
try:
role = (
"admin" if user_count == 0 else request.app.state.config.DEFAULT_USER_ROLE
)
# The password passed to bcrypt must be 72 bytes or fewer. If it is longer, it will be truncated before hashing.
if len(form_data.password.encode("utf-8")) > 72:
raise HTTPException(
status.HTTP_400_BAD_REQUEST,
detail=ERROR_MESSAGES.PASSWORD_TOO_LONG,
)
try:
validate_password(form_data.password)
except Exception as e:
raise HTTPException(400, detail=str(e))
hashed = get_password_hash(form_data.password)
role = "admin" if not has_users else request.app.state.config.DEFAULT_USER_ROLE
user = Auths.insert_new_auth(
form_data.email.lower(),
hashed,
@ -535,7 +708,7 @@ async def signup(request: Request, response: Response, form_data: SignupForm):
)
if request.app.state.config.WEBHOOK_URL:
post_webhook(
await post_webhook(
request.app.state.WEBUI_NAME,
request.app.state.config.WEBHOOK_URL,
WEBHOOK_MESSAGES.USER_SIGNUP(user.name),
@ -550,10 +723,15 @@ async def signup(request: Request, response: Response, form_data: SignupForm):
user.id, request.app.state.config.USER_PERMISSIONS
)
if user_count == 0:
if not has_users:
# Disable signup after the first user is created
request.app.state.config.ENABLE_SIGNUP = False
apply_default_group_assignment(
request.app.state.config.DEFAULT_GROUP_ID,
user.id,
)
return {
"token": token,
"token_type": "Bearer",
@ -574,38 +752,66 @@ async def signup(request: Request, response: Response, form_data: SignupForm):
@router.get("/signout")
async def signout(request: Request, response: Response):
# get auth token from headers or cookies
token = None
auth_header = request.headers.get("Authorization")
if auth_header:
auth_cred = get_http_authorization_cred(auth_header)
token = auth_cred.credentials
else:
token = request.cookies.get("token")
if token:
await invalidate_token(request, token)
response.delete_cookie("token")
response.delete_cookie("oui-session")
response.delete_cookie("oauth_id_token")
if ENABLE_OAUTH_SIGNUP.value:
oauth_id_token = request.cookies.get("oauth_id_token")
if oauth_id_token:
oauth_session_id = request.cookies.get("oauth_session_id")
if oauth_session_id:
response.delete_cookie("oauth_session_id")
session = OAuthSessions.get_session_by_id(oauth_session_id)
oauth_server_metadata_url = (
request.app.state.oauth_manager.get_server_metadata_url(session.provider)
if session
else None
) or OPENID_PROVIDER_URL.value
if session and oauth_server_metadata_url:
oauth_id_token = session.token.get("id_token")
try:
async with ClientSession() as session:
async with session.get(OPENID_PROVIDER_URL.value) as resp:
if resp.status == 200:
openid_data = await resp.json()
async with ClientSession(trust_env=True) as session:
async with session.get(oauth_server_metadata_url) as r:
if r.status == 200:
openid_data = await r.json()
logout_url = openid_data.get("end_session_endpoint")
if logout_url:
response.delete_cookie("oauth_id_token")
if logout_url:
return JSONResponse(
status_code=200,
content={
"status": True,
"redirect_url": f"{logout_url}?id_token_hint={oauth_id_token}",
"redirect_url": f"{logout_url}?id_token_hint={oauth_id_token}"
+ (
f"&post_logout_redirect_uri={WEBUI_AUTH_SIGNOUT_REDIRECT_URL}"
if WEBUI_AUTH_SIGNOUT_REDIRECT_URL
else ""
),
},
headers=response.headers,
)
else:
raise HTTPException(
status_code=resp.status,
detail="Failed to fetch OpenID configuration",
)
raise Exception("Failed to fetch OpenID configuration")
except Exception as e:
log.error(f"OpenID signout error: {str(e)}")
raise HTTPException(
status_code=500,
detail="Failed to sign out from the OpenID provider.",
headers=response.headers,
)
if WEBUI_AUTH_SIGNOUT_REDIRECT_URL:
@ -629,7 +835,9 @@ async def signout(request: Request, response: Response):
@router.post("/add", response_model=SigninResponse)
async def add_user(form_data: AddUserForm, user=Depends(get_admin_user)):
async def add_user(
request: Request, form_data: AddUserForm, user=Depends(get_admin_user)
):
if not validate_email_format(form_data.email.lower()):
raise HTTPException(
status.HTTP_400_BAD_REQUEST, detail=ERROR_MESSAGES.INVALID_EMAIL_FORMAT
@ -639,6 +847,11 @@ async def add_user(form_data: AddUserForm, user=Depends(get_admin_user)):
raise HTTPException(400, detail=ERROR_MESSAGES.EMAIL_TAKEN)
try:
try:
validate_password(form_data.password)
except Exception as e:
raise HTTPException(400, detail=str(e))
hashed = get_password_hash(form_data.password)
user = Auths.insert_new_auth(
form_data.email.lower(),
@ -649,6 +862,11 @@ async def add_user(form_data: AddUserForm, user=Depends(get_admin_user)):
)
if user:
apply_default_group_assignment(
request.app.state.config.DEFAULT_GROUP_ID,
user.id,
)
token = create_token(data={"id": user.id})
return {
"token": token,
@ -710,13 +928,15 @@ async def get_admin_config(request: Request, user=Depends(get_admin_user)):
"SHOW_ADMIN_DETAILS": request.app.state.config.SHOW_ADMIN_DETAILS,
"WEBUI_URL": request.app.state.config.WEBUI_URL,
"ENABLE_SIGNUP": request.app.state.config.ENABLE_SIGNUP,
"ENABLE_API_KEY": request.app.state.config.ENABLE_API_KEY,
"ENABLE_API_KEY_ENDPOINT_RESTRICTIONS": request.app.state.config.ENABLE_API_KEY_ENDPOINT_RESTRICTIONS,
"API_KEY_ALLOWED_ENDPOINTS": request.app.state.config.API_KEY_ALLOWED_ENDPOINTS,
"ENABLE_API_KEYS": request.app.state.config.ENABLE_API_KEYS,
"ENABLE_API_KEYS_ENDPOINT_RESTRICTIONS": request.app.state.config.ENABLE_API_KEYS_ENDPOINT_RESTRICTIONS,
"API_KEYS_ALLOWED_ENDPOINTS": request.app.state.config.API_KEYS_ALLOWED_ENDPOINTS,
"DEFAULT_USER_ROLE": request.app.state.config.DEFAULT_USER_ROLE,
"DEFAULT_GROUP_ID": request.app.state.config.DEFAULT_GROUP_ID,
"JWT_EXPIRES_IN": request.app.state.config.JWT_EXPIRES_IN,
"ENABLE_COMMUNITY_SHARING": request.app.state.config.ENABLE_COMMUNITY_SHARING,
"ENABLE_MESSAGE_RATING": request.app.state.config.ENABLE_MESSAGE_RATING,
"ENABLE_FOLDERS": request.app.state.config.ENABLE_FOLDERS,
"ENABLE_CHANNELS": request.app.state.config.ENABLE_CHANNELS,
"ENABLE_NOTES": request.app.state.config.ENABLE_NOTES,
"ENABLE_USER_WEBHOOKS": request.app.state.config.ENABLE_USER_WEBHOOKS,
@ -730,13 +950,15 @@ class AdminConfig(BaseModel):
SHOW_ADMIN_DETAILS: bool
WEBUI_URL: str
ENABLE_SIGNUP: bool
ENABLE_API_KEY: bool
ENABLE_API_KEY_ENDPOINT_RESTRICTIONS: bool
API_KEY_ALLOWED_ENDPOINTS: str
ENABLE_API_KEYS: bool
ENABLE_API_KEYS_ENDPOINT_RESTRICTIONS: bool
API_KEYS_ALLOWED_ENDPOINTS: str
DEFAULT_USER_ROLE: str
DEFAULT_GROUP_ID: str
JWT_EXPIRES_IN: str
ENABLE_COMMUNITY_SHARING: bool
ENABLE_MESSAGE_RATING: bool
ENABLE_FOLDERS: bool
ENABLE_CHANNELS: bool
ENABLE_NOTES: bool
ENABLE_USER_WEBHOOKS: bool
@ -753,20 +975,23 @@ async def update_admin_config(
request.app.state.config.WEBUI_URL = form_data.WEBUI_URL
request.app.state.config.ENABLE_SIGNUP = form_data.ENABLE_SIGNUP
request.app.state.config.ENABLE_API_KEY = form_data.ENABLE_API_KEY
request.app.state.config.ENABLE_API_KEY_ENDPOINT_RESTRICTIONS = (
form_data.ENABLE_API_KEY_ENDPOINT_RESTRICTIONS
request.app.state.config.ENABLE_API_KEYS = form_data.ENABLE_API_KEYS
request.app.state.config.ENABLE_API_KEYS_ENDPOINT_RESTRICTIONS = (
form_data.ENABLE_API_KEYS_ENDPOINT_RESTRICTIONS
)
request.app.state.config.API_KEY_ALLOWED_ENDPOINTS = (
form_data.API_KEY_ALLOWED_ENDPOINTS
request.app.state.config.API_KEYS_ALLOWED_ENDPOINTS = (
form_data.API_KEYS_ALLOWED_ENDPOINTS
)
request.app.state.config.ENABLE_FOLDERS = form_data.ENABLE_FOLDERS
request.app.state.config.ENABLE_CHANNELS = form_data.ENABLE_CHANNELS
request.app.state.config.ENABLE_NOTES = form_data.ENABLE_NOTES
if form_data.DEFAULT_USER_ROLE in ["pending", "user", "admin"]:
request.app.state.config.DEFAULT_USER_ROLE = form_data.DEFAULT_USER_ROLE
request.app.state.config.DEFAULT_GROUP_ID = form_data.DEFAULT_GROUP_ID
pattern = r"^(-1|0|(-?\d+(\.\d+)?)(ms|s|m|h|d|w))$"
# Check if the input string matches the pattern
@ -793,13 +1018,15 @@ async def update_admin_config(
"SHOW_ADMIN_DETAILS": request.app.state.config.SHOW_ADMIN_DETAILS,
"WEBUI_URL": request.app.state.config.WEBUI_URL,
"ENABLE_SIGNUP": request.app.state.config.ENABLE_SIGNUP,
"ENABLE_API_KEY": request.app.state.config.ENABLE_API_KEY,
"ENABLE_API_KEY_ENDPOINT_RESTRICTIONS": request.app.state.config.ENABLE_API_KEY_ENDPOINT_RESTRICTIONS,
"API_KEY_ALLOWED_ENDPOINTS": request.app.state.config.API_KEY_ALLOWED_ENDPOINTS,
"ENABLE_API_KEYS": request.app.state.config.ENABLE_API_KEYS,
"ENABLE_API_KEYS_ENDPOINT_RESTRICTIONS": request.app.state.config.ENABLE_API_KEYS_ENDPOINT_RESTRICTIONS,
"API_KEYS_ALLOWED_ENDPOINTS": request.app.state.config.API_KEYS_ALLOWED_ENDPOINTS,
"DEFAULT_USER_ROLE": request.app.state.config.DEFAULT_USER_ROLE,
"DEFAULT_GROUP_ID": request.app.state.config.DEFAULT_GROUP_ID,
"JWT_EXPIRES_IN": request.app.state.config.JWT_EXPIRES_IN,
"ENABLE_COMMUNITY_SHARING": request.app.state.config.ENABLE_COMMUNITY_SHARING,
"ENABLE_MESSAGE_RATING": request.app.state.config.ENABLE_MESSAGE_RATING,
"ENABLE_FOLDERS": request.app.state.config.ENABLE_FOLDERS,
"ENABLE_CHANNELS": request.app.state.config.ENABLE_CHANNELS,
"ENABLE_NOTES": request.app.state.config.ENABLE_NOTES,
"ENABLE_USER_WEBHOOKS": request.app.state.config.ENABLE_USER_WEBHOOKS,
@ -920,9 +1147,11 @@ async def update_ldap_config(
# create api key
@router.post("/api_key", response_model=ApiKey)
async def generate_api_key(request: Request, user=Depends(get_current_user)):
if not request.app.state.config.ENABLE_API_KEY:
if not request.app.state.config.ENABLE_API_KEYS or not has_permission(
user.id, "features.api_keys", request.app.state.config.USER_PERMISSIONS
):
raise HTTPException(
status.HTTP_403_FORBIDDEN,
status_code=status.HTTP_403_FORBIDDEN,
detail=ERROR_MESSAGES.API_KEY_CREATION_NOT_ALLOWED,
)
@ -940,8 +1169,7 @@ async def generate_api_key(request: Request, user=Depends(get_current_user)):
# delete api key
@router.delete("/api_key", response_model=bool)
async def delete_api_key(user=Depends(get_current_user)):
success = Users.update_user_api_key_by_id(user.id, None)
return success
return Users.delete_user_api_key_by_id(user.id)
# get api key

File diff suppressed because it is too large Load diff

View file

@ -7,6 +7,7 @@ from open_webui.socket.main import get_event_emitter
from open_webui.models.chats import (
ChatForm,
ChatImportForm,
ChatsImportForm,
ChatResponse,
Chats,
ChatTitleIdResponse,
@ -36,16 +37,33 @@ router = APIRouter()
@router.get("/", response_model=list[ChatTitleIdResponse])
@router.get("/list", response_model=list[ChatTitleIdResponse])
async def get_session_user_chat_list(
user=Depends(get_verified_user), page: Optional[int] = None
def get_session_user_chat_list(
user=Depends(get_verified_user),
page: Optional[int] = None,
include_pinned: Optional[bool] = False,
include_folders: Optional[bool] = False,
):
if page is not None:
limit = 60
skip = (page - 1) * limit
try:
if page is not None:
limit = 60
skip = (page - 1) * limit
return Chats.get_chat_title_id_list_by_user_id(user.id, skip=skip, limit=limit)
else:
return Chats.get_chat_title_id_list_by_user_id(user.id)
return Chats.get_chat_title_id_list_by_user_id(
user.id,
include_folders=include_folders,
include_pinned=include_pinned,
skip=skip,
limit=limit,
)
else:
return Chats.get_chat_title_id_list_by_user_id(
user.id, include_folders=include_folders, include_pinned=include_pinned
)
except Exception as e:
log.exception(e)
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST, detail=ERROR_MESSAGES.DEFAULT()
)
############################
@ -125,26 +143,15 @@ async def create_new_chat(form_data: ChatForm, user=Depends(get_verified_user)):
############################
# ImportChat
# ImportChats
############################
@router.post("/import", response_model=Optional[ChatResponse])
async def import_chat(form_data: ChatImportForm, user=Depends(get_verified_user)):
@router.post("/import", response_model=list[ChatResponse])
async def import_chats(form_data: ChatsImportForm, user=Depends(get_verified_user)):
try:
chat = Chats.import_chat(user.id, form_data)
if chat:
tags = chat.meta.get("tags", [])
for tag_id in tags:
tag_id = tag_id.replace(" ", "_").lower()
tag_name = " ".join([word.capitalize() for word in tag_id.split("_")])
if (
tag_id != "none"
and Tags.get_tag_by_name_and_user_id(tag_name, user.id) is None
):
Tags.insert_new_tag(tag_name, user.id)
return ChatResponse(**chat.model_dump())
chats = Chats.import_chats(user.id, form_data.chats)
return chats
except Exception as e:
log.exception(e)
raise HTTPException(
@ -158,7 +165,7 @@ async def import_chat(form_data: ChatImportForm, user=Depends(get_verified_user)
@router.get("/search", response_model=list[ChatTitleIdResponse])
async def search_user_chats(
def search_user_chats(
text: str, page: Optional[int] = None, user=Depends(get_verified_user)
):
if page is None:
@ -206,6 +213,28 @@ async def get_chats_by_folder_id(folder_id: str, user=Depends(get_verified_user)
]
@router.get("/folder/{folder_id}/list")
async def get_chat_list_by_folder_id(
folder_id: str, page: Optional[int] = 1, user=Depends(get_verified_user)
):
try:
limit = 10
skip = (page - 1) * limit
return [
{"title": chat.title, "id": chat.id, "updated_at": chat.updated_at}
for chat in Chats.get_chats_by_folder_id_and_user_id(
folder_id, user.id, skip=skip, limit=limit
)
]
except Exception as e:
log.exception(e)
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST, detail=ERROR_MESSAGES.DEFAULT()
)
############################
# GetPinnedChats
############################
@ -327,6 +356,16 @@ async def archive_all_chats(user=Depends(get_verified_user)):
return Chats.archive_all_chats_by_user_id(user.id)
############################
# UnarchiveAllChats
############################
@router.post("/unarchive/all", response_model=bool)
async def unarchive_all_chats(user=Depends(get_verified_user)):
return Chats.unarchive_all_chats_by_user_id(user.id)
############################
# GetSharedChatById
############################
@ -609,8 +648,28 @@ async def clone_chat_by_id(
"title": form_data.title if form_data.title else f"Clone of {chat.title}",
}
chat = Chats.insert_new_chat(user.id, ChatForm(**{"chat": updated_chat}))
return ChatResponse(**chat.model_dump())
chats = Chats.import_chats(
user.id,
[
ChatImportForm(
**{
"chat": updated_chat,
"meta": chat.meta,
"pinned": chat.pinned,
"folder_id": chat.folder_id,
}
)
],
)
if chats:
chat = chats[0]
return ChatResponse(**chat.model_dump())
else:
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=ERROR_MESSAGES.DEFAULT(),
)
else:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED, detail=ERROR_MESSAGES.DEFAULT()
@ -638,8 +697,28 @@ async def clone_shared_chat_by_id(id: str, user=Depends(get_verified_user)):
"title": f"Clone of {chat.title}",
}
chat = Chats.insert_new_chat(user.id, ChatForm(**{"chat": updated_chat}))
return ChatResponse(**chat.model_dump())
chats = Chats.import_chats(
user.id,
[
ChatImportForm(
**{
"chat": updated_chat,
"meta": chat.meta,
"pinned": chat.pinned,
"folder_id": chat.folder_id,
}
)
],
)
if chats:
chat = chats[0]
return ChatResponse(**chat.model_dump())
else:
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=ERROR_MESSAGES.DEFAULT(),
)
else:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED, detail=ERROR_MESSAGES.DEFAULT()
@ -684,8 +763,10 @@ async def archive_chat_by_id(id: str, user=Depends(get_verified_user)):
@router.post("/{id}/share", response_model=Optional[ChatResponse])
async def share_chat_by_id(request: Request, id: str, user=Depends(get_verified_user)):
if not has_permission(
user.id, "chat.share", request.app.state.config.USER_PERMISSIONS
if (user.role != "admin") and (
not has_permission(
user.id, "chat.share", request.app.state.config.USER_PERMISSIONS
)
):
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,

View file

@ -1,5 +1,8 @@
import logging
import copy
from fastapi import APIRouter, Depends, Request, HTTPException
from pydantic import BaseModel, ConfigDict
import aiohttp
from typing import Optional
@ -7,11 +10,30 @@ from open_webui.utils.auth import get_admin_user, get_verified_user
from open_webui.config import get_config, save_config
from open_webui.config import BannerModel
from open_webui.utils.tools import get_tool_server_data, get_tool_servers_data
from open_webui.utils.tools import (
get_tool_server_data,
get_tool_server_url,
set_tool_servers,
)
from open_webui.utils.mcp.client import MCPClient
from open_webui.models.oauth_sessions import OAuthSessions
from open_webui.env import SRC_LOG_LEVELS
from open_webui.utils.oauth import (
get_discovery_urls,
get_oauth_client_info_with_dynamic_client_registration,
encrypt_data,
decrypt_data,
OAuthClientInformationFull,
)
from mcp.shared.auth import OAuthMetadata
router = APIRouter()
log = logging.getLogger(__name__)
log.setLevel(SRC_LOG_LEVELS["MAIN"])
############################
# ImportConfig
@ -39,35 +61,79 @@ async def export_config(user=Depends(get_admin_user)):
############################
# Direct Connections Config
# Connections Config
############################
class DirectConnectionsConfigForm(BaseModel):
class ConnectionsConfigForm(BaseModel):
ENABLE_DIRECT_CONNECTIONS: bool
ENABLE_BASE_MODELS_CACHE: bool
@router.get("/direct_connections", response_model=DirectConnectionsConfigForm)
async def get_direct_connections_config(request: Request, user=Depends(get_admin_user)):
@router.get("/connections", response_model=ConnectionsConfigForm)
async def get_connections_config(request: Request, user=Depends(get_admin_user)):
return {
"ENABLE_DIRECT_CONNECTIONS": request.app.state.config.ENABLE_DIRECT_CONNECTIONS,
"ENABLE_BASE_MODELS_CACHE": request.app.state.config.ENABLE_BASE_MODELS_CACHE,
}
@router.post("/direct_connections", response_model=DirectConnectionsConfigForm)
async def set_direct_connections_config(
@router.post("/connections", response_model=ConnectionsConfigForm)
async def set_connections_config(
request: Request,
form_data: DirectConnectionsConfigForm,
form_data: ConnectionsConfigForm,
user=Depends(get_admin_user),
):
request.app.state.config.ENABLE_DIRECT_CONNECTIONS = (
form_data.ENABLE_DIRECT_CONNECTIONS
)
request.app.state.config.ENABLE_BASE_MODELS_CACHE = (
form_data.ENABLE_BASE_MODELS_CACHE
)
return {
"ENABLE_DIRECT_CONNECTIONS": request.app.state.config.ENABLE_DIRECT_CONNECTIONS,
"ENABLE_BASE_MODELS_CACHE": request.app.state.config.ENABLE_BASE_MODELS_CACHE,
}
class OAuthClientRegistrationForm(BaseModel):
url: str
client_id: str
client_name: Optional[str] = None
@router.post("/oauth/clients/register")
async def register_oauth_client(
request: Request,
form_data: OAuthClientRegistrationForm,
type: Optional[str] = None,
user=Depends(get_admin_user),
):
try:
oauth_client_id = form_data.client_id
if type:
oauth_client_id = f"{type}:{form_data.client_id}"
oauth_client_info = (
await get_oauth_client_info_with_dynamic_client_registration(
request, oauth_client_id, form_data.url
)
)
return {
"status": True,
"oauth_client_info": encrypt_data(
oauth_client_info.model_dump(mode="json")
),
}
except Exception as e:
log.debug(f"Failed to register OAuth client: {e}")
raise HTTPException(
status_code=400,
detail=f"Failed to register OAuth client",
)
############################
# ToolServers Config
############################
@ -76,7 +142,9 @@ async def set_direct_connections_config(
class ToolServerConnection(BaseModel):
url: str
path: str
type: Optional[str] = "openapi" # openapi, mcp
auth_type: Optional[str]
headers: Optional[dict | str] = None
key: Optional[str]
config: Optional[dict]
@ -100,13 +168,47 @@ async def set_tool_servers_config(
form_data: ToolServersConfigForm,
user=Depends(get_admin_user),
):
for connection in request.app.state.config.TOOL_SERVER_CONNECTIONS:
server_type = connection.get("type", "openapi")
auth_type = connection.get("auth_type", "none")
if auth_type == "oauth_2.1":
# Remove existing OAuth clients for tool servers
server_id = connection.get("info", {}).get("id")
client_key = f"{server_type}:{server_id}"
try:
request.app.state.oauth_client_manager.remove_client(client_key)
except:
pass
# Set new tool server connections
request.app.state.config.TOOL_SERVER_CONNECTIONS = [
connection.model_dump() for connection in form_data.TOOL_SERVER_CONNECTIONS
]
request.app.state.TOOL_SERVERS = await get_tool_servers_data(
request.app.state.config.TOOL_SERVER_CONNECTIONS
)
await set_tool_servers(request)
for connection in request.app.state.config.TOOL_SERVER_CONNECTIONS:
server_type = connection.get("type", "openapi")
if server_type == "mcp":
server_id = connection.get("info", {}).get("id")
auth_type = connection.get("auth_type", "none")
if auth_type == "oauth_2.1" and server_id:
try:
oauth_client_info = connection.get("info", {}).get(
"oauth_client_info", ""
)
oauth_client_info = decrypt_data(oauth_client_info)
request.app.state.oauth_client_manager.add_client(
f"{server_type}:{server_id}",
OAuthClientInformationFull(**oauth_client_info),
)
except Exception as e:
log.debug(f"Failed to add OAuth client for MCP tool server: {e}")
continue
return {
"TOOL_SERVER_CONNECTIONS": request.app.state.config.TOOL_SERVER_CONNECTIONS,
@ -121,19 +223,129 @@ async def verify_tool_servers_config(
Verify the connection to the tool server.
"""
try:
if form_data.type == "mcp":
if form_data.auth_type == "oauth_2.1":
discovery_urls = get_discovery_urls(form_data.url)
for discovery_url in discovery_urls:
log.debug(
f"Trying to fetch OAuth 2.1 discovery document from {discovery_url}"
)
async with aiohttp.ClientSession(trust_env=True) as session:
async with session.get(
discovery_url
) as oauth_server_metadata_response:
if oauth_server_metadata_response.status == 200:
try:
oauth_server_metadata = (
OAuthMetadata.model_validate(
await oauth_server_metadata_response.json()
)
)
return {
"status": True,
"oauth_server_metadata": oauth_server_metadata.model_dump(
mode="json"
),
}
except Exception as e:
log.info(
f"Failed to parse OAuth 2.1 discovery document: {e}"
)
raise HTTPException(
status_code=400,
detail=f"Failed to parse OAuth 2.1 discovery document from {discovery_url}",
)
token = None
if form_data.auth_type == "bearer":
token = form_data.key
elif form_data.auth_type == "session":
token = request.state.token.credentials
raise HTTPException(
status_code=400,
detail=f"Failed to fetch OAuth 2.1 discovery document from {discovery_urls}",
)
else:
try:
client = MCPClient()
headers = None
url = f"{form_data.url}/{form_data.path}"
return await get_tool_server_data(token, url)
token = None
if form_data.auth_type == "bearer":
token = form_data.key
elif form_data.auth_type == "session":
token = request.state.token.credentials
elif form_data.auth_type == "system_oauth":
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),
)
if oauth_token:
token = oauth_token.get("access_token", "")
except Exception as e:
pass
if token:
headers = {"Authorization": f"Bearer {token}"}
if form_data.headers and isinstance(form_data.headers, dict):
if headers is None:
headers = {}
headers.update(form_data.headers)
await client.connect(form_data.url, headers=headers)
specs = await client.list_tool_specs()
return {
"status": True,
"specs": specs,
}
except Exception as e:
log.debug(f"Failed to create MCP client: {e}")
raise HTTPException(
status_code=400,
detail=f"Failed to create MCP client",
)
finally:
if client:
await client.disconnect()
else: # openapi
token = None
headers = None
if form_data.auth_type == "bearer":
token = form_data.key
elif form_data.auth_type == "session":
token = request.state.token.credentials
elif form_data.auth_type == "system_oauth":
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),
)
)
if oauth_token:
token = oauth_token.get("access_token", "")
except Exception as e:
pass
if token:
headers = {"Authorization": f"Bearer {token}"}
if form_data.headers and isinstance(form_data.headers, dict):
if headers is None:
headers = {}
headers.update(form_data.headers)
url = get_tool_server_url(form_data.url, form_data.path)
return await get_tool_server_data(url, headers=headers)
except HTTPException as e:
raise e
except Exception as e:
log.debug(f"Failed to connect to the tool server: {e}")
raise HTTPException(
status_code=400,
detail=f"Failed to connect to the tool server: {str(e)}",
detail=f"Failed to connect to the tool server",
)
@ -251,6 +463,7 @@ async def set_code_execution_config(
############################
class ModelsConfigForm(BaseModel):
DEFAULT_MODELS: Optional[str]
DEFAULT_PINNED_MODELS: Optional[str]
MODEL_ORDER_LIST: Optional[list[str]]
@ -258,6 +471,7 @@ class ModelsConfigForm(BaseModel):
async def get_models_config(request: Request, user=Depends(get_admin_user)):
return {
"DEFAULT_MODELS": request.app.state.config.DEFAULT_MODELS,
"DEFAULT_PINNED_MODELS": request.app.state.config.DEFAULT_PINNED_MODELS,
"MODEL_ORDER_LIST": request.app.state.config.MODEL_ORDER_LIST,
}
@ -267,9 +481,11 @@ async def set_models_config(
request: Request, form_data: ModelsConfigForm, user=Depends(get_admin_user)
):
request.app.state.config.DEFAULT_MODELS = form_data.DEFAULT_MODELS
request.app.state.config.DEFAULT_PINNED_MODELS = form_data.DEFAULT_PINNED_MODELS
request.app.state.config.MODEL_ORDER_LIST = form_data.MODEL_ORDER_LIST
return {
"DEFAULT_MODELS": request.app.state.config.DEFAULT_MODELS,
"DEFAULT_PINNED_MODELS": request.app.state.config.DEFAULT_PINNED_MODELS,
"MODEL_ORDER_LIST": request.app.state.config.MODEL_ORDER_LIST,
}

View file

@ -7,6 +7,8 @@ from open_webui.models.feedbacks import (
FeedbackModel,
FeedbackResponse,
FeedbackForm,
FeedbackUserResponse,
FeedbackListResponse,
Feedbacks,
)
@ -56,35 +58,10 @@ async def update_config(
}
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
class FeedbackUserResponse(FeedbackResponse):
user: Optional[UserResponse] = None
@router.get("/feedbacks/all", response_model=list[FeedbackUserResponse])
@router.get("/feedbacks/all", response_model=list[FeedbackResponse])
async def get_all_feedbacks(user=Depends(get_admin_user)):
feedbacks = Feedbacks.get_all_feedbacks()
feedback_list = []
for feedback in feedbacks:
user = Users.get_user_by_id(feedback.user_id)
feedback_list.append(
FeedbackUserResponse(
**feedback.model_dump(),
user=UserResponse(**user.model_dump()) if user else None,
)
)
return feedback_list
return feedbacks
@router.delete("/feedbacks/all")
@ -111,6 +88,31 @@ async def delete_feedbacks(user=Depends(get_verified_user)):
return success
PAGE_ITEM_COUNT = 30
@router.get("/feedbacks/list", response_model=FeedbackListResponse)
async def get_feedbacks(
order_by: Optional[str] = None,
direction: Optional[str] = None,
page: Optional[int] = 1,
user=Depends(get_admin_user),
):
limit = PAGE_ITEM_COUNT
page = max(1, page)
skip = (page - 1) * limit
filter = {}
if order_by:
filter["order_by"] = order_by
if direction:
filter["direction"] = direction
result = Feedbacks.get_feedback_items(filter=filter, skip=skip, limit=limit)
return result
@router.post("/feedback", response_model=FeedbackModel)
async def create_feedback(
request: Request,
@ -129,7 +131,10 @@ async def create_feedback(
@router.get("/feedback/{id}", response_model=FeedbackModel)
async def get_feedback_by_id(id: str, user=Depends(get_verified_user)):
feedback = Feedbacks.get_feedback_by_id_and_user_id(id=id, user_id=user.id)
if user.role == "admin":
feedback = Feedbacks.get_feedback_by_id(id=id)
else:
feedback = Feedbacks.get_feedback_by_id_and_user_id(id=id, user_id=user.id)
if not feedback:
raise HTTPException(
@ -143,9 +148,12 @@ async def get_feedback_by_id(id: str, user=Depends(get_verified_user)):
async def update_feedback_by_id(
id: str, form_data: FeedbackForm, user=Depends(get_verified_user)
):
feedback = Feedbacks.update_feedback_by_id_and_user_id(
id=id, user_id=user.id, form_data=form_data
)
if user.role == "admin":
feedback = Feedbacks.update_feedback_by_id(id=id, form_data=form_data)
else:
feedback = Feedbacks.update_feedback_by_id_and_user_id(
id=id, user_id=user.id, form_data=form_data
)
if not feedback:
raise HTTPException(

View file

@ -6,8 +6,10 @@ from fnmatch import fnmatch
from pathlib import Path
from typing import Optional
from urllib.parse import quote
import asyncio
from fastapi import (
BackgroundTasks,
APIRouter,
Depends,
File,
@ -18,9 +20,12 @@ from fastapi import (
status,
Query,
)
from fastapi.responses import FileResponse, StreamingResponse
from open_webui.constants import ERROR_MESSAGES
from open_webui.env import SRC_LOG_LEVELS
from open_webui.retrieval.vector.factory import VECTOR_DB_CLIENT
from open_webui.models.users import Users
from open_webui.models.files import (
@ -30,18 +35,24 @@ from open_webui.models.files import (
Files,
)
from open_webui.models.knowledge import Knowledges
from open_webui.models.groups import Groups
from open_webui.routers.knowledge import get_knowledge, get_knowledge_list
from open_webui.routers.retrieval import ProcessFileForm, process_file
from open_webui.routers.audio import transcribe
from open_webui.storage.provider import Storage
from open_webui.utils.auth import get_admin_user, get_verified_user
from open_webui.utils.access_control import has_access
from pydantic import BaseModel
log = logging.getLogger(__name__)
log.setLevel(SRC_LOG_LEVELS["MODELS"])
router = APIRouter()
@ -50,31 +61,37 @@ router = APIRouter()
############################
# TODO: Optimize this function to use the knowledge_file table for faster lookups.
def has_access_to_file(
file_id: Optional[str], access_type: str, user=Depends(get_verified_user)
) -> bool:
file = Files.get_file_by_id(file_id)
log.debug(f"Checking if user has {access_type} access to file")
if not file:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=ERROR_MESSAGES.NOT_FOUND,
)
has_access = False
knowledge_base_id = file.meta.get("collection_name") if file.meta else None
knowledge_bases = Knowledges.get_knowledges_by_file_id(file_id)
user_group_ids = {group.id for group in Groups.get_groups_by_member_id(user.id)}
for knowledge_base in knowledge_bases:
if knowledge_base.user_id == user.id or has_access(
user.id, access_type, knowledge_base.access_control, user_group_ids
):
return True
knowledge_base_id = file.meta.get("collection_name") if file.meta else None
if knowledge_base_id:
knowledge_bases = Knowledges.get_knowledge_bases_by_user_id(
user.id, access_type
)
for knowledge_base in knowledge_bases:
if knowledge_base.id == knowledge_base_id:
has_access = True
break
return True
return has_access
return False
############################
@ -82,14 +99,85 @@ def has_access_to_file(
############################
def process_uploaded_file(request, file, file_path, file_item, file_metadata, user):
try:
if file.content_type:
stt_supported_content_types = getattr(
request.app.state.config, "STT_SUPPORTED_CONTENT_TYPES", []
)
if any(
fnmatch(file.content_type, content_type)
for content_type in (
stt_supported_content_types
if stt_supported_content_types
and any(t.strip() for t in stt_supported_content_types)
else ["audio/*", "video/webm"]
)
):
file_path = Storage.get_file(file_path)
result = transcribe(request, file_path, file_metadata, user)
process_file(
request,
ProcessFileForm(
file_id=file_item.id, content=result.get("text", "")
),
user=user,
)
elif (not file.content_type.startswith(("image/", "video/"))) or (
request.app.state.config.CONTENT_EXTRACTION_ENGINE == "external"
):
process_file(request, ProcessFileForm(file_id=file_item.id), user=user)
else:
raise Exception(
f"File type {file.content_type} is not supported for processing"
)
else:
log.info(
f"File type {file.content_type} is not provided, but trying to process anyway"
)
process_file(request, ProcessFileForm(file_id=file_item.id), user=user)
except Exception as e:
log.error(f"Error processing file: {file_item.id}")
Files.update_file_data_by_id(
file_item.id,
{
"status": "failed",
"error": str(e.detail) if hasattr(e, "detail") else str(e),
},
)
@router.post("/", response_model=FileModelResponse)
def upload_file(
request: Request,
background_tasks: BackgroundTasks,
file: UploadFile = File(...),
metadata: Optional[dict | str] = Form(None),
process: bool = Query(True),
process_in_background: bool = Query(True),
user=Depends(get_verified_user),
):
return upload_file_handler(
request,
file=file,
metadata=metadata,
process=process,
process_in_background=process_in_background,
user=user,
background_tasks=background_tasks,
)
def upload_file_handler(
request: Request,
file: UploadFile = File(...),
metadata: Optional[dict | str] = Form(None),
process: bool = Query(True),
internal: bool = False,
process_in_background: bool = Query(True),
user=Depends(get_verified_user),
background_tasks: Optional[BackgroundTasks] = None,
):
log.info(f"file.content_type: {file.content_type}")
@ -111,7 +199,7 @@ def upload_file(
# Remove the leading dot from the file extension
file_extension = file_extension[1:] if file_extension else ""
if (not internal) and request.app.state.config.ALLOWED_FILE_EXTENSIONS:
if process and request.app.state.config.ALLOWED_FILE_EXTENSIONS:
request.app.state.config.ALLOWED_FILE_EXTENSIONS = [
ext for ext in request.app.state.config.ALLOWED_FILE_EXTENSIONS if ext
]
@ -128,13 +216,16 @@ def upload_file(
id = str(uuid.uuid4())
name = filename
filename = f"{id}_{filename}"
tags = {
"OpenWebUI-User-Email": user.email,
"OpenWebUI-User-Id": user.id,
"OpenWebUI-User-Name": user.name,
"OpenWebUI-File-Id": id,
}
contents, file_path = Storage.upload_file(file.file, filename, tags)
contents, file_path = Storage.upload_file(
file.file,
filename,
{
"OpenWebUI-User-Email": user.email,
"OpenWebUI-User-Id": user.id,
"OpenWebUI-User-Name": user.name,
"OpenWebUI-File-Id": id,
},
)
file_item = Files.insert_new_file(
user.id,
@ -143,6 +234,9 @@ def upload_file(
"id": id,
"filename": name,
"path": file_path,
"data": {
**({"status": "pending"} if process else {}),
},
"meta": {
"name": name,
"content_type": file.content_type,
@ -152,48 +246,37 @@ def upload_file(
}
),
)
if process:
try:
if file.content_type:
if file.content_type.startswith("audio/") or file.content_type in {
"video/webm"
}:
file_path = Storage.get_file(file_path)
result = transcribe(request, file_path, file_metadata)
process_file(
request,
ProcessFileForm(file_id=id, content=result.get("text", "")),
user=user,
)
elif (not file.content_type.startswith(("image/", "video/"))) or (
request.app.state.config.CONTENT_EXTRACTION_ENGINE == "external"
):
process_file(request, ProcessFileForm(file_id=id), user=user)
else:
log.info(
f"File type {file.content_type} is not provided, but trying to process anyway"
)
process_file(request, ProcessFileForm(file_id=id), user=user)
file_item = Files.get_file_by_id(id=id)
except Exception as e:
log.exception(e)
log.error(f"Error processing file: {file_item.id}")
file_item = FileModelResponse(
**{
**file_item.model_dump(),
"error": str(e.detail) if hasattr(e, "detail") else str(e),
}
if background_tasks and process_in_background:
background_tasks.add_task(
process_uploaded_file,
request,
file,
file_path,
file_item,
file_metadata,
user,
)
if file_item:
return file_item
return {"status": True, **file_item.model_dump()}
else:
process_uploaded_file(
request,
file,
file_path,
file_item,
file_metadata,
user,
)
return {"status": True, **file_item.model_dump()}
else:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=ERROR_MESSAGES.DEFAULT("Error uploading file"),
)
if file_item:
return file_item
else:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=ERROR_MESSAGES.DEFAULT("Error uploading file"),
)
except Exception as e:
log.exception(e)
@ -276,6 +359,7 @@ async def delete_all_files(user=Depends(get_admin_user)):
if result:
try:
Storage.delete_all_files()
VECTOR_DB_CLIENT.reset()
except Exception as e:
log.exception(e)
log.error("Error deleting files")
@ -319,6 +403,63 @@ async def get_file_by_id(id: str, user=Depends(get_verified_user)):
)
@router.get("/{id}/process/status")
async def get_file_process_status(
id: str, stream: bool = Query(False), user=Depends(get_verified_user)
):
file = Files.get_file_by_id(id)
if not file:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=ERROR_MESSAGES.NOT_FOUND,
)
if (
file.user_id == user.id
or user.role == "admin"
or has_access_to_file(id, "read", user)
):
if stream:
MAX_FILE_PROCESSING_DURATION = 3600 * 2
async def event_stream(file_item):
if file_item:
for _ in range(MAX_FILE_PROCESSING_DURATION):
file_item = Files.get_file_by_id(file_item.id)
if file_item:
data = file_item.model_dump().get("data", {})
status = data.get("status")
if status:
event = {"status": status}
if status == "failed":
event["error"] = data.get("error")
yield f"data: {json.dumps(event)}\n\n"
if status in ("completed", "failed"):
break
else:
# Legacy
break
await asyncio.sleep(0.5)
else:
yield f"data: {json.dumps({'status': 'not_found'})}\n\n"
return StreamingResponse(
event_stream(file),
media_type="text/event-stream",
)
else:
return {"status": file.data.get("status", "pending")}
else:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=ERROR_MESSAGES.NOT_FOUND,
)
############################
# Get File Data Content By Id
############################
@ -593,12 +734,12 @@ async def delete_file_by_id(id: str, user=Depends(get_verified_user)):
or user.role == "admin"
or has_access_to_file(id, "write", user)
):
# We should add Chroma cleanup here
result = Files.delete_file_by_id(id)
if result:
try:
Storage.delete_file(file.path)
VECTOR_DB_CLIENT.delete(collection_name=f"file-{id}")
except Exception as e:
log.exception(e)
log.error("Error deleting files")

View file

@ -10,10 +10,15 @@ import mimetypes
from open_webui.models.folders import (
FolderForm,
FolderUpdateForm,
FolderModel,
FolderNameIdResponse,
Folders,
)
from open_webui.models.chats import Chats
from open_webui.models.files import Files
from open_webui.models.knowledge import Knowledges
from open_webui.config import UPLOAD_DIR
from open_webui.env import SRC_LOG_LEVELS
@ -40,24 +45,62 @@ router = APIRouter()
############################
@router.get("/", response_model=list[FolderModel])
async def get_folders(user=Depends(get_verified_user)):
@router.get("/", response_model=list[FolderNameIdResponse])
async def get_folders(request: Request, user=Depends(get_verified_user)):
if request.app.state.config.ENABLE_FOLDERS is False:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail=ERROR_MESSAGES.ACCESS_PROHIBITED,
)
if user.role != "admin" and not has_permission(
user.id,
"features.folders",
request.app.state.config.USER_PERMISSIONS,
):
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail=ERROR_MESSAGES.ACCESS_PROHIBITED,
)
folders = Folders.get_folders_by_user_id(user.id)
return [
{
**folder.model_dump(),
"items": {
"chats": [
{"title": chat.title, "id": chat.id}
for chat in Chats.get_chats_by_folder_id_and_user_id(
folder.id, user.id
)
]
},
}
for folder in folders
]
# Verify folder data integrity
folder_list = []
for folder in folders:
if folder.parent_id and not Folders.get_folder_by_id_and_user_id(
folder.parent_id, user.id
):
folder = Folders.update_folder_parent_id_by_id_and_user_id(
folder.id, user.id, None
)
if folder.data:
if "files" in folder.data:
valid_files = []
for file in folder.data["files"]:
if file.get("type") == "file":
if Files.check_access_by_user_id(
file.get("id"), user.id, "read"
):
valid_files.append(file)
elif file.get("type") == "collection":
if Knowledges.check_access_by_user_id(
file.get("id"), user.id, "read"
):
valid_files.append(file)
else:
valid_files.append(file)
folder.data["files"] = valid_files
Folders.update_folder_by_id_and_user_id(
folder.id, user.id, FolderUpdateForm(data=folder.data)
)
folder_list.append(FolderNameIdResponse(**folder.model_dump()))
return folder_list
############################
@ -78,7 +121,7 @@ def create_folder(form_data: FolderForm, user=Depends(get_verified_user)):
)
try:
folder = Folders.insert_new_folder(user.id, form_data.name)
folder = Folders.insert_new_folder(user.id, form_data)
return folder
except Exception as e:
log.exception(e)
@ -113,24 +156,24 @@ async def get_folder_by_id(id: str, user=Depends(get_verified_user)):
@router.post("/{id}/update")
async def update_folder_name_by_id(
id: str, form_data: FolderForm, user=Depends(get_verified_user)
id: str, form_data: FolderUpdateForm, user=Depends(get_verified_user)
):
folder = Folders.get_folder_by_id_and_user_id(id, user.id)
if folder:
existing_folder = Folders.get_folder_by_parent_id_and_user_id_and_name(
folder.parent_id, user.id, form_data.name
)
if existing_folder:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=ERROR_MESSAGES.DEFAULT("Folder already exists"),
if form_data.name is not None:
# Check if folder with same name exists
existing_folder = Folders.get_folder_by_parent_id_and_user_id_and_name(
folder.parent_id, user.id, form_data.name
)
if existing_folder and existing_folder.id != id:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=ERROR_MESSAGES.DEFAULT("Folder already exists"),
)
try:
folder = Folders.update_folder_name_by_id_and_user_id(
id, user.id, form_data.name
)
folder = Folders.update_folder_by_id_and_user_id(id, user.id, form_data)
return folder
except Exception as e:
log.exception(e)
@ -231,33 +274,52 @@ async def update_folder_is_expanded_by_id(
@router.delete("/{id}")
async def delete_folder_by_id(
request: Request, id: str, user=Depends(get_verified_user)
request: Request,
id: str,
delete_contents: Optional[bool] = True,
user=Depends(get_verified_user),
):
chat_delete_permission = has_permission(
user.id, "chat.delete", request.app.state.config.USER_PERMISSIONS
)
if user.role != "admin" and not chat_delete_permission:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail=ERROR_MESSAGES.ACCESS_PROHIBITED,
if Chats.count_chats_by_folder_id_and_user_id(id, user.id):
chat_delete_permission = has_permission(
user.id, "chat.delete", request.app.state.config.USER_PERMISSIONS
)
folder = Folders.get_folder_by_id_and_user_id(id, user.id)
if folder:
try:
result = Folders.delete_folder_by_id_and_user_id(id, user.id)
if result:
return result
else:
raise Exception("Error deleting folder")
except Exception as e:
log.exception(e)
log.error(f"Error deleting folder: {id}")
if user.role != "admin" and not chat_delete_permission:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=ERROR_MESSAGES.DEFAULT("Error deleting folder"),
status_code=status.HTTP_403_FORBIDDEN,
detail=ERROR_MESSAGES.ACCESS_PROHIBITED,
)
folders = []
folders.append(Folders.get_folder_by_id_and_user_id(id, user.id))
while folders:
folder = folders.pop()
if folder:
try:
folder_ids = Folders.delete_folder_by_id_and_user_id(id, user.id)
for folder_id in folder_ids:
if delete_contents:
Chats.delete_chats_by_user_id_and_folder_id(user.id, folder_id)
else:
Chats.move_chats_by_user_id_and_folder_id(
user.id, folder_id, None
)
return True
except Exception as e:
log.exception(e)
log.error(f"Error deleting folder: {id}")
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=ERROR_MESSAGES.DEFAULT("Error deleting folder"),
)
finally:
# Get all subfolders
subfolders = Folders.get_folders_by_parent_id_and_user_id(
folder.id, user.id
)
folders.extend(subfolders)
else:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,

View file

@ -10,6 +10,8 @@ from open_webui.models.functions import (
FunctionForm,
FunctionModel,
FunctionResponse,
FunctionUserResponse,
FunctionWithValvesModel,
Functions,
)
from open_webui.utils.plugin import (
@ -41,14 +43,19 @@ async def get_functions(user=Depends(get_verified_user)):
return Functions.get_functions()
@router.get("/list", response_model=list[FunctionUserResponse])
async def get_function_list(user=Depends(get_admin_user)):
return Functions.get_function_list()
############################
# ExportFunctions
############################
@router.get("/export", response_model=list[FunctionModel])
async def get_functions(user=Depends(get_admin_user)):
return Functions.get_functions()
@router.get("/export", response_model=list[FunctionModel | FunctionWithValvesModel])
async def get_functions(include_valves: bool = False, user=Depends(get_admin_user)):
return Functions.get_functions(include_valves=include_valves)
############################
@ -105,7 +112,7 @@ async def load_function_from_url(
)
try:
async with aiohttp.ClientSession() as session:
async with aiohttp.ClientSession(trust_env=True) as session:
async with session.get(
url, headers={"Content-Type": "application/json"}
) as resp:
@ -131,15 +138,41 @@ async def load_function_from_url(
############################
class SyncFunctionsForm(FunctionForm):
functions: list[FunctionModel] = []
class SyncFunctionsForm(BaseModel):
functions: list[FunctionWithValvesModel] = []
@router.post("/sync", response_model=Optional[FunctionModel])
@router.post("/sync", response_model=list[FunctionWithValvesModel])
async def sync_functions(
request: Request, form_data: SyncFunctionsForm, user=Depends(get_admin_user)
):
return Functions.sync_functions(user.id, form_data.functions)
try:
for function in form_data.functions:
function.content = replace_imports(function.content)
function_module, function_type, frontmatter = load_function_module_by_id(
function.id,
content=function.content,
)
if hasattr(function_module, "Valves") and function.valves:
Valves = function_module.Valves
try:
Valves(
**{k: v for k, v in function.valves.items() if v is not None}
)
except Exception as e:
log.exception(
f"Error validating valves for function {function.id}: {e}"
)
raise e
return Functions.sync_functions(user.id, form_data.functions)
except Exception as e:
log.exception(f"Failed to load a function: {e}")
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=ERROR_MESSAGES.DEFAULT(e),
)
############################
@ -177,6 +210,9 @@ async def create_new_function(
function_cache_dir = CACHE_DIR / "functions" / form_data.id
function_cache_dir.mkdir(parents=True, exist_ok=True)
if function_type == "filter" and getattr(function_module, "toggle", None):
Functions.update_function_metadata_by_id(id, {"toggle": True})
if function:
return function
else:
@ -293,6 +329,9 @@ async def update_function_by_id(
function = Functions.update_function_by_id(id, updated)
if function_type == "filter" and getattr(function_module, "toggle", None):
Functions.update_function_metadata_by_id(id, {"toggle": True})
if function:
return function
else:
@ -398,8 +437,10 @@ async def update_function_valves_by_id(
try:
form_data = {k: v for k, v in form_data.items() if v is not None}
valves = Valves(**form_data)
Functions.update_function_valves_by_id(id, valves.model_dump())
return valves.model_dump()
valves_dict = valves.model_dump(exclude_unset=True)
Functions.update_function_valves_by_id(id, valves_dict)
return valves_dict
except Exception as e:
log.exception(f"Error updating function values by id {id}: {e}")
raise HTTPException(
@ -481,10 +522,11 @@ async def update_function_user_valves_by_id(
try:
form_data = {k: v for k, v in form_data.items() if v is not None}
user_valves = UserValves(**form_data)
user_valves_dict = user_valves.model_dump(exclude_unset=True)
Functions.update_user_valves_by_id_and_user_id(
id, user.id, user_valves.model_dump()
id, user.id, user_valves_dict
)
return user_valves.model_dump()
return user_valves_dict
except Exception as e:
log.exception(f"Error updating function user valves by id {id}: {e}")
raise HTTPException(

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