Compare commits

...
269 Commits
Author SHA1 Message Date
Timothy Jaeryang Baek 29a2719595 Merge pull request #7562 from open-webui/dev
0.4.8
2024-12-07 00:42:50 -08:00
Timothy Jaeryang Baek 976676a482 chore: format 2024-12-07 00:34:52 -08:00
Timothy Jaeryang Baek 19ad8b4f26 doc: changelog 2024-12-07 00:29:23 -08:00
Timothy Jaeryang Baek f04767f1fe Merge pull request #7655 from aleixdorca/dev
i18n: Update catalan translation.json
2024-12-06 12:01:38 -08:00
Timothy Jaeryang Baek 36085f3036 refac 2024-12-06 11:55:25 -08:00
Aleix Dorca 4a46417275 Update catalan translation.json 2024-12-06 15:02:54 +01:00
Timothy Jaeryang Baek a68e717dcf Update CHANGELOG.md 2024-12-05 14:11:52 -08:00
Timothy Jaeryang Baek 346b7ddb14 refac: tailwind 2024-12-05 10:42:37 -08:00
Timothy Jaeryang Baek 31545439a0 Merge pull request #7590 from HUD4K/cs-CZ-translation
Cs cz translation
2024-12-05 10:41:55 -08:00
Timothy Jaeryang Baek 5401b24717 Merge pull request #7589 from AliveDedSec/main
Updated and expanded translation into Russian
2024-12-05 10:41:44 -08:00
Michal Hudak 152b1224a8 updated changelog 2024-12-04 09:55:34 +01:00
Michal Hudak ac5ea4ff25 Updated and improved cs-CZ translation 2024-12-04 09:30:08 +01:00
SVAROG 6e73b3d075 Update translation.json 2024-12-04 07:50:43 +03:00
SVAROG 054a718da0 Update translation.json 2024-12-04 07:43:46 +03:00
SVAROG a63326b550 Update translation.json 2024-12-04 07:37:10 +03:00
SVAROG 547d17c362 Update translation.json 2024-12-04 07:35:35 +03:00
SVAROG 10382998f1 Update translation.json 2024-12-04 07:24:14 +03:00
SVAROG c5f7ad2009 Update translation.json 2024-12-04 06:56:30 +03:00
SVAROG 6173636a11 Update translation.json 2024-12-04 06:26:55 +03:00
Timothy Jaeryang Baek 9b6076f726 Merge pull request #7549 from panda44312/dev
i18n: Update Traditional Chinese translation
2024-12-02 23:42:34 -08:00
Panda abbf37f3d9 Update translation.json 2024-12-02 09:49:02 +01:00
Timothy Jaeryang Baek 5433340bb1 enh: banner md support 2024-12-01 23:58:22 -08:00
Timothy Jaeryang Baek 1dab0cfada refac 2024-12-01 23:35:24 -08:00
Timothy Jaeryang Baek 2714a1bb64 refac: styling 2024-12-01 23:29:21 -08:00
Timothy Jaeryang Baek 4053de5825 refac: styling 2024-12-01 23:16:00 -08:00
Timothy Jaeryang Baek 29573afb91 Merge pull request #7536 from marvinvr/main
style: Add proper favicon and iOS home screen support
2024-12-01 22:21:33 -08:00
Marvin von Rappard 39b7e54482 feat: properly format favicons and make home screen icons look better 2024-12-02 07:06:32 +01:00
Timothy Jaeryang Baek 7f77828e3f refac: styling 2024-12-01 21:50:21 -08:00
Timothy Jaeryang Baek 59c3a18118 enh: BYPASS_MODEL_ACCESS_CONTROL 2024-12-01 18:25:44 -08:00
Timothy Jaeryang Baek 460992613f refac: styling 2024-12-01 17:15:16 -08:00
Timothy Jaeryang Baek 0055f3dcb6 refac: rich text input 2024-12-01 14:49:10 -08:00
Timothy Jaeryang Baek 1c078bdb55 fix: api query collection 2024-12-01 13:36:36 -08:00
Timothy Jaeryang Baek 85d2b898c6 Merge pull request #7512 from panda44312/dev
i18n: Update Simplified Chinese Translation
2024-12-01 12:30:15 -08:00
Timothy Jaeryang Baek b903e3a896 Merge pull request #7507 from TiancongLx/dev
i18n: fix zh-TW
2024-12-01 12:30:05 -08:00
panda44312 aecb7ae398 Update translation.json 2024-12-01 15:43:53 +01:00
Tiancong Li 7f4a83ea0e i18n: fix zh-TW 2024-12-01 18:16:05 +08:00
Timothy Jaeryang Baek c4ea31357f Merge pull request #7475 from open-webui/dev
0.4.7
2024-12-01 00:42:48 -08:00
Timothy Jaeryang Baek 19bcda2362 refac: styling 2024-12-01 00:42:14 -08:00
Timothy Jaeryang Baek 39c2f70778 refac 2024-12-01 00:23:06 -08:00
Timothy Jaeryang Baek 368e11e2b2 doc: changelog 2024-12-01 00:19:44 -08:00
Timothy Jaeryang Baek 430a79e177 refac 2024-12-01 00:15:43 -08:00
Timothy Jaeryang Baek 9b25efc3bb refac 2024-11-30 23:50:05 -08:00
Timothy Jaeryang Baek 746fe9ea16 refac: autocomplete 2024-11-30 23:46:29 -08:00
Timothy Jaeryang Baek 2fac9b45cd chore: format 2024-11-30 23:36:30 -08:00
Timothy Jaeryang Baek 370f97b44e refac 2024-11-30 23:33:19 -08:00
Timothy Jaeryang Baek a26c5d9549 Merge pull request #7501 from open-webui/dependabot/pip/backend/dev/aiohttp-3.11.8
chore(deps): bump aiohttp from 3.10.8 to 3.11.8 in /backend
2024-11-30 22:58:10 -08:00
Timothy Jaeryang Baek 96cc8bf127 Merge pull request #7500 from open-webui/dependabot/pip/backend/dev/python-multipart-0.0.18
chore(deps): bump python-multipart from 0.0.17 to 0.0.18 in /backend
2024-11-30 22:58:03 -08:00
Timothy Jaeryang Baek 4bf9d071f0 Merge pull request #7499 from open-webui/dependabot/pip/backend/dev/pymilvus-2.5.0
chore(deps): bump pymilvus from 2.4.9 to 2.5.0 in /backend
2024-11-30 22:57:56 -08:00
Timothy Jaeryang Baek 1a74199656 Merge pull request #7498 from open-webui/dependabot/pip/backend/dev/sentence-transformers-3.3.1
chore(deps): bump sentence-transformers from 3.2.0 to 3.3.1 in /backend
2024-11-30 22:57:49 -08:00
Timothy Jaeryang Baek 4e19e66e7d Merge pull request #7497 from open-webui/dependabot/pip/backend/dev/alembic-1.14.0
chore(deps): bump alembic from 1.13.2 to 1.14.0 in /backend
2024-11-30 22:57:36 -08:00
Timothy Jaeryang Baek 27b1a35778 refac: styling 2024-11-30 22:32:40 -08:00
Timothy Jaeryang Baek ba0b3a984d refac 2024-11-30 22:29:53 -08:00
dependabot[bot] 2f84e6b877 chore(deps): bump aiohttp from 3.10.8 to 3.11.8 in /backend
Bumps [aiohttp](https://github.com/aio-libs/aiohttp) from 3.10.8 to 3.11.8.
- [Release notes](https://github.com/aio-libs/aiohttp/releases)
- [Changelog](https://github.com/aio-libs/aiohttp/blob/master/CHANGES.rst)
- [Commits](https://github.com/aio-libs/aiohttp/compare/v3.10.8...v3.11.8)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-01 02:40:53 +00:00
dependabot[bot] f311c03a21 chore(deps): bump python-multipart from 0.0.17 to 0.0.18 in /backend
Bumps [python-multipart](https://github.com/Kludex/python-multipart) from 0.0.17 to 0.0.18.
- [Release notes](https://github.com/Kludex/python-multipart/releases)
- [Changelog](https://github.com/Kludex/python-multipart/blob/master/CHANGELOG.md)
- [Commits](https://github.com/Kludex/python-multipart/compare/0.0.17...0.0.18)

---
updated-dependencies:
- dependency-name: python-multipart
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-01 02:40:36 +00:00
dependabot[bot] ef3b6083ff chore(deps): bump pymilvus from 2.4.9 to 2.5.0 in /backend
Bumps [pymilvus](https://github.com/milvus-io/pymilvus) from 2.4.9 to 2.5.0.
- [Release notes](https://github.com/milvus-io/pymilvus/releases)
- [Commits](https://github.com/milvus-io/pymilvus/compare/v2.4.9...v2.5.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-01 02:40:34 +00:00
dependabot[bot] d23fac2a5b chore(deps): bump sentence-transformers from 3.2.0 to 3.3.1 in /backend
Bumps [sentence-transformers](https://github.com/UKPLab/sentence-transformers) from 3.2.0 to 3.3.1.
- [Release notes](https://github.com/UKPLab/sentence-transformers/releases)
- [Commits](https://github.com/UKPLab/sentence-transformers/compare/v3.2.0...v3.3.1)

---
updated-dependencies:
- dependency-name: sentence-transformers
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-01 02:40:29 +00:00
dependabot[bot] 8cb981c3f4 chore(deps): bump alembic from 1.13.2 to 1.14.0 in /backend
Bumps [alembic](https://github.com/sqlalchemy/alembic) from 1.13.2 to 1.14.0.
- [Release notes](https://github.com/sqlalchemy/alembic/releases)
- [Changelog](https://github.com/sqlalchemy/alembic/blob/main/CHANGES)
- [Commits](https://github.com/sqlalchemy/alembic/commits)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-01 02:40:25 +00:00
Timothy Jaeryang Baek 81a8ad2762 refac: autocomplete settings 2024-11-30 18:30:59 -08:00
Timothy Jaeryang Baekandhi-unc1e dbf6ec71fe enh: remove trailing slash from ollama
Co-Authored-By: hi-unc1e <67778054+hi-unc1e@users.noreply.github.com>
2024-11-30 18:13:10 -08:00
Timothy Jaeryang Baek 0b17ff6eef Merge pull request #7493 from diwakar-s-maurya/dev
feat: Feature to set HTTP header "Content-Security-Policy"
2024-11-30 18:11:20 -08:00
Timothy Jaeryang Baek 19663e539a enh: temporary chat shortcut 2024-11-30 18:07:49 -08:00
Timothy Jaeryang Baek c192475528 refac: autocompletion 2024-11-30 18:02:21 -08:00
Timothy Jaeryang Baek 9e436fe6b0 refac: styling 2024-11-30 16:01:26 -08:00
Timothy Jaeryang Baek 96b9f81ca7 refac: styling 2024-11-30 15:53:36 -08:00
Timothy Jaeryang Baek 62622893a5 refac: styling 2024-11-30 15:46:42 -08:00
Timothy Jaeryang Baek aed2caefe1 refac 2024-11-30 15:44:04 -08:00
Timothy Jaeryang Baek acb61d3c42 enh: rich text input preserve breaks 2024-11-30 15:05:08 -08:00
Timothy Jaeryang Baek d3778b0bda refac: styling 2024-11-30 14:16:39 -08:00
Timothy Jaeryang Baek fda26b4ad0 refac: rich text input 2024-11-30 14:15:08 -08:00
Timothy Jaeryang Baek 5b879a2121 refac: autocompletion behaviour 2024-11-30 13:10:47 -08:00
Timothy Jaeryang Baek 51e344f5b2 refac 2024-11-30 12:24:23 -08:00
Timothy Jaeryang Baek d0eb59ffdb refac: table styling 2024-11-30 12:22:07 -08:00
Diwakar 541ff6b41a Feature to set HTTP header "Content-Security-Policy"
Introduce CONTENT_SECURITY_POLICY environment variable to set HTTP header "Content-Security-Policy".

Content Security Policy (CSP) is a feature that helps to prevent or minimize the risk of certain types of security threats. It consists of a series of instructions from a website to a browser, which instruct the browser to place restrictions on the things that the code comprising the site is allowed to do.
https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP
2024-11-30 21:31:54 +07:00
Timothy Jaeryang Baek 3792051604 enh: swipe to accept completion 2024-11-30 01:06:56 -08:00
Timothy Jaeryang Baek 684a7f0455 refac 2024-11-30 00:36:01 -08:00
Timothy Jaeryang Baek 1f53e0922e enh: autocompletion 2024-11-30 00:29:27 -08:00
Timothy Jaeryang Baek ba6dc71810 refac: model editor 2024-11-30 00:10:30 -08:00
Timothy Jaeryang Baek 33e54a9d3b refac 2024-11-29 13:24:37 -08:00
Timothy Jaeryang Baek 9f981db0b9 Merge pull request #7422 from alpha-pet/feat-youtube-transscript-proxy
feat: Optional proxy setting for downloading Youtube transscripts
2024-11-29 12:40:46 -08:00
Timothy Jaeryang Baek 3e1c42bdc9 Merge pull request #7482 from TiancongLx/dev
i18n: Completed zh-TW
2024-11-29 11:46:23 -08:00
Tiancong Li aba610ac29 i18n: Completed zh-TW
已盡力將大陸簡體中文翻譯映射到台灣正體中文,如有翻譯不當之處,還請諒解並提交修正。
2024-11-30 03:09:51 +08:00
Timothy Jaeryang Baek b4fb0d1da2 refac 2024-11-29 01:10:46 -08:00
Timothy Jaeryang Baek f547f1424c refac 2024-11-29 01:02:32 -08:00
Timothy Jaeryang Baek a07213b5be feat: autocompletion 2024-11-29 00:16:49 -08:00
Timothy Jaeryang Baek 0e8e9820d0 feat: autocomplete backend endpoint 2024-11-28 23:53:52 -08:00
Timothy Jaeryang Baek 28ce102a79 refac: modal 2024-11-28 23:49:24 -08:00
Timothy Jaeryang Baek c1fd1d3490 refac: auto completion 2024-11-28 23:26:09 -08:00
Timothy Jaeryang Baek fa5e1f7452 refac 2024-11-28 23:24:16 -08:00
Timothy Jaeryang Baek 95000c7b15 feat: ai autocompletion 2024-11-28 23:22:53 -08:00
Timothy Jaeryang Baek 8300fa85b0 Merge pull request #7469 from TiancongLx/dev
i18n: update zh-CN
2024-11-28 22:17:07 -08:00
Timothy Jaeryang Baek 94235397cf Merge pull request #7466 from Slayingripper/main
feat: Added el-GR translation
2024-11-28 22:16:53 -08:00
Timothy Jaeryang Baek 99e20bfe82 Merge pull request #7453 from JonesJugHead/update-french-translation
i18n: Complete French translation for missing keys
2024-11-28 22:16:39 -08:00
Timothy Jaeryang Baek 6440d8629e Merge pull request #7451 from aleixdorca/dev
Update catalan translation.json
2024-11-28 22:16:28 -08:00
Tiancong Li e5563ca435 i18n: update zh-CN 2024-11-29 11:44:00 +08:00
Timothy Jaeryang Baek 53fbb19fa0 fix: /embed endpoint 2024-11-28 17:56:02 -08:00
slayingripper 297b9470e8 updated languages.json 2024-11-29 00:27:20 +00:00
slayingripper 1346559227 Added Greek Translation 2024-11-28 14:17:54 +00:00
Gui ac1ee888be Update french translation.json 2024-11-28 11:04:23 +01:00
Aleix Dorca ff0a7f1cbb Update catalan translation.json 2024-11-28 08:53:13 +01:00
Timothy Jaeryang Baek ea883b2ed4 refac 2024-11-27 19:38:57 -08:00
Timothy Jaeryang Baek b85cfdc90f refac: textarea 2024-11-27 19:26:05 -08:00
Timothy Jaeryang Baek 17ed7351a4 refac 2024-11-27 19:24:20 -08:00
Timothy Jaeryang Baek 7be93d2a84 fix: tools export 2024-11-27 18:11:50 -08:00
Timothy Jaeryang Baek 5d724d0b84 Merge pull request #7420 from Xelaph/dev
**i18n** - Update Dutch translation.json
2024-11-27 14:32:45 -08:00
Timothy Jaeryang Baek dbd6ac8080 refac: textarea 2024-11-27 11:25:54 -08:00
Thomas Rehn 53296c1005 [feat] Allow use of proxy for downloading Youtube transscripts 2024-11-27 15:20:48 +01:00
Alex 26615ba995 Update translation.json
Updated some translations for better consistency and accuracy. Also translated some newly added sentences
2024-11-27 15:04:54 +01:00
Timothy Jaeryang Baek c5ef53a09f refac: disable empty model id 2024-11-26 21:28:49 -08:00
Timothy Jaeryang Baek 0a26c41c7b Merge pull request #7399 from open-webui/dev
0.4.6
2024-11-26 20:24:33 -08:00
Timothy Jaeryang Baek da6535eeb4 refac: wording 2024-11-26 20:19:46 -08:00
Timothy Jaeryang Baek 2db35d5969 chore: format 2024-11-26 20:14:48 -08:00
Timothy Jaeryang Baek 582ce23bb5 doc: changelog 2024-11-26 20:12:01 -08:00
Timothy Jaeryang Baek f42cc90a00 doc: changelogs 2024-11-26 20:03:10 -08:00
Timothy Jaeryang Baek cbc7801b0e chore: format 2024-11-26 20:03:06 -08:00
Timothy Jaeryang Baek 22b5feb747 Merge pull request #7401 from walker-chen2024/main
bugfix - {{CURRENT_WEEKDAY}} is not working
2024-11-26 19:54:27 -08:00
Walker.Chen 4b536b5283 bugfix - {{CURRENT_WEEKDAY}} is not working
Signed-off-by: Walker.Chen <walker_chen@moremote.com>  https://uniwillai.net
2024-11-27 08:53:45 +08:00
Timothy Jaeryang Baek ede29e98b7 refac 2024-11-26 15:30:35 -08:00
Timothy Jaeryang Baek 789e1db260 refac: textarea 2024-11-26 14:14:26 -08:00
Timothy Jaeryang Baek 4383306770 refac 2024-11-26 14:00:49 -08:00
Timothy Jaeryang Baek 13796fe3b3 fix: legacy query generation support 2024-11-26 13:44:19 -08:00
Timothy Jaeryang Baek 63402c48a8 refac: query gen prompt 2024-11-26 10:23:29 -08:00
Timothy Jaeryang Baek 44efd4d372 refac 2024-11-26 10:17:17 -08:00
Timothy Jaeryang Baek a18292da84 Merge pull request #7381 from panda44312/dev
i18n - Update Simplified Chinese translation
2024-11-26 08:59:40 -08:00
Timothy Jaeryang Baek adba11ebeb Merge pull request #7382 from juananpe/dev
i18n: Add Basque (eu-ES, Euskara) translation
2024-11-26 08:59:30 -08:00
Timothy Jaeryang Baek 442f99e075 refac 2024-11-26 08:55:34 -08:00
Timothy Jaeryang Baek dc7221816f fix: models configure 2024-11-26 08:55:06 -08:00
Juanan Pereira bddc293d82 Add Basque (eu-ES, Euskara) translation 2024-11-26 14:18:59 +01:00
panda44312 3a2247b7a0 Update translation.json 更新翻译 2024-11-26 13:48:28 +01:00
Timothy Jaeryang Baek 4831c9e57e Merge pull request #7307 from open-webui/dev
0.4.5
2024-11-26 01:55:13 -08:00
Timothy Jaeryang Baek e3236622f3 chore: format 2024-11-26 01:53:06 -08:00
Timothy Jaeryang Baek 83f1fa7bb9 Merge pull request #7373 from open-webui/main
dev
2024-11-26 01:52:45 -08:00
Timothy Jaeryang Baek ba427bee3f Merge pull request #7369 from PieterBecking/fix/i18n-typo-groepen
fix(i18n): Correct capitalization typo in Dutch localization
2024-11-26 01:52:24 -08:00
Timothy Jaeryang Baek b9458817b1 doc: changelogs 2024-11-26 01:51:04 -08:00
Timothy Jaeryang Baek 38eb6abbfc chore: version bump 2024-11-26 01:43:28 -08:00
Timothy Jaeryang Baek b173f86690 fix: escape source id 2024-11-26 01:39:12 -08:00
Timothy Jaeryang Baek 9b8f9c689b refac 2024-11-26 01:22:09 -08:00
Timothy Jaeryang Baek e40212a662 refac 2024-11-26 01:14:32 -08:00
Timothy Jaeryang Baek a37cad2ecf chore: format 2024-11-26 01:05:50 -08:00
Timothy Jaeryang Baek 3b9e21ecf9 refac 2024-11-26 00:58:40 -08:00
Timothy Jaeryang Baek 5fac25a002 enh: reintroduce model order/default models 2024-11-26 00:55:58 -08:00
Pieter Becking 2b4e8f6cea fix(i18n): Correct capitalization typo in Dutch localization (GRoepen -> Groepen) 2024-11-26 09:42:13 +01:00
Timothy Jaeryang Baek 29fac5ecca refac: admin models settings 2024-11-25 22:57:54 -08:00
Timothy Jaeryang Baek f9e24968e3 fix: input issue 2024-11-25 22:43:34 -08:00
Timothy Jaeryang Baek c4f82309dc fix: min_p save issue 2024-11-25 16:11:49 -08:00
Timothy Jaeryang Baek da4676de2e Merge pull request #7326 from bnodnarb/fix/ollama-authentication
fix: Include Authorization header in /api/pull and /api/chat requests
2024-11-25 15:38:43 -08:00
Timothy Jaeryang Baek d870386d7d Update CODE_OF_CONDUCT.md 2024-11-24 23:48:02 -08:00
bnodnarb 8dc73e8744 Fix: Add authorization header with bearer token for remote Ollama server endpoints 2024-11-24 20:29:54 -10:00
Timothy Jaeryang Baek 840437e58f refac: o1 title generation issue 2024-11-24 19:07:51 -08:00
Timothy Jaeryang Baek bd28e1ed7d refac: rag prompt template 2024-11-24 18:49:56 -08:00
Timothy Jaeryang Baek 50c3be2136 refac 2024-11-24 18:11:48 -08:00
Timothy Jaeryang Baek 907cf61da7 refac: query generation 2024-11-24 18:03:58 -08:00
Timothy Jaeryang Baek a3a205abd1 fix: function list 2024-11-24 15:45:23 -08:00
Timothy Jaeryang Baek fac8c3259c refac: rich text input behaviour 2024-11-23 23:57:05 -08:00
Timothy Jaeryang Baek e67fdce727 enh: model editor reset image 2024-11-23 23:26:11 -08:00
Timothy Jaeryang Baek c465a37597 fix: textarea auto height issue 2024-11-23 23:10:12 -08:00
Timothy Jaeryang Baek f5bda4bc27 chore: pyproject bump 2024-11-23 23:00:04 -08:00
Timothy Jaeryang Baek 9ff580b8ca fix: textarea input new line issue 2024-11-23 22:58:09 -08:00
Timothy Jaeryang Baek d3acb5cbaa Merge pull request #7286 from houcheng/tts-aiofiles
Tts aiofiles
2024-11-23 22:37:05 -08:00
Timothy Jaeryang Baek 2a5506a9cd fix: textarea input height issue 2024-11-23 22:27:01 -08:00
Timothy Jaeryang Baek c567185cb1 refac: rich text input behaviour 2024-11-23 20:31:33 -08:00
Timothy Jaeryang Baek 5ed5e532a9 Merge pull request #7279 from Luceurre/fix/missing-tool-description
fix: add missing tool description
2024-11-23 10:39:47 -08:00
houcheng a83f89d430 fix: prevent TTS blocking using aiohttp and aiofiles 2024-11-24 00:28:14 +08:00
Pierre Glandon b2d3bfa3a8 feat: add description in Tool 2024-11-23 11:22:12 +01:00
Timothy Jaeryang Baek 8744a12abb Merge pull request #7274 from DmitriyAlergant-T1A/fix/logging_cleanup
Fix: logging cleanup
2024-11-22 20:16:47 -08:00
DmitriyAlergant-T1A 374d6cad18 Python Formatting (Failed CI - fixed) 2024-11-22 23:11:46 -05:00
DmitriyAlergant-T1A d24c21b40f Fix
Logging cleanup: removed some extraneous hard prints (including some that revealed message content!); improved debug logging a bit.

+ added chat_id to task metadata (helpful for logging/tracking in some pipe functions)
2024-11-22 23:05:45 -05:00
Timothy Jaeryang Baek db929b5d5e Merge pull request #7262 from open-webui/dev
0.4.4
2024-11-22 19:27:41 -08:00
Timothy Jaeryang Baek 47ae5221f7 doc: wording 2024-11-22 19:27:20 -08:00
Timothy Jaeryang Baek 79e988b281 doc: changelogs 2024-11-22 19:26:42 -08:00
Timothy Jaeryang Baek 9412f51c19 chore: version bump 2024-11-22 19:22:35 -08:00
Timothy Jaeryang Baek 320cf06333 Merge pull request #7266 from s1adem4n/update-german-translation
i18n: Add missing German translations and fix existing ones
2024-11-22 16:23:51 -08:00
Jonathan cede8a966f add missing translations and fix existing ones 2024-11-23 01:17:22 +01:00
Timothy Jaeryang Baek c561a4c42b Merge pull request #7263 from michaelpoluektov/fix/docstring-event-emitter
fix: docstring event emitter
2024-11-22 16:06:46 -08:00
Michael Poluektov b4e7957a00 docstring quickfix 2024-11-22 20:51:48 +00:00
Timothy Jaeryang Baek c4eacbfc0f refac: rm print statement 2024-11-22 12:39:08 -08:00
Timothy Jaeryang Baek 429fa2befa fix: query generation 2024-11-22 12:31:06 -08:00
Timothy Jaeryang Baek 3cfd4f8993 fix: controls not being shown in mobile 2024-11-22 11:21:53 -08:00
Timothy Jaeryang Baek 1c3bc99b86 refac: accept legacy 'citation' type 2024-11-22 10:49:30 -08:00
Timothy Jaeryang Baek 335337fc75 Merge pull request #7233 from iamcristi/patch-1
Fix: LDAP integration used None for image and that broke Authentication
2024-11-22 10:42:49 -08:00
Timothy Jaeryang Baek 1ea03cc156 Merge pull request #7228 from OriginalSimon/main
18n: Update Ukrainian translation
2024-11-22 10:42:37 -08:00
Timothy Jaeryang Baek 3923c4df65 Merge pull request #7225 from hilam/translate_pt_br
i18b: Translate missing pt-BR strings and enhancements
2024-11-22 10:42:21 -08:00
Timothy Jaeryang Baek 70ea85484c Merge pull request #7238 from aleixdorca/dev
i18n: Update Catalan translation.json
2024-11-22 10:41:46 -08:00
Timothy Jaeryang Baek 35d75e733d refac: file handling 2024-11-22 10:35:59 -08:00
Aleix Dorca 259881f0b6 Update Catalan translation.json 2024-11-22 17:41:35 +01:00
iamcristi b4cd685795 Update auths.py
changed Auths.insert_new_auth for LDAP accounts. Previously setting image to "None" was causing an error.
2024-11-22 17:23:49 +02:00
SimonOriginal 2d5e1a8c6f fix 2024-11-22 14:51:45 +01:00
SimonOriginal f07ba60f2a Update uk translation.json 2024-11-22 14:43:11 +01:00
Hildeberto e12cf77553 Update pt-BR translation.json 2024-11-22 09:35:25 -03:00
Timothy Jaeryang Baek c13bcfdfc9 Merge pull request #7202 from open-webui/dev
chore: version bump
2024-11-21 22:47:45 -08:00
Timothy Jaeryang Baek 9aac02824d chore: version bump 2024-11-21 22:47:29 -08:00
Timothy Jaeryang Baek edd224d542 Merge pull request #7180 from open-webui/dev
0.4.3
2024-11-21 22:46:34 -08:00
Timothy Jaeryang Baek 5c49740aa5 doc: changelog 2024-11-21 22:45:49 -08:00
Timothy Jaeryang Baek 20f31b5bc8 refac 2024-11-21 22:20:57 -08:00
Timothy Jaeryang Baek b23600b49d refac 2024-11-21 22:15:04 -08:00
Timothy Jaeryang Baek 7b9b0f23fe refac 2024-11-21 22:12:40 -08:00
Timothy Jaeryang Baek 2e7af7bdbf fix 2024-11-21 22:03:51 -08:00
Timothy Jaeryang Baek 12c8257c92 refac 2024-11-21 21:32:19 -08:00
Timothy Jaeryang Baek 9a2dd5b126 refac: optimisation 2024-11-21 21:04:35 -08:00
Timothy Jaeryang Baek 4b83a83576 fix: openai connection config 2024-11-21 20:49:40 -08:00
Timothy Jaeryang Baek e18a43aec8 fix: styling 2024-11-21 20:39:00 -08:00
Timothy Jaeryang Baek ea28747baa fix: knowledge base reset 2024-11-21 20:29:54 -08:00
Timothy Jaeryang Baek 3816e3c2ef refac: styling 2024-11-21 20:20:46 -08:00
Timothy Jaeryang Baek 9bc0de2c6a refac 2024-11-21 20:03:30 -08:00
Timothy Jaeryang Baek 81386e9b04 refac: citations -> sources 2024-11-21 19:46:09 -08:00
Timothy Jaeryang Baek 7062e637e8 refac: inline citations 2024-11-21 18:26:38 -08:00
Timothy Jaeryang Baek 714ed248fb refac: styling 2024-11-21 18:05:45 -08:00
Timothy Jaeryang Baek 7b675a1488 refac: styling 2024-11-21 18:02:33 -08:00
Timothy Jaeryang Baek 386c976e9a enh: inline citations 2024-11-21 17:58:29 -08:00
Timothy Jaeryang Baek 5be7cbfdf5 Merge pull request #7182 from michaelpoluektov/fix/tools-metadata
fix: Fix tools metadata
2024-11-21 16:32:08 -08:00
Timothy Jaeryang Baek 0217c044c9 Merge pull request #7195 from michaelpoluektov/remove-unused-router
refactor: Remove unused router
2024-11-21 16:30:22 -08:00
Michael Poluektov 26efc76d40 remove unused router 2024-11-22 00:15:11 +00:00
Michael Poluektov 8abf5d57c1 remove unused schemas file 2024-11-21 23:49:58 +00:00
Timothy Jaeryang Baek 948b65e43f refac 2024-11-21 12:16:50 -08:00
Michael Poluektov e1a85c99ab format 2024-11-21 17:52:19 +00:00
Michael Poluektov 6783c98539 Merge branch 'dev' of https://github.com/open-webui/open-webui into fix/tools-metadata 2024-11-21 17:46:44 +00:00
Michael Poluektov c03bfd141e fix optional args not present 2024-11-21 17:41:35 +00:00
Michael Poluektov 70838148e7 fix tools metadata 2024-11-21 17:19:56 +00:00
Timothy Jaeryang Baek d587206929 refac: add suffix field to ollama generate form 2024-11-21 08:12:19 -08:00
Timothy Jaeryang Baek 1c0327ed7f Merge pull request #7163 from igorbrai/main
feat: Add Mojeek as web search provider
2024-11-21 08:10:14 -08:00
Timothy Jaeryang Baek b0162dfee0 refac 2024-11-21 08:10:05 -08:00
Timothy Jaeryang Baek 1fcde2272b Merge pull request #7162 from morgan55555/dev
LDAP auth fixes: do not store LDAP password, use default user role on first auth, allow ldap only form.
2024-11-21 08:08:47 -08:00
Timothy Jaeryang Baek 48c03ef551 Merge pull request #7147 from aindriu80/main
i18n: Updated Irish translations
2024-11-21 08:06:33 -08:00
Igor Brai d16b09bee5 Merge branch 'open-webui:main' into main 2024-11-21 15:25:41 +02:00
alexey_rechkalov b9e637ee2b Now ENABLE_LOGIN_FORM=False disabling only email form.
LDAP form will be showed instead.
Also added "name" property to inputs for Chrome autocompletion.
2024-11-21 18:11:12 +05:00
alexey_rechkalov b1237cf389 Fixed security vulnerability: now LDAP password hashes are not stored, same as trusted header auth.
LDAP users role now getting DEFAULT_USER_ROLE, not "pending".
2024-11-21 18:09:29 +05:00
Michael Poluektov 3dfea834ca Merge branch 'dev' of https://github.com/open-webui/open-webui into dev 2024-11-21 13:02:39 +00:00
Aindriú Mac Giolla Eoin c03cae811d Updated Irish translations 2024-11-21 10:15:37 +00:00
Timothy Jaeryang Baek 6088acf36d refac: styling 2024-11-20 23:31:46 -08:00
Timothy Jaeryang Baek c5cd1e4403 refac: rich text input 2024-11-20 23:14:06 -08:00
Timothy Jaeryang Baek aca06f92e8 enh: rich text input 2024-11-20 22:56:26 -08:00
Timothy Jaeryang Baek e30c5e628c refac: rich text input 2024-11-20 22:46:51 -08:00
Timothy Jaeryang Baek 6f4bc9864c Merge pull request #7119 from open-webui/dev
doc
2024-11-20 12:24:35 -08:00
Timothy Jaeryang Baek 9b03b1a453 doc: readme 2024-11-20 12:24:07 -08:00
Timothy Jaeryang Baek 99c5cf1a00 doc: wording 2024-11-20 12:23:31 -08:00
Timothy Jaeryang Baek 639bea64d8 Merge pull request #7097 from open-webui/dev
0.4.2
2024-11-20 12:15:14 -08:00
Timothy Jaeryang Baek 02d02b0518 doc: changelog 2024-11-20 12:11:38 -08:00
Timothy Jaeryang Baek bc57acb3a6 Merge pull request #7113 from Xelaph/patch-1
**i18n** Update Dutch translation.json
2024-11-20 10:03:04 -08:00
Timothy Jaeryang Baek 2b890cf747 fix: knowledge files issue 2024-11-20 10:02:14 -08:00
Timothy Jaeryang Baek 4156b62811 refac 2024-11-20 10:02:07 -08:00
Timothy Jaeryang Baek c0055afdb3 refac: youtube loader 2024-11-20 10:01:58 -08:00
Alex 882a070cc9 Update translation.json
Added all new translations and changed a lot to better represent Dutch language rules
2024-11-20 18:47:01 +01:00
Timothy Jaeryang Baek d68aa5c708 chore: requirements 2024-11-20 06:53:11 -08:00
Timothy Jaeryang Baek 2450953080 refac 2024-11-20 06:34:52 -08:00
Timothy Jaeryang Baek db1a71c753 fix: usage capability in model editor 2024-11-20 06:24:09 -08:00
Timothy Jaeryang Baek 822a43c53d fix: arena model access control 2024-11-20 06:21:08 -08:00
Timothy Jaeryang Baek d5c65e36c9 fix: prefix issue 2024-11-20 06:12:20 -08:00
Timothy Jaeryang Baek bd1dae2c66 Merge branch 'dev' of https://github.com/open-webui/open-webui into dev 2024-11-20 06:09:53 -08:00
Timothy Jaeryang Baek 0dd1d6de2a fix: import 2024-11-20 06:09:48 -08:00
Timothy Jaeryang Baek 505bee066e Merge pull request #7069 from KarlLee830/translate
i18n: Update Chinese translation
2024-11-20 06:07:25 -08:00
Timothy Jaeryang Baek 54cabc9776 Merge pull request #7072 from antpyykk-kone/feature/fix-pipeline-middleware-http-exception-handling
fix: handle uncaught HTTP exceptions in PipelineMiddleware
2024-11-20 06:07:15 -08:00
Antti Pyykkönen 4f6ae8239d fix: handle http exceptions 2024-11-20 09:25:50 +02:00
KarlLee830 3531cf827a i18n: Update Chinese translation 2024-11-20 12:39:22 +08:00
Timothy Jaeryang Baek 02e94c8264 Merge pull request #7057 from open-webui/dev
0.4.1
2024-11-19 20:17:05 -08:00
Timothy Jaeryang Baek 31f00bbc69 doc: changelog 2024-11-19 20:16:34 -08:00
Timothy Jaeryang Baek cb19dbf72b chore: format 2024-11-19 20:15:37 -08:00
Timothy Jaeryang Baek e54879aeb1 enh: detailed 1-10 rating 2024-11-19 20:12:22 -08:00
Timothy Jaeryang Baek 7966367107 refac: models api 2024-11-19 18:45:26 -08:00
Timothy Jaeryang BaekandAlex Morgan 7c5b845d16 refac: add citations to outlet/actions form
Co-Authored-By: Alex Morgan <alxmrg55@gmail.com>
2024-11-19 18:26:01 -08:00
Timothy Jaeryang Baek cbcd14cdbf doc: changelog 2024-11-19 18:22:45 -08:00
Timothy Jaeryang Baek 0e0a9b4b27 Merge pull request #7061 from zhoubinxin/dev
ci: fix huggingface deploy errors and track .jpg file types
2024-11-19 18:22:35 -08:00
Timothy Jaeryang Baek 70c6d3bf9f fix 2024-11-19 18:21:55 -08:00
Timothy Jaeryang Baek 206a4877c7 fix: api key creation 2024-11-19 18:17:38 -08:00
bx 2c2c2ea9e1 fix(workflow): fix huggingface deploy errors and track .jpg file types 2024-11-20 10:15:40 +08:00
Timothy Jaeryang Baek 7f7b1b18cd doc: changelog 2024-11-19 17:27:34 -08:00
Timothy Jaeryang Baek 00e41790bf enh: show tool description on hover 2024-11-19 17:10:43 -08:00
Timothy Jaeryang Baek 9d9a60afec refac 2024-11-19 16:50:45 -08:00
Timothy Jaeryang Baek eefb2926d8 Merge pull request #7056 from open-webui/dev
fix: deleted user handling
2024-11-19 16:48:30 -08:00
Timothy Jaeryang Baek 0c43c1edf6 fix: deleted user handling 2024-11-19 16:47:35 -08:00
Igor Brai b7d730e244 add mojeek as websearch option 2024-10-29 16:45:38 +02:00
149 changed files with 9749 additions and 4268 deletions
@@ -28,6 +28,8 @@ jobs:
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v4 uses: actions/checkout@v4
with:
lfs: true
- name: Remove git history - name: Remove git history
run: rm -rf .git run: rm -rf .git
@@ -52,7 +54,9 @@ jobs:
- name: Set up Git and push to Space - name: Set up Git and push to Space
run: | run: |
git init --initial-branch=main git init --initial-branch=main
git lfs install
git lfs track "*.ttf" git lfs track "*.ttf"
git lfs track "*.jpg"
rm demo.gif rm demo.gif
git add . git add .
git commit -m "GitHub deploy: ${{ github.sha }}" git commit -m "GitHub deploy: ${{ github.sha }}"
+115
View File
@@ -5,6 +5,121 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [0.4.8] - 2024-12-07
### Added
- **🔓 Bypass Model Access Control**: Introduced the 'BYPASS_MODEL_ACCESS_CONTROL' environment variable. Easily bypass model access controls for user roles when access control isn't required, simplifying workflows for trusted environments.
- **📝 Markdown in Banners**: Now supports markdown for banners, enabling richer, more visually engaging announcements.
- **🌐 Internationalization Updates**: Enhanced translations across multiple languages, further improving accessibility and global user experience.
- **🎨 Styling Enhancements**: General UI style refinements for a cleaner and more polished interface.
- **📋 Rich Text Reliability**: Improved the reliability and stability of rich text input across chats for smoother interactions.
### Fixed
- **💡 Tailwind Build Issue**: Resolved a breaking bug caused by Tailwind, ensuring smoother builds and overall system reliability.
- **📚 Knowledge Collection Query Fix**: Addressed API endpoint issues with querying knowledge collections, ensuring accurate and reliable information retrieval.
## [0.4.7] - 2024-12-01
### Added
- **✨ Prompt Input Auto-Completion**: Type a prompt and let AI intelligently suggest and complete your inputs. Simply press 'Tab' or swipe right on mobile to confirm. Available only with Rich Text Input (default setting). Disable via Admin Settings for full control.
- **🌍 Improved Translations**: Enhanced localization for multiple languages, ensuring a more polished and accessible experience for international users.
### Fixed
- **🛠️ Tools Export Issue**: Resolved a critical issue where exporting tools wasn’t functioning, restoring seamless export capabilities.
- **🔗 Model ID Registration**: Fixed an issue where model IDs weren’t registering correctly in the model editor, ensuring reliable model setup and tracking.
- **🖋️ Textarea Auto-Expansion**: Corrected a bug where textareas didn’t expand automatically on certain browsers, improving usability for multi-line inputs.
- **🔧 Ollama Embed Endpoint**: Addressed the /ollama/embed endpoint malfunction, ensuring consistent performance and functionality.
### Changed
- **🎨 Knowledge Base Styling**: Refined knowledge base visuals for a cleaner, more modern look, laying the groundwork for further enhancements in upcoming releases.
## [0.4.6] - 2024-11-26
### Added
- **🌍 Enhanced Translations**: Various language translations improved to make the WebUI more accessible and user-friendly worldwide.
### Fixed
- **✏️ Textarea Shifting Bug**: Resolved the issue where the textarea shifted unexpectedly, ensuring a smoother typing experience.
- **⚙️ Model Configuration Modal**: Fixed the issue where the models configuration modal introduced in 0.4.5 wasn’t working for some users.
- **🔍 Legacy Query Support**: Restored functionality for custom query generation in RAG when using legacy prompts, ensuring both default and custom templates now work seamlessly.
- **⚡ Improved General Reliability**: Various minor fixes improve platform stability and ensure a smoother overall experience across workflows.
## [0.4.5] - 2024-11-26
### Added
- **🎨 Model Order/Defaults Reintroduced**: Brought back the ability to set model order and default models, now configurable via Admin Settings > Models > Configure (Gear Icon).
### Fixed
- **🔍 Query Generation Issue**: Resolved an error in web search query generation, enhancing search accuracy and ensuring smoother search workflows.
- **📏 Textarea Auto Height Bug**: Fixed a layout issue where textarea input height was shifting unpredictably, particularly when editing system prompts.
- **🔑 Ollama Authentication**: Corrected an issue with Ollama’s authorization headers, guaranteeing reliable authentication across all endpoints.
- **⚙️ Missing Min_P Save**: Resolved an issue where the 'min_p' parameter was not being saved in configurations.
- **🛠️ Tools Description**: Fixed a key issue that omitted tool descriptions in tools payload.
## [0.4.4] - 2024-11-22
### Added
- **🌐 Translation Updates**: Refreshed Catalan, Brazilian Portuguese, German, and Ukrainian translations, further enhancing the platform's accessibility and improving the experience for international users.
### Fixed
- **📱 Mobile Controls Visibility**: Resolved an issue where the controls button was not displaying on the new chats page for mobile users, ensuring smoother navigation and functionality on smaller screens.
- **📷 LDAP Profile Image Issue**: Fixed an LDAP integration bug related to profile images, ensuring seamless authentication and a reliable login experience for users.
- **⏳ RAG Query Generation Issue**: Addressed a significant problem where RAG query generation occurred unnecessarily without attached files, drastically improving speed and reducing delays during chat completions.
### Changed
- **⚙️ Legacy Event Emitter Support**: Reintroduced compatibility with legacy "citation" types for event emitters in tools and functions, providing smoother workflows and broader tool support for users.
## [0.4.3] - 2024-11-21
### Added
- **📚 Inline Citations for RAG Results**: Get seamless inline citations for Retrieval-Augmented Generation (RAG) responses using the default RAG prompt. Note: This feature only supports newly uploaded files, improving traceability and providing source clarity.
- **🎨 Better Rich Text Input Support**: Enjoy smoother and more reliable rich text formatting for chats, enhancing communication quality.
- **⚡ Faster Model Retrieval**: Implemented caching optimizations for faster model loading, providing a noticeable speed boost across workflows. Further improvements are on the way!
### Fixed
- **🔗 Pipelines Feature Restored**: Resolved a critical issue that previously prevented Pipelines from functioning, ensuring seamless workflows.
- **✏️ Missing Suffix Field in Ollama Form**: Added the missing "suffix" field to the Ollama generate form, enhancing customization options.
### Changed
- **🗂️ Renamed "Citations" to "Sources"**: Improved clarity and consistency by renaming the "citations" field to "sources" in messages.
## [0.4.2] - 2024-11-20
### Fixed
- **📁 Knowledge Files Visibility Issue**: Resolved the bug preventing individual files in knowledge collections from displaying when referenced with '#'.
- **🔗 OpenAI Endpoint Prefix**: Fixed the issue where certain OpenAI connections that deviate from the official API spec weren’t working correctly with prefixes.
- **⚔️ Arena Model Access Control**: Corrected an issue where arena model access control settings were not being saved.
- **🔧 Usage Capability Selector**: Fixed the broken usage capabilities selector in the model editor.
## [0.4.1] - 2024-11-19
### Added
- **📊 Enhanced Feedback System**: Introduced a detailed 1-10 rating scale for feedback alongside thumbs up/down, preparing for more precise model fine-tuning and improving feedback quality.
- **ℹ️ Tool Descriptions on Hover**: Easily access tool descriptions by hovering over the message input, providing a smoother workflow with more context when utilizing tools.
### Fixed
- **🗑️ Graceful Handling of Deleted Users**: Resolved an issue where deleted users caused workspace items (models, knowledge, prompts, tools) to fail, ensuring reliable workspace loading.
- **🔑 API Key Creation**: Fixed an issue preventing users from creating new API keys, restoring secure and seamless API management.
- **🔗 HTTPS Proxy Fix**: Corrected HTTPS proxy issues affecting the '/api/v1/models/' endpoint, ensuring smoother, uninterrupted model management.
## [0.4.0] - 2024-11-19 ## [0.4.0] - 2024-11-19
### Added ### Added
+60 -38
View File
@@ -2,76 +2,98 @@
## Our Pledge ## Our Pledge
We as members, contributors, and leaders pledge to make participation in our As members, contributors, and leaders of this community, we pledge to make participation in our open-source project a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation.
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
nationality, personal appearance, race, religion, or sexual identity
and orientation.
We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community. We are committed to creating and maintaining an open, respectful, and professional environment where positive contributions and meaningful discussions can flourish. By participating in this project, you agree to uphold these values and align your behavior to the standards outlined in this Code of Conduct.
## Why These Standards Are Important
Open-source projects rely on a community of volunteers dedicating their time, expertise, and effort toward a shared goal. These projects are inherently collaborative but also fragile, as the success of the project depends on the goodwill, energy, and productivity of those involved.
Maintaining a positive and respectful environment is essential to safeguarding the integrity of this project and protecting contributors' efforts. Behavior that disrupts this atmosphere—whether through hostility, entitlement, or unprofessional conduct—can severely harm the morale and productivity of the community. **Strict enforcement of these standards ensures a safe and supportive space for meaningful collaboration.**
This is a community where **respect and professionalism are mandatory.** Violations of these standards will result in **zero tolerance** and immediate enforcement to prevent disruption and ensure the well-being of all participants.
## Our Standards ## Our Standards
Examples of behavior that contribute to a positive environment for our community include: Examples of behavior that contribute to a positive and professional community include:
- Demonstrating empathy and kindness toward other people - **Respecting others.** Be considerate, listen actively, and engage with empathy toward others' viewpoints and experiences.
- Being respectful of differing opinions, viewpoints, and experiences - **Constructive feedback.** Provide actionable, thoughtful, and respectful feedback that helps improve the project and encourages collaboration. Avoid unproductive negativity or hypercriticism.
- Giving and gracefully accepting constructive feedback - **Recognizing volunteer contributions.** Appreciate that contributors dedicate their free time and resources selflessly. Approach them with gratitude and patience.
- Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience - **Focusing on shared goals.** Collaborate in ways that prioritize the health, success, and sustainability of the community over individual agendas.
- Focusing on what is best not just for us as individuals, but for the overall community
Examples of unacceptable behavior include: Examples of unacceptable behavior include:
- The use of sexualized language or imagery, and sexual attention or advances of any kind - The use of discriminatory, demeaning, or sexualized language or behavior.
- Trolling, insulting or derogatory comments, and personal or political attacks - Personal attacks, derogatory comments, trolling, or inflammatory political or ideological arguments.
- Public or private harassment - Harassment, intimidation, or any behavior intended to create a hostile, uncomfortable, or unsafe environment.
- Publishing others' private information, such as a physical or email address, without their explicit permission - Publishing others' private information (e.g., physical or email addresses) without explicit permission.
- **Spamming of any kind** - **Entitlement, demand, or aggression toward contributors.** Volunteers are under no obligation to provide immediate or personalized support. Rude or dismissive behavior will not be tolerated.
- Aggressive sales tactics targeting our community members are strictly prohibited. You can mention your product if it's relevant to the discussion, but under no circumstances should you push it forcefully - **Unproductive or destructive behavior.** This includes venting frustration as hostility ("tantrums"), hypercriticism, attention-seeking negativity, or anything that distracts from the project's goals.
- Other conduct which could reasonably be considered inappropriate in a professional setting - **Spamming and promotional exploitation.** Sharing irrelevant product promotions or self-promotion in the community is not allowed unless it directly contributes value to the discussion.
### Feedback and Community Engagement
- **Constructive feedback is encouraged, but hostile or entitled behavior will result in immediate action.** If you disagree with elements of the project, we encourage you to offer meaningful improvements or fork the project if necessary. Healthy discussions and technical disagreements are welcome only when handled with professionalism.
- **Respect contributors' time and efforts.** No one is entitled to personalized or on-demand assistance. This is a community built on collaboration and shared effort; demanding or demeaning behavior undermines that trust and will not be allowed.
### Zero Tolerance: No Warnings, Immediate Action
This community operates under a **zero-tolerance policy.** Any behavior deemed unacceptable under this Code of Conduct will result in **immediate enforcement, without prior warning.**
We employ this approach to ensure that unproductive or disruptive behavior does not escalate further or cause unnecessary harm to other contributors. The standards are clear, and violations of any kind—whether mild or severe—will be addressed decisively to protect the community.
## Enforcement Responsibilities ## Enforcement Responsibilities
Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful. Community leaders are responsible for upholding and enforcing these standards. They are empowered to take **immediate and appropriate action** to address any behaviors they deem unacceptable under this Code of Conduct. These actions are taken with the goal of protecting the community and preserving its safe, positive, and productive environment.
## Scope ## Scope
This Code of Conduct applies within all community spaces and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. This Code of Conduct applies to all community spaces, including forums, repositories, social media accounts, and in-person events. It also applies when an individual represents the community in public settings, such as conferences or official communications.
## Enforcement Additionally, any behavior outside of these defined spaces that negatively impacts the community or its members may fall within the scope of this Code of Conduct.
Instances of abusive, harassing, spamming, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at hello@openwebui.com. All complaints will be reviewed and investigated promptly and fairly. ## Reporting Violations
All community leaders are obligated to respect the privacy and security of the reporter of any incident. Instances of unacceptable behavior can be reported to the leadership team at **hello@openwebui.com**. Reports will be handled promptly, confidentially, and with consideration for the safety and well-being of the reporter.
All community leaders are required to uphold confidentiality and impartiality when addressing reports of violations.
## Enforcement Guidelines ## Enforcement Guidelines
Community leaders will follow these Community Impact Guidelines in determining the consequences for any action they deem in violation of this Code of Conduct: ### Ban
### 1. Temporary Ban **Community Impact**: Community leaders will issue a ban to any participant whose behavior is deemed unacceptable according to this Code of Conduct. Bans are enforced immediately and without prior notice.
**Community Impact**: Any violation of community standards, including but not limited to inappropriate language, unprofessional behavior, harassment, or spamming. A ban may be temporary or permanent, depending on the severity of the violation. This includes—but is not limited to—behavior such as:
**Consequence**: A temporary ban from any sort of interaction or public communication with the community for a specified period of time. No public or private interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this period. Violating these terms may lead to a permanent ban. - Harassment or abusive behavior toward contributors.
- Persistent negativity or hostility that disrupts the collaborative environment.
- Disrespectful, demanding, or aggressive interactions with others.
- Attempts to cause harm or sabotage the community.
### 2. Permanent Ban **Consequence**: A banned individual is immediately removed from access to all community spaces, communication channels, and events. Community leaders reserve the right to enforce either a time-limited suspension or a permanent ban based on the specific circumstances of the violation.
**Community Impact**: Repeated or severe violations of community standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals. This approach ensures that disruptive behaviors are addressed swiftly and decisively in order to maintain the integrity and productivity of the community.
**Consequence**: A permanent ban from any sort of public interaction within the community. ## Why Zero Tolerance Is Necessary
Open-source projects thrive on collaboration, goodwill, and mutual respect. Toxic behaviors—such as entitlement, hostility, or persistent negativity—threaten not just individual contributors but the health of the project as a whole. Allowing such behaviors to persist robs contributors of their time, energy, and enthusiasm for the work they do.
By enforcing a zero-tolerance policy, we ensure that the community remains a safe, welcoming space for all participants. These measures are not about harshness—they are about protecting contributors and fostering a productive environment where innovation can thrive.
Our expectations are clear, and our enforcement reflects our commitment to this project's long-term success.
## Attribution ## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 2.0, available at
version 2.0, available at
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
Community Impact Guidelines were inspired by [Mozilla's code of conduct Community Impact Guidelines were inspired by [Mozilla's code of conduct enforcement ladder](https://github.com/mozilla/diversity).
enforcement ladder](https://github.com/mozilla/diversity).
[homepage]: https://www.contributor-covenant.org [homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see the FAQ at For answers to common questions about this code of conduct, see the FAQ at
https://www.contributor-covenant.org/faq. Translations are available at https://www.contributor-covenant.org/faq. Translations are available at
https://www.contributor-covenant.org/translations. https://www.contributor-covenant.org/translations.
+3 -1
View File
@@ -21,7 +21,7 @@ Open WebUI is an [extensible](https://github.com/open-webui/pipelines), feature-
- 🤝 **Ollama/OpenAI API Integration**: Effortlessly integrate OpenAI-compatible APIs for versatile conversations alongside Ollama models. Customize the OpenAI API URL to link with **LMStudio, GroqCloud, Mistral, OpenRouter, and more**. - 🤝 **Ollama/OpenAI API Integration**: Effortlessly integrate OpenAI-compatible APIs for versatile conversations alongside Ollama models. Customize the OpenAI API URL to link with **LMStudio, GroqCloud, Mistral, OpenRouter, and more**.
- 🧩 **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. - 🛡️ **Granular Permissions and User Groups**: By allowing administrators to create detailed user roles and permissions, we ensure a secure user environment. This granularity not only enhances security but also allows for customized user experiences, fostering a sense of ownership and responsibility amongst users.
- 📱 **Responsive Design**: Enjoy a seamless experience across Desktop PC, Laptop, and Mobile devices. - 📱 **Responsive Design**: Enjoy a seamless experience across Desktop PC, Laptop, and Mobile devices.
@@ -49,6 +49,8 @@ Open WebUI is an [extensible](https://github.com/open-webui/pipelines), feature-
- 🌐🌍 **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! - 🌐🌍 **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.
- 🌟 **Continuous Updates**: We are committed to improving Open WebUI with regular updates, fixes, and new features. - 🌟 **Continuous Updates**: We are committed to improving Open WebUI with regular updates, fixes, and new features.
Want to learn more about Open WebUI's features? Check out our [Open WebUI documentation](https://docs.openwebui.com/features) for a comprehensive overview! Want to learn more about Open WebUI's features? Check out our [Open WebUI documentation](https://docs.openwebui.com/features) for a comprehensive overview!
+46 -56
View File
@@ -8,6 +8,8 @@ from pathlib import Path
from pydub import AudioSegment from pydub import AudioSegment
from pydub.silence import split_on_silence from pydub.silence import split_on_silence
import aiohttp
import aiofiles
import requests import requests
from open_webui.config import ( from open_webui.config import (
AUDIO_STT_ENGINE, AUDIO_STT_ENGINE,
@@ -292,46 +294,39 @@ async def speech(request: Request, user=Depends(get_verified_user)):
except Exception: except Exception:
pass pass
r = None
try: try:
r = requests.post( async with aiohttp.ClientSession() as session:
url=f"{app.state.config.TTS_OPENAI_API_BASE_URL}/audio/speech", async with session.post(
data=body, url=f"{app.state.config.TTS_OPENAI_API_BASE_URL}/audio/speech",
headers=headers, data=body,
stream=True, headers=headers,
) ) 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(json.loads(body.decode("utf-8"))))
# Save the streaming content to a file
with open(file_path, "wb") as f:
for chunk in r.iter_content(chunk_size=8192):
f.write(chunk)
with open(file_body_path, "w") as f:
json.dump(json.loads(body.decode("utf-8")), f)
# Return the saved file
return FileResponse(file_path) return FileResponse(file_path)
except Exception as e: except Exception as e:
log.exception(e) log.exception(e)
error_detail = "Open WebUI: Server Connection Error" error_detail = "Open WebUI: Server Connection Error"
if r is not None: try:
try: if r.status != 200:
res = r.json() res = await r.json()
if "error" in res: if "error" in res:
error_detail = f"External: {res['error']['message']}" error_detail = f"External: {res['error']['message']}"
except Exception: except Exception:
error_detail = f"External: {e}" error_detail = f"External: {e}"
raise HTTPException( raise HTTPException(
status_code=r.status_code if r != None else 500, status_code=getattr(r, "status", 500),
detail=error_detail, detail=error_detail,
) )
elif app.state.config.TTS_ENGINE == "elevenlabs": elif app.state.config.TTS_ENGINE == "elevenlabs":
payload = None
try: try:
payload = json.loads(body.decode("utf-8")) payload = json.loads(body.decode("utf-8"))
except Exception as e: except Exception as e:
@@ -339,7 +334,6 @@ async def speech(request: Request, user=Depends(get_verified_user)):
raise HTTPException(status_code=400, detail="Invalid JSON payload") raise HTTPException(status_code=400, detail="Invalid JSON payload")
voice_id = payload.get("voice", "") voice_id = payload.get("voice", "")
if voice_id not in get_available_voices(): if voice_id not in get_available_voices():
raise HTTPException( raise HTTPException(
status_code=400, status_code=400,
@@ -347,13 +341,11 @@ async def speech(request: Request, user=Depends(get_verified_user)):
) )
url = f"https://api.elevenlabs.io/v1/text-to-speech/{voice_id}" url = f"https://api.elevenlabs.io/v1/text-to-speech/{voice_id}"
headers = { headers = {
"Accept": "audio/mpeg", "Accept": "audio/mpeg",
"Content-Type": "application/json", "Content-Type": "application/json",
"xi-api-key": app.state.config.TTS_API_KEY, "xi-api-key": app.state.config.TTS_API_KEY,
} }
data = { data = {
"text": payload["input"], "text": payload["input"],
"model_id": app.state.config.TTS_MODEL, "model_id": app.state.config.TTS_MODEL,
@@ -361,39 +353,34 @@ async def speech(request: Request, user=Depends(get_verified_user)):
} }
try: try:
r = requests.post(url, json=data, headers=headers) async with aiohttp.ClientSession() as session:
async with session.post(url, json=data, headers=headers) 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(json.loads(body.decode("utf-8"))))
# Save the streaming content to a file
with open(file_path, "wb") as f:
for chunk in r.iter_content(chunk_size=8192):
f.write(chunk)
with open(file_body_path, "w") as f:
json.dump(json.loads(body.decode("utf-8")), f)
# Return the saved file
return FileResponse(file_path) return FileResponse(file_path)
except Exception as e: except Exception as e:
log.exception(e) log.exception(e)
error_detail = "Open WebUI: Server Connection Error" error_detail = "Open WebUI: Server Connection Error"
if r is not None: try:
try: if r.status != 200:
res = r.json() res = await r.json()
if "error" in res: if "error" in res:
error_detail = f"External: {res['error']['message']}" error_detail = f"External: {res['error']['message']}"
except Exception: except Exception:
error_detail = f"External: {e}" error_detail = f"External: {e}"
raise HTTPException( raise HTTPException(
status_code=r.status_code if r != None else 500, status_code=getattr(r, "status", 500),
detail=error_detail, detail=error_detail,
) )
elif app.state.config.TTS_ENGINE == "azure": elif app.state.config.TTS_ENGINE == "azure":
payload = None
try: try:
payload = json.loads(body.decode("utf-8")) payload = json.loads(body.decode("utf-8"))
except Exception as e: except Exception as e:
@@ -416,17 +403,20 @@ async def speech(request: Request, user=Depends(get_verified_user)):
<voice name="{language}">{payload["input"]}</voice> <voice name="{language}">{payload["input"]}</voice>
</speak>""" </speak>"""
response = requests.post(url, headers=headers, data=data) try:
async with aiohttp.ClientSession() as session:
if response.status_code == 200: async with session.post(url, headers=headers, data=data) as response:
with open(file_path, "wb") as f: if response.status == 200:
f.write(response.content) async with aiofiles.open(file_path, "wb") as f:
return FileResponse(file_path) await f.write(await response.read())
else: return FileResponse(file_path)
log.error(f"Error synthesizing speech - {response.reason}") else:
raise HTTPException( error_msg = f"Error synthesizing speech - {response.reason}"
status_code=500, detail=f"Error synthesizing speech - {response.reason}" log.error(error_msg)
) raise HTTPException(status_code=500, detail=error_msg)
except Exception as e:
log.exception(e)
raise HTTPException(status_code=500, detail=str(e))
elif app.state.config.TTS_ENGINE == "transformers": elif app.state.config.TTS_ENGINE == "transformers":
payload = None payload = None
try: try:
+44 -17
View File
@@ -9,6 +9,8 @@ from typing import Optional, Union
from urllib.parse import urlparse from urllib.parse import urlparse
import aiohttp import aiohttp
from aiocache import cached
import requests import requests
from open_webui.apps.webui.models.models import Models from open_webui.apps.webui.models.models import Models
from open_webui.config import ( from open_webui.config import (
@@ -22,6 +24,7 @@ from open_webui.config import (
from open_webui.env import ( from open_webui.env import (
AIOHTTP_CLIENT_TIMEOUT, AIOHTTP_CLIENT_TIMEOUT,
AIOHTTP_CLIENT_TIMEOUT_OPENAI_MODEL_LIST, AIOHTTP_CLIENT_TIMEOUT_OPENAI_MODEL_LIST,
BYPASS_MODEL_ACCESS_CONTROL,
) )
@@ -193,7 +196,10 @@ async def post_streaming_url(
trust_env=True, timeout=aiohttp.ClientTimeout(total=AIOHTTP_CLIENT_TIMEOUT) trust_env=True, timeout=aiohttp.ClientTimeout(total=AIOHTTP_CLIENT_TIMEOUT)
) )
api_config = app.state.config.OLLAMA_API_CONFIGS.get(url, {}) parsed_url = urlparse(url)
base_url = f"{parsed_url.scheme}://{parsed_url.netloc}"
api_config = app.state.config.OLLAMA_API_CONFIGS.get(base_url, {})
key = api_config.get("key", None) key = api_config.get("key", None)
headers = {"Content-Type": "application/json"} headers = {"Content-Type": "application/json"}
@@ -208,13 +214,13 @@ async def post_streaming_url(
r.raise_for_status() r.raise_for_status()
if stream: if stream:
headers = dict(r.headers) response_headers = dict(r.headers)
if content_type: if content_type:
headers["Content-Type"] = content_type response_headers["Content-Type"] = content_type
return StreamingResponse( return StreamingResponse(
r.content, r.content,
status_code=r.status, status_code=r.status,
headers=headers, headers=response_headers,
background=BackgroundTask( background=BackgroundTask(
cleanup_response, response=r, session=session cleanup_response, response=r, session=session
), ),
@@ -256,6 +262,7 @@ def merge_models_lists(model_lists):
return list(merged_models.values()) return list(merged_models.values())
@cached(ttl=3)
async def get_all_models(): async def get_all_models():
log.info("get_all_models()") log.info("get_all_models()")
if app.state.config.ENABLE_OLLAMA_API: if app.state.config.ENABLE_OLLAMA_API:
@@ -295,8 +302,6 @@ async def get_all_models():
for model in response.get("models", []): for model in response.get("models", []):
model["model"] = f"{prefix_id}.{model['model']}" model["model"] = f"{prefix_id}.{model['model']}"
print(responses)
models = { models = {
"models": merge_models_lists( "models": merge_models_lists(
map( map(
@@ -323,7 +328,10 @@ async def get_ollama_tags(
else: else:
url = app.state.config.OLLAMA_BASE_URLS[url_idx] url = app.state.config.OLLAMA_BASE_URLS[url_idx]
api_config = app.state.config.OLLAMA_API_CONFIGS.get(url, {}) parsed_url = urlparse(url)
base_url = f"{parsed_url.scheme}://{parsed_url.netloc}"
api_config = app.state.config.OLLAMA_API_CONFIGS.get(base_url, {})
key = api_config.get("key", None) key = api_config.get("key", None)
headers = {} headers = {}
@@ -352,7 +360,7 @@ async def get_ollama_tags(
detail=error_detail, detail=error_detail,
) )
if user.role == "user": if user.role == "user" and not BYPASS_MODEL_ACCESS_CONTROL:
# Filter models based on user access control # Filter models based on user access control
filtered_models = [] filtered_models = []
for model in models.get("models", []): for model in models.get("models", []):
@@ -524,7 +532,10 @@ async def copy_model(
url = app.state.config.OLLAMA_BASE_URLS[url_idx] url = app.state.config.OLLAMA_BASE_URLS[url_idx]
log.info(f"url: {url}") log.info(f"url: {url}")
api_config = app.state.config.OLLAMA_API_CONFIGS.get(url, {}) parsed_url = urlparse(url)
base_url = f"{parsed_url.scheme}://{parsed_url.netloc}"
api_config = app.state.config.OLLAMA_API_CONFIGS.get(base_url, {})
key = api_config.get("key", None) key = api_config.get("key", None)
headers = {"Content-Type": "application/json"} headers = {"Content-Type": "application/json"}
@@ -583,7 +594,10 @@ async def delete_model(
url = app.state.config.OLLAMA_BASE_URLS[url_idx] url = app.state.config.OLLAMA_BASE_URLS[url_idx]
log.info(f"url: {url}") log.info(f"url: {url}")
api_config = app.state.config.OLLAMA_API_CONFIGS.get(url, {}) parsed_url = urlparse(url)
base_url = f"{parsed_url.scheme}://{parsed_url.netloc}"
api_config = app.state.config.OLLAMA_API_CONFIGS.get(base_url, {})
key = api_config.get("key", None) key = api_config.get("key", None)
headers = {"Content-Type": "application/json"} headers = {"Content-Type": "application/json"}
@@ -634,7 +648,10 @@ async def show_model_info(form_data: ModelNameForm, user=Depends(get_verified_us
url = app.state.config.OLLAMA_BASE_URLS[url_idx] url = app.state.config.OLLAMA_BASE_URLS[url_idx]
log.info(f"url: {url}") log.info(f"url: {url}")
api_config = app.state.config.OLLAMA_API_CONFIGS.get(url, {}) parsed_url = urlparse(url)
base_url = f"{parsed_url.scheme}://{parsed_url.netloc}"
api_config = app.state.config.OLLAMA_API_CONFIGS.get(base_url, {})
key = api_config.get("key", None) key = api_config.get("key", None)
headers = {"Content-Type": "application/json"} headers = {"Content-Type": "application/json"}
@@ -690,7 +707,7 @@ async def generate_embeddings(
url_idx: Optional[int] = None, url_idx: Optional[int] = None,
user=Depends(get_verified_user), user=Depends(get_verified_user),
): ):
return generate_ollama_batch_embeddings(form_data, url_idx) return await generate_ollama_batch_embeddings(form_data, url_idx)
@app.post("/api/embeddings") @app.post("/api/embeddings")
@@ -729,7 +746,10 @@ async def generate_ollama_embeddings(
url = app.state.config.OLLAMA_BASE_URLS[url_idx] url = app.state.config.OLLAMA_BASE_URLS[url_idx]
log.info(f"url: {url}") log.info(f"url: {url}")
api_config = app.state.config.OLLAMA_API_CONFIGS.get(url, {}) parsed_url = urlparse(url)
base_url = f"{parsed_url.scheme}://{parsed_url.netloc}"
api_config = app.state.config.OLLAMA_API_CONFIGS.get(base_url, {})
key = api_config.get("key", None) key = api_config.get("key", None)
headers = {"Content-Type": "application/json"} headers = {"Content-Type": "application/json"}
@@ -796,7 +816,10 @@ async def generate_ollama_batch_embeddings(
url = app.state.config.OLLAMA_BASE_URLS[url_idx] url = app.state.config.OLLAMA_BASE_URLS[url_idx]
log.info(f"url: {url}") log.info(f"url: {url}")
api_config = app.state.config.OLLAMA_API_CONFIGS.get(url, {}) parsed_url = urlparse(url)
base_url = f"{parsed_url.scheme}://{parsed_url.netloc}"
api_config = app.state.config.OLLAMA_API_CONFIGS.get(base_url, {})
key = api_config.get("key", None) key = api_config.get("key", None)
headers = {"Content-Type": "application/json"} headers = {"Content-Type": "application/json"}
@@ -837,6 +860,7 @@ async def generate_ollama_batch_embeddings(
class GenerateCompletionForm(BaseModel): class GenerateCompletionForm(BaseModel):
model: str model: str
prompt: str prompt: str
suffix: Optional[str] = None
images: Optional[list[str]] = None images: Optional[list[str]] = None
format: Optional[str] = None format: Optional[str] = None
options: Optional[dict] = None options: Optional[dict] = None
@@ -972,7 +996,10 @@ async def generate_chat_completion(
log.info(f"url: {url}") log.info(f"url: {url}")
log.debug(f"generate_chat_completion() - 2.payload = {payload}") log.debug(f"generate_chat_completion() - 2.payload = {payload}")
api_config = app.state.config.OLLAMA_API_CONFIGS.get(url, {}) parsed_url = urlparse(url)
base_url = f"{parsed_url.scheme}://{parsed_url.netloc}"
api_config = app.state.config.OLLAMA_API_CONFIGS.get(base_url, {})
prefix_id = api_config.get("prefix_id", None) prefix_id = api_config.get("prefix_id", None)
if prefix_id: if prefix_id:
payload["model"] = payload["model"].replace(f"{prefix_id}.", "") payload["model"] = payload["model"].replace(f"{prefix_id}.", "")
@@ -1041,7 +1068,7 @@ async def generate_openai_chat_completion(
payload = apply_model_system_prompt_to_body(params, payload, user) payload = apply_model_system_prompt_to_body(params, payload, user)
# Check if user has access to the model # Check if user has access to the model
if user.role == "user": if user.role == "user" and not BYPASS_MODEL_ACCESS_CONTROL:
if not ( if not (
user.id == model_info.user_id user.id == model_info.user_id
or has_access( or has_access(
@@ -1130,7 +1157,7 @@ async def get_openai_models(
detail=error_detail, detail=error_detail,
) )
if user.role == "user": if user.role == "user" and not BYPASS_MODEL_ACCESS_CONTROL:
# Filter models based on user access control # Filter models based on user access control
filtered_models = [] filtered_models = []
for model in models: for model in models:
+11 -4
View File
@@ -6,7 +6,10 @@ from pathlib import Path
from typing import Literal, Optional, overload from typing import Literal, Optional, overload
import aiohttp import aiohttp
from aiocache import cached
import requests import requests
from open_webui.apps.webui.models.models import Models from open_webui.apps.webui.models.models import Models
from open_webui.config import ( from open_webui.config import (
CACHE_DIR, CACHE_DIR,
@@ -21,6 +24,7 @@ from open_webui.env import (
AIOHTTP_CLIENT_TIMEOUT, AIOHTTP_CLIENT_TIMEOUT,
AIOHTTP_CLIENT_TIMEOUT_OPENAI_MODEL_LIST, AIOHTTP_CLIENT_TIMEOUT_OPENAI_MODEL_LIST,
ENABLE_FORWARD_USER_INFO_HEADERS, ENABLE_FORWARD_USER_INFO_HEADERS,
BYPASS_MODEL_ACCESS_CONTROL,
) )
from open_webui.constants import ERROR_MESSAGES from open_webui.constants import ERROR_MESSAGES
@@ -302,6 +306,8 @@ async def get_all_models_responses() -> list:
} }
tasks.append(asyncio.ensure_future(asyncio.sleep(0, model_list))) tasks.append(asyncio.ensure_future(asyncio.sleep(0, model_list)))
else:
tasks.append(asyncio.ensure_future(asyncio.sleep(0, None)))
responses = await asyncio.gather(*tasks) responses = await asyncio.gather(*tasks)
@@ -313,7 +319,9 @@ async def get_all_models_responses() -> list:
prefix_id = api_config.get("prefix_id", None) prefix_id = api_config.get("prefix_id", None)
if prefix_id: if prefix_id:
for model in response["data"]: for model in (
response if isinstance(response, list) else response.get("data", [])
):
model["id"] = f"{prefix_id}.{model['id']}" model["id"] = f"{prefix_id}.{model['id']}"
log.debug(f"get_all_models:responses() {responses}") log.debug(f"get_all_models:responses() {responses}")
@@ -321,6 +329,7 @@ async def get_all_models_responses() -> list:
return responses return responses
@cached(ttl=3)
async def get_all_models() -> dict[str, list]: async def get_all_models() -> dict[str, list]:
log.info("get_all_models()") log.info("get_all_models()")
@@ -414,7 +423,7 @@ async def get_models(url_idx: Optional[int] = None, user=Depends(get_verified_us
error_detail = f"Unexpected error: {str(e)}" error_detail = f"Unexpected error: {str(e)}"
raise HTTPException(status_code=500, detail=error_detail) raise HTTPException(status_code=500, detail=error_detail)
if user.role == "user": if user.role == "user" and not BYPASS_MODEL_ACCESS_CONTROL:
# Filter models based on user access control # Filter models based on user access control
filtered_models = [] filtered_models = []
for model in models.get("data", []): for model in models.get("data", []):
@@ -577,8 +586,6 @@ async def generate_chat_completion(
# Convert the modified body back to JSON # Convert the modified body back to JSON
payload = json.dumps(payload) payload = json.dumps(payload)
log.debug(payload)
headers = {} headers = {}
headers["Authorization"] = f"Bearer {key}" headers["Authorization"] = f"Bearer {key}"
headers["Content-Type"] = "application/json" headers["Content-Type"] = "application/json"
@@ -0,0 +1,117 @@
import logging
from typing import Any, Dict, Generator, List, Optional, Sequence, Union
from urllib.parse import parse_qs, urlparse
from langchain_core.documents import Document
from open_webui.env import SRC_LOG_LEVELS
log = logging.getLogger(__name__)
log.setLevel(SRC_LOG_LEVELS["RAG"])
ALLOWED_SCHEMES = {"http", "https"}
ALLOWED_NETLOCS = {
"youtu.be",
"m.youtube.com",
"youtube.com",
"www.youtube.com",
"www.youtube-nocookie.com",
"vid.plus",
}
def _parse_video_id(url: str) -> Optional[str]:
"""Parse a YouTube URL and return the video ID if valid, otherwise None."""
parsed_url = urlparse(url)
if parsed_url.scheme not in ALLOWED_SCHEMES:
return None
if parsed_url.netloc not in ALLOWED_NETLOCS:
return None
path = parsed_url.path
if path.endswith("/watch"):
query = parsed_url.query
parsed_query = parse_qs(query)
if "v" in parsed_query:
ids = parsed_query["v"]
video_id = ids if isinstance(ids, str) else ids[0]
else:
return None
else:
path = parsed_url.path.lstrip("/")
video_id = path.split("/")[-1]
if len(video_id) != 11: # Video IDs are 11 characters long
return None
return video_id
class YoutubeLoader:
"""Load `YouTube` video transcripts."""
def __init__(
self,
video_id: str,
language: Union[str, Sequence[str]] = "en",
proxy_url: Optional[str] = None,
):
"""Initialize with YouTube video ID."""
_video_id = _parse_video_id(video_id)
self.video_id = _video_id if _video_id is not None else video_id
self._metadata = {"source": video_id}
self.language = language
self.proxy_url = proxy_url
if isinstance(language, str):
self.language = [language]
else:
self.language = language
def load(self) -> List[Document]:
"""Load YouTube transcripts into `Document` objects."""
try:
from youtube_transcript_api import (
NoTranscriptFound,
TranscriptsDisabled,
YouTubeTranscriptApi,
)
except ImportError:
raise ImportError(
'Could not import "youtube_transcript_api" Python package. '
"Please install it with `pip install youtube-transcript-api`."
)
if self.proxy_url:
youtube_proxies = {
"http": self.proxy_url,
"https": self.proxy_url,
}
# Don't log complete URL because it might contain secrets
log.debug(f"Using proxy URL: {self.proxy_url[:14]}...")
else:
youtube_proxies = None
try:
transcript_list = YouTubeTranscriptApi.list_transcripts(
self.video_id, proxies=youtube_proxies
)
except Exception as e:
log.exception("Loading YouTube transcript failed")
return []
try:
transcript = transcript_list.find_transcript(self.language)
except NoTranscriptFound:
transcript = transcript_list.find_transcript(["en"])
transcript_pieces: List[Dict[str, Any]] = transcript.fetch()
transcript = " ".join(
map(
lambda transcript_piece: transcript_piece["text"].strip(" "),
transcript_pieces,
)
)
return [Document(page_content=transcript, metadata=self._metadata)]
+59 -21
View File
@@ -23,11 +23,13 @@ from open_webui.apps.retrieval.vector.connector import VECTOR_DB_CLIENT
# Document loaders # Document loaders
from open_webui.apps.retrieval.loaders.main import Loader from open_webui.apps.retrieval.loaders.main import Loader
from open_webui.apps.retrieval.loaders.youtube import YoutubeLoader
# Web search engines # Web search engines
from open_webui.apps.retrieval.web.main import SearchResult from open_webui.apps.retrieval.web.main import SearchResult
from open_webui.apps.retrieval.web.utils import get_web_loader from open_webui.apps.retrieval.web.utils import get_web_loader
from open_webui.apps.retrieval.web.brave import search_brave from open_webui.apps.retrieval.web.brave import search_brave
from open_webui.apps.retrieval.web.mojeek import search_mojeek
from open_webui.apps.retrieval.web.duckduckgo import search_duckduckgo from open_webui.apps.retrieval.web.duckduckgo import search_duckduckgo
from open_webui.apps.retrieval.web.google_pse import search_google_pse from open_webui.apps.retrieval.web.google_pse import search_google_pse
from open_webui.apps.retrieval.web.jina_search import search_jina from open_webui.apps.retrieval.web.jina_search import search_jina
@@ -52,6 +54,7 @@ from open_webui.apps.retrieval.utils import (
from open_webui.apps.webui.models.files import Files from open_webui.apps.webui.models.files import Files
from open_webui.config import ( from open_webui.config import (
BRAVE_SEARCH_API_KEY, BRAVE_SEARCH_API_KEY,
MOJEEK_SEARCH_API_KEY,
TIKTOKEN_ENCODING_NAME, TIKTOKEN_ENCODING_NAME,
RAG_TEXT_SPLITTER, RAG_TEXT_SPLITTER,
CHUNK_OVERLAP, CHUNK_OVERLAP,
@@ -102,6 +105,7 @@ from open_webui.config import (
TIKA_SERVER_URL, TIKA_SERVER_URL,
UPLOAD_DIR, UPLOAD_DIR,
YOUTUBE_LOADER_LANGUAGE, YOUTUBE_LOADER_LANGUAGE,
YOUTUBE_LOADER_PROXY_URL,
DEFAULT_LOCALE, DEFAULT_LOCALE,
AppConfig, AppConfig,
) )
@@ -120,9 +124,6 @@ from open_webui.utils.misc import (
from open_webui.utils.utils import get_admin_user, get_verified_user from open_webui.utils.utils import get_admin_user, get_verified_user
from langchain.text_splitter import RecursiveCharacterTextSplitter, TokenTextSplitter from langchain.text_splitter import RecursiveCharacterTextSplitter, TokenTextSplitter
from langchain_community.document_loaders import (
YoutubeLoader,
)
from langchain_core.documents import Document from langchain_core.documents import Document
@@ -171,6 +172,7 @@ app.state.config.OLLAMA_API_KEY = RAG_OLLAMA_API_KEY
app.state.config.PDF_EXTRACT_IMAGES = PDF_EXTRACT_IMAGES app.state.config.PDF_EXTRACT_IMAGES = PDF_EXTRACT_IMAGES
app.state.config.YOUTUBE_LOADER_LANGUAGE = YOUTUBE_LOADER_LANGUAGE app.state.config.YOUTUBE_LOADER_LANGUAGE = YOUTUBE_LOADER_LANGUAGE
app.state.config.YOUTUBE_LOADER_PROXY_URL = YOUTUBE_LOADER_PROXY_URL
app.state.YOUTUBE_LOADER_TRANSLATION = None app.state.YOUTUBE_LOADER_TRANSLATION = None
@@ -182,6 +184,7 @@ app.state.config.SEARXNG_QUERY_URL = SEARXNG_QUERY_URL
app.state.config.GOOGLE_PSE_API_KEY = GOOGLE_PSE_API_KEY app.state.config.GOOGLE_PSE_API_KEY = GOOGLE_PSE_API_KEY
app.state.config.GOOGLE_PSE_ENGINE_ID = GOOGLE_PSE_ENGINE_ID app.state.config.GOOGLE_PSE_ENGINE_ID = GOOGLE_PSE_ENGINE_ID
app.state.config.BRAVE_SEARCH_API_KEY = BRAVE_SEARCH_API_KEY app.state.config.BRAVE_SEARCH_API_KEY = BRAVE_SEARCH_API_KEY
app.state.config.MOJEEK_SEARCH_API_KEY = MOJEEK_SEARCH_API_KEY
app.state.config.SERPSTACK_API_KEY = SERPSTACK_API_KEY app.state.config.SERPSTACK_API_KEY = SERPSTACK_API_KEY
app.state.config.SERPSTACK_HTTPS = SERPSTACK_HTTPS app.state.config.SERPSTACK_HTTPS = SERPSTACK_HTTPS
app.state.config.SERPER_API_KEY = SERPER_API_KEY app.state.config.SERPER_API_KEY = SERPER_API_KEY
@@ -470,6 +473,7 @@ async def get_rag_config(user=Depends(get_admin_user)):
"youtube": { "youtube": {
"language": app.state.config.YOUTUBE_LOADER_LANGUAGE, "language": app.state.config.YOUTUBE_LOADER_LANGUAGE,
"translation": app.state.YOUTUBE_LOADER_TRANSLATION, "translation": app.state.YOUTUBE_LOADER_TRANSLATION,
"proxy_url": app.state.config.YOUTUBE_LOADER_PROXY_URL,
}, },
"web": { "web": {
"web_loader_ssl_verification": app.state.config.ENABLE_RAG_WEB_LOADER_SSL_VERIFICATION, "web_loader_ssl_verification": app.state.config.ENABLE_RAG_WEB_LOADER_SSL_VERIFICATION,
@@ -480,6 +484,7 @@ async def get_rag_config(user=Depends(get_admin_user)):
"google_pse_api_key": app.state.config.GOOGLE_PSE_API_KEY, "google_pse_api_key": app.state.config.GOOGLE_PSE_API_KEY,
"google_pse_engine_id": app.state.config.GOOGLE_PSE_ENGINE_ID, "google_pse_engine_id": app.state.config.GOOGLE_PSE_ENGINE_ID,
"brave_search_api_key": app.state.config.BRAVE_SEARCH_API_KEY, "brave_search_api_key": app.state.config.BRAVE_SEARCH_API_KEY,
"mojeek_search_api_key": app.state.config.MOJEEK_SEARCH_API_KEY,
"serpstack_api_key": app.state.config.SERPSTACK_API_KEY, "serpstack_api_key": app.state.config.SERPSTACK_API_KEY,
"serpstack_https": app.state.config.SERPSTACK_HTTPS, "serpstack_https": app.state.config.SERPSTACK_HTTPS,
"serper_api_key": app.state.config.SERPER_API_KEY, "serper_api_key": app.state.config.SERPER_API_KEY,
@@ -516,6 +521,7 @@ class ChunkParamUpdateForm(BaseModel):
class YoutubeLoaderConfig(BaseModel): class YoutubeLoaderConfig(BaseModel):
language: list[str] language: list[str]
translation: Optional[str] = None translation: Optional[str] = None
proxy_url: str = ""
class WebSearchConfig(BaseModel): class WebSearchConfig(BaseModel):
@@ -525,6 +531,7 @@ class WebSearchConfig(BaseModel):
google_pse_api_key: Optional[str] = None google_pse_api_key: Optional[str] = None
google_pse_engine_id: Optional[str] = None google_pse_engine_id: Optional[str] = None
brave_search_api_key: Optional[str] = None brave_search_api_key: Optional[str] = None
mojeek_search_api_key: Optional[str] = None
serpstack_api_key: Optional[str] = None serpstack_api_key: Optional[str] = None
serpstack_https: Optional[bool] = None serpstack_https: Optional[bool] = None
serper_api_key: Optional[str] = None serper_api_key: Optional[str] = None
@@ -577,6 +584,7 @@ async def update_rag_config(form_data: ConfigUpdateForm, user=Depends(get_admin_
if form_data.youtube is not None: if form_data.youtube is not None:
app.state.config.YOUTUBE_LOADER_LANGUAGE = form_data.youtube.language app.state.config.YOUTUBE_LOADER_LANGUAGE = form_data.youtube.language
app.state.config.YOUTUBE_LOADER_PROXY_URL = form_data.youtube.proxy_url
app.state.YOUTUBE_LOADER_TRANSLATION = form_data.youtube.translation app.state.YOUTUBE_LOADER_TRANSLATION = form_data.youtube.translation
if form_data.web is not None: if form_data.web is not None:
@@ -595,6 +603,9 @@ async def update_rag_config(form_data: ConfigUpdateForm, user=Depends(get_admin_
app.state.config.BRAVE_SEARCH_API_KEY = ( app.state.config.BRAVE_SEARCH_API_KEY = (
form_data.web.search.brave_search_api_key form_data.web.search.brave_search_api_key
) )
app.state.config.MOJEEK_SEARCH_API_KEY = (
form_data.web.search.mojeek_search_api_key
)
app.state.config.SERPSTACK_API_KEY = form_data.web.search.serpstack_api_key app.state.config.SERPSTACK_API_KEY = form_data.web.search.serpstack_api_key
app.state.config.SERPSTACK_HTTPS = form_data.web.search.serpstack_https app.state.config.SERPSTACK_HTTPS = form_data.web.search.serpstack_https
app.state.config.SERPER_API_KEY = form_data.web.search.serper_api_key app.state.config.SERPER_API_KEY = form_data.web.search.serper_api_key
@@ -634,6 +645,7 @@ async def update_rag_config(form_data: ConfigUpdateForm, user=Depends(get_admin_
}, },
"youtube": { "youtube": {
"language": app.state.config.YOUTUBE_LOADER_LANGUAGE, "language": app.state.config.YOUTUBE_LOADER_LANGUAGE,
"proxy_url": app.state.config.YOUTUBE_LOADER_PROXY_URL,
"translation": app.state.YOUTUBE_LOADER_TRANSLATION, "translation": app.state.YOUTUBE_LOADER_TRANSLATION,
}, },
"web": { "web": {
@@ -645,6 +657,7 @@ async def update_rag_config(form_data: ConfigUpdateForm, user=Depends(get_admin_
"google_pse_api_key": app.state.config.GOOGLE_PSE_API_KEY, "google_pse_api_key": app.state.config.GOOGLE_PSE_API_KEY,
"google_pse_engine_id": app.state.config.GOOGLE_PSE_ENGINE_ID, "google_pse_engine_id": app.state.config.GOOGLE_PSE_ENGINE_ID,
"brave_search_api_key": app.state.config.BRAVE_SEARCH_API_KEY, "brave_search_api_key": app.state.config.BRAVE_SEARCH_API_KEY,
"mojeek_search_api_key": app.state.config.MOJEEK_SEARCH_API_KEY,
"serpstack_api_key": app.state.config.SERPSTACK_API_KEY, "serpstack_api_key": app.state.config.SERPSTACK_API_KEY,
"serpstack_https": app.state.config.SERPSTACK_HTTPS, "serpstack_https": app.state.config.SERPSTACK_HTTPS,
"serper_api_key": app.state.config.SERPER_API_KEY, "serper_api_key": app.state.config.SERPER_API_KEY,
@@ -860,7 +873,7 @@ def save_docs_to_vector_db(
return True return True
except Exception as e: except Exception as e:
log.exception(e) log.exception(e)
return False raise e
class ProcessFileForm(BaseModel): class ProcessFileForm(BaseModel):
@@ -886,19 +899,17 @@ def process_file(
# Update the content in the file # Update the content in the file
# Usage: /files/{file_id}/data/content/update # Usage: /files/{file_id}/data/content/update
VECTOR_DB_CLIENT.delete( VECTOR_DB_CLIENT.delete_collection(collection_name=f"file-{file.id}")
collection_name=f"file-{file.id}",
filter={"file_id": file.id},
)
docs = [ docs = [
Document( Document(
page_content=form_data.content, page_content=form_data.content.replace("<br/>", "\n"),
metadata={ metadata={
"name": file.meta.get("name", file.filename), **file.meta,
"name": file.filename,
"created_by": file.user_id, "created_by": file.user_id,
"file_id": file.id, "file_id": file.id,
**file.meta, "source": file.filename,
}, },
) )
] ]
@@ -925,10 +936,11 @@ def process_file(
Document( Document(
page_content=file.data.get("content", ""), page_content=file.data.get("content", ""),
metadata={ metadata={
"name": file.meta.get("name", file.filename), **file.meta,
"name": file.filename,
"created_by": file.user_id, "created_by": file.user_id,
"file_id": file.id, "file_id": file.id,
**file.meta, "source": file.filename,
}, },
) )
] ]
@@ -948,15 +960,30 @@ def process_file(
docs = loader.load( docs = loader.load(
file.filename, file.meta.get("content_type"), file_path file.filename, file.meta.get("content_type"), file_path
) )
docs = [
Document(
page_content=doc.page_content,
metadata={
**doc.metadata,
"name": file.filename,
"created_by": file.user_id,
"file_id": file.id,
"source": file.filename,
},
)
for doc in docs
]
else: else:
docs = [ docs = [
Document( Document(
page_content=file.data.get("content", ""), page_content=file.data.get("content", ""),
metadata={ metadata={
**file.meta,
"name": file.filename, "name": file.filename,
"created_by": file.user_id, "created_by": file.user_id,
"file_id": file.id, "file_id": file.id,
**file.meta, "source": file.filename,
}, },
) )
] ]
@@ -977,7 +1004,7 @@ def process_file(
collection_name=collection_name, collection_name=collection_name,
metadata={ metadata={
"file_id": file.id, "file_id": file.id,
"name": file.meta.get("name", file.filename), "name": file.filename,
"hash": hash, "hash": hash,
}, },
add=(True if form_data.collection_name else False), add=(True if form_data.collection_name else False),
@@ -994,7 +1021,7 @@ def process_file(
return { return {
"status": True, "status": True,
"collection_name": collection_name, "collection_name": collection_name,
"filename": file.meta.get("name", file.filename), "filename": file.filename,
"content": text_content, "content": text_content,
} }
except Exception as e: except Exception as e:
@@ -1059,12 +1086,12 @@ def process_youtube_video(form_data: ProcessUrlForm, user=Depends(get_verified_u
if not collection_name: if not collection_name:
collection_name = calculate_sha256_string(form_data.url)[:63] collection_name = calculate_sha256_string(form_data.url)[:63]
loader = YoutubeLoader.from_youtube_url( loader = YoutubeLoader(
form_data.url, form_data.url,
add_video_info=False,
language=app.state.config.YOUTUBE_LOADER_LANGUAGE, language=app.state.config.YOUTUBE_LOADER_LANGUAGE,
translation=app.state.YOUTUBE_LOADER_TRANSLATION, proxy_url=app.state.config.YOUTUBE_LOADER_PROXY_URL,
) )
docs = loader.load() docs = loader.load()
content = " ".join([doc.page_content for doc in docs]) content = " ".join([doc.page_content for doc in docs])
log.debug(f"text_content: {content}") log.debug(f"text_content: {content}")
@@ -1135,6 +1162,7 @@ def search_web(engine: str, query: str) -> list[SearchResult]:
- SEARXNG_QUERY_URL - SEARXNG_QUERY_URL
- GOOGLE_PSE_API_KEY + GOOGLE_PSE_ENGINE_ID - GOOGLE_PSE_API_KEY + GOOGLE_PSE_ENGINE_ID
- BRAVE_SEARCH_API_KEY - BRAVE_SEARCH_API_KEY
- MOJEEK_SEARCH_API_KEY
- SERPSTACK_API_KEY - SERPSTACK_API_KEY
- SERPER_API_KEY - SERPER_API_KEY
- SERPLY_API_KEY - SERPLY_API_KEY
@@ -1181,6 +1209,16 @@ def search_web(engine: str, query: str) -> list[SearchResult]:
) )
else: else:
raise Exception("No BRAVE_SEARCH_API_KEY found in environment variables") raise Exception("No BRAVE_SEARCH_API_KEY found in environment variables")
elif engine == "mojeek":
if app.state.config.MOJEEK_SEARCH_API_KEY:
return search_mojeek(
app.state.config.MOJEEK_SEARCH_API_KEY,
query,
app.state.config.RAG_WEB_SEARCH_RESULT_COUNT,
app.state.config.RAG_WEB_SEARCH_DOMAIN_FILTER_LIST,
)
else:
raise Exception("No MOJEEK_SEARCH_API_KEY found in environment variables")
elif engine == "serpstack": elif engine == "serpstack":
if app.state.config.SERPSTACK_API_KEY: if app.state.config.SERPSTACK_API_KEY:
return search_serpstack( return search_serpstack(
@@ -1361,7 +1399,7 @@ def query_collection_handler(
if app.state.config.ENABLE_RAG_HYBRID_SEARCH: if app.state.config.ENABLE_RAG_HYBRID_SEARCH:
return query_collection_with_hybrid_search( return query_collection_with_hybrid_search(
collection_names=form_data.collection_names, collection_names=form_data.collection_names,
query=form_data.query, queries=[form_data.query],
embedding_function=app.state.EMBEDDING_FUNCTION, embedding_function=app.state.EMBEDDING_FUNCTION,
k=form_data.k if form_data.k else app.state.config.TOP_K, k=form_data.k if form_data.k else app.state.config.TOP_K,
reranking_function=app.state.sentence_transformer_rf, reranking_function=app.state.sentence_transformer_rf,
@@ -1372,7 +1410,7 @@ def query_collection_handler(
else: else:
return query_collection( return query_collection(
collection_names=form_data.collection_names, collection_names=form_data.collection_names,
query=form_data.query, queries=[form_data.query],
embedding_function=app.state.EMBEDDING_FUNCTION, embedding_function=app.state.EMBEDDING_FUNCTION,
k=form_data.k if form_data.k else app.state.config.TOP_K, k=form_data.k if form_data.k else app.state.config.TOP_K,
) )
+8 -68
View File
@@ -15,8 +15,6 @@ from open_webui.apps.retrieval.vector.connector import VECTOR_DB_CLIENT
from open_webui.utils.misc import get_last_user_message from open_webui.utils.misc import get_last_user_message
from open_webui.env import SRC_LOG_LEVELS from open_webui.env import SRC_LOG_LEVELS
from open_webui.config import DEFAULT_RAG_TEMPLATE
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
log.setLevel(SRC_LOG_LEVELS["RAG"]) log.setLevel(SRC_LOG_LEVELS["RAG"])
@@ -238,44 +236,6 @@ def query_collection_with_hybrid_search(
return merge_and_sort_query_results(results, k=k, reverse=True) return merge_and_sort_query_results(results, k=k, reverse=True)
def rag_template(template: str, context: str, query: str):
if template == "":
template = DEFAULT_RAG_TEMPLATE
if "[context]" not in template and "{{CONTEXT}}" not in template:
log.debug(
"WARNING: The RAG template does not contain the '[context]' or '{{CONTEXT}}' placeholder."
)
if "<context>" in context and "</context>" in context:
log.debug(
"WARNING: Potential prompt injection attack: the RAG "
"context contains '<context>' and '</context>'. This might be "
"nothing, or the user might be trying to hack something."
)
query_placeholders = []
if "[query]" in context:
query_placeholder = "{{QUERY" + str(uuid.uuid4()) + "}}"
template = template.replace("[query]", query_placeholder)
query_placeholders.append(query_placeholder)
if "{{QUERY}}" in context:
query_placeholder = "{{QUERY" + str(uuid.uuid4()) + "}}"
template = template.replace("{{QUERY}}", query_placeholder)
query_placeholders.append(query_placeholder)
template = template.replace("[context]", context)
template = template.replace("{{CONTEXT}}", context)
template = template.replace("[query]", query)
template = template.replace("{{QUERY}}", query)
for query_placeholder in query_placeholders:
template = template.replace(query_placeholder, query)
return template
def get_embedding_function( def get_embedding_function(
embedding_engine, embedding_engine,
embedding_model, embedding_model,
@@ -307,7 +267,7 @@ def get_embedding_function(
return lambda query: generate_multiple(query, func) return lambda query: generate_multiple(query, func)
def get_rag_context( def get_sources_from_files(
files, files,
queries, queries,
embedding_function, embedding_function,
@@ -387,43 +347,24 @@ def get_rag_context(
del file["data"] del file["data"]
relevant_contexts.append({**context, "file": file}) relevant_contexts.append({**context, "file": file})
contexts = [] sources = []
citations = []
for context in relevant_contexts: for context in relevant_contexts:
try: try:
if "documents" in context: if "documents" in context:
file_names = list(
set(
[
metadata["name"]
for metadata in context["metadatas"][0]
if metadata is not None and "name" in metadata
]
)
)
contexts.append(
((", ".join(file_names) + ":\n\n") if file_names else "")
+ "\n\n".join(
[text for text in context["documents"][0] if text is not None]
)
)
if "metadatas" in context: if "metadatas" in context:
citation = { source = {
"source": context["file"], "source": context["file"],
"document": context["documents"][0], "document": context["documents"][0],
"metadata": context["metadatas"][0], "metadata": context["metadatas"][0],
} }
if "distances" in context and context["distances"]: if "distances" in context and context["distances"]:
citation["distances"] = context["distances"][0] source["distances"] = context["distances"][0]
citations.append(citation)
sources.append(source)
except Exception as e: except Exception as e:
log.exception(e) log.exception(e)
print("contexts", contexts) return sources
print("citations", citations)
return contexts, citations
def get_model_path(model: str, update_model: bool = False): def get_model_path(model: str, update_model: bool = False):
@@ -488,7 +429,7 @@ def generate_openai_batch_embeddings(
def generate_ollama_batch_embeddings( def generate_ollama_batch_embeddings(
model: str, texts: list[str], url: str, key: str model: str, texts: list[str], url: str, key: str = ""
) -> Optional[list[list[float]]]: ) -> Optional[list[list[float]]]:
try: try:
r = requests.post( r = requests.post(
@@ -502,7 +443,6 @@ def generate_ollama_batch_embeddings(
r.raise_for_status() r.raise_for_status()
data = r.json() data = r.json()
print(data)
if "embeddings" in data: if "embeddings" in data:
return data["embeddings"] return data["embeddings"]
else: else:
@@ -1,7 +1,7 @@
from opensearchpy import OpenSearch from opensearchpy import OpenSearch
from typing import Optional from typing import Optional
from open_webui.apps.rag.vector.main import VectorItem, SearchResult, GetResult from open_webui.apps.retrieval.vector.main import VectorItem, SearchResult, GetResult
from open_webui.config import ( from open_webui.config import (
OPENSEARCH_URI, OPENSEARCH_URI,
OPENSEARCH_SSL, OPENSEARCH_SSL,
@@ -0,0 +1,40 @@
import logging
from typing import Optional
import requests
from open_webui.apps.retrieval.web.main import SearchResult, get_filtered_results
from open_webui.env import SRC_LOG_LEVELS
log = logging.getLogger(__name__)
log.setLevel(SRC_LOG_LEVELS["RAG"])
def search_mojeek(
api_key: str, query: str, count: int, filter_list: Optional[list[str]] = None
) -> list[SearchResult]:
"""Search using Mojeek's Search API and return the results as a list of SearchResult objects.
Args:
api_key (str): A Mojeek Search API key
query (str): The query to search for
"""
url = "https://api.mojeek.com/search"
headers = {
"Accept": "application/json",
}
params = {"q": query, "api_key": api_key, "fmt": "json", "t": count}
response = requests.get(url, headers=headers, params=params)
response.raise_for_status()
json_response = response.json()
results = json_response.get("response", {}).get("results", [])
print(results)
if filter_list:
results = get_filtered_results(results, filter_list)
return [
SearchResult(
link=result["url"], title=result.get("title"), snippet=result.get("desc")
)
for result in results
]
+15 -4
View File
@@ -31,6 +31,7 @@ from open_webui.config import (
DEFAULT_MODELS, DEFAULT_MODELS,
DEFAULT_PROMPT_SUGGESTIONS, DEFAULT_PROMPT_SUGGESTIONS,
DEFAULT_USER_ROLE, DEFAULT_USER_ROLE,
MODEL_ORDER_LIST,
ENABLE_COMMUNITY_SHARING, ENABLE_COMMUNITY_SHARING,
ENABLE_LOGIN_FORM, ENABLE_LOGIN_FORM,
ENABLE_MESSAGE_RATING, ENABLE_MESSAGE_RATING,
@@ -68,6 +69,7 @@ from open_webui.config import (
) )
from open_webui.env import ( from open_webui.env import (
ENV, ENV,
SRC_LOG_LEVELS,
WEBUI_AUTH_TRUSTED_EMAIL_HEADER, WEBUI_AUTH_TRUSTED_EMAIL_HEADER,
WEBUI_AUTH_TRUSTED_NAME_HEADER, WEBUI_AUTH_TRUSTED_NAME_HEADER,
) )
@@ -94,6 +96,7 @@ app = FastAPI(
) )
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
log.setLevel(SRC_LOG_LEVELS["MAIN"])
app.state.config = AppConfig() app.state.config = AppConfig()
@@ -118,6 +121,7 @@ app.state.config.DEFAULT_USER_ROLE = DEFAULT_USER_ROLE
app.state.config.USER_PERMISSIONS = USER_PERMISSIONS app.state.config.USER_PERMISSIONS = USER_PERMISSIONS
app.state.config.WEBHOOK_URL = WEBHOOK_URL app.state.config.WEBHOOK_URL = WEBHOOK_URL
app.state.config.BANNERS = WEBUI_BANNERS app.state.config.BANNERS = WEBUI_BANNERS
app.state.config.MODEL_ORDER_LIST = MODEL_ORDER_LIST
app.state.config.ENABLE_COMMUNITY_SHARING = ENABLE_COMMUNITY_SHARING app.state.config.ENABLE_COMMUNITY_SHARING = ENABLE_COMMUNITY_SHARING
app.state.config.ENABLE_MESSAGE_RATING = ENABLE_MESSAGE_RATING app.state.config.ENABLE_MESSAGE_RATING = ENABLE_MESSAGE_RATING
@@ -270,7 +274,9 @@ async def get_pipe_models():
log.exception(e) log.exception(e)
sub_pipes = [] sub_pipes = []
print(sub_pipes) log.debug(
f"get_pipe_models: function '{pipe.id}' is a manifold of {sub_pipes}"
)
for p in sub_pipes: for p in sub_pipes:
sub_pipe_id = f'{pipe.id}.{p["id"]}' sub_pipe_id = f'{pipe.id}.{p["id"]}'
@@ -280,6 +286,7 @@ async def get_pipe_models():
sub_pipe_name = f"{function_module.name}{sub_pipe_name}" sub_pipe_name = f"{function_module.name}{sub_pipe_name}"
pipe_flag = {"type": pipe.type} pipe_flag = {"type": pipe.type}
pipe_models.append( pipe_models.append(
{ {
"id": sub_pipe_id, "id": sub_pipe_id,
@@ -293,6 +300,10 @@ async def get_pipe_models():
else: else:
pipe_flag = {"type": "pipe"} pipe_flag = {"type": "pipe"}
log.debug(
f"get_pipe_models: function '{pipe.id}' is a single pipe {{ 'id': {pipe.id}, 'name': {pipe.name} }}"
)
pipe_models.append( pipe_models.append(
{ {
"id": pipe.id, "id": pipe.id,
@@ -346,7 +357,7 @@ def get_pipe_id(form_data: dict) -> str:
pipe_id = form_data["model"] pipe_id = form_data["model"]
if "." in pipe_id: if "." in pipe_id:
pipe_id, _ = pipe_id.split(".", 1) pipe_id, _ = pipe_id.split(".", 1)
print(pipe_id)
return pipe_id return pipe_id
@@ -453,7 +464,7 @@ async def generate_function_chat_completion(form_data, user, models: dict = {}):
return return
except Exception as e: except Exception as e:
print(f"Error: {e}") log.error(f"Error: {e}")
yield f"data: {json.dumps({'error': {'detail':str(e)}})}\n\n" yield f"data: {json.dumps({'error': {'detail':str(e)}})}\n\n"
return return
@@ -483,7 +494,7 @@ async def generate_function_chat_completion(form_data, user, models: dict = {}):
res = await execute_pipe(pipe, params) res = await execute_pipe(pipe, params)
except Exception as e: except Exception as e:
print(f"Error: {e}") log.error(f"Error: {e}")
return {"error": {"detail": str(e)}} return {"error": {"detail": str(e)}}
if isinstance(res, StreamingResponse) or isinstance(res, dict): if isinstance(res, StreamingResponse) or isinstance(res, dict):
@@ -128,17 +128,20 @@ class KnowledgeTable:
def get_knowledge_bases(self) -> list[KnowledgeUserModel]: def get_knowledge_bases(self) -> list[KnowledgeUserModel]:
with get_db() as db: with get_db() as db:
return [ knowledge_bases = []
KnowledgeUserModel.model_validate( for knowledge in (
{ db.query(Knowledge).order_by(Knowledge.updated_at.desc()).all()
**KnowledgeModel.model_validate(knowledge).model_dump(), ):
"user": Users.get_user_by_id(knowledge.user_id).model_dump(), user = Users.get_user_by_id(knowledge.user_id)
} knowledge_bases.append(
KnowledgeUserModel.model_validate(
{
**KnowledgeModel.model_validate(knowledge).model_dump(),
"user": user.model_dump() if user else None,
}
)
) )
for knowledge in db.query(Knowledge) return knowledge_bases
.order_by(Knowledge.updated_at.desc())
.all()
]
def get_knowledge_bases_by_user_id( def get_knowledge_bases_by_user_id(
self, user_id: str, permission: str = "write" self, user_id: str, permission: str = "write"
+11 -8
View File
@@ -175,15 +175,18 @@ class ModelsTable:
def get_models(self) -> list[ModelUserResponse]: def get_models(self) -> list[ModelUserResponse]:
with get_db() as db: with get_db() as db:
return [ models = []
ModelUserResponse.model_validate( for model in db.query(Model).filter(Model.base_model_id != None).all():
{ user = Users.get_user_by_id(model.user_id)
**ModelModel.model_validate(model).model_dump(), models.append(
"user": Users.get_user_by_id(model.user_id).model_dump(), ModelUserResponse.model_validate(
} {
**ModelModel.model_validate(model).model_dump(),
"user": user.model_dump() if user else None,
}
)
) )
for model in db.query(Model).filter(Model.base_model_id != None).all() return models
]
def get_base_models(self) -> list[ModelModel]: def get_base_models(self) -> list[ModelModel]:
with get_db() as db: with get_db() as db:
@@ -103,15 +103,20 @@ class PromptsTable:
def get_prompts(self) -> list[PromptUserResponse]: def get_prompts(self) -> list[PromptUserResponse]:
with get_db() as db: with get_db() as db:
return [ prompts = []
PromptUserResponse.model_validate(
{ for prompt in db.query(Prompt).order_by(Prompt.timestamp.desc()).all():
**PromptModel.model_validate(prompt).model_dump(), user = Users.get_user_by_id(prompt.user_id)
"user": Users.get_user_by_id(prompt.user_id).model_dump(), prompts.append(
} PromptUserResponse.model_validate(
{
**PromptModel.model_validate(prompt).model_dump(),
"user": user.model_dump() if user else None,
}
)
) )
for prompt in db.query(Prompt).all()
] return prompts
def get_prompts_by_user_id( def get_prompts_by_user_id(
self, user_id: str, permission: str = "write" self, user_id: str, permission: str = "write"
+17 -10
View File
@@ -76,6 +76,10 @@ class ToolModel(BaseModel):
#################### ####################
class ToolUserModel(ToolModel):
user: Optional[UserResponse] = None
class ToolResponse(BaseModel): class ToolResponse(BaseModel):
id: str id: str
user_id: str user_id: str
@@ -138,21 +142,24 @@ class ToolsTable:
except Exception: except Exception:
return None return None
def get_tools(self) -> list[ToolUserResponse]: def get_tools(self) -> list[ToolUserModel]:
with get_db() as db: with get_db() as db:
return [ tools = []
ToolUserResponse.model_validate( for tool in db.query(Tool).order_by(Tool.updated_at.desc()).all():
{ user = Users.get_user_by_id(tool.user_id)
**ToolModel.model_validate(tool).model_dump(), tools.append(
"user": Users.get_user_by_id(tool.user_id).model_dump(), ToolUserModel.model_validate(
} {
**ToolModel.model_validate(tool).model_dump(),
"user": user.model_dump() if user else None,
}
)
) )
for tool in db.query(Tool).order_by(Tool.updated_at.desc()).all() return tools
]
def get_tools_by_user_id( def get_tools_by_user_id(
self, user_id: str, permission: str = "write" self, user_id: str, permission: str = "write"
) -> list[ToolUserResponse]: ) -> list[ToolUserModel]:
tools = self.get_tools() tools = self.get_tools()
return [ return [
+12 -6
View File
@@ -238,10 +238,16 @@ async def ldap_auth(request: Request, response: Response, form_data: LdapForm):
user = Users.get_user_by_email(mail) user = Users.get_user_by_email(mail)
if not user: if not user:
try: try:
hashed = get_password_hash(form_data.password) role = (
user = Auths.insert_new_auth(mail, hashed, cn) "admin"
if Users.get_num_users() == 0
else request.app.state.config.DEFAULT_USER_ROLE
)
user = Auths.insert_new_auth(
email=mail, password=str(uuid.uuid4()), name=cn, role=role
)
if not user: if not user:
raise HTTPException( raise HTTPException(
@@ -253,7 +259,7 @@ async def ldap_auth(request: Request, response: Response, form_data: LdapForm):
except Exception as err: except Exception as err:
raise HTTPException(500, detail=ERROR_MESSAGES.DEFAULT(err)) raise HTTPException(500, detail=ERROR_MESSAGES.DEFAULT(err))
user = Auths.authenticate_user(mail, password=str(form_data.password)) user = Auths.authenticate_user_by_trusted_header(mail)
if user: if user:
token = create_token( token = create_token(
@@ -738,8 +744,8 @@ async def update_ldap_config(
# create api key # create api key
@router.post("/api_key", response_model=ApiKey) @router.post("/api_key", response_model=ApiKey)
async def create_api_key(request: Request, user=Depends(get_current_user)): async def generate_api_key(request: Request, user=Depends(get_current_user)):
if not request.app.config.state.ENABLE_API_KEY: if not request.app.state.config.ENABLE_API_KEY:
raise HTTPException( raise HTTPException(
status.HTTP_403_FORBIDDEN, status.HTTP_403_FORBIDDEN,
detail=ERROR_MESSAGES.API_KEY_CREATION_NOT_ALLOWED, detail=ERROR_MESSAGES.API_KEY_CREATION_NOT_ALLOWED,
@@ -1,10 +1,12 @@
from open_webui.config import BannerModel
from fastapi import APIRouter, Depends, Request from fastapi import APIRouter, Depends, Request
from pydantic import BaseModel from pydantic import BaseModel
from typing import Optional
from open_webui.utils.utils import get_admin_user, get_verified_user from open_webui.utils.utils import get_admin_user, get_verified_user
from open_webui.config import get_config, save_config from open_webui.config import get_config, save_config
from open_webui.config import BannerModel
router = APIRouter() router = APIRouter()
@@ -34,8 +36,32 @@ async def export_config(user=Depends(get_admin_user)):
return get_config() return get_config()
class SetDefaultModelsForm(BaseModel): ############################
models: str # SetDefaultModels
############################
class ModelsConfigForm(BaseModel):
DEFAULT_MODELS: Optional[str]
MODEL_ORDER_LIST: Optional[list[str]]
@router.get("/models", response_model=ModelsConfigForm)
async def get_models_config(request: Request, user=Depends(get_admin_user)):
return {
"DEFAULT_MODELS": request.app.state.config.DEFAULT_MODELS,
"MODEL_ORDER_LIST": request.app.state.config.MODEL_ORDER_LIST,
}
@router.post("/models", response_model=ModelsConfigForm)
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.MODEL_ORDER_LIST = form_data.MODEL_ORDER_LIST
return {
"DEFAULT_MODELS": request.app.state.config.DEFAULT_MODELS,
"MODEL_ORDER_LIST": request.app.state.config.MODEL_ORDER_LIST,
}
class PromptSuggestion(BaseModel): class PromptSuggestion(BaseModel):
@@ -47,21 +73,8 @@ class SetDefaultSuggestionsForm(BaseModel):
suggestions: list[PromptSuggestion] suggestions: list[PromptSuggestion]
############################ @router.post("/suggestions", response_model=list[PromptSuggestion])
# SetDefaultModels async def set_default_suggestions(
############################
@router.post("/default/models", response_model=str)
async def set_global_default_models(
request: Request, form_data: SetDefaultModelsForm, user=Depends(get_admin_user)
):
request.app.state.config.DEFAULT_MODELS = form_data.models
return request.app.state.config.DEFAULT_MODELS
@router.post("/default/suggestions", response_model=list[PromptSuggestion])
async def set_global_default_suggestions(
request: Request, request: Request,
form_data: SetDefaultSuggestionsForm, form_data: SetDefaultSuggestionsForm,
user=Depends(get_admin_user), user=Depends(get_admin_user),
@@ -1,155 +0,0 @@
import json
from typing import Optional
from open_webui.apps.webui.models.documents import (
DocumentForm,
DocumentResponse,
Documents,
DocumentUpdateForm,
)
from open_webui.constants import ERROR_MESSAGES
from fastapi import APIRouter, Depends, HTTPException, status
from pydantic import BaseModel
from open_webui.utils.utils import get_admin_user, get_verified_user
router = APIRouter()
############################
# GetDocuments
############################
@router.get("/", response_model=list[DocumentResponse])
async def get_documents(user=Depends(get_verified_user)):
docs = [
DocumentResponse(
**{
**doc.model_dump(),
"content": json.loads(doc.content if doc.content else "{}"),
}
)
for doc in Documents.get_docs()
]
return docs
############################
# CreateNewDoc
############################
@router.post("/create", response_model=Optional[DocumentResponse])
async def create_new_doc(form_data: DocumentForm, user=Depends(get_admin_user)):
doc = Documents.get_doc_by_name(form_data.name)
if doc is None:
doc = Documents.insert_new_doc(user.id, form_data)
if doc:
return DocumentResponse(
**{
**doc.model_dump(),
"content": json.loads(doc.content if doc.content else "{}"),
}
)
else:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=ERROR_MESSAGES.FILE_EXISTS,
)
else:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=ERROR_MESSAGES.NAME_TAG_TAKEN,
)
############################
# GetDocByName
############################
@router.get("/doc", response_model=Optional[DocumentResponse])
async def get_doc_by_name(name: str, user=Depends(get_verified_user)):
doc = Documents.get_doc_by_name(name)
if doc:
return DocumentResponse(
**{
**doc.model_dump(),
"content": json.loads(doc.content if doc.content else "{}"),
}
)
else:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail=ERROR_MESSAGES.NOT_FOUND,
)
############################
# TagDocByName
############################
class TagItem(BaseModel):
name: str
class TagDocumentForm(BaseModel):
name: str
tags: list[dict]
@router.post("/doc/tags", response_model=Optional[DocumentResponse])
async def tag_doc_by_name(form_data: TagDocumentForm, user=Depends(get_verified_user)):
doc = Documents.update_doc_content_by_name(form_data.name, {"tags": form_data.tags})
if doc:
return DocumentResponse(
**{
**doc.model_dump(),
"content": json.loads(doc.content if doc.content else "{}"),
}
)
else:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail=ERROR_MESSAGES.NOT_FOUND,
)
############################
# UpdateDocByName
############################
@router.post("/doc/update", response_model=Optional[DocumentResponse])
async def update_doc_by_name(
name: str,
form_data: DocumentUpdateForm,
user=Depends(get_admin_user),
):
doc = Documents.update_doc_by_name(name, form_data)
if doc:
return DocumentResponse(
**{
**doc.model_dump(),
"content": json.loads(doc.content if doc.content else "{}"),
}
)
else:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=ERROR_MESSAGES.NAME_TAG_TAKEN,
)
############################
# DeleteDocByName
############################
@router.delete("/doc/delete", response_model=bool)
async def delete_doc_by_name(name: str, user=Depends(get_admin_user)):
result = Documents.delete_doc_by_name(name)
return result
@@ -56,7 +56,7 @@ def upload_file(file: UploadFile = File(...), user=Depends(get_verified_user)):
FileForm( FileForm(
**{ **{
"id": id, "id": id,
"filename": filename, "filename": name,
"path": file_path, "path": file_path,
"meta": { "meta": {
"name": name, "name": name,
@@ -43,6 +43,7 @@ async def get_knowledge(user=Depends(get_verified_user)):
knowledge_bases = Knowledges.get_knowledge_bases_by_user_id(user.id, "read") knowledge_bases = Knowledges.get_knowledge_bases_by_user_id(user.id, "read")
# Get files for each knowledge base # Get files for each knowledge base
knowledge_with_files = []
for knowledge_base in knowledge_bases: for knowledge_base in knowledge_bases:
files = [] files = []
if knowledge_base.data: if knowledge_base.data:
@@ -70,12 +71,14 @@ async def get_knowledge(user=Depends(get_verified_user)):
files = Files.get_file_metadatas_by_ids(file_ids) files = Files.get_file_metadatas_by_ids(file_ids)
knowledge_base = KnowledgeResponse( knowledge_with_files.append(
**knowledge_base.model_dump(), KnowledgeUserResponse(
files=files, **knowledge_base.model_dump(),
files=files,
)
) )
return knowledge_bases return knowledge_with_files
@router.get("/list", response_model=list[KnowledgeUserResponse]) @router.get("/list", response_model=list[KnowledgeUserResponse])
@@ -88,6 +91,7 @@ async def get_knowledge_list(user=Depends(get_verified_user)):
knowledge_bases = Knowledges.get_knowledge_bases_by_user_id(user.id, "write") knowledge_bases = Knowledges.get_knowledge_bases_by_user_id(user.id, "write")
# Get files for each knowledge base # Get files for each knowledge base
knowledge_with_files = []
for knowledge_base in knowledge_bases: for knowledge_base in knowledge_bases:
files = [] files = []
if knowledge_base.data: if knowledge_base.data:
@@ -115,12 +119,13 @@ async def get_knowledge_list(user=Depends(get_verified_user)):
files = Files.get_file_metadatas_by_ids(file_ids) files = Files.get_file_metadatas_by_ids(file_ids)
knowledge_base = KnowledgeResponse( knowledge_with_files.append(
**knowledge_base.model_dump(), KnowledgeUserResponse(
files=files, **knowledge_base.model_dump(),
files=files,
)
) )
return knowledge_with_files
return knowledge_bases
############################ ############################
@@ -83,7 +83,8 @@ async def create_new_model(
########################### ###########################
@router.get("/id/{id}", response_model=Optional[ModelResponse]) # Note: We're not using the typical url path param here, but instead using a query parameter to allow '/' in the id
@router.get("/model", response_model=Optional[ModelResponse])
async def get_model_by_id(id: str, user=Depends(get_verified_user)): async def get_model_by_id(id: str, user=Depends(get_verified_user)):
model = Models.get_model_by_id(id) model = Models.get_model_by_id(id)
if model: if model:
@@ -105,7 +106,7 @@ async def get_model_by_id(id: str, user=Depends(get_verified_user)):
############################ ############################
@router.post("/id/{id}/toggle", response_model=Optional[ModelResponse]) @router.post("/model/toggle", response_model=Optional[ModelResponse])
async def toggle_model_by_id(id: str, user=Depends(get_verified_user)): async def toggle_model_by_id(id: str, user=Depends(get_verified_user)):
model = Models.get_model_by_id(id) model = Models.get_model_by_id(id)
if model: if model:
@@ -140,7 +141,7 @@ async def toggle_model_by_id(id: str, user=Depends(get_verified_user)):
############################ ############################
@router.post("/id/{id}/update", response_model=Optional[ModelModel]) @router.post("/model/update", response_model=Optional[ModelModel])
async def update_model_by_id( async def update_model_by_id(
id: str, id: str,
form_data: ModelForm, form_data: ModelForm,
@@ -163,7 +164,7 @@ async def update_model_by_id(
############################ ############################
@router.delete("/id/{id}/delete", response_model=bool) @router.delete("/model/delete", response_model=bool)
async def delete_model_by_id(id: str, user=Depends(get_verified_user)): async def delete_model_by_id(id: str, user=Depends(get_verified_user)):
model = Models.get_model_by_id(id) model = Models.get_model_by_id(id)
if not model: if not model:
+26 -30
View File
@@ -1,4 +1,3 @@
import os
from pathlib import Path from pathlib import Path
from typing import Optional from typing import Optional
@@ -10,7 +9,7 @@ from open_webui.apps.webui.models.tools import (
Tools, Tools,
) )
from open_webui.apps.webui.utils import load_tools_module_by_id, replace_imports from open_webui.apps.webui.utils import load_tools_module_by_id, replace_imports
from open_webui.config import CACHE_DIR, DATA_DIR from open_webui.config import CACHE_DIR
from open_webui.constants import ERROR_MESSAGES from open_webui.constants import ERROR_MESSAGES
from fastapi import APIRouter, Depends, HTTPException, Request, status from fastapi import APIRouter, Depends, HTTPException, Request, status
from open_webui.utils.tools import get_tools_specs from open_webui.utils.tools import get_tools_specs
@@ -300,38 +299,35 @@ async def update_tools_valves_by_id(
request: Request, id: str, form_data: dict, user=Depends(get_verified_user) request: Request, id: str, form_data: dict, user=Depends(get_verified_user)
): ):
tools = Tools.get_tool_by_id(id) tools = Tools.get_tool_by_id(id)
if tools: if not tools:
if id in request.app.state.TOOLS:
tools_module = request.app.state.TOOLS[id]
else:
tools_module, _ = load_tools_module_by_id(id)
request.app.state.TOOLS[id] = tools_module
if hasattr(tools_module, "Valves"):
Valves = tools_module.Valves
try:
form_data = {k: v for k, v in form_data.items() if v is not None}
valves = Valves(**form_data)
Tools.update_tool_valves_by_id(id, valves.model_dump())
return valves.model_dump()
except Exception as e:
print(e)
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=ERROR_MESSAGES.DEFAULT(str(e)),
)
else:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail=ERROR_MESSAGES.NOT_FOUND,
)
else:
raise HTTPException( raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED, status_code=status.HTTP_401_UNAUTHORIZED,
detail=ERROR_MESSAGES.NOT_FOUND, detail=ERROR_MESSAGES.NOT_FOUND,
) )
if id in request.app.state.TOOLS:
tools_module = request.app.state.TOOLS[id]
else:
tools_module, _ = load_tools_module_by_id(id)
request.app.state.TOOLS[id] = tools_module
if not hasattr(tools_module, "Valves"):
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail=ERROR_MESSAGES.NOT_FOUND,
)
Valves = tools_module.Valves
try:
form_data = {k: v for k, v in form_data.items() if v is not None}
valves = Valves(**form_data)
Tools.update_tool_valves_by_id(id, valves.model_dump())
return valves.model_dump()
except Exception as e:
print(e)
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=ERROR_MESSAGES.DEFAULT(str(e)),
)
############################ ############################
+11 -6
View File
@@ -5,10 +5,15 @@ import sys
from importlib import util from importlib import util
import types import types
import tempfile import tempfile
import logging
from open_webui.env import SRC_LOG_LEVELS
from open_webui.apps.webui.models.functions import Functions from open_webui.apps.webui.models.functions import Functions
from open_webui.apps.webui.models.tools import Tools from open_webui.apps.webui.models.tools import Tools
log = logging.getLogger(__name__)
log.setLevel(SRC_LOG_LEVELS["MAIN"])
def extract_frontmatter(content): def extract_frontmatter(content):
""" """
@@ -95,7 +100,7 @@ def load_tools_module_by_id(toolkit_id, content=None):
# Executing the modified content in the created module's namespace # Executing the modified content in the created module's namespace
exec(content, module.__dict__) exec(content, module.__dict__)
frontmatter = extract_frontmatter(content) frontmatter = extract_frontmatter(content)
print(f"Loaded module: {module.__name__}") log.info(f"Loaded module: {module.__name__}")
# Create and return the object if the class 'Tools' is found in the module # Create and return the object if the class 'Tools' is found in the module
if hasattr(module, "Tools"): if hasattr(module, "Tools"):
@@ -103,7 +108,7 @@ def load_tools_module_by_id(toolkit_id, content=None):
else: else:
raise Exception("No Tools class found in the module") raise Exception("No Tools class found in the module")
except Exception as e: except Exception as e:
print(f"Error loading module: {toolkit_id}: {e}") log.error(f"Error loading module: {toolkit_id}: {e}")
del sys.modules[module_name] # Clean up del sys.modules[module_name] # Clean up
raise e raise e
finally: finally:
@@ -139,7 +144,7 @@ def load_function_module_by_id(function_id, content=None):
# Execute the modified content in the created module's namespace # Execute the modified content in the created module's namespace
exec(content, module.__dict__) exec(content, module.__dict__)
frontmatter = extract_frontmatter(content) frontmatter = extract_frontmatter(content)
print(f"Loaded module: {module.__name__}") log.info(f"Loaded module: {module.__name__}")
# Create appropriate object based on available class type in the module # Create appropriate object based on available class type in the module
if hasattr(module, "Pipe"): if hasattr(module, "Pipe"):
@@ -151,7 +156,7 @@ def load_function_module_by_id(function_id, content=None):
else: else:
raise Exception("No Function class found in the module") raise Exception("No Function class found in the module")
except Exception as e: except Exception as e:
print(f"Error loading module: {function_id}: {e}") log.error(f"Error loading module: {function_id}: {e}")
del sys.modules[module_name] # Cleanup by removing the module in case of error del sys.modules[module_name] # Cleanup by removing the module in case of error
Functions.update_function_by_id(function_id, {"is_active": False}) Functions.update_function_by_id(function_id, {"is_active": False})
@@ -164,7 +169,7 @@ def install_frontmatter_requirements(requirements):
if requirements: if requirements:
req_list = [req.strip() for req in requirements.split(",")] req_list = [req.strip() for req in requirements.split(",")]
for req in req_list: for req in req_list:
print(f"Installing requirement: {req}") log.info(f"Installing requirement: {req}")
subprocess.check_call([sys.executable, "-m", "pip", "install", req]) subprocess.check_call([sys.executable, "-m", "pip", "install", req])
else: else:
print("No requirements found in frontmatter.") log.info("No requirements found in frontmatter.")
+116 -20
View File
@@ -583,6 +583,12 @@ OLLAMA_API_BASE_URL = os.environ.get(
) )
OLLAMA_BASE_URL = os.environ.get("OLLAMA_BASE_URL", "") OLLAMA_BASE_URL = os.environ.get("OLLAMA_BASE_URL", "")
if OLLAMA_BASE_URL:
# Remove trailing slash
OLLAMA_BASE_URL = (
OLLAMA_BASE_URL[:-1] if OLLAMA_BASE_URL.endswith("/") else OLLAMA_BASE_URL
)
K8S_FLAG = os.environ.get("K8S_FLAG", "") K8S_FLAG = os.environ.get("K8S_FLAG", "")
USE_OLLAMA_DOCKER = os.environ.get("USE_OLLAMA_DOCKER", "false") USE_OLLAMA_DOCKER = os.environ.get("USE_OLLAMA_DOCKER", "false")
@@ -696,6 +702,7 @@ ENABLE_LOGIN_FORM = PersistentConfig(
os.environ.get("ENABLE_LOGIN_FORM", "True").lower() == "true", os.environ.get("ENABLE_LOGIN_FORM", "True").lower() == "true",
) )
DEFAULT_LOCALE = PersistentConfig( DEFAULT_LOCALE = PersistentConfig(
"DEFAULT_LOCALE", "DEFAULT_LOCALE",
"ui.default_locale", "ui.default_locale",
@@ -740,13 +747,18 @@ DEFAULT_PROMPT_SUGGESTIONS = PersistentConfig(
], ],
) )
MODEL_ORDER_LIST = PersistentConfig(
"MODEL_ORDER_LIST",
"ui.model_order_list",
[],
)
DEFAULT_USER_ROLE = PersistentConfig( DEFAULT_USER_ROLE = PersistentConfig(
"DEFAULT_USER_ROLE", "DEFAULT_USER_ROLE",
"ui.default_user_role", "ui.default_user_role",
os.getenv("DEFAULT_USER_ROLE", "pending"), os.getenv("DEFAULT_USER_ROLE", "pending"),
) )
USER_PERMISSIONS_WORKSPACE_MODELS_ACCESS = ( USER_PERMISSIONS_WORKSPACE_MODELS_ACCESS = (
os.environ.get("USER_PERMISSIONS_WORKSPACE_MODELS_ACCESS", "False").lower() os.environ.get("USER_PERMISSIONS_WORKSPACE_MODELS_ACCESS", "False").lower()
== "true" == "true"
@@ -969,19 +981,20 @@ QUERY_GENERATION_PROMPT_TEMPLATE = PersistentConfig(
) )
DEFAULT_QUERY_GENERATION_PROMPT_TEMPLATE = """### Task: DEFAULT_QUERY_GENERATION_PROMPT_TEMPLATE = """### Task:
Based on the chat history, determine whether a search is necessary, and if so, generate a 1-3 broad search queries to retrieve comprehensive and updated information. If no search is required, return an empty list. Analyze the chat history to determine the necessity of generating search queries, in the given language. By default, **prioritize generating 1-3 broad and relevant search queries** unless it is absolutely certain that no additional information is required. The aim is to retrieve comprehensive, updated, and valuable information even with minimal uncertainty. If no search is unequivocally needed, return an empty list.
### Guidelines: ### Guidelines:
- Respond exclusively with a JSON object. - Respond **EXCLUSIVELY** with a JSON object. Any form of extra commentary, explanation, or additional text is strictly prohibited.
- If a search query is needed, return an object like: { "queries": ["query1", "query2"] } where each query is distinct and concise. - When generating search queries, respond in the format: { "queries": ["query1", "query2"] }, ensuring each query is distinct, concise, and relevant to the topic.
- If no search query is necessary, output should be: { "queries": [] } - If and only if it is entirely certain that no useful results can be retrieved by a search, return: { "queries": [] }.
- Default to suggesting a search query to ensure accurate and updated information, unless it is definitively clear no search is required. - Err on the side of suggesting search queries if there is **any chance** they might provide useful or updated information.
- Be concise, focusing strictly on composing search queries with no additional commentary or text. - Be concise and focused on composing high-quality search queries, avoiding unnecessary elaboration, commentary, or assumptions.
- When in doubt, prefer to suggest a search for comprehensiveness. - Today's date is: {{CURRENT_DATE}}.
- Today's date is: {{CURRENT_DATE}} - Always prioritize providing actionable and broad queries that maximize informational coverage.
### Output: ### Output:
JSON format: { Strictly return in JSON format:
{
"queries": ["query1", "query2"] "queries": ["query1", "query2"]
} }
@@ -991,6 +1004,66 @@ JSON format: {
</chat_history> </chat_history>
""" """
ENABLE_AUTOCOMPLETE_GENERATION = PersistentConfig(
"ENABLE_AUTOCOMPLETE_GENERATION",
"task.autocomplete.enable",
os.environ.get("ENABLE_AUTOCOMPLETE_GENERATION", "True").lower() == "true",
)
AUTOCOMPLETE_GENERATION_INPUT_MAX_LENGTH = PersistentConfig(
"AUTOCOMPLETE_GENERATION_INPUT_MAX_LENGTH",
"task.autocomplete.input_max_length",
int(os.environ.get("AUTOCOMPLETE_GENERATION_INPUT_MAX_LENGTH", "-1")),
)
AUTOCOMPLETE_GENERATION_PROMPT_TEMPLATE = PersistentConfig(
"AUTOCOMPLETE_GENERATION_PROMPT_TEMPLATE",
"task.autocomplete.prompt_template",
os.environ.get("AUTOCOMPLETE_GENERATION_PROMPT_TEMPLATE", ""),
)
DEFAULT_AUTOCOMPLETE_GENERATION_PROMPT_TEMPLATE = """### Task:
You are an autocompletion system. Continue the text in `<text>` based on the **completion type** in `<type>` and the given language.
### **Instructions**:
1. Analyze `<text>` for context and meaning.
2. Use `<type>` to guide your output:
- **General**: Provide a natural, concise continuation.
- **Search Query**: Complete as if generating a realistic search query.
3. Start as if you are directly continuing `<text>`. Do **not** repeat, paraphrase, or respond as a model. Simply complete the text.
4. Ensure the continuation:
- Flows naturally from `<text>`.
- Avoids repetition, overexplaining, or unrelated ideas.
5. If unsure, return: `{ "text": "" }`.
### **Output Rules**:
- Respond only in JSON format: `{ "text": "<your_completion>" }`.
### **Examples**:
#### Example 1:
Input:
<type>General</type>
<text>The sun was setting over the horizon, painting the sky</text>
Output:
{ "text": "with vibrant shades of orange and pink." }
#### Example 2:
Input:
<type>Search Query</type>
<text>Top-rated restaurants in</text>
Output:
{ "text": "New York City for Italian cuisine." }
---
### Context:
<chat_history>
{{MESSAGES:END:6}}
</chat_history>
<type>{{TYPE}}</type>
<text>{{PROMPT}}</text>
#### Output:
"""
TOOLS_FUNCTION_CALLING_PROMPT_TEMPLATE = PersistentConfig( TOOLS_FUNCTION_CALLING_PROMPT_TEMPLATE = PersistentConfig(
"TOOLS_FUNCTION_CALLING_PROMPT_TEMPLATE", "TOOLS_FUNCTION_CALLING_PROMPT_TEMPLATE",
@@ -1181,21 +1254,32 @@ CHUNK_OVERLAP = PersistentConfig(
int(os.environ.get("CHUNK_OVERLAP", "100")), int(os.environ.get("CHUNK_OVERLAP", "100")),
) )
DEFAULT_RAG_TEMPLATE = """You are given a user query, some textual context and rules, all inside xml tags. You have to answer the query based on the context while respecting the rules. DEFAULT_RAG_TEMPLATE = """### Task:
Respond to the user query using the provided context, incorporating inline citations in the format [source_id] **only when the <source_id> tag is explicitly provided** in the context.
### Guidelines:
- If you don't know the answer, clearly state that.
- If uncertain, ask the user for clarification.
- Respond in the same language as the user's query.
- If the context is unreadable or of poor quality, inform the user and provide the best possible answer.
- If the answer isn't present in the context but you possess the knowledge, explain this to the user and provide the answer using your own understanding.
- **Only include inline citations using [source_id] when a <source_id> tag is explicitly provided in the context.**
- Do not cite if the <source_id> tag is not provided in the context.
- Do not use XML tags in your response.
- Ensure citations are concise and directly related to the information provided.
### Example of Citation:
If the user asks about a specific topic and the information is found in "whitepaper.pdf" with a provided <source_id>, the response should include the citation like so:
* "According to the study, the proposed method increases efficiency by 20% [whitepaper.pdf]."
If no <source_id> is present, the response should omit the citation.
### Output:
Provide a clear and direct response to the user's query, including inline citations in the format [source_id] only when the <source_id> tag is present in the context.
<context> <context>
{{CONTEXT}} {{CONTEXT}}
</context> </context>
<rules>
- If you don't know, just say so.
- If you are not sure, ask for clarification.
- Answer in the same language as the user query.
- If the context appears unreadable or of poor quality, tell the user then answer as best as you can.
- If the answer is not in the context but you think you know the answer, explain that to the user then answer with your own knowledge.
- Answer directly and without using xml tags.
</rules>
<user_query> <user_query>
{{QUERY}} {{QUERY}}
</user_query> </user_query>
@@ -1241,6 +1325,12 @@ YOUTUBE_LOADER_LANGUAGE = PersistentConfig(
os.getenv("YOUTUBE_LOADER_LANGUAGE", "en").split(","), os.getenv("YOUTUBE_LOADER_LANGUAGE", "en").split(","),
) )
YOUTUBE_LOADER_PROXY_URL = PersistentConfig(
"YOUTUBE_LOADER_PROXY_URL",
"rag.youtube_loader_proxy_url",
os.getenv("YOUTUBE_LOADER_PROXY_URL", ""),
)
ENABLE_RAG_WEB_SEARCH = PersistentConfig( ENABLE_RAG_WEB_SEARCH = PersistentConfig(
"ENABLE_RAG_WEB_SEARCH", "ENABLE_RAG_WEB_SEARCH",
@@ -1290,6 +1380,12 @@ BRAVE_SEARCH_API_KEY = PersistentConfig(
os.getenv("BRAVE_SEARCH_API_KEY", ""), os.getenv("BRAVE_SEARCH_API_KEY", ""),
) )
MOJEEK_SEARCH_API_KEY = PersistentConfig(
"MOJEEK_SEARCH_API_KEY",
"rag.web.search.mojeek_search_api_key",
os.getenv("MOJEEK_SEARCH_API_KEY", ""),
)
SERPSTACK_API_KEY = PersistentConfig( SERPSTACK_API_KEY = PersistentConfig(
"SERPSTACK_API_KEY", "SERPSTACK_API_KEY",
"rag.web.search.serpstack_api_key", "rag.web.search.serpstack_api_key",
+1
View File
@@ -113,5 +113,6 @@ class TASKS(str, Enum):
TAGS_GENERATION = "tags_generation" TAGS_GENERATION = "tags_generation"
EMOJI_GENERATION = "emoji_generation" EMOJI_GENERATION = "emoji_generation"
QUERY_GENERATION = "query_generation" QUERY_GENERATION = "query_generation"
AUTOCOMPLETE_GENERATION = "autocomplete_generation"
FUNCTION_CALLING = "function_calling" FUNCTION_CALLING = "function_calling"
MOA_RESPONSE_GENERATION = "moa_response_generation" MOA_RESPONSE_GENERATION = "moa_response_generation"
+5 -2
View File
@@ -329,6 +329,9 @@ WEBUI_AUTH_TRUSTED_EMAIL_HEADER = os.environ.get(
) )
WEBUI_AUTH_TRUSTED_NAME_HEADER = os.environ.get("WEBUI_AUTH_TRUSTED_NAME_HEADER", None) WEBUI_AUTH_TRUSTED_NAME_HEADER = os.environ.get("WEBUI_AUTH_TRUSTED_NAME_HEADER", None)
BYPASS_MODEL_ACCESS_CONTROL = (
os.environ.get("BYPASS_MODEL_ACCESS_CONTROL", "False").lower() == "true"
)
#################################### ####################################
# WEBUI_SECRET_KEY # WEBUI_SECRET_KEY
@@ -373,7 +376,7 @@ else:
AIOHTTP_CLIENT_TIMEOUT = 300 AIOHTTP_CLIENT_TIMEOUT = 300
AIOHTTP_CLIENT_TIMEOUT_OPENAI_MODEL_LIST = os.environ.get( AIOHTTP_CLIENT_TIMEOUT_OPENAI_MODEL_LIST = os.environ.get(
"AIOHTTP_CLIENT_TIMEOUT_OPENAI_MODEL_LIST", "3" "AIOHTTP_CLIENT_TIMEOUT_OPENAI_MODEL_LIST", "5"
) )
if AIOHTTP_CLIENT_TIMEOUT_OPENAI_MODEL_LIST == "": if AIOHTTP_CLIENT_TIMEOUT_OPENAI_MODEL_LIST == "":
@@ -384,7 +387,7 @@ else:
AIOHTTP_CLIENT_TIMEOUT_OPENAI_MODEL_LIST AIOHTTP_CLIENT_TIMEOUT_OPENAI_MODEL_LIST
) )
except Exception: except Exception:
AIOHTTP_CLIENT_TIMEOUT_OPENAI_MODEL_LIST = 3 AIOHTTP_CLIENT_TIMEOUT_OPENAI_MODEL_LIST = 5
#################################### ####################################
# OFFLINE_MODE # OFFLINE_MODE
+271 -94
View File
@@ -49,7 +49,9 @@ from open_webui.apps.openai.main import (
get_all_models_responses as get_openai_models_responses, get_all_models_responses as get_openai_models_responses,
) )
from open_webui.apps.retrieval.main import app as retrieval_app from open_webui.apps.retrieval.main import app as retrieval_app
from open_webui.apps.retrieval.utils import get_rag_context, rag_template from open_webui.apps.retrieval.utils import get_sources_from_files
from open_webui.apps.socket.main import ( from open_webui.apps.socket.main import (
app as socket_app, app as socket_app,
periodic_usage_pool_cleanup, periodic_usage_pool_cleanup,
@@ -87,6 +89,10 @@ from open_webui.config import (
DEFAULT_QUERY_GENERATION_PROMPT_TEMPLATE, DEFAULT_QUERY_GENERATION_PROMPT_TEMPLATE,
TITLE_GENERATION_PROMPT_TEMPLATE, TITLE_GENERATION_PROMPT_TEMPLATE,
TAGS_GENERATION_PROMPT_TEMPLATE, TAGS_GENERATION_PROMPT_TEMPLATE,
ENABLE_AUTOCOMPLETE_GENERATION,
AUTOCOMPLETE_GENERATION_INPUT_MAX_LENGTH,
AUTOCOMPLETE_GENERATION_PROMPT_TEMPLATE,
DEFAULT_AUTOCOMPLETE_GENERATION_PROMPT_TEMPLATE,
TOOLS_FUNCTION_CALLING_PROMPT_TEMPLATE, TOOLS_FUNCTION_CALLING_PROMPT_TEMPLATE,
WEBHOOK_URL, WEBHOOK_URL,
WEBUI_AUTH, WEBUI_AUTH,
@@ -106,6 +112,7 @@ from open_webui.env import (
WEBUI_SESSION_COOKIE_SAME_SITE, WEBUI_SESSION_COOKIE_SAME_SITE,
WEBUI_SESSION_COOKIE_SECURE, WEBUI_SESSION_COOKIE_SECURE,
WEBUI_URL, WEBUI_URL,
BYPASS_MODEL_ACCESS_CONTROL,
RESET_CONFIG_ON_START, RESET_CONFIG_ON_START,
OFFLINE_MODE, OFFLINE_MODE,
) )
@@ -122,11 +129,13 @@ from open_webui.utils.response import (
) )
from open_webui.utils.security_headers import SecurityHeadersMiddleware from open_webui.utils.security_headers import SecurityHeadersMiddleware
from open_webui.utils.task import ( from open_webui.utils.task import (
moa_response_generation_template, rag_template,
tags_generation_template,
query_generation_template,
emoji_generation_template,
title_generation_template, title_generation_template,
query_generation_template,
autocomplete_generation_template,
tags_generation_template,
emoji_generation_template,
moa_response_generation_template,
tools_function_calling_generation_template, tools_function_calling_generation_template,
) )
from open_webui.utils.tools import get_tools from open_webui.utils.tools import get_tools
@@ -204,6 +213,11 @@ app.state.config.TASK_MODEL_EXTERNAL = TASK_MODEL_EXTERNAL
app.state.config.TITLE_GENERATION_PROMPT_TEMPLATE = TITLE_GENERATION_PROMPT_TEMPLATE app.state.config.TITLE_GENERATION_PROMPT_TEMPLATE = TITLE_GENERATION_PROMPT_TEMPLATE
app.state.config.ENABLE_AUTOCOMPLETE_GENERATION = ENABLE_AUTOCOMPLETE_GENERATION
app.state.config.AUTOCOMPLETE_GENERATION_INPUT_MAX_LENGTH = (
AUTOCOMPLETE_GENERATION_INPUT_MAX_LENGTH
)
app.state.config.ENABLE_TAGS_GENERATION = ENABLE_TAGS_GENERATION app.state.config.ENABLE_TAGS_GENERATION = ENABLE_TAGS_GENERATION
app.state.config.TAGS_GENERATION_PROMPT_TEMPLATE = TAGS_GENERATION_PROMPT_TEMPLATE app.state.config.TAGS_GENERATION_PROMPT_TEMPLATE = TAGS_GENERATION_PROMPT_TEMPLATE
@@ -212,6 +226,10 @@ app.state.config.ENABLE_SEARCH_QUERY_GENERATION = ENABLE_SEARCH_QUERY_GENERATION
app.state.config.ENABLE_RETRIEVAL_QUERY_GENERATION = ENABLE_RETRIEVAL_QUERY_GENERATION app.state.config.ENABLE_RETRIEVAL_QUERY_GENERATION = ENABLE_RETRIEVAL_QUERY_GENERATION
app.state.config.QUERY_GENERATION_PROMPT_TEMPLATE = QUERY_GENERATION_PROMPT_TEMPLATE app.state.config.QUERY_GENERATION_PROMPT_TEMPLATE = QUERY_GENERATION_PROMPT_TEMPLATE
app.state.config.AUTOCOMPLETE_GENERATION_PROMPT_TEMPLATE = (
AUTOCOMPLETE_GENERATION_PROMPT_TEMPLATE
)
app.state.config.TOOLS_FUNCTION_CALLING_PROMPT_TEMPLATE = ( app.state.config.TOOLS_FUNCTION_CALLING_PROMPT_TEMPLATE = (
TOOLS_FUNCTION_CALLING_PROMPT_TEMPLATE TOOLS_FUNCTION_CALLING_PROMPT_TEMPLATE
) )
@@ -380,8 +398,7 @@ async def chat_completion_tools_handler(
return body, {} return body, {}
skip_files = False skip_files = False
contexts = [] sources = []
citations = []
task_model_id = get_task_model_id( task_model_id = get_task_model_id(
body["model"], body["model"],
@@ -463,21 +480,39 @@ async def chat_completion_tools_handler(
except Exception as e: except Exception as e:
tool_output = str(e) tool_output = str(e)
if tools[tool_function_name]["citation"]: print(tools[tool_function_name]["citation"])
citations.append(
{
"source": {
"name": f"TOOL:{tools[tool_function_name]['toolkit_id']}/{tool_function_name}"
},
"document": [tool_output],
"metadata": [{"source": tool_function_name}],
}
)
if tools[tool_function_name]["file_handler"]:
skip_files = True
if isinstance(tool_output, str): if isinstance(tool_output, str):
contexts.append(tool_output) if tools[tool_function_name]["citation"]:
sources.append(
{
"source": {
"name": f"TOOL:{tools[tool_function_name]['toolkit_id']}/{tool_function_name}"
},
"document": [tool_output],
"metadata": [
{
"source": f"TOOL:{tools[tool_function_name]['toolkit_id']}/{tool_function_name}"
}
],
}
)
else:
sources.append(
{
"source": {},
"document": [tool_output],
"metadata": [
{
"source": f"TOOL:{tools[tool_function_name]['toolkit_id']}/{tool_function_name}"
}
],
}
)
if tools[tool_function_name]["file_handler"]:
skip_files = True
except Exception as e: except Exception as e:
log.exception(f"Error: {e}") log.exception(f"Error: {e}")
content = None content = None
@@ -485,47 +520,51 @@ async def chat_completion_tools_handler(
log.exception(f"Error: {e}") log.exception(f"Error: {e}")
content = None content = None
log.debug(f"tool_contexts: {contexts}") log.debug(f"tool_contexts: {sources}")
if skip_files and "files" in body.get("metadata", {}): if skip_files and "files" in body.get("metadata", {}):
del body["metadata"]["files"] del body["metadata"]["files"]
return body, {"contexts": contexts, "citations": citations} return body, {"sources": sources}
async def chat_completion_files_handler( async def chat_completion_files_handler(
body: dict, user: UserModel body: dict, user: UserModel
) -> tuple[dict, dict[str, list]]: ) -> tuple[dict, dict[str, list]]:
contexts = [] sources = []
citations = []
try:
queries_response = await generate_queries(
{
"model": body["model"],
"messages": body["messages"],
"type": "retrieval",
},
user,
)
queries_response = queries_response["choices"][0]["message"]["content"]
try:
queries_response = json.loads(queries_response)
except Exception as e:
queries_response = {"queries": []}
queries = queries_response.get("queries", [])
except Exception as e:
queries = []
if len(queries) == 0:
queries = [get_last_user_message(body["messages"])]
print(f"{queries=}")
if files := body.get("metadata", {}).get("files", None): if files := body.get("metadata", {}).get("files", None):
contexts, citations = get_rag_context( try:
queries_response = await generate_queries(
{
"model": body["model"],
"messages": body["messages"],
"type": "retrieval",
},
user,
)
queries_response = queries_response["choices"][0]["message"]["content"]
try:
bracket_start = queries_response.find("{")
bracket_end = queries_response.rfind("}") + 1
if bracket_start == -1 or bracket_end == -1:
raise Exception("No JSON object found in the response")
queries_response = queries_response[bracket_start:bracket_end]
queries_response = json.loads(queries_response)
except Exception as e:
queries_response = {"queries": [queries_response]}
queries = queries_response.get("queries", [])
except Exception as e:
queries = []
if len(queries) == 0:
queries = [get_last_user_message(body["messages"])]
sources = get_sources_from_files(
files=files, files=files,
queries=queries, queries=queries,
embedding_function=retrieval_app.state.EMBEDDING_FUNCTION, embedding_function=retrieval_app.state.EMBEDDING_FUNCTION,
@@ -535,9 +574,8 @@ async def chat_completion_files_handler(
hybrid_search=retrieval_app.state.config.ENABLE_RAG_HYBRID_SEARCH, hybrid_search=retrieval_app.state.config.ENABLE_RAG_HYBRID_SEARCH,
) )
log.debug(f"rag_contexts: {contexts}, citations: {citations}") log.debug(f"rag_contexts:sources: {sources}")
return body, {"sources": sources}
return body, {"contexts": contexts, "citations": citations}
def is_chat_completion_request(request): def is_chat_completion_request(request):
@@ -584,7 +622,7 @@ class ChatCompletionMiddleware(BaseHTTPMiddleware):
) )
model_info = Models.get_model_by_id(model["id"]) model_info = Models.get_model_by_id(model["id"])
if user.role == "user": if user.role == "user" and not BYPASS_MODEL_ACCESS_CONTROL:
if model.get("arena"): if model.get("arena"):
if not has_access( if not has_access(
user.id, user.id,
@@ -638,8 +676,7 @@ class ChatCompletionMiddleware(BaseHTTPMiddleware):
# Initialize data_items to store additional data to be sent to the client # Initialize data_items to store additional data to be sent to the client
# Initialize contexts and citation # Initialize contexts and citation
data_items = [] data_items = []
contexts = [] sources = []
citations = []
try: try:
body, flags = await chat_completion_filter_functions_handler( body, flags = await chat_completion_filter_functions_handler(
@@ -665,21 +702,37 @@ class ChatCompletionMiddleware(BaseHTTPMiddleware):
body, flags = await chat_completion_tools_handler( body, flags = await chat_completion_tools_handler(
body, user, models, extra_params body, user, models, extra_params
) )
contexts.extend(flags.get("contexts", [])) sources.extend(flags.get("sources", []))
citations.extend(flags.get("citations", []))
except Exception as e: except Exception as e:
log.exception(e) log.exception(e)
try: try:
body, flags = await chat_completion_files_handler(body, user) body, flags = await chat_completion_files_handler(body, user)
contexts.extend(flags.get("contexts", [])) sources.extend(flags.get("sources", []))
citations.extend(flags.get("citations", []))
except Exception as e: except Exception as e:
log.exception(e) log.exception(e)
# If context is not empty, insert it into the messages # If context is not empty, insert it into the messages
if len(contexts) > 0: if len(sources) > 0:
context_string = "/n".join(contexts).strip() context_string = ""
for source_idx, source in enumerate(sources):
source_id = source.get("source", {}).get("name", "")
if "document" in source:
for doc_idx, doc_context in enumerate(source["document"]):
metadata = source.get("metadata")
doc_source_id = None
if metadata:
doc_source_id = metadata[doc_idx].get("source", source_id)
if source_id:
context_string += f"<source><source_id>{doc_source_id if doc_source_id is not None else source_id}</source_id><source_context>{doc_context}</source_context></source>\n"
else:
# If there is no source_id, then do not include the source_id tag
context_string += f"<source><source_context>{doc_context}</source_context></source>\n"
context_string = context_string.strip()
prompt = get_last_user_message(body["messages"]) prompt = get_last_user_message(body["messages"])
if prompt is None: if prompt is None:
@@ -710,8 +763,11 @@ class ChatCompletionMiddleware(BaseHTTPMiddleware):
) )
# If there are citations, add them to the data_items # If there are citations, add them to the data_items
if len(citations) > 0: sources = [
data_items.append({"citations": citations}) source for source in sources if source.get("source", {}).get("name", "")
]
if len(sources) > 0:
data_items.append({"sources": sources})
modified_body_bytes = json.dumps(body).encode("utf-8") modified_body_bytes = json.dumps(body).encode("utf-8")
# Replace the request body with the modified one # Replace the request body with the modified one
@@ -855,6 +911,11 @@ class PipelineMiddleware(BaseHTTPMiddleware):
status_code=status.HTTP_401_UNAUTHORIZED, status_code=status.HTTP_401_UNAUTHORIZED,
content={"detail": "Not authenticated"}, content={"detail": "Not authenticated"},
) )
except HTTPException as e:
return JSONResponse(
status_code=e.status_code,
content={"detail": e.detail},
)
model_list = await get_all_models() model_list = await get_all_models()
models = {model["id"]: model for model in model_list} models = {model["id"]: model for model in model_list}
@@ -932,7 +993,7 @@ app.add_middleware(SecurityHeadersMiddleware)
@app.middleware("http") @app.middleware("http")
async def commit_session_after_request(request: Request, call_next): async def commit_session_after_request(request: Request, call_next):
response = await call_next(request) response = await call_next(request)
log.debug("Commit session after request") # log.debug("Commit session after request")
Session.commit() Session.commit()
return response return response
@@ -1015,7 +1076,7 @@ async def get_all_base_models():
return models return models
@cached(ttl=1) @cached(ttl=3)
async def get_all_models(): async def get_all_models():
models = await get_all_base_models() models = await get_all_base_models()
@@ -1139,6 +1200,8 @@ async def get_all_models():
model["actions"].extend( model["actions"].extend(
get_action_items_from_module(action_function, function_module) get_action_items_from_module(action_function, function_module)
) )
log.debug(f"get_all_models() returned {len(models)} models")
return models return models
@@ -1153,8 +1216,16 @@ async def get_models(user=Depends(get_verified_user)):
if "pipeline" not in model or model["pipeline"].get("type", None) != "filter" if "pipeline" not in model or model["pipeline"].get("type", None) != "filter"
] ]
model_order_list = webui_app.state.config.MODEL_ORDER_LIST
if model_order_list:
model_order_dict = {model_id: i for i, model_id in enumerate(model_order_list)}
# Sort models by order list priority, with fallback for those not in the list
models.sort(
key=lambda x: (model_order_dict.get(x["id"], float("inf")), x["name"])
)
# Filter out models that the user does not have access to # Filter out models that the user does not have access to
if user.role == "user": if user.role == "user" and not BYPASS_MODEL_ACCESS_CONTROL:
filtered_models = [] filtered_models = []
for model in models: for model in models:
if model.get("arena"): if model.get("arena"):
@@ -1176,6 +1247,10 @@ async def get_models(user=Depends(get_verified_user)):
filtered_models.append(model) filtered_models.append(model)
models = filtered_models models = filtered_models
log.debug(
f"/api/models returned filtered models accessible to the user: {json.dumps([model['id'] for model in models])}"
)
return {"data": models} return {"data": models}
@@ -1308,7 +1383,6 @@ async def generate_chat_completions(
@app.post("/api/chat/completed") @app.post("/api/chat/completed")
async def chat_completed(form_data: dict, user=Depends(get_verified_user)): async def chat_completed(form_data: dict, user=Depends(get_verified_user)):
model_list = await get_all_models() model_list = await get_all_models()
models = {model["id"]: model for model in model_list} models = {model["id"]: model for model in model_list}
@@ -1606,6 +1680,8 @@ async def get_task_config(user=Depends(get_verified_user)):
"TASK_MODEL": app.state.config.TASK_MODEL, "TASK_MODEL": app.state.config.TASK_MODEL,
"TASK_MODEL_EXTERNAL": app.state.config.TASK_MODEL_EXTERNAL, "TASK_MODEL_EXTERNAL": app.state.config.TASK_MODEL_EXTERNAL,
"TITLE_GENERATION_PROMPT_TEMPLATE": app.state.config.TITLE_GENERATION_PROMPT_TEMPLATE, "TITLE_GENERATION_PROMPT_TEMPLATE": app.state.config.TITLE_GENERATION_PROMPT_TEMPLATE,
"ENABLE_AUTOCOMPLETE_GENERATION": app.state.config.ENABLE_AUTOCOMPLETE_GENERATION,
"AUTOCOMPLETE_GENERATION_INPUT_MAX_LENGTH": app.state.config.AUTOCOMPLETE_GENERATION_INPUT_MAX_LENGTH,
"TAGS_GENERATION_PROMPT_TEMPLATE": app.state.config.TAGS_GENERATION_PROMPT_TEMPLATE, "TAGS_GENERATION_PROMPT_TEMPLATE": app.state.config.TAGS_GENERATION_PROMPT_TEMPLATE,
"ENABLE_TAGS_GENERATION": app.state.config.ENABLE_TAGS_GENERATION, "ENABLE_TAGS_GENERATION": app.state.config.ENABLE_TAGS_GENERATION,
"ENABLE_SEARCH_QUERY_GENERATION": app.state.config.ENABLE_SEARCH_QUERY_GENERATION, "ENABLE_SEARCH_QUERY_GENERATION": app.state.config.ENABLE_SEARCH_QUERY_GENERATION,
@@ -1619,6 +1695,8 @@ class TaskConfigForm(BaseModel):
TASK_MODEL: Optional[str] TASK_MODEL: Optional[str]
TASK_MODEL_EXTERNAL: Optional[str] TASK_MODEL_EXTERNAL: Optional[str]
TITLE_GENERATION_PROMPT_TEMPLATE: str TITLE_GENERATION_PROMPT_TEMPLATE: str
ENABLE_AUTOCOMPLETE_GENERATION: bool
AUTOCOMPLETE_GENERATION_INPUT_MAX_LENGTH: int
TAGS_GENERATION_PROMPT_TEMPLATE: str TAGS_GENERATION_PROMPT_TEMPLATE: str
ENABLE_TAGS_GENERATION: bool ENABLE_TAGS_GENERATION: bool
ENABLE_SEARCH_QUERY_GENERATION: bool ENABLE_SEARCH_QUERY_GENERATION: bool
@@ -1634,6 +1712,14 @@ async def update_task_config(form_data: TaskConfigForm, user=Depends(get_admin_u
app.state.config.TITLE_GENERATION_PROMPT_TEMPLATE = ( app.state.config.TITLE_GENERATION_PROMPT_TEMPLATE = (
form_data.TITLE_GENERATION_PROMPT_TEMPLATE form_data.TITLE_GENERATION_PROMPT_TEMPLATE
) )
app.state.config.ENABLE_AUTOCOMPLETE_GENERATION = (
form_data.ENABLE_AUTOCOMPLETE_GENERATION
)
app.state.config.AUTOCOMPLETE_GENERATION_INPUT_MAX_LENGTH = (
form_data.AUTOCOMPLETE_GENERATION_INPUT_MAX_LENGTH
)
app.state.config.TAGS_GENERATION_PROMPT_TEMPLATE = ( app.state.config.TAGS_GENERATION_PROMPT_TEMPLATE = (
form_data.TAGS_GENERATION_PROMPT_TEMPLATE form_data.TAGS_GENERATION_PROMPT_TEMPLATE
) )
@@ -1656,6 +1742,8 @@ async def update_task_config(form_data: TaskConfigForm, user=Depends(get_admin_u
"TASK_MODEL": app.state.config.TASK_MODEL, "TASK_MODEL": app.state.config.TASK_MODEL,
"TASK_MODEL_EXTERNAL": app.state.config.TASK_MODEL_EXTERNAL, "TASK_MODEL_EXTERNAL": app.state.config.TASK_MODEL_EXTERNAL,
"TITLE_GENERATION_PROMPT_TEMPLATE": app.state.config.TITLE_GENERATION_PROMPT_TEMPLATE, "TITLE_GENERATION_PROMPT_TEMPLATE": app.state.config.TITLE_GENERATION_PROMPT_TEMPLATE,
"ENABLE_AUTOCOMPLETE_GENERATION": app.state.config.ENABLE_AUTOCOMPLETE_GENERATION,
"AUTOCOMPLETE_GENERATION_INPUT_MAX_LENGTH": app.state.config.AUTOCOMPLETE_GENERATION_INPUT_MAX_LENGTH,
"TAGS_GENERATION_PROMPT_TEMPLATE": app.state.config.TAGS_GENERATION_PROMPT_TEMPLATE, "TAGS_GENERATION_PROMPT_TEMPLATE": app.state.config.TAGS_GENERATION_PROMPT_TEMPLATE,
"ENABLE_TAGS_GENERATION": app.state.config.ENABLE_TAGS_GENERATION, "ENABLE_TAGS_GENERATION": app.state.config.ENABLE_TAGS_GENERATION,
"ENABLE_SEARCH_QUERY_GENERATION": app.state.config.ENABLE_SEARCH_QUERY_GENERATION, "ENABLE_SEARCH_QUERY_GENERATION": app.state.config.ENABLE_SEARCH_QUERY_GENERATION,
@@ -1667,7 +1755,6 @@ async def update_task_config(form_data: TaskConfigForm, user=Depends(get_admin_u
@app.post("/api/task/title/completions") @app.post("/api/task/title/completions")
async def generate_title(form_data: dict, user=Depends(get_verified_user)): async def generate_title(form_data: dict, user=Depends(get_verified_user)):
print("generate_title")
model_list = await get_all_models() model_list = await get_all_models()
models = {model["id"]: model for model in model_list} models = {model["id"]: model for model in model_list}
@@ -1688,9 +1775,9 @@ async def generate_title(form_data: dict, user=Depends(get_verified_user)):
models, models,
) )
print(task_model_id) log.debug(
f"generating chat title using model {task_model_id} for user {user.email} "
model = models[task_model_id] )
if app.state.config.TITLE_GENERATION_PROMPT_TEMPLATE != "": if app.state.config.TITLE_GENERATION_PROMPT_TEMPLATE != "":
template = app.state.config.TITLE_GENERATION_PROMPT_TEMPLATE template = app.state.config.TITLE_GENERATION_PROMPT_TEMPLATE
@@ -1729,10 +1816,12 @@ Artificial Intelligence in Healthcare
"max_completion_tokens": 50, "max_completion_tokens": 50,
} }
), ),
"chat_id": form_data.get("chat_id", None), "metadata": {
"metadata": {"task": str(TASKS.TITLE_GENERATION), "task_body": form_data}, "task": str(TASKS.TITLE_GENERATION),
"task_body": form_data,
"chat_id": form_data.get("chat_id", None),
},
} }
log.debug(payload)
# Handle pipeline filters # Handle pipeline filters
try: try:
@@ -1756,7 +1845,7 @@ Artificial Intelligence in Healthcare
@app.post("/api/task/tags/completions") @app.post("/api/task/tags/completions")
async def generate_chat_tags(form_data: dict, user=Depends(get_verified_user)): async def generate_chat_tags(form_data: dict, user=Depends(get_verified_user)):
print("generate_chat_tags")
if not app.state.config.ENABLE_TAGS_GENERATION: if not app.state.config.ENABLE_TAGS_GENERATION:
return JSONResponse( return JSONResponse(
status_code=status.HTTP_200_OK, status_code=status.HTTP_200_OK,
@@ -1781,7 +1870,10 @@ async def generate_chat_tags(form_data: dict, user=Depends(get_verified_user)):
app.state.config.TASK_MODEL_EXTERNAL, app.state.config.TASK_MODEL_EXTERNAL,
models, models,
) )
print(task_model_id)
log.debug(
f"generating chat tags using model {task_model_id} for user {user.email} "
)
if app.state.config.TAGS_GENERATION_PROMPT_TEMPLATE != "": if app.state.config.TAGS_GENERATION_PROMPT_TEMPLATE != "":
template = app.state.config.TAGS_GENERATION_PROMPT_TEMPLATE template = app.state.config.TAGS_GENERATION_PROMPT_TEMPLATE
@@ -1812,9 +1904,12 @@ JSON format: { "tags": ["tag1", "tag2", "tag3"] }
"model": task_model_id, "model": task_model_id,
"messages": [{"role": "user", "content": content}], "messages": [{"role": "user", "content": content}],
"stream": False, "stream": False,
"metadata": {"task": str(TASKS.TAGS_GENERATION), "task_body": form_data}, "metadata": {
"task": str(TASKS.TAGS_GENERATION),
"task_body": form_data,
"chat_id": form_data.get("chat_id", None),
},
} }
log.debug(payload)
# Handle pipeline filters # Handle pipeline filters
try: try:
@@ -1838,7 +1933,7 @@ JSON format: { "tags": ["tag1", "tag2", "tag3"] }
@app.post("/api/task/queries/completions") @app.post("/api/task/queries/completions")
async def generate_queries(form_data: dict, user=Depends(get_verified_user)): async def generate_queries(form_data: dict, user=Depends(get_verified_user)):
print("generate_queries")
type = form_data.get("type") type = form_data.get("type")
if type == "web_search": if type == "web_search":
if not app.state.config.ENABLE_SEARCH_QUERY_GENERATION: if not app.state.config.ENABLE_SEARCH_QUERY_GENERATION:
@@ -1871,11 +1966,12 @@ async def generate_queries(form_data: dict, user=Depends(get_verified_user)):
app.state.config.TASK_MODEL_EXTERNAL, app.state.config.TASK_MODEL_EXTERNAL,
models, models,
) )
print(task_model_id)
model = models[task_model_id] log.debug(
f"generating {type} queries using model {task_model_id} for user {user.email}"
)
if app.state.config.QUERY_GENERATION_PROMPT_TEMPLATE != "": if (app.state.config.QUERY_GENERATION_PROMPT_TEMPLATE).strip() != "":
template = app.state.config.QUERY_GENERATION_PROMPT_TEMPLATE template = app.state.config.QUERY_GENERATION_PROMPT_TEMPLATE
else: else:
template = DEFAULT_QUERY_GENERATION_PROMPT_TEMPLATE template = DEFAULT_QUERY_GENERATION_PROMPT_TEMPLATE
@@ -1888,9 +1984,96 @@ async def generate_queries(form_data: dict, user=Depends(get_verified_user)):
"model": task_model_id, "model": task_model_id,
"messages": [{"role": "user", "content": content}], "messages": [{"role": "user", "content": content}],
"stream": False, "stream": False,
"metadata": {"task": str(TASKS.QUERY_GENERATION), "task_body": form_data}, "metadata": {
"task": str(TASKS.QUERY_GENERATION),
"task_body": form_data,
"chat_id": form_data.get("chat_id", None),
},
} }
log.debug(payload)
# Handle pipeline filters
try:
payload = filter_pipeline(payload, user, models)
except Exception as e:
if len(e.args) > 1:
return JSONResponse(
status_code=e.args[0],
content={"detail": e.args[1]},
)
else:
return JSONResponse(
status_code=status.HTTP_400_BAD_REQUEST,
content={"detail": str(e)},
)
if "chat_id" in payload:
del payload["chat_id"]
return await generate_chat_completions(form_data=payload, user=user)
@app.post("/api/task/auto/completions")
async def generate_autocompletion(form_data: dict, user=Depends(get_verified_user)):
if not app.state.config.ENABLE_AUTOCOMPLETE_GENERATION:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=f"Autocompletion generation is disabled",
)
type = form_data.get("type")
prompt = form_data.get("prompt")
messages = form_data.get("messages")
if app.state.config.AUTOCOMPLETE_GENERATION_INPUT_MAX_LENGTH > 0:
if len(prompt) > app.state.config.AUTOCOMPLETE_GENERATION_INPUT_MAX_LENGTH:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=f"Input prompt exceeds maximum length of {app.state.config.AUTOCOMPLETE_GENERATION_INPUT_MAX_LENGTH}",
)
model_list = await get_all_models()
models = {model["id"]: model for model in model_list}
model_id = form_data["model"]
if model_id not in models:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Model not found",
)
# Check if the user has a custom task model
# If the user has a custom task model, use that model
task_model_id = get_task_model_id(
model_id,
app.state.config.TASK_MODEL,
app.state.config.TASK_MODEL_EXTERNAL,
models,
)
log.debug(
f"generating autocompletion using model {task_model_id} for user {user.email}"
)
if (app.state.config.AUTOCOMPLETE_GENERATION_PROMPT_TEMPLATE).strip() != "":
template = app.state.config.AUTOCOMPLETE_GENERATION_PROMPT_TEMPLATE
else:
template = DEFAULT_AUTOCOMPLETE_GENERATION_PROMPT_TEMPLATE
content = autocomplete_generation_template(
template, prompt, messages, type, {"name": user.name}
)
payload = {
"model": task_model_id,
"messages": [{"role": "user", "content": content}],
"stream": False,
"metadata": {
"task": str(TASKS.AUTOCOMPLETE_GENERATION),
"task_body": form_data,
"chat_id": form_data.get("chat_id", None),
},
}
print(payload)
# Handle pipeline filters # Handle pipeline filters
try: try:
@@ -1914,7 +2097,6 @@ async def generate_queries(form_data: dict, user=Depends(get_verified_user)):
@app.post("/api/task/emoji/completions") @app.post("/api/task/emoji/completions")
async def generate_emoji(form_data: dict, user=Depends(get_verified_user)): async def generate_emoji(form_data: dict, user=Depends(get_verified_user)):
print("generate_emoji")
model_list = await get_all_models() model_list = await get_all_models()
models = {model["id"]: model for model in model_list} models = {model["id"]: model for model in model_list}
@@ -1934,9 +2116,8 @@ async def generate_emoji(form_data: dict, user=Depends(get_verified_user)):
app.state.config.TASK_MODEL_EXTERNAL, app.state.config.TASK_MODEL_EXTERNAL,
models, models,
) )
print(task_model_id)
model = models[task_model_id] log.debug(f"generating emoji using model {task_model_id} for user {user.email} ")
template = ''' template = '''
Your task is to reflect the speaker's likely facial expression through a fitting emoji. Interpret emotions from the message and reflect their facial expression using fitting, diverse emojis (e.g., 😊, 😢, 😡, 😱). Your task is to reflect the speaker's likely facial expression through a fitting emoji. Interpret emotions from the message and reflect their facial expression using fitting, diverse emojis (e.g., 😊, 😢, 😡, 😱).
@@ -1966,7 +2147,6 @@ Message: """{{prompt}}"""
"chat_id": form_data.get("chat_id", None), "chat_id": form_data.get("chat_id", None),
"metadata": {"task": str(TASKS.EMOJI_GENERATION), "task_body": form_data}, "metadata": {"task": str(TASKS.EMOJI_GENERATION), "task_body": form_data},
} }
log.debug(payload)
# Handle pipeline filters # Handle pipeline filters
try: try:
@@ -1990,7 +2170,6 @@ Message: """{{prompt}}"""
@app.post("/api/task/moa/completions") @app.post("/api/task/moa/completions")
async def generate_moa_response(form_data: dict, user=Depends(get_verified_user)): async def generate_moa_response(form_data: dict, user=Depends(get_verified_user)):
print("generate_moa_response")
model_list = await get_all_models() model_list = await get_all_models()
models = {model["id"]: model for model in model_list} models = {model["id"]: model for model in model_list}
@@ -2010,9 +2189,8 @@ async def generate_moa_response(form_data: dict, user=Depends(get_verified_user)
app.state.config.TASK_MODEL_EXTERNAL, app.state.config.TASK_MODEL_EXTERNAL,
models, models,
) )
print(task_model_id)
model = models[task_model_id] log.debug(f"generating MOA model {task_model_id} for user {user.email} ")
template = """You have been provided with a set of responses from various models to the latest user query: "{{prompt}}" template = """You have been provided with a set of responses from various models to the latest user query: "{{prompt}}"
@@ -2036,7 +2214,6 @@ Responses from models: {{responses}}"""
"task_body": form_data, "task_body": form_data,
}, },
} }
log.debug(payload)
try: try:
payload = filter_pipeline(payload, user, models) payload = filter_pipeline(payload, user, models)
@@ -2071,7 +2248,7 @@ Responses from models: {{responses}}"""
async def get_pipelines_list(user=Depends(get_admin_user)): async def get_pipelines_list(user=Depends(get_admin_user)):
responses = await get_openai_models_responses() responses = await get_openai_models_responses()
print(responses) log.debug(f"get_pipelines_list: get_openai_models_responses returned {responses}")
urlIdxs = [ urlIdxs = [
idx idx
for idx, response in enumerate(responses) for idx, response in enumerate(responses)
-112
View File
@@ -1,112 +0,0 @@
from ast import literal_eval
from typing import Any, Literal, Optional, Type
from pydantic import BaseModel, Field, create_model
def json_schema_to_model(tool_dict: dict[str, Any]) -> Type[BaseModel]:
"""
Converts a JSON schema to a Pydantic BaseModel class.
Args:
json_schema: The JSON schema to convert.
Returns:
A Pydantic BaseModel class.
"""
# Extract the model name from the schema title.
model_name = tool_dict["name"]
schema = tool_dict["parameters"]
# Extract the field definitions from the schema properties.
field_definitions = {
name: json_schema_to_pydantic_field(name, prop, schema.get("required", []))
for name, prop in schema.get("properties", {}).items()
}
# Create the BaseModel class using create_model().
return create_model(model_name, **field_definitions)
def json_schema_to_pydantic_field(
name: str, json_schema: dict[str, Any], required: list[str]
) -> Any:
"""
Converts a JSON schema property to a Pydantic field definition.
Args:
name: The field name.
json_schema: The JSON schema property.
Returns:
A Pydantic field definition.
"""
# Get the field type.
type_ = json_schema_to_pydantic_type(json_schema)
# Get the field description.
description = json_schema.get("description")
# Get the field examples.
examples = json_schema.get("examples")
# Create a Field object with the type, description, and examples.
# The 'required' flag will be set later when creating the model.
return (
type_,
Field(
description=description,
examples=examples,
default=... if name in required else None,
),
)
def json_schema_to_pydantic_type(json_schema: dict[str, Any]) -> Any:
"""
Converts a JSON schema type to a Pydantic type.
Args:
json_schema: The JSON schema to convert.
Returns:
A Pydantic type.
"""
type_ = json_schema.get("type")
if type_ == "string" or type_ == "str":
return str
elif type_ == "integer" or type_ == "int":
return int
elif type_ == "number" or type_ == "float":
return float
elif type_ == "boolean" or type_ == "bool":
return bool
elif type_ == "array" or type_ == "list":
items_schema = json_schema.get("items")
if items_schema:
item_type = json_schema_to_pydantic_type(items_schema)
return list[item_type]
else:
return list
elif type_ == "object":
# Handle nested models.
properties = json_schema.get("properties")
if properties:
nested_model = json_schema_to_model(json_schema)
return nested_model
else:
return dict
elif type_ == "null":
return Optional[Any] # Use Optional[Any] for nullable fields
elif type_ == "literal":
return Literal[literal_eval(json_schema.get("enum"))]
elif type_ == "optional":
inner_schema = json_schema.get("items", {"type": "string"})
inner_type = json_schema_to_pydantic_type(inner_schema)
return Optional[inner_type]
else:
raise ValueError(f"Unsupported JSON schema type: {type_}")
@@ -27,6 +27,7 @@ def set_security_headers() -> Dict[str, str]:
- x-download-options - x-download-options
- x-frame-options - x-frame-options
- x-permitted-cross-domain-policies - x-permitted-cross-domain-policies
- content-security-policy
Each environment variable is associated with a specific setter function Each environment variable is associated with a specific setter function
that constructs the header. If the environment variable is set, the that constructs the header. If the environment variable is set, the
@@ -45,6 +46,7 @@ def set_security_headers() -> Dict[str, str]:
"XDOWNLOAD_OPTIONS": set_xdownload_options, "XDOWNLOAD_OPTIONS": set_xdownload_options,
"XFRAME_OPTIONS": set_xframe, "XFRAME_OPTIONS": set_xframe,
"XPERMITTED_CROSS_DOMAIN_POLICIES": set_xpermitted_cross_domain_policies, "XPERMITTED_CROSS_DOMAIN_POLICIES": set_xpermitted_cross_domain_policies,
"CONTENT_SECURITY_POLICY": set_content_security_policy,
} }
for env_var, setter in header_setters.items(): for env_var, setter in header_setters.items():
@@ -124,3 +126,8 @@ def set_xpermitted_cross_domain_policies(value: str):
if not match: if not match:
value = "none" value = "none"
return {"X-Permitted-Cross-Domain-Policies": value} return {"X-Permitted-Cross-Domain-Policies": value}
# Set Content-Security-Policy response header
def set_content_security_policy(value: str):
return {"Content-Security-Policy": value}
+83 -7
View File
@@ -1,11 +1,20 @@
import logging
import math import math
import re import re
from datetime import datetime from datetime import datetime
from typing import Optional from typing import Optional
import uuid
from open_webui.utils.misc import get_last_user_message, get_messages_content from open_webui.utils.misc import get_last_user_message, get_messages_content
from open_webui.env import SRC_LOG_LEVELS
from open_webui.config import DEFAULT_RAG_TEMPLATE
log = logging.getLogger(__name__)
log.setLevel(SRC_LOG_LEVELS["RAG"])
def prompt_template( def prompt_template(
template: str, user_name: Optional[str] = None, user_location: Optional[str] = None template: str, user_name: Optional[str] = None, user_location: Optional[str] = None
@@ -16,12 +25,14 @@ def prompt_template(
# Format the date to YYYY-MM-DD # Format the date to YYYY-MM-DD
formatted_date = current_date.strftime("%Y-%m-%d") formatted_date = current_date.strftime("%Y-%m-%d")
formatted_time = current_date.strftime("%I:%M:%S %p") formatted_time = current_date.strftime("%I:%M:%S %p")
formatted_weekday = current_date.strftime("%A")
template = template.replace("{{CURRENT_DATE}}", formatted_date) template = template.replace("{{CURRENT_DATE}}", formatted_date)
template = template.replace("{{CURRENT_TIME}}", formatted_time) template = template.replace("{{CURRENT_TIME}}", formatted_time)
template = template.replace( template = template.replace(
"{{CURRENT_DATETIME}}", f"{formatted_date} {formatted_time}" "{{CURRENT_DATETIME}}", f"{formatted_date} {formatted_time}"
) )
template = template.replace("{{CURRENT_WEEKDAY}}", formatted_weekday)
if user_name: if user_name:
# Replace {{USER_NAME}} in the template with the user's name # Replace {{USER_NAME}} in the template with the user's name
@@ -42,7 +53,9 @@ def prompt_template(
def replace_prompt_variable(template: str, prompt: str) -> str: def replace_prompt_variable(template: str, prompt: str) -> str:
def replacement_function(match): def replacement_function(match):
full_match = match.group(0) full_match = match.group(
0
).lower() # Normalize to lowercase for consistent handling
start_length = match.group(1) start_length = match.group(1)
end_length = match.group(2) end_length = match.group(2)
middle_length = match.group(3) middle_length = match.group(3)
@@ -62,20 +75,23 @@ def replace_prompt_variable(template: str, prompt: str) -> str:
return f"{start}...{end}" return f"{start}...{end}"
return "" return ""
template = re.sub( # Updated regex pattern to make it case-insensitive with the `(?i)` flag
r"{{prompt}}|{{prompt:start:(\d+)}}|{{prompt:end:(\d+)}}|{{prompt:middletruncate:(\d+)}}", pattern = r"(?i){{prompt}}|{{prompt:start:(\d+)}}|{{prompt:end:(\d+)}}|{{prompt:middletruncate:(\d+)}}"
replacement_function, template = re.sub(pattern, replacement_function, template)
template,
)
return template return template
def replace_messages_variable(template: str, messages: list[str]) -> str: def replace_messages_variable(
template: str, messages: Optional[list[str]] = None
) -> str:
def replacement_function(match): def replacement_function(match):
full_match = match.group(0) full_match = match.group(0)
start_length = match.group(1) start_length = match.group(1)
end_length = match.group(2) end_length = match.group(2)
middle_length = match.group(3) middle_length = match.group(3)
# If messages is None, handle it as an empty list
if messages is None:
return ""
# Process messages based on the number of messages required # Process messages based on the number of messages required
if full_match == "{{MESSAGES}}": if full_match == "{{MESSAGES}}":
@@ -110,6 +126,44 @@ def replace_messages_variable(template: str, messages: list[str]) -> str:
# {{prompt:middletruncate:8000}} # {{prompt:middletruncate:8000}}
def rag_template(template: str, context: str, query: str):
if template.strip() == "":
template = DEFAULT_RAG_TEMPLATE
if "[context]" not in template and "{{CONTEXT}}" not in template:
log.debug(
"WARNING: The RAG template does not contain the '[context]' or '{{CONTEXT}}' placeholder."
)
if "<context>" in context and "</context>" in context:
log.debug(
"WARNING: Potential prompt injection attack: the RAG "
"context contains '<context>' and '</context>'. This might be "
"nothing, or the user might be trying to hack something."
)
query_placeholders = []
if "[query]" in context:
query_placeholder = "{{QUERY" + str(uuid.uuid4()) + "}}"
template = template.replace("[query]", query_placeholder)
query_placeholders.append(query_placeholder)
if "{{QUERY}}" in context:
query_placeholder = "{{QUERY" + str(uuid.uuid4()) + "}}"
template = template.replace("{{QUERY}}", query_placeholder)
query_placeholders.append(query_placeholder)
template = template.replace("[context]", context)
template = template.replace("{{CONTEXT}}", context)
template = template.replace("[query]", query)
template = template.replace("{{QUERY}}", query)
for query_placeholder in query_placeholders:
template = template.replace(query_placeholder, query)
return template
def title_generation_template( def title_generation_template(
template: str, messages: list[dict], user: Optional[dict] = None template: str, messages: list[dict], user: Optional[dict] = None
) -> str: ) -> str:
@@ -163,6 +217,28 @@ def emoji_generation_template(
return template return template
def autocomplete_generation_template(
template: str,
prompt: str,
messages: Optional[list[dict]] = None,
type: Optional[str] = None,
user: Optional[dict] = None,
) -> str:
template = template.replace("{{TYPE}}", type if type else "")
template = replace_prompt_variable(template, prompt)
template = replace_messages_variable(template, messages)
template = prompt_template(
template,
**(
{"user_name": user.get("name"), "user_location": user.get("location")}
if user
else {}
),
)
return template
def query_generation_template( def query_generation_template(
template: str, messages: list[dict], user: Optional[dict] = None template: str, messages: list[dict], user: Optional[dict] = None
) -> str: ) -> str:
+112 -87
View File
@@ -1,11 +1,14 @@
import inspect import inspect
import logging import logging
from typing import Awaitable, Callable, get_type_hints import re
from typing import Any, Awaitable, Callable, get_type_hints
from functools import update_wrapper, partial
from langchain_core.utils.function_calling import convert_to_openai_function
from open_webui.apps.webui.models.tools import Tools from open_webui.apps.webui.models.tools import Tools
from open_webui.apps.webui.models.users import UserModel from open_webui.apps.webui.models.users import UserModel
from open_webui.apps.webui.utils import load_tools_module_by_id from open_webui.apps.webui.utils import load_tools_module_by_id
from open_webui.utils.schemas import json_schema_to_model from pydantic import BaseModel, Field, create_model
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@@ -14,17 +17,16 @@ def apply_extra_params_to_tool_function(
function: Callable, extra_params: dict function: Callable, extra_params: dict
) -> Callable[..., Awaitable]: ) -> Callable[..., Awaitable]:
sig = inspect.signature(function) sig = inspect.signature(function)
extra_params = { extra_params = {k: v for k, v in extra_params.items() if k in sig.parameters}
key: value for key, value in extra_params.items() if key in sig.parameters partial_func = partial(function, **extra_params)
} if inspect.iscoroutinefunction(function):
is_coroutine = inspect.iscoroutinefunction(function) update_wrapper(partial_func, function)
return partial_func
async def new_function(**kwargs): async def new_function(*args, **kwargs):
extra_kwargs = kwargs | extra_params return partial_func(*args, **kwargs)
if is_coroutine:
return await function(**extra_kwargs)
return function(**extra_kwargs)
update_wrapper(new_function, function)
return new_function return new_function
@@ -55,11 +57,6 @@ def get_tools(
) )
for spec in tools.specs: for spec in tools.specs:
# TODO: Fix hack for OpenAI API
for val in spec.get("parameters", {}).get("properties", {}).values():
if val["type"] == "str":
val["type"] = "string"
# Remove internal parameters # Remove internal parameters
spec["parameters"]["properties"] = { spec["parameters"]["properties"] = {
key: val key: val
@@ -72,15 +69,12 @@ def get_tools(
# convert to function that takes only model params and inserts custom params # convert to function that takes only model params and inserts custom params
original_func = getattr(module, function_name) original_func = getattr(module, function_name)
callable = apply_extra_params_to_tool_function(original_func, extra_params) callable = apply_extra_params_to_tool_function(original_func, extra_params)
if hasattr(original_func, "__doc__"):
callable.__doc__ = original_func.__doc__
# TODO: This needs to be a pydantic model # TODO: This needs to be a pydantic model
tool_dict = { tool_dict = {
"toolkit_id": tool_id, "toolkit_id": tool_id,
"callable": callable, "callable": callable,
"spec": spec, "spec": spec,
"pydantic_model": json_schema_to_model(spec), "pydantic_model": function_to_pydantic_model(callable),
"file_handler": hasattr(module, "file_handler") and module.file_handler, "file_handler": hasattr(module, "file_handler") and module.file_handler,
"citation": hasattr(module, "citation") and module.citation, "citation": hasattr(module, "citation") and module.citation,
} }
@@ -96,78 +90,109 @@ def get_tools(
return tools_dict return tools_dict
def doc_to_dict(docstring): def parse_description(docstring: str | None) -> str:
lines = docstring.split("\n") """
description = lines[1].strip() Parse a function's docstring to extract the description.
param_dict = {}
Args:
docstring (str): The docstring to parse.
Returns:
str: The description.
"""
if not docstring:
return ""
lines = [line.strip() for line in docstring.strip().split("\n")]
description_lines: list[str] = []
for line in lines: for line in lines:
if ":param" in line: if re.match(r":param", line) or re.match(r":return", line):
line = line.replace(":param", "").strip() break
param, desc = line.split(":", 1)
param_dict[param.strip()] = desc.strip() description_lines.append(line)
ret_dict = {"description": description, "params": param_dict}
return ret_dict return "\n".join(description_lines)
def get_tools_specs(tools) -> list[dict]: def parse_docstring(docstring):
function_list = [ """
{"name": func, "function": getattr(tools, func)} Parse a function's docstring to extract parameter descriptions in reST format.
for func in dir(tools)
if callable(getattr(tools, func)) Args:
docstring (str): The docstring to parse.
Returns:
dict: A dictionary where keys are parameter names and values are descriptions.
"""
if not docstring:
return {}
# Regex to match `:param name: description` format
param_pattern = re.compile(r":param (\w+):\s*(.+)")
param_descriptions = {}
for line in docstring.splitlines():
match = param_pattern.match(line.strip())
if not match:
continue
param_name, param_description = match.groups()
if param_name.startswith("__"):
continue
param_descriptions[param_name] = param_description
return param_descriptions
def function_to_pydantic_model(func: Callable) -> type[BaseModel]:
"""
Converts a Python function's type hints and docstring to a Pydantic model,
including support for nested types, default values, and descriptions.
Args:
func: The function whose type hints and docstring should be converted.
model_name: The name of the generated Pydantic model.
Returns:
A Pydantic model class.
"""
type_hints = get_type_hints(func)
signature = inspect.signature(func)
parameters = signature.parameters
docstring = func.__doc__
descriptions = parse_docstring(docstring)
tool_description = parse_description(docstring)
field_defs = {}
for name, param in parameters.items():
type_hint = type_hints.get(name, Any)
default_value = param.default if param.default is not param.empty else ...
description = descriptions.get(name, None)
if not description:
field_defs[name] = type_hint, default_value
continue
field_defs[name] = type_hint, Field(default_value, description=description)
model = create_model(func.__name__, **field_defs)
model.__doc__ = tool_description
return model
def get_callable_attributes(tool: object) -> list[Callable]:
return [
getattr(tool, func)
for func in dir(tool)
if callable(getattr(tool, func))
and not func.startswith("__") and not func.startswith("__")
and not inspect.isclass(getattr(tools, func)) and not inspect.isclass(getattr(tool, func))
] ]
specs = []
for function_item in function_list:
function_name = function_item["name"]
function = function_item["function"]
function_doc = doc_to_dict(function.__doc__ or function_name) def get_tools_specs(tool_class: object) -> list[dict]:
specs.append( function_list = get_callable_attributes(tool_class)
{ models = map(function_to_pydantic_model, function_list)
"name": function_name, return [convert_to_openai_function(tool) for tool in models]
# TODO: multi-line desc?
"description": function_doc.get("description", function_name),
"parameters": {
"type": "object",
"properties": {
param_name: {
"type": param_annotation.__name__.lower(),
**(
{
"enum": (
str(param_annotation.__args__)
if hasattr(param_annotation, "__args__")
else None
)
}
if hasattr(param_annotation, "__args__")
else {}
),
"description": function_doc.get("params", {}).get(
param_name, param_name
),
}
for param_name, param_annotation in get_type_hints(
function
).items()
if param_name != "return"
and not (
param_name.startswith("__") and param_name.endswith("__")
)
},
"required": [
name
for name, param in inspect.signature(
function
).parameters.items()
if param.default is param.empty
and not (name.startswith("__") and name.endswith("__"))
],
},
}
)
return specs
+9 -8
View File
@@ -1,7 +1,7 @@
fastapi==0.111.0 fastapi==0.111.0
uvicorn[standard]==0.30.6 uvicorn[standard]==0.30.6
pydantic==2.9.2 pydantic==2.9.2
python-multipart==0.0.17 python-multipart==0.0.18
Flask==3.0.3 Flask==3.0.3
Flask-Cors==5.0.0 Flask-Cors==5.0.0
@@ -11,12 +11,13 @@ python-jose==3.3.0
passlib[bcrypt]==1.7.4 passlib[bcrypt]==1.7.4
requests==2.32.3 requests==2.32.3
aiohttp==3.10.8 aiohttp==3.11.8
async-timeout async-timeout
aiocache aiocache
aiofiles
sqlalchemy==2.0.32 sqlalchemy==2.0.32
alembic==1.13.2 alembic==1.14.0
peewee==3.17.6 peewee==3.17.6
peewee-migrate==1.12.2 peewee-migrate==1.12.2
psycopg2-binary==2.9.9 psycopg2-binary==2.9.9
@@ -37,17 +38,17 @@ anthropic
google-generativeai==0.7.2 google-generativeai==0.7.2
tiktoken tiktoken
langchain==0.3.5 langchain==0.3.7
langchain-community==0.3.3 langchain-community==0.3.7
langchain-chroma==0.1.4 langchain-chroma==0.1.4
fake-useragent==1.5.1 fake-useragent==1.5.1
chromadb==0.5.15 chromadb==0.5.15
pymilvus==2.4.9 pymilvus==2.5.0
qdrant-client~=1.12.0 qdrant-client~=1.12.0
opensearch-py==2.7.1 opensearch-py==2.7.1
sentence-transformers==3.2.0 sentence-transformers==3.3.1
colbert-ai==0.2.21 colbert-ai==0.2.21
einops==0.8.0 einops==0.8.0
@@ -82,7 +83,7 @@ authlib==1.3.2
black==24.8.0 black==24.8.0
langfuse==2.44.0 langfuse==2.44.0
youtube-transcript-api==0.6.2 youtube-transcript-api==0.6.3
pytube==15.0.0 pytube==15.0.0
extract_msg extract_msg
+974 -39
View File
File diff suppressed because it is too large Load Diff
+9 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "open-webui", "name": "open-webui",
"version": "0.4.0", "version": "0.4.8",
"private": true, "private": true,
"scripts": { "scripts": {
"dev": "npm run pyodide:fetch && vite dev --host", "dev": "npm run pyodide:fetch && vite dev --host",
@@ -37,6 +37,7 @@
"postcss": "^8.4.31", "postcss": "^8.4.31",
"prettier": "^3.3.3", "prettier": "^3.3.3",
"prettier-plugin-svelte": "^3.2.6", "prettier-plugin-svelte": "^3.2.6",
"sass-embedded": "^1.81.0",
"svelte": "^4.2.18", "svelte": "^4.2.18",
"svelte-check": "^3.8.5", "svelte-check": "^3.8.5",
"svelte-confetti": "^1.3.2", "svelte-confetti": "^1.3.2",
@@ -56,6 +57,13 @@
"@mediapipe/tasks-vision": "^0.10.17", "@mediapipe/tasks-vision": "^0.10.17",
"@pyscript/core": "^0.4.32", "@pyscript/core": "^0.4.32",
"@sveltejs/adapter-node": "^2.0.0", "@sveltejs/adapter-node": "^2.0.0",
"@tiptap/core": "^2.10.0",
"@tiptap/extension-code-block-lowlight": "^2.10.0",
"@tiptap/extension-highlight": "^2.10.0",
"@tiptap/extension-placeholder": "^2.10.0",
"@tiptap/extension-typography": "^2.10.0",
"@tiptap/pm": "^2.10.0",
"@tiptap/starter-kit": "^2.10.0",
"@xyflow/svelte": "^0.1.19", "@xyflow/svelte": "^0.1.19",
"async": "^3.2.5", "async": "^3.2.5",
"bits-ui": "^0.19.7", "bits-ui": "^0.19.7",
+9 -8
View File
@@ -9,7 +9,7 @@ dependencies = [
"fastapi==0.111.0", "fastapi==0.111.0",
"uvicorn[standard]==0.30.6", "uvicorn[standard]==0.30.6",
"pydantic==2.9.2", "pydantic==2.9.2",
"python-multipart==0.0.17", "python-multipart==0.0.18",
"Flask==3.0.3", "Flask==3.0.3",
"Flask-Cors==5.0.0", "Flask-Cors==5.0.0",
@@ -19,12 +19,13 @@ dependencies = [
"passlib[bcrypt]==1.7.4", "passlib[bcrypt]==1.7.4",
"requests==2.32.3", "requests==2.32.3",
"aiohttp==3.10.8", "aiohttp==3.11.8",
"async-timeout", "async-timeout",
"aiocache", "aiocache",
"aiofiles",
"sqlalchemy==2.0.32", "sqlalchemy==2.0.32",
"alembic==1.13.2", "alembic==1.14.0",
"peewee==3.17.6", "peewee==3.17.6",
"peewee-migrate==1.12.2", "peewee-migrate==1.12.2",
"psycopg2-binary==2.9.9", "psycopg2-binary==2.9.9",
@@ -44,17 +45,17 @@ dependencies = [
"google-generativeai==0.7.2", "google-generativeai==0.7.2",
"tiktoken", "tiktoken",
"langchain==0.3.5", "langchain==0.3.7",
"langchain-community==0.3.3", "langchain-community==0.3.7",
"langchain-chroma==0.1.4", "langchain-chroma==0.1.4",
"fake-useragent==1.5.1", "fake-useragent==1.5.1",
"chromadb==0.5.15", "chromadb==0.5.15",
"pymilvus==2.4.9", "pymilvus==2.5.0",
"qdrant-client~=1.12.0", "qdrant-client~=1.12.0",
"opensearch-py==2.7.1", "opensearch-py==2.7.1",
"sentence-transformers==3.2.0", "sentence-transformers==3.3.1",
"colbert-ai==0.2.21", "colbert-ai==0.2.21",
"einops==0.8.0", "einops==0.8.0",
@@ -88,7 +89,7 @@ dependencies = [
"black==24.8.0", "black==24.8.0",
"langfuse==2.44.0", "langfuse==2.44.0",
"youtube-transcript-api==0.6.2", "youtube-transcript-api==0.6.3",
"pytube==15.0.0", "pytube==15.0.0",
"extract_msg", "extract_msg",
+92 -8
View File
@@ -45,15 +45,15 @@ math {
} }
.input-prose { .input-prose {
@apply prose dark:prose-invert prose-p:my-0 prose-img:my-1 prose-headings:my-1 prose-pre:my-0 prose-table:my-0 prose-blockquote:my-0 prose-ul:-my-0 prose-ol:-my-0 prose-li:-my-0 whitespace-pre-line; @apply prose dark:prose-invert prose-headings:font-semibold prose-hr:my-4 prose-hr:border-gray-100 prose-hr:dark:border-gray-800 prose-p:my-0 prose-img:my-1 prose-headings:my-1 prose-pre:my-0 prose-table:my-0 prose-blockquote:my-0 prose-ul:-my-0 prose-ol:-my-0 prose-li:-my-0 whitespace-pre-line;
} }
.input-prose-sm { .input-prose-sm {
@apply prose dark:prose-invert prose-p:my-0 prose-img:my-1 prose-headings:my-1 prose-pre:my-0 prose-table:my-0 prose-blockquote:my-0 prose-ul:-my-0 prose-ol:-my-0 prose-li:-my-0 whitespace-pre-line text-sm; @apply prose dark:prose-invert prose-headings:font-semibold prose-hr:my-4 prose-hr:border-gray-100 prose-hr:dark:border-gray-800 prose-p:my-0 prose-img:my-1 prose-headings:my-1 prose-pre:my-0 prose-table:my-0 prose-blockquote:my-0 prose-ul:-my-0 prose-ol:-my-0 prose-li:-my-0 whitespace-pre-line text-sm;
} }
.markdown-prose { .markdown-prose {
@apply prose dark:prose-invert prose-p:my-0 prose-img:my-1 prose-headings:my-1 prose-pre:my-0 prose-table:my-0 prose-blockquote:my-0 prose-ul:-my-0 prose-ol:-my-0 prose-li:-my-0 whitespace-pre-line; @apply prose dark:prose-invert prose-headings:font-semibold prose-hr:my-4 prose-p:my-0 prose-img:my-1 prose-headings:my-1 prose-pre:my-0 prose-table:my-0 prose-blockquote:my-0 prose-ul:-my-0 prose-ol:-my-0 prose-li:-my-0 whitespace-pre-line;
} }
.markdown a { .markdown a {
@@ -199,19 +199,103 @@ input[type='number'] {
} }
.ProseMirror { .ProseMirror {
@apply h-full min-h-fit max-h-full whitespace-pre-wrap; @apply h-full min-h-fit max-h-full whitespace-pre-wrap;
} }
.ProseMirror:focus { .ProseMirror:focus {
outline: none; outline: none;
} }
.placeholder::after { .ProseMirror p.is-editor-empty:first-child::before {
content: attr(data-placeholder); content: attr(data-placeholder);
cursor: text; float: left;
color: #adb5bd;
pointer-events: none; pointer-events: none;
float: left; @apply line-clamp-1 absolute;
}
@apply absolute inset-0 z-0 text-gray-500; .ai-autocompletion::after {
color: #a0a0a0;
content: attr(data-suggestion);
pointer-events: none;
}
.tiptap > pre > code {
border-radius: 0.4rem;
font-size: 0.85rem;
padding: 0.25em 0.3em;
@apply dark:bg-gray-800 bg-gray-100;
}
.tiptap > pre {
border-radius: 0.5rem;
font-family: 'JetBrainsMono', monospace;
margin: 1.5rem 0;
padding: 0.75rem 1rem;
@apply dark:bg-gray-800 bg-gray-100;
}
.tiptap p code {
color: #eb5757;
border-width: 0px;
padding: 3px 8px;
font-size: 0.8em;
font-weight: 600;
@apply rounded-md dark:bg-gray-800 bg-gray-100 mx-0.5;
}
/* Code styling */
.hljs-comment,
.hljs-quote {
color: #616161;
}
.hljs-variable,
.hljs-template-variable,
.hljs-attribute,
.hljs-tag,
.hljs-regexp,
.hljs-link,
.hljs-name,
.hljs-selector-id,
.hljs-selector-class {
color: #f98181;
}
.hljs-number,
.hljs-meta,
.hljs-built_in,
.hljs-builtin-name,
.hljs-literal,
.hljs-type,
.hljs-params {
color: #fbbc88;
}
.hljs-string,
.hljs-symbol,
.hljs-bullet {
color: #b9f18d;
}
.hljs-title,
.hljs-section {
color: #faf594;
}
.hljs-keyword,
.hljs-selector-tag {
color: #70cff8;
}
.hljs-emphasis {
font-style: italic;
}
.hljs-strong {
font-weight: 700;
} }
+6 -3
View File
@@ -2,9 +2,12 @@
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />
<link rel="icon" href="%sveltekit.assets%/favicon.png" /> <link rel="icon" type="image/png" href="/favicon/favicon-96x96.png" sizes="96x96" />
<link rel="apple-touch-icon" href="%sveltekit.assets%/favicon.png" /> <link rel="icon" type="image/svg+xml" href="/favicon/favicon.svg" />
<link rel="manifest" href="%sveltekit.assets%/manifest.json" crossorigin="use-credentials" /> <link rel="shortcut icon" href="/favicon/favicon.ico" />
<link rel="apple-touch-icon" sizes="180x180" href="/favicon/apple-touch-icon.png" />
<meta name="apple-mobile-web-app-title" content="Open WebUI" />
<link rel="manifest" href="/favicon/site.webmanifest" />
<meta <meta
name="viewport" name="viewport"
content="width=device-width, initial-scale=1, maximum-scale=1, viewport-fit=cover" content="width=device-width, initial-scale=1, maximum-scale=1, viewport-fit=cover"
+31 -4
View File
@@ -58,17 +58,44 @@ export const exportConfig = async (token: string) => {
return res; return res;
}; };
export const setDefaultModels = async (token: string, models: string) => { export const getModelsConfig = async (token: string) => {
let error = null; let error = null;
const res = await fetch(`${WEBUI_API_BASE_URL}/configs/default/models`, { const res = await fetch(`${WEBUI_API_BASE_URL}/configs/models`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${token}`
}
})
.then(async (res) => {
if (!res.ok) throw await res.json();
return res.json();
})
.catch((err) => {
console.log(err);
error = err.detail;
return null;
});
if (error) {
throw error;
}
return res;
};
export const setModelsConfig = async (token: string, config: object) => {
let error = null;
const res = await fetch(`${WEBUI_API_BASE_URL}/configs/models`, {
method: 'POST', method: 'POST',
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
Authorization: `Bearer ${token}` Authorization: `Bearer ${token}`
}, },
body: JSON.stringify({ body: JSON.stringify({
models: models ...config
}) })
}) })
.then(async (res) => { .then(async (res) => {
@@ -91,7 +118,7 @@ export const setDefaultModels = async (token: string, models: string) => {
export const setDefaultPromptSuggestions = async (token: string, promptSuggestions: string) => { export const setDefaultPromptSuggestions = async (token: string, promptSuggestions: string) => {
let error = null; let error = null;
const res = await fetch(`${WEBUI_API_BASE_URL}/configs/default/suggestions`, { const res = await fetch(`${WEBUI_API_BASE_URL}/configs/suggestions`, {
method: 'POST', method: 'POST',
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
+80 -34
View File
@@ -25,26 +25,6 @@ export const getModels = async (token: string = '', base: boolean = false) => {
} }
let models = res?.data ?? []; let models = res?.data ?? [];
models = models
.filter((models) => models)
// Sort the models
.sort((a, b) => {
// Compare case-insensitively by name for models without position property
const lowerA = a.name.toLowerCase();
const lowerB = b.name.toLowerCase();
if (lowerA < lowerB) return -1;
if (lowerA > lowerB) return 1;
// If same case-insensitively, sort by original strings,
// lowercase will come before uppercase due to ASCII values
if (a.name < b.name) return -1;
if (a.name > b.name) return 1;
return 0; // They are equal
});
console.log(models);
return models; return models;
}; };
@@ -387,20 +367,15 @@ export const generateQueries = async (
throw error; throw error;
} }
// Step 1: Safely extract the response string
const response = res?.choices[0]?.message?.content ?? '';
try { try {
// Step 1: Safely extract the response string const jsonStartIndex = response.indexOf('{');
const response = res?.choices[0]?.message?.content ?? ''; const jsonEndIndex = response.lastIndexOf('}');
// Step 2: Attempt to fix common JSON format issues like single quotes
const sanitizedResponse = response.replace(/['‘’`]/g, '"'); // Convert single quotes to double quotes for valid JSON
// Step 3: Find the relevant JSON block within the response
const jsonStartIndex = sanitizedResponse.indexOf('{');
const jsonEndIndex = sanitizedResponse.lastIndexOf('}');
// Step 4: Check if we found a valid JSON block (with both `{` and `}`)
if (jsonStartIndex !== -1 && jsonEndIndex !== -1) { if (jsonStartIndex !== -1 && jsonEndIndex !== -1) {
const jsonResponse = sanitizedResponse.substring(jsonStartIndex, jsonEndIndex + 1); const jsonResponse = response.substring(jsonStartIndex, jsonEndIndex + 1);
// Step 5: Parse the JSON block // Step 5: Parse the JSON block
const parsed = JSON.parse(jsonResponse); const parsed = JSON.parse(jsonResponse);
@@ -413,12 +388,83 @@ export const generateQueries = async (
} }
} }
// If no valid JSON block found, return an empty array // If no valid JSON block found, return response as is
return []; return [response];
} catch (e) { } catch (e) {
// Catch and safely return empty array on any parsing errors // Catch and safely return empty array on any parsing errors
console.error('Failed to parse response: ', e); console.error('Failed to parse response: ', e);
return []; return [response];
}
};
export const generateAutoCompletion = async (
token: string = '',
model: string,
prompt: string,
messages?: object[],
type: string = 'search query'
) => {
const controller = new AbortController();
let error = null;
const res = await fetch(`${WEBUI_BASE_URL}/api/task/auto/completions`, {
signal: controller.signal,
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
Authorization: `Bearer ${token}`
},
body: JSON.stringify({
model: model,
prompt: prompt,
...(messages && { messages: messages }),
type: type,
stream: false
})
})
.then(async (res) => {
if (!res.ok) throw await res.json();
return res.json();
})
.catch((err) => {
console.log(err);
if ('detail' in err) {
error = err.detail;
}
return null;
});
if (error) {
throw error;
}
const response = res?.choices[0]?.message?.content ?? '';
try {
const jsonStartIndex = response.indexOf('{');
const jsonEndIndex = response.lastIndexOf('}');
if (jsonStartIndex !== -1 && jsonEndIndex !== -1) {
const jsonResponse = response.substring(jsonStartIndex, jsonEndIndex + 1);
// Step 5: Parse the JSON block
const parsed = JSON.parse(jsonResponse);
// Step 6: If there's a "queries" key, return the queries array; otherwise, return an empty array
if (parsed && parsed.text) {
return parsed.text;
} else {
return '';
}
}
// If no valid JSON block found, return response as is
return response;
} catch (e) {
// Catch and safely return empty array on any parsing errors
console.error('Failed to parse response: ', e);
return response;
} }
}; };
+5 -5
View File
@@ -3,7 +3,7 @@ import { WEBUI_API_BASE_URL } from '$lib/constants';
export const getModels = async (token: string = '') => { export const getModels = async (token: string = '') => {
let error = null; let error = null;
const res = await fetch(`${WEBUI_API_BASE_URL}/models`, { const res = await fetch(`${WEBUI_API_BASE_URL}/models/`, {
method: 'GET', method: 'GET',
headers: { headers: {
Accept: 'application/json', Accept: 'application/json',
@@ -97,7 +97,7 @@ export const getModelById = async (token: string, id: string) => {
const searchParams = new URLSearchParams(); const searchParams = new URLSearchParams();
searchParams.append('id', id); searchParams.append('id', id);
const res = await fetch(`${WEBUI_API_BASE_URL}/models/id/${id}`, { const res = await fetch(`${WEBUI_API_BASE_URL}/models/model?${searchParams.toString()}`, {
method: 'GET', method: 'GET',
headers: { headers: {
Accept: 'application/json', Accept: 'application/json',
@@ -132,7 +132,7 @@ export const toggleModelById = async (token: string, id: string) => {
const searchParams = new URLSearchParams(); const searchParams = new URLSearchParams();
searchParams.append('id', id); searchParams.append('id', id);
const res = await fetch(`${WEBUI_API_BASE_URL}/models/id/${id}/toggle`, { const res = await fetch(`${WEBUI_API_BASE_URL}/models/model/toggle?${searchParams.toString()}`, {
method: 'POST', method: 'POST',
headers: { headers: {
Accept: 'application/json', Accept: 'application/json',
@@ -167,7 +167,7 @@ export const updateModelById = async (token: string, id: string, model: object)
const searchParams = new URLSearchParams(); const searchParams = new URLSearchParams();
searchParams.append('id', id); searchParams.append('id', id);
const res = await fetch(`${WEBUI_API_BASE_URL}/models/id/${id}/update`, { const res = await fetch(`${WEBUI_API_BASE_URL}/models/model/update?${searchParams.toString()}`, {
method: 'POST', method: 'POST',
headers: { headers: {
Accept: 'application/json', Accept: 'application/json',
@@ -203,7 +203,7 @@ export const deleteModelById = async (token: string, id: string) => {
const searchParams = new URLSearchParams(); const searchParams = new URLSearchParams();
searchParams.append('id', id); searchParams.append('id', id);
const res = await fetch(`${WEBUI_API_BASE_URL}/models/id/${id}/delete`, { const res = await fetch(`${WEBUI_API_BASE_URL}/models/model/delete?${searchParams.toString()}`, {
method: 'DELETE', method: 'DELETE',
headers: { headers: {
Accept: 'application/json', Accept: 'application/json',
+1
View File
@@ -40,6 +40,7 @@ type ContentExtractConfigForm = {
type YoutubeConfigForm = { type YoutubeConfigForm = {
language: string[]; language: string[];
translation?: string | null; translation?: string | null;
proxy_url: string;
}; };
type RAGConfigForm = { type RAGConfigForm = {
+4 -4
View File
@@ -5,7 +5,7 @@ type TextStreamUpdate = {
done: boolean; done: boolean;
value: string; value: string;
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
citations?: any; sources?: any;
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
selectedModelId?: any; selectedModelId?: any;
error?: any; error?: any;
@@ -67,8 +67,8 @@ async function* openAIStreamToIterator(
break; break;
} }
if (parsedData.citations) { if (parsedData.sources) {
yield { done: false, value: '', citations: parsedData.citations }; yield { done: false, value: '', sources: parsedData.sources };
continue; continue;
} }
@@ -98,7 +98,7 @@ async function* streamLargeDeltasAsRandomChunks(
yield textStreamUpdate; yield textStreamUpdate;
return; return;
} }
if (textStreamUpdate.citations) { if (textStreamUpdate.sources) {
yield textStreamUpdate; yield textStreamUpdate;
continue; continue;
} }
+1 -1
View File
@@ -219,7 +219,7 @@
</div> </div>
<div class="mb-5"> <div class="mb-5">
{#each filteredItems as func} {#each filteredItems as func (func.id)}
<div <div
class=" flex space-x-4 cursor-pointer w-full px-3 py-2 dark:hover:bg-white/5 hover:bg-black/5 rounded-xl" class=" flex space-x-4 cursor-pointer w-full px-3 py-2 dark:hover:bg-white/5 hover:bg-black/5 rounded-xl"
> >
@@ -60,7 +60,7 @@
/> />
{#if pipeline} {#if pipeline}
<div class=" absolute top-2.5 right-2.5"> <div class=" absolute top-0.5 right-2.5">
<Tooltip content="Pipelines"> <Tooltip content="Pipelines">
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
@@ -5,6 +5,7 @@
const dispatch = createEventDispatcher(); const dispatch = createEventDispatcher();
import { getModels } from '$lib/apis'; import { getModels } from '$lib/apis';
import { getConfig, updateConfig } from '$lib/apis/evaluations';
import Switch from '$lib/components/common/Switch.svelte'; import Switch from '$lib/components/common/Switch.svelte';
import Spinner from '$lib/components/common/Spinner.svelte'; import Spinner from '$lib/components/common/Spinner.svelte';
@@ -12,7 +13,6 @@
import Plus from '$lib/components/icons/Plus.svelte'; import Plus from '$lib/components/icons/Plus.svelte';
import Model from './Evaluations/Model.svelte'; import Model from './Evaluations/Model.svelte';
import ArenaModelModal from './Evaluations/ArenaModelModal.svelte'; import ArenaModelModal from './Evaluations/ArenaModelModal.svelte';
import { getConfig, updateConfig } from '$lib/apis/evaluations';
const i18n = getContext('i18n'); const i18n = getContext('i18n');
@@ -27,6 +27,7 @@
if (config) { if (config) {
toast.success('Settings saved successfully'); toast.success('Settings saved successfully');
models.set(await getModels(localStorage.token));
} }
}; };
@@ -102,7 +102,7 @@
description = model.meta.description; description = model.meta.description;
modelIds = model.meta.model_ids || []; modelIds = model.meta.model_ids || [];
filterMode = model.meta?.filter_mode ?? 'include'; filterMode = model.meta?.filter_mode ?? 'include';
accessControl = model.meta?.access_control ?? {}; accessControl = 'access_control' in model.meta ? model.meta.access_control : {};
} }
}; };
@@ -375,7 +375,7 @@
<div class="flex justify-end pt-3 text-sm font-medium gap-1.5"> <div class="flex justify-end pt-3 text-sm font-medium gap-1.5">
{#if edit} {#if edit}
<button <button
class="px-3.5 py-1.5 text-sm font-medium dark:bg-black dark:hover:bg-gray-900 dark:text-white bg-white text-black hover:bg-gray-100 transition rounded-full flex flex-row space-x-1 items-center" class="px-3.5 py-1.5 text-sm font-medium dark:bg-black dark:hover:bg-gray-950 dark:text-white bg-white text-black hover:bg-gray-100 transition rounded-full flex flex-row space-x-1 items-center"
type="button" type="button"
on:click={() => { on:click={() => {
dispatch('delete', model); dispatch('delete', model);
@@ -387,7 +387,7 @@
{/if} {/if}
<button <button
class="px-3.5 py-1.5 text-sm font-medium bg-black hover:bg-gray-900 text-white dark:bg-white dark:text-black dark:hover:bg-gray-100 transition rounded-full flex flex-row space-x-1 items-center {loading class="px-3.5 py-1.5 text-sm font-medium bg-black hover:bg-gray-950 text-white dark:bg-white dark:text-black dark:hover:bg-gray-100 transition rounded-full flex flex-row space-x-1 items-center {loading
? ' cursor-not-allowed' ? ' cursor-not-allowed'
: ''}" : ''}"
type="submit" type="submit"
+301 -261
View File
@@ -24,6 +24,8 @@
TASK_MODEL: '', TASK_MODEL: '',
TASK_MODEL_EXTERNAL: '', TASK_MODEL_EXTERNAL: '',
TITLE_GENERATION_PROMPT_TEMPLATE: '', TITLE_GENERATION_PROMPT_TEMPLATE: '',
ENABLE_AUTOCOMPLETE_GENERATION: true,
AUTOCOMPLETE_GENERATION_INPUT_MAX_LENGTH: -1,
TAGS_GENERATION_PROMPT_TEMPLATE: '', TAGS_GENERATION_PROMPT_TEMPLATE: '',
ENABLE_TAGS_GENERATION: true, ENABLE_TAGS_GENERATION: true,
ENABLE_SEARCH_QUERY_GENERATION: true, ENABLE_SEARCH_QUERY_GENERATION: true,
@@ -55,255 +57,207 @@
}; };
</script> </script>
<form {#if taskConfig}
class="flex flex-col h-full justify-between space-y-3 text-sm" <form
on:submit|preventDefault={() => { class="flex flex-col h-full justify-between space-y-3 text-sm"
updateInterfaceHandler(); on:submit|preventDefault={() => {
dispatch('save'); updateInterfaceHandler();
}} dispatch('save');
> }}
<div class=" overflow-y-scroll scrollbar-hidden h-full pr-1.5"> >
<div> <div class=" overflow-y-scroll scrollbar-hidden h-full pr-1.5">
<div class=" mb-2.5 text-sm font-medium flex items-center"> <div>
<div class=" mr-1">{$i18n.t('Set Task Model')}</div> <div class=" mb-2.5 text-sm font-medium flex items-center">
<Tooltip <div class=" mr-1">{$i18n.t('Set Task Model')}</div>
content={$i18n.t( <Tooltip
'A task model is used when performing tasks such as generating titles for chats and web search queries' content={$i18n.t(
)} 'A task model is used when performing tasks such as generating titles for chats and web search queries'
> )}
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
class="size-3.5"
> >
<path <svg
stroke-linecap="round" xmlns="http://www.w3.org/2000/svg"
stroke-linejoin="round" fill="none"
d="m11.25 11.25.041-.02a.75.75 0 0 1 1.063.852l-.708 2.836a.75.75 0 0 0 1.063.853l.041-.021M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Zm-9-3.75h.008v.008H12V8.25Z" viewBox="0 0 24 24"
/> stroke-width="1.5"
</svg> stroke="currentColor"
</Tooltip> class="size-3.5"
</div> >
<div class="flex w-full gap-2"> <path
<div class="flex-1"> stroke-linecap="round"
<div class=" text-xs mb-1">{$i18n.t('Local Models')}</div> stroke-linejoin="round"
<select d="m11.25 11.25.041-.02a.75.75 0 0 1 1.063.852l-.708 2.836a.75.75 0 0 0 1.063.853l.041-.021M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Zm-9-3.75h.008v.008H12V8.25Z"
class="w-full rounded-lg py-2 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none" />
bind:value={taskConfig.TASK_MODEL} </svg>
placeholder={$i18n.t('Select a model')} </Tooltip>
> </div>
<option value="" selected>{$i18n.t('Current Model')}</option> <div class="flex w-full gap-2">
{#each $models.filter((m) => m.owned_by === 'ollama') as model} <div class="flex-1">
<option value={model.id} class="bg-gray-100 dark:bg-gray-700"> <div class=" text-xs mb-1">{$i18n.t('Local Models')}</div>
{model.name} <select
</option> class="w-full rounded-lg py-2 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none"
{/each} bind:value={taskConfig.TASK_MODEL}
</select> placeholder={$i18n.t('Select a model')}
>
<option value="" selected>{$i18n.t('Current Model')}</option>
{#each $models.filter((m) => m.owned_by === 'ollama') as model}
<option value={model.id} class="bg-gray-100 dark:bg-gray-700">
{model.name}
</option>
{/each}
</select>
</div>
<div class="flex-1">
<div class=" text-xs mb-1">{$i18n.t('External Models')}</div>
<select
class="w-full rounded-lg py-2 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none"
bind:value={taskConfig.TASK_MODEL_EXTERNAL}
placeholder={$i18n.t('Select a model')}
>
<option value="" selected>{$i18n.t('Current Model')}</option>
{#each $models as model}
<option value={model.id} class="bg-gray-100 dark:bg-gray-700">
{model.name}
</option>
{/each}
</select>
</div>
</div> </div>
<div class="flex-1">
<div class=" text-xs mb-1">{$i18n.t('External Models')}</div>
<select
class="w-full rounded-lg py-2 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none"
bind:value={taskConfig.TASK_MODEL_EXTERNAL}
placeholder={$i18n.t('Select a model')}
>
<option value="" selected>{$i18n.t('Current Model')}</option>
{#each $models as model}
<option value={model.id} class="bg-gray-100 dark:bg-gray-700">
{model.name}
</option>
{/each}
</select>
</div>
</div>
<div class="mt-3">
<div class=" mb-2.5 text-xs font-medium">{$i18n.t('Title Generation Prompt')}</div>
<Tooltip
content={$i18n.t('Leave empty to use the default prompt, or enter a custom prompt')}
placement="top-start"
>
<Textarea
bind:value={taskConfig.TITLE_GENERATION_PROMPT_TEMPLATE}
placeholder={$i18n.t('Leave empty to use the default prompt, or enter a custom prompt')}
/>
</Tooltip>
</div>
<hr class=" dark:border-gray-850 my-3" />
<div class="my-3 flex w-full items-center justify-between">
<div class=" self-center text-xs font-medium">
{$i18n.t('Enable Tags Generation')}
</div>
<Switch bind:state={taskConfig.ENABLE_TAGS_GENERATION} />
</div>
{#if taskConfig.ENABLE_TAGS_GENERATION}
<div class="mt-3"> <div class="mt-3">
<div class=" mb-2.5 text-xs font-medium">{$i18n.t('Tags Generation Prompt')}</div> <div class=" mb-2.5 text-xs font-medium">{$i18n.t('Title Generation Prompt')}</div>
<Tooltip <Tooltip
content={$i18n.t('Leave empty to use the default prompt, or enter a custom prompt')} content={$i18n.t('Leave empty to use the default prompt, or enter a custom prompt')}
placement="top-start" placement="top-start"
> >
<Textarea <Textarea
bind:value={taskConfig.TAGS_GENERATION_PROMPT_TEMPLATE} bind:value={taskConfig.TITLE_GENERATION_PROMPT_TEMPLATE}
placeholder={$i18n.t( placeholder={$i18n.t(
'Leave empty to use the default prompt, or enter a custom prompt' 'Leave empty to use the default prompt, or enter a custom prompt'
)} )}
/> />
</Tooltip> </Tooltip>
</div> </div>
{/if}
<hr class=" dark:border-gray-850 my-3" /> <hr class=" border-gray-50 dark:border-gray-850 my-3" />
<div class="my-3 flex w-full items-center justify-between"> <div class="my-3 flex w-full items-center justify-between">
<div class=" self-center text-xs font-medium"> <div class=" self-center text-xs font-medium">
{$i18n.t('Enable Retrieval Query Generation')} {$i18n.t('Autocomplete Generation')}
</div>
<Tooltip content={$i18n.t('Enable autocomplete generation for chat messages')}>
<Switch bind:state={taskConfig.ENABLE_AUTOCOMPLETE_GENERATION} />
</Tooltip>
</div> </div>
<Switch bind:state={taskConfig.ENABLE_RETRIEVAL_QUERY_GENERATION} /> {#if taskConfig.ENABLE_AUTOCOMPLETE_GENERATION}
</div> <div class="mt-3">
<div class=" mb-2.5 text-xs font-medium">
<div class="my-3 flex w-full items-center justify-between"> {$i18n.t('Autocomplete Generation Input Max Length')}
<div class=" self-center text-xs font-medium">
{$i18n.t('Enable Web Search Query Generation')}
</div>
<Switch bind:state={taskConfig.ENABLE_SEARCH_QUERY_GENERATION} />
</div>
<div class="">
<div class=" mb-2.5 text-xs font-medium">{$i18n.t('Query Generation Prompt')}</div>
<Tooltip
content={$i18n.t('Leave empty to use the default prompt, or enter a custom prompt')}
placement="top-start"
>
<Textarea
bind:value={taskConfig.QUERY_GENERATION_PROMPT_TEMPLATE}
placeholder={$i18n.t('Leave empty to use the default prompt, or enter a custom prompt')}
/>
</Tooltip>
</div>
</div>
<hr class=" dark:border-gray-850 my-3" />
<div class=" space-y-3 {banners.length > 0 ? ' mb-3' : ''}">
<div class="flex w-full justify-between">
<div class=" self-center text-sm font-semibold">
{$i18n.t('Banners')}
</div>
<button
class="p-1 px-3 text-xs flex rounded transition"
type="button"
on:click={() => {
if (banners.length === 0 || banners.at(-1).content !== '') {
banners = [
...banners,
{
id: uuidv4(),
type: '',
title: '',
content: '',
dismissible: true,
timestamp: Math.floor(Date.now() / 1000)
}
];
}
}}
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
fill="currentColor"
class="w-4 h-4"
>
<path
d="M10.75 4.75a.75.75 0 00-1.5 0v4.5h-4.5a.75.75 0 000 1.5h4.5v4.5a.75.75 0 001.5 0v-4.5h4.5a.75.75 0 000-1.5h-4.5v-4.5z"
/>
</svg>
</button>
</div>
<div class="flex flex-col space-y-1">
{#each banners as banner, bannerIdx}
<div class=" flex justify-between">
<div class="flex flex-row flex-1 border rounded-xl dark:border-gray-800">
<select
class="w-fit capitalize rounded-xl py-2 px-4 text-xs bg-transparent outline-none"
bind:value={banner.type}
required
>
{#if banner.type == ''}
<option value="" selected disabled class="text-gray-900">{$i18n.t('Type')}</option
>
{/if}
<option value="info" class="text-gray-900">{$i18n.t('Info')}</option>
<option value="warning" class="text-gray-900">{$i18n.t('Warning')}</option>
<option value="error" class="text-gray-900">{$i18n.t('Error')}</option>
<option value="success" class="text-gray-900">{$i18n.t('Success')}</option>
</select>
<input
class="pr-5 py-1.5 text-xs w-full bg-transparent outline-none"
placeholder={$i18n.t('Content')}
bind:value={banner.content}
/>
<div class="relative top-1.5 -left-2">
<Tooltip content={$i18n.t('Dismissible')} className="flex h-fit items-center">
<Switch bind:state={banner.dismissible} />
</Tooltip>
</div>
</div> </div>
<button <Tooltip
class="px-2" content={$i18n.t('Character limit for autocomplete generation input')}
type="button" placement="top-start"
on:click={() => {
banners.splice(bannerIdx, 1);
banners = banners;
}}
> >
<svg <input
xmlns="http://www.w3.org/2000/svg" class="w-full outline-none bg-transparent"
viewBox="0 0 20 20" bind:value={taskConfig.AUTOCOMPLETE_GENERATION_INPUT_MAX_LENGTH}
fill="currentColor" placeholder={$i18n.t('-1 for no limit, or a positive integer for a specific limit')}
class="w-4 h-4" />
> </Tooltip>
<path
d="M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z"
/>
</svg>
</button>
</div> </div>
{/each} {/if}
</div>
</div>
{#if $user.role === 'admin'} <hr class=" border-gray-50 dark:border-gray-850 my-3" />
<div class=" space-y-3">
<div class="flex w-full justify-between mb-2"> <div class="my-3 flex w-full items-center justify-between">
<div class=" self-center text-xs font-medium">
{$i18n.t('Tags Generation')}
</div>
<Switch bind:state={taskConfig.ENABLE_TAGS_GENERATION} />
</div>
{#if taskConfig.ENABLE_TAGS_GENERATION}
<div class="mt-3">
<div class=" mb-2.5 text-xs font-medium">{$i18n.t('Tags Generation Prompt')}</div>
<Tooltip
content={$i18n.t('Leave empty to use the default prompt, or enter a custom prompt')}
placement="top-start"
>
<Textarea
bind:value={taskConfig.TAGS_GENERATION_PROMPT_TEMPLATE}
placeholder={$i18n.t(
'Leave empty to use the default prompt, or enter a custom prompt'
)}
/>
</Tooltip>
</div>
{/if}
<hr class=" border-gray-50 dark:border-gray-850 my-3" />
<div class="my-3 flex w-full items-center justify-between">
<div class=" self-center text-xs font-medium">
{$i18n.t('Retrieval Query Generation')}
</div>
<Switch bind:state={taskConfig.ENABLE_RETRIEVAL_QUERY_GENERATION} />
</div>
<div class="my-3 flex w-full items-center justify-between">
<div class=" self-center text-xs font-medium">
{$i18n.t('Web Search Query Generation')}
</div>
<Switch bind:state={taskConfig.ENABLE_SEARCH_QUERY_GENERATION} />
</div>
<div class="">
<div class=" mb-2.5 text-xs font-medium">{$i18n.t('Query Generation Prompt')}</div>
<Tooltip
content={$i18n.t('Leave empty to use the default prompt, or enter a custom prompt')}
placement="top-start"
>
<Textarea
bind:value={taskConfig.QUERY_GENERATION_PROMPT_TEMPLATE}
placeholder={$i18n.t(
'Leave empty to use the default prompt, or enter a custom prompt'
)}
/>
</Tooltip>
</div>
</div>
<hr class=" border-gray-50 dark:border-gray-850 my-3" />
<div class=" space-y-3 {banners.length > 0 ? ' mb-3' : ''}">
<div class="flex w-full justify-between">
<div class=" self-center text-sm font-semibold"> <div class=" self-center text-sm font-semibold">
{$i18n.t('Default Prompt Suggestions')} {$i18n.t('Banners')}
</div> </div>
<button <button
class="p-1 px-3 text-xs flex rounded transition" class="p-1 px-3 text-xs flex rounded transition"
type="button" type="button"
on:click={() => { on:click={() => {
if (promptSuggestions.length === 0 || promptSuggestions.at(-1).content !== '') { if (banners.length === 0 || banners.at(-1).content !== '') {
promptSuggestions = [...promptSuggestions, { content: '', title: ['', ''] }]; banners = [
...banners,
{
id: uuidv4(),
type: '',
title: '',
content: '',
dismissible: true,
timestamp: Math.floor(Date.now() / 1000)
}
];
} }
}} }}
> >
@@ -319,40 +273,45 @@
</svg> </svg>
</button> </button>
</div> </div>
<div class="grid lg:grid-cols-2 flex-col gap-1.5"> <div class="flex flex-col space-y-1">
{#each promptSuggestions as prompt, promptIdx} {#each banners as banner, bannerIdx}
<div <div class=" flex justify-between">
class=" flex border border-gray-100 dark:border-none dark:bg-gray-850 rounded-xl py-1.5" <div class="flex flex-row flex-1 border rounded-xl dark:border-gray-800">
> <select
<div class="flex flex-col flex-1 pl-1"> class="w-fit capitalize rounded-xl py-2 px-4 text-xs bg-transparent outline-none"
<div class="flex border-b border-gray-100 dark:border-gray-800 w-full"> bind:value={banner.type}
<input required
class="px-3 py-1.5 text-xs w-full bg-transparent outline-none border-r border-gray-100 dark:border-gray-800" >
placeholder={$i18n.t('Title (e.g. Tell me a fun fact)')} {#if banner.type == ''}
bind:value={prompt.title[0]} <option value="" selected disabled class="text-gray-900"
/> >{$i18n.t('Type')}</option
>
{/if}
<option value="info" class="text-gray-900">{$i18n.t('Info')}</option>
<option value="warning" class="text-gray-900">{$i18n.t('Warning')}</option>
<option value="error" class="text-gray-900">{$i18n.t('Error')}</option>
<option value="success" class="text-gray-900">{$i18n.t('Success')}</option>
</select>
<input <input
class="px-3 py-1.5 text-xs w-full bg-transparent outline-none border-r border-gray-100 dark:border-gray-800" class="pr-5 py-1.5 text-xs w-full bg-transparent outline-none"
placeholder={$i18n.t('Subtitle (e.g. about the Roman Empire)')} placeholder={$i18n.t('Content')}
bind:value={prompt.title[1]} bind:value={banner.content}
/>
</div>
<textarea
class="px-3 py-1.5 text-xs w-full bg-transparent outline-none border-r border-gray-100 dark:border-gray-800 resize-none"
placeholder={$i18n.t('Prompt (e.g. Tell me a fun fact about the Roman Empire)')}
rows="3"
bind:value={prompt.content}
/> />
<div class="relative top-1.5 -left-2">
<Tooltip content={$i18n.t('Dismissible')} className="flex h-fit items-center">
<Switch bind:state={banner.dismissible} />
</Tooltip>
</div>
</div> </div>
<button <button
class="px-3" class="px-2"
type="button" type="button"
on:click={() => { on:click={() => {
promptSuggestions.splice(promptIdx, 1); banners.splice(bannerIdx, 1);
promptSuggestions = promptSuggestions; banners = banners;
}} }}
> >
<svg <svg
@@ -369,22 +328,103 @@
</div> </div>
{/each} {/each}
</div> </div>
{#if promptSuggestions.length > 0}
<div class="text-xs text-left w-full mt-2">
{$i18n.t('Adjusting these settings will apply changes universally to all users.')}
</div>
{/if}
</div> </div>
{/if}
</div>
<div class="flex justify-end text-sm font-medium"> {#if $user.role === 'admin'}
<button <div class=" space-y-3">
class="px-3.5 py-1.5 text-sm font-medium bg-black hover:bg-gray-900 text-white dark:bg-white dark:text-black dark:hover:bg-gray-100 transition rounded-full" <div class="flex w-full justify-between mb-2">
type="submit" <div class=" self-center text-sm font-semibold">
> {$i18n.t('Default Prompt Suggestions')}
{$i18n.t('Save')} </div>
</button>
</div> <button
</form> class="p-1 px-3 text-xs flex rounded transition"
type="button"
on:click={() => {
if (promptSuggestions.length === 0 || promptSuggestions.at(-1).content !== '') {
promptSuggestions = [...promptSuggestions, { content: '', title: ['', ''] }];
}
}}
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
fill="currentColor"
class="w-4 h-4"
>
<path
d="M10.75 4.75a.75.75 0 00-1.5 0v4.5h-4.5a.75.75 0 000 1.5h4.5v4.5a.75.75 0 001.5 0v-4.5h4.5a.75.75 0 000-1.5h-4.5v-4.5z"
/>
</svg>
</button>
</div>
<div class="grid lg:grid-cols-2 flex-col gap-1.5">
{#each promptSuggestions as prompt, promptIdx}
<div
class=" flex border border-gray-100 dark:border-none dark:bg-gray-850 rounded-xl py-1.5"
>
<div class="flex flex-col flex-1 pl-1">
<div class="flex border-b border-gray-100 dark:border-gray-800 w-full">
<input
class="px-3 py-1.5 text-xs w-full bg-transparent outline-none border-r border-gray-100 dark:border-gray-800"
placeholder={$i18n.t('Title (e.g. Tell me a fun fact)')}
bind:value={prompt.title[0]}
/>
<input
class="px-3 py-1.5 text-xs w-full bg-transparent outline-none border-r border-gray-100 dark:border-gray-800"
placeholder={$i18n.t('Subtitle (e.g. about the Roman Empire)')}
bind:value={prompt.title[1]}
/>
</div>
<textarea
class="px-3 py-1.5 text-xs w-full bg-transparent outline-none border-r border-gray-100 dark:border-gray-800 resize-none"
placeholder={$i18n.t('Prompt (e.g. Tell me a fun fact about the Roman Empire)')}
rows="3"
bind:value={prompt.content}
/>
</div>
<button
class="px-3"
type="button"
on:click={() => {
promptSuggestions.splice(promptIdx, 1);
promptSuggestions = promptSuggestions;
}}
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
fill="currentColor"
class="w-4 h-4"
>
<path
d="M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z"
/>
</svg>
</button>
</div>
{/each}
</div>
{#if promptSuggestions.length > 0}
<div class="text-xs text-left w-full mt-2">
{$i18n.t('Adjusting these settings will apply changes universally to all users.')}
</div>
{/if}
</div>
{/if}
</div>
<div class="flex justify-end text-sm font-medium">
<button
class="px-3.5 py-1.5 text-sm font-medium bg-black hover:bg-gray-900 text-white dark:bg-white dark:text-black dark:hover:bg-gray-100 transition rounded-full"
type="submit"
>
{$i18n.t('Save')}
</button>
</div>
</form>
{/if}
+20 -24
View File
@@ -24,6 +24,8 @@
import ModelEditor from '$lib/components/workspace/Models/ModelEditor.svelte'; import ModelEditor from '$lib/components/workspace/Models/ModelEditor.svelte';
import { toast } from 'svelte-sonner'; import { toast } from 'svelte-sonner';
import ConfirmDialog from '$lib/components/common/ConfirmDialog.svelte'; import ConfirmDialog from '$lib/components/common/ConfirmDialog.svelte';
import Cog6 from '$lib/components/icons/Cog6.svelte';
import ConfigureModelsModal from './Models/ConfigureModelsModal.svelte';
let importFiles; let importFiles;
let modelsImportInputElement: HTMLInputElement; let modelsImportInputElement: HTMLInputElement;
@@ -35,12 +37,20 @@
let filteredModels = []; let filteredModels = [];
let selectedModelId = null; let selectedModelId = null;
let showResetModal = false;
let showConfigModal = false;
$: if (models) { $: if (models) {
filteredModels = models.filter( filteredModels = models
(m) => searchValue === '' || m.name.toLowerCase().includes(searchValue.toLowerCase()) .filter((m) => searchValue === '' || m.name.toLowerCase().includes(searchValue.toLowerCase()))
); .sort((a, b) => {
// // Check if either model is inactive and push them to the bottom
// if ((a.is_active ?? true) !== (b.is_active ?? true)) {
// return (b.is_active ?? true) - (a.is_active ?? true);
// }
// If both models' active states are the same, sort alphabetically
return a.name.localeCompare(b.name);
});
} }
let searchValue = ''; let searchValue = '';
@@ -114,12 +124,11 @@
}).catch((error) => { }).catch((error) => {
return null; return null;
}); });
await init();
} else { } else {
await toggleModelById(localStorage.token, model.id); await toggleModelById(localStorage.token, model.id);
} }
await init();
_models.set(await getModels(localStorage.token)); _models.set(await getModels(localStorage.token));
}; };
@@ -128,18 +137,7 @@
}); });
</script> </script>
<ConfirmDialog <ConfigureModelsModal bind:show={showConfigModal} {init} />
title={$i18n.t('Delete All Models')}
message={$i18n.t('This will delete all models including custom models and cannot be undone.')}
bind:show={showResetModal}
onConfirm={async () => {
const res = deleteAllModels(localStorage.token);
if (res) {
toast.success($i18n.t('All models deleted successfully'));
init();
}
}}
/>
{#if models !== null} {#if models !== null}
{#if selectedModelId === null} {#if selectedModelId === null}
@@ -154,17 +152,15 @@
</div> </div>
<div> <div>
<Tooltip content={$i18n.t('This will delete all models including custom models')}> <Tooltip content={$i18n.t('Configure')}>
<button <button
class=" px-2.5 py-1 rounded-full flex gap-1 items-center" class=" px-2.5 py-1 rounded-full flex gap-1 items-center"
type="button" type="button"
on:click={() => { on:click={() => {
showResetModal = true; showConfigModal = true;
}} }}
> >
<div class="text-xs flex-shrink-0"> <Cog6 />
{$i18n.t('Reset')}
</div>
</button> </button>
</Tooltip> </Tooltip>
</div> </div>
@@ -186,7 +182,7 @@
<div class=" my-2 mb-5" id="model-list"> <div class=" my-2 mb-5" id="model-list">
{#if models.length > 0} {#if models.length > 0}
{#each filteredModels as model (model.id)} {#each filteredModels as model, modelIdx (model.id)}
<div <div
class=" flex space-x-4 cursor-pointer w-full px-3 py-2 dark:hover:bg-white/5 hover:bg-black/5 rounded-lg transition" class=" flex space-x-4 cursor-pointer w-full px-3 py-2 dark:hover:bg-white/5 hover:bg-black/5 rounded-lg transition"
id="model-item-{model.id}" id="model-item-{model.id}"
@@ -0,0 +1,268 @@
<script>
import { toast } from 'svelte-sonner';
import { createEventDispatcher, getContext, onMount } from 'svelte';
const i18n = getContext('i18n');
const dispatch = createEventDispatcher();
import { models } from '$lib/stores';
import { deleteAllModels } from '$lib/apis/models';
import Modal from '$lib/components/common/Modal.svelte';
import ConfirmDialog from '$lib/components/common/ConfirmDialog.svelte';
import Tooltip from '$lib/components/common/Tooltip.svelte';
import ModelList from './ModelList.svelte';
import { getModelsConfig, setModelsConfig } from '$lib/apis/configs';
import Spinner from '$lib/components/common/Spinner.svelte';
import Minus from '$lib/components/icons/Minus.svelte';
import Plus from '$lib/components/icons/Plus.svelte';
export let show = false;
export let init = () => {};
let config = null;
let selectedModelId = '';
let defaultModelIds = [];
let modelIds = [];
let loading = false;
let showResetModal = false;
const submitHandler = async () => {
loading = true;
const res = await setModelsConfig(localStorage.token, {
DEFAULT_MODELS: defaultModelIds.join(','),
MODEL_ORDER_LIST: modelIds
});
if (res) {
toast.success($i18n.t('Models configuration saved successfully'));
init();
show = false;
} else {
toast.error($i18n.t('Failed to save models configuration'));
}
loading = false;
};
onMount(async () => {
config = await getModelsConfig(localStorage.token);
if (config?.DEFAULT_MODELS) {
defaultModelIds = (config?.DEFAULT_MODELS).split(',').filter((id) => id);
} else {
defaultModelIds = [];
}
const modelOrderList = config.MODEL_ORDER_LIST || [];
const allModelIds = $models.map((model) => model.id);
// Create a Set for quick lookup of ordered IDs
const orderedSet = new Set(modelOrderList);
modelIds = [
// Add all IDs from MODEL_ORDER_LIST that exist in allModelIds
...modelOrderList.filter((id) => orderedSet.has(id) && allModelIds.includes(id)),
// Add remaining IDs not in MODEL_ORDER_LIST, sorted alphabetically
...allModelIds.filter((id) => !orderedSet.has(id)).sort((a, b) => a.localeCompare(b))
];
});
</script>
<ConfirmDialog
title={$i18n.t('Reset All Models')}
message={$i18n.t('This will delete all models including custom models and cannot be undone.')}
bind:show={showResetModal}
onConfirm={async () => {
const res = deleteAllModels(localStorage.token);
if (res) {
toast.success($i18n.t('All models deleted successfully'));
init();
}
}}
/>
<Modal size="sm" bind:show>
<div>
<div class=" flex justify-between dark:text-gray-100 px-5 pt-4 pb-2">
<div class=" text-lg font-medium self-center font-primary">
{$i18n.t('Configure Models')}
</div>
<button
class="self-center"
on:click={() => {
show = false;
}}
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
fill="currentColor"
class="w-5 h-5"
>
<path
d="M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z"
/>
</svg>
</button>
</div>
<div class="flex flex-col md:flex-row w-full px-5 pb-4 md:space-x-4 dark:text-gray-200">
<div class=" flex flex-col w-full sm:flex-row sm:justify-center sm:space-x-6">
{#if config}
<form
class="flex flex-col w-full"
on:submit|preventDefault={() => {
submitHandler();
}}
>
<div>
<div class="flex flex-col w-full">
<div class="mb-1 flex justify-between">
<div class="text-xs text-gray-500">{$i18n.t('Reorder Models')}</div>
</div>
<ModelList bind:modelIds />
</div>
</div>
<hr class=" border-gray-100 dark:border-gray-700/10 my-2.5 w-full" />
<div>
<div class="flex flex-col w-full">
<div class="mb-1 flex justify-between">
<div class="text-xs text-gray-500">{$i18n.t('Default Models')}</div>
</div>
{#if defaultModelIds.length > 0}
<div class="flex flex-col">
{#each defaultModelIds as modelId, modelIdx}
<div class=" flex gap-2 w-full justify-between items-center">
<div class=" text-sm flex-1 py-1 rounded-lg">
{$models.find((model) => model.id === modelId)?.name}
</div>
<div class="flex-shrink-0">
<button
type="button"
on:click={() => {
defaultModelIds = defaultModelIds.filter(
(_, idx) => idx !== modelIdx
);
}}
>
<Minus strokeWidth="2" className="size-3.5" />
</button>
</div>
</div>
{/each}
</div>
{:else}
<div class="text-gray-500 text-xs text-center py-2">
{$i18n.t('No models selected')}
</div>
{/if}
<hr class=" border-gray-100 dark:border-gray-700/10 my-2.5 w-full" />
<div class="flex items-center">
<select
class="w-full py-1 text-sm rounded-lg bg-transparent {selectedModelId
? ''
: 'text-gray-500'} placeholder:text-gray-300 dark:placeholder:text-gray-700 outline-none"
bind:value={selectedModelId}
>
<option value="">{$i18n.t('Select a model')}</option>
{#each $models as model}
<option value={model.id} class="bg-gray-50 dark:bg-gray-700"
>{model.name}</option
>
{/each}
</select>
<div>
<button
type="button"
on:click={() => {
if (selectedModelId === '') {
return;
}
if (defaultModelIds.includes(selectedModelId)) {
return;
}
defaultModelIds = [...defaultModelIds, selectedModelId];
selectedModelId = '';
}}
>
<Plus className="size-3.5" strokeWidth="2" />
</button>
</div>
</div>
</div>
</div>
<div class="flex justify-between pt-3 text-sm font-medium gap-1.5">
<Tooltip content={$i18n.t('This will delete all models including custom models')}>
<button
class="px-3.5 py-1.5 text-sm font-medium dark:bg-black dark:hover:bg-gray-950 dark:text-white bg-white text-black hover:bg-gray-100 transition rounded-full flex flex-row space-x-1 items-center"
type="button"
on:click={() => {
showResetModal = true;
}}
>
<!-- {$i18n.t('Delete All Models')} -->
{$i18n.t('Reset All Models')}
</button>
</Tooltip>
<button
class="px-3.5 py-1.5 text-sm font-medium bg-black hover:bg-gray-900 text-white dark:bg-white dark:text-black dark:hover:bg-gray-100 transition rounded-full flex flex-row space-x-1 items-center {loading
? ' cursor-not-allowed'
: ''}"
type="submit"
disabled={loading}
>
{$i18n.t('Save')}
{#if loading}
<div class="ml-2 self-center">
<svg
class=" w-4 h-4"
viewBox="0 0 24 24"
fill="currentColor"
xmlns="http://www.w3.org/2000/svg"
><style>
.spinner_ajPY {
transform-origin: center;
animation: spinner_AtaB 0.75s infinite linear;
}
@keyframes spinner_AtaB {
100% {
transform: rotate(360deg);
}
}
</style><path
d="M12,1A11,11,0,1,0,23,12,11,11,0,0,0,12,1Zm0,19a8,8,0,1,1,8-8A8,8,0,0,1,12,20Z"
opacity=".25"
/><path
d="M10.14,1.16a11,11,0,0,0-9,8.92A1.59,1.59,0,0,0,2.46,12,1.52,1.52,0,0,0,4.11,10.7a8,8,0,0,1,6.66-6.61A1.42,1.42,0,0,0,12,2.69h0A1.57,1.57,0,0,0,10.14,1.16Z"
class="spinner_ajPY"
/></svg
>
</div>
{/if}
</button>
</div>
</form>
{:else}
<div>
<Spinner />
</div>
{/if}
</div>
</div>
</div>
</Modal>
@@ -0,0 +1,58 @@
<script lang="ts">
import Sortable from 'sortablejs';
import { createEventDispatcher, getContext, onMount } from 'svelte';
const i18n = getContext('i18n');
import { models } from '$lib/stores';
import Tooltip from '$lib/components/common/Tooltip.svelte';
import EllipsisVertical from '$lib/components/icons/EllipsisVertical.svelte';
export let modelIds = [];
let sortable = null;
let modelListElement = null;
const positionChangeHandler = () => {
const modelList = Array.from(modelListElement.children).map((child) =>
child.id.replace('model-item-', '')
);
modelIds = modelList;
};
onMount(() => {
sortable = Sortable.create(modelListElement, {
animation: 150,
onUpdate: async (event) => {
positionChangeHandler();
}
});
});
</script>
{#if modelIds.length > 0}
<div class="flex flex-col -translate-x-1" bind:this={modelListElement}>
{#each modelIds as modelId, modelIdx (modelId)}
<div class=" flex gap-2 w-full justify-between items-center" id="model-item-{modelId}">
<Tooltip content={modelId} placement="top-start">
<div class="flex items-center gap-1">
<EllipsisVertical className="size-4 cursor-move" />
<div class=" text-sm flex-1 py-1 rounded-lg">
{#if $models.find((model) => model.id === modelId)}
{$models.find((model) => model.id === modelId).name}
{:else}
{modelId}
{/if}
</div>
</div>
</Tooltip>
</div>
{/each}
</div>
{:else}
<div class="text-gray-500 text-xs text-center py-2">
{$i18n.t('No models found')}
</div>
{/if}
@@ -16,6 +16,7 @@
'searxng', 'searxng',
'google_pse', 'google_pse',
'brave', 'brave',
'mojeek',
'serpstack', 'serpstack',
'serper', 'serper',
'serply', 'serply',
@@ -28,13 +29,15 @@
let youtubeLanguage = 'en'; let youtubeLanguage = 'en';
let youtubeTranslation = null; let youtubeTranslation = null;
let youtubeProxyUrl = '';
const submitHandler = async () => { const submitHandler = async () => {
const res = await updateRAGConfig(localStorage.token, { const res = await updateRAGConfig(localStorage.token, {
web: webConfig, web: webConfig,
youtube: { youtube: {
language: youtubeLanguage.split(',').map((lang) => lang.trim()), language: youtubeLanguage.split(',').map((lang) => lang.trim()),
translation: youtubeTranslation translation: youtubeTranslation,
proxy_url: youtubeProxyUrl
} }
}); });
}; };
@@ -47,6 +50,7 @@
youtubeLanguage = res.youtube.language.join(','); youtubeLanguage = res.youtube.language.join(',');
youtubeTranslation = res.youtube.translation; youtubeTranslation = res.youtube.translation;
youtubeProxyUrl = res.youtube.proxy_url;
} }
}); });
</script> </script>
@@ -151,6 +155,17 @@
bind:value={webConfig.search.brave_search_api_key} bind:value={webConfig.search.brave_search_api_key}
/> />
</div> </div>
{:else if webConfig.search.engine === 'mojeek'}
<div>
<div class=" self-center text-xs font-medium mb-1">
{$i18n.t('Mojeek Search API Key')}
</div>
<SensitiveInput
placeholder={$i18n.t('Enter Mojeek Search API Key')}
bind:value={webConfig.search.mojeek_search_api_key}
/>
</div>
{:else if webConfig.search.engine === 'serpstack'} {:else if webConfig.search.engine === 'serpstack'}
<div> <div>
<div class=" self-center text-xs font-medium mb-1"> <div class=" self-center text-xs font-medium mb-1">
@@ -346,6 +361,21 @@
</div> </div>
</div> </div>
</div> </div>
<div>
<div class=" py-0.5 flex w-full justify-between">
<div class=" w-20 text-xs font-medium self-center">{$i18n.t('Proxy URL')}</div>
<div class=" flex-1 self-center">
<input
class="w-full rounded-lg py-2 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none"
type="text"
placeholder={$i18n.t('Enter proxy URL (e.g. https://user:password@host:port)')}
bind:value={youtubeProxyUrl}
autocomplete="off"
/>
</div>
</div>
</div>
</div> </div>
{/if} {/if}
</div> </div>
@@ -9,13 +9,14 @@
import Modal from '$lib/components/common/Modal.svelte'; import Modal from '$lib/components/common/Modal.svelte';
import Tooltip from '$lib/components/common/Tooltip.svelte'; import Tooltip from '$lib/components/common/Tooltip.svelte';
import Spinner from '$lib/components/common/Spinner.svelte';
const i18n = getContext('i18n'); const i18n = getContext('i18n');
export let show = false; export let show = false;
export let user; export let user;
let chats = []; let chats = null;
const deleteChatHandler = async (chatId) => { const deleteChatHandler = async (chatId) => {
const res = await deleteChatById(localStorage.token, chatId).catch((error) => { const res = await deleteChatById(localStorage.token, chatId).catch((error) => {
@@ -31,6 +32,8 @@
chats = await getChatListByUserId(localStorage.token, user.id); chats = await getChatListByUserId(localStorage.token, user.id);
} }
})(); })();
} else {
chats = null;
} }
let sortKey = 'updated_at'; // default sort key let sortKey = 'updated_at'; // default sort key
@@ -46,33 +49,32 @@
</script> </script>
<Modal size="lg" bind:show> <Modal size="lg" bind:show>
<div> <div class=" flex justify-between dark:text-gray-300 px-5 pt-4">
<div class=" flex justify-between dark:text-gray-300 px-5 py-4"> <div class=" text-lg font-medium self-center capitalize">
<div class=" text-lg font-medium self-center capitalize"> {$i18n.t("{{user}}'s Chats", { user: user.name })}
{$i18n.t("{{user}}'s Chats", { user: user.name })}
</div>
<button
class="self-center"
on:click={() => {
show = false;
}}
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
fill="currentColor"
class="w-5 h-5"
>
<path
d="M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z"
/>
</svg>
</button>
</div> </div>
<hr class=" dark:border-gray-850" /> <button
class="self-center"
on:click={() => {
show = false;
}}
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
fill="currentColor"
class="w-5 h-5"
>
<path
d="M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z"
/>
</svg>
</button>
</div>
<div class="flex flex-col md:flex-row w-full px-5 py-4 md:space-x-4 dark:text-gray-200"> <div class="flex flex-col md:flex-row w-full px-5 pt-2 pb-4 md:space-x-4 dark:text-gray-200">
<div class=" flex flex-col w-full sm:flex-row sm:justify-center sm:space-x-6"> <div class=" flex flex-col w-full sm:flex-row sm:justify-center sm:space-x-6">
{#if chats}
{#if chats.length > 0} {#if chats.length > 0}
<div class="text-left text-sm w-full mb-4 max-h-[22rem] overflow-y-scroll"> <div class="text-left text-sm w-full mb-4 max-h-[22rem] overflow-y-scroll">
<div class="relative overflow-x-auto"> <div class="relative overflow-x-auto">
@@ -176,7 +178,9 @@
{$i18n.t('has no conversations.')} {$i18n.t('has no conversations.')}
</div> </div>
{/if} {/if}
</div> {:else}
<Spinner />
{/if}
</div> </div>
</div> </div>
</Modal> </Modal>
+32 -23
View File
@@ -216,7 +216,7 @@
} else { } else {
message.statusHistory = [data]; message.statusHistory = [data];
} }
} else if (type === 'citation') { } else if (type === 'source' || type === 'citation') {
if (data?.type === 'code_execution') { if (data?.type === 'code_execution') {
// Code execution; update existing code execution by ID, or add new one. // Code execution; update existing code execution by ID, or add new one.
if (!message?.code_executions) { if (!message?.code_executions) {
@@ -235,11 +235,11 @@
message.code_executions = message.code_executions; message.code_executions = message.code_executions;
} else { } else {
// Regular citation. // Regular source.
if (message?.citations) { if (message?.sources) {
message.citations.push(data); message.sources.push(data);
} else { } else {
message.citations = [data]; message.sources = [data];
} }
} }
} else if (type === 'message') { } else if (type === 'message') {
@@ -663,7 +663,8 @@
role: m.role, role: m.role,
content: m.content, content: m.content,
info: m.info ? m.info : undefined, info: m.info ? m.info : undefined,
timestamp: m.timestamp timestamp: m.timestamp,
...(m.sources ? { sources: m.sources } : {})
})), })),
chat_id: chatId, chat_id: chatId,
session_id: $socket?.id, session_id: $socket?.id,
@@ -716,7 +717,8 @@
role: m.role, role: m.role,
content: m.content, content: m.content,
info: m.info ? m.info : undefined, info: m.info ? m.info : undefined,
timestamp: m.timestamp timestamp: m.timestamp,
...(m.sources ? { sources: m.sources } : {})
})), })),
...(event ? { event: event } : {}), ...(event ? { event: event } : {}),
chat_id: chatId, chat_id: chatId,
@@ -886,11 +888,10 @@
await tick(); await tick();
// Reset chat input textarea // Reset chat input textarea
const chatInputContainer = document.getElementById('chat-input-container'); const chatInputElement = document.getElementById('chat-input');
if (chatInputContainer) { if (chatInputElement) {
chatInputContainer.value = ''; chatInputElement.style.height = '';
chatInputContainer.style.height = '';
} }
const _files = JSON.parse(JSON.stringify(files)); const _files = JSON.parse(JSON.stringify(files));
@@ -1276,8 +1277,8 @@
console.log(line); console.log(line);
let data = JSON.parse(line); let data = JSON.parse(line);
if ('citations' in data) { if ('sources' in data) {
responseMessage.citations = data.citations; responseMessage.sources = data.sources;
// Only remove status if it was initially set // Only remove status if it was initially set
if (model?.info?.meta?.knowledge ?? false) { if (model?.info?.meta?.knowledge ?? false) {
responseMessage.statusHistory = responseMessage.statusHistory.filter( responseMessage.statusHistory = responseMessage.statusHistory.filter(
@@ -1630,7 +1631,7 @@
const textStream = await createOpenAITextStream(res.body, $settings.splitLargeChunks); const textStream = await createOpenAITextStream(res.body, $settings.splitLargeChunks);
for await (const update of textStream) { for await (const update of textStream) {
const { value, done, citations, selectedModelId, error, usage } = update; const { value, done, sources, selectedModelId, error, usage } = update;
if (error) { if (error) {
await handleOpenAIError(error, null, model, responseMessage); await handleOpenAIError(error, null, model, responseMessage);
break; break;
@@ -1656,8 +1657,8 @@
continue; continue;
} }
if (citations) { if (sources) {
responseMessage.citations = citations; responseMessage.sources = sources;
// Only remove status if it was initially set // Only remove status if it was initially set
if (model?.info?.meta?.knowledge ?? false) { if (model?.info?.meta?.knowledge ?? false) {
responseMessage.statusHistory = responseMessage.statusHistory.filter( responseMessage.statusHistory = responseMessage.statusHistory.filter(
@@ -1936,7 +1937,7 @@
if (res && res.ok && res.body) { if (res && res.ok && res.body) {
const textStream = await createOpenAITextStream(res.body, $settings.splitLargeChunks); const textStream = await createOpenAITextStream(res.body, $settings.splitLargeChunks);
for await (const update of textStream) { for await (const update of textStream) {
const { value, done, citations, error, usage } = update; const { value, done, sources, error, usage } = update;
if (error || done) { if (error || done) {
break; break;
} }
@@ -1975,7 +1976,7 @@
} }
); );
return title; return title ? title : (lastUserMessage?.content ?? 'New Chat');
} else { } else {
return lastUserMessage?.content ?? 'New Chat'; return lastUserMessage?.content ?? 'New Chat';
} }
@@ -2283,7 +2284,7 @@
</div> </div>
</div> </div>
<div class=" pb-[1.6rem]"> <div class=" pb-[1rem]">
<MessageInput <MessageInput
{history} {history}
{selectedModels} {selectedModels}
@@ -2308,15 +2309,19 @@
on:submit={async (e) => { on:submit={async (e) => {
if (e.detail) { if (e.detail) {
await tick(); await tick();
submitPrompt(e.detail.replaceAll('\n\n', '\n')); submitPrompt(
($settings?.richTextInput ?? true)
? e.detail.replaceAll('\n\n', '\n')
: e.detail
);
} }
}} }}
/> />
<div <div
class="absolute bottom-1.5 text-xs text-gray-500 text-center line-clamp-1 right-0 left-0" class="absolute bottom-1 text-xs text-gray-500 text-center line-clamp-1 right-0 left-0"
> >
{$i18n.t('LLMs can make mistakes. Verify important information.')} <!-- {$i18n.t('LLMs can make mistakes. Verify important information.')} -->
</div> </div>
</div> </div>
{:else} {:else}
@@ -2345,7 +2350,11 @@
on:submit={async (e) => { on:submit={async (e) => {
if (e.detail) { if (e.detail) {
await tick(); await tick();
submitPrompt(e.detail.replaceAll('\n\n', '\n')); submitPrompt(
($settings?.richTextInput ?? true)
? e.detail.replaceAll('\n\n', '\n')
: e.detail
);
} }
}} }}
/> />
+225 -206
View File
@@ -18,7 +18,7 @@
showControls showControls
} from '$lib/stores'; } from '$lib/stores';
import { blobToFile, findWordIndices } from '$lib/utils'; import { blobToFile, createMessagesList, findWordIndices } from '$lib/utils';
import { transcribeAudio } from '$lib/apis/audio'; import { transcribeAudio } from '$lib/apis/audio';
import { uploadFile } from '$lib/apis/files'; import { uploadFile } from '$lib/apis/files';
import { getTools } from '$lib/apis/tools'; import { getTools } from '$lib/apis/tools';
@@ -34,6 +34,8 @@
import Commands from './MessageInput/Commands.svelte'; import Commands from './MessageInput/Commands.svelte';
import XMark from '../icons/XMark.svelte'; import XMark from '../icons/XMark.svelte';
import RichTextInput from '../common/RichTextInput.svelte'; import RichTextInput from '../common/RichTextInput.svelte';
import { generateAutoCompletion } from '$lib/apis';
import { error, text } from '@sveltejs/kit';
const i18n = getContext('i18n'); const i18n = getContext('i18n');
@@ -47,6 +49,9 @@
export let atSelectedModel: Model | undefined; export let atSelectedModel: Model | undefined;
export let selectedModels: ['']; export let selectedModels: [''];
let selectedModelIds = [];
$: selectedModelIds = atSelectedModel !== undefined ? [atSelectedModel.id] : selectedModels;
export let history; export let history;
export let prompt = ''; export let prompt = '';
@@ -75,14 +80,6 @@
(model) => $models.find((m) => m.id === model)?.info?.meta?.capabilities?.vision ?? true (model) => $models.find((m) => m.id === model)?.info?.meta?.capabilities?.vision ?? true
); );
$: if (prompt) {
if (chatInputContainerElement) {
chatInputContainerElement.style.height = '';
chatInputContainerElement.style.height =
Math.min(chatInputContainerElement.scrollHeight, 200) + 'px';
}
}
const scrollToBottom = () => { const scrollToBottom = () => {
const element = document.getElementById('messages-container'); const element = document.getElementById('messages-container');
element.scrollTo({ element.scrollTo({
@@ -274,8 +271,8 @@
{#if loaded} {#if loaded}
<div class="w-full font-primary"> <div class="w-full font-primary">
<div class=" -mb-0.5 mx-auto inset-x-0 bg-transparent flex justify-center"> <div class=" mx-auto inset-x-0 bg-transparent flex justify-center">
<div class="flex flex-col px-2.5 max-w-6xl w-full"> <div class="flex flex-col px-3 max-w-6xl w-full">
<div class="relative"> <div class="relative">
{#if autoScroll === false && history?.currentId} {#if autoScroll === false && history?.currentId}
<div <div
@@ -308,7 +305,7 @@
<div class="w-full relative"> <div class="w-full relative">
{#if atSelectedModel !== undefined || selectedToolIds.length > 0 || webSearchEnabled} {#if atSelectedModel !== undefined || selectedToolIds.length > 0 || webSearchEnabled}
<div <div
class="px-4 pb-0.5 pt-1.5 text-left w-full flex flex-col absolute bottom-0 left-0 right-0 bg-gradient-to-t from-white dark:from-gray-900 z-10" class="px-3 pb-0.5 pt-1.5 text-left w-full flex flex-col absolute bottom-0 left-0 right-0 bg-gradient-to-t from-white dark:from-gray-900 z-10"
> >
{#if selectedToolIds.length > 0} {#if selectedToolIds.length > 0}
<div class="flex items-center justify-between w-full"> <div class="flex items-center justify-between w-full">
@@ -321,12 +318,22 @@
<span class="relative inline-flex rounded-full size-2 bg-yellow-500" /> <span class="relative inline-flex rounded-full size-2 bg-yellow-500" />
</span> </span>
</div> </div>
<div class=" translate-y-[0.5px] line-clamp-1"> <div class=" translate-y-[0.5px] text-ellipsis line-clamp-1 flex">
{selectedToolIds {#each selectedToolIds.map((id) => {
.map((id) => { return $tools ? $tools.find((t) => t.id === id) : { id: id, name: id };
return $tools ? $tools.find((tool) => tool.id === id)?.name : id; }) as tool, toolIdx (toolIdx)}
}) <Tooltip
.join(', ')} content={tool?.meta?.description ?? ''}
className=" {toolIdx !== 0 ? 'pl-0.5' : ''} flex-shrink-0"
placement="top"
>
{tool.name}
</Tooltip>
{#if toolIdx !== selectedToolIds.length - 1}
<span>, </span>
{/if}
{/each}
</div> </div>
</div> </div>
</div> </div>
@@ -403,7 +410,7 @@
</div> </div>
<div class="{transparentBackground ? 'bg-transparent' : 'bg-white dark:bg-gray-900'} "> <div class="{transparentBackground ? 'bg-transparent' : 'bg-white dark:bg-gray-900'} ">
<div class="max-w-6xl px-4 mx-auto inset-x-0"> <div class="max-w-6xl px-2.5 mx-auto inset-x-0">
<div class=""> <div class="">
<input <input
bind:this={filesInputElement} bind:this={filesInputElement}
@@ -455,7 +462,7 @@
}} }}
> >
<div <div
class="flex-1 flex flex-col relative w-full rounded-3xl px-1.5 bg-gray-50 dark:bg-gray-850 dark:text-gray-100" class="flex-1 flex flex-col relative w-full rounded-3xl px-1 bg-gray-50 dark:bg-gray-400/5 dark:text-gray-100"
dir={$settings?.chatDirection ?? 'LTR'} dir={$settings?.chatDirection ?? 'LTR'}
> >
{#if files.length > 0} {#if files.length > 0}
@@ -540,7 +547,7 @@
{/if} {/if}
<div class=" flex"> <div class=" flex">
<div class=" ml-0.5 self-end mb-1.5 flex space-x-1"> <div class="ml-1 self-end mb-1.5 flex space-x-1">
<InputMenu <InputMenu
bind:webSearchEnabled bind:webSearchEnabled
bind:selectedToolIds bind:selectedToolIds
@@ -555,18 +562,18 @@
}} }}
> >
<button <button
class="bg-gray-50 hover:bg-gray-100 text-gray-800 dark:bg-gray-850 dark:text-white dark:hover:bg-gray-800 transition rounded-full p-2 outline-none focus:outline-none" class="bg-transparent hover:bg-white/80 text-gray-800 dark:text-white dark:hover:bg-gray-800 transition rounded-full p-2 outline-none focus:outline-none"
type="button" type="button"
aria-label="More" aria-label="More"
> >
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 16 16" viewBox="0 0 20 20"
fill="currentColor" fill="currentColor"
class="size-5" class="size-5"
> >
<path <path
d="M8.75 3.75a.75.75 0 0 0-1.5 0v3.5h-3.5a.75.75 0 0 0 0 1.5h3.5v3.5a.75.75 0 0 0 1.5 0v-3.5h3.5a.75.75 0 0 0 0-1.5h-3.5v-3.5Z" d="M10.75 4.75a.75.75 0 0 0-1.5 0v4.5h-4.5a.75.75 0 0 0 0 1.5h4.5v4.5a.75.75 0 0 0 1.5 0v-4.5h4.5a.75.75 0 0 0 0-1.5h-4.5v-4.5Z"
/> />
</svg> </svg>
</button> </button>
@@ -575,54 +582,46 @@
{#if $settings?.richTextInput ?? true} {#if $settings?.richTextInput ?? true}
<div <div
bind:this={chatInputContainerElement} class="scrollbar-hidden text-left bg-transparent dark:text-gray-100 outline-none w-full py-2.5 px-1 rounded-xl resize-none h-fit max-h-80 overflow-auto"
id="chat-input-container"
class="scrollbar-hidden text-left bg-gray-50 dark:bg-gray-850 dark:text-gray-100 outline-none w-full py-2.5 px-1 rounded-xl resize-none h-[48px] overflow-auto"
> >
<RichTextInput <RichTextInput
bind:this={chatInputElement} bind:this={chatInputElement}
id="chat-input"
trim={true}
placeholder={placeholder ? placeholder : $i18n.t('Send a Message')}
largeTextAsFile={$settings?.largeTextAsFile ?? false}
bind:value={prompt} bind:value={prompt}
id="chat-input"
messageInput={true}
shiftEnter={!$mobile || shiftEnter={!$mobile ||
!( !(
'ontouchstart' in window || 'ontouchstart' in window ||
navigator.maxTouchPoints > 0 || navigator.maxTouchPoints > 0 ||
navigator.msMaxTouchPoints > 0 navigator.msMaxTouchPoints > 0
)} )}
on:enter={async (e) => { placeholder={placeholder ? placeholder : $i18n.t('Send a Message')}
if (prompt !== '') { largeTextAsFile={$settings?.largeTextAsFile ?? false}
dispatch('submit', prompt); autocomplete={true}
generateAutoCompletion={async (text) => {
if (selectedModelIds.length === 0 || !selectedModelIds.at(0)) {
toast.error($i18n.t('Please select a model first.'));
} }
}}
on:input={async (e) => { const res = await generateAutoCompletion(
if (chatInputContainerElement) { localStorage.token,
chatInputContainerElement.style.height = ''; selectedModelIds.at(0),
chatInputContainerElement.style.height = text,
Math.min(chatInputContainerElement.scrollHeight, 200) + 'px'; history?.currentId
} ? createMessagesList(history, history.currentId)
}} : null
on:focus={async (e) => { ).catch((error) => {
if (chatInputContainerElement) { console.log(error);
chatInputContainerElement.style.height = '';
chatInputContainerElement.style.height = return null;
Math.min(chatInputContainerElement.scrollHeight, 200) + 'px'; });
}
}} console.log(res);
on:keypress={(e) => { return res;
e = e.detail.event;
}} }}
on:keydown={async (e) => { on:keydown={async (e) => {
e = e.detail.event; e = e.detail.event;
if (chatInputContainerElement) {
chatInputContainerElement.style.height = '';
chatInputContainerElement.style.height =
Math.min(chatInputContainerElement.scrollHeight, 200) + 'px';
}
const isCtrlPressed = e.ctrlKey || e.metaKey; // metaKey is for Cmd key on Mac const isCtrlPressed = e.ctrlKey || e.metaKey; // metaKey is for Cmd key on Mac
const commandsContainerElement = const commandsContainerElement =
document.getElementById('commands-container'); document.getElementById('commands-container');
@@ -662,50 +661,70 @@
editButton?.click(); editButton?.click();
} }
if (commandsContainerElement && e.key === 'ArrowUp') { if (commandsContainerElement) {
e.preventDefault(); if (commandsContainerElement && e.key === 'ArrowUp') {
commandsElement.selectUp(); e.preventDefault();
commandsElement.selectUp();
const commandOptionButton = [ const commandOptionButton = [
...document.getElementsByClassName('selected-command-option-button') ...document.getElementsByClassName('selected-command-option-button')
]?.at(-1); ]?.at(-1);
commandOptionButton.scrollIntoView({ block: 'center' }); commandOptionButton.scrollIntoView({ block: 'center' });
}
if (commandsContainerElement && e.key === 'ArrowDown') {
e.preventDefault();
commandsElement.selectDown();
const commandOptionButton = [
...document.getElementsByClassName('selected-command-option-button')
]?.at(-1);
commandOptionButton.scrollIntoView({ block: 'center' });
}
if (commandsContainerElement && e.key === 'Enter') {
e.preventDefault();
const commandOptionButton = [
...document.getElementsByClassName('selected-command-option-button')
]?.at(-1);
if (e.shiftKey) {
prompt = `${prompt}\n`;
} else if (commandOptionButton) {
commandOptionButton?.click();
} else {
document.getElementById('send-message-button')?.click();
} }
}
if (commandsContainerElement && e.key === 'Tab') { if (commandsContainerElement && e.key === 'ArrowDown') {
e.preventDefault(); e.preventDefault();
commandsElement.selectDown();
const commandOptionButton = [ const commandOptionButton = [
...document.getElementsByClassName('selected-command-option-button') ...document.getElementsByClassName('selected-command-option-button')
]?.at(-1); ]?.at(-1);
commandOptionButton.scrollIntoView({ block: 'center' });
}
commandOptionButton?.click(); if (commandsContainerElement && e.key === 'Tab') {
e.preventDefault();
const commandOptionButton = [
...document.getElementsByClassName('selected-command-option-button')
]?.at(-1);
commandOptionButton?.click();
}
if (commandsContainerElement && e.key === 'Enter') {
e.preventDefault();
const commandOptionButton = [
...document.getElementsByClassName('selected-command-option-button')
]?.at(-1);
if (commandOptionButton) {
commandOptionButton?.click();
} else {
document.getElementById('send-message-button')?.click();
}
}
} else {
if (
!$mobile ||
!(
'ontouchstart' in window ||
navigator.maxTouchPoints > 0 ||
navigator.msMaxTouchPoints > 0
)
) {
// Prevent Enter key from creating a new line
// Uses keyCode '13' for Enter key for chinese/japanese keyboards
if (e.keyCode === 13 && !e.shiftKey) {
e.preventDefault();
}
// Submit the prompt when Enter key is pressed
if (prompt !== '' && e.keyCode === 13 && !e.shiftKey) {
dispatch('submit', prompt);
}
}
} }
if (e.key === 'Escape') { if (e.key === 'Escape') {
@@ -888,7 +907,7 @@
} }
e.target.style.height = ''; e.target.style.height = '';
e.target.style.height = Math.min(e.target.scrollHeight, 200) + 'px'; e.target.style.height = Math.min(e.target.scrollHeight, 320) + 'px';
} }
if (e.key === 'Escape') { if (e.key === 'Escape') {
@@ -901,12 +920,11 @@
rows="1" rows="1"
on:input={async (e) => { on:input={async (e) => {
e.target.style.height = ''; e.target.style.height = '';
e.target.style.height = Math.min(e.target.scrollHeight, 200) + 'px'; e.target.style.height = Math.min(e.target.scrollHeight, 320) + 'px';
user = null;
}} }}
on:focus={async (e) => { on:focus={async (e) => {
e.target.style.height = ''; e.target.style.height = '';
e.target.style.height = Math.min(e.target.scrollHeight, 200) + 'px'; e.target.style.height = Math.min(e.target.scrollHeight, 320) + 'px';
}} }}
on:paste={async (e) => { on:paste={async (e) => {
const clipboardData = e.clipboardData || window.clipboardData; const clipboardData = e.clipboardData || window.clipboardData;
@@ -949,12 +967,12 @@
/> />
{/if} {/if}
<div class="self-end mb-2 flex space-x-1 mr-1"> <div class="self-end mb-1.5 flex space-x-1 mr-1">
{#if !history?.currentId || history.messages[history.currentId]?.done == true} {#if !history?.currentId || history.messages[history.currentId]?.done == true}
<Tooltip content={$i18n.t('Record voice')}> <Tooltip content={$i18n.t('Record voice')}>
<button <button
id="voice-input-button" id="voice-input-button"
class=" text-gray-600 dark:text-gray-300 hover:bg-gray-50 dark:hover:bg-gray-850 transition rounded-full p-1.5 mr-0.5 self-center" class=" text-gray-600 dark:text-gray-300 hover:text-gray-700 dark:hover:text-gray-200 transition rounded-full p-1.5 mr-0.5 self-center"
type="button" type="button"
on:click={async () => { on:click={async () => {
try { try {
@@ -998,113 +1016,114 @@
</button> </button>
</Tooltip> </Tooltip>
{/if} {/if}
{#if !history.currentId || history.messages[history.currentId]?.done == true}
{#if prompt === ''}
<div class=" flex items-center">
<Tooltip content={$i18n.t('Call')}>
<button
class=" bg-black text-white hover:bg-gray-900 dark:bg-white dark:text-black dark:hover:bg-gray-100 transition rounded-full p-2 self-center"
type="button"
on:click={async () => {
if (selectedModels.length > 1) {
toast.error($i18n.t('Select only one model to call'));
return;
}
if ($config.audio.stt.engine === 'web') {
toast.error(
$i18n.t(
'Call feature is not supported when using Web STT engine'
)
);
return;
}
// check if user has access to getUserMedia
try {
let stream = await navigator.mediaDevices.getUserMedia({
audio: true
});
// If the user grants the permission, proceed to show the call overlay
if (stream) {
const tracks = stream.getTracks();
tracks.forEach((track) => track.stop());
}
stream = null;
showCallOverlay.set(true);
showControls.set(true);
} catch (err) {
// If the user denies the permission or an error occurs, show an error message
toast.error(
$i18n.t('Permission denied when accessing media devices')
);
}
}}
aria-label="Call"
>
<Headphone className="size-5" />
</button>
</Tooltip>
</div>
{:else}
<div class=" flex items-center">
<Tooltip content={$i18n.t('Send message')}>
<button
id="send-message-button"
class="{prompt !== ''
? 'bg-black text-white hover:bg-gray-900 dark:bg-white dark:text-black dark:hover:bg-gray-100 '
: 'text-white bg-gray-200 dark:text-gray-900 dark:bg-gray-700 disabled'} transition rounded-full p-1.5 self-center"
type="submit"
disabled={prompt === ''}
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 16 16"
fill="currentColor"
class="size-6"
>
<path
fill-rule="evenodd"
d="M8 14a.75.75 0 0 1-.75-.75V4.56L4.03 7.78a.75.75 0 0 1-1.06-1.06l4.5-4.5a.75.75 0 0 1 1.06 0l4.5 4.5a.75.75 0 0 1-1.06 1.06L8.75 4.56v8.69A.75.75 0 0 1 8 14Z"
clip-rule="evenodd"
/>
</svg>
</button>
</Tooltip>
</div>
{/if}
{:else}
<div class=" flex items-center">
<Tooltip content={$i18n.t('Stop')}>
<button
class="bg-white hover:bg-gray-100 text-gray-800 dark:bg-gray-700 dark:text-white dark:hover:bg-gray-800 transition rounded-full p-1.5"
on:click={() => {
stopResponse();
}}
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="currentColor"
class="size-6"
>
<path
fill-rule="evenodd"
d="M2.25 12c0-5.385 4.365-9.75 9.75-9.75s9.75 4.365 9.75 9.75-4.365 9.75-9.75 9.75S2.25 17.385 2.25 12zm6-2.438c0-.724.588-1.312 1.313-1.312h4.874c.725 0 1.313.588 1.313 1.313v4.874c0 .725-.588 1.313-1.313 1.313H9.564a1.312 1.312 0 01-1.313-1.313V9.564z"
clip-rule="evenodd"
/>
</svg>
</button>
</Tooltip>
</div>
{/if}
</div> </div>
</div> </div>
</div> </div>
<div class="flex items-end w-10">
{#if !history.currentId || history.messages[history.currentId]?.done == true}
{#if prompt === ''}
<div class=" flex items-center mb-1">
<Tooltip content={$i18n.t('Call')}>
<button
class=" text-gray-600 dark:text-gray-300 hover:bg-gray-50 dark:hover:bg-gray-850 transition rounded-full p-2 self-center"
type="button"
on:click={async () => {
if (selectedModels.length > 1) {
toast.error($i18n.t('Select only one model to call'));
return;
}
if ($config.audio.stt.engine === 'web') {
toast.error(
$i18n.t('Call feature is not supported when using Web STT engine')
);
return;
}
// check if user has access to getUserMedia
try {
let stream = await navigator.mediaDevices.getUserMedia({
audio: true
});
// If the user grants the permission, proceed to show the call overlay
if (stream) {
const tracks = stream.getTracks();
tracks.forEach((track) => track.stop());
}
stream = null;
showCallOverlay.set(true);
showControls.set(true);
} catch (err) {
// If the user denies the permission or an error occurs, show an error message
toast.error(
$i18n.t('Permission denied when accessing media devices')
);
}
}}
aria-label="Call"
>
<Headphone className="size-6" />
</button>
</Tooltip>
</div>
{:else}
<div class=" flex items-center mb-1">
<Tooltip content={$i18n.t('Send message')}>
<button
id="send-message-button"
class="{prompt !== ''
? 'bg-black text-white hover:bg-gray-900 dark:bg-white dark:text-black dark:hover:bg-gray-100 '
: 'text-white bg-gray-200 dark:text-gray-900 dark:bg-gray-700 disabled'} transition rounded-full p-1.5 m-0.5 self-center"
type="submit"
disabled={prompt === ''}
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 16 16"
fill="currentColor"
class="size-6"
>
<path
fill-rule="evenodd"
d="M8 14a.75.75 0 0 1-.75-.75V4.56L4.03 7.78a.75.75 0 0 1-1.06-1.06l4.5-4.5a.75.75 0 0 1 1.06 0l4.5 4.5a.75.75 0 0 1-1.06 1.06L8.75 4.56v8.69A.75.75 0 0 1 8 14Z"
clip-rule="evenodd"
/>
</svg>
</button>
</Tooltip>
</div>
{/if}
{:else}
<div class=" flex items-center mb-1.5">
<Tooltip content={$i18n.t('Stop')}>
<button
class="bg-white hover:bg-gray-100 text-gray-800 dark:bg-gray-700 dark:text-white dark:hover:bg-gray-800 transition rounded-full p-1.5"
on:click={() => {
stopResponse();
}}
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="currentColor"
class="size-6"
>
<path
fill-rule="evenodd"
d="M2.25 12c0-5.385 4.365-9.75 9.75-9.75s9.75 4.365 9.75 9.75-4.365 9.75-9.75 9.75S2.25 17.385 2.25 12zm6-2.438c0-.724.588-1.312 1.313-1.312h4.874c.725 0 1.313.588 1.313 1.313v4.874c0 .725-.588 1.313-1.313 1.313H9.564a1.312 1.312 0 01-1.313-1.313V9.564z"
clip-rule="evenodd"
/>
</svg>
</button>
</Tooltip>
</div>
{/if}
</div>
</form> </form>
{/if} {/if}
</div> </div>
@@ -106,7 +106,7 @@
{:else} {:else}
<div <div
id="commands-container" id="commands-container"
class="pl-3 pr-14 mb-3 text-left w-full absolute bottom-0 left-0 right-0 z-10" class="px-2 mb-2 text-left w-full absolute bottom-0 left-0 right-0 z-10"
> >
<div class="flex w-full rounded-xl border border-gray-50 dark:border-gray-850"> <div class="flex w-full rounded-xl border border-gray-50 dark:border-gray-850">
<div <div
@@ -159,7 +159,7 @@
{#if filteredItems.length > 0 || prompt.split(' ')?.at(0)?.substring(1).startsWith('http')} {#if filteredItems.length > 0 || prompt.split(' ')?.at(0)?.substring(1).startsWith('http')}
<div <div
id="commands-container" id="commands-container"
class="pl-3 pr-14 mb-3 text-left w-full absolute bottom-0 left-0 right-0 z-10" class="px-2 mb-2 text-left w-full absolute bottom-0 left-0 right-0 z-10"
> >
<div class="flex w-full rounded-xl border border-gray-50 dark:border-gray-850"> <div class="flex w-full rounded-xl border border-gray-50 dark:border-gray-850">
<div <div
@@ -68,7 +68,7 @@
{#if filteredItems.length > 0} {#if filteredItems.length > 0}
<div <div
id="commands-container" id="commands-container"
class="pl-3 pr-14 mb-3 text-left w-full absolute bottom-0 left-0 right-0 z-10" class="px-2 mb-2 text-left w-full absolute bottom-0 left-0 right-0 z-10"
> >
<div class="flex w-full rounded-xl border border-gray-50 dark:border-gray-850"> <div class="flex w-full rounded-xl border border-gray-50 dark:border-gray-850">
<div <div
@@ -137,7 +137,7 @@
{#if filteredPrompts.length > 0} {#if filteredPrompts.length > 0}
<div <div
id="commands-container" id="commands-container"
class="pl-3 pr-14 mb-3 text-left w-full absolute bottom-0 left-0 right-0 z-10" class="px-2 mb-2 text-left w-full absolute bottom-0 left-0 right-0 z-10"
> >
<div class="flex w-full rounded-xl border border-gray-50 dark:border-gray-850"> <div class="flex w-full rounded-xl border border-gray-50 dark:border-gray-850">
<div <div
@@ -7,9 +7,9 @@
const i18n = getContext('i18n'); const i18n = getContext('i18n');
export let citations = []; export let sources = [];
let _citations = []; let citations = [];
let showPercentage = false; let showPercentage = false;
let showRelevance = true; let showRelevance = true;
@@ -17,8 +17,8 @@
let selectedCitation: any = null; let selectedCitation: any = null;
let isCollapsibleOpen = false; let isCollapsibleOpen = false;
function calculateShowRelevance(citations: any[]) { function calculateShowRelevance(sources: any[]) {
const distances = citations.flatMap((citation) => citation.distances ?? []); const distances = sources.flatMap((citation) => citation.distances ?? []);
const inRange = distances.filter((d) => d !== undefined && d >= -1 && d <= 1).length; const inRange = distances.filter((d) => d !== undefined && d >= -1 && d <= 1).length;
const outOfRange = distances.filter((d) => d !== undefined && (d < -1 || d > 1)).length; const outOfRange = distances.filter((d) => d !== undefined && (d < -1 || d > 1)).length;
@@ -36,25 +36,31 @@
return true; return true;
} }
function shouldShowPercentage(citations: any[]) { function shouldShowPercentage(sources: any[]) {
const distances = citations.flatMap((citation) => citation.distances ?? []); const distances = sources.flatMap((citation) => citation.distances ?? []);
return distances.every((d) => d !== undefined && d >= -1 && d <= 1); return distances.every((d) => d !== undefined && d >= -1 && d <= 1);
} }
$: { $: {
_citations = citations.reduce((acc, citation) => { citations = sources.reduce((acc, source) => {
citation.document.forEach((document, index) => { if (Object.keys(source).length === 0) {
const metadata = citation.metadata?.[index]; return acc;
const distance = citation.distances?.[index]; }
source.document.forEach((document, index) => {
const metadata = source.metadata?.[index];
const distance = source.distances?.[index];
// Within the same citation there could be multiple documents
const id = metadata?.source ?? 'N/A'; const id = metadata?.source ?? 'N/A';
let source = citation?.source; let _source = source?.source;
if (metadata?.name) { if (metadata?.name) {
source = { ...source, name: metadata.name }; _source = { ..._source, name: metadata.name };
} }
if (id.startsWith('http://') || id.startsWith('https://')) { if (id.startsWith('http://') || id.startsWith('https://')) {
source = { ...source, name: id, url: id }; _source = { ..._source, name: id, url: id };
} }
const existingSource = acc.find((item) => item.id === id); const existingSource = acc.find((item) => item.id === id);
@@ -66,7 +72,7 @@
} else { } else {
acc.push({ acc.push({
id: id, id: id,
source: source, source: _source,
document: [document], document: [document],
metadata: metadata ? [metadata] : [], metadata: metadata ? [metadata] : [],
distances: distance !== undefined ? [distance] : undefined distances: distance !== undefined ? [distance] : undefined
@@ -76,8 +82,8 @@
return acc; return acc;
}, []); }, []);
showRelevance = calculateShowRelevance(_citations); showRelevance = calculateShowRelevance(citations);
showPercentage = shouldShowPercentage(_citations); showPercentage = shouldShowPercentage(citations);
} }
</script> </script>
@@ -88,24 +94,27 @@
{showRelevance} {showRelevance}
/> />
{#if _citations.length > 0} {#if citations.length > 0}
<div class=" py-0.5 -mx-0.5 w-full flex gap-1 items-center flex-wrap"> <div class=" py-0.5 -mx-0.5 w-full flex gap-1 items-center flex-wrap">
{#if _citations.length <= 3} {#if citations.length <= 3}
<div class="flex text-xs font-medium"> <div class="flex text-xs font-medium">
{#each _citations as citation, idx} {#each citations as citation, idx}
<button <button
class="no-toggle outline-none flex dark:text-gray-300 p-1 bg-gray-50 hover:bg-gray-100 dark:bg-gray-900 dark:hover:bg-gray-850 transition rounded-xl max-w-96" id={`source-${citation.source.name}`}
class="no-toggle outline-none flex dark:text-gray-300 p-1 bg-white dark:bg-gray-900 rounded-xl max-w-96"
on:click={() => { on:click={() => {
showCitationModal = true; showCitationModal = true;
selectedCitation = citation; selectedCitation = citation;
}} }}
> >
{#if _citations.every((c) => c.distances !== undefined)} {#if citations.every((c) => c.distances !== undefined)}
<div class="bg-gray-50 dark:bg-gray-800 rounded-full size-4"> <div class="bg-gray-50 dark:bg-gray-800 rounded-full size-4">
{idx + 1} {idx + 1}
</div> </div>
{/if} {/if}
<div class="flex-1 mx-1 line-clamp-1 truncate"> <div
class="flex-1 mx-1 line-clamp-1 text-black/60 hover:text-black dark:text-white/60 dark:hover:text-white transition"
>
{citation.source.name} {citation.source.name}
</div> </div>
</button> </button>
@@ -120,7 +129,7 @@
<span class="whitespace-nowrap hidden sm:inline">{$i18n.t('References from')}</span> <span class="whitespace-nowrap hidden sm:inline">{$i18n.t('References from')}</span>
<div class="flex items-center"> <div class="flex items-center">
<div class="flex text-xs font-medium items-center"> <div class="flex text-xs font-medium items-center">
{#each _citations.slice(0, 2) as citation, idx} {#each citations.slice(0, 2) as citation, idx}
<button <button
class="no-toggle outline-none flex dark:text-gray-300 p-1 bg-gray-50 hover:bg-gray-100 dark:bg-gray-900 dark:hover:bg-gray-850 transition rounded-xl max-w-96" class="no-toggle outline-none flex dark:text-gray-300 p-1 bg-gray-50 hover:bg-gray-100 dark:bg-gray-900 dark:hover:bg-gray-850 transition rounded-xl max-w-96"
on:click={() => { on:click={() => {
@@ -131,7 +140,7 @@
e.stopPropagation(); e.stopPropagation();
}} }}
> >
{#if _citations.every((c) => c.distances !== undefined)} {#if citations.every((c) => c.distances !== undefined)}
<div class="bg-gray-50 dark:bg-gray-800 rounded-full size-4"> <div class="bg-gray-50 dark:bg-gray-800 rounded-full size-4">
{idx + 1} {idx + 1}
</div> </div>
@@ -145,7 +154,7 @@
</div> </div>
<div class="flex items-center gap-1 whitespace-nowrap"> <div class="flex items-center gap-1 whitespace-nowrap">
<span class="hidden sm:inline">{$i18n.t('and')}</span> <span class="hidden sm:inline">{$i18n.t('and')}</span>
{_citations.length - 2} {citations.length - 2}
<span>{$i18n.t('more')}</span> <span>{$i18n.t('more')}</span>
</div> </div>
</div> </div>
@@ -159,7 +168,7 @@
</div> </div>
<div slot="content"> <div slot="content">
<div class="flex text-xs font-medium"> <div class="flex text-xs font-medium">
{#each _citations as citation, idx} {#each citations as citation, idx}
<button <button
class="no-toggle outline-none flex dark:text-gray-300 p-1 bg-gray-50 hover:bg-gray-100 dark:bg-gray-900 dark:hover:bg-gray-850 transition rounded-xl max-w-96" class="no-toggle outline-none flex dark:text-gray-300 p-1 bg-gray-50 hover:bg-gray-100 dark:bg-gray-900 dark:hover:bg-gray-850 transition rounded-xl max-w-96"
on:click={() => { on:click={() => {
@@ -167,7 +176,7 @@
selectedCitation = citation; selectedCitation = citation;
}} }}
> >
{#if _citations.every((c) => c.distances !== undefined)} {#if citations.every((c) => c.distances !== undefined)}
<div class="bg-gray-50 dark:bg-gray-800 rounded-full size-4"> <div class="bg-gray-50 dark:bg-gray-800 rounded-full size-4">
{idx + 1} {idx + 1}
</div> </div>
@@ -38,7 +38,9 @@
}; };
}); });
if (mergedDocuments.every((doc) => doc.distance !== undefined)) { if (mergedDocuments.every((doc) => doc.distance !== undefined)) {
mergedDocuments.sort((a, b) => (a.distance ?? Infinity) - (b.distance ?? Infinity)); mergedDocuments = mergedDocuments.sort(
(a, b) => (b.distance ?? Infinity) - (a.distance ?? Infinity)
);
} }
} }
</script> </script>
@@ -7,13 +7,16 @@
import LightBlub from '$lib/components/icons/LightBlub.svelte'; import LightBlub from '$lib/components/icons/LightBlub.svelte';
import { chatId, mobile, showArtifacts, showControls, showOverview } from '$lib/stores'; import { chatId, mobile, showArtifacts, showControls, showOverview } from '$lib/stores';
import ChatBubble from '$lib/components/icons/ChatBubble.svelte'; import ChatBubble from '$lib/components/icons/ChatBubble.svelte';
import { stringify } from 'postcss';
export let id; export let id;
export let content; export let content;
export let model = null; export let model = null;
export let sources = null;
export let save = false; export let save = false;
export let floatingButtons = true; export let floatingButtons = true;
export let onSourceClick = () => {};
let contentContainerElement; let contentContainerElement;
let buttonsContainerElement; let buttonsContainerElement;
@@ -129,6 +132,32 @@
{content} {content}
{model} {model}
{save} {save}
sourceIds={(sources ?? []).reduce((acc, s) => {
let ids = [];
s.document.forEach((document, index) => {
const metadata = s.metadata?.[index];
const id = metadata?.source ?? 'N/A';
if (metadata?.name) {
ids.push(metadata.name);
return ids;
}
if (id.startsWith('http://') || id.startsWith('https://')) {
ids.push(id);
} else {
ids.push(s?.source?.name ?? id);
}
return ids;
});
acc = [...acc, ...ids];
// remove duplicates
return acc.filter((item, index) => acc.indexOf(item) === index);
}, [])}
{onSourceClick}
on:update={(e) => { on:update={(e) => {
dispatch('update', e.detail); dispatch('update', e.detail);
}} }}
@@ -4,9 +4,9 @@
export let content = ''; export let content = '';
</script> </script>
<div class="flex my-2 gap-2.5 border px-4 py-3 border-red-800 bg-red-800/30 rounded-lg"> <div class="flex my-2 gap-2.5 border px-4 py-3 border-red-600/10 bg-red-600/10 rounded-lg">
<div class=" self-start mt-0.5"> <div class=" self-start mt-0.5">
<Info className="size-5" /> <Info className="size-5 text-red-700 dark:text-red-400" />
</div> </div>
<div class=" self-center text-sm"> <div class=" self-center text-sm">
@@ -16,6 +16,9 @@
export let model = null; export let model = null;
export let save = false; export let save = false;
export let sourceIds = [];
export let onSourceClick = () => {};
let tokens = []; let tokens = [];
const options = { const options = {
@@ -28,7 +31,7 @@
$: (async () => { $: (async () => {
if (content) { if (content) {
tokens = marked.lexer( tokens = marked.lexer(
replaceTokens(processResponseContent(content), model?.name, $user?.name) replaceTokens(processResponseContent(content), sourceIds, model?.name, $user?.name)
); );
} }
})(); })();
@@ -39,6 +42,7 @@
{tokens} {tokens}
{id} {id}
{save} {save}
{onSourceClick}
on:update={(e) => { on:update={(e) => {
dispatch('update', e.detail); dispatch('update', e.detail);
}} }}
@@ -12,9 +12,11 @@
import Image from '$lib/components/common/Image.svelte'; import Image from '$lib/components/common/Image.svelte';
import KatexRenderer from './KatexRenderer.svelte'; import KatexRenderer from './KatexRenderer.svelte';
import Source from './Source.svelte';
export let id: string; export let id: string;
export let tokens: Token[]; export let tokens: Token[];
export let onSourceClick: Function = () => {};
</script> </script>
{#each tokens as token} {#each tokens as token}
@@ -26,13 +28,15 @@
{@html html} {@html html}
{:else if token.text.includes(`<iframe src="${WEBUI_BASE_URL}/api/v1/files/`)} {:else if token.text.includes(`<iframe src="${WEBUI_BASE_URL}/api/v1/files/`)}
{@html `${token.text}`} {@html `${token.text}`}
{:else if token.text.includes(`<source_id`)}
<Source {token} onClick={onSourceClick} />
{:else} {:else}
{token.text} {token.text}
{/if} {/if}
{:else if token.type === 'link'} {:else if token.type === 'link'}
{#if token.tokens} {#if token.tokens}
<a href={token.href} target="_blank" rel="nofollow" title={token.title}> <a href={token.href} target="_blank" rel="nofollow" title={token.title}>
<svelte:self id={`${id}-a`} tokens={token.tokens} /> <svelte:self id={`${id}-a`} tokens={token.tokens} {onSourceClick} />
</a> </a>
{:else} {:else}
<a href={token.href} target="_blank" rel="nofollow" title={token.title}>{token.text}</a> <a href={token.href} target="_blank" rel="nofollow" title={token.title}>{token.text}</a>
@@ -41,11 +45,11 @@
<Image src={token.href} alt={token.text} /> <Image src={token.href} alt={token.text} />
{:else if token.type === 'strong'} {:else if token.type === 'strong'}
<strong> <strong>
<svelte:self id={`${id}-strong`} tokens={token.tokens} /> <svelte:self id={`${id}-strong`} tokens={token.tokens} {onSourceClick} />
</strong> </strong>
{:else if token.type === 'em'} {:else if token.type === 'em'}
<em> <em>
<svelte:self id={`${id}-em`} tokens={token.tokens} /> <svelte:self id={`${id}-em`} tokens={token.tokens} {onSourceClick} />
</em> </em>
{:else if token.type === 'codespan'} {:else if token.type === 'codespan'}
<!-- svelte-ignore a11y-click-events-have-key-events --> <!-- svelte-ignore a11y-click-events-have-key-events -->
@@ -61,7 +65,7 @@
<br /> <br />
{:else if token.type === 'del'} {:else if token.type === 'del'}
<del> <del>
<svelte:self id={`${id}-del`} tokens={token.tokens} /> <svelte:self id={`${id}-del`} tokens={token.tokens} {onSourceClick} />
</del> </del>
{:else if token.type === 'inlineKatex'} {:else if token.type === 'inlineKatex'}
{#if token.text} {#if token.text}
@@ -25,6 +25,7 @@
export let top = true; export let top = true;
export let save = false; export let save = false;
export let onSourceClick: Function = () => {};
const headerComponent = (depth: number) => { const headerComponent = (depth: number) => {
return 'h' + depth; return 'h' + depth;
@@ -59,10 +60,10 @@
<!-- {JSON.stringify(tokens)} --> <!-- {JSON.stringify(tokens)} -->
{#each tokens as token, tokenIdx (tokenIdx)} {#each tokens as token, tokenIdx (tokenIdx)}
{#if token.type === 'hr'} {#if token.type === 'hr'}
<hr /> <hr class=" border-gray-50 dark:border-gray-850" />
{:else if token.type === 'heading'} {:else if token.type === 'heading'}
<svelte:element this={headerComponent(token.depth)}> <svelte:element this={headerComponent(token.depth)}>
<MarkdownInlineTokens id={`${id}-${tokenIdx}-h`} tokens={token.tokens} /> <MarkdownInlineTokens id={`${id}-${tokenIdx}-h`} tokens={token.tokens} {onSourceClick} />
</svelte:element> </svelte:element>
{:else if token.type === 'code'} {:else if token.type === 'code'}
{#if token.raw.includes('```')} {#if token.raw.includes('```')}
@@ -88,11 +89,9 @@
{/if} {/if}
{:else if token.type === 'table'} {:else if token.type === 'table'}
<div class="relative w-full group"> <div class="relative w-full group">
<div <div class="scrollbar-hidden relative overflow-x-auto max-w-full rounded-lg">
class="scrollbar-hidden relative whitespace-nowrap overflow-x-auto max-w-full rounded-lg"
>
<table <table
class="table-auto w-full text-sm text-left text-gray-500 dark:text-gray-400 max-w-full rounded-xl" class=" w-full text-sm text-left text-gray-500 dark:text-gray-400 max-w-full rounded-xl"
> >
<thead <thead
class="text-xs text-gray-700 uppercase bg-gray-50 dark:bg-gray-850 dark:text-gray-400 border-none" class="text-xs text-gray-700 uppercase bg-gray-50 dark:bg-gray-850 dark:text-gray-400 border-none"
@@ -101,14 +100,17 @@
{#each token.header as header, headerIdx} {#each token.header as header, headerIdx}
<th <th
scope="col" scope="col"
class="!px-2 !py-1.5 cursor-pointer select-none border border-gray-50 dark:border-gray-850" class="!px-3 !py-1.5 cursor-pointer select-none border border-gray-50 dark:border-gray-850"
style={token.align[headerIdx] ? '' : `text-align: ${token.align[headerIdx]}`} style={token.align[headerIdx] ? '' : `text-align: ${token.align[headerIdx]}`}
> >
<div class="flex gap-1.5 items-center"> <div class="flex flex-col gap-1.5 text-left">
<MarkdownInlineTokens <div class="flex-shrink-0 break-normal">
id={`${id}-${tokenIdx}-header-${headerIdx}`} <MarkdownInlineTokens
tokens={header.tokens} id={`${id}-${tokenIdx}-header-${headerIdx}`}
/> tokens={header.tokens}
{onSourceClick}
/>
</div>
</div> </div>
</th> </th>
{/each} {/each}
@@ -119,13 +121,14 @@
<tr class="bg-white dark:bg-gray-900 dark:border-gray-850 text-xs"> <tr class="bg-white dark:bg-gray-900 dark:border-gray-850 text-xs">
{#each row ?? [] as cell, cellIdx} {#each row ?? [] as cell, cellIdx}
<td <td
class="!px-2 !py-1.5 font-medium text-gray-900 dark:text-white w-max border border-gray-50 dark:border-gray-850" class="!px-3 !py-1.5 text-gray-900 dark:text-white w-max border border-gray-50 dark:border-gray-850"
style={token.align[cellIdx] ? '' : `text-align: ${token.align[cellIdx]}`} style={token.align[cellIdx] ? '' : `text-align: ${token.align[cellIdx]}`}
> >
<div class="flex"> <div class="flex flex-col break-normal">
<MarkdownInlineTokens <MarkdownInlineTokens
id={`${id}-${tokenIdx}-row-${rowIdx}-${cellIdx}`} id={`${id}-${tokenIdx}-row-${rowIdx}-${cellIdx}`}
tokens={cell.tokens} tokens={cell.tokens}
{onSourceClick}
/> />
</div> </div>
</td> </td>
@@ -205,19 +208,27 @@
></iframe> ></iframe>
{:else if token.type === 'paragraph'} {:else if token.type === 'paragraph'}
<p> <p>
<MarkdownInlineTokens id={`${id}-${tokenIdx}-p`} tokens={token.tokens ?? []} /> <MarkdownInlineTokens
id={`${id}-${tokenIdx}-p`}
tokens={token.tokens ?? []}
{onSourceClick}
/>
</p> </p>
{:else if token.type === 'text'} {:else if token.type === 'text'}
{#if top} {#if top}
<p> <p>
{#if token.tokens} {#if token.tokens}
<MarkdownInlineTokens id={`${id}-${tokenIdx}-t`} tokens={token.tokens} /> <MarkdownInlineTokens id={`${id}-${tokenIdx}-t`} tokens={token.tokens} {onSourceClick} />
{:else} {:else}
{unescapeHtml(token.text)} {unescapeHtml(token.text)}
{/if} {/if}
</p> </p>
{:else if token.tokens} {:else if token.tokens}
<MarkdownInlineTokens id={`${id}-${tokenIdx}-p`} tokens={token.tokens ?? []} /> <MarkdownInlineTokens
id={`${id}-${tokenIdx}-p`}
tokens={token.tokens ?? []}
{onSourceClick}
/>
{:else} {:else}
{unescapeHtml(token.text)} {unescapeHtml(token.text)}
{/if} {/if}
@@ -0,0 +1,25 @@
<script lang="ts">
export let token;
export let onClick: Function = () => {};
let id = '';
function extractDataAttribute(input) {
// Use a regular expression to extract the value of the `data` attribute
const match = input.match(/data="([^"]*)"/);
// Check if a match was found and return the first captured group
return match ? match[1] : null;
}
$: id = extractDataAttribute(token.text);
</script>
<button
class="text-xs font-medium w-fit translate-y-[2px] px-2 py-0.5 dark:bg-white/5 dark:text-white/60 dark:hover:text-white bg-gray-50 text-black/60 hover:text-black transition rounded-lg"
on:click={() => {
onClick(id);
}}
>
<span class="line-clamp-1">
{id}
</span>
</button>
@@ -38,6 +38,7 @@
let selectedReason = null; let selectedReason = null;
let comment = ''; let comment = '';
let detailedRating = null;
let selectedModel = null; let selectedModel = null;
$: if (message?.annotation?.rating === 1) { $: if (message?.annotation?.rating === 1) {
@@ -56,6 +57,7 @@
tags = (message?.annotation?.tags ?? []).map((tag) => ({ tags = (message?.annotation?.tags ?? []).map((tag) => ({
name: tag name: tag
})); }));
detailedRating = message?.annotation?.details?.rating ?? null;
}; };
onMount(() => { onMount(() => {
@@ -79,7 +81,10 @@
dispatch('save', { dispatch('save', {
reason: selectedReason, reason: selectedReason,
comment: comment, comment: comment,
tags: tags.map((tag) => tag.name) tags: tags.map((tag) => tag.name),
details: {
rating: detailedRating
}
}); });
toast.success($i18n.t('Thanks for your feedback!')); toast.success($i18n.t('Thanks for your feedback!'));
@@ -100,7 +105,9 @@
id="message-feedback-{message.id}" id="message-feedback-{message.id}"
> >
<div class="flex justify-between items-center"> <div class="flex justify-between items-center">
<div class=" text-sm">{$i18n.t('Tell us more:')}</div> <div class="text-sm font-medium">{$i18n.t('How would you rate this response?')}</div>
<!-- <div class=" text-sm">{$i18n.t('Tell us more:')}</div> -->
<button <button
on:click={() => { on:click={() => {
@@ -120,53 +127,89 @@
</button> </button>
</div> </div>
{#if reasons.length > 0} <div class="w-full flex justify-center">
<div class="flex flex-wrap gap-1.5 text-sm mt-2.5"> <div class=" relative w-fit">
{#each reasons as reason} <div class="mt-1.5 w-fit flex gap-1 pb-5">
<button <!-- 1-10 scale -->
class="px-3 py-0.5 border border-gray-50 dark:border-gray-850 hover:bg-gray-100 dark:hover:bg-gray-850 {selectedReason === {#each Array.from({ length: 10 }).map((_, i) => i + 1) as rating}
reason <button
? 'bg-gray-200 dark:bg-gray-800' class="size-7 text-sm border border-gray-50 dark:border-gray-850 hover:bg-gray-50 dark:hover:bg-gray-850 {detailedRating ===
: ''} transition rounded-lg" rating
on:click={() => { ? 'bg-gray-100 dark:bg-gray-800'
selectedReason = reason; : ''} transition rounded-full disabled:cursor-not-allowed disabled:text-gray-500 disabled:bg-white disabled:dark:bg-gray-900"
}} on:click={() => {
> detailedRating = rating;
{#if reason === 'accurate_information'} }}
{$i18n.t('Accurate information')} disabled={message?.annotation?.rating === -1 ? rating > 5 : rating < 6}
{:else if reason === 'followed_instructions_perfectly'} >
{$i18n.t('Followed instructions perfectly')} {rating}
{:else if reason === 'showcased_creativity'} </button>
{$i18n.t('Showcased creativity')} {/each}
{:else if reason === 'positive_attitude'} </div>
{$i18n.t('Positive attitude')}
{:else if reason === 'attention_to_detail'} <div class="absolute bottom-0 left-0 right-0 flex justify-between text-xs">
{$i18n.t('Attention to detail')} <div>
{:else if reason === 'thorough_explanation'} 1 - {$i18n.t('Awful')}
{$i18n.t('Thorough explanation')} </div>
{:else if reason === 'dont_like_the_style'}
{$i18n.t("Don't like the style")} <div>
{:else if reason === 'too_verbose'} 10 - {$i18n.t('Amazing')}
{$i18n.t('Too verbose')} </div>
{:else if reason === 'not_helpful'} </div>
{$i18n.t('Not helpful')}
{:else if reason === 'not_factually_correct'}
{$i18n.t('Not factually correct')}
{:else if reason === 'didnt_fully_follow_instructions'}
{$i18n.t("Didn't fully follow instructions")}
{:else if reason === 'refused_when_it_shouldnt_have'}
{$i18n.t("Refused when it shouldn't have")}
{:else if reason === 'being_lazy'}
{$i18n.t('Being lazy')}
{:else if reason === 'other'}
{$i18n.t('Other')}
{:else}
{reason}
{/if}
</button>
{/each}
</div> </div>
{/if} </div>
<div>
{#if reasons.length > 0}
<div class="text-sm mt-1.5 font-medium">{$i18n.t('Why?')}</div>
<div class="flex flex-wrap gap-1.5 text-sm mt-1.5">
{#each reasons as reason}
<button
class="px-3 py-0.5 border border-gray-50 dark:border-gray-850 hover:bg-gray-50 dark:hover:bg-gray-850 {selectedReason ===
reason
? 'bg-gray-100 dark:bg-gray-800'
: ''} transition rounded-xl"
on:click={() => {
selectedReason = reason;
}}
>
{#if reason === 'accurate_information'}
{$i18n.t('Accurate information')}
{:else if reason === 'followed_instructions_perfectly'}
{$i18n.t('Followed instructions perfectly')}
{:else if reason === 'showcased_creativity'}
{$i18n.t('Showcased creativity')}
{:else if reason === 'positive_attitude'}
{$i18n.t('Positive attitude')}
{:else if reason === 'attention_to_detail'}
{$i18n.t('Attention to detail')}
{:else if reason === 'thorough_explanation'}
{$i18n.t('Thorough explanation')}
{:else if reason === 'dont_like_the_style'}
{$i18n.t("Don't like the style")}
{:else if reason === 'too_verbose'}
{$i18n.t('Too verbose')}
{:else if reason === 'not_helpful'}
{$i18n.t('Not helpful')}
{:else if reason === 'not_factually_correct'}
{$i18n.t('Not factually correct')}
{:else if reason === 'didnt_fully_follow_instructions'}
{$i18n.t("Didn't fully follow instructions")}
{:else if reason === 'refused_when_it_shouldnt_have'}
{$i18n.t("Refused when it shouldn't have")}
{:else if reason === 'being_lazy'}
{$i18n.t('Being lazy')}
{:else if reason === 'other'}
{$i18n.t('Other')}
{:else}
{reason}
{/if}
</button>
{/each}
</div>
{/if}
</div>
<div class="mt-2"> <div class="mt-2">
<textarea <textarea
@@ -195,7 +238,7 @@
</div> </div>
<button <button
class=" bg-emerald-700 hover:bg-emerald-800 transition text-white text-sm font-medium rounded-xl px-3.5 py-1.5" class="px-3.5 py-1.5 text-sm font-medium bg-black hover:bg-gray-900 text-white dark:bg-white dark:text-black dark:hover:bg-gray-100 transition rounded-full"
on:click={() => { on:click={() => {
saveHandler(); saveHandler();
}} }}
@@ -64,7 +64,7 @@
}; };
done: boolean; done: boolean;
error?: boolean | { content: string }; error?: boolean | { content: string };
citations?: string[]; sources?: string[];
code_executions?: { code_executions?: {
uuid: string; uuid: string;
name: string; name: string;
@@ -340,19 +340,16 @@
let feedbackLoading = false; let feedbackLoading = false;
const feedbackHandler = async ( const feedbackHandler = async (rating: number | null = null, details: object | null = null) => {
rating: number | null = null,
annotation: object | null = null
) => {
feedbackLoading = true; feedbackLoading = true;
console.log('Feedback', rating, annotation); console.log('Feedback', rating, details);
const updatedMessage = { const updatedMessage = {
...message, ...message,
annotation: { annotation: {
...(message?.annotation ?? {}), ...(message?.annotation ?? {}),
...(rating !== null ? { rating: rating } : {}), ...(rating !== null ? { rating: rating } : {}),
...(annotation ? annotation : {}) ...(details ? details : {})
} }
}; };
@@ -429,7 +426,7 @@
await tick(); await tick();
if (!annotation) { if (!details) {
showRateComment = true; showRateComment = true;
if (!updatedMessage.annotation?.tags) { if (!updatedMessage.annotation?.tags) {
@@ -624,9 +621,18 @@
<ContentRenderer <ContentRenderer
id={message.id} id={message.id}
content={message.content} content={message.content}
sources={message.sources}
floatingButtons={message?.done} floatingButtons={message?.done}
save={!readOnly} save={!readOnly}
{model} {model}
onSourceClick={(e) => {
console.log(e);
const sourceButton = document.getElementById(`source-${e}`);
if (sourceButton) {
sourceButton.click();
}
}}
on:update={(e) => { on:update={(e) => {
const { raw, oldContent, newContent } = e.detail; const { raw, oldContent, newContent } = e.detail;
@@ -652,12 +658,12 @@
/> />
{/if} {/if}
{#if message.error} {#if message?.error}
<Error content={message?.error?.content ?? message.content} /> <Error content={message?.error?.content ?? message.content} />
{/if} {/if}
{#if message.citations && (model?.info?.meta?.capabilities?.citations ?? true)} {#if (message?.sources || message?.citations) && (model?.info?.meta?.capabilities?.citations ?? true)}
<Citations citations={message.citations} /> <Citations sources={message?.sources ?? message?.citations} />
{/if} {/if}
{#if message.code_executions} {#if message.code_executions}
@@ -1194,9 +1200,7 @@
bind:show={showRateComment} bind:show={showRateComment}
on:save={async (e) => { on:save={async (e) => {
await feedbackHandler(null, { await feedbackHandler(null, {
tags: e.detail.tags, ...e.detail
comment: e.detail.comment,
reason: e.detail.reason
}); });
}} }}
/> />
@@ -5,11 +5,7 @@
import { models, settings } from '$lib/stores'; import { models, settings } from '$lib/stores';
import { user as _user } from '$lib/stores'; import { user as _user } from '$lib/stores';
import { import { copyToClipboard as _copyToClipboard } from '$lib/utils';
copyToClipboard as _copyToClipboard,
processResponseContent,
replaceTokens
} from '$lib/utils';
import Name from './Name.svelte'; import Name from './Name.svelte';
import ProfileImage from './ProfileImage.svelte'; import ProfileImage from './ProfileImage.svelte';
@@ -5,9 +5,7 @@
import Selector from './ModelSelector/Selector.svelte'; import Selector from './ModelSelector/Selector.svelte';
import Tooltip from '../common/Tooltip.svelte'; import Tooltip from '../common/Tooltip.svelte';
import { setDefaultModels } from '$lib/apis/configs';
import { updateUserSettings } from '$lib/apis/users'; import { updateUserSettings } from '$lib/apis/users';
const i18n = getContext('i18n'); const i18n = getContext('i18n');
export let selectedModels = ['']; export let selectedModels = [''];
@@ -55,6 +55,7 @@
mirostat_tau: null, mirostat_tau: null,
top_k: null, top_k: null,
top_p: null, top_p: null,
min_p: null,
stop: null, stop: null,
tfs_z: null, tfs_z: null,
num_ctx: null, num_ctx: null,
@@ -340,6 +341,7 @@
mirostat_tau: params.mirostat_tau !== null ? params.mirostat_tau : undefined, mirostat_tau: params.mirostat_tau !== null ? params.mirostat_tau : undefined,
top_k: params.top_k !== null ? params.top_k : undefined, top_k: params.top_k !== null ? params.top_k : undefined,
top_p: params.top_p !== null ? params.top_p : undefined, top_p: params.top_p !== null ? params.top_p : undefined,
min_p: params.min_p !== null ? params.min_p : undefined,
tfs_z: params.tfs_z !== null ? params.tfs_z : undefined, tfs_z: params.tfs_z !== null ? params.tfs_z : undefined,
num_ctx: params.num_ctx !== null ? params.num_ctx : undefined, num_ctx: params.num_ctx !== null ? params.num_ctx : undefined,
num_batch: params.num_batch !== null ? params.num_batch : undefined, num_batch: params.num_batch !== null ? params.num_batch : undefined,
+3 -1
View File
@@ -2,6 +2,8 @@
import type { Banner } from '$lib/types'; import type { Banner } from '$lib/types';
import { onMount, createEventDispatcher } from 'svelte'; import { onMount, createEventDispatcher } from 'svelte';
import { fade } from 'svelte/transition'; import { fade } from 'svelte/transition';
import DOMPurify from 'dompurify';
import { marked } from 'marked';
const dispatch = createEventDispatcher(); const dispatch = createEventDispatcher();
@@ -81,7 +83,7 @@
</div> </div>
<div class="flex-1 text-xs text-gray-700 dark:text-white"> <div class="flex-1 text-xs text-gray-700 dark:text-white">
{banner.content} {@html marked.parse(DOMPurify.sanitize(banner.content))}
</div> </div>
</div> </div>
+62 -40
View File
@@ -5,6 +5,7 @@
import FileItemModal from './FileItemModal.svelte'; import FileItemModal from './FileItemModal.svelte';
import GarbageBin from '../icons/GarbageBin.svelte'; import GarbageBin from '../icons/GarbageBin.svelte';
import Spinner from './Spinner.svelte'; import Spinner from './Spinner.svelte';
import Tooltip from './Tooltip.svelte';
const i18n = getContext('i18n'); const i18n = getContext('i18n');
const dispatch = createEventDispatcher(); const dispatch = createEventDispatcher();
@@ -18,6 +19,7 @@
export let item = null; export let item = null;
export let edit = false; export let edit = false;
export let small = false;
export let name: string; export let name: string;
export let type: string; export let type: string;
@@ -31,7 +33,9 @@
{/if} {/if}
<button <button
class="relative group p-1.5 {className} flex items-center {colorClassName} rounded-2xl text-left" class="relative group p-1.5 {className} flex items-center gap-1 {colorClassName} {small
? 'rounded-xl'
: 'rounded-2xl'} text-left"
type="button" type="button"
on:click={async () => { on:click={async () => {
if (item?.file?.data?.content) { if (item?.file?.data?.content) {
@@ -49,48 +53,66 @@
dispatch('click'); dispatch('click');
}} }}
> >
<div class="p-3 bg-black/20 dark:bg-white/10 text-white rounded-xl"> {#if !small}
{#if !loading} <div class="p-3 bg-black/20 dark:bg-white/10 text-white rounded-xl">
<svg {#if !loading}
xmlns="http://www.w3.org/2000/svg" <svg
viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"
fill="currentColor" viewBox="0 0 24 24"
class=" size-5" fill="currentColor"
> class=" size-5"
<path >
fill-rule="evenodd" <path
d="M5.625 1.5c-1.036 0-1.875.84-1.875 1.875v17.25c0 1.035.84 1.875 1.875 1.875h12.75c1.035 0 1.875-.84 1.875-1.875V12.75A3.75 3.75 0 0 0 16.5 9h-1.875a1.875 1.875 0 0 1-1.875-1.875V5.25A3.75 3.75 0 0 0 9 1.5H5.625ZM7.5 15a.75.75 0 0 1 .75-.75h7.5a.75.75 0 0 1 0 1.5h-7.5A.75.75 0 0 1 7.5 15Zm.75 2.25a.75.75 0 0 0 0 1.5H12a.75.75 0 0 0 0-1.5H8.25Z" fill-rule="evenodd"
clip-rule="evenodd" d="M5.625 1.5c-1.036 0-1.875.84-1.875 1.875v17.25c0 1.035.84 1.875 1.875 1.875h12.75c1.035 0 1.875-.84 1.875-1.875V12.75A3.75 3.75 0 0 0 16.5 9h-1.875a1.875 1.875 0 0 1-1.875-1.875V5.25A3.75 3.75 0 0 0 9 1.5H5.625ZM7.5 15a.75.75 0 0 1 .75-.75h7.5a.75.75 0 0 1 0 1.5h-7.5A.75.75 0 0 1 7.5 15Zm.75 2.25a.75.75 0 0 0 0 1.5H12a.75.75 0 0 0 0-1.5H8.25Z"
/> clip-rule="evenodd"
<path />
d="M12.971 1.816A5.23 5.23 0 0 1 14.25 5.25v1.875c0 .207.168.375.375.375H16.5a5.23 5.23 0 0 1 3.434 1.279 9.768 9.768 0 0 0-6.963-6.963Z" <path
/> d="M12.971 1.816A5.23 5.23 0 0 1 14.25 5.25v1.875c0 .207.168.375.375.375H16.5a5.23 5.23 0 0 1 3.434 1.279 9.768 9.768 0 0 0-6.963-6.963Z"
</svg> />
{:else} </svg>
<Spinner />
{/if}
</div>
<div class="flex flex-col justify-center -space-y-0.5 ml-1 px-2.5 w-full">
<div class=" dark:text-gray-100 text-sm font-medium line-clamp-1 mb-1">
{name}
</div>
<div class=" flex justify-between text-gray-500 text-xs line-clamp-1">
{#if type === 'file'}
{$i18n.t('File')}
{:else if type === 'doc'}
{$i18n.t('Document')}
{:else if type === 'collection'}
{$i18n.t('Collection')}
{:else} {:else}
<span class=" capitalize line-clamp-1">{type}</span> <Spinner />
{/if}
{#if size}
<span class="capitalize">{formatFileSize(size)}</span>
{/if} {/if}
</div> </div>
</div> {/if}
{#if !small}
<div class="flex flex-col justify-center -space-y-0.5 px-2.5 w-full">
<div class=" dark:text-gray-100 text-sm font-medium line-clamp-1 mb-1">
{name}
</div>
<div class=" flex justify-between text-gray-500 text-xs line-clamp-1">
{#if type === 'file'}
{$i18n.t('File')}
{:else if type === 'doc'}
{$i18n.t('Document')}
{:else if type === 'collection'}
{$i18n.t('Collection')}
{:else}
<span class=" capitalize line-clamp-1">{type}</span>
{/if}
{#if size}
<span class="capitalize">{formatFileSize(size)}</span>
{/if}
</div>
</div>
{:else}
<Tooltip content={name} className="flex flex-col w-full" placement="top-start">
<div class="flex flex-col justify-center -space-y-0.5 px-2.5 w-full">
<div class=" dark:text-gray-100 text-sm flex justify-between items-center">
{#if loading}
<div class=" shrink-0 mr-2">
<Spinner className="size-4" />
</div>
{/if}
<div class="font-medium line-clamp-1 flex-1">{name}</div>
<div class="text-gray-500 text-xs capitalize shrink-0">{formatFileSize(size)}</div>
</div>
</div>
</Tooltip>
{/if}
{#if dismissible} {#if dismissible}
<div class=" absolute -top-1 -right-1"> <div class=" absolute -top-1 -right-1">
+5 -3
View File
@@ -6,7 +6,9 @@
export let show = true; export let show = true;
export let size = 'md'; export let size = 'md';
export let className = 'bg-gray-50 dark:bg-gray-900 rounded-2xl';
export let containerClassName = 'p-3';
export let className = 'bg-gray-50 dark:bg-gray-900 rounded-2xl';
let modalElement = null; let modalElement = null;
let mounted = false; let mounted = false;
@@ -65,7 +67,7 @@
<!-- svelte-ignore a11y-no-static-element-interactions --> <!-- svelte-ignore a11y-no-static-element-interactions -->
<div <div
bind:this={modalElement} bind:this={modalElement}
class="modal fixed top-0 right-0 left-0 bottom-0 bg-black/60 w-full h-screen max-h-[100dvh] flex justify-center z-[9999] overflow-hidden overscroll-contain" class="modal fixed top-0 right-0 left-0 bottom-0 bg-black/60 w-full h-screen max-h-[100dvh] {containerClassName} flex justify-center z-[9999] overflow-y-auto overscroll-contain"
in:fade={{ duration: 10 }} in:fade={{ duration: 10 }}
on:mousedown={() => { on:mousedown={() => {
show = false; show = false;
@@ -74,7 +76,7 @@
<div <div
class=" m-auto max-w-full {sizeToWidth(size)} {size !== 'full' class=" m-auto max-w-full {sizeToWidth(size)} {size !== 'full'
? 'mx-2' ? 'mx-2'
: ''} shadow-3xl max-h-[100dvh] overflow-y-auto scrollbar-hidden {className}" : ''} shadow-3xl min-h-fit scrollbar-hidden {className}"
in:flyAndScale in:flyAndScale
on:mousedown={(e) => { on:mousedown={(e) => {
e.stopPropagation(); e.stopPropagation();
+265 -416
View File
@@ -1,241 +1,55 @@
<script lang="ts"> <script lang="ts">
import { onDestroy, onMount } from 'svelte'; import { marked } from 'marked';
import TurndownService from 'turndown';
const turndownService = new TurndownService({
codeBlockStyle: 'fenced',
headingStyle: 'atx'
});
turndownService.escape = (string) => string;
import { onMount, onDestroy } from 'svelte';
import { createEventDispatcher } from 'svelte'; import { createEventDispatcher } from 'svelte';
const eventDispatch = createEventDispatcher(); const eventDispatch = createEventDispatcher();
import { EditorState, Plugin, TextSelection } from 'prosemirror-state'; import { EditorState, Plugin, PluginKey, TextSelection } from 'prosemirror-state';
import { EditorView, Decoration, DecorationSet } from 'prosemirror-view'; import { Decoration, DecorationSet } from 'prosemirror-view';
import { undo, redo, history } from 'prosemirror-history';
import { import { Editor } from '@tiptap/core';
schema,
defaultMarkdownParser, import { AIAutocompletion } from './RichTextInput/AutoCompletion.js';
MarkdownParser,
defaultMarkdownSerializer import CodeBlockLowlight from '@tiptap/extension-code-block-lowlight';
} from 'prosemirror-markdown'; import Placeholder from '@tiptap/extension-placeholder';
import Highlight from '@tiptap/extension-highlight';
import Typography from '@tiptap/extension-typography';
import StarterKit from '@tiptap/starter-kit';
import { all, createLowlight } from 'lowlight';
import {
inputRules,
wrappingInputRule,
textblockTypeInputRule,
InputRule
} from 'prosemirror-inputrules'; // Import input rules
import { splitListItem, liftListItem, sinkListItem } from 'prosemirror-schema-list'; // Import from prosemirror-schema-list
import { keymap } from 'prosemirror-keymap';
import { baseKeymap, chainCommands } from 'prosemirror-commands';
import { DOMParser, DOMSerializer, Schema, Fragment } from 'prosemirror-model';
import { PASTED_TEXT_CHARACTER_LIMIT } from '$lib/constants'; import { PASTED_TEXT_CHARACTER_LIMIT } from '$lib/constants';
// create a lowlight instance with all languages loaded
const lowlight = createLowlight(all);
export let className = 'input-prose'; export let className = 'input-prose';
export let placeholder = 'Type here...';
export let value = '';
export let id = '';
export let preserveBreaks = false;
export let generateAutoCompletion: Function = async () => null;
export let autocomplete = false;
export let messageInput = false;
export let shiftEnter = false; export let shiftEnter = false;
export let largeTextAsFile = false; export let largeTextAsFile = false;
export let id = ''; let element;
export let value = ''; let editor;
export let placeholder = 'Type here...';
export let trim = false;
let element: HTMLElement; // Element where ProseMirror will attach const options = {
let state; throwOnError: false
let view; };
// Plugin to add placeholder when the content is empty
function placeholderPlugin(placeholder: string) {
return new Plugin({
props: {
decorations(state) {
const doc = state.doc;
if (
doc.childCount === 1 &&
doc.firstChild.isTextblock &&
doc.firstChild?.textContent === ''
) {
// If there's nothing in the editor, show the placeholder decoration
const decoration = Decoration.node(0, doc.content.size, {
'data-placeholder': placeholder,
class: 'placeholder'
});
return DecorationSet.create(doc, [decoration]);
}
return DecorationSet.empty;
}
}
});
}
function unescapeMarkdown(text: string): string {
return text
.replace(/\\([\\`*{}[\]()#+\-.!_>])/g, '$1') // unescape backslashed characters
.replace(/&amp;/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/&quot;/g, '"')
.replace(/&#39;/g, "'");
}
// Custom parsing rule that creates proper paragraphs for newlines and empty lines
function markdownToProseMirrorDoc(markdown: string) {
// Split the markdown into lines
const lines = markdown.split('\n\n');
// Create an array to hold our paragraph nodes
const paragraphs = [];
// Process each line
lines.forEach((line) => {
if (line.trim() === '') {
// For empty lines, create an empty paragraph
paragraphs.push(schema.nodes.paragraph.create());
} else {
// For non-empty lines, parse as usual
const doc = defaultMarkdownParser.parse(line);
// Extract the content of the parsed document
doc.content.forEach((node) => {
paragraphs.push(node);
});
}
});
// Create a new document with these paragraphs
return schema.node('doc', null, paragraphs);
}
// Create a custom serializer for paragraphs
// Custom paragraph serializer to preserve newlines for empty paragraphs (empty block).
function serializeParagraph(state, node: Node) {
const content = node.textContent.trim();
// If the paragraph is empty, just add an empty line.
if (content === '') {
state.write('\n\n');
} else {
state.renderInline(node);
state.closeBlock(node);
}
}
const customMarkdownSerializer = new defaultMarkdownSerializer.constructor(
{
...defaultMarkdownSerializer.nodes,
paragraph: (state, node) => {
serializeParagraph(state, node); // Use custom paragraph serialization
}
// Customize other block formats if needed
},
// Copy marks directly from the original serializer (or customize them if necessary)
defaultMarkdownSerializer.marks
);
// Utility function to convert ProseMirror content back to markdown text
function serializeEditorContent(doc) {
const markdown = customMarkdownSerializer.serialize(doc);
if (trim) {
return unescapeMarkdown(markdown).trim();
} else {
return unescapeMarkdown(markdown);
}
}
// ---- Input Rules ----
// Input rule for heading (e.g., # Headings)
function headingRule(schema) {
return textblockTypeInputRule(/^(#{1,6})\s$/, schema.nodes.heading, (match) => ({
level: match[1].length
}));
}
// Input rule for bullet list (e.g., `- item`)
function bulletListRule(schema) {
return wrappingInputRule(/^\s*([-+*])\s$/, schema.nodes.bullet_list);
}
// Input rule for ordered list (e.g., `1. item`)
function orderedListRule(schema) {
return wrappingInputRule(/^(\d+)\.\s$/, schema.nodes.ordered_list, (match) => ({
order: +match[1]
}));
}
// Custom input rules for Bold/Italic (using * or _)
function markInputRule(regexp: RegExp, markType: any) {
return new InputRule(regexp, (state, match, start, end) => {
const { tr } = state;
if (match) {
tr.replaceWith(start, end, schema.text(match[1], [markType.create()]));
}
return tr;
});
}
function boldRule(schema) {
return markInputRule(/(?<=^|\s)\*([^*]+)\*(?=\s|$)/, schema.marks.strong);
}
function italicRule(schema) {
// Using lookbehind and lookahead to prevent the space from being consumed
return markInputRule(/(?<=^|\s)_([^*_]+)_(?=\s|$)/, schema.marks.em);
}
// Initialize Editor State and View
function afterSpacePress(state, dispatch) {
// Get the position right after the space was naturally inserted by the browser.
let { from, to, empty } = state.selection;
if (dispatch && empty) {
let tr = state.tr;
// Check for any active marks at `from - 1` (the space we just inserted)
const storedMarks = state.storedMarks || state.selection.$from.marks();
const hasBold = storedMarks.some((mark) => mark.type === state.schema.marks.strong);
const hasItalic = storedMarks.some((mark) => mark.type === state.schema.marks.em);
// Remove marks from the space character (marks applied to the space character will be marked as false)
if (hasBold) {
tr = tr.removeMark(from - 1, from, state.schema.marks.strong);
}
if (hasItalic) {
tr = tr.removeMark(from - 1, from, state.schema.marks.em);
}
// Dispatch the resulting transaction to update the editor state
dispatch(tr);
}
return true;
}
function toggleMark(markType) {
return (state, dispatch) => {
const { from, to } = state.selection;
if (state.doc.rangeHasMark(from, to, markType)) {
if (dispatch) dispatch(state.tr.removeMark(from, to, markType));
return true;
} else {
if (dispatch) dispatch(state.tr.addMark(from, to, markType.create()));
return true;
}
};
}
function isInList(state) {
const { $from } = state.selection;
return (
$from.parent.type === schema.nodes.paragraph && $from.node(-1).type === schema.nodes.list_item
);
}
function isEmptyListItem(state) {
const { $from } = state.selection;
return isInList(state) && $from.parent.content.size === 0 && $from.node(-1).childCount === 1;
}
function exitList(state, dispatch) {
return liftListItem(schema.nodes.list_item)(state, dispatch);
}
// Function to find the next template in the document
function findNextTemplate(doc, from = 0) { function findNextTemplate(doc, from = 0) {
const patterns = [ const patterns = [
{ start: '[', end: ']' }, { start: '[', end: ']' },
@@ -270,6 +84,7 @@
return result; return result;
} }
// Function to select the next template in the document
function selectNextTemplate(state, dispatch) { function selectNextTemplate(state, dispatch) {
const { doc, selection } = state; const { doc, selection } = state;
const from = selection.to; const from = selection.to;
@@ -290,220 +105,254 @@
return false; return false;
} }
// Replace tabs with four spaces export const setContent = (content) => {
function handleTabIndentation(text: string): string { editor.commands.setContent(content);
// Replace each tab character with four spaces };
return text.replace(/\t/g, ' ');
}
onMount(() => { const selectTemplate = () => {
const initialDoc = markdownToProseMirrorDoc(value || ''); // Convert the initial content if (value !== '') {
// After updating the state, try to find and select the next template
setTimeout(() => {
const templateFound = selectNextTemplate(editor.view.state, editor.view.dispatch);
if (!templateFound) {
// If no template found, set cursor at the end
const endPos = editor.view.state.doc.content.size;
editor.view.dispatch(
editor.view.state.tr.setSelection(TextSelection.create(editor.view.state.doc, endPos))
);
}
}, 0);
}
};
state = EditorState.create({ onMount(async () => {
doc: initialDoc, console.log(value);
schema,
plugins: [ if (preserveBreaks) {
history(), turndownService.addRule('preserveBreaks', {
placeholderPlugin(placeholder), filter: 'br', // Target <br> elements
inputRules({ replacement: function (content) {
rules: [ return '<br/>';
headingRule(schema), // Handle markdown-style headings (# H1, ## H2, etc.) }
bulletListRule(schema), // Handle `-` or `*` input to start bullet list });
orderedListRule(schema), // Handle `1.` input to start ordered list }
boldRule(schema), // Bold input rule
italicRule(schema) // Italic input rule async function tryParse(value, attempts = 3, interval = 100) {
] try {
// Try parsing the value
return marked.parse(value.replaceAll(`\n<br/>`, `<br/>`), {
breaks: false
});
} catch (error) {
// If no attempts remain, fallback to plain text
if (attempts <= 1) {
return value;
}
// Wait for the interval, then retry
await new Promise((resolve) => setTimeout(resolve, interval));
return tryParse(value, attempts - 1, interval); // Recursive call
}
}
// Usage example
let content = await tryParse(value);
editor = new Editor({
element: element,
extensions: [
StarterKit,
CodeBlockLowlight.configure({
lowlight
}), }),
keymap({ Highlight,
...baseKeymap, Typography,
'Mod-z': undo, Placeholder.configure({ placeholder }),
'Mod-y': redo, ...(autocomplete
Enter: (state, dispatch, view) => { ? [
if (shiftEnter) { AIAutocompletion.configure({
eventDispatch('enter'); generateCompletion: async (text) => {
return true; if (text.trim().length === 0) {
} return null;
return chainCommands( }
(state, dispatch, view) => {
if (isEmptyListItem(state)) {
return exitList(state, dispatch);
}
return false;
},
(state, dispatch, view) => {
if (isInList(state)) {
return splitListItem(schema.nodes.list_item)(state, dispatch);
}
return false;
},
baseKeymap.Enter
)(state, dispatch, view);
},
'Shift-Enter': (state, dispatch, view) => { const suggestion = await generateAutoCompletion(text).catch(() => null);
if (shiftEnter) { if (!suggestion || suggestion.trim().length === 0) {
return chainCommands( return null;
(state, dispatch, view) => {
if (isEmptyListItem(state)) {
return exitList(state, dispatch);
} }
return false;
}, return suggestion;
(state, dispatch, view) => { }
if (isInList(state)) { })
return splitListItem(schema.nodes.list_item)(state, dispatch); ]
} : [])
return false; ],
}, content: content,
baseKeymap.Enter autofocus: messageInput ? true : false,
)(state, dispatch, view); onTransaction: () => {
} else { // force re-render so `editor.isActive` works as expected
return baseKeymap.Enter(state, dispatch, view); editor = editor;
const newValue = turndownService
.turndown(
(preserveBreaks
? editor.getHTML().replace(/<p><\/p>/g, '<br/>')
: editor.getHTML()
).replace(/ {2,}/g, (m) => m.replace(/ /g, '\u00a0'))
)
.replace(/\u00a0/g, ' ');
if (value !== newValue) {
value = newValue;
// check if the node is paragraph as well
if (editor.isActive('paragraph')) {
if (value === '') {
editor.commands.clearContent();
} }
}
}
},
editorProps: {
attributes: { id },
handleDOMEvents: {
focus: (view, event) => {
eventDispatch('focus', { event });
return false; return false;
}, },
keyup: (view, event) => {
// Prevent default tab navigation and provide indent/outdent behavior inside lists: eventDispatch('keyup', { event });
Tab: chainCommands((state, dispatch, view) => { return false;
const { $from } = state.selection;
if (isInList(state)) {
return sinkListItem(schema.nodes.list_item)(state, dispatch);
} else {
return selectNextTemplate(state, dispatch);
}
return true; // Prevent Tab from moving the focus
}),
'Shift-Tab': (state, dispatch, view) => {
const { $from } = state.selection;
if (isInList(state)) {
return liftListItem(schema.nodes.list_item)(state, dispatch);
}
return true; // Prevent Shift-Tab from moving the focus
}, },
'Mod-b': toggleMark(schema.marks.strong), keydown: (view, event) => {
'Mod-i': toggleMark(schema.marks.em) if (messageInput) {
}) // Handle Tab Key
] if (event.key === 'Tab') {
}); const handled = selectNextTemplate(view.state, view.dispatch);
if (handled) {
view = new EditorView(element, {
state,
dispatchTransaction(transaction) {
// Update editor state
let newState = view.state.apply(transaction);
view.updateState(newState);
value = serializeEditorContent(newState.doc); // Convert ProseMirror content to markdown text
eventDispatch('input', { value });
},
handleDOMEvents: {
focus: (view, event) => {
eventDispatch('focus', { event });
return false;
},
keypress: (view, event) => {
eventDispatch('keypress', { event });
return false;
},
keydown: (view, event) => {
eventDispatch('keydown', { event });
return false;
},
paste: (view, event) => {
if (event.clipboardData) {
// Extract plain text from clipboard and paste it without formatting
const plainText = event.clipboardData.getData('text/plain');
if (plainText) {
if (largeTextAsFile) {
if (plainText.length > PASTED_TEXT_CHARACTER_LIMIT) {
// Dispatch paste event to parent component
eventDispatch('paste', { event });
event.preventDefault(); event.preventDefault();
return true; return true;
} }
} }
const modifiedText = handleTabIndentation(plainText); if (event.key === 'Enter') {
console.log(modifiedText); // Check if the current selection is inside a structured block (like codeBlock or list)
const { state } = view;
const { $head } = state.selection;
// Replace the current selection with the plain text content // Recursive function to check ancestors for specific node types
const tr = view.state.tr.replaceSelectionWith( function isInside(nodeTypes: string[]): boolean {
view.state.schema.text(modifiedText), let currentNode = $head;
false while (currentNode) {
if (nodeTypes.includes(currentNode.parent.type.name)) {
return true;
}
if (!currentNode.depth) break; // Stop if we reach the top
currentNode = state.doc.resolve(currentNode.before()); // Move to the parent node
}
return false;
}
const isInCodeBlock = isInside(['codeBlock']);
const isInList = isInside(['listItem', 'bulletList', 'orderedList']);
const isInHeading = isInside(['heading']);
if (isInCodeBlock || isInList || isInHeading) {
// Let ProseMirror handle the normal Enter behavior
return false;
}
}
// Handle shift + Enter for a line break
if (shiftEnter) {
if (event.key === 'Enter' && event.shiftKey && !event.ctrlKey && !event.metaKey) {
editor.commands.setHardBreak(); // Insert a hard break
view.dispatch(view.state.tr.scrollIntoView()); // Move viewport to the cursor
event.preventDefault();
return true;
}
}
}
eventDispatch('keydown', { event });
return false;
},
paste: (view, event) => {
if (event.clipboardData) {
// Extract plain text from clipboard and paste it without formatting
const plainText = event.clipboardData.getData('text/plain');
if (plainText) {
if (largeTextAsFile) {
if (plainText.length > PASTED_TEXT_CHARACTER_LIMIT) {
// Dispatch paste event to parent component
eventDispatch('paste', { event });
event.preventDefault();
return true;
}
}
return false;
}
// Check if the pasted content contains image files
const hasImageFile = Array.from(event.clipboardData.files).some((file) =>
file.type.startsWith('image/')
); );
view.dispatch(tr.scrollIntoView());
event.preventDefault(); // Prevent the default paste behavior // Check for image in dataTransfer items (for cases where files are not available)
return true; const hasImageItem = Array.from(event.clipboardData.items).some((item) =>
item.type.startsWith('image/')
);
if (hasImageFile) {
// If there's an image, dispatch the event to the parent
eventDispatch('paste', { event });
event.preventDefault();
return true;
}
if (hasImageItem) {
// If there's an image item, dispatch the event to the parent
eventDispatch('paste', { event });
event.preventDefault();
return true;
}
} }
// Check if the pasted content contains image files // For all other cases (text, formatted text, etc.), let ProseMirror handle it
const hasImageFile = Array.from(event.clipboardData.files).some((file) => view.dispatch(view.state.tr.scrollIntoView()); // Move viewport to the cursor after pasting
file.type.startsWith('image/') return false;
);
// Check for image in dataTransfer items (for cases where files are not available)
const hasImageItem = Array.from(event.clipboardData.items).some((item) =>
item.type.startsWith('image/')
);
if (hasImageFile) {
// If there's an image, dispatch the event to the parent
eventDispatch('paste', { event });
event.preventDefault();
return true;
}
if (hasImageItem) {
// If there's an image item, dispatch the event to the parent
eventDispatch('paste', { event });
event.preventDefault();
return true;
}
} }
// For all other cases (text, formatted text, etc.), let ProseMirror handle it
return false;
},
// Handle space input after browser has completed it
keyup: (view, event) => {
if (event.key === ' ' && event.code === 'Space') {
afterSpacePress(view.state, view.dispatch);
}
return false;
} }
}, }
attributes: { id }
}); });
});
// Reinitialize the editor if the value is externally changed (i.e. when `value` is updated) if (messageInput) {
$: if (view && value !== serializeEditorContent(view.state.doc)) { selectTemplate();
const newDoc = markdownToProseMirrorDoc(value || '');
const newState = EditorState.create({
doc: newDoc,
schema,
plugins: view.state.plugins,
selection: TextSelection.atEnd(newDoc) // This sets the cursor at the end
});
view.updateState(newState);
if (value !== '') {
// After updating the state, try to find and select the next template
setTimeout(() => {
const templateFound = selectNextTemplate(view.state, view.dispatch);
if (!templateFound) {
// If no template found, set cursor at the end
const endPos = view.state.doc.content.size;
view.dispatch(view.state.tr.setSelection(TextSelection.create(view.state.doc, endPos)));
}
}, 0);
} }
}
// Destroy ProseMirror instance on unmount
onDestroy(() => {
view?.destroy();
}); });
onDestroy(() => {
if (editor) {
editor.destroy();
}
});
// Update the editor content if the external `value` changes
$: if (
editor &&
value !==
turndownService
.turndown(
(preserveBreaks
? editor.getHTML().replace(/<p><\/p>/g, '<br/>')
: editor.getHTML()
).replace(/ {2,}/g, (m) => m.replace(/ /g, '\u00a0'))
)
.replace(/\u00a0/g, ' ')
) {
editor.commands.setContent(
marked.parse(value.replaceAll(`\n<br/>`, `<br/>`), {
breaks: false
})
); // Update editor content
selectTemplate();
}
</script> </script>
<div bind:this={element} class="relative w-full min-w-full h-full min-h-fit {className}"></div> <div bind:this={element} class="relative w-full min-w-full h-full min-h-fit {className}" />
@@ -0,0 +1,278 @@
/*
Here we initialize the plugin with keyword mapping.
Intended to handle user interactions seamlessly.
Observe the keydown events for proactive suggestions.
Provide a mechanism for accepting AI suggestions.
Evaluate each input change with debounce logic.
Next, we implement touch and mouse interactions.
Anchor the user experience to intuitive behavior.
Intelligently reset suggestions on new input.
*/
import { Extension } from '@tiptap/core';
import { Plugin, PluginKey } from 'prosemirror-state';
export const AIAutocompletion = Extension.create({
name: 'aiAutocompletion',
addOptions() {
return {
generateCompletion: () => Promise.resolve(''),
debounceTime: 1000
};
},
addGlobalAttributes() {
return [
{
types: ['paragraph'],
attributes: {
class: {
default: null,
parseHTML: (element) => element.getAttribute('class'),
renderHTML: (attributes) => {
if (!attributes.class) return {};
return { class: attributes.class };
}
},
'data-prompt': {
default: null,
parseHTML: (element) => element.getAttribute('data-prompt'),
renderHTML: (attributes) => {
if (!attributes['data-prompt']) return {};
return { 'data-prompt': attributes['data-prompt'] };
}
},
'data-suggestion': {
default: null,
parseHTML: (element) => element.getAttribute('data-suggestion'),
renderHTML: (attributes) => {
if (!attributes['data-suggestion']) return {};
return { 'data-suggestion': attributes['data-suggestion'] };
}
}
}
}
];
},
addProseMirrorPlugins() {
let debounceTimer = null;
let loading = false;
let touchStartX = 0;
let touchStartY = 0;
return [
new Plugin({
key: new PluginKey('aiAutocompletion'),
props: {
handleKeyDown: (view, event) => {
const { state, dispatch } = view;
const { selection } = state;
const { $head } = selection;
if ($head.parent.type.name !== 'paragraph') return false;
const node = $head.parent;
if (event.key === 'Tab') {
// if (!node.attrs['data-suggestion']) {
// // Generate completion
// if (loading) return true
// loading = true
// const prompt = node.textContent
// this.options.generateCompletion(prompt).then(suggestion => {
// if (suggestion && suggestion.trim() !== '') {
// dispatch(state.tr.setNodeMarkup($head.before(), null, {
// ...node.attrs,
// class: 'ai-autocompletion',
// 'data-prompt': prompt,
// 'data-suggestion': suggestion,
// }))
// }
// // If suggestion is empty or null, do nothing
// }).finally(() => {
// loading = false
// })
// }
if (node.attrs['data-suggestion']) {
// Accept suggestion
const suggestion = node.attrs['data-suggestion'];
dispatch(
state.tr.insertText(suggestion, $head.pos).setNodeMarkup($head.before(), null, {
...node.attrs,
class: null,
'data-prompt': null,
'data-suggestion': null
})
);
return true;
}
} else {
if (node.attrs['data-suggestion']) {
// Reset suggestion on any other key press
dispatch(
state.tr.setNodeMarkup($head.before(), null, {
...node.attrs,
class: null,
'data-prompt': null,
'data-suggestion': null
})
);
}
// Start debounce logic for AI generation only if the cursor is at the end of the paragraph
if (selection.empty && $head.pos === $head.end()) {
// Set up debounce for AI generation
if (this.options.debounceTime !== null) {
clearTimeout(debounceTimer);
// Capture current position
const currentPos = $head.before();
debounceTimer = setTimeout(() => {
const newState = view.state;
const newSelection = newState.selection;
const newNode = newState.doc.nodeAt(currentPos);
// Check if the node still exists and is still a paragraph
if (
newNode &&
newNode.type.name === 'paragraph' &&
newSelection.$head.pos === newSelection.$head.end() &&
newSelection.$head.pos === currentPos + newNode.nodeSize - 1
) {
const prompt = newNode.textContent;
if (prompt.trim() !== '') {
if (loading) return true;
loading = true;
this.options
.generateCompletion(prompt)
.then((suggestion) => {
if (suggestion && suggestion.trim() !== '') {
if (
view.state.selection.$head.pos === view.state.selection.$head.end()
) {
view.dispatch(
newState.tr.setNodeMarkup(currentPos, null, {
...newNode.attrs,
class: 'ai-autocompletion',
'data-prompt': prompt,
'data-suggestion': suggestion
})
);
}
}
})
.finally(() => {
loading = false;
});
}
}
}, this.options.debounceTime);
}
}
}
return false;
},
handleDOMEvents: {
touchstart: (view, event) => {
touchStartX = event.touches[0].clientX;
touchStartY = event.touches[0].clientY;
return false;
},
touchend: (view, event) => {
const touchEndX = event.changedTouches[0].clientX;
const touchEndY = event.changedTouches[0].clientY;
const deltaX = touchEndX - touchStartX;
const deltaY = touchEndY - touchStartY;
// Check if the swipe was primarily horizontal and to the right
if (Math.abs(deltaX) > Math.abs(deltaY) && deltaX > 50) {
const { state, dispatch } = view;
const { selection } = state;
const { $head } = selection;
const node = $head.parent;
if (node.type.name === 'paragraph' && node.attrs['data-suggestion']) {
const suggestion = node.attrs['data-suggestion'];
dispatch(
state.tr.insertText(suggestion, $head.pos).setNodeMarkup($head.before(), null, {
...node.attrs,
class: null,
'data-prompt': null,
'data-suggestion': null
})
);
return true;
}
}
return false;
},
// Add mousedown behavior
// mouseup: (view, event) => {
// const { state, dispatch } = view;
// const { selection } = state;
// const { $head } = selection;
// const node = $head.parent;
// // Reset debounce timer on mouse click
// clearTimeout(debounceTimer);
// // If a suggestion exists and the cursor moves, remove the suggestion
// if (
// node.type.name === 'paragraph' &&
// node.attrs['data-suggestion'] &&
// view.state.selection.$head.pos !== view.state.selection.$head.end()
// ) {
// dispatch(
// state.tr.setNodeMarkup($head.before(), null, {
// ...node.attrs,
// class: null,
// 'data-prompt': null,
// 'data-suggestion': null
// })
// );
// }
// return false;
// }
mouseup: (view, event) => {
const { state, dispatch } = view;
// Reset debounce timer on mouse click
clearTimeout(debounceTimer);
// Iterate over all nodes in the document
const tr = state.tr;
state.doc.descendants((node, pos) => {
if (node.type.name === 'paragraph' && node.attrs['data-suggestion']) {
// Remove suggestion from this paragraph
tr.setNodeMarkup(pos, null, {
...node.attrs,
class: null,
'data-prompt': null,
'data-suggestion': null
});
}
});
// Apply the transaction if any changes were made
if (tr.docChanged) {
dispatch(tr);
}
return false;
}
}
}
})
];
}
});
+50 -27
View File
@@ -3,42 +3,65 @@
export let value = ''; export let value = '';
export let placeholder = ''; export let placeholder = '';
export let rows = 1;
export let required = false;
export let className = export let className =
'w-full rounded-lg px-3 py-2 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none resize-none h-full'; 'w-full rounded-lg px-3 py-2 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none resize-none h-full';
let textareaElement; let textareaElement;
onMount(async () => { $: if (textareaElement) {
await tick(); if (textareaElement.innerText !== value && value !== '') {
if (textareaElement) { textareaElement.innerText = value ?? '';
await tick();
setTimeout(adjustHeight, 0);
} }
});
$: if (value) {
setTimeout(adjustHeight, 0);
} }
const adjustHeight = () => { // Adjust height on mount and after setting the element.
if (textareaElement) { onMount(async () => {
textareaElement.style.height = ''; await tick();
textareaElement.style.height = `${textareaElement.scrollHeight}px`; });
}
}; // Handle paste event to ensure only plaintext is pasted
function handlePaste(event: ClipboardEvent) {
event.preventDefault(); // Prevent the default paste action
const clipboardData = event.clipboardData?.getData('text/plain'); // Get plaintext from clipboard
// Insert plaintext into the textarea
document.execCommand('insertText', false, clipboardData);
}
</script> </script>
<textarea <div
contenteditable="true"
bind:this={textareaElement} bind:this={textareaElement}
bind:value class="{className} whitespace-pre-wrap relative {value
{placeholder} ? !value.trim()
on:input={adjustHeight} ? 'placeholder'
on:focus={adjustHeight} : ''
class={className} : 'placeholder'}"
{rows} style="field-sizing: content; -moz-user-select: text !important;"
{required} on:input={() => {
const text = textareaElement.innerText;
if (text === '\n') {
value = '';
return;
}
value = text;
}}
on:paste={handlePaste}
data-placeholder={placeholder}
/> />
<style>
.placeholder::before {
/* abolute */
position: absolute;
content: attr(data-placeholder);
color: #adb5bd;
overflow: hidden;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 1;
pointer-events: none;
touch-action: none;
}
</style>
+14
View File
@@ -115,6 +115,20 @@
</div> </div>
</button> </button>
</Menu> </Menu>
{:else if $mobile}
<Tooltip content={$i18n.t('Controls')}>
<button
class=" flex cursor-pointer px-2 py-2 rounded-xl hover:bg-gray-50 dark:hover:bg-gray-850 transition"
on:click={async () => {
await showControls.set(!$showControls);
}}
aria-label="Controls"
>
<div class=" m-auto self-center">
<AdjustmentsHorizontal className=" size-5" strokeWidth="0.5" />
</div>
</button>
</Tooltip>
{/if} {/if}
{#if !$mobile} {#if !$mobile}
@@ -165,12 +165,14 @@
<div class="mt-3 flex justify-between"> <div class="mt-3 flex justify-between">
<div class="text-xs text-gray-500"> <div class="text-xs text-gray-500">
<Tooltip <Tooltip
content={item?.user?.email} content={item?.user?.email ?? $i18n.t('Deleted User')}
className="flex shrink-0" className="flex shrink-0"
placement="top-start" placement="top-start"
> >
{$i18n.t('By {{name}}', { {$i18n.t('By {{name}}', {
name: capitalizeFirstLetter(item?.user?.name ?? item?.user?.email) name: capitalizeFirstLetter(
item?.user?.name ?? item?.user?.email ?? $i18n.t('Deleted User')
)
})} })}
</Tooltip> </Tooltip>
</div> </div>
@@ -68,7 +68,7 @@
let inputFiles = null; let inputFiles = null;
let filteredItems = []; let filteredItems = [];
$: if (knowledge) { $: if (knowledge && knowledge.files) {
fuse = new Fuse(knowledge.files, { fuse = new Fuse(knowledge.files, {
keys: ['meta.name', 'meta.description'] keys: ['meta.name', 'meta.description']
}); });
@@ -446,7 +446,13 @@
const onDragOver = (e) => { const onDragOver = (e) => {
e.preventDefault(); e.preventDefault();
dragged = true;
// Check if a file is being draggedOver.
if (e.dataTransfer?.types?.includes('Files')) {
dragged = true;
} else {
dragged = false;
}
}; };
const onDragLeave = () => { const onDragLeave = () => {
@@ -457,15 +463,17 @@
e.preventDefault(); e.preventDefault();
dragged = false; dragged = false;
if (e.dataTransfer?.files) { if (e.dataTransfer?.types?.includes('Files')) {
const inputFiles = e.dataTransfer?.files; if (e.dataTransfer?.files) {
const inputFiles = e.dataTransfer?.files;
if (inputFiles && inputFiles.length > 0) { if (inputFiles && inputFiles.length > 0) {
for (const file of inputFiles) { for (const file of inputFiles) {
await uploadFileHandler(file); await uploadFileHandler(file);
}
} else {
toast.error($i18n.t(`File not found.`));
} }
} else {
toast.error($i18n.t(`File not found.`));
} }
} }
}; };
@@ -600,7 +608,7 @@
}} }}
/> />
<div class="flex flex-col w-full h-full max-h-[100dvh] translate-y-1" id="collection-container"> <div class="flex flex-col w-full translate-y-1" id="collection-container">
{#if id && knowledge} {#if id && knowledge}
<AccessControlModal <AccessControlModal
bind:show={showAccessControlModal} bind:show={showAccessControlModal}
@@ -657,228 +665,205 @@
</div> </div>
</div> </div>
<div class="flex flex-row flex-1 h-full max-h-full pb-2.5"> <div class="flex flex-row flex-1 h-full max-h-full pb-2.5 gap-3">
<PaneGroup direction="horizontal"> {#if largeScreen}
<Pane <div class="flex-1 flex justify-start w-full h-full max-h-full">
bind:pane {#if selectedFile}
defaultSize={minSize} <div class=" flex flex-col w-full h-full max-h-full">
collapsible={true} <div class="flex-shrink-0 mb-2 flex items-center">
maxSize={50} {#if !showSidepanel}
{minSize} <div class="-translate-x-2">
class="h-full"
onExpand={() => {
showSidepanel = true;
}}
onCollapse={() => {
showSidepanel = false;
}}
>
<div
class="{largeScreen ? 'flex-shrink-0' : 'flex-1'}
flex
py-2
rounded-2xl
border
border-gray-50
h-full
dark:border-gray-850"
>
<div class=" flex flex-col w-full space-x-2 rounded-lg h-full">
<div class="w-full h-full flex flex-col">
<div class=" px-3">
<div class="flex py-1">
<div class=" self-center ml-1 mr-3">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
fill="currentColor"
class="w-4 h-4"
>
<path
fill-rule="evenodd"
d="M9 3.5a5.5 5.5 0 100 11 5.5 5.5 0 000-11zM2 9a7 7 0 1112.452 4.391l3.328 3.329a.75.75 0 11-1.06 1.06l-3.329-3.328A7 7 0 012 9z"
clip-rule="evenodd"
/>
</svg>
</div>
<input
class=" w-full text-sm pr-4 py-1 rounded-r-xl outline-none bg-transparent"
bind:value={query}
placeholder={$i18n.t('Search Collection')}
on:focus={() => {
selectedFileId = null;
}}
/>
<div>
<AddContentMenu
on:upload={(e) => {
if (e.detail.type === 'directory') {
uploadDirectoryHandler();
} else if (e.detail.type === 'text') {
showAddTextContentModal = true;
} else {
document.getElementById('files-input').click();
}
}}
on:sync={(e) => {
showSyncConfirmModal = true;
}}
/>
</div>
</div>
</div>
{#if filteredItems.length > 0}
<div class=" flex overflow-y-auto h-full w-full scrollbar-hidden text-xs">
<Files
files={filteredItems}
{selectedFileId}
on:click={(e) => {
selectedFileId = selectedFileId === e.detail ? null : e.detail;
}}
on:delete={(e) => {
console.log(e.detail);
selectedFileId = null;
deleteFileHandler(e.detail);
}}
/>
</div>
{:else}
<div
class="m-auto flex flex-col justify-center text-center text-gray-500 text-xs"
>
<div>
{$i18n.t('No content found')}
</div>
<div class="mx-12 mt-2 text-center text-gray-200 dark:text-gray-700">
{$i18n.t('Drag and drop a file to upload or select a file to view')}
</div>
</div>
{/if}
</div>
</div>
</div>
</Pane>
{#if largeScreen}
<PaneResizer class="relative flex w-2 items-center justify-center bg-background group">
<div class="z-10 flex h-7 w-5 items-center justify-center rounded-sm">
<EllipsisVertical className="size-4 invisible group-hover:visible" />
</div>
</PaneResizer>
<Pane>
<div class="flex-1 flex justify-start h-full max-h-full">
{#if selectedFile}
<div class=" flex flex-col w-full h-full max-h-full ml-2.5">
<div class="flex-shrink-0 mb-2 flex items-center">
{#if !showSidepanel}
<div class="-translate-x-2">
<button
class="w-full text-left text-sm p-1.5 rounded-lg dark:text-gray-300 dark:hover:text-white hover:bg-black/5 dark:hover:bg-gray-850"
on:click={() => {
pane.expand();
}}
>
<ChevronLeft strokeWidth="2.5" />
</button>
</div>
{/if}
<div class=" flex-1 text-2xl font-medium">
<a
class="hover:text-gray-500 hover:dark:text-gray-100 hover:underline flex-grow line-clamp-1"
href={selectedFile.id ? `/api/v1/files/${selectedFile.id}/content` : '#'}
target="_blank"
>
{selectedFile?.meta?.name}
</a>
</div>
<div>
<button
class="self-center w-fit text-sm py-1 px-2.5 dark:text-gray-300 dark:hover:text-white hover:bg-black/5 dark:hover:bg-white/5 rounded-lg"
on:click={() => {
updateFileContentHandler();
}}
>
{$i18n.t('Save')}
</button>
</div>
</div>
<div
class=" flex-1 w-full h-full max-h-full text-sm bg-transparent outline-none overflow-y-auto scrollbar-hidden"
>
{#key selectedFile.id}
<RichTextInput
className="input-prose-sm"
bind:value={selectedFile.data.content}
placeholder={$i18n.t('Add content here')}
/>
{/key}
</div>
</div>
{:else}
<div></div>
{/if}
</div>
</Pane>
{:else if !largeScreen && selectedFileId !== null}
<Drawer
className="h-full"
show={selectedFileId !== null}
on:close={() => {
selectedFileId = null;
}}
>
<div class="flex flex-col justify-start h-full max-h-full p-2">
<div class=" flex flex-col w-full h-full max-h-full">
<div class="flex-shrink-0 mt-1 mb-2 flex items-center">
<div class="mr-2">
<button <button
class="w-full text-left text-sm p-1.5 rounded-lg dark:text-gray-300 dark:hover:text-white hover:bg-black/5 dark:hover:bg-gray-850" class="w-full text-left text-sm p-1.5 rounded-lg dark:text-gray-300 dark:hover:text-white hover:bg-black/5 dark:hover:bg-gray-850"
on:click={() => { on:click={() => {
selectedFileId = null; pane.expand();
}} }}
> >
<ChevronLeft strokeWidth="2.5" /> <ChevronLeft strokeWidth="2.5" />
</button> </button>
</div> </div>
<div class=" flex-1 text-xl line-clamp-1"> {/if}
{selectedFile?.meta?.name}
</div>
<div> <div class=" flex-1 text-xl font-medium">
<button <a
class="self-center w-fit text-sm py-1 px-2.5 dark:text-gray-300 dark:hover:text-white hover:bg-black/5 dark:hover:bg-white/5 rounded-lg" class="hover:text-gray-500 hover:dark:text-gray-100 hover:underline flex-grow line-clamp-1"
on:click={() => { href={selectedFile.id ? `/api/v1/files/${selectedFile.id}/content` : '#'}
updateFileContentHandler(); target="_blank"
}} >
> {selectedFile?.meta?.name}
{$i18n.t('Save')} </a>
</button>
</div>
</div> </div>
<div <div>
class=" flex-1 w-full h-full max-h-full py-2.5 px-3.5 rounded-lg text-sm bg-transparent overflow-y-auto scrollbar-hidden" <button
> class="self-center w-fit text-sm py-1 px-2.5 dark:text-gray-300 dark:hover:text-white hover:bg-black/5 dark:hover:bg-white/5 rounded-lg"
{#key selectedFile.id} on:click={() => {
<RichTextInput updateFileContentHandler();
className="input-prose-sm" }}
bind:value={selectedFile.data.content} >
placeholder={$i18n.t('Add content here')} {$i18n.t('Save')}
</button>
</div>
</div>
<div
class=" flex-1 w-full h-full max-h-full text-sm bg-transparent outline-none overflow-y-auto scrollbar-hidden"
>
{#key selectedFile.id}
<RichTextInput
className="input-prose-sm"
bind:value={selectedFile.data.content}
placeholder={$i18n.t('Add content here')}
preserveBreaks={true}
/>
{/key}
</div>
</div>
{:else}
<div class="h-full flex w-full">
<div class="m-auto text-xs text-center text-gray-200 dark:text-gray-700">
{$i18n.t('Drag and drop a file to upload or select a file to view')}
</div>
</div>
{/if}
</div>
{:else if !largeScreen && selectedFileId !== null}
<Drawer
className="h-full"
show={selectedFileId !== null}
on:close={() => {
selectedFileId = null;
}}
>
<div class="flex flex-col justify-start h-full max-h-full p-2">
<div class=" flex flex-col w-full h-full max-h-full">
<div class="flex-shrink-0 mt-1 mb-2 flex items-center">
<div class="mr-2">
<button
class="w-full text-left text-sm p-1.5 rounded-lg dark:text-gray-300 dark:hover:text-white hover:bg-black/5 dark:hover:bg-gray-850"
on:click={() => {
selectedFileId = null;
}}
>
<ChevronLeft strokeWidth="2.5" />
</button>
</div>
<div class=" flex-1 text-xl line-clamp-1">
{selectedFile?.meta?.name}
</div>
<div>
<button
class="self-center w-fit text-sm py-1 px-2.5 dark:text-gray-300 dark:hover:text-white hover:bg-black/5 dark:hover:bg-white/5 rounded-lg"
on:click={() => {
updateFileContentHandler();
}}
>
{$i18n.t('Save')}
</button>
</div>
</div>
<div
class=" flex-1 w-full h-full max-h-full py-2.5 px-3.5 rounded-lg text-sm bg-transparent overflow-y-auto scrollbar-hidden"
>
{#key selectedFile.id}
<RichTextInput
className="input-prose-sm"
bind:value={selectedFile.data.content}
placeholder={$i18n.t('Add content here')}
preserveBreaks={true}
/>
{/key}
</div>
</div>
</div>
</Drawer>
{/if}
<div
class="{largeScreen ? 'flex-shrink-0 w-72 max-w-72' : 'flex-1'}
flex
py-2
rounded-2xl
border
border-gray-50
h-full
dark:border-gray-850"
>
<div class=" flex flex-col w-full space-x-2 rounded-lg h-full">
<div class="w-full h-full flex flex-col">
<div class=" px-3">
<div class="flex mb-0.5">
<div class=" self-center ml-1 mr-3">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
fill="currentColor"
class="w-4 h-4"
>
<path
fill-rule="evenodd"
d="M9 3.5a5.5 5.5 0 100 11 5.5 5.5 0 000-11zM2 9a7 7 0 1112.452 4.391l3.328 3.329a.75.75 0 11-1.06 1.06l-3.329-3.328A7 7 0 012 9z"
clip-rule="evenodd"
/> />
{/key} </svg>
</div>
<input
class=" w-full text-sm pr-4 py-1 rounded-r-xl outline-none bg-transparent"
bind:value={query}
placeholder={$i18n.t('Search Collection')}
on:focus={() => {
selectedFileId = null;
}}
/>
<div>
<AddContentMenu
on:upload={(e) => {
if (e.detail.type === 'directory') {
uploadDirectoryHandler();
} else if (e.detail.type === 'text') {
showAddTextContentModal = true;
} else {
document.getElementById('files-input').click();
}
}}
on:sync={(e) => {
showSyncConfirmModal = true;
}}
/>
</div> </div>
</div> </div>
</div> </div>
</Drawer>
{/if} {#if filteredItems.length > 0}
</PaneGroup> <div class=" flex overflow-y-auto h-full w-full scrollbar-hidden text-xs">
<Files
small
files={filteredItems}
{selectedFileId}
on:click={(e) => {
selectedFileId = selectedFileId === e.detail ? null : e.detail;
}}
on:delete={(e) => {
console.log(e.detail);
selectedFileId = null;
deleteFileHandler(e.detail);
}}
/>
</div>
{:else}
<div class="my-3 flex flex-col justify-center text-center text-gray-500 text-xs">
<div>
{$i18n.t('No content found')}
</div>
</div>
{/if}
</div>
</div>
</div>
</div> </div>
{:else} {:else}
<Spinner /> <Spinner />
@@ -20,7 +20,7 @@
let voiceInput = false; let voiceInput = false;
</script> </script>
<Modal size="full" className="h-full bg-white dark:bg-gray-900" bind:show> <Modal size="full" containerClassName="" className="h-full bg-white dark:bg-gray-900" bind:show>
<div class="absolute top-0 right-0 p-5"> <div class="absolute top-0 right-0 p-5">
<button <button
class="self-center dark:text-white" class="self-center dark:text-white"
@@ -67,7 +67,11 @@
</div> </div>
<div class=" flex-1 w-full h-full"> <div class=" flex-1 w-full h-full">
<RichTextInput bind:value={content} placeholder={$i18n.t('Write something...')} /> <RichTextInput
bind:value={content}
placeholder={$i18n.t('Write something...')}
preserveBreaks={true}
/>
</div> </div>
</div> </div>
</div> </div>
@@ -77,7 +81,7 @@
> >
<div class=""> <div class="">
{#if voiceInput} {#if voiceInput}
<div class=" max-w-full w-64"> <div class=" max-w-full w-full">
<VoiceRecording <VoiceRecording
bind:recording={voiceInput} bind:recording={voiceInput}
className="p-1" className="p-1"
@@ -6,6 +6,8 @@
export let selectedFileId = null; export let selectedFileId = null;
export let files = []; export let files = [];
export let small = false;
</script> </script>
<div class=" max-h-full flex flex-col w-full"> <div class=" max-h-full flex flex-col w-full">
@@ -16,6 +18,7 @@
colorClassName="{selectedFileId === file.id colorClassName="{selectedFileId === file.id
? ' bg-gray-50 dark:bg-gray-850' ? ' bg-gray-50 dark:bg-gray-850'
: 'bg-transparent'} hover:bg-gray-50 dark:hover:bg-gray-850 transition" : 'bg-transparent'} hover:bg-gray-50 dark:hover:bg-gray-850 transition"
{small}
{file} {file}
name={file?.name ?? file?.meta?.name} name={file?.name ?? file?.meta?.name}
type="file" type="file"
+8 -2
View File
@@ -279,10 +279,16 @@
<div class="flex justify-between items-center -mb-0.5 px-0.5"> <div class="flex justify-between items-center -mb-0.5 px-0.5">
<div class=" text-xs mt-0.5"> <div class=" text-xs mt-0.5">
<Tooltip content={model?.user?.email} className="flex shrink-0" placement="top-start"> <Tooltip
content={model?.user?.email ?? $i18n.t('Deleted User')}
className="flex shrink-0"
placement="top-start"
>
<div class="shrink-0 text-gray-500"> <div class="shrink-0 text-gray-500">
{$i18n.t('By {{name}}', { {$i18n.t('By {{name}}', {
name: capitalizeFirstLetter(model?.user?.name ?? model?.user?.email) name: capitalizeFirstLetter(
model?.user?.name ?? model?.user?.email ?? $i18n.t('Deleted User')
)
})} })}
</div> </div>
</Tooltip> </Tooltip>
@@ -15,6 +15,7 @@
import { getKnowledgeBases } from '$lib/apis/knowledge'; import { getKnowledgeBases } from '$lib/apis/knowledge';
import AccessControl from '../common/AccessControl.svelte'; import AccessControl from '../common/AccessControl.svelte';
import { stringify } from 'postcss'; import { stringify } from 'postcss';
import { toast } from 'svelte-sonner';
const i18n = getContext('i18n'); const i18n = getContext('i18n');
@@ -68,7 +69,9 @@
} }
}; };
let params = {}; let params = {
system: ''
};
let capabilities = { let capabilities = {
vision: true, vision: true,
usage: undefined, usage: undefined,
@@ -101,6 +104,14 @@
info.id = id; info.id = id;
info.name = name; info.name = name;
if (id === '') {
toast.error('Model ID is required.');
}
if (name === '') {
toast.error('Model Name is required.');
}
info.access_control = accessControl; info.access_control = accessControl;
info.meta.capabilities = capabilities; info.meta.capabilities = capabilities;
@@ -210,9 +221,6 @@
} }
}); });
capabilities = { ...capabilities, ...(model?.meta?.capabilities ?? {}) }; capabilities = { ...capabilities, ...(model?.meta?.capabilities ?? {}) };
if (model?.owned_by === 'openai') {
capabilities.usage = false;
}
if ('access_control' in model) { if ('access_control' in model) {
accessControl = model.access_control; accessControl = model.access_control;
@@ -320,6 +328,7 @@
info.meta.profile_image_url = compressedSrc; info.meta.profile_image_url = compressedSrc;
inputFiles = null; inputFiles = null;
filesInputElement.value = '';
}; };
}; };
@@ -348,7 +357,7 @@
<div class="self-center md:self-start flex justify-center my-2 flex-shrink-0"> <div class="self-center md:self-start flex justify-center my-2 flex-shrink-0">
<div class="self-center"> <div class="self-center">
<button <button
class="rounded-2xl flex flex-shrink-0 items-center {info.meta.profile_image_url !== class="rounded-xl flex flex-shrink-0 items-center {info.meta.profile_image_url !==
'/static/favicon.png' '/static/favicon.png'
? 'bg-transparent' ? 'bg-transparent'
: 'bg-white'} shadow-xl group relative" : 'bg-white'} shadow-xl group relative"
@@ -361,13 +370,13 @@
<img <img
src={info.meta.profile_image_url} src={info.meta.profile_image_url}
alt="model profile" alt="model profile"
class="rounded-lg size-72 md:size-60 object-cover shrink-0" class="rounded-xl size-72 md:size-60 object-cover shrink-0"
/> />
{:else} {:else}
<img <img
src="/static/favicon.png" src="/static/favicon.png"
alt="model profile" alt="model profile"
class=" rounded-lg size-72 md:size-60 object-cover shrink-0" class=" rounded-xl size-72 md:size-60 object-cover shrink-0"
/> />
{/if} {/if}
@@ -396,6 +405,18 @@
class="absolute top-0 bottom-0 left-0 right-0 bg-white dark:bg-black rounded-lg opacity-0 group-hover:opacity-20 transition" class="absolute top-0 bottom-0 left-0 right-0 bg-white dark:bg-black rounded-lg opacity-0 group-hover:opacity-20 transition"
></div> ></div>
</button> </button>
<div class="flex w-full mt-1 justify-end">
<button
class="px-2 py-1 text-gray-500 rounded-lg text-xs"
on:click={() => {
info.meta.profile_image_url = '/static/favicon.png';
}}
type="button"
>
Reset Image</button
>
</div>
</div> </div>
</div> </div>
@@ -413,12 +434,11 @@
</div> </div>
<div class="flex-1"> <div class="flex-1">
<!-- <div class=" text-sm font-semibold">{$i18n.t('Model ID')}*</div> -->
<div> <div>
<input <input
class="text-xs w-full bg-transparent text-gray-500 outline-none" class="text-xs w-full bg-transparent text-gray-500 outline-none"
placeholder={$i18n.t('Model ID')} placeholder={$i18n.t('Model ID')}
value={id} bind:value={id}
disabled={edit} disabled={edit}
required required
/> />
+4 -2
View File
@@ -155,13 +155,15 @@
<div class=" text-xs px-0.5"> <div class=" text-xs px-0.5">
<Tooltip <Tooltip
content={prompt?.user?.email} content={prompt?.user?.email ?? $i18n.t('Deleted User')}
className="flex shrink-0" className="flex shrink-0"
placement="top-start" placement="top-start"
> >
<div class="shrink-0 text-gray-500"> <div class="shrink-0 text-gray-500">
{$i18n.t('By {{name}}', { {$i18n.t('By {{name}}', {
name: capitalizeFirstLetter(prompt?.user?.name ?? prompt?.user?.email) name: capitalizeFirstLetter(
prompt?.user?.name ?? prompt?.user?.email ?? $i18n.t('Deleted User')
)
})} })}
</div> </div>
</Tooltip> </Tooltip>
+4 -2
View File
@@ -251,12 +251,14 @@
<div class="text-xs text-gray-500 shrink-0"> <div class="text-xs text-gray-500 shrink-0">
<Tooltip <Tooltip
content={tool?.user?.email} content={tool?.user?.email ?? $i18n.t('Deleted User')}
className="flex shrink-0" className="flex shrink-0"
placement="top-start" placement="top-start"
> >
{$i18n.t('By {{name}}', { {$i18n.t('By {{name}}', {
name: capitalizeFirstLetter(tool?.user?.name ?? tool?.user?.email) name: capitalizeFirstLetter(
tool?.user?.name ?? tool?.user?.email ?? $i18n.t('Deleted User')
)
})} })}
</Tooltip> </Tooltip>
</div> </div>
+25 -3
View File
@@ -1,4 +1,5 @@
{ {
"-1 for no limit, or a positive integer for a specific limit": "",
"'s', 'm', 'h', 'd', 'w' or '-1' for no expiration.": "'s', 'm', 'h', 'd', 'w' أو '-1' لا توجد انتهاء", "'s', 'm', 'h', 'd', 'w' or '-1' for no expiration.": "'s', 'm', 'h', 'd', 'w' أو '-1' لا توجد انتهاء",
"(e.g. `sh webui.sh --api --api-auth username_password`)": "", "(e.g. `sh webui.sh --api --api-auth username_password`)": "",
"(e.g. `sh webui.sh --api`)": "( `sh webui.sh --api`مثال)", "(e.g. `sh webui.sh --api`)": "( `sh webui.sh --api`مثال)",
@@ -59,6 +60,7 @@
"Allow Voice Interruption in Call": "", "Allow Voice Interruption in Call": "",
"Already have an account?": "هل تملك حساب ؟", "Already have an account?": "هل تملك حساب ؟",
"Alternative to the top_p, and aims to ensure a balance of quality and variety. The parameter p represents the minimum probability for a token to be considered, relative to the probability of the most likely token. For example, with p=0.05 and the most likely token having a probability of 0.9, logits with a value less than 0.045 are filtered out. (Default: 0.0)": "", "Alternative to the top_p, and aims to ensure a balance of quality and variety. The parameter p represents the minimum probability for a token to be considered, relative to the probability of the most likely token. For example, with p=0.05 and the most likely token having a probability of 0.9, logits with a value less than 0.045 are filtered out. (Default: 0.0)": "",
"Amazing": "",
"an assistant": "مساعد", "an assistant": "مساعد",
"and": "و", "and": "و",
"and {{COUNT}} more": "", "and {{COUNT}} more": "",
@@ -89,12 +91,15 @@
"Authenticate": "", "Authenticate": "",
"Auto-Copy Response to Clipboard": "النسخ التلقائي للاستجابة إلى الحافظة", "Auto-Copy Response to Clipboard": "النسخ التلقائي للاستجابة إلى الحافظة",
"Auto-playback response": "استجابة التشغيل التلقائي", "Auto-playback response": "استجابة التشغيل التلقائي",
"Autocomplete Generation": "",
"Autocomplete Generation Input Max Length": "",
"Automatic1111": "", "Automatic1111": "",
"AUTOMATIC1111 Api Auth String": "", "AUTOMATIC1111 Api Auth String": "",
"AUTOMATIC1111 Base URL": "AUTOMATIC1111 الرابط الرئيسي", "AUTOMATIC1111 Base URL": "AUTOMATIC1111 الرابط الرئيسي",
"AUTOMATIC1111 Base URL is required.": "AUTOMATIC1111 الرابط مطلوب", "AUTOMATIC1111 Base URL is required.": "AUTOMATIC1111 الرابط مطلوب",
"Available list": "", "Available list": "",
"available!": "متاح", "available!": "متاح",
"Awful": "",
"Azure AI Speech": "", "Azure AI Speech": "",
"Azure Region": "", "Azure Region": "",
"Back": "خلف", "Back": "خلف",
@@ -117,6 +122,7 @@
"Certificate Path": "", "Certificate Path": "",
"Change Password": "تغير الباسورد", "Change Password": "تغير الباسورد",
"Character": "", "Character": "",
"Character limit for autocomplete generation input": "",
"Chart new frontiers": "", "Chart new frontiers": "",
"Chat": "المحادثة", "Chat": "المحادثة",
"Chat Background Image": "", "Chat Background Image": "",
@@ -165,6 +171,7 @@
"Completions": "", "Completions": "",
"Concurrent Requests": "الطلبات المتزامنة", "Concurrent Requests": "الطلبات المتزامنة",
"Configure": "", "Configure": "",
"Configure Models": "",
"Confirm": "", "Confirm": "",
"Confirm Password": "تأكيد كلمة المرور", "Confirm Password": "تأكيد كلمة المرور",
"Confirm your action": "", "Confirm your action": "",
@@ -213,6 +220,7 @@
"Default (SentenceTransformers)": "(SentenceTransformers) الإفتراضي", "Default (SentenceTransformers)": "(SentenceTransformers) الإفتراضي",
"Default Model": "النموذج الافتراضي", "Default Model": "النموذج الافتراضي",
"Default model updated": "الإفتراضي تحديث الموديل", "Default model updated": "الإفتراضي تحديث الموديل",
"Default Models": "",
"Default permissions": "", "Default permissions": "",
"Default permissions updated successfully": "", "Default permissions updated successfully": "",
"Default Prompt Suggestions": "الإفتراضي Prompt الاقتراحات", "Default Prompt Suggestions": "الإفتراضي Prompt الاقتراحات",
@@ -234,6 +242,7 @@
"Delete User": "حذف المستخدم", "Delete User": "حذف المستخدم",
"Deleted {{deleteModelTag}}": "{{deleteModelTag}} حذف", "Deleted {{deleteModelTag}}": "{{deleteModelTag}} حذف",
"Deleted {{name}}": "حذف {{name}}", "Deleted {{name}}": "حذف {{name}}",
"Deleted User": "",
"Describe your knowledge base and objectives": "", "Describe your knowledge base and objectives": "",
"Description": "وصف", "Description": "وصف",
"Didn't fully follow instructions": "لم أتبع التعليمات بشكل كامل", "Didn't fully follow instructions": "لم أتبع التعليمات بشكل كامل",
@@ -292,16 +301,14 @@
"Embedding Model Engine": "تضمين محرك النموذج", "Embedding Model Engine": "تضمين محرك النموذج",
"Embedding model set to \"{{embedding_model}}\"": "تم تعيين نموذج التضمين على \"{{embedding_model}}\"", "Embedding model set to \"{{embedding_model}}\"": "تم تعيين نموذج التضمين على \"{{embedding_model}}\"",
"Enable API Key Auth": "", "Enable API Key Auth": "",
"Enable autocomplete generation for chat messages": "",
"Enable Community Sharing": "تمكين مشاركة المجتمع", "Enable Community Sharing": "تمكين مشاركة المجتمع",
"Enable Memory Locking (mlock) to prevent model data from being swapped out of RAM. This option locks the model's working set of pages into RAM, ensuring that they will not be swapped out to disk. This can help maintain performance by avoiding page faults and ensuring fast data access.": "", "Enable Memory Locking (mlock) to prevent model data from being swapped out of RAM. This option locks the model's working set of pages into RAM, ensuring that they will not be swapped out to disk. This can help maintain performance by avoiding page faults and ensuring fast data access.": "",
"Enable Memory Mapping (mmap) to load model data. This option allows the system to use disk storage as an extension of RAM by treating disk files as if they were in RAM. This can improve model performance by allowing for faster data access. However, it may not work correctly with all systems and can consume a significant amount of disk space.": "", "Enable Memory Mapping (mmap) to load model data. This option allows the system to use disk storage as an extension of RAM by treating disk files as if they were in RAM. This can improve model performance by allowing for faster data access. However, it may not work correctly with all systems and can consume a significant amount of disk space.": "",
"Enable Message Rating": "", "Enable Message Rating": "",
"Enable Mirostat sampling for controlling perplexity. (Default: 0, 0 = Disabled, 1 = Mirostat, 2 = Mirostat 2.0)": "", "Enable Mirostat sampling for controlling perplexity. (Default: 0, 0 = Disabled, 1 = Mirostat, 2 = Mirostat 2.0)": "",
"Enable New Sign Ups": "تفعيل عمليات التسجيل الجديدة", "Enable New Sign Ups": "تفعيل عمليات التسجيل الجديدة",
"Enable Retrieval Query Generation": "",
"Enable Tags Generation": "",
"Enable Web Search": "تمكين بحث الويب", "Enable Web Search": "تمكين بحث الويب",
"Enable Web Search Query Generation": "",
"Enabled": "", "Enabled": "",
"Engine": "", "Engine": "",
"Ensure your CSV file includes 4 columns in this order: Name, Email, Password, Role.": "تأكد من أن ملف CSV الخاص بك يتضمن 4 أعمدة بهذا الترتيب: Name, Email, Password, Role.", "Ensure your CSV file includes 4 columns in this order: Name, Email, Password, Role.": "تأكد من أن ملف CSV الخاص بك يتضمن 4 أعمدة بهذا الترتيب: Name, Email, Password, Role.",
@@ -326,7 +333,9 @@
"Enter language codes": "أدخل كود اللغة", "Enter language codes": "أدخل كود اللغة",
"Enter Model ID": "", "Enter Model ID": "",
"Enter model tag (e.g. {{modelTag}})": "(e.g. {{modelTag}}) أدخل الموديل تاق", "Enter model tag (e.g. {{modelTag}})": "(e.g. {{modelTag}}) أدخل الموديل تاق",
"Enter Mojeek Search API Key": "",
"Enter Number of Steps (e.g. 50)": "(e.g. 50) أدخل عدد الخطوات", "Enter Number of Steps (e.g. 50)": "(e.g. 50) أدخل عدد الخطوات",
"Enter proxy URL (e.g. https://user:password@host:port)": "",
"Enter Sampler (e.g. Euler a)": "", "Enter Sampler (e.g. Euler a)": "",
"Enter Scheduler (e.g. Karras)": "", "Enter Scheduler (e.g. Karras)": "",
"Enter Score": "أدخل النتيجة", "Enter Score": "أدخل النتيجة",
@@ -379,6 +388,7 @@
"Failed to add file.": "", "Failed to add file.": "",
"Failed to create API Key.": "فشل في إنشاء مفتاح API.", "Failed to create API Key.": "فشل في إنشاء مفتاح API.",
"Failed to read clipboard contents": "فشل في قراءة محتويات الحافظة", "Failed to read clipboard contents": "فشل في قراءة محتويات الحافظة",
"Failed to save models configuration": "",
"Failed to update settings": "", "Failed to update settings": "",
"Failed to upload file.": "", "Failed to upload file.": "",
"February": "فبراير", "February": "فبراير",
@@ -449,6 +459,7 @@
"Hide": "أخفاء", "Hide": "أخفاء",
"Host": "", "Host": "",
"How can I help you today?": "كيف استطيع مساعدتك اليوم؟", "How can I help you today?": "كيف استطيع مساعدتك اليوم؟",
"How would you rate this response?": "",
"Hybrid Search": "البحث الهجين", "Hybrid Search": "البحث الهجين",
"I acknowledge that I have read and I understand the implications of my action. I am aware of the risks associated with executing arbitrary code and I have verified the trustworthiness of the source.": "", "I acknowledge that I have read and I understand the implications of my action. I am aware of the risks associated with executing arbitrary code and I have verified the trustworthiness of the source.": "",
"ID": "", "ID": "",
@@ -565,6 +576,8 @@
"Modelfile Content": "محتوى الملف النموذجي", "Modelfile Content": "محتوى الملف النموذجي",
"Models": "الموديلات", "Models": "الموديلات",
"Models Access": "", "Models Access": "",
"Models configuration saved successfully": "",
"Mojeek Search API Key": "",
"more": "", "more": "",
"More": "المزيد", "More": "المزيد",
"Name": "الأسم", "Name": "الأسم",
@@ -583,6 +596,7 @@
"No knowledge found": "", "No knowledge found": "",
"No model IDs": "", "No model IDs": "",
"No models found": "", "No models found": "",
"No models selected": "",
"No results found": "لا توجد نتايج", "No results found": "لا توجد نتايج",
"No search query generated": "لم يتم إنشاء استعلام بحث", "No search query generated": "لم يتم إنشاء استعلام بحث",
"No source available": "لا يوجد مصدر متاح", "No source available": "لا يوجد مصدر متاح",
@@ -658,6 +672,7 @@
"Please carefully review the following warnings:": "", "Please carefully review the following warnings:": "",
"Please enter a prompt": "", "Please enter a prompt": "",
"Please fill in all fields.": "", "Please fill in all fields.": "",
"Please select a model first.": "",
"Please select a reason": "", "Please select a reason": "",
"Port": "", "Port": "",
"Positive attitude": "موقف ايجابي", "Positive attitude": "موقف ايجابي",
@@ -673,6 +688,7 @@
"Prompt updated successfully": "", "Prompt updated successfully": "",
"Prompts": "مطالبات", "Prompts": "مطالبات",
"Prompts Access": "", "Prompts Access": "",
"Proxy URL": "",
"Pull \"{{searchValue}}\" from Ollama.com": "Ollama.com \"{{searchValue}}\" أسحب من ", "Pull \"{{searchValue}}\" from Ollama.com": "Ollama.com \"{{searchValue}}\" أسحب من ",
"Pull a model from Ollama.com": "Ollama.com سحب الموديل من ", "Pull a model from Ollama.com": "Ollama.com سحب الموديل من ",
"Query Generation Prompt": "", "Query Generation Prompt": "",
@@ -693,17 +709,20 @@
"Remove": "إزالة", "Remove": "إزالة",
"Remove Model": "حذف الموديل", "Remove Model": "حذف الموديل",
"Rename": "إعادة تسمية", "Rename": "إعادة تسمية",
"Reorder Models": "",
"Repeat Last N": "N كرر آخر", "Repeat Last N": "N كرر آخر",
"Request Mode": "وضع الطلب", "Request Mode": "وضع الطلب",
"Reranking Model": "إعادة تقييم النموذج", "Reranking Model": "إعادة تقييم النموذج",
"Reranking model disabled": "تم تعطيل نموذج إعادة الترتيب", "Reranking model disabled": "تم تعطيل نموذج إعادة الترتيب",
"Reranking model set to \"{{reranking_model}}\"": "تم ضبط نموذج إعادة الترتيب على \"{{reranking_model}}\"", "Reranking model set to \"{{reranking_model}}\"": "تم ضبط نموذج إعادة الترتيب على \"{{reranking_model}}\"",
"Reset": "", "Reset": "",
"Reset All Models": "",
"Reset Upload Directory": "", "Reset Upload Directory": "",
"Reset Vector Storage/Knowledge": "", "Reset Vector Storage/Knowledge": "",
"Response notifications cannot be activated as the website permissions have been denied. Please visit your browser settings to grant the necessary access.": "", "Response notifications cannot be activated as the website permissions have been denied. Please visit your browser settings to grant the necessary access.": "",
"Response splitting": "", "Response splitting": "",
"Result": "", "Result": "",
"Retrieval Query Generation": "",
"Rich Text Input for Chat": "", "Rich Text Input for Chat": "",
"RK": "", "RK": "",
"Role": "منصب", "Role": "منصب",
@@ -829,6 +848,7 @@
"System": "النظام", "System": "النظام",
"System Instructions": "", "System Instructions": "",
"System Prompt": "محادثة النظام", "System Prompt": "محادثة النظام",
"Tags Generation": "",
"Tags Generation Prompt": "", "Tags Generation Prompt": "",
"Tail free sampling is used to reduce the impact of less probable tokens from the output. A higher value (e.g., 2.0) will reduce the impact more, while a value of 1.0 disables this setting. (default: 1)": "", "Tail free sampling is used to reduce the impact of less probable tokens from the output. A higher value (e.g., 2.0) will reduce the impact more, while a value of 1.0 disables this setting. (default: 1)": "",
"Tap to interrupt": "", "Tap to interrupt": "",
@@ -971,6 +991,7 @@
"Web Loader Settings": "Web تحميل اعدادات", "Web Loader Settings": "Web تحميل اعدادات",
"Web Search": "بحث الويب", "Web Search": "بحث الويب",
"Web Search Engine": "محرك بحث الويب", "Web Search Engine": "محرك بحث الويب",
"Web Search Query Generation": "",
"Webhook URL": "Webhook الرابط", "Webhook URL": "Webhook الرابط",
"WebUI Settings": "WebUI اعدادات", "WebUI Settings": "WebUI اعدادات",
"WebUI will make requests to \"{{url}}/api/chat\"": "", "WebUI will make requests to \"{{url}}/api/chat\"": "",
@@ -981,6 +1002,7 @@
"When enabled, the model will respond to each chat message in real-time, generating a response as soon as the user sends a message. This mode is useful for live chat applications, but may impact performance on slower hardware.": "", "When enabled, the model will respond to each chat message in real-time, generating a response as soon as the user sends a message. This mode is useful for live chat applications, but may impact performance on slower hardware.": "",
"wherever you are": "", "wherever you are": "",
"Whisper (Local)": "", "Whisper (Local)": "",
"Why?": "",
"Widescreen Mode": "", "Widescreen Mode": "",
"Won": "", "Won": "",
"Works together with top-k. A higher value (e.g., 0.95) will lead to more diverse text, while a lower value (e.g., 0.5) will generate more focused and conservative text. (Default: 0.9)": "", "Works together with top-k. A higher value (e.g., 0.95) will lead to more diverse text, while a lower value (e.g., 0.5) will generate more focused and conservative text. (Default: 0.9)": "",
+25 -3
View File
@@ -1,4 +1,5 @@
{ {
"-1 for no limit, or a positive integer for a specific limit": "",
"'s', 'm', 'h', 'd', 'w' or '-1' for no expiration.": "'s', 'm', 'h', 'd', 'w' или '-1' за неограничен срок.", "'s', 'm', 'h', 'd', 'w' or '-1' for no expiration.": "'s', 'm', 'h', 'd', 'w' или '-1' за неограничен срок.",
"(e.g. `sh webui.sh --api --api-auth username_password`)": "", "(e.g. `sh webui.sh --api --api-auth username_password`)": "",
"(e.g. `sh webui.sh --api`)": "(например `sh webui.sh --api`)", "(e.g. `sh webui.sh --api`)": "(например `sh webui.sh --api`)",
@@ -59,6 +60,7 @@
"Allow Voice Interruption in Call": "", "Allow Voice Interruption in Call": "",
"Already have an account?": "Вече имате акаунт? ", "Already have an account?": "Вече имате акаунт? ",
"Alternative to the top_p, and aims to ensure a balance of quality and variety. The parameter p represents the minimum probability for a token to be considered, relative to the probability of the most likely token. For example, with p=0.05 and the most likely token having a probability of 0.9, logits with a value less than 0.045 are filtered out. (Default: 0.0)": "", "Alternative to the top_p, and aims to ensure a balance of quality and variety. The parameter p represents the minimum probability for a token to be considered, relative to the probability of the most likely token. For example, with p=0.05 and the most likely token having a probability of 0.9, logits with a value less than 0.045 are filtered out. (Default: 0.0)": "",
"Amazing": "",
"an assistant": "асистент", "an assistant": "асистент",
"and": "и", "and": "и",
"and {{COUNT}} more": "", "and {{COUNT}} more": "",
@@ -89,12 +91,15 @@
"Authenticate": "", "Authenticate": "",
"Auto-Copy Response to Clipboard": "Аувтоматично копиране на отговор в клипборда", "Auto-Copy Response to Clipboard": "Аувтоматично копиране на отговор в клипборда",
"Auto-playback response": "Аувтоматично възпроизвеждане на Отговора", "Auto-playback response": "Аувтоматично възпроизвеждане на Отговора",
"Autocomplete Generation": "",
"Autocomplete Generation Input Max Length": "",
"Automatic1111": "", "Automatic1111": "",
"AUTOMATIC1111 Api Auth String": "", "AUTOMATIC1111 Api Auth String": "",
"AUTOMATIC1111 Base URL": "AUTOMATIC1111 Базов URL", "AUTOMATIC1111 Base URL": "AUTOMATIC1111 Базов URL",
"AUTOMATIC1111 Base URL is required.": "AUTOMATIC1111 Базов URL е задължителен.", "AUTOMATIC1111 Base URL is required.": "AUTOMATIC1111 Базов URL е задължителен.",
"Available list": "", "Available list": "",
"available!": "наличен!", "available!": "наличен!",
"Awful": "",
"Azure AI Speech": "", "Azure AI Speech": "",
"Azure Region": "", "Azure Region": "",
"Back": "Назад", "Back": "Назад",
@@ -117,6 +122,7 @@
"Certificate Path": "", "Certificate Path": "",
"Change Password": "Промяна на Парола", "Change Password": "Промяна на Парола",
"Character": "", "Character": "",
"Character limit for autocomplete generation input": "",
"Chart new frontiers": "", "Chart new frontiers": "",
"Chat": "Чат", "Chat": "Чат",
"Chat Background Image": "", "Chat Background Image": "",
@@ -165,6 +171,7 @@
"Completions": "", "Completions": "",
"Concurrent Requests": "Едновременни искания", "Concurrent Requests": "Едновременни искания",
"Configure": "", "Configure": "",
"Configure Models": "",
"Confirm": "", "Confirm": "",
"Confirm Password": "Потвърди Парола", "Confirm Password": "Потвърди Парола",
"Confirm your action": "", "Confirm your action": "",
@@ -213,6 +220,7 @@
"Default (SentenceTransformers)": "По подразбиране (SentenceTransformers)", "Default (SentenceTransformers)": "По подразбиране (SentenceTransformers)",
"Default Model": "Модел по подразбиране", "Default Model": "Модел по подразбиране",
"Default model updated": "Моделът по подразбиране е обновен", "Default model updated": "Моделът по подразбиране е обновен",
"Default Models": "",
"Default permissions": "", "Default permissions": "",
"Default permissions updated successfully": "", "Default permissions updated successfully": "",
"Default Prompt Suggestions": "Промпт Предложения по подразбиране", "Default Prompt Suggestions": "Промпт Предложения по подразбиране",
@@ -234,6 +242,7 @@
"Delete User": "Изтриване на потребител", "Delete User": "Изтриване на потребител",
"Deleted {{deleteModelTag}}": "Изтрито {{deleteModelTag}}", "Deleted {{deleteModelTag}}": "Изтрито {{deleteModelTag}}",
"Deleted {{name}}": "Изтрито {{име}}", "Deleted {{name}}": "Изтрито {{име}}",
"Deleted User": "",
"Describe your knowledge base and objectives": "", "Describe your knowledge base and objectives": "",
"Description": "Описание", "Description": "Описание",
"Didn't fully follow instructions": "Не следва инструкциите", "Didn't fully follow instructions": "Не следва инструкциите",
@@ -292,16 +301,14 @@
"Embedding Model Engine": "Модел за вграждане", "Embedding Model Engine": "Модел за вграждане",
"Embedding model set to \"{{embedding_model}}\"": "Модел за вграждане е настроен на \"{{embedding_model}}\"", "Embedding model set to \"{{embedding_model}}\"": "Модел за вграждане е настроен на \"{{embedding_model}}\"",
"Enable API Key Auth": "", "Enable API Key Auth": "",
"Enable autocomplete generation for chat messages": "",
"Enable Community Sharing": "Разрешаване на споделяне в общност", "Enable Community Sharing": "Разрешаване на споделяне в общност",
"Enable Memory Locking (mlock) to prevent model data from being swapped out of RAM. This option locks the model's working set of pages into RAM, ensuring that they will not be swapped out to disk. This can help maintain performance by avoiding page faults and ensuring fast data access.": "", "Enable Memory Locking (mlock) to prevent model data from being swapped out of RAM. This option locks the model's working set of pages into RAM, ensuring that they will not be swapped out to disk. This can help maintain performance by avoiding page faults and ensuring fast data access.": "",
"Enable Memory Mapping (mmap) to load model data. This option allows the system to use disk storage as an extension of RAM by treating disk files as if they were in RAM. This can improve model performance by allowing for faster data access. However, it may not work correctly with all systems and can consume a significant amount of disk space.": "", "Enable Memory Mapping (mmap) to load model data. This option allows the system to use disk storage as an extension of RAM by treating disk files as if they were in RAM. This can improve model performance by allowing for faster data access. However, it may not work correctly with all systems and can consume a significant amount of disk space.": "",
"Enable Message Rating": "", "Enable Message Rating": "",
"Enable Mirostat sampling for controlling perplexity. (Default: 0, 0 = Disabled, 1 = Mirostat, 2 = Mirostat 2.0)": "", "Enable Mirostat sampling for controlling perplexity. (Default: 0, 0 = Disabled, 1 = Mirostat, 2 = Mirostat 2.0)": "",
"Enable New Sign Ups": "Вклюване на Нови Потребители", "Enable New Sign Ups": "Вклюване на Нови Потребители",
"Enable Retrieval Query Generation": "",
"Enable Tags Generation": "",
"Enable Web Search": "Разрешаване на търсене в уеб", "Enable Web Search": "Разрешаване на търсене в уеб",
"Enable Web Search Query Generation": "",
"Enabled": "", "Enabled": "",
"Engine": "", "Engine": "",
"Ensure your CSV file includes 4 columns in this order: Name, Email, Password, Role.": "Уверете се, че вашият CSV файл включва 4 колони в следния ред: Име, Имейл, Парола, Роля.", "Ensure your CSV file includes 4 columns in this order: Name, Email, Password, Role.": "Уверете се, че вашият CSV файл включва 4 колони в следния ред: Име, Имейл, Парола, Роля.",
@@ -326,7 +333,9 @@
"Enter language codes": "Въведете кодове на езика", "Enter language codes": "Въведете кодове на езика",
"Enter Model ID": "", "Enter Model ID": "",
"Enter model tag (e.g. {{modelTag}})": "Въведете таг на модел (напр. {{modelTag}})", "Enter model tag (e.g. {{modelTag}})": "Въведете таг на модел (напр. {{modelTag}})",
"Enter Mojeek Search API Key": "",
"Enter Number of Steps (e.g. 50)": "Въведете брой стъпки (напр. 50)", "Enter Number of Steps (e.g. 50)": "Въведете брой стъпки (напр. 50)",
"Enter proxy URL (e.g. https://user:password@host:port)": "",
"Enter Sampler (e.g. Euler a)": "", "Enter Sampler (e.g. Euler a)": "",
"Enter Scheduler (e.g. Karras)": "", "Enter Scheduler (e.g. Karras)": "",
"Enter Score": "Въведете оценка", "Enter Score": "Въведете оценка",
@@ -379,6 +388,7 @@
"Failed to add file.": "", "Failed to add file.": "",
"Failed to create API Key.": "Неуспешно създаване на API ключ.", "Failed to create API Key.": "Неуспешно създаване на API ключ.",
"Failed to read clipboard contents": "Грешка при четене на съдържанието от клипборда", "Failed to read clipboard contents": "Грешка при четене на съдържанието от клипборда",
"Failed to save models configuration": "",
"Failed to update settings": "", "Failed to update settings": "",
"Failed to upload file.": "", "Failed to upload file.": "",
"February": "Февруари", "February": "Февруари",
@@ -449,6 +459,7 @@
"Hide": "Скрий", "Hide": "Скрий",
"Host": "", "Host": "",
"How can I help you today?": "Как мога да ви помогна днес?", "How can I help you today?": "Как мога да ви помогна днес?",
"How would you rate this response?": "",
"Hybrid Search": "Hybrid Search", "Hybrid Search": "Hybrid Search",
"I acknowledge that I have read and I understand the implications of my action. I am aware of the risks associated with executing arbitrary code and I have verified the trustworthiness of the source.": "", "I acknowledge that I have read and I understand the implications of my action. I am aware of the risks associated with executing arbitrary code and I have verified the trustworthiness of the source.": "",
"ID": "", "ID": "",
@@ -565,6 +576,8 @@
"Modelfile Content": "Съдържание на модфайл", "Modelfile Content": "Съдържание на модфайл",
"Models": "Модели", "Models": "Модели",
"Models Access": "", "Models Access": "",
"Models configuration saved successfully": "",
"Mojeek Search API Key": "",
"more": "", "more": "",
"More": "Повече", "More": "Повече",
"Name": "Име", "Name": "Име",
@@ -583,6 +596,7 @@
"No knowledge found": "", "No knowledge found": "",
"No model IDs": "", "No model IDs": "",
"No models found": "", "No models found": "",
"No models selected": "",
"No results found": "Няма намерени резултати", "No results found": "Няма намерени резултати",
"No search query generated": "Не е генерирана заявка за търсене", "No search query generated": "Не е генерирана заявка за търсене",
"No source available": "Няма наличен източник", "No source available": "Няма наличен източник",
@@ -658,6 +672,7 @@
"Please carefully review the following warnings:": "", "Please carefully review the following warnings:": "",
"Please enter a prompt": "", "Please enter a prompt": "",
"Please fill in all fields.": "", "Please fill in all fields.": "",
"Please select a model first.": "",
"Please select a reason": "", "Please select a reason": "",
"Port": "", "Port": "",
"Positive attitude": "Позитивна ативност", "Positive attitude": "Позитивна ативност",
@@ -673,6 +688,7 @@
"Prompt updated successfully": "", "Prompt updated successfully": "",
"Prompts": "Промптове", "Prompts": "Промптове",
"Prompts Access": "", "Prompts Access": "",
"Proxy URL": "",
"Pull \"{{searchValue}}\" from Ollama.com": "Извади \"{{searchValue}}\" от Ollama.com", "Pull \"{{searchValue}}\" from Ollama.com": "Извади \"{{searchValue}}\" от Ollama.com",
"Pull a model from Ollama.com": "Издърпайте модел от Ollama.com", "Pull a model from Ollama.com": "Издърпайте модел от Ollama.com",
"Query Generation Prompt": "", "Query Generation Prompt": "",
@@ -693,17 +709,20 @@
"Remove": "Изтриване", "Remove": "Изтриване",
"Remove Model": "Изтриване на модела", "Remove Model": "Изтриване на модела",
"Rename": "Преименуване", "Rename": "Преименуване",
"Reorder Models": "",
"Repeat Last N": "Repeat Last N", "Repeat Last N": "Repeat Last N",
"Request Mode": "Request Mode", "Request Mode": "Request Mode",
"Reranking Model": "Reranking Model", "Reranking Model": "Reranking Model",
"Reranking model disabled": "Reranking model disabled", "Reranking model disabled": "Reranking model disabled",
"Reranking model set to \"{{reranking_model}}\"": "Reranking model set to \"{{reranking_model}}\"", "Reranking model set to \"{{reranking_model}}\"": "Reranking model set to \"{{reranking_model}}\"",
"Reset": "", "Reset": "",
"Reset All Models": "",
"Reset Upload Directory": "", "Reset Upload Directory": "",
"Reset Vector Storage/Knowledge": "", "Reset Vector Storage/Knowledge": "",
"Response notifications cannot be activated as the website permissions have been denied. Please visit your browser settings to grant the necessary access.": "", "Response notifications cannot be activated as the website permissions have been denied. Please visit your browser settings to grant the necessary access.": "",
"Response splitting": "", "Response splitting": "",
"Result": "", "Result": "",
"Retrieval Query Generation": "",
"Rich Text Input for Chat": "", "Rich Text Input for Chat": "",
"RK": "", "RK": "",
"Role": "Роля", "Role": "Роля",
@@ -825,6 +844,7 @@
"System": "Система", "System": "Система",
"System Instructions": "", "System Instructions": "",
"System Prompt": "Системен Промпт", "System Prompt": "Системен Промпт",
"Tags Generation": "",
"Tags Generation Prompt": "", "Tags Generation Prompt": "",
"Tail free sampling is used to reduce the impact of less probable tokens from the output. A higher value (e.g., 2.0) will reduce the impact more, while a value of 1.0 disables this setting. (default: 1)": "", "Tail free sampling is used to reduce the impact of less probable tokens from the output. A higher value (e.g., 2.0) will reduce the impact more, while a value of 1.0 disables this setting. (default: 1)": "",
"Tap to interrupt": "", "Tap to interrupt": "",
@@ -967,6 +987,7 @@
"Web Loader Settings": "Настройки за зареждане на уеб", "Web Loader Settings": "Настройки за зареждане на уеб",
"Web Search": "Търсене в уеб", "Web Search": "Търсене в уеб",
"Web Search Engine": "Уеб търсачка", "Web Search Engine": "Уеб търсачка",
"Web Search Query Generation": "",
"Webhook URL": "Уебхук URL", "Webhook URL": "Уебхук URL",
"WebUI Settings": "WebUI Настройки", "WebUI Settings": "WebUI Настройки",
"WebUI will make requests to \"{{url}}/api/chat\"": "", "WebUI will make requests to \"{{url}}/api/chat\"": "",
@@ -977,6 +998,7 @@
"When enabled, the model will respond to each chat message in real-time, generating a response as soon as the user sends a message. This mode is useful for live chat applications, but may impact performance on slower hardware.": "", "When enabled, the model will respond to each chat message in real-time, generating a response as soon as the user sends a message. This mode is useful for live chat applications, but may impact performance on slower hardware.": "",
"wherever you are": "", "wherever you are": "",
"Whisper (Local)": "", "Whisper (Local)": "",
"Why?": "",
"Widescreen Mode": "", "Widescreen Mode": "",
"Won": "", "Won": "",
"Works together with top-k. A higher value (e.g., 0.95) will lead to more diverse text, while a lower value (e.g., 0.5) will generate more focused and conservative text. (Default: 0.9)": "", "Works together with top-k. A higher value (e.g., 0.95) will lead to more diverse text, while a lower value (e.g., 0.5) will generate more focused and conservative text. (Default: 0.9)": "",
+25 -3
View File
@@ -1,4 +1,5 @@
{ {
"-1 for no limit, or a positive integer for a specific limit": "",
"'s', 'm', 'h', 'd', 'w' or '-1' for no expiration.": "'s', 'm', 'h', 'd', 'w' অথবা অনির্দিষ্টকাল মেয়াদের জন্য '-1' ", "'s', 'm', 'h', 'd', 'w' or '-1' for no expiration.": "'s', 'm', 'h', 'd', 'w' অথবা অনির্দিষ্টকাল মেয়াদের জন্য '-1' ",
"(e.g. `sh webui.sh --api --api-auth username_password`)": "", "(e.g. `sh webui.sh --api --api-auth username_password`)": "",
"(e.g. `sh webui.sh --api`)": "(যেমন `sh webui.sh --api`)", "(e.g. `sh webui.sh --api`)": "(যেমন `sh webui.sh --api`)",
@@ -59,6 +60,7 @@
"Allow Voice Interruption in Call": "", "Allow Voice Interruption in Call": "",
"Already have an account?": "আগে থেকেই একাউন্ট আছে?", "Already have an account?": "আগে থেকেই একাউন্ট আছে?",
"Alternative to the top_p, and aims to ensure a balance of quality and variety. The parameter p represents the minimum probability for a token to be considered, relative to the probability of the most likely token. For example, with p=0.05 and the most likely token having a probability of 0.9, logits with a value less than 0.045 are filtered out. (Default: 0.0)": "", "Alternative to the top_p, and aims to ensure a balance of quality and variety. The parameter p represents the minimum probability for a token to be considered, relative to the probability of the most likely token. For example, with p=0.05 and the most likely token having a probability of 0.9, logits with a value less than 0.045 are filtered out. (Default: 0.0)": "",
"Amazing": "",
"an assistant": "একটা এসিস্ট্যান্ট", "an assistant": "একটা এসিস্ট্যান্ট",
"and": "এবং", "and": "এবং",
"and {{COUNT}} more": "", "and {{COUNT}} more": "",
@@ -89,12 +91,15 @@
"Authenticate": "", "Authenticate": "",
"Auto-Copy Response to Clipboard": "রেসপন্সগুলো স্বয়ংক্রিভাবে ক্লিপবোর্ডে কপি হবে", "Auto-Copy Response to Clipboard": "রেসপন্সগুলো স্বয়ংক্রিভাবে ক্লিপবোর্ডে কপি হবে",
"Auto-playback response": "রেসপন্স অটো-প্লেব্যাক", "Auto-playback response": "রেসপন্স অটো-প্লেব্যাক",
"Autocomplete Generation": "",
"Autocomplete Generation Input Max Length": "",
"Automatic1111": "", "Automatic1111": "",
"AUTOMATIC1111 Api Auth String": "", "AUTOMATIC1111 Api Auth String": "",
"AUTOMATIC1111 Base URL": "AUTOMATIC1111 বেজ ইউআরএল", "AUTOMATIC1111 Base URL": "AUTOMATIC1111 বেজ ইউআরএল",
"AUTOMATIC1111 Base URL is required.": "AUTOMATIC1111 বেজ ইউআরএল আবশ্যক", "AUTOMATIC1111 Base URL is required.": "AUTOMATIC1111 বেজ ইউআরএল আবশ্যক",
"Available list": "", "Available list": "",
"available!": "উপলব্ধ!", "available!": "উপলব্ধ!",
"Awful": "",
"Azure AI Speech": "", "Azure AI Speech": "",
"Azure Region": "", "Azure Region": "",
"Back": "পেছনে", "Back": "পেছনে",
@@ -117,6 +122,7 @@
"Certificate Path": "", "Certificate Path": "",
"Change Password": "পাসওয়ার্ড পরিবর্তন করুন", "Change Password": "পাসওয়ার্ড পরিবর্তন করুন",
"Character": "", "Character": "",
"Character limit for autocomplete generation input": "",
"Chart new frontiers": "", "Chart new frontiers": "",
"Chat": "চ্যাট", "Chat": "চ্যাট",
"Chat Background Image": "", "Chat Background Image": "",
@@ -165,6 +171,7 @@
"Completions": "", "Completions": "",
"Concurrent Requests": "সমকালীন অনুরোধ", "Concurrent Requests": "সমকালীন অনুরোধ",
"Configure": "", "Configure": "",
"Configure Models": "",
"Confirm": "", "Confirm": "",
"Confirm Password": "পাসওয়ার্ড নিশ্চিত করুন", "Confirm Password": "পাসওয়ার্ড নিশ্চিত করুন",
"Confirm your action": "", "Confirm your action": "",
@@ -213,6 +220,7 @@
"Default (SentenceTransformers)": "ডিফল্ট (SentenceTransformers)", "Default (SentenceTransformers)": "ডিফল্ট (SentenceTransformers)",
"Default Model": "ডিফল্ট মডেল", "Default Model": "ডিফল্ট মডেল",
"Default model updated": "ডিফল্ট মডেল আপডেট হয়েছে", "Default model updated": "ডিফল্ট মডেল আপডেট হয়েছে",
"Default Models": "",
"Default permissions": "", "Default permissions": "",
"Default permissions updated successfully": "", "Default permissions updated successfully": "",
"Default Prompt Suggestions": "ডিফল্ট প্রম্পট সাজেশন", "Default Prompt Suggestions": "ডিফল্ট প্রম্পট সাজেশন",
@@ -234,6 +242,7 @@
"Delete User": "ইউজার মুছে ফেলুন", "Delete User": "ইউজার মুছে ফেলুন",
"Deleted {{deleteModelTag}}": "{{deleteModelTag}} মুছে ফেলা হয়েছে", "Deleted {{deleteModelTag}}": "{{deleteModelTag}} মুছে ফেলা হয়েছে",
"Deleted {{name}}": "{{name}} মোছা হয়েছে", "Deleted {{name}}": "{{name}} মোছা হয়েছে",
"Deleted User": "",
"Describe your knowledge base and objectives": "", "Describe your knowledge base and objectives": "",
"Description": "বিবরণ", "Description": "বিবরণ",
"Didn't fully follow instructions": "ইনস্ট্রাকশন সম্পূর্ণ অনুসরণ করা হয়নি", "Didn't fully follow instructions": "ইনস্ট্রাকশন সম্পূর্ণ অনুসরণ করা হয়নি",
@@ -292,16 +301,14 @@
"Embedding Model Engine": "ইমেজ ইমেবডিং মডেল ইঞ্জিন", "Embedding Model Engine": "ইমেজ ইমেবডিং মডেল ইঞ্জিন",
"Embedding model set to \"{{embedding_model}}\"": "ইমেজ ইমেবডিং মডেল সেট করা হয়েছে - \"{{embedding_model}}\"", "Embedding model set to \"{{embedding_model}}\"": "ইমেজ ইমেবডিং মডেল সেট করা হয়েছে - \"{{embedding_model}}\"",
"Enable API Key Auth": "", "Enable API Key Auth": "",
"Enable autocomplete generation for chat messages": "",
"Enable Community Sharing": "সম্প্রদায় শেয়ারকরণ সক্ষম করুন", "Enable Community Sharing": "সম্প্রদায় শেয়ারকরণ সক্ষম করুন",
"Enable Memory Locking (mlock) to prevent model data from being swapped out of RAM. This option locks the model's working set of pages into RAM, ensuring that they will not be swapped out to disk. This can help maintain performance by avoiding page faults and ensuring fast data access.": "", "Enable Memory Locking (mlock) to prevent model data from being swapped out of RAM. This option locks the model's working set of pages into RAM, ensuring that they will not be swapped out to disk. This can help maintain performance by avoiding page faults and ensuring fast data access.": "",
"Enable Memory Mapping (mmap) to load model data. This option allows the system to use disk storage as an extension of RAM by treating disk files as if they were in RAM. This can improve model performance by allowing for faster data access. However, it may not work correctly with all systems and can consume a significant amount of disk space.": "", "Enable Memory Mapping (mmap) to load model data. This option allows the system to use disk storage as an extension of RAM by treating disk files as if they were in RAM. This can improve model performance by allowing for faster data access. However, it may not work correctly with all systems and can consume a significant amount of disk space.": "",
"Enable Message Rating": "", "Enable Message Rating": "",
"Enable Mirostat sampling for controlling perplexity. (Default: 0, 0 = Disabled, 1 = Mirostat, 2 = Mirostat 2.0)": "", "Enable Mirostat sampling for controlling perplexity. (Default: 0, 0 = Disabled, 1 = Mirostat, 2 = Mirostat 2.0)": "",
"Enable New Sign Ups": "নতুন সাইনআপ চালু করুন", "Enable New Sign Ups": "নতুন সাইনআপ চালু করুন",
"Enable Retrieval Query Generation": "",
"Enable Tags Generation": "",
"Enable Web Search": "ওয়েব অনুসন্ধান সক্ষম করুন", "Enable Web Search": "ওয়েব অনুসন্ধান সক্ষম করুন",
"Enable Web Search Query Generation": "",
"Enabled": "", "Enabled": "",
"Engine": "", "Engine": "",
"Ensure your CSV file includes 4 columns in this order: Name, Email, Password, Role.": "আপনার সিএসভি ফাইলটিতে এই ক্রমে 4 টি কলাম অন্তর্ভুক্ত রয়েছে তা নিশ্চিত করুন: নাম, ইমেল, পাসওয়ার্ড, ভূমিকা।.", "Ensure your CSV file includes 4 columns in this order: Name, Email, Password, Role.": "আপনার সিএসভি ফাইলটিতে এই ক্রমে 4 টি কলাম অন্তর্ভুক্ত রয়েছে তা নিশ্চিত করুন: নাম, ইমেল, পাসওয়ার্ড, ভূমিকা।.",
@@ -326,7 +333,9 @@
"Enter language codes": "ল্যাঙ্গুয়েজ কোড লিখুন", "Enter language codes": "ল্যাঙ্গুয়েজ কোড লিখুন",
"Enter Model ID": "", "Enter Model ID": "",
"Enter model tag (e.g. {{modelTag}})": "মডেল ট্যাগ লিখুন (e.g. {{modelTag}})", "Enter model tag (e.g. {{modelTag}})": "মডেল ট্যাগ লিখুন (e.g. {{modelTag}})",
"Enter Mojeek Search API Key": "",
"Enter Number of Steps (e.g. 50)": "ধাপের সংখ্যা দিন (যেমন: 50)", "Enter Number of Steps (e.g. 50)": "ধাপের সংখ্যা দিন (যেমন: 50)",
"Enter proxy URL (e.g. https://user:password@host:port)": "",
"Enter Sampler (e.g. Euler a)": "", "Enter Sampler (e.g. Euler a)": "",
"Enter Scheduler (e.g. Karras)": "", "Enter Scheduler (e.g. Karras)": "",
"Enter Score": "স্কোর দিন", "Enter Score": "স্কোর দিন",
@@ -379,6 +388,7 @@
"Failed to add file.": "", "Failed to add file.": "",
"Failed to create API Key.": "API Key তৈরি করা যায়নি।", "Failed to create API Key.": "API Key তৈরি করা যায়নি।",
"Failed to read clipboard contents": "ক্লিপবোর্ডের বিষয়বস্তু পড়া সম্ভব হয়নি", "Failed to read clipboard contents": "ক্লিপবোর্ডের বিষয়বস্তু পড়া সম্ভব হয়নি",
"Failed to save models configuration": "",
"Failed to update settings": "", "Failed to update settings": "",
"Failed to upload file.": "", "Failed to upload file.": "",
"February": "ফেব্রুয়ারি", "February": "ফেব্রুয়ারি",
@@ -449,6 +459,7 @@
"Hide": "লুকান", "Hide": "লুকান",
"Host": "", "Host": "",
"How can I help you today?": "আপনাকে আজ কিভাবে সাহায্য করতে পারি?", "How can I help you today?": "আপনাকে আজ কিভাবে সাহায্য করতে পারি?",
"How would you rate this response?": "",
"Hybrid Search": "হাইব্রিড অনুসন্ধান", "Hybrid Search": "হাইব্রিড অনুসন্ধান",
"I acknowledge that I have read and I understand the implications of my action. I am aware of the risks associated with executing arbitrary code and I have verified the trustworthiness of the source.": "", "I acknowledge that I have read and I understand the implications of my action. I am aware of the risks associated with executing arbitrary code and I have verified the trustworthiness of the source.": "",
"ID": "", "ID": "",
@@ -565,6 +576,8 @@
"Modelfile Content": "মডেলফাইল কনটেন্ট", "Modelfile Content": "মডেলফাইল কনটেন্ট",
"Models": "মডেলসমূহ", "Models": "মডেলসমূহ",
"Models Access": "", "Models Access": "",
"Models configuration saved successfully": "",
"Mojeek Search API Key": "",
"more": "", "more": "",
"More": "আরো", "More": "আরো",
"Name": "নাম", "Name": "নাম",
@@ -583,6 +596,7 @@
"No knowledge found": "", "No knowledge found": "",
"No model IDs": "", "No model IDs": "",
"No models found": "", "No models found": "",
"No models selected": "",
"No results found": "কোন ফলাফল পাওয়া যায়নি", "No results found": "কোন ফলাফল পাওয়া যায়নি",
"No search query generated": "কোনও অনুসন্ধান ক্যোয়ারী উত্পন্ন হয়নি", "No search query generated": "কোনও অনুসন্ধান ক্যোয়ারী উত্পন্ন হয়নি",
"No source available": "কোন উৎস পাওয়া যায়নি", "No source available": "কোন উৎস পাওয়া যায়নি",
@@ -658,6 +672,7 @@
"Please carefully review the following warnings:": "", "Please carefully review the following warnings:": "",
"Please enter a prompt": "", "Please enter a prompt": "",
"Please fill in all fields.": "", "Please fill in all fields.": "",
"Please select a model first.": "",
"Please select a reason": "", "Please select a reason": "",
"Port": "", "Port": "",
"Positive attitude": "পজিটিভ আক্রমণ", "Positive attitude": "পজিটিভ আক্রমণ",
@@ -673,6 +688,7 @@
"Prompt updated successfully": "", "Prompt updated successfully": "",
"Prompts": "প্রম্পটসমূহ", "Prompts": "প্রম্পটসমূহ",
"Prompts Access": "", "Prompts Access": "",
"Proxy URL": "",
"Pull \"{{searchValue}}\" from Ollama.com": "Ollama.com থেকে \"{{searchValue}}\" টানুন", "Pull \"{{searchValue}}\" from Ollama.com": "Ollama.com থেকে \"{{searchValue}}\" টানুন",
"Pull a model from Ollama.com": "Ollama.com থেকে একটি টেনে আনুন আনুন", "Pull a model from Ollama.com": "Ollama.com থেকে একটি টেনে আনুন আনুন",
"Query Generation Prompt": "", "Query Generation Prompt": "",
@@ -693,17 +709,20 @@
"Remove": "রিমুভ করুন", "Remove": "রিমুভ করুন",
"Remove Model": "মডেল রিমুভ করুন", "Remove Model": "মডেল রিমুভ করুন",
"Rename": "রেনেম", "Rename": "রেনেম",
"Reorder Models": "",
"Repeat Last N": "রিপিট Last N", "Repeat Last N": "রিপিট Last N",
"Request Mode": "রিকোয়েস্ট মোড", "Request Mode": "রিকোয়েস্ট মোড",
"Reranking Model": "রির্যাক্টিং মডেল", "Reranking Model": "রির্যাক্টিং মডেল",
"Reranking model disabled": "রির্যাক্টিং মডেল নিষ্ক্রিয় করা", "Reranking model disabled": "রির্যাক্টিং মডেল নিষ্ক্রিয় করা",
"Reranking model set to \"{{reranking_model}}\"": "রির ্যাঙ্কিং মডেল \"{{reranking_model}}\" -এ সেট করা আছে", "Reranking model set to \"{{reranking_model}}\"": "রির ্যাঙ্কিং মডেল \"{{reranking_model}}\" -এ সেট করা আছে",
"Reset": "", "Reset": "",
"Reset All Models": "",
"Reset Upload Directory": "", "Reset Upload Directory": "",
"Reset Vector Storage/Knowledge": "", "Reset Vector Storage/Knowledge": "",
"Response notifications cannot be activated as the website permissions have been denied. Please visit your browser settings to grant the necessary access.": "", "Response notifications cannot be activated as the website permissions have been denied. Please visit your browser settings to grant the necessary access.": "",
"Response splitting": "", "Response splitting": "",
"Result": "", "Result": "",
"Retrieval Query Generation": "",
"Rich Text Input for Chat": "", "Rich Text Input for Chat": "",
"RK": "", "RK": "",
"Role": "পদবি", "Role": "পদবি",
@@ -825,6 +844,7 @@
"System": "সিস্টেম", "System": "সিস্টেম",
"System Instructions": "", "System Instructions": "",
"System Prompt": "সিস্টেম প্রম্পট", "System Prompt": "সিস্টেম প্রম্পট",
"Tags Generation": "",
"Tags Generation Prompt": "", "Tags Generation Prompt": "",
"Tail free sampling is used to reduce the impact of less probable tokens from the output. A higher value (e.g., 2.0) will reduce the impact more, while a value of 1.0 disables this setting. (default: 1)": "", "Tail free sampling is used to reduce the impact of less probable tokens from the output. A higher value (e.g., 2.0) will reduce the impact more, while a value of 1.0 disables this setting. (default: 1)": "",
"Tap to interrupt": "", "Tap to interrupt": "",
@@ -967,6 +987,7 @@
"Web Loader Settings": "ওয়েব লোডার সেটিংস", "Web Loader Settings": "ওয়েব লোডার সেটিংস",
"Web Search": "ওয়েব অনুসন্ধান", "Web Search": "ওয়েব অনুসন্ধান",
"Web Search Engine": "ওয়েব সার্চ ইঞ্জিন", "Web Search Engine": "ওয়েব সার্চ ইঞ্জিন",
"Web Search Query Generation": "",
"Webhook URL": "ওয়েবহুক URL", "Webhook URL": "ওয়েবহুক URL",
"WebUI Settings": "WebUI সেটিংসমূহ", "WebUI Settings": "WebUI সেটিংসমূহ",
"WebUI will make requests to \"{{url}}/api/chat\"": "", "WebUI will make requests to \"{{url}}/api/chat\"": "",
@@ -977,6 +998,7 @@
"When enabled, the model will respond to each chat message in real-time, generating a response as soon as the user sends a message. This mode is useful for live chat applications, but may impact performance on slower hardware.": "", "When enabled, the model will respond to each chat message in real-time, generating a response as soon as the user sends a message. This mode is useful for live chat applications, but may impact performance on slower hardware.": "",
"wherever you are": "", "wherever you are": "",
"Whisper (Local)": "", "Whisper (Local)": "",
"Why?": "",
"Widescreen Mode": "", "Widescreen Mode": "",
"Won": "", "Won": "",
"Works together with top-k. A higher value (e.g., 0.95) will lead to more diverse text, while a lower value (e.g., 0.5) will generate more focused and conservative text. (Default: 0.9)": "", "Works together with top-k. A higher value (e.g., 0.95) will lead to more diverse text, while a lower value (e.g., 0.5) will generate more focused and conservative text. (Default: 0.9)": "",
+90 -68
View File
@@ -1,4 +1,5 @@
{ {
"-1 for no limit, or a positive integer for a specific limit": "-1 per a cap límit, o un nombre positiu per a un límit específic",
"'s', 'm', 'h', 'd', 'w' or '-1' for no expiration.": "'s', 'm', 'h', 'd', 'w' o '-1' perquè no caduqui mai.", "'s', 'm', 'h', 'd', 'w' or '-1' for no expiration.": "'s', 'm', 'h', 'd', 'w' o '-1' perquè no caduqui mai.",
"(e.g. `sh webui.sh --api --api-auth username_password`)": "(p. ex. `sh webui.sh --api --api-auth username_password`)", "(e.g. `sh webui.sh --api --api-auth username_password`)": "(p. ex. `sh webui.sh --api --api-auth username_password`)",
"(e.g. `sh webui.sh --api`)": "(p. ex. `sh webui.sh --api`)", "(e.g. `sh webui.sh --api`)": "(p. ex. `sh webui.sh --api`)",
@@ -11,9 +12,9 @@
"A task model is used when performing tasks such as generating titles for chats and web search queries": "Un model de tasca s'utilitza quan es realitzen tasques com ara generar títols per a xats i consultes de cerca per a la web", "A task model is used when performing tasks such as generating titles for chats and web search queries": "Un model de tasca s'utilitza quan es realitzen tasques com ara generar títols per a xats i consultes de cerca per a la web",
"a user": "un usuari", "a user": "un usuari",
"About": "Sobre", "About": "Sobre",
"Access": "", "Access": "Accés",
"Access Control": "", "Access Control": "Control d'accés",
"Accessible to all users": "", "Accessible to all users": "Accessible a tots els usuaris",
"Account": "Compte", "Account": "Compte",
"Account Activation Pending": "Activació del compte pendent", "Account Activation Pending": "Activació del compte pendent",
"Accurate information": "Informació precisa", "Accurate information": "Informació precisa",
@@ -30,14 +31,14 @@
"Add content here": "Afegir contingut aquí", "Add content here": "Afegir contingut aquí",
"Add custom prompt": "Afegir una indicació personalitzada", "Add custom prompt": "Afegir una indicació personalitzada",
"Add Files": "Afegir arxius", "Add Files": "Afegir arxius",
"Add Group": "", "Add Group": "Afegir grup",
"Add Memory": "Afegir memòria", "Add Memory": "Afegir memòria",
"Add Model": "Afegir un model", "Add Model": "Afegir un model",
"Add Tag": "Afegir etiqueta", "Add Tag": "Afegir etiqueta",
"Add Tags": "Afegir etiquetes", "Add Tags": "Afegir etiquetes",
"Add text content": "Afegir contingut de text", "Add text content": "Afegir contingut de text",
"Add User": "Afegir un usuari", "Add User": "Afegir un usuari",
"Add User Group": "", "Add User Group": "Afegir grup d'usuaris",
"Adjusting these settings will apply changes universally to all users.": "Si ajustes aquesta preferència, els canvis s'aplicaran de manera universal a tots els usuaris.", "Adjusting these settings will apply changes universally to all users.": "Si ajustes aquesta preferència, els canvis s'aplicaran de manera universal a tots els usuaris.",
"admin": "administrador", "admin": "administrador",
"Admin": "Administrador", "Admin": "Administrador",
@@ -48,17 +49,18 @@
"Advanced Params": "Paràmetres avançats", "Advanced Params": "Paràmetres avançats",
"All chats": "Tots els xats", "All chats": "Tots els xats",
"All Documents": "Tots els documents", "All Documents": "Tots els documents",
"All models deleted successfully": "", "All models deleted successfully": "Tots els models s'han eliminat correctament",
"Allow Chat Delete": "", "Allow Chat Delete": "Permetre eliminar el xat",
"Allow Chat Deletion": "Permetre la supressió del xat", "Allow Chat Deletion": "Permetre la supressió del xat",
"Allow Chat Edit": "", "Allow Chat Edit": "Permetre editar el xat",
"Allow File Upload": "", "Allow File Upload": "Permetre la pujada d'arxius",
"Allow non-local voices": "Permetre veus no locals", "Allow non-local voices": "Permetre veus no locals",
"Allow Temporary Chat": "Permetre el xat temporal", "Allow Temporary Chat": "Permetre el xat temporal",
"Allow User Location": "Permetre la ubicació de l'usuari", "Allow User Location": "Permetre la ubicació de l'usuari",
"Allow Voice Interruption in Call": "Permetre la interrupció de la veu en una trucada", "Allow Voice Interruption in Call": "Permetre la interrupció de la veu en una trucada",
"Already have an account?": "Ja tens un compte?", "Already have an account?": "Ja tens un compte?",
"Alternative to the top_p, and aims to ensure a balance of quality and variety. The parameter p represents the minimum probability for a token to be considered, relative to the probability of the most likely token. For example, with p=0.05 and the most likely token having a probability of 0.9, logits with a value less than 0.045 are filtered out. (Default: 0.0)": "Alternativa al top_p, i pretén garantir un equilibri de qualitat i varietat. El paràmetre p representa la probabilitat mínima que es consideri un token, en relació amb la probabilitat del token més probable. Per exemple, amb p=0,05 i el token més probable amb una probabilitat de 0,9, es filtren els logits amb un valor inferior a 0,045. (Per defecte: 0.0)", "Alternative to the top_p, and aims to ensure a balance of quality and variety. The parameter p represents the minimum probability for a token to be considered, relative to the probability of the most likely token. For example, with p=0.05 and the most likely token having a probability of 0.9, logits with a value less than 0.045 are filtered out. (Default: 0.0)": "Alternativa al top_p, i pretén garantir un equilibri de qualitat i varietat. El paràmetre p representa la probabilitat mínima que es consideri un token, en relació amb la probabilitat del token més probable. Per exemple, amb p=0,05 i el token més probable amb una probabilitat de 0,9, es filtren els logits amb un valor inferior a 0,045. (Per defecte: 0.0)",
"Amazing": "Al·lucinant",
"an assistant": "un assistent", "an assistant": "un assistent",
"and": "i", "and": "i",
"and {{COUNT}} more": "i {{COUNT}} més", "and {{COUNT}} more": "i {{COUNT}} més",
@@ -69,7 +71,7 @@
"API keys": "Claus de l'API", "API keys": "Claus de l'API",
"Application DN": "DN d'aplicació", "Application DN": "DN d'aplicació",
"Application DN Password": "Contrasenya del DN d'aplicació", "Application DN Password": "Contrasenya del DN d'aplicació",
"applies to all users with the \"user\" role": "", "applies to all users with the \"user\" role": "s'aplica a tots els usuaris amb el rol \"usuari\"",
"April": "Abril", "April": "Abril",
"Archive": "Arxiu", "Archive": "Arxiu",
"Archive All Chats": "Arxiva tots els xats", "Archive All Chats": "Arxiva tots els xats",
@@ -89,12 +91,15 @@
"Authenticate": "Autenticar", "Authenticate": "Autenticar",
"Auto-Copy Response to Clipboard": "Copiar la resposta automàticament al porta-retalls", "Auto-Copy Response to Clipboard": "Copiar la resposta automàticament al porta-retalls",
"Auto-playback response": "Reproduir la resposta automàticament", "Auto-playback response": "Reproduir la resposta automàticament",
"Autocomplete Generation": "Generació automàtica",
"Autocomplete Generation Input Max Length": "Entrada màxima de la generació automàtica",
"Automatic1111": "Automatic1111", "Automatic1111": "Automatic1111",
"AUTOMATIC1111 Api Auth String": "Cadena d'autenticació de l'API d'AUTOMATIC1111", "AUTOMATIC1111 Api Auth String": "Cadena d'autenticació de l'API d'AUTOMATIC1111",
"AUTOMATIC1111 Base URL": "URL Base d'AUTOMATIC1111", "AUTOMATIC1111 Base URL": "URL Base d'AUTOMATIC1111",
"AUTOMATIC1111 Base URL is required.": "Es requereix l'URL Base d'AUTOMATIC1111.", "AUTOMATIC1111 Base URL is required.": "Es requereix l'URL Base d'AUTOMATIC1111.",
"Available list": "Llista de disponibles", "Available list": "Llista de disponibles",
"available!": "disponible!", "available!": "disponible!",
"Awful": "Terrible",
"Azure AI Speech": "Azure AI Speech", "Azure AI Speech": "Azure AI Speech",
"Azure Region": "Regió d'Azure", "Azure Region": "Regió d'Azure",
"Back": "Enrere", "Back": "Enrere",
@@ -107,7 +112,7 @@
"Bing Search V7 Endpoint": "Punt de connexió a Bing Search V7", "Bing Search V7 Endpoint": "Punt de connexió a Bing Search V7",
"Bing Search V7 Subscription Key": "Clau de subscripció a Bing Search V7", "Bing Search V7 Subscription Key": "Clau de subscripció a Bing Search V7",
"Brave Search API Key": "Clau API de Brave Search", "Brave Search API Key": "Clau API de Brave Search",
"By {{name}}": "", "By {{name}}": "Per {{name}}",
"Bypass SSL verification for Websites": "Desactivar la verificació SSL per a l'accés a Internet", "Bypass SSL verification for Websites": "Desactivar la verificació SSL per a l'accés a Internet",
"Call": "Trucada", "Call": "Trucada",
"Call feature is not supported when using Web STT engine": "La funció de trucada no s'admet quan s'utilitza el motor Web STT", "Call feature is not supported when using Web STT engine": "La funció de trucada no s'admet quan s'utilitza el motor Web STT",
@@ -117,6 +122,7 @@
"Certificate Path": "Camí del certificat", "Certificate Path": "Camí del certificat",
"Change Password": "Canviar la contrasenya", "Change Password": "Canviar la contrasenya",
"Character": "Personatge", "Character": "Personatge",
"Character limit for autocomplete generation input": "Límit de caràcters per a l'entrada de generació automàtica",
"Chart new frontiers": "Traça noves fronteres", "Chart new frontiers": "Traça noves fronteres",
"Chat": "Xat", "Chat": "Xat",
"Chat Background Image": "Imatge de fons del xat", "Chat Background Image": "Imatge de fons del xat",
@@ -124,7 +130,7 @@
"Chat Controls": "Controls de xat", "Chat Controls": "Controls de xat",
"Chat direction": "Direcció del xat", "Chat direction": "Direcció del xat",
"Chat Overview": "Vista general del xat", "Chat Overview": "Vista general del xat",
"Chat Permissions": "", "Chat Permissions": "Permisos del xat",
"Chat Tags Auto-Generation": "Generació automàtica d'etiquetes del xat", "Chat Tags Auto-Generation": "Generació automàtica d'etiquetes del xat",
"Chats": "Xats", "Chats": "Xats",
"Check Again": "Comprovar-ho de nou", "Check Again": "Comprovar-ho de nou",
@@ -155,7 +161,7 @@
"Code execution": "Execució de codi", "Code execution": "Execució de codi",
"Code formatted successfully": "Codi formatat correctament", "Code formatted successfully": "Codi formatat correctament",
"Collection": "Col·lecció", "Collection": "Col·lecció",
"Color": "", "Color": "Color",
"ComfyUI": "ComfyUI", "ComfyUI": "ComfyUI",
"ComfyUI Base URL": "URL base de ComfyUI", "ComfyUI Base URL": "URL base de ComfyUI",
"ComfyUI Base URL is required.": "L'URL base de ComfyUI és obligatòria.", "ComfyUI Base URL is required.": "L'URL base de ComfyUI és obligatòria.",
@@ -165,6 +171,7 @@
"Completions": "Completaments", "Completions": "Completaments",
"Concurrent Requests": "Peticions simultànies", "Concurrent Requests": "Peticions simultànies",
"Configure": "Configurar", "Configure": "Configurar",
"Configure Models": "Configurar models",
"Confirm": "Confirmar", "Confirm": "Confirmar",
"Confirm Password": "Confirmar la contrasenya", "Confirm Password": "Confirmar la contrasenya",
"Confirm your action": "Confirma la teva acció", "Confirm your action": "Confirma la teva acció",
@@ -189,12 +196,12 @@
"Copy Link": "Copiar l'enllaç", "Copy Link": "Copiar l'enllaç",
"Copy to clipboard": "Copiar al porta-retalls", "Copy to clipboard": "Copiar al porta-retalls",
"Copying to clipboard was successful!": "La còpia al porta-retalls s'ha realitzat correctament", "Copying to clipboard was successful!": "La còpia al porta-retalls s'ha realitzat correctament",
"Create": "", "Create": "Crear",
"Create a knowledge base": "Crear una base de coneixement", "Create a knowledge base": "Crear una base de coneixement",
"Create a model": "Crear un model", "Create a model": "Crear un model",
"Create Account": "Crear un compte", "Create Account": "Crear un compte",
"Create Admin Account": "Crear un compte d'Administrador", "Create Admin Account": "Crear un compte d'Administrador",
"Create Group": "", "Create Group": "Crear grup",
"Create Knowledge": "Crear Coneixement", "Create Knowledge": "Crear Coneixement",
"Create new key": "Crear una nova clau", "Create new key": "Crear una nova clau",
"Create new secret key": "Crear una nova clau secreta", "Create new secret key": "Crear una nova clau secreta",
@@ -213,8 +220,9 @@
"Default (SentenceTransformers)": "Per defecte (SentenceTransformers)", "Default (SentenceTransformers)": "Per defecte (SentenceTransformers)",
"Default Model": "Model per defecte", "Default Model": "Model per defecte",
"Default model updated": "Model per defecte actualitzat", "Default model updated": "Model per defecte actualitzat",
"Default permissions": "", "Default Models": "Models per defecte",
"Default permissions updated successfully": "", "Default permissions": "Permisos per defecte",
"Default permissions updated successfully": "Permisos per defecte actualitzats correctament",
"Default Prompt Suggestions": "Suggeriments d'indicació per defecte", "Default Prompt Suggestions": "Suggeriments d'indicació per defecte",
"Default to 389 or 636 if TLS is enabled": "Per defecte 389 o 636 si TLS està habilitat", "Default to 389 or 636 if TLS is enabled": "Per defecte 389 o 636 si TLS està habilitat",
"Default to ALL": "Per defecte TOTS", "Default to ALL": "Per defecte TOTS",
@@ -222,7 +230,7 @@
"Delete": "Eliminar", "Delete": "Eliminar",
"Delete a model": "Eliminar un model", "Delete a model": "Eliminar un model",
"Delete All Chats": "Eliminar tots els xats", "Delete All Chats": "Eliminar tots els xats",
"Delete All Models": "", "Delete All Models": "Eliminar tots els models",
"Delete chat": "Eliminar xat", "Delete chat": "Eliminar xat",
"Delete Chat": "Eliminar xat", "Delete Chat": "Eliminar xat",
"Delete chat?": "Eliminar el xat?", "Delete chat?": "Eliminar el xat?",
@@ -234,6 +242,7 @@
"Delete User": "Eliminar usuari", "Delete User": "Eliminar usuari",
"Deleted {{deleteModelTag}}": "S'ha eliminat {{deleteModelTag}}", "Deleted {{deleteModelTag}}": "S'ha eliminat {{deleteModelTag}}",
"Deleted {{name}}": "S'ha eliminat {{name}}", "Deleted {{name}}": "S'ha eliminat {{name}}",
"Deleted User": "Usuari eliminat",
"Describe your knowledge base and objectives": "Descriu la teva base de coneixement i objectius", "Describe your knowledge base and objectives": "Descriu la teva base de coneixement i objectius",
"Description": "Descripció", "Description": "Descripció",
"Didn't fully follow instructions": "No s'han seguit les instruccions completament", "Didn't fully follow instructions": "No s'han seguit les instruccions completament",
@@ -248,10 +257,10 @@
"Discover, download, and explore custom tools": "Descobrir, descarregar i explorar eines personalitzades", "Discover, download, and explore custom tools": "Descobrir, descarregar i explorar eines personalitzades",
"Discover, download, and explore model presets": "Descobrir, descarregar i explorar models preconfigurats", "Discover, download, and explore model presets": "Descobrir, descarregar i explorar models preconfigurats",
"Dismissible": "Descartable", "Dismissible": "Descartable",
"Display": "", "Display": "Mostrar",
"Display Emoji in Call": "Mostrar emojis a la trucada", "Display Emoji in Call": "Mostrar emojis a la trucada",
"Display the username instead of You in the Chat": "Mostrar el nom d'usuari en lloc de 'Tu' al xat", "Display the username instead of You in the Chat": "Mostrar el nom d'usuari en lloc de 'Tu' al xat",
"Displays citations in the response": "", "Displays citations in the response": "Mostra les referències a la resposta",
"Dive into knowledge": "Aprofundir en el coneixement", "Dive into knowledge": "Aprofundir en el coneixement",
"Do not install functions from sources you do not fully trust.": "No instal·lis funcions de fonts en què no confiïs plenament.", "Do not install functions from sources you do not fully trust.": "No instal·lis funcions de fonts en què no confiïs plenament.",
"Do not install tools from sources you do not fully trust.": "No instal·lis eines de fonts en què no confiïs plenament.", "Do not install tools from sources you do not fully trust.": "No instal·lis eines de fonts en què no confiïs plenament.",
@@ -267,23 +276,23 @@
"Download": "Descarregar", "Download": "Descarregar",
"Download canceled": "Descàrrega cancel·lada", "Download canceled": "Descàrrega cancel·lada",
"Download Database": "Descarregar la base de dades", "Download Database": "Descarregar la base de dades",
"Drag and drop a file to upload or select a file to view": "", "Drag and drop a file to upload or select a file to view": "Arrossegar un arxiu per pujar o escull un arxiu a veure",
"Draw": "Dibuixar", "Draw": "Dibuixar",
"Drop any files here to add to the conversation": "Deixa qualsevol arxiu aquí per afegir-lo a la conversa", "Drop any files here to add to the conversation": "Deixa qualsevol arxiu aquí per afegir-lo a la conversa",
"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "p. ex. '30s','10m'. Les unitats de temps vàlides són 's', 'm', 'h'.", "e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "p. ex. '30s','10m'. Les unitats de temps vàlides són 's', 'm', 'h'.",
"e.g. A filter to remove profanity from text": "p. ex. Un filtre per eliminar paraules malsonants del text", "e.g. A filter to remove profanity from text": "p. ex. Un filtre per eliminar paraules malsonants del text",
"e.g. My Filter": "p. ex. El meu filtre", "e.g. My Filter": "p. ex. El meu filtre",
"e.g. My Tools": "", "e.g. My Tools": "p. ex. Les meves eines",
"e.g. my_filter": "p. ex. els_meus_filtres", "e.g. my_filter": "p. ex. els_meus_filtres",
"e.g. my_tools": "", "e.g. my_tools": "p. ex. les_meves_eines",
"e.g. Tools for performing various operations": "", "e.g. Tools for performing various operations": "p. ex. Eines per dur a terme operacions",
"Edit": "Editar", "Edit": "Editar",
"Edit Arena Model": "Editar model de l'Arena", "Edit Arena Model": "Editar model de l'Arena",
"Edit Connection": "Editar la connexió", "Edit Connection": "Editar la connexió",
"Edit Default Permissions": "", "Edit Default Permissions": "Editar el permisos per defecte",
"Edit Memory": "Editar la memòria", "Edit Memory": "Editar la memòria",
"Edit User": "Editar l'usuari", "Edit User": "Editar l'usuari",
"Edit User Group": "", "Edit User Group": "Editar el grup d'usuaris",
"ElevenLabs": "ElevenLabs", "ElevenLabs": "ElevenLabs",
"Email": "Correu electrònic", "Email": "Correu electrònic",
"Embark on adventures": "Embarcar en aventures", "Embark on adventures": "Embarcar en aventures",
@@ -291,17 +300,15 @@
"Embedding Model": "Model d'incrustació", "Embedding Model": "Model d'incrustació",
"Embedding Model Engine": "Motor de model d'incrustació", "Embedding Model Engine": "Motor de model d'incrustació",
"Embedding model set to \"{{embedding_model}}\"": "Model d'incrustació configurat a \"{{embedding_model}}\"", "Embedding model set to \"{{embedding_model}}\"": "Model d'incrustació configurat a \"{{embedding_model}}\"",
"Enable API Key Auth": "", "Enable API Key Auth": "Activar l'autenticació amb clau API",
"Enable autocomplete generation for chat messages": "Activar la generació automàtica per als missatges del xat",
"Enable Community Sharing": "Activar l'ús compartit amb la comunitat", "Enable Community Sharing": "Activar l'ús compartit amb la comunitat",
"Enable Memory Locking (mlock) to prevent model data from being swapped out of RAM. This option locks the model's working set of pages into RAM, ensuring that they will not be swapped out to disk. This can help maintain performance by avoiding page faults and ensuring fast data access.": "Activar el bloqueig de memòria (mlock) per evitar que les dades del model s'intercanviïn fora de la memòria RAM. Aquesta opció bloqueja el conjunt de pàgines de treball del model a la memòria RAM, assegurant-se que no s'intercanviaran al disc. Això pot ajudar a mantenir el rendiment evitant errors de pàgina i garantint un accés ràpid a les dades.", "Enable Memory Locking (mlock) to prevent model data from being swapped out of RAM. This option locks the model's working set of pages into RAM, ensuring that they will not be swapped out to disk. This can help maintain performance by avoiding page faults and ensuring fast data access.": "Activar el bloqueig de memòria (mlock) per evitar que les dades del model s'intercanviïn fora de la memòria RAM. Aquesta opció bloqueja el conjunt de pàgines de treball del model a la memòria RAM, assegurant-se que no s'intercanviaran al disc. Això pot ajudar a mantenir el rendiment evitant errors de pàgina i garantint un accés ràpid a les dades.",
"Enable Memory Mapping (mmap) to load model data. This option allows the system to use disk storage as an extension of RAM by treating disk files as if they were in RAM. This can improve model performance by allowing for faster data access. However, it may not work correctly with all systems and can consume a significant amount of disk space.": "Activar l'assignació de memòria (mmap) per carregar les dades del model. Aquesta opció permet que el sistema utilitzi l'emmagatzematge en disc com a extensió de la memòria RAM tractant els fitxers de disc com si estiguessin a la memòria RAM. Això pot millorar el rendiment del model permetent un accés més ràpid a les dades. Tanmateix, és possible que no funcioni correctament amb tots els sistemes i pot consumir una quantitat important d'espai en disc.", "Enable Memory Mapping (mmap) to load model data. This option allows the system to use disk storage as an extension of RAM by treating disk files as if they were in RAM. This can improve model performance by allowing for faster data access. However, it may not work correctly with all systems and can consume a significant amount of disk space.": "Activar l'assignació de memòria (mmap) per carregar les dades del model. Aquesta opció permet que el sistema utilitzi l'emmagatzematge en disc com a extensió de la memòria RAM tractant els fitxers de disc com si estiguessin a la memòria RAM. Això pot millorar el rendiment del model permetent un accés més ràpid a les dades. Tanmateix, és possible que no funcioni correctament amb tots els sistemes i pot consumir una quantitat important d'espai en disc.",
"Enable Message Rating": "Permetre la qualificació de missatges", "Enable Message Rating": "Permetre la qualificació de missatges",
"Enable Mirostat sampling for controlling perplexity. (Default: 0, 0 = Disabled, 1 = Mirostat, 2 = Mirostat 2.0)": "Activar el mostreig de Mirostat per controlar la perplexitat. (Per defecte: 0, 0 = Inhabilitat, 1 = Mirostat, 2 = Mirostat 2.0)", "Enable Mirostat sampling for controlling perplexity. (Default: 0, 0 = Disabled, 1 = Mirostat, 2 = Mirostat 2.0)": "Activar el mostreig de Mirostat per controlar la perplexitat. (Per defecte: 0, 0 = Inhabilitat, 1 = Mirostat, 2 = Mirostat 2.0)",
"Enable New Sign Ups": "Permetre nous registres", "Enable New Sign Ups": "Permetre nous registres",
"Enable Retrieval Query Generation": "",
"Enable Tags Generation": "Activar la generació d'etiquetes",
"Enable Web Search": "Activar la cerca web", "Enable Web Search": "Activar la cerca web",
"Enable Web Search Query Generation": "Activa la generació de consultes de cerca web",
"Enabled": "Habilitat", "Enabled": "Habilitat",
"Engine": "Motor", "Engine": "Motor",
"Ensure your CSV file includes 4 columns in this order: Name, Email, Password, Role.": "Assegura't que els teus fitxers CSV inclouen 4 columnes en aquest ordre: Nom, Correu electrònic, Contrasenya, Rol.", "Ensure your CSV file includes 4 columns in this order: Name, Email, Password, Role.": "Assegura't que els teus fitxers CSV inclouen 4 columnes en aquest ordre: Nom, Correu electrònic, Contrasenya, Rol.",
@@ -326,7 +333,9 @@
"Enter language codes": "Introdueix els codis de llenguatge", "Enter language codes": "Introdueix els codis de llenguatge",
"Enter Model ID": "Introdueix l'identificador del model", "Enter Model ID": "Introdueix l'identificador del model",
"Enter model tag (e.g. {{modelTag}})": "Introdueix l'etiqueta del model (p. ex. {{modelTag}})", "Enter model tag (e.g. {{modelTag}})": "Introdueix l'etiqueta del model (p. ex. {{modelTag}})",
"Enter Mojeek Search API Key": "Introdueix la clau API de Mojeek Search",
"Enter Number of Steps (e.g. 50)": "Introdueix el nombre de passos (p. ex. 50)", "Enter Number of Steps (e.g. 50)": "Introdueix el nombre de passos (p. ex. 50)",
"Enter proxy URL (e.g. https://user:password@host:port)": "Entra l'URL (p. ex. https://user:password@host:port)",
"Enter Sampler (e.g. Euler a)": "Introdueix el mostrejador (p.ex. Euler a)", "Enter Sampler (e.g. Euler a)": "Introdueix el mostrejador (p.ex. Euler a)",
"Enter Scheduler (e.g. Karras)": "Entra el programador (p.ex. Karras)", "Enter Scheduler (e.g. Karras)": "Entra el programador (p.ex. Karras)",
"Enter Score": "Introdueix la puntuació", "Enter Score": "Introdueix la puntuació",
@@ -371,7 +380,7 @@
"Export Config to JSON File": "Exportar la configuració a un arxiu JSON", "Export Config to JSON File": "Exportar la configuració a un arxiu JSON",
"Export Functions": "Exportar funcions", "Export Functions": "Exportar funcions",
"Export Models": "Exportar els models", "Export Models": "Exportar els models",
"Export Presets": "", "Export Presets": "Exportar les configuracions",
"Export Prompts": "Exportar les indicacions", "Export Prompts": "Exportar les indicacions",
"Export to CSV": "Exportar a CSV", "Export to CSV": "Exportar a CSV",
"Export Tools": "Exportar les eines", "Export Tools": "Exportar les eines",
@@ -379,6 +388,7 @@
"Failed to add file.": "No s'ha pogut afegir l'arxiu.", "Failed to add file.": "No s'ha pogut afegir l'arxiu.",
"Failed to create API Key.": "No s'ha pogut crear la clau API.", "Failed to create API Key.": "No s'ha pogut crear la clau API.",
"Failed to read clipboard contents": "No s'ha pogut llegir el contingut del porta-retalls", "Failed to read clipboard contents": "No s'ha pogut llegir el contingut del porta-retalls",
"Failed to save models configuration": "No s'ha pogut desar la configuració dels models",
"Failed to update settings": "No s'han pogut actualitzar les preferències", "Failed to update settings": "No s'han pogut actualitzar les preferències",
"Failed to upload file.": "No s'ha pogut pujar l'arxiu.", "Failed to upload file.": "No s'ha pogut pujar l'arxiu.",
"February": "Febrer", "February": "Febrer",
@@ -432,11 +442,11 @@
"Good Response": "Bona resposta", "Good Response": "Bona resposta",
"Google PSE API Key": "Clau API PSE de Google", "Google PSE API Key": "Clau API PSE de Google",
"Google PSE Engine Id": "Identificador del motor PSE de Google", "Google PSE Engine Id": "Identificador del motor PSE de Google",
"Group created successfully": "", "Group created successfully": "El grup s'ha creat correctament",
"Group deleted successfully": "", "Group deleted successfully": "El grup s'ha eliminat correctament",
"Group Description": "", "Group Description": "Descripció del grup",
"Group Name": "", "Group Name": "Nom del grup",
"Group updated successfully": "", "Group updated successfully": "Grup actualitzat correctament",
"Groups": "Grups", "Groups": "Grups",
"h:mm a": "h:mm a", "h:mm a": "h:mm a",
"Haptic Feedback": "Retorn hàptic", "Haptic Feedback": "Retorn hàptic",
@@ -444,11 +454,12 @@
"Hello, {{name}}": "Hola, {{name}}", "Hello, {{name}}": "Hola, {{name}}",
"Help": "Ajuda", "Help": "Ajuda",
"Help us create the best community leaderboard by sharing your feedback history!": "Ajuda'ns a crear la millor taula de classificació de la comunitat compartint el teu historial de comentaris!", "Help us create the best community leaderboard by sharing your feedback history!": "Ajuda'ns a crear la millor taula de classificació de la comunitat compartint el teu historial de comentaris!",
"Hex Color": "", "Hex Color": "Color hexadecimal",
"Hex Color - Leave empty for default color": "", "Hex Color - Leave empty for default color": "Color hexadecimal - Deixar buit per a color per defecte",
"Hide": "Amaga", "Hide": "Amaga",
"Host": "Servidor", "Host": "Servidor",
"How can I help you today?": "Com et puc ajudar avui?", "How can I help you today?": "Com et puc ajudar avui?",
"How would you rate this response?": "Com avaluaries aquesta resposta?",
"Hybrid Search": "Cerca híbrida", "Hybrid Search": "Cerca híbrida",
"I acknowledge that I have read and I understand the implications of my action. I am aware of the risks associated with executing arbitrary code and I have verified the trustworthiness of the source.": "Afirmo que he llegit i entenc les implicacions de la meva acció. Soc conscient dels riscos associats a l'execució de codi arbitrari i he verificat la fiabilitat de la font.", "I acknowledge that I have read and I understand the implications of my action. I am aware of the risks associated with executing arbitrary code and I have verified the trustworthiness of the source.": "Afirmo que he llegit i entenc les implicacions de la meva acció. Soc conscient dels riscos associats a l'execució de codi arbitrari i he verificat la fiabilitat de la font.",
"ID": "ID", "ID": "ID",
@@ -461,7 +472,7 @@
"Import Config from JSON File": "Importar la configuració des d'un arxiu JSON", "Import Config from JSON File": "Importar la configuració des d'un arxiu JSON",
"Import Functions": "Importar funcions", "Import Functions": "Importar funcions",
"Import Models": "Importar models", "Import Models": "Importar models",
"Import Presets": "", "Import Presets": "Importar configuracions",
"Import Prompts": "Importar indicacions", "Import Prompts": "Importar indicacions",
"Import Tools": "Importar eines", "Import Tools": "Importar eines",
"Include": "Incloure", "Include": "Incloure",
@@ -488,7 +499,7 @@
"Key": "Clau", "Key": "Clau",
"Keyboard shortcuts": "Dreceres de teclat", "Keyboard shortcuts": "Dreceres de teclat",
"Knowledge": "Coneixement", "Knowledge": "Coneixement",
"Knowledge Access": "", "Knowledge Access": "Accés al coneixement",
"Knowledge created successfully.": "Coneixement creat correctament.", "Knowledge created successfully.": "Coneixement creat correctament.",
"Knowledge deleted successfully.": "Coneixement eliminat correctament.", "Knowledge deleted successfully.": "Coneixement eliminat correctament.",
"Knowledge reset successfully.": "Coneixement restablert correctament.", "Knowledge reset successfully.": "Coneixement restablert correctament.",
@@ -518,7 +529,7 @@
"Make sure to export a workflow.json file as API format from ComfyUI.": "Assegura't d'exportar un fitxer workflow.json com a format API des de ComfyUI.", "Make sure to export a workflow.json file as API format from ComfyUI.": "Assegura't d'exportar un fitxer workflow.json com a format API des de ComfyUI.",
"Manage": "Gestionar", "Manage": "Gestionar",
"Manage Arena Models": "Gestionar els models de l'Arena", "Manage Arena Models": "Gestionar els models de l'Arena",
"Manage Ollama": "", "Manage Ollama": "Gestionar Ollama",
"Manage Ollama API Connections": "Gestionar les connexions a l'API d'Ollama", "Manage Ollama API Connections": "Gestionar les connexions a l'API d'Ollama",
"Manage OpenAI API Connections": "Gestionar les connexions a l'API d'OpenAI", "Manage OpenAI API Connections": "Gestionar les connexions a l'API d'OpenAI",
"Manage Pipelines": "Gestionar les Pipelines", "Manage Pipelines": "Gestionar les Pipelines",
@@ -554,17 +565,19 @@
"Model accepts image inputs": "El model accepta entrades d'imatge", "Model accepts image inputs": "El model accepta entrades d'imatge",
"Model created successfully!": "Model creat correctament", "Model created successfully!": "Model creat correctament",
"Model filesystem path detected. Model shortname is required for update, cannot continue.": "S'ha detectat el camí del sistema de fitxers del model. És necessari un nom curt del model per actualitzar, no es pot continuar.", "Model filesystem path detected. Model shortname is required for update, cannot continue.": "S'ha detectat el camí del sistema de fitxers del model. És necessari un nom curt del model per actualitzar, no es pot continuar.",
"Model Filtering": "", "Model Filtering": "Filtrat de models",
"Model ID": "Identificador del model", "Model ID": "Identificador del model",
"Model IDs": "Identificadors del model", "Model IDs": "Identificadors del model",
"Model Name": "Nom del model", "Model Name": "Nom del model",
"Model not selected": "Model no seleccionat", "Model not selected": "Model no seleccionat",
"Model Params": "Paràmetres del model", "Model Params": "Paràmetres del model",
"Model Permissions": "", "Model Permissions": "Permisos dels models",
"Model updated successfully": "Model actualitzat correctament", "Model updated successfully": "Model actualitzat correctament",
"Modelfile Content": "Contingut del Modelfile", "Modelfile Content": "Contingut del Modelfile",
"Models": "Models", "Models": "Models",
"Models Access": "", "Models Access": "Accés als models",
"Models configuration saved successfully": "La configuració dels models s'ha desat correctament",
"Mojeek Search API Key": "Clau API de Mojeek Search",
"more": "més", "more": "més",
"More": "Més", "More": "Més",
"Name": "Nom", "Name": "Nom",
@@ -578,15 +591,16 @@
"No feedbacks found": "No s'han trobat comentaris", "No feedbacks found": "No s'han trobat comentaris",
"No file selected": "No s'ha escollit cap fitxer", "No file selected": "No s'ha escollit cap fitxer",
"No files found.": "No s'han trobat arxius.", "No files found.": "No s'han trobat arxius.",
"No groups with access, add a group to grant access": "", "No groups with access, add a group to grant access": "No hi ha cap grup amb accés, afegeix un grup per concedir accés",
"No HTML, CSS, or JavaScript content found.": "No s'ha trobat contingut HTML, CSS o JavaScript.", "No HTML, CSS, or JavaScript content found.": "No s'ha trobat contingut HTML, CSS o JavaScript.",
"No knowledge found": "No s'ha trobat Coneixement", "No knowledge found": "No s'ha trobat Coneixement",
"No model IDs": "", "No model IDs": "No hi ha IDs de model",
"No models found": "No s'han trobat models", "No models found": "No s'han trobat models",
"No models selected": "No s'ha seleccionat cap model",
"No results found": "No s'han trobat resultats", "No results found": "No s'han trobat resultats",
"No search query generated": "No s'ha generat cap consulta", "No search query generated": "No s'ha generat cap consulta",
"No source available": "Sense font disponible", "No source available": "Sense font disponible",
"No users were found.": "", "No users were found.": "No s'han trobat usuaris",
"No valves to update": "No hi ha cap Valve per actualitzar", "No valves to update": "No hi ha cap Valve per actualitzar",
"None": "Cap", "None": "Cap",
"Not factually correct": "No és clarament correcte", "Not factually correct": "No és clarament correcte",
@@ -611,7 +625,7 @@
"Only alphanumeric characters and hyphens are allowed": "Només es permeten caràcters alfanumèrics i guions", "Only alphanumeric characters and hyphens are allowed": "Només es permeten caràcters alfanumèrics i guions",
"Only alphanumeric characters and hyphens are allowed in the command string.": "Només es permeten caràcters alfanumèrics i guions en la comanda.", "Only alphanumeric characters and hyphens are allowed in the command string.": "Només es permeten caràcters alfanumèrics i guions en la comanda.",
"Only collections can be edited, create a new knowledge base to edit/add documents.": "Només es poden editar col·leccions, crea una nova base de coneixement per editar/afegir documents.", "Only collections can be edited, create a new knowledge base to edit/add documents.": "Només es poden editar col·leccions, crea una nova base de coneixement per editar/afegir documents.",
"Only select users and groups with permission can access": "", "Only select users and groups with permission can access": "Només hi poden accedir usuaris i grups seleccionats amb permís",
"Oops! Looks like the URL is invalid. Please double-check and try again.": "Ui! Sembla que l'URL no és vàlida. Si us plau, revisa-la i torna-ho a provar.", "Oops! Looks like the URL is invalid. Please double-check and try again.": "Ui! Sembla que l'URL no és vàlida. Si us plau, revisa-la i torna-ho a provar.",
"Oops! There are files still uploading. Please wait for the upload to complete.": "Ui! Encara hi ha fitxers pujant-se. Si us plau, espera que finalitzi la càrrega.", "Oops! There are files still uploading. Please wait for the upload to complete.": "Ui! Encara hi ha fitxers pujant-se. Si us plau, espera que finalitzi la càrrega.",
"Oops! There was an error in the previous response.": "Ui! Hi ha hagut un error a la resposta anterior.", "Oops! There was an error in the previous response.": "Ui! Hi ha hagut un error a la resposta anterior.",
@@ -629,21 +643,21 @@
"OpenAI API settings updated": "Configuració de l'API d'OpenAI actualitzada", "OpenAI API settings updated": "Configuració de l'API d'OpenAI actualitzada",
"OpenAI URL/Key required.": "URL/Clau d'OpenAI requerides.", "OpenAI URL/Key required.": "URL/Clau d'OpenAI requerides.",
"or": "o", "or": "o",
"Organize your users": "", "Organize your users": "Organitza els teus usuaris",
"Other": "Altres", "Other": "Altres",
"OUTPUT": "SORTIDA", "OUTPUT": "SORTIDA",
"Output format": "Format de sortida", "Output format": "Format de sortida",
"Overview": "Vista general", "Overview": "Vista general",
"page": "pàgina", "page": "pàgina",
"Password": "Contrasenya", "Password": "Contrasenya",
"Paste Large Text as File": "", "Paste Large Text as File": "Enganxa un text llarg com a fitxer",
"PDF document (.pdf)": "Document PDF (.pdf)", "PDF document (.pdf)": "Document PDF (.pdf)",
"PDF Extract Images (OCR)": "Extreu imatges del PDF (OCR)", "PDF Extract Images (OCR)": "Extreu imatges del PDF (OCR)",
"pending": "pendent", "pending": "pendent",
"Permission denied when accessing media devices": "Permís denegat en accedir a dispositius multimèdia", "Permission denied when accessing media devices": "Permís denegat en accedir a dispositius multimèdia",
"Permission denied when accessing microphone": "Permís denegat en accedir al micròfon", "Permission denied when accessing microphone": "Permís denegat en accedir al micròfon",
"Permission denied when accessing microphone: {{error}}": "Permís denegat en accedir al micròfon: {{error}}", "Permission denied when accessing microphone: {{error}}": "Permís denegat en accedir al micròfon: {{error}}",
"Permissions": "", "Permissions": "Permisos",
"Personalization": "Personalització", "Personalization": "Personalització",
"Pin": "Fixar", "Pin": "Fixar",
"Pinned": "Fixat", "Pinned": "Fixat",
@@ -658,6 +672,7 @@
"Please carefully review the following warnings:": "Si us plau, revisa els següents avisos amb cura:", "Please carefully review the following warnings:": "Si us plau, revisa els següents avisos amb cura:",
"Please enter a prompt": "Si us plau, entra una indicació", "Please enter a prompt": "Si us plau, entra una indicació",
"Please fill in all fields.": "Emplena tots els camps, si us plau.", "Please fill in all fields.": "Emplena tots els camps, si us plau.",
"Please select a model first.": "Si us plau, selecciona un model primer",
"Please select a reason": "Si us plau, selecciona una raó", "Please select a reason": "Si us plau, selecciona una raó",
"Port": "Port", "Port": "Port",
"Positive attitude": "Actitud positiva", "Positive attitude": "Actitud positiva",
@@ -668,14 +683,15 @@
"Profile Image": "Imatge de perfil", "Profile Image": "Imatge de perfil",
"Prompt (e.g. Tell me a fun fact about the Roman Empire)": "Indicació (p.ex. Digues-me quelcom divertit sobre l'Imperi Romà)", "Prompt (e.g. Tell me a fun fact about the Roman Empire)": "Indicació (p.ex. Digues-me quelcom divertit sobre l'Imperi Romà)",
"Prompt Content": "Contingut de la indicació", "Prompt Content": "Contingut de la indicació",
"Prompt created successfully": "", "Prompt created successfully": "Indicació creada correctament",
"Prompt suggestions": "Suggeriments d'indicacions", "Prompt suggestions": "Suggeriments d'indicacions",
"Prompt updated successfully": "", "Prompt updated successfully": "Indicació actualitzada correctament",
"Prompts": "Indicacions", "Prompts": "Indicacions",
"Prompts Access": "", "Prompts Access": "Accés a les indicacions",
"Proxy URL": "URL del proxy",
"Pull \"{{searchValue}}\" from Ollama.com": "Obtenir \"{{searchValue}}\" de Ollama.com", "Pull \"{{searchValue}}\" from Ollama.com": "Obtenir \"{{searchValue}}\" de Ollama.com",
"Pull a model from Ollama.com": "Obtenir un model d'Ollama.com", "Pull a model from Ollama.com": "Obtenir un model d'Ollama.com",
"Query Generation Prompt": "", "Query Generation Prompt": "Indicació per a generació de consulta",
"Query Params": "Paràmetres de consulta", "Query Params": "Paràmetres de consulta",
"RAG Template": "Plantilla RAG", "RAG Template": "Plantilla RAG",
"Rating": "Valoració", "Rating": "Valoració",
@@ -693,17 +709,20 @@
"Remove": "Eliminar", "Remove": "Eliminar",
"Remove Model": "Eliminar el model", "Remove Model": "Eliminar el model",
"Rename": "Canviar el nom", "Rename": "Canviar el nom",
"Reorder Models": "Reordenar els models",
"Repeat Last N": "Repeteix els darrers N", "Repeat Last N": "Repeteix els darrers N",
"Request Mode": "Mode de sol·licitud", "Request Mode": "Mode de sol·licitud",
"Reranking Model": "Model de reavaluació", "Reranking Model": "Model de reavaluació",
"Reranking model disabled": "Model de reavaluació desactivat", "Reranking model disabled": "Model de reavaluació desactivat",
"Reranking model set to \"{{reranking_model}}\"": "Model de reavaluació establert a \"{{reranking_model}}\"", "Reranking model set to \"{{reranking_model}}\"": "Model de reavaluació establert a \"{{reranking_model}}\"",
"Reset": "Restableix", "Reset": "Restableix",
"Reset All Models": "Restablir tots els models",
"Reset Upload Directory": "Restableix el directori de pujades", "Reset Upload Directory": "Restableix el directori de pujades",
"Reset Vector Storage/Knowledge": "Restableix el Repositori de vectors/Coneixement", "Reset Vector Storage/Knowledge": "Restableix el Repositori de vectors/Coneixement",
"Response notifications cannot be activated as the website permissions have been denied. Please visit your browser settings to grant the necessary access.": "Les notifications de resposta no es poden activar perquè els permisos del lloc web han estat rebutjats. Comprova les preferències del navegador per donar l'accés necessari.", "Response notifications cannot be activated as the website permissions have been denied. Please visit your browser settings to grant the necessary access.": "Les notifications de resposta no es poden activar perquè els permisos del lloc web han estat rebutjats. Comprova les preferències del navegador per donar l'accés necessari.",
"Response splitting": "Divisió de la resposta", "Response splitting": "Divisió de la resposta",
"Result": "Resultat", "Result": "Resultat",
"Retrieval Query Generation": "Generació de consultes Retrieval",
"Rich Text Input for Chat": "Entrada de text ric per al xat", "Rich Text Input for Chat": "Entrada de text ric per al xat",
"RK": "RK", "RK": "RK",
"Role": "Rol", "Role": "Rol",
@@ -749,7 +768,7 @@
"Select a base model": "Seleccionar un model base", "Select a base model": "Seleccionar un model base",
"Select a engine": "Seleccionar un motor", "Select a engine": "Seleccionar un motor",
"Select a function": "Seleccionar una funció", "Select a function": "Seleccionar una funció",
"Select a group": "", "Select a group": "Seleccionar un grup",
"Select a model": "Seleccionar un model", "Select a model": "Seleccionar un model",
"Select a pipeline": "Seleccionar una Pipeline", "Select a pipeline": "Seleccionar una Pipeline",
"Select a pipeline url": "Seleccionar l'URL d'una Pipeline", "Select a pipeline url": "Seleccionar l'URL d'una Pipeline",
@@ -772,7 +791,7 @@
"Set as default": "Establir com a predeterminat", "Set as default": "Establir com a predeterminat",
"Set CFG Scale": "Establir l'escala CFG", "Set CFG Scale": "Establir l'escala CFG",
"Set Default Model": "Establir el model predeterminat", "Set Default Model": "Establir el model predeterminat",
"Set embedding model": "", "Set embedding model": "Establir el model d'incrustació",
"Set embedding model (e.g. {{model}})": "Establir el model d'incrustació (p.ex. {{model}})", "Set embedding model (e.g. {{model}})": "Establir el model d'incrustació (p.ex. {{model}})",
"Set Image Size": "Establir la mida de la image", "Set Image Size": "Establir la mida de la image",
"Set reranking model (e.g. {{model}})": "Establir el model de reavaluació (p.ex. {{model}})", "Set reranking model (e.g. {{model}})": "Establir el model de reavaluació (p.ex. {{model}})",
@@ -826,6 +845,7 @@
"System": "Sistema", "System": "Sistema",
"System Instructions": "Instruccions de sistema", "System Instructions": "Instruccions de sistema",
"System Prompt": "Indicació del Sistema", "System Prompt": "Indicació del Sistema",
"Tags Generation": "Generació d'etiquetes",
"Tags Generation Prompt": "Indicació per a la generació d'etiquetes", "Tags Generation Prompt": "Indicació per a la generació d'etiquetes",
"Tail free sampling is used to reduce the impact of less probable tokens from the output. A higher value (e.g., 2.0) will reduce the impact more, while a value of 1.0 disables this setting. (default: 1)": "El mostreig sense cua s'utilitza per reduir l'impacte de tokens menys probables de la sortida. Un valor més alt (p. ex., 2,0) reduirà més l'impacte, mentre que un valor d'1,0 desactiva aquesta configuració. (per defecte: 1)", "Tail free sampling is used to reduce the impact of less probable tokens from the output. A higher value (e.g., 2.0) will reduce the impact more, while a value of 1.0 disables this setting. (default: 1)": "El mostreig sense cua s'utilitza per reduir l'impacte de tokens menys probables de la sortida. Un valor més alt (p. ex., 2,0) reduirà més l'impacte, mentre que un valor d'1,0 desactiva aquesta configuració. (per defecte: 1)",
"Tap to interrupt": "Prem per interrompre", "Tap to interrupt": "Prem per interrompre",
@@ -860,8 +880,8 @@
"This response was generated by \"{{model}}\"": "Aquesta resposta l'ha generat el model \"{{model}}\"", "This response was generated by \"{{model}}\"": "Aquesta resposta l'ha generat el model \"{{model}}\"",
"This will delete": "Això eliminarà", "This will delete": "Això eliminarà",
"This will delete <strong>{{NAME}}</strong> and <strong>all its contents</strong>.": "Això eliminarà <strong>{{NAME}}</strong> i <strong>tots els continguts</strong>.", "This will delete <strong>{{NAME}}</strong> and <strong>all its contents</strong>.": "Això eliminarà <strong>{{NAME}}</strong> i <strong>tots els continguts</strong>.",
"This will delete all models including custom models": "", "This will delete all models including custom models": "Això eliminarà tots els models incloent els personalitzats",
"This will delete all models including custom models and cannot be undone.": "", "This will delete all models including custom models and cannot be undone.": "Això eliminarà tots els models incloent els personalitzats i no es pot desfer",
"This will reset the knowledge base and sync all files. Do you wish to continue?": "Això restablirà la base de coneixement i sincronitzarà tots els fitxers. Vols continuar?", "This will reset the knowledge base and sync all files. Do you wish to continue?": "Això restablirà la base de coneixement i sincronitzarà tots els fitxers. Vols continuar?",
"Thorough explanation": "Explicació en detall", "Thorough explanation": "Explicació en detall",
"Tika": "Tika", "Tika": "Tika",
@@ -891,13 +911,13 @@
"Too verbose": "Massa explicit", "Too verbose": "Massa explicit",
"Tool created successfully": "Eina creada correctament", "Tool created successfully": "Eina creada correctament",
"Tool deleted successfully": "Eina eliminada correctament", "Tool deleted successfully": "Eina eliminada correctament",
"Tool Description": "", "Tool Description": "Descripció de l'eina",
"Tool ID": "", "Tool ID": "ID de l'eina",
"Tool imported successfully": "Eina importada correctament", "Tool imported successfully": "Eina importada correctament",
"Tool Name": "", "Tool Name": "Nom de l'eina",
"Tool updated successfully": "Eina actualitzada correctament", "Tool updated successfully": "Eina actualitzada correctament",
"Tools": "Eines", "Tools": "Eines",
"Tools Access": "", "Tools Access": "Accés a les eines",
"Tools are a function calling system with arbitrary code execution": "Les eines són un sistema de crida a funcions amb execució de codi arbitrari", "Tools are a function calling system with arbitrary code execution": "Les eines són un sistema de crida a funcions amb execució de codi arbitrari",
"Tools have a function calling system that allows arbitrary code execution": "Les eines disposen d'un sistema de crida a funcions que permet execució de codi arbitrari", "Tools have a function calling system that allows arbitrary code execution": "Les eines disposen d'un sistema de crida a funcions que permet execució de codi arbitrari",
"Tools have a function calling system that allows arbitrary code execution.": "Les eines disposen d'un sistema de crida a funcions que permet execució de codi arbitrari.", "Tools have a function calling system that allows arbitrary code execution.": "Les eines disposen d'un sistema de crida a funcions que permet execució de codi arbitrari.",
@@ -937,7 +957,7 @@
"URL Mode": "Mode URL", "URL Mode": "Mode URL",
"Use '#' in the prompt input to load and include your knowledge.": "Utilitza '#' a l'entrada de la indicació per carregar i incloure els teus coneixements.", "Use '#' in the prompt input to load and include your knowledge.": "Utilitza '#' a l'entrada de la indicació per carregar i incloure els teus coneixements.",
"Use Gravatar": "Utilitzar Gravatar", "Use Gravatar": "Utilitzar Gravatar",
"Use groups to group your users and assign permissions.": "", "Use groups to group your users and assign permissions.": "Utilitza grups per agrupar els usuaris i assignar permisos.",
"Use Initials": "Utilitzar inicials", "Use Initials": "Utilitzar inicials",
"use_mlock (Ollama)": "use_mlock (Ollama)", "use_mlock (Ollama)": "use_mlock (Ollama)",
"use_mmap (Ollama)": "use_mmap (Ollama)", "use_mmap (Ollama)": "use_mmap (Ollama)",
@@ -956,18 +976,19 @@
"variable to have them replaced with clipboard content.": "variable per tenir-les reemplaçades amb el contingut del porta-retalls.", "variable to have them replaced with clipboard content.": "variable per tenir-les reemplaçades amb el contingut del porta-retalls.",
"Version": "Versió", "Version": "Versió",
"Version {{selectedVersion}} of {{totalVersions}}": "Versió {{selectedVersion}} de {{totalVersions}}", "Version {{selectedVersion}} of {{totalVersions}}": "Versió {{selectedVersion}} de {{totalVersions}}",
"Visibility": "", "Visibility": "Visibilitat",
"Voice": "Veu", "Voice": "Veu",
"Voice Input": "Entrada de veu", "Voice Input": "Entrada de veu",
"Warning": "Avís", "Warning": "Avís",
"Warning:": "Avís:", "Warning:": "Avís:",
"Warning: Enabling this will allow users to upload arbitrary code on the server.": "", "Warning: Enabling this will allow users to upload arbitrary code on the server.": "Avís: Habilitar això permetrà als usuaris penjar codi arbitrari al servidor.",
"Warning: If you update or change your embedding model, you will need to re-import all documents.": "Avís: Si s'actualitza o es canvia el model d'incrustació, s'hauran de tornar a importar tots els documents.", "Warning: If you update or change your embedding model, you will need to re-import all documents.": "Avís: Si s'actualitza o es canvia el model d'incrustació, s'hauran de tornar a importar tots els documents.",
"Web": "Web", "Web": "Web",
"Web API": "Web API", "Web API": "Web API",
"Web Loader Settings": "Preferències del carregador web", "Web Loader Settings": "Preferències del carregador web",
"Web Search": "Cerca la web", "Web Search": "Cerca la web",
"Web Search Engine": "Motor de cerca de la web", "Web Search Engine": "Motor de cerca de la web",
"Web Search Query Generation": "Generació de consultes per a la cerca de la web",
"Webhook URL": "URL del webhook", "Webhook URL": "URL del webhook",
"WebUI Settings": "Preferències de WebUI", "WebUI Settings": "Preferències de WebUI",
"WebUI will make requests to \"{{url}}/api/chat\"": "WebUI farà peticions a \"{{url}}/api/chat\"", "WebUI will make requests to \"{{url}}/api/chat\"": "WebUI farà peticions a \"{{url}}/api/chat\"",
@@ -978,11 +999,12 @@
"When enabled, the model will respond to each chat message in real-time, generating a response as soon as the user sends a message. This mode is useful for live chat applications, but may impact performance on slower hardware.": "Quan està activat, el model respondrà a cada missatge de xat en temps real, generant una resposta tan bon punt l'usuari envia un missatge. Aquest mode és útil per a aplicacions de xat en directe, però pot afectar el rendiment en maquinari més lent.", "When enabled, the model will respond to each chat message in real-time, generating a response as soon as the user sends a message. This mode is useful for live chat applications, but may impact performance on slower hardware.": "Quan està activat, el model respondrà a cada missatge de xat en temps real, generant una resposta tan bon punt l'usuari envia un missatge. Aquest mode és útil per a aplicacions de xat en directe, però pot afectar el rendiment en maquinari més lent.",
"wherever you are": "allà on estiguis", "wherever you are": "allà on estiguis",
"Whisper (Local)": "Whisper (local)", "Whisper (Local)": "Whisper (local)",
"Why?": "Per què?",
"Widescreen Mode": "Mode de pantalla ampla", "Widescreen Mode": "Mode de pantalla ampla",
"Won": "Ha guanyat", "Won": "Ha guanyat",
"Works together with top-k. A higher value (e.g., 0.95) will lead to more diverse text, while a lower value (e.g., 0.5) will generate more focused and conservative text. (Default: 0.9)": "Funciona juntament amb top-k. Un valor més alt (p. ex., 0,95) donarà lloc a un text més divers, mentre que un valor més baix (p. ex., 0,5) generarà un text més concentrat i conservador. (Per defecte: 0,9)", "Works together with top-k. A higher value (e.g., 0.95) will lead to more diverse text, while a lower value (e.g., 0.5) will generate more focused and conservative text. (Default: 0.9)": "Funciona juntament amb top-k. Un valor més alt (p. ex., 0,95) donarà lloc a un text més divers, mentre que un valor més baix (p. ex., 0,5) generarà un text més concentrat i conservador. (Per defecte: 0,9)",
"Workspace": "Espai de treball", "Workspace": "Espai de treball",
"Workspace Permissions": "", "Workspace Permissions": "Permisos de l'espai de treball",
"Write a prompt suggestion (e.g. Who are you?)": "Escriu una suggerència d'indicació (p. ex. Qui ets?)", "Write a prompt suggestion (e.g. Who are you?)": "Escriu una suggerència d'indicació (p. ex. Qui ets?)",
"Write a summary in 50 words that summarizes [topic or keyword].": "Escriu un resum en 50 paraules que resumeixi [tema o paraula clau].", "Write a summary in 50 words that summarizes [topic or keyword].": "Escriu un resum en 50 paraules que resumeixi [tema o paraula clau].",
"Write something...": "Escriu quelcom...", "Write something...": "Escriu quelcom...",
@@ -992,7 +1014,7 @@
"You can only chat with a maximum of {{maxCount}} file(s) at a time.": "Només pots xatejar amb un màxim de {{maxCount}} fitxers alhora.", "You can only chat with a maximum of {{maxCount}} file(s) at a time.": "Només pots xatejar amb un màxim de {{maxCount}} fitxers alhora.",
"You can personalize your interactions with LLMs by adding memories through the 'Manage' button below, making them more helpful and tailored to you.": "Pots personalitzar les teves interaccions amb els models de llenguatge afegint memòries mitjançant el botó 'Gestiona' que hi ha a continuació, fent-les més útils i adaptades a tu.", "You can personalize your interactions with LLMs by adding memories through the 'Manage' button below, making them more helpful and tailored to you.": "Pots personalitzar les teves interaccions amb els models de llenguatge afegint memòries mitjançant el botó 'Gestiona' que hi ha a continuació, fent-les més útils i adaptades a tu.",
"You cannot upload an empty file.": "No es pot pujar un ariux buit.", "You cannot upload an empty file.": "No es pot pujar un ariux buit.",
"You do not have permission to upload files.": "", "You do not have permission to upload files.": "No tens permisos per pujar arxius.",
"You have no archived conversations.": "No tens converses arxivades.", "You have no archived conversations.": "No tens converses arxivades.",
"You have shared this chat": "Has compartit aquest xat", "You have shared this chat": "Has compartit aquest xat",
"You're a helpful assistant.": "Ets un assistent útil.", "You're a helpful assistant.": "Ets un assistent útil.",
+25 -3
View File
@@ -1,4 +1,5 @@
{ {
"-1 for no limit, or a positive integer for a specific limit": "",
"'s', 'm', 'h', 'd', 'w' or '-1' for no expiration.": "'s', 'm', 'h', 'd', 'w' o '-1' para walay expiration.", "'s', 'm', 'h', 'd', 'w' or '-1' for no expiration.": "'s', 'm', 'h', 'd', 'w' o '-1' para walay expiration.",
"(e.g. `sh webui.sh --api --api-auth username_password`)": "", "(e.g. `sh webui.sh --api --api-auth username_password`)": "",
"(e.g. `sh webui.sh --api`)": "(pananglitan `sh webui.sh --api`)", "(e.g. `sh webui.sh --api`)": "(pananglitan `sh webui.sh --api`)",
@@ -59,6 +60,7 @@
"Allow Voice Interruption in Call": "", "Allow Voice Interruption in Call": "",
"Already have an account?": "Naa na kay account ?", "Already have an account?": "Naa na kay account ?",
"Alternative to the top_p, and aims to ensure a balance of quality and variety. The parameter p represents the minimum probability for a token to be considered, relative to the probability of the most likely token. For example, with p=0.05 and the most likely token having a probability of 0.9, logits with a value less than 0.045 are filtered out. (Default: 0.0)": "", "Alternative to the top_p, and aims to ensure a balance of quality and variety. The parameter p represents the minimum probability for a token to be considered, relative to the probability of the most likely token. For example, with p=0.05 and the most likely token having a probability of 0.9, logits with a value less than 0.045 are filtered out. (Default: 0.0)": "",
"Amazing": "",
"an assistant": "usa ka katabang", "an assistant": "usa ka katabang",
"and": "Ug", "and": "Ug",
"and {{COUNT}} more": "", "and {{COUNT}} more": "",
@@ -89,12 +91,15 @@
"Authenticate": "", "Authenticate": "",
"Auto-Copy Response to Clipboard": "Awtomatikong kopya sa tubag sa clipboard", "Auto-Copy Response to Clipboard": "Awtomatikong kopya sa tubag sa clipboard",
"Auto-playback response": "Autoplay nga tubag", "Auto-playback response": "Autoplay nga tubag",
"Autocomplete Generation": "",
"Autocomplete Generation Input Max Length": "",
"Automatic1111": "", "Automatic1111": "",
"AUTOMATIC1111 Api Auth String": "", "AUTOMATIC1111 Api Auth String": "",
"AUTOMATIC1111 Base URL": "Base URL AUTOMATIC1111", "AUTOMATIC1111 Base URL": "Base URL AUTOMATIC1111",
"AUTOMATIC1111 Base URL is required.": "Ang AUTOMATIC1111 base URL gikinahanglan.", "AUTOMATIC1111 Base URL is required.": "Ang AUTOMATIC1111 base URL gikinahanglan.",
"Available list": "", "Available list": "",
"available!": "magamit!", "available!": "magamit!",
"Awful": "",
"Azure AI Speech": "", "Azure AI Speech": "",
"Azure Region": "", "Azure Region": "",
"Back": "Balik", "Back": "Balik",
@@ -117,6 +122,7 @@
"Certificate Path": "", "Certificate Path": "",
"Change Password": "Usba ang password", "Change Password": "Usba ang password",
"Character": "", "Character": "",
"Character limit for autocomplete generation input": "",
"Chart new frontiers": "", "Chart new frontiers": "",
"Chat": "Panaghisgot", "Chat": "Panaghisgot",
"Chat Background Image": "", "Chat Background Image": "",
@@ -165,6 +171,7 @@
"Completions": "", "Completions": "",
"Concurrent Requests": "", "Concurrent Requests": "",
"Configure": "", "Configure": "",
"Configure Models": "",
"Confirm": "", "Confirm": "",
"Confirm Password": "Kumpirma ang password", "Confirm Password": "Kumpirma ang password",
"Confirm your action": "", "Confirm your action": "",
@@ -213,6 +220,7 @@
"Default (SentenceTransformers)": "", "Default (SentenceTransformers)": "",
"Default Model": "", "Default Model": "",
"Default model updated": "Gi-update nga default template", "Default model updated": "Gi-update nga default template",
"Default Models": "",
"Default permissions": "", "Default permissions": "",
"Default permissions updated successfully": "", "Default permissions updated successfully": "",
"Default Prompt Suggestions": "Default nga prompt nga mga sugyot", "Default Prompt Suggestions": "Default nga prompt nga mga sugyot",
@@ -234,6 +242,7 @@
"Delete User": "", "Delete User": "",
"Deleted {{deleteModelTag}}": "{{deleteModelTag}} gipapas", "Deleted {{deleteModelTag}}": "{{deleteModelTag}} gipapas",
"Deleted {{name}}": "", "Deleted {{name}}": "",
"Deleted User": "",
"Describe your knowledge base and objectives": "", "Describe your knowledge base and objectives": "",
"Description": "Deskripsyon", "Description": "Deskripsyon",
"Didn't fully follow instructions": "", "Didn't fully follow instructions": "",
@@ -292,16 +301,14 @@
"Embedding Model Engine": "", "Embedding Model Engine": "",
"Embedding model set to \"{{embedding_model}}\"": "", "Embedding model set to \"{{embedding_model}}\"": "",
"Enable API Key Auth": "", "Enable API Key Auth": "",
"Enable autocomplete generation for chat messages": "",
"Enable Community Sharing": "", "Enable Community Sharing": "",
"Enable Memory Locking (mlock) to prevent model data from being swapped out of RAM. This option locks the model's working set of pages into RAM, ensuring that they will not be swapped out to disk. This can help maintain performance by avoiding page faults and ensuring fast data access.": "", "Enable Memory Locking (mlock) to prevent model data from being swapped out of RAM. This option locks the model's working set of pages into RAM, ensuring that they will not be swapped out to disk. This can help maintain performance by avoiding page faults and ensuring fast data access.": "",
"Enable Memory Mapping (mmap) to load model data. This option allows the system to use disk storage as an extension of RAM by treating disk files as if they were in RAM. This can improve model performance by allowing for faster data access. However, it may not work correctly with all systems and can consume a significant amount of disk space.": "", "Enable Memory Mapping (mmap) to load model data. This option allows the system to use disk storage as an extension of RAM by treating disk files as if they were in RAM. This can improve model performance by allowing for faster data access. However, it may not work correctly with all systems and can consume a significant amount of disk space.": "",
"Enable Message Rating": "", "Enable Message Rating": "",
"Enable Mirostat sampling for controlling perplexity. (Default: 0, 0 = Disabled, 1 = Mirostat, 2 = Mirostat 2.0)": "", "Enable Mirostat sampling for controlling perplexity. (Default: 0, 0 = Disabled, 1 = Mirostat, 2 = Mirostat 2.0)": "",
"Enable New Sign Ups": "I-enable ang bag-ong mga rehistro", "Enable New Sign Ups": "I-enable ang bag-ong mga rehistro",
"Enable Retrieval Query Generation": "",
"Enable Tags Generation": "",
"Enable Web Search": "", "Enable Web Search": "",
"Enable Web Search Query Generation": "",
"Enabled": "", "Enabled": "",
"Engine": "", "Engine": "",
"Ensure your CSV file includes 4 columns in this order: Name, Email, Password, Role.": "", "Ensure your CSV file includes 4 columns in this order: Name, Email, Password, Role.": "",
@@ -326,7 +333,9 @@
"Enter language codes": "", "Enter language codes": "",
"Enter Model ID": "", "Enter Model ID": "",
"Enter model tag (e.g. {{modelTag}})": "Pagsulod sa template tag (e.g. {{modelTag}})", "Enter model tag (e.g. {{modelTag}})": "Pagsulod sa template tag (e.g. {{modelTag}})",
"Enter Mojeek Search API Key": "",
"Enter Number of Steps (e.g. 50)": "Pagsulod sa gidaghanon sa mga lakang (e.g. 50)", "Enter Number of Steps (e.g. 50)": "Pagsulod sa gidaghanon sa mga lakang (e.g. 50)",
"Enter proxy URL (e.g. https://user:password@host:port)": "",
"Enter Sampler (e.g. Euler a)": "", "Enter Sampler (e.g. Euler a)": "",
"Enter Scheduler (e.g. Karras)": "", "Enter Scheduler (e.g. Karras)": "",
"Enter Score": "", "Enter Score": "",
@@ -379,6 +388,7 @@
"Failed to add file.": "", "Failed to add file.": "",
"Failed to create API Key.": "", "Failed to create API Key.": "",
"Failed to read clipboard contents": "Napakyas sa pagbasa sa sulod sa clipboard", "Failed to read clipboard contents": "Napakyas sa pagbasa sa sulod sa clipboard",
"Failed to save models configuration": "",
"Failed to update settings": "", "Failed to update settings": "",
"Failed to upload file.": "", "Failed to upload file.": "",
"February": "", "February": "",
@@ -449,6 +459,7 @@
"Hide": "Tagoa", "Hide": "Tagoa",
"Host": "", "Host": "",
"How can I help you today?": "Unsaon nako pagtabang kanimo karon?", "How can I help you today?": "Unsaon nako pagtabang kanimo karon?",
"How would you rate this response?": "",
"Hybrid Search": "", "Hybrid Search": "",
"I acknowledge that I have read and I understand the implications of my action. I am aware of the risks associated with executing arbitrary code and I have verified the trustworthiness of the source.": "", "I acknowledge that I have read and I understand the implications of my action. I am aware of the risks associated with executing arbitrary code and I have verified the trustworthiness of the source.": "",
"ID": "", "ID": "",
@@ -565,6 +576,8 @@
"Modelfile Content": "Mga sulod sa template file", "Modelfile Content": "Mga sulod sa template file",
"Models": "Mga modelo", "Models": "Mga modelo",
"Models Access": "", "Models Access": "",
"Models configuration saved successfully": "",
"Mojeek Search API Key": "",
"more": "", "more": "",
"More": "", "More": "",
"Name": "Ngalan", "Name": "Ngalan",
@@ -583,6 +596,7 @@
"No knowledge found": "", "No knowledge found": "",
"No model IDs": "", "No model IDs": "",
"No models found": "", "No models found": "",
"No models selected": "",
"No results found": "", "No results found": "",
"No search query generated": "", "No search query generated": "",
"No source available": "Walay tinubdan nga anaa", "No source available": "Walay tinubdan nga anaa",
@@ -658,6 +672,7 @@
"Please carefully review the following warnings:": "", "Please carefully review the following warnings:": "",
"Please enter a prompt": "", "Please enter a prompt": "",
"Please fill in all fields.": "", "Please fill in all fields.": "",
"Please select a model first.": "",
"Please select a reason": "", "Please select a reason": "",
"Port": "", "Port": "",
"Positive attitude": "", "Positive attitude": "",
@@ -673,6 +688,7 @@
"Prompt updated successfully": "", "Prompt updated successfully": "",
"Prompts": "Mga aghat", "Prompts": "Mga aghat",
"Prompts Access": "", "Prompts Access": "",
"Proxy URL": "",
"Pull \"{{searchValue}}\" from Ollama.com": "", "Pull \"{{searchValue}}\" from Ollama.com": "",
"Pull a model from Ollama.com": "Pagkuha ug template gikan sa Ollama.com", "Pull a model from Ollama.com": "Pagkuha ug template gikan sa Ollama.com",
"Query Generation Prompt": "", "Query Generation Prompt": "",
@@ -693,17 +709,20 @@
"Remove": "", "Remove": "",
"Remove Model": "", "Remove Model": "",
"Rename": "", "Rename": "",
"Reorder Models": "",
"Repeat Last N": "Balika ang katapusang N", "Repeat Last N": "Balika ang katapusang N",
"Request Mode": "Query mode", "Request Mode": "Query mode",
"Reranking Model": "", "Reranking Model": "",
"Reranking model disabled": "", "Reranking model disabled": "",
"Reranking model set to \"{{reranking_model}}\"": "", "Reranking model set to \"{{reranking_model}}\"": "",
"Reset": "", "Reset": "",
"Reset All Models": "",
"Reset Upload Directory": "", "Reset Upload Directory": "",
"Reset Vector Storage/Knowledge": "", "Reset Vector Storage/Knowledge": "",
"Response notifications cannot be activated as the website permissions have been denied. Please visit your browser settings to grant the necessary access.": "", "Response notifications cannot be activated as the website permissions have been denied. Please visit your browser settings to grant the necessary access.": "",
"Response splitting": "", "Response splitting": "",
"Result": "", "Result": "",
"Retrieval Query Generation": "",
"Rich Text Input for Chat": "", "Rich Text Input for Chat": "",
"RK": "", "RK": "",
"Role": "Papel", "Role": "Papel",
@@ -825,6 +844,7 @@
"System": "Sistema", "System": "Sistema",
"System Instructions": "", "System Instructions": "",
"System Prompt": "Madasig nga Sistema", "System Prompt": "Madasig nga Sistema",
"Tags Generation": "",
"Tags Generation Prompt": "", "Tags Generation Prompt": "",
"Tail free sampling is used to reduce the impact of less probable tokens from the output. A higher value (e.g., 2.0) will reduce the impact more, while a value of 1.0 disables this setting. (default: 1)": "", "Tail free sampling is used to reduce the impact of less probable tokens from the output. A higher value (e.g., 2.0) will reduce the impact more, while a value of 1.0 disables this setting. (default: 1)": "",
"Tap to interrupt": "", "Tap to interrupt": "",
@@ -967,6 +987,7 @@
"Web Loader Settings": "", "Web Loader Settings": "",
"Web Search": "", "Web Search": "",
"Web Search Engine": "", "Web Search Engine": "",
"Web Search Query Generation": "",
"Webhook URL": "", "Webhook URL": "",
"WebUI Settings": "Mga Setting sa WebUI", "WebUI Settings": "Mga Setting sa WebUI",
"WebUI will make requests to \"{{url}}/api/chat\"": "", "WebUI will make requests to \"{{url}}/api/chat\"": "",
@@ -977,6 +998,7 @@
"When enabled, the model will respond to each chat message in real-time, generating a response as soon as the user sends a message. This mode is useful for live chat applications, but may impact performance on slower hardware.": "", "When enabled, the model will respond to each chat message in real-time, generating a response as soon as the user sends a message. This mode is useful for live chat applications, but may impact performance on slower hardware.": "",
"wherever you are": "", "wherever you are": "",
"Whisper (Local)": "", "Whisper (Local)": "",
"Why?": "",
"Widescreen Mode": "", "Widescreen Mode": "",
"Won": "", "Won": "",
"Works together with top-k. A higher value (e.g., 0.95) will lead to more diverse text, while a lower value (e.g., 0.5) will generate more focused and conservative text. (Default: 0.9)": "", "Works together with top-k. A higher value (e.g., 0.95) will lead to more diverse text, while a lower value (e.g., 0.5) will generate more focused and conservative text. (Default: 0.9)": "",
+105 -83
View File
@@ -1,67 +1,69 @@
{ {
"-1 for no limit, or a positive integer for a specific limit": "",
"'s', 'm', 'h', 'd', 'w' or '-1' for no expiration.": "'s', 'm', 'h', 'd', 'w' nebo '-1' pro žádné vypršení platnosti.", "'s', 'm', 'h', 'd', 'w' or '-1' for no expiration.": "'s', 'm', 'h', 'd', 'w' nebo '-1' pro žádné vypršení platnosti.",
"(e.g. `sh webui.sh --api --api-auth username_password`)": "(např. `sh webui.sh --api --api-auth username_password`)", "(e.g. `sh webui.sh --api --api-auth username_password`)": "(např. `sh webui.sh --api --api-auth username_password`)",
"(e.g. `sh webui.sh --api`)": "(např. `sh webui.sh --api`)", "(e.g. `sh webui.sh --api`)": "(např. `sh webui.sh --api`)",
"(latest)": "Nejnovější", "(latest)": "Nejnovější",
"{{ models }}": "{{ models }}", "{{ models }}": "{{ models }}",
"{{user}}'s Chats": "{{user}}'s konverzace", "{{user}}'s Chats": "{{user}}'s konverzace",
"{{webUIName}} Backend Required": "Požadováno {{webUIName}} Backend", "{{webUIName}} Backend Required": "Požadován {{webUIName}} Backend",
"*Prompt node ID(s) are required for image generation": "*ID (ID) uzlu pro generování obrázků jsou vyžadována.", "*Prompt node ID(s) are required for image generation": "*Jsou vyžadovány IDs pro prompt node pro generování obrázků",
"A new version (v{{LATEST_VERSION}}) is now available.": "Nová verze (v{{LATEST_VERSION}}) je nyní k dispozici.", "A new version (v{{LATEST_VERSION}}) is now available.": "Nová verze (v{{LATEST_VERSION}}) je nyní k dispozici.",
"A task model is used when performing tasks such as generating titles for chats and web search queries": "Model úloh se používá při provádění úloh, jako je generování názvů pro chaty a vyhledávací dotazy na webu.", "A task model is used when performing tasks such as generating titles for chats and web search queries": "Model úloh se používá při provádění úloh, jako je generování názvů pro chaty a vyhledávací dotazy na webu.",
"a user": "uživatel", "a user": "uživatel",
"About": "O programu", "About": "O programu",
"Access": "", "Access": "Přístup",
"Access Control": "", "Access Control": "",
"Accessible to all users": "", "Accessible to all users": "Přístupné pro všecny uživatele",
"Account": "Účet", "Account": "Účet",
"Account Activation Pending": "Čeká na aktivaci účtu", "Account Activation Pending": "Čeká na aktivaci účtu",
"Accurate information": "Přesné informace", "Accurate information": "Přesné informace",
"Actions": "Akce", "Actions": "Akce",
"Activate this command by typing \"/{{COMMAND}}\" to chat input.": "", "Activate this command by typing \"/{{COMMAND}}\" to chat input.": "Aktivujte tenhle příkaz napsáním \"/{{COMMAND}}\" do chat inputu",
"Active Users": "Aktivní uživatelé", "Active Users": "Aktivní uživatelé",
"Add": "Přidat", "Add": "Přidat",
"Add a model ID": "", "Add a model ID": "Přidat ID modelu",
"Add a short description about what this model does": "Přidejte krátký popis toho, co tento model dělá.", "Add a short description about what this model does": "Přidejte krátký popis toho, co tento model dělá.",
"Add a tag": "Přidat štítek", "Add a tag": "Přidat štítek",
"Add Arena Model": "Přidat model Arena", "Add Arena Model": "Přidat Arena model",
"Add Connection": "", "Add Connection": "Přidat připojení",
"Add Content": "Přidat obsah", "Add Content": "Přidat obsah",
"Add content here": "Přidat obsah zde", "Add content here": "Přidat obsah zde",
"Add custom prompt": "Přidání vlastního výzvy", "Add custom prompt": "Přidání vlastního promptu",
"Add Files": "Přidat soubory", "Add Files": "Přidat soubory",
"Add Group": "", "Add Group": "Přidat skupinu",
"Add Memory": "Přidání paměti", "Add Memory": "Přidat paměť",
"Add Model": "Přidat model", "Add Model": "Přidat model",
"Add Tag": "Přidat štítek", "Add Tag": "Přidat štítek",
"Add Tags": "Přidat značky", "Add Tags": "Přidat štítky",
"Add text content": "Přidejte textový obsah", "Add text content": "Přidejte textový obsah",
"Add User": "Přidat uživatele", "Add User": "Přidat uživatele",
"Add User Group": "", "Add User Group": "Přidatg skupinu uživatelů",
"Adjusting these settings will apply changes universally to all users.": "Úprava těchto nastavení se projeví univerzálně u všech uživatelů.", "Adjusting these settings will apply changes universally to all users.": "Úprava těchto nastavení se projeví univerzálně u všech uživatelů.",
"admin": "Správce", "admin": "amin",
"Admin": "Ahoj! Jak ti mohu pomoci?", "Admin": "Admin",
"Admin Panel": "Administrační panel", "Admin Panel": "Adminis panel",
"Admin Settings": "Nastavení administrátora", "Admin Settings": "Nastavení admina",
"Admins have access to all tools at all times; users need tools assigned per model in the workspace.": "Administrátoři mají přístup ke všem nástrojům kdykoliv; uživatelé potřebují mít nástroje přiřazené podle modelu ve workspace.", "Admins have access to all tools at all times; users need tools assigned per model in the workspace.": "Administrátoři mají přístup ke všem nástrojům kdykoliv; uživatelé potřebují mít nástroje přiřazené podle modelu ve workspace.",
"Advanced Parameters": "Pokročilé parametry", "Advanced Parameters": "Pokročilé parametry",
"Advanced Params": "Pokročilé parametry", "Advanced Params": "Pokročilé parametry",
"All chats": "Všechny chaty", "All chats": "Všechny chaty",
"All Documents": "Všechny dokumenty", "All Documents": "Všechny dokumenty",
"All models deleted successfully": "", "All models deleted successfully": "Všechny modely úspěšně odstráněny",
"Allow Chat Delete": "", "Allow Chat Delete": "Povolit odstranění chatu",
"Allow Chat Deletion": "Povolit odstranění chatu", "Allow Chat Deletion": "Povolit odstranění chatu",
"Allow Chat Edit": "", "Allow Chat Edit": "Povolit úpravu chatu",
"Allow File Upload": "", "Allow File Upload": "Povolit nahrávat soubory",
"Allow non-local voices": "Povolit ne-místní hlasy", "Allow non-local voices": "Povolit ne-místní hlasy",
"Allow Temporary Chat": "Povolit dočasný chat", "Allow Temporary Chat": "Povolit dočasný chat",
"Allow User Location": "Povolit uživatelskou polohu", "Allow User Location": "Povolit uživatelskou polohu",
"Allow Voice Interruption in Call": "Povolit přerušení hlasu při hovoru", "Allow Voice Interruption in Call": "Povolit přerušení hlasu při hovoru",
"Already have an account?": "Už máte účet?", "Already have an account?": "Už máte účet?",
"Alternative to the top_p, and aims to ensure a balance of quality and variety. The parameter p represents the minimum probability for a token to be considered, relative to the probability of the most likely token. For example, with p=0.05 and the most likely token having a probability of 0.9, logits with a value less than 0.045 are filtered out. (Default: 0.0)": "", "Alternative to the top_p, and aims to ensure a balance of quality and variety. The parameter p represents the minimum probability for a token to be considered, relative to the probability of the most likely token. For example, with p=0.05 and the most likely token having a probability of 0.9, logits with a value less than 0.045 are filtered out. (Default: 0.0)": "",
"Amazing": "",
"an assistant": "asistent", "an assistant": "asistent",
"and": "a", "and": "a",
"and {{COUNT}} more": "a {{COUNT}} dalších", "and {{COUNT}} more": "a {{COUNT}} další/ch",
"and create a new shared link.": "a vytvořit nový sdílený odkaz.", "and create a new shared link.": "a vytvořit nový sdílený odkaz.",
"API Base URL": "Základní URL adresa API", "API Base URL": "Základní URL adresa API",
"API Key": "Klíč API", "API Key": "Klíč API",
@@ -76,8 +78,8 @@
"Archived Chats": "Archivované chaty", "Archived Chats": "Archivované chaty",
"archived-chat-export": "", "archived-chat-export": "",
"Are you sure you want to unarchive all archived chats?": "", "Are you sure you want to unarchive all archived chats?": "",
"Are you sure?": "Ano, jsem si jistý.", "Are you sure?": "Jste si jistý?",
"Arena Models": "Modely Arena", "Arena Models": "Arena modely",
"Artifacts": "Artefakty", "Artifacts": "Artefakty",
"Ask a question": "Zeptejte se na otázku", "Ask a question": "Zeptejte se na otázku",
"Assistant": "Ano, jak vám mohu pomoci?", "Assistant": "Ano, jak vám mohu pomoci?",
@@ -86,24 +88,27 @@
"Attribute for Username": "", "Attribute for Username": "",
"Audio": "Zvuk", "Audio": "Zvuk",
"August": "Srpen", "August": "Srpen",
"Authenticate": "", "Authenticate": "Autentikace",
"Auto-Copy Response to Clipboard": "Automatické kopírování odpovědi do schránky", "Auto-Copy Response to Clipboard": "Automatické kopírování odpovědi do schránky",
"Auto-playback response": "Automatická odpověď při přehrávání", "Auto-playback response": "Automatická odpověď při přehrávání",
"Autocomplete Generation": "",
"Autocomplete Generation Input Max Length": "",
"Automatic1111": "Automatic1111", "Automatic1111": "Automatic1111",
"AUTOMATIC1111 Api Auth String": "AUTOMATIC1111 Api Auth String", "AUTOMATIC1111 Api Auth String": "AUTOMATIC1111 Api Auth String",
"AUTOMATIC1111 Base URL": "Výchozí URL pro AUTOMATIC1111", "AUTOMATIC1111 Base URL": "Výchozí URL pro AUTOMATIC1111",
"AUTOMATIC1111 Base URL is required.": "Vyžaduje se základní URL pro AUTOMATIC1111.", "AUTOMATIC1111 Base URL is required.": "Vyžaduje se základní URL pro AUTOMATIC1111.",
"Available list": "Dostupný seznam", "Available list": "Dostupný seznam",
"available!": "k dispozici!", "available!": "k dispozici!",
"Awful": "",
"Azure AI Speech": "Azure AI syntéza řeči", "Azure AI Speech": "Azure AI syntéza řeči",
"Azure Region": "Azure oblast", "Azure Region": "Azure oblast",
"Back": "Zpět", "Back": "Zpět",
"Bad Response": "Špatná odezva", "Bad Response": "Špatná odezva",
"Banners": "Bannery", "Banners": "Bannery",
"Base Model (From)": "Základní model (Z)", "Base Model (From)": "Základní model (z)",
"Batch Size (num_batch)": "Velikost dávky (num_batch)", "Batch Size (num_batch)": "Batch size (num_batch)",
"before": "před", "before": "před",
"Being lazy": "Být líný", "Being lazy": "",
"Bing Search V7 Endpoint": "", "Bing Search V7 Endpoint": "",
"Bing Search V7 Subscription Key": "", "Bing Search V7 Subscription Key": "",
"Brave Search API Key": "Klíč API pro Brave Search", "Brave Search API Key": "Klíč API pro Brave Search",
@@ -112,11 +117,12 @@
"Call": "Volání", "Call": "Volání",
"Call feature is not supported when using Web STT engine": "Funkce pro volání není podporována při použití Web STT engine.", "Call feature is not supported when using Web STT engine": "Funkce pro volání není podporována při použití Web STT engine.",
"Camera": "Kamera", "Camera": "Kamera",
"Cancel": "Zrušit.", "Cancel": "Zrušit",
"Capabilities": "Schopnosti", "Capabilities": "Schopnosti",
"Certificate Path": "", "Certificate Path": "",
"Change Password": "Změna hesla", "Change Password": "Změnit heslo",
"Character": "Znak", "Character": "Znak",
"Character limit for autocomplete generation input": "",
"Chart new frontiers": "", "Chart new frontiers": "",
"Chat": "Chat", "Chat": "Chat",
"Chat Background Image": "Obrázek pozadí chatu", "Chat Background Image": "Obrázek pozadí chatu",
@@ -131,9 +137,9 @@
"Check for updates": "Zkontrolovat aktualizace", "Check for updates": "Zkontrolovat aktualizace",
"Checking for updates...": "Kontrola aktualizací...", "Checking for updates...": "Kontrola aktualizací...",
"Choose a model before saving...": "Vyberte model před uložením...", "Choose a model before saving...": "Vyberte model před uložením...",
"Chunk Overlap": "Překrytí bloků", "Chunk Overlap": "",
"Chunk Params": "Parametry chunků", "Chunk Params": "",
"Chunk Size": "Velikost bloku", "Chunk Size": "",
"Ciphers": "", "Ciphers": "",
"Citation": "Odkaz", "Citation": "Odkaz",
"Clear memory": "Vymazat paměť", "Clear memory": "Vymazat paměť",
@@ -154,8 +160,8 @@
"Close": "Zavřít", "Close": "Zavřít",
"Code execution": "Provádění kódu", "Code execution": "Provádění kódu",
"Code formatted successfully": "Kód byl úspěšně naformátován.", "Code formatted successfully": "Kód byl úspěšně naformátován.",
"Collection": "Sbírka", "Collection": "",
"Color": "", "Color": "Barva",
"ComfyUI": "ComfyUI.", "ComfyUI": "ComfyUI.",
"ComfyUI Base URL": "Základní URL ComfyUI", "ComfyUI Base URL": "Základní URL ComfyUI",
"ComfyUI Base URL is required.": "Je vyžadována základní URL pro ComfyUI.", "ComfyUI Base URL is required.": "Je vyžadována základní URL pro ComfyUI.",
@@ -164,7 +170,8 @@
"Command": "Příkaz", "Command": "Příkaz",
"Completions": "Doplnění", "Completions": "Doplnění",
"Concurrent Requests": "Současné požadavky", "Concurrent Requests": "Současné požadavky",
"Configure": "", "Configure": "Konfigurovat",
"Configure Models": "Konfigurovat modely",
"Confirm": "Potvrdit", "Confirm": "Potvrdit",
"Confirm Password": "Potvrzení hesla", "Confirm Password": "Potvrzení hesla",
"Confirm your action": "Potvrďte svoji akci", "Confirm your action": "Potvrďte svoji akci",
@@ -189,13 +196,13 @@
"Copy Link": "Kopírovat odkaz", "Copy Link": "Kopírovat odkaz",
"Copy to clipboard": "Kopírovat do schránky", "Copy to clipboard": "Kopírovat do schránky",
"Copying to clipboard was successful!": "Kopírování do schránky bylo úspěšné!", "Copying to clipboard was successful!": "Kopírování do schránky bylo úspěšné!",
"Create": "", "Create": "Vytvořit",
"Create a knowledge base": "", "Create a knowledge base": "Vytvořit knowledge base",
"Create a model": "Vytvořte model", "Create a model": "Vytvořte model",
"Create Account": "Vytvořit účet", "Create Account": "Vytvořit účet",
"Create Admin Account": "", "Create Admin Account": "Vytvořit admin účet",
"Create Group": "", "Create Group": "Vytvořit skupinu",
"Create Knowledge": "Vytvořit znalosti", "Create Knowledge": "Vytvořit knowledge",
"Create new key": "Vytvořit nový klíč", "Create new key": "Vytvořit nový klíč",
"Create new secret key": "Vytvořte nový tajný klíč", "Create new secret key": "Vytvořte nový tajný klíč",
"Created at": "Vytvořeno dne", "Created at": "Vytvořeno dne",
@@ -213,8 +220,9 @@
"Default (SentenceTransformers)": "Výchozí (SentenceTransformers)", "Default (SentenceTransformers)": "Výchozí (SentenceTransformers)",
"Default Model": "Výchozí model", "Default Model": "Výchozí model",
"Default model updated": "Výchozí model aktualizován.", "Default model updated": "Výchozí model aktualizován.",
"Default permissions": "", "Default Models": "Výchozí modely",
"Default permissions updated successfully": "", "Default permissions": "Výchozí povolení",
"Default permissions updated successfully": "Výchozí povolení úspěšne aktualizovány",
"Default Prompt Suggestions": "Výchozí návrhy promptů", "Default Prompt Suggestions": "Výchozí návrhy promptů",
"Default to 389 or 636 if TLS is enabled": "", "Default to 389 or 636 if TLS is enabled": "",
"Default to ALL": "", "Default to ALL": "",
@@ -228,23 +236,24 @@
"Delete chat?": "Smazat konverzaci?", "Delete chat?": "Smazat konverzaci?",
"Delete folder?": "Smazat složku?", "Delete folder?": "Smazat složku?",
"Delete function?": "Funkce pro odstranění?", "Delete function?": "Funkce pro odstranění?",
"Delete prompt?": "Smazat výzvu?", "Delete prompt?": "Smazat prompt?",
"delete this link": "smazat tento odkaz", "delete this link": "smazat tento odkaz",
"Delete tool?": "Odstranit nástroj?", "Delete tool?": "Odstranit nástroj?",
"Delete User": "Smazat uživatele", "Delete User": "Smazat uživatele",
"Deleted {{deleteModelTag}}": "Smazáno {{deleteModelTag}}", "Deleted {{deleteModelTag}}": "Smazáno {{deleteModelTag}}",
"Deleted {{name}}": "Smazáno {{name}}", "Deleted {{name}}": "Smazáno {{name}}",
"Deleted User": "",
"Describe your knowledge base and objectives": "", "Describe your knowledge base and objectives": "",
"Description": "Popis", "Description": "Popis",
"Didn't fully follow instructions": "Nenásledovali jste přesně všechny instrukce.", "Didn't fully follow instructions": "Nenásledovali jste přesně všechny instrukce.",
"Disabled": "Zakázáno", "Disabled": "Zakázáno",
"Discover a function": "Objevit funkci", "Discover a function": "Objevit funkci",
"Discover a model": "Objevte model", "Discover a model": "Objevte model",
"Discover a prompt": "Objevte výzvu", "Discover a prompt": "Objevte prompt",
"Discover a tool": "Objevte nástroj", "Discover a tool": "Objevte nástroj",
"Discover wonders": "", "Discover wonders": "",
"Discover, download, and explore custom functions": "Objevujte, stahujte a zkoumejte vlastní funkce", "Discover, download, and explore custom functions": "Objevujte, stahujte a zkoumejte vlastní funkce",
"Discover, download, and explore custom prompts": "Objevte, stáhněte a prozkoumejte vlastní výzvy.", "Discover, download, and explore custom prompts": "Objevte, stáhněte a prozkoumejte vlastní prompty.",
"Discover, download, and explore custom tools": "Objevujte, stahujte a prozkoumávejte vlastní nástroje", "Discover, download, and explore custom tools": "Objevujte, stahujte a prozkoumávejte vlastní nástroje",
"Discover, download, and explore model presets": "Objevte, stáhněte a prozkoumejte přednastavení modelů", "Discover, download, and explore model presets": "Objevte, stáhněte a prozkoumejte přednastavení modelů",
"Dismissible": "Odstranitelné", "Dismissible": "Odstranitelné",
@@ -287,21 +296,19 @@
"ElevenLabs": "ElevenLabs", "ElevenLabs": "ElevenLabs",
"Email": "E-mail", "Email": "E-mail",
"Embark on adventures": "", "Embark on adventures": "",
"Embedding Batch Size": "Velikost dávky pro vkládání", "Embedding Batch Size": "",
"Embedding Model": "Vkládací model (Embedding Model)", "Embedding Model": "Vkládací model (Embedding Model)",
"Embedding Model Engine": "Model zabudovaný motor", "Embedding Model Engine": "",
"Embedding model set to \"{{embedding_model}}\"": "Model vkládání nastaven na \"{{embedding_model}}\"", "Embedding model set to \"{{embedding_model}}\"": "Model vkládání nastaven na \"{{embedding_model}}\"",
"Enable API Key Auth": "", "Enable API Key Auth": "",
"Enable autocomplete generation for chat messages": "",
"Enable Community Sharing": "Povolit sdílení komunity", "Enable Community Sharing": "Povolit sdílení komunity",
"Enable Memory Locking (mlock) to prevent model data from being swapped out of RAM. This option locks the model's working set of pages into RAM, ensuring that they will not be swapped out to disk. This can help maintain performance by avoiding page faults and ensuring fast data access.": "", "Enable Memory Locking (mlock) to prevent model data from being swapped out of RAM. This option locks the model's working set of pages into RAM, ensuring that they will not be swapped out to disk. This can help maintain performance by avoiding page faults and ensuring fast data access.": "",
"Enable Memory Mapping (mmap) to load model data. This option allows the system to use disk storage as an extension of RAM by treating disk files as if they were in RAM. This can improve model performance by allowing for faster data access. However, it may not work correctly with all systems and can consume a significant amount of disk space.": "", "Enable Memory Mapping (mmap) to load model data. This option allows the system to use disk storage as an extension of RAM by treating disk files as if they were in RAM. This can improve model performance by allowing for faster data access. However, it may not work correctly with all systems and can consume a significant amount of disk space.": "",
"Enable Message Rating": "Povolit hodnocení zpráv", "Enable Message Rating": "Povolit hodnocení zpráv",
"Enable Mirostat sampling for controlling perplexity. (Default: 0, 0 = Disabled, 1 = Mirostat, 2 = Mirostat 2.0)": "", "Enable Mirostat sampling for controlling perplexity. (Default: 0, 0 = Disabled, 1 = Mirostat, 2 = Mirostat 2.0)": "",
"Enable New Sign Ups": "Povolit nové registrace", "Enable New Sign Ups": "Povolit nové registrace",
"Enable Retrieval Query Generation": "",
"Enable Tags Generation": "",
"Enable Web Search": "Povolit webové vyhledávání", "Enable Web Search": "Povolit webové vyhledávání",
"Enable Web Search Query Generation": "Povolit generování dotazů pro webové vyhledávání",
"Enabled": "Povoleno", "Enabled": "Povoleno",
"Engine": "Engine", "Engine": "Engine",
"Ensure your CSV file includes 4 columns in this order: Name, Email, Password, Role.": "Ujistěte se, že váš CSV soubor obsahuje 4 sloupce v tomto pořadí: Name, Email, Password, Role.", "Ensure your CSV file includes 4 columns in this order: Name, Email, Password, Role.": "Ujistěte se, že váš CSV soubor obsahuje 4 sloupce v tomto pořadí: Name, Email, Password, Role.",
@@ -326,7 +333,9 @@
"Enter language codes": "Zadejte kódy jazyků", "Enter language codes": "Zadejte kódy jazyků",
"Enter Model ID": "Zadejte ID modelu", "Enter Model ID": "Zadejte ID modelu",
"Enter model tag (e.g. {{modelTag}})": "Zadejte označení modelu (např. {{modelTag}})", "Enter model tag (e.g. {{modelTag}})": "Zadejte označení modelu (např. {{modelTag}})",
"Enter Mojeek Search API Key": "",
"Enter Number of Steps (e.g. 50)": "Zadejte počet kroků (např. 50)", "Enter Number of Steps (e.g. 50)": "Zadejte počet kroků (např. 50)",
"Enter proxy URL (e.g. https://user:password@host:port)": "",
"Enter Sampler (e.g. Euler a)": "Zadejte vzorkovač (např. Euler a)", "Enter Sampler (e.g. Euler a)": "Zadejte vzorkovač (např. Euler a)",
"Enter Scheduler (e.g. Karras)": "Zadejte plánovač (např. Karras)", "Enter Scheduler (e.g. Karras)": "Zadejte plánovač (např. Karras)",
"Enter Score": "Zadejte skóre", "Enter Score": "Zadejte skóre",
@@ -341,7 +350,7 @@
"Enter server label": "", "Enter server label": "",
"Enter server port": "", "Enter server port": "",
"Enter stop sequence": "Zadejte ukončovací sekvenci", "Enter stop sequence": "Zadejte ukončovací sekvenci",
"Enter system prompt": "Vložte systémovou výzvu", "Enter system prompt": "Vložte systémový prompt",
"Enter Tavily API Key": "Zadejte API klíč Tavily", "Enter Tavily API Key": "Zadejte API klíč Tavily",
"Enter Tika Server URL": "Zadejte URL serveru Tika", "Enter Tika Server URL": "Zadejte URL serveru Tika",
"Enter Top K": "Zadejte horní K", "Enter Top K": "Zadejte horní K",
@@ -372,13 +381,14 @@
"Export Functions": "Exportovat funkce", "Export Functions": "Exportovat funkce",
"Export Models": "Export modelů", "Export Models": "Export modelů",
"Export Presets": "", "Export Presets": "",
"Export Prompts": "Exportovat výzvy", "Export Prompts": "Exportovat prompty",
"Export to CSV": "", "Export to CSV": "",
"Export Tools": "Exportní nástroje", "Export Tools": "Exportní nástroje",
"External Models": "Externí modely", "External Models": "Externí modely",
"Failed to add file.": "Nepodařilo se přidat soubor.", "Failed to add file.": "Nepodařilo se přidat soubor.",
"Failed to create API Key.": "Nepodařilo se vytvořit API klíč.", "Failed to create API Key.": "Nepodařilo se vytvořit API klíč.",
"Failed to read clipboard contents": "Nepodařilo se přečíst obsah schránky", "Failed to read clipboard contents": "Nepodařilo se přečíst obsah schránky",
"Failed to save models configuration": "",
"Failed to update settings": "Nepodařilo se aktualizovat nastavení", "Failed to update settings": "Nepodařilo se aktualizovat nastavení",
"Failed to upload file.": "Nepodařilo se nahrát soubor.", "Failed to upload file.": "Nepodařilo se nahrát soubor.",
"February": "Únor", "February": "Únor",
@@ -449,6 +459,7 @@
"Hide": "Schovej", "Hide": "Schovej",
"Host": "", "Host": "",
"How can I help you today?": "Jak vám mohu dnes pomoci?", "How can I help you today?": "Jak vám mohu dnes pomoci?",
"How would you rate this response?": "",
"Hybrid Search": "Hybridní vyhledávání", "Hybrid Search": "Hybridní vyhledávání",
"I acknowledge that I have read and I understand the implications of my action. I am aware of the risks associated with executing arbitrary code and I have verified the trustworthiness of the source.": "Beru na vědomí, že jsem si přečetl a chápu důsledky svých činů. Jsem si vědom rizik spojených s vykonáváním libovolného kódu a ověřil jsem důvěryhodnost zdroje.", "I acknowledge that I have read and I understand the implications of my action. I am aware of the risks associated with executing arbitrary code and I have verified the trustworthiness of the source.": "Beru na vědomí, že jsem si přečetl a chápu důsledky svých činů. Jsem si vědom rizik spojených s vykonáváním libovolného kódu a ověřil jsem důvěryhodnost zdroje.",
"ID": "ID", "ID": "ID",
@@ -521,7 +532,7 @@
"Manage Ollama": "", "Manage Ollama": "",
"Manage Ollama API Connections": "", "Manage Ollama API Connections": "",
"Manage OpenAI API Connections": "", "Manage OpenAI API Connections": "",
"Manage Pipelines": "Správa potrubí", "Manage Pipelines": "Správa pipelines",
"March": "Březen", "March": "Březen",
"Max Tokens (num_predict)": "Maximální počet tokenů (num_predict)", "Max Tokens (num_predict)": "Maximální počet tokenů (num_predict)",
"Max Upload Count": "Maximální počet nahrání", "Max Upload Count": "Maximální počet nahrání",
@@ -565,6 +576,8 @@
"Modelfile Content": "Obsah souboru modelfile", "Modelfile Content": "Obsah souboru modelfile",
"Models": "Modely", "Models": "Modely",
"Models Access": "", "Models Access": "",
"Models configuration saved successfully": "",
"Mojeek Search API Key": "",
"more": "více", "more": "více",
"More": "Více", "More": "Více",
"Name": "Jméno", "Name": "Jméno",
@@ -583,6 +596,7 @@
"No knowledge found": "Nebyly nalezeny žádné znalosti", "No knowledge found": "Nebyly nalezeny žádné znalosti",
"No model IDs": "", "No model IDs": "",
"No models found": "Nebyly nalezeny žádné modely", "No models found": "Nebyly nalezeny žádné modely",
"No models selected": "",
"No results found": "Nebyly nalezeny žádné výsledky", "No results found": "Nebyly nalezeny žádné výsledky",
"No search query generated": "Nebyl vygenerován žádný vyhledávací dotaz.", "No search query generated": "Nebyl vygenerován žádný vyhledávací dotaz.",
"No source available": "Není k dispozici žádný zdroj.", "No source available": "Není k dispozici žádný zdroj.",
@@ -648,16 +662,17 @@
"Pin": "Kolík", "Pin": "Kolík",
"Pinned": "Připnuto", "Pinned": "Připnuto",
"Pioneer insights": "", "Pioneer insights": "",
"Pipeline deleted successfully": "Potrubí bylo úspěšně odstraněno.", "Pipeline deleted successfully": "Pipeline byla úspěšně odstraněna",
"Pipeline downloaded successfully": "Kanál byl úspěšně stažen", "Pipeline downloaded successfully": "Kanál byl úspěšně stažen",
"Pipelines": "Potrubí (Pipelines)", "Pipelines": "",
"Pipelines Not Detected": "Přenosové kanály nebyly detekovány", "Pipelines Not Detected": "Přenosové kanály nebyly detekovány",
"Pipelines Valves": "Potrubní ventily", "Pipelines Valves": "",
"Plain text (.txt)": "Čistý text (.txt)", "Plain text (.txt)": "Čistý text (.txt)",
"Playground": "Hřiště", "Playground": "",
"Please carefully review the following warnings:": "Prosím, pečlivě si přečtěte následující upozornění:", "Please carefully review the following warnings:": "Prosím, pečlivě si přečtěte následující upozornění:",
"Please enter a prompt": "Prosím, zadejte zadání.", "Please enter a prompt": "Prosím, zadejte zadání.",
"Please fill in all fields.": "Prosím, vyplňte všechna pole.", "Please fill in all fields.": "Prosím, vyplňte všechna pole.",
"Please select a model first.": "",
"Please select a reason": "Prosím vyberte důvod", "Please select a reason": "Prosím vyberte důvod",
"Port": "", "Port": "",
"Positive attitude": "Pozitivní přístup", "Positive attitude": "Pozitivní přístup",
@@ -666,13 +681,14 @@
"Previous 30 days": "Předchozích 30 dnů", "Previous 30 days": "Předchozích 30 dnů",
"Previous 7 days": "Předchozích 7 dní", "Previous 7 days": "Předchozích 7 dní",
"Profile Image": "Profilový obrázek", "Profile Image": "Profilový obrázek",
"Prompt (e.g. Tell me a fun fact about the Roman Empire)": "Výzva (např. Řekni mi zábavný fakt o Římské říši)", "Prompt (e.g. Tell me a fun fact about the Roman Empire)": "Prompt (např. Řekni mi zábavný fakt o Římské říši)",
"Prompt Content": "Obsah výzvy", "Prompt Content": "Obsah promptu",
"Prompt created successfully": "", "Prompt created successfully": "",
"Prompt suggestions": "Návrhy výzev", "Prompt suggestions": "Návrhy výzev",
"Prompt updated successfully": "", "Prompt updated successfully": "",
"Prompts": "Výzvy", "Prompts": "Prompty",
"Prompts Access": "", "Prompts Access": "",
"Proxy URL": "",
"Pull \"{{searchValue}}\" from Ollama.com": "Stáhněte \"{{searchValue}}\" z Ollama.com", "Pull \"{{searchValue}}\" from Ollama.com": "Stáhněte \"{{searchValue}}\" z Ollama.com",
"Pull a model from Ollama.com": "Stáhněte model z Ollama.com", "Pull a model from Ollama.com": "Stáhněte model z Ollama.com",
"Query Generation Prompt": "", "Query Generation Prompt": "",
@@ -693,17 +709,20 @@
"Remove": "Odebrat", "Remove": "Odebrat",
"Remove Model": "Odebrat model", "Remove Model": "Odebrat model",
"Rename": "Přejmenovat", "Rename": "Přejmenovat",
"Reorder Models": "",
"Repeat Last N": "Opakovat posledních N", "Repeat Last N": "Opakovat posledních N",
"Request Mode": "Režim žádosti", "Request Mode": "Režim žádosti",
"Reranking Model": "Model pro přehodnocení pořadí", "Reranking Model": "Model pro přehodnocení pořadí",
"Reranking model disabled": "Přeřazovací model je deaktivován", "Reranking model disabled": "Přeřazovací model je deaktivován",
"Reranking model set to \"{{reranking_model}}\"": "Model pro přeřazení nastaven na \"{{reranking_model}}\"", "Reranking model set to \"{{reranking_model}}\"": "Model pro přeřazení nastaven na \"{{reranking_model}}\"",
"Reset": "režim Reset", "Reset": "režim Reset",
"Reset All Models": "",
"Reset Upload Directory": "Resetovat adresář nahrávání", "Reset Upload Directory": "Resetovat adresář nahrávání",
"Reset Vector Storage/Knowledge": "Resetování úložiště vektorů/znalostí", "Reset Vector Storage/Knowledge": "Resetování úložiště vektorů/znalostí",
"Response notifications cannot be activated as the website permissions have been denied. Please visit your browser settings to grant the necessary access.": "Oznámení o odpovědích nelze aktivovat, protože oprávnění webu byla zamítnuta. Navštivte nastavení svého prohlížeče a udělte potřebný přístup.", "Response notifications cannot be activated as the website permissions have been denied. Please visit your browser settings to grant the necessary access.": "Oznámení o odpovědích nelze aktivovat, protože oprávnění webu byla zamítnuta. Navštivte nastavení svého prohlížeče a udělte potřebný přístup.",
"Response splitting": "Rozdělení odpovědi", "Response splitting": "Rozdělení odpovědi",
"Result": "Výsledek", "Result": "Výsledek",
"Retrieval Query Generation": "",
"Rich Text Input for Chat": "Vstup pro chat ve formátu Rich Text", "Rich Text Input for Chat": "Vstup pro chat ve formátu Rich Text",
"RK": "RK", "RK": "RK",
"Role": "Funkci", "Role": "Funkci",
@@ -748,14 +767,14 @@
"See what's new": "Podívejte se, co je nového", "See what's new": "Podívejte se, co je nového",
"Seed": "Semínko", "Seed": "Semínko",
"Select a base model": "Vyberte základní model", "Select a base model": "Vyberte základní model",
"Select a engine": "Vyberte motor", "Select a engine": "Vyberte engine",
"Select a function": "Vyberte funkci", "Select a function": "Vyberte funkci",
"Select a group": "", "Select a group": "",
"Select a model": "Vyberte model", "Select a model": "Vyberte model",
"Select a pipeline": "Vyberte si potrubí (pipeline)", "Select a pipeline": "Vyberte pipeline",
"Select a pipeline url": "Vyberte URL adresu kanálu", "Select a pipeline url": "Vyberte URL adresu kanálu",
"Select a tool": "Vyberte nástroj", "Select a tool": "Vyberte nástroj",
"Select Engine": "Vyberte motor", "Select Engine": "Vyberte engine",
"Select Knowledge": "Vybrat znalosti", "Select Knowledge": "Vybrat znalosti",
"Select model": "Vyberte model", "Select model": "Vyberte model",
"Select only one model to call": "Vyberte pouze jeden model, který chcete použít", "Select only one model to call": "Vyberte pouze jeden model, který chcete použít",
@@ -825,20 +844,21 @@
"Support this plugin:": "Podpořte tento plugin:", "Support this plugin:": "Podpořte tento plugin:",
"Sync directory": "Synchronizovat adresář", "Sync directory": "Synchronizovat adresář",
"System": "System", "System": "System",
"System Instructions": "Pokyny systému", "System Instructions": "",
"System Prompt": "Systémová výzva", "System Prompt": "Systémový prompt",
"Tags Generation Prompt": "Výzva pro generování značek", "Tags Generation": "",
"Tags Generation Prompt": "Prompt pro generování značek",
"Tail free sampling is used to reduce the impact of less probable tokens from the output. A higher value (e.g., 2.0) will reduce the impact more, while a value of 1.0 disables this setting. (default: 1)": "", "Tail free sampling is used to reduce the impact of less probable tokens from the output. A higher value (e.g., 2.0) will reduce the impact more, while a value of 1.0 disables this setting. (default: 1)": "",
"Tap to interrupt": "Klepněte pro přerušení", "Tap to interrupt": "Klepněte pro přerušení",
"Tavily API Key": "Klíč API pro Tavily", "Tavily API Key": "Klíč API pro Tavily",
"Tell us more:": "Řekněte nám více.", "Tell us more:": "Řekněte nám více.",
"Temperature": "Teplota", "Temperature": "",
"Template": "Šablona", "Template": "Šablona",
"Temporary Chat": "Dočasný chat", "Temporary Chat": "Dočasný chat",
"Text Splitter": "Rozdělovač textu", "Text Splitter": "Rozdělovač textu",
"Text-to-Speech Engine": "Stroj pro převod textu na řeč", "Text-to-Speech Engine": "Stroj pro převod textu na řeč",
"Tfs Z": "Tfs Z", "Tfs Z": "Tfs Z",
"Thanks for your feedback!": "Děkuji za vaši zpětnou vazbu!", "Thanks for your feedback!": "Děkujeme za vaši zpětnou vazbu!",
"The Application Account DN you bind with for search": "", "The Application Account DN you bind with for search": "",
"The base to search for users": "", "The base to search for users": "",
"The batch size determines how many text requests are processed together at once. A higher batch size can increase the performance and speed of the model, but it also requires more memory. (Default: 512)": "", "The batch size determines how many text requests are processed together at once. A higher batch size can increase the performance and speed of the model, but it also requires more memory. (Default: 512)": "",
@@ -859,7 +879,7 @@
"This option sets the maximum number of tokens the model can generate in its response. Increasing this limit allows the model to provide longer answers, but it may also increase the likelihood of unhelpful or irrelevant content being generated. (Default: 128)": "", "This option sets the maximum number of tokens the model can generate in its response. Increasing this limit allows the model to provide longer answers, but it may also increase the likelihood of unhelpful or irrelevant content being generated. (Default: 128)": "",
"This option will delete all existing files in the collection and replace them with newly uploaded files.": "Tato volba odstraní všechny existující soubory ve sbírce a nahradí je nově nahranými soubory.", "This option will delete all existing files in the collection and replace them with newly uploaded files.": "Tato volba odstraní všechny existující soubory ve sbírce a nahradí je nově nahranými soubory.",
"This response was generated by \"{{model}}\"": "Tato odpověď byla vygenerována pomocí \"{{model}}\"", "This response was generated by \"{{model}}\"": "Tato odpověď byla vygenerována pomocí \"{{model}}\"",
"This will delete": "Tím se odstraní", "This will delete": "Tohle odstraní",
"This will delete <strong>{{NAME}}</strong> and <strong>all its contents</strong>.": "Tímto dojde k odstranění <strong>{{NAME}}</strong> a <strong>všech jeho obsahů</strong>.", "This will delete <strong>{{NAME}}</strong> and <strong>all its contents</strong>.": "Tímto dojde k odstranění <strong>{{NAME}}</strong> a <strong>všech jeho obsahů</strong>.",
"This will delete all models including custom models": "", "This will delete all models including custom models": "",
"This will delete all models including custom models and cannot be undone.": "", "This will delete all models including custom models and cannot be undone.": "",
@@ -873,16 +893,16 @@
"Title (e.g. Tell me a fun fact)": "Název (např. Řekni mi zajímavost)", "Title (e.g. Tell me a fun fact)": "Název (např. Řekni mi zajímavost)",
"Title Auto-Generation": "Automatické generování názvu", "Title Auto-Generation": "Automatické generování názvu",
"Title cannot be an empty string.": "Název nemůže být prázdným řetězcem.", "Title cannot be an empty string.": "Název nemůže být prázdným řetězcem.",
"Title Generation Prompt": "Generování názvu výzvy", "Title Generation Prompt": "Generování názvu promptu",
"TLS": "", "TLS": "",
"To access the available model names for downloading,": "Pro získání dostupných názvů modelů ke stažení,", "To access the available model names for downloading,": "Pro získání dostupných názvů modelů ke stažení,",
"To access the GGUF models available for downloading,": "Pro přístup k modelům GGUF dostupným pro stažení,", "To access the GGUF models available for downloading,": "Pro přístup k modelům GGUF dostupným pro stažení,",
"To access the WebUI, please reach out to the administrator. Admins can manage user statuses from the Admin Panel.": "Pro přístup k WebUI se prosím obraťte na administrátora. Administrátoři mohou spravovat stavy uživatelů z Admin Panelu.", "To access the WebUI, please reach out to the administrator. Admins can manage user statuses from the Admin Panel.": "Pro přístup k WebUI se prosím obraťte na administrátora. Administrátoři mohou spravovat stavy uživatelů z Admin Panelu.",
"To attach knowledge base here, add them to the \"Knowledge\" workspace first.": "Chcete-li zde připojit znalostní databázi, nejprve ji přidejte do pracovního prostoru \"Knowledge\".", "To attach knowledge base here, add them to the \"Knowledge\" workspace first.": "Chcete-li zde připojit znalostní databázi, nejprve ji přidejte do workspace \"Knowledge\".",
"To protect your privacy, only ratings, model IDs, tags, and metadata are shared from your feedback—your chat logs remain private and are not included.": "Aby byla chráněna vaše soukromí, z vaší zpětné vazby jsou sdílena pouze hodnocení, ID modelů, značky a metadata – vaše chatové záznamy zůstávají soukromé a nejsou zahrnuty.", "To protect your privacy, only ratings, model IDs, tags, and metadata are shared from your feedback—your chat logs remain private and are not included.": "Aby byla chráněna vaše soukromí, z vaší zpětné vazby jsou sdílena pouze hodnocení, ID modelů, značky a metadata – vaše chatové záznamy zůstávají soukromé a nejsou zahrnuty.",
"To select actions here, add them to the \"Functions\" workspace first.": "Chcete-li zde vybrat akce, nejprve je přidejte do pracovní plochy \"Functions\".", "To select actions here, add them to the \"Functions\" workspace first.": "Chcete-li zde vybrat akce, nejprve je přidejte do pracovní plochy \"Functions\".",
"To select filters here, add them to the \"Functions\" workspace first.": "Chcete-li zde vybrat filtry, nejprve je přidejte do pracovního prostoru „Functions“.", "To select filters here, add them to the \"Functions\" workspace first.": "Chcete-li zde vybrat filtry, nejprve je přidejte do workspace „Functions“.",
"To select toolkits here, add them to the \"Tools\" workspace first.": "Chcete-li zde vybrat nástroje, přidejte je nejprve do pracovního prostoru \"Tools\".", "To select toolkits here, add them to the \"Tools\" workspace first.": "Chcete-li zde vybrat nástroje, přidejte je nejprve do workspace \"Tools\".",
"Toast notifications for new updates": "Oznámení ve formě toastů pro nové aktualizace", "Toast notifications for new updates": "Oznámení ve formě toastů pro nové aktualizace",
"Today": "Dnes", "Today": "Dnes",
"Toggle settings": "Přepnout nastavení", "Toggle settings": "Přepnout nastavení",
@@ -893,7 +913,7 @@
"Tool created successfully": "Nástroj byl úspěšně vytvořen.", "Tool created successfully": "Nástroj byl úspěšně vytvořen.",
"Tool deleted successfully": "Nástroj byl úspěšně smazán.", "Tool deleted successfully": "Nástroj byl úspěšně smazán.",
"Tool Description": "", "Tool Description": "",
"Tool ID": "", "Tool ID": "ID nástroje",
"Tool imported successfully": "Nástroj byl úspěšně importován", "Tool imported successfully": "Nástroj byl úspěšně importován",
"Tool Name": "", "Tool Name": "",
"Tool updated successfully": "Nástroj byl úspěšně aktualizován.", "Tool updated successfully": "Nástroj byl úspěšně aktualizován.",
@@ -913,7 +933,7 @@
"Type Hugging Face Resolve (Download) URL": "Zadejte URL pro úspěšné stažení z Hugging Face.", "Type Hugging Face Resolve (Download) URL": "Zadejte URL pro úspěšné stažení z Hugging Face.",
"Uh-oh! There was an issue connecting to {{provider}}.": "Jejda! Došlo k problému s připojením k poskytovateli {{provider}}.", "Uh-oh! There was an issue connecting to {{provider}}.": "Jejda! Došlo k problému s připojením k poskytovateli {{provider}}.",
"UI": "UI", "UI": "UI",
"Unarchive All": "", "Unarchive All": "Odarchivovat všechny",
"Unarchive All Archived Chats": "", "Unarchive All Archived Chats": "",
"Unarchive Chat": "", "Unarchive Chat": "",
"Unlock mysteries": "", "Unlock mysteries": "",
@@ -932,7 +952,7 @@
"Upload directory": "Nahrát adresář", "Upload directory": "Nahrát adresář",
"Upload files": "Nahrát soubory", "Upload files": "Nahrát soubory",
"Upload Files": "Nahrát soubory", "Upload Files": "Nahrát soubory",
"Upload Pipeline": "Nahrát potrubí", "Upload Pipeline": "Nahrát pipeline",
"Upload Progress": "Průběh nahrávání", "Upload Progress": "Průběh nahrávání",
"URL": "", "URL": "",
"URL Mode": "Režim URL", "URL Mode": "Režim URL",
@@ -945,7 +965,7 @@
"user": "uživatel", "user": "uživatel",
"User": "Uživatel", "User": "Uživatel",
"User location successfully retrieved.": "Umístění uživatele bylo úspěšně získáno.", "User location successfully retrieved.": "Umístění uživatele bylo úspěšně získáno.",
"Username": "", "Username": "Uživatelské jméno",
"Users": "Uživatelé", "Users": "Uživatelé",
"Using the default arena model with all models. Click the plus button to add custom models.": "Použití výchozího modelu arény se všemi modely. Kliknutím na tlačítko plus přidejte vlastní modely.", "Using the default arena model with all models. Click the plus button to add custom models.": "Použití výchozího modelu arény se všemi modely. Kliknutím na tlačítko plus přidejte vlastní modely.",
"Utilize": "Využít", "Utilize": "Využít",
@@ -957,7 +977,7 @@
"variable to have them replaced with clipboard content.": "proměnnou, aby byl jejich obsah nahrazen obsahem schránky.", "variable to have them replaced with clipboard content.": "proměnnou, aby byl jejich obsah nahrazen obsahem schránky.",
"Version": "Verze", "Version": "Verze",
"Version {{selectedVersion}} of {{totalVersions}}": "Verze {{selectedVersion}} z {{totalVersions}}", "Version {{selectedVersion}} of {{totalVersions}}": "Verze {{selectedVersion}} z {{totalVersions}}",
"Visibility": "", "Visibility": "Viditelnost",
"Voice": "Hlas", "Voice": "Hlas",
"Voice Input": "Hlasový vstup", "Voice Input": "Hlasový vstup",
"Warning": "Varování", "Warning": "Varování",
@@ -969,6 +989,7 @@
"Web Loader Settings": "Nastavení Web Loaderu", "Web Loader Settings": "Nastavení Web Loaderu",
"Web Search": "Vyhledávání na webu", "Web Search": "Vyhledávání na webu",
"Web Search Engine": "Webový vyhledávač", "Web Search Engine": "Webový vyhledávač",
"Web Search Query Generation": "",
"Webhook URL": "Webhook URL", "Webhook URL": "Webhook URL",
"WebUI Settings": "Nastavení WebUI", "WebUI Settings": "Nastavení WebUI",
"WebUI will make requests to \"{{url}}/api/chat\"": "", "WebUI will make requests to \"{{url}}/api/chat\"": "",
@@ -977,16 +998,17 @@
"What are you working on?": "", "What are you working on?": "",
"What’s New in": "Co je nového v", "What’s New in": "Co je nového v",
"When enabled, the model will respond to each chat message in real-time, generating a response as soon as the user sends a message. This mode is useful for live chat applications, but may impact performance on slower hardware.": "", "When enabled, the model will respond to each chat message in real-time, generating a response as soon as the user sends a message. This mode is useful for live chat applications, but may impact performance on slower hardware.": "",
"wherever you are": "", "wherever you are": "kdekoliv jste",
"Whisper (Local)": "Whisper (Lokálně)", "Whisper (Local)": "Whisper (Lokálně)",
"Why?": "Proč?",
"Widescreen Mode": "Režim širokoúhlého zobrazení", "Widescreen Mode": "Režim širokoúhlého zobrazení",
"Won": "Vyhrál", "Won": "Vyhrál",
"Works together with top-k. A higher value (e.g., 0.95) will lead to more diverse text, while a lower value (e.g., 0.5) will generate more focused and conservative text. (Default: 0.9)": "", "Works together with top-k. A higher value (e.g., 0.95) will lead to more diverse text, while a lower value (e.g., 0.5) will generate more focused and conservative text. (Default: 0.9)": "",
"Workspace": "Pracovní prostor", "Workspace": "",
"Workspace Permissions": "", "Workspace Permissions": "",
"Write a prompt suggestion (e.g. Who are you?)": "Navrhněte dotaz (např. Kdo jsi?)", "Write a prompt suggestion (e.g. Who are you?)": "Navrhněte dotaz (např. Kdo jsi?)",
"Write a summary in 50 words that summarizes [topic or keyword].": "Napište shrnutí na 50 slov, které shrnuje [téma nebo klíčové slovo].", "Write a summary in 50 words that summarizes [topic or keyword].": "Napište shrnutí na 50 slov, které shrnuje [téma nebo klíčové slovo].",
"Write something...": "Napiš něco...", "Write something...": "Napište něco...",
"Write your model template content here": "", "Write your model template content here": "",
"Yesterday": "Včera", "Yesterday": "Včera",
"You": "Vy", "You": "Vy",
@@ -1001,5 +1023,5 @@
"Your account status is currently pending activation.": "Stav vašeho účtu je nyní čekající na aktivaci.", "Your account status is currently pending activation.": "Stav vašeho účtu je nyní čekající na aktivaci.",
"Your entire contribution will go directly to the plugin developer; Open WebUI does not take any percentage. However, the chosen funding platform might have its own fees.": "Váš celý příspěvek půjde přímo vývojáři pluginu; Open WebUI si nebere žádné procento. Vybraná platforma pro financování však může mít vlastní poplatky.", "Your entire contribution will go directly to the plugin developer; Open WebUI does not take any percentage. However, the chosen funding platform might have its own fees.": "Váš celý příspěvek půjde přímo vývojáři pluginu; Open WebUI si nebere žádné procento. Vybraná platforma pro financování však může mít vlastní poplatky.",
"Youtube": "YouTube", "Youtube": "YouTube",
"Youtube Loader Settings": "Nastavení Stahovače YouTube" "Youtube Loader Settings": "Nastavení YouTube loaderu"
} }
+25 -3
View File
@@ -1,4 +1,5 @@
{ {
"-1 for no limit, or a positive integer for a specific limit": "",
"'s', 'm', 'h', 'd', 'w' or '-1' for no expiration.": "'s', 'm', 'h', 'd', 'w' eller '-1' for ingen udløb", "'s', 'm', 'h', 'd', 'w' or '-1' for no expiration.": "'s', 'm', 'h', 'd', 'w' eller '-1' for ingen udløb",
"(e.g. `sh webui.sh --api --api-auth username_password`)": "(f.eks. `sh webui.sh --api --api-auth username_password`)", "(e.g. `sh webui.sh --api --api-auth username_password`)": "(f.eks. `sh webui.sh --api --api-auth username_password`)",
"(e.g. `sh webui.sh --api`)": "(f.eks. `sh webui.sh --api`)", "(e.g. `sh webui.sh --api`)": "(f.eks. `sh webui.sh --api`)",
@@ -59,6 +60,7 @@
"Allow Voice Interruption in Call": "Tillad afbrydelser i stemme i opkald", "Allow Voice Interruption in Call": "Tillad afbrydelser i stemme i opkald",
"Already have an account?": "Har du allerede en profil?", "Already have an account?": "Har du allerede en profil?",
"Alternative to the top_p, and aims to ensure a balance of quality and variety. The parameter p represents the minimum probability for a token to be considered, relative to the probability of the most likely token. For example, with p=0.05 and the most likely token having a probability of 0.9, logits with a value less than 0.045 are filtered out. (Default: 0.0)": "", "Alternative to the top_p, and aims to ensure a balance of quality and variety. The parameter p represents the minimum probability for a token to be considered, relative to the probability of the most likely token. For example, with p=0.05 and the most likely token having a probability of 0.9, logits with a value less than 0.045 are filtered out. (Default: 0.0)": "",
"Amazing": "",
"an assistant": "en assistent", "an assistant": "en assistent",
"and": "og", "and": "og",
"and {{COUNT}} more": "", "and {{COUNT}} more": "",
@@ -89,12 +91,15 @@
"Authenticate": "", "Authenticate": "",
"Auto-Copy Response to Clipboard": "Automatisk kopiering af svar til udklipsholder", "Auto-Copy Response to Clipboard": "Automatisk kopiering af svar til udklipsholder",
"Auto-playback response": "Automatisk afspil svar", "Auto-playback response": "Automatisk afspil svar",
"Autocomplete Generation": "",
"Autocomplete Generation Input Max Length": "",
"Automatic1111": "Automatic1111", "Automatic1111": "Automatic1111",
"AUTOMATIC1111 Api Auth String": "AUTOMATIC1111 Api Auth String", "AUTOMATIC1111 Api Auth String": "AUTOMATIC1111 Api Auth String",
"AUTOMATIC1111 Base URL": "AUTOMATIC1111 Base URL", "AUTOMATIC1111 Base URL": "AUTOMATIC1111 Base URL",
"AUTOMATIC1111 Base URL is required.": "AUTOMATIC1111 Base URL er påkrævet.", "AUTOMATIC1111 Base URL is required.": "AUTOMATIC1111 Base URL er påkrævet.",
"Available list": "Tilgængelige lister", "Available list": "Tilgængelige lister",
"available!": "tilgængelig!", "available!": "tilgængelig!",
"Awful": "",
"Azure AI Speech": "Azure AI Speech", "Azure AI Speech": "Azure AI Speech",
"Azure Region": "Azure Region", "Azure Region": "Azure Region",
"Back": "Tilbage", "Back": "Tilbage",
@@ -117,6 +122,7 @@
"Certificate Path": "", "Certificate Path": "",
"Change Password": "Skift password", "Change Password": "Skift password",
"Character": "", "Character": "",
"Character limit for autocomplete generation input": "",
"Chart new frontiers": "", "Chart new frontiers": "",
"Chat": "Chat", "Chat": "Chat",
"Chat Background Image": "Chat baggrundsbillede", "Chat Background Image": "Chat baggrundsbillede",
@@ -165,6 +171,7 @@
"Completions": "", "Completions": "",
"Concurrent Requests": "Concurrent requests", "Concurrent Requests": "Concurrent requests",
"Configure": "", "Configure": "",
"Configure Models": "",
"Confirm": "Bekræft", "Confirm": "Bekræft",
"Confirm Password": "Bekræft password", "Confirm Password": "Bekræft password",
"Confirm your action": "Bekræft din handling", "Confirm your action": "Bekræft din handling",
@@ -213,6 +220,7 @@
"Default (SentenceTransformers)": "Standard (SentenceTransformers)", "Default (SentenceTransformers)": "Standard (SentenceTransformers)",
"Default Model": "Standard model", "Default Model": "Standard model",
"Default model updated": "Standard model opdateret", "Default model updated": "Standard model opdateret",
"Default Models": "",
"Default permissions": "", "Default permissions": "",
"Default permissions updated successfully": "", "Default permissions updated successfully": "",
"Default Prompt Suggestions": "Standardforslag til prompt", "Default Prompt Suggestions": "Standardforslag til prompt",
@@ -234,6 +242,7 @@
"Delete User": "Slet bruger", "Delete User": "Slet bruger",
"Deleted {{deleteModelTag}}": "Slettede {{deleteModelTag}}", "Deleted {{deleteModelTag}}": "Slettede {{deleteModelTag}}",
"Deleted {{name}}": "Slettede {{name}}", "Deleted {{name}}": "Slettede {{name}}",
"Deleted User": "",
"Describe your knowledge base and objectives": "", "Describe your knowledge base and objectives": "",
"Description": "Beskrivelse", "Description": "Beskrivelse",
"Didn't fully follow instructions": "Fulgte ikke instruktioner", "Didn't fully follow instructions": "Fulgte ikke instruktioner",
@@ -292,16 +301,14 @@
"Embedding Model Engine": "Embedding Model engine", "Embedding Model Engine": "Embedding Model engine",
"Embedding model set to \"{{embedding_model}}\"": "Embedding model sat til \"{{embedding_model}}\"", "Embedding model set to \"{{embedding_model}}\"": "Embedding model sat til \"{{embedding_model}}\"",
"Enable API Key Auth": "", "Enable API Key Auth": "",
"Enable autocomplete generation for chat messages": "",
"Enable Community Sharing": "Aktiver deling til Community", "Enable Community Sharing": "Aktiver deling til Community",
"Enable Memory Locking (mlock) to prevent model data from being swapped out of RAM. This option locks the model's working set of pages into RAM, ensuring that they will not be swapped out to disk. This can help maintain performance by avoiding page faults and ensuring fast data access.": "", "Enable Memory Locking (mlock) to prevent model data from being swapped out of RAM. This option locks the model's working set of pages into RAM, ensuring that they will not be swapped out to disk. This can help maintain performance by avoiding page faults and ensuring fast data access.": "",
"Enable Memory Mapping (mmap) to load model data. This option allows the system to use disk storage as an extension of RAM by treating disk files as if they were in RAM. This can improve model performance by allowing for faster data access. However, it may not work correctly with all systems and can consume a significant amount of disk space.": "", "Enable Memory Mapping (mmap) to load model data. This option allows the system to use disk storage as an extension of RAM by treating disk files as if they were in RAM. This can improve model performance by allowing for faster data access. However, it may not work correctly with all systems and can consume a significant amount of disk space.": "",
"Enable Message Rating": "Aktiver rating af besked", "Enable Message Rating": "Aktiver rating af besked",
"Enable Mirostat sampling for controlling perplexity. (Default: 0, 0 = Disabled, 1 = Mirostat, 2 = Mirostat 2.0)": "", "Enable Mirostat sampling for controlling perplexity. (Default: 0, 0 = Disabled, 1 = Mirostat, 2 = Mirostat 2.0)": "",
"Enable New Sign Ups": "Aktiver nye signups", "Enable New Sign Ups": "Aktiver nye signups",
"Enable Retrieval Query Generation": "",
"Enable Tags Generation": "",
"Enable Web Search": "Aktiver websøgning", "Enable Web Search": "Aktiver websøgning",
"Enable Web Search Query Generation": "Aktiver query generation med websøgning",
"Enabled": "Aktiveret", "Enabled": "Aktiveret",
"Engine": "engine", "Engine": "engine",
"Ensure your CSV file includes 4 columns in this order: Name, Email, Password, Role.": "Sørg for at din CSV-fil indeholder 4 kolonner in denne rækkefølge: Name, Email, Password, Role.", "Ensure your CSV file includes 4 columns in this order: Name, Email, Password, Role.": "Sørg for at din CSV-fil indeholder 4 kolonner in denne rækkefølge: Name, Email, Password, Role.",
@@ -326,7 +333,9 @@
"Enter language codes": "Indtast sprogkoder", "Enter language codes": "Indtast sprogkoder",
"Enter Model ID": "Indtast model-ID", "Enter Model ID": "Indtast model-ID",
"Enter model tag (e.g. {{modelTag}})": "Indtast modelmærke (f.eks. {{modelTag}})", "Enter model tag (e.g. {{modelTag}})": "Indtast modelmærke (f.eks. {{modelTag}})",
"Enter Mojeek Search API Key": "",
"Enter Number of Steps (e.g. 50)": "Indtast antal trin (f.eks. 50)", "Enter Number of Steps (e.g. 50)": "Indtast antal trin (f.eks. 50)",
"Enter proxy URL (e.g. https://user:password@host:port)": "",
"Enter Sampler (e.g. Euler a)": "Indtast sampler (f.eks. Euler a)", "Enter Sampler (e.g. Euler a)": "Indtast sampler (f.eks. Euler a)",
"Enter Scheduler (e.g. Karras)": "Indtast scheduler (f.eks. Karras)", "Enter Scheduler (e.g. Karras)": "Indtast scheduler (f.eks. Karras)",
"Enter Score": "Indtast score", "Enter Score": "Indtast score",
@@ -379,6 +388,7 @@
"Failed to add file.": "", "Failed to add file.": "",
"Failed to create API Key.": "Kunne ikke oprette API-nøgle.", "Failed to create API Key.": "Kunne ikke oprette API-nøgle.",
"Failed to read clipboard contents": "Kunne ikke læse indholdet af udklipsholderen", "Failed to read clipboard contents": "Kunne ikke læse indholdet af udklipsholderen",
"Failed to save models configuration": "",
"Failed to update settings": "Kunne ikke opdatere indstillinger", "Failed to update settings": "Kunne ikke opdatere indstillinger",
"Failed to upload file.": "Kunne ikke uploade fil.", "Failed to upload file.": "Kunne ikke uploade fil.",
"February": "Februar", "February": "Februar",
@@ -449,6 +459,7 @@
"Hide": "Skjul", "Hide": "Skjul",
"Host": "", "Host": "",
"How can I help you today?": "Hvordan kan jeg hjælpe dig i dag?", "How can I help you today?": "Hvordan kan jeg hjælpe dig i dag?",
"How would you rate this response?": "",
"Hybrid Search": "Hybrid søgning", "Hybrid Search": "Hybrid søgning",
"I acknowledge that I have read and I understand the implications of my action. I am aware of the risks associated with executing arbitrary code and I have verified the trustworthiness of the source.": "Jeg anerkender, at jeg har læst og forstået konsekvenserne af min handling. Jeg er opmærksom på de risici, der er forbundet med at udføre vilkårlig kode, og jeg har verificeret kildens troværdighed.", "I acknowledge that I have read and I understand the implications of my action. I am aware of the risks associated with executing arbitrary code and I have verified the trustworthiness of the source.": "Jeg anerkender, at jeg har læst og forstået konsekvenserne af min handling. Jeg er opmærksom på de risici, der er forbundet med at udføre vilkårlig kode, og jeg har verificeret kildens troværdighed.",
"ID": "", "ID": "",
@@ -565,6 +576,8 @@
"Modelfile Content": "Modelfilindhold", "Modelfile Content": "Modelfilindhold",
"Models": "Modeller", "Models": "Modeller",
"Models Access": "", "Models Access": "",
"Models configuration saved successfully": "",
"Mojeek Search API Key": "",
"more": "", "more": "",
"More": "Mere", "More": "Mere",
"Name": "Navn", "Name": "Navn",
@@ -583,6 +596,7 @@
"No knowledge found": "Ingen viden fundet", "No knowledge found": "Ingen viden fundet",
"No model IDs": "", "No model IDs": "",
"No models found": "", "No models found": "",
"No models selected": "",
"No results found": "Ingen resultater fundet", "No results found": "Ingen resultater fundet",
"No search query generated": "Ingen søgeforespørgsel genereret", "No search query generated": "Ingen søgeforespørgsel genereret",
"No source available": "Ingen kilde tilgængelig", "No source available": "Ingen kilde tilgængelig",
@@ -658,6 +672,7 @@
"Please carefully review the following warnings:": "Gennemgå omhyggeligt følgende advarsler:", "Please carefully review the following warnings:": "Gennemgå omhyggeligt følgende advarsler:",
"Please enter a prompt": "", "Please enter a prompt": "",
"Please fill in all fields.": "Udfyld alle felter.", "Please fill in all fields.": "Udfyld alle felter.",
"Please select a model first.": "",
"Please select a reason": "Vælg en årsag", "Please select a reason": "Vælg en årsag",
"Port": "", "Port": "",
"Positive attitude": "Positiv holdning", "Positive attitude": "Positiv holdning",
@@ -673,6 +688,7 @@
"Prompt updated successfully": "", "Prompt updated successfully": "",
"Prompts": "Prompts", "Prompts": "Prompts",
"Prompts Access": "", "Prompts Access": "",
"Proxy URL": "",
"Pull \"{{searchValue}}\" from Ollama.com": "Hent \"{{searchValue}}\" fra Ollama.com", "Pull \"{{searchValue}}\" from Ollama.com": "Hent \"{{searchValue}}\" fra Ollama.com",
"Pull a model from Ollama.com": "Hent en model fra Ollama.com", "Pull a model from Ollama.com": "Hent en model fra Ollama.com",
"Query Generation Prompt": "", "Query Generation Prompt": "",
@@ -693,17 +709,20 @@
"Remove": "Fjern", "Remove": "Fjern",
"Remove Model": "Fjern model", "Remove Model": "Fjern model",
"Rename": "Omdøb", "Rename": "Omdøb",
"Reorder Models": "",
"Repeat Last N": "Gentag sidste N", "Repeat Last N": "Gentag sidste N",
"Request Mode": "Forespørgselstilstand", "Request Mode": "Forespørgselstilstand",
"Reranking Model": "Omarrangeringsmodel", "Reranking Model": "Omarrangeringsmodel",
"Reranking model disabled": "Omarrangeringsmodel deaktiveret", "Reranking model disabled": "Omarrangeringsmodel deaktiveret",
"Reranking model set to \"{{reranking_model}}\"": "Omarrangeringsmodel sat til \"{{reranking_model}}\"", "Reranking model set to \"{{reranking_model}}\"": "Omarrangeringsmodel sat til \"{{reranking_model}}\"",
"Reset": "Nulstil", "Reset": "Nulstil",
"Reset All Models": "",
"Reset Upload Directory": "Nulstil uploadmappe", "Reset Upload Directory": "Nulstil uploadmappe",
"Reset Vector Storage/Knowledge": "", "Reset Vector Storage/Knowledge": "",
"Response notifications cannot be activated as the website permissions have been denied. Please visit your browser settings to grant the necessary access.": "Svarnotifikationer kan ikke aktiveres, da webstedets tilladelser er blevet nægtet. Besøg dine browserindstillinger for at give den nødvendige adgang.", "Response notifications cannot be activated as the website permissions have been denied. Please visit your browser settings to grant the necessary access.": "Svarnotifikationer kan ikke aktiveres, da webstedets tilladelser er blevet nægtet. Besøg dine browserindstillinger for at give den nødvendige adgang.",
"Response splitting": "Svaropdeling", "Response splitting": "Svaropdeling",
"Result": "", "Result": "",
"Retrieval Query Generation": "",
"Rich Text Input for Chat": "", "Rich Text Input for Chat": "",
"RK": "", "RK": "",
"Role": "Rolle", "Role": "Rolle",
@@ -825,6 +844,7 @@
"System": "System", "System": "System",
"System Instructions": "", "System Instructions": "",
"System Prompt": "Systemprompt", "System Prompt": "Systemprompt",
"Tags Generation": "",
"Tags Generation Prompt": "", "Tags Generation Prompt": "",
"Tail free sampling is used to reduce the impact of less probable tokens from the output. A higher value (e.g., 2.0) will reduce the impact more, while a value of 1.0 disables this setting. (default: 1)": "", "Tail free sampling is used to reduce the impact of less probable tokens from the output. A higher value (e.g., 2.0) will reduce the impact more, while a value of 1.0 disables this setting. (default: 1)": "",
"Tap to interrupt": "Tryk for at afbryde", "Tap to interrupt": "Tryk for at afbryde",
@@ -967,6 +987,7 @@
"Web Loader Settings": "Web Loader-indstillinger", "Web Loader Settings": "Web Loader-indstillinger",
"Web Search": "Websøgning", "Web Search": "Websøgning",
"Web Search Engine": "Websøgemaskine", "Web Search Engine": "Websøgemaskine",
"Web Search Query Generation": "",
"Webhook URL": "Webhook-URL", "Webhook URL": "Webhook-URL",
"WebUI Settings": "WebUI-indstillinger", "WebUI Settings": "WebUI-indstillinger",
"WebUI will make requests to \"{{url}}/api/chat\"": "", "WebUI will make requests to \"{{url}}/api/chat\"": "",
@@ -977,6 +998,7 @@
"When enabled, the model will respond to each chat message in real-time, generating a response as soon as the user sends a message. This mode is useful for live chat applications, but may impact performance on slower hardware.": "", "When enabled, the model will respond to each chat message in real-time, generating a response as soon as the user sends a message. This mode is useful for live chat applications, but may impact performance on slower hardware.": "",
"wherever you are": "", "wherever you are": "",
"Whisper (Local)": "Whisper (lokal)", "Whisper (Local)": "Whisper (lokal)",
"Why?": "",
"Widescreen Mode": "Widescreen-tilstand", "Widescreen Mode": "Widescreen-tilstand",
"Won": "", "Won": "",
"Works together with top-k. A higher value (e.g., 0.95) will lead to more diverse text, while a lower value (e.g., 0.5) will generate more focused and conservative text. (Default: 0.9)": "", "Works together with top-k. A higher value (e.g., 0.95) will lead to more diverse text, while a lower value (e.g., 0.5) will generate more focused and conservative text. (Default: 0.9)": "",
+217 -195
View File
@@ -1,4 +1,5 @@
{ {
"-1 for no limit, or a positive integer for a specific limit": "",
"'s', 'm', 'h', 'd', 'w' or '-1' for no expiration.": "'s', 'm', 'h', 'd', 'w' oder '-1' für keine Ablaufzeit.", "'s', 'm', 'h', 'd', 'w' or '-1' for no expiration.": "'s', 'm', 'h', 'd', 'w' oder '-1' für keine Ablaufzeit.",
"(e.g. `sh webui.sh --api --api-auth username_password`)": "(z. B. `sh webui.sh --api --api-auth username_password`)", "(e.g. `sh webui.sh --api --api-auth username_password`)": "(z. B. `sh webui.sh --api --api-auth username_password`)",
"(e.g. `sh webui.sh --api`)": "(z. B. `sh webui.sh --api`)", "(e.g. `sh webui.sh --api`)": "(z. B. `sh webui.sh --api`)",
@@ -11,33 +12,33 @@
"A task model is used when performing tasks such as generating titles for chats and web search queries": "Aufgabenmodelle können Unterhaltungstitel oder Websuchanfragen generieren.", "A task model is used when performing tasks such as generating titles for chats and web search queries": "Aufgabenmodelle können Unterhaltungstitel oder Websuchanfragen generieren.",
"a user": "ein Benutzer", "a user": "ein Benutzer",
"About": "Über", "About": "Über",
"Access": "", "Access": "Zugang",
"Access Control": "", "Access Control": "Zugangskontrolle",
"Accessible to all users": "", "Accessible to all users": "Für alle Benutzer zugänglich",
"Account": "Konto", "Account": "Konto",
"Account Activation Pending": "Kontoaktivierung ausstehend", "Account Activation Pending": "Kontoaktivierung ausstehend",
"Accurate information": "Präzise Information(en)", "Accurate information": "Präzise Information(en)",
"Actions": "Aktionen", "Actions": "Aktionen",
"Activate this command by typing \"/{{COMMAND}}\" to chat input.": "", "Activate this command by typing \"/{{COMMAND}}\" to chat input.": "Aktivieren Sie diesen Befehl, indem Sie \"/{{COMMAND}}\" in die Chat-Eingabe eingeben.",
"Active Users": "Aktive Benutzer", "Active Users": "Aktive Benutzer",
"Add": "Hinzufügen", "Add": "Hinzufügen",
"Add a model ID": "", "Add a model ID": "Modell-ID hinzufügen",
"Add a short description about what this model does": "Fügen Sie eine kurze Beschreibung über dieses Modell hinzu", "Add a short description about what this model does": "Fügen Sie eine kurze Beschreibung über dieses Modell hinzu",
"Add a tag": "Tag hinzufügen", "Add a tag": "Tag hinzufügen",
"Add Arena Model": "Arena-Modell hinzufügen", "Add Arena Model": "Arena-Modell hinzufügen",
"Add Connection": "", "Add Connection": "Verbindung hinzufügen",
"Add Content": "Inhalt hinzufügen", "Add Content": "Inhalt hinzufügen",
"Add content here": "Inhalt hier hinzufügen", "Add content here": "Inhalt hier hinzufügen",
"Add custom prompt": "Benutzerdefinierten Prompt hinzufügen", "Add custom prompt": "Benutzerdefinierten Prompt hinzufügen",
"Add Files": "Dateien hinzufügen", "Add Files": "Dateien hinzufügen",
"Add Group": "", "Add Group": "Gruppe hinzufügen",
"Add Memory": "Erinnerung hinzufügen", "Add Memory": "Erinnerung hinzufügen",
"Add Model": "Modell hinzufügen", "Add Model": "Modell hinzufügen",
"Add Tag": "Tag hinzufügen", "Add Tag": "Tag hinzufügen",
"Add Tags": "Tags hinzufügen", "Add Tags": "Tags hinzufügen",
"Add text content": "Textinhalt hinzufügen", "Add text content": "Textinhalt hinzufügen",
"Add User": "Benutzer hinzufügen", "Add User": "Benutzer hinzufügen",
"Add User Group": "", "Add User Group": "Benutzergruppe hinzufügen",
"Adjusting these settings will apply changes universally to all users.": "Das Anpassen dieser Einstellungen wird Änderungen universell auf alle Benutzer anwenden.", "Adjusting these settings will apply changes universally to all users.": "Das Anpassen dieser Einstellungen wird Änderungen universell auf alle Benutzer anwenden.",
"admin": "Administrator", "admin": "Administrator",
"Admin": "Administrator", "Admin": "Administrator",
@@ -48,17 +49,18 @@
"Advanced Params": "Erweiterte Parameter", "Advanced Params": "Erweiterte Parameter",
"All chats": "Alle Unterhaltungen", "All chats": "Alle Unterhaltungen",
"All Documents": "Alle Dokumente", "All Documents": "Alle Dokumente",
"All models deleted successfully": "", "All models deleted successfully": "Alle Modelle erfolgreich gelöscht",
"Allow Chat Delete": "", "Allow Chat Delete": "Löschen von Unterhaltungen erlauben",
"Allow Chat Deletion": "Löschen von Unterhaltungen erlauben", "Allow Chat Deletion": "Löschen von Unterhaltungen erlauben",
"Allow Chat Edit": "", "Allow Chat Edit": "Bearbeiten von Unterhaltungen erlauben",
"Allow File Upload": "", "Allow File Upload": "Hochladen von Dateien erlauben",
"Allow non-local voices": "Nicht-lokale Stimmen erlauben", "Allow non-local voices": "Nicht-lokale Stimmen erlauben",
"Allow Temporary Chat": "Temporäre Unterhaltungen erlauben", "Allow Temporary Chat": "Temporäre Unterhaltungen erlauben",
"Allow User Location": "Standort freigeben", "Allow User Location": "Standort freigeben",
"Allow Voice Interruption in Call": "Unterbrechung durch Stimme im Anruf zulassen", "Allow Voice Interruption in Call": "Unterbrechung durch Stimme im Anruf zulassen",
"Already have an account?": "Haben Sie bereits einen Account?", "Already have an account?": "Haben Sie bereits einen Account?",
"Alternative to the top_p, and aims to ensure a balance of quality and variety. The parameter p represents the minimum probability for a token to be considered, relative to the probability of the most likely token. For example, with p=0.05 and the most likely token having a probability of 0.9, logits with a value less than 0.045 are filtered out. (Default: 0.0)": "", "Alternative to the top_p, and aims to ensure a balance of quality and variety. The parameter p represents the minimum probability for a token to be considered, relative to the probability of the most likely token. For example, with p=0.05 and the most likely token having a probability of 0.9, logits with a value less than 0.045 are filtered out. (Default: 0.0)": "Alternative zu top_p und zielt darauf ab, ein Gleichgewicht zwischen Qualität und Vielfalt zu gewährleisten. Der Parameter p repräsentiert die Mindestwahrscheinlichkeit für ein Token, um berücksichtigt zu werden, relativ zur Wahrscheinlichkeit des wahrscheinlichsten Tokens. Zum Beispiel, bei p=0.05 und das wahrscheinlichste Token hat eine Wahrscheinlichkeit von 0.9, werden Logits mit einem Wert von weniger als 0.045 herausgefiltert. (Standard: 0.0)",
"Amazing": "Fantastisch",
"an assistant": "ein Assistent", "an assistant": "ein Assistent",
"and": "und", "and": "und",
"and {{COUNT}} more": "und {{COUNT}} mehr", "and {{COUNT}} more": "und {{COUNT}} mehr",
@@ -67,15 +69,15 @@
"API Key": "API-Schlüssel", "API Key": "API-Schlüssel",
"API Key created.": "API-Schlüssel erstellt.", "API Key created.": "API-Schlüssel erstellt.",
"API keys": "API-Schlüssel", "API keys": "API-Schlüssel",
"Application DN": "", "Application DN": "Anwendungs-DN",
"Application DN Password": "", "Application DN Password": "Anwendungs-DN-Passwort",
"applies to all users with the \"user\" role": "", "applies to all users with the \"user\" role": "gilt für alle Benutzer mit der Rolle \"Benutzer\"",
"April": "April", "April": "April",
"Archive": "Archivieren", "Archive": "Archivieren",
"Archive All Chats": "Alle Unterhaltungen archivieren", "Archive All Chats": "Alle Unterhaltungen archivieren",
"Archived Chats": "Archivierte Unterhaltungen", "Archived Chats": "Archivierte Unterhaltungen",
"archived-chat-export": "", "archived-chat-export": "archivierter-chat-export",
"Are you sure you want to unarchive all archived chats?": "", "Are you sure you want to unarchive all archived chats?": "Sind Sie sicher, dass Sie alle archivierten Unterhaltungen wiederherstellen möchten?",
"Are you sure?": "Sind Sie sicher?", "Are you sure?": "Sind Sie sicher?",
"Arena Models": "Arena-Modelle", "Arena Models": "Arena-Modelle",
"Artifacts": "Artefakte", "Artifacts": "Artefakte",
@@ -83,18 +85,21 @@
"Assistant": "Assistent", "Assistant": "Assistent",
"Attach file": "Datei anhängen", "Attach file": "Datei anhängen",
"Attention to detail": "Aufmerksamkeit für Details", "Attention to detail": "Aufmerksamkeit für Details",
"Attribute for Username": "", "Attribute for Username": "Attribut für Benutzername",
"Audio": "Audio", "Audio": "Audio",
"August": "August", "August": "August",
"Authenticate": "", "Authenticate": "Authentifizieren",
"Auto-Copy Response to Clipboard": "Antwort automatisch in die Zwischenablage kopieren", "Auto-Copy Response to Clipboard": "Antwort automatisch in die Zwischenablage kopieren",
"Auto-playback response": "Antwort automatisch abspielen", "Auto-playback response": "Antwort automatisch abspielen",
"Autocomplete Generation": "",
"Autocomplete Generation Input Max Length": "",
"Automatic1111": "Automatic1111", "Automatic1111": "Automatic1111",
"AUTOMATIC1111 Api Auth String": "AUTOMATIC1111-API-Authentifizierungszeichenfolge", "AUTOMATIC1111 Api Auth String": "AUTOMATIC1111-API-Authentifizierungszeichenfolge",
"AUTOMATIC1111 Base URL": "AUTOMATIC1111-Basis-URL", "AUTOMATIC1111 Base URL": "AUTOMATIC1111-Basis-URL",
"AUTOMATIC1111 Base URL is required.": "AUTOMATIC1111-Basis-URL ist erforderlich.", "AUTOMATIC1111 Base URL is required.": "AUTOMATIC1111-Basis-URL ist erforderlich.",
"Available list": "Verfügbare Liste", "Available list": "Verfügbare Liste",
"available!": "Verfügbar!", "available!": "Verfügbar!",
"Awful": "Schrecklich",
"Azure AI Speech": "Azure AI Speech", "Azure AI Speech": "Azure AI Speech",
"Azure Region": "Azure-Region", "Azure Region": "Azure-Region",
"Back": "Zurück", "Back": "Zurück",
@@ -104,27 +109,28 @@
"Batch Size (num_batch)": "Stapelgröße (num_batch)", "Batch Size (num_batch)": "Stapelgröße (num_batch)",
"before": "bereits geteilt", "before": "bereits geteilt",
"Being lazy": "Faulheit", "Being lazy": "Faulheit",
"Bing Search V7 Endpoint": "", "Bing Search V7 Endpoint": "Bing Search V7-Endpunkt",
"Bing Search V7 Subscription Key": "", "Bing Search V7 Subscription Key": "Bing Search V7-Abonnement-Schlüssel",
"Brave Search API Key": "Brave Search API-Schlüssel", "Brave Search API Key": "Brave Search API-Schlüssel",
"By {{name}}": "", "By {{name}}": "Von {{name}}",
"Bypass SSL verification for Websites": "SSL-Überprüfung für Webseiten umgehen", "Bypass SSL verification for Websites": "SSL-Überprüfung für Webseiten umgehen",
"Call": "Anrufen", "Call": "Anrufen",
"Call feature is not supported when using Web STT engine": "Die Anruffunktion wird nicht unterstützt, wenn die Web-STT-Engine verwendet wird.", "Call feature is not supported when using Web STT engine": "Die Anruffunktion wird nicht unterstützt, wenn die Web-STT-Engine verwendet wird.",
"Camera": "Kamera", "Camera": "Kamera",
"Cancel": "Abbrechen", "Cancel": "Abbrechen",
"Capabilities": "Fähigkeiten", "Capabilities": "Fähigkeiten",
"Certificate Path": "", "Certificate Path": "Zertifikatpfad",
"Change Password": "Passwort ändern", "Change Password": "Passwort ändern",
"Character": "Zeichen", "Character": "Zeichen",
"Chart new frontiers": "", "Character limit for autocomplete generation input": "",
"Chart new frontiers": "Neue Wege beschreiten",
"Chat": "Gespräch", "Chat": "Gespräch",
"Chat Background Image": "Hintergrundbild des Unterhaltungsfensters", "Chat Background Image": "Hintergrundbild des Unterhaltungsfensters",
"Chat Bubble UI": "Chat Bubble UI", "Chat Bubble UI": "Chat Bubble UI",
"Chat Controls": "Chat-Steuerung", "Chat Controls": "Chat-Steuerung",
"Chat direction": "Textrichtung", "Chat direction": "Textrichtung",
"Chat Overview": "Unterhaltungsübersicht", "Chat Overview": "Unterhaltungsübersicht",
"Chat Permissions": "", "Chat Permissions": "Unterhaltungsberechtigungen",
"Chat Tags Auto-Generation": "Automatische Generierung von Unterhaltungstags", "Chat Tags Auto-Generation": "Automatische Generierung von Unterhaltungstags",
"Chats": "Unterhaltungen", "Chats": "Unterhaltungen",
"Check Again": "Erneut überprüfen", "Check Again": "Erneut überprüfen",
@@ -134,11 +140,11 @@
"Chunk Overlap": "Blocküberlappung", "Chunk Overlap": "Blocküberlappung",
"Chunk Params": "Blockparameter", "Chunk Params": "Blockparameter",
"Chunk Size": "Blockgröße", "Chunk Size": "Blockgröße",
"Ciphers": "", "Ciphers": "Verschlüsselungen",
"Citation": "Zitate", "Citation": "Zitate",
"Clear memory": "Alle Erinnerungen entfernen", "Clear memory": "Alle Erinnerungen entfernen",
"click here": "", "click here": "hier klicken",
"Click here for filter guides.": "", "Click here for filter guides.": "Klicken Sie hier für Filteranleitungen.",
"Click here for help.": "Klicken Sie hier für Hilfe.", "Click here for help.": "Klicken Sie hier für Hilfe.",
"Click here to": "Klicken Sie hier, um", "Click here to": "Klicken Sie hier, um",
"Click here to download user import template file.": "Klicken Sie hier, um die Vorlage für den Benutzerimport herunterzuladen.", "Click here to download user import template file.": "Klicken Sie hier, um die Vorlage für den Benutzerimport herunterzuladen.",
@@ -155,7 +161,7 @@
"Code execution": "Codeausführung", "Code execution": "Codeausführung",
"Code formatted successfully": "Code erfolgreich formatiert", "Code formatted successfully": "Code erfolgreich formatiert",
"Collection": "Kollektion", "Collection": "Kollektion",
"Color": "", "Color": "Farbe",
"ComfyUI": "ComfyUI", "ComfyUI": "ComfyUI",
"ComfyUI Base URL": "ComfyUI-Basis-URL", "ComfyUI Base URL": "ComfyUI-Basis-URL",
"ComfyUI Base URL is required.": "ComfyUI-Basis-URL wird benötigt.", "ComfyUI Base URL is required.": "ComfyUI-Basis-URL wird benötigt.",
@@ -164,7 +170,8 @@
"Command": "Befehl", "Command": "Befehl",
"Completions": "Vervollständigungen", "Completions": "Vervollständigungen",
"Concurrent Requests": "Anzahl gleichzeitiger Anfragen", "Concurrent Requests": "Anzahl gleichzeitiger Anfragen",
"Configure": "", "Configure": "Konfigurieren",
"Configure Models": "",
"Confirm": "Bestätigen", "Confirm": "Bestätigen",
"Confirm Password": "Passwort bestätigen", "Confirm Password": "Passwort bestätigen",
"Confirm your action": "Bestätigen Sie Ihre Aktion.", "Confirm your action": "Bestätigen Sie Ihre Aktion.",
@@ -175,11 +182,11 @@
"Context Length": "Kontextlänge", "Context Length": "Kontextlänge",
"Continue Response": "Antwort fortsetzen", "Continue Response": "Antwort fortsetzen",
"Continue with {{provider}}": "Mit {{provider}} fortfahren", "Continue with {{provider}}": "Mit {{provider}} fortfahren",
"Continue with Email": "", "Continue with Email": "Mit Email fortfahren",
"Continue with LDAP": "", "Continue with LDAP": "Mit LDAP fortfahren",
"Control how message text is split for TTS requests. 'Punctuation' splits into sentences, 'paragraphs' splits into paragraphs, and 'none' keeps the message as a single string.": "Kontrollieren Sie, wie Nachrichtentext für TTS-Anfragen aufgeteilt wird. 'Punctuation' teilt in Sätze auf, 'paragraphs' teilt in Absätze auf und 'none' behält die Nachricht als einzelnen String.", "Control how message text is split for TTS requests. 'Punctuation' splits into sentences, 'paragraphs' splits into paragraphs, and 'none' keeps the message as a single string.": "Kontrollieren Sie, wie Nachrichtentext für TTS-Anfragen aufgeteilt wird. 'Punctuation' teilt in Sätze auf, 'paragraphs' teilt in Absätze auf und 'none' behält die Nachricht als einzelnen String.",
"Controls": "Steuerung", "Controls": "Steuerung",
"Controls the balance between coherence and diversity of the output. A lower value will result in more focused and coherent text. (Default: 5.0)": "", "Controls the balance between coherence and diversity of the output. A lower value will result in more focused and coherent text. (Default: 5.0)": "Kontrolliert das Gleichgewicht zwischen Kohärenz und Vielfalt des Ausgabetextes. Ein niedrigerer Wert führt zu fokussierterem und kohärenterem Text. (Standard: 5.0)",
"Copied": "Kopiert", "Copied": "Kopiert",
"Copied shared chat URL to clipboard!": "Freigabelink in die Zwischenablage kopiert!", "Copied shared chat URL to clipboard!": "Freigabelink in die Zwischenablage kopiert!",
"Copied to clipboard": "In die Zwischenablage kopiert", "Copied to clipboard": "In die Zwischenablage kopiert",
@@ -189,12 +196,12 @@
"Copy Link": "Link kopieren", "Copy Link": "Link kopieren",
"Copy to clipboard": "In die Zwischenablage kopieren", "Copy to clipboard": "In die Zwischenablage kopieren",
"Copying to clipboard was successful!": "Das Kopieren in die Zwischenablage war erfolgreich!", "Copying to clipboard was successful!": "Das Kopieren in die Zwischenablage war erfolgreich!",
"Create": "", "Create": "Erstellen",
"Create a knowledge base": "", "Create a knowledge base": "Wissensspeicher erstellen",
"Create a model": "Ein Modell erstellen", "Create a model": "Modell erstellen",
"Create Account": "Konto erstellen", "Create Account": "Konto erstellen",
"Create Admin Account": "", "Create Admin Account": "Administrator-Account erstellen",
"Create Group": "", "Create Group": "Gruppe erstellen",
"Create Knowledge": "Wissen erstellen", "Create Knowledge": "Wissen erstellen",
"Create new key": "Neuen Schlüssel erstellen", "Create new key": "Neuen Schlüssel erstellen",
"Create new secret key": "Neuen API-Schlüssel erstellen", "Create new secret key": "Neuen API-Schlüssel erstellen",
@@ -213,16 +220,17 @@
"Default (SentenceTransformers)": "Standard (SentenceTransformers)", "Default (SentenceTransformers)": "Standard (SentenceTransformers)",
"Default Model": "Standardmodell", "Default Model": "Standardmodell",
"Default model updated": "Standardmodell aktualisiert", "Default model updated": "Standardmodell aktualisiert",
"Default permissions": "", "Default Models": "",
"Default permissions updated successfully": "", "Default permissions": "Standardberechtigungen",
"Default permissions updated successfully": "Standardberechtigungen erfolgreich aktualisiert",
"Default Prompt Suggestions": "Prompt-Vorschläge", "Default Prompt Suggestions": "Prompt-Vorschläge",
"Default to 389 or 636 if TLS is enabled": "", "Default to 389 or 636 if TLS is enabled": "Standardmäßig auf 389 oder 636 setzen, wenn TLS aktiviert ist",
"Default to ALL": "", "Default to ALL": "Standardmäßig auf ALLE setzen",
"Default User Role": "Standardbenutzerrolle", "Default User Role": "Standardbenutzerrolle",
"Delete": "Löschen", "Delete": "Löschen",
"Delete a model": "Ein Modell löschen", "Delete a model": "Ein Modell löschen",
"Delete All Chats": "Alle Unterhaltungen löschen", "Delete All Chats": "Alle Unterhaltungen löschen",
"Delete All Models": "", "Delete All Models": "Alle Modelle löschen",
"Delete chat": "Unterhaltung löschen", "Delete chat": "Unterhaltung löschen",
"Delete Chat": "Unterhaltung löschen", "Delete Chat": "Unterhaltung löschen",
"Delete chat?": "Unterhaltung löschen?", "Delete chat?": "Unterhaltung löschen?",
@@ -234,7 +242,8 @@
"Delete User": "Benutzer löschen", "Delete User": "Benutzer löschen",
"Deleted {{deleteModelTag}}": "{{deleteModelTag}} gelöscht", "Deleted {{deleteModelTag}}": "{{deleteModelTag}} gelöscht",
"Deleted {{name}}": "{{name}} gelöscht", "Deleted {{name}}": "{{name}} gelöscht",
"Describe your knowledge base and objectives": "", "Deleted User": "Benutzer gelöscht",
"Describe your knowledge base and objectives": "Beschreibe deinen Wissensspeicher und deine Ziele",
"Description": "Beschreibung", "Description": "Beschreibung",
"Didn't fully follow instructions": "Nicht genau den Answeisungen gefolgt", "Didn't fully follow instructions": "Nicht genau den Answeisungen gefolgt",
"Disabled": "Deaktiviert", "Disabled": "Deaktiviert",
@@ -242,17 +251,17 @@
"Discover a model": "Entdecken Sie weitere Modelle", "Discover a model": "Entdecken Sie weitere Modelle",
"Discover a prompt": "Entdecken Sie weitere Prompts", "Discover a prompt": "Entdecken Sie weitere Prompts",
"Discover a tool": "Entdecken Sie weitere Werkzeuge", "Discover a tool": "Entdecken Sie weitere Werkzeuge",
"Discover wonders": "", "Discover wonders": "Entdecken Sie Wunder",
"Discover, download, and explore custom functions": "Entdecken und beziehen Sie benutzerdefinierte Funktionen", "Discover, download, and explore custom functions": "Entdecken und beziehen Sie benutzerdefinierte Funktionen",
"Discover, download, and explore custom prompts": "Entdecken und beziehen Sie benutzerdefinierte Prompts", "Discover, download, and explore custom prompts": "Entdecken und beziehen Sie benutzerdefinierte Prompts",
"Discover, download, and explore custom tools": "Entdecken und beziehen Sie benutzerdefinierte Werkzeuge", "Discover, download, and explore custom tools": "Entdecken und beziehen Sie benutzerdefinierte Werkzeuge",
"Discover, download, and explore model presets": "Entdecken und beziehen Sie benutzerdefinierte Modellvorlagen", "Discover, download, and explore model presets": "Entdecken und beziehen Sie benutzerdefinierte Modellvorlagen",
"Dismissible": "ausblendbar", "Dismissible": "ausblendbar",
"Display": "", "Display": "Anzeigen",
"Display Emoji in Call": "Emojis im Anruf anzeigen", "Display Emoji in Call": "Emojis im Anruf anzeigen",
"Display the username instead of You in the Chat": "Soll \"Sie\" durch Ihren Benutzernamen ersetzt werden?", "Display the username instead of You in the Chat": "Soll \"Sie\" durch Ihren Benutzernamen ersetzt werden?",
"Displays citations in the response": "", "Displays citations in the response": "Zeigt Zitate in der Antwort an",
"Dive into knowledge": "", "Dive into knowledge": "Tauchen Sie in das Wissen ein",
"Do not install functions from sources you do not fully trust.": "Installieren Sie keine Funktionen aus Quellen, denen Sie nicht vollständig vertrauen.", "Do not install functions from sources you do not fully trust.": "Installieren Sie keine Funktionen aus Quellen, denen Sie nicht vollständig vertrauen.",
"Do not install tools from sources you do not fully trust.": "Installieren Sie keine Werkzeuge aus Quellen, denen Sie nicht vollständig vertrauen.", "Do not install tools from sources you do not fully trust.": "Installieren Sie keine Werkzeuge aus Quellen, denen Sie nicht vollständig vertrauen.",
"Document": "Dokument", "Document": "Dokument",
@@ -267,53 +276,51 @@
"Download": "Exportieren", "Download": "Exportieren",
"Download canceled": "Exportierung abgebrochen", "Download canceled": "Exportierung abgebrochen",
"Download Database": "Datenbank exportieren", "Download Database": "Datenbank exportieren",
"Drag and drop a file to upload or select a file to view": "", "Drag and drop a file to upload or select a file to view": "Ziehen Sie eine Datei zum Hochladen oder wählen Sie eine Datei zum Anzeigen aus",
"Draw": "Zeichnen", "Draw": "Zeichnen",
"Drop any files here to add to the conversation": "Ziehen Sie beliebige Dateien hierher, um sie der Unterhaltung hinzuzufügen", "Drop any files here to add to the conversation": "Ziehen Sie beliebige Dateien hierher, um sie der Unterhaltung hinzuzufügen",
"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "z. B. '30s','10m'. Gültige Zeiteinheiten sind 's', 'm', 'h'.", "e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "z. B. '30s','10m'. Gültige Zeiteinheiten sind 's', 'm', 'h'.",
"e.g. A filter to remove profanity from text": "", "e.g. A filter to remove profanity from text": "z. B. Ein Filter, um Schimpfwörter aus Text zu entfernen",
"e.g. My Filter": "", "e.g. My Filter": "z. B. Mein Filter",
"e.g. My Tools": "", "e.g. My Tools": "z. B. Meine Werkzeuge",
"e.g. my_filter": "", "e.g. my_filter": "z. B. mein_filter",
"e.g. my_tools": "", "e.g. my_tools": "z. B. meine_werkzeuge",
"e.g. Tools for performing various operations": "", "e.g. Tools for performing various operations": "z. B. Werkzeuge für verschiedene Operationen",
"Edit": "Bearbeiten", "Edit": "Bearbeiten",
"Edit Arena Model": "Arena-Modell bearbeiten", "Edit Arena Model": "Arena-Modell bearbeiten",
"Edit Connection": "", "Edit Connection": "Verbindung bearbeiten",
"Edit Default Permissions": "", "Edit Default Permissions": "Standardberechtigungen bearbeiten",
"Edit Memory": "Erinnerungen bearbeiten", "Edit Memory": "Erinnerungen bearbeiten",
"Edit User": "Benutzer bearbeiten", "Edit User": "Benutzer bearbeiten",
"Edit User Group": "", "Edit User Group": "Benutzergruppe bearbeiten",
"ElevenLabs": "ElevenLabs", "ElevenLabs": "ElevenLabs",
"Email": "E-Mail", "Email": "E-Mail",
"Embark on adventures": "", "Embark on adventures": "Abenteuer erleben",
"Embedding Batch Size": "Embedding-Stapelgröße", "Embedding Batch Size": "Embedding-Stapelgröße",
"Embedding Model": "Embedding-Modell", "Embedding Model": "Embedding-Modell",
"Embedding Model Engine": "Embedding-Modell-Engine", "Embedding Model Engine": "Embedding-Modell-Engine",
"Embedding model set to \"{{embedding_model}}\"": "Embedding-Modell auf \"{{embedding_model}}\" gesetzt", "Embedding model set to \"{{embedding_model}}\"": "Embedding-Modell auf \"{{embedding_model}}\" gesetzt",
"Enable API Key Auth": "", "Enable API Key Auth": "API-Schlüssel-Authentifizierung aktivieren",
"Enable autocomplete generation for chat messages": "",
"Enable Community Sharing": "Community-Freigabe aktivieren", "Enable Community Sharing": "Community-Freigabe aktivieren",
"Enable Memory Locking (mlock) to prevent model data from being swapped out of RAM. This option locks the model's working set of pages into RAM, ensuring that they will not be swapped out to disk. This can help maintain performance by avoiding page faults and ensuring fast data access.": "", "Enable Memory Locking (mlock) to prevent model data from being swapped out of RAM. This option locks the model's working set of pages into RAM, ensuring that they will not be swapped out to disk. This can help maintain performance by avoiding page faults and ensuring fast data access.": "Aktiviere Memory Locking (mlock), um zu verhindern, dass Modelldaten aus dem RAM ausgelagert werden. Diese Option sperrt die Arbeitsseiten des Modells im RAM, um sicherzustellen, dass sie nicht auf die Festplatte ausgelagert werden. Dies kann die Leistung verbessern, indem Page Faults vermieden und ein schneller Datenzugriff sichergestellt werden.",
"Enable Memory Mapping (mmap) to load model data. This option allows the system to use disk storage as an extension of RAM by treating disk files as if they were in RAM. This can improve model performance by allowing for faster data access. However, it may not work correctly with all systems and can consume a significant amount of disk space.": "", "Enable Memory Mapping (mmap) to load model data. This option allows the system to use disk storage as an extension of RAM by treating disk files as if they were in RAM. This can improve model performance by allowing for faster data access. However, it may not work correctly with all systems and can consume a significant amount of disk space.": "Aktiviere Memory Mapping (mmap), um Modelldaten zu laden. Diese Option ermöglicht es dem System, den Festplattenspeicher als Erweiterung des RAM zu verwenden, indem Festplattendateien so behandelt werden, als ob sie im RAM wären. Dies kann die Modellleistung verbessern, indem ein schnellerer Datenzugriff ermöglicht wird. Es kann jedoch nicht auf allen Systemen korrekt funktionieren und einen erheblichen Teil des Festplattenspeichers beanspruchen.",
"Enable Message Rating": "Nachrichtenbewertung aktivieren", "Enable Message Rating": "Nachrichtenbewertung aktivieren",
"Enable Mirostat sampling for controlling perplexity. (Default: 0, 0 = Disabled, 1 = Mirostat, 2 = Mirostat 2.0)": "", "Enable Mirostat sampling for controlling perplexity. (Default: 0, 0 = Disabled, 1 = Mirostat, 2 = Mirostat 2.0)": "Mirostat Sampling zur Steuerung der Perplexität aktivieren. (Standard: 0, 0 = Deaktiviert, 1 = Mirostat, 2 = Mirostat 2.0)",
"Enable New Sign Ups": "Registrierung erlauben", "Enable New Sign Ups": "Registrierung erlauben",
"Enable Retrieval Query Generation": "",
"Enable Tags Generation": "",
"Enable Web Search": "Websuche aktivieren", "Enable Web Search": "Websuche aktivieren",
"Enable Web Search Query Generation": "Websuchanfragen-Generierung aktivieren",
"Enabled": "Aktiviert", "Enabled": "Aktiviert",
"Engine": "Engine", "Engine": "Engine",
"Ensure your CSV file includes 4 columns in this order: Name, Email, Password, Role.": "Stellen Sie sicher, dass Ihre CSV-Datei 4 Spalten in dieser Reihenfolge enthält: Name, E-Mail, Passwort, Rolle.", "Ensure your CSV file includes 4 columns in this order: Name, Email, Password, Role.": "Stellen Sie sicher, dass Ihre CSV-Datei 4 Spalten in dieser Reihenfolge enthält: Name, E-Mail, Passwort, Rolle.",
"Enter {{role}} message here": "Geben Sie die {{role}}-Nachricht hier ein", "Enter {{role}} message here": "Geben Sie die {{role}}-Nachricht hier ein",
"Enter a detail about yourself for your LLMs to recall": "Geben Sie ein Detail über sich selbst ein, das Ihre Sprachmodelle (LLMs) sich merken sollen", "Enter a detail about yourself for your LLMs to recall": "Geben Sie ein Detail über sich selbst ein, das Ihre Sprachmodelle (LLMs) sich merken sollen",
"Enter api auth string (e.g. username:password)": "Geben Sie die API-Authentifizierungszeichenfolge ein (z. B. Benutzername:Passwort)", "Enter api auth string (e.g. username:password)": "Geben Sie die API-Authentifizierungszeichenfolge ein (z. B. Benutzername:Passwort)",
"Enter Application DN": "", "Enter Application DN": "Geben Sie die Anwendungs-DN ein",
"Enter Application DN Password": "", "Enter Application DN Password": "Geben Sie das Anwendungs-DN-Passwort ein",
"Enter Bing Search V7 Endpoint": "", "Enter Bing Search V7 Endpoint": "Geben Sie den Bing Search V7-Endpunkt ein",
"Enter Bing Search V7 Subscription Key": "", "Enter Bing Search V7 Subscription Key": "Geben Sie den Bing Search V7-Abonnement-Schlüssel ein",
"Enter Brave Search API Key": "Geben Sie den Brave Search API-Schlüssel ein", "Enter Brave Search API Key": "Geben Sie den Brave Search API-Schlüssel ein",
"Enter certificate path": "", "Enter certificate path": "Geben Sie den Zertifikatpfad ein",
"Enter CFG Scale (e.g. 7.0)": "Geben Sie die CFG-Skala ein (z. B. 7.0)", "Enter CFG Scale (e.g. 7.0)": "Geben Sie die CFG-Skala ein (z. B. 7.0)",
"Enter Chunk Overlap": "Geben Sie die Blocküberlappung ein", "Enter Chunk Overlap": "Geben Sie die Blocküberlappung ein",
"Enter Chunk Size": "Geben Sie die Blockgröße ein", "Enter Chunk Size": "Geben Sie die Blockgröße ein",
@@ -322,24 +329,26 @@
"Enter Google PSE API Key": "Geben Sie den Google PSE-API-Schlüssel ein", "Enter Google PSE API Key": "Geben Sie den Google PSE-API-Schlüssel ein",
"Enter Google PSE Engine Id": "Geben Sie die Google PSE-Engine-ID ein", "Enter Google PSE Engine Id": "Geben Sie die Google PSE-Engine-ID ein",
"Enter Image Size (e.g. 512x512)": "Geben Sie die Bildgröße ein (z. B. 512x512)", "Enter Image Size (e.g. 512x512)": "Geben Sie die Bildgröße ein (z. B. 512x512)",
"Enter Jina API Key": "", "Enter Jina API Key": "Geben Sie den Jina-API-Schlüssel ein",
"Enter language codes": "Geben Sie die Sprachcodes ein", "Enter language codes": "Geben Sie die Sprachcodes ein",
"Enter Model ID": "Geben Sie die Modell-ID ein", "Enter Model ID": "Geben Sie die Modell-ID ein",
"Enter model tag (e.g. {{modelTag}})": "Gebn Sie den Model-Tag ein", "Enter model tag (e.g. {{modelTag}})": "Geben Sie den Model-Tag ein",
"Enter Mojeek Search API Key": "Geben Sie den Mojeek Search API-Schlüssel ein",
"Enter Number of Steps (e.g. 50)": "Geben Sie die Anzahl an Schritten ein (z. B. 50)", "Enter Number of Steps (e.g. 50)": "Geben Sie die Anzahl an Schritten ein (z. B. 50)",
"Enter proxy URL (e.g. https://user:password@host:port)": "",
"Enter Sampler (e.g. Euler a)": "Geben Sie den Sampler ein (z. B. Euler a)", "Enter Sampler (e.g. Euler a)": "Geben Sie den Sampler ein (z. B. Euler a)",
"Enter Scheduler (e.g. Karras)": "Geben Sie den Scheduler ein (z. B. Karras)", "Enter Scheduler (e.g. Karras)": "Geben Sie den Scheduler ein (z. B. Karras)",
"Enter Score": "Punktzahl eingeben", "Enter Score": "Punktzahl eingeben",
"Enter SearchApi API Key": "Geben Sie den SearchApi-API-Schlüssel ein", "Enter SearchApi API Key": "Geben Sie den SearchApi-API-Schlüssel ein",
"Enter SearchApi Engine": "Geben Sie die SearchApi-Engine ein", "Enter SearchApi Engine": "Geben Sie die SearchApi-Engine ein",
"Enter Searxng Query URL": "Geben Sie die Searxng-Abfrage-URL ein", "Enter Searxng Query URL": "Geben Sie die Searxng-Abfrage-URL ein",
"Enter Seed": "", "Enter Seed": "Geben Sie den Seed ein",
"Enter Serper API Key": "Geben Sie den Serper-API-Schlüssel ein", "Enter Serper API Key": "Geben Sie den Serper-API-Schlüssel ein",
"Enter Serply API Key": "Geben Sie den", "Enter Serply API Key": "Geben Sie den",
"Enter Serpstack API Key": "Geben Sie den Serpstack-API-Schlüssel ein", "Enter Serpstack API Key": "Geben Sie den Serpstack-API-Schlüssel ein",
"Enter server host": "", "Enter server host": "Geben Sie den Server-Host ein",
"Enter server label": "", "Enter server label": "Geben Sie das Server-Label ein",
"Enter server port": "", "Enter server port": "Geben Sie den Server-Port ein",
"Enter stop sequence": "Stop-Sequenz eingeben", "Enter stop sequence": "Stop-Sequenz eingeben",
"Enter system prompt": "Systemprompt eingeben", "Enter system prompt": "Systemprompt eingeben",
"Enter Tavily API Key": "Geben Sie den Tavily-API-Schlüssel ein", "Enter Tavily API Key": "Geben Sie den Tavily-API-Schlüssel ein",
@@ -352,38 +361,39 @@
"Enter your message": "Geben Sie Ihre Nachricht ein", "Enter your message": "Geben Sie Ihre Nachricht ein",
"Enter Your Password": "Geben Sie Ihr Passwort ein", "Enter Your Password": "Geben Sie Ihr Passwort ein",
"Enter Your Role": "Geben Sie Ihre Rolle ein", "Enter Your Role": "Geben Sie Ihre Rolle ein",
"Enter Your Username": "", "Enter Your Username": "Geben Sie Ihren Benutzernamen ein",
"Error": "Fehler", "Error": "Fehler",
"ERROR": "FEHLER", "ERROR": "FEHLER",
"Evaluations": "Evaluationen", "Evaluations": "Evaluationen",
"Example: (&(objectClass=inetOrgPerson)(uid=%s))": "", "Example: (&(objectClass=inetOrgPerson)(uid=%s))": "Beispiel: (&(objectClass=inetOrgPerson)(uid=%s))",
"Example: ALL": "", "Example: ALL": "Beispiel: ALL",
"Example: ou=users,dc=foo,dc=example": "", "Example: ou=users,dc=foo,dc=example": "Beispiel: ou=users,dc=foo,dc=example",
"Example: sAMAccountName or uid or userPrincipalName": "", "Example: sAMAccountName or uid or userPrincipalName": "Beispiel: sAMAccountName or uid or userPrincipalName",
"Exclude": "Ausschließen", "Exclude": "Ausschließen",
"Experimental": "Experimentell", "Experimental": "Experimentell",
"Explore the cosmos": "", "Explore the cosmos": "Erforschen Sie das Universum",
"Export": "Exportieren", "Export": "Exportieren",
"Export All Archived Chats": "", "Export All Archived Chats": "Alle archivierten Unterhaltungen exportieren",
"Export All Chats (All Users)": "Alle Unterhaltungen exportieren (alle Benutzer)", "Export All Chats (All Users)": "Alle Unterhaltungen exportieren (alle Benutzer)",
"Export chat (.json)": "Unterhaltung exportieren (.json)", "Export chat (.json)": "Unterhaltung exportieren (.json)",
"Export Chats": "Unterhaltungen exportieren", "Export Chats": "Unterhaltungen exportieren",
"Export Config to JSON File": "Exportiere Konfiguration als JSON-Datei", "Export Config to JSON File": "Exportiere Konfiguration als JSON-Datei",
"Export Functions": "Funktionen exportieren", "Export Functions": "Funktionen exportieren",
"Export Models": "Modelle exportieren", "Export Models": "Modelle exportieren",
"Export Presets": "", "Export Presets": "Voreinstellungen exportieren",
"Export Prompts": "Prompts exportieren", "Export Prompts": "Prompts exportieren",
"Export to CSV": "", "Export to CSV": "Als CSV exportieren",
"Export Tools": "Werkzeuge exportieren", "Export Tools": "Werkzeuge exportieren",
"External Models": "Externe Modelle", "External Models": "Externe Modelle",
"Failed to add file.": "Fehler beim Hinzufügen der Datei.", "Failed to add file.": "Fehler beim Hinzufügen der Datei.",
"Failed to create API Key.": "Fehler beim Erstellen des API-Schlüssels.", "Failed to create API Key.": "Fehler beim Erstellen des API-Schlüssels.",
"Failed to read clipboard contents": "Fehler beim Abruf der Zwischenablage", "Failed to read clipboard contents": "Fehler beim Abruf der Zwischenablage",
"Failed to save models configuration": "",
"Failed to update settings": "Fehler beim Aktualisieren der Einstellungen", "Failed to update settings": "Fehler beim Aktualisieren der Einstellungen",
"Failed to upload file.": "Fehler beim Hochladen der Datei.", "Failed to upload file.": "Fehler beim Hochladen der Datei.",
"February": "Februar", "February": "Februar",
"Feedback History": "Feedback-Verlauf", "Feedback History": "Feedback-Verlauf",
"Feedbacks": "", "Feedbacks": "Feedbacks",
"Feel free to add specific details": "Fühlen Sie sich frei, spezifische Details hinzuzufügen", "Feel free to add specific details": "Fühlen Sie sich frei, spezifische Details hinzuzufügen",
"File": "Datei", "File": "Datei",
"File added successfully.": "Datei erfolgreich hinzugefügt.", "File added successfully.": "Datei erfolgreich hinzugefügt.",
@@ -404,18 +414,18 @@
"Folder name cannot be empty.": "Ordnername darf nicht leer sein.", "Folder name cannot be empty.": "Ordnername darf nicht leer sein.",
"Folder name updated successfully": "Ordnername erfolgreich aktualisiert", "Folder name updated successfully": "Ordnername erfolgreich aktualisiert",
"Followed instructions perfectly": "Anweisungen perfekt befolgt", "Followed instructions perfectly": "Anweisungen perfekt befolgt",
"Forge new paths": "", "Forge new paths": "Neue Wege beschreiten",
"Form": "Formular", "Form": "Formular",
"Format your variables using brackets like this:": "Formatieren Sie Ihre Variablen mit Klammern, wie hier:", "Format your variables using brackets like this:": "Formatieren Sie Ihre Variablen mit Klammern, wie hier:",
"Frequency Penalty": "Frequenzstrafe", "Frequency Penalty": "Frequenzstrafe",
"Function": "Funktion", "Function": "Funktion",
"Function created successfully": "Funktion erfolgreich erstellt", "Function created successfully": "Funktion erfolgreich erstellt",
"Function deleted successfully": "Funktion erfolgreich gelöscht", "Function deleted successfully": "Funktion erfolgreich gelöscht",
"Function Description": "", "Function Description": "Funktionsbeschreibung",
"Function ID": "", "Function ID": "Funktions-ID",
"Function is now globally disabled": "Die Funktion ist jetzt global deaktiviert", "Function is now globally disabled": "Die Funktion ist jetzt global deaktiviert",
"Function is now globally enabled": "Die Funktion ist jetzt global aktiviert", "Function is now globally enabled": "Die Funktion ist jetzt global aktiviert",
"Function Name": "", "Function Name": "Funktionsname",
"Function updated successfully": "Funktion erfolgreich aktualisiert", "Function updated successfully": "Funktion erfolgreich aktualisiert",
"Functions": "Funktionen", "Functions": "Funktionen",
"Functions allow arbitrary code execution": "Funktionen ermöglichen die Ausführung beliebigen Codes", "Functions allow arbitrary code execution": "Funktionen ermöglichen die Ausführung beliebigen Codes",
@@ -426,33 +436,34 @@
"Generate Image": "Bild erzeugen", "Generate Image": "Bild erzeugen",
"Generating search query": "Suchanfrage wird erstellt", "Generating search query": "Suchanfrage wird erstellt",
"Generation Info": "Generierungsinformationen", "Generation Info": "Generierungsinformationen",
"Get started": "", "Get started": "Loslegen",
"Get started with {{WEBUI_NAME}}": "", "Get started with {{WEBUI_NAME}}": "Loslegen mit {{WEBUI_NAME}}",
"Global": "Global", "Global": "Global",
"Good Response": "Gute Antwort", "Good Response": "Gute Antwort",
"Google PSE API Key": "Google PSE-API-Schlüssel", "Google PSE API Key": "Google PSE-API-Schlüssel",
"Google PSE Engine Id": "Google PSE-Engine-ID", "Google PSE Engine Id": "Google PSE-Engine-ID",
"Group created successfully": "", "Group created successfully": "Gruppe erfolgreich erstellt",
"Group deleted successfully": "", "Group deleted successfully": "Gruppe erfolgreich gelöscht",
"Group Description": "", "Group Description": "Gruppenbeschreibung",
"Group Name": "", "Group Name": "Gruppenname",
"Group updated successfully": "", "Group updated successfully": "Gruppe erfolgreich aktualisiert",
"Groups": "", "Groups": "Gruppen",
"h:mm a": "h:mm a", "h:mm a": "h:mm a",
"Haptic Feedback": "Haptisches Feedback", "Haptic Feedback": "Haptisches Feedback",
"has no conversations.": "hat keine Unterhaltungen.", "has no conversations.": "hat keine Unterhaltungen.",
"Hello, {{name}}": "Hallo, {{name}}", "Hello, {{name}}": "Hallo, {{name}}",
"Help": "Hilfe", "Help": "Hilfe",
"Help us create the best community leaderboard by sharing your feedback history!": "Helfen Sie uns, die beste Community-Bestenliste zu erstellen, indem Sie Ihren Feedback-Verlauf teilen!", "Help us create the best community leaderboard by sharing your feedback history!": "Helfen Sie uns, die beste Community-Bestenliste zu erstellen, indem Sie Ihren Feedback-Verlauf teilen!",
"Hex Color": "", "Hex Color": "Hex-Farbe",
"Hex Color - Leave empty for default color": "", "Hex Color - Leave empty for default color": "Hex-Farbe - Leer lassen für Standardfarbe",
"Hide": "Verbergen", "Hide": "Verbergen",
"Host": "", "Host": "Host",
"How can I help you today?": "Wie kann ich Ihnen heute helfen?", "How can I help you today?": "Wie kann ich Ihnen heute helfen?",
"How would you rate this response?": "Wie würden Sie diese Antwort bewerten?",
"Hybrid Search": "Hybride Suche", "Hybrid Search": "Hybride Suche",
"I acknowledge that I have read and I understand the implications of my action. I am aware of the risks associated with executing arbitrary code and I have verified the trustworthiness of the source.": "Ich bestätige, dass ich gelesen habe und die Auswirkungen meiner Aktion verstehe. Mir sind die Risiken bewusst, die mit der Ausführung beliebigen Codes verbunden sind, und ich habe die Vertrauenswürdigkeit der Quelle überprüft.", "I acknowledge that I have read and I understand the implications of my action. I am aware of the risks associated with executing arbitrary code and I have verified the trustworthiness of the source.": "Ich bestätige, dass ich gelesen habe und die Auswirkungen meiner Aktion verstehe. Mir sind die Risiken bewusst, die mit der Ausführung beliebigen Codes verbunden sind, und ich habe die Vertrauenswürdigkeit der Quelle überprüft.",
"ID": "ID", "ID": "ID",
"Ignite curiosity": "", "Ignite curiosity": "Neugier entfachen",
"Image Generation (Experimental)": "Bildgenerierung (experimentell)", "Image Generation (Experimental)": "Bildgenerierung (experimentell)",
"Image Generation Engine": "Bildgenerierungs-Engine", "Image Generation Engine": "Bildgenerierungs-Engine",
"Image Settings": "Bildeinstellungen", "Image Settings": "Bildeinstellungen",
@@ -461,13 +472,13 @@
"Import Config from JSON File": "Konfiguration aus JSON-Datei importieren", "Import Config from JSON File": "Konfiguration aus JSON-Datei importieren",
"Import Functions": "Funktionen importieren", "Import Functions": "Funktionen importieren",
"Import Models": "Modelle importieren", "Import Models": "Modelle importieren",
"Import Presets": "", "Import Presets": "Voreinstellungen importieren",
"Import Prompts": "Prompts importieren", "Import Prompts": "Prompts importieren",
"Import Tools": "Werkzeuge importieren", "Import Tools": "Werkzeuge importieren",
"Include": "Einschließen", "Include": "Einschließen",
"Include `--api-auth` flag when running stable-diffusion-webui": "Fügen Sie beim Ausführen von stable-diffusion-webui die Option `--api-auth` hinzu", "Include `--api-auth` flag when running stable-diffusion-webui": "Fügen Sie beim Ausführen von stable-diffusion-webui die Option `--api-auth` hinzu",
"Include `--api` flag when running stable-diffusion-webui": "Fügen Sie beim Ausführen von stable-diffusion-webui die Option `--api` hinzu", "Include `--api` flag when running stable-diffusion-webui": "Fügen Sie beim Ausführen von stable-diffusion-webui die Option `--api` hinzu",
"Influences how quickly the algorithm responds to feedback from the generated text. A lower learning rate will result in slower adjustments, while a higher learning rate will make the algorithm more responsive. (Default: 0.1)": "", "Influences how quickly the algorithm responds to feedback from the generated text. A lower learning rate will result in slower adjustments, while a higher learning rate will make the algorithm more responsive. (Default: 0.1)": "Beeinflusst, wie schnell der Algorithmus auf Feedback aus dem generierten Text reagiert. Eine niedrigere Lernrate führt zu langsameren Anpassungen, während eine höhere Lernrate den Algorithmus reaktionsschneller macht. (Standard: 0.1)",
"Info": "Info", "Info": "Info",
"Input commands": "Eingabebefehle", "Input commands": "Eingabebefehle",
"Install from Github URL": "Installiere von der Github-URL", "Install from Github URL": "Installiere von der Github-URL",
@@ -476,7 +487,7 @@
"Invalid file format.": "Ungültiges Dateiformat.", "Invalid file format.": "Ungültiges Dateiformat.",
"Invalid Tag": "Ungültiger Tag", "Invalid Tag": "Ungültiger Tag",
"January": "Januar", "January": "Januar",
"Jina API Key": "", "Jina API Key": "Jina-API-Schlüssel",
"join our Discord for help.": "Treten Sie unserem Discord bei, um Hilfe zu erhalten.", "join our Discord for help.": "Treten Sie unserem Discord bei, um Hilfe zu erhalten.",
"JSON": "JSON", "JSON": "JSON",
"JSON Preview": "JSON-Vorschau", "JSON Preview": "JSON-Vorschau",
@@ -485,31 +496,31 @@
"JWT Expiration": "JWT-Ablauf", "JWT Expiration": "JWT-Ablauf",
"JWT Token": "JWT-Token", "JWT Token": "JWT-Token",
"Keep Alive": "Verbindung aufrechterhalten", "Keep Alive": "Verbindung aufrechterhalten",
"Key": "", "Key": "Schlüssel",
"Keyboard shortcuts": "Tastenkombinationen", "Keyboard shortcuts": "Tastenkombinationen",
"Knowledge": "Wissen", "Knowledge": "Wissen",
"Knowledge Access": "", "Knowledge Access": "Wissenszugriff",
"Knowledge created successfully.": "Wissen erfolgreich erstellt.", "Knowledge created successfully.": "Wissen erfolgreich erstellt.",
"Knowledge deleted successfully.": "Wissen erfolgreich gelöscht.", "Knowledge deleted successfully.": "Wissen erfolgreich gelöscht.",
"Knowledge reset successfully.": "Wissen erfolgreich zurückgesetzt.", "Knowledge reset successfully.": "Wissen erfolgreich zurückgesetzt.",
"Knowledge updated successfully": "Wissen erfolgreich aktualisiert", "Knowledge updated successfully": "Wissen erfolgreich aktualisiert",
"Label": "", "Label": "Label",
"Landing Page Mode": "Startseitenmodus", "Landing Page Mode": "Startseitenmodus",
"Language": "Sprache", "Language": "Sprache",
"Last Active": "Zuletzt aktiv", "Last Active": "Zuletzt aktiv",
"Last Modified": "Zuletzt bearbeitet", "Last Modified": "Zuletzt bearbeitet",
"LDAP": "", "LDAP": "LDAP",
"LDAP server updated": "", "LDAP server updated": "LDAP-Server aktualisiert",
"Leaderboard": "Bestenliste", "Leaderboard": "Bestenliste",
"Leave empty for unlimited": "Leer lassen für unbegrenzt", "Leave empty for unlimited": "Leer lassen für unbegrenzt",
"Leave empty to include all models from \"{{URL}}/api/tags\" endpoint": "", "Leave empty to include all models from \"{{URL}}/api/tags\" endpoint": "Leer lassen, um alle Modelle vom \"{{URL}}/api/tags\"-Endpunkt einzuschließen",
"Leave empty to include all models from \"{{URL}}/models\" endpoint": "", "Leave empty to include all models from \"{{URL}}/models\" endpoint": "Leer lassen, um alle Modelle vom \"{{URL}}/models\"-Endpunkt einzuschließen",
"Leave empty to include all models or select specific models": "Leer lassen, um alle Modelle einzuschließen oder spezifische Modelle auszuwählen", "Leave empty to include all models or select specific models": "Leer lassen, um alle Modelle einzuschließen oder spezifische Modelle auszuwählen",
"Leave empty to use the default prompt, or enter a custom prompt": "Leer lassen, um den Standardprompt zu verwenden, oder geben Sie einen benutzerdefinierten Prompt ein", "Leave empty to use the default prompt, or enter a custom prompt": "Leer lassen, um den Standardprompt zu verwenden, oder geben Sie einen benutzerdefinierten Prompt ein",
"Light": "Hell", "Light": "Hell",
"Listening...": "Höre zu...", "Listening...": "Höre zu...",
"LLMs can make mistakes. Verify important information.": "LLMs können Fehler machen. Überprüfe wichtige Informationen.", "LLMs can make mistakes. Verify important information.": "LLMs können Fehler machen. Überprüfe wichtige Informationen.",
"Local": "", "Local": "Lokal",
"Local Models": "Lokale Modelle", "Local Models": "Lokale Modelle",
"Lost": "Verloren", "Lost": "Verloren",
"LTR": "LTR", "LTR": "LTR",
@@ -518,9 +529,9 @@
"Make sure to export a workflow.json file as API format from ComfyUI.": "Stellen Sie sicher, dass sie eine workflow.json-Datei im API-Format von ComfyUI exportieren.", "Make sure to export a workflow.json file as API format from ComfyUI.": "Stellen Sie sicher, dass sie eine workflow.json-Datei im API-Format von ComfyUI exportieren.",
"Manage": "Verwalten", "Manage": "Verwalten",
"Manage Arena Models": "Arena-Modelle verwalten", "Manage Arena Models": "Arena-Modelle verwalten",
"Manage Ollama": "", "Manage Ollama": "Ollama verwalten",
"Manage Ollama API Connections": "", "Manage Ollama API Connections": "Ollama-API-Verbindungen verwalten",
"Manage OpenAI API Connections": "", "Manage OpenAI API Connections": "OpenAI-API-Verbindungen verwalten",
"Manage Pipelines": "Pipelines verwalten", "Manage Pipelines": "Pipelines verwalten",
"March": "März", "March": "März",
"Max Tokens (num_predict)": "Maximale Tokenanzahl (num_predict)", "Max Tokens (num_predict)": "Maximale Tokenanzahl (num_predict)",
@@ -554,21 +565,23 @@
"Model accepts image inputs": "Modell akzeptiert Bileingaben", "Model accepts image inputs": "Modell akzeptiert Bileingaben",
"Model created successfully!": "Modell erfolgreich erstellt!", "Model created successfully!": "Modell erfolgreich erstellt!",
"Model filesystem path detected. Model shortname is required for update, cannot continue.": "Modell-Dateisystempfad erkannt. Modellkurzname ist für das Update erforderlich, Fortsetzung nicht möglich.", "Model filesystem path detected. Model shortname is required for update, cannot continue.": "Modell-Dateisystempfad erkannt. Modellkurzname ist für das Update erforderlich, Fortsetzung nicht möglich.",
"Model Filtering": "", "Model Filtering": "Modellfilterung",
"Model ID": "Modell-ID", "Model ID": "Modell-ID",
"Model IDs": "", "Model IDs": "Modell-IDs",
"Model Name": "Modell-Name", "Model Name": "Modell-Name",
"Model not selected": "Modell nicht ausgewählt", "Model not selected": "Modell nicht ausgewählt",
"Model Params": "Modell-Params", "Model Params": "Modell-Parameter",
"Model Permissions": "", "Model Permissions": "Modellberechtigungen",
"Model updated successfully": "Modell erfolgreich aktualisiert", "Model updated successfully": "Modell erfolgreich aktualisiert",
"Modelfile Content": "Modelfile-Inhalt", "Modelfile Content": "Modelfile-Inhalt",
"Models": "Modelle", "Models": "Modelle",
"Models Access": "", "Models Access": "Modell-Zugriff",
"Models configuration saved successfully": "",
"Mojeek Search API Key": "Mojeek Search API-Schlüssel",
"more": "mehr", "more": "mehr",
"More": "Mehr", "More": "Mehr",
"Name": "Name", "Name": "Name",
"Name your knowledge base": "", "Name your knowledge base": "Benennen Sie Ihren Wissensspeicher",
"New Chat": "Neue Unterhaltung", "New Chat": "Neue Unterhaltung",
"New folder": "Neuer Ordner", "New folder": "Neuer Ordner",
"New Password": "Neues Passwort", "New Password": "Neues Passwort",
@@ -578,15 +591,16 @@
"No feedbacks found": "Kein Feedback gefunden", "No feedbacks found": "Kein Feedback gefunden",
"No file selected": "Keine Datei ausgewählt", "No file selected": "Keine Datei ausgewählt",
"No files found.": "Keine Dateien gefunden.", "No files found.": "Keine Dateien gefunden.",
"No groups with access, add a group to grant access": "", "No groups with access, add a group to grant access": "Keine Gruppen mit Zugriff, fügen Sie eine Gruppe hinzu, um Zugriff zu gewähren",
"No HTML, CSS, or JavaScript content found.": "Keine HTML-, CSS- oder JavaScript-Inhalte gefunden.", "No HTML, CSS, or JavaScript content found.": "Keine HTML-, CSS- oder JavaScript-Inhalte gefunden.",
"No knowledge found": "Kein Wissen gefunden", "No knowledge found": "Kein Wissen gefunden",
"No model IDs": "", "No model IDs": "Keine Modell-IDs",
"No models found": "Keine Modelle gefunden", "No models found": "Keine Modelle gefunden",
"No models selected": "",
"No results found": "Keine Ergebnisse gefunden", "No results found": "Keine Ergebnisse gefunden",
"No search query generated": "Keine Suchanfrage generiert", "No search query generated": "Keine Suchanfrage generiert",
"No source available": "Keine Quelle verfügbar", "No source available": "Keine Quelle verfügbar",
"No users were found.": "", "No users were found.": "Keine Benutzer gefunden.",
"No valves to update": "Keine Valves zum Aktualisieren", "No valves to update": "Keine Valves zum Aktualisieren",
"None": "Nichts", "None": "Nichts",
"Not factually correct": "Nicht sachlich korrekt", "Not factually correct": "Nicht sachlich korrekt",
@@ -605,13 +619,13 @@
"Ollama": "Ollama", "Ollama": "Ollama",
"Ollama API": "Ollama-API", "Ollama API": "Ollama-API",
"Ollama API disabled": "Ollama-API deaktiviert", "Ollama API disabled": "Ollama-API deaktiviert",
"Ollama API settings updated": "", "Ollama API settings updated": "Ollama-API-Einstellungen aktualisiert",
"Ollama Version": "Ollama-Version", "Ollama Version": "Ollama-Version",
"On": "Ein", "On": "Ein",
"Only alphanumeric characters and hyphens are allowed": "", "Only alphanumeric characters and hyphens are allowed": "Nur alphanumerische Zeichen und Bindestriche sind erlaubt",
"Only alphanumeric characters and hyphens are allowed in the command string.": "In der Befehlszeichenfolge sind nur alphanumerische Zeichen und Bindestriche erlaubt.", "Only alphanumeric characters and hyphens are allowed in the command string.": "In der Befehlszeichenfolge sind nur alphanumerische Zeichen und Bindestriche erlaubt.",
"Only collections can be edited, create a new knowledge base to edit/add documents.": "Nur Sammlungen können bearbeitet werden. Erstellen Sie eine neue Wissensbasis, um Dokumente zu bearbeiten/hinzuzufügen.", "Only collections can be edited, create a new knowledge base to edit/add documents.": "Nur Sammlungen können bearbeitet werden. Erstellen Sie eine neue Wissensbasis, um Dokumente zu bearbeiten/hinzuzufügen.",
"Only select users and groups with permission can access": "", "Only select users and groups with permission can access": "Nur ausgewählte Benutzer und Gruppen mit Berechtigung können darauf zugreifen",
"Oops! Looks like the URL is invalid. Please double-check and try again.": "Hoppla! Es scheint, dass die URL ungültig ist. Bitte überprüfen Sie diese und versuchen Sie es erneut.", "Oops! Looks like the URL is invalid. Please double-check and try again.": "Hoppla! Es scheint, dass die URL ungültig ist. Bitte überprüfen Sie diese und versuchen Sie es erneut.",
"Oops! There are files still uploading. Please wait for the upload to complete.": "Hoppla! Es werden noch Dateien hochgeladen. Bitte warten Sie, bis der Upload abgeschlossen ist.", "Oops! There are files still uploading. Please wait for the upload to complete.": "Hoppla! Es werden noch Dateien hochgeladen. Bitte warten Sie, bis der Upload abgeschlossen ist.",
"Oops! There was an error in the previous response.": "Hoppla! Es gab einen Fehler in der vorherigen Antwort.", "Oops! There was an error in the previous response.": "Hoppla! Es gab einen Fehler in der vorherigen Antwort.",
@@ -620,34 +634,34 @@
"Open in full screen": "Im Vollbildmodus öffnen", "Open in full screen": "Im Vollbildmodus öffnen",
"Open new chat": "Neuen Chat öffnen", "Open new chat": "Neuen Chat öffnen",
"Open WebUI uses faster-whisper internally.": "Open WebUI verwendet intern faster-whisper.", "Open WebUI uses faster-whisper internally.": "Open WebUI verwendet intern faster-whisper.",
"Open WebUI uses SpeechT5 and CMU Arctic speaker embeddings.": "", "Open WebUI uses SpeechT5 and CMU Arctic speaker embeddings.": "Open WebUI verwendet SpeechT5 und CMU Arctic-Sprecher-Embeddings.",
"Open WebUI version (v{{OPEN_WEBUI_VERSION}}) is lower than required version (v{{REQUIRED_VERSION}})": "Die installierte Open-WebUI-Version (v{{OPEN_WEBUI_VERSION}}) ist niedriger als die erforderliche Version (v{{REQUIRED_VERSION}})", "Open WebUI version (v{{OPEN_WEBUI_VERSION}}) is lower than required version (v{{REQUIRED_VERSION}})": "Die installierte Open-WebUI-Version (v{{OPEN_WEBUI_VERSION}}) ist niedriger als die erforderliche Version (v{{REQUIRED_VERSION}})",
"OpenAI": "OpenAI", "OpenAI": "OpenAI",
"OpenAI API": "OpenAI-API", "OpenAI API": "OpenAI-API",
"OpenAI API Config": "OpenAI-API-Konfiguration", "OpenAI API Config": "OpenAI-API-Konfiguration",
"OpenAI API Key is required.": "OpenAI-API-Schlüssel erforderlich.", "OpenAI API Key is required.": "OpenAI-API-Schlüssel erforderlich.",
"OpenAI API settings updated": "", "OpenAI API settings updated": "OpenAI-API-Einstellungen aktualisiert",
"OpenAI URL/Key required.": "OpenAI-URL/Schlüssel erforderlich.", "OpenAI URL/Key required.": "OpenAI-URL/Schlüssel erforderlich.",
"or": "oder", "or": "oder",
"Organize your users": "", "Organize your users": "Organisieren Sie Ihre Benutzer",
"Other": "Andere", "Other": "Andere",
"OUTPUT": "AUSGABE", "OUTPUT": "AUSGABE",
"Output format": "Ausgabeformat", "Output format": "Ausgabeformat",
"Overview": "Übersicht", "Overview": "Übersicht",
"page": "Seite", "page": "Seite",
"Password": "Passwort", "Password": "Passwort",
"Paste Large Text as File": "", "Paste Large Text as File": "Großen Text als Datei einfügen",
"PDF document (.pdf)": "PDF-Dokument (.pdf)", "PDF document (.pdf)": "PDF-Dokument (.pdf)",
"PDF Extract Images (OCR)": "Text von Bildern aus PDFs extrahieren (OCR)", "PDF Extract Images (OCR)": "Text von Bildern aus PDFs extrahieren (OCR)",
"pending": "ausstehend", "pending": "ausstehend",
"Permission denied when accessing media devices": "Zugriff auf Mediengeräte verweigert", "Permission denied when accessing media devices": "Zugriff auf Mediengeräte verweigert",
"Permission denied when accessing microphone": "Zugriff auf das Mikrofon verweigert", "Permission denied when accessing microphone": "Zugriff auf das Mikrofon verweigert",
"Permission denied when accessing microphone: {{error}}": "Zugriff auf das Mikrofon verweigert: {{error}}", "Permission denied when accessing microphone: {{error}}": "Zugriff auf das Mikrofon verweigert: {{error}}",
"Permissions": "", "Permissions": "Berechtigungen",
"Personalization": "Personalisierung", "Personalization": "Personalisierung",
"Pin": "Anheften", "Pin": "Anheften",
"Pinned": "Angeheftet", "Pinned": "Angeheftet",
"Pioneer insights": "", "Pioneer insights": "Bahnbrechende Erkenntnisse",
"Pipeline deleted successfully": "Pipeline erfolgreich gelöscht", "Pipeline deleted successfully": "Pipeline erfolgreich gelöscht",
"Pipeline downloaded successfully": "Pipeline erfolgreich heruntergeladen", "Pipeline downloaded successfully": "Pipeline erfolgreich heruntergeladen",
"Pipelines": "Pipelines", "Pipelines": "Pipelines",
@@ -658,24 +672,26 @@
"Please carefully review the following warnings:": "Bitte überprüfen Sie die folgenden Warnungen sorgfältig:", "Please carefully review the following warnings:": "Bitte überprüfen Sie die folgenden Warnungen sorgfältig:",
"Please enter a prompt": "Bitte geben Sie einen Prompt ein", "Please enter a prompt": "Bitte geben Sie einen Prompt ein",
"Please fill in all fields.": "Bitte füllen Sie alle Felder aus.", "Please fill in all fields.": "Bitte füllen Sie alle Felder aus.",
"Please select a model first.": "",
"Please select a reason": "Bitte wählen Sie einen Grund aus", "Please select a reason": "Bitte wählen Sie einen Grund aus",
"Port": "", "Port": "Port",
"Positive attitude": "Positive Einstellung", "Positive attitude": "Positive Einstellung",
"Prefix ID": "", "Prefix ID": "Präfix-ID",
"Prefix ID is used to avoid conflicts with other connections by adding a prefix to the model IDs - leave empty to disable": "", "Prefix ID is used to avoid conflicts with other connections by adding a prefix to the model IDs - leave empty to disable": "Prefix-ID wird verwendet, um Konflikte mit anderen Verbindungen zu vermeiden, indem ein Präfix zu den Modell-IDs hinzugefügt wird - leer lassen, um zu deaktivieren",
"Previous 30 days": "Vorherige 30 Tage", "Previous 30 days": "Vorherige 30 Tage",
"Previous 7 days": "Vorherige 7 Tage", "Previous 7 days": "Vorherige 7 Tage",
"Profile Image": "Profilbild", "Profile Image": "Profilbild",
"Prompt (e.g. Tell me a fun fact about the Roman Empire)": "Prompt (z. B. \"Erzähle mir eine interessante Tatsache über das Römische Reich\")", "Prompt (e.g. Tell me a fun fact about the Roman Empire)": "Prompt (z. B. \"Erzähle mir eine interessante Tatsache über das Römische Reich\")",
"Prompt Content": "Prompt-Inhalt", "Prompt Content": "Prompt-Inhalt",
"Prompt created successfully": "", "Prompt created successfully": "Prompt erfolgreich erstellt",
"Prompt suggestions": "Prompt-Vorschläge", "Prompt suggestions": "Prompt-Vorschläge",
"Prompt updated successfully": "", "Prompt updated successfully": "Prompt erfolgreich aktualisiert",
"Prompts": "Prompts", "Prompts": "Prompts",
"Prompts Access": "", "Prompts Access": "Prompt-Zugriff",
"Proxy URL": "",
"Pull \"{{searchValue}}\" from Ollama.com": "\"{{searchValue}}\" von Ollama.com beziehen", "Pull \"{{searchValue}}\" from Ollama.com": "\"{{searchValue}}\" von Ollama.com beziehen",
"Pull a model from Ollama.com": "Modell von Ollama.com beziehen", "Pull a model from Ollama.com": "Modell von Ollama.com beziehen",
"Query Generation Prompt": "", "Query Generation Prompt": "Abfragegenerierungsprompt",
"Query Params": "Abfrageparameter", "Query Params": "Abfrageparameter",
"RAG Template": "RAG-Vorlage", "RAG Template": "RAG-Vorlage",
"Rating": "Bewertung", "Rating": "Bewertung",
@@ -683,7 +699,7 @@
"Read Aloud": "Vorlesen", "Read Aloud": "Vorlesen",
"Record voice": "Stimme aufnehmen", "Record voice": "Stimme aufnehmen",
"Redirecting you to OpenWebUI Community": "Sie werden zur OpenWebUI-Community weitergeleitet", "Redirecting you to OpenWebUI Community": "Sie werden zur OpenWebUI-Community weitergeleitet",
"Reduces the probability of generating nonsense. A higher value (e.g. 100) will give more diverse answers, while a lower value (e.g. 10) will be more conservative. (Default: 40)": "", "Reduces the probability of generating nonsense. A higher value (e.g. 100) will give more diverse answers, while a lower value (e.g. 10) will be more conservative. (Default: 40)": "Reduziert die Wahrscheinlichkeit, Unsinn zu generieren. Ein höherer Wert (z.B. 100) liefert vielfältigere Antworten, während ein niedrigerer Wert (z.B. 10) konservativer ist. (Standard: 40)",
"Refer to yourself as \"User\" (e.g., \"User is learning Spanish\")": "Beziehen Sie sich auf sich selbst als \"Benutzer\" (z. B. \"Benutzer lernt Spanisch\")", "Refer to yourself as \"User\" (e.g., \"User is learning Spanish\")": "Beziehen Sie sich auf sich selbst als \"Benutzer\" (z. B. \"Benutzer lernt Spanisch\")",
"References from": "Referenzen aus", "References from": "Referenzen aus",
"Refused when it shouldn't have": "Abgelehnt, obwohl es nicht hätte abgelehnt werden sollen", "Refused when it shouldn't have": "Abgelehnt, obwohl es nicht hätte abgelehnt werden sollen",
@@ -693,17 +709,20 @@
"Remove": "Entfernen", "Remove": "Entfernen",
"Remove Model": "Modell entfernen", "Remove Model": "Modell entfernen",
"Rename": "Umbenennen", "Rename": "Umbenennen",
"Reorder Models": "",
"Repeat Last N": "Wiederhole die letzten N", "Repeat Last N": "Wiederhole die letzten N",
"Request Mode": "Anforderungsmodus", "Request Mode": "Anforderungsmodus",
"Reranking Model": "Reranking-Modell", "Reranking Model": "Reranking-Modell",
"Reranking model disabled": "Reranking-Modell deaktiviert", "Reranking model disabled": "Reranking-Modell deaktiviert",
"Reranking model set to \"{{reranking_model}}\"": "Reranking-Modell \"{{reranking_model}}\" fesgelegt", "Reranking model set to \"{{reranking_model}}\"": "Reranking-Modell \"{{reranking_model}}\" fesgelegt",
"Reset": "Zurücksetzen", "Reset": "Zurücksetzen",
"Reset All Models": "",
"Reset Upload Directory": "Upload-Verzeichnis zurücksetzen", "Reset Upload Directory": "Upload-Verzeichnis zurücksetzen",
"Reset Vector Storage/Knowledge": "Vektorspeicher/Wissen zurücksetzen", "Reset Vector Storage/Knowledge": "Vektorspeicher/Wissen zurücksetzen",
"Response notifications cannot be activated as the website permissions have been denied. Please visit your browser settings to grant the necessary access.": "Benachrichtigungen können nicht aktiviert werden, da die Website-Berechtigungen abgelehnt wurden. Bitte besuchen Sie Ihre Browser-Einstellungen, um den erforderlichen Zugriff zu gewähren.", "Response notifications cannot be activated as the website permissions have been denied. Please visit your browser settings to grant the necessary access.": "Benachrichtigungen können nicht aktiviert werden, da die Website-Berechtigungen abgelehnt wurden. Bitte besuchen Sie Ihre Browser-Einstellungen, um den erforderlichen Zugriff zu gewähren.",
"Response splitting": "Antwortaufteilung", "Response splitting": "Antwortaufteilung",
"Result": "Ergebnis", "Result": "Ergebnis",
"Retrieval Query Generation": "",
"Rich Text Input for Chat": "Rich-Text-Eingabe für Unterhaltungen", "Rich Text Input for Chat": "Rich-Text-Eingabe für Unterhaltungen",
"RK": "RK", "RK": "RK",
"Role": "Rolle", "Role": "Rolle",
@@ -722,18 +741,18 @@
"Scroll to bottom when switching between branches": "Beim Wechsel zwischen Branches nach unten scrollen", "Scroll to bottom when switching between branches": "Beim Wechsel zwischen Branches nach unten scrollen",
"Search": "Suchen", "Search": "Suchen",
"Search a model": "Modell suchen", "Search a model": "Modell suchen",
"Search Base": "", "Search Base": "Suchbasis",
"Search Chats": "Unterhaltungen durchsuchen...", "Search Chats": "Unterhaltungen durchsuchen...",
"Search Collection": "Sammlung durchsuchen", "Search Collection": "Sammlung durchsuchen",
"Search Filters": "", "Search Filters": "Suchfilter",
"search for tags": "nach Tags suchen", "search for tags": "nach Tags suchen",
"Search Functions": "Funktionen durchsuchen...", "Search Functions": "Funktionen durchsuchen...",
"Search Knowledge": "Wissen durchsuchen", "Search Knowledge": "Wissen durchsuchen",
"Search Models": "Modelle durchsuchen...", "Search Models": "Modelle durchsuchen...",
"Search options": "", "Search options": "Suchoptionen",
"Search Prompts": "Prompts durchsuchen...", "Search Prompts": "Prompts durchsuchen...",
"Search Result Count": "Anzahl der Suchergebnisse", "Search Result Count": "Anzahl der Suchergebnisse",
"Search the web": "", "Search the web": "Im Web suchen",
"Search Tools": "Werkzeuge durchsuchen...", "Search Tools": "Werkzeuge durchsuchen...",
"SearchApi API Key": "SearchApi-API-Schlüssel", "SearchApi API Key": "SearchApi-API-Schlüssel",
"SearchApi Engine": "SearchApi-Engine", "SearchApi Engine": "SearchApi-Engine",
@@ -748,7 +767,7 @@
"Select a base model": "Wählen Sie ein Basismodell", "Select a base model": "Wählen Sie ein Basismodell",
"Select a engine": "Wählen Sie eine Engine", "Select a engine": "Wählen Sie eine Engine",
"Select a function": "Wählen Sie eine Funktion", "Select a function": "Wählen Sie eine Funktion",
"Select a group": "", "Select a group": "Wählen Sie eine Gruppe",
"Select a model": "Wählen Sie ein Modell", "Select a model": "Wählen Sie ein Modell",
"Select a pipeline": "Wählen Sie eine Pipeline", "Select a pipeline": "Wählen Sie eine Pipeline",
"Select a pipeline url": "Wählen Sie eine Pipeline-URL", "Select a pipeline url": "Wählen Sie eine Pipeline-URL",
@@ -771,7 +790,7 @@
"Set as default": "Als Standard festlegen", "Set as default": "Als Standard festlegen",
"Set CFG Scale": "CFG-Skala festlegen", "Set CFG Scale": "CFG-Skala festlegen",
"Set Default Model": "Standardmodell festlegen", "Set Default Model": "Standardmodell festlegen",
"Set embedding model": "", "Set embedding model": "Einbettungsmodell festlegen",
"Set embedding model (e.g. {{model}})": "Einbettungsmodell festlegen (z. B. {{model}})", "Set embedding model (e.g. {{model}})": "Einbettungsmodell festlegen (z. B. {{model}})",
"Set Image Size": "Bildgröße festlegen", "Set Image Size": "Bildgröße festlegen",
"Set reranking model (e.g. {{model}})": "Rerankingmodell festlegen (z. B. {{model}})", "Set reranking model (e.g. {{model}})": "Rerankingmodell festlegen (z. B. {{model}})",
@@ -779,29 +798,29 @@
"Set Scheduler": "Scheduler festlegen", "Set Scheduler": "Scheduler festlegen",
"Set Steps": "Schrittgröße festlegen", "Set Steps": "Schrittgröße festlegen",
"Set Task Model": "Aufgabenmodell festlegen", "Set Task Model": "Aufgabenmodell festlegen",
"Set the number of GPU devices used for computation. This option controls how many GPU devices (if available) are used to process incoming requests. Increasing this value can significantly improve performance for models that are optimized for GPU acceleration but may also consume more power and GPU resources.": "", "Set the number of GPU devices used for computation. This option controls how many GPU devices (if available) are used to process incoming requests. Increasing this value can significantly improve performance for models that are optimized for GPU acceleration but may also consume more power and GPU resources.": "Legt die Anzahl der für die Berechnung verwendeten GPU-Geräte fest. Diese Option steuert, wie viele GPU-Geräte (falls verfügbar) zur Verarbeitung eingehender Anfragen verwendet werden. Eine Erhöhung dieses Wertes kann die Leistung für Modelle, die für GPU-Beschleunigung optimiert sind, erheblich verbessern, kann jedoch auch mehr Strom und GPU-Ressourcen verbrauchen.",
"Set the number of worker threads used for computation. This option controls how many threads are used to process incoming requests concurrently. Increasing this value can improve performance under high concurrency workloads but may also consume more CPU resources.": "", "Set the number of worker threads used for computation. This option controls how many threads are used to process incoming requests concurrently. Increasing this value can improve performance under high concurrency workloads but may also consume more CPU resources.": "Legt die Anzahl der für die Berechnung verwendeten GPU-Geräte fest. Diese Option steuert, wie viele GPU-Geräte (falls verfügbar) zur Verarbeitung eingehender Anfragen verwendet werden. Eine Erhöhung dieses Wertes kann die Leistung für Modelle, die für GPU-Beschleunigung optimiert sind, erheblich verbessern, kann jedoch auch mehr Strom und GPU-Ressourcen verbrauchen.",
"Set Voice": "Stimme festlegen", "Set Voice": "Stimme festlegen",
"Set whisper model": "Whisper-Modell festlegen", "Set whisper model": "Whisper-Modell festlegen",
"Sets how far back for the model to look back to prevent repetition. (Default: 64, 0 = disabled, -1 = num_ctx)": "", "Sets how far back for the model to look back to prevent repetition. (Default: 64, 0 = disabled, -1 = num_ctx)": "Legt fest, wie weit das Modell zurückblicken soll, um Wiederholungen zu verhindern. (Standard: 64, 0 = deaktiviert, -1 = num_ctx)",
"Sets how strongly to penalize repetitions. A higher value (e.g., 1.5) will penalize repetitions more strongly, while a lower value (e.g., 0.9) will be more lenient. (Default: 1.1)": "", "Sets how strongly to penalize repetitions. A higher value (e.g., 1.5) will penalize repetitions more strongly, while a lower value (e.g., 0.9) will be more lenient. (Default: 1.1)": "Legt fest, wie stark Wiederholungen bestraft werden sollen. Ein höherer Wert (z.B. 1.5) bestraft Wiederholungen stärker, während ein niedrigerer Wert (z.B. 0.9) nachsichtiger ist. (Standard: 1.1)",
"Sets the random number seed to use for generation. Setting this to a specific number will make the model generate the same text for the same prompt. (Default: random)": "", "Sets the random number seed to use for generation. Setting this to a specific number will make the model generate the same text for the same prompt. (Default: random)": "Legt den Zufallszahlengenerator-Seed für die Generierung fest. Wenn dieser auf eine bestimmte Zahl gesetzt wird, erzeugt das Modell denselben Text für denselben Prompt. (Standard: zufällig)",
"Sets the size of the context window used to generate the next token. (Default: 2048)": "", "Sets the size of the context window used to generate the next token. (Default: 2048)": "Legt die Größe des Kontextfensters fest, das zur Generierung des nächsten Tokens verwendet wird. (Standard: 2048)",
"Sets the stop sequences to use. When this pattern is encountered, the LLM will stop generating text and return. Multiple stop patterns may be set by specifying multiple separate stop parameters in a modelfile.": "", "Sets the stop sequences to use. When this pattern is encountered, the LLM will stop generating text and return. Multiple stop patterns may be set by specifying multiple separate stop parameters in a modelfile.": "Legt die zu verwendenden Stoppsequenzen fest. Wenn dieses Muster erkannt wird, stoppt das LLM die Textgenerierung und gibt zurück. Mehrere Stoppmuster können festgelegt werden, indem mehrere separate Stopp-Parameter in einer Modelldatei angegeben werden.",
"Settings": "Einstellungen", "Settings": "Einstellungen",
"Settings saved successfully!": "Einstellungen erfolgreich gespeichert!", "Settings saved successfully!": "Einstellungen erfolgreich gespeichert!",
"Share": "Teilen", "Share": "Teilen",
"Share Chat": "Unterhaltung teilen", "Share Chat": "Unterhaltung teilen",
"Share to OpenWebUI Community": "Mit OpenWebUI Community teilen", "Share to OpenWebUI Community": "Mit OpenWebUI Community teilen",
"Show": "Anzeigen", "Show": "Anzeigen",
"Show \"What's New\" modal on login": "", "Show \"What's New\" modal on login": "\"Was gibt's Neues\"-Modal beim Anmelden anzeigen",
"Show Admin Details in Account Pending Overlay": "Admin-Details im Account-Pending-Overlay anzeigen", "Show Admin Details in Account Pending Overlay": "Admin-Details im Account-Pending-Overlay anzeigen",
"Show shortcuts": "Verknüpfungen anzeigen", "Show shortcuts": "Verknüpfungen anzeigen",
"Show your support!": "Zeigen Sie Ihre Unterstützung!", "Show your support!": "Zeigen Sie Ihre Unterstützung!",
"Showcased creativity": "Kreativität gezeigt", "Showcased creativity": "Kreativität gezeigt",
"Sign in": "Anmelden", "Sign in": "Anmelden",
"Sign in to {{WEBUI_NAME}}": "Bei {{WEBUI_NAME}} anmelden", "Sign in to {{WEBUI_NAME}}": "Bei {{WEBUI_NAME}} anmelden",
"Sign in to {{WEBUI_NAME}} with LDAP": "", "Sign in to {{WEBUI_NAME}} with LDAP": "Bei {{WEBUI_NAME}} mit LDAP anmelden",
"Sign Out": "Abmelden", "Sign Out": "Abmelden",
"Sign up": "Registrieren", "Sign up": "Registrieren",
"Sign up to {{WEBUI_NAME}}": "Bei {{WEBUI_NAME}} registrieren", "Sign up to {{WEBUI_NAME}}": "Bei {{WEBUI_NAME}} registrieren",
@@ -825,8 +844,9 @@
"System": "System", "System": "System",
"System Instructions": "Systemanweisungen", "System Instructions": "Systemanweisungen",
"System Prompt": "System-Prompt", "System Prompt": "System-Prompt",
"Tags Generation": "",
"Tags Generation Prompt": "Prompt für Tag-Generierung", "Tags Generation Prompt": "Prompt für Tag-Generierung",
"Tail free sampling is used to reduce the impact of less probable tokens from the output. A higher value (e.g., 2.0) will reduce the impact more, while a value of 1.0 disables this setting. (default: 1)": "", "Tail free sampling is used to reduce the impact of less probable tokens from the output. A higher value (e.g., 2.0) will reduce the impact more, while a value of 1.0 disables this setting. (default: 1)": "Tail-Free Sampling wird verwendet, um den Einfluss weniger wahrscheinlicher Tokens auf die Ausgabe zu reduzieren. Ein höherer Wert (z.B. 2.0) reduziert den Einfluss stärker, während ein Wert von 1.0 diese Einstellung deaktiviert. (Standard: 1)",
"Tap to interrupt": "Zum Unterbrechen tippen", "Tap to interrupt": "Zum Unterbrechen tippen",
"Tavily API Key": "Tavily-API-Schlüssel", "Tavily API Key": "Tavily-API-Schlüssel",
"Tell us more:": "Erzähl uns mehr", "Tell us more:": "Erzähl uns mehr",
@@ -837,30 +857,30 @@
"Text-to-Speech Engine": "Text-zu-Sprache-Engine", "Text-to-Speech Engine": "Text-zu-Sprache-Engine",
"Tfs Z": "Tfs Z", "Tfs Z": "Tfs Z",
"Thanks for your feedback!": "Danke für Ihr Feedback!", "Thanks for your feedback!": "Danke für Ihr Feedback!",
"The Application Account DN you bind with for search": "", "The Application Account DN you bind with for search": "Der Anwendungs-Konto-DN, mit dem Sie für die Suche binden",
"The base to search for users": "", "The base to search for users": "Die Basis, in der nach Benutzern gesucht wird",
"The batch size determines how many text requests are processed together at once. A higher batch size can increase the performance and speed of the model, but it also requires more memory. (Default: 512)": "", "The batch size determines how many text requests are processed together at once. A higher batch size can increase the performance and speed of the model, but it also requires more memory. (Default: 512)": "Die Batch-Größe bestimmt, wie viele Textanfragen gleichzeitig verarbeitet werden. Eine größere Batch-Größe kann die Leistung und Geschwindigkeit des Modells erhöhen, erfordert jedoch auch mehr Speicher. (Standard: 512)",
"The developers behind this plugin are passionate volunteers from the community. If you find this plugin helpful, please consider contributing to its development.": "Die Entwickler hinter diesem Plugin sind leidenschaftliche Freiwillige aus der Community. Wenn Sie dieses Plugin hilfreich finden, erwägen Sie bitte, zu seiner Entwicklung beizutragen.", "The developers behind this plugin are passionate volunteers from the community. If you find this plugin helpful, please consider contributing to its development.": "Die Entwickler hinter diesem Plugin sind leidenschaftliche Freiwillige aus der Community. Wenn Sie dieses Plugin hilfreich finden, erwägen Sie bitte, zu seiner Entwicklung beizutragen.",
"The evaluation leaderboard is based on the Elo rating system and is updated in real-time.": "Die Bewertungs-Bestenliste basiert auf dem Elo-Bewertungssystem und wird in Echtzeit aktualisiert.", "The evaluation leaderboard is based on the Elo rating system and is updated in real-time.": "Die Bewertungs-Bestenliste basiert auf dem Elo-Bewertungssystem und wird in Echtzeit aktualisiert.",
"The LDAP attribute that maps to the username that users use to sign in.": "", "The LDAP attribute that maps to the username that users use to sign in.": "Das LDAP-Attribut, das dem Benutzernamen zugeordnet ist, den Benutzer zum Anmelden verwenden.",
"The leaderboard is currently in beta, and we may adjust the rating calculations as we refine the algorithm.": "Die Bestenliste befindet sich derzeit in der Beta-Phase, und es ist möglich, dass wir die Bewertungsberechnungen anpassen, während wir den Algorithmus verfeinern.", "The leaderboard is currently in beta, and we may adjust the rating calculations as we refine the algorithm.": "Die Bestenliste befindet sich derzeit in der Beta-Phase, und es ist möglich, dass wir die Bewertungsberechnungen anpassen, während wir den Algorithmus verfeinern.",
"The maximum file size in MB. If the file size exceeds this limit, the file will not be uploaded.": "Die maximale Dateigröße in MB. Wenn die Dateigröße dieses Limit überschreitet, wird die Datei nicht hochgeladen.", "The maximum file size in MB. If the file size exceeds this limit, the file will not be uploaded.": "Die maximale Dateigröße in MB. Wenn die Dateigröße dieses Limit überschreitet, wird die Datei nicht hochgeladen.",
"The maximum number of files that can be used at once in chat. If the number of files exceeds this limit, the files will not be uploaded.": "Die maximale Anzahl von Dateien, die gleichzeitig in der Unterhaltung verwendet werden können. Wenn die Anzahl der Dateien dieses Limit überschreitet, werden die Dateien nicht hochgeladen.", "The maximum number of files that can be used at once in chat. If the number of files exceeds this limit, the files will not be uploaded.": "Die maximale Anzahl von Dateien, die gleichzeitig in der Unterhaltung verwendet werden können. Wenn die Anzahl der Dateien dieses Limit überschreitet, werden die Dateien nicht hochgeladen.",
"The score should be a value between 0.0 (0%) and 1.0 (100%).": "Die Punktzahl sollte ein Wert zwischen 0,0 (0 %) und 1,0 (100 %) sein.", "The score should be a value between 0.0 (0%) and 1.0 (100%).": "Die Punktzahl sollte ein Wert zwischen 0,0 (0 %) und 1,0 (100 %) sein.",
"The temperature of the model. Increasing the temperature will make the model answer more creatively. (Default: 0.8)": "", "The temperature of the model. Increasing the temperature will make the model answer more creatively. (Default: 0.8)": "Die Temperatur des Modells. Eine Erhöhung der Temperatur führt dazu, dass das Modell kreativer antwortet. (Standard: 0,8)",
"Theme": "Design", "Theme": "Design",
"Thinking...": "Denke nach...", "Thinking...": "Denke nach...",
"This action cannot be undone. Do you wish to continue?": "Diese Aktion kann nicht rückgängig gemacht werden. Möchten Sie fortfahren?", "This action cannot be undone. Do you wish to continue?": "Diese Aktion kann nicht rückgängig gemacht werden. Möchten Sie fortfahren?",
"This ensures that your valuable conversations are securely saved to your backend database. Thank you!": "Dies stellt sicher, dass Ihre wertvollen Unterhaltungen sicher in Ihrer Backend-Datenbank gespeichert werden. Vielen Dank!", "This ensures that your valuable conversations are securely saved to your backend database. Thank you!": "Dies stellt sicher, dass Ihre wertvollen Unterhaltungen sicher in Ihrer Backend-Datenbank gespeichert werden. Vielen Dank!",
"This is an experimental feature, it may not function as expected and is subject to change at any time.": "Dies ist eine experimentelle Funktion, sie funktioniert möglicherweise nicht wie erwartet und kann jederzeit geändert werden.", "This is an experimental feature, it may not function as expected and is subject to change at any time.": "Dies ist eine experimentelle Funktion, sie funktioniert möglicherweise nicht wie erwartet und kann jederzeit geändert werden.",
"This option controls how many tokens are preserved when refreshing the context. For example, if set to 2, the last 2 tokens of the conversation context will be retained. Preserving context can help maintain the continuity of a conversation, but it may reduce the ability to respond to new topics. (Default: 24)": "", "This option controls how many tokens are preserved when refreshing the context. For example, if set to 2, the last 2 tokens of the conversation context will be retained. Preserving context can help maintain the continuity of a conversation, but it may reduce the ability to respond to new topics. (Default: 24)": "Diese Option steuert, wie viele Tokens beim Aktualisieren des Kontexts beibehalten werden. Wenn sie beispielsweise auf 2 gesetzt ist, werden die letzten 2 Tokens des Gesprächskontexts beibehalten. Das Beibehalten des Kontexts kann helfen, die Kontinuität eines Gesprächs aufrechtzuerhalten, kann jedoch die Fähigkeit verringern, auf neue Themen zu reagieren. (Standard: 24)",
"This option sets the maximum number of tokens the model can generate in its response. Increasing this limit allows the model to provide longer answers, but it may also increase the likelihood of unhelpful or irrelevant content being generated. (Default: 128)": "", "This option sets the maximum number of tokens the model can generate in its response. Increasing this limit allows the model to provide longer answers, but it may also increase the likelihood of unhelpful or irrelevant content being generated. (Default: 128)": "Diese Option legt die maximale Anzahl von Tokens fest, die das Modell in seiner Antwort generieren kann. Eine Erhöhung dieses Limits ermöglicht es dem Modell, längere Antworten zu geben, kann jedoch auch die Wahrscheinlichkeit erhöhen, dass unhilfreicher oder irrelevanter Inhalt generiert wird. (Standard: 128)",
"This option will delete all existing files in the collection and replace them with newly uploaded files.": "Diese Option löscht alle vorhandenen Dateien in der Sammlung und ersetzt sie durch neu hochgeladene Dateien.", "This option will delete all existing files in the collection and replace them with newly uploaded files.": "Diese Option löscht alle vorhandenen Dateien in der Sammlung und ersetzt sie durch neu hochgeladene Dateien.",
"This response was generated by \"{{model}}\"": "Diese Antwort wurde von \"{{model}}\" generiert", "This response was generated by \"{{model}}\"": "Diese Antwort wurde von \"{{model}}\" generiert",
"This will delete": "Dies löscht", "This will delete": "Dies löscht",
"This will delete <strong>{{NAME}}</strong> and <strong>all its contents</strong>.": "Dies löscht <strong>{{NAME}}</strong> und <strong>alle Inhalte</strong>.", "This will delete <strong>{{NAME}}</strong> and <strong>all its contents</strong>.": "Dies löscht <strong>{{NAME}}</strong> und <strong>alle Inhalte</strong>.",
"This will delete all models including custom models": "", "This will delete all models including custom models": "Dies wird alle Modelle einschließlich benutzerdefinierter Modelle löschen",
"This will delete all models including custom models and cannot be undone.": "", "This will delete all models including custom models and cannot be undone.": "Dies wird alle Modelle einschließlich benutzerdefinierter Modelle löschen und kann nicht rückgängig gemacht werden.",
"This will reset the knowledge base and sync all files. Do you wish to continue?": "Dadurch wird die Wissensdatenbank zurückgesetzt und alle Dateien synchronisiert. Möchten Sie fortfahren?", "This will reset the knowledge base and sync all files. Do you wish to continue?": "Dadurch wird die Wissensdatenbank zurückgesetzt und alle Dateien synchronisiert. Möchten Sie fortfahren?",
"Thorough explanation": "Ausführliche Erklärung", "Thorough explanation": "Ausführliche Erklärung",
"Tika": "Tika", "Tika": "Tika",
@@ -872,7 +892,7 @@
"Title Auto-Generation": "Unterhaltungstitel automatisch generieren", "Title Auto-Generation": "Unterhaltungstitel automatisch generieren",
"Title cannot be an empty string.": "Titel darf nicht leer sein.", "Title cannot be an empty string.": "Titel darf nicht leer sein.",
"Title Generation Prompt": "Prompt für Titelgenerierung", "Title Generation Prompt": "Prompt für Titelgenerierung",
"TLS": "", "TLS": "TLS",
"To access the available model names for downloading,": "Um auf die verfügbaren Modellnamen zuzugreifen,", "To access the available model names for downloading,": "Um auf die verfügbaren Modellnamen zuzugreifen,",
"To access the GGUF models available for downloading,": "Um auf die verfügbaren GGUF-Modelle zuzugreifen,", "To access the GGUF models available for downloading,": "Um auf die verfügbaren GGUF-Modelle zuzugreifen,",
"To access the WebUI, please reach out to the administrator. Admins can manage user statuses from the Admin Panel.": "Um auf das WebUI zugreifen zu können, wenden Sie sich bitte an einen Administrator. Administratoren können den Benutzerstatus über das Admin-Panel verwalten.", "To access the WebUI, please reach out to the administrator. Admins can manage user statuses from the Admin Panel.": "Um auf das WebUI zugreifen zu können, wenden Sie sich bitte an einen Administrator. Administratoren können den Benutzerstatus über das Admin-Panel verwalten.",
@@ -890,19 +910,19 @@
"Too verbose": "Zu ausführlich", "Too verbose": "Zu ausführlich",
"Tool created successfully": "Werkzeug erfolgreich erstellt", "Tool created successfully": "Werkzeug erfolgreich erstellt",
"Tool deleted successfully": "Werkzeug erfolgreich gelöscht", "Tool deleted successfully": "Werkzeug erfolgreich gelöscht",
"Tool Description": "", "Tool Description": "Werkzeugbeschreibung",
"Tool ID": "", "Tool ID": "Werkzeug-ID",
"Tool imported successfully": "Werkzeug erfolgreich importiert", "Tool imported successfully": "Werkzeug erfolgreich importiert",
"Tool Name": "", "Tool Name": "Werkzeugname",
"Tool updated successfully": "Werkzeug erfolgreich aktualisiert", "Tool updated successfully": "Werkzeug erfolgreich aktualisiert",
"Tools": "Werkzeuge", "Tools": "Werkzeuge",
"Tools Access": "", "Tools Access": "Werkzeugzugriff",
"Tools are a function calling system with arbitrary code execution": "Wekzeuge sind ein Funktionssystem mit beliebiger Codeausführung", "Tools are a function calling system with arbitrary code execution": "Wekzeuge sind ein Funktionssystem mit beliebiger Codeausführung",
"Tools have a function calling system that allows arbitrary code execution": "Werkezuge verfügen über ein Funktionssystem, das die Ausführung beliebigen Codes ermöglicht", "Tools have a function calling system that allows arbitrary code execution": "Werkezuge verfügen über ein Funktionssystem, das die Ausführung beliebigen Codes ermöglicht",
"Tools have a function calling system that allows arbitrary code execution.": "Werkzeuge verfügen über ein Funktionssystem, das die Ausführung beliebigen Codes ermöglicht.", "Tools have a function calling system that allows arbitrary code execution.": "Werkzeuge verfügen über ein Funktionssystem, das die Ausführung beliebigen Codes ermöglicht.",
"Top K": "Top K", "Top K": "Top K",
"Top P": "Top P", "Top P": "Top P",
"Transformers": "", "Transformers": "Transformers",
"Trouble accessing Ollama?": "Probleme beim Zugriff auf Ollama?", "Trouble accessing Ollama?": "Probleme beim Zugriff auf Ollama?",
"TTS Model": "TTS-Modell", "TTS Model": "TTS-Modell",
"TTS Settings": "TTS-Einstellungen", "TTS Settings": "TTS-Einstellungen",
@@ -911,12 +931,12 @@
"Type Hugging Face Resolve (Download) URL": "Geben Sie die Hugging Face Resolve-URL ein", "Type Hugging Face Resolve (Download) URL": "Geben Sie die Hugging Face Resolve-URL ein",
"Uh-oh! There was an issue connecting to {{provider}}.": "Ups! Es gab ein Problem bei der Verbindung mit {{provider}}.", "Uh-oh! There was an issue connecting to {{provider}}.": "Ups! Es gab ein Problem bei der Verbindung mit {{provider}}.",
"UI": "Oberfläche", "UI": "Oberfläche",
"Unarchive All": "", "Unarchive All": "Alle wiederherstellen",
"Unarchive All Archived Chats": "", "Unarchive All Archived Chats": "Alle archivierten Unterhaltungen wiederherstellen",
"Unarchive Chat": "", "Unarchive Chat": "Unterhaltung wiederherstellen",
"Unlock mysteries": "", "Unlock mysteries": "Geheimnisse entsperren",
"Unpin": "Lösen", "Unpin": "Lösen",
"Unravel secrets": "", "Unravel secrets": "Geheimnisse lüften",
"Untagged": "Ungetaggt", "Untagged": "Ungetaggt",
"Update": "Aktualisieren", "Update": "Aktualisieren",
"Update and Copy Link": "Aktualisieren und Link kopieren", "Update and Copy Link": "Aktualisieren und Link kopieren",
@@ -932,18 +952,18 @@
"Upload Files": "Datei(en) hochladen", "Upload Files": "Datei(en) hochladen",
"Upload Pipeline": "Pipeline hochladen", "Upload Pipeline": "Pipeline hochladen",
"Upload Progress": "Hochladefortschritt", "Upload Progress": "Hochladefortschritt",
"URL": "", "URL": "URL",
"URL Mode": "URL-Modus", "URL Mode": "URL-Modus",
"Use '#' in the prompt input to load and include your knowledge.": "Nutzen Sie '#' in der Prompt-Eingabe, um Ihr Wissen zu laden und einzuschließen.", "Use '#' in the prompt input to load and include your knowledge.": "Nutzen Sie '#' in der Prompt-Eingabe, um Ihr Wissen zu laden und einzuschließen.",
"Use Gravatar": "Gravatar verwenden", "Use Gravatar": "Gravatar verwenden",
"Use groups to group your users and assign permissions.": "", "Use groups to group your users and assign permissions.": "Nutzen Sie Gruppen, um Ihre Benutzer zu gruppieren und Berechtigungen zuzuweisen.",
"Use Initials": "Initialen verwenden", "Use Initials": "Initialen verwenden",
"use_mlock (Ollama)": "use_mlock (Ollama)", "use_mlock (Ollama)": "use_mlock (Ollama)",
"use_mmap (Ollama)": "use_mmap (Ollama)", "use_mmap (Ollama)": "use_mmap (Ollama)",
"user": "Benutzer", "user": "Benutzer",
"User": "Benutzer", "User": "Benutzer",
"User location successfully retrieved.": "Benutzerstandort erfolgreich ermittelt.", "User location successfully retrieved.": "Benutzerstandort erfolgreich ermittelt.",
"Username": "", "Username": "Benutzername",
"Users": "Benutzer", "Users": "Benutzer",
"Using the default arena model with all models. Click the plus button to add custom models.": "Verwendung des Standard-Arena-Modells mit allen Modellen. Klicken Sie auf die Plus-Schaltfläche, um benutzerdefinierte Modelle hinzuzufügen.", "Using the default arena model with all models. Click the plus button to add custom models.": "Verwendung des Standard-Arena-Modells mit allen Modellen. Klicken Sie auf die Plus-Schaltfläche, um benutzerdefinierte Modelle hinzuzufügen.",
"Utilize": "Verwende", "Utilize": "Verwende",
@@ -955,43 +975,45 @@
"variable to have them replaced with clipboard content.": "Variable, um den Inhalt der Zwischenablage beim Nutzen des Prompts zu ersetzen.", "variable to have them replaced with clipboard content.": "Variable, um den Inhalt der Zwischenablage beim Nutzen des Prompts zu ersetzen.",
"Version": "Version", "Version": "Version",
"Version {{selectedVersion}} of {{totalVersions}}": "Version {{selectedVersion}} von {{totalVersions}}", "Version {{selectedVersion}} of {{totalVersions}}": "Version {{selectedVersion}} von {{totalVersions}}",
"Visibility": "", "Visibility": "Sichtbarkeit",
"Voice": "Stimme", "Voice": "Stimme",
"Voice Input": "Spracheingabe", "Voice Input": "Spracheingabe",
"Warning": "Warnung", "Warning": "Warnung",
"Warning:": "Warnung:", "Warning:": "Warnung:",
"Warning: Enabling this will allow users to upload arbitrary code on the server.": "", "Warning: Enabling this will allow users to upload arbitrary code on the server.": "Warnung: Wenn Sie dies aktivieren, können Benutzer beliebigen Code auf dem Server hochladen.",
"Warning: If you update or change your embedding model, you will need to re-import all documents.": "Warnung: Wenn Sie das Einbettungsmodell aktualisieren oder ändern, müssen Sie alle Dokumente erneut importieren.", "Warning: If you update or change your embedding model, you will need to re-import all documents.": "Warnung: Wenn Sie das Einbettungsmodell aktualisieren oder ändern, müssen Sie alle Dokumente erneut importieren.",
"Web": "Web", "Web": "Web",
"Web API": "Web-API", "Web API": "Web-API",
"Web Loader Settings": "Web Loader Einstellungen", "Web Loader Settings": "Web Loader Einstellungen",
"Web Search": "Websuche", "Web Search": "Websuche",
"Web Search Engine": "Suchmaschine", "Web Search Engine": "Suchmaschine",
"Web Search Query Generation": "",
"Webhook URL": "Webhook URL", "Webhook URL": "Webhook URL",
"WebUI Settings": "WebUI-Einstellungen", "WebUI Settings": "WebUI-Einstellungen",
"WebUI will make requests to \"{{url}}/api/chat\"": "", "WebUI will make requests to \"{{url}}/api/chat\"": "WebUI wird Anfragen an \"{{url}}/api/chat\" senden",
"WebUI will make requests to \"{{url}}/chat/completions\"": "", "WebUI will make requests to \"{{url}}/chat/completions\"": "WebUI wird Anfragen an \"{{url}}/chat/completions\" senden",
"What are you trying to achieve?": "", "What are you trying to achieve?": "Was versuchen Sie zu erreichen?",
"What are you working on?": "", "What are you working on?": "Woran arbeiten Sie?",
"What’s New in": "Neuigkeiten von", "What’s New in": "Neuigkeiten von",
"When enabled, the model will respond to each chat message in real-time, generating a response as soon as the user sends a message. This mode is useful for live chat applications, but may impact performance on slower hardware.": "", "When enabled, the model will respond to each chat message in real-time, generating a response as soon as the user sends a message. This mode is useful for live chat applications, but may impact performance on slower hardware.": "Wenn aktiviert, antwortet das Modell in Echtzeit auf jede Chat-Nachricht und generiert eine Antwort, sobald der Benutzer eine Nachricht sendet. Dieser Modus ist nützlich für Live-Chat-Anwendungen, kann jedoch die Leistung auf langsamerer Hardware beeinträchtigen.",
"wherever you are": "", "wherever you are": "wo immer Sie sind",
"Whisper (Local)": "Whisper (lokal)", "Whisper (Local)": "Whisper (lokal)",
"Why?": "Warum?",
"Widescreen Mode": "Breitbildmodus", "Widescreen Mode": "Breitbildmodus",
"Won": "Gewonnen", "Won": "Gewonnen",
"Works together with top-k. A higher value (e.g., 0.95) will lead to more diverse text, while a lower value (e.g., 0.5) will generate more focused and conservative text. (Default: 0.9)": "", "Works together with top-k. A higher value (e.g., 0.95) will lead to more diverse text, while a lower value (e.g., 0.5) will generate more focused and conservative text. (Default: 0.9)": "Funktioniert zusammen mit top-k. Ein höherer Wert (z.B. 0,95) führt zu vielfältigerem Text, während ein niedrigerer Wert (z.B. 0,5) fokussierteren und konservativeren Text erzeugt. (Standard: 0,9)",
"Workspace": "Arbeitsbereich", "Workspace": "Arbeitsbereich",
"Workspace Permissions": "", "Workspace Permissions": "Arbeitsbereichsberechtigungen",
"Write a prompt suggestion (e.g. Who are you?)": "Schreiben Sie einen Promptvorschlag (z. B. Wer sind Sie?)", "Write a prompt suggestion (e.g. Who are you?)": "Schreiben Sie einen Promptvorschlag (z. B. Wer sind Sie?)",
"Write a summary in 50 words that summarizes [topic or keyword].": "Schreibe eine kurze Zusammenfassung in 50 Wörtern, die [Thema oder Schlüsselwort] zusammenfasst.", "Write a summary in 50 words that summarizes [topic or keyword].": "Schreibe eine kurze Zusammenfassung in 50 Wörtern, die [Thema oder Schlüsselwort] zusammenfasst.",
"Write something...": "Schreiben Sie etwas...", "Write something...": "Schreiben Sie etwas...",
"Write your model template content here": "", "Write your model template content here": "Schreiben Sie hier Ihren Modellvorlageninhalt",
"Yesterday": "Gestern", "Yesterday": "Gestern",
"You": "Sie", "You": "Sie",
"You can only chat with a maximum of {{maxCount}} file(s) at a time.": "Sie können nur mit maximal {{maxCount}} Datei(en) gleichzeitig chatten.", "You can only chat with a maximum of {{maxCount}} file(s) at a time.": "Sie können nur mit maximal {{maxCount}} Datei(en) gleichzeitig chatten.",
"You can personalize your interactions with LLMs by adding memories through the 'Manage' button below, making them more helpful and tailored to you.": "Personalisieren Sie Interaktionen mit LLMs, indem Sie über die Schaltfläche \"Verwalten\" Erinnerungen hinzufügen.", "You can personalize your interactions with LLMs by adding memories through the 'Manage' button below, making them more helpful and tailored to you.": "Personalisieren Sie Interaktionen mit LLMs, indem Sie über die Schaltfläche \"Verwalten\" Erinnerungen hinzufügen.",
"You cannot upload an empty file.": "Sie können keine leere Datei hochladen.", "You cannot upload an empty file.": "Sie können keine leere Datei hochladen.",
"You do not have permission to upload files.": "", "You do not have permission to upload files.": "Sie haben keine Berechtigung zum Hochladen von Dateien.",
"You have no archived conversations.": "Du hast keine archivierten Unterhaltungen.", "You have no archived conversations.": "Du hast keine archivierten Unterhaltungen.",
"You have shared this chat": "Sie haben diese Unterhaltung geteilt", "You have shared this chat": "Sie haben diese Unterhaltung geteilt",
"You're a helpful assistant.": "Du bist ein hilfreicher Assistent.", "You're a helpful assistant.": "Du bist ein hilfreicher Assistent.",
+25 -3
View File
@@ -1,4 +1,5 @@
{ {
"-1 for no limit, or a positive integer for a specific limit": "",
"'s', 'm', 'h', 'd', 'w' or '-1' for no expiration.": "'s', 'm', 'h', 'd', 'w' or '-1' for no expire. Much permanent, very wow.", "'s', 'm', 'h', 'd', 'w' or '-1' for no expiration.": "'s', 'm', 'h', 'd', 'w' or '-1' for no expire. Much permanent, very wow.",
"(e.g. `sh webui.sh --api --api-auth username_password`)": "", "(e.g. `sh webui.sh --api --api-auth username_password`)": "",
"(e.g. `sh webui.sh --api`)": "(such e.g. `sh webui.sh --api`)", "(e.g. `sh webui.sh --api`)": "(such e.g. `sh webui.sh --api`)",
@@ -59,6 +60,7 @@
"Allow Voice Interruption in Call": "", "Allow Voice Interruption in Call": "",
"Already have an account?": "Such account exists?", "Already have an account?": "Such account exists?",
"Alternative to the top_p, and aims to ensure a balance of quality and variety. The parameter p represents the minimum probability for a token to be considered, relative to the probability of the most likely token. For example, with p=0.05 and the most likely token having a probability of 0.9, logits with a value less than 0.045 are filtered out. (Default: 0.0)": "", "Alternative to the top_p, and aims to ensure a balance of quality and variety. The parameter p represents the minimum probability for a token to be considered, relative to the probability of the most likely token. For example, with p=0.05 and the most likely token having a probability of 0.9, logits with a value less than 0.045 are filtered out. (Default: 0.0)": "",
"Amazing": "",
"an assistant": "such assistant", "an assistant": "such assistant",
"and": "and", "and": "and",
"and {{COUNT}} more": "", "and {{COUNT}} more": "",
@@ -89,12 +91,15 @@
"Authenticate": "", "Authenticate": "",
"Auto-Copy Response to Clipboard": "Copy Bark Auto Bark", "Auto-Copy Response to Clipboard": "Copy Bark Auto Bark",
"Auto-playback response": "Auto-playback response", "Auto-playback response": "Auto-playback response",
"Autocomplete Generation": "",
"Autocomplete Generation Input Max Length": "",
"Automatic1111": "", "Automatic1111": "",
"AUTOMATIC1111 Api Auth String": "", "AUTOMATIC1111 Api Auth String": "",
"AUTOMATIC1111 Base URL": "AUTOMATIC1111 Base URL", "AUTOMATIC1111 Base URL": "AUTOMATIC1111 Base URL",
"AUTOMATIC1111 Base URL is required.": "AUTOMATIC1111 Base URL is required.", "AUTOMATIC1111 Base URL is required.": "AUTOMATIC1111 Base URL is required.",
"Available list": "", "Available list": "",
"available!": "available! So excite!", "available!": "available! So excite!",
"Awful": "",
"Azure AI Speech": "", "Azure AI Speech": "",
"Azure Region": "", "Azure Region": "",
"Back": "Back", "Back": "Back",
@@ -117,6 +122,7 @@
"Certificate Path": "", "Certificate Path": "",
"Change Password": "Change Password", "Change Password": "Change Password",
"Character": "", "Character": "",
"Character limit for autocomplete generation input": "",
"Chart new frontiers": "", "Chart new frontiers": "",
"Chat": "Chat", "Chat": "Chat",
"Chat Background Image": "", "Chat Background Image": "",
@@ -165,6 +171,7 @@
"Completions": "", "Completions": "",
"Concurrent Requests": "", "Concurrent Requests": "",
"Configure": "", "Configure": "",
"Configure Models": "",
"Confirm": "", "Confirm": "",
"Confirm Password": "Confirm Password", "Confirm Password": "Confirm Password",
"Confirm your action": "", "Confirm your action": "",
@@ -213,6 +220,7 @@
"Default (SentenceTransformers)": "", "Default (SentenceTransformers)": "",
"Default Model": "", "Default Model": "",
"Default model updated": "Default model much updated", "Default model updated": "Default model much updated",
"Default Models": "",
"Default permissions": "", "Default permissions": "",
"Default permissions updated successfully": "", "Default permissions updated successfully": "",
"Default Prompt Suggestions": "Default Prompt Suggestions", "Default Prompt Suggestions": "Default Prompt Suggestions",
@@ -234,6 +242,7 @@
"Delete User": "", "Delete User": "",
"Deleted {{deleteModelTag}}": "Deleted {{deleteModelTag}}", "Deleted {{deleteModelTag}}": "Deleted {{deleteModelTag}}",
"Deleted {{name}}": "", "Deleted {{name}}": "",
"Deleted User": "",
"Describe your knowledge base and objectives": "", "Describe your knowledge base and objectives": "",
"Description": "Description", "Description": "Description",
"Didn't fully follow instructions": "", "Didn't fully follow instructions": "",
@@ -292,16 +301,14 @@
"Embedding Model Engine": "", "Embedding Model Engine": "",
"Embedding model set to \"{{embedding_model}}\"": "", "Embedding model set to \"{{embedding_model}}\"": "",
"Enable API Key Auth": "", "Enable API Key Auth": "",
"Enable autocomplete generation for chat messages": "",
"Enable Community Sharing": "", "Enable Community Sharing": "",
"Enable Memory Locking (mlock) to prevent model data from being swapped out of RAM. This option locks the model's working set of pages into RAM, ensuring that they will not be swapped out to disk. This can help maintain performance by avoiding page faults and ensuring fast data access.": "", "Enable Memory Locking (mlock) to prevent model data from being swapped out of RAM. This option locks the model's working set of pages into RAM, ensuring that they will not be swapped out to disk. This can help maintain performance by avoiding page faults and ensuring fast data access.": "",
"Enable Memory Mapping (mmap) to load model data. This option allows the system to use disk storage as an extension of RAM by treating disk files as if they were in RAM. This can improve model performance by allowing for faster data access. However, it may not work correctly with all systems and can consume a significant amount of disk space.": "", "Enable Memory Mapping (mmap) to load model data. This option allows the system to use disk storage as an extension of RAM by treating disk files as if they were in RAM. This can improve model performance by allowing for faster data access. However, it may not work correctly with all systems and can consume a significant amount of disk space.": "",
"Enable Message Rating": "", "Enable Message Rating": "",
"Enable Mirostat sampling for controlling perplexity. (Default: 0, 0 = Disabled, 1 = Mirostat, 2 = Mirostat 2.0)": "", "Enable Mirostat sampling for controlling perplexity. (Default: 0, 0 = Disabled, 1 = Mirostat, 2 = Mirostat 2.0)": "",
"Enable New Sign Ups": "Enable New Bark Ups", "Enable New Sign Ups": "Enable New Bark Ups",
"Enable Retrieval Query Generation": "",
"Enable Tags Generation": "",
"Enable Web Search": "", "Enable Web Search": "",
"Enable Web Search Query Generation": "",
"Enabled": "", "Enabled": "",
"Engine": "", "Engine": "",
"Ensure your CSV file includes 4 columns in this order: Name, Email, Password, Role.": "", "Ensure your CSV file includes 4 columns in this order: Name, Email, Password, Role.": "",
@@ -326,7 +333,9 @@
"Enter language codes": "", "Enter language codes": "",
"Enter Model ID": "", "Enter Model ID": "",
"Enter model tag (e.g. {{modelTag}})": "Enter model doge tag (e.g. {{modelTag}})", "Enter model tag (e.g. {{modelTag}})": "Enter model doge tag (e.g. {{modelTag}})",
"Enter Mojeek Search API Key": "",
"Enter Number of Steps (e.g. 50)": "Enter Number of Steps (e.g. 50)", "Enter Number of Steps (e.g. 50)": "Enter Number of Steps (e.g. 50)",
"Enter proxy URL (e.g. https://user:password@host:port)": "",
"Enter Sampler (e.g. Euler a)": "", "Enter Sampler (e.g. Euler a)": "",
"Enter Scheduler (e.g. Karras)": "", "Enter Scheduler (e.g. Karras)": "",
"Enter Score": "", "Enter Score": "",
@@ -379,6 +388,7 @@
"Failed to add file.": "", "Failed to add file.": "",
"Failed to create API Key.": "", "Failed to create API Key.": "",
"Failed to read clipboard contents": "Failed to read clipboard borks", "Failed to read clipboard contents": "Failed to read clipboard borks",
"Failed to save models configuration": "",
"Failed to update settings": "", "Failed to update settings": "",
"Failed to upload file.": "", "Failed to upload file.": "",
"February": "", "February": "",
@@ -449,6 +459,7 @@
"Hide": "Hide", "Hide": "Hide",
"Host": "", "Host": "",
"How can I help you today?": "How can I halp u today?", "How can I help you today?": "How can I halp u today?",
"How would you rate this response?": "",
"Hybrid Search": "", "Hybrid Search": "",
"I acknowledge that I have read and I understand the implications of my action. I am aware of the risks associated with executing arbitrary code and I have verified the trustworthiness of the source.": "", "I acknowledge that I have read and I understand the implications of my action. I am aware of the risks associated with executing arbitrary code and I have verified the trustworthiness of the source.": "",
"ID": "", "ID": "",
@@ -565,6 +576,8 @@
"Modelfile Content": "Modelfile Content", "Modelfile Content": "Modelfile Content",
"Models": "Wowdels", "Models": "Wowdels",
"Models Access": "", "Models Access": "",
"Models configuration saved successfully": "",
"Mojeek Search API Key": "",
"more": "", "more": "",
"More": "", "More": "",
"Name": "Name", "Name": "Name",
@@ -583,6 +596,7 @@
"No knowledge found": "", "No knowledge found": "",
"No model IDs": "", "No model IDs": "",
"No models found": "", "No models found": "",
"No models selected": "",
"No results found": "", "No results found": "",
"No search query generated": "", "No search query generated": "",
"No source available": "No source available", "No source available": "No source available",
@@ -658,6 +672,7 @@
"Please carefully review the following warnings:": "", "Please carefully review the following warnings:": "",
"Please enter a prompt": "", "Please enter a prompt": "",
"Please fill in all fields.": "", "Please fill in all fields.": "",
"Please select a model first.": "",
"Please select a reason": "", "Please select a reason": "",
"Port": "", "Port": "",
"Positive attitude": "", "Positive attitude": "",
@@ -673,6 +688,7 @@
"Prompt updated successfully": "", "Prompt updated successfully": "",
"Prompts": "Promptos", "Prompts": "Promptos",
"Prompts Access": "", "Prompts Access": "",
"Proxy URL": "",
"Pull \"{{searchValue}}\" from Ollama.com": "", "Pull \"{{searchValue}}\" from Ollama.com": "",
"Pull a model from Ollama.com": "Pull a wowdel from Ollama.com", "Pull a model from Ollama.com": "Pull a wowdel from Ollama.com",
"Query Generation Prompt": "", "Query Generation Prompt": "",
@@ -693,17 +709,20 @@
"Remove": "", "Remove": "",
"Remove Model": "", "Remove Model": "",
"Rename": "", "Rename": "",
"Reorder Models": "",
"Repeat Last N": "Repeat Last N", "Repeat Last N": "Repeat Last N",
"Request Mode": "Request Bark", "Request Mode": "Request Bark",
"Reranking Model": "", "Reranking Model": "",
"Reranking model disabled": "", "Reranking model disabled": "",
"Reranking model set to \"{{reranking_model}}\"": "", "Reranking model set to \"{{reranking_model}}\"": "",
"Reset": "", "Reset": "",
"Reset All Models": "",
"Reset Upload Directory": "", "Reset Upload Directory": "",
"Reset Vector Storage/Knowledge": "", "Reset Vector Storage/Knowledge": "",
"Response notifications cannot be activated as the website permissions have been denied. Please visit your browser settings to grant the necessary access.": "", "Response notifications cannot be activated as the website permissions have been denied. Please visit your browser settings to grant the necessary access.": "",
"Response splitting": "", "Response splitting": "",
"Result": "", "Result": "",
"Retrieval Query Generation": "",
"Rich Text Input for Chat": "", "Rich Text Input for Chat": "",
"RK": "", "RK": "",
"Role": "Role", "Role": "Role",
@@ -827,6 +846,7 @@
"System": "System very system", "System": "System very system",
"System Instructions": "", "System Instructions": "",
"System Prompt": "System Prompt much prompt", "System Prompt": "System Prompt much prompt",
"Tags Generation": "",
"Tags Generation Prompt": "", "Tags Generation Prompt": "",
"Tail free sampling is used to reduce the impact of less probable tokens from the output. A higher value (e.g., 2.0) will reduce the impact more, while a value of 1.0 disables this setting. (default: 1)": "", "Tail free sampling is used to reduce the impact of less probable tokens from the output. A higher value (e.g., 2.0) will reduce the impact more, while a value of 1.0 disables this setting. (default: 1)": "",
"Tap to interrupt": "", "Tap to interrupt": "",
@@ -969,6 +989,7 @@
"Web Loader Settings": "", "Web Loader Settings": "",
"Web Search": "", "Web Search": "",
"Web Search Engine": "", "Web Search Engine": "",
"Web Search Query Generation": "",
"Webhook URL": "", "Webhook URL": "",
"WebUI Settings": "WebUI Settings much settings", "WebUI Settings": "WebUI Settings much settings",
"WebUI will make requests to \"{{url}}/api/chat\"": "", "WebUI will make requests to \"{{url}}/api/chat\"": "",
@@ -979,6 +1000,7 @@
"When enabled, the model will respond to each chat message in real-time, generating a response as soon as the user sends a message. This mode is useful for live chat applications, but may impact performance on slower hardware.": "", "When enabled, the model will respond to each chat message in real-time, generating a response as soon as the user sends a message. This mode is useful for live chat applications, but may impact performance on slower hardware.": "",
"wherever you are": "", "wherever you are": "",
"Whisper (Local)": "", "Whisper (Local)": "",
"Why?": "",
"Widescreen Mode": "", "Widescreen Mode": "",
"Won": "", "Won": "",
"Works together with top-k. A higher value (e.g., 0.95) will lead to more diverse text, while a lower value (e.g., 0.5) will generate more focused and conservative text. (Default: 0.9)": "", "Works together with top-k. A higher value (e.g., 0.95) will lead to more diverse text, while a lower value (e.g., 0.5) will generate more focused and conservative text. (Default: 0.9)": "",
File diff suppressed because it is too large Load Diff
+25 -3
View File
@@ -1,4 +1,5 @@
{ {
"-1 for no limit, or a positive integer for a specific limit": "",
"'s', 'm', 'h', 'd', 'w' or '-1' for no expiration.": "", "'s', 'm', 'h', 'd', 'w' or '-1' for no expiration.": "",
"(e.g. `sh webui.sh --api --api-auth username_password`)": "", "(e.g. `sh webui.sh --api --api-auth username_password`)": "",
"(e.g. `sh webui.sh --api`)": "", "(e.g. `sh webui.sh --api`)": "",
@@ -59,6 +60,7 @@
"Allow Voice Interruption in Call": "", "Allow Voice Interruption in Call": "",
"Already have an account?": "", "Already have an account?": "",
"Alternative to the top_p, and aims to ensure a balance of quality and variety. The parameter p represents the minimum probability for a token to be considered, relative to the probability of the most likely token. For example, with p=0.05 and the most likely token having a probability of 0.9, logits with a value less than 0.045 are filtered out. (Default: 0.0)": "", "Alternative to the top_p, and aims to ensure a balance of quality and variety. The parameter p represents the minimum probability for a token to be considered, relative to the probability of the most likely token. For example, with p=0.05 and the most likely token having a probability of 0.9, logits with a value less than 0.045 are filtered out. (Default: 0.0)": "",
"Amazing": "",
"an assistant": "", "an assistant": "",
"and": "", "and": "",
"and {{COUNT}} more": "", "and {{COUNT}} more": "",
@@ -89,12 +91,15 @@
"Authenticate": "", "Authenticate": "",
"Auto-Copy Response to Clipboard": "", "Auto-Copy Response to Clipboard": "",
"Auto-playback response": "", "Auto-playback response": "",
"Autocomplete Generation": "",
"Autocomplete Generation Input Max Length": "",
"Automatic1111": "", "Automatic1111": "",
"AUTOMATIC1111 Api Auth String": "", "AUTOMATIC1111 Api Auth String": "",
"AUTOMATIC1111 Base URL": "", "AUTOMATIC1111 Base URL": "",
"AUTOMATIC1111 Base URL is required.": "", "AUTOMATIC1111 Base URL is required.": "",
"Available list": "", "Available list": "",
"available!": "", "available!": "",
"Awful": "",
"Azure AI Speech": "", "Azure AI Speech": "",
"Azure Region": "", "Azure Region": "",
"Back": "", "Back": "",
@@ -117,6 +122,7 @@
"Certificate Path": "", "Certificate Path": "",
"Change Password": "", "Change Password": "",
"Character": "", "Character": "",
"Character limit for autocomplete generation input": "",
"Chart new frontiers": "", "Chart new frontiers": "",
"Chat": "", "Chat": "",
"Chat Background Image": "", "Chat Background Image": "",
@@ -165,6 +171,7 @@
"Completions": "", "Completions": "",
"Concurrent Requests": "", "Concurrent Requests": "",
"Configure": "", "Configure": "",
"Configure Models": "",
"Confirm": "", "Confirm": "",
"Confirm Password": "", "Confirm Password": "",
"Confirm your action": "", "Confirm your action": "",
@@ -213,6 +220,7 @@
"Default (SentenceTransformers)": "", "Default (SentenceTransformers)": "",
"Default Model": "", "Default Model": "",
"Default model updated": "", "Default model updated": "",
"Default Models": "",
"Default permissions": "", "Default permissions": "",
"Default permissions updated successfully": "", "Default permissions updated successfully": "",
"Default Prompt Suggestions": "", "Default Prompt Suggestions": "",
@@ -234,6 +242,7 @@
"Delete User": "", "Delete User": "",
"Deleted {{deleteModelTag}}": "", "Deleted {{deleteModelTag}}": "",
"Deleted {{name}}": "", "Deleted {{name}}": "",
"Deleted User": "",
"Describe your knowledge base and objectives": "", "Describe your knowledge base and objectives": "",
"Description": "", "Description": "",
"Didn't fully follow instructions": "", "Didn't fully follow instructions": "",
@@ -292,16 +301,14 @@
"Embedding Model Engine": "", "Embedding Model Engine": "",
"Embedding model set to \"{{embedding_model}}\"": "", "Embedding model set to \"{{embedding_model}}\"": "",
"Enable API Key Auth": "", "Enable API Key Auth": "",
"Enable autocomplete generation for chat messages": "",
"Enable Community Sharing": "", "Enable Community Sharing": "",
"Enable Memory Locking (mlock) to prevent model data from being swapped out of RAM. This option locks the model's working set of pages into RAM, ensuring that they will not be swapped out to disk. This can help maintain performance by avoiding page faults and ensuring fast data access.": "", "Enable Memory Locking (mlock) to prevent model data from being swapped out of RAM. This option locks the model's working set of pages into RAM, ensuring that they will not be swapped out to disk. This can help maintain performance by avoiding page faults and ensuring fast data access.": "",
"Enable Memory Mapping (mmap) to load model data. This option allows the system to use disk storage as an extension of RAM by treating disk files as if they were in RAM. This can improve model performance by allowing for faster data access. However, it may not work correctly with all systems and can consume a significant amount of disk space.": "", "Enable Memory Mapping (mmap) to load model data. This option allows the system to use disk storage as an extension of RAM by treating disk files as if they were in RAM. This can improve model performance by allowing for faster data access. However, it may not work correctly with all systems and can consume a significant amount of disk space.": "",
"Enable Message Rating": "", "Enable Message Rating": "",
"Enable Mirostat sampling for controlling perplexity. (Default: 0, 0 = Disabled, 1 = Mirostat, 2 = Mirostat 2.0)": "", "Enable Mirostat sampling for controlling perplexity. (Default: 0, 0 = Disabled, 1 = Mirostat, 2 = Mirostat 2.0)": "",
"Enable New Sign Ups": "", "Enable New Sign Ups": "",
"Enable Retrieval Query Generation": "",
"Enable Tags Generation": "",
"Enable Web Search": "", "Enable Web Search": "",
"Enable Web Search Query Generation": "",
"Enabled": "", "Enabled": "",
"Engine": "", "Engine": "",
"Ensure your CSV file includes 4 columns in this order: Name, Email, Password, Role.": "", "Ensure your CSV file includes 4 columns in this order: Name, Email, Password, Role.": "",
@@ -326,7 +333,9 @@
"Enter language codes": "", "Enter language codes": "",
"Enter Model ID": "", "Enter Model ID": "",
"Enter model tag (e.g. {{modelTag}})": "", "Enter model tag (e.g. {{modelTag}})": "",
"Enter Mojeek Search API Key": "",
"Enter Number of Steps (e.g. 50)": "", "Enter Number of Steps (e.g. 50)": "",
"Enter proxy URL (e.g. https://user:password@host:port)": "",
"Enter Sampler (e.g. Euler a)": "", "Enter Sampler (e.g. Euler a)": "",
"Enter Scheduler (e.g. Karras)": "", "Enter Scheduler (e.g. Karras)": "",
"Enter Score": "", "Enter Score": "",
@@ -379,6 +388,7 @@
"Failed to add file.": "", "Failed to add file.": "",
"Failed to create API Key.": "", "Failed to create API Key.": "",
"Failed to read clipboard contents": "", "Failed to read clipboard contents": "",
"Failed to save models configuration": "",
"Failed to update settings": "", "Failed to update settings": "",
"Failed to upload file.": "", "Failed to upload file.": "",
"February": "", "February": "",
@@ -449,6 +459,7 @@
"Hide": "", "Hide": "",
"Host": "", "Host": "",
"How can I help you today?": "", "How can I help you today?": "",
"How would you rate this response?": "",
"Hybrid Search": "", "Hybrid Search": "",
"I acknowledge that I have read and I understand the implications of my action. I am aware of the risks associated with executing arbitrary code and I have verified the trustworthiness of the source.": "", "I acknowledge that I have read and I understand the implications of my action. I am aware of the risks associated with executing arbitrary code and I have verified the trustworthiness of the source.": "",
"ID": "", "ID": "",
@@ -565,6 +576,8 @@
"Modelfile Content": "", "Modelfile Content": "",
"Models": "", "Models": "",
"Models Access": "", "Models Access": "",
"Models configuration saved successfully": "",
"Mojeek Search API Key": "",
"more": "", "more": "",
"More": "", "More": "",
"Name": "", "Name": "",
@@ -583,6 +596,7 @@
"No knowledge found": "", "No knowledge found": "",
"No model IDs": "", "No model IDs": "",
"No models found": "", "No models found": "",
"No models selected": "",
"No results found": "", "No results found": "",
"No search query generated": "", "No search query generated": "",
"No source available": "", "No source available": "",
@@ -658,6 +672,7 @@
"Please carefully review the following warnings:": "", "Please carefully review the following warnings:": "",
"Please enter a prompt": "", "Please enter a prompt": "",
"Please fill in all fields.": "", "Please fill in all fields.": "",
"Please select a model first.": "",
"Please select a reason": "", "Please select a reason": "",
"Port": "", "Port": "",
"Positive attitude": "", "Positive attitude": "",
@@ -673,6 +688,7 @@
"Prompt updated successfully": "", "Prompt updated successfully": "",
"Prompts": "", "Prompts": "",
"Prompts Access": "", "Prompts Access": "",
"Proxy URL": "",
"Pull \"{{searchValue}}\" from Ollama.com": "", "Pull \"{{searchValue}}\" from Ollama.com": "",
"Pull a model from Ollama.com": "", "Pull a model from Ollama.com": "",
"Query Generation Prompt": "", "Query Generation Prompt": "",
@@ -693,17 +709,20 @@
"Remove": "", "Remove": "",
"Remove Model": "", "Remove Model": "",
"Rename": "", "Rename": "",
"Reorder Models": "",
"Repeat Last N": "", "Repeat Last N": "",
"Request Mode": "", "Request Mode": "",
"Reranking Model": "", "Reranking Model": "",
"Reranking model disabled": "", "Reranking model disabled": "",
"Reranking model set to \"{{reranking_model}}\"": "", "Reranking model set to \"{{reranking_model}}\"": "",
"Reset": "", "Reset": "",
"Reset All Models": "",
"Reset Upload Directory": "", "Reset Upload Directory": "",
"Reset Vector Storage/Knowledge": "", "Reset Vector Storage/Knowledge": "",
"Response notifications cannot be activated as the website permissions have been denied. Please visit your browser settings to grant the necessary access.": "", "Response notifications cannot be activated as the website permissions have been denied. Please visit your browser settings to grant the necessary access.": "",
"Response splitting": "", "Response splitting": "",
"Result": "", "Result": "",
"Retrieval Query Generation": "",
"Rich Text Input for Chat": "", "Rich Text Input for Chat": "",
"RK": "", "RK": "",
"Role": "", "Role": "",
@@ -825,6 +844,7 @@
"System": "", "System": "",
"System Instructions": "", "System Instructions": "",
"System Prompt": "", "System Prompt": "",
"Tags Generation": "",
"Tags Generation Prompt": "", "Tags Generation Prompt": "",
"Tail free sampling is used to reduce the impact of less probable tokens from the output. A higher value (e.g., 2.0) will reduce the impact more, while a value of 1.0 disables this setting. (default: 1)": "", "Tail free sampling is used to reduce the impact of less probable tokens from the output. A higher value (e.g., 2.0) will reduce the impact more, while a value of 1.0 disables this setting. (default: 1)": "",
"Tap to interrupt": "", "Tap to interrupt": "",
@@ -967,6 +987,7 @@
"Web Loader Settings": "", "Web Loader Settings": "",
"Web Search": "", "Web Search": "",
"Web Search Engine": "", "Web Search Engine": "",
"Web Search Query Generation": "",
"Webhook URL": "", "Webhook URL": "",
"WebUI Settings": "", "WebUI Settings": "",
"WebUI will make requests to \"{{url}}/api/chat\"": "", "WebUI will make requests to \"{{url}}/api/chat\"": "",
@@ -977,6 +998,7 @@
"When enabled, the model will respond to each chat message in real-time, generating a response as soon as the user sends a message. This mode is useful for live chat applications, but may impact performance on slower hardware.": "", "When enabled, the model will respond to each chat message in real-time, generating a response as soon as the user sends a message. This mode is useful for live chat applications, but may impact performance on slower hardware.": "",
"wherever you are": "", "wherever you are": "",
"Whisper (Local)": "", "Whisper (Local)": "",
"Why?": "",
"Widescreen Mode": "", "Widescreen Mode": "",
"Won": "", "Won": "",
"Works together with top-k. A higher value (e.g., 0.95) will lead to more diverse text, while a lower value (e.g., 0.5) will generate more focused and conservative text. (Default: 0.9)": "", "Works together with top-k. A higher value (e.g., 0.95) will lead to more diverse text, while a lower value (e.g., 0.5) will generate more focused and conservative text. (Default: 0.9)": "",
+25 -3
View File
@@ -1,4 +1,5 @@
{ {
"-1 for no limit, or a positive integer for a specific limit": "",
"'s', 'm', 'h', 'd', 'w' or '-1' for no expiration.": "", "'s', 'm', 'h', 'd', 'w' or '-1' for no expiration.": "",
"(e.g. `sh webui.sh --api --api-auth username_password`)": "", "(e.g. `sh webui.sh --api --api-auth username_password`)": "",
"(e.g. `sh webui.sh --api`)": "", "(e.g. `sh webui.sh --api`)": "",
@@ -59,6 +60,7 @@
"Allow Voice Interruption in Call": "", "Allow Voice Interruption in Call": "",
"Already have an account?": "", "Already have an account?": "",
"Alternative to the top_p, and aims to ensure a balance of quality and variety. The parameter p represents the minimum probability for a token to be considered, relative to the probability of the most likely token. For example, with p=0.05 and the most likely token having a probability of 0.9, logits with a value less than 0.045 are filtered out. (Default: 0.0)": "", "Alternative to the top_p, and aims to ensure a balance of quality and variety. The parameter p represents the minimum probability for a token to be considered, relative to the probability of the most likely token. For example, with p=0.05 and the most likely token having a probability of 0.9, logits with a value less than 0.045 are filtered out. (Default: 0.0)": "",
"Amazing": "",
"an assistant": "", "an assistant": "",
"and": "", "and": "",
"and {{COUNT}} more": "", "and {{COUNT}} more": "",
@@ -89,12 +91,15 @@
"Authenticate": "", "Authenticate": "",
"Auto-Copy Response to Clipboard": "", "Auto-Copy Response to Clipboard": "",
"Auto-playback response": "", "Auto-playback response": "",
"Autocomplete Generation": "",
"Autocomplete Generation Input Max Length": "",
"Automatic1111": "", "Automatic1111": "",
"AUTOMATIC1111 Api Auth String": "", "AUTOMATIC1111 Api Auth String": "",
"AUTOMATIC1111 Base URL": "", "AUTOMATIC1111 Base URL": "",
"AUTOMATIC1111 Base URL is required.": "", "AUTOMATIC1111 Base URL is required.": "",
"Available list": "", "Available list": "",
"available!": "", "available!": "",
"Awful": "",
"Azure AI Speech": "", "Azure AI Speech": "",
"Azure Region": "", "Azure Region": "",
"Back": "", "Back": "",
@@ -117,6 +122,7 @@
"Certificate Path": "", "Certificate Path": "",
"Change Password": "", "Change Password": "",
"Character": "", "Character": "",
"Character limit for autocomplete generation input": "",
"Chart new frontiers": "", "Chart new frontiers": "",
"Chat": "", "Chat": "",
"Chat Background Image": "", "Chat Background Image": "",
@@ -165,6 +171,7 @@
"Completions": "", "Completions": "",
"Concurrent Requests": "", "Concurrent Requests": "",
"Configure": "", "Configure": "",
"Configure Models": "",
"Confirm": "", "Confirm": "",
"Confirm Password": "", "Confirm Password": "",
"Confirm your action": "", "Confirm your action": "",
@@ -213,6 +220,7 @@
"Default (SentenceTransformers)": "", "Default (SentenceTransformers)": "",
"Default Model": "", "Default Model": "",
"Default model updated": "", "Default model updated": "",
"Default Models": "",
"Default permissions": "", "Default permissions": "",
"Default permissions updated successfully": "", "Default permissions updated successfully": "",
"Default Prompt Suggestions": "", "Default Prompt Suggestions": "",
@@ -234,6 +242,7 @@
"Delete User": "", "Delete User": "",
"Deleted {{deleteModelTag}}": "", "Deleted {{deleteModelTag}}": "",
"Deleted {{name}}": "", "Deleted {{name}}": "",
"Deleted User": "",
"Describe your knowledge base and objectives": "", "Describe your knowledge base and objectives": "",
"Description": "", "Description": "",
"Didn't fully follow instructions": "", "Didn't fully follow instructions": "",
@@ -292,16 +301,14 @@
"Embedding Model Engine": "", "Embedding Model Engine": "",
"Embedding model set to \"{{embedding_model}}\"": "", "Embedding model set to \"{{embedding_model}}\"": "",
"Enable API Key Auth": "", "Enable API Key Auth": "",
"Enable autocomplete generation for chat messages": "",
"Enable Community Sharing": "", "Enable Community Sharing": "",
"Enable Memory Locking (mlock) to prevent model data from being swapped out of RAM. This option locks the model's working set of pages into RAM, ensuring that they will not be swapped out to disk. This can help maintain performance by avoiding page faults and ensuring fast data access.": "", "Enable Memory Locking (mlock) to prevent model data from being swapped out of RAM. This option locks the model's working set of pages into RAM, ensuring that they will not be swapped out to disk. This can help maintain performance by avoiding page faults and ensuring fast data access.": "",
"Enable Memory Mapping (mmap) to load model data. This option allows the system to use disk storage as an extension of RAM by treating disk files as if they were in RAM. This can improve model performance by allowing for faster data access. However, it may not work correctly with all systems and can consume a significant amount of disk space.": "", "Enable Memory Mapping (mmap) to load model data. This option allows the system to use disk storage as an extension of RAM by treating disk files as if they were in RAM. This can improve model performance by allowing for faster data access. However, it may not work correctly with all systems and can consume a significant amount of disk space.": "",
"Enable Message Rating": "", "Enable Message Rating": "",
"Enable Mirostat sampling for controlling perplexity. (Default: 0, 0 = Disabled, 1 = Mirostat, 2 = Mirostat 2.0)": "", "Enable Mirostat sampling for controlling perplexity. (Default: 0, 0 = Disabled, 1 = Mirostat, 2 = Mirostat 2.0)": "",
"Enable New Sign Ups": "", "Enable New Sign Ups": "",
"Enable Retrieval Query Generation": "",
"Enable Tags Generation": "",
"Enable Web Search": "", "Enable Web Search": "",
"Enable Web Search Query Generation": "",
"Enabled": "", "Enabled": "",
"Engine": "", "Engine": "",
"Ensure your CSV file includes 4 columns in this order: Name, Email, Password, Role.": "", "Ensure your CSV file includes 4 columns in this order: Name, Email, Password, Role.": "",
@@ -326,7 +333,9 @@
"Enter language codes": "", "Enter language codes": "",
"Enter Model ID": "", "Enter Model ID": "",
"Enter model tag (e.g. {{modelTag}})": "", "Enter model tag (e.g. {{modelTag}})": "",
"Enter Mojeek Search API Key": "",
"Enter Number of Steps (e.g. 50)": "", "Enter Number of Steps (e.g. 50)": "",
"Enter proxy URL (e.g. https://user:password@host:port)": "",
"Enter Sampler (e.g. Euler a)": "", "Enter Sampler (e.g. Euler a)": "",
"Enter Scheduler (e.g. Karras)": "", "Enter Scheduler (e.g. Karras)": "",
"Enter Score": "", "Enter Score": "",
@@ -379,6 +388,7 @@
"Failed to add file.": "", "Failed to add file.": "",
"Failed to create API Key.": "", "Failed to create API Key.": "",
"Failed to read clipboard contents": "", "Failed to read clipboard contents": "",
"Failed to save models configuration": "",
"Failed to update settings": "", "Failed to update settings": "",
"Failed to upload file.": "", "Failed to upload file.": "",
"February": "", "February": "",
@@ -449,6 +459,7 @@
"Hide": "", "Hide": "",
"Host": "", "Host": "",
"How can I help you today?": "", "How can I help you today?": "",
"How would you rate this response?": "",
"Hybrid Search": "", "Hybrid Search": "",
"I acknowledge that I have read and I understand the implications of my action. I am aware of the risks associated with executing arbitrary code and I have verified the trustworthiness of the source.": "", "I acknowledge that I have read and I understand the implications of my action. I am aware of the risks associated with executing arbitrary code and I have verified the trustworthiness of the source.": "",
"ID": "", "ID": "",
@@ -565,6 +576,8 @@
"Modelfile Content": "", "Modelfile Content": "",
"Models": "", "Models": "",
"Models Access": "", "Models Access": "",
"Models configuration saved successfully": "",
"Mojeek Search API Key": "",
"more": "", "more": "",
"More": "", "More": "",
"Name": "", "Name": "",
@@ -583,6 +596,7 @@
"No knowledge found": "", "No knowledge found": "",
"No model IDs": "", "No model IDs": "",
"No models found": "", "No models found": "",
"No models selected": "",
"No results found": "", "No results found": "",
"No search query generated": "", "No search query generated": "",
"No source available": "", "No source available": "",
@@ -658,6 +672,7 @@
"Please carefully review the following warnings:": "", "Please carefully review the following warnings:": "",
"Please enter a prompt": "", "Please enter a prompt": "",
"Please fill in all fields.": "", "Please fill in all fields.": "",
"Please select a model first.": "",
"Please select a reason": "", "Please select a reason": "",
"Port": "", "Port": "",
"Positive attitude": "", "Positive attitude": "",
@@ -673,6 +688,7 @@
"Prompt updated successfully": "", "Prompt updated successfully": "",
"Prompts": "", "Prompts": "",
"Prompts Access": "", "Prompts Access": "",
"Proxy URL": "",
"Pull \"{{searchValue}}\" from Ollama.com": "", "Pull \"{{searchValue}}\" from Ollama.com": "",
"Pull a model from Ollama.com": "", "Pull a model from Ollama.com": "",
"Query Generation Prompt": "", "Query Generation Prompt": "",
@@ -693,17 +709,20 @@
"Remove": "", "Remove": "",
"Remove Model": "", "Remove Model": "",
"Rename": "", "Rename": "",
"Reorder Models": "",
"Repeat Last N": "", "Repeat Last N": "",
"Request Mode": "", "Request Mode": "",
"Reranking Model": "", "Reranking Model": "",
"Reranking model disabled": "", "Reranking model disabled": "",
"Reranking model set to \"{{reranking_model}}\"": "", "Reranking model set to \"{{reranking_model}}\"": "",
"Reset": "", "Reset": "",
"Reset All Models": "",
"Reset Upload Directory": "", "Reset Upload Directory": "",
"Reset Vector Storage/Knowledge": "", "Reset Vector Storage/Knowledge": "",
"Response notifications cannot be activated as the website permissions have been denied. Please visit your browser settings to grant the necessary access.": "", "Response notifications cannot be activated as the website permissions have been denied. Please visit your browser settings to grant the necessary access.": "",
"Response splitting": "", "Response splitting": "",
"Result": "", "Result": "",
"Retrieval Query Generation": "",
"Rich Text Input for Chat": "", "Rich Text Input for Chat": "",
"RK": "", "RK": "",
"Role": "", "Role": "",
@@ -825,6 +844,7 @@
"System": "", "System": "",
"System Instructions": "", "System Instructions": "",
"System Prompt": "", "System Prompt": "",
"Tags Generation": "",
"Tags Generation Prompt": "", "Tags Generation Prompt": "",
"Tail free sampling is used to reduce the impact of less probable tokens from the output. A higher value (e.g., 2.0) will reduce the impact more, while a value of 1.0 disables this setting. (default: 1)": "", "Tail free sampling is used to reduce the impact of less probable tokens from the output. A higher value (e.g., 2.0) will reduce the impact more, while a value of 1.0 disables this setting. (default: 1)": "",
"Tap to interrupt": "", "Tap to interrupt": "",
@@ -967,6 +987,7 @@
"Web Loader Settings": "", "Web Loader Settings": "",
"Web Search": "", "Web Search": "",
"Web Search Engine": "", "Web Search Engine": "",
"Web Search Query Generation": "",
"Webhook URL": "", "Webhook URL": "",
"WebUI Settings": "", "WebUI Settings": "",
"WebUI will make requests to \"{{url}}/api/chat\"": "", "WebUI will make requests to \"{{url}}/api/chat\"": "",
@@ -977,6 +998,7 @@
"When enabled, the model will respond to each chat message in real-time, generating a response as soon as the user sends a message. This mode is useful for live chat applications, but may impact performance on slower hardware.": "", "When enabled, the model will respond to each chat message in real-time, generating a response as soon as the user sends a message. This mode is useful for live chat applications, but may impact performance on slower hardware.": "",
"wherever you are": "", "wherever you are": "",
"Whisper (Local)": "", "Whisper (Local)": "",
"Why?": "",
"Widescreen Mode": "", "Widescreen Mode": "",
"Won": "", "Won": "",
"Works together with top-k. A higher value (e.g., 0.95) will lead to more diverse text, while a lower value (e.g., 0.5) will generate more focused and conservative text. (Default: 0.9)": "", "Works together with top-k. A higher value (e.g., 0.95) will lead to more diverse text, while a lower value (e.g., 0.5) will generate more focused and conservative text. (Default: 0.9)": "",

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