From c6d80496abe7fb5d3a122332025334f04b5a989a Mon Sep 17 00:00:00 2001 From: silentoplayz Date: Sat, 23 Aug 2025 15:08:07 -0400 Subject: [PATCH 0001/1079] feat: improve ollama model management experience This commit introduces several improvements to the Ollama model management modal: - Adds a cancel button to the model pulling operation, using the existing 'x' button pattern. - Adds a cancel button to the "Update All" models operation, allowing the user to cancel the update for the currently processing model. - Cleans up toast notifications when updating all models. A single toast is now shown at the beginning and a summary toast at the end, preventing notification spam. - Refactors the `ManageOllama.svelte` component to support these new cancellation features. - Adds tooltips to all buttons in the modal to improve clarity. - Disables buttons when their corresponding input fields are empty to prevent accidental clicks. --- .../Models/Manage/ManageOllama.svelte | 374 +++++++++++------- 1 file changed, 225 insertions(+), 149 deletions(-) diff --git a/src/lib/components/admin/Settings/Models/Manage/ManageOllama.svelte b/src/lib/components/admin/Settings/Models/Manage/ManageOllama.svelte index fbfcf93677..29682a7a37 100644 --- a/src/lib/components/admin/Settings/Models/Manage/ManageOllama.svelte +++ b/src/lib/components/admin/Settings/Models/Manage/ManageOllama.svelte @@ -36,6 +36,8 @@ let updateModelId = null; let updateProgress = null; + let updateModelsControllers = {}; + let updateCancelled = false; let showExperimentalOllama = false; const MAX_PARALLEL_DOWNLOADS = 3; @@ -65,17 +67,31 @@ let deleteModelTag = ''; const updateModelsHandler = async () => { + updateCancelled = false; + toast.info('Checking for model updates...'); + for (const model of ollamaModels) { + if (updateCancelled) { + break; + } + console.debug(model); updateModelId = model.id; const [res, controller] = await pullModel(localStorage.token, model.id, urlIdx).catch( (error) => { - toast.error(`${error}`); - return null; + if (error.name !== 'AbortError') { + toast.error(`${error}`); + } + return [null, null]; } ); + updateModelsControllers = { + ...updateModelsControllers, + [model.id]: controller + }; + if (res) { const reader = res.body .pipeThrough(new TextDecoderStream()) @@ -108,19 +124,28 @@ } else { updateProgress = 100; } - } else { - toast.success(data.status); } } } } } catch (err) { - console.error(err); + if (err.name !== 'AbortError') { + console.error(err); + } + break; } } } + + delete updateModelsControllers[model.id]; + updateModelsControllers = { ...updateModelsControllers }; } + if (updateCancelled) { + toast.info('Model update cancelled'); + } else { + toast.success('All models are up to date'); + } updateModelId = null; updateProgress = null; }; @@ -143,10 +168,13 @@ return; } + modelTransferring = true; const [res, controller] = await pullModel(localStorage.token, sanitizedModelTag, urlIdx).catch( (error) => { - toast.error(`${error}`); - return null; + if (error.name !== 'AbortError') { + toast.error(`${error}`); + } + return [null, null]; } ); @@ -202,8 +230,6 @@ } }); } else { - toast.success(data.status); - MODEL_DOWNLOAD_POOL.set({ ...$MODEL_DOWNLOAD_POOL, [sanitizedModelTag]: { @@ -216,19 +242,23 @@ } } } catch (err) { - console.error(err); - if (typeof err !== 'string') { - err = err.message; - } + if (err.name !== 'AbortError') { + console.error(err); + if (typeof err !== 'string') { + err = err.message; + } - toast.error(`${err}`); - // opts.callback({ success: false, error, modelName: opts.modelName }); + toast.error(`${err}`); + // opts.callback({ success: false, error, modelName: opts.modelName }); + } else { + break; + } } } console.log($MODEL_DOWNLOAD_POOL[sanitizedModelTag]); - if ($MODEL_DOWNLOAD_POOL[sanitizedModelTag].done) { + if ($MODEL_DOWNLOAD_POOL[sanitizedModelTag]?.done) { toast.success( $i18n.t(`Model '{{modelName}}' has been successfully downloaded.`, { modelName: sanitizedModelTag @@ -425,6 +455,14 @@ ); }; + const cancelUpdateModelHandler = async (model: string) => { + const controller = updateModelsControllers[model]; + if (controller) { + controller.abort(); + updateCancelled = true; + } + }; + const cancelModelPullHandler = async (model: string) => { const { reader, abortController } = $MODEL_DOWNLOAD_POOL[model]; if (abortController) { @@ -437,7 +475,7 @@ ...$MODEL_DOWNLOAD_POOL }); await deleteModel(localStorage.token, model); - toast.success($i18n.t('{{model}} download has been canceled', { model: model })); + toast.success(`${model} download has been canceled`); } }; @@ -605,59 +643,61 @@ bind:value={modelTag} /> - + {/if} + +
@@ -670,8 +710,35 @@
{#if updateModelId} -
- Updating "{updateModelId}" {updateProgress ? `(${updateProgress}%)` : ''} +
+
Updating "{updateModelId}" {updateProgress ? `(${updateProgress}%)` : ''}
+ + + +
{/if} @@ -754,25 +821,28 @@ {/each}
- + + + + + @@ -799,27 +869,31 @@
- + + + + + +
@@ -936,57 +1010,59 @@ {#if (modelUploadMode === 'file' && modelInputFile && modelInputFile.length > 0) || (modelUploadMode === 'url' && modelFileUrl !== '')} - + {/if} + + {/if} From 8df74dde80271d7b0083e73677c90401910c5516 Mon Sep 17 00:00:00 2001 From: silentoplayz Date: Sat, 23 Aug 2025 15:19:35 -0400 Subject: [PATCH 0002/1079] fix --- .../components/admin/Settings/Models/Manage/ManageOllama.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/components/admin/Settings/Models/Manage/ManageOllama.svelte b/src/lib/components/admin/Settings/Models/Manage/ManageOllama.svelte index 29682a7a37..6c73b7a4a0 100644 --- a/src/lib/components/admin/Settings/Models/Manage/ManageOllama.svelte +++ b/src/lib/components/admin/Settings/Models/Manage/ManageOllama.svelte @@ -475,7 +475,7 @@ ...$MODEL_DOWNLOAD_POOL }); await deleteModel(localStorage.token, model); - toast.success(`${model} download has been canceled`); + toast.success($i18n.t('{{model}} download has been canceled', { model: model })); } }; From e214d59d109a11eb61bad1a58208d6ae89d66a5b Mon Sep 17 00:00:00 2001 From: Andrew Baek Date: Wed, 27 Aug 2025 01:04:27 +0900 Subject: [PATCH 0003/1079] Update groups.py fix issue #16870 --- backend/open_webui/models/groups.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/backend/open_webui/models/groups.py b/backend/open_webui/models/groups.py index 6615f95142..1c84f4c1ae 100644 --- a/backend/open_webui/models/groups.py +++ b/backend/open_webui/models/groups.py @@ -12,6 +12,7 @@ from open_webui.models.files import FileMetadataResponse from pydantic import BaseModel, ConfigDict from sqlalchemy import BigInteger, Column, String, Text, JSON, func +from sqlalchemy.ext.mutable import MutableList log = logging.getLogger(__name__) @@ -35,7 +36,7 @@ class Group(Base): meta = Column(JSON, nullable=True) permissions = Column(JSON, nullable=True) - user_ids = Column(JSON, nullable=True) + user_ids = Column(MutableList.as_mutable(JSON), nullable=True) created_at = Column(BigInteger) updated_at = Column(BigInteger) From ceaafbbfd21dc3ab766066a1b6253f05560361b1 Mon Sep 17 00:00:00 2001 From: Andrew Baek Date: Wed, 27 Aug 2025 03:22:34 +0900 Subject: [PATCH 0004/1079] Update groups.py --- backend/open_webui/models/groups.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/backend/open_webui/models/groups.py b/backend/open_webui/models/groups.py index 4c636b4f0f..a09b2b73f9 100644 --- a/backend/open_webui/models/groups.py +++ b/backend/open_webui/models/groups.py @@ -12,7 +12,6 @@ from open_webui.models.files import FileMetadataResponse from pydantic import BaseModel, ConfigDict from sqlalchemy import BigInteger, Column, String, Text, JSON, func -from sqlalchemy.ext.mutable import MutableList log = logging.getLogger(__name__) @@ -36,7 +35,7 @@ class Group(Base): meta = Column(JSON, nullable=True) permissions = Column(JSON, nullable=True) - user_ids = Column(MutableList.as_mutable(JSON), nullable=True) + user_ids = Column(JSON, nullable=True) created_at = Column(BigInteger) updated_at = Column(BigInteger) From ef5374a34e0d76ef787593b192c15755be8d52f9 Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Thu, 28 Aug 2025 14:46:47 +0400 Subject: [PATCH 0005/1079] typo --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 349b984e19..3a5663bb58 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,7 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - 🛂 **Granular Chat Interaction Permissions**: Added fine-grained permission controls for individual chat actions including "Continue Response", "Regenerate Response", "Rate Response", and "Delete Messages". Administrators can now configure these permissions per user group or set system defaults via environment variables, providing enhanced security and governance by preventing potential system prompt leakage through response continuation and enabling precise control over user interactions with AI responses. - 🧠 **Custom Reasoning Tags Configuration**: Added configurable reasoning tag detection for AI model responses, allowing administrators and users to customize how the system identifies and processes reasoning content. Users can now define custom reasoning tag pairs, use default tags like "think" and "reasoning", or disable reasoning detection entirely through the Advanced Parameters interface, providing enhanced control over AI thought process visibility. -- 📱 **Pull-to-Refresh SupportA**: Added pull-to-refresh functionality allowing user to easily refresh the interface by pulling down on the navbar area. This resolves timeout issues that occurred when temporarily switching away from the app during long AI response generations, eliminating the need to close and relaunch the PWA. +- 📱 **Pull-to-Refresh Support**: Added pull-to-refresh functionality allowing user to easily refresh the interface by pulling down on the navbar area. This resolves timeout issues that occurred when temporarily switching away from the app during long AI response generations, eliminating the need to close and relaunch the PWA. - 📁 **Configurable File Upload Processing Mode**: Added "process_in_background" query parameter to the file upload API endpoint, allowing clients to choose between asynchronous (default) and synchronous file processing. Setting "process_in_background=false" forces the upload request to wait until extraction and embedding complete, returning immediately usable files and simplifying integration for backend API consumers that prefer blocking calls over polling workflows. - 🔐 **Azure Document Intelligence DefaultAzureCredential Support**: Added support for authenticating with Azure Document Intelligence using DefaultAzureCredential in addition to API key authentication, enabling seamless integration with Azure Entra ID and managed identity authentication for enterprise Azure environments. - 🔐 **Authentication Bootstrapping Enhancements**: Added "ENABLE_INITIAL_ADMIN_SIGNUP" environment variable and "?form=true" URL parameter to enable initial admin user creation and forced login form display in SSO-only deployments. This resolves bootstrap issues where administrators couldn't create the first user when login forms were disabled, allowing proper initialization of SSO-configured deployments without requiring temporary configuration changes. From 0bca4e230ef276bec46889e3be036242ad11086f Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Thu, 28 Aug 2025 15:08:13 +0400 Subject: [PATCH 0006/1079] refac: rename tools to external tools for clarity --- src/lib/components/admin/Settings.svelte | 2 +- src/lib/components/chat/SettingsModal.svelte | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lib/components/admin/Settings.svelte b/src/lib/components/admin/Settings.svelte index d6a9e8a925..765a1d3ec9 100644 --- a/src/lib/components/admin/Settings.svelte +++ b/src/lib/components/admin/Settings.svelte @@ -204,7 +204,7 @@ /> -
{$i18n.t('Tools')}
+
{$i18n.t('External Tools')}
{/if} {:else if tabId === 'personalization'} From 898826dc22d90c4180ee466f378b6c3acefce72a Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Thu, 28 Aug 2025 19:16:50 +0400 Subject: [PATCH 0007/1079] Update dependabot.yml --- .github/dependabot.yml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index ed93957ea4..1c83fd305b 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -12,12 +12,6 @@ updates: interval: monthly target-branch: 'dev' - - package-ecosystem: npm - directory: '/' - schedule: - interval: monthly - target-branch: 'dev' - - package-ecosystem: 'github-actions' directory: '/' schedule: From be373e9fd42ac73b0302bdb487e16dbeae178b4e Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Thu, 28 Aug 2025 19:42:28 +0400 Subject: [PATCH 0008/1079] refac: dockerfile --- Dockerfile | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/Dockerfile b/Dockerfile index 9c982e69e2..88afd66c38 100644 --- a/Dockerfile +++ b/Dockerfile @@ -123,7 +123,6 @@ RUN apt-get update && \ COPY --chown=$UID:$GID ./backend/requirements.txt ./requirements.txt RUN pip3 install --no-cache-dir uv && \ - if [ "$USE_SLIM" != "true" ]; then \ if [ "$USE_CUDA" = "true" ]; then \ # If you use CUDA the whisper and embedding model will be downloaded on first use pip3 install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/$USE_CUDA_DOCKER_VER --no-cache-dir && \ @@ -134,17 +133,17 @@ RUN pip3 install --no-cache-dir uv && \ else \ pip3 install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cpu --no-cache-dir && \ uv pip install --system -r requirements.txt --no-cache-dir && \ + if [ "$USE_SLIM" != "true" ]; then \ python -c "import os; from sentence_transformers import SentenceTransformer; SentenceTransformer(os.environ['RAG_EMBEDDING_MODEL'], device='cpu')" && \ python -c "import os; from faster_whisper import WhisperModel; WhisperModel(os.environ['WHISPER_MODEL'], device='cpu', compute_type='int8', download_root=os.environ['WHISPER_MODEL_DIR'])"; \ python -c "import os; import tiktoken; tiktoken.get_encoding(os.environ['TIKTOKEN_ENCODING_NAME'])"; \ fi; \ - else \ - uv pip install --system -r requirements.txt --no-cache-dir; \ fi; \ - mkdir -p /app/backend/data && chown -R $UID:$GID /app/backend/data/ + mkdir -p /app/backend/data && chown -R $UID:$GID /app/backend/data/ && \ + rm -rf /var/lib/apt/lists/*; # Install Ollama if requested -RUN if [ "$USE_OLLAMA" = "true" ] && [ "$USE_SLIM" != "true" ]; then \ +RUN if [ "$USE_OLLAMA" = "true" ]; then \ date +%s > /tmp/ollama_build_hash && \ echo "Cache broken at timestamp: `cat /tmp/ollama_build_hash`" && \ curl -fsSL https://ollama.com/install.sh | sh && \ From 0ebe4f8f8490451ac8e85a4846f010854d9b54e5 Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Thu, 28 Aug 2025 20:19:47 +0400 Subject: [PATCH 0009/1079] refac: conditional USE_PERMISSION_HARDENING --- Dockerfile | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 88afd66c38..ad393338d8 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,6 +4,7 @@ ARG USE_CUDA=false ARG USE_OLLAMA=false ARG USE_SLIM=false +ARG USE_PERMISSION_HARDENING=false # Tested with cu117 for CUDA 11 and cu121 for CUDA 12 (default) ARG USE_CUDA_VER=cu128 # any sentence transformer model; models to use can be found at https://huggingface.co/models?library=sentence-transformers @@ -25,6 +26,9 @@ ARG GID=0 FROM --platform=$BUILDPLATFORM node:22-alpine3.20 AS build ARG BUILD_HASH +# Set Node.js options (heap limit Allocation failed - JavaScript heap out of memory) +# ENV NODE_OPTIONS="--max-old-space-size=4096" + WORKDIR /app # to store git revision in build @@ -45,6 +49,7 @@ ARG USE_CUDA ARG USE_OLLAMA ARG USE_CUDA_VER ARG USE_SLIM +ARG USE_PERMISSION_HARDENING ARG USE_EMBEDDING_MODEL ARG USE_RERANKING_MODEL ARG UID @@ -169,11 +174,13 @@ HEALTHCHECK CMD curl --silent --fail http://localhost:${PORT:-8080}/health | jq # Minimal, atomic permission hardening for OpenShift (arbitrary UID): # - Group 0 owns /app and /root # - Directories are group-writable and have SGID so new files inherit GID 0 -RUN set -eux; \ +RUN if [ "$USE_PERMISSION_HARDENING" = "true" ]; then \ + set -eux; \ chgrp -R 0 /app /root || true; \ chmod -R g+rwX /app /root || true; \ find /app -type d -exec chmod g+s {} + || true; \ - find /root -type d -exec chmod g+s {} + || true + find /root -type d -exec chmod g+s {} + || true; \ + fi USER $UID:$GID From 9d80cc3b2d39e5e986b53ba19d66379643e80645 Mon Sep 17 00:00:00 2001 From: Thomas Cooper Date: Thu, 28 Aug 2025 14:47:13 -0400 Subject: [PATCH 0010/1079] PKCE requires no secret, with no secret the login button does not ever show --- backend/open_webui/config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/open_webui/config.py b/backend/open_webui/config.py index 1fe031cdad..3cc522c26d 100644 --- a/backend/open_webui/config.py +++ b/backend/open_webui/config.py @@ -660,7 +660,7 @@ def load_oauth_providers(): if ( OAUTH_CLIENT_ID.value - and OAUTH_CLIENT_SECRET.value + and (OAUTH_CLIENT_SECRET.value or OAUTH_CODE_CHALLENGE_METHOD.value) and OPENID_PROVIDER_URL.value ): From d735b036fe4b3f05cf4eaf62249c277818fa8be5 Mon Sep 17 00:00:00 2001 From: Athanasios Oikonomou Date: Thu, 28 Aug 2025 22:19:25 +0300 Subject: [PATCH 0011/1079] fix: handle unicode filenames in external document loader MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Files with special characters in their names (e.g., ü.pdf) caused issues since HTTP headers only allow Latin-1 characters. This change URL-encodes `X-Filename` before adding it to request headers, preventing failures when uploading or processing such files. Fixes: #17000 --- backend/open_webui/retrieval/loaders/external_document.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/backend/open_webui/retrieval/loaders/external_document.py b/backend/open_webui/retrieval/loaders/external_document.py index c0ccd72432..1be2ca3f24 100644 --- a/backend/open_webui/retrieval/loaders/external_document.py +++ b/backend/open_webui/retrieval/loaders/external_document.py @@ -1,6 +1,7 @@ import requests import logging, os from typing import Iterator, List, Union +from urllib.parse import quote from langchain_core.document_loaders import BaseLoader from langchain_core.documents import Document @@ -37,7 +38,7 @@ class ExternalDocumentLoader(BaseLoader): headers["Authorization"] = f"Bearer {self.api_key}" try: - headers["X-Filename"] = os.path.basename(self.file_path) + headers["X-Filename"] = quote(os.path.basename(self.file_path)) except: pass From 3111d1bf610a96eef849bf0ac73352aa812c3f9f Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Fri, 29 Aug 2025 02:07:31 +0400 Subject: [PATCH 0012/1079] refac --- src/lib/components/chat/Chat.svelte | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/src/lib/components/chat/Chat.svelte b/src/lib/components/chat/Chat.svelte index 86d86a9ae4..284ddd7ad1 100644 --- a/src/lib/components/chat/Chat.svelte +++ b/src/lib/components/chat/Chat.svelte @@ -1396,10 +1396,10 @@ const submitPrompt = async (userPrompt, { _raw = false } = {}) => { console.log('submitPrompt', userPrompt, $chatId); - const messages = createMessagesList(history, history.currentId); const _selectedModels = selectedModels.map((modelId) => $models.map((m) => m.id).includes(modelId) ? modelId : '' ); + if (JSON.stringify(selectedModels) !== JSON.stringify(_selectedModels)) { selectedModels = _selectedModels; } @@ -1413,15 +1413,6 @@ return; } - if (messages.length != 0 && messages.at(-1).done != true) { - // Response not done - return; - } - if (messages.length != 0 && messages.at(-1).error && !messages.at(-1).content) { - // Error in response - toast.error($i18n.t(`Oops! There was an error in the previous response.`)); - return; - } if ( files.length > 0 && files.filter((file) => file.type !== 'image' && file.status === 'uploading').length > 0 @@ -1431,6 +1422,7 @@ ); return; } + if ( ($config?.file?.max_count ?? null) !== null && files.length + chatFiles.length > $config?.file?.max_count @@ -1443,9 +1435,25 @@ return; } + if (history?.currentId) { + const lastMessage = history.messages[history.currentId]; + if (lastMessage.done != true) { + // Response not done + return; + } + + if (lastMessage.error && !lastMessage.content) { + // Error in response + toast.error($i18n.t(`Oops! There was an error in the previous response.`)); + return; + } + } + messageInput?.setText(''); prompt = ''; + const messages = createMessagesList(history, history.currentId); + // Reset chat input textarea if (!($settings?.richTextInput ?? true)) { const chatInputElement = document.getElementById('chat-input'); From 1ca5ad47b12d780c5ccfd555eacbdcf97a9f794f Mon Sep 17 00:00:00 2001 From: joaoback <156559121+joaoback@users.noreply.github.com> Date: Thu, 28 Aug 2025 19:49:23 -0300 Subject: [PATCH 0013/1079] Update translation.json (pt-BR) Some minor translations improvements --- src/lib/i18n/locales/pt-BR/translation.json | 36 ++++++++++----------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/src/lib/i18n/locales/pt-BR/translation.json b/src/lib/i18n/locales/pt-BR/translation.json index 61417f27b0..f216814eb1 100644 --- a/src/lib/i18n/locales/pt-BR/translation.json +++ b/src/lib/i18n/locales/pt-BR/translation.json @@ -78,15 +78,15 @@ "Allow Chat Delete": "Permitir Exclusão de Chats", "Allow Chat Deletion": "Permitir Exclusão de Chats", "Allow Chat Edit": "Permitir Edição de Chats", - "Allow Chat Export": "Permitir exportação de chat", - "Allow Chat Params": "Permitir parâmetros de chat", - "Allow Chat Share": "Permitir compartilhamento de chat", - "Allow Chat System Prompt": "Permitir prompt do sistema de chat", - "Allow Chat Valves": "Permitir válvulas de chat", - "Allow Continue Response": "Permitir resposta contínua", - "Allow Delete Messages": "Permitir exclusão de mensagens", + "Allow Chat Export": "Permitir Exportação de Chat", + "Allow Chat Params": "Permitir Parâmetros de Chat", + "Allow Chat Share": "Permitir Compartilhamento de Chat", + "Allow Chat System Prompt": "Permitir Prompt do Sistema no Chat", + "Allow Chat Valves": "Permitir Configurações de Chat", + "Allow Continue Response": "Permitir Resposta Contínua", + "Allow Delete Messages": "Permitir Exclusão de Mensagens", "Allow File Upload": "Permitir Envio de arquivos", - "Allow Multiple Models in Chat": "Permitir vários modelos no chat", + "Allow Multiple Models in Chat": "Permitir Vários Modelos no Chat", "Allow non-local voices": "Permitir vozes não locais", "Allow Rate Response": "Permitir Avaliar Resposta", "Allow Regenerate Response": "Permitir Regenerar Resposta", @@ -123,7 +123,7 @@ "API Key Endpoint Restrictions": "Restrições de endpoint de chave de API", "API keys": "Chaves API", "API Version": "Versão da API", - "API Version is required": "", + "API Version is required": "Versão da API é obrigatória", "Application DN": "DN da Aplicação", "Application DN Password": "Senha da aplicação DN", "applies to all users with the \"user\" role": "Aplicar para todos com permissão de \"usuário\"", @@ -485,7 +485,7 @@ "Embedding Model Engine": "Motor do Modelo de Embedding", "Embedding model set to \"{{embedding_model}}\"": "Modelo de embedding definido para \"{{embedding_model}}\"", "Enable API Key": "Habilitar chave de API", - "Enable autocomplete generation for chat messages": "Habilitar geração de preenchimento automático para mensagens de bate-papo", + "Enable autocomplete generation for chat messages": "Habilitar geração de preenchimento automático para mensagens do chat", "Enable Code Execution": "Habilitar execução de código", "Enable Code Interpreter": "Habilitar intérprete de código", "Enable Community Sharing": "Ativar Compartilhamento com a Comunidade", @@ -715,7 +715,7 @@ "Folder updated successfully": "Pasta atualizada com sucesso", "Follow up": "Acompanhamento", "Follow Up Generation": "Geração de Acompanhamento", - "Follow Up Generation Prompt": "Prompt de Geração de Acompanhamento", + "Follow Up Generation Prompt": "Prompt para Geração dos Acompanhamentos", "Follow-Up Auto-Generation": "Geração automática de acompanhamento", "Followed instructions perfectly": "Seguiu as instruções perfeitamente", "Force OCR": "Forçar OCR", @@ -1124,7 +1124,7 @@ "Pipelines": "Pipelines", "Pipelines are a plugin system with arbitrary code execution —": "Pipelines é um sistema de plugins com execução arbitrária de código —", "Pipelines Not Detected": "Pipelines Não Detectados", - "Pipelines Valves": "Válvulas de Pipelines", + "Pipelines Valves": "Configurações de Pipelines", "Plain text (.md)": "Texto simples (.md)", "Plain text (.txt)": "Texto simples (.txt)", "Playground": "Playground", @@ -1175,7 +1175,7 @@ "Read Aloud": "Ler em Voz Alta", "Reason": "Razão", "Reasoning Effort": "Esforço de raciocínio", - "Reasoning Tags": "", + "Reasoning Tags": "Tags de raciocínio", "Record": "Registro", "Record voice": "Gravar voz", "Redirecting you to Open WebUI Community": "Redirecionando você para a Comunidade OpenWebUI", @@ -1239,7 +1239,7 @@ "Search": "Pesquisar", "Search a model": "Pesquisar um modelo", "Search all emojis": "Pesquisar todos os emojis", - "Search Base": "Pesquisar base", + "Search Base": "Pesquisar Base", "Search Chats": "Pesquisar Chats", "Search Collection": "Pesquisar Coleção", "Search Filters": "Pesquisar Filtros", @@ -1252,7 +1252,7 @@ "Search In Models": "Pesquisar em modelos", "Search Knowledge": "Pesquisar Conhecimento", "Search Models": "Pesquisar Modelos", - "Search Notes": "Pesquisaar Notas", + "Search Notes": "Pesquisar Notas", "Search options": "Opções de pesquisa", "Search Prompts": "Prompts de Pesquisa", "Search Result Count": "Contagem de Resultados da Pesquisa", @@ -1572,9 +1572,9 @@ "Using the default arena model with all models. Click the plus button to add custom models.": "Usando a arena de modelos padrão para todos os modelos. Clique no botão mais para adicionar modelos personalizados.", "Valid time units:": "Unidades de tempo válidas:", "Validate certificate": "Validar certificado", - "Valves": "Válvulas", - "Valves updated": "Válvulas atualizadas", - "Valves updated successfully": "Válvulas atualizadas com sucesso", + "Valves": "Configurações", + "Valves updated": "Configurações atualizadas", + "Valves updated successfully": "Configurações atualizadas com sucesso", "variable": "variável", "Verify Connection": "Verificar conexão", "Verify SSL Certificate": "Verificar certificado SSL", From 1d1a83b75455b2c0a75f7faad5fea8b4c4a29522 Mon Sep 17 00:00:00 2001 From: Shirasawa <764798966@qq.com> Date: Fri, 29 Aug 2025 03:49:15 +0000 Subject: [PATCH 0014/1079] i18n: improve zh-CN translation --- src/lib/i18n/locales/zh-CN/translation.json | 12 ++++++------ src/lib/i18n/locales/zh-TW/translation.json | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/lib/i18n/locales/zh-CN/translation.json b/src/lib/i18n/locales/zh-CN/translation.json index 4dfcf8fe2a..a1bb51fa3e 100644 --- a/src/lib/i18n/locales/zh-CN/translation.json +++ b/src/lib/i18n/locales/zh-CN/translation.json @@ -11,7 +11,7 @@ "{{ models }}": "{{ models }}", "{{COUNT}} Available Tools": "{{COUNT}} 个可用工具", "{{COUNT}} characters": "{{COUNT}} 个字符", - "{{COUNT}} extracted lines": "", + "{{COUNT}} extracted lines": "已提取 {{COUNT}} 行", "{{COUNT}} hidden lines": "{{COUNT}} 行被隐藏", "{{COUNT}} Replies": "{{COUNT}} 条回复", "{{COUNT}} words": "{{COUNT}} 个词", @@ -494,9 +494,9 @@ "Enable Message Rating": "启用回复评价", "Enable Mirostat sampling for controlling perplexity.": "启用 Mirostat 采样以控制困惑度", "Enable New Sign Ups": "允许新用户注册", - "Enable, disable, or customize the reasoning tags used by the model. \"Enabled\" uses default tags, \"Disabled\" turns off reasoning tags, and \"Custom\" lets you specify your own start and end tags.": "", + "Enable, disable, or customize the reasoning tags used by the model. \"Enabled\" uses default tags, \"Disabled\" turns off reasoning tags, and \"Custom\" lets you specify your own start and end tags.": "启用、禁用或自定义模型的推理过程标签。“启用”将使用默认的推理过程标签,“禁用”则表示不识别推理过程标签,“自定义”允许您指定模型推理过程的起始和结束标签。", "Enabled": "已启用", - "End Tag": "", + "End Tag": "结束标签", "Endpoint URL": "端点 URL", "Enforce Temporary Chat": "强制临时对话", "Enhance": "润色", @@ -725,7 +725,7 @@ "Format Lines": "行内容格式化", "Format the lines in the output. Defaults to False. If set to True, the lines will be formatted to detect inline math and styles.": "对输出中的文本行进行格式处理。默认为 False。设置为 True 时,将会格式化这些文本行,以检测并识别行内数学公式和样式。", "Format your variables using brackets like this:": "使用括号格式化您的变量,如下所示:", - "Formatting may be inconsistent from source.": "", + "Formatting may be inconsistent from source.": "格式可能会与原始文件不完全一致。", "Forwards system user session credentials to authenticate": "转发系统用户 session 凭证以进行身份验证", "Full Context Mode": "完整上下文模式", "Function": "函数", @@ -1175,7 +1175,7 @@ "Read Aloud": "朗读", "Reason": "原因", "Reasoning Effort": "推理努力 (Reasoning Effort)", - "Reasoning Tags": "", + "Reasoning Tags": "推理过程标签", "Record": "录制", "Record voice": "录音", "Redirecting you to Open WebUI Community": "正在将您重定向到 Open WebUI 社区", @@ -1376,7 +1376,7 @@ "Speech-to-Text": "语音转文本", "Speech-to-Text Engine": "语音转文本引擎", "Start of the channel": "频道起点", - "Start Tag": "", + "Start Tag": "起始标签", "STDOUT/STDERR": "标准输出/标准错误", "Stop": "停止", "Stop Generating": "停止生成", diff --git a/src/lib/i18n/locales/zh-TW/translation.json b/src/lib/i18n/locales/zh-TW/translation.json index 096b44a82d..2ecf6b7953 100644 --- a/src/lib/i18n/locales/zh-TW/translation.json +++ b/src/lib/i18n/locales/zh-TW/translation.json @@ -11,7 +11,7 @@ "{{ models }}": "{{ models }}", "{{COUNT}} Available Tools": "{{COUNT}} 個可用工具", "{{COUNT}} characters": "{{COUNT}} 個字元", - "{{COUNT}} extracted lines": "", + "{{COUNT}} extracted lines": "已擷取 {{COUNT}} 行", "{{COUNT}} hidden lines": "已隱藏 {{COUNT}} 行", "{{COUNT}} Replies": "{{COUNT}} 回覆", "{{COUNT}} words": "{{COUNT}} 個詞", @@ -494,9 +494,9 @@ "Enable Message Rating": "啟用訊息評分", "Enable Mirostat sampling for controlling perplexity.": "啟用 Mirostat 取樣以控制 perplexity。", "Enable New Sign Ups": "允許新使用者註冊", - "Enable, disable, or customize the reasoning tags used by the model. \"Enabled\" uses default tags, \"Disabled\" turns off reasoning tags, and \"Custom\" lets you specify your own start and end tags.": "", + "Enable, disable, or customize the reasoning tags used by the model. \"Enabled\" uses default tags, \"Disabled\" turns off reasoning tags, and \"Custom\" lets you specify your own start and end tags.": "啟用、停用或自訂模型的推理標籤。「啟用」將使用預設的推理標籤,「停用」則不使用推理標籤,「自訂」允許您指定模型推理過程的起始與結束標籤。", "Enabled": "已啟用", - "End Tag": "", + "End Tag": "結束標籤", "Endpoint URL": "端點 URL", "Enforce Temporary Chat": "強制使用臨時對話", "Enhance": "增強", @@ -725,7 +725,7 @@ "Format Lines": "行內容格式化", "Format the lines in the output. Defaults to False. If set to True, the lines will be formatted to detect inline math and styles.": "對輸出中的文字行進行格式處理。預設為 False。設定為 True 時,將會格式化這些文字行,以偵測並識別行內數學公式和樣式。", "Format your variables using brackets like this:": "使用方括號格式化您的變數,如下所示:", - "Formatting may be inconsistent from source.": "", + "Formatting may be inconsistent from source.": "可能與原始格式不完全一致。", "Forwards system user session credentials to authenticate": "轉發系統使用者 session 憑證以進行驗證", "Full Context Mode": "完整上下文模式", "Function": "函式", @@ -1175,7 +1175,7 @@ "Read Aloud": "大聲朗讀", "Reason": "原因", "Reasoning Effort": "推理程度", - "Reasoning Tags": "", + "Reasoning Tags": "推理標籤", "Record": "錄製", "Record voice": "錄音", "Redirecting you to Open WebUI Community": "正在將您重導向至 Open WebUI 社群", @@ -1376,7 +1376,7 @@ "Speech-to-Text": "語音轉文字 (STT) ", "Speech-to-Text Engine": "語音轉文字 (STT) 引擎", "Start of the channel": "頻道起點", - "Start Tag": "", + "Start Tag": "起始標籤", "STDOUT/STDERR": "STDOUT/STDERR", "Stop": "停止", "Stop Generating": "停止生成", From 32a303f392689c75f13d6f519c259cf7dc73b0d6 Mon Sep 17 00:00:00 2001 From: Aleix Dorca Date: Fri, 29 Aug 2025 11:59:09 +0200 Subject: [PATCH 0015/1079] Update catalan translation.json --- src/lib/i18n/locales/ca-ES/translation.json | 22 ++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/lib/i18n/locales/ca-ES/translation.json b/src/lib/i18n/locales/ca-ES/translation.json index 2a73c826b6..d83fd1c8c4 100644 --- a/src/lib/i18n/locales/ca-ES/translation.json +++ b/src/lib/i18n/locales/ca-ES/translation.json @@ -11,7 +11,7 @@ "{{ models }}": "{{ models }}", "{{COUNT}} Available Tools": "{{COUNT}} eines disponibles", "{{COUNT}} characters": "{{COUNT}} caràcters", - "{{COUNT}} extracted lines": "", + "{{COUNT}} extracted lines": "{{COUNT}} línies extretes", "{{COUNT}} hidden lines": "{{COUNT}} línies ocultes", "{{COUNT}} Replies": "{{COUNT}} respostes", "{{COUNT}} words": "{{COUNT}} paraules", @@ -83,13 +83,13 @@ "Allow Chat Share": "Permetre compartir el xat", "Allow Chat System Prompt": "Permet la indicació de sistema al xat", "Allow Chat Valves": "Permetre Valves al xat", - "Allow Continue Response": "", - "Allow Delete Messages": "", + "Allow Continue Response": "Permetre continuar la resposta", + "Allow Delete Messages": "Permetre eliminar missatges", "Allow File Upload": "Permetre la pujada d'arxius", "Allow Multiple Models in Chat": "Permetre múltiple models al xat", "Allow non-local voices": "Permetre veus no locals", - "Allow Rate Response": "", - "Allow Regenerate Response": "", + "Allow Rate Response": "Permetre valorar les respostes", + "Allow Regenerate Response": "Permetre regenerar respostes", "Allow Speech to Text": "Permetre Parla a Text", "Allow Temporary Chat": "Permetre el xat temporal", "Allow Text to Speech": "Permetre Text a Parla", @@ -430,7 +430,7 @@ "Docling Server URL required.": "La URL del servidor Docling és necessària", "Document": "Document", "Document Intelligence": "Document Intelligence", - "Document Intelligence endpoint required.": "", + "Document Intelligence endpoint required.": "Es necessita un punt de connexió de Document Intelligence", "Documentation": "Documentació", "Documents": "Documents", "does not make any external connections, and your data stays securely on your locally hosted server.": "no realitza connexions externes, i les teves dades romanen segures al teu servidor allotjat localment.", @@ -494,9 +494,9 @@ "Enable Message Rating": "Permetre la qualificació de missatges", "Enable Mirostat sampling for controlling perplexity.": "Permetre el mostreig de Mirostat per controlar la perplexitat", "Enable New Sign Ups": "Permetre nous registres", - "Enable, disable, or customize the reasoning tags used by the model. \"Enabled\" uses default tags, \"Disabled\" turns off reasoning tags, and \"Custom\" lets you specify your own start and end tags.": "", + "Enable, disable, or customize the reasoning tags used by the model. \"Enabled\" uses default tags, \"Disabled\" turns off reasoning tags, and \"Custom\" lets you specify your own start and end tags.": "Activar, desactivar o personalitzar les etiquetes de raonament que utilitza el model. \"Activat\" utilitza etiquetes predeterminades, \"Desactivat\" desactiva les etiquetes de raonament i \"Personalitzat\" permet especificar les etiquetes d'inici i finalització.", "Enabled": "Habilitat", - "End Tag": "", + "End Tag": "Etiqueta de finalització", "Endpoint URL": "URL de connexió", "Enforce Temporary Chat": "Forçar els xats temporals", "Enhance": "Millorar", @@ -725,7 +725,7 @@ "Format Lines": "Formatar les línies", "Format the lines in the output. Defaults to False. If set to True, the lines will be formatted to detect inline math and styles.": "Formata les línies a la sortida. Per defecte, és Fals. Si es defineix com a Cert, les línies es formataran per detectar matemàtiques i estils en línia.", "Format your variables using brackets like this:": "Formata les teves variables utilitzant claudàtors així:", - "Formatting may be inconsistent from source.": "", + "Formatting may be inconsistent from source.": "La formatació pot ser inconsistent amb l'origen", "Forwards system user session credentials to authenticate": "Envia les credencials de l'usuari del sistema per autenticar", "Full Context Mode": "Mode de context complert", "Function": "Funció", @@ -1175,7 +1175,7 @@ "Read Aloud": "Llegir en veu alta", "Reason": "Raó", "Reasoning Effort": "Esforç de raonament", - "Reasoning Tags": "", + "Reasoning Tags": "Etiqueta de raonament", "Record": "Enregistrar", "Record voice": "Enregistrar la veu", "Redirecting you to Open WebUI Community": "Redirigint-te a la comunitat OpenWebUI", @@ -1376,7 +1376,7 @@ "Speech-to-Text": "Àudio-a-Text", "Speech-to-Text Engine": "Motor de veu a text", "Start of the channel": "Inici del canal", - "Start Tag": "", + "Start Tag": "Etiqueta d'inici", "STDOUT/STDERR": "STDOUT/STDERR", "Stop": "Atura", "Stop Generating": "Atura la generació", From 292cb62d4af2ed34e11c866c8d45e1a25ef2057d Mon Sep 17 00:00:00 2001 From: _00_ <131402327+rgaricano@users.noreply.github.com> Date: Sat, 30 Aug 2025 01:48:31 +0200 Subject: [PATCH 0016/1079] FIX: Hybrid Search lexical-semantic tags FIX Error in Hybrid Search lexical-semantic terms places I was reviewing and I noticed that the lexical-semantic terms are inverted. BM25 weight=1 --> lexical BM25 weight=0 --> semantic --- src/lib/components/admin/Settings/Documents.svelte | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/components/admin/Settings/Documents.svelte b/src/lib/components/admin/Settings/Documents.svelte index d3a244fa45..087a9bb950 100644 --- a/src/lib/components/admin/Settings/Documents.svelte +++ b/src/lib/components/admin/Settings/Documents.svelte @@ -1104,10 +1104,10 @@
- {$i18n.t('lexical')} + {$i18n.t('semantic')}
- {$i18n.t('semantic')} + {$i18n.t('lexical')}
From 647e38f701ff93bb40ed71ba445a8ba903518306 Mon Sep 17 00:00:00 2001 From: _00_ <131402327+rgaricano@users.noreply.github.com> Date: Sat, 30 Aug 2025 10:45:35 +0200 Subject: [PATCH 0017/1079] Revert bypass hybrid search when BM25_weight=0 Revert PR https://github.com/open-webui/open-webui/commit/74b1c801 --- backend/open_webui/retrieval/utils.py | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/backend/open_webui/retrieval/utils.py b/backend/open_webui/retrieval/utils.py index 100c92c6c0..856527083c 100644 --- a/backend/open_webui/retrieval/utils.py +++ b/backend/open_webui/retrieval/utils.py @@ -128,8 +128,6 @@ def query_doc_with_hybrid_search( log.warning(f"query_doc_with_hybrid_search:no_docs {collection_name}") return {"documents": [], "metadatas": [], "distances": []} - # BM_25 required only if weight is greater than 0 - if hybrid_bm25_weight > 0: log.debug(f"query_doc_with_hybrid_search:doc {collection_name}") bm25_retriever = BM25Retriever.from_texts( texts=collection_result.documents[0], @@ -343,8 +341,7 @@ def query_collection_with_hybrid_search( # Fetch collection data once per collection sequentially # Avoid fetching the same data multiple times later collection_results = {} - # Only retrieve entire collection if bm_25 calculation is required - if hybrid_bm25_weight > 0: + for collection_name in collection_names: try: log.debug( @@ -356,9 +353,7 @@ def query_collection_with_hybrid_search( except Exception as e: log.exception(f"Failed to fetch collection {collection_name}: {e}") collection_results[collection_name] = None - else: - for collection_name in collection_names: - collection_results[collection_name] = [] + log.info( f"Starting hybrid search for {len(queries)} queries in {len(collection_names)} collections..." ) From 562710fe33a71425cd6e14c132056f8bbf757da8 Mon Sep 17 00:00:00 2001 From: Adam Date: Sat, 30 Aug 2025 20:05:53 -0400 Subject: [PATCH 0018/1079] join the url instead of concatenating a string in case the user adds a slash to the end of their configured url. --- backend/open_webui/routers/audio.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/backend/open_webui/routers/audio.py b/backend/open_webui/routers/audio.py index cc5711569d..e1b08fa619 100644 --- a/backend/open_webui/routers/audio.py +++ b/backend/open_webui/routers/audio.py @@ -4,7 +4,7 @@ import logging import os import uuid from functools import lru_cache -from pathlib import Path + from pydub import AudioSegment from pydub.silence import split_on_silence from concurrent.futures import ThreadPoolExecutor @@ -15,7 +15,7 @@ import aiohttp import aiofiles import requests import mimetypes -from urllib.parse import quote +from urllib.parse import urljoin from fastapi import ( Depends, @@ -308,6 +308,7 @@ def load_speech_pipeline(request): @router.post("/speech") async def speech(request: Request, user=Depends(get_verified_user)): body = await request.body() + tts_model = request.app.state.config.TTS_MODEL name = hashlib.sha256( body + str(request.app.state.config.TTS_ENGINE).encode("utf-8") @@ -337,8 +338,9 @@ async def speech(request: Request, user=Depends(get_verified_user)): async with aiohttp.ClientSession( timeout=timeout, trust_env=True ) as session: + r = await session.post( - url=f"{request.app.state.config.TTS_OPENAI_API_BASE_URL}/audio/speech", + url=urljoin(request.app.state.config.TTS_OPENAI_API_BASE_URL, "/audio/speech"), json=payload, headers={ "Content-Type": "application/json", @@ -466,8 +468,7 @@ async def speech(request: Request, user=Depends(get_verified_user)): timeout=timeout, trust_env=True ) as session: async with session.post( - (base_url or f"https://{region}.tts.speech.microsoft.com") - + "/cognitiveservices/v1", + urljoin(base_url or f"https://{region}.tts.speech.microsoft.com", "/cognitiveservices/v1"), headers={ "Ocp-Apim-Subscription-Key": request.app.state.config.TTS_API_KEY, "Content-Type": "application/ssml+xml", From c62f30e22c50efa9846fe1fdd6ed1e11e8deebb7 Mon Sep 17 00:00:00 2001 From: Adam Date: Sat, 30 Aug 2025 20:12:46 -0400 Subject: [PATCH 0019/1079] remove whitespace --- backend/open_webui/routers/audio.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/backend/open_webui/routers/audio.py b/backend/open_webui/routers/audio.py index e1b08fa619..2f6703e1c9 100644 --- a/backend/open_webui/routers/audio.py +++ b/backend/open_webui/routers/audio.py @@ -4,7 +4,6 @@ import logging import os import uuid from functools import lru_cache - from pydub import AudioSegment from pydub.silence import split_on_silence from concurrent.futures import ThreadPoolExecutor @@ -338,7 +337,6 @@ async def speech(request: Request, user=Depends(get_verified_user)): async with aiohttp.ClientSession( timeout=timeout, trust_env=True ) as session: - r = await session.post( url=urljoin(request.app.state.config.TTS_OPENAI_API_BASE_URL, "/audio/speech"), json=payload, From 08b958cfc9607d1b76777b67ba1d92924127d07c Mon Sep 17 00:00:00 2001 From: Adam Date: Sat, 30 Aug 2025 20:29:16 -0400 Subject: [PATCH 0020/1079] re-add used var --- backend/open_webui/routers/audio.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/backend/open_webui/routers/audio.py b/backend/open_webui/routers/audio.py index 2f6703e1c9..b94f5dc60a 100644 --- a/backend/open_webui/routers/audio.py +++ b/backend/open_webui/routers/audio.py @@ -3,6 +3,7 @@ import json import logging import os import uuid + from functools import lru_cache from pydub import AudioSegment from pydub.silence import split_on_silence @@ -14,7 +15,7 @@ import aiohttp import aiofiles import requests import mimetypes -from urllib.parse import urljoin +from urllib.parse import urljoin, quote from fastapi import ( Depends, From 20b6902b9f0cc42dc0d81b654287ea873a7be724 Mon Sep 17 00:00:00 2001 From: Adam Date: Sat, 30 Aug 2025 20:30:34 -0400 Subject: [PATCH 0021/1079] whitespace --- backend/open_webui/routers/audio.py | 1 - 1 file changed, 1 deletion(-) diff --git a/backend/open_webui/routers/audio.py b/backend/open_webui/routers/audio.py index b94f5dc60a..7ee6fce3df 100644 --- a/backend/open_webui/routers/audio.py +++ b/backend/open_webui/routers/audio.py @@ -3,7 +3,6 @@ import json import logging import os import uuid - from functools import lru_cache from pydub import AudioSegment from pydub.silence import split_on_silence From 39ae9167ee3b79e7f68fe4563c02570cb9e77f29 Mon Sep 17 00:00:00 2001 From: Adam Date: Sat, 30 Aug 2025 20:31:13 -0400 Subject: [PATCH 0022/1079] removed test code. --- backend/open_webui/routers/audio.py | 1 - 1 file changed, 1 deletion(-) diff --git a/backend/open_webui/routers/audio.py b/backend/open_webui/routers/audio.py index 7ee6fce3df..f71be198af 100644 --- a/backend/open_webui/routers/audio.py +++ b/backend/open_webui/routers/audio.py @@ -307,7 +307,6 @@ def load_speech_pipeline(request): @router.post("/speech") async def speech(request: Request, user=Depends(get_verified_user)): body = await request.body() - tts_model = request.app.state.config.TTS_MODEL name = hashlib.sha256( body + str(request.app.state.config.TTS_ENGINE).encode("utf-8") From b50ae5bf694a070147ea770e27bb1c146dbfacdf Mon Sep 17 00:00:00 2001 From: Andrew Baek Date: Sun, 31 Aug 2025 14:48:37 +0900 Subject: [PATCH 0023/1079] Update MessageInput.svelte --- src/lib/components/chat/MessageInput.svelte | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lib/components/chat/MessageInput.svelte b/src/lib/components/chat/MessageInput.svelte index adea345ed0..407cb2262f 100644 --- a/src/lib/components/chat/MessageInput.svelte +++ b/src/lib/components/chat/MessageInput.svelte @@ -1051,6 +1051,7 @@
{#if files.length > 0}
From 48afd424a37414364da19ccfa5f31789137e7ff2 Mon Sep 17 00:00:00 2001 From: Hadad Date: Sun, 31 Aug 2025 14:33:32 +0700 Subject: [PATCH 0024/1079] fix: Resolve admin account creation on Hugging Face Spaces. Signed-off-by: Hadad --- backend/start.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/start.sh b/backend/start.sh index 9e106760c8..c32498aa45 100755 --- a/backend/start.sh +++ b/backend/start.sh @@ -53,12 +53,12 @@ if [ -n "$SPACE_ID" ]; then WEBUI_SECRET_KEY="$WEBUI_SECRET_KEY" uvicorn open_webui.main:app --host "$HOST" --port "$PORT" --forwarded-allow-ips '*' & webui_pid=$! echo "Waiting for webui to start..." - while ! curl -s http://localhost:8080/health > /dev/null; do + while ! curl -s "http://localhost:${PORT}/health" > /dev/null; do sleep 1 done echo "Creating admin user..." curl \ - -X POST "http://localhost:8080/api/v1/auths/signup" \ + -X POST "http://localhost:${PORT}/api/v1/auths/signup" \ -H "accept: application/json" \ -H "Content-Type: application/json" \ -d "{ \"email\": \"${ADMIN_USER_EMAIL}\", \"password\": \"${ADMIN_USER_PASSWORD}\", \"name\": \"Admin\" }" From 61f530ff4bb6ce3429fe28edc0d987d8a407391d Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Sun, 31 Aug 2025 23:06:58 +0400 Subject: [PATCH 0025/1079] refac: styling --- src/lib/components/common/Banner.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/components/common/Banner.svelte b/src/lib/components/common/Banner.svelte index a79b8b42c3..d135cb605a 100644 --- a/src/lib/components/common/Banner.svelte +++ b/src/lib/components/common/Banner.svelte @@ -46,7 +46,7 @@ {#if !dismissed} {#if mounted}
From b0f6f24ca80fb8cadea2fd58392cb375977edf19 Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Sun, 31 Aug 2025 23:42:34 +0400 Subject: [PATCH 0026/1079] refac --- backend/open_webui/utils/oauth.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/backend/open_webui/utils/oauth.py b/backend/open_webui/utils/oauth.py index 5ac189d48d..9385897f4c 100644 --- a/backend/open_webui/utils/oauth.py +++ b/backend/open_webui/utils/oauth.py @@ -553,6 +553,15 @@ class OAuthManager: ) if ENABLE_OAUTH_SIGNUP.value: + oauth_access_token = token.get("access_token") + response.set_cookie( + key="oauth_access_token", + value=oauth_access_token, + httponly=True, + samesite=WEBUI_AUTH_COOKIE_SAME_SITE, + secure=WEBUI_AUTH_COOKIE_SECURE, + ) + oauth_id_token = token.get("id_token") response.set_cookie( key="oauth_id_token", From e0ab5adb9794c264d2ae537aea74f37c449a6ab8 Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Sun, 31 Aug 2025 23:52:50 +0400 Subject: [PATCH 0027/1079] refac --- backend/open_webui/utils/middleware.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/backend/open_webui/utils/middleware.py b/backend/open_webui/utils/middleware.py index a298ebeb31..91a125aafb 100644 --- a/backend/open_webui/utils/middleware.py +++ b/backend/open_webui/utils/middleware.py @@ -1297,7 +1297,13 @@ async def process_chat_response( response_data = response if "error" in response_data: - error = response_data["error"].get("detail", response_data["error"]) + error = response_data.get("error") + + if isinstance(error, dict): + error = error.get("detail", error) + else: + error = str(error) + Chats.upsert_message_to_chat_by_id_and_message_id( metadata["chat_id"], metadata["message_id"], From c2b4976c82d335ed524bd80dc914b5e2f5bfbd9e Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Sun, 31 Aug 2025 23:58:18 +0400 Subject: [PATCH 0028/1079] enh: PGVECTOR_CREATE_EXTENSION env var --- .../retrieval/vector/dbs/pgvector.py | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/backend/open_webui/retrieval/vector/dbs/pgvector.py b/backend/open_webui/retrieval/vector/dbs/pgvector.py index d978f0c824..06c1698cdd 100644 --- a/backend/open_webui/retrieval/vector/dbs/pgvector.py +++ b/backend/open_webui/retrieval/vector/dbs/pgvector.py @@ -37,6 +37,7 @@ from open_webui.retrieval.vector.main import ( from open_webui.config import ( PGVECTOR_DB_URL, PGVECTOR_INITIALIZE_MAX_VECTOR_LENGTH, + PGVECTOR_CREATE_EXTENSION, PGVECTOR_PGCRYPTO, PGVECTOR_PGCRYPTO_KEY, PGVECTOR_POOL_SIZE, @@ -112,18 +113,19 @@ class PgvectorClient(VectorDBBase): try: # Ensure the pgvector extension is available # Use a conditional check to avoid permission issues on Azure PostgreSQL - self.session.execute( - text( - """ - DO $$ - BEGIN - IF NOT EXISTS (SELECT 1 FROM pg_extension WHERE extname = 'vector') THEN - CREATE EXTENSION IF NOT EXISTS vector; - END IF; - END $$; - """ + if PGVECTOR_CREATE_EXTENSION: + self.session.execute( + text( + """ + DO $$ + BEGIN + IF NOT EXISTS (SELECT 1 FROM pg_extension WHERE extname = 'vector') THEN + CREATE EXTENSION IF NOT EXISTS vector; + END IF; + END $$; + """ + ) ) - ) if PGVECTOR_PGCRYPTO: # Ensure the pgcrypto extension is available for encryption From b45219c8b15b48d5ee3d42983e1107bbcefbab01 Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Mon, 1 Sep 2025 00:04:26 +0400 Subject: [PATCH 0029/1079] refac --- backend/open_webui/config.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/backend/open_webui/config.py b/backend/open_webui/config.py index 3cc522c26d..c69ef76c0b 100644 --- a/backend/open_webui/config.py +++ b/backend/open_webui/config.py @@ -1998,6 +1998,9 @@ PGVECTOR_INITIALIZE_MAX_VECTOR_LENGTH = int( os.environ.get("PGVECTOR_INITIALIZE_MAX_VECTOR_LENGTH", "1536") ) +PGVECTOR_CREATE_EXTENSION = ( + os.getenv("PGVECTOR_CREATE_EXTENSION", "true").lower() == "true" +) PGVECTOR_PGCRYPTO = os.getenv("PGVECTOR_PGCRYPTO", "false").lower() == "true" PGVECTOR_PGCRYPTO_KEY = os.getenv("PGVECTOR_PGCRYPTO_KEY", None) if PGVECTOR_PGCRYPTO and not PGVECTOR_PGCRYPTO_KEY: From 50371975417fbba81e3e32cfe544316cc4b20df6 Mon Sep 17 00:00:00 2001 From: Shirasawa <764798966@qq.com> Date: Mon, 1 Sep 2025 04:49:06 +0800 Subject: [PATCH 0030/1079] fix: fix event binding for composition end in MessageInput --- src/lib/components/chat/MessageInput.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/components/chat/MessageInput.svelte b/src/lib/components/chat/MessageInput.svelte index adea345ed0..e38f8e2c79 100644 --- a/src/lib/components/chat/MessageInput.svelte +++ b/src/lib/components/chat/MessageInput.svelte @@ -1407,7 +1407,7 @@ command = getCommand(); }} on:compositionstart={() => (isComposing = true)} - oncompositionend={(e) => { + on:compositionend={(e) => { compositionEndedAt = e.timeStamp; isComposing = false; }} From ac0243e8b78aff68d3d8ed39e1f8123a817ea4d8 Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Mon, 1 Sep 2025 00:57:13 +0400 Subject: [PATCH 0031/1079] refac --- backend/open_webui/retrieval/utils.py | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/backend/open_webui/retrieval/utils.py b/backend/open_webui/retrieval/utils.py index 856527083c..8d4efd3f72 100644 --- a/backend/open_webui/retrieval/utils.py +++ b/backend/open_webui/retrieval/utils.py @@ -341,19 +341,18 @@ def query_collection_with_hybrid_search( # Fetch collection data once per collection sequentially # Avoid fetching the same data multiple times later collection_results = {} + for collection_name in collection_names: + try: + log.debug( + f"query_collection_with_hybrid_search:VECTOR_DB_CLIENT.get:collection {collection_name}" + ) + collection_results[collection_name] = VECTOR_DB_CLIENT.get( + collection_name=collection_name + ) + except Exception as e: + log.exception(f"Failed to fetch collection {collection_name}: {e}") + collection_results[collection_name] = None - for collection_name in collection_names: - try: - log.debug( - f"query_collection_with_hybrid_search:VECTOR_DB_CLIENT.get:collection {collection_name}" - ) - collection_results[collection_name] = VECTOR_DB_CLIENT.get( - collection_name=collection_name - ) - except Exception as e: - log.exception(f"Failed to fetch collection {collection_name}: {e}") - collection_results[collection_name] = None - log.info( f"Starting hybrid search for {len(queries)} queries in {len(collection_names)} collections..." ) From 487979859a6ffcfd60468f523822cdf838fbef5b Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Mon, 1 Sep 2025 01:22:50 +0400 Subject: [PATCH 0032/1079] fix: web/youtube attachements --- backend/open_webui/retrieval/utils.py | 7 +++++-- src/lib/components/chat/Chat.svelte | 4 ++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/backend/open_webui/retrieval/utils.py b/backend/open_webui/retrieval/utils.py index 8d4efd3f72..4ef6dbce3b 100644 --- a/backend/open_webui/retrieval/utils.py +++ b/backend/open_webui/retrieval/utils.py @@ -487,9 +487,12 @@ def get_sources_from_items( if item.get("type") == "text": # Raw Text - # Used during temporary chat file uploads + # Used during temporary chat file uploads or web page & youtube attachements - if item.get("file"): + if item.get("collection_name"): + # If item has a collection name, use it + collection_names.append(item.get("collection_name")) + elif item.get("file"): # if item has file data, use it query_result = { "documents": [ diff --git a/src/lib/components/chat/Chat.svelte b/src/lib/components/chat/Chat.svelte index 284ddd7ad1..8ad7da577e 100644 --- a/src/lib/components/chat/Chat.svelte +++ b/src/lib/components/chat/Chat.svelte @@ -671,7 +671,7 @@ console.log(url); const fileItem = { - type: 'doc', + type: 'text', name: url, collection_name: '', status: 'uploading', @@ -704,7 +704,7 @@ console.log(url); const fileItem = { - type: 'doc', + type: 'text', name: url, collection_name: '', status: 'uploading', From 77b65ccbfbf3971ca71d3c7a70d77168e8f007dd Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Mon, 1 Sep 2025 01:52:10 +0400 Subject: [PATCH 0033/1079] refac/enh: forward headers to tool server --- backend/open_webui/utils/tools.py | 31 ++++++++++++------- src/lib/apis/openai/index.ts | 1 + src/lib/components/AddServerModal.svelte | 10 ++++++ .../components/notes/NoteEditor/Chat.svelte | 2 +- src/lib/components/playground/Chat.svelte | 2 +- 5 files changed, 32 insertions(+), 14 deletions(-) diff --git a/backend/open_webui/utils/tools.py b/backend/open_webui/utils/tools.py index e68124bd5c..9007f59326 100644 --- a/backend/open_webui/utils/tools.py +++ b/backend/open_webui/utils/tools.py @@ -119,18 +119,26 @@ async def get_tools( function_name = spec["name"] auth_type = tool_server_connection.get("auth_type", "bearer") - token = None + headers = {} if auth_type == "bearer": - token = tool_server_connection.get("key", "") + headers["Authorization"] = ( + f"Bearer {tool_server_connection.get("key", "")}" + ) elif auth_type == "session": - token = request.state.token.credentials + headers["Authorization"] = ( + f"Bearer {request.state.token.credentials}" + ) + elif auth_type == "request_headers": + headers.update(dict(request.headers)) - def make_tool_function(function_name, token, tool_server_data): + headers["Content-Type"] = "application/json" + + def make_tool_function(function_name, tool_server_data, headers): async def tool_function(**kwargs): return await execute_tool_server( - token=token, url=tool_server_data["url"], + headers=headers, name=function_name, params=kwargs, server_data=tool_server_data, @@ -139,7 +147,7 @@ async def get_tools( return tool_function tool_function = make_tool_function( - function_name, token, tool_server_data + function_name, tool_server_data, headers ) callable = get_async_tool_function_and_apply_extra_params( @@ -610,7 +618,11 @@ async def get_tool_servers_data( async def execute_tool_server( - token: str, url: str, name: str, params: Dict[str, Any], server_data: Dict[str, Any] + url: str, + headers: Dict[str, str], + name: str, + params: Dict[str, Any], + server_data: Dict[str, Any], ) -> Any: error = None try: @@ -671,11 +683,6 @@ async def execute_tool_server( f"Request body expected for operation '{name}' but none found." ) - headers = {"Content-Type": "application/json"} - - if token: - headers["Authorization"] = f"Bearer {token}" - async with aiohttp.ClientSession( trust_env=True, timeout=aiohttp.ClientTimeout(total=AIOHTTP_CLIENT_TIMEOUT) ) as session: diff --git a/src/lib/apis/openai/index.ts b/src/lib/apis/openai/index.ts index c0cbe7b6d3..276fad145d 100644 --- a/src/lib/apis/openai/index.ts +++ b/src/lib/apis/openai/index.ts @@ -372,6 +372,7 @@ export const generateOpenAIChatCompletion = async ( Authorization: `Bearer ${token}`, 'Content-Type': 'application/json' }, + credentials: 'include', body: JSON.stringify(body) }) .then(async (res) => { diff --git a/src/lib/components/AddServerModal.svelte b/src/lib/components/AddServerModal.svelte index 4d3cbabce5..6fad62bc15 100644 --- a/src/lib/components/AddServerModal.svelte +++ b/src/lib/components/AddServerModal.svelte @@ -285,6 +285,10 @@ > + + {#if !direct} + + {/if}
@@ -301,6 +305,12 @@ > {$i18n.t('Forwards system user session credentials to authenticate')}
+ {:else if auth_type === 'request_headers'} +
+ {$i18n.t('Forwards system user headers to authenticate')} +
{/if}
diff --git a/src/lib/components/notes/NoteEditor/Chat.svelte b/src/lib/components/notes/NoteEditor/Chat.svelte index d0c050ef5d..9a509a3dc9 100644 --- a/src/lib/components/notes/NoteEditor/Chat.svelte +++ b/src/lib/components/notes/NoteEditor/Chat.svelte @@ -43,7 +43,7 @@ } from '$lib/constants'; import { WEBUI_NAME, config, user, models, settings } from '$lib/stores'; - import { chatCompletion, generateOpenAIChatCompletion } from '$lib/apis/openai'; + import { chatCompletion } from '$lib/apis/openai'; import { splitStream } from '$lib/utils'; diff --git a/src/lib/components/playground/Chat.svelte b/src/lib/components/playground/Chat.svelte index b060220268..933c9b34b8 100644 --- a/src/lib/components/playground/Chat.svelte +++ b/src/lib/components/playground/Chat.svelte @@ -12,7 +12,7 @@ } from '$lib/constants'; import { WEBUI_NAME, config, user, models, settings } from '$lib/stores'; - import { chatCompletion, generateOpenAIChatCompletion } from '$lib/apis/openai'; + import { chatCompletion } from '$lib/apis/openai'; import { splitStream } from '$lib/utils'; import Collapsible from '../common/Collapsible.svelte'; From ed5d95f4344251aa4bf6a2bcc1f2f3783a1b6d5a Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Mon, 1 Sep 2025 02:41:29 +0400 Subject: [PATCH 0034/1079] refac --- backend/open_webui/config.py | 2 +- backend/open_webui/utils/tools.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/open_webui/config.py b/backend/open_webui/config.py index c69ef76c0b..d62b219668 100644 --- a/backend/open_webui/config.py +++ b/backend/open_webui/config.py @@ -313,7 +313,7 @@ JWT_EXPIRES_IN = PersistentConfig( #################################### ENABLE_OAUTH_PERSISTENT_CONFIG = ( - os.environ.get("ENABLE_OAUTH_PERSISTENT_CONFIG", "True").lower() == "true" + os.environ.get("ENABLE_OAUTH_PERSISTENT_CONFIG", "False").lower() == "true" ) ENABLE_OAUTH_SIGNUP = PersistentConfig( diff --git a/backend/open_webui/utils/tools.py b/backend/open_webui/utils/tools.py index 9007f59326..d3ea432019 100644 --- a/backend/open_webui/utils/tools.py +++ b/backend/open_webui/utils/tools.py @@ -123,7 +123,7 @@ async def get_tools( if auth_type == "bearer": headers["Authorization"] = ( - f"Bearer {tool_server_connection.get("key", "")}" + f"Bearer {tool_server_connection.get('key', '')}" ) elif auth_type == "session": headers["Authorization"] = ( From 0e0c1c4ca422cdeaf0eb99e9a444b6b7fe74f496 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Sep 2025 05:53:53 +0000 Subject: [PATCH 0035/1079] build(deps): bump pdfjs-dist from 5.3.93 to 5.4.149 Bumps [pdfjs-dist](https://github.com/mozilla/pdf.js) from 5.3.93 to 5.4.149. - [Release notes](https://github.com/mozilla/pdf.js/releases) - [Commits](https://github.com/mozilla/pdf.js/compare/v5.3.93...v5.4.149) --- updated-dependencies: - dependency-name: pdfjs-dist dependency-version: 5.4.149 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- package-lock.json | 96 +++++++++++++++++++++++------------------------ package.json | 2 +- 2 files changed, 49 insertions(+), 49 deletions(-) diff --git a/package-lock.json b/package-lock.json index f5960587a1..9081ba5357 100644 --- a/package-lock.json +++ b/package-lock.json @@ -69,7 +69,7 @@ "mermaid": "^11.6.0", "paneforge": "^0.0.6", "panzoom": "^9.4.3", - "pdfjs-dist": "^5.3.93", + "pdfjs-dist": "^5.4.149", "prosemirror-collab": "^1.3.1", "prosemirror-commands": "^1.6.0", "prosemirror-example-setup": "^1.2.3", @@ -2238,9 +2238,9 @@ "integrity": "sha512-Y28PR25bHXUg88kCV7nivXrP2Nj2RueZ3/l/jdx6J9f8J4nsEGcgX0Qe6lt7Pa+J79+kPiJU3LguR6O/6zrLOw==" }, "node_modules/@napi-rs/canvas": { - "version": "0.1.73", - "resolved": "https://registry.npmjs.org/@napi-rs/canvas/-/canvas-0.1.73.tgz", - "integrity": "sha512-9iwPZrNlCK4rG+vWyDvyvGeYjck9MoP0NVQP6N60gqJNFA1GsN0imG05pzNsqfCvFxUxgiTYlR8ff0HC1HXJiw==", + "version": "0.1.78", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas/-/canvas-0.1.78.tgz", + "integrity": "sha512-YaBHJvT+T1DoP16puvWM6w46Lq3VhwKIJ8th5m1iEJyGh7mibk5dT7flBvMQ1EH1LYmMzXJ+OUhu+8wQ9I6u7g==", "license": "MIT", "optional": true, "workspaces": [ @@ -2250,22 +2250,22 @@ "node": ">= 10" }, "optionalDependencies": { - "@napi-rs/canvas-android-arm64": "0.1.73", - "@napi-rs/canvas-darwin-arm64": "0.1.73", - "@napi-rs/canvas-darwin-x64": "0.1.73", - "@napi-rs/canvas-linux-arm-gnueabihf": "0.1.73", - "@napi-rs/canvas-linux-arm64-gnu": "0.1.73", - "@napi-rs/canvas-linux-arm64-musl": "0.1.73", - "@napi-rs/canvas-linux-riscv64-gnu": "0.1.73", - "@napi-rs/canvas-linux-x64-gnu": "0.1.73", - "@napi-rs/canvas-linux-x64-musl": "0.1.73", - "@napi-rs/canvas-win32-x64-msvc": "0.1.73" + "@napi-rs/canvas-android-arm64": "0.1.78", + "@napi-rs/canvas-darwin-arm64": "0.1.78", + "@napi-rs/canvas-darwin-x64": "0.1.78", + "@napi-rs/canvas-linux-arm-gnueabihf": "0.1.78", + "@napi-rs/canvas-linux-arm64-gnu": "0.1.78", + "@napi-rs/canvas-linux-arm64-musl": "0.1.78", + "@napi-rs/canvas-linux-riscv64-gnu": "0.1.78", + "@napi-rs/canvas-linux-x64-gnu": "0.1.78", + "@napi-rs/canvas-linux-x64-musl": "0.1.78", + "@napi-rs/canvas-win32-x64-msvc": "0.1.78" } }, "node_modules/@napi-rs/canvas-android-arm64": { - "version": "0.1.73", - "resolved": "https://registry.npmjs.org/@napi-rs/canvas-android-arm64/-/canvas-android-arm64-0.1.73.tgz", - "integrity": "sha512-s8dMhfYIHVv7gz8BXg3Nb6cFi950Y0xH5R/sotNZzUVvU9EVqHfkqiGJ4UIqu+15UhqguT6mI3Bv1mhpRkmMQw==", + "version": "0.1.78", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-android-arm64/-/canvas-android-arm64-0.1.78.tgz", + "integrity": "sha512-N1ikxztjrRmh8xxlG5kYm1RuNr8ZW1EINEDQsLhhuy7t0pWI/e7SH91uFVLZKCMDyjel1tyWV93b5fdCAi7ggw==", "cpu": [ "arm64" ], @@ -2279,9 +2279,9 @@ } }, "node_modules/@napi-rs/canvas-darwin-arm64": { - "version": "0.1.73", - "resolved": "https://registry.npmjs.org/@napi-rs/canvas-darwin-arm64/-/canvas-darwin-arm64-0.1.73.tgz", - "integrity": "sha512-bLPCq8Yyq1vMdVdIpQAqmgf6VGUknk8e7NdSZXJJFOA9gxkJ1RGcHOwoXo7h0gzhHxSorg71hIxyxtwXpq10Rw==", + "version": "0.1.78", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-darwin-arm64/-/canvas-darwin-arm64-0.1.78.tgz", + "integrity": "sha512-FA3aCU3G5yGc74BSmnLJTObnZRV+HW+JBTrsU+0WVVaNyVKlb5nMvYAQuieQlRVemsAA2ek2c6nYtHh6u6bwFw==", "cpu": [ "arm64" ], @@ -2295,9 +2295,9 @@ } }, "node_modules/@napi-rs/canvas-darwin-x64": { - "version": "0.1.73", - "resolved": "https://registry.npmjs.org/@napi-rs/canvas-darwin-x64/-/canvas-darwin-x64-0.1.73.tgz", - "integrity": "sha512-GR1CcehDjdNYXN3bj8PIXcXfYLUUOQANjQpM+KNnmpRo7ojsuqPjT7ZVH+6zoG/aqRJWhiSo+ChQMRazZlRU9g==", + "version": "0.1.78", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-darwin-x64/-/canvas-darwin-x64-0.1.78.tgz", + "integrity": "sha512-xVij69o9t/frixCDEoyWoVDKgE3ksLGdmE2nvBWVGmoLu94MWUlv2y4Qzf5oozBmydG5Dcm4pRHFBM7YWa1i6g==", "cpu": [ "x64" ], @@ -2311,9 +2311,9 @@ } }, "node_modules/@napi-rs/canvas-linux-arm-gnueabihf": { - "version": "0.1.73", - "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-arm-gnueabihf/-/canvas-linux-arm-gnueabihf-0.1.73.tgz", - "integrity": "sha512-cM7F0kBJVFio0+U2iKSW4fWSfYQ8CPg4/DRZodSum/GcIyfB8+UPJSRM1BvvlcWinKLfX1zUYOwonZX9IFRRcw==", + "version": "0.1.78", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-arm-gnueabihf/-/canvas-linux-arm-gnueabihf-0.1.78.tgz", + "integrity": "sha512-aSEXrLcIpBtXpOSnLhTg4jPsjJEnK7Je9KqUdAWjc7T8O4iYlxWxrXFIF8rV8J79h5jNdScgZpAUWYnEcutR3g==", "cpu": [ "arm" ], @@ -2327,9 +2327,9 @@ } }, "node_modules/@napi-rs/canvas-linux-arm64-gnu": { - "version": "0.1.73", - "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-arm64-gnu/-/canvas-linux-arm64-gnu-0.1.73.tgz", - "integrity": "sha512-PMWNrMON9uz9klz1B8ZY/RXepQSC5dxxHQTowfw93Tb3fLtWO5oNX2k9utw7OM4ypT9BUZUWJnDQ5bfuXc/EUQ==", + "version": "0.1.78", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-arm64-gnu/-/canvas-linux-arm64-gnu-0.1.78.tgz", + "integrity": "sha512-dlEPRX1hLGKaY3UtGa1dtkA1uGgFITn2mDnfI6YsLlYyLJQNqHx87D1YTACI4zFCUuLr/EzQDzuX+vnp9YveVg==", "cpu": [ "arm64" ], @@ -2343,9 +2343,9 @@ } }, "node_modules/@napi-rs/canvas-linux-arm64-musl": { - "version": "0.1.73", - "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-arm64-musl/-/canvas-linux-arm64-musl-0.1.73.tgz", - "integrity": "sha512-lX0z2bNmnk1PGZ+0a9OZwI2lPPvWjRYzPqvEitXX7lspyLFrOzh2kcQiLL7bhyODN23QvfriqwYqp5GreSzVvA==", + "version": "0.1.78", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-arm64-musl/-/canvas-linux-arm64-musl-0.1.78.tgz", + "integrity": "sha512-TsCfjOPZtm5Q/NO1EZHR5pwDPSPjPEttvnv44GL32Zn1uvudssjTLbvaG1jHq81Qxm16GTXEiYLmx4jOLZQYlg==", "cpu": [ "arm64" ], @@ -2359,9 +2359,9 @@ } }, "node_modules/@napi-rs/canvas-linux-riscv64-gnu": { - "version": "0.1.73", - "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-riscv64-gnu/-/canvas-linux-riscv64-gnu-0.1.73.tgz", - "integrity": "sha512-QDQgMElwxAoADsSR3UYvdTTQk5XOyD9J5kq15Z8XpGwpZOZsSE0zZ/X1JaOtS2x+HEZL6z1S6MF/1uhZFZb5ig==", + "version": "0.1.78", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-riscv64-gnu/-/canvas-linux-riscv64-gnu-0.1.78.tgz", + "integrity": "sha512-+cpTTb0GDshEow/5Fy8TpNyzaPsYb3clQIjgWRmzRcuteLU+CHEU/vpYvAcSo7JxHYPJd8fjSr+qqh+nI5AtmA==", "cpu": [ "riscv64" ], @@ -2375,9 +2375,9 @@ } }, "node_modules/@napi-rs/canvas-linux-x64-gnu": { - "version": "0.1.73", - "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-x64-gnu/-/canvas-linux-x64-gnu-0.1.73.tgz", - "integrity": "sha512-wbzLJrTalQrpyrU1YRrO6w6pdr5vcebbJa+Aut5QfTaW9eEmMb1WFG6l1V+cCa5LdHmRr8bsvl0nJDU/IYDsmw==", + "version": "0.1.78", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-x64-gnu/-/canvas-linux-x64-gnu-0.1.78.tgz", + "integrity": "sha512-wxRcvKfvYBgtrO0Uy8OmwvjlnTcHpY45LLwkwVNIWHPqHAsyoTyG/JBSfJ0p5tWRzMOPDCDqdhpIO4LOgXjeyg==", "cpu": [ "x64" ], @@ -2391,9 +2391,9 @@ } }, "node_modules/@napi-rs/canvas-linux-x64-musl": { - "version": "0.1.73", - "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-x64-musl/-/canvas-linux-x64-musl-0.1.73.tgz", - "integrity": "sha512-xbfhYrUufoTAKvsEx2ZUN4jvACabIF0h1F5Ik1Rk4e/kQq6c+Dwa5QF0bGrfLhceLpzHT0pCMGMDeQKQrcUIyA==", + "version": "0.1.78", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-x64-musl/-/canvas-linux-x64-musl-0.1.78.tgz", + "integrity": "sha512-vQFOGwC9QDP0kXlhb2LU1QRw/humXgcbVp8mXlyBqzc/a0eijlLF9wzyarHC1EywpymtS63TAj8PHZnhTYN6hg==", "cpu": [ "x64" ], @@ -2407,9 +2407,9 @@ } }, "node_modules/@napi-rs/canvas-win32-x64-msvc": { - "version": "0.1.73", - "resolved": "https://registry.npmjs.org/@napi-rs/canvas-win32-x64-msvc/-/canvas-win32-x64-msvc-0.1.73.tgz", - "integrity": "sha512-YQmHXBufFBdWqhx+ympeTPkMfs3RNxaOgWm59vyjpsub7Us07BwCcmu1N5kildhO8Fm0syoI2kHnzGkJBLSvsg==", + "version": "0.1.78", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-win32-x64-msvc/-/canvas-win32-x64-msvc-0.1.78.tgz", + "integrity": "sha512-/eKlTZBtGUgpRKalzOzRr6h7KVSuziESWXgBcBnXggZmimwIJWPJlEcbrx5Tcwj8rPuZiANXQOG9pPgy9Q4LTQ==", "cpu": [ "x64" ], @@ -10252,15 +10252,15 @@ } }, "node_modules/pdfjs-dist": { - "version": "5.3.93", - "resolved": "https://registry.npmjs.org/pdfjs-dist/-/pdfjs-dist-5.3.93.tgz", - "integrity": "sha512-w3fQKVL1oGn8FRyx5JUG5tnbblggDqyx2XzA5brsJ5hSuS+I0NdnJANhmeWKLjotdbPQucLBug5t0MeWr0AAdg==", + "version": "5.4.149", + "resolved": "https://registry.npmjs.org/pdfjs-dist/-/pdfjs-dist-5.4.149.tgz", + "integrity": "sha512-Xe8/1FMJEQPUVSti25AlDpwpUm2QAVmNOpFP0SIahaPIOKBKICaefbzogLdwey3XGGoaP4Lb9wqiw2e9Jqp0LA==", "license": "Apache-2.0", "engines": { "node": ">=20.16.0 || >=22.3.0" }, "optionalDependencies": { - "@napi-rs/canvas": "^0.1.71" + "@napi-rs/canvas": "^0.1.77" } }, "node_modules/pend": { diff --git a/package.json b/package.json index d4f736d598..f1a391db5d 100644 --- a/package.json +++ b/package.json @@ -113,7 +113,7 @@ "mermaid": "^11.6.0", "paneforge": "^0.0.6", "panzoom": "^9.4.3", - "pdfjs-dist": "^5.3.93", + "pdfjs-dist": "^5.4.149", "prosemirror-collab": "^1.3.1", "prosemirror-commands": "^1.6.0", "prosemirror-example-setup": "^1.2.3", From bcc0cbf895e59d06e254e77ce381ba3d9bd8ceb8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Sep 2025 05:59:19 +0000 Subject: [PATCH 0036/1079] build(deps): bump argon2-cffi from 23.1.0 to 25.1.0 in /backend Bumps [argon2-cffi](https://github.com/hynek/argon2-cffi) from 23.1.0 to 25.1.0. - [Release notes](https://github.com/hynek/argon2-cffi/releases) - [Changelog](https://github.com/hynek/argon2-cffi/blob/main/CHANGELOG.md) - [Commits](https://github.com/hynek/argon2-cffi/compare/23.1.0...25.1.0) --- updated-dependencies: - dependency-name: argon2-cffi dependency-version: 25.1.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- backend/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/requirements.txt b/backend/requirements.txt index 03eeba2a1e..01a74ecb7e 100644 --- a/backend/requirements.txt +++ b/backend/requirements.txt @@ -29,7 +29,7 @@ pymongo redis boto3==1.40.5 -argon2-cffi==23.1.0 +argon2-cffi==25.1.0 APScheduler==3.10.4 pycrdt==0.12.25 From 3e69f10af7788d908f7759bcb7dbfa67ef8a7996 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Sep 2025 05:59:27 +0000 Subject: [PATCH 0037/1079] build(deps): bump youtube-transcript-api from 1.1.0 to 1.2.2 in /backend Bumps [youtube-transcript-api](https://github.com/jdepoix/youtube-transcript-api) from 1.1.0 to 1.2.2. - [Release notes](https://github.com/jdepoix/youtube-transcript-api/releases) - [Commits](https://github.com/jdepoix/youtube-transcript-api/compare/v1.1.0...v1.2.2) --- updated-dependencies: - dependency-name: youtube-transcript-api dependency-version: 1.2.2 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- backend/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/requirements.txt b/backend/requirements.txt index 03eeba2a1e..714054221e 100644 --- a/backend/requirements.txt +++ b/backend/requirements.txt @@ -102,7 +102,7 @@ PyJWT[crypto]==2.10.1 authlib==1.6.1 black==25.1.0 -youtube-transcript-api==1.1.0 +youtube-transcript-api==1.2.2 pytube==15.0.0 pydub From 94dae76d0f0ca9cc3a8d8f3d7e867ea278de2a97 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Sep 2025 06:01:03 +0000 Subject: [PATCH 0038/1079] build(deps): bump pyodide from 0.27.7 to 0.28.2 Bumps [pyodide](https://github.com/pyodide/pyodide) from 0.27.7 to 0.28.2. - [Release notes](https://github.com/pyodide/pyodide/releases) - [Commits](https://github.com/pyodide/pyodide/compare/0.27.7...0.28.2) --- updated-dependencies: - dependency-name: pyodide dependency-version: 0.28.2 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- package-lock.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index f5960587a1..9fa5962e1d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -82,7 +82,7 @@ "prosemirror-state": "^1.4.3", "prosemirror-tables": "^1.7.1", "prosemirror-view": "^1.34.3", - "pyodide": "^0.27.3", + "pyodide": "^0.28.2", "socket.io-client": "^4.2.0", "sortablejs": "^1.15.6", "svelte-sonner": "^0.3.19", @@ -10905,9 +10905,9 @@ } }, "node_modules/pyodide": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/pyodide/-/pyodide-0.27.7.tgz", - "integrity": "sha512-RUSVJlhQdfWfgO9hVHCiXoG+nVZQRS5D9FzgpLJ/VcgGBLSAKoPL8kTiOikxbHQm1kRISeWUBdulEgO26qpSRA==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/pyodide/-/pyodide-0.28.2.tgz", + "integrity": "sha512-2BrZHrALvhYZfIuTGDHOvyiirHNLziHfBiBb1tpBFzLgAvDBb2ACxNPFFROCOzLnqapORmgArDYY8mJmMWH1Eg==", "license": "MPL-2.0", "dependencies": { "ws": "^8.5.0" diff --git a/package.json b/package.json index d4f736d598..3b3ed32d4b 100644 --- a/package.json +++ b/package.json @@ -126,7 +126,7 @@ "prosemirror-state": "^1.4.3", "prosemirror-tables": "^1.7.1", "prosemirror-view": "^1.34.3", - "pyodide": "^0.27.3", + "pyodide": "^0.28.2", "socket.io-client": "^4.2.0", "sortablejs": "^1.15.6", "svelte-sonner": "^0.3.19", From 4cd550d1d01b4183b683b3dec45ab417477abc2d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Sep 2025 06:02:31 +0000 Subject: [PATCH 0039/1079] build(deps): update pytest requirement in /backend Updates the requirements on [pytest](https://github.com/pytest-dev/pytest) to permit the latest version. - [Release notes](https://github.com/pytest-dev/pytest/releases) - [Changelog](https://github.com/pytest-dev/pytest/blob/main/CHANGELOG.rst) - [Commits](https://github.com/pytest-dev/pytest/compare/8.3.5...8.4.1) --- updated-dependencies: - dependency-name: pytest dependency-version: 8.4.1 dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- backend/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/requirements.txt b/backend/requirements.txt index 03eeba2a1e..fa7320920a 100644 --- a/backend/requirements.txt +++ b/backend/requirements.txt @@ -115,7 +115,7 @@ google-auth-oauthlib ## Tests docker~=7.1.0 -pytest~=8.3.5 +pytest~=8.4.1 pytest-docker~=3.1.1 googleapis-common-protos==1.63.2 From 201eaa965567188ccfc55aede2df822f7d25380f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Sep 2025 06:02:48 +0000 Subject: [PATCH 0040/1079] build(deps): bump @tiptap/extension-highlight from 3.0.7 to 3.3.0 Bumps [@tiptap/extension-highlight](https://github.com/ueberdosis/tiptap/tree/HEAD/packages/extension-highlight) from 3.0.7 to 3.3.0. - [Release notes](https://github.com/ueberdosis/tiptap/releases) - [Changelog](https://github.com/ueberdosis/tiptap/blob/develop/packages/extension-highlight/CHANGELOG.md) - [Commits](https://github.com/ueberdosis/tiptap/commits/v3.3.0/packages/extension-highlight) --- updated-dependencies: - dependency-name: "@tiptap/extension-highlight" dependency-version: 3.3.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- package-lock.json | 10 +++++----- package.json | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package-lock.json b/package-lock.json index f5960587a1..ca187a29d8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -26,7 +26,7 @@ "@tiptap/extension-drag-handle": "^3.0.7", "@tiptap/extension-file-handler": "^3.0.7", "@tiptap/extension-floating-menu": "^2.26.1", - "@tiptap/extension-highlight": "^3.0.7", + "@tiptap/extension-highlight": "^3.3.0", "@tiptap/extension-image": "^3.0.7", "@tiptap/extension-link": "^3.0.7", "@tiptap/extension-list": "^3.0.7", @@ -3516,16 +3516,16 @@ } }, "node_modules/@tiptap/extension-highlight": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/@tiptap/extension-highlight/-/extension-highlight-3.0.7.tgz", - "integrity": "sha512-3oIRuXAg7l9+VPIMwHycXcqtZ7XJcC5vnLhPAQXIesYun6L9EoXmQox0225z8jpPG70N8zfl+YSd4qjsTMPaAg==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/@tiptap/extension-highlight/-/extension-highlight-3.3.0.tgz", + "integrity": "sha512-G+mHVXkoQ4uG97JRFN56qL42iJVKbSeWgDGssmnjNZN/W4Nsc40LuNryNbQUOM9CJbEMIT5NGAwvc/RG0OpGGQ==", "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/core": "^3.0.7" + "@tiptap/core": "^3.3.0" } }, "node_modules/@tiptap/extension-horizontal-rule": { diff --git a/package.json b/package.json index d4f736d598..e325784f08 100644 --- a/package.json +++ b/package.json @@ -70,7 +70,7 @@ "@tiptap/extension-drag-handle": "^3.0.7", "@tiptap/extension-file-handler": "^3.0.7", "@tiptap/extension-floating-menu": "^2.26.1", - "@tiptap/extension-highlight": "^3.0.7", + "@tiptap/extension-highlight": "^3.3.0", "@tiptap/extension-image": "^3.0.7", "@tiptap/extension-link": "^3.0.7", "@tiptap/extension-list": "^3.0.7", From dbe36e841a0363fa9264c5e40d4bc9efddc561c0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Sep 2025 06:02:57 +0000 Subject: [PATCH 0041/1079] build(deps): bump actions/download-artifact from 4 to 5 Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 4 to 5. - [Release notes](https://github.com/actions/download-artifact/releases) - [Commits](https://github.com/actions/download-artifact/compare/v4...v5) --- updated-dependencies: - dependency-name: actions/download-artifact dependency-version: '5' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/docker-build.yaml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/docker-build.yaml b/.github/workflows/docker-build.yaml index e597ff8055..a9b930ee8c 100644 --- a/.github/workflows/docker-build.yaml +++ b/.github/workflows/docker-build.yaml @@ -535,7 +535,7 @@ jobs: IMAGE_NAME: '${{ github.repository }}' - name: Download digests - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v5 with: pattern: digests-main-* path: /tmp/digests @@ -589,7 +589,7 @@ jobs: IMAGE_NAME: '${{ github.repository }}' - name: Download digests - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v5 with: pattern: digests-cuda-* path: /tmp/digests @@ -645,7 +645,7 @@ jobs: IMAGE_NAME: '${{ github.repository }}' - name: Download digests - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v5 with: pattern: digests-cuda126-* path: /tmp/digests @@ -701,7 +701,7 @@ jobs: IMAGE_NAME: '${{ github.repository }}' - name: Download digests - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v5 with: pattern: digests-ollama-* path: /tmp/digests @@ -757,7 +757,7 @@ jobs: IMAGE_NAME: '${{ github.repository }}' - name: Download digests - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v5 with: pattern: digests-slim-* path: /tmp/digests From 4dd9484b48aa28b43918ce33326944a6c5e2314d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Sep 2025 06:03:14 +0000 Subject: [PATCH 0042/1079] build(deps): bump actions/checkout from 4 to 5 Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 5. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v4...v5) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: '5' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/build-release.yml | 2 +- .github/workflows/deploy-to-hf-spaces.yml | 2 +- .github/workflows/docker-build.yaml | 10 +++++----- .github/workflows/format-backend.yaml | 2 +- .github/workflows/format-build-frontend.yaml | 4 ++-- .github/workflows/release-pypi.yml | 2 +- 6 files changed, 11 insertions(+), 11 deletions(-) diff --git a/.github/workflows/build-release.yml b/.github/workflows/build-release.yml index 443d904199..7d5e30e23e 100644 --- a/.github/workflows/build-release.yml +++ b/.github/workflows/build-release.yml @@ -11,7 +11,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Check for changes in package.json run: | diff --git a/.github/workflows/deploy-to-hf-spaces.yml b/.github/workflows/deploy-to-hf-spaces.yml index 7fc66acf5c..a30046af89 100644 --- a/.github/workflows/deploy-to-hf-spaces.yml +++ b/.github/workflows/deploy-to-hf-spaces.yml @@ -27,7 +27,7 @@ jobs: HF_TOKEN: ${{ secrets.HF_TOKEN }} steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v5 with: lfs: true diff --git a/.github/workflows/docker-build.yaml b/.github/workflows/docker-build.yaml index e597ff8055..f5eb544f1d 100644 --- a/.github/workflows/docker-build.yaml +++ b/.github/workflows/docker-build.yaml @@ -43,7 +43,7 @@ jobs: echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Set up QEMU uses: docker/setup-qemu-action@v3 @@ -142,7 +142,7 @@ jobs: echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Set up QEMU uses: docker/setup-qemu-action@v3 @@ -244,7 +244,7 @@ jobs: echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Set up QEMU uses: docker/setup-qemu-action@v3 @@ -347,7 +347,7 @@ jobs: echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Set up QEMU uses: docker/setup-qemu-action@v3 @@ -449,7 +449,7 @@ jobs: echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Set up QEMU uses: docker/setup-qemu-action@v3 diff --git a/.github/workflows/format-backend.yaml b/.github/workflows/format-backend.yaml index 1bcdd92c1d..56074a84f4 100644 --- a/.github/workflows/format-backend.yaml +++ b/.github/workflows/format-backend.yaml @@ -30,7 +30,7 @@ jobs: - 3.12.x steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Set up Python uses: actions/setup-python@v5 diff --git a/.github/workflows/format-build-frontend.yaml b/.github/workflows/format-build-frontend.yaml index 15dc53cc63..df961ca3f5 100644 --- a/.github/workflows/format-build-frontend.yaml +++ b/.github/workflows/format-build-frontend.yaml @@ -24,7 +24,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout Repository - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Setup Node.js uses: actions/setup-node@v4 @@ -51,7 +51,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout Repository - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Setup Node.js uses: actions/setup-node@v4 diff --git a/.github/workflows/release-pypi.yml b/.github/workflows/release-pypi.yml index fd1adab3a9..c4ae97422d 100644 --- a/.github/workflows/release-pypi.yml +++ b/.github/workflows/release-pypi.yml @@ -16,7 +16,7 @@ jobs: id-token: write steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v5 with: fetch-depth: 0 - name: Install Git From 9f5df72d7cb40d345937d0eeaee017a170812e29 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Sep 2025 06:16:43 +0000 Subject: [PATCH 0043/1079] build(deps): bump dompurify from 3.2.5 to 3.2.6 Bumps [dompurify](https://github.com/cure53/DOMPurify) from 3.2.5 to 3.2.6. - [Release notes](https://github.com/cure53/DOMPurify/releases) - [Commits](https://github.com/cure53/DOMPurify/compare/3.2.5...3.2.6) --- updated-dependencies: - dependency-name: dompurify dependency-version: 3.2.6 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- package-lock.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index f5960587a1..f7b1ae1701 100644 --- a/package-lock.json +++ b/package-lock.json @@ -46,7 +46,7 @@ "codemirror-lang-hcl": "^0.1.0", "crc-32": "^1.2.2", "dayjs": "^1.11.10", - "dompurify": "^3.2.5", + "dompurify": "^3.2.6", "eventsource-parser": "^1.1.2", "file-saver": "^2.0.5", "focus-trap": "^7.6.4", @@ -6767,9 +6767,9 @@ } }, "node_modules/dompurify": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.2.5.tgz", - "integrity": "sha512-mLPd29uoRe9HpvwP2TxClGQBzGXeEC/we/q+bFlmPPmj2p2Ugl3r6ATu/UU1v77DXNcehiBg9zsr1dREyA/dJQ==", + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.2.6.tgz", + "integrity": "sha512-/2GogDQlohXPZe6D6NOgQvXLPSYBqIWMnZ8zzOhn09REE4eyAzb+Hed3jhoM9OkuaJ8P6ZGTTVWQKAi8ieIzfQ==", "license": "(MPL-2.0 OR Apache-2.0)", "optionalDependencies": { "@types/trusted-types": "^2.0.7" diff --git a/package.json b/package.json index d4f736d598..28d345be2e 100644 --- a/package.json +++ b/package.json @@ -90,7 +90,7 @@ "codemirror-lang-hcl": "^0.1.0", "crc-32": "^1.2.2", "dayjs": "^1.11.10", - "dompurify": "^3.2.5", + "dompurify": "^3.2.6", "eventsource-parser": "^1.1.2", "file-saver": "^2.0.5", "focus-trap": "^7.6.4", From 4b97884fce7152e78b22098bf6cb378088bb5aab Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Mon, 1 Sep 2025 11:46:52 +0400 Subject: [PATCH 0044/1079] refac --- backend/requirements.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/requirements.txt b/backend/requirements.txt index 03eeba2a1e..7d002d094b 100644 --- a/backend/requirements.txt +++ b/backend/requirements.txt @@ -65,8 +65,8 @@ transformers sentence-transformers==4.1.0 accelerate colbert-ai==0.2.21 -pyarrow==20.0.0 -einops==0.8.1 +pyarrow==20.0.0 # fix: pin pyarrow version to 20 for rpi compatibility #15897 +einops==0.8.1 ftfy==6.2.3 From cef4028c1c68b57cf9ad56e89ce1df252b41de43 Mon Sep 17 00:00:00 2001 From: _00_ <131402327+rgaricano@users.noreply.github.com> Date: Mon, 1 Sep 2025 09:48:05 +0200 Subject: [PATCH 0045/1079] UPD: i18n Translation es-ES v.0.6.27 ### Update of i18n Translation es-ES v.0.6.27 Added new strings --- src/lib/i18n/locales/es-ES/translation.json | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/lib/i18n/locales/es-ES/translation.json b/src/lib/i18n/locales/es-ES/translation.json index b90265dda4..454be0d79e 100644 --- a/src/lib/i18n/locales/es-ES/translation.json +++ b/src/lib/i18n/locales/es-ES/translation.json @@ -11,7 +11,7 @@ "{{ models }}": "{{ models }}", "{{COUNT}} Available Tools": "{{COUNT}} herramientas disponibles", "{{COUNT}} characters": "{{COUNT}} caracteres", - "{{COUNT}} extracted lines": "", + "{{COUNT}} extracted lines": "{{COUNT}} líneas extraidas", "{{COUNT}} hidden lines": "{{COUNT}} líneas ocultas", "{{COUNT}} Replies": "{{COUNT}} Respuestas", "{{COUNT}} words": "{{COUNT}} palabras", @@ -83,13 +83,13 @@ "Allow Chat Share": "Permitir Compartir Chat", "Allow Chat System Prompt": "Permitir Indicador del Sistema en Chat", "Allow Chat Valves": "Permitir Válvulas en Chat", - "Allow Continue Response": "", - "Allow Delete Messages": "", + "Allow Continue Response": "Permitir Continuar Respuesta", + "Allow Delete Messages": "Permitir Borrar Mensajes", "Allow File Upload": "Permitir Subida de Archivos", "Allow Multiple Models in Chat": "Permitir Chat con Múltiples Modelos", "Allow non-local voices": "Permitir voces no locales", - "Allow Rate Response": "", - "Allow Regenerate Response": "", + "Allow Rate Response": "Permitir Calificar Respuesta", + "Allow Regenerate Response": "Permitir Regenerar Respuesta", "Allow Speech to Text": "Permitir Transcribir Voz a Texto", "Allow Temporary Chat": "Permitir Chat Temporal", "Allow Text to Speech": "Permitir Leer Texto", @@ -430,7 +430,7 @@ "Docling Server URL required.": "Docling URL del servidor necesaria.", "Document": "Documento", "Document Intelligence": "Azure Doc Intelligence", - "Document Intelligence endpoint required.": "", + "Document Intelligence endpoint required.": "Endpoint Azure Doc Intelligence requerido", "Documentation": "Documentación", "Documents": "Documentos", "does not make any external connections, and your data stays securely on your locally hosted server.": "no se realiza ninguna conexión externa y tus datos permanecen seguros alojados localmente en tu servidor.", @@ -496,7 +496,7 @@ "Enable New Sign Ups": "Habilitar Registros de Nuevos Usuarios", "Enable, disable, or customize the reasoning tags used by the model. \"Enabled\" uses default tags, \"Disabled\" turns off reasoning tags, and \"Custom\" lets you specify your own start and end tags.": "", "Enabled": "Habilitado", - "End Tag": "", + "End Tag": "Etiqueta de Fin", "Endpoint URL": "Endpoint URL", "Enforce Temporary Chat": "Forzar el uso de Chat Temporal", "Enhance": "Mejorar", @@ -725,7 +725,7 @@ "Format Lines": "Formatear Líneas", "Format the lines in the output. Defaults to False. If set to True, the lines will be formatted to detect inline math and styles.": "Formatear las lineas en la salida. Por defecto, False. Si la opción es True, las líneas se formatearán detectando estilos e 'inline math'", "Format your variables using brackets like this:": "Formatea tus variables usando corchetes así:", - "Formatting may be inconsistent from source.": "", + "Formatting may be inconsistent from source.": "El formato puede ser inconsistente con el original", "Forwards system user session credentials to authenticate": "Reenvío de las credenciales de la sesión del usuario del sistema para autenticación", "Full Context Mode": "Modo Contexto Completo", "Function": "Función", @@ -1175,7 +1175,7 @@ "Read Aloud": "Leer en voz alta", "Reason": "Razonamiento", "Reasoning Effort": "Esfuerzo del Razonamiento", - "Reasoning Tags": "", + "Reasoning Tags": "Eriquetas de Razonamiento", "Record": "Grabar", "Record voice": "Grabar voz", "Redirecting you to Open WebUI Community": "Redireccionando a la Comunidad Open-WebUI", @@ -1376,7 +1376,7 @@ "Speech-to-Text": "Voz a Texto", "Speech-to-Text Engine": "Motor Voz a Texto(STT)", "Start of the channel": "Inicio del canal", - "Start Tag": "", + "Start Tag": "Etiqueta de Inicio", "STDOUT/STDERR": "STDOUT/STDERR", "Stop": "Detener", "Stop Generating": "Detener la Generación", From f56889c5c7f0cf1a501c05d35dfa614e4f8b6958 Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Mon, 1 Sep 2025 14:14:20 +0400 Subject: [PATCH 0046/1079] fix: fillter exception handling --- backend/open_webui/main.py | 19 +++++++++++++------ backend/open_webui/utils/middleware.py | 6 +++--- src/lib/components/chat/Chat.svelte | 7 +++++++ 3 files changed, 23 insertions(+), 9 deletions(-) diff --git a/backend/open_webui/main.py b/backend/open_webui/main.py index d24bd5dcf1..2245be5bb7 100644 --- a/backend/open_webui/main.py +++ b/backend/open_webui/main.py @@ -1519,7 +1519,7 @@ async def chat_completion( try: event_emitter = get_event_emitter(metadata) await event_emitter( - {"type": "task-cancelled"}, + {"type": "chat:tasks:cancel"}, ) except Exception as e: pass @@ -1535,14 +1535,21 @@ async def chat_completion( "error": {"content": str(e)}, }, ) + + event_emitter = get_event_emitter(metadata) + await event_emitter( + { + "type": "chat:message:error", + "data": {"error": {"content": str(e)}}, + } + ) + await event_emitter( + {"type": "chat:tasks:cancel"}, + ) + except: pass - raise HTTPException( - status_code=status.HTTP_400_BAD_REQUEST, - detail=str(e), - ) - if ( metadata.get("session_id") and metadata.get("chat_id") diff --git a/backend/open_webui/utils/middleware.py b/backend/open_webui/utils/middleware.py index 91a125aafb..c2378db885 100644 --- a/backend/open_webui/utils/middleware.py +++ b/backend/open_webui/utils/middleware.py @@ -885,7 +885,7 @@ async def process_chat_payload(request, form_data, user, metadata, model): extra_params=extra_params, ) except Exception as e: - raise Exception(f"Error: {e}") + raise Exception(f"{e}") features = form_data.pop("features", None) if features: @@ -1316,7 +1316,7 @@ async def process_chat_response( { "type": "chat:message:error", "data": {"error": {"content": error}}, - }, + } ) if "selected_model_id" in response_data: @@ -2624,7 +2624,7 @@ async def process_chat_response( await background_tasks_handler() except asyncio.CancelledError: log.warning("Task was cancelled!") - await event_emitter({"type": "task-cancelled"}) + await event_emitter({"type": "chat:tasks:cancel"}) if not ENABLE_REALTIME_CHAT_SAVE: # Save message in the database diff --git a/src/lib/components/chat/Chat.svelte b/src/lib/components/chat/Chat.svelte index 8ad7da577e..f258207593 100644 --- a/src/lib/components/chat/Chat.svelte +++ b/src/lib/components/chat/Chat.svelte @@ -318,6 +318,13 @@ } } else if (type === 'chat:completion') { chatCompletionEventHandler(data, message, event.chat_id); + } else if (type === 'chat:tasks:cancel') { + taskIds = null; + const responseMessage = history.messages[history.currentId]; + // Set all response messages to done + for (const messageId of history.messages[responseMessage.parentId].childrenIds) { + history.messages[messageId].done = true; + } } else if (type === 'chat:message:delta' || type === 'message') { message.content += data.content; } else if (type === 'chat:message' || type === 'replace') { From 85153afda8848f9591e04c877eb5989a0d50c9cd Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Mon, 1 Sep 2025 14:21:17 +0400 Subject: [PATCH 0047/1079] refac --- backend/open_webui/retrieval/utils.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/backend/open_webui/retrieval/utils.py b/backend/open_webui/retrieval/utils.py index 4ef6dbce3b..eb88ad580a 100644 --- a/backend/open_webui/retrieval/utils.py +++ b/backend/open_webui/retrieval/utils.py @@ -128,12 +128,12 @@ def query_doc_with_hybrid_search( log.warning(f"query_doc_with_hybrid_search:no_docs {collection_name}") return {"documents": [], "metadatas": [], "distances": []} - log.debug(f"query_doc_with_hybrid_search:doc {collection_name}") - bm25_retriever = BM25Retriever.from_texts( - texts=collection_result.documents[0], - metadatas=collection_result.metadatas[0], - ) - bm25_retriever.k = k + log.debug(f"query_doc_with_hybrid_search:doc {collection_name}") + bm25_retriever = BM25Retriever.from_texts( + texts=collection_result.documents[0], + metadatas=collection_result.metadatas[0], + ) + bm25_retriever.k = k vector_search_retriever = VectorSearchRetriever( collection_name=collection_name, From 609a6a3721e33993a0f1e925cd9ff6243d3be718 Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Mon, 1 Sep 2025 14:22:02 +0400 Subject: [PATCH 0048/1079] refac --- backend/open_webui/retrieval/utils.py | 1 + 1 file changed, 1 insertion(+) diff --git a/backend/open_webui/retrieval/utils.py b/backend/open_webui/retrieval/utils.py index eb88ad580a..87ea090daf 100644 --- a/backend/open_webui/retrieval/utils.py +++ b/backend/open_webui/retrieval/utils.py @@ -129,6 +129,7 @@ def query_doc_with_hybrid_search( return {"documents": [], "metadatas": [], "distances": []} log.debug(f"query_doc_with_hybrid_search:doc {collection_name}") + bm25_retriever = BM25Retriever.from_texts( texts=collection_result.documents[0], metadatas=collection_result.metadatas[0], From 4f2e426fc701a80cc76e8dc1b377f8b4ac0cc6b1 Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Mon, 1 Sep 2025 14:27:20 +0400 Subject: [PATCH 0049/1079] refac --- backend/open_webui/retrieval/utils.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/backend/open_webui/retrieval/utils.py b/backend/open_webui/retrieval/utils.py index 87ea090daf..dead8458cb 100644 --- a/backend/open_webui/retrieval/utils.py +++ b/backend/open_webui/retrieval/utils.py @@ -499,9 +499,7 @@ def get_sources_from_items( "documents": [ [item.get("file", {}).get("data", {}).get("content")] ], - "metadatas": [ - [item.get("file", {}).get("data", {}).get("meta", {})] - ], + "metadatas": [[item.get("file", {}).get("meta", {})]], } else: # Fallback to item content From e6daad2ab9a7ae5ad5697444275f81dacbb064af Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Mon, 1 Sep 2025 22:45:06 +0400 Subject: [PATCH 0050/1079] chore: bump mermaid --- package-lock.json | 30 +++++++++++++++--------------- package.json | 2 +- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/package-lock.json b/package-lock.json index 4e731101b6..7a9dbea76d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -66,7 +66,7 @@ "leaflet": "^1.9.4", "lowlight": "^3.3.0", "marked": "^9.1.0", - "mermaid": "^11.6.0", + "mermaid": "^11.10.1", "paneforge": "^0.0.6", "panzoom": "^9.4.3", "pdfjs-dist": "^5.4.149", @@ -2224,9 +2224,9 @@ } }, "node_modules/@mermaid-js/parser": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/@mermaid-js/parser/-/parser-0.4.0.tgz", - "integrity": "sha512-wla8XOWvQAwuqy+gxiZqY+c7FokraOTHRWMsbB4AgRx9Sy7zKslNyejy7E+a77qHfey5GXw/ik3IXv/NHMJgaA==", + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/@mermaid-js/parser/-/parser-0.6.2.tgz", + "integrity": "sha512-+PO02uGF6L6Cs0Bw8RpGhikVvMWEysfAyl27qTlroUB8jSWr1lL0Sf6zi78ZxlSnmgSY2AMMKVgghnN9jTtwkQ==", "license": "MIT", "dependencies": { "langium": "3.3.1" @@ -9577,14 +9577,14 @@ } }, "node_modules/mermaid": { - "version": "11.6.0", - "resolved": "https://registry.npmjs.org/mermaid/-/mermaid-11.6.0.tgz", - "integrity": "sha512-PE8hGUy1LDlWIHWBP05SFdqUHGmRcCcK4IzpOKPE35eOw+G9zZgcnMpyunJVUEOgb//KBORPjysKndw8bFLuRg==", + "version": "11.10.1", + "resolved": "https://registry.npmjs.org/mermaid/-/mermaid-11.10.1.tgz", + "integrity": "sha512-0PdeADVWURz7VMAX0+MiMcgfxFKY4aweSGsjgFihe3XlMKNqmai/cugMrqTd3WNHM93V+K+AZL6Wu6tB5HmxRw==", "license": "MIT", "dependencies": { "@braintree/sanitize-url": "^7.0.4", "@iconify/utils": "^2.1.33", - "@mermaid-js/parser": "^0.4.0", + "@mermaid-js/parser": "^0.6.2", "@types/d3": "^7.4.3", "cytoscape": "^3.29.3", "cytoscape-cose-bilkent": "^4.1.0", @@ -9593,11 +9593,11 @@ "d3-sankey": "^0.12.3", "dagre-d3-es": "7.0.11", "dayjs": "^1.11.13", - "dompurify": "^3.2.4", - "katex": "^0.16.9", + "dompurify": "^3.2.5", + "katex": "^0.16.22", "khroma": "^2.1.0", "lodash-es": "^4.17.21", - "marked": "^15.0.7", + "marked": "^16.0.0", "roughjs": "^4.6.6", "stylis": "^4.3.6", "ts-dedent": "^2.2.0", @@ -9605,15 +9605,15 @@ } }, "node_modules/mermaid/node_modules/marked": { - "version": "15.0.8", - "resolved": "https://registry.npmjs.org/marked/-/marked-15.0.8.tgz", - "integrity": "sha512-rli4l2LyZqpQuRve5C0rkn6pj3hT8EWPC+zkAxFTAJLxRbENfTAhEQq9itrmf1Y81QtAX5D/MYlGlIomNgj9lA==", + "version": "16.2.1", + "resolved": "https://registry.npmjs.org/marked/-/marked-16.2.1.tgz", + "integrity": "sha512-r3UrXED9lMlHF97jJByry90cwrZBBvZmjG1L68oYfuPMW+uDTnuMbyJDymCWwbTE+f+3LhpNDKfpR3a3saFyjA==", "license": "MIT", "bin": { "marked": "bin/marked.js" }, "engines": { - "node": ">= 18" + "node": ">= 20" } }, "node_modules/mermaid/node_modules/uuid": { diff --git a/package.json b/package.json index 1be82e605b..ba031b04d9 100644 --- a/package.json +++ b/package.json @@ -110,7 +110,7 @@ "leaflet": "^1.9.4", "lowlight": "^3.3.0", "marked": "^9.1.0", - "mermaid": "^11.6.0", + "mermaid": "^11.10.1", "paneforge": "^0.0.6", "panzoom": "^9.4.3", "pdfjs-dist": "^5.4.149", From e830b4959ecd4b2795e29e53026984a58a7696a9 Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Mon, 1 Sep 2025 22:49:49 +0400 Subject: [PATCH 0051/1079] enh: llama cpp timing stats --- backend/open_webui/utils/middleware.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/backend/open_webui/utils/middleware.py b/backend/open_webui/utils/middleware.py index c2378db885..fa5634edbd 100644 --- a/backend/open_webui/utils/middleware.py +++ b/backend/open_webui/utils/middleware.py @@ -1964,6 +1964,10 @@ async def process_chat_response( } ) usage = data.get("usage", {}) + usage.update( + data.get("timing", {}) + ) # llama.cpp + if usage: await event_emitter( { From 513cab94b107679e3ca4af4f1d2160496758cd9d Mon Sep 17 00:00:00 2001 From: Chanwoo An Date: Tue, 2 Sep 2025 20:53:04 +0900 Subject: [PATCH 0052/1079] fix: prevent double-save race by awaiting API calls and adding isSaving guard --- .../workspace/Knowledge/KnowledgeBase.svelte | 60 +++++++++++-------- 1 file changed, 35 insertions(+), 25 deletions(-) diff --git a/src/lib/components/workspace/Knowledge/KnowledgeBase.svelte b/src/lib/components/workspace/Knowledge/KnowledgeBase.svelte index 5608ff0b74..179f9cfdfa 100644 --- a/src/lib/components/workspace/Knowledge/KnowledgeBase.svelte +++ b/src/lib/components/workspace/Knowledge/KnowledgeBase.svelte @@ -115,6 +115,7 @@ let debounceTimeout = null; let mediaQuery; let dragged = false; + let isSaving = false; const createFileFromText = (name, content) => { const blob = new Blob([content], { type: 'text/plain' }); @@ -434,27 +435,34 @@ }; const updateFileContentHandler = async () => { - const fileId = selectedFile.id; - const content = selectedFileContent; - - // Clear the cache for this file since we're updating it - fileContentCache.delete(fileId); - - const res = updateFileDataContentById(localStorage.token, fileId, content).catch((e) => { - toast.error(`${e}`); - }); - - const updatedKnowledge = await updateFileFromKnowledgeById( - localStorage.token, - id, - fileId - ).catch((e) => { - toast.error(`${e}`); - }); - - if (res && updatedKnowledge) { - knowledge = updatedKnowledge; - toast.success($i18n.t('File content updated successfully.')); + if (isSaving) { + console.log('Save operation already in progress, skipping...'); + return; + } + isSaving = true; + try { + const fileId = selectedFile.id; + const content = selectedFileContent; + // Clear the cache for this file since we're updating it + fileContentCache.delete(fileId); + const res = await updateFileDataContentById(localStorage.token, fileId, content).catch( + (e) => { + toast.error(`${e}`); + } + ); + const updatedKnowledge = await updateFileFromKnowledgeById( + localStorage.token, + id, + fileId + ).catch((e) => { + toast.error(`${e}`); + }); + if (res && updatedKnowledge) { + knowledge = updatedKnowledge; + toast.success($i18n.t('File content updated successfully.')); + } + } finally { + isSaving = false; } }; @@ -779,12 +787,13 @@
@@ -836,12 +845,13 @@
From 182408a52ed1f12f1f292a67cb04bd358217cdb5 Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Tue, 2 Sep 2025 17:03:59 +0400 Subject: [PATCH 0053/1079] refac --- src/lib/components/notes/NoteEditor.svelte | 1 - 1 file changed, 1 deletion(-) diff --git a/src/lib/components/notes/NoteEditor.svelte b/src/lib/components/notes/NoteEditor.svelte index c580b14e9a..4cd1e6b681 100644 --- a/src/lib/components/notes/NoteEditor.svelte +++ b/src/lib/components/notes/NoteEditor.svelte @@ -978,7 +978,6 @@ Provide the enhanced notes in markdown format. Use markdown syntax for headings, disabled={(note?.user_id !== $user?.id && $user?.role !== 'admin') || titleGenerating} required - on:input={changeDebounceHandler} on:focus={() => { titleInputFocused = true; }} From bc6afc905706afce70a235ba4ada78c7468ec360 Mon Sep 17 00:00:00 2001 From: Andreas Fuerer Date: Tue, 2 Sep 2025 15:37:15 +0200 Subject: [PATCH 0054/1079] feature: Azure OpenAI image generation support The image generation API used on Azure OpenAI requires to specify the API version by appending an `api-version` query parameter to the endpoint URL. Added the environment variable `IMAGES_OPENAI_API_VERSION` with configuration functionality in the administration UI. --- backend/open_webui/config.py | 6 +++ backend/open_webui/main.py | 2 + backend/open_webui/routers/images.py | 12 ++++- .../components/admin/Settings/Images.svelte | 48 ++++++++++++++----- 4 files changed, 54 insertions(+), 14 deletions(-) diff --git a/backend/open_webui/config.py b/backend/open_webui/config.py index d62b219668..069faab439 100644 --- a/backend/open_webui/config.py +++ b/backend/open_webui/config.py @@ -3100,6 +3100,12 @@ IMAGES_OPENAI_API_BASE_URL = PersistentConfig( "image_generation.openai.api_base_url", os.getenv("IMAGES_OPENAI_API_BASE_URL", OPENAI_API_BASE_URL), ) +IMAGES_OPENAI_API_VERSION = PersistentConfig( + "IMAGES_OPENAI_API_VERSION", + "image_generation.openai.api_version", + os.getenv("IMAGES_OPENAI_API_VERSION", ""), +) + IMAGES_OPENAI_API_KEY = PersistentConfig( "IMAGES_OPENAI_API_KEY", "image_generation.openai.api_key", diff --git a/backend/open_webui/main.py b/backend/open_webui/main.py index 2245be5bb7..e8c0e267fc 100644 --- a/backend/open_webui/main.py +++ b/backend/open_webui/main.py @@ -157,6 +157,7 @@ from open_webui.config import ( IMAGE_SIZE, IMAGE_STEPS, IMAGES_OPENAI_API_BASE_URL, + IMAGES_OPENAI_API_VERSION, IMAGES_OPENAI_API_KEY, IMAGES_GEMINI_API_BASE_URL, IMAGES_GEMINI_API_KEY, @@ -1019,6 +1020,7 @@ app.state.config.ENABLE_IMAGE_GENERATION = ENABLE_IMAGE_GENERATION app.state.config.ENABLE_IMAGE_PROMPT_GENERATION = ENABLE_IMAGE_PROMPT_GENERATION app.state.config.IMAGES_OPENAI_API_BASE_URL = IMAGES_OPENAI_API_BASE_URL +app.state.config.IMAGES_OPENAI_API_VERSION = IMAGES_OPENAI_API_VERSION app.state.config.IMAGES_OPENAI_API_KEY = IMAGES_OPENAI_API_KEY app.state.config.IMAGES_GEMINI_API_BASE_URL = IMAGES_GEMINI_API_BASE_URL diff --git a/backend/open_webui/routers/images.py b/backend/open_webui/routers/images.py index 9311cb6e2c..f10a9e1690 100644 --- a/backend/open_webui/routers/images.py +++ b/backend/open_webui/routers/images.py @@ -48,6 +48,7 @@ async def get_config(request: Request, user=Depends(get_admin_user)): "prompt_generation": request.app.state.config.ENABLE_IMAGE_PROMPT_GENERATION, "openai": { "OPENAI_API_BASE_URL": request.app.state.config.IMAGES_OPENAI_API_BASE_URL, + "OPENAI_API_VERSION": request.app.state.config.IMAGES_OPENAI_API_VERSION, "OPENAI_API_KEY": request.app.state.config.IMAGES_OPENAI_API_KEY, }, "automatic1111": { @@ -72,6 +73,7 @@ async def get_config(request: Request, user=Depends(get_admin_user)): class OpenAIConfigForm(BaseModel): OPENAI_API_BASE_URL: str + OPENAI_API_VERSION: str OPENAI_API_KEY: str @@ -119,6 +121,9 @@ async def update_config( request.app.state.config.IMAGES_OPENAI_API_BASE_URL = ( form_data.openai.OPENAI_API_BASE_URL ) + request.app.state.config.IMAGES_OPENAI_API_VERSION = ( + form_data.openai.OPENAI_API_VERSION + ) request.app.state.config.IMAGES_OPENAI_API_KEY = form_data.openai.OPENAI_API_KEY request.app.state.config.IMAGES_GEMINI_API_BASE_URL = ( @@ -165,6 +170,7 @@ async def update_config( "prompt_generation": request.app.state.config.ENABLE_IMAGE_PROMPT_GENERATION, "openai": { "OPENAI_API_BASE_URL": request.app.state.config.IMAGES_OPENAI_API_BASE_URL, + "OPENAI_API_VERSION": request.app.state.config.IMAGES_OPENAI_API_VERSION, "OPENAI_API_KEY": request.app.state.config.IMAGES_OPENAI_API_KEY, }, "automatic1111": { @@ -543,11 +549,15 @@ async def image_generations( else {"response_format": "b64_json"} ), } + + api_version_query_param = "" + if (request.app.state.config.IMAGES_OPENAI_API_VERSION): + api_version_query_param = f"?api-version={request.app.state.config.IMAGES_OPENAI_API_VERSION}" # Use asyncio.to_thread for the requests.post call r = await asyncio.to_thread( requests.post, - url=f"{request.app.state.config.IMAGES_OPENAI_API_BASE_URL}/images/generations", + url=f"{request.app.state.config.IMAGES_OPENAI_API_BASE_URL}/images/generations{api_version_query_param}", json=data, headers=headers, ) diff --git a/src/lib/components/admin/Settings/Images.svelte b/src/lib/components/admin/Settings/Images.svelte index 100ec7ad22..37a4092231 100644 --- a/src/lib/components/admin/Settings/Images.svelte +++ b/src/lib/components/admin/Settings/Images.svelte @@ -599,20 +599,42 @@ {/if} {:else if config?.engine === 'openai'}
-
{$i18n.t('OpenAI API Config')}
+
{$i18n.t('OpenAI API Config')}
+
+
+ +
+
+
-
- - - +
+
{$i18n.t('API Key')}
+
+
+ +
+
+
+ +
+
{$i18n.t('API Version')}
+
+
+ +
{:else if config?.engine === 'gemini'} From df0d29c81c83d598aa868275e4362f30678c73a2 Mon Sep 17 00:00:00 2001 From: Andreas Fuerer Date: Tue, 2 Sep 2025 19:15:32 +0200 Subject: [PATCH 0055/1079] style: fix formatting issues --- backend/open_webui/routers/images.py | 8 +++++--- src/lib/components/admin/Settings/Images.svelte | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/backend/open_webui/routers/images.py b/backend/open_webui/routers/images.py index f10a9e1690..802a3e9924 100644 --- a/backend/open_webui/routers/images.py +++ b/backend/open_webui/routers/images.py @@ -549,10 +549,12 @@ async def image_generations( else {"response_format": "b64_json"} ), } - + api_version_query_param = "" - if (request.app.state.config.IMAGES_OPENAI_API_VERSION): - api_version_query_param = f"?api-version={request.app.state.config.IMAGES_OPENAI_API_VERSION}" + if request.app.state.config.IMAGES_OPENAI_API_VERSION: + api_version_query_param = ( + f"?api-version={request.app.state.config.IMAGES_OPENAI_API_VERSION}" + ) # Use asyncio.to_thread for the requests.post call r = await asyncio.to_thread( diff --git a/src/lib/components/admin/Settings/Images.svelte b/src/lib/components/admin/Settings/Images.svelte index 37a4092231..0c19e0ba71 100644 --- a/src/lib/components/admin/Settings/Images.svelte +++ b/src/lib/components/admin/Settings/Images.svelte @@ -624,7 +624,7 @@
- +
{$i18n.t('API Version')}
From 22c4ef4fb096498066b73befe993ae3a82f7a8e7 Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Tue, 2 Sep 2025 21:32:07 +0400 Subject: [PATCH 0056/1079] enh: delete_file query param --- backend/open_webui/routers/knowledge.py | 26 +++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/backend/open_webui/routers/knowledge.py b/backend/open_webui/routers/knowledge.py index e9ba9c39ad..10af496579 100644 --- a/backend/open_webui/routers/knowledge.py +++ b/backend/open_webui/routers/knowledge.py @@ -1,6 +1,6 @@ from typing import List, Optional from pydantic import BaseModel -from fastapi import APIRouter, Depends, HTTPException, status, Request +from fastapi import APIRouter, Depends, HTTPException, status, Request, Query import logging from open_webui.models.knowledge import ( @@ -492,6 +492,7 @@ def update_file_from_knowledge_by_id( def remove_file_from_knowledge_by_id( id: str, form_data: KnowledgeFileIdForm, + delete_file: bool = Query(True), user=Depends(get_verified_user), ): knowledge = Knowledges.get_knowledge_by_id(id=id) @@ -528,18 +529,19 @@ def remove_file_from_knowledge_by_id( log.debug(e) pass - try: - # Remove the file's collection from vector database - file_collection = f"file-{form_data.file_id}" - if VECTOR_DB_CLIENT.has_collection(collection_name=file_collection): - VECTOR_DB_CLIENT.delete_collection(collection_name=file_collection) - except Exception as e: - log.debug("This was most likely caused by bypassing embedding processing") - log.debug(e) - pass + if delete_file: + try: + # Remove the file's collection from vector database + file_collection = f"file-{form_data.file_id}" + if VECTOR_DB_CLIENT.has_collection(collection_name=file_collection): + VECTOR_DB_CLIENT.delete_collection(collection_name=file_collection) + except Exception as e: + log.debug("This was most likely caused by bypassing embedding processing") + log.debug(e) + pass - # Delete file from database - Files.delete_file_by_id(form_data.file_id) + # Delete file from database + Files.delete_file_by_id(form_data.file_id) if knowledge: data = knowledge.data or {} From a980af3ca46d05e6959aacdc324a032bbfcb972b Mon Sep 17 00:00:00 2001 From: viruz Date: Tue, 2 Sep 2025 10:30:19 -0300 Subject: [PATCH 0057/1079] fix: update default name to use i18n translation --- .../Knowledge/KnowledgeBase/AddTextContentModal.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/components/workspace/Knowledge/KnowledgeBase/AddTextContentModal.svelte b/src/lib/components/workspace/Knowledge/KnowledgeBase/AddTextContentModal.svelte index 2e288c6d7e..83844e2c76 100644 --- a/src/lib/components/workspace/Knowledge/KnowledgeBase/AddTextContentModal.svelte +++ b/src/lib/components/workspace/Knowledge/KnowledgeBase/AddTextContentModal.svelte @@ -14,7 +14,7 @@ import VoiceRecording from '$lib/components/chat/MessageInput/VoiceRecording.svelte'; export let show = false; - let name = 'Untitled'; + let name = $i18n.t('Untitled'); let content = ''; let voiceInput = false; From 3ccbb4693809f12380daa5c1e7e180f9afe45310 Mon Sep 17 00:00:00 2001 From: Sihyeon Jang Date: Wed, 3 Sep 2025 05:17:41 +0900 Subject: [PATCH 0058/1079] perf: fix cache key generation for model list caching - Replace Request object with user.id in cache key for get_all_models - Request objects are new instances per HTTP request, preventing cache hits - Cache keys now use user.id ensuring proper cache functionality - Affects both Ollama and OpenAI model list endpoints Signed-off-by: Sihyeon Jang --- backend/open_webui/routers/ollama.py | 2 +- backend/open_webui/routers/openai.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/open_webui/routers/ollama.py b/backend/open_webui/routers/ollama.py index 1a6b75c555..4c5cdce8ca 100644 --- a/backend/open_webui/routers/ollama.py +++ b/backend/open_webui/routers/ollama.py @@ -340,7 +340,7 @@ def merge_ollama_models_lists(model_lists): return list(merged_models.values()) -@cached(ttl=MODELS_CACHE_TTL) +@cached(ttl=MODELS_CACHE_TTL, key=lambda _, user: f"ollama_all_models_{user.id}" if user else "ollama_all_models") async def get_all_models(request: Request, user: UserModel = None): log.info("get_all_models()") if request.app.state.config.ENABLE_OLLAMA_API: diff --git a/backend/open_webui/routers/openai.py b/backend/open_webui/routers/openai.py index 7ba0c5f68a..a94791bdf5 100644 --- a/backend/open_webui/routers/openai.py +++ b/backend/open_webui/routers/openai.py @@ -401,7 +401,7 @@ async def get_filtered_models(models, user): return filtered_models -@cached(ttl=MODELS_CACHE_TTL) +@cached(ttl=MODELS_CACHE_TTL, key=lambda _, user: f"openai_all_models_{user.id}" if user else "openai_all_models") async def get_all_models(request: Request, user: UserModel) -> dict[str, list]: log.info("get_all_models()") From c45201a8a2a31a4faaa61cd038a701e83d8dbe0c Mon Sep 17 00:00:00 2001 From: Sihyeon Jang Date: Wed, 3 Sep 2025 05:25:26 +0900 Subject: [PATCH 0059/1079] perf: fix N+1 query issue in get_prompts method - Replace individual user queries with batch fetching - Use single query to fetch all required users at once - Implement O(1) user lookup with dictionary mapping - Reduce query count from 1+N to 1+1 pattern Signed-off-by: Sihyeon Jang --- backend/open_webui/models/prompts.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/backend/open_webui/models/prompts.py b/backend/open_webui/models/prompts.py index 8ef4cd2bec..706f0096ca 100644 --- a/backend/open_webui/models/prompts.py +++ b/backend/open_webui/models/prompts.py @@ -103,10 +103,16 @@ class PromptsTable: def get_prompts(self) -> list[PromptUserResponse]: with get_db() as db: - prompts = [] + all_prompts = db.query(Prompt).order_by(Prompt.timestamp.desc()).all() - for prompt in db.query(Prompt).order_by(Prompt.timestamp.desc()).all(): - user = Users.get_user_by_id(prompt.user_id) + user_ids = list(set(prompt.user_id for prompt in all_prompts)) + + users = Users.get_users_by_user_ids(user_ids) if user_ids else [] + users_dict = {user.id: user for user in users} + + prompts = [] + for prompt in all_prompts: + user = users_dict.get(prompt.user_id) prompts.append( PromptUserResponse.model_validate( { From f588655f7f4e72f861b96f3278e31f59b6330a66 Mon Sep 17 00:00:00 2001 From: Sihyeon Jang Date: Wed, 3 Sep 2025 05:29:47 +0900 Subject: [PATCH 0060/1079] perf: fix N+1 query issue in get_knowledge_bases method - Replace individual user queries with batch fetching - Use single query to fetch all required users at once - Implement O(1) user lookup with dictionary mapping - Reduce query count from 1+N to 1+1 pattern Signed-off-by: Sihyeon Jang --- backend/open_webui/models/knowledge.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/backend/open_webui/models/knowledge.py b/backend/open_webui/models/knowledge.py index bed3d5542e..29db82e408 100644 --- a/backend/open_webui/models/knowledge.py +++ b/backend/open_webui/models/knowledge.py @@ -128,11 +128,16 @@ class KnowledgeTable: def get_knowledge_bases(self) -> list[KnowledgeUserModel]: with get_db() as db: + all_knowledge = db.query(Knowledge).order_by(Knowledge.updated_at.desc()).all() + + user_ids = list(set(knowledge.user_id for knowledge in all_knowledge)) + + users = Users.get_users_by_user_ids(user_ids) if user_ids else [] + users_dict = {user.id: user for user in users} + knowledge_bases = [] - for knowledge in ( - db.query(Knowledge).order_by(Knowledge.updated_at.desc()).all() - ): - user = Users.get_user_by_id(knowledge.user_id) + for knowledge in all_knowledge: + user = users_dict.get(knowledge.user_id) knowledge_bases.append( KnowledgeUserModel.model_validate( { From c0b3db38a5cf0c5225f84d14045ea05db8e9524b Mon Sep 17 00:00:00 2001 From: Sihyeon Jang Date: Wed, 3 Sep 2025 05:33:41 +0900 Subject: [PATCH 0061/1079] perf: fix N+1 query issue in get_models method - Replace individual user queries with batch fetching - Use single query to fetch all required users at once - Implement O(1) user lookup with dictionary mapping - Reduce query count from 1+N to 1+1 pattern for models with base_model_id Signed-off-by: Sihyeon Jang --- backend/open_webui/models/models.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/backend/open_webui/models/models.py b/backend/open_webui/models/models.py index 1a29b86eae..e450e65ed9 100755 --- a/backend/open_webui/models/models.py +++ b/backend/open_webui/models/models.py @@ -175,9 +175,16 @@ class ModelsTable: def get_models(self) -> list[ModelUserResponse]: with get_db() as db: + all_models = db.query(Model).filter(Model.base_model_id != None).all() + + user_ids = list(set(model.user_id for model in all_models)) + + users = Users.get_users_by_user_ids(user_ids) if user_ids else [] + users_dict = {user.id: user for user in users} + models = [] - for model in db.query(Model).filter(Model.base_model_id != None).all(): - user = Users.get_user_by_id(model.user_id) + for model in all_models: + user = users_dict.get(model.user_id) models.append( ModelUserResponse.model_validate( { From 03d1d2a88b12043086d489090bb9c14594f7de68 Mon Sep 17 00:00:00 2001 From: Sihyeon Jang Date: Wed, 3 Sep 2025 05:35:35 +0900 Subject: [PATCH 0062/1079] perf: fix N+1 query issue in get_tools method - Replace individual user queries with batch fetching - Use single query to fetch all required users at once - Implement O(1) user lookup with dictionary mapping - Reduce query count from 1+N to 1+1 pattern for tools listing Signed-off-by: Sihyeon Jang --- backend/open_webui/models/tools.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/backend/open_webui/models/tools.py b/backend/open_webui/models/tools.py index 7f1409a900..29fc064c4b 100644 --- a/backend/open_webui/models/tools.py +++ b/backend/open_webui/models/tools.py @@ -144,9 +144,16 @@ class ToolsTable: def get_tools(self) -> list[ToolUserModel]: with get_db() as db: + all_tools = db.query(Tool).order_by(Tool.updated_at.desc()).all() + + user_ids = list(set(tool.user_id for tool in all_tools)) + + users = Users.get_users_by_user_ids(user_ids) if user_ids else [] + users_dict = {user.id: user for user in users} + tools = [] - for tool in db.query(Tool).order_by(Tool.updated_at.desc()).all(): - user = Users.get_user_by_id(tool.user_id) + for tool in all_tools: + user = users_dict.get(tool.user_id) tools.append( ToolUserModel.model_validate( { From 0503fbd2e347f34353f4cde218eee70ff67928a4 Mon Sep 17 00:00:00 2001 From: Sihyeon Jang Date: Wed, 3 Sep 2025 05:49:53 +0900 Subject: [PATCH 0063/1079] perf: fix N+1 query issue in tools access control checking - Pre-fetch user group IDs once per request in get_tools endpoint - Pass user_group_ids to has_access to avoid repeated group queries - Optimize access control validation from 1+N to 1+1 query pattern - Reduce database load when checking multiple tools access permissions Signed-off-by: Sihyeon Jang --- backend/open_webui/routers/tools.py | 4 +++- backend/open_webui/utils/access_control.py | 9 ++++++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/backend/open_webui/routers/tools.py b/backend/open_webui/routers/tools.py index c017233765..5f82e7f1bd 100644 --- a/backend/open_webui/routers/tools.py +++ b/backend/open_webui/routers/tools.py @@ -4,6 +4,7 @@ from typing import Optional import time import re import aiohttp +from open_webui.models.groups import Groups from pydantic import BaseModel, HttpUrl from fastapi import APIRouter, Depends, HTTPException, Request, status @@ -71,11 +72,12 @@ async def get_tools(request: Request, user=Depends(get_verified_user)): # Admin can see all tools return tools else: + user_group_ids = {group.id for group in Groups.get_groups_by_member_id(user.id)} tools = [ tool for tool in tools if tool.user_id == user.id - or has_access(user.id, "read", tool.access_control) + or has_access(user.id, "read", tool.access_control, user_group_ids) ] return tools diff --git a/backend/open_webui/utils/access_control.py b/backend/open_webui/utils/access_control.py index c36d861ad6..1529773c44 100644 --- a/backend/open_webui/utils/access_control.py +++ b/backend/open_webui/utils/access_control.py @@ -1,4 +1,4 @@ -from typing import Optional, Union, List, Dict, Any +from typing import Optional, Set, Union, List, Dict, Any from open_webui.models.users import Users, UserModel from open_webui.models.groups import Groups @@ -109,12 +109,15 @@ def has_access( user_id: str, type: str = "write", access_control: Optional[dict] = None, + user_group_ids: Optional[Set[str]] = None, ) -> bool: if access_control is None: return type == "read" - user_groups = Groups.get_groups_by_member_id(user_id) - user_group_ids = [group.id for group in user_groups] + if user_group_ids is None: + user_groups = Groups.get_groups_by_member_id(user_id) + user_group_ids = {group.id for group in user_groups} + permission_access = access_control.get(type, {}) permitted_group_ids = permission_access.get("group_ids", []) permitted_user_ids = permission_access.get("user_ids", []) From eff06538a65cb9685dcdf00c536e13f4996d83c2 Mon Sep 17 00:00:00 2001 From: Sihyeon Jang Date: Wed, 3 Sep 2025 05:56:48 +0900 Subject: [PATCH 0064/1079] perf: fix N+1 query issues in user group access control validation - Pre-fetch user group IDs in get_*_by_user_id methods across models layer - Pass user_group_ids to has_access to avoid repeated group queries - Reduce query count from 1+N to 1+1 pattern for access control validation - Apply consistent optimization across knowledge, models, notes, prompts, and tools Signed-off-by: Sihyeon Jang --- backend/open_webui/models/knowledge.py | 4 +++- backend/open_webui/models/models.py | 4 +++- backend/open_webui/models/notes.py | 4 +++- backend/open_webui/models/prompts.py | 4 +++- backend/open_webui/models/tools.py | 3 ++- 5 files changed, 14 insertions(+), 5 deletions(-) diff --git a/backend/open_webui/models/knowledge.py b/backend/open_webui/models/knowledge.py index bed3d5542e..c0f68cf34d 100644 --- a/backend/open_webui/models/knowledge.py +++ b/backend/open_webui/models/knowledge.py @@ -8,6 +8,7 @@ from open_webui.internal.db import Base, get_db from open_webui.env import SRC_LOG_LEVELS from open_webui.models.files import FileMetadataResponse +from open_webui.models.groups import Groups from open_webui.models.users import Users, UserResponse @@ -147,11 +148,12 @@ class KnowledgeTable: self, user_id: str, permission: str = "write" ) -> list[KnowledgeUserModel]: knowledge_bases = self.get_knowledge_bases() + user_group_ids = {group.id for group in Groups.get_groups_by_member_id(user_id)} return [ knowledge_base for knowledge_base in knowledge_bases if knowledge_base.user_id == user_id - or has_access(user_id, permission, knowledge_base.access_control) + or has_access(user_id, permission, knowledge_base.access_control, user_group_ids) ] def get_knowledge_by_id(self, id: str) -> Optional[KnowledgeModel]: diff --git a/backend/open_webui/models/models.py b/backend/open_webui/models/models.py index 1a29b86eae..defc02572d 100755 --- a/backend/open_webui/models/models.py +++ b/backend/open_webui/models/models.py @@ -5,6 +5,7 @@ from typing import Optional from open_webui.internal.db import Base, JSONField, get_db from open_webui.env import SRC_LOG_LEVELS +from open_webui.models.groups import Groups from open_webui.models.users import Users, UserResponse @@ -199,11 +200,12 @@ class ModelsTable: self, user_id: str, permission: str = "write" ) -> list[ModelUserResponse]: models = self.get_models() + user_group_ids = {group.id for group in Groups.get_groups_by_member_id(user_id)} return [ model for model in models if model.user_id == user_id - or has_access(user_id, permission, model.access_control) + or has_access(user_id, permission, model.access_control, user_group_ids) ] def get_model_by_id(self, id: str) -> Optional[ModelModel]: diff --git a/backend/open_webui/models/notes.py b/backend/open_webui/models/notes.py index ce3b9f2e20..c720ff80a4 100644 --- a/backend/open_webui/models/notes.py +++ b/backend/open_webui/models/notes.py @@ -4,6 +4,7 @@ import uuid from typing import Optional from open_webui.internal.db import Base, get_db +from open_webui.models.groups import Groups from open_webui.utils.access_control import has_access from open_webui.models.users import Users, UserResponse @@ -105,11 +106,12 @@ class NoteTable: self, user_id: str, permission: str = "write" ) -> list[NoteModel]: notes = self.get_notes() + user_group_ids = {group.id for group in Groups.get_groups_by_member_id(user_id)} return [ note for note in notes if note.user_id == user_id - or has_access(user_id, permission, note.access_control) + or has_access(user_id, permission, note.access_control, user_group_ids) ] def get_note_by_id(self, id: str) -> Optional[NoteModel]: diff --git a/backend/open_webui/models/prompts.py b/backend/open_webui/models/prompts.py index 8ef4cd2bec..ef85c047a0 100644 --- a/backend/open_webui/models/prompts.py +++ b/backend/open_webui/models/prompts.py @@ -2,6 +2,7 @@ import time from typing import Optional from open_webui.internal.db import Base, get_db +from open_webui.models.groups import Groups from open_webui.models.users import Users, UserResponse from pydantic import BaseModel, ConfigDict @@ -122,12 +123,13 @@ class PromptsTable: self, user_id: str, permission: str = "write" ) -> list[PromptUserResponse]: prompts = self.get_prompts() + user_group_ids = {group.id for group in Groups.get_groups_by_member_id(user_id)} return [ prompt for prompt in prompts if prompt.user_id == user_id - or has_access(user_id, permission, prompt.access_control) + or has_access(user_id, permission, prompt.access_control, user_group_ids) ] def update_prompt_by_command( diff --git a/backend/open_webui/models/tools.py b/backend/open_webui/models/tools.py index 7f1409a900..fa0fb77893 100644 --- a/backend/open_webui/models/tools.py +++ b/backend/open_webui/models/tools.py @@ -161,12 +161,13 @@ class ToolsTable: self, user_id: str, permission: str = "write" ) -> list[ToolUserModel]: tools = self.get_tools() + user_group_ids = {group.id for group in Groups.get_groups_by_member_id(user_id)} return [ tool for tool in tools if tool.user_id == user_id - or has_access(user_id, permission, tool.access_control) + or has_access(user_id, permission, tool.access_control, user_group_ids) ] def get_tool_valves_by_id(self, id: str) -> Optional[dict]: From 2d627966161e7bfa652fbce9024b3f69510ed133 Mon Sep 17 00:00:00 2001 From: Gary Meng Date: Wed, 3 Sep 2025 13:20:43 +0400 Subject: [PATCH 0065/1079] Allow user get /api/config with auth header --- backend/open_webui/main.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/backend/open_webui/main.py b/backend/open_webui/main.py index d24bd5dcf1..91798b555e 100644 --- a/backend/open_webui/main.py +++ b/backend/open_webui/main.py @@ -1642,8 +1642,18 @@ async def list_tasks_by_chat_id_endpoint( @app.get("/api/config") async def get_app_config(request: Request): user = None - if "token" in request.cookies: + token = None + + auth_header = request.headers.get("Authorization") + if auth_header: + cred = get_http_authorization_cred(auth_header) + if cred: + token = cred.credentials + + if not token and "token" in request.cookies: token = request.cookies.get("token") + + if token: try: data = decode_token(token) except Exception as e: From 4ca936f0bf9813bee11ec8aea41d7e34fb6b16a9 Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Wed, 3 Sep 2025 13:38:07 +0400 Subject: [PATCH 0066/1079] refac --- .../workspace/Knowledge/KnowledgeBase.svelte | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/lib/components/workspace/Knowledge/KnowledgeBase.svelte b/src/lib/components/workspace/Knowledge/KnowledgeBase.svelte index 179f9cfdfa..ebbd9d4eba 100644 --- a/src/lib/components/workspace/Knowledge/KnowledgeBase.svelte +++ b/src/lib/components/workspace/Knowledge/KnowledgeBase.svelte @@ -793,7 +793,12 @@ updateFileContentHandler(); }} > - {isSaving ? $i18n.t('Running...') : $i18n.t('Save')} + {$i18n.t('Save')} + {#if isSaving} +
+ +
+ {/if}
@@ -851,7 +856,12 @@ updateFileContentHandler(); }} > - {isSaving ? $i18n.t('Running...') : $i18n.t('Save')} + {$i18n.t('Save')} + {#if isSaving} +
+ +
+ {/if} From 37bf0087e5b8a324009c9d06b304027df351ea6b Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Wed, 3 Sep 2025 13:57:14 +0400 Subject: [PATCH 0067/1079] refac: tool message format --- backend/open_webui/utils/middleware.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/backend/open_webui/utils/middleware.py b/backend/open_webui/utils/middleware.py index fa5634edbd..d68936d02a 100644 --- a/backend/open_webui/utils/middleware.py +++ b/backend/open_webui/utils/middleware.py @@ -1501,7 +1501,7 @@ async def process_chat_response( tool_result_files = result.get("files", None) break - if tool_result: + if tool_result is not None: tool_calls_display_content = f'{tool_calls_display_content}
\nTool Executed\n
\n' else: tool_calls_display_content = f'{tool_calls_display_content}
\nExecuting...\n
\n' @@ -1616,7 +1616,7 @@ async def process_chat_response( { "role": "tool", "tool_call_id": result["tool_call_id"], - "content": result["content"], + "content": result.get("content", "") or "", } ) temp_blocks = [] @@ -2341,7 +2341,7 @@ async def process_chat_response( results.append( { "tool_call_id": tool_call_id, - "content": tool_result, + "content": tool_result or "", **( {"files": tool_result_files} if tool_result_files From bbe116795860a81a647d9567e0d9cb1950650095 Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Wed, 3 Sep 2025 15:46:42 +0400 Subject: [PATCH 0068/1079] refac/fix: pyodide import issue --- src/lib/components/chat/Messages/CodeBlock.svelte | 2 +- src/routes/+layout.svelte | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/components/chat/Messages/CodeBlock.svelte b/src/lib/components/chat/Messages/CodeBlock.svelte index e3109a25e7..635e7a085e 100644 --- a/src/lib/components/chat/Messages/CodeBlock.svelte +++ b/src/lib/components/chat/Messages/CodeBlock.svelte @@ -222,7 +222,7 @@ code.includes('pandas') ? 'pandas' : null, code.includes('sklearn') ? 'scikit-learn' : null, code.includes('scipy') ? 'scipy' : null, - code.includes('re') ? 'regex' : null, + /\bimport\s+re\b|\bfrom\s+re\b/.test(code) ? 'regex' : null, code.includes('seaborn') ? 'seaborn' : null, code.includes('sympy') ? 'sympy' : null, code.includes('tiktoken') ? 'tiktoken' : null, diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte index 67054cb3ad..88229786da 100644 --- a/src/routes/+layout.svelte +++ b/src/routes/+layout.svelte @@ -126,7 +126,7 @@ code.includes('matplotlib') ? 'matplotlib' : null, code.includes('sklearn') ? 'scikit-learn' : null, code.includes('scipy') ? 'scipy' : null, - code.includes('re') ? 'regex' : null, + /\bimport\s+re\b|\bfrom\s+re\b/.test(code) ? 'regex' : null, code.includes('seaborn') ? 'seaborn' : null, code.includes('sympy') ? 'sympy' : null, code.includes('tiktoken') ? 'tiktoken' : null, From 66bf28cd85b06b498c960ab3b31294b381e3a25a Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Wed, 3 Sep 2025 15:48:07 +0400 Subject: [PATCH 0069/1079] refac --- .../components/chat/Messages/CodeBlock.svelte | 24 +++++++++---------- src/routes/+layout.svelte | 23 +++++++++--------- 2 files changed, 24 insertions(+), 23 deletions(-) diff --git a/src/lib/components/chat/Messages/CodeBlock.svelte b/src/lib/components/chat/Messages/CodeBlock.svelte index 635e7a085e..e954dc7232 100644 --- a/src/lib/components/chat/Messages/CodeBlock.svelte +++ b/src/lib/components/chat/Messages/CodeBlock.svelte @@ -216,19 +216,19 @@ const executePythonAsWorker = async (code) => { let packages = [ - code.includes('requests') ? 'requests' : null, - code.includes('bs4') ? 'beautifulsoup4' : null, - code.includes('numpy') ? 'numpy' : null, - code.includes('pandas') ? 'pandas' : null, - code.includes('sklearn') ? 'scikit-learn' : null, - code.includes('scipy') ? 'scipy' : null, + /\bimport\s+requests\b|\bfrom\s+requests\b/.test(code) ? 'requests' : null, + /\bimport\s+bs4\b|\bfrom\s+bs4\b/.test(code) ? 'beautifulsoup4' : null, + /\bimport\s+numpy\b|\bfrom\s+numpy\b/.test(code) ? 'numpy' : null, + /\bimport\s+pandas\b|\bfrom\s+pandas\b/.test(code) ? 'pandas' : null, + /\bimport\s+matplotlib\b|\bfrom\s+matplotlib\b/.test(code) ? 'matplotlib' : null, + /\bimport\s+seaborn\b|\bfrom\s+seaborn\b/.test(code) ? 'seaborn' : null, + /\bimport\s+sklearn\b|\bfrom\s+sklearn\b/.test(code) ? 'scikit-learn' : null, + /\bimport\s+scipy\b|\bfrom\s+scipy\b/.test(code) ? 'scipy' : null, /\bimport\s+re\b|\bfrom\s+re\b/.test(code) ? 'regex' : null, - code.includes('seaborn') ? 'seaborn' : null, - code.includes('sympy') ? 'sympy' : null, - code.includes('tiktoken') ? 'tiktoken' : null, - code.includes('matplotlib') ? 'matplotlib' : null, - code.includes('pytz') ? 'pytz' : null, - code.includes('openai') ? 'openai' : null + /\bimport\s+seaborn\b|\bfrom\s+seaborn\b/.test(code) ? 'seaborn' : null, + /\bimport\s+sympy\b|\bfrom\s+sympy\b/.test(code) ? 'sympy' : null, + /\bimport\s+tiktoken\b|\bfrom\s+tiktoken\b/.test(code) ? 'tiktoken' : null, + /\bimport\s+pytz\b|\bfrom\s+pytz\b/.test(code) ? 'pytz' : null ].filter(Boolean); console.log(packages); diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte index 88229786da..d0453dc5ed 100644 --- a/src/routes/+layout.svelte +++ b/src/routes/+layout.svelte @@ -119,18 +119,19 @@ let executing = true; let packages = [ - code.includes('requests') ? 'requests' : null, - code.includes('bs4') ? 'beautifulsoup4' : null, - code.includes('numpy') ? 'numpy' : null, - code.includes('pandas') ? 'pandas' : null, - code.includes('matplotlib') ? 'matplotlib' : null, - code.includes('sklearn') ? 'scikit-learn' : null, - code.includes('scipy') ? 'scipy' : null, + /\bimport\s+requests\b|\bfrom\s+requests\b/.test(code) ? 'requests' : null, + /\bimport\s+bs4\b|\bfrom\s+bs4\b/.test(code) ? 'beautifulsoup4' : null, + /\bimport\s+numpy\b|\bfrom\s+numpy\b/.test(code) ? 'numpy' : null, + /\bimport\s+pandas\b|\bfrom\s+pandas\b/.test(code) ? 'pandas' : null, + /\bimport\s+matplotlib\b|\bfrom\s+matplotlib\b/.test(code) ? 'matplotlib' : null, + /\bimport\s+seaborn\b|\bfrom\s+seaborn\b/.test(code) ? 'seaborn' : null, + /\bimport\s+sklearn\b|\bfrom\s+sklearn\b/.test(code) ? 'scikit-learn' : null, + /\bimport\s+scipy\b|\bfrom\s+scipy\b/.test(code) ? 'scipy' : null, /\bimport\s+re\b|\bfrom\s+re\b/.test(code) ? 'regex' : null, - code.includes('seaborn') ? 'seaborn' : null, - code.includes('sympy') ? 'sympy' : null, - code.includes('tiktoken') ? 'tiktoken' : null, - code.includes('pytz') ? 'pytz' : null + /\bimport\s+seaborn\b|\bfrom\s+seaborn\b/.test(code) ? 'seaborn' : null, + /\bimport\s+sympy\b|\bfrom\s+sympy\b/.test(code) ? 'sympy' : null, + /\bimport\s+tiktoken\b|\bfrom\s+tiktoken\b/.test(code) ? 'tiktoken' : null, + /\bimport\s+pytz\b|\bfrom\s+pytz\b/.test(code) ? 'pytz' : null ].filter(Boolean); const pyodideWorker = new PyodideWorker(); From 926954f93b1c9c12cbfb3d039e682403aa23bb5e Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Wed, 3 Sep 2025 18:37:25 +0400 Subject: [PATCH 0070/1079] refac: styling --- src/app.css | 8 ++++++++ src/lib/components/chat/Messages/CodeBlock.svelte | 12 ++++++------ 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/src/app.css b/src/app.css index 7d465210ba..c48914febf 100644 --- a/src/app.css +++ b/src/app.css @@ -282,6 +282,14 @@ input[type='number'] { outline: none; } +.cm-gutters { + @apply !bg-white dark:!bg-black !border-none; +} + +.cm-editor { + @apply bg-white dark:bg-black; +} + .tippy-box[data-theme~='dark'] { @apply rounded-lg bg-gray-950 text-xs border border-gray-900 shadow-xl; } diff --git a/src/lib/components/chat/Messages/CodeBlock.svelte b/src/lib/components/chat/Messages/CodeBlock.svelte index e954dc7232..6697d3e199 100644 --- a/src/lib/components/chat/Messages/CodeBlock.svelte +++ b/src/lib/components/chat/Messages/CodeBlock.svelte @@ -437,7 +437,7 @@ >
@@ -512,7 +512,7 @@ ? '' : 'rounded-b-lg'} overflow-hidden" > -
+
{#if !collapsed} {#if edit} From 7a166152d9d989db6c38c1ff521e18575a915e9b Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Wed, 3 Sep 2025 18:40:25 +0400 Subject: [PATCH 0071/1079] refac: styling --- src/lib/components/common/Banner.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/components/common/Banner.svelte b/src/lib/components/common/Banner.svelte index d135cb605a..a64cc857ff 100644 --- a/src/lib/components/common/Banner.svelte +++ b/src/lib/components/common/Banner.svelte @@ -46,7 +46,7 @@ {#if !dismissed} {#if mounted}
From 51fc7925014ab79f2fb50c2a273d8f29e1184dd3 Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Wed, 3 Sep 2025 18:47:50 +0400 Subject: [PATCH 0072/1079] refac: styling --- src/lib/components/layout/Sidebar.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/components/layout/Sidebar.svelte b/src/lib/components/layout/Sidebar.svelte index b3f20aedb1..ed3eadf4c0 100644 --- a/src/lib/components/layout/Sidebar.svelte +++ b/src/lib/components/layout/Sidebar.svelte @@ -513,7 +513,7 @@ {#if !$mobile && !$showSidebar} +
+ {/if} + {#if azure}
From 71b6a942fe6a9a01baf4ff525e6bf785b850f3f8 Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Wed, 3 Sep 2025 20:53:50 +0400 Subject: [PATCH 0080/1079] refac: styling --- src/lib/components/common/Tags.svelte | 2 +- src/lib/components/layout/Navbar/Menu.svelte | 36 +++++++++---------- .../components/layout/Sidebar/ChatMenu.svelte | 18 +++++----- 3 files changed, 28 insertions(+), 28 deletions(-) diff --git a/src/lib/components/common/Tags.svelte b/src/lib/components/common/Tags.svelte index 6c34b72dee..9ed1a321c7 100644 --- a/src/lib/components/common/Tags.svelte +++ b/src/lib/components/common/Tags.svelte @@ -9,7 +9,7 @@ export let tags = []; -
    +
      { diff --git a/src/lib/components/layout/Navbar/Menu.svelte b/src/lib/components/layout/Navbar/Menu.svelte index a174fe3bbd..344a3f73f4 100644 --- a/src/lib/components/layout/Navbar/Menu.svelte +++ b/src/lib/components/layout/Navbar/Menu.svelte @@ -281,7 +281,7 @@ transition={flyAndScale} >
      @@ -1797,7 +1797,7 @@ > @@ -1816,7 +1816,7 @@ > @@ -1842,7 +1842,7 @@ > From e5ea595425c9d46a431c58bba8cb33f05f252649 Mon Sep 17 00:00:00 2001 From: Shirasawa <764798966@qq.com> Date: Thu, 4 Sep 2025 16:52:19 +0800 Subject: [PATCH 0086/1079] fix: fix error when stopping non-existent task --- backend/open_webui/tasks.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/open_webui/tasks.py b/backend/open_webui/tasks.py index 714c532fca..a15e8ac146 100644 --- a/backend/open_webui/tasks.py +++ b/backend/open_webui/tasks.py @@ -153,9 +153,9 @@ async def stop_task(redis, task_id: str): # Optionally check if task_id still in Redis a few moments later for feedback? return {"status": True, "message": f"Stop signal sent for {task_id}"} - task = tasks.pop(task_id) + task = tasks.pop(task_id, None) if not task: - raise ValueError(f"Task with ID {task_id} not found.") + return {"status": False, "message": f"Task with ID {task_id} not found."} task.cancel() # Request task cancellation try: From c0a47169fa059154d5f5a9ea6b94f9a66d82f255 Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Thu, 4 Sep 2025 22:10:46 +0400 Subject: [PATCH 0087/1079] refac: emoji picker case sensitivity --- src/lib/components/common/EmojiPicker.svelte | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/lib/components/common/EmojiPicker.svelte b/src/lib/components/common/EmojiPicker.svelte index 23f1a465bd..cc761039b8 100644 --- a/src/lib/components/common/EmojiPicker.svelte +++ b/src/lib/components/common/EmojiPicker.svelte @@ -30,16 +30,18 @@ $: { if (search) { emojis = Object.keys(emojiShortCodes).reduce((acc, key) => { - if (key.includes(search)) { + if (key.includes(search.toLowerCase())) { acc[key] = emojiShortCodes[key]; } else { if (Array.isArray(emojiShortCodes[key])) { - const filtered = emojiShortCodes[key].filter((emoji) => emoji.includes(search)); + const filtered = emojiShortCodes[key].filter((emoji) => + emoji.includes(search.toLowerCase()) + ); if (filtered.length) { acc[key] = filtered; } } else { - if (emojiShortCodes[key].includes(search)) { + if (emojiShortCodes[key].includes(search.toLowerCase())) { acc[key] = emojiShortCodes[key]; } } From 49e045ea3d2eeadb35cffc9ffe5945b3346603fe Mon Sep 17 00:00:00 2001 From: Shirasawa <764798966@qq.com> Date: Fri, 5 Sep 2025 17:55:04 +0800 Subject: [PATCH 0088/1079] feat: dynamically load pdfjs --- src/lib/components/chat/MessageInput.svelte | 6 +---- src/lib/utils/index.ts | 28 ++++++++++++++------- 2 files changed, 20 insertions(+), 14 deletions(-) diff --git a/src/lib/components/chat/MessageInput.svelte b/src/lib/components/chat/MessageInput.svelte index d489b8278d..a50c2f1c11 100644 --- a/src/lib/components/chat/MessageInput.svelte +++ b/src/lib/components/chat/MessageInput.svelte @@ -1,8 +1,4 @@ + + + +{#if statusHistory} +
      + {#if showHistory} +
      + {#if statusHistory.length > 1} +
      + +
      + {#each statusHistory as status, idx} + {#if idx !== statusHistory.length - 1} +
      +
      + + + +
      + +
      + {/if} + {/each} +
      + {/if} +
      + {/if} + + {#if statusHistory.length > 0} + {@const status = statusHistory.at(-1)} + + {/if} +
      +{/if} diff --git a/src/lib/components/chat/Messages/ResponseMessage/StatusHistory/StatusItem.svelte b/src/lib/components/chat/Messages/ResponseMessage/StatusHistory/StatusItem.svelte new file mode 100644 index 0000000000..7f325f7f6e --- /dev/null +++ b/src/lib/components/chat/Messages/ResponseMessage/StatusHistory/StatusItem.svelte @@ -0,0 +1,101 @@ + + +{#if !status?.hidden} +
      + {#if status?.action === 'web_search' && (status?.urls || status?.items)} + +
      +
      + + + + {#if status?.description.includes('{{count}}')} + {$i18n.t(status?.description, { + count: (status?.urls || status?.items).length + })} + {:else if status?.description === 'No search query generated'} + {$i18n.t('No search query generated')} + {:else if status?.description === 'Generating search query'} + {$i18n.t('Generating search query')} + {:else} + {status?.description} + {/if} +
      +
      +
      + {:else if status?.action === 'knowledge_search'} +
      +
      + {$i18n.t(`Searching Knowledge for "{{searchQuery}}"`, { + searchQuery: status.query + })} +
      +
      + {:else if status?.action === 'web_search_queries_generated' && status?.queries} +
      +
      + {$i18n.t(`Searching`)} +
      + +
      + {#each status.queries as query, idx (query)} +
      +
      + +
      + + + {query} + +
      + {/each} +
      +
      + {:else} +
      +
      + + {#if status?.description.includes('{{searchQuery}}')} + {$i18n.t(status?.description, { + searchQuery: status?.query + })} + {:else if status?.description === 'No search query generated'} + {$i18n.t('No search query generated')} + {:else if status?.description === 'Generating search query'} + {$i18n.t('Generating search query')} + {:else if status?.description === 'Searching the web'} + {$i18n.t('Searching the web')} + {:else} + {status?.description} + {/if} +
      +
      + {/if} +
      +{/if} diff --git a/src/lib/components/chat/Messages/ResponseMessage/WebSearchResults.svelte b/src/lib/components/chat/Messages/ResponseMessage/WebSearchResults.svelte index 0e81116c5f..31360f7132 100644 --- a/src/lib/components/chat/Messages/ResponseMessage/WebSearchResults.svelte +++ b/src/lib/components/chat/Messages/ResponseMessage/WebSearchResults.svelte @@ -8,20 +8,13 @@ let state = false; - -
      + +
      - - {#if state} - - {:else} - - {/if}
      +
      {#if status?.query} diff --git a/src/lib/components/common/Collapsible.svelte b/src/lib/components/common/Collapsible.svelte index 56e214ae34..b092a49826 100644 --- a/src/lib/components/common/Collapsible.svelte +++ b/src/lib/components/common/Collapsible.svelte @@ -169,7 +169,10 @@
      { + on:click={(e) => { + e.stopPropagation(); + }} + on:pointerup={(e) => { if (!disabled) { open = !open; } diff --git a/src/lib/i18n/locales/ar-BH/translation.json b/src/lib/i18n/locales/ar-BH/translation.json index 659bdba40a..0087fc96c5 100644 --- a/src/lib/i18n/locales/ar-BH/translation.json +++ b/src/lib/i18n/locales/ar-BH/translation.json @@ -1263,7 +1263,7 @@ "Searched {{count}} sites": "", "Searching \"{{searchQuery}}\"": "", "Searching Knowledge for \"{{searchQuery}}\"": "", - "Searching the web...": "", + "Searching the web": "", "Searxng Query URL": "عنوان URL لاستعلام Searxng", "See readme.md for instructions": "readme.md للحصول على التعليمات", "See what's new": "ما الجديد", diff --git a/src/lib/i18n/locales/ar/translation.json b/src/lib/i18n/locales/ar/translation.json index 68724db173..30893078a8 100644 --- a/src/lib/i18n/locales/ar/translation.json +++ b/src/lib/i18n/locales/ar/translation.json @@ -1263,7 +1263,7 @@ "Searched {{count}} sites": "تم البحث في {{count}} مواقع", "Searching \"{{searchQuery}}\"": "جارٍ البحث عن \"{{searchQuery}}\"", "Searching Knowledge for \"{{searchQuery}}\"": "جارٍ البحث في المعرفة عن \"{{searchQuery}}\"", - "Searching the web...": "", + "Searching the web": "", "Searxng Query URL": "عنوان URL لاستعلام Searxng", "See readme.md for instructions": "readme.md للحصول على التعليمات", "See what's new": "ما الجديد", diff --git a/src/lib/i18n/locales/bg-BG/translation.json b/src/lib/i18n/locales/bg-BG/translation.json index 012f5fdf66..69dd424f33 100644 --- a/src/lib/i18n/locales/bg-BG/translation.json +++ b/src/lib/i18n/locales/bg-BG/translation.json @@ -1263,7 +1263,7 @@ "Searched {{count}} sites": "Претърсени {{count}} сайт", "Searching \"{{searchQuery}}\"": "Търсене на \"{{searchQuery}}\"", "Searching Knowledge for \"{{searchQuery}}\"": "Търсене в знанията за \"{{searchQuery}}\"", - "Searching the web...": "Търсене в интернет...", + "Searching the web": "Търсене в интернет...", "Searxng Query URL": "URL адрес на заявка за търсене в Searxng", "See readme.md for instructions": "Вижте readme.md за инструкции", "See what's new": "Виж какво е новото", diff --git a/src/lib/i18n/locales/bn-BD/translation.json b/src/lib/i18n/locales/bn-BD/translation.json index cf6b2498a0..3bca969fb7 100644 --- a/src/lib/i18n/locales/bn-BD/translation.json +++ b/src/lib/i18n/locales/bn-BD/translation.json @@ -1263,7 +1263,7 @@ "Searched {{count}} sites": "", "Searching \"{{searchQuery}}\"": "", "Searching Knowledge for \"{{searchQuery}}\"": "", - "Searching the web...": "", + "Searching the web": "", "Searxng Query URL": "Searxng ক্যোয়ারী URL", "See readme.md for instructions": "নির্দেশিকার জন্য readme.md দেখুন", "See what's new": "নতুন কী আছে দেখুন", diff --git a/src/lib/i18n/locales/bo-TB/translation.json b/src/lib/i18n/locales/bo-TB/translation.json index 5a6adeca78..3472696b31 100644 --- a/src/lib/i18n/locales/bo-TB/translation.json +++ b/src/lib/i18n/locales/bo-TB/translation.json @@ -1263,7 +1263,7 @@ "Searched {{count}} sites": "དྲ་ཚིགས་ {{count}} འཚོལ་བཤེར་བྱས།", "Searching \"{{searchQuery}}\"": "\"{{searchQuery}}\" འཚོལ་བཞིན་པ།", "Searching Knowledge for \"{{searchQuery}}\"": "\"{{searchQuery}}\" ཆེད་དུ་ཤེས་བྱ་འཚོལ་བཞིན་པ།", - "Searching the web...": "", + "Searching the web": "", "Searxng Query URL": "Searxng Query URL", "See readme.md for instructions": "ལམ་སྟོན་ཆེད་དུ་ readme.md ལ་ལྟ་བ།", "See what's new": "གསར་པ་ཅི་ཡོད་ལྟ་བ།", diff --git a/src/lib/i18n/locales/ca-ES/translation.json b/src/lib/i18n/locales/ca-ES/translation.json index d83fd1c8c4..d8a38d5470 100644 --- a/src/lib/i18n/locales/ca-ES/translation.json +++ b/src/lib/i18n/locales/ca-ES/translation.json @@ -1263,7 +1263,7 @@ "Searched {{count}} sites": "S'han cercat {{count}} pàgines", "Searching \"{{searchQuery}}\"": "Cercant \"{{searchQuery}}\"", "Searching Knowledge for \"{{searchQuery}}\"": "Cercant \"{{searchQuery}}\" al coneixement", - "Searching the web...": "Cercant la web...", + "Searching the web": "Cercant la web...", "Searxng Query URL": "URL de consulta de Searxng", "See readme.md for instructions": "Consulta l'arxiu readme.md per obtenir instruccions", "See what's new": "Veure què hi ha de nou", diff --git a/src/lib/i18n/locales/ceb-PH/translation.json b/src/lib/i18n/locales/ceb-PH/translation.json index 862d589352..90fae95c6b 100644 --- a/src/lib/i18n/locales/ceb-PH/translation.json +++ b/src/lib/i18n/locales/ceb-PH/translation.json @@ -1263,7 +1263,7 @@ "Searched {{count}} sites": "", "Searching \"{{searchQuery}}\"": "", "Searching Knowledge for \"{{searchQuery}}\"": "", - "Searching the web...": "", + "Searching the web": "", "Searxng Query URL": "", "See readme.md for instructions": "Tan-awa ang readme.md alang sa mga panudlo", "See what's new": "Tan-awa unsay bag-o", diff --git a/src/lib/i18n/locales/cs-CZ/translation.json b/src/lib/i18n/locales/cs-CZ/translation.json index d027fe8d84..cc8cbc046d 100644 --- a/src/lib/i18n/locales/cs-CZ/translation.json +++ b/src/lib/i18n/locales/cs-CZ/translation.json @@ -1263,7 +1263,7 @@ "Searched {{count}} sites": "Prohledáno {{count}} stránek", "Searching \"{{searchQuery}}\"": "Hledám \"{{searchQuery}}\"", "Searching Knowledge for \"{{searchQuery}}\"": "Hledám ve znalostech \"{{searchQuery}}\"", - "Searching the web...": "Hledám na webu...", + "Searching the web": "Hledám na webu...", "Searxng Query URL": "URL dotazu pro Searxng", "See readme.md for instructions": "Pokyny naleznete v souboru readme.md.", "See what's new": "Podívejte se, co je nového", diff --git a/src/lib/i18n/locales/da-DK/translation.json b/src/lib/i18n/locales/da-DK/translation.json index 63b962204f..d65435642e 100644 --- a/src/lib/i18n/locales/da-DK/translation.json +++ b/src/lib/i18n/locales/da-DK/translation.json @@ -1263,7 +1263,7 @@ "Searched {{count}} sites": "Søgte {{count}} sider", "Searching \"{{searchQuery}}\"": "Søger efter \"{{searchQuery}}\"", "Searching Knowledge for \"{{searchQuery}}\"": "Søger i viden efter \"{{searchQuery}}\"", - "Searching the web...": "Søger på internettet...", + "Searching the web": "Søger på internettet...", "Searxng Query URL": "Searxng forespørgsels-URL", "See readme.md for instructions": "Se readme.md for instruktioner", "See what's new": "Se, hvad der er nyt", diff --git a/src/lib/i18n/locales/de-DE/translation.json b/src/lib/i18n/locales/de-DE/translation.json index 5955d71c13..6579f802a1 100644 --- a/src/lib/i18n/locales/de-DE/translation.json +++ b/src/lib/i18n/locales/de-DE/translation.json @@ -1263,7 +1263,7 @@ "Searched {{count}} sites": "{{count}} Websites durchsucht", "Searching \"{{searchQuery}}\"": "Suche nach \"{{searchQuery}}\"", "Searching Knowledge for \"{{searchQuery}}\"": "Suche im Wissen nach \"{{searchQuery}}\"", - "Searching the web...": "Durchsuche das Web...", + "Searching the web": "Durchsuche das Web...", "Searxng Query URL": "Searxng-Abfrage-URL", "See readme.md for instructions": "Anleitung in readme.md anzeigen", "See what's new": "Entdecken Sie die Neuigkeiten", diff --git a/src/lib/i18n/locales/dg-DG/translation.json b/src/lib/i18n/locales/dg-DG/translation.json index a891994023..26727016ee 100644 --- a/src/lib/i18n/locales/dg-DG/translation.json +++ b/src/lib/i18n/locales/dg-DG/translation.json @@ -1263,7 +1263,7 @@ "Searched {{count}} sites": "", "Searching \"{{searchQuery}}\"": "", "Searching Knowledge for \"{{searchQuery}}\"": "", - "Searching the web...": "", + "Searching the web": "", "Searxng Query URL": "", "See readme.md for instructions": "See readme.md for instructions wow", "See what's new": "See what's new so amaze", diff --git a/src/lib/i18n/locales/el-GR/translation.json b/src/lib/i18n/locales/el-GR/translation.json index c09f33df3c..070f6e6e7a 100644 --- a/src/lib/i18n/locales/el-GR/translation.json +++ b/src/lib/i18n/locales/el-GR/translation.json @@ -1263,7 +1263,7 @@ "Searched {{count}} sites": "", "Searching \"{{searchQuery}}\"": "Αναζήτηση \"{{searchQuery}}\"", "Searching Knowledge for \"{{searchQuery}}\"": "Αναζήτηση Γνώσης για \"{{searchQuery}}\"", - "Searching the web...": "", + "Searching the web": "", "Searxng Query URL": "URL Ερώτησης Searxng", "See readme.md for instructions": "Δείτε readme.md για οδηγίες", "See what's new": "Δείτε τι νέο υπάρχει", diff --git a/src/lib/i18n/locales/en-GB/translation.json b/src/lib/i18n/locales/en-GB/translation.json index d063e9ef3d..47a9f17c1c 100644 --- a/src/lib/i18n/locales/en-GB/translation.json +++ b/src/lib/i18n/locales/en-GB/translation.json @@ -1263,7 +1263,7 @@ "Searched {{count}} sites": "", "Searching \"{{searchQuery}}\"": "", "Searching Knowledge for \"{{searchQuery}}\"": "", - "Searching the web...": "", + "Searching the web": "", "Searxng Query URL": "", "See readme.md for instructions": "", "See what's new": "", diff --git a/src/lib/i18n/locales/en-US/translation.json b/src/lib/i18n/locales/en-US/translation.json index bcf47bb956..9f172b7a3c 100644 --- a/src/lib/i18n/locales/en-US/translation.json +++ b/src/lib/i18n/locales/en-US/translation.json @@ -1263,7 +1263,7 @@ "Searched {{count}} sites": "", "Searching \"{{searchQuery}}\"": "", "Searching Knowledge for \"{{searchQuery}}\"": "", - "Searching the web...": "", + "Searching the web": "", "Searxng Query URL": "", "See readme.md for instructions": "", "See what's new": "", diff --git a/src/lib/i18n/locales/es-ES/translation.json b/src/lib/i18n/locales/es-ES/translation.json index 454be0d79e..a4a23abe3b 100644 --- a/src/lib/i18n/locales/es-ES/translation.json +++ b/src/lib/i18n/locales/es-ES/translation.json @@ -1263,7 +1263,7 @@ "Searched {{count}} sites": "{{count}} sitios buscados", "Searching \"{{searchQuery}}\"": "Buscando \"{{searchQuery}}\"", "Searching Knowledge for \"{{searchQuery}}\"": "Buscando \"{{searchQuery}}\" en Conocimiento", - "Searching the web...": "Buscando en la web...", + "Searching the web": "Buscando en la web...", "Searxng Query URL": "Searxng URL de Consulta", "See readme.md for instructions": "Ver readme.md para instrucciones", "See what's new": "Ver las novedades", diff --git a/src/lib/i18n/locales/et-EE/translation.json b/src/lib/i18n/locales/et-EE/translation.json index 792e553ad5..b1fbe21ffe 100644 --- a/src/lib/i18n/locales/et-EE/translation.json +++ b/src/lib/i18n/locales/et-EE/translation.json @@ -1263,7 +1263,7 @@ "Searched {{count}} sites": "Otsiti {{count}} saidilt", "Searching \"{{searchQuery}}\"": "Otsimine: \"{{searchQuery}}\"", "Searching Knowledge for \"{{searchQuery}}\"": "Teadmistest otsimine: \"{{searchQuery}}\"", - "Searching the web...": "", + "Searching the web": "", "Searxng Query URL": "Searxng päringu URL", "See readme.md for instructions": "Juhiste saamiseks vaadake readme.md", "See what's new": "Vaata, mis on uut", diff --git a/src/lib/i18n/locales/eu-ES/translation.json b/src/lib/i18n/locales/eu-ES/translation.json index 5d78777fec..68eedaa525 100644 --- a/src/lib/i18n/locales/eu-ES/translation.json +++ b/src/lib/i18n/locales/eu-ES/translation.json @@ -1263,7 +1263,7 @@ "Searched {{count}} sites": "", "Searching \"{{searchQuery}}\"": "\"{{searchQuery}}\" bilatzen", "Searching Knowledge for \"{{searchQuery}}\"": "\"{{searchQuery}}\"rentzako ezagutza bilatzen", - "Searching the web...": "", + "Searching the web": "", "Searxng Query URL": "Searxng kontsulta URLa", "See readme.md for instructions": "Ikusi readme.md argibideetarako", "See what's new": "Ikusi berritasunak", diff --git a/src/lib/i18n/locales/fa-IR/translation.json b/src/lib/i18n/locales/fa-IR/translation.json index d74570c6fc..c8bb3072bc 100644 --- a/src/lib/i18n/locales/fa-IR/translation.json +++ b/src/lib/i18n/locales/fa-IR/translation.json @@ -1263,7 +1263,7 @@ "Searched {{count}} sites": "جستجوی {{count}} سایت", "Searching \"{{searchQuery}}\"": "جستجوی «{{searchQuery}}»", "Searching Knowledge for \"{{searchQuery}}\"": "جستجوی دانش برای «{{searchQuery}}»", - "Searching the web...": "", + "Searching the web": "", "Searxng Query URL": "نشانی وب جستجوی Searxng", "See readme.md for instructions": "برای مشاهده دستورالعمل\u200cها به readme.md مراجعه کنید", "See what's new": "ببینید موارد جدید چه بوده", diff --git a/src/lib/i18n/locales/fi-FI/translation.json b/src/lib/i18n/locales/fi-FI/translation.json index 1b4a7d21ea..91a0f28a35 100644 --- a/src/lib/i18n/locales/fi-FI/translation.json +++ b/src/lib/i18n/locales/fi-FI/translation.json @@ -1263,7 +1263,7 @@ "Searched {{count}} sites": "Etsitty {{count}} sivulta", "Searching \"{{searchQuery}}\"": "Haetaan \"{{searchQuery}}\"", "Searching Knowledge for \"{{searchQuery}}\"": "Haetaan tietämystä \"{{searchQuery}}\"", - "Searching the web...": "Haetaan verkosta...", + "Searching the web": "Haetaan verkosta...", "Searxng Query URL": "Searxng-kyselyn verkko-osoite", "See readme.md for instructions": "Katso ohjeet readme.md-tiedostosta", "See what's new": "Katso, mitä uutta", diff --git a/src/lib/i18n/locales/fr-CA/translation.json b/src/lib/i18n/locales/fr-CA/translation.json index 093200f411..0a9393e0da 100644 --- a/src/lib/i18n/locales/fr-CA/translation.json +++ b/src/lib/i18n/locales/fr-CA/translation.json @@ -1263,7 +1263,7 @@ "Searched {{count}} sites": "{{count}} sites recherchés", "Searching \"{{searchQuery}}\"": "Recherche de « {{searchQuery}} »", "Searching Knowledge for \"{{searchQuery}}\"": "Recherche des connaissances pour « {{searchQuery}} »", - "Searching the web...": "Recherche sur internet...", + "Searching the web": "Recherche sur internet...", "Searxng Query URL": "URL de recherche Searxng", "See readme.md for instructions": "Voir le fichier readme.md pour les instructions", "See what's new": "Découvrez les nouvelles fonctionnalités", diff --git a/src/lib/i18n/locales/fr-FR/translation.json b/src/lib/i18n/locales/fr-FR/translation.json index 2d6ce819d9..db15fe1f32 100644 --- a/src/lib/i18n/locales/fr-FR/translation.json +++ b/src/lib/i18n/locales/fr-FR/translation.json @@ -1263,7 +1263,7 @@ "Searched {{count}} sites": "{{count}} sites recherchés", "Searching \"{{searchQuery}}\"": "Recherche de « {{searchQuery}} »", "Searching Knowledge for \"{{searchQuery}}\"": "Recherche des connaissances pour « {{searchQuery}} »", - "Searching the web...": "Recherche sur internet...", + "Searching the web": "Recherche sur internet...", "Searxng Query URL": "URL de recherche Searxng", "See readme.md for instructions": "Voir le fichier readme.md pour les instructions", "See what's new": "Découvrez les nouvelles fonctionnalités", diff --git a/src/lib/i18n/locales/gl-ES/translation.json b/src/lib/i18n/locales/gl-ES/translation.json index 45a2ec2ce6..3f3950d1a1 100644 --- a/src/lib/i18n/locales/gl-ES/translation.json +++ b/src/lib/i18n/locales/gl-ES/translation.json @@ -1263,7 +1263,7 @@ "Searched {{count}} sites": "Buscadas {{count}} sitios", "Searching \"{{searchQuery}}\"": "Buscando \"{{searchQuery}}\"", "Searching Knowledge for \"{{searchQuery}}\"": "Buscando coñecemento para \"{{searchQuery}}\"", - "Searching the web...": "", + "Searching the web": "", "Searxng Query URL": "Searxng URL de consulta", "See readme.md for instructions": "Vea o readme.md para instruccions", "See what's new": "Ver as novedades", diff --git a/src/lib/i18n/locales/he-IL/translation.json b/src/lib/i18n/locales/he-IL/translation.json index c35cfaefab..e08670d061 100644 --- a/src/lib/i18n/locales/he-IL/translation.json +++ b/src/lib/i18n/locales/he-IL/translation.json @@ -1263,7 +1263,7 @@ "Searched {{count}} sites": "", "Searching \"{{searchQuery}}\"": "", "Searching Knowledge for \"{{searchQuery}}\"": "", - "Searching the web...": "", + "Searching the web": "", "Searxng Query URL": "כתובת URL של שאילתת Searxng", "See readme.md for instructions": "ראה את readme.md להוראות", "See what's new": "ראה מה חדש", diff --git a/src/lib/i18n/locales/hi-IN/translation.json b/src/lib/i18n/locales/hi-IN/translation.json index c1533096c1..224c379e3b 100644 --- a/src/lib/i18n/locales/hi-IN/translation.json +++ b/src/lib/i18n/locales/hi-IN/translation.json @@ -1263,7 +1263,7 @@ "Searched {{count}} sites": "", "Searching \"{{searchQuery}}\"": "", "Searching Knowledge for \"{{searchQuery}}\"": "", - "Searching the web...": "", + "Searching the web": "", "Searxng Query URL": "Searxng क्वेरी URL", "See readme.md for instructions": "निर्देशों के लिए readme.md देखें", "See what's new": "देखें, क्या नया है", diff --git a/src/lib/i18n/locales/hr-HR/translation.json b/src/lib/i18n/locales/hr-HR/translation.json index 9eb5e405f4..d0585d85ae 100644 --- a/src/lib/i18n/locales/hr-HR/translation.json +++ b/src/lib/i18n/locales/hr-HR/translation.json @@ -1263,7 +1263,7 @@ "Searched {{count}} sites": "", "Searching \"{{searchQuery}}\"": "", "Searching Knowledge for \"{{searchQuery}}\"": "", - "Searching the web...": "", + "Searching the web": "", "Searxng Query URL": "Searxng URL upita", "See readme.md for instructions": "Pogledajte readme.md za upute", "See what's new": "Pogledajte što je novo", diff --git a/src/lib/i18n/locales/hu-HU/translation.json b/src/lib/i18n/locales/hu-HU/translation.json index 016f3bbbe7..90dea5b83f 100644 --- a/src/lib/i18n/locales/hu-HU/translation.json +++ b/src/lib/i18n/locales/hu-HU/translation.json @@ -1263,7 +1263,7 @@ "Searched {{count}} sites": "{{count}} oldal keresése megtörtént", "Searching \"{{searchQuery}}\"": "Keresés: \"{{searchQuery}}\"", "Searching Knowledge for \"{{searchQuery}}\"": "Tudásbázis keresése: \"{{searchQuery}}\"", - "Searching the web...": "", + "Searching the web": "", "Searxng Query URL": "Searxng lekérdezési URL", "See readme.md for instructions": "Lásd a readme.md fájlt az útmutatásért", "See what's new": "Újdonságok megtekintése", diff --git a/src/lib/i18n/locales/id-ID/translation.json b/src/lib/i18n/locales/id-ID/translation.json index 0b106d19e0..25bdbaee13 100644 --- a/src/lib/i18n/locales/id-ID/translation.json +++ b/src/lib/i18n/locales/id-ID/translation.json @@ -1263,7 +1263,7 @@ "Searched {{count}} sites": "", "Searching \"{{searchQuery}}\"": "Mencari \"{{searchQuery}}\"", "Searching Knowledge for \"{{searchQuery}}\"": "", - "Searching the web...": "", + "Searching the web": "", "Searxng Query URL": "URL Kueri Pencarian Searxng", "See readme.md for instructions": "Lihat readme.md untuk instruksi", "See what's new": "Lihat apa yang baru", diff --git a/src/lib/i18n/locales/ie-GA/translation.json b/src/lib/i18n/locales/ie-GA/translation.json index bfa21e96c2..b617131ceb 100644 --- a/src/lib/i18n/locales/ie-GA/translation.json +++ b/src/lib/i18n/locales/ie-GA/translation.json @@ -1263,7 +1263,7 @@ "Searched {{count}} sites": "Cuardaíodh {{count}} suíomh", "Searching \"{{searchQuery}}\"": "Ag cuardach \"{{searchQuery}}\"", "Searching Knowledge for \"{{searchQuery}}\"": "Cuardach Eolas do \"{{searchQuery}}\"", - "Searching the web...": "Ag cuardach an ghréasáin...", + "Searching the web": "Ag cuardach an ghréasáin...", "Searxng Query URL": "URL ceisteanna cuardaigh", "See readme.md for instructions": "Féach readme.md le haghaidh treoracha", "See what's new": "Féach cad atá nua", diff --git a/src/lib/i18n/locales/it-IT/translation.json b/src/lib/i18n/locales/it-IT/translation.json index 891ff74fe6..5a1d172b1d 100644 --- a/src/lib/i18n/locales/it-IT/translation.json +++ b/src/lib/i18n/locales/it-IT/translation.json @@ -1263,7 +1263,7 @@ "Searched {{count}} sites": "Cercati {{count}} siti", "Searching \"{{searchQuery}}\"": "Cercando \"{{searchQuery}}\"", "Searching Knowledge for \"{{searchQuery}}\"": "Cercando conoscenza per \"{{searchQuery}}\"", - "Searching the web...": "Cercando nel web...", + "Searching the web": "Cercando nel web...", "Searxng Query URL": "Searxng Query URL", "See readme.md for instructions": "Vedi readme.md per le istruzioni", "See what's new": "Guarda le novità", diff --git a/src/lib/i18n/locales/ja-JP/translation.json b/src/lib/i18n/locales/ja-JP/translation.json index 82235e95fc..d856cfe387 100644 --- a/src/lib/i18n/locales/ja-JP/translation.json +++ b/src/lib/i18n/locales/ja-JP/translation.json @@ -1263,7 +1263,7 @@ "Searched {{count}} sites": "{{count}} サイトを検索しました", "Searching \"{{searchQuery}}\"": "「{{searchQuery}}」を検索中...", "Searching Knowledge for \"{{searchQuery}}\"": "「{{searchQuery}}」のナレッジを検索中...", - "Searching the web...": "ウェブを検索中...", + "Searching the web": "ウェブを検索中...", "Searxng Query URL": "Searxng クエリ URL", "See readme.md for instructions": "手順については readme.md を参照してください", "See what's new": "新機能を見る", diff --git a/src/lib/i18n/locales/ka-GE/translation.json b/src/lib/i18n/locales/ka-GE/translation.json index f90d197991..f8449b4d6e 100644 --- a/src/lib/i18n/locales/ka-GE/translation.json +++ b/src/lib/i18n/locales/ka-GE/translation.json @@ -1263,7 +1263,7 @@ "Searched {{count}} sites": "მოძებნილია {{count}} საიტზე", "Searching \"{{searchQuery}}\"": "მიმდინარეობს ძებნა \"{{searchQuery}}\"", "Searching Knowledge for \"{{searchQuery}}\"": "", - "Searching the web...": "", + "Searching the web": "", "Searxng Query URL": "Searxng Query URL", "See readme.md for instructions": "ინსტრუქციებისთვის იხილეთ readme.md", "See what's new": "ნახეთ, რა არის ახალი", diff --git a/src/lib/i18n/locales/kab-DZ/translation.json b/src/lib/i18n/locales/kab-DZ/translation.json index 6657f8811b..154078c895 100644 --- a/src/lib/i18n/locales/kab-DZ/translation.json +++ b/src/lib/i18n/locales/kab-DZ/translation.json @@ -1263,7 +1263,7 @@ "Searched {{count}} sites": "Inuda deg {{count}} n yismal web", "Searching \"{{searchQuery}}\"": "Anadi ɣef \"{{searchQuery}}\"", "Searching Knowledge for \"{{searchQuery}}\"": "Anadi n tmessunin ɣef \"{{searchQuery}}\"", - "Searching the web...": "Anadi deg web…", + "Searching the web": "Anadi deg web…", "Searxng Query URL": "URL n unadi Searxng", "See readme.md for instructions": "Ẓer taɣuṛi i lewṣaya", "See what's new": "Wali d acu i yellan d amaynut", diff --git a/src/lib/i18n/locales/ko-KR/translation.json b/src/lib/i18n/locales/ko-KR/translation.json index ce61d0af9c..9e24750c85 100644 --- a/src/lib/i18n/locales/ko-KR/translation.json +++ b/src/lib/i18n/locales/ko-KR/translation.json @@ -1263,7 +1263,7 @@ "Searched {{count}} sites": "{{count}}개 사이트 검색됨", "Searching \"{{searchQuery}}\"": "\"{{searchQuery}}\" 검색 중", "Searching Knowledge for \"{{searchQuery}}\"": "\"{{searchQuery}}\"에 대한 지식 기반 검색 중", - "Searching the web...": "웹에서 검색 중...", + "Searching the web": "웹에서 검색 중...", "Searxng Query URL": "Searxng 쿼리 URL", "See readme.md for instructions": "설명은 readme.md를 참조하세요.", "See what's new": "새로운 기능 보기", diff --git a/src/lib/i18n/locales/lt-LT/translation.json b/src/lib/i18n/locales/lt-LT/translation.json index 9ca884f652..343d5d7203 100644 --- a/src/lib/i18n/locales/lt-LT/translation.json +++ b/src/lib/i18n/locales/lt-LT/translation.json @@ -1263,7 +1263,7 @@ "Searched {{count}} sites": "", "Searching \"{{searchQuery}}\"": "Ieškoma \"{{searchQuery}}\"", "Searching Knowledge for \"{{searchQuery}}\"": "", - "Searching the web...": "", + "Searching the web": "", "Searxng Query URL": "Searxng užklausos URL", "See readme.md for instructions": "Žiūrėti readme.md papildomoms instrukcijoms", "See what's new": "Žiūrėti naujoves", diff --git a/src/lib/i18n/locales/ms-MY/translation.json b/src/lib/i18n/locales/ms-MY/translation.json index bddd127f28..622cf681ae 100644 --- a/src/lib/i18n/locales/ms-MY/translation.json +++ b/src/lib/i18n/locales/ms-MY/translation.json @@ -1263,7 +1263,7 @@ "Searched {{count}} sites": "", "Searching \"{{searchQuery}}\"": "encari \"{{ searchQuery }}\"", "Searching Knowledge for \"{{searchQuery}}\"": "", - "Searching the web...": "", + "Searching the web": "", "Searxng Query URL": "URL Pertanyaan Searxng", "See readme.md for instructions": "Lihat readme.md untuk arahan", "See what's new": "Lihat apa yang terbaru", diff --git a/src/lib/i18n/locales/nb-NO/translation.json b/src/lib/i18n/locales/nb-NO/translation.json index 891673d510..6b0594558e 100644 --- a/src/lib/i18n/locales/nb-NO/translation.json +++ b/src/lib/i18n/locales/nb-NO/translation.json @@ -1263,7 +1263,7 @@ "Searched {{count}} sites": "Søkte på {{count}} nettsider", "Searching \"{{searchQuery}}\"": "Søker etter \"{{searchQuery}}\"", "Searching Knowledge for \"{{searchQuery}}\"": "Søker etter kunnskap for \"{{searchQuery}}\"", - "Searching the web...": "", + "Searching the web": "", "Searxng Query URL": "Searxng forespørsels-URL", "See readme.md for instructions": "Se readme.md for å få instruksjoner", "See what's new": "Se hva som er nytt", diff --git a/src/lib/i18n/locales/nl-NL/translation.json b/src/lib/i18n/locales/nl-NL/translation.json index 5c323a0ade..a2b17497a9 100644 --- a/src/lib/i18n/locales/nl-NL/translation.json +++ b/src/lib/i18n/locales/nl-NL/translation.json @@ -1263,7 +1263,7 @@ "Searched {{count}} sites": "Zocht op {{count}} sites", "Searching \"{{searchQuery}}\"": "\"{{searchQuery}}\" aan het zoeken.", "Searching Knowledge for \"{{searchQuery}}\"": "Zoek kennis bij \"{{searchQuery}}\"", - "Searching the web...": "", + "Searching the web": "", "Searxng Query URL": "Searxng Query URL", "See readme.md for instructions": "Zie readme.md voor instructies", "See what's new": "Zie wat er nieuw is", diff --git a/src/lib/i18n/locales/pa-IN/translation.json b/src/lib/i18n/locales/pa-IN/translation.json index c2a158195c..13e320892b 100644 --- a/src/lib/i18n/locales/pa-IN/translation.json +++ b/src/lib/i18n/locales/pa-IN/translation.json @@ -1263,7 +1263,7 @@ "Searched {{count}} sites": "", "Searching \"{{searchQuery}}\"": "", "Searching Knowledge for \"{{searchQuery}}\"": "", - "Searching the web...": "", + "Searching the web": "", "Searxng Query URL": "Searxng Query URL", "See readme.md for instructions": "ਹਦਾਇਤਾਂ ਲਈ readme.md ਵੇਖੋ", "See what's new": "ਨਵਾਂ ਕੀ ਹੈ ਵੇਖੋ", diff --git a/src/lib/i18n/locales/pl-PL/translation.json b/src/lib/i18n/locales/pl-PL/translation.json index ab88c58cb2..4822919795 100644 --- a/src/lib/i18n/locales/pl-PL/translation.json +++ b/src/lib/i18n/locales/pl-PL/translation.json @@ -1263,7 +1263,7 @@ "Searched {{count}} sites": "Przeszukano {{count}} stron", "Searching \"{{searchQuery}}\"": "Wyszukiwanie \"{{searchQuery}}\"", "Searching Knowledge for \"{{searchQuery}}\"": "Przeszukiwanie wiedzy dla \"{{searchQuery}}\"", - "Searching the web...": "Przeszukuję sieć Web...", + "Searching the web": "Przeszukuję sieć Web...", "Searxng Query URL": "Adres URL zapytania Searxng", "See readme.md for instructions": "Sprawdź readme.md dla instrukcji", "See what's new": "Sprawdź nowości", diff --git a/src/lib/i18n/locales/pt-BR/translation.json b/src/lib/i18n/locales/pt-BR/translation.json index f216814eb1..6453b7ebcd 100644 --- a/src/lib/i18n/locales/pt-BR/translation.json +++ b/src/lib/i18n/locales/pt-BR/translation.json @@ -1263,7 +1263,7 @@ "Searched {{count}} sites": "{{count}} sites pesquisados", "Searching \"{{searchQuery}}\"": "Pesquisando \"{{searchQuery}}\"", "Searching Knowledge for \"{{searchQuery}}\"": "Buscando conhecimento para \"{{searchQuery}}\"", - "Searching the web...": "Pesquisando na Internet...", + "Searching the web": "Pesquisando na Internet...", "Searxng Query URL": "URL da Consulta Searxng", "See readme.md for instructions": "Veja readme.md para instruções", "See what's new": "Veja o que há de novo", diff --git a/src/lib/i18n/locales/pt-PT/translation.json b/src/lib/i18n/locales/pt-PT/translation.json index 058b580035..f1c0df3721 100644 --- a/src/lib/i18n/locales/pt-PT/translation.json +++ b/src/lib/i18n/locales/pt-PT/translation.json @@ -1263,7 +1263,7 @@ "Searched {{count}} sites": "", "Searching \"{{searchQuery}}\"": "", "Searching Knowledge for \"{{searchQuery}}\"": "", - "Searching the web...": "", + "Searching the web": "", "Searxng Query URL": "URL de consulta Searxng", "See readme.md for instructions": "Consulte readme.md para obter instruções", "See what's new": "Veja o que há de novo", diff --git a/src/lib/i18n/locales/ro-RO/translation.json b/src/lib/i18n/locales/ro-RO/translation.json index 7c5e927db3..9e66001058 100644 --- a/src/lib/i18n/locales/ro-RO/translation.json +++ b/src/lib/i18n/locales/ro-RO/translation.json @@ -1263,7 +1263,7 @@ "Searched {{count}} sites": "", "Searching \"{{searchQuery}}\"": "Căutare \"{{searchQuery}}\"", "Searching Knowledge for \"{{searchQuery}}\"": "Căutare cunoștințe pentru \"{{searchQuery}}\"", - "Searching the web...": "", + "Searching the web": "", "Searxng Query URL": "URL Interogare Searxng", "See readme.md for instructions": "Consultați readme.md pentru instrucțiuni", "See what's new": "Vezi ce e nou", diff --git a/src/lib/i18n/locales/ru-RU/translation.json b/src/lib/i18n/locales/ru-RU/translation.json index be10181aec..92639ac759 100644 --- a/src/lib/i18n/locales/ru-RU/translation.json +++ b/src/lib/i18n/locales/ru-RU/translation.json @@ -1263,7 +1263,7 @@ "Searched {{count}} sites": "Поиск по {{count}} сайтам", "Searching \"{{searchQuery}}\"": "Поиск по запросу \"{{searchQuery}}\"", "Searching Knowledge for \"{{searchQuery}}\"": "Поиск знания для \"{{searchQuery}}\"", - "Searching the web...": "Поиск в интернете...", + "Searching the web": "Поиск в интернете...", "Searxng Query URL": "URL-адрес запроса Searxng", "See readme.md for instructions": "Смотрите readme.md для инструкций", "See what's new": "Посмотреть, что нового", diff --git a/src/lib/i18n/locales/sk-SK/translation.json b/src/lib/i18n/locales/sk-SK/translation.json index 92ccc3f2c5..81c56d3cea 100644 --- a/src/lib/i18n/locales/sk-SK/translation.json +++ b/src/lib/i18n/locales/sk-SK/translation.json @@ -1263,7 +1263,7 @@ "Searched {{count}} sites": "", "Searching \"{{searchQuery}}\"": "Hľadanie \"{{searchQuery}}\"", "Searching Knowledge for \"{{searchQuery}}\"": "Vyhľadávanie znalostí pre \"{{searchQuery}}\"", - "Searching the web...": "", + "Searching the web": "", "Searxng Query URL": "Adresa URL dotazu Searxng", "See readme.md for instructions": "Pozrite si {{readme.md}} pre pokyny.", "See what's new": "Pozrite sa, čo je nové", diff --git a/src/lib/i18n/locales/sr-RS/translation.json b/src/lib/i18n/locales/sr-RS/translation.json index 4f5cae32d7..27aecc40bc 100644 --- a/src/lib/i18n/locales/sr-RS/translation.json +++ b/src/lib/i18n/locales/sr-RS/translation.json @@ -1263,7 +1263,7 @@ "Searched {{count}} sites": "", "Searching \"{{searchQuery}}\"": "", "Searching Knowledge for \"{{searchQuery}}\"": "", - "Searching the web...": "", + "Searching the web": "", "Searxng Query URL": "УРЛ адреса Сеарxнг упита", "See readme.md for instructions": "Погледај readme.md за упутства", "See what's new": "Погледај шта је ново", diff --git a/src/lib/i18n/locales/sv-SE/translation.json b/src/lib/i18n/locales/sv-SE/translation.json index a6743b1102..23eed43f69 100644 --- a/src/lib/i18n/locales/sv-SE/translation.json +++ b/src/lib/i18n/locales/sv-SE/translation.json @@ -1263,7 +1263,7 @@ "Searched {{count}} sites": "Sökte på {{count}} webbplatser", "Searching \"{{searchQuery}}\"": "Söker \"{{searchQuery}}\"", "Searching Knowledge for \"{{searchQuery}}\"": "Söker kunskap efter \"{{searchQuery}}\"", - "Searching the web...": "Söker på webben...", + "Searching the web": "Söker på webben...", "Searxng Query URL": "Searxng Query URL", "See readme.md for instructions": "Se readme.md för instruktioner", "See what's new": "Se vad som är nytt", diff --git a/src/lib/i18n/locales/th-TH/translation.json b/src/lib/i18n/locales/th-TH/translation.json index d9b03389ae..ddcb1073d3 100644 --- a/src/lib/i18n/locales/th-TH/translation.json +++ b/src/lib/i18n/locales/th-TH/translation.json @@ -1263,7 +1263,7 @@ "Searched {{count}} sites": "", "Searching \"{{searchQuery}}\"": "กำลังค้นหา \"{{searchQuery}}\"", "Searching Knowledge for \"{{searchQuery}}\"": "", - "Searching the web...": "", + "Searching the web": "", "Searxng Query URL": "URL คำค้นหา", "See readme.md for instructions": "ดู readme.md สำหรับคำแนะนำ", "See what's new": "ดูสิ่งที่ใหม่", diff --git a/src/lib/i18n/locales/tk-TM/translation.json b/src/lib/i18n/locales/tk-TM/translation.json index 039c301198..227e302006 100644 --- a/src/lib/i18n/locales/tk-TM/translation.json +++ b/src/lib/i18n/locales/tk-TM/translation.json @@ -1263,7 +1263,7 @@ "Searched {{count}} sites": "", "Searching \"{{searchQuery}}\"": "", "Searching Knowledge for \"{{searchQuery}}\"": "", - "Searching the web...": "", + "Searching the web": "", "Searxng Query URL": "", "See readme.md for instructions": "", "See what's new": "", diff --git a/src/lib/i18n/locales/tr-TR/translation.json b/src/lib/i18n/locales/tr-TR/translation.json index be131eee27..1bb5e22e21 100644 --- a/src/lib/i18n/locales/tr-TR/translation.json +++ b/src/lib/i18n/locales/tr-TR/translation.json @@ -1263,7 +1263,7 @@ "Searched {{count}} sites": "{{count}} site arandı", "Searching \"{{searchQuery}}\"": "\"{{searchQuery}}\" aranıyor", "Searching Knowledge for \"{{searchQuery}}\"": "\"{{searchQuery}}\" için Bilgi aranıyor", - "Searching the web...": "İnternette aranıyor...", + "Searching the web": "İnternette aranıyor...", "Searxng Query URL": "Searxng Sorgu URL'si", "See readme.md for instructions": "Yönergeler için readme.md dosyasına bakın", "See what's new": "Yeniliklere göz atın", diff --git a/src/lib/i18n/locales/ug-CN/translation.json b/src/lib/i18n/locales/ug-CN/translation.json index 3dd5aeb111..dfc13ef660 100644 --- a/src/lib/i18n/locales/ug-CN/translation.json +++ b/src/lib/i18n/locales/ug-CN/translation.json @@ -1263,7 +1263,7 @@ "Searched {{count}} sites": "{{count}} تور بېكەت ئىزدەندى", "Searching \"{{searchQuery}}\"": "\"{{searchQuery}}\" ئىزدەۋاتىدۇ", "Searching Knowledge for \"{{searchQuery}}\"": "\"{{searchQuery}}\" ئۈچۈن بىلىم ئىزدەۋاتىدۇ", - "Searching the web...": "تور ئىزدەۋاتىدۇ...", + "Searching the web": "تور ئىزدەۋاتىدۇ...", "Searxng Query URL": "Searxng ئىزدەش URL", "See readme.md for instructions": "قوللانما ئۈچۈن readme.md غا قاراڭ", "See what's new": "يېڭىلىقلارنى كۆرۈش", diff --git a/src/lib/i18n/locales/uk-UA/translation.json b/src/lib/i18n/locales/uk-UA/translation.json index 4ddad9df64..50b142e660 100644 --- a/src/lib/i18n/locales/uk-UA/translation.json +++ b/src/lib/i18n/locales/uk-UA/translation.json @@ -1263,7 +1263,7 @@ "Searched {{count}} sites": "Шукалося {{count}} сайтів", "Searching \"{{searchQuery}}\"": "Шукаю \"{{searchQuery}}\"", "Searching Knowledge for \"{{searchQuery}}\"": "Пошук знань для \"{{searchQuery}}\"", - "Searching the web...": "", + "Searching the web": "", "Searxng Query URL": "Searxng Query URL", "See readme.md for instructions": "Див. readme.md для інструкцій", "See what's new": "Подивіться, що нового", diff --git a/src/lib/i18n/locales/ur-PK/translation.json b/src/lib/i18n/locales/ur-PK/translation.json index 9168f15573..daa09e7f5f 100644 --- a/src/lib/i18n/locales/ur-PK/translation.json +++ b/src/lib/i18n/locales/ur-PK/translation.json @@ -1263,7 +1263,7 @@ "Searched {{count}} sites": "", "Searching \"{{searchQuery}}\"": "\"{{searchQuery}}\" تلاش کر رہے ہیں", "Searching Knowledge for \"{{searchQuery}}\"": "\"{{searchQuery}}\" کے لیے علم کی تلاش", - "Searching the web...": "", + "Searching the web": "", "Searxng Query URL": "تلاش کا سوال URL", "See readme.md for instructions": "ہدایات کے لیے readme.md دیکھیں", "See what's new": "نیا کیا ہے دیکھیں", diff --git a/src/lib/i18n/locales/uz-Cyrl-UZ/translation.json b/src/lib/i18n/locales/uz-Cyrl-UZ/translation.json index 93bac0e7bd..1c4360b801 100644 --- a/src/lib/i18n/locales/uz-Cyrl-UZ/translation.json +++ b/src/lib/i18n/locales/uz-Cyrl-UZ/translation.json @@ -1263,7 +1263,7 @@ "Searched {{count}} sites": "{{count}} та сайт қидирилди", "Searching \"{{searchQuery}}\"": "“{{searchQuery}}” қидирилмоқда", "Searching Knowledge for \"{{searchQuery}}\"": "“{{searchQuery}}” бўйича маълумотлар қидирилмоқда", - "Searching the web...": "Интернетда қидирилмоқда...", + "Searching the web": "Интернетда қидирилмоқда...", "Searxng Query URL": "Searxng сўрови УРЛ", "See readme.md for instructions": "Кўрсатмалар учун readme.md га қаранг", "See what's new": "Нима янгиликлар борлигини кўринг", diff --git a/src/lib/i18n/locales/uz-Latn-Uz/translation.json b/src/lib/i18n/locales/uz-Latn-Uz/translation.json index 16a16603e4..66d0fb39aa 100644 --- a/src/lib/i18n/locales/uz-Latn-Uz/translation.json +++ b/src/lib/i18n/locales/uz-Latn-Uz/translation.json @@ -1263,7 +1263,7 @@ "Searched {{count}} sites": "{{count}} ta sayt qidirildi", "Searching \"{{searchQuery}}\"": "“{{searchQuery}}” qidirilmoqda", "Searching Knowledge for \"{{searchQuery}}\"": "“{{searchQuery}}” boʻyicha maʼlumotlar qidirilmoqda", - "Searching the web...": "Internetda qidirilmoqda...", + "Searching the web": "Internetda qidirilmoqda...", "Searxng Query URL": "Searchxng so'rovi URL", "See readme.md for instructions": "Ko'rsatmalar uchun readme.md ga qarang", "See what's new": "Nima yangiliklar borligini ko'ring", diff --git a/src/lib/i18n/locales/vi-VN/translation.json b/src/lib/i18n/locales/vi-VN/translation.json index 34a3218a11..880751215b 100644 --- a/src/lib/i18n/locales/vi-VN/translation.json +++ b/src/lib/i18n/locales/vi-VN/translation.json @@ -1263,7 +1263,7 @@ "Searched {{count}} sites": "Đã tìm kiếm {{count}} trang web", "Searching \"{{searchQuery}}\"": "Đang tìm \"{{searchQuery}}\"", "Searching Knowledge for \"{{searchQuery}}\"": "Đang tìm kiếm Kiến thức cho \"{{searchQuery}}\"", - "Searching the web...": "", + "Searching the web": "", "Searxng Query URL": "URL truy vấn Searxng", "See readme.md for instructions": "Xem readme.md để biết hướng dẫn", "See what's new": "Xem những cập nhật mới", diff --git a/src/lib/i18n/locales/zh-CN/translation.json b/src/lib/i18n/locales/zh-CN/translation.json index e4a5e82478..63fa58322b 100644 --- a/src/lib/i18n/locales/zh-CN/translation.json +++ b/src/lib/i18n/locales/zh-CN/translation.json @@ -1267,7 +1267,7 @@ "Searched {{count}} sites": "已搜索 {{count}} 个网站", "Searching \"{{searchQuery}}\"": "搜索 \"{{searchQuery}}\" 中", "Searching Knowledge for \"{{searchQuery}}\"": "检索有关 \"{{searchQuery}}\" 的知识中", - "Searching the web...": "正在搜索网络...", + "Searching the web": "正在搜索网络...", "Searxng Query URL": "Searxng 查询 URL", "See readme.md for instructions": "查看 readme.md 以获取说明", "See what's new": "查阅最新更新内容", diff --git a/src/lib/i18n/locales/zh-TW/translation.json b/src/lib/i18n/locales/zh-TW/translation.json index dfc0eb3d2b..867d97ffcc 100644 --- a/src/lib/i18n/locales/zh-TW/translation.json +++ b/src/lib/i18n/locales/zh-TW/translation.json @@ -1267,7 +1267,7 @@ "Searched {{count}} sites": "搜尋到 {{count}} 個網站", "Searching \"{{searchQuery}}\"": "正在搜尋「{{searchQuery}}」", "Searching Knowledge for \"{{searchQuery}}\"": "正在搜尋知識庫中的「{{searchQuery}}」", - "Searching the web...": "正在搜尋網路...", + "Searching the web": "正在搜尋網路...", "Searxng Query URL": "Searxng 查詢 URL", "See readme.md for instructions": "檢視 readme.md 以取得說明", "See what's new": "檢視新功能", From f2525ebc447c008cf7269ef20ce04fa456f302c4 Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Sun, 7 Sep 2025 04:25:52 +0400 Subject: [PATCH 0109/1079] refac --- .../ResponseMessage/StatusHistory.svelte | 57 ++++++++++--------- 1 file changed, 29 insertions(+), 28 deletions(-) diff --git a/src/lib/components/chat/Messages/ResponseMessage/StatusHistory.svelte b/src/lib/components/chat/Messages/ResponseMessage/StatusHistory.svelte index 6a33dd7bf7..bfdac3a817 100644 --- a/src/lib/components/chat/Messages/ResponseMessage/StatusHistory.svelte +++ b/src/lib/components/chat/Messages/ResponseMessage/StatusHistory.svelte @@ -19,35 +19,36 @@ --> -{#if statusHistory} -
      - {#if showHistory} -
      - {#if statusHistory.length > 1} -
      +{#if statusHistory && statusHistory.length > 0} + {@const status = statusHistory.at(-1)} -
      - {#each statusHistory as status, idx} - {#if idx !== statusHistory.length - 1} -
      -
      - - - + {#if status?.hidden !== true} +
      + {#if showHistory} +
      + {#if statusHistory.length > 1} +
      + +
      + {#each statusHistory as status, idx} + {#if idx !== statusHistory.length - 1} +
      +
      + + + +
      +
      - -
      - {/if} - {/each} -
      - {/if} -
      - {/if} + {/if} + {/each} +
      + {/if} +
      + {/if} - {#if statusHistory.length > 0} - {@const status = statusHistory.at(-1)}
      - {/if} -
      +
      + {/if} {/if} From 7f523de408ede4075349d8de71ae0214b7e1a62e Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Sun, 7 Sep 2025 04:27:42 +0400 Subject: [PATCH 0110/1079] refac --- .../chat/Messages/ResponseMessage/WebSearchResults.svelte | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/lib/components/chat/Messages/ResponseMessage/WebSearchResults.svelte b/src/lib/components/chat/Messages/ResponseMessage/WebSearchResults.svelte index 31360f7132..bcd35f7586 100644 --- a/src/lib/components/chat/Messages/ResponseMessage/WebSearchResults.svelte +++ b/src/lib/components/chat/Messages/ResponseMessage/WebSearchResults.svelte @@ -8,9 +8,14 @@ let state = false; - +
      + {#if state} + + {:else} + + {/if}
      Date: Sun, 7 Sep 2025 05:06:03 +0400 Subject: [PATCH 0111/1079] refac --- backend/open_webui/utils/middleware.py | 35 ++++++++++---- .../chat/Messages/ResponseMessage.svelte | 7 ++- .../ResponseMessage/StatusHistory.svelte | 35 +++++++------- .../StatusHistory/StatusItem.svelte | 48 ++++++++++++++++++- 4 files changed, 92 insertions(+), 33 deletions(-) diff --git a/backend/open_webui/utils/middleware.py b/backend/open_webui/utils/middleware.py index d17b03b882..7320d77816 100644 --- a/backend/open_webui/utils/middleware.py +++ b/backend/open_webui/utils/middleware.py @@ -663,16 +663,16 @@ async def chat_completion_files_handler( if len(queries) == 0: queries = [get_last_user_message(body["messages"])] - # await __event_emitter__( - # { - # "type": "status", - # "data": { - # "action": "queries_generated", - # "queries": queries, - # "done": True, - # }, - # } - # ) + await __event_emitter__( + { + "type": "status", + "data": { + "action": "queries_generated", + "queries": queries, + "done": False, + }, + } + ) try: # Offload get_sources_from_items to a separate thread @@ -710,6 +710,21 @@ async def chat_completion_files_handler( log.debug(f"rag_contexts:sources: {sources}") + sources_count = 0 + for source in sources: + sources_count += len(source.get("document", [])) + + await __event_emitter__( + { + "type": "status", + "data": { + "action": "sources_retrieved", + "count": sources_count, + "done": True, + }, + } + ) + return body, {"sources": sources} diff --git a/src/lib/components/chat/Messages/ResponseMessage.svelte b/src/lib/components/chat/Messages/ResponseMessage.svelte index 271a49e940..35aaf111e2 100644 --- a/src/lib/components/chat/Messages/ResponseMessage.svelte +++ b/src/lib/components/chat/Messages/ResponseMessage.svelte @@ -643,7 +643,10 @@
      - + {#if message?.files && message.files?.filter((f) => f.type === 'image').length > 0}
      @@ -729,7 +732,7 @@
      {:else}
      - {#if message.content === '' && !message.error && (message?.statusHistory ?? [...(message?.status ? [message?.status] : [])]).length === 0} + {#if message.content === '' && !message.error && ((message?.statusHistory ?? [...(message?.status ? [message?.status] : [])]).length === 0 || (message?.statusHistory?.at(-1)?.hidden ?? false))} {:else if message.content && message.error !== true} diff --git a/src/lib/components/chat/Messages/ResponseMessage/StatusHistory.svelte b/src/lib/components/chat/Messages/ResponseMessage/StatusHistory.svelte index bfdac3a817..ed32d16fdd 100644 --- a/src/lib/components/chat/Messages/ResponseMessage/StatusHistory.svelte +++ b/src/lib/components/chat/Messages/ResponseMessage/StatusHistory.svelte @@ -2,36 +2,33 @@ import { getContext } from 'svelte'; const i18n = getContext('i18n'); - import Collapsible from '$lib/components/common/Collapsible.svelte'; import StatusItem from './StatusHistory/StatusItem.svelte'; export let statusHistory = []; + export let showHistory = true; - let showHistory = false; + let history = []; + let status = null; + + $: if (history && history.length > 0) { + status = history.at(-1); + } + + $: if (JSON.stringify(statusHistory) !== JSON.stringify(history)) { + history = statusHistory; + } - - -{#if statusHistory && statusHistory.length > 0} - {@const status = statusHistory.at(-1)} - +{#if history && history.length > 0} {#if status?.hidden !== true}
      {#if showHistory}
      - {#if statusHistory.length > 1} + {#if history.length > 1}
      - {#each statusHistory as status, idx} - {#if idx !== statusHistory.length - 1} + {#each history as status, idx} + {#if idx !== history.length - 1}
      @@ -55,7 +52,7 @@ showHistory = !showHistory; }} > -
      +
      {#if status?.done === false} diff --git a/src/lib/components/chat/Messages/ResponseMessage/StatusHistory/StatusItem.svelte b/src/lib/components/chat/Messages/ResponseMessage/StatusHistory/StatusItem.svelte index 7f325f7f6e..f4728a2bf0 100644 --- a/src/lib/components/chat/Messages/ResponseMessage/StatusHistory/StatusItem.svelte +++ b/src/lib/components/chat/Messages/ResponseMessage/StatusHistory/StatusItem.svelte @@ -66,13 +66,57 @@
      - + {query}
      {/each}
      + {:else if status?.action === 'queries_generated' && status?.queries} +
      +
      + {$i18n.t(`Querying`)} +
      + +
      + {#each status.queries as query, idx (query)} +
      +
      + +
      + + + {query} + +
      + {/each} +
      +
      + {:else if status?.action === 'sources_retrieved' && status?.count !== undefined} +
      +
      + {#if status.count === 0} + {$i18n.t('No sources found')} + {:else if status.count === 1} + {$i18n.t('Retrieved 1 source')} + {:else} + {$i18n.t('Retrieved {{count}} sources', { + count: status.count + })} + {/if} +
      +
      {:else}
      - {#if status?.description.includes('{{searchQuery}}')} + {#if status?.description?.includes('{{searchQuery}}')} {$i18n.t(status?.description, { searchQuery: status?.query })} From cd5e2be27b613314aadda6107089331783987985 Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Sun, 7 Sep 2025 05:09:14 +0400 Subject: [PATCH 0112/1079] refac --- .../components/chat/Messages/ResponseMessage.svelte | 2 +- .../chat/Messages/ResponseMessage/StatusHistory.svelte | 10 +++++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/lib/components/chat/Messages/ResponseMessage.svelte b/src/lib/components/chat/Messages/ResponseMessage.svelte index 35aaf111e2..0dfe4381c7 100644 --- a/src/lib/components/chat/Messages/ResponseMessage.svelte +++ b/src/lib/components/chat/Messages/ResponseMessage.svelte @@ -645,7 +645,7 @@
      {#if message?.files && message.files?.filter((f) => f.type === 'image').length > 0} diff --git a/src/lib/components/chat/Messages/ResponseMessage/StatusHistory.svelte b/src/lib/components/chat/Messages/ResponseMessage/StatusHistory.svelte index ed32d16fdd..b31e5ee601 100644 --- a/src/lib/components/chat/Messages/ResponseMessage/StatusHistory.svelte +++ b/src/lib/components/chat/Messages/ResponseMessage/StatusHistory.svelte @@ -4,7 +4,15 @@ import StatusItem from './StatusHistory/StatusItem.svelte'; export let statusHistory = []; - export let showHistory = true; + export let expand = false; + + let showHistory = true; + + $: if (expand) { + showHistory = true; + } else { + showHistory = false; + } let history = []; let status = null; From 6dc0df247347aede2762fe2065cf30275fd137ae Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Sun, 7 Sep 2025 05:17:38 +0400 Subject: [PATCH 0113/1079] refac --- backend/open_webui/utils/middleware.py | 23 ++++++++++++++++--- .../chat/Messages/ResponseMessage.svelte | 12 ++++++---- .../workspace/Models/Capabilities.svelte | 5 ++++ .../workspace/Models/ModelEditor.svelte | 1 + 4 files changed, 33 insertions(+), 8 deletions(-) diff --git a/backend/open_webui/utils/middleware.py b/backend/open_webui/utils/middleware.py index 7320d77816..463f52d0af 100644 --- a/backend/open_webui/utils/middleware.py +++ b/backend/open_webui/utils/middleware.py @@ -710,9 +710,26 @@ async def chat_completion_files_handler( log.debug(f"rag_contexts:sources: {sources}") - sources_count = 0 - for source in sources: - sources_count += len(source.get("document", [])) + unique_ids = set() + + for source in sources or []: + if not source or len(source.keys()) == 0: + continue + + documents = source.get("document") or [] + metadatas = source.get("metadata") or [] + src_info = source.get("source") or {} + + for index, _ in enumerate(documents): + metadata = metadatas[index] if index < len(metadatas) else None + _id = ( + (metadata or {}).get("source") + or (src_info or {}).get("id") + or "N/A" + ) + unique_ids.add(_id) + + sources_count = len(unique_ids) await __event_emitter__( { diff --git a/src/lib/components/chat/Messages/ResponseMessage.svelte b/src/lib/components/chat/Messages/ResponseMessage.svelte index 0dfe4381c7..9db76bfa85 100644 --- a/src/lib/components/chat/Messages/ResponseMessage.svelte +++ b/src/lib/components/chat/Messages/ResponseMessage.svelte @@ -643,10 +643,12 @@
      - + {#if model?.info?.meta?.capabilities?.status_updates ?? true} + + {/if} {#if message?.files && message.files?.filter((f) => f.type === 'image').length > 0}
      @@ -732,7 +734,7 @@
      {:else}
      - {#if message.content === '' && !message.error && ((message?.statusHistory ?? [...(message?.status ? [message?.status] : [])]).length === 0 || (message?.statusHistory?.at(-1)?.hidden ?? false))} + {#if message.content === '' && !message.error && ((model?.info?.meta?.capabilities?.status_updates ?? true) ? (message?.statusHistory ?? [...(message?.status ? [message?.status] : [])]).length === 0 || (message?.statusHistory?.at(-1)?.hidden ?? false) : true)} {:else if message.content && message.error !== true} diff --git a/src/lib/components/workspace/Models/Capabilities.svelte b/src/lib/components/workspace/Models/Capabilities.svelte index d0b45384a6..2d10802df8 100644 --- a/src/lib/components/workspace/Models/Capabilities.svelte +++ b/src/lib/components/workspace/Models/Capabilities.svelte @@ -36,6 +36,10 @@ citations: { label: $i18n.t('Citations'), description: $i18n.t('Displays citations in the response') + }, + status_updates: { + label: $i18n.t('Status Updates'), + description: $i18n.t('Displays status updates (e.g., web search progress) in the response') } }; @@ -47,6 +51,7 @@ code_interpreter?: boolean; usage?: boolean; citations?: boolean; + status_updates?: boolean; } = {}; diff --git a/src/lib/components/workspace/Models/ModelEditor.svelte b/src/lib/components/workspace/Models/ModelEditor.svelte index 0791b80bbb..fc9c167e9b 100644 --- a/src/lib/components/workspace/Models/ModelEditor.svelte +++ b/src/lib/components/workspace/Models/ModelEditor.svelte @@ -86,6 +86,7 @@ image_generation: true, code_interpreter: true, citations: true, + status_updates: true, usage: undefined }; From e023a98f11fc52feb21e4065ec707cc98e50c7d3 Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Sun, 7 Sep 2025 19:24:32 +0400 Subject: [PATCH 0114/1079] refac: submit suggestion prompt by default --- src/lib/components/chat/Chat.svelte | 7 +++- src/lib/components/chat/MessageInput.svelte | 38 ++++++++++++------- .../components/chat/Settings/Interface.svelte | 21 ++++++++++ 3 files changed, 51 insertions(+), 15 deletions(-) diff --git a/src/lib/components/chat/Chat.svelte b/src/lib/components/chat/Chat.svelte index ded370b4c2..e0b9a0a3c0 100644 --- a/src/lib/components/chat/Chat.svelte +++ b/src/lib/components/chat/Chat.svelte @@ -202,7 +202,12 @@ if (type === 'prompt') { // Handle prompt selection - messageInput?.setText(data); + messageInput?.setText(data, async () => { + if (!($settings?.insertSuggestionPrompt ?? false)) { + await tick(); + submitPrompt(prompt); + } + }); } }; diff --git a/src/lib/components/chat/MessageInput.svelte b/src/lib/components/chat/MessageInput.svelte index 6109767929..dbac52ad59 100644 --- a/src/lib/components/chat/MessageInput.svelte +++ b/src/lib/components/chat/MessageInput.svelte @@ -101,6 +101,7 @@ export let codeInterpreterEnabled = false; let showInputVariablesModal = false; + let inputVariablesModalCallback = (variableValues) => {}; let inputVariables = {}; let inputVariableValues = {}; @@ -122,11 +123,24 @@ codeInterpreterEnabled }); - const inputVariableHandler = async (text: string) => { + const inputVariableHandler = async (text: string): Promise => { inputVariables = extractInputVariables(text); - if (Object.keys(inputVariables).length > 0) { - showInputVariablesModal = true; + + // No variables? return the original text immediately. + if (Object.keys(inputVariables).length === 0) { + return text; } + + // Show modal and wait for the user's input. + showInputVariablesModal = true; + return await new Promise((resolve) => { + inputVariablesModalCallback = (variableValues) => { + inputVariableValues = { ...inputVariableValues, ...variableValues }; + replaceVariables(inputVariableValues); + showInputVariablesModal = false; + resolve(text); + }; + }); }; const textVariableHandler = async (text: string) => { @@ -244,7 +258,6 @@ text = text.replaceAll('{{CURRENT_WEEKDAY}}', weekday); } - inputVariableHandler(text); return text; }; @@ -280,7 +293,7 @@ } }; - export const setText = async (text?: string) => { + export const setText = async (text?: string, cb?: (text: string) => void) => { const chatInput = document.getElementById('chat-input'); if (chatInput) { @@ -296,6 +309,10 @@ chatInput.focus(); chatInput.dispatchEvent(new Event('input')); } + + text = await inputVariableHandler(text); + await tick(); + if (cb) await cb(text); } }; @@ -758,11 +775,7 @@ } ]; }; - reader.readAsDataURL( - file['type'] === 'image/heic' - ? await convertHeicToJpeg(file) - : file - ); + reader.readAsDataURL(file['type'] === 'image/heic' ? await convertHeicToJpeg(file) : file); } else { uploadFileHandler(file); } @@ -868,10 +881,7 @@ { - inputVariableValues = { ...inputVariableValues, ...variableValues }; - replaceVariables(inputVariableValues); - }} + onSave={inputVariablesModalCallback} /> {#if loaded} diff --git a/src/lib/components/chat/Settings/Interface.svelte b/src/lib/components/chat/Settings/Interface.svelte index 9c6526c1d2..c383b831bb 100644 --- a/src/lib/components/chat/Settings/Interface.svelte +++ b/src/lib/components/chat/Settings/Interface.svelte @@ -49,6 +49,7 @@ let largeTextAsFile = false; + let insertSuggestionPrompt = false; let keepFollowUpPrompts = false; let insertFollowUpPrompt = false; @@ -200,6 +201,7 @@ insertPromptAsRichText = $settings?.insertPromptAsRichText ?? false; promptAutocomplete = $settings?.promptAutocomplete ?? false; + insertSuggestionPrompt = $settings?.insertSuggestionPrompt ?? false; keepFollowUpPrompts = $settings?.keepFollowUpPrompts ?? false; insertFollowUpPrompt = $settings?.insertFollowUpPrompt ?? false; @@ -697,6 +699,25 @@
      +
      +
      +
      + {$i18n.t('Insert Suggestion Prompt to Input')} +
      + +
      + { + saveSettings({ insertSuggestionPrompt }); + }} + /> +
      +
      +
      +
      From a28ca305193d13724a00784ebe6b4e82cad3281f Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Mon, 8 Sep 2025 02:35:51 +0400 Subject: [PATCH 0115/1079] refac/fix: source citation --- src/lib/utils/index.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/lib/utils/index.ts b/src/lib/utils/index.ts index 0bf594a560..ea635bf286 100644 --- a/src/lib/utils/index.ts +++ b/src/lib/utils/index.ts @@ -70,7 +70,10 @@ export const replaceTokens = (content, sourceIds, char, user) => { if (Array.isArray(sourceIds)) { sourceIds.forEach((sourceId, idx) => { const regex = new RegExp(`\\[${idx + 1}\\]`, 'g'); - segment = segment.replace(regex, ``); + segment = segment.replace( + regex, + `` + ); }); } From 91755309cebc5d732c79e265fdf8ce25451822fe Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Mon, 8 Sep 2025 14:18:25 +0400 Subject: [PATCH 0116/1079] refac --- backend/open_webui/env.py | 4 ++++ backend/open_webui/routers/auths.py | 8 +++++++- backend/open_webui/utils/auth.py | 6 ++++++ backend/open_webui/utils/oauth.py | 16 +++++++++++++--- 4 files changed, 30 insertions(+), 4 deletions(-) diff --git a/backend/open_webui/env.py b/backend/open_webui/env.py index f0b26ae25c..f72d827afc 100644 --- a/backend/open_webui/env.py +++ b/backend/open_webui/env.py @@ -465,6 +465,10 @@ ENABLE_COMPRESSION_MIDDLEWARE = ( os.environ.get("ENABLE_COMPRESSION_MIDDLEWARE", "True").lower() == "true" ) +ENABLE_OAUTH_SESSION_TOKENS_COOKIES = ( + os.environ.get("ENABLE_OAUTH_SESSION_TOKENS_COOKIES", "True").lower() == "true" +) + #################################### # SCIM Configuration diff --git a/backend/open_webui/routers/auths.py b/backend/open_webui/routers/auths.py index b8670edeaa..665660a954 100644 --- a/backend/open_webui/routers/auths.py +++ b/backend/open_webui/routers/auths.py @@ -28,6 +28,7 @@ from open_webui.env import ( WEBUI_AUTH_TRUSTED_GROUPS_HEADER, WEBUI_AUTH_COOKIE_SAME_SITE, WEBUI_AUTH_COOKIE_SECURE, + ENABLE_OAUTH_SESSION_TOKENS_COOKIES, WEBUI_AUTH_SIGNOUT_REDIRECT_URL, ENABLE_INITIAL_ADMIN_SIGNUP, SRC_LOG_LEVELS, @@ -678,6 +679,7 @@ async def signout(request: Request, response: Response): response.delete_cookie("oui-session") if ENABLE_OAUTH_SIGNUP.value: + # TODO: update this to use oauth_session_tokens in User Object oauth_id_token = request.cookies.get("oauth_id_token") if oauth_id_token and OPENID_PROVIDER_URL.value: try: @@ -687,7 +689,11 @@ async def signout(request: Request, response: Response): openid_data = await resp.json() logout_url = openid_data.get("end_session_endpoint") if logout_url: - response.delete_cookie("oauth_id_token") + + if ENABLE_OAUTH_SESSION_TOKENS_COOKIES: + response.delete_cookie("oauth_id_token") + response.delete_cookie("oauth_access_token") + response.delete_cookie("oauth_refresh_token") return JSONResponse( status_code=200, diff --git a/backend/open_webui/utils/auth.py b/backend/open_webui/utils/auth.py index 228dd3e30a..33b377ad03 100644 --- a/backend/open_webui/utils/auth.py +++ b/backend/open_webui/utils/auth.py @@ -285,8 +285,14 @@ def get_current_user( # Delete the token cookie response.delete_cookie("token") # Delete OAuth token if present + if request.cookies.get("oauth_id_token"): response.delete_cookie("oauth_id_token") + if request.cookies.get("oauth_access_token"): + response.delete_cookie("oauth_access_token") + if request.cookies.get("oauth_refresh_token"): + response.delete_cookie("oauth_refresh_token") + raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="User mismatch. Please sign in again.", diff --git a/backend/open_webui/utils/oauth.py b/backend/open_webui/utils/oauth.py index 9dfdad50a1..9763b35463 100644 --- a/backend/open_webui/utils/oauth.py +++ b/backend/open_webui/utils/oauth.py @@ -626,6 +626,15 @@ class OAuthManager: ) if ENABLE_OAUTH_SIGNUP.value: + oauth_id_token = token.get("id_token") + response.set_cookie( + key="oauth_id_token", + value=oauth_id_token, + httponly=True, + samesite=WEBUI_AUTH_COOKIE_SAME_SITE, + secure=WEBUI_AUTH_COOKIE_SECURE, + ) + oauth_access_token = token.get("access_token") response.set_cookie( key="oauth_access_token", @@ -635,12 +644,13 @@ class OAuthManager: secure=WEBUI_AUTH_COOKIE_SECURE, ) - oauth_id_token = token.get("id_token") + oauth_refresh_token = token.get("refresh_token") response.set_cookie( - key="oauth_id_token", - value=oauth_id_token, + key="oauth_refresh_token", + value=oauth_refresh_token, httponly=True, samesite=WEBUI_AUTH_COOKIE_SAME_SITE, secure=WEBUI_AUTH_COOKIE_SECURE, ) + return response From 6d38ac41b6375c70659cab1c8551032bba94efcc Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Mon, 8 Sep 2025 14:36:00 +0400 Subject: [PATCH 0117/1079] refac --- backend/open_webui/routers/auths.py | 11 ++--- backend/open_webui/utils/oauth.py | 68 ++++++++++++++--------------- 2 files changed, 40 insertions(+), 39 deletions(-) diff --git a/backend/open_webui/routers/auths.py b/backend/open_webui/routers/auths.py index 665660a954..524edf373d 100644 --- a/backend/open_webui/routers/auths.py +++ b/backend/open_webui/routers/auths.py @@ -681,15 +681,16 @@ async def signout(request: Request, response: Response): if ENABLE_OAUTH_SIGNUP.value: # TODO: update this to use oauth_session_tokens in User Object oauth_id_token = request.cookies.get("oauth_id_token") + if oauth_id_token and OPENID_PROVIDER_URL.value: try: async with ClientSession(trust_env=True) as session: - async with session.get(OPENID_PROVIDER_URL.value) as resp: - if resp.status == 200: - openid_data = await resp.json() + async with session.get(OPENID_PROVIDER_URL.value) as r: + if r.status == 200: + openid_data = await r.json() logout_url = openid_data.get("end_session_endpoint") - if logout_url: + if logout_url: if ENABLE_OAUTH_SESSION_TOKENS_COOKIES: response.delete_cookie("oauth_id_token") response.delete_cookie("oauth_access_token") @@ -710,7 +711,7 @@ async def signout(request: Request, response: Response): ) else: raise HTTPException( - status_code=resp.status, + status_code=r.status, detail="Failed to fetch OpenID configuration", ) except Exception as e: diff --git a/backend/open_webui/utils/oauth.py b/backend/open_webui/utils/oauth.py index 9763b35463..4411f40e3b 100644 --- a/backend/open_webui/utils/oauth.py +++ b/backend/open_webui/utils/oauth.py @@ -49,6 +49,7 @@ from open_webui.env import ( WEBUI_NAME, WEBUI_AUTH_COOKIE_SAME_SITE, WEBUI_AUTH_COOKIE_SECURE, + ENABLE_OAUTH_SESSION_TOKENS_COOKIES, ) from open_webui.utils.misc import parse_duration from open_webui.utils.auth import get_password_hash, create_token @@ -410,6 +411,8 @@ class OAuthManager: except Exception as e: log.warning(f"OAuth callback error: {e}") raise HTTPException(400, detail=ERROR_MESSAGES.INVALID_CRED) + + # Try to get userinfo from the token first, some providers include it there user_data: UserInfo = token.get("userinfo") if ( (not user_data) @@ -421,18 +424,19 @@ class OAuthManager: log.warning(f"OAuth callback failed, user data is missing: {token}") raise HTTPException(400, detail=ERROR_MESSAGES.INVALID_CRED) + # Extract the "sub" claim, using custom claim if configured if auth_manager_config.OAUTH_SUB_CLAIM: sub = user_data.get(auth_manager_config.OAUTH_SUB_CLAIM) else: # Fallback to the default sub claim if not configured sub = user_data.get(OAUTH_PROVIDERS[provider].get("sub_claim", "sub")) - if not sub: log.warning(f"OAuth callback failed, sub is missing: {user_data}") raise HTTPException(400, detail=ERROR_MESSAGES.INVALID_CRED) provider_sub = f"{provider}@{sub}" + # Email extraction email_claim = auth_manager_config.OAUTH_EMAIL_CLAIM email = user_data.get(email_claim, "") # We currently mandate that email addresses are provided @@ -480,6 +484,8 @@ class OAuthManager: log.warning(f"OAuth callback failed, email is missing: {user_data}") raise HTTPException(400, detail=ERROR_MESSAGES.INVALID_CRED) email = email.lower() + + # If allowed domains are configured, check if the email domain is in the list if ( "*" not in auth_manager_config.OAUTH_ALLOWED_DOMAINS and email.split("@")[-1] @@ -492,7 +498,6 @@ class OAuthManager: # Check if the user exists user = Users.get_user_by_oauth_sub(provider_sub) - if not user: # If the user does not exist, check if merging is enabled if auth_manager_config.OAUTH_MERGE_ACCOUNTS_BY_EMAIL: @@ -506,7 +511,6 @@ class OAuthManager: determined_role = self.get_user_role(user, user_data) if user.role != determined_role: Users.update_user_role_by_id(user.id, determined_role) - # Update profile picture if enabled and different from current if auth_manager_config.OAUTH_UPDATE_PICTURE_ON_LOGIN: picture_claim = auth_manager_config.OAUTH_PICTURE_CLAIM @@ -523,8 +527,7 @@ class OAuthManager: user.id, processed_picture_url ) log.debug(f"Updated profile picture for user {user.email}") - - if not user: + else: # If the user does not exist, check if signups are enabled if auth_manager_config.ENABLE_OAUTH_SIGNUP: # Check if an existing user with the same email already exists @@ -543,7 +546,6 @@ class OAuthManager: ) else: picture_url = "/user.png" - username_claim = auth_manager_config.OAUTH_USERNAME_CLAIM name = user_data.get(username_claim) @@ -551,8 +553,6 @@ class OAuthManager: log.warning("Username claim is missing, using email as name") name = email - role = self.get_user_role(None, user_data) - user = Auths.insert_new_auth( email=email, password=get_password_hash( @@ -560,7 +560,7 @@ class OAuthManager: ), # Random password, not used name=name, profile_image_url=picture_url, - role=role, + role=self.get_user_role(None, user_data), oauth_sub=provider_sub, ) @@ -585,7 +585,6 @@ class OAuthManager: data={"id": user.id}, expires_delta=parse_duration(auth_manager_config.JWT_EXPIRES_IN), ) - if ( auth_manager_config.ENABLE_OAUTH_GROUP_MANAGEMENT and user.role != "admin" @@ -626,31 +625,32 @@ class OAuthManager: ) if ENABLE_OAUTH_SIGNUP.value: - oauth_id_token = token.get("id_token") - response.set_cookie( - key="oauth_id_token", - value=oauth_id_token, - httponly=True, - samesite=WEBUI_AUTH_COOKIE_SAME_SITE, - secure=WEBUI_AUTH_COOKIE_SECURE, - ) + if ENABLE_OAUTH_SESSION_TOKENS_COOKIES: + oauth_id_token = token.get("id_token") + response.set_cookie( + key="oauth_id_token", + value=oauth_id_token, + httponly=True, + samesite=WEBUI_AUTH_COOKIE_SAME_SITE, + secure=WEBUI_AUTH_COOKIE_SECURE, + ) - oauth_access_token = token.get("access_token") - response.set_cookie( - key="oauth_access_token", - value=oauth_access_token, - httponly=True, - samesite=WEBUI_AUTH_COOKIE_SAME_SITE, - secure=WEBUI_AUTH_COOKIE_SECURE, - ) + oauth_access_token = token.get("access_token") + response.set_cookie( + key="oauth_access_token", + value=oauth_access_token, + httponly=True, + samesite=WEBUI_AUTH_COOKIE_SAME_SITE, + secure=WEBUI_AUTH_COOKIE_SECURE, + ) - oauth_refresh_token = token.get("refresh_token") - response.set_cookie( - key="oauth_refresh_token", - value=oauth_refresh_token, - httponly=True, - samesite=WEBUI_AUTH_COOKIE_SAME_SITE, - secure=WEBUI_AUTH_COOKIE_SECURE, - ) + oauth_refresh_token = token.get("refresh_token") + response.set_cookie( + key="oauth_refresh_token", + value=oauth_refresh_token, + httponly=True, + samesite=WEBUI_AUTH_COOKIE_SAME_SITE, + secure=WEBUI_AUTH_COOKIE_SECURE, + ) return response From 217f4daef09b36d3d4cc4681e11d3ebd9984a1a5 Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Mon, 8 Sep 2025 18:05:43 +0400 Subject: [PATCH 0118/1079] feat: server-side OAuth token management system Co-Authored-By: Classic298 <27028174+Classic298@users.noreply.github.com> --- backend/open_webui/env.py | 13 +- backend/open_webui/main.py | 1 + .../38d63c18f30f_add_oauth_session_table.py | 52 ++++ backend/open_webui/models/oauth_sessions.py | 247 ++++++++++++++++++ backend/open_webui/routers/auths.py | 32 +-- backend/open_webui/utils/auth.py | 96 +++---- backend/open_webui/utils/middleware.py | 9 + backend/open_webui/utils/oauth.py | 247 ++++++++++++++++-- backend/open_webui/utils/tools.py | 15 ++ src/lib/components/AddServerModal.svelte | 7 + 10 files changed, 627 insertions(+), 92 deletions(-) create mode 100644 backend/open_webui/migrations/versions/38d63c18f30f_add_oauth_session_table.py create mode 100644 backend/open_webui/models/oauth_sessions.py diff --git a/backend/open_webui/env.py b/backend/open_webui/env.py index f72d827afc..b4fdc97d82 100644 --- a/backend/open_webui/env.py +++ b/backend/open_webui/env.py @@ -465,8 +465,17 @@ ENABLE_COMPRESSION_MIDDLEWARE = ( os.environ.get("ENABLE_COMPRESSION_MIDDLEWARE", "True").lower() == "true" ) -ENABLE_OAUTH_SESSION_TOKENS_COOKIES = ( - os.environ.get("ENABLE_OAUTH_SESSION_TOKENS_COOKIES", "True").lower() == "true" +#################################### +# OAUTH Configuration +#################################### + + +ENABLE_OAUTH_ID_TOKEN_COOKIE = ( + os.environ.get("ENABLE_OAUTH_ID_TOKEN_COOKIE", "True").lower() == "true" +) + +OAUTH_SESSION_TOKEN_ENCRYPTION_KEY = os.environ.get( + "OAUTH_SESSION_TOKEN_ENCRYPTION_KEY", WEBUI_SECRET_KEY ) diff --git a/backend/open_webui/main.py b/backend/open_webui/main.py index 7decfcd83b..de7dcae086 100644 --- a/backend/open_webui/main.py +++ b/backend/open_webui/main.py @@ -592,6 +592,7 @@ app = FastAPI( ) oauth_manager = OAuthManager(app) +app.state.oauth_manager = oauth_manager app.state.instance_id = None app.state.config = AppConfig( diff --git a/backend/open_webui/migrations/versions/38d63c18f30f_add_oauth_session_table.py b/backend/open_webui/migrations/versions/38d63c18f30f_add_oauth_session_table.py new file mode 100644 index 0000000000..8ead6db6d4 --- /dev/null +++ b/backend/open_webui/migrations/versions/38d63c18f30f_add_oauth_session_table.py @@ -0,0 +1,52 @@ +"""Add oauth_session table + +Revision ID: 38d63c18f30f +Revises: 3af16a1c9fb6 +Create Date: 2025-09-08 14:19:59.583921 + +""" + +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision: str = "38d63c18f30f" +down_revision: Union[str, None] = "3af16a1c9fb6" +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + # Create oauth_session table + op.create_table( + "oauth_session", + sa.Column("id", sa.Text(), nullable=False), + sa.Column("user_id", sa.Text(), nullable=False), + sa.Column("provider", sa.Text(), nullable=False), + sa.Column("token", sa.Text(), nullable=False), + sa.Column("expires_at", sa.BigInteger(), nullable=False), + sa.Column("created_at", sa.BigInteger(), nullable=False), + sa.Column("updated_at", sa.BigInteger(), nullable=False), + sa.PrimaryKeyConstraint("id"), + sa.ForeignKeyConstraint(["user_id"], ["user.id"], ondelete="CASCADE"), + ) + + # Create indexes for better performance + op.create_index("idx_oauth_session_user_id", "oauth_session", ["user_id"]) + op.create_index("idx_oauth_session_expires_at", "oauth_session", ["expires_at"]) + op.create_index( + "idx_oauth_session_user_provider", "oauth_session", ["user_id", "provider"] + ) + + +def downgrade() -> None: + # Drop indexes first + op.drop_index("idx_oauth_session_user_provider", table_name="oauth_session") + op.drop_index("idx_oauth_session_expires_at", table_name="oauth_session") + op.drop_index("idx_oauth_session_user_id", table_name="oauth_session") + + # Drop the table + op.drop_table("oauth_session") diff --git a/backend/open_webui/models/oauth_sessions.py b/backend/open_webui/models/oauth_sessions.py new file mode 100644 index 0000000000..b0b5aa29a6 --- /dev/null +++ b/backend/open_webui/models/oauth_sessions.py @@ -0,0 +1,247 @@ +import time +import logging +import uuid +from typing import Optional, List +import base64 +import hashlib +import json + +from cryptography.fernet import Fernet + +from open_webui.internal.db import Base, get_db +from open_webui.env import SRC_LOG_LEVELS, OAUTH_SESSION_TOKEN_ENCRYPTION_KEY + +from pydantic import BaseModel, ConfigDict +from sqlalchemy import BigInteger, Column, String, Text, Index + +log = logging.getLogger(__name__) +log.setLevel(SRC_LOG_LEVELS["MODELS"]) + +#################### +# DB MODEL +#################### + + +class OAuthSession(Base): + __tablename__ = "oauth_session" + + id = Column(Text, primary_key=True) + user_id = Column(Text, nullable=False) + provider = Column(Text, nullable=False) + token = Column( + Text, nullable=False + ) # JSON with access_token, id_token, refresh_token + expires_at = Column(BigInteger, nullable=False) + created_at = Column(BigInteger, nullable=False) + updated_at = Column(BigInteger, nullable=False) + + # Add indexes for better performance + __table_args__ = ( + Index("idx_oauth_session_user_id", "user_id"), + Index("idx_oauth_session_expires_at", "expires_at"), + Index("idx_oauth_session_user_provider", "user_id", "provider"), + ) + + +class OAuthSessionModel(BaseModel): + id: str + user_id: str + provider: str + token: dict + expires_at: int # timestamp in epoch + created_at: int # timestamp in epoch + updated_at: int # timestamp in epoch + + model_config = ConfigDict(from_attributes=True) + + +#################### +# Forms +#################### + + +class OAuthSessionResponse(BaseModel): + id: str + user_id: str + provider: str + expires_at: int + + +class OAuthSessionTable: + def __init__(self): + self.encryption_key = OAUTH_SESSION_TOKEN_ENCRYPTION_KEY + if not self.encryption_key: + raise Exception("OAUTH_SESSION_TOKEN_ENCRYPTION_KEY is not set") + + # check if encryption key is in the right format for Fernet (32 url-safe base64-encoded bytes) + if len(self.encryption_key) != 44: + key_bytes = hashlib.sha256(self.encryption_key.encode()).digest() + self.encryption_key = base64.urlsafe_b64encode(key_bytes) + else: + self.encryption_key = self.encryption_key.encode() + + try: + self.fernet = Fernet(self.encryption_key) + except Exception as e: + log.error(f"Error initializing Fernet with provided key: {e}") + raise + + def _encrypt_token(self, token) -> str: + """Encrypt OAuth tokens for storage""" + try: + token_json = json.dumps(token) + encrypted = self.fernet.encrypt(token_json.encode()).decode() + return encrypted + except Exception as e: + log.error(f"Error encrypting tokens: {e}") + raise + + def _decrypt_token(self, token: str): + """Decrypt OAuth tokens from storage""" + try: + decrypted = self.fernet.decrypt(token.encode()).decode() + return json.loads(decrypted) + except Exception as e: + log.error(f"Error decrypting tokens: {e}") + raise + + def create_session( + self, + user_id: str, + provider: str, + token: dict, + ) -> Optional[OAuthSessionModel]: + """Create a new OAuth session""" + try: + with get_db() as db: + current_time = int(time.time()) + id = str(uuid.uuid4()) + + result = OAuthSession( + **{ + "id": id, + "user_id": user_id, + "provider": provider, + "token": self._encrypt_token(token), + "expires_at": token.get("expires_at"), + "created_at": current_time, + "updated_at": current_time, + } + ) + + db.add(result) + db.commit() + db.refresh(result) + + if result: + result.token = token # Return decrypted token + return OAuthSessionModel.model_validate(result) + else: + return None + except Exception as e: + log.error(f"Error creating OAuth session: {e}") + return None + + def get_session_by_id(self, session_id: str) -> Optional[OAuthSessionModel]: + """Get OAuth session by ID""" + try: + with get_db() as db: + session = db.query(OAuthSession).filter_by(id=session_id).first() + if session: + session.token = self._decrypt_token(session.token) + return OAuthSessionModel.model_validate(session) + + return None + except Exception as e: + log.error(f"Error getting OAuth session by ID: {e}") + return None + + def get_session_by_id_and_user_id( + self, session_id: str, user_id: str + ) -> Optional[OAuthSessionModel]: + """Get OAuth session by ID and user ID""" + try: + with get_db() as db: + session = ( + db.query(OAuthSession) + .filter_by(id=session_id, user_id=user_id) + .first() + ) + if session: + session.token = self._decrypt_token(session.token) + return OAuthSessionModel.model_validate(session) + ) + return None + except Exception as e: + log.error(f"Error getting OAuth session by ID: {e}") + return None + + def get_sessions_by_user_id(self, user_id: str) -> List[OAuthSessionModel]: + """Get all OAuth sessions for a user""" + try: + with get_db() as db: + sessions = db.query(OAuthSession).filter_by(user_id=user_id).all() + + + results = [] + for session in sessions: + session.token = self._decrypt_token(session.token) + results.append(OAuthSessionModel.model_validate(session)) + + return results + + except Exception as e: + log.error(f"Error getting OAuth sessions by user ID: {e}") + return [] + + def update_session_by_id( + self, session_id: str, token: dict + ) -> Optional[OAuthSessionModel]: + """Update OAuth session tokens""" + try: + with get_db() as db: + current_time = int(time.time()) + + db.query(OAuthSession).filter_by(id=session_id).update( + { + "token": self._encrypt_token(token), + "expires_at": token.get("expires_at"), + "updated_at": current_time, + } + ) + db.commit() + session = db.query(OAuthSession).filter_by(id=session_id).first() + + if session: + session.token = self._decrypt_token(session.token) + return OAuthSessionModel.model_validate(session) + + return None + except Exception as e: + log.error(f"Error updating OAuth session tokens: {e}") + return None + + def delete_session_by_id(self, session_id: str) -> bool: + """Delete an OAuth session""" + try: + with get_db() as db: + result = db.query(OAuthSession).filter_by(id=session_id).delete() + db.commit() + return result > 0 + except Exception as e: + log.error(f"Error deleting OAuth session: {e}") + return False + + def delete_sessions_by_user_id(self, user_id: str) -> bool: + """Delete all OAuth sessions for a user""" + try: + with get_db() as db: + result = db.query(OAuthSession).filter_by(user_id=user_id).delete() + db.commit() + return True + except Exception as e: + log.error(f"Error deleting OAuth sessions by user ID: {e}") + return False + + +OAuthSessions = OAuthSessionTable() diff --git a/backend/open_webui/routers/auths.py b/backend/open_webui/routers/auths.py index 524edf373d..d044b4a168 100644 --- a/backend/open_webui/routers/auths.py +++ b/backend/open_webui/routers/auths.py @@ -19,6 +19,7 @@ from open_webui.models.auths import ( ) from open_webui.models.users import Users, UpdateProfileForm from open_webui.models.groups import Groups +from open_webui.models.oauth_sessions import OAuthSessions from open_webui.constants import ERROR_MESSAGES, WEBHOOK_MESSAGES from open_webui.env import ( @@ -28,7 +29,6 @@ from open_webui.env import ( WEBUI_AUTH_TRUSTED_GROUPS_HEADER, WEBUI_AUTH_COOKIE_SAME_SITE, WEBUI_AUTH_COOKIE_SECURE, - ENABLE_OAUTH_SESSION_TOKENS_COOKIES, WEBUI_AUTH_SIGNOUT_REDIRECT_URL, ENABLE_INITIAL_ADMIN_SIGNUP, SRC_LOG_LEVELS, @@ -678,24 +678,27 @@ async def signout(request: Request, response: Response): response.delete_cookie("token") response.delete_cookie("oui-session") - if ENABLE_OAUTH_SIGNUP.value: - # TODO: update this to use oauth_session_tokens in User Object - oauth_id_token = request.cookies.get("oauth_id_token") + oauth_session_id = request.cookies.get("oauth_session_id") + if oauth_session_id: + response.delete_cookie("oauth_session_id") - if oauth_id_token and OPENID_PROVIDER_URL.value: + session = OAuthSessions.get_session_by_id(oauth_session_id) + oauth_server_metadata_url = ( + request.app.state.oauth_manager.get_server_metadata_url(session.provider) + if session + else None + ) or OPENID_PROVIDER_URL.value + + if session and oauth_server_metadata_url: + oauth_id_token = session.token.get("id_token") try: async with ClientSession(trust_env=True) as session: - async with session.get(OPENID_PROVIDER_URL.value) as r: + async with session.get(oauth_server_metadata_url) as r: if r.status == 200: openid_data = await r.json() logout_url = openid_data.get("end_session_endpoint") if logout_url: - if ENABLE_OAUTH_SESSION_TOKENS_COOKIES: - response.delete_cookie("oauth_id_token") - response.delete_cookie("oauth_access_token") - response.delete_cookie("oauth_refresh_token") - return JSONResponse( status_code=200, content={ @@ -710,15 +713,14 @@ async def signout(request: Request, response: Response): headers=response.headers, ) else: - raise HTTPException( - status_code=r.status, - detail="Failed to fetch OpenID configuration", - ) + raise Exception("Failed to fetch OpenID configuration") + except Exception as e: log.error(f"OpenID signout error: {str(e)}") raise HTTPException( status_code=500, detail="Failed to sign out from the OpenID provider.", + headers=response.headers, ) if WEBUI_AUTH_SIGNOUT_REDIRECT_URL: diff --git a/backend/open_webui/utils/auth.py b/backend/open_webui/utils/auth.py index 33b377ad03..19994bafbd 100644 --- a/backend/open_webui/utils/auth.py +++ b/backend/open_webui/utils/auth.py @@ -261,61 +261,63 @@ def get_current_user( return user # auth by jwt token - try: - data = decode_token(token) - except Exception as e: - raise HTTPException( - status_code=status.HTTP_401_UNAUTHORIZED, - detail="Invalid token", - ) - if data is not None and "id" in data: - user = Users.get_user_by_id(data["id"]) - if user is None: + try: + try: + data = decode_token(token) + except Exception as e: raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, - detail=ERROR_MESSAGES.INVALID_TOKEN, + detail="Invalid token", ) - else: - if WEBUI_AUTH_TRUSTED_EMAIL_HEADER: - trusted_email = request.headers.get( - WEBUI_AUTH_TRUSTED_EMAIL_HEADER, "" - ).lower() - if trusted_email and user.email != trusted_email: - # Delete the token cookie - response.delete_cookie("token") - # Delete OAuth token if present - if request.cookies.get("oauth_id_token"): - response.delete_cookie("oauth_id_token") - if request.cookies.get("oauth_access_token"): - response.delete_cookie("oauth_access_token") - if request.cookies.get("oauth_refresh_token"): - response.delete_cookie("oauth_refresh_token") + if data is not None and "id" in data: + user = Users.get_user_by_id(data["id"]) + if user is None: + raise HTTPException( + status_code=status.HTTP_401_UNAUTHORIZED, + detail=ERROR_MESSAGES.INVALID_TOKEN, + ) + else: + if WEBUI_AUTH_TRUSTED_EMAIL_HEADER: + trusted_email = request.headers.get( + WEBUI_AUTH_TRUSTED_EMAIL_HEADER, "" + ).lower() + if trusted_email and user.email != trusted_email: + raise HTTPException( + status_code=status.HTTP_401_UNAUTHORIZED, + detail="User mismatch. Please sign in again.", + ) - raise HTTPException( - status_code=status.HTTP_401_UNAUTHORIZED, - detail="User mismatch. Please sign in again.", + # Add user info to current span + current_span = trace.get_current_span() + if current_span: + current_span.set_attribute("client.user.id", user.id) + current_span.set_attribute("client.user.email", user.email) + current_span.set_attribute("client.user.role", user.role) + current_span.set_attribute("client.auth.type", "jwt") + + # Refresh the user's last active timestamp asynchronously + # to prevent blocking the request + if background_tasks: + background_tasks.add_task( + Users.update_user_last_active_by_id, user.id ) + return user + else: + raise HTTPException( + status_code=status.HTTP_401_UNAUTHORIZED, + detail=ERROR_MESSAGES.UNAUTHORIZED, + ) + except Exception as e: + # Delete the token cookie + if request.cookies.get("token"): + response.delete_cookie("token") + # Delete OAuth session if present + if request.cookies.get("oauth_session_id"): + response.delete_cookie("oauth_session_id") - # Add user info to current span - current_span = trace.get_current_span() - if current_span: - current_span.set_attribute("client.user.id", user.id) - current_span.set_attribute("client.user.email", user.email) - current_span.set_attribute("client.user.role", user.role) - current_span.set_attribute("client.auth.type", "jwt") - - # Refresh the user's last active timestamp asynchronously - # to prevent blocking the request - if background_tasks: - background_tasks.add_task(Users.update_user_last_active_by_id, user.id) - return user - else: - raise HTTPException( - status_code=status.HTTP_401_UNAUTHORIZED, - detail=ERROR_MESSAGES.UNAUTHORIZED, - ) + raise e def get_current_user_by_api_key(api_key: str): diff --git a/backend/open_webui/utils/middleware.py b/backend/open_webui/utils/middleware.py index 463f52d0af..27b0f11290 100644 --- a/backend/open_webui/utils/middleware.py +++ b/backend/open_webui/utils/middleware.py @@ -815,6 +815,14 @@ async def process_chat_payload(request, form_data, user, metadata, model): event_emitter = get_event_emitter(metadata) event_call = get_event_call(metadata) + oauth_token = None + try: + oauth_token = await request.app.state.oauth_manager.get_oauth_token( + user.id, request.cookies.get("oauth_session_id", None) + ) + except Exception as e: + log.error(f"Error getting OAuth token: {e}") + extra_params = { "__event_emitter__": event_emitter, "__event_call__": event_call, @@ -822,6 +830,7 @@ async def process_chat_payload(request, form_data, user, metadata, model): "__metadata__": metadata, "__request__": request, "__model__": model, + "__oauth_token__": oauth_token, } # Initialize events to store additional event to be sent to the client diff --git a/backend/open_webui/utils/oauth.py b/backend/open_webui/utils/oauth.py index 4411f40e3b..55ee3eee54 100644 --- a/backend/open_webui/utils/oauth.py +++ b/backend/open_webui/utils/oauth.py @@ -4,9 +4,11 @@ import mimetypes import sys import uuid import json +from datetime import datetime, timedelta import re import fnmatch +import time import aiohttp from authlib.integrations.starlette_client import OAuth @@ -17,8 +19,12 @@ from fastapi import ( ) from starlette.responses import RedirectResponse + from open_webui.models.auths import Auths +from open_webui.models.oauth_sessions import OAuthSessions from open_webui.models.users import Users + + from open_webui.models.groups import Groups, GroupModel, GroupUpdateForm, GroupForm from open_webui.config import ( DEFAULT_USER_ROLE, @@ -49,7 +55,7 @@ from open_webui.env import ( WEBUI_NAME, WEBUI_AUTH_COOKIE_SAME_SITE, WEBUI_AUTH_COOKIE_SECURE, - ENABLE_OAUTH_SESSION_TOKENS_COOKIES, + ENABLE_OAUTH_ID_TOKEN_COOKIE, ) from open_webui.utils.misc import parse_duration from open_webui.utils.auth import get_password_hash, create_token @@ -131,11 +137,187 @@ class OAuthManager: def __init__(self, app): self.oauth = OAuth() self.app = app + + self._clients = {} for _, provider_config in OAUTH_PROVIDERS.items(): provider_config["register"](self.oauth) def get_client(self, provider_name): - return self.oauth.create_client(provider_name) + if provider_name not in self._clients: + self._clients[provider_name] = self.oauth.create_client(provider_name) + return self._clients[provider_name] + + def get_server_metadata_url(self, provider_name): + if provider_name in self._clients: + client = self._clients[provider_name] + return ( + client.server_metadata_url + if hasattr(client, "server_metadata_url") + else None + ) + return None + + def get_oauth_token( + self, user_id: str, session_id: str, force_refresh: bool = False + ): + """ + Get a valid OAuth token for the user, automatically refreshing if needed. + + Args: + user_id: The user ID + provider: Optional provider name. If None, gets the most recent session. + force_refresh: Force token refresh even if current token appears valid + + Returns: + dict: OAuth token data with access_token, or None if no valid token available + """ + try: + # Get the OAuth session + session = OAuthSessions.get_session_by_id_and_user_id(session_id, user_id) + if not session: + log.warning( + f"No OAuth session found for user {user_id}, session {session_id}" + ) + return None + + if force_refresh or datetime.now() + timedelta( + minutes=5 + ) >= datetime.fromtimestamp(session.expires_at): + log.debug( + f"Token refresh needed for user {user_id}, provider {session.provider}" + ) + refreshed_token = self._refresh_token(session) + if refreshed_token: + return refreshed_token + else: + log.warning( + f"Token refresh failed for user {user_id}, provider {session.provider}" + ) + return None + return session.token + + except Exception as e: + log.error(f"Error getting OAuth token for user {user_id}: {e}") + return None + + async def _refresh_token(self, session) -> dict: + """ + Refresh an OAuth token if needed, with concurrency protection. + + Args: + session: The OAuth session object + + Returns: + dict: Refreshed token data, or None if refresh failed + """ + try: + # Perform the actual refresh + refreshed_token = await self._perform_token_refresh(session) + + if refreshed_token: + # Update the session with new token data + session = OAuthSessions.update_session_by_id( + session.id, refreshed_token + ) + log.info(f"Successfully refreshed token for session {session.id}") + return session.token + else: + log.error(f"Failed to refresh token for session {session.id}") + return None + + except Exception as e: + log.error(f"Error refreshing token for session {session.id}: {e}") + return None + + async def _perform_token_refresh(self, session) -> dict: + """ + Perform the actual OAuth token refresh. + + Args: + session: The OAuth session object + + Returns: + dict: New token data, or None if refresh failed + """ + provider = session.provider + token_data = session.token + + if not token_data.get("refresh_token"): + log.warning(f"No refresh token available for session {session.id}") + return None + + try: + client = self.get_client(provider) + if not client: + log.error(f"No OAuth client found for provider {provider}") + return None + + token_endpoint = None + async with aiohttp.ClientSession(trust_env=True) as session_http: + async with session_http.get(client.gserver_metadata_url) as r: + if r.status == 200: + openid_data = await r.json() + token_endpoint = openid_data.get("token_endpoint") + else: + log.error( + f"Failed to fetch OpenID configuration for provider {provider}" + ) + if not token_endpoint: + log.error(f"No token endpoint found for provider {provider}") + return None + + # Prepare refresh request + refresh_data = { + "grant_type": "refresh_token", + "refresh_token": token_data["refresh_token"], + "client_id": client.client_id, + } + # Add client_secret if available (some providers require it) + if hasattr(client, "client_secret") and client.client_secret: + refresh_data["client_secret"] = client.client_secret + + # Make refresh request + async with aiohttp.ClientSession(trust_env=True) as session_http: + async with session_http.post( + token_endpoint, + data=refresh_data, + headers={"Content-Type": "application/x-www-form-urlencoded"}, + ssl=AIOHTTP_CLIENT_SESSION_SSL, + ) as r: + if r.status == 200: + new_token_data = await r.json() + + # Merge with existing token data (preserve refresh_token if not provided) + if "refresh_token" not in new_token_data: + new_token_data["refresh_token"] = token_data[ + "refresh_token" + ] + + # Add timestamp for tracking + new_token_data["issued_at"] = datetime.now().timestamp() + + # Calculate expires_at if we have expires_in + if ( + "expires_in" in new_token_data + and "expires_at" not in new_token_data + ): + new_token_data["expires_at"] = ( + datetime.now().timestamp() + + new_token_data["expires_in"] + ) + + log.debug(f"Token refresh successful for provider {provider}") + return new_token_data + else: + error_text = await r.text() + log.error( + f"Token refresh failed for provider {provider}: {r.status} - {error_text}" + ) + return None + + except Exception as e: + log.error(f"Exception during token refresh for provider {provider}: {e}") + return None def get_user_role(self, user, user_data): user_count = Users.get_num_users() @@ -624,33 +806,42 @@ class OAuthManager: secure=WEBUI_AUTH_COOKIE_SECURE, ) - if ENABLE_OAUTH_SIGNUP.value: - if ENABLE_OAUTH_SESSION_TOKENS_COOKIES: - oauth_id_token = token.get("id_token") - response.set_cookie( - key="oauth_id_token", - value=oauth_id_token, - httponly=True, - samesite=WEBUI_AUTH_COOKIE_SAME_SITE, - secure=WEBUI_AUTH_COOKIE_SECURE, - ) + # Legacy cookies for compatibility with older frontend versions + if ENABLE_OAUTH_ID_TOKEN_COOKIE: + response.set_cookie( + key="oauth_id_token", + value=token.get("id_token"), + httponly=True, + samesite=WEBUI_AUTH_COOKIE_SAME_SITE, + secure=WEBUI_AUTH_COOKIE_SECURE, + ) - oauth_access_token = token.get("access_token") - response.set_cookie( - key="oauth_access_token", - value=oauth_access_token, - httponly=True, - samesite=WEBUI_AUTH_COOKIE_SAME_SITE, - secure=WEBUI_AUTH_COOKIE_SECURE, - ) + try: + # Add timestamp for tracking + token["issued_at"] = datetime.now().timestamp() - oauth_refresh_token = token.get("refresh_token") - response.set_cookie( - key="oauth_refresh_token", - value=oauth_refresh_token, - httponly=True, - samesite=WEBUI_AUTH_COOKIE_SAME_SITE, - secure=WEBUI_AUTH_COOKIE_SECURE, - ) + # Calculate expires_at if we have expires_in + if "expires_in" in token and "expires_at" not in token: + token["expires_at"] = datetime.now().timestamp() + token["expires_in"] + + session_id = await OAuthSessions.create_session( + user_id=user.id, + provider=provider, + token=token, + ) + + response.set_cookie( + key="oauth_session_id", + value=session_id, + httponly=True, + samesite=WEBUI_AUTH_COOKIE_SAME_SITE, + secure=WEBUI_AUTH_COOKIE_SECURE, + ) + + log.info( + f"Stored OAuth session server-side for user {user.id}, provider {provider}" + ) + except Exception as e: + log.error(f"Failed to store OAuth session server-side: {e}") return response diff --git a/backend/open_webui/utils/tools.py b/backend/open_webui/utils/tools.py index d3ea432019..f0e889d023 100644 --- a/backend/open_webui/utils/tools.py +++ b/backend/open_webui/utils/tools.py @@ -129,6 +129,21 @@ async def get_tools( headers["Authorization"] = ( f"Bearer {request.state.token.credentials}" ) + elif auth_type == "oauth": + oauth_token = None + try: + oauth_token = ( + await request.app.state.oauth_manager.get_oauth_token( + user.id, + request.cookies.get("oauth_session_id", None), + ) + ) + except Exception as e: + log.error(f"Error getting OAuth token: {e}") + + headers["Authorization"] = ( + f"Bearer {oauth_token.get('access_token', '')}" + ) elif auth_type == "request_headers": headers.update(dict(request.headers)) diff --git a/src/lib/components/AddServerModal.svelte b/src/lib/components/AddServerModal.svelte index 6fad62bc15..8951696c74 100644 --- a/src/lib/components/AddServerModal.svelte +++ b/src/lib/components/AddServerModal.svelte @@ -287,6 +287,7 @@ {#if !direct} + {/if} @@ -305,6 +306,12 @@ > {$i18n.t('Forwards system user session credentials to authenticate')}
      + {:else if auth_type === 'oauth'} +
      + {$i18n.t('Forwards user OAuth access token to authenticate')} +
      {:else if auth_type === 'request_headers'}
      Date: Mon, 8 Sep 2025 18:09:01 +0400 Subject: [PATCH 0119/1079] refac --- backend/open_webui/models/oauth_sessions.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/backend/open_webui/models/oauth_sessions.py b/backend/open_webui/models/oauth_sessions.py index b0b5aa29a6..9fd5335ce5 100644 --- a/backend/open_webui/models/oauth_sessions.py +++ b/backend/open_webui/models/oauth_sessions.py @@ -150,7 +150,7 @@ class OAuthSessionTable: if session: session.token = self._decrypt_token(session.token) return OAuthSessionModel.model_validate(session) - + return None except Exception as e: log.error(f"Error getting OAuth session by ID: {e}") @@ -170,7 +170,7 @@ class OAuthSessionTable: if session: session.token = self._decrypt_token(session.token) return OAuthSessionModel.model_validate(session) - ) + return None except Exception as e: log.error(f"Error getting OAuth session by ID: {e}") @@ -182,14 +182,13 @@ class OAuthSessionTable: with get_db() as db: sessions = db.query(OAuthSession).filter_by(user_id=user_id).all() - results = [] for session in sessions: session.token = self._decrypt_token(session.token) results.append(OAuthSessionModel.model_validate(session)) return results - + except Exception as e: log.error(f"Error getting OAuth sessions by user ID: {e}") return [] @@ -215,7 +214,7 @@ class OAuthSessionTable: if session: session.token = self._decrypt_token(session.token) return OAuthSessionModel.model_validate(session) - + return None except Exception as e: log.error(f"Error updating OAuth session tokens: {e}") From fc11e4384fe98fac659e10596f67c23483578867 Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Mon, 8 Sep 2025 18:17:11 +0400 Subject: [PATCH 0120/1079] refac --- backend/open_webui/routers/auths.py | 1 + backend/open_webui/utils/auth.py | 4 ++++ backend/open_webui/utils/oauth.py | 4 ++-- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/backend/open_webui/routers/auths.py b/backend/open_webui/routers/auths.py index d044b4a168..e3271250c1 100644 --- a/backend/open_webui/routers/auths.py +++ b/backend/open_webui/routers/auths.py @@ -677,6 +677,7 @@ async def signup(request: Request, response: Response, form_data: SignupForm): async def signout(request: Request, response: Response): response.delete_cookie("token") response.delete_cookie("oui-session") + response.delete_cookie("oauth_id_token") oauth_session_id = request.cookies.get("oauth_session_id") if oauth_session_id: diff --git a/backend/open_webui/utils/auth.py b/backend/open_webui/utils/auth.py index 19994bafbd..f941ef9263 100644 --- a/backend/open_webui/utils/auth.py +++ b/backend/open_webui/utils/auth.py @@ -313,6 +313,10 @@ def get_current_user( # Delete the token cookie if request.cookies.get("token"): response.delete_cookie("token") + + if request.cookies.get("oauth_id_token"): + response.delete_cookie("oauth_id_token") + # Delete OAuth session if present if request.cookies.get("oauth_session_id"): response.delete_cookie("oauth_session_id") diff --git a/backend/open_webui/utils/oauth.py b/backend/open_webui/utils/oauth.py index 55ee3eee54..63250c2a54 100644 --- a/backend/open_webui/utils/oauth.py +++ b/backend/open_webui/utils/oauth.py @@ -824,7 +824,7 @@ class OAuthManager: if "expires_in" in token and "expires_at" not in token: token["expires_at"] = datetime.now().timestamp() + token["expires_in"] - session_id = await OAuthSessions.create_session( + session = OAuthSessions.create_session( user_id=user.id, provider=provider, token=token, @@ -832,7 +832,7 @@ class OAuthManager: response.set_cookie( key="oauth_session_id", - value=session_id, + value=session.id, httponly=True, samesite=WEBUI_AUTH_COOKIE_SAME_SITE, secure=WEBUI_AUTH_COOKIE_SECURE, From 35c1c48fd2aea41c89b701aaf16cbffff1e9327d Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Mon, 8 Sep 2025 18:18:04 +0400 Subject: [PATCH 0121/1079] refac --- src/lib/components/AddServerModal.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/components/AddServerModal.svelte b/src/lib/components/AddServerModal.svelte index 8951696c74..6863c8cbb8 100644 --- a/src/lib/components/AddServerModal.svelte +++ b/src/lib/components/AddServerModal.svelte @@ -310,7 +310,7 @@
      - {$i18n.t('Forwards user OAuth access token to authenticate')} + {$i18n.t('Forwards system user OAuth access token to authenticate')}
      {:else if auth_type === 'request_headers'}
      Date: Mon, 8 Sep 2025 18:23:44 +0400 Subject: [PATCH 0122/1079] refac --- backend/open_webui/utils/tools.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/backend/open_webui/utils/tools.py b/backend/open_webui/utils/tools.py index f0e889d023..9d64db7d78 100644 --- a/backend/open_webui/utils/tools.py +++ b/backend/open_webui/utils/tools.py @@ -119,6 +119,8 @@ async def get_tools( function_name = spec["name"] auth_type = tool_server_connection.get("auth_type", "bearer") + + cookies = {} headers = {} if auth_type == "bearer": @@ -126,10 +128,12 @@ async def get_tools( f"Bearer {tool_server_connection.get('key', '')}" ) elif auth_type == "session": + cookies = request.cookies headers["Authorization"] = ( f"Bearer {request.state.token.credentials}" ) elif auth_type == "oauth": + cookies = request.cookies oauth_token = None try: oauth_token = ( @@ -145,6 +149,7 @@ async def get_tools( f"Bearer {oauth_token.get('access_token', '')}" ) elif auth_type == "request_headers": + cookies = request.cookies headers.update(dict(request.headers)) headers["Content-Type"] = "application/json" @@ -154,6 +159,7 @@ async def get_tools( return await execute_tool_server( url=tool_server_data["url"], headers=headers, + cookies=cookies, name=function_name, params=kwargs, server_data=tool_server_data, @@ -635,6 +641,7 @@ async def get_tool_servers_data( async def execute_tool_server( url: str, headers: Dict[str, str], + cookies: Dict[str, str], name: str, params: Dict[str, Any], server_data: Dict[str, Any], From f71834720e623761d972d4d740e9bbd90a3a86c6 Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Mon, 8 Sep 2025 18:35:09 +0400 Subject: [PATCH 0123/1079] refac --- backend/open_webui/utils/middleware.py | 2 +- backend/open_webui/utils/tools.py | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/backend/open_webui/utils/middleware.py b/backend/open_webui/utils/middleware.py index 27b0f11290..406ee1b16f 100644 --- a/backend/open_webui/utils/middleware.py +++ b/backend/open_webui/utils/middleware.py @@ -817,7 +817,7 @@ async def process_chat_payload(request, form_data, user, metadata, model): oauth_token = None try: - oauth_token = await request.app.state.oauth_manager.get_oauth_token( + oauth_token = request.app.state.oauth_manager.get_oauth_token( user.id, request.cookies.get("oauth_session_id", None) ) except Exception as e: diff --git a/backend/open_webui/utils/tools.py b/backend/open_webui/utils/tools.py index 9d64db7d78..efdb7c85b1 100644 --- a/backend/open_webui/utils/tools.py +++ b/backend/open_webui/utils/tools.py @@ -137,7 +137,7 @@ async def get_tools( oauth_token = None try: oauth_token = ( - await request.app.state.oauth_manager.get_oauth_token( + request.app.state.oauth_manager.get_oauth_token( user.id, request.cookies.get("oauth_session_id", None), ) @@ -715,6 +715,7 @@ async def execute_tool_server( final_url, json=body_params, headers=headers, + cookies=cookies, ssl=AIOHTTP_CLIENT_SESSION_TOOL_SERVER_SSL, ) as response: if response.status >= 400: @@ -731,6 +732,7 @@ async def execute_tool_server( async with request_method( final_url, headers=headers, + cookies=cookies, ssl=AIOHTTP_CLIENT_SESSION_TOOL_SERVER_SSL, ) as response: if response.status >= 400: From b5bb6ae177dcdc4e8274d7e5ffa50bc8099fd466 Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Mon, 8 Sep 2025 18:50:23 +0400 Subject: [PATCH 0124/1079] refac --- backend/open_webui/functions.py | 10 ++++++++++ backend/open_webui/main.py | 8 ++++++++ backend/open_webui/routers/users.py | 14 ++++++++++++++ backend/open_webui/utils/middleware.py | 13 ++++++++++++- backend/open_webui/utils/tools.py | 12 +----------- 5 files changed, 45 insertions(+), 12 deletions(-) diff --git a/backend/open_webui/functions.py b/backend/open_webui/functions.py index db367ccbd0..4122cbbe0d 100644 --- a/backend/open_webui/functions.py +++ b/backend/open_webui/functions.py @@ -219,6 +219,15 @@ async def generate_function_chat_completion( __task__ = metadata.get("task", None) __task_body__ = metadata.get("task_body", None) + oauth_token = None + try: + oauth_token = request.app.state.oauth_manager.get_oauth_token( + user.id, + request.cookies.get("oauth_session_id", None), + ) + except Exception as e: + log.error(f"Error getting OAuth token: {e}") + extra_params = { "__event_emitter__": __event_emitter__, "__event_call__": __event_call__, @@ -230,6 +239,7 @@ async def generate_function_chat_completion( "__files__": files, "__user__": user.model_dump() if isinstance(user, UserModel) else {}, "__metadata__": metadata, + "__oauth_token__": oauth_token, "__request__": request, } extra_params["__tools__"] = await get_tools( diff --git a/backend/open_webui/main.py b/backend/open_webui/main.py index de7dcae086..ea60900c9c 100644 --- a/backend/open_webui/main.py +++ b/backend/open_webui/main.py @@ -1408,6 +1408,14 @@ async def chat_completion( model_item = form_data.pop("model_item", {}) tasks = form_data.pop("background_tasks", None) + oauth_token = None + try: + oauth_token = request.app.state.oauth_manager.get_oauth_token( + user.id, request.cookies.get("oauth_session_id", None) + ) + except Exception as e: + log.error(f"Error getting OAuth token: {e}") + metadata = {} try: if not model_item.get("direct", False): diff --git a/backend/open_webui/routers/users.py b/backend/open_webui/routers/users.py index 4d2539a18e..5b331dce73 100644 --- a/backend/open_webui/routers/users.py +++ b/backend/open_webui/routers/users.py @@ -10,6 +10,8 @@ from pydantic import BaseModel from open_webui.models.auths import Auths +from open_webui.models.oauth_sessions import OAuthSessions + from open_webui.models.groups import Groups from open_webui.models.chats import Chats from open_webui.models.users import ( @@ -340,6 +342,18 @@ async def get_user_by_id(user_id: str, user=Depends(get_verified_user)): ) +@router.get("/{user_id}/oauth/sessions", response_model=Optional[dict]) +async def get_user_oauth_sessions_by_id(user_id: str, user=Depends(get_admin_user)): + sessions = OAuthSessions.get_sessions_by_user_id(user_id) + if sessions and len(sessions) > 0: + return sessions + else: + raise HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, + detail=ERROR_MESSAGES.USER_NOT_FOUND, + ) + + ############################ # GetUserProfileImageById ############################ diff --git a/backend/open_webui/utils/middleware.py b/backend/open_webui/utils/middleware.py index 406ee1b16f..ae2c96c6da 100644 --- a/backend/open_webui/utils/middleware.py +++ b/backend/open_webui/utils/middleware.py @@ -818,7 +818,8 @@ async def process_chat_payload(request, form_data, user, metadata, model): oauth_token = None try: oauth_token = request.app.state.oauth_manager.get_oauth_token( - user.id, request.cookies.get("oauth_session_id", None) + user.id, + request.cookies.get("oauth_session_id", None), ) except Exception as e: log.error(f"Error getting OAuth token: {e}") @@ -1493,11 +1494,21 @@ async def process_chat_response( ): return response + oauth_token = None + try: + oauth_token = request.app.state.oauth_manager.get_oauth_token( + user.id, + request.cookies.get("oauth_session_id", None), + ) + except Exception as e: + log.error(f"Error getting OAuth token: {e}") + extra_params = { "__event_emitter__": event_emitter, "__event_call__": event_caller, "__user__": user.model_dump() if isinstance(user, UserModel) else {}, "__metadata__": metadata, + "__oauth_token__": oauth_token, "__request__": request, "__model__": model, } diff --git a/backend/open_webui/utils/tools.py b/backend/open_webui/utils/tools.py index efdb7c85b1..f7e7f7acef 100644 --- a/backend/open_webui/utils/tools.py +++ b/backend/open_webui/utils/tools.py @@ -134,17 +134,7 @@ async def get_tools( ) elif auth_type == "oauth": cookies = request.cookies - oauth_token = None - try: - oauth_token = ( - request.app.state.oauth_manager.get_oauth_token( - user.id, - request.cookies.get("oauth_session_id", None), - ) - ) - except Exception as e: - log.error(f"Error getting OAuth token: {e}") - + oauth_token = extra_params.get("__oauth_token__", None) headers["Authorization"] = ( f"Bearer {oauth_token.get('access_token', '')}" ) From b786d1e3f3308ef4f0f95d7130ddbcaaca4fc927 Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Mon, 8 Sep 2025 18:52:59 +0400 Subject: [PATCH 0125/1079] refac --- backend/open_webui/utils/oauth.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/backend/open_webui/utils/oauth.py b/backend/open_webui/utils/oauth.py index 63250c2a54..7eedc30c31 100644 --- a/backend/open_webui/utils/oauth.py +++ b/backend/open_webui/utils/oauth.py @@ -824,6 +824,12 @@ class OAuthManager: if "expires_in" in token and "expires_at" not in token: token["expires_at"] = datetime.now().timestamp() + token["expires_in"] + # Clean up any existing sessions for this user/provider first + sessions = OAuthSessions.get_sessions_by_user_id(user.id) + for session in sessions: + if session.provider == provider: + OAuthSessions.delete_session_by_id(session.id) + session = OAuthSessions.create_session( user_id=user.id, provider=provider, From 001dab0439972e9cbd7b88afcb6f7f6950a01bba Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Mon, 8 Sep 2025 18:55:57 +0400 Subject: [PATCH 0126/1079] refac: wording --- src/lib/components/AddConnectionModal.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/components/AddConnectionModal.svelte b/src/lib/components/AddConnectionModal.svelte index 21c900fba2..fd19c0b302 100644 --- a/src/lib/components/AddConnectionModal.svelte +++ b/src/lib/components/AddConnectionModal.svelte @@ -355,7 +355,7 @@ for="prefix-id-input" class={`mb-0.5 text-xs text-gray-500 ${($settings?.highContrastMode ?? false) ? 'text-gray-800 dark:text-gray-100' : ''}`} - >{$i18n.t('Provider')}{$i18n.t('Provider Type')}
      From 8a9f8627017bd0a74cbd647891552b26e56aabb7 Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Mon, 8 Sep 2025 19:07:00 +0400 Subject: [PATCH 0127/1079] refac --- src/lib/components/AddConnectionModal.svelte | 130 ++++++++++++------ src/lib/components/AddServerModal.svelte | 7 - .../Connections/OllamaConnection.svelte | 1 + .../Connections/OpenAIConnection.svelte | 8 +- .../Settings/Connections/Connection.svelte | 6 - 5 files changed, 91 insertions(+), 61 deletions(-) diff --git a/src/lib/components/AddConnectionModal.svelte b/src/lib/components/AddConnectionModal.svelte index fd19c0b302..02784a7abb 100644 --- a/src/lib/components/AddConnectionModal.svelte +++ b/src/lib/components/AddConnectionModal.svelte @@ -31,6 +31,7 @@ let url = ''; let key = ''; + let auth_type = 'bearer'; let connectionType = 'external'; let azure = false; @@ -73,6 +74,7 @@ { url, key, + auth_type, config: { azure: azure, api_version: apiVersion @@ -140,6 +142,7 @@ const connection = { url, key, + auth_type, config: { enable: enable, tags: tags, @@ -157,6 +160,7 @@ url = ''; key = ''; + auth_type = 'bearer'; prefixId = ''; tags = []; modelIds = []; @@ -167,6 +171,8 @@ url = connection.url; key = connection.key; + auth_type = connection.auth_type ?? 'bearer'; + enable = connection.config?.enable ?? true; tags = connection.config?.tags ?? []; prefixId = connection.config?.prefix_id ?? ''; @@ -305,23 +311,63 @@
      -
      {$i18n.t('Auth')} - {$i18n.t('Key')} -
      -
      - +
      +
      + +
      + +
      + {#if auth_type === 'bearer'} + + {:else if auth_type === 'none'} +
      + {$i18n.t('No authentication')} +
      + {:else if auth_type === 'session'} +
      + {$i18n.t('Forwards system user session credentials to authenticate')} +
      + {:else if auth_type === 'oauth'} +
      + {$i18n.t('Forwards system user OAuth access token to authenticate')} +
      + {/if} +
      +
      +
      - {:else if auth_type === 'request_headers'} -
      - {$i18n.t('Forwards system user headers to authenticate')} -
      {/if}
      diff --git a/src/lib/components/admin/Settings/Connections/OllamaConnection.svelte b/src/lib/components/admin/Settings/Connections/OllamaConnection.svelte index 48976f0769..3e25965391 100644 --- a/src/lib/components/admin/Settings/Connections/OllamaConnection.svelte +++ b/src/lib/components/admin/Settings/Connections/OllamaConnection.svelte @@ -71,6 +71,7 @@ class="w-full text-sm bg-transparent outline-hidden" placeholder={$i18n.t('Enter URL (e.g. http://localhost:11434)')} bind:value={url} + readonly={true} /> diff --git a/src/lib/components/admin/Settings/Connections/OpenAIConnection.svelte b/src/lib/components/admin/Settings/Connections/OpenAIConnection.svelte index b82e47be76..a4c2ada059 100644 --- a/src/lib/components/admin/Settings/Connections/OpenAIConnection.svelte +++ b/src/lib/components/admin/Settings/Connections/OpenAIConnection.svelte @@ -69,6 +69,7 @@ placeholder={$i18n.t('API Base URL')} bind:value={url} autocomplete="off" + readonly={true} /> {#if pipeline} @@ -94,13 +95,6 @@
      {/if}
      - -
      diff --git a/src/lib/components/chat/Settings/Connections/Connection.svelte b/src/lib/components/chat/Settings/Connections/Connection.svelte index ea9089f2f1..c8a5ec152a 100644 --- a/src/lib/components/chat/Settings/Connections/Connection.svelte +++ b/src/lib/components/chat/Settings/Connections/Connection.svelte @@ -72,12 +72,6 @@ autocomplete="off" />
      - -
      From 4cea3a57be18869eb2b9dc2484b80a9cb0a68619 Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Mon, 8 Sep 2025 19:09:26 +0400 Subject: [PATCH 0128/1079] refac --- backend/open_webui/utils/tools.py | 3 +++ src/lib/components/AddServerModal.svelte | 8 ++++++++ 2 files changed, 11 insertions(+) diff --git a/backend/open_webui/utils/tools.py b/backend/open_webui/utils/tools.py index f7e7f7acef..74974e1461 100644 --- a/backend/open_webui/utils/tools.py +++ b/backend/open_webui/utils/tools.py @@ -127,6 +127,9 @@ async def get_tools( headers["Authorization"] = ( f"Bearer {tool_server_connection.get('key', '')}" ) + elif auth_type == "none": + # No authentication + pass elif auth_type == "session": cookies = request.cookies headers["Authorization"] = ( diff --git a/src/lib/components/AddServerModal.svelte b/src/lib/components/AddServerModal.svelte index fdd905aef0..fd70534d28 100644 --- a/src/lib/components/AddServerModal.svelte +++ b/src/lib/components/AddServerModal.svelte @@ -283,6 +283,8 @@ class={`w-full text-sm bg-transparent pr-5 ${($settings?.highContrastMode ?? false) ? 'placeholder:text-gray-700 dark:placeholder:text-gray-100' : 'outline-hidden placeholder:text-gray-300 dark:placeholder:text-gray-700'}`} bind:value={auth_type} > + + @@ -299,6 +301,12 @@ placeholder={$i18n.t('API Key')} required={false} /> + {:else if auth_type === 'none'} +
      + {$i18n.t('No authentication')} +
      {:else if auth_type === 'session'}
      Date: Mon, 8 Sep 2025 19:12:20 +0400 Subject: [PATCH 0129/1079] refac --- src/lib/apis/index.ts | 13 ++++++++++++- src/routes/+layout.svelte | 13 ++++++++++++- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/src/lib/apis/index.ts b/src/lib/apis/index.ts index 646514eee8..e36eeba12e 100644 --- a/src/lib/apis/index.ts +++ b/src/lib/apis/index.ts @@ -354,8 +354,19 @@ export const getToolServersData = async (servers: object[]) => { .filter((server) => server?.config?.enable) .map(async (server) => { let error = null; + + let toolServerToken = null; + const auth_type = server?.auth_type ?? 'bearer'; + if (auth_type === 'bearer') { + toolServerToken = server?.key; + } else if (auth_type === 'none') { + // No authentication + } else if (auth_type === 'session') { + toolServerToken = localStorage.token; + } + const data = await getToolServerData( - (server?.auth_type ?? 'bearer') === 'bearer' ? server?.key : localStorage.token, + toolServerToken, (server?.path ?? '').includes('://') ? server?.path : `${server?.url}${(server?.path ?? '').startsWith('/') ? '' : '/'}${server?.path}` diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte index d0453dc5ed..d4205bb9ee 100644 --- a/src/routes/+layout.svelte +++ b/src/routes/+layout.svelte @@ -222,8 +222,19 @@ if (toolServer) { console.log(toolServer); + + let toolServerToken = null; + const auth_type = toolServer?.auth_type ?? 'bearer'; + if (auth_type === 'bearer') { + toolServerToken = toolServer?.key; + } else if (auth_type === 'none') { + // No authentication + } else if (auth_type === 'session') { + toolServerToken = localStorage.token; + } + const res = await executeToolServer( - (toolServer?.auth_type ?? 'bearer') === 'bearer' ? toolServer?.key : localStorage.token, + toolServerToken, toolServer.url, data?.name, data?.params, From 474df5e534ef85848f32d541366f955edab4cdf9 Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Mon, 8 Sep 2025 19:18:55 +0400 Subject: [PATCH 0130/1079] refac --- src/lib/components/AddConnectionModal.svelte | 6 +++--- src/lib/components/admin/Settings/Connections.svelte | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/lib/components/AddConnectionModal.svelte b/src/lib/components/AddConnectionModal.svelte index 02784a7abb..ecf03ee2ce 100644 --- a/src/lib/components/AddConnectionModal.svelte +++ b/src/lib/components/AddConnectionModal.svelte @@ -74,8 +74,8 @@ { url, key, - auth_type, config: { + auth_type, azure: azure, api_version: apiVersion } @@ -142,13 +142,13 @@ const connection = { url, key, - auth_type, config: { enable: enable, tags: tags, prefix_id: prefixId, model_ids: modelIds, connection_type: connectionType, + auth_type, ...(!ollama && azure ? { azure: true, api_version: apiVersion } : {}) } }; @@ -171,7 +171,7 @@ url = connection.url; key = connection.key; - auth_type = connection.auth_type ?? 'bearer'; + auth_type = connection.config.auth_type ?? 'bearer'; enable = connection.config?.enable ?? true; tags = connection.config?.tags ?? []; diff --git a/src/lib/components/admin/Settings/Connections.svelte b/src/lib/components/admin/Settings/Connections.svelte index 0b9d2874b2..03897accc6 100644 --- a/src/lib/components/admin/Settings/Connections.svelte +++ b/src/lib/components/admin/Settings/Connections.svelte @@ -261,10 +261,10 @@
      {#each OPENAI_API_BASE_URLS as url, idx} { updateOpenAIHandler(); }} @@ -326,7 +326,7 @@
      {#each OLLAMA_BASE_URLS as url, idx} { From 2b2d123531eb3f42c0e940593832a64e2806240d Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Mon, 8 Sep 2025 19:42:50 +0400 Subject: [PATCH 0131/1079] refac: oauth auth type in openai connection --- backend/open_webui/routers/openai.py | 222 ++++++++++--------- backend/open_webui/utils/tools.py | 19 +- src/lib/components/AddConnectionModal.svelte | 2 - 3 files changed, 121 insertions(+), 122 deletions(-) diff --git a/backend/open_webui/routers/openai.py b/backend/open_webui/routers/openai.py index a94791bdf5..d7b5f75260 100644 --- a/backend/open_webui/routers/openai.py +++ b/backend/open_webui/routers/openai.py @@ -119,6 +119,74 @@ def openai_reasoning_model_handler(payload): return payload +def get_headers_and_cookies( + request: Request, + url, + key=None, + config=None, + metadata: Optional[dict] = None, + user: UserModel = None, +): + cookies = {} + headers = { + "Content-Type": "application/json", + **( + { + "HTTP-Referer": "https://openwebui.com/", + "X-Title": "Open WebUI", + } + if "openrouter.ai" in url + else {} + ), + **( + { + "X-OpenWebUI-User-Name": quote(user.name, safe=" "), + "X-OpenWebUI-User-Id": user.id, + "X-OpenWebUI-User-Email": user.email, + "X-OpenWebUI-User-Role": user.role, + **( + {"X-OpenWebUI-Chat-Id": metadata.get("chat_id")} + if metadata and metadata.get("chat_id") + else {} + ), + } + if ENABLE_FORWARD_USER_INFO_HEADERS + else {} + ), + } + + token = None + auth_type = config.get("auth_type") + + if auth_type == "bearer" or auth_type is None: + # Default to bearer if not specified + token = f"{key}" + elif auth_type == "none": + token = None + elif auth_type == "session": + cookies = request.cookies + token = request.state.token.credentials + elif auth_type == "oauth": + cookies = request.cookies + + oauth_token = None + try: + oauth_token = request.app.state.oauth_manager.get_oauth_token( + user.id, + request.cookies.get("oauth_session_id", None), + ) + except Exception as e: + log.error(f"Error getting OAuth token: {e}") + + if oauth_token: + token = f"{oauth_token.get('access_token', '')}" + + if token: + headers["Authorization"] = f"Bearer {token}" + + return headers, cookies + + ########################################## # # API routes @@ -210,34 +278,23 @@ async def speech(request: Request, user=Depends(get_verified_user)): return FileResponse(file_path) url = request.app.state.config.OPENAI_API_BASE_URLS[idx] + key = request.app.state.config.OPENAI_API_KEYS[idx] + api_config = request.app.state.config.OPENAI_API_CONFIGS.get( + str(idx), + request.app.state.config.OPENAI_API_CONFIGS.get(url, {}), # Legacy support + ) + + headers, cookies = get_headers_and_cookies( + request, url, key, api_config, user=user + ) r = None try: r = requests.post( url=f"{url}/audio/speech", data=body, - headers={ - "Content-Type": "application/json", - "Authorization": f"Bearer {request.app.state.config.OPENAI_API_KEYS[idx]}", - **( - { - "HTTP-Referer": "https://openwebui.com/", - "X-Title": "Open WebUI", - } - if "openrouter.ai" in url - else {} - ), - **( - { - "X-OpenWebUI-User-Name": quote(user.name, safe=" "), - "X-OpenWebUI-User-Id": user.id, - "X-OpenWebUI-User-Email": user.email, - "X-OpenWebUI-User-Role": user.role, - } - if ENABLE_FORWARD_USER_INFO_HEADERS - else {} - ), - }, + headers=headers, + cookies=cookies, stream=True, ) @@ -401,7 +458,10 @@ async def get_filtered_models(models, user): return filtered_models -@cached(ttl=MODELS_CACHE_TTL, key=lambda _, user: f"openai_all_models_{user.id}" if user else "openai_all_models") +@cached( + ttl=MODELS_CACHE_TTL, + key=lambda _, user: f"openai_all_models_{user.id}" if user else "openai_all_models", +) async def get_all_models(request: Request, user: UserModel) -> dict[str, list]: log.info("get_all_models()") @@ -489,19 +549,9 @@ async def get_models( timeout=aiohttp.ClientTimeout(total=AIOHTTP_CLIENT_TIMEOUT_MODEL_LIST), ) as session: try: - headers = { - "Content-Type": "application/json", - **( - { - "X-OpenWebUI-User-Name": quote(user.name, safe=" "), - "X-OpenWebUI-User-Id": user.id, - "X-OpenWebUI-User-Email": user.email, - "X-OpenWebUI-User-Role": user.role, - } - if ENABLE_FORWARD_USER_INFO_HEADERS - else {} - ), - } + headers, cookies = get_headers_and_cookies( + request, url, key, api_config, user=user + ) if api_config.get("azure", False): models = { @@ -509,11 +559,10 @@ async def get_models( "object": "list", } else: - headers["Authorization"] = f"Bearer {key}" - async with session.get( f"{url}/models", headers=headers, + cookies=cookies, ssl=AIOHTTP_CLIENT_SESSION_SSL, ) as r: if r.status != 200: @@ -572,7 +621,9 @@ class ConnectionVerificationForm(BaseModel): @router.post("/verify") async def verify_connection( - form_data: ConnectionVerificationForm, user=Depends(get_admin_user) + request: Request, + form_data: ConnectionVerificationForm, + user=Depends(get_admin_user), ): url = form_data.url key = form_data.key @@ -584,19 +635,9 @@ async def verify_connection( timeout=aiohttp.ClientTimeout(total=AIOHTTP_CLIENT_TIMEOUT_MODEL_LIST), ) as session: try: - headers = { - "Content-Type": "application/json", - **( - { - "X-OpenWebUI-User-Name": quote(user.name, safe=" "), - "X-OpenWebUI-User-Id": user.id, - "X-OpenWebUI-User-Email": user.email, - "X-OpenWebUI-User-Role": user.role, - } - if ENABLE_FORWARD_USER_INFO_HEADERS - else {} - ), - } + headers, cookies = get_headers_and_cookies( + request, url, key, api_config, user=user + ) if api_config.get("azure", False): headers["api-key"] = key @@ -605,6 +646,7 @@ async def verify_connection( async with session.get( url=f"{url}/openai/models?api-version={api_version}", headers=headers, + cookies=cookies, ssl=AIOHTTP_CLIENT_SESSION_SSL, ) as r: try: @@ -624,11 +666,10 @@ async def verify_connection( return response_data else: - headers["Authorization"] = f"Bearer {key}" - async with session.get( f"{url}/models", headers=headers, + cookies=cookies, ssl=AIOHTTP_CLIENT_SESSION_SSL, ) as r: try: @@ -836,32 +877,9 @@ async def generate_chat_completion( convert_logit_bias_input_to_json(payload["logit_bias"]) ) - headers = { - "Content-Type": "application/json", - **( - { - "HTTP-Referer": "https://openwebui.com/", - "X-Title": "Open WebUI", - } - if "openrouter.ai" in url - else {} - ), - **( - { - "X-OpenWebUI-User-Name": quote(user.name, safe=" "), - "X-OpenWebUI-User-Id": user.id, - "X-OpenWebUI-User-Email": user.email, - "X-OpenWebUI-User-Role": user.role, - **( - {"X-OpenWebUI-Chat-Id": metadata.get("chat_id")} - if metadata and metadata.get("chat_id") - else {} - ), - } - if ENABLE_FORWARD_USER_INFO_HEADERS - else {} - ), - } + headers, cookies = get_headers_and_cookies( + request, url, key, api_config, metadata, user=user + ) if api_config.get("azure", False): api_version = api_config.get("api_version", "2023-03-15-preview") @@ -871,7 +889,6 @@ async def generate_chat_completion( request_url = f"{request_url}/chat/completions?api-version={api_version}" else: request_url = f"{url}/chat/completions" - headers["Authorization"] = f"Bearer {key}" payload = json.dumps(payload) @@ -890,6 +907,7 @@ async def generate_chat_completion( url=request_url, data=payload, headers=headers, + cookies=cookies, ssl=AIOHTTP_CLIENT_SESSION_SSL, ) @@ -951,31 +969,27 @@ async def embeddings(request: Request, form_data: dict, user): models = request.app.state.OPENAI_MODELS if model_id in models: idx = models[model_id]["urlIdx"] + url = request.app.state.config.OPENAI_API_BASE_URLS[idx] key = request.app.state.config.OPENAI_API_KEYS[idx] + api_config = request.app.state.config.OPENAI_API_CONFIGS.get( + str(idx), + request.app.state.config.OPENAI_API_CONFIGS.get(url, {}), # Legacy support + ) + r = None session = None streaming = False + + headers, cookies = get_headers_and_cookies(request, url, key, api_config, user=user) try: session = aiohttp.ClientSession(trust_env=True) r = await session.request( method="POST", url=f"{url}/embeddings", data=body, - headers={ - "Authorization": f"Bearer {key}", - "Content-Type": "application/json", - **( - { - "X-OpenWebUI-User-Name": quote(user.name, safe=" "), - "X-OpenWebUI-User-Id": user.id, - "X-OpenWebUI-User-Email": user.email, - "X-OpenWebUI-User-Role": user.role, - } - if ENABLE_FORWARD_USER_INFO_HEADERS and user - else {} - ), - }, + headers=headers, + cookies=cookies, ) if "text/event-stream" in r.headers.get("Content-Type", ""): @@ -1037,19 +1051,9 @@ async def proxy(path: str, request: Request, user=Depends(get_verified_user)): streaming = False try: - headers = { - "Content-Type": "application/json", - **( - { - "X-OpenWebUI-User-Name": quote(user.name, safe=" "), - "X-OpenWebUI-User-Id": user.id, - "X-OpenWebUI-User-Email": user.email, - "X-OpenWebUI-User-Role": user.role, - } - if ENABLE_FORWARD_USER_INFO_HEADERS - else {} - ), - } + headers, cookies = get_headers_and_cookies( + request, url, key, api_config, user=user + ) if api_config.get("azure", False): api_version = api_config.get("api_version", "2023-03-15-preview") @@ -1062,7 +1066,6 @@ async def proxy(path: str, request: Request, user=Depends(get_verified_user)): request_url = f"{url}/{path}?api-version={api_version}" else: - headers["Authorization"] = f"Bearer {key}" request_url = f"{url}/{path}" session = aiohttp.ClientSession(trust_env=True) @@ -1071,6 +1074,7 @@ async def proxy(path: str, request: Request, user=Depends(get_verified_user)): url=request_url, data=body, headers=headers, + cookies=cookies, ssl=AIOHTTP_CLIENT_SESSION_SSL, ) diff --git a/backend/open_webui/utils/tools.py b/backend/open_webui/utils/tools.py index 74974e1461..fd441df9e1 100644 --- a/backend/open_webui/utils/tools.py +++ b/backend/open_webui/utils/tools.py @@ -138,12 +138,10 @@ async def get_tools( elif auth_type == "oauth": cookies = request.cookies oauth_token = extra_params.get("__oauth_token__", None) - headers["Authorization"] = ( - f"Bearer {oauth_token.get('access_token', '')}" - ) - elif auth_type == "request_headers": - cookies = request.cookies - headers.update(dict(request.headers)) + if oauth_token: + headers["Authorization"] = ( + f"Bearer {oauth_token.get('access_token', '')}" + ) headers["Content-Type"] = "application/json" @@ -564,9 +562,7 @@ async def get_tool_server_data(token: str, url: str) -> Dict[str, Any]: return data -async def get_tool_servers_data( - servers: List[Dict[str, Any]], session_token: Optional[str] = None -) -> List[Dict[str, Any]]: +async def get_tool_servers_data(servers: List[Dict[str, Any]]) -> List[Dict[str, Any]]: # Prepare list of enabled servers along with their original index server_entries = [] for idx, server in enumerate(servers): @@ -582,8 +578,9 @@ async def get_tool_servers_data( if auth_type == "bearer": token = server.get("key", "") - elif auth_type == "session": - token = session_token + elif auth_type == "none": + # No authentication + pass id = info.get("id") if not id: diff --git a/src/lib/components/AddConnectionModal.svelte b/src/lib/components/AddConnectionModal.svelte index ecf03ee2ce..900dc74d2b 100644 --- a/src/lib/components/AddConnectionModal.svelte +++ b/src/lib/components/AddConnectionModal.svelte @@ -443,8 +443,6 @@
      {/if} -
      -
      Date: Mon, 8 Sep 2025 19:53:44 +0400 Subject: [PATCH 0132/1079] refac --- backend/open_webui/routers/openai.py | 2 +- backend/open_webui/utils/tools.py | 2 +- src/lib/components/AddConnectionModal.svelte | 4 ++-- src/lib/components/AddServerModal.svelte | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/backend/open_webui/routers/openai.py b/backend/open_webui/routers/openai.py index d7b5f75260..184f47038d 100644 --- a/backend/open_webui/routers/openai.py +++ b/backend/open_webui/routers/openai.py @@ -166,7 +166,7 @@ def get_headers_and_cookies( elif auth_type == "session": cookies = request.cookies token = request.state.token.credentials - elif auth_type == "oauth": + elif auth_type == "system_oauth": cookies = request.cookies oauth_token = None diff --git a/backend/open_webui/utils/tools.py b/backend/open_webui/utils/tools.py index fd441df9e1..0ef0cf47fb 100644 --- a/backend/open_webui/utils/tools.py +++ b/backend/open_webui/utils/tools.py @@ -135,7 +135,7 @@ async def get_tools( headers["Authorization"] = ( f"Bearer {request.state.token.credentials}" ) - elif auth_type == "oauth": + elif auth_type == "system_oauth": cookies = request.cookies oauth_token = extra_params.get("__oauth_token__", None) if oauth_token: diff --git a/src/lib/components/AddConnectionModal.svelte b/src/lib/components/AddConnectionModal.svelte index 900dc74d2b..fb4da3175f 100644 --- a/src/lib/components/AddConnectionModal.svelte +++ b/src/lib/components/AddConnectionModal.svelte @@ -330,7 +330,7 @@ {#if !ollama} {#if !direct} - + {/if} {/if} @@ -355,7 +355,7 @@ > {$i18n.t('Forwards system user session credentials to authenticate')}
      - {:else if auth_type === 'oauth'} + {:else if auth_type === 'system_oauth'}
      diff --git a/src/lib/components/AddServerModal.svelte b/src/lib/components/AddServerModal.svelte index fd70534d28..e35421a091 100644 --- a/src/lib/components/AddServerModal.svelte +++ b/src/lib/components/AddServerModal.svelte @@ -289,7 +289,7 @@ {#if !direct} - + {/if}
      From 41f9a8caff14e2c0c780b3cb23ffccaaa859ec83 Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Mon, 8 Sep 2025 20:25:19 +0400 Subject: [PATCH 0133/1079] refac --- src/lib/components/AddServerModal.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/components/AddServerModal.svelte b/src/lib/components/AddServerModal.svelte index e35421a091..4a407b8279 100644 --- a/src/lib/components/AddServerModal.svelte +++ b/src/lib/components/AddServerModal.svelte @@ -313,7 +313,7 @@ > {$i18n.t('Forwards system user session credentials to authenticate')}
      - {:else if auth_type === 'oauth'} + {:else if auth_type === 'system_oauth'}
      From daa2a036f8743f58c158663d7b5ee32cce837b3a Mon Sep 17 00:00:00 2001 From: Antonio Pisano Date: Mon, 8 Sep 2025 18:51:33 +0200 Subject: [PATCH 0134/1079] Extend docling configuration options to include: * do_ocr * force_ocr * pdf_backend * table_mode * pipeline as per https://github.com/docling-project/docling-serve/blob/main/docs/usage.md See https://github.com/open-webui/open-webui/issues/17148 --- backend/open_webui/config.py | 30 +++++ backend/open_webui/main.py | 10 ++ backend/open_webui/retrieval/loaders/main.py | 18 ++- backend/open_webui/routers/retrieval.py | 46 +++++++- .../admin/Settings/Documents.svelte | 111 +++++++++++++++--- 5 files changed, 198 insertions(+), 17 deletions(-) diff --git a/backend/open_webui/config.py b/backend/open_webui/config.py index 1fe031cdad..692b22c742 100644 --- a/backend/open_webui/config.py +++ b/backend/open_webui/config.py @@ -2229,6 +2229,18 @@ DOCLING_SERVER_URL = PersistentConfig( os.getenv("DOCLING_SERVER_URL", "http://docling:5001"), ) +DOCLING_DO_OCR = PersistentConfig( + "DOCLING_DO_OCR", + "rag.docling_do_ocr", + os.getenv("DOCLING_DO_OCR", "True").lower() == "true", +) + +DOCLING_FORCE_OCR = PersistentConfig( + "DOCLING_FORCE_OCR", + "rag.docling_force_ocr", + os.getenv("DOCLING_FORCE_OCR", "False").lower() == "true", +) + DOCLING_OCR_ENGINE = PersistentConfig( "DOCLING_OCR_ENGINE", "rag.docling_ocr_engine", @@ -2241,6 +2253,24 @@ DOCLING_OCR_LANG = PersistentConfig( os.getenv("DOCLING_OCR_LANG", "eng,fra,deu,spa"), ) +DOCLING_PDF_BACKEND = PersistentConfig( + "DOCLING_PDF_BACKEND", + "rag.docling_pdf_backend", + os.getenv("DOCLING_PDF_BACKEND", "dlparse_v4"), +) + +DOCLING_TABLE_MODE = PersistentConfig( + "DOCLING_TABLE_MODE", + "rag.docling_table_mode", + os.getenv("DOCLING_TABLE_MODE", "accurate"), +) + +DOCLING_PIPELINE = PersistentConfig( + "DOCLING_PIPELINE", + "rag.docling_pipeline", + os.getenv("DOCLING_PIPELINE", "standard"), +) + DOCLING_DO_PICTURE_DESCRIPTION = PersistentConfig( "DOCLING_DO_PICTURE_DESCRIPTION", "rag.docling_do_picture_description", diff --git a/backend/open_webui/main.py b/backend/open_webui/main.py index d24bd5dcf1..547027bc9d 100644 --- a/backend/open_webui/main.py +++ b/backend/open_webui/main.py @@ -243,8 +243,13 @@ from open_webui.config import ( EXTERNAL_DOCUMENT_LOADER_API_KEY, TIKA_SERVER_URL, DOCLING_SERVER_URL, + DOCLING_DO_OCR, + DOCLING_FORCE_OCR, DOCLING_OCR_ENGINE, DOCLING_OCR_LANG, + DOCLING_PDF_BACKEND, + DOCLING_TABLE_MODE, + DOCLING_PIPELINE, DOCLING_DO_PICTURE_DESCRIPTION, DOCLING_PICTURE_DESCRIPTION_MODE, DOCLING_PICTURE_DESCRIPTION_LOCAL, @@ -810,8 +815,13 @@ app.state.config.EXTERNAL_DOCUMENT_LOADER_URL = EXTERNAL_DOCUMENT_LOADER_URL app.state.config.EXTERNAL_DOCUMENT_LOADER_API_KEY = EXTERNAL_DOCUMENT_LOADER_API_KEY app.state.config.TIKA_SERVER_URL = TIKA_SERVER_URL app.state.config.DOCLING_SERVER_URL = DOCLING_SERVER_URL +app.state.config.DOCLING_DO_OCR = DOCLING_DO_OCR +app.state.config.DOCLING_FORCE_OCR = DOCLING_FORCE_OCR app.state.config.DOCLING_OCR_ENGINE = DOCLING_OCR_ENGINE app.state.config.DOCLING_OCR_LANG = DOCLING_OCR_LANG +app.state.config.DOCLING_PDF_BACKEND = DOCLING_PDF_BACKEND +app.state.config.DOCLING_TABLE_MODE = DOCLING_TABLE_MODE +app.state.config.DOCLING_PIPELINE = DOCLING_PIPELINE app.state.config.DOCLING_DO_PICTURE_DESCRIPTION = DOCLING_DO_PICTURE_DESCRIPTION app.state.config.DOCLING_PICTURE_DESCRIPTION_MODE = DOCLING_PICTURE_DESCRIPTION_MODE app.state.config.DOCLING_PICTURE_DESCRIPTION_LOCAL = DOCLING_PICTURE_DESCRIPTION_LOCAL diff --git a/backend/open_webui/retrieval/loaders/main.py b/backend/open_webui/retrieval/loaders/main.py index 9b90dca041..a459274a09 100644 --- a/backend/open_webui/retrieval/loaders/main.py +++ b/backend/open_webui/retrieval/loaders/main.py @@ -148,7 +148,7 @@ class DoclingLoader: ) } - params = {"image_export_mode": "placeholder", "table_mode": "accurate"} + params = {"image_export_mode": "placeholder"} if self.params: if self.params.get("do_picture_description"): @@ -174,7 +174,11 @@ class DoclingLoader: self.params.get("picture_description_api", {}) ) - if self.params.get("ocr_engine") and self.params.get("ocr_lang"): + params["do_ocr"] = self.params.get("do_ocr") + + params["force_ocr"] = self.params.get("force_ocr") + + if self.params.get("do_ocr") and self.params.get("ocr_engine") and self.params.get("ocr_lang"): params["ocr_engine"] = self.params.get("ocr_engine") params["ocr_lang"] = [ lang.strip() @@ -182,6 +186,16 @@ class DoclingLoader: if lang.strip() ] + if self.params.get("pdf_backend"): + params["pdf_backend"] = self.params.get("pdf_backend") + + if self.params.get("table_mode"): + params["table_mode"] = self.params.get("table_mode") + + if self.params.get("pipeline"): + params["pipeline"] = self.params.get("pipeline") + + endpoint = f"{self.url}/v1/convert/file" r = requests.post(endpoint, files=files, data=params) diff --git a/backend/open_webui/routers/retrieval.py b/backend/open_webui/routers/retrieval.py index fdb7786258..d76944868b 100644 --- a/backend/open_webui/routers/retrieval.py +++ b/backend/open_webui/routers/retrieval.py @@ -426,8 +426,13 @@ async def get_rag_config(request: Request, user=Depends(get_admin_user)): "EXTERNAL_DOCUMENT_LOADER_API_KEY": request.app.state.config.EXTERNAL_DOCUMENT_LOADER_API_KEY, "TIKA_SERVER_URL": request.app.state.config.TIKA_SERVER_URL, "DOCLING_SERVER_URL": request.app.state.config.DOCLING_SERVER_URL, + "DOCLING_DO_OCR": request.app.state.config.DOCLING_DO_OCR, + "DOCLING_FORCE_OCR": request.app.state.config.DOCLING_FORCE_OCR, "DOCLING_OCR_ENGINE": request.app.state.config.DOCLING_OCR_ENGINE, "DOCLING_OCR_LANG": request.app.state.config.DOCLING_OCR_LANG, + "DOCLING_PDF_BACKEND": request.app.state.config.DOCLING_PDF_BACKEND, + "DOCLING_TABLE_MODE": request.app.state.config.DOCLING_TABLE_MODE, + "DOCLING_PIPELINE": request.app.state.config.DOCLING_PIPELINE, "DOCLING_DO_PICTURE_DESCRIPTION": request.app.state.config.DOCLING_DO_PICTURE_DESCRIPTION, "DOCLING_PICTURE_DESCRIPTION_MODE": request.app.state.config.DOCLING_PICTURE_DESCRIPTION_MODE, "DOCLING_PICTURE_DESCRIPTION_LOCAL": request.app.state.config.DOCLING_PICTURE_DESCRIPTION_LOCAL, @@ -596,8 +601,13 @@ class ConfigForm(BaseModel): TIKA_SERVER_URL: Optional[str] = None DOCLING_SERVER_URL: Optional[str] = None + DOCLING_DO_OCR: Optional[bool] = None + DOCLING_FORCE_OCR: Optional[bool] = None DOCLING_OCR_ENGINE: Optional[str] = None DOCLING_OCR_LANG: Optional[str] = None + DOCLING_PDF_BACKEND: Optional[str] = None + DOCLING_TABLE_MODE: Optional[str] = None + DOCLING_PIPELINE: Optional[str] = None DOCLING_DO_PICTURE_DESCRIPTION: Optional[bool] = None DOCLING_PICTURE_DESCRIPTION_MODE: Optional[str] = None DOCLING_PICTURE_DESCRIPTION_LOCAL: Optional[dict] = None @@ -767,6 +777,16 @@ async def update_rag_config( if form_data.DOCLING_SERVER_URL is not None else request.app.state.config.DOCLING_SERVER_URL ) + request.app.state.config.DOCLING_DO_OCR = ( + form_data.DOCLING_DO_OCR + if form_data.DOCLING_DO_OCR is not None + else request.app.state.config.DOCLING_DO_OCR + ) + request.app.state.config.DOCLING_FORCE_OCR = ( + form_data.DOCLING_FORCE_OCR + if form_data.DOCLING_FORCE_OCR is not None + else request.app.state.config.DOCLING_FORCE_OCR + ) request.app.state.config.DOCLING_OCR_ENGINE = ( form_data.DOCLING_OCR_ENGINE if form_data.DOCLING_OCR_ENGINE is not None @@ -777,7 +797,21 @@ async def update_rag_config( if form_data.DOCLING_OCR_LANG is not None else request.app.state.config.DOCLING_OCR_LANG ) - + request.app.state.config.DOCLING_PDF_BACKEND = ( + form_data.DOCLING_PDF_BACKEND + if form_data.DOCLING_PDF_BACKEND is not None + else request.app.state.config.DOCLING_PDF_BACKEND + ) + request.app.state.config.DOCLING_TABLE_MODE = ( + form_data.DOCLING_TABLE_MODE + if form_data.DOCLING_TABLE_MODE is not None + else request.app.state.config.DOCLING_TABLE_MODE + ) + request.app.state.config.DOCLING_PIPELINE = ( + form_data.DOCLING_PIPELINE + if form_data.DOCLING_PIPELINE is not None + else request.app.state.config.DOCLING_PIPELINE + ) request.app.state.config.DOCLING_DO_PICTURE_DESCRIPTION = ( form_data.DOCLING_DO_PICTURE_DESCRIPTION if form_data.DOCLING_DO_PICTURE_DESCRIPTION is not None @@ -1062,8 +1096,13 @@ async def update_rag_config( "EXTERNAL_DOCUMENT_LOADER_API_KEY": request.app.state.config.EXTERNAL_DOCUMENT_LOADER_API_KEY, "TIKA_SERVER_URL": request.app.state.config.TIKA_SERVER_URL, "DOCLING_SERVER_URL": request.app.state.config.DOCLING_SERVER_URL, + "DOCLING_DO_OCR": request.app.state.config.DOCLING_DO_OCR, + "DOCLING_FORCE_OCR": request.app.state.config.DOCLING_FORCE_OCR, "DOCLING_OCR_ENGINE": request.app.state.config.DOCLING_OCR_ENGINE, "DOCLING_OCR_LANG": request.app.state.config.DOCLING_OCR_LANG, + "DOCLING_PDF_BACKEND": request.app.state.config.DOCLING_PDF_BACKEND, + "DOCLING_TABLE_MODE": request.app.state.config.DOCLING_TABLE_MODE, + "DOCLING_PIPELINE": request.app.state.config.DOCLING_PIPELINE, "DOCLING_DO_PICTURE_DESCRIPTION": request.app.state.config.DOCLING_DO_PICTURE_DESCRIPTION, "DOCLING_PICTURE_DESCRIPTION_MODE": request.app.state.config.DOCLING_PICTURE_DESCRIPTION_MODE, "DOCLING_PICTURE_DESCRIPTION_LOCAL": request.app.state.config.DOCLING_PICTURE_DESCRIPTION_LOCAL, @@ -1453,8 +1492,13 @@ def process_file( TIKA_SERVER_URL=request.app.state.config.TIKA_SERVER_URL, DOCLING_SERVER_URL=request.app.state.config.DOCLING_SERVER_URL, DOCLING_PARAMS={ + "do_ocr": request.app.state.config.DOCLING_DO_OCR, + "force_ocr": request.app.state.config.DOCLING_FORCE_OCR, "ocr_engine": request.app.state.config.DOCLING_OCR_ENGINE, "ocr_lang": request.app.state.config.DOCLING_OCR_LANG, + "pdf_backend": request.app.state.config.DOCLING_PDF_BACKEND, + "table_mode": request.app.state.config.DOCLING_TABLE_MODE, + "pipeline": request.app.state.config.DOCLING_PIPELINE, "do_picture_description": request.app.state.config.DOCLING_DO_PICTURE_DESCRIPTION, "picture_description_mode": request.app.state.config.DOCLING_PICTURE_DESCRIPTION_MODE, "picture_description_local": request.app.state.config.DOCLING_PICTURE_DESCRIPTION_LOCAL, diff --git a/src/lib/components/admin/Settings/Documents.svelte b/src/lib/components/admin/Settings/Documents.svelte index d3a244fa45..b3e8aa1a44 100644 --- a/src/lib/components/admin/Settings/Documents.svelte +++ b/src/lib/components/admin/Settings/Documents.svelte @@ -152,7 +152,8 @@ return; } if ( - RAGConfig.CONTENT_EXTRACTION_ENGINE === 'docling' && + RAGConfig.CONTENT_EXTRACTION_ENGINE === 'docling' && + RAGConfig.DOCLING_DO_OCR && ((RAGConfig.DOCLING_OCR_ENGINE === '' && RAGConfig.DOCLING_OCR_LANG !== '') || (RAGConfig.DOCLING_OCR_ENGINE !== '' && RAGConfig.DOCLING_OCR_LANG === '')) ) { @@ -161,6 +162,16 @@ ); return; } + if ( + RAGConfig.CONTENT_EXTRACTION_ENGINE === 'docling' && + RAGConfig.DOCLING_DO_OCR === false && + RAGConfig.DOCLING_FORCE_OCR === true + ) { + toast.error( + $i18n.t('In order to force OCR, performing OCR must be enabled.') + ); + return; + } if ( RAGConfig.CONTENT_EXTRACTION_ENGINE === 'datalab_marker' && @@ -544,21 +555,93 @@ placeholder={$i18n.t('Enter Docling Server URL')} bind:value={RAGConfig.DOCLING_SERVER_URL} /> -
      -
      - - -
      +
      +
      +
      + {$i18n.t('Perform OCR')} +
      +
      + +
      +
      +
      + {#if RAGConfig.DOCLING_DO_OCR} +
      + + +
      + {/if} +
      +
      +
      + {$i18n.t('Force OCR')} +
      +
      + +
      +
      +
      +
      +
      + + {$i18n.t('PDF Backend')} + +
      +
      + +
      +
      +
      +
      + + {$i18n.t('Table Mode')} + +
      +
      + +
      +
      +
      +
      + + {$i18n.t('Pipeline')} + +
      +
      + +
      +
      +
      {$i18n.t('Describe Pictures in Documents')} From fcebc82ec2009a37233e71aad823978b4348b781 Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Mon, 8 Sep 2025 20:54:18 +0400 Subject: [PATCH 0135/1079] refac: chat controls panel resize logic --- src/lib/components/chat/ChatControls.svelte | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/src/lib/components/chat/ChatControls.svelte b/src/lib/components/chat/ChatControls.svelte index 96160a7e13..cfd1fdc1c1 100644 --- a/src/lib/components/chat/ChatControls.svelte +++ b/src/lib/components/chat/ChatControls.svelte @@ -39,7 +39,11 @@ export const openPane = () => { if (parseInt(localStorage?.chatControlsSize)) { - pane.resize(parseInt(localStorage?.chatControlsSize)); + const container = document.getElementById('chat-container'); + let size = Math.floor( + (parseInt(localStorage?.chatControlsSize) / container.clientWidth) * 100 + ); + pane.resize(size); } else { pane.resize(minSize); } @@ -91,7 +95,7 @@ const resizeObserver = new ResizeObserver((entries) => { for (let entry of entries) { const width = entry.contentRect.width; - // calculate the percentage of 200px + // calculate the percentage of 350px const percentage = (350 / width) * 100; // set the minSize to the percentage, must be an integer minSize = Math.floor(percentage); @@ -99,6 +103,13 @@ if ($showControls) { if (pane && pane.isExpanded() && pane.getSize() < minSize) { pane.resize(minSize); + } else { + let size = Math.floor( + (parseInt(localStorage?.chatControlsSize) / container.clientWidth) * 100 + ); + if (size < minSize) { + pane.resize(minSize); + } } } } @@ -217,7 +228,11 @@ if (size < minSize) { localStorage.chatControlsSize = 0; } else { - localStorage.chatControlsSize = size; + // save the size in pixels to localStorage + const container = document.getElementById('chat-container'); + localStorage.chatControlsSize = Math.floor((size / 100) * container.clientWidth); + + console.log('saved size', localStorage.chatControlsSize); } } }} From 4a76bea80cdcd757807e72e5ccd527ca24816d82 Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Mon, 8 Sep 2025 21:03:29 +0400 Subject: [PATCH 0136/1079] refac --- src/lib/components/chat/Messages/Markdown/Source.svelte | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/lib/components/chat/Messages/Markdown/Source.svelte b/src/lib/components/chat/Messages/Markdown/Source.svelte index 7215d19134..f87de43461 100644 --- a/src/lib/components/chat/Messages/Markdown/Source.svelte +++ b/src/lib/components/chat/Messages/Markdown/Source.svelte @@ -21,6 +21,10 @@ // Helper function to return only the domain from a URL function getDomain(url: string): string { const domain = url.replace('http://', '').replace('https://', '').split(/[/?#]/)[0]; + + if (domain.startsWith('www.')) { + return domain.slice(4); + } return domain; } @@ -44,7 +48,9 @@ }} > - {attributes.title ? formattedTitle(attributes.title) : ''} + {decodeURIComponent(attributes.title) + ? formattedTitle(decodeURIComponent(attributes.title)) + : ''} {/if} From c6a46195c06f9f954a8c6a2659250201a3b27f63 Mon Sep 17 00:00:00 2001 From: Classic298 <27028174+Classic298@users.noreply.github.com> Date: Mon, 8 Sep 2025 23:40:39 +0200 Subject: [PATCH 0137/1079] Changelog dev (#18) * Update CHANGELOG.md * Update CHANGELOG.md * Update CHANGELOG.md * Update CHANGELOG.md * Update CHANGELOG.md * Update CHANGELOG.md * Update CHANGELOG.md --- CHANGELOG.md | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 349b984e19..ec9154c6d1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,60 @@ 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/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.6.27] - 2025-09-08 + +### Added + +- 🎨 Azure OpenAI image generation is now supported, with configurations for IMAGES_OPENAI_API_VERSION via environment variable and admin UI. [#17147](https://github.com/open-webui/open-webui/pull/17147), [#16274](https://github.com/open-webui/open-webui/discussions/16274), [Docs:#679](https://github.com/open-webui/docs/pull/679) +- 📁 Emoji folder icons were added, allowing users to personalize workspace organization with visual cues, including improved chevron display. [Commit](https://github.com/open-webui/open-webui/pull/17070/commits/1588f42fe777ad5d807e3f2fc8dbbc47a8db87c0), [Commit](https://github.com/open-webui/open-webui/pull/17070/commits/b70c0f36c0f5bbfc2a767429984d6fba1a7bb26c), [Commit](https://github.com/open-webui/open-webui/pull/17070/commits/11dea8795bfce42aa5d8d58ef316ded05173bd87), [Commit](https://github.com/open-webui/open-webui/pull/17070/commits/c0a47169fa059154d5f5a9ea6b94f9a66d82f255) +- 📁 The 'Search Collection' input field now dynamically displays the total number of files within the knowledge base. [Commit](https://github.com/open-webui/open-webui/pull/17070/commits/fbbe1117ae4c9c8fec6499d790eee275818eccc5) +- ☁️ A provider toggle in connection settings now allows users to manually specify Azure OpenAI deployments. [Commit](https://github.com/open-webui/open-webui/pull/17070/commits/5bdd334b74fbd154085f2d590f4afdba32469c8a) +- ⚡ Model list caching performance was optimized by fixing cache key generation to reduce redundant API calls. [#17158](https://github.com/open-webui/open-webui/pull/17158) +- ⚡ Comprehensive N+1 query performance is optimized by reducing database queries from 1+N to 1+1 patterns across major listing endpoints. [#17165](https://github.com/open-webui/open-webui/pull/17165), [#17160](https://github.com/open-webui/open-webui/pull/17160), [#17161](https://github.com/open-webui/open-webui/pull/17161), [#17162](https://github.com/open-webui/open-webui/pull/17162), [#17159](https://github.com/open-webui/open-webui/pull/17159), [#17166](https://github.com/open-webui/open-webui/pull/17166) +- ⚡ The PDF.js library is now dynamically loaded, significantly reducing initial page load size and improving responsiveness. [#17222](https://github.com/open-webui/open-webui/pull/17222) +- ⚡ The heic2any library is now dynamically loaded across various message input components, including channels, for faster page loads. [#17225](https://github.com/open-webui/open-webui/pull/17225), [#17229](https://github.com/open-webui/open-webui/pull/17229) +- 📚 The knowledge API now supports a "delete_file" query parameter, allowing configurable file deletion behavior. [Commit](https://github.com/open-webui/open-webui/pull/17070/commits/22c4ef4fb096498066b73befe993ae3a82f7a8e7) +- 📊 Llama.cpp timing statistics are now integrated into the usage field for comprehensive model performance metrics. [Commit](https://github.com/open-webui/open-webui/pull/17070/commits/e830b4959ecd4b2795e29e53026984a58a7696a9) +- 🗄️ The PGVECTOR_CREATE_EXTENSION environment variable now allows control over automatic pgvector extension creation. [Commit](https://github.com/open-webui/open-webui/pull/17070/commits/c2b4976c82d335ed524bd80dc914b5e2f5bfbd9e), [Commit](https://github.com/open-webui/open-webui/pull/17070/commits/b45219c8b15b48d5ee3d42983e1107bbcefbab01), [Docs:#672](https://github.com/open-webui/docs/pull/672) +- 🔧 External tool server authentication is enhanced with a "request_headers" type that forwards complete request headers. [Commit](https://github.com/open-webui/open-webui/pull/17070/commits/77b65ccbfbf3971ca71d3c7a70d77168e8f007dd) +- 🔒 Comprehensive server-side OAuth token management was implemented, securely storing encrypted tokens in a new database table and introducing an automatic refresh mechanism, enabling seamless and secure forwarding of valid user-specific OAuth tokens to downstream services, including OpenAI-compatible endpoints and external tool servers via the new "system_oauth" authentication type, resolving long-standing issues such as large token size limitations, stale/expired tokens, and reliable token propagation, and enhancing overall security by minimizing client-side token exposure, configurable via "ENABLE_OAUTH_ID_TOKEN_COOKIE" and "OAUTH_SESSION_TOKEN_ENCRYPTION_KEY" environment variables. [Docs:#683](https://github.com/open-webui/docs/pull/683), [#17210](https://github.com/open-webui/open-webui/pull/17210), [#8957](https://github.com/open-webui/open-webui/discussions/8957), [#11029](https://github.com/open-webui/open-webui/discussions/11029), [#17178](https://github.com/open-webui/open-webui/issues/17178), [#17183](https://github.com/open-webui/open-webui/issues/17183), [Commit](https://github.com/open-webui/open-webui/commit/217f4daef09b36d3d4cc4681e11d3ebd9984a1a5), [Commit](https://github.com/open-webui/open-webui/commit/fc11e4384fe98fac659e10596f67c23483578867), [Commit](https://github.com/open-webui/open-webui/commit/f11bdc6ab5dd5682bb3e27166e77581f5b8af3e0), [Commit](https://github.com/open-webui/open-webui/commit/f71834720e623761d972d4d740e9bbd90a3a86c6), [Commit](https://github.com/open-webui/open-webui/commit/b5bb6ae177dcdc4e8274d7e5ffa50bc8099fd466), [Commit](https://github.com/open-webui/open-webui/commit/b786d1e3f3308ef4f0f95d7130ddbcaaca4fc927), [Commit](https://github.com/open-webui/open-webui/commit/8a9f8627017bd0a74cbd647891552b26e56aabb7), [Commit](https://github.com/open-webui/open-webui/commit/30d1dc2c60e303756120fe1c5538968c4e6139f4), [Commit](https://github.com/open-webui/open-webui/commit/2b2d123531eb3f42c0e940593832a64e2806240d), [Commit](https://github.com/open-webui/open-webui/commit/6f6412dd16c63c2bb4df79a96b814bf69cb3f880) +- 🔒 Conditional Permission Hardening for OpenShift Deployments: Added a build argument to enable optional permission hardening for OpenShift and container environments. [Commit](https://github.com/open-webui/open-webui/pull/17070/commits/0ebe4f8f8490451ac8e85a4846f010854d9b54e5) +- 👥 Regex pattern support is added for OAuth blocked groups, allowing more flexible group filtering rules. [Commit](https://github.com/open-webui/open-webui/pull/17070/commits/df66e21472646648d008ebb22b0e8d5424d491df) +- 💬 Web search result display was enhanced to include titles and favicons, providing a clearer overview of search sources. [Commit](https://github.com/open-webui/open-webui/pull/17070/commits/33f04a771455e3fabf8f0e8ebb994ae7f41b8ed4), [Commit](https://github.com/open-webui/open-webui/pull/17070/commits/0a85dd4bca23022729eafdbc82c8c139fa365af2), [Commit](https://github.com/open-webui/open-webui/pull/17070/commits/16090bc2721fde492afa2c4af5927e2b668527e1), [#17197](https://github.com/open-webui/open-webui/pull/17197), [#14179](https://github.com/open-webui/open-webui/issues/14179), [Commit](https://github.com/open-webui/open-webui/pull/17070/commits/1cdb7aed1ee9bf81f2fd0404be52dcfa64f8ed4f), [Commit](https://github.com/open-webui/open-webui/pull/17070/commits/f2525ebc447c008cf7269ef20ce04fa456f302c4), [Commit](https://github.com/open-webui/open-webui/pull/17070/commits/7f523de408ede4075349d8de71ae0214b7e1a62e), [Commit](https://github.com/open-webui/open-webui/pull/17070/commits/3d37e4a42d344051ae715ab59bd7b5718e46c343), [Commit](https://github.com/open-webui/open-webui/pull/17070/commits/cd5e2be27b613314aadda6107089331783987985), [Commit](https://github.com/open-webui/open-webui/pull/17070/commits/6dc0df247347aede2762fe2065cf30275fd137ae) +- 💬 A new setting was added to control whether clicking a suggested prompt automatically sends the message or only inserts the text. [#17192](https://github.com/open-webui/open-webui/issues/17192), [Commit](https://github.com/open-webui/open-webui/commit/e023a98f11fc52feb21e4065ec707cc98e50c7d3) +- 🔄 Various improvements were implemented across the frontend and backend to enhance performance, stability, and security. +- 🌐 Translations for Portuguese (Brazil), Simplified Chinese, Catalan, and Spanish were enhanced and expanded. + +### Fixed + +- 🔍 Hybrid search functionality now correctly handles lexical-semantic weight labels and avoids errors when BM25 weight is zero. [#17049](https://github.com/open-webui/open-webui/pull/17049), [#17046](https://github.com/open-webui/open-webui/issues/17046) +- 🛑 Task stopping errors are prevented by gracefully handling multiple stop requests for the same task. [#17195](https://github.com/open-webui/open-webui/pull/17195) +- 🐍 Code execution package detection precision is improved in Pyodide to prevent unnecessary package inclusions. [Commit](https://github.com/open-webui/open-webui/pull/17070/commits/bbe116795860a81a647d9567e0d9cb1950650095) +- 🛠️ Tool message format API compliance is fixed by ensuring content fields in tool call responses contain valid string values instead of null. [Commit](https://github.com/open-webui/open-webui/pull/17070/commits/37bf0087e5b8a324009c9d06b304027df351ea6b) +- 📱 Mobile app config API authentication now supports Authorization header token verification with cookie fallback for iOS and Android requests. [#17175](https://github.com/open-webui/open-webui/pull/17175) +- 💾 Knowledge file save race conditions are prevented by serializing API calls and adding an "isSaving" guard. [#17137](https://github.com/open-webui/open-webui/pull/17137), [Commit](https://github.com/open-webui/open-webui/pull/17070/commits/4ca936f0bf9813bee11ec8aea41d7e34fb6b16a9) +- 🔐 The SSO login button visibility is restored for OIDC PKCE authentication without a client secret. [#17012](https://github.com/open-webui/open-webui/pull/17012) +- 🔊 Text-to-Speech (TTS) API requests now use proper URL joining methods, ensuring reliable functionality regardless of trailing slashes in the base URL. [#17061](https://github.com/open-webui/open-webui/pull/17061) +- 🛡️ Admin account creation on Hugging Face Spaces now correctly detects the configured port, resolving issues with custom port deployments. [#17064](https://github.com/open-webui/open-webui/pull/17064) +- 📁 Unicode filename support is improved for external document loaders by properly URL-encoding filenames in HTTP headers. [#17013](https://github.com/open-webui/open-webui/pull/17013), [#17000](https://github.com/open-webui/open-webui/issues/17000) +- 🔗 Web page and YouTube attachments are now correctly processed by setting their type as "text" and using collection names for accurate content retrieval. [Commit](https://github.com/open-webui/open-webui/pull/17070/commits/487979859a6ffcfd60468f523822cdf838fbef5b) +- ✍️ Message input composition event handling is fixed to properly manage text input for multilingual users using Input Method Editors (IME). [#17085](https://github.com/open-webui/open-webui/pull/17085) +- 💬 Follow-up tooltip duplication is removed, streamlining the user interface and preventing visual clutter. [#17186](https://github.com/open-webui/open-webui/pull/17186) +- 🎨 Chat button text display is corrected by preventing clipping of descending characters and removing unnecessary capitalization. [#17191](https://github.com/open-webui/open-webui/pull/17191) +- 🧠 RAG Loop/Error with Gemma 3.1 2B Instruct is fixed by correctly unwrapping unexpected single-item list responses from models. [Commit](https://github.com/open-webui/open-webui/pull/17070/commits/1bc9711afd2b72cd07c4e539a83783868733767c), [#17213](https://github.com/open-webui/open-webui/issues/17213) +- 🖼️ HEIC conversion failures are resolved, improving robustness of image handling. [#17225](https://github.com/open-webui/open-webui/pull/17225) +- 📦 The slim Docker image size regression has been fixed by refining the build process to correctly exclude components when USE_SLIM=true. [#16997](https://github.com/open-webui/open-webui/issues/16997), [Commit](https://github.com/open-webui/open-webui/commit/be373e9fd42ac73b0302bdb487e16dbeae178b4e), [Commit](https://github.com/open-webui/open-webui/commit/0ebe4f8f8490451ac8e85a4846f010854d9b54e5) +- 📁 Knowledge base update validation errors are resolved, ensuring seamless management via UI or API. [#17244](https://github.com/open-webui/open-webui/issues/17244), [Commit](https://github.com/open-webui/open-webui/commit/9aac1489080a5c9441e89b1a56de0d3a672bc5fb) +- 🔐 Resolved a security issue where a global web search setting overrode model-specific restrictions, ensuring model-level settings are now correctly prioritized. [#17151](https://github.com/open-webui/open-webui/issues/17151), [Commit](https://github.com/open-webui/open-webui/commit/9368d0ac751ec3072d5a96712b80a9b20a642ce6) +- 🔐 OAuth redirect reliability is improved by robustly preserving the intended redirect path using session storage. [#17235](https://github.com/open-webui/open-webui/issues/17235), [Commit](https://github.com/open-webui/open-webui/pull/17070/commits/4f2b821088367da18374027919594365c7a3f459), [#15575](https://github.com/open-webui/open-webui/pull/15575), [Commit](https://github.com/open-webui/open-webui/pull/17070/commits/d9f97c832c556fae4b116759da0177bf4fe619de) +- 🔐 Fixed a security vulnerability where knowledge base access within chat folders persisted after permissions were revoked. [#17182](https://github.com/open-webui/open-webui/issues/17182), [Commit](https://github.com/open-webui/open-webui/commit/40e40d1dddf9ca937e99af41c8ca038dbc93a7e6) +- 🔒 OIDC access denied errors are now displayed as user-friendly toast notifications instead of raw JSON. [#17208](https://github.com/open-webui/open-webui/issues/17208), [Commit](https://github.com/open-webui/open-webui/commit/3d6d050ad82d360adc42d6e9f42e8faf8d13c9f4) +- 💬 Chat exception handling is enhanced to prevent system instability during message generation and ensure graceful error recovery. [Commit](https://github.com/open-webui/open-webui/pull/17070/commits/f56889c5c7f0cf1a501c05d35dfa614e4f8b6958) + +### Changed + +- 🛠️ Renamed "Tools" to "External Tools" across the UI for clearer distinction between built-in and external functionalities. [Commit](https://github.com/open-webui/open-webui/pull/17070/commits/0bca4e230ef276bec468889e3be036242ad11086f) + ## [0.6.26] - 2025-08-28 ### Added From 218701e6174d6c04a4f6f58259e07312c983ec3b Mon Sep 17 00:00:00 2001 From: Classic298 <27028174+Classic298@users.noreply.github.com> Date: Mon, 8 Sep 2025 23:42:58 +0200 Subject: [PATCH 0138/1079] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ec9154c6d1..12fcc8fb21 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ 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/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [0.6.27] - 2025-09-08 +## [0.6.27] - 2025-09-09 ### Added From d783708745d028ab39d7ed15ff2ba94aa7963cbf Mon Sep 17 00:00:00 2001 From: Shirasawa <764798966@qq.com> Date: Tue, 9 Sep 2025 15:42:58 +0800 Subject: [PATCH 0139/1079] feat: change default permission check for regenerate and delete actions --- src/lib/components/chat/Messages/ResponseMessage.svelte | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/components/chat/Messages/ResponseMessage.svelte b/src/lib/components/chat/Messages/ResponseMessage.svelte index 9db76bfa85..fd4b2ebb45 100644 --- a/src/lib/components/chat/Messages/ResponseMessage.svelte +++ b/src/lib/components/chat/Messages/ResponseMessage.svelte @@ -1275,7 +1275,7 @@ {/if} - {#if $user?.role === 'admin' || ($user?.permissions?.chat?.regenerate_response ?? false)} + {#if $user?.role === 'admin' || ($user?.permissions?.chat?.regenerate_response ?? true)} {#if $settings?.regenerateMenu ?? true} - {/each} + {@const urlCitations = citations.filter((c) => c?.source?.name?.startsWith('http'))} +
      + - {/each} -
      -
      -
      - - {citations.length - ($mobile ? 1 : 2)} - {$i18n.t('more')} -
      -
      -
      - {#if isCollapsibleOpen} - - {:else} - - {/if} -
      -
      -
      -
      - {#each citations.slice($mobile ? 1 : 2) as citation, idx} - - {/each} -
      -
      - - {/if} +
      {/if} diff --git a/src/lib/components/chat/Messages/CitationsModal.svelte b/src/lib/components/chat/Messages/Citations/CitationModal.svelte similarity index 100% rename from src/lib/components/chat/Messages/CitationsModal.svelte rename to src/lib/components/chat/Messages/Citations/CitationModal.svelte diff --git a/src/lib/components/chat/Messages/Citations/CitationsModal.svelte b/src/lib/components/chat/Messages/Citations/CitationsModal.svelte new file mode 100644 index 0000000000..1f53c8e186 --- /dev/null +++ b/src/lib/components/chat/Messages/Citations/CitationsModal.svelte @@ -0,0 +1,68 @@ + + + +
      +
      +
      + {$i18n.t('Citations')} +
      + +
      + +
      +
      + {#each citations as citation, idx} + + {/each} +
      +
      +
      +
      diff --git a/src/lib/components/chat/Messages/Markdown/Source.svelte b/src/lib/components/chat/Messages/Markdown/Source.svelte index f87de43461..b298337320 100644 --- a/src/lib/components/chat/Messages/Markdown/Source.svelte +++ b/src/lib/components/chat/Messages/Markdown/Source.svelte @@ -37,6 +37,14 @@ return title; } + const getDisplayTitle = (title: string) => { + if (!title) return 'N/A'; + if (title.length > 30) { + return title.slice(0, 15) + '...' + title.slice(-10); + } + return title; + }; + $: attributes = extractAttributes(token.text); @@ -48,9 +56,11 @@ }} > - {decodeURIComponent(attributes.title) - ? formattedTitle(decodeURIComponent(attributes.title)) - : ''} + {getDisplayTitle( + decodeURIComponent(attributes.title) + ? formattedTitle(decodeURIComponent(attributes.title)) + : '' + )} {/if} From 63ca0b8cba292f943a1578ff4d98cf0e7bdc7438 Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Tue, 9 Sep 2025 17:36:18 +0400 Subject: [PATCH 0150/1079] refac --- .../components/chat/Messages/Citations.svelte | 15 ++++++++++++--- .../Messages/Citations/CitationsModal.svelte | 16 ++++++++++++++++ 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/src/lib/components/chat/Messages/Citations.svelte b/src/lib/components/chat/Messages/Citations.svelte index a76b5def80..f234b52d4c 100644 --- a/src/lib/components/chat/Messages/Citations.svelte +++ b/src/lib/components/chat/Messages/Citations.svelte @@ -11,14 +11,16 @@ let showPercentage = false; let showRelevance = true; + let citationModal = null; let showCitationModal = false; let selectedCitation: any = null; let isCollapsibleOpen = false; export const showSourceModal = (sourceIdx) => { if (citations[sourceIdx]) { - selectedCitation = citations[sourceIdx]; - showCitationModal = true; + console.log('Showing citation modal for:', citations[sourceIdx]); + citationModal?.showCitation(citations[sourceIdx]); + // showCitationModal = true; } }; @@ -94,7 +96,14 @@ } - + {#if citations.length > 0} {@const urlCitations = citations.filter((c) => c?.source?.name?.startsWith('http'))} diff --git a/src/lib/components/chat/Messages/Citations/CitationsModal.svelte b/src/lib/components/chat/Messages/Citations/CitationsModal.svelte index 1f53c8e186..0e40827ac6 100644 --- a/src/lib/components/chat/Messages/Citations/CitationsModal.svelte +++ b/src/lib/components/chat/Messages/Citations/CitationsModal.svelte @@ -5,6 +5,7 @@ import Modal from '$lib/components/common/Modal.svelte'; import XMark from '$lib/components/icons/XMark.svelte'; + import CitationModal from './CitationModal.svelte'; export let id = ''; export let show = false; @@ -12,6 +13,14 @@ export let showPercentage = false; export let showRelevance = true; + let showCitationModal = false; + let selectedCitation: any = null; + + export const showCitation = (citation) => { + selectedCitation = citation; + showCitationModal = true; + }; + const decodeString = (str: string) => { try { return decodeURIComponent(str); @@ -21,6 +30,13 @@ }; + +
      From d0f338bb9954109eb19c7f211fb1d7a84aa142a2 Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Tue, 9 Sep 2025 17:48:41 +0400 Subject: [PATCH 0151/1079] refac/enh: ability to export/sync function valves --- backend/open_webui/models/functions.py | 37 ++++++++++++++++++++----- backend/open_webui/routers/functions.py | 11 ++++---- 2 files changed, 36 insertions(+), 12 deletions(-) diff --git a/backend/open_webui/models/functions.py b/backend/open_webui/models/functions.py index 7530573e79..2bb6d60889 100644 --- a/backend/open_webui/models/functions.py +++ b/backend/open_webui/models/functions.py @@ -54,6 +54,22 @@ class FunctionModel(BaseModel): model_config = ConfigDict(from_attributes=True) +class FunctionWithValvesModel(BaseModel): + id: str + user_id: str + name: str + type: str + content: str + meta: FunctionMeta + valves: Optional[dict] = None + is_active: bool = False + is_global: bool = False + updated_at: int # timestamp in epoch + created_at: int # timestamp in epoch + + model_config = ConfigDict(from_attributes=True) + + #################### # Forms #################### @@ -111,8 +127,8 @@ class FunctionsTable: return None def sync_functions( - self, user_id: str, functions: list[FunctionModel] - ) -> list[FunctionModel]: + self, user_id: str, functions: list[FunctionWithValvesModel] + ) -> list[FunctionWithValvesModel]: # Synchronize functions for a user by updating existing ones, inserting new ones, and removing those that are no longer present. try: with get_db() as db: @@ -166,17 +182,24 @@ class FunctionsTable: except Exception: return None - def get_functions(self, active_only=False) -> list[FunctionModel]: + def get_functions( + self, active_only=False, include_valves=False + ) -> list[FunctionModel | FunctionWithValvesModel]: with get_db() as db: if active_only: + functions = db.query(Function).filter_by(is_active=True).all() + + else: + functions = db.query(Function).all() + + if include_valves: return [ - FunctionModel.model_validate(function) - for function in db.query(Function).filter_by(is_active=True).all() + FunctionWithValvesModel.model_validate(function) + for function in functions ] else: return [ - FunctionModel.model_validate(function) - for function in db.query(Function).all() + FunctionModel.model_validate(function) for function in functions ] def get_functions_by_type( diff --git a/backend/open_webui/routers/functions.py b/backend/open_webui/routers/functions.py index b5beb96cf0..9ef6915709 100644 --- a/backend/open_webui/routers/functions.py +++ b/backend/open_webui/routers/functions.py @@ -10,6 +10,7 @@ from open_webui.models.functions import ( FunctionForm, FunctionModel, FunctionResponse, + FunctionWithValvesModel, Functions, ) from open_webui.utils.plugin import ( @@ -46,9 +47,9 @@ async def get_functions(user=Depends(get_verified_user)): ############################ -@router.get("/export", response_model=list[FunctionModel]) -async def get_functions(user=Depends(get_admin_user)): - return Functions.get_functions() +@router.get("/export", response_model=list[FunctionModel | FunctionWithValvesModel]) +async def get_functions(include_valves: bool = False, user=Depends(get_admin_user)): + return Functions.get_functions(include_valves=include_valves) ############################ @@ -132,10 +133,10 @@ async def load_function_from_url( class SyncFunctionsForm(BaseModel): - functions: list[FunctionModel] = [] + functions: list[FunctionWithValvesModel] = [] -@router.post("/sync", response_model=list[FunctionModel]) +@router.post("/sync", response_model=list[FunctionWithValvesModel]) async def sync_functions( request: Request, form_data: SyncFunctionsForm, user=Depends(get_admin_user) ): From 77779b30d46340b39e4be65e4c124746f917ec1a Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Tue, 9 Sep 2025 18:01:59 +0400 Subject: [PATCH 0152/1079] refac --- .../Messages/Citations/CitationModal.svelte | 141 ++++++++---------- 1 file changed, 63 insertions(+), 78 deletions(-) diff --git a/src/lib/components/chat/Messages/Citations/CitationModal.svelte b/src/lib/components/chat/Messages/Citations/CitationModal.svelte index 566f0c6e06..c6e460d964 100644 --- a/src/lib/components/chat/Messages/Citations/CitationModal.svelte +++ b/src/lib/components/chat/Messages/Citations/CitationModal.svelte @@ -61,8 +61,34 @@
      -
      - {$i18n.t('Citation')} +
      + {#if citation?.source?.name} + {@const document = mergedDocuments?.[0]} + {#if document?.metadata?.file_id || document.source?.url?.includes('http')} + + + {decodeString(citation?.source?.name)} + + + {:else} + {decodeString(citation?.source?.name)} + {/if} + {:else} + {$i18n.t('Citation')} + {/if}
      {/if} + {#if $config?.oauth?.providers?.feishu} + + {/if}
      {/if} From c9e69fb2f4d19bc39b9060f0a3b9afdf10eeff36 Mon Sep 17 00:00:00 2001 From: Jan Kessler Date: Fri, 12 Sep 2025 10:36:37 +0200 Subject: [PATCH 0198/1079] 100% german translation (~ 250 strings added/changed) --- src/lib/i18n/locales/de-DE/translation.json | 498 ++++++++++---------- 1 file changed, 249 insertions(+), 249 deletions(-) diff --git a/src/lib/i18n/locales/de-DE/translation.json b/src/lib/i18n/locales/de-DE/translation.json index 0825e2a8a0..8f7edf901d 100644 --- a/src/lib/i18n/locales/de-DE/translation.json +++ b/src/lib/i18n/locales/de-DE/translation.json @@ -5,22 +5,22 @@ "(e.g. `sh webui.sh --api`)": "(z. B. `sh webui.sh --api`)", "(latest)": "(neueste)", "(leave blank for to use commercial endpoint)": "(leer lassen, um kommerziellen Endpunkt zu verwenden)", - "[Last] dddd [at] h:mm A": "", - "[Today at] h:mm A": "", - "[Yesterday at] h:mm A": "", + "[Last] dddd [at] h:mm A": "[Zuletzt] dddd [um] h:mm A", + "[Today at] h:mm A": "[Heute um] h:mm A", + "[Yesterday at] h:mm A": "[Gestern um] h:mm A", "{{ models }}": "{{ Modelle }}", "{{COUNT}} Available Tools": "{{COUNT}} verfügbare Werkzeuge", "{{COUNT}} characters": "{{COUNT}} Zeichen", - "{{COUNT}} extracted lines": "", + "{{COUNT}} extracted lines": "{{COUNT}} extrahierte Zeilen", "{{COUNT}} hidden lines": "{{COUNT}} versteckte Zeilen", "{{COUNT}} Replies": "{{COUNT}} Antworten", - "{{COUNT}} Sources": "", + "{{COUNT}} Sources": "{{COUNT}} Quellen", "{{COUNT}} words": "{{COUNT}} Wörter", - "{{model}} download has been canceled": "", + "{{model}} download has been canceled": "Der Download von {{model}} wurde abgebrochen", "{{user}}'s Chats": "{{user}}s Chats", "{{webUIName}} Backend Required": "{{webUIName}}-Backend erforderlich", "*Prompt node ID(s) are required for image generation": "*Prompt-Node-ID(s) sind für die Bildgenerierung erforderlich", - "1 Source": "", + "1 Source": "1 Quelle", "A new version (v{{LATEST_VERSION}}) is now available.": "Eine neue Version (v{{LATEST_VERSION}}) ist jetzt verfügbar.", "A task model is used when performing tasks such as generating titles for chats and web search queries": "Aufgabenmodelle werden beispielsweise zur Generierung von Chat-Titeln oder Websuchanfragen verwendet", "a user": "ein Benutzer", @@ -31,10 +31,10 @@ "Accessible to all users": "Für alle Benutzer zugänglich", "Account": "Konto", "Account Activation Pending": "Kontoaktivierung ausstehend", - "accurate": "", + "accurate": "präzise", "Accurate information": "Präzise Information(en)", "Action": "Aktion", - "Action not found": "", + "Action not found": "Aktion nicht gefunden", "Action Required for Chat Log Storage": "Aktion erforderlich für die Speicherung des Chat-Protokolls", "Actions": "Aktionen", "Activate": "Aktivieren", @@ -62,8 +62,8 @@ "Add text content": "Textinhalt hinzufügen", "Add User": "Benutzer hinzufügen", "Add User Group": "Benutzergruppe hinzufügen", - "Additional Config": "", - "Additional configuration options for marker. This should be a JSON string with key-value pairs. For example, '{\"key\": \"value\"}'. Supported keys include: disable_links, keep_pageheader_in_output, keep_pagefooter_in_output, filter_blank_pages, drop_repeated_text, layout_coverage_threshold, merge_threshold, height_tolerance, gap_threshold, image_threshold, min_line_length, level_count, default_level": "", + "Additional Config": "Zusätzliche Konfiguration", + "Additional configuration options for marker. This should be a JSON string with key-value pairs. For example, '{\"key\": \"value\"}'. Supported keys include: disable_links, keep_pageheader_in_output, keep_pagefooter_in_output, filter_blank_pages, drop_repeated_text, layout_coverage_threshold, merge_threshold, height_tolerance, gap_threshold, image_threshold, min_line_length, level_count, default_level": "Zusätzliche Konfigurationsoptionen für den Marker. Dies sollte eine JSON-Zeichenfolge mit Schlüssel-Wert-Paaren sein. Zum Beispiel '{\"key\": \"value\"}'. Unterstützte Schlüssel sind: disable_links, keep_pageheader_in_output, keep_pagefooter_in_output, filter_blank_pages, drop_repeated_text, layout_coverage_threshold, merge_threshold, height_tolerance, gap_threshold, image_threshold, min_line_length, level_count, default_level", "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", @@ -82,17 +82,17 @@ "Allow Chat Deletion": "Löschen von Chats erlauben", "Allow Chat Edit": "Bearbeiten von Chats erlauben", "Allow Chat Export": "Erlaube Chat Export", - "Allow Chat Params": "", + "Allow Chat Params": "Parameter für Chats erlauben", "Allow Chat Share": "Erlaube Chat teilen", "Allow Chat System Prompt": "Erlaube Chat System Prompt", "Allow Chat Valves": "Erlaube Chat Valves", - "Allow Continue Response": "", - "Allow Delete Messages": "", + "Allow Continue Response": "Erlaube fortgesetzte Antwort", + "Allow Delete Messages": "Löschen von Nachrichten erlauben", "Allow File Upload": "Hochladen von Dateien erlauben", "Allow Multiple Models in Chat": "Multiple Modelle in Chat erlauben", "Allow non-local voices": "Nicht-lokale Stimmen erlauben", - "Allow Rate Response": "", - "Allow Regenerate Response": "", + "Allow Rate Response": "Erlaube Bewertung der Antwort", + "Allow Regenerate Response": "Antwort neu generieren erlauben", "Allow Speech to Text": "Sprache zu Text erlauben", "Allow Temporary Chat": "Temporäre Chats erlauben", "Allow Text to Speech": "Text zu Sprache erlauben", @@ -102,15 +102,15 @@ "Allowed File Extensions": "Erlaubte Dateiendungen", "Allowed file extensions for upload. Separate multiple extensions with commas. Leave empty for all file types.": "Erlaubte Dateiendungen für den Upload. Trennen Sie mehrere Erweiterungen mit Kommas. Leer lassen, um alle Dateiendungen zu erlauben.", "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.": "Eine Alternative zu top_p, die darauf abzielt, ein ausgewogenes Verhältnis von Qualität und Vielfalt zu gewährleisten. Der Parameter p stellt die Mindestwahrscheinlichkeit dar, mit der ein Token berücksichtigt wird, relativ zur Wahrscheinlichkeit des wahrscheinlichsten Token. Wenn beispielsweise p = 0,05 ist und das wahrscheinlichste Token eine Wahrscheinlichkeit von 0,9 hat, werden Logits mit einem Wert kleiner als 0,045 herausgefiltert.", + "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.": "Eine Alternative zu top_p, die darauf abzielt, ein ausgewogenes Verhältnis von Qualität und Vielfalt zu gewährleisten. Der Parameter p stellt die Mindestwahrscheinlichkeit dar, mit der ein Token berücksichtigt wird, relativ zur Wahrscheinlichkeit des wahrscheinlichsten Token. Wenn beispielsweise p = 0.05 ist und das wahrscheinlichste Token eine Wahrscheinlichkeit von 0.9 hat, werden Logits mit einem Wert kleiner als 0.045 herausgefiltert.", "Always": "Immer", "Always Collapse Code Blocks": "Code-Blöcke immer zuklappen", "Always Expand Details": "Details immer aufklappen", "Always Play Notification Sound": "Benachrichtungston immer abspielen", "Amazing": "Fantastisch", "an assistant": "ein Assistent", - "An error occurred while fetching the explanation": "", - "Analytics": "", + "An error occurred while fetching the explanation": "Ein Fehler ist beim Abrufen der Erklärung aufgetreten", + "Analytics": "Analyse", "Analyzed": "Analysiert", "Analyzing...": "Analysiere...", "and": "und", @@ -119,14 +119,14 @@ "Android": "Android", "API": "API", "API Base URL": "API-Basis-URL", - "API Base URL for Datalab Marker service. Defaults to: https://www.datalab.to/api/v1/marker": "", - "API details for using a vision-language model in the picture description. This parameter is mutually exclusive with picture_description_local.": "", + "API Base URL for Datalab Marker service. Defaults to: https://www.datalab.to/api/v1/marker": "API-Basis-URL für den Datalab-Marker-Dienst. Standardwert: https://www.datalab.to/api/v1/marker", + "API details for using a vision-language model in the picture description. This parameter is mutually exclusive with picture_description_local.": "API-Details zur Verwendung eines Vision-Language-Modells in der Bildbeschreibung. Dieser Parameter schließt sich mit picture_description_local gegenseitig aus.", "API Key": "API-Schlüssel", "API Key created.": "API-Schlüssel erstellt.", "API Key Endpoint Restrictions": "API-Schlüssel Endpunkteinschränkungen", "API keys": "API-Schlüssel", "API Version": "API Version", - "API Version is required": "", + "API Version is required": "API-Version ist erforderlich", "Application DN": "Anwendungs-DN", "Application DN Password": "Anwendungs-DN-Passwort", "applies to all users with the \"user\" role": "gilt für alle Benutzer mit der Rolle \"Benutzer\"", @@ -176,28 +176,28 @@ "Bad Response": "Schlechte Antwort", "Banners": "Banner", "Base Model (From)": "Basismodell (From)", - "Base Model List Cache speeds up access by fetching base models only at startup or on settings save—faster, but may not show recent base model changes.": "", - "Bearer": "", + "Base Model List Cache speeds up access by fetching base models only at startup or on settings save—faster, but may not show recent base model changes.": "Die Cache-Liste der Basismodelle beschleunigt den Zugriff, indem Basismodelle nur beim Start oder beim Speichern der Einstellungen abgerufen werden – schneller, zeigt aber möglicherweise keine aktuellen Änderungen an Basismodellen an.", + "Bearer": "Bearer", "before": "bereits geteilt", "Being lazy": "Faulheit", "Beta": "Beta", "Bing Search V7 Endpoint": "Bing Search V7-Endpunkt", "Bing Search V7 Subscription Key": "Bing Search V7-Abonnement-Schlüssel", - "Bio": "", - "Birth Date": "", - "BM25 Weight": "", + "Bio": "Biografie", + "Birth Date": "Geburtsdatum", + "BM25 Weight": "BM25-Gewichtung", "Bocha Search API Key": "Bocha Search API-Schlüssel", "Bold": "Fett", "Boosting or penalizing specific tokens for constrained responses. Bias values will be clamped between -100 and 100 (inclusive). (Default: none)": "Verstärkung oder Bestrafung spezifischer Token für eingeschränkte Antworten. Bias-Werte werden zwischen -100 und 100 (einschließlich) begrenzt. (Standard: keine)", "Both Docling OCR Engine and Language(s) must be provided or both left empty.": "Entweder müssen Docling OCR-Engine und Sprache(n) angegeben oder beide leer gelassen werden.", "Brave Search API Key": "Brave Search API-Schlüssel", "Bullet List": "Aufzählungsliste", - "Button ID": "", - "Button Label": "", - "Button Prompt": "", + "Button ID": "Schaltflächen-ID", + "Button Label": "Schaltflächenbeschriftung", + "Button Prompt": "Schaltflächen-Prompt", "By {{name}}": "Von {{name}}", "Bypass Embedding and Retrieval": "Embedding und Retrieval umgehen", - "Bypass Web Loader": "", + "Bypass Web Loader": "Web-Loader umgehen", "Cache Base Model List": "Basis Modell-Liste cachen", "Calendar": "Kalender", "Call": "Anrufen", @@ -209,9 +209,9 @@ "Capture Audio": "Audio aufzeichnen", "Certificate Path": "Zertifikatpfad", "Change Password": "Passwort ändern", - "Channel deleted successfully": "", + "Channel deleted successfully": "Kanal erfolgreich gelöscht", "Channel Name": "Kanalname", - "Channel updated successfully": "", + "Channel updated successfully": "Kanal erfolgreich aktualisiert", "Channels": "Kanäle", "Character": "Zeichen", "Character limit for autocomplete generation input": "Zeichenlimit für die Eingabe der automatischen Vervollständigung", @@ -220,10 +220,10 @@ "Chat Background Image": "Hintergrundbild des Chat-Fensters", "Chat Bubble UI": "Sprechblasen-Layout", "Chat Controls": "Chat-Steuerung", - "Chat Conversation": "", + "Chat Conversation": "Chat-Unterhaltung", "Chat direction": "Textrichtung", - "Chat ID": "", - "Chat moved successfully": "", + "Chat ID": "Chat-ID", + "Chat moved successfully": "Chat erfolgreich verschoben", "Chat Overview": "Chat-Übersicht", "Chat Permissions": "Chat-Berechtigungen", "Chat Tags Auto-Generation": "Automatische Generierung von Chat-Tags", @@ -258,11 +258,11 @@ "Clone of {{TITLE}}": "Klon von {{TITLE}}", "Close": "Schließen", "Close Banner": "Banner schließen", - "Close Configure Connection Modal": "", - "Close modal": "", - "Close settings modal": "", + "Close Configure Connection Modal": "Konfigurationsfenster für Verbindung schließen", + "Close modal": "Modal schließen", + "Close settings modal": "Einstellungsfenster schließen", "Close Sidebar": "Seitenleiste schließen", - "CMU ARCTIC speaker embedding name": "", + "CMU ARCTIC speaker embedding name": "Name des CMU ARCTIC Sprecher-Embeddings", "Code Block": "Codeblock", "Code execution": "Codeausführung", "Code Execution": "Codeausführung", @@ -281,13 +281,13 @@ "ComfyUI Base URL is required.": "ComfyUI-Basis-URL wird benötigt.", "ComfyUI Workflow": "ComfyUI-Workflow", "ComfyUI Workflow Nodes": "ComfyUI-Workflow-Knoten", - "Comma separated Node Ids (e.g. 1 or 1,2)": "", + "Comma separated Node Ids (e.g. 1 or 1,2)": "Durch Komma getrennte Knoten-IDs (z. B. 1 oder 1,2)", "Command": "Befehl", "Comment": "Kommentar", "Completions": "Vervollständigungen", - "Compress Images in Channels": "", + "Compress Images in Channels": "Bilder in Kanälen komprimieren", "Concurrent Requests": "Anzahl gleichzeitiger Anfragen", - "Config imported successfully": "", + "Config imported successfully": "Konfiguration erfolgreich importiert", "Configure": "Konfigurieren", "Confirm": "Bestätigen", "Confirm Password": "Passwort bestätigen", @@ -304,17 +304,17 @@ "Connections settings updated": "Verbindungseinstellungen aktualisiert", "Constrains effort on reasoning for reasoning models. Only applicable to reasoning models from specific providers that support reasoning effort.": "Beschränkt den reasoning effort für Reasoning-Modelle. Nur anwendbar auf Reasoning-Modelle von spezifischen Anbietern, die den reasoning effort Parameter unterstützen.", "Contact Admin for WebUI Access": "Kontaktieren Sie den Administrator für den Zugriff auf die Weboberfläche", - "Content": "", - "Content Extraction Engine": "", + "Content": "Inhalt", + "Content Extraction Engine": "Inhalts-Extraktions-Engine", "Continue Response": "Antwort fortsetzen", "Continue with {{provider}}": "Mit {{provider}} fortfahren", "Continue with Email": "Mit Email fortfahren", "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 the repetition of token sequences in the generated text. A higher value (e.g., 1.5) will penalize repetitions more strongly, while a lower value (e.g., 1.1) will be more lenient. At 1, it is disabled.": "Steuert die Wiederholung von Token-Sequenzen im generierten Text. Ein höherer Wert (z. B. 1,5) bestraft Wiederholungen stärker, während ein niedrigerer Wert (z. B. 1,1) toleranter ist. Bei 1 ist die Funktion deaktiviert.", + "Control the repetition of token sequences in the generated text. A higher value (e.g., 1.5) will penalize repetitions more strongly, while a lower value (e.g., 1.1) will be more lenient. At 1, it is disabled.": "Steuert die Wiederholung von Token-Sequenzen im generierten Text. Ein höherer Wert (z. B. 1.5) bestraft Wiederholungen stärker, während ein niedrigerer Wert (z. B. 1.1) toleranter ist. Bei 1 ist die Funktion deaktiviert.", "Controls": "Steuerung", - "Controls the balance between coherence and diversity of the output. A lower value will result in more focused and coherent text.": "", - "Conversation saved successfully": "", + "Controls the balance between coherence and diversity of the output. A lower value will result in more focused and coherent text.": "Steuert das Gleichgewicht zwischen Kohärenz und Vielfalt der Ausgabe. Ein niedrigerer Wert führt zu fokussierterem und kohärenterem Text.", + "Conversation saved successfully": "Unterhaltung erfolgreich gespeichert", "Copied": "Kopiert", "Copied link to clipboard": "Link in Zwischenablage kopiert", "Copied shared chat URL to clipboard!": "Freigabelink in die Zwischenablage kopiert!", @@ -334,7 +334,7 @@ "Create Account": "Konto erstellen", "Create Admin Account": "Administrator-Account erstellen", "Create Channel": "Kanal erstellen", - "Create Folder": "", + "Create Folder": "Ordner erstellen", "Create Group": "Gruppe erstellen", "Create Knowledge": "Wissen erstellen", "Create new key": "Neuen Schlüssel erstellen", @@ -355,15 +355,15 @@ "Danger Zone": "Gefahrenzone", "Dark": "Dunkel", "Database": "Datenbank", - "Datalab Marker API": "", - "Datalab Marker API Key required.": "", + "Datalab Marker API": "Datalab Marker API", + "Datalab Marker API Key required.": "Datalab Marker API-Schlüssel erforderlich.", "DD/MM/YYYY": "TT/MM/JJJJ", "December": "Dezember", - "Deepgram": "", + "Deepgram": "Deepgram", "Default": "Standard", "Default (Open AI)": "Standard (Open AI)", "Default (SentenceTransformers)": "Standard (SentenceTransformers)", - "Default action buttons will be used.": "", + "Default action buttons will be used.": "Es werden Standard-Aktionsschaltflächen verwendet.", "Default description enabled": "Standard Beschreibung aktiviert", "Default mode works with a wider range of models by calling tools once before execution. Native mode leverages the model's built-in tool-calling capabilities, but requires the model to inherently support this feature.": "Der Standardmodus funktioniert mit einer breiteren Auswahl von Modellen, indem er Werkzeuge einmal vor der Ausführung aufruft. Der native Modus nutzt die integrierten Tool-Aufrufmöglichkeiten des Modells, erfordert jedoch, dass das Modell diese Funktion von Natur aus unterstützt.", "Default Model": "Standardmodell", @@ -406,10 +406,10 @@ "Direct Connections": "Direktverbindungen", "Direct Connections allow users to connect to their own OpenAI compatible API endpoints.": "Direktverbindungen ermöglichen es Benutzern, sich mit ihren eigenen OpenAI-kompatiblen API-Endpunkten zu verbinden.", "Direct Tool Servers": "Direkt verbundene Werkzeug-Server", - "Directory selection was cancelled": "", + "Directory selection was cancelled": "Die Verzeichnisauswahl wurde abgebrochen", "Disable Code Interpreter": "Deaktivierte Code Interpreter", "Disable Image Extraction": "Deaktivierte Bildextraktion", - "Disable image extraction from the PDF. If Use LLM is enabled, images will be automatically captioned. Defaults to False.": "", + "Disable image extraction from the PDF. If Use LLM is enabled, images will be automatically captioned. Defaults to False.": "Bildextraktion aus dem PDF deaktivieren. Wenn \"LLM verwenden\" aktiviert ist, werden Bilder automatisch beschriftet. Standardmäßig auf False gesetzt.", "Disabled": "Deaktiviert", "Discover a function": "Entdecken Sie weitere Funktionen", "Discover a model": "Entdecken Sie weitere Modelle", @@ -423,21 +423,21 @@ "Discover, download, and explore model presets": "Entdecken und beziehen Sie benutzerdefinierte Modellvorlagen", "Display": "Anzeigen", "Display Emoji in Call": "Emojis im Anruf anzeigen", - "Display Multi-model Responses in Tabs": "", + "Display Multi-model Responses in Tabs": "Multi-Modell-Antworten in Tabs anzeigen", "Display the username instead of You in the Chat": "Soll \"Sie\" durch Ihren Benutzernamen ersetzt werden?", "Displays citations in the response": "Zeigt Zitate in der Antwort an", - "Displays status updates (e.g., web search progress) in the response": "", + "Displays status updates (e.g., web search progress) in the response": "Zeigt Statusaktualisierungen (z. B. Fortschritt der Websuche) in der Antwort an", "Dive into knowledge": "Tauchen Sie in das Wissen ein", - "dlparse_v1": "", - "dlparse_v2": "", - "dlparse_v4": "", + "dlparse_v1": "dlparse_v1", + "dlparse_v2": "dlparse_v2", + "dlparse_v4": "dlparse_v4", "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.", "Docling": "Docling", "Docling Server URL required.": "Docling Server URL erforderlich", "Document": "Dokument", - "Document Intelligence": "", - "Document Intelligence endpoint required.": "", + "Document Intelligence": "Dokumentenintelligenz", + "Document Intelligence endpoint required.": "Dokumentenintelligenz-Endpunkt erforderlich.", "Documentation": "Dokumentation", "Documents": "Dokumente", "does not make any external connections, and your data stays securely on your locally hosted server.": "stellt keine externen Verbindungen her, und Ihre Daten bleiben sicher auf Ihrem lokal gehosteten Server.", @@ -468,7 +468,7 @@ "e.g. pdf, docx, txt": "z. B. pdf, docx, txt", "e.g. Tools for performing various operations": "z. B. Werkzeuge für verschiedene Operationen", "e.g., 3, 4, 5 (leave blank for default)": "z. B. 3, 4, 5 (leer lassen für Standard)", - "e.g., audio/wav,audio/mpeg,video/* (leave blank for defaults)": "", + "e.g., audio/wav,audio/mpeg,video/* (leave blank for defaults)": "z. B. audio/wav,audio/mpeg,video/* (leer lassen für Standardwerte)", "e.g., en-US,ja-JP (leave blank for auto-detect)": "z. B. en-US,de-DE (freilassen für automatische Erkennung)", "e.g., westus (leave blank for eastus)": "z. B. westus (leer lassen für eastus)", "Edit": "Bearbeiten", @@ -499,11 +499,11 @@ "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.": "Aktivieren Sie 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.": "Aktivieren Sie 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 Mirostat sampling for controlling perplexity.": "", + "Enable Mirostat sampling for controlling perplexity.": "Mirostat-Sampling zur Kontrolle der Perplexity aktivieren.", "Enable New Sign Ups": "Registrierung erlauben", - "Enable, disable, or customize the reasoning tags used by the model. \"Enabled\" uses default tags, \"Disabled\" turns off reasoning tags, and \"Custom\" lets you specify your own start and end tags.": "", + "Enable, disable, or customize the reasoning tags used by the model. \"Enabled\" uses default tags, \"Disabled\" turns off reasoning tags, and \"Custom\" lets you specify your own start and end tags.": "Aktivieren, deaktivieren oder anpassen der vom Modell verwendeten Reasoning-Tags. \"Aktiviert\" verwendet Standard-Tags, \"Deaktiviert\" schaltet die Reasoning-Tags aus, und \"Benutzerdefiniert\" ermöglicht es Ihnen, eigene Start- und End-Tags festzulegen.", "Enabled": "Aktiviert", - "End Tag": "", + "End Tag": "End-Tag", "Endpoint URL": "Endpunkt-URL", "Enforce Temporary Chat": "Temporären Chat erzwingen", "Enhance": "Verbessern", @@ -527,14 +527,14 @@ "Enter Config in JSON format": "Konfiguration in JSON Format eingeben", "Enter content for the pending user info overlay. Leave empty for default.": "Geben Sie Inhalt für das Overlay 'Ausstehende Kontoaktivierung' ein. Für Standard leer lassen.", "Enter coordinates (e.g. 51.505, -0.09)": "Geben Sie Koordinaten ein (z. B. 51.505, -0.09)", - "Enter Datalab Marker API Base URL": "", - "Enter Datalab Marker API Key": "", + "Enter Datalab Marker API Base URL": "Geben Sie die Basis-URL für die Datalab Marker API ein", + "Enter Datalab Marker API Key": "Geben Sie den Datalab Marker API-Schlüssel ein", "Enter description": "Geben Sie eine Beschreibung ein", "Enter Docling OCR Engine": "Docling OCR-Engine eingeben", "Enter Docling OCR Language(s)": "Docling OCR-Sprache(n) eingeben", "Enter Docling Server URL": "Docling Server-URL eingeben", - "Enter Document Intelligence Endpoint": "ndpunkt für Dokumentenintelligenz eingeben", - "Enter Document Intelligence Key": "Schlüssel für Dokumentenintelligenz eingeben", + "Enter Document Intelligence Endpoint": "Endpunkt für Document Intelligence eingeben", + "Enter Document Intelligence Key": "Schlüssel für Document Intelligence eingeben", "Enter domains separated by commas (e.g., example.com,site.org)": "Geben Sie die Domains durch Kommas separiert ein (z. B. example.com,site.org)", "Enter Exa API Key": "Geben Sie den Exa-API-Schlüssel ein", "Enter External Document Loader API Key": "API-Schlüssel für externen Dokumenten-Loader eingeben", @@ -550,10 +550,10 @@ "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 hex color (e.g. #FF0000)": "Geben Sie eine Hex-Farbe ein (z. B. #FF0000)", - "Enter ID": "", + "Enter ID": "ID eingeben", "Enter Image Size (e.g. 512x512)": "Geben Sie die Bildgröße ein (z. B. 512x512)", "Enter Jina API Key": "Geben Sie den Jina-API-Schlüssel ein", - "Enter JSON config (e.g., {\"disable_links\": true})": "", + "Enter JSON config (e.g., {\"disable_links\": true})": "Geben Sie die JSON-Konfiguration ein (z. B. {\"disable_links\": true})", "Enter Jupyter Password": "Geben Sie das Jupyter-Passwort ein", "Enter Jupyter Token": "Geben Sie den Jupyter-Token ein", "Enter Jupyter URL": "Geben Sie die Jupyter-URL ein", @@ -571,7 +571,7 @@ "Enter Playwright Timeout": "Playwright Timeout eingeben", "Enter Playwright WebSocket URL": "Geben Sie die Playwright WebSocket-URL ein", "Enter proxy URL (e.g. https://user:password@host:port)": "Geben sie die Proxy-URL ein (z. B. https://user:password@host:port)", - "Enter reasoning effort": "Geben Sie den Schlussfolgerungsaufwand ein", + "Enter reasoning effort": "Geben Sie den Reasoning Effort ein", "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 Score": "Punktzahl eingeben", @@ -613,7 +613,7 @@ "Enter your current password": "Geben Sie Ihr aktuelles Passwort ein", "Enter Your Email": "Geben Sie Ihre E-Mail-Adresse ein", "Enter Your Full Name": "Geben Sie Ihren vollständigen Namen ein", - "Enter your gender": "", + "Enter your gender": "Geben Sie Ihr Geschlecht ein", "Enter your message": "Geben Sie Ihre Nachricht ein", "Enter your name": "Geben Sie Ihren Namen ein", "Enter Your Name": "Geben Sie Ihren Namen ein", @@ -624,16 +624,16 @@ "Enter your webhook URL": "Geben Sie Ihre Webhook-URL ein", "Error": "Fehler", "ERROR": "FEHLER", - "Error accessing directory": "", + "Error accessing directory": "Fehler beim Zugriff auf das Verzeichnis", "Error accessing Google Drive: {{error}}": "Fehler beim Zugriff auf Google Drive: {{error}}", "Error accessing media devices.": "Fehler beim Zugreifen auf Mediengeräte", "Error starting recording.": "Fehler beim Starten der Aufnahme", - "Error unloading model: {{error}}": "", + "Error unloading model: {{error}}": "Fehler beim Entladen des Modells: {{error}}", "Error uploading file: {{error}}": "Fehler beim Hochladen der Datei: {{error}}", - "Error: A model with the ID '{{modelId}}' already exists. Please select a different ID to proceed.": "", - "Error: Model ID cannot be empty. Please enter a valid ID to proceed.": "", + "Error: A model with the ID '{{modelId}}' already exists. Please select a different ID to proceed.": "Fehler: Ein Modell mit der ID '{{modelId}}' existiert bereits. Bitte wählen Sie eine andere ID, um fortzufahren.", + "Error: Model ID cannot be empty. Please enter a valid ID to proceed.": "Fehler: Die Modell-ID darf nicht leer sein. Bitte geben Sie eine gültige ID ein, um fortzufahren.", "Evaluations": "Evaluationen", - "Everyone": "", + "Everyone": "Jeder", "Exa API Key": "Exa-API-Schlüssel", "Example: (&(objectClass=inetOrgPerson)(uid=%s))": "Beispiel: (&(objectClass=inetOrgPerson)(uid=%s))", "Example: ALL": "Beispiel: ALL", @@ -657,20 +657,20 @@ "Export Functions": "Funktionen exportieren", "Export Models": "Modelle exportieren", "Export Presets": "Voreinstellungen exportieren", - "Export Prompt Suggestions": "Prompt Vorschläge exportieren", + "Export Prompt Suggestions": "Prompt-Vorschläge exportieren", "Export Prompts": "Prompts exportieren", "Export to CSV": "Als CSV exportieren", "Export Tools": "Werkzeuge exportieren", - "Export Users": "", + "Export Users": "Benutzer exportieren", "External": "Extern", "External Document Loader URL required.": "URL für externen Dokumenten-Loader erforderlich.", "External Task Model": "Externes Aufgabenmodell", - "External Tools": "", + "External Tools": "Externe Werkzeuge", "External Web Loader API Key": "Externer Web-Loader API-Schlüssel", "External Web Loader URL": "Externer Web-Loader URL", "External Web Search API Key": "Externe Websuche API-Schlüssel", "External Web Search URL": "Externe Websuche URL", - "Fade Effect for Streaming Text": "", + "Fade Effect for Streaming Text": "Überblendeffekt für Text-Streaming", "Failed to add file.": "Fehler beim Hinzufügen der Datei.", "Failed to connect to {{URL}} OpenAPI tool server": "Verbindung zum OpenAPI-Toolserver {{URL}} fehlgeschlagen", "Failed to copy link": "Fehler beim kopieren des Links", @@ -680,24 +680,24 @@ "Failed to extract content from the file.": "Fehler beim extrahieren des Inhalts aus der Datei.", "Failed to fetch models": "Fehler beim Abrufen der Modelle", "Failed to generate title": "Fehler beim generieren des Titels", - "Failed to load chat preview": "", + "Failed to load chat preview": "Chat-Vorschau konnte nicht geladen werden", "Failed to load file content.": "Fehler beim Laden des Dateiinhalts.", - "Failed to move chat": "", + "Failed to move chat": "Chat konnte nicht verschoben werden", "Failed to read clipboard contents": "Fehler beim Lesen des Inhalts der Zwischenablage.", "Failed to save connections": "Verbindungen konnten nicht gespeichert werden", "Failed to save conversation": "Unterhaltung konnte nicht gespeichert werden", "Failed to save models configuration": "Fehler beim Speichern der Modellkonfiguration", "Failed to update settings": "Fehler beim Aktualisieren der Einstellungen", "Failed to upload file.": "Fehler beim Hochladen der Datei.", - "fast": "", + "fast": "schnell", "Features": "Funktionalitäten", "Features Permissions": "Funktionen-Berechtigungen", "February": "Februar", - "Feedback Details": "", + "Feedback Details": "Feedback-Details", "Feedback History": "Feedback-Verlauf", "Feedbacks": "Feedbacks", "Feel free to add specific details": "Fühlen Sie sich frei, spezifische Details hinzuzufügen", - "Female": "", + "Female": "Weiblich", "File": "Datei", "File added successfully.": "Datei erfolgreich hinzugefügt.", "File content updated successfully.": "Dateiinhalt erfolgreich aktualisiert.", @@ -715,7 +715,7 @@ "Fingerprint spoofing detected: Unable to use initials as avatar. Defaulting to default profile image.": "Fingerabdruck-Spoofing erkannt: Initialen können nicht als Avatar verwendet werden. Standard-Avatar wird verwendet.", "Firecrawl API Base URL": "Firecrawl API Basis-URL", "Firecrawl API Key": "Firecrawl API-Schlüssel", - "Floating Quick Actions": "", + "Floating Quick Actions": "Schnellaktionen", "Focus chat input": "Chat-Eingabe fokussieren", "Folder deleted successfully": "Ordner erfolgreich gelöscht", "Folder Name": "Ordner-Name", @@ -728,14 +728,14 @@ "Follow-Up Auto-Generation": "Auto-Generierung für Folgefragen", "Followed instructions perfectly": "Anweisungen perfekt befolgt", "Force OCR": "OCR erzwingen", - "Force OCR on all pages of the PDF. This can lead to worse results if you have good text in your PDFs. Defaults to False.": "", + "Force OCR on all pages of the PDF. This can lead to worse results if you have good text in your PDFs. Defaults to False.": "OCR für alle Seiten des PDF erzwingen. Dies kann zu schlechteren Ergebnissen führen, wenn bereits guter Text in Ihren PDFs vorhanden ist. Standardwert ist False.", "Forge new paths": "Neue Wege beschreiten", "Form": "Formular", - "Format Lines": "", - "Format the lines in the output. Defaults to False. If set to True, the lines will be formatted to detect inline math and styles.": "", + "Format Lines": "Zeilen formatieren", + "Format the lines in the output. Defaults to False. If set to True, the lines will be formatted to detect inline math and styles.": "Zeilen in der Ausgabe formatieren. Standardwert ist False. Wenn auf True gesetzt, werden die Zeilen formatiert, um mathematische Formeln und Stile zu erkennen.", "Format your variables using brackets like this:": "Formatieren Sie Ihre Variablen mit Klammern, wie hier:", - "Formatting may be inconsistent from source.": "", - "Forwards system user OAuth access token to authenticate": "", + "Formatting may be inconsistent from source.": "Die Formatierung kann inkonsistent mit der Quelle sein.", + "Forwards system user OAuth access token to authenticate": "Leitet OAuth-Zugriffstoken des Systembenutzers zur Authentifizierung weiter", "Forwards system user session credentials to authenticate": "Leitet Anmeldedaten der user session zur Authentifizierung weiter", "Full Context Mode": "Voll-Kontext Modus", "Function": "Funktion", @@ -755,7 +755,7 @@ "Gemini": "Gemini", "Gemini API Config": "Gemini API-Konfiguration", "Gemini API Key is required.": "Gemini API-Schlüssel ist erforderlich.", - "Gender": "", + "Gender": "Geschlecht", "General": "Allgemein", "Generate": "Generieren", "Generate an image": "Bild erzeugen", @@ -763,7 +763,7 @@ "Generate prompt pair": "Prompt-Paar generieren", "Generating search query": "Suchanfrage wird erstellt", "Generating...": "Generiere...", - "Get information on {{name}} in the UI": "", + "Get information on {{name}} in the UI": "Informationen zu {{name}} in der Benutzeroberfläche abrufen", "Get started": "Loslegen", "Get started with {{WEBUI_NAME}}": "Loslegen mit {{WEBUI_NAME}}", "Global": "Global", @@ -771,7 +771,7 @@ "Google Drive": "Google Drive", "Google PSE API Key": "Google PSE-API-Schlüssel", "Google PSE Engine Id": "Google PSE-Engine-ID", - "Gravatar": "", + "Gravatar": "Gravatar", "Group": "Gruppe", "Group created successfully": "Gruppe erfolgreich erstellt", "Group deleted successfully": "Gruppe erfolgreich gelöscht", @@ -779,9 +779,9 @@ "Group Name": "Gruppenname", "Group updated successfully": "Gruppe erfolgreich aktualisiert", "Groups": "Gruppen", - "H1": "", - "H2": "", - "H3": "", + "H1": "Überschrift 1", + "H2": "Überschrift 2", + "H3": "Überschrift 3", "Haptic Feedback": "Haptisches Feedback", "Height": "Höhe", "Hello, {{name}}": "Hallo, {{name}}", @@ -792,7 +792,7 @@ "Hide": "Verbergen", "Hide from Sidebar": "Von Seitenleiste entfernen", "Hide Model": "Modell verstecken", - "High": "", + "High": "Hoch", "High Contrast Mode": "Modus für hohen Kontrast", "Home": "Startseite", "Host": "Host", @@ -827,27 +827,27 @@ "Import Models": "Modelle importieren", "Import Notes": "Notizen importieren", "Import Presets": "Voreinstellungen importieren", - "Import Prompt Suggestions": "Prompt Vorschläge importieren", + "Import Prompt Suggestions": "Prompt-Vorschläge importieren", "Import Prompts": "Prompts importieren", "Import Tools": "Werkzeuge importieren", "Important Update": "Wichtiges Update", - "In order to force OCR, performing OCR must be enabled.": "", + "In order to force OCR, performing OCR must be enabled.": "Um die OCR erzwingen zu können, muss die Durchführung der OCR aktiviert sein.", "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` flag when running stable-diffusion-webui": "Fügen Sie beim Ausführen von stable-diffusion-webui die Option `--api` hinzu", "Includes SharePoint": "Enthält SharePoint", "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.": "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.", "Info": "Info", - "Initials": "", + "Initials": "Initialen", "Inject the entire content as context for comprehensive processing, this is recommended for complex queries.": "Gesamten Inhalt als Kontext für umfassende Verarbeitung einfügen, dies wird für komplexe Abfragen empfohlen.", "Input": "Eingabe", "Input commands": "Eingabebefehle", - "Input Key (e.g. text, unet_name, steps)": "", + "Input Key (e.g. text, unet_name, steps)": "Eingabe Schlüssel (z.B. text, unet_name, steps)", "Input Variables": "Eingabe Variablen", "Insert": "Einfügen", - "Insert Follow-Up Prompt to Input": "", + "Insert Follow-Up Prompt to Input": "Folgefrage in Eingabe einfügen", "Insert Prompt as Rich Text": "Prompt als Rich Text einfügen", - "Insert Suggestion Prompt to Input": "", + "Insert Suggestion Prompt to Input": "Prompt-Vorschlag in Eingabe einfügen", "Install from Github URL": "Von GitHub-URL installieren", "Instant Auto-Send After Voice Transcription": "Spracherkennung direkt absenden", "Integration": "Integration", @@ -855,8 +855,8 @@ "Invalid file content": "Ungültiger Dateiinhalt", "Invalid file format.": "Ungültiges Dateiformat.", "Invalid JSON file": "Ungültige JSON Datei", - "Invalid JSON format for ComfyUI Workflow.": "", - "Invalid JSON format in Additional Config": "", + "Invalid JSON format for ComfyUI Workflow.": "Ungültiges JSON-Format für ComfyUI-Workflow.", + "Invalid JSON format in Additional Config": "Ungültiges JSON-Format in der zusätzlichen Konfiguration", "Invalid Tag": "Ungültiger Tag", "is typing...": "schreibt ...", "Italic": "Kursiv", @@ -872,34 +872,34 @@ "JWT Expiration": "JWT-Ablauf", "JWT Token": "JWT-Token", "Kagi Search API Key": "Kagi Search API-Schlüssel", - "Keep Follow-Up Prompts in Chat": "", + "Keep Follow-Up Prompts in Chat": "Folgefragen im Chat behalten", "Keep in Sidebar": "In Seitenleiste anzeigen", "Key": "Schlüssel", - "Key is required": "", + "Key is required": "Schlüssel ist erforderlich", "Keyboard shortcuts": "Tastenkombinationen", "Knowledge": "Wissen", "Knowledge Access": "Wissenszugriff", "Knowledge Base": "Wissensdatenbank", "Knowledge created successfully.": "Wissen erfolgreich erstellt.", "Knowledge deleted successfully.": "Wissen erfolgreich gelöscht.", - "Knowledge Description": "", - "Knowledge Name": "", + "Knowledge Description": "Wissensbeschreibung", + "Knowledge Name": "Wissensname", "Knowledge Public Sharing": "Öffentliche Freigabe von Wissen", "Knowledge reset successfully.": "Wissen erfolgreich zurückgesetzt.", "Knowledge updated successfully": "Wissen erfolgreich aktualisiert", - "Kokoro.js (Browser)": "", - "Kokoro.js Dtype": "", + "Kokoro.js (Browser)": "Kokoro.js (Browser)", + "Kokoro.js Dtype": "Kokoro.js Dtype", "Label": "Label", "Landing Page Mode": "Startseitenmodus", "Language": "Sprache", - "Language Locales": "", + "Language Locales": "Sprach-Locales", "Last Active": "Zuletzt aktiv", "Last Modified": "Zuletzt bearbeitet", "Last reply": "Letzte Antwort", "LDAP": "LDAP", "LDAP server updated": "LDAP-Server aktualisiert", "Leaderboard": "Bestenliste", - "Learn More": "", + "Learn More": "Mehr erfahren", "Learn more about OpenAPI tool servers.": "Erfahren Sie mehr über OpenAPI-Toolserver.", "Leave empty for no compression": "Leer lassen für keine Kompression", "Leave empty for unlimited": "Leer lassen für unbegrenzt", @@ -909,27 +909,27 @@ "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 model field empty to use the default model.": "Leer lassen, um das Standardmodell zu verwenden.", - "lexical": "", + "lexical": "lexikalisch", "License": "Lizenz", - "Lift List": "", + "Lift List": "Liste anheben", "Light": "Hell", "Listening...": "Höre zu...", "Llama.cpp": "Llama.cpp", "LLMs can make mistakes. Verify important information.": "LLMs können Fehler machen. Überprüfen Sie wichtige Informationen.", - "Loader": "", + "Loader": "Loader", "Loading Kokoro.js...": "Lade Kokoro.js...", "Loading...": "Lade...", "Local": "Lokal", "Local Task Model": "Lokales Aufgabenmodell", "Location access not allowed": "Standortzugriff nicht erlaubt", "Lost": "Verloren", - "Low": "", + "Low": "Niedrig", "LTR": "LTR", "Made by Open WebUI Community": "Von der OpenWebUI-Community", "Make password visible in the user interface": "Passwort im Benutzerinterface sichtbar machen", "Make sure to enclose them with": "Umschließen Sie Variablen mit", "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.", - "Male": "", + "Male": "Männlich", "Manage": "Verwalten", "Manage Direct Connections": "Direkte Verbindungen verwalten", "Manage Models": "Modelle verwalten", @@ -938,16 +938,16 @@ "Manage OpenAI API Connections": "OpenAI-API-Verbindungen verwalten", "Manage Pipelines": "Pipelines verwalten", "Manage Tool Servers": "Tool Server verwalten", - "Manage your account information.": "", + "Manage your account information.": "Verwalten Sie Ihre Kontoinformationen.", "March": "März", "Markdown": "Markdown", - "Markdown (Header)": "", - "Max Speakers": "", + "Markdown (Header)": "Markdown (Überschrift)", + "Max Speakers": "Maximale Anzahl der Sprecher", "Max Upload Count": "Maximale Anzahl der Uploads", "Max Upload Size": "Maximale Uploadgröße", "Maximum of 3 models can be downloaded simultaneously. Please try again later.": "Es können maximal 3 Modelle gleichzeitig heruntergeladen werden. Bitte versuchen Sie es später erneut.", "May": "Mai", - "Medium": "", + "Medium": "Mittel", "Memories accessible by LLMs will be shown here.": "Erinnerungen, die für Modelle zugänglich sind, werden hier angezeigt.", "Memory": "Erinnerungen", "Memory added successfully": "Erinnerung erfolgreich hinzugefügt", @@ -958,10 +958,10 @@ "Merged Response": "Zusammengeführte Antwort", "Message rating should be enabled to use this feature": "Antwortbewertung muss aktiviert sein, um diese Funktion zu verwenden", "Messages you send after creating your link won't be shared. Users with the URL will be able to view the shared chat.": "Nachrichten, die Sie nach der Erstellung Ihres Links senden, werden nicht geteilt. Nutzer mit der URL können den freigegebenen Chat einsehen.", - "Microsoft OneDrive": "", + "Microsoft OneDrive": "Microsoft OneDrive", "Microsoft OneDrive (personal)": "Microsoft OneDrive (persönlich)", "Microsoft OneDrive (work/school)": "Microsoft OneDrive (Arbeit/Schule)", - "Mistral OCR": "", + "Mistral OCR": "Mistral OCR", "Mistral OCR API Key required.": "Mistral OCR API-Schlüssel erforderlich.", "Model": "Modell", "Model '{{modelName}}' has been successfully downloaded.": "Modell '{{modelName}}' wurde erfolgreich heruntergeladen.", @@ -980,15 +980,15 @@ "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": "Modellfilterung", "Model ID": "Modell-ID", - "Model ID is required.": "", + "Model ID is required.": "Modell-ID ist erforderlich.", "Model IDs": "Modell-IDs", "Model Name": "Modell-Name", - "Model name already exists, please choose a different one": "", - "Model Name is required.": "", + "Model name already exists, please choose a different one": "Modellname existiert bereits, bitte wählen Sie einen anderen", + "Model Name is required.": "Modellname ist erforderlich.", "Model not selected": "Modell nicht ausgewählt", "Model Params": "Modell-Parameter", "Model Permissions": "Modellberechtigungen", - "Model unloaded successfully": "", + "Model unloaded successfully": "Modell erfolgreich entladen", "Model updated successfully": "Modell erfolgreich aktualisiert", "Model(s) do not support file upload": "Modell(e) unterstützen keinen Dateiupload", "Modelfile Content": "Modelfile-Inhalt", @@ -1000,9 +1000,9 @@ "More": "Mehr", "More Concise": "Kürzer", "More Options": "Mehr Optionen", - "Move": "", + "Move": "Verschieben", "Name": "Name", - "Name and ID are required, please fill them out": "", + "Name and ID are required, please fill them out": "Name und ID sind erforderlich, bitte füllen Sie diese aus", "Name your knowledge base": "Benennen Sie Ihren Wissensspeicher", "Native": "Nativ", "New Button": "Neuer Button", @@ -1014,7 +1014,7 @@ "New Tool": "Neues Werkzeug", "new-channel": "neuer-kanal", "Next message": "Nächste Nachricht", - "No authentication": "", + "No authentication": "Keine Authentifizierung", "No chats found": "Keine Chats gefunden", "No chats found for this user.": "Keine Chats für diesen Nutzer gefunden.", "No chats found.": "Keine Chats gefunden.", @@ -1022,7 +1022,7 @@ "No content found": "Kein Inhalt gefunden", "No content found in file.": "Kein Inhalt in Datei gefunden.", "No content to speak": "Kein Inhalt zum Vorlesen", - "No conversation to save": "", + "No conversation to save": "Keine Unterhaltung zum Speichern vorhanden", "No distance available": "Keine Distanz verfügbar", "No feedbacks found": "Kein Feedback gefunden", "No file selected": "Keine Datei ausgewählt", @@ -1039,12 +1039,12 @@ "No results found": "Keine Ergebnisse gefunden", "No search query generated": "Keine Suchanfrage generiert", "No source available": "Keine Quelle verfügbar", - "No sources found": "", + "No sources found": "Keine Quellen gefunden", "No suggestion prompts": "Keine Vorschlags-Prompts", "No users were found.": "Keine Benutzer gefunden.", "No valves": "Keine Valves", "No valves to update": "Keine Valves zum Aktualisieren", - "Node Ids": "", + "Node Ids": "Knoten-IDs", "None": "Nichts", "Not factually correct": "Nicht sachlich korrekt", "Not helpful": "Nicht hilfreich", @@ -1055,7 +1055,7 @@ "Notification Webhook": "Benachrichtigungs-Webhook", "Notifications": "Benachrichtigungen", "November": "November", - "OAuth": "", + "OAuth": "OAuth", "OAuth ID": "OAuth-ID", "October": "Oktober", "Off": "Aus", @@ -1080,14 +1080,14 @@ "Oops! You're using an unsupported method (frontend only). Please serve the WebUI from the backend.": "Hoppla! Sie verwenden eine nicht unterstützte Methode (nur Frontend). Bitte stellen Sie die WebUI vom Backend bereit.", "Open file": "Datei öffnen", "Open in full screen": "Im Vollbildmodus öffnen", - "Open modal to configure connection": "", - "Open Modal To Manage Floating Quick Actions": "", + "Open modal to configure connection": "Modal öffnen, um die Verbindung zu konfigurieren", + "Open Modal To Manage Floating Quick Actions": "Modal öffnen, um Schnellaktionen zu verwalten", "Open new chat": "Neuen Chat öffnen", "Open Sidebar": "Seitenleiste öffnen", - "Open User Profile Menu": "", + "Open User Profile Menu": "Benutzerprofilmenü öffnen", "Open WebUI can use tools provided by any OpenAPI server.": "Open WebUI kann Werkzeuge verwenden, die von irgendeinem OpenAPI-Server bereitgestellt werden.", "Open WebUI uses faster-whisper internally.": "Open WebUI verwendet intern faster-whisper.", - "Open WebUI uses SpeechT5 and CMU Arctic speaker embeddings.": "Open WebUI verwendet SpeechT5 und CMU Arctic-Sprecher-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}})", "OpenAI": "OpenAI", "OpenAI API": "OpenAI-API", @@ -1095,9 +1095,9 @@ "OpenAI API Key is required.": "OpenAI-API-Schlüssel erforderlich.", "OpenAI API settings updated": "OpenAI-API-Einstellungen aktualisiert", "OpenAI URL/Key required.": "OpenAI-URL/Schlüssel erforderlich.", - "openapi.json URL or Path": "", - "Optional": "", - "Options for running a local vision-language model in the picture description. The parameters refer to a model hosted on Hugging Face. This parameter is mutually exclusive with picture_description_api.": "", + "openapi.json URL or Path": "openapi.json-URL oder Pfad", + "Optional": "Optional", + "Options for running a local vision-language model in the picture description. The parameters refer to a model hosted on Hugging Face. This parameter is mutually exclusive with picture_description_api.": "Optionen zum Ausführen eines lokalen Vision-Language-Modells in der Bildbeschreibung. Die Parameter beziehen sich auf ein Modell, das auf Hugging Face gehostet wird. Dieser Parameter ist gegenseitig ausschließend mit picture_description_api.", "or": "oder", "Ordered List": "Geordnete Liste", "Organize your users": "Organisieren Sie Ihre Benutzer", @@ -1107,35 +1107,35 @@ "Output Format": "Ausgabe Format", "Overview": "Übersicht", "page": "Seite", - "Paginate": "", + "Paginate": "Paginieren", "Parameters": "Parameter", "Password": "Passwort", - "Passwords do not match.": "", + "Passwords do not match.": "Die Passwörter stimmen nicht überein.", "Paste Large Text as File": "Großen Text als Datei einfügen", - "PDF Backend": "", + "PDF Backend": "PDF-Backend", "PDF document (.pdf)": "PDF-Dokument (.pdf)", "PDF Extract Images (OCR)": "Text von Bildern aus PDFs extrahieren (OCR)", "pending": "ausstehend", "Pending": "Ausstehend", "Pending User Overlay Content": "Inhalt des Overlays 'Ausstehende Kontoaktivierung'", "Pending User Overlay Title": "Titel des Overlays 'Ausstehende Kontoaktivierung'", - "Perform OCR": "", + "Perform OCR": "OCR durchführen", "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: {{error}}": "Zugriff auf das Mikrofon verweigert: {{error}}", "Permissions": "Berechtigungen", "Perplexity API Key": "Perplexity API-Schlüssel", "Perplexity Model": "Perplexity Modell", - "Perplexity Search Context Usage": "", + "Perplexity Search Context Usage": "Perplexity-Suchkontextnutzung", "Personalization": "Personalisierung", - "Picture Description API Config": "", - "Picture Description Local Config": "", - "Picture Description Mode": "", + "Picture Description API Config": "Konfiguration der Bildbeschreibungs-API", + "Picture Description Local Config": "Lokale Konfiguration der Bildbeschreibung", + "Picture Description Mode": "Modus der Bildbeschreibung", "Pin": "Anheften", "Pinned": "Angeheftet", "Pioneer insights": "Bahnbrechende Erkenntnisse", - "Pipe": "", - "Pipeline": "", + "Pipe": "Pipe", + "Pipeline": "Pipeline", "Pipeline deleted successfully": "Pipeline erfolgreich gelöscht", "Pipeline downloaded successfully": "Pipeline erfolgreich heruntergeladen", "Pipelines": "Pipelines", @@ -1149,7 +1149,7 @@ "Playwright WebSocket URL": "Playwright WebSocket-URL", "Please carefully review the following warnings:": "Bitte überprüfen Sie die folgenden Warnungen sorgfältig:", "Please do not close the settings page while loading the model.": "Bitte schließen die Einstellungen-Seite nicht, während das Modell lädt.", - "Please enter a message or attach a file.": "", + "Please enter a message or attach a file.": "Bitte geben Sie eine Nachricht ein oder hängen Sie eine Datei an.", "Please enter a prompt": "Bitte geben Sie einen Prompt ein", "Please enter a valid path": "Bitte geben Sie einen gültigen Pfad ein", "Please enter a valid URL": "Bitte geben Sie eine gültige URL ein", @@ -1157,10 +1157,10 @@ "Please select a model first.": "Bitte wählen Sie zuerst ein Modell aus.", "Please select a model.": "Bitte wählen Sie ein Modell aus.", "Please select a reason": "Bitte wählen Sie einen Grund aus", - "Please wait until all files are uploaded.": "", + "Please wait until all files are uploaded.": "Bitte warten Sie, bis alle Dateien hochgeladen sind.", "Port": "Port", "Positive attitude": "Positive Einstellung", - "Prefer not to say": "", + "Prefer not to say": "Keine Angabe", "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 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", "Prevent file creation": "Dateierstellung verhindern", @@ -1180,37 +1180,37 @@ "Prompts": "Prompts", "Prompts Access": "Prompt-Zugriff", "Prompts Public Sharing": "Öffentliche Freigabe von Prompts", - "Provider Type": "", + "Provider Type": "Anbietertyp", "Public": "Öffentlich", "Pull \"{{searchValue}}\" from Ollama.com": "\"{{searchValue}}\" von Ollama.com beziehen", "Pull a model from Ollama.com": "Modell von Ollama.com beziehen", - "pypdfium2": "", + "pypdfium2": "pypdfium2", "Query Generation Prompt": "Abfragegenerierungsprompt", - "Querying": "", - "Quick Actions": "", + "Querying": "Suche...", + "Quick Actions": "Schnellaktionen", "RAG Template": "RAG-Vorlage", "Rating": "Bewertung", "Re-rank models by topic similarity": "Modelle nach thematischer Ähnlichkeit neu ordnen", "Read": "Lesen", "Read Aloud": "Vorlesen", "Reason": "Nachdenken", - "Reasoning Effort": "Nachdenk-Aufwand", - "Reasoning Tags": "", + "Reasoning Effort": "Reasoning Effort", + "Reasoning Tags": "Reasoning Tags", "Record": "Aufzeichnen", "Record voice": "Stimme aufnehmen", "Redirecting you to Open WebUI 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.": "", + "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.": "Verringert die Wahrscheinlichkeit, Unsinn zu generieren. Ein höherer Wert (z. B. 100) führt zu vielfältigeren Antworten, während ein niedrigerer Wert (z. B. 10) konservativer ist.", "Refer to yourself as \"User\" (e.g., \"User is learning Spanish\")": "Beziehen Sie sich auf sich selbst als \"Benutzer\" (z. B. \"Benutzer lernt Spanisch\")", "Refused when it shouldn't have": "Abgelehnt, obwohl es nicht hätte abgelehnt werden sollen", "Regenerate": "Neu generieren", - "Regenerate Menu": "", + "Regenerate Menu": "Menü neu generieren", "Reindex": "Neu indexieren", "Reindex Knowledge Base Vectors": "Vektoren der Wissensdatenbank neu indizieren", "Release Notes": "Veröffentlichungshinweise", "Releases": "Versionen", "Relevance": "Relevanz", "Relevance Threshold": "Relevanzschwelle", - "Remember Dismissal": "", + "Remember Dismissal": "Ablehnung merken", "Remove": "Entfernen", "Remove {{MODELID}} from list.": "{{MODELID}} von der Liste entfernen.", "Remove file": "Datei entfernen", @@ -1236,12 +1236,12 @@ "Response Watermark": "Antwort Wasserzeichen", "Result": "Ergebnis", "RESULT": "Ergebnis", - "Retrieval": "", + "Retrieval": "Abruf", "Retrieval Query Generation": "Abfragegenerierung", - "Retrieved {{count}} sources": "", - "Retrieved {{count}} sources_one": "", - "Retrieved {{count}} sources_other": "", - "Retrieved 1 source": "", + "Retrieved {{count}} sources": "{{count}} Quellen abgerufen", + "Retrieved {{count}} sources_one": "{{count}} Quelle abgerufen", + "Retrieved {{count}} sources_other": "{{count}} Quellen abgerufen", + "Retrieved 1 source": "1 Quelle abgerufen", "Rich Text Input for Chat": "Rich-Text-Eingabe für Chats", "RK": "RK", "Role": "Rolle", @@ -1255,25 +1255,25 @@ "Save & Create": "Erstellen", "Save & Update": "Aktualisieren", "Save As Copy": "Als Kopie speichern", - "Save Chat": "", + "Save Chat": "Chat speichern", "Save Tag": "Tag speichern", "Saved": "Gespeichert", "Saving chat logs directly to your browser's storage is no longer supported. Please take a moment to download and delete your chat logs by clicking the button below. Don't worry, you can easily re-import your chat logs to the backend through": "Das direkte Speichern von Chats im Browser-Speicher wird nicht mehr unterstützt. Bitte nehmen Sie einen Moment Zeit, um Ihre Chats zu exportieren und zu löschen, indem Sie auf die Schaltfläche unten klicken. Keine Sorge, Sie können Ihre Chats problemlos über das Backend wieder importieren.", - "Scroll On Branch Change": "", + "Scroll On Branch Change": "Beim Wechseln des Zweigs scrollen", "Search": "Suchen", "Search a model": "Modell suchen", - "Search all emojis": "", + "Search all emojis": "Alle Emojis durchsuchen", "Search Base": "Suchbasis", "Search Chats": "Chats durchsuchen...", "Search Collection": "Sammlung durchsuchen", "Search Filters": "Suchfilter", - "search for archived chats": "", - "search for folders": "", - "search for pinned chats": "", - "search for shared chats": "", + "search for archived chats": "nach archivierten Chats suchen", + "search for folders": "nach Ordnern suchen", + "search for pinned chats": "nach angehefteten Chats suchen", + "search for shared chats": "nach geteilten Chats suchen", "search for tags": "nach Tags suchen", "Search Functions": "Funktionen durchsuchen...", - "Search In Models": "", + "Search In Models": "In Modellen suchen...", "Search Knowledge": "Wissen durchsuchen", "Search Models": "Modelle durchsuchen...", "Search Notes": "Notizen durchsuchen...", @@ -1285,7 +1285,7 @@ "SearchApi API Key": "SearchApi-API-Schlüssel", "SearchApi Engine": "SearchApi-Engine", "Searched {{count}} sites": "{{count}} Websites durchsucht", - "Searching": "", + "Searching": "Suche...", "Searching \"{{searchQuery}}\"": "Suche nach \"{{searchQuery}}\"", "Searching Knowledge for \"{{searchQuery}}\"": "Suche im Wissen nach \"{{searchQuery}}\"", "Searching the web": "Durchsuche das Web...", @@ -1295,34 +1295,34 @@ "Seed": "Seed", "Select": "Auswählen", "Select a base model": "Wählen Sie ein Basismodell", - "Select a base model (e.g. llama3, gpt-4o)": "", - "Select a conversation to preview": "", + "Select a base model (e.g. llama3, gpt-4o)": "Wählen Sie ein Basismodell (z. B. llama3, gpt-4o)", + "Select a conversation to preview": "Wählen Sie eine Unterhaltung zur Vorschau aus", "Select a engine": "Wählen Sie eine Engine", "Select a function": "Wählen Sie eine Funktion", "Select a group": "Wählen Sie eine Gruppe", "Select a language": "Wählen Sie eine Sprache", - "Select a mode": "", + "Select a mode": "Wählen Sie einen Modus", "Select a model": "Wählen Sie ein Modell", - "Select a model (optional)": "", + "Select a model (optional)": "Wählen Sie ein Modell (optional)", "Select a pipeline": "Wählen Sie eine Pipeline", "Select a pipeline url": "Wählen Sie eine Pipeline-URL", - "Select a reranking model engine": "", + "Select a reranking model engine": "Wählen Sie eine Reranking-Modell-Engine", "Select a role": "Wählen Sie eine Rolle", "Select a theme": "Wählen Sie ein Design", "Select a tool": "Wählen Sie ein Werkzeug", "Select a voice": "Wählen Sie eine Stimme", "Select an auth method": "Wählen Sie eine Authentifizierungsmethode", - "Select an embedding model engine": "", - "Select an engine": "", + "Select an embedding model engine": "Wählen Sie eine Embedding-Modell-Engine aus", + "Select an engine": "Engine auswählen", "Select an Ollama instance": "Wählen Sie eine Ollama-Instanz", - "Select an output format": "", - "Select dtype": "", + "Select an output format": "Wählen Sie ein Ausgabeformat aus", + "Select dtype": "Wählen Sie dtype aus", "Select Engine": "Engine auswählen", - "Select how to split message text for TTS requests": "", + "Select how to split message text for TTS requests": "Wählen Sie, wie der Nachrichtentext für TTS-Anfragen aufgeteilt werden soll", "Select Knowledge": "Wissensdatenbank auswählen", "Select only one model to call": "Wählen Sie nur ein Modell zum Anrufen aus", "Selected model(s) do not support image inputs": "Ihre ausgewählten Modelle unterstützen keine Bildeingaben", - "semantic": "", + "semantic": "semantisch", "Send": "Senden", "Send a Message": "Eine Nachricht senden", "Send message": "Nachricht senden", @@ -1349,10 +1349,10 @@ "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 Worker-Threads fest. Diese Option steuert, wie viele Threads zur gleichzeitigen Verarbeitung eingehender Anfragen verwendet werden. Eine Erhöhung dieses Wertes kann die Leistung bei hoher Parallelität verbessern, kann aber mehr CPU-Ressourcen verbrauchen.", "Set Voice": "Stimme festlegen", "Set whisper model": "Whisper-Modell festlegen", - "Sets a flat bias against tokens that have appeared at least once. A higher value (e.g., 1.5) will penalize repetitions more strongly, while a lower value (e.g., 0.9) will be more lenient. At 0, it is disabled.": "", - "Sets a scaling bias against tokens to penalize repetitions, based on how many times they have appeared. A higher value (e.g., 1.5) will penalize repetitions more strongly, while a lower value (e.g., 0.9) will be more lenient. At 0, it is disabled.": "", - "Sets how far back for the model to look back to prevent repetition.": "", - "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.": "", + "Sets a flat bias against tokens that have appeared at least once. A higher value (e.g., 1.5) will penalize repetitions more strongly, while a lower value (e.g., 0.9) will be more lenient. At 0, it is disabled.": "Legt einen festen Bias gegen Token fest, die mindestens einmal erschienen sind. Ein höherer Wert (z. B. 1.5) bestraft Wiederholungen stärker, während ein niedrigerer Wert (z. B. 0.9) nachsichtiger ist. Bei 0 ist die Funktion deaktiviert.", + "Sets a scaling bias against tokens to penalize repetitions, based on how many times they have appeared. A higher value (e.g., 1.5) will penalize repetitions more strongly, while a lower value (e.g., 0.9) will be more lenient. At 0, it is disabled.": "Legt einen skalierenden Bias gegen Token fest, um Wiederholungen basierend auf ihrer Häufigkeit zu bestrafen. Ein höherer Wert (z. B. 1.5) bestraft Wiederholungen stärker, während ein niedrigerer Wert (z. B. 0.9) nachsichtiger ist. Bei 0 ist die Funktion deaktiviert.", + "Sets how far back for the model to look back to prevent repetition.": "Legt fest, wie weit das Modell zurückblickt, um Wiederholungen zu verhindern.", + "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.": "Legt den Startwert für die Zufallszahlengenerierung fest. Wenn dieser auf eine bestimmte Zahl gesetzt wird, erzeugt das Modell für denselben Prompt immer denselben Text.", "Sets the size of the context window used to generate the next token.": "Legt die Größe des Kontextfensters fest, das zur Generierung des nächsten Token verwendet wird.", "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", @@ -1360,14 +1360,14 @@ "Share": "Teilen", "Share Chat": "Chat teilen", "Share to Open WebUI Community": "Mit OpenWebUI Community teilen", - "Share your background and interests": "", + "Share your background and interests": "Teilen Sie Ihren Hintergrund und Ihre Interessen", "Sharing Permissions": "Berechtigungen teilen", - "Shortcuts with an asterisk (*) are situational and only active under specific conditions.": "", + "Shortcuts with an asterisk (*) are situational and only active under specific conditions.": "Tastenkürzel mit einem Sternchen (*) sind situationsbedingt und nur unter bestimmten Bedingungen aktiv.", "Show": "Anzeigen", "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 All": "Alle anzeigen", - "Show Formatting Toolbar": "", + "Show Formatting Toolbar": "Formatierungsleiste anzeigen", "Show image preview": "Bildvorschau anzeigen", "Show Less": "Weniger anzeigen", "Show Model": "Modell anzeigen", @@ -1380,18 +1380,18 @@ "Sign Out": "Abmelden", "Sign up": "Registrieren", "Sign up to {{WEBUI_NAME}}": "Bei {{WEBUI_NAME}} registrieren", - "Significantly improves accuracy by using an LLM to enhance tables, forms, inline math, and layout detection. Will increase latency. Defaults to False.": "", + "Significantly improves accuracy by using an LLM to enhance tables, forms, inline math, and layout detection. Will increase latency. Defaults to False.": "Verbessert die Genauigkeit erheblich, indem ein LLM zur Verbesserung von Tabellen, Formularen, mathematischen Formeln und Layout-Erkennung verwendet wird. Erhöht die Latenz. Standardmäßig deaktiviert.", "Signing in to {{WEBUI_NAME}}": "Wird bei {{WEBUI_NAME}} angemeldet", - "Sink List": "", + "Sink List": "Senkenliste", "sk-1234": "sk-1234", - "Skip Cache": "Cache überspringen", - "Skip the cache and re-run the inference. Defaults to False.": "", + "Skip Cache": "Cache ignorieren", + "Skip the cache and re-run the inference. Defaults to False.": "Cache ignorieren und die Inferenz erneut ausführen. Standardmäßig deaktiviert.", "Something went wrong :/": "Etwas ist schief gelaufen :/", - "Sonar": "", - "Sonar Deep Research": "", - "Sonar Pro": "", - "Sonar Reasoning": "", - "Sonar Reasoning Pro": "", + "Sonar": "Sonar", + "Sonar Deep Research": "Sonar Deep Research", + "Sonar Pro": "Sonar Pro", + "Sonar Reasoning": "Sonar Reasoning", + "Sonar Reasoning Pro": "Sonar Reasoning Pro", "Sougou Search API sID": "Sougou Search API sID", "Sougou Search API SK": "Sougou Search API SK", "Source": "Quelle", @@ -1399,36 +1399,36 @@ "Speech recognition error: {{error}}": "Spracherkennungsfehler: {{error}}", "Speech-to-Text": "Sprache-zu-Text", "Speech-to-Text Engine": "Sprache-zu-Text-Engine", - "standard": "", + "standard": "Standard", "Start of the channel": "Beginn des Kanals", - "Start Tag": "", - "Status Updates": "", + "Start Tag": "Start-Tag", + "Status Updates": "Statusaktualisierungen", "STDOUT/STDERR": "STDOUT/STDERR", "Stop": "Stop", "Stop Generating": "Generierung stoppen", "Stop Sequence": "Stop-Sequenz", "Stream Chat Response": "Chat-Antwort streamen", - "Stream Delta Chunk Size": "", + "Stream Delta Chunk Size": "Stream-Delta-Chunk-Größe", "Strikethrough": "Durchgestrichen", - "Strip Existing OCR": "", - "Strip existing OCR text from the PDF and re-run OCR. Ignored if Force OCR is enabled. Defaults to False.": "", + "Strip Existing OCR": "Vorhandenes OCR entfernen", + "Strip existing OCR text from the PDF and re-run OCR. Ignored if Force OCR is enabled. Defaults to False.": "Vorhandenen OCR-Text aus der PDF entfernen und OCR erneut ausführen. Wird ignoriert, wenn OCR erzwingen aktiviert ist. Standardmäßig deaktiviert.", "STT Model": "STT-Modell", "STT Settings": "STT-Einstellungen", "Stylized PDF Export": "Stilisierter PDF-Export", "Subtitle (e.g. about the Roman Empire)": "Untertitel (z. B. über das Römische Reich)", "Success": "Erfolg", - "Successfully imported {{userCount}} users.": "", + "Successfully imported {{userCount}} users.": "Erfolgreich {{userCount}} Benutzer importiert.", "Successfully updated.": "Erfolgreich aktualisiert.", "Suggest a change": "Eine Änderung vorschlagen", "Suggested": "Vorgeschlagen", "Support": "Unterstützung", "Support this plugin:": "Unterstützen Sie dieses Plugin:", - "Supported MIME Types": "", + "Supported MIME Types": "Unterstützte MIME-Typen", "Sync directory": "Verzeichnis synchronisieren", "System": "System", "System Instructions": "Systemanweisungen", "System Prompt": "System-Prompt", - "Table Mode": "", + "Table Mode": "Tabellenmodus", "Tags": "Tags", "Tags Generation": "Tag-Generierung", "Tags Generation Prompt": "Prompt für Tag-Generierung", @@ -1443,43 +1443,43 @@ "Tell us more:": "Erzählen Sie uns mehr:", "Temperature": "Temperatur", "Temporary Chat": "Temporäre Unterhaltung", - "Temporary Chat by Default": "", + "Temporary Chat by Default": "Temporäre Unterhaltung standardmäßig", "Text Splitter": "Text-Splitter", "Text-to-Speech": "Text-zu-Sprache", "Text-to-Speech Engine": "Text-zu-Sprache-Engine", "Thanks for your feedback!": "Danke für Ihr Feedback!", "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": "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.": "", + "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.": "Die Batch-Größe bestimmt, wie viele Textanfragen gleichzeitig zusammen verarbeitet werden. Eine höhere Batch-Größe kann die Leistung und Geschwindigkeit des Modells erhöhen, erfordert jedoch auch mehr Speicher.", "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 format to return a response in. Format can be json or a JSON schema.": "", - "The height in pixels to compress images to. Leave empty for no compression.": "", - "The language of the input audio. Supplying the input language in ISO-639-1 (e.g. en) format will improve accuracy and latency. Leave blank to automatically detect the language.": "", + "The format to return a response in. Format can be json or a JSON schema.": "Das Format, in dem eine Antwort zurückgegeben wird. Das Format kann JSON oder ein JSON-Schema sein.", + "The height in pixels to compress images to. Leave empty for no compression.": "Die Höhe in Pixeln, auf die Bilder komprimiert werden sollen. Leer lassen, um keine Komprimierung durchzuführen.", + "The language of the input audio. Supplying the input language in ISO-639-1 (e.g. en) format will improve accuracy and latency. Leave blank to automatically detect the language.": "Die Sprache des Eingangsaudios. Die Angabe der Eingangssprache im ISO-639-1-Format (z. B. en) verbessert die Genauigkeit und verringert die Latenz. Leer lassen, um die Sprache automatisch zu erkennen.", "The LDAP attribute that maps to the mail that users use to sign in.": "Das LDAP-Attribut, das der Mail zugeordnet ist, die Benutzer zum Anmelden verwenden.", "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 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 im Chat verwendet werden können. Wenn die Anzahl der Dateien dieses Limit überschreitet, werden die Dateien nicht hochgeladen.", - "The output format for the text. Can be 'json', 'markdown', or 'html'. Defaults to 'markdown'.": "", - "The passwords you entered don't quite match. Please double-check and try again.": "", - "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 stream delta chunk size for the model. Increasing the chunk size will make the model respond with larger pieces of text at once.": "", + "The output format for the text. Can be 'json', 'markdown', or 'html'. Defaults to 'markdown'.": "Das Ausgabeformat für den Text. Kann 'json', 'markdown' oder 'html' sein. Standardmäßig auf 'markdown' gesetzt.", + "The passwords you entered don't quite match. Please double-check and try again.": "Die eingegebenen Passwörter stimmen nicht überein. Bitte überprüfen Sie sie erneut und versuchen Sie es noch einmal.", + "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 stream delta chunk size for the model. Increasing the chunk size will make the model respond with larger pieces of text at once.": "Die Stream-Delta-Chunk-Größe für das Modell. Eine Erhöhung der Chunk-Größe führt dazu, dass das Modell mit größeren Textabschnitten auf einmal antwortet.", "The temperature of the model. Increasing the temperature will make the model answer more creatively.": "Die Temperatur des Modells. Eine Erhöhung der Temperatur führt zu kreativeren Antworten des Modells.", - "The Weight of BM25 Hybrid Search. 0 more lexical, 1 more semantic. Default 0.5": "", - "The width in pixels to compress images to. Leave empty for no compression.": "", + "The Weight of BM25 Hybrid Search. 0 more lexical, 1 more semantic. Default 0.5": "Das Gewicht der BM25-Hybridsuche. 0 stärker lexikalisch, 1 stärker semantisch. Standardwert 0.5", + "The width in pixels to compress images to. Leave empty for no compression.": "Die Breite in Pixeln, auf die Bilder komprimiert werden sollen. Leer lassen, um keine Komprimierung durchzuführen.", "Theme": "Design", "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 channel was created on {{createdAt}}. This is the very beginning of the {{channelName}} channel.": "Dieser Kanal wurde am {{createdAt}} erstellt. Dies ist der Beginn des {{channelName}} Kanals.", "This chat won't appear in history and your messages will not be saved.": "Diese Unterhaltung erscheint nicht in Ihrem Chat-Verlauf. Alle Nachrichten sind privat und werden nicht gespeichert.", "This ensures that your valuable conversations are securely saved to your backend database. Thank you!": "Dies stellt sicher, dass Ihre wertvollen Chats sicher in Ihrer Backend-Datenbank gespeichert werden. Vielen Dank!", - "This feature is experimental and may be modified or discontinued without notice.": "", + "This feature is experimental and may be modified or discontinued without notice.": "Diese Funktion ist experimentell und kann ohne Ankündigung geändert oder entfernt 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 model is not publicly available. Please select another model.": "Dieses Modell ist nicht öffentlich verfügbar. Bitte wählen Sie ein anderes Modell aus.", - "This option controls how long the model will stay loaded into memory following the request (default: 5m)": "", + "This option controls how long the model will stay loaded into memory following the request (default: 5m)": "Diese Option steuert, wie lange das Modell nach der Anfrage im Speicher verbleibt (Standard: 5m)", "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.": "Diese Option steuert, wie viele Token beim Aktualisieren des Kontexts beibehalten werden. Wenn beispielsweise 2 eingestellt ist, werden die letzten 2 Tokens des Gesprächskontexts beibehalten. Das Beibehalten des Kontexts kann helfen, die Kontinuität eines Gesprächs zu wahren, kann aber die Fähigkeit, auf neue Themen zu reagieren, einschränken.", - "This option enables or disables the use of the reasoning feature in Ollama, which allows the model to think before generating a response. When enabled, the model can take a moment to process the conversation context and generate a more thoughtful response.": "", + "This option enables or disables the use of the reasoning feature in Ollama, which allows the model to think before generating a response. When enabled, the model can take a moment to process the conversation context and generate a more thoughtful response.": "Diese Option aktiviert oder deaktiviert die Verwendung der Reasoning-Funktion in Ollama, wodurch das Modell vor der Erstellung einer Antwort nachdenken kann. Wenn aktiviert, kann das Modell einen Moment benötigen, um den Gesprächskontext zu verarbeiten und eine durchdachtere Antwort zu generieren.", "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.": "Diese Option legt die maximale Anzahl von Token fest, die das Modell in seiner Antwort generieren kann. Eine Erhöhung dieses Limits ermöglicht längere Antworten, kann aber auch die Wahrscheinlichkeit erhöhen, dass unbrauchbare oder irrelevante Inhalte generiert werden.", "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", @@ -1511,15 +1511,15 @@ "To learn more about available endpoints, visit our documentation.": "Um mehr über verfügbare Endpunkte zu erfahren, besuchen Sie unsere Dokumentation.", "To learn more about powerful prompt variables, click here": "Um mehr über Prompt-Variablen zu erfahren, klicken Sie hier", "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.": "Um Ihre Privatsphäre zu schützen, werden nur Bewertungen, Modell-IDs, Tags und Metadaten aus Ihrem Feedback geteilt – Ihre Chats bleiben privat und werden nicht einbezogen.", - "To select actions here, add them to the \"Functions\" workspace first.": "Um Aktionen auszuwählen, fügen Sie diese zunächst dem Arbeitsbereich „Funktionen“ hinzu.", - "To select filters here, add them to the \"Functions\" workspace first.": "Um Filter auszuwählen, fügen Sie diese zunächst dem Arbeitsbereich „Funktionen“ hinzu.", - "To select toolkits here, add them to the \"Tools\" workspace first.": "Um Toolkits auszuwählen, fügen Sie sie zunächst dem Arbeitsbereich „Werkzeuge“ hinzu.", + "To select actions here, add them to the \"Functions\" workspace first.": "Um Aktionen auszuwählen, fügen Sie diese zunächst dem Arbeitsbereich \"Funktionen\" hinzu.", + "To select filters here, add them to the \"Functions\" workspace first.": "Um Filter auszuwählen, fügen Sie diese zunächst dem Arbeitsbereich \"Funktionen\" hinzu.", + "To select toolkits here, add them to the \"Tools\" workspace first.": "Um Toolkits auszuwählen, fügen Sie sie zunächst dem Arbeitsbereich \"Werkzeuge\" hinzu.", "Toast notifications for new updates": "Toast-Benachrichtigungen für neue Updates", "Today": "Heute", "Toggle search": "Suche umschalten", "Toggle settings": "Einstellungen umschalten", "Toggle sidebar": "Seitenleiste umschalten", - "Toggle whether current connection is active.": "", + "Toggle whether current connection is active.": "Aktuelle Verbindung umschalten.", "Token": "Token", "Too verbose": "Zu ausführlich", "Tool created successfully": "Werkzeug erfolgreich erstellt", @@ -1541,7 +1541,7 @@ "Transformers": "Transformers", "Trouble accessing Ollama?": "Probleme beim Zugriff auf Ollama?", "Trust Proxy Environment": "Proxy-Umgebung vertrauen", - "Try Again": "", + "Try Again": "Erneut versuchen", "TTS Model": "TTS-Modell", "TTS Settings": "TTS-Einstellungen", "TTS Voice": "TTS-Stimme", @@ -1553,11 +1553,11 @@ "Unarchive All Archived Chats": "Alle archivierten Chats wiederherstellen", "Unarchive Chat": "Chat wiederherstellen", "Underline": "Unterstreichen", - "Unloads {{FROM_NOW}}": "", + "Unloads {{FROM_NOW}}": "Entlädt {{FROM_NOW}}", "Unlock mysteries": "Geheimnisse entsperren", "Unpin": "Lösen", "Unravel secrets": "Geheimnisse lüften", - "Unsupported file type.": "", + "Unsupported file type.": "Nicht unterstützter Dateityp.", "Untagged": "Ungetaggt", "Untitled": "Unbenannt", "Update": "Aktualisieren", @@ -1575,8 +1575,8 @@ "Upload files": "Dateien hochladen", "Upload Files": "Datei(en) hochladen", "Upload Pipeline": "Pipeline hochladen", - "Upload Progress": "Hochladefortschritt", - "Upload Progress: {{uploadedFiles}}/{{totalFiles}} ({{percentage}}%)": "", + "Upload Progress": "Fortschritt", + "Upload Progress: {{uploadedFiles}}/{{totalFiles}} ({{percentage}}%)": "Fortschritt: {{uploadedFiles}}/{{totalFiles}} ({{percentage}}%)", "URL": "URL", "URL is required": "URL wird benötigt", "URL Mode": "URL-Modus", @@ -1594,11 +1594,11 @@ "User Webhooks": "Benutzer Webhooks", "Username": "Benutzername", "Users": "Benutzer", - "Using Entire Document": "", - "Using Focused Retrieval": "", + "Using Entire Document": "Verwendung des gesamten Dokuments", + "Using Focused Retrieval": "Verwendung relevanter Abschnitte", "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.", "Valid time units:": "Gültige Zeiteinheiten:", - "Validate certificate": "", + "Validate certificate": "Zertifikat überprüfen", "Valves": "Valves", "Valves updated": "Valves aktualisiert", "Valves updated successfully": "Valves erfolgreich aktualisiert", @@ -1611,7 +1611,7 @@ "View Result from **{{NAME}}**": "Ergebnis von **{{NAME}}** anzeigen", "Visibility": "Sichtbarkeit", "Vision": "Bilderkennung", - "vlm": "", + "vlm": "vlm", "Voice": "Stimme", "Voice Input": "Spracheingabe", "Voice mode": "Sprachmodus", @@ -1622,7 +1622,7 @@ "Warning: Jupyter execution enables arbitrary code execution, posing severe security risks—proceed with extreme caution.": "Warnung: Die Jupyter-Ausführung ermöglicht beliebige Codeausführung und birgt erhebliche Sicherheitsrisiken – gehen Sie mit äußerster Vorsicht vor.", "Web": "Web", "Web API": "Web-API", - "Web Loader Engine": "", + "Web Loader Engine": "Web-Loader-Engine", "Web Search": "Websuche", "Web Search Engine": "Suchmaschine", "Web Search in Chat": "Websuche im Chat", @@ -1638,13 +1638,13 @@ "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.": "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": "wo immer Sie sind", - "Whether to paginate the output. Each page will be separated by a horizontal rule and page number. Defaults to False.": "", + "Whether to paginate the output. Each page will be separated by a horizontal rule and page number. Defaults to False.": "Ob die Ausgabe paginiert werden soll. Jede Seite wird durch eine horizontale Linie und eine Seitenzahl getrennt. Standardmäßig deaktiviert.", "Whisper (Local)": "Whisper (lokal)", "Why?": "Warum?", "Widescreen Mode": "Breitbildmodus", "Width": "Breite", "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.": "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.", + "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.": "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.", "Workspace": "Arbeitsbereich", "Workspace Permissions": "Arbeitsbereichsberechtigungen", "Write": "Schreiben", @@ -1666,7 +1666,7 @@ "You have shared this chat": "Sie haben diesen Chat geteilt", "You're a helpful assistant.": "Du bist ein hilfreicher Assistent.", "You're now logged in.": "Sie sind jetzt eingeloggt.", - "Your Account": "", + "Your Account": "Ihr Konto", "Your account status is currently pending activation.": "Ihr Kontostatus ist derzeit ausstehend und wartet auf Aktivierung.", "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.": "Ihr gesamter Beitrag geht direkt an den Plugin-Entwickler; Open WebUI behält keinen Prozentsatz ein. Die gewählte Finanzierungsplattform kann jedoch eigene Gebühren haben.", "Youtube": "YouTube", From ff8100dc6f0f015f79d969d720379fd1dea23e97 Mon Sep 17 00:00:00 2001 From: Shirasawa <764798966@qq.com> Date: Fri, 12 Sep 2025 16:54:24 +0800 Subject: [PATCH 0199/1079] feat: improve zh-CN translation --- src/lib/i18n/locales/zh-CN/translation.json | 48 ++++++++++----------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/src/lib/i18n/locales/zh-CN/translation.json b/src/lib/i18n/locales/zh-CN/translation.json index 25e22da339..06ccabed15 100644 --- a/src/lib/i18n/locales/zh-CN/translation.json +++ b/src/lib/i18n/locales/zh-CN/translation.json @@ -96,7 +96,7 @@ "Allow Speech to Text": "允许语音转文本", "Allow Temporary Chat": "允许临时对话", "Allow Text to Speech": "允许文本转语音", - "Allow User Location": "允许获取您的位置", + "Allow User Location": "获取您的位置", "Allow Voice Interruption in Call": "允许语音通话时打断对话", "Allowed Endpoints": "允许的 API 端点", "Allowed File Extensions": "允许的文件扩展名", @@ -218,10 +218,10 @@ "Chart new frontiers": "开辟前沿", "Chat": "对话", "Chat Background Image": "对话背景图片", - "Chat Bubble UI": "对话气泡样式", + "Chat Bubble UI": "以聊天气泡的形式显示对话内容", "Chat Controls": "对话高级设置", "Chat Conversation": "对话内容", - "Chat direction": "对话样式方向", + "Chat direction": "对话显示方向", "Chat ID": "对话 ID", "Chat moved successfully": "移动对话成功", "Chat Overview": "对话概述", @@ -320,7 +320,7 @@ "Copied shared chat URL to clipboard!": "已复制对话的分享链接到剪贴板!", "Copied to clipboard": "已复制到剪贴板", "Copy": "复制", - "Copy Formatted Text": "复制格式化文本", + "Copy Formatted Text": "复制文本时包含特殊格式", "Copy last code block": "复制最后一个代码块中的代码", "Copy last response": "复制最后一次回复内容", "Copy link": "复制链接", @@ -422,7 +422,7 @@ "Discover, download, and explore custom tools": "发现、下载并探索更多自定义工具", "Discover, download, and explore model presets": "发现、下载并探索更多模型预设", "Display": "显示", - "Display Emoji in Call": "在通话中显示 Emoji 表情符号", + "Display Emoji in Call": "在通话中显示 Emoji", "Display Multi-model Responses in Tabs": "以标签页的形式展示多个模型的回答", "Display the username instead of You in the Chat": "在对话中显示用户名而不是“你”", "Displays citations in the response": "在回答中显示引用来源", @@ -498,7 +498,7 @@ "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.": "启用内存锁定 (mlock),防止模型数据被移出内存。此选项将模型的工作集页面锁定在内存中,确保它们不会被交换到磁盘,避免页面错误,确保快速数据访问,从而维持性能。", "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.": "启用内存映射 (mmap) 加载模型数据。此选项将磁盘文件视作内存中的数据,允许系统使用磁盘存储作为内存的扩展,通过更快的数据访问来提高模型性能。然而,它可能无法在所有系统上正常工作,并且可能会消耗大量磁盘空间。", - "Enable Message Rating": "启用回复评价", + "Enable Message Rating": "启用模型回答结果评价", "Enable Mirostat sampling for controlling perplexity.": "启用 Mirostat 采样以控制困惑度", "Enable New Sign Ups": "允许新用户注册", "Enable, disable, or customize the reasoning tags used by the model. \"Enabled\" uses default tags, \"Disabled\" turns off reasoning tags, and \"Custom\" lets you specify your own start and end tags.": "启用、禁用或自定义模型使用的推理过程标签。“启用”表示使用默认标签,“禁用”将不识别推理标签,“自定义”可指定起始和闭合标签。", @@ -670,7 +670,7 @@ "External Web Loader URL": "外部网页加载器 URL", "External Web Search API Key": "外部联网搜索 API 密钥", "External Web Search URL": "外部联网搜索 URL", - "Fade Effect for Streaming Text": "流式文本淡入效果", + "Fade Effect for Streaming Text": "流式输出内容时启用动态渐显效果", "Failed to add file.": "添加文件失败", "Failed to connect to {{URL}} OpenAPI tool server": "连接到 {{URL}} OpenAPI 工具服务器失败", "Failed to copy link": "复制链接失败", @@ -806,15 +806,15 @@ "iframe Sandbox Allow Same Origin": "iframe 沙盒允许同源访问", "Ignite curiosity": "点燃求知", "Image": "图像生成", - "Image Compression": "图像压缩", - "Image Compression Height": "图片压缩高度", - "Image Compression Width": "图片压缩宽度", + "Image Compression": "压缩图像", + "Image Compression Height": "压缩图像高度", + "Image Compression Width": "压缩图像宽度", "Image Generation": "图像生成", "Image Generation (Experimental)": "图像生成(实验性)", "Image Generation Engine": "图像生成引擎", - "Image Max Compression Size": "图像压缩后最大分辨率", - "Image Max Compression Size height": "图片最大压缩尺寸高度", - "Image Max Compression Size width": "图片最大压缩尺寸宽度", + "Image Max Compression Size": "压缩图像后最大分辨率", + "Image Max Compression Size height": "压缩图像最大尺寸高度", + "Image Max Compression Size width": "压缩图像最大尺寸宽度", "Image Prompt Generation": "图像提示词生成", "Image Prompt Generation Prompt": "用于生成图像提示词的提示词", "Image Settings": "图像设置", @@ -846,7 +846,7 @@ "Input Variables": "插入变量", "Insert": "插入", "Insert Follow-Up Prompt to Input": "回填追问提示词到输入框", - "Insert Prompt as Rich Text": "以富文本的格式回填提示词", + "Insert Prompt as Rich Text": "以富文本的形式回填提示词", "Insert Suggestion Prompt to Input": "回填推荐提示词到输入框", "Install from Github URL": "从 Github URL 安装", "Instant Auto-Send After Voice Transcription": "语音转录文字后即时自动发送", @@ -1144,7 +1144,7 @@ "Pipelines Valves": "Pipeline 配置项", "Plain text (.md)": "纯文本文档(.md)", "Plain text (.txt)": "纯文本文档 (.txt)", - "Playground": "AI 对话游乐场", + "Playground": "AI 对话探索区", "Playwright Timeout (ms)": "Playwright 超时时间 (ms)", "Playwright WebSocket URL": "Playwright WebSocket URL", "Please carefully review the following warnings:": "请仔细阅读以下警告信息:", @@ -1172,7 +1172,7 @@ "Profile": "个人资料", "Prompt": "提示词", "Prompt (e.g. Tell me a fun fact about the Roman Empire)": "提示词(例如:给我讲一则罗马帝国的趣事)", - "Prompt Autocompletion": "提示词自动补全", + "Prompt Autocompletion": "自动补全提示词", "Prompt Content": "提示词内容", "Prompt created successfully": "提示词创建成功", "Prompt suggestions": "提示词建议", @@ -1203,7 +1203,7 @@ "Refer to yourself as \"User\" (e.g., \"User is learning Spanish\")": "使用\"User\" (用户) 来指代自己(例如:“User 正在学习西班牙语”)", "Refused when it shouldn't have": "拒绝了我的要求", "Regenerate": "重新生成", - "Regenerate Menu": "重新生成前显示菜单", + "Regenerate Menu": "显示重新生成选项菜单", "Reindex": "重建索引", "Reindex Knowledge Base Vectors": "重建知识库向量索引", "Release Notes": "更新日志", @@ -1257,10 +1257,10 @@ "Save Tag": "保存标签", "Saved": "已保存", "Saving chat logs directly to your browser's storage is no longer supported. Please take a moment to download and delete your chat logs by clicking the button below. Don't worry, you can easily re-import your chat logs to the backend through": "我们不再支持将对话记录直接保存到浏览器的存储空间。请点击下面的按钮下载并删除您的对话记录。别担心,您可以轻松将对话记录重新导入到后台。", - "Scroll On Branch Change": "切换对话分支时滚动到最新回复", + "Scroll On Branch Change": "切换对话分支时滚动到最新回答", "Search": "搜索", "Search a model": "搜索模型", - "Search all emojis": "搜索 Emoji 表情符号", + "Search all emojis": "搜索 Emoji", "Search Base": "搜索库", "Search Chats": "搜索对话", "Search Collection": "搜索内容", @@ -1362,7 +1362,7 @@ "Sharing Permissions": "共享权限", "Shortcuts with an asterisk (*) are situational and only active under specific conditions.": "带星号 (*) 的快捷键受场景限制,仅在特定条件下生效。", "Show": "显示", - "Show \"What's New\" modal on login": "在登录时显示“更新内容”弹窗", + "Show \"What's New\" modal on login": "版本更新后首次登录时显示“新功能介绍”弹窗", "Show Admin Details in Account Pending Overlay": "在待激活用户的界面中显示管理员邮箱等详细信息", "Show All": "显示全部", "Show Formatting Toolbar": "显示文本格式工具栏", @@ -1412,7 +1412,7 @@ "Strip existing OCR text from the PDF and re-run OCR. Ignored if Force OCR is enabled. Defaults to False.": "清除 PDF 中现有的文字识别内容并重新识别文字。若启用“强制文字识别”则此设置无效。默认为关闭", "STT Model": "语音转文本模型", "STT Settings": "语音转文本设置", - "Stylized PDF Export": "风格化 PDF 导出", + "Stylized PDF Export": "美化 PDF 导出", "Subtitle (e.g. about the Roman Empire)": "副标题(例如:聊聊罗马帝国)", "Success": "成功", "Successfully imported {{userCount}} users.": "成功导入 {{userCount}} 个用户。", @@ -1469,7 +1469,7 @@ "Theme": "主题", "Thinking...": "正在思考...", "This action cannot be undone. Do you wish to continue?": "此操作无法撤销。您确认要继续吗?", - "This channel was created on {{createdAt}}. This is the very beginning of the {{channelName}} channel.": "此频道创建于{{createdAt}},这里是{{channelName}}频道的开始", + "This channel was created on {{createdAt}}. This is the very beginning of the {{channelName}} channel.": "欢迎来到 {{channelName}},本频道创建于 {{createdAt}}。", "This chat won't appear in history and your messages will not be saved.": "此对话将不会出现在历史记录中,且您的消息不会被保存", "This ensures that your valuable conversations are securely saved to your backend database. Thank you!": "这将确保您宝贵的对话数据被安全地保存到后台数据库中。谢谢!", "This feature is experimental and may be modified or discontinued without notice.": "此功能为实验性功能,可能会在未经通知的情况下修改或停用。", @@ -1512,7 +1512,7 @@ "To select actions here, add them to the \"Functions\" workspace first.": "如需在这里选择操作,请先将其添加到工作空间中的“函数”", "To select filters here, add them to the \"Functions\" workspace first.": "如需在这里选择过滤器,请先将其添加到工作空间中的“函数”", "To select toolkits here, add them to the \"Tools\" workspace first.": "如需在这里选择工具包,请先将其添加到工作空间中的“工具”", - "Toast notifications for new updates": "更新后弹窗提示更新内容", + "Toast notifications for new updates": "检测到新版本时显示更新通知", "Today": "今天", "Toggle search": "切换搜索", "Toggle settings": "切换设置", @@ -1623,7 +1623,7 @@ "Web Loader Engine": "网页加载引擎", "Web Search": "联网搜索", "Web Search Engine": "联网搜索引擎", - "Web Search in Chat": "对话中的联网搜索", + "Web Search in Chat": "在对话时进行联网搜索", "Web Search Query Generation": "联网搜索关键词生成", "Webhook URL": "Webhook URL", "WebUI Settings": "WebUI 设置", From cba2e44de4b067a56d2451dca78e8ddd43cf0696 Mon Sep 17 00:00:00 2001 From: Shirasawa <764798966@qq.com> Date: Fri, 12 Sep 2025 17:01:08 +0800 Subject: [PATCH 0200/1079] i18n: improve zh-TW translation --- src/lib/i18n/locales/zh-TW/translation.json | 32 ++++++++++----------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/lib/i18n/locales/zh-TW/translation.json b/src/lib/i18n/locales/zh-TW/translation.json index 220cedfb18..0a5b4c39bc 100644 --- a/src/lib/i18n/locales/zh-TW/translation.json +++ b/src/lib/i18n/locales/zh-TW/translation.json @@ -14,13 +14,13 @@ "{{COUNT}} extracted lines": "已擷取 {{COUNT}} 行", "{{COUNT}} hidden lines": "已隱藏 {{COUNT}} 行", "{{COUNT}} Replies": "{{COUNT}} 回覆", - "{{COUNT}} Sources": "", + "{{COUNT}} Sources": "{{COUNT}} 個來源", "{{COUNT}} words": "{{COUNT}} 個詞", "{{model}} download has been canceled": "已取消模型 {{model}} 的下載", "{{user}}'s Chats": "{{user}} 的對話", "{{webUIName}} Backend Required": "需要提供 {{webUIName}} 後端", "*Prompt node ID(s) are required for image generation": "* 圖片生成需要提示詞節點 ID", - "1 Source": "", + "1 Source": "1 個來源", "A new version (v{{LATEST_VERSION}}) is now available.": "新版本 (v{{LATEST_VERSION}}) 已釋出。", "A task model is used when performing tasks such as generating titles for chats and web search queries": "執行「產生對話標題」和「網頁搜尋查詢生成」等任務時使用的任務模型", "a user": "使用者", @@ -31,7 +31,7 @@ "Accessible to all users": "所有使用者皆可存取", "Account": "帳號", "Account Activation Pending": "帳號待啟用", - "accurate": "", + "accurate": "準確", "Accurate information": "準確資訊", "Action": "操作", "Action not found": "找不到對應的操作項目", @@ -428,9 +428,9 @@ "Displays citations in the response": "在回應中顯示引用", "Displays status updates (e.g., web search progress) in the response": "在回應中顯示進度狀態(例如:網路搜尋進度)", "Dive into knowledge": "挖掘知識", - "dlparse_v1": "", - "dlparse_v2": "", - "dlparse_v4": "", + "dlparse_v1": "dlparse_v1", + "dlparse_v2": "dlparse_v2", + "dlparse_v4": "dlparse_v4", "Do not install functions from sources you do not fully trust.": "請勿從您無法完全信任的來源安裝函式。", "Do not install tools from sources you do not fully trust.": "請勿從您無法完全信任的來源安裝工具。", "Docling": "Docling", @@ -689,7 +689,7 @@ "Failed to save models configuration": "儲存模型設定失敗", "Failed to update settings": "更新設定失敗", "Failed to upload file.": "上傳檔案失敗。", - "fast": "", + "fast": "快速", "Features": "功能", "Features Permissions": "功能權限", "February": "2 月", @@ -831,7 +831,7 @@ "Import Prompts": "匯入提示詞", "Import Tools": "匯入工具", "Important Update": "重要更新", - "In order to force OCR, performing OCR must be enabled.": "", + "In order to force OCR, performing OCR must be enabled.": "要啟用「強制執行 OCR」,必須先啟用「執行 OCR」。", "Include": "包含", "Include `--api-auth` flag when running stable-diffusion-webui": "執行 stable-diffusion-webui 時包含 `--api-auth` 參數", "Include `--api` flag when running stable-diffusion-webui": "執行 stable-diffusion-webui 時包含 `--api` 參數", @@ -1112,14 +1112,14 @@ "Password": "密碼", "Passwords do not match.": "兩次輸入的密碼不一致。", "Paste Large Text as File": "將大型文字以檔案貼上", - "PDF Backend": "", + "PDF Backend": "PDF 解析器後端", "PDF document (.pdf)": "PDF 檔案 (.pdf)", "PDF Extract Images (OCR)": "PDF 影像擷取(OCR 光學文字辨識)", "pending": "待處理", "Pending": "待處理", "Pending User Overlay Content": "待處理的使用者訊息覆蓋層內容", "Pending User Overlay Title": "待處理的使用者訊息覆蓋層標題", - "Perform OCR": "", + "Perform OCR": "執行 OCR", "Permission denied when accessing media devices": "存取媒體裝置時權限遭拒", "Permission denied when accessing microphone": "存取麥克風時權限遭拒", "Permission denied when accessing microphone: {{error}}": "存取麥克風時權限遭拒:{{error}}", @@ -1135,7 +1135,7 @@ "Pinned": "已釘選", "Pioneer insights": "先驅見解", "Pipe": "Pipe", - "Pipeline": "", + "Pipeline": "管線", "Pipeline deleted successfully": "成功刪除管線", "Pipeline downloaded successfully": "成功下載管線", "Pipelines": "管線", @@ -1184,7 +1184,7 @@ "Public": "公開", "Pull \"{{searchValue}}\" from Ollama.com": "從 Ollama.com 下載「{{searchValue}}」", "Pull a model from Ollama.com": "從 Ollama.com 下載模型", - "pypdfium2": "", + "pypdfium2": "pypdfium2", "Query Generation Prompt": "查詢生成提示詞", "Querying": "查詢中", "Quick Actions": "快速操作", @@ -1237,7 +1237,7 @@ "RESULT": "結果", "Retrieval": "檢索", "Retrieval Query Generation": "檢索查詢生成", - "Retrieved {{count}} sources": "", + "Retrieved {{count}} sources": "搜尋到 {{count}} 個來源", "Retrieved {{count}} sources_other": "搜索到 {{count}} 個來源", "Retrieved 1 source": "搜索到 1 個來源", "Rich Text Input for Chat": "使用富文字輸入對話", @@ -1397,7 +1397,7 @@ "Speech recognition error: {{error}}": "語音辨識錯誤:{{error}}", "Speech-to-Text": "語音轉文字 (STT) ", "Speech-to-Text Engine": "語音轉文字 (STT) 引擎", - "standard": "", + "standard": "標準", "Start of the channel": "頻道起點", "Start Tag": "起始標籤", "Status Updates": "顯示實時回答狀態", @@ -1426,7 +1426,7 @@ "System": "系統", "System Instructions": "系統指令", "System Prompt": "系統提示詞", - "Table Mode": "", + "Table Mode": "表格模式", "Tags": "標籤", "Tags Generation": "標籤生成", "Tags Generation Prompt": "標籤生成提示詞", @@ -1609,7 +1609,7 @@ "View Result from **{{NAME}}**": "檢視來自 **{{NAME}}** 的結果", "Visibility": "可見度", "Vision": "視覺", - "vlm": "", + "vlm": "視覺語言模型(VLM)", "Voice": "語音", "Voice Input": "語音輸入", "Voice mode": "語音模式", From fbf9b3f1bbed4d29fd2b6ad6be4a708fdc9b219a Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Fri, 12 Sep 2025 13:38:02 +0400 Subject: [PATCH 0201/1079] refac: styling --- src/lib/components/chat/MessageInput.svelte | 3 ++- src/lib/components/icons/Photo.svelte | 27 +++++++++++++-------- src/lib/components/icons/Terminal.svelte | 23 ++++++++++++++++++ 3 files changed, 42 insertions(+), 11 deletions(-) create mode 100644 src/lib/components/icons/Terminal.svelte diff --git a/src/lib/components/chat/MessageInput.svelte b/src/lib/components/chat/MessageInput.svelte index 046085beb3..e2fd03d8a5 100644 --- a/src/lib/components/chat/MessageInput.svelte +++ b/src/lib/components/chat/MessageInput.svelte @@ -70,6 +70,7 @@ import InputVariablesModal from './MessageInput/InputVariablesModal.svelte'; import Voice from '../icons/Voice.svelte'; import { getSessionUser } from '$lib/apis/auths'; + import Terminal from '../icons/Terminal.svelte'; const i18n = getContext('i18n'); export let onChange: Function = () => {}; @@ -1848,7 +1849,7 @@ ? 'm-1' : 'focus:outline-hidden rounded-full'}" > - + + > diff --git a/src/lib/components/icons/Terminal.svelte b/src/lib/components/icons/Terminal.svelte new file mode 100644 index 0000000000..be4c5d02d9 --- /dev/null +++ b/src/lib/components/icons/Terminal.svelte @@ -0,0 +1,23 @@ + + + From 042191372a52123fbe40c0d35fac946fc8a6c964 Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Fri, 12 Sep 2025 13:49:53 +0400 Subject: [PATCH 0202/1079] refac: styling --- .../components/chat/Messages/Citations.svelte | 52 +++++++++++++++---- 1 file changed, 43 insertions(+), 9 deletions(-) diff --git a/src/lib/components/chat/Messages/Citations.svelte b/src/lib/components/chat/Messages/Citations.svelte index f234b52d4c..8360fb0c95 100644 --- a/src/lib/components/chat/Messages/Citations.svelte +++ b/src/lib/components/chat/Messages/Citations.svelte @@ -1,6 +1,6 @@ - @@ -111,7 +119,7 @@
      {/if} + +{#if showCitations} +
      +
      + {#each citations as citation, idx} + + {/each} +
      +
      +{/if} From a68342d5a887e36695e21f8c2aec593b159654ff Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Fri, 12 Sep 2025 15:05:37 +0400 Subject: [PATCH 0203/1079] refac: input menu --- src/lib/components/chat/MessageInput.svelte | 245 ++++++++------ .../chat/MessageInput/InputMenu.svelte | 194 +++++------ .../chat/MessageInput/OptionsMenu.svelte | 309 ++++++++++++++++++ src/lib/components/icons/Camera.svelte | 22 ++ src/lib/components/icons/Clip.svelte | 18 + src/lib/components/icons/Component.svelte | 22 ++ src/lib/components/icons/Grid.svelte | 22 ++ src/lib/components/icons/PlusAlt.svelte | 15 + src/lib/components/icons/Union.svelte | 22 ++ 9 files changed, 646 insertions(+), 223 deletions(-) create mode 100644 src/lib/components/chat/MessageInput/OptionsMenu.svelte create mode 100644 src/lib/components/icons/Camera.svelte create mode 100644 src/lib/components/icons/Clip.svelte create mode 100644 src/lib/components/icons/Component.svelte create mode 100644 src/lib/components/icons/Grid.svelte create mode 100644 src/lib/components/icons/PlusAlt.svelte create mode 100644 src/lib/components/icons/Union.svelte diff --git a/src/lib/components/chat/MessageInput.svelte b/src/lib/components/chat/MessageInput.svelte index e2fd03d8a5..7e1a765750 100644 --- a/src/lib/components/chat/MessageInput.svelte +++ b/src/lib/components/chat/MessageInput.svelte @@ -71,6 +71,9 @@ import Voice from '../icons/Voice.svelte'; import { getSessionUser } from '$lib/apis/auths'; import Terminal from '../icons/Terminal.svelte'; + import OptionsMenu from './MessageInput/OptionsMenu.svelte'; + import Component from '../icons/Component.svelte'; + import PlusAlt from '../icons/PlusAlt.svelte'; const i18n = getContext('i18n'); export let onChange: Function = () => {}; @@ -1657,7 +1660,6 @@
      - +
      - {#if $_user && (showToolsButton || (toggleFilters && toggleFilters.length > 0) || showWebSearchButton || showImageGenerationButton || showCodeInterpreterButton)} +
      + + { + await tick(); + + const chatInput = document.getElementById('chat-input'); + chatInput?.focus(); + }} + >
      + class="bg-transparent hover:bg-gray-100 text-gray-700 dark:text-white dark:hover:bg-gray-800 rounded-full size-8 flex justify-center items-center outline-hidden focus:outline-hidden" + > + +
      +
      -
      - {#if showToolsButton} - + {#if showToolsButton} + + - - {/if} + + {toolServers.length + selectedToolIds.length} + + + + {/if} - {#each toggleFilters as filter, filterIdx (filter.id)} + {#each selectedFilterIds as filterId} + {@const filter = toggleFilters.find((f) => f.id === filterId)} + {#if filter} - - {/each} - {#if showWebSearchButton} - - {/if} + {/each} - {#if showImageGenerationButton} - - - - {/if} - {#if showCodeInterpreterButton} - - + + {/if} + + {#if imageGenerationEnabled} + + - - {/if} -
      - {/if} + + + + + {/if} + + {#if codeInterpreterEnabled} + + + + {/if} +
      diff --git a/src/lib/components/chat/MessageInput/InputMenu.svelte b/src/lib/components/chat/MessageInput/InputMenu.svelte index 351c882388..6192e22180 100644 --- a/src/lib/components/chat/MessageInput/InputMenu.svelte +++ b/src/lib/components/chat/MessageInput/InputMenu.svelte @@ -10,14 +10,10 @@ import Dropdown from '$lib/components/common/Dropdown.svelte'; import Tooltip from '$lib/components/common/Tooltip.svelte'; - import DocumentArrowUpSolid from '$lib/components/icons/DocumentArrowUpSolid.svelte'; - import Switch from '$lib/components/common/Switch.svelte'; - import GlobeAltSolid from '$lib/components/icons/GlobeAltSolid.svelte'; - import WrenchSolid from '$lib/components/icons/WrenchSolid.svelte'; - import CameraSolid from '$lib/components/icons/CameraSolid.svelte'; - import PhotoSolid from '$lib/components/icons/PhotoSolid.svelte'; - import CommandLineSolid from '$lib/components/icons/CommandLineSolid.svelte'; - import Spinner from '$lib/components/common/Spinner.svelte'; + import DocumentArrowUp from '$lib/components/icons/DocumentArrowUp.svelte'; + import Camera from '$lib/components/icons/Camera.svelte'; + import Note from '$lib/components/icons/Note.svelte'; + import Clip from '$lib/components/icons/Clip.svelte'; const i18n = getContext('i18n'); @@ -35,34 +31,13 @@ export let onClose: Function; - let tools = null; let show = false; - let showAllTools = false; - - $: if (show) { - init(); - } let fileUploadEnabled = true; $: fileUploadEnabled = fileUploadCapableModels.length === selectedModels.length && ($user?.role === 'admin' || $user?.permissions?.chat?.file_upload); - const init = async () => { - await _tools.set(await getTools(localStorage.token)); - if ($_tools) { - tools = $_tools.reduce((a, tool, i, arr) => { - a[tool.id] = { - name: tool.name, - description: tool.meta.description, - enabled: selectedToolIds.includes(tool.id) - }; - return a; - }, {}); - selectedToolIds = selectedToolIds.filter((id) => $_tools?.some((tool) => tool.id === id)); - } - }; - const detectMobile = () => { const userAgent = navigator.userAgent || navigator.vendor || window.opera; return /android|iphone|ipad|ipod|windows phone/i.test(userAgent); @@ -101,86 +76,36 @@
      - {#if tools} - {#if Object.keys(tools).length > 0} -
      - {#each Object.keys(tools) as toolId} - - {/each} -
      - {#if Object.keys(tools).length > 3} - - {/if} -
      - {/if} - {:else} -
      - -
      - -
      - {/if} +
      {$i18n.t('Upload Files')}
      + + { @@ -208,7 +133,7 @@ } }} > - +
      {$i18n.t('Capture')}
      @@ -222,24 +147,63 @@ className="w-full" > { if (fileUploadEnabled) { - uploadFilesHandler(); + if (!detectMobile()) { + screenCaptureHandler(); + } else { + const cameraInputElement = document.getElementById('camera-input'); + + if (cameraInputElement) { + cameraInputElement.click(); + } + } } }} > - -
      {$i18n.t('Upload Files')}
      + +
      {$i18n.t('Attach Notes')}
      +
      + + + + { + if (fileUploadEnabled) { + if (!detectMobile()) { + screenCaptureHandler(); + } else { + const cameraInputElement = document.getElementById('camera-input'); + + if (cameraInputElement) { + cameraInputElement.click(); + } + } + } + }} + > + +
      {$i18n.t('Attach Knowledge')}
      {#if fileUploadEnabled} {#if $config?.features?.enable_google_drive_integration} { uploadGoogleDriveHandler(); }} @@ -277,7 +241,7 @@ {#if $config?.features?.enable_onedrive_integration} { uploadOneDriveHandler('personal'); }} @@ -381,7 +345,7 @@
      {$i18n.t('Microsoft OneDrive (personal)')}
      { uploadOneDriveHandler('organizations'); }} diff --git a/src/lib/components/chat/MessageInput/OptionsMenu.svelte b/src/lib/components/chat/MessageInput/OptionsMenu.svelte new file mode 100644 index 0000000000..d1928c913c --- /dev/null +++ b/src/lib/components/chat/MessageInput/OptionsMenu.svelte @@ -0,0 +1,309 @@ + + + { + if (e.detail === false) { + onClose(); + } + }} +> + + + + +
      + + {#if toggleFilters && toggleFilters.length > 0} + {#each toggleFilters as filter, filterIdx (filter.id)} + + + + {/each} + {/if} + + {#if showWebSearchButton} + + + + {/if} + + {#if showImageGenerationButton} + + + + {/if} + + {#if showCodeInterpreterButton} + + + + {/if} + + {#if tools} +
      + + {#if Object.keys(tools).length > 0} +
      + {#each Object.keys(tools) as toolId} + + {/each} +
      + {#if Object.keys(tools).length > 3} + + {/if} + {/if} + {:else} +
      + +
      + {/if} +
      +
      +
      diff --git a/src/lib/components/icons/Camera.svelte b/src/lib/components/icons/Camera.svelte new file mode 100644 index 0000000000..a553475fa1 --- /dev/null +++ b/src/lib/components/icons/Camera.svelte @@ -0,0 +1,22 @@ + + + diff --git a/src/lib/components/icons/Clip.svelte b/src/lib/components/icons/Clip.svelte new file mode 100644 index 0000000000..d3c89be163 --- /dev/null +++ b/src/lib/components/icons/Clip.svelte @@ -0,0 +1,18 @@ + + + diff --git a/src/lib/components/icons/Component.svelte b/src/lib/components/icons/Component.svelte new file mode 100644 index 0000000000..cafc7058ec --- /dev/null +++ b/src/lib/components/icons/Component.svelte @@ -0,0 +1,22 @@ + + + diff --git a/src/lib/components/icons/Grid.svelte b/src/lib/components/icons/Grid.svelte new file mode 100644 index 0000000000..8846f03107 --- /dev/null +++ b/src/lib/components/icons/Grid.svelte @@ -0,0 +1,22 @@ + + + diff --git a/src/lib/components/icons/PlusAlt.svelte b/src/lib/components/icons/PlusAlt.svelte new file mode 100644 index 0000000000..895fe0b763 --- /dev/null +++ b/src/lib/components/icons/PlusAlt.svelte @@ -0,0 +1,15 @@ + + + diff --git a/src/lib/components/icons/Union.svelte b/src/lib/components/icons/Union.svelte new file mode 100644 index 0000000000..71953329f4 --- /dev/null +++ b/src/lib/components/icons/Union.svelte @@ -0,0 +1,22 @@ + + + From ca853ca4656180487afcd84230d214f91db52533 Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Fri, 12 Sep 2025 15:06:11 +0400 Subject: [PATCH 0204/1079] refac/enh: sort toggle filter by default --- src/lib/components/chat/MessageInput/OptionsMenu.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/components/chat/MessageInput/OptionsMenu.svelte b/src/lib/components/chat/MessageInput/OptionsMenu.svelte index d1928c913c..cd9587a9ee 100644 --- a/src/lib/components/chat/MessageInput/OptionsMenu.svelte +++ b/src/lib/components/chat/MessageInput/OptionsMenu.svelte @@ -88,7 +88,7 @@ transition={flyAndScale} > {#if toggleFilters && toggleFilters.length > 0} - {#each toggleFilters as filter, filterIdx (filter.id)} + {#each toggleFilters.sort( (a, b) => a.name.localeCompare( b.name, undefined, { sensitivity: 'base' } ) ) as filter, filterIdx (filter.id)} @@ -1770,7 +1770,7 @@ {#each selectedFilterIds as filterId} {@const filter = toggleFilters.find((f) => f.id === filterId)} {#if filter} - + + {:else} +
      + +
      + {/if} + + {#if showAllTools} + {#each Object.keys(tools) as toolId} + + {/each} + {:else} + {#if toggleFilters && toggleFilters.length > 0} + {#each toggleFilters.sort( (a, b) => a.name.localeCompare( b.name, undefined, { sensitivity: 'base' } ) ) as filter, filterIdx (filter.id)} + + + + {/each} + {/if} + + {#if showWebSearchButton} + + + {/if} + + {#if showImageGenerationButton} + + + + {/if} + + {#if showCodeInterpreterButton} + + - {/each} - {/if} - - {#if showWebSearchButton} - - - - {/if} - - {#if showImageGenerationButton} - - - - {/if} - - {#if showCodeInterpreterButton} - - - - {/if} - - {#if tools} -
      - - {#if Object.keys(tools).length > 0} -
      - {#each Object.keys(tools) as toolId} - - {/each} -
      - {#if Object.keys(tools).length > 3} - - {/if} {/if} - {:else} -
      - -
      {/if}
      From 136972ccf077998ab33e9b8d734a4ebf3c15fcdc Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Fri, 12 Sep 2025 15:54:42 +0400 Subject: [PATCH 0207/1079] refac: styling --- .../chat/MessageInput/InputMenu.svelte | 16 ++++++------- .../chat/MessageInput/OptionsMenu.svelte | 12 +++++----- src/lib/components/common/Switch.svelte | 4 ++-- src/lib/components/icons/Keyboard.svelte | 12 ++++------ .../components/layout/Sidebar/ChatMenu.svelte | 24 +++++++++---------- .../components/layout/Sidebar/UserMenu.svelte | 20 ++++++++-------- 6 files changed, 43 insertions(+), 45 deletions(-) diff --git a/src/lib/components/chat/MessageInput/InputMenu.svelte b/src/lib/components/chat/MessageInput/InputMenu.svelte index 6192e22180..2a58fffa9d 100644 --- a/src/lib/components/chat/MessageInput/InputMenu.svelte +++ b/src/lib/components/chat/MessageInput/InputMenu.svelte @@ -92,7 +92,7 @@ className="w-full" > { @@ -116,7 +116,7 @@ className="w-full" > { @@ -147,7 +147,7 @@ className="w-full" > { @@ -178,7 +178,7 @@ className="w-full" > { @@ -203,7 +203,7 @@ {#if fileUploadEnabled} {#if $config?.features?.enable_google_drive_integration} { uploadGoogleDriveHandler(); }} @@ -241,7 +241,7 @@ {#if $config?.features?.enable_onedrive_integration} { uploadOneDriveHandler('personal'); }} @@ -345,7 +345,7 @@
      {$i18n.t('Microsoft OneDrive (personal)')}
      { uploadOneDriveHandler('organizations'); }} diff --git a/src/lib/components/chat/MessageInput/OptionsMenu.svelte b/src/lib/components/chat/MessageInput/OptionsMenu.svelte index f650dddf9a..3345e96687 100644 --- a/src/lib/components/chat/MessageInput/OptionsMenu.svelte +++ b/src/lib/components/chat/MessageInput/OptionsMenu.svelte @@ -93,7 +93,7 @@ > {#if tools} + {/if} + + {/if} {:else}
      From 6b69c4da0fb9329ccf7024483960e070cf52ccab Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Fri, 12 Sep 2025 20:31:57 +0400 Subject: [PATCH 0210/1079] refac/enh: commands ui --- package-lock.json | 12 +- package.json | 1 + .../components/channel/MessageInput.svelte | 49 +- src/lib/components/chat/Chat.svelte | 1 - src/lib/components/chat/MessageInput.svelte | 699 +++++++++--------- .../MessageInput/CommandSuggestionList.svelte | 163 ++++ .../MessageInput/Commands/Knowledge.svelte | 341 ++++----- .../chat/MessageInput/Commands/Models.svelte | 117 +-- .../chat/MessageInput/Commands/Prompts.svelte | 153 ++-- src/lib/components/chat/Navbar.svelte | 45 +- src/lib/components/common/FileItem.svelte | 34 +- .../components/common/RichTextInput.svelte | 44 +- .../RichTextInput/FormattingButtons.svelte | 2 +- .../common/RichTextInput/MentionList.svelte | 85 +++ .../common/RichTextInput/commands.ts | 26 + .../common/RichTextInput/suggestions.ts | 69 ++ src/lib/components/icons/Database.svelte | 16 + src/lib/components/icons/DocumentPage.svelte | 26 + src/lib/components/icons/Youtube.svelte | 16 + 19 files changed, 1052 insertions(+), 847 deletions(-) create mode 100644 src/lib/components/chat/MessageInput/CommandSuggestionList.svelte create mode 100644 src/lib/components/common/RichTextInput/MentionList.svelte create mode 100644 src/lib/components/common/RichTextInput/commands.ts create mode 100644 src/lib/components/common/RichTextInput/suggestions.ts create mode 100644 src/lib/components/icons/Database.svelte create mode 100644 src/lib/components/icons/DocumentPage.svelte create mode 100644 src/lib/components/icons/Youtube.svelte diff --git a/package-lock.json b/package-lock.json index 48122812b8..d2fd62c282 100644 --- a/package-lock.json +++ b/package-lock.json @@ -37,6 +37,7 @@ "@tiptap/extensions": "^3.0.7", "@tiptap/pm": "^3.0.7", "@tiptap/starter-kit": "^3.0.7", + "@tiptap/suggestion": "^3.4.2", "@xyflow/svelte": "^0.1.19", "async": "^3.2.5", "bits-ui": "^0.21.15", @@ -3856,18 +3857,17 @@ } }, "node_modules/@tiptap/suggestion": { - "version": "3.0.9", - "resolved": "https://registry.npmjs.org/@tiptap/suggestion/-/suggestion-3.0.9.tgz", - "integrity": "sha512-irthqfUybezo3IwR6AXvyyTOtkzwfvvst58VXZtTnR1nN6NEcrs3TQoY3bGKGbN83bdiquKh6aU2nLnZfAhoXg==", + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/@tiptap/suggestion/-/suggestion-3.4.2.tgz", + "integrity": "sha512-sljtfiDtdAsbPOwrXrFGf64D6sXUjeU3Iz5v3TvN7TVJKozkZ/gaMkPRl+WC1CGwC6BnzQVDBEEa1e+aApV0mA==", "license": "MIT", - "peer": true, "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/core": "^3.0.9", - "@tiptap/pm": "^3.0.9" + "@tiptap/core": "^3.4.2", + "@tiptap/pm": "^3.4.2" } }, "node_modules/@tiptap/y-tiptap": { diff --git a/package.json b/package.json index 054baf39c6..19c897cb48 100644 --- a/package.json +++ b/package.json @@ -81,6 +81,7 @@ "@tiptap/extensions": "^3.0.7", "@tiptap/pm": "^3.0.7", "@tiptap/starter-kit": "^3.0.7", + "@tiptap/suggestion": "^3.4.2", "@xyflow/svelte": "^0.1.19", "async": "^3.2.5", "bits-ui": "^0.21.15", diff --git a/src/lib/components/channel/MessageInput.svelte b/src/lib/components/channel/MessageInput.svelte index fc85ea2aa1..c7ca93b902 100644 --- a/src/lib/components/channel/MessageInput.svelte +++ b/src/lib/components/channel/MessageInput.svelte @@ -753,53 +753,10 @@ e = e.detail.event; const isCtrlPressed = e.ctrlKey || e.metaKey; // metaKey is for Cmd key on Mac - const commandsContainerElement = document.getElementById('commands-container'); + const suggestionsContainerElement = + document.getElementById('suggestions-container'); - if (commandsContainerElement) { - if (commandsContainerElement && e.key === 'ArrowUp') { - e.preventDefault(); - commandsElement.selectUp(); - - const commandOptionButton = [ - ...document.getElementsByClassName('selected-command-option-button') - ]?.at(-1); - 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 === '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 (!suggestionsContainerElement) { if ( !$mobile || !( diff --git a/src/lib/components/chat/Chat.svelte b/src/lib/components/chat/Chat.svelte index faf4b30861..fd64c0226a 100644 --- a/src/lib/components/chat/Chat.svelte +++ b/src/lib/components/chat/Chat.svelte @@ -2259,7 +2259,6 @@ bind:selectedModels shareEnabled={!!history.currentId} {initNewChat} - showBanners={!showCommands} archiveChatHandler={() => {}} {moveChatHandler} onSaveTempChat={async () => { diff --git a/src/lib/components/chat/MessageInput.svelte b/src/lib/components/chat/MessageInput.svelte index 28128d5433..d24c5a5d2b 100644 --- a/src/lib/components/chat/MessageInput.svelte +++ b/src/lib/components/chat/MessageInput.svelte @@ -76,6 +76,10 @@ import { KokoroWorker } from '$lib/workers/KokoroWorker'; + import { getSuggestionRenderer } from '../common/RichTextInput/suggestions'; + import MentionList from '../common/RichTextInput/MentionList.svelte'; + import CommandSuggestionList from './MessageInput/CommandSuggestionList.svelte'; + const i18n = getContext('i18n'); export let onChange: Function = () => {}; @@ -428,9 +432,9 @@ }; let command = ''; - export let showCommands = false; $: showCommands = ['/', '#', '@'].includes(command?.charAt(0)) || '\\#' === command?.slice(0, 2); + let suggestions = null; let showTools = false; @@ -845,6 +849,115 @@ }; onMount(async () => { + suggestions = [ + { + char: '@', + render: getSuggestionRenderer(CommandSuggestionList, { + i18n, + onSelect: (e) => { + const { type, data } = e; + + if (type === 'model') { + atSelectedModel = data; + } + + document.getElementById('chat-input')?.focus(); + }, + + insertTextHandler: insertTextAtCursor, + onUpload: (e) => { + const { type, data } = e; + + if (type === 'file') { + if (files.find((f) => f.id === data.id)) { + return; + } + files = [ + ...files, + { + ...data, + status: 'processed' + } + ]; + } else { + dispatch('upload', e); + } + } + }) + }, + { + char: '/', + render: getSuggestionRenderer(CommandSuggestionList, { + i18n, + onSelect: (e) => { + const { type, data } = e; + + if (type === 'model') { + atSelectedModel = data; + } + + document.getElementById('chat-input')?.focus(); + }, + + insertTextHandler: insertTextAtCursor, + onUpload: (e) => { + const { type, data } = e; + + if (type === 'file') { + if (files.find((f) => f.id === data.id)) { + return; + } + files = [ + ...files, + { + ...data, + status: 'processed' + } + ]; + } else { + dispatch('upload', e); + } + } + }) + }, + { + char: '#', + render: getSuggestionRenderer(CommandSuggestionList, { + i18n, + onSelect: (e) => { + const { type, data } = e; + + if (type === 'model') { + atSelectedModel = data; + } + + document.getElementById('chat-input')?.focus(); + }, + + insertTextHandler: insertTextAtCursor, + onUpload: (e) => { + const { type, data } = e; + + if (type === 'file') { + if (files.find((f) => f.id === data.id)) { + return; + } + files = [ + ...files, + { + ...data, + status: 'processed' + } + ]; + } else { + dispatch('upload', e); + } + } + }) + } + ]; + + console.log(suggestions); loaded = true; window.setTimeout(() => { @@ -929,78 +1042,6 @@
      {/if}
      - -
      - {#if atSelectedModel !== undefined} -
      -
      -
      - model profile model.id === atSelectedModel.id)?.info?.meta - ?.profile_image_url ?? - ($i18n.language === 'dg-DG' - ? `${WEBUI_BASE_URL}/doge.png` - : `${WEBUI_BASE_URL}/static/favicon.png`)} - /> -
      - {$i18n.t('Talk to model')}: - {atSelectedModel.name} -
      -
      -
      - -
      -
      -
      - {/if} - - { - const { type, data } = e; - - if (type === 'file') { - if (files.find((f) => f.id === data.id)) { - return; - } - files = [ - ...files, - { - ...data, - status: 'processed' - } - ]; - } else { - dispatch('upload', e); - } - }} - onSelect={(e) => { - const { type, data } = e; - - if (type === 'model') { - atSelectedModel = data; - } - - document.getElementById('chat-input')?.focus(); - }} - /> -
      @@ -1066,6 +1107,38 @@ class="flex-1 flex flex-col relative w-full shadow-lg rounded-3xl border border-gray-50 dark:border-gray-850 hover:border-gray-100 focus-within:border-gray-100 hover:dark:border-gray-800 focus-within:dark:border-gray-800 transition px-1 bg-white/90 dark:bg-gray-400/5 dark:text-gray-100" dir={$settings?.chatDirection ?? 'auto'} > + {#if atSelectedModel !== undefined} +
      +
      +
      + model profile model.id === atSelectedModel.id)?.info?.meta + ?.profile_image_url ?? + ($i18n.language === 'dg-DG' + ? `${WEBUI_BASE_URL}/doge.png` + : `${WEBUI_BASE_URL}/static/favicon.png`)} + /> +
      + {atSelectedModel.name} +
      +
      +
      + +
      +
      +
      + {/if} + {#if files.length > 0}
      {#each files as file, fileIdx} @@ -1075,7 +1148,7 @@ {#if atSelectedModel ? visionCapableModels.length === 0 : selectedModels.length !== visionCapableModels.length} { // Remove from UI state @@ -1161,250 +1235,201 @@ class="scrollbar-hidden rtl:text-right ltr:text-left bg-transparent dark:text-gray-100 outline-hidden w-full pt-2.5 pb-[5px] px-1 resize-none h-fit max-h-80 overflow-auto" id="chat-input-container" > - {#key $settings?.showFormattingToolbar ?? false} - { - prompt = e.md; - command = getCommand(); - }} - json={true} - messageInput={true} - showFormattingToolbar={$settings?.showFormattingToolbar ?? false} - floatingMenuPlacement={'top-start'} - insertPromptAsRichText={$settings?.insertPromptAsRichText ?? false} - shiftEnter={!($settings?.ctrlEnterToSend ?? false) && - (!$mobile || - !( - 'ontouchstart' in window || - navigator.maxTouchPoints > 0 || - navigator.msMaxTouchPoints > 0 - ))} - placeholder={placeholder ? placeholder : $i18n.t('Send a Message')} - largeTextAsFile={($settings?.largeTextAsFile ?? false) && !shiftKey} - autocomplete={$config?.features?.enable_autocomplete_generation && - ($settings?.promptAutocomplete ?? false)} - generateAutoCompletion={async (text) => { - if (selectedModelIds.length === 0 || !selectedModelIds.at(0)) { - toast.error($i18n.t('Please select a model first.')); - } - - const res = await generateAutoCompletion( - localStorage.token, - selectedModelIds.at(0), - text, - history?.currentId - ? createMessagesList(history, history.currentId) - : null - ).catch((error) => { - console.log(error); - - return null; - }); - - console.log(res); - return res; - }} - oncompositionstart={() => (isComposing = true)} - oncompositionend={(e) => { - compositionEndedAt = e.timeStamp; - isComposing = false; - }} - on:keydown={async (e) => { - e = e.detail.event; - - const isCtrlPressed = e.ctrlKey || e.metaKey; // metaKey is for Cmd key on Mac - const commandsContainerElement = - document.getElementById('commands-container'); - - if (e.key === 'Escape') { - stopResponse(); - } - - // Command/Ctrl + Shift + Enter to submit a message pair - if (isCtrlPressed && e.key === 'Enter' && e.shiftKey) { - e.preventDefault(); - createMessagePair(prompt); - } - - // Check if Ctrl + R is pressed - if (prompt === '' && isCtrlPressed && e.key.toLowerCase() === 'r') { - e.preventDefault(); - console.log('regenerate'); - - const regenerateButton = [ - ...document.getElementsByClassName('regenerate-response-button') - ]?.at(-1); - - regenerateButton?.click(); - } - - if (prompt === '' && e.key == 'ArrowUp') { - e.preventDefault(); - - const userMessageElement = [ - ...document.getElementsByClassName('user-message') - ]?.at(-1); - - if (userMessageElement) { - userMessageElement.scrollIntoView({ block: 'center' }); - const editButton = [ - ...document.getElementsByClassName('edit-user-message-button') - ]?.at(-1); - - editButton?.click(); - } - } - - if (commandsContainerElement) { - if (commandsContainerElement && e.key === 'ArrowUp') { - e.preventDefault(); - commandsElement.selectUp(); - - const commandOptionButton = [ - ...document.getElementsByClassName( - 'selected-command-option-button' - ) - ]?.at(-1); - 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 === '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 || + {#if suggestions} + {#key $settings?.showFormattingToolbar ?? false} + { + prompt = e.md; + command = getCommand(); + }} + json={true} + messageInput={true} + showFormattingToolbar={$settings?.showFormattingToolbar ?? false} + floatingMenuPlacement={'top-start'} + insertPromptAsRichText={$settings?.insertPromptAsRichText ?? false} + shiftEnter={!($settings?.ctrlEnterToSend ?? false) && + (!$mobile || !( 'ontouchstart' in window || navigator.maxTouchPoints > 0 || navigator.msMaxTouchPoints > 0 - ) - ) { - if (inOrNearComposition(e)) { - return; - } + ))} + placeholder={placeholder ? placeholder : $i18n.t('Send a Message')} + largeTextAsFile={($settings?.largeTextAsFile ?? false) && !shiftKey} + autocomplete={$config?.features?.enable_autocomplete_generation && + ($settings?.promptAutocomplete ?? false)} + generateAutoCompletion={async (text) => { + if (selectedModelIds.length === 0 || !selectedModelIds.at(0)) { + toast.error($i18n.t('Please select a model first.')); + } - // Uses keyCode '13' for Enter key for chinese/japanese keyboards. - // - // Depending on the user's settings, it will send the message - // either when Enter is pressed or when Ctrl+Enter is pressed. - const enterPressed = - ($settings?.ctrlEnterToSend ?? false) - ? (e.key === 'Enter' || e.keyCode === 13) && isCtrlPressed - : (e.key === 'Enter' || e.keyCode === 13) && !e.shiftKey; + const res = await generateAutoCompletion( + localStorage.token, + selectedModelIds.at(0), + text, + history?.currentId + ? createMessagesList(history, history.currentId) + : null + ).catch((error) => { + console.log(error); - if (enterPressed) { - e.preventDefault(); - if (prompt !== '' || files.length > 0) { - dispatch('submit', prompt); - } + return null; + }); + + console.log(res); + return res; + }} + {suggestions} + oncompositionstart={() => (isComposing = true)} + oncompositionend={(e) => { + compositionEndedAt = e.timeStamp; + isComposing = false; + }} + on:keydown={async (e) => { + e = e.detail.event; + + const isCtrlPressed = e.ctrlKey || e.metaKey; // metaKey is for Cmd key on Mac + const suggestionsContainerElement = + document.getElementById('suggestions-container'); + + if (e.key === 'Escape') { + stopResponse(); + } + + // Command/Ctrl + Shift + Enter to submit a message pair + if (isCtrlPressed && e.key === 'Enter' && e.shiftKey) { + e.preventDefault(); + createMessagePair(prompt); + } + + // Check if Ctrl + R is pressed + if (prompt === '' && isCtrlPressed && e.key.toLowerCase() === 'r') { + e.preventDefault(); + console.log('regenerate'); + + const regenerateButton = [ + ...document.getElementsByClassName('regenerate-response-button') + ]?.at(-1); + + regenerateButton?.click(); + } + + if (prompt === '' && e.key == 'ArrowUp') { + e.preventDefault(); + + const userMessageElement = [ + ...document.getElementsByClassName('user-message') + ]?.at(-1); + + if (userMessageElement) { + userMessageElement.scrollIntoView({ block: 'center' }); + const editButton = [ + ...document.getElementsByClassName('edit-user-message-button') + ]?.at(-1); + + editButton?.click(); } } - } - if (e.key === 'Escape') { - console.log('Escape'); - atSelectedModel = undefined; - selectedToolIds = []; - selectedFilterIds = []; - - webSearchEnabled = false; - imageGenerationEnabled = false; - codeInterpreterEnabled = false; - } - }} - on:paste={async (e) => { - e = e.detail.event; - console.log(e); - - const clipboardData = e.clipboardData || window.clipboardData; - - if (clipboardData && clipboardData.items) { - for (const item of clipboardData.items) { - if (item.type.indexOf('image') !== -1) { - const blob = item.getAsFile(); - const reader = new FileReader(); - - reader.onload = function (e) { - files = [ - ...files, - { - type: 'image', - url: `${e.target.result}` - } - ]; - }; - - reader.readAsDataURL(blob); - } else if (item?.kind === 'file') { - const file = item.getAsFile(); - if (file) { - const _files = [file]; - await inputFilesHandler(_files); - e.preventDefault(); + if (!suggestionsContainerElement) { + if ( + !$mobile || + !( + 'ontouchstart' in window || + navigator.maxTouchPoints > 0 || + navigator.msMaxTouchPoints > 0 + ) + ) { + if (inOrNearComposition(e)) { + return; } - } else if (item.type === 'text/plain') { - if (($settings?.largeTextAsFile ?? false) && !shiftKey) { - const text = clipboardData.getData('text/plain'); - if (text.length > PASTED_TEXT_CHARACTER_LIMIT) { - e.preventDefault(); - const blob = new Blob([text], { type: 'text/plain' }); - const file = new File( - [blob], - `Pasted_Text_${Date.now()}.txt`, - { - type: 'text/plain' - } - ); + // Uses keyCode '13' for Enter key for chinese/japanese keyboards. + // + // Depending on the user's settings, it will send the message + // either when Enter is pressed or when Ctrl+Enter is pressed. + const enterPressed = + ($settings?.ctrlEnterToSend ?? false) + ? (e.key === 'Enter' || e.keyCode === 13) && isCtrlPressed + : (e.key === 'Enter' || e.keyCode === 13) && !e.shiftKey; - await uploadFileHandler(file, true); + if (enterPressed) { + e.preventDefault(); + if (prompt !== '' || files.length > 0) { + dispatch('submit', prompt); } } } } - } - }} - /> - {/key} + + if (e.key === 'Escape') { + console.log('Escape'); + atSelectedModel = undefined; + selectedToolIds = []; + selectedFilterIds = []; + + webSearchEnabled = false; + imageGenerationEnabled = false; + codeInterpreterEnabled = false; + } + }} + on:paste={async (e) => { + e = e.detail.event; + console.log(e); + + const clipboardData = e.clipboardData || window.clipboardData; + + if (clipboardData && clipboardData.items) { + for (const item of clipboardData.items) { + if (item.type.indexOf('image') !== -1) { + const blob = item.getAsFile(); + const reader = new FileReader(); + + reader.onload = function (e) { + files = [ + ...files, + { + type: 'image', + url: `${e.target.result}` + } + ]; + }; + + reader.readAsDataURL(blob); + } else if (item?.kind === 'file') { + const file = item.getAsFile(); + if (file) { + const _files = [file]; + await inputFilesHandler(_files); + e.preventDefault(); + } + } else if (item.type === 'text/plain') { + if (($settings?.largeTextAsFile ?? false) && !shiftKey) { + const text = clipboardData.getData('text/plain'); + + if (text.length > PASTED_TEXT_CHARACTER_LIMIT) { + e.preventDefault(); + const blob = new Blob([text], { type: 'text/plain' }); + const file = new File( + [blob], + `Pasted_Text_${Date.now()}.txt`, + { + type: 'text/plain' + } + ); + + await uploadFileHandler(file, true); + } + } + } + } + } + }} + /> + {/key} + {/if}
      {:else} {/if}
      diff --git a/src/lib/components/workspace/Prompts/PromptEditor.svelte b/src/lib/components/workspace/Prompts/PromptEditor.svelte index de71a1cb9b..21868be80e 100644 --- a/src/lib/components/workspace/Prompts/PromptEditor.svelte +++ b/src/lib/components/workspace/Prompts/PromptEditor.svelte @@ -106,7 +106,7 @@