From 0afe972bc6bf3b52a77832a32d39607570457826 Mon Sep 17 00:00:00 2001 From: hdnh2006 Date: Fri, 14 Feb 2025 17:57:47 +0100 Subject: [PATCH 001/180] embeddings function added 100% OpenAI compatible --- backend/open_webui/main.py | 5 ++ backend/open_webui/routers/openai.py | 74 ++++++++++++++++++++++++++++ 2 files changed, 79 insertions(+) diff --git a/backend/open_webui/main.py b/backend/open_webui/main.py index a363231512..79d56795d4 100644 --- a/backend/open_webui/main.py +++ b/backend/open_webui/main.py @@ -1038,6 +1038,11 @@ async def list_tasks_endpoint(user=Depends(get_verified_user)): return {"tasks": list_tasks()} # Use the function from tasks.py +@app.post("/api/embeddings") +async def api_embeddings(request: Request, user=Depends(get_verified_user)): + return await openai.generate_embeddings(request=request, user=user) + + ################################## # # Config Endpoints diff --git a/backend/open_webui/routers/openai.py b/backend/open_webui/routers/openai.py index afda362373..1b707150ee 100644 --- a/backend/open_webui/routers/openai.py +++ b/backend/open_webui/routers/openai.py @@ -715,6 +715,80 @@ async def generate_chat_completion( r.close() await session.close() +@router.post("/embeddings") +async def generate_embeddings(request: Request, user=Depends(get_verified_user)): + """ + Call embeddings endpoint + """ + + body = await request.body() + + idx = 0 + url = request.app.state.config.OPENAI_API_BASE_URLS[idx] + key = request.app.state.config.OPENAI_API_KEYS[idx] + + r = None + session = None + streaming = False + + try: + session = aiohttp.ClientSession(trust_env=True) + r = await session.request( + method=request.method, + url=f"{url}/embeddings", + data=body, + headers={ + "Authorization": f"Bearer {key}", + "Content-Type": "application/json", + **( + { + "X-OpenWebUI-User-Name": user.name, + "X-OpenWebUI-User-Id": user.id, + "X-OpenWebUI-User-Email": user.email, + "X-OpenWebUI-User-Role": user.role, + } + if ENABLE_FORWARD_USER_INFO_HEADERS + else {} + ), + }, + ) + r.raise_for_status() + + # Check if response is SSE + if "text/event-stream" in r.headers.get("Content-Type", ""): + streaming = True + return StreamingResponse( + r.content, + status_code=r.status, + headers=dict(r.headers), + background=BackgroundTask( + cleanup_response, response=r, session=session + ), + ) + else: + response_data = await r.json() + return response_data + + except Exception as e: + log.exception(e) + + detail = None + if r is not None: + try: + res = await r.json() + if "error" in res: + detail = f"External: {res['error']['message'] if 'message' in res['error'] else res['error']}" + except Exception: + detail = f"External: {e}" + raise HTTPException( + status_code=r.status if r else 500, + detail=detail if detail else "Open WebUI: Server Connection Error", + ) + finally: + if not streaming and session: + if r: + r.close() + await session.close() @router.api_route("/{path:path}", methods=["GET", "POST", "PUT", "DELETE"]) async def proxy(path: str, request: Request, user=Depends(get_verified_user)): From 7dde16d0ac2a645803671cfef4b98c8e0d8c446a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Link=20=5B=D0=A1=D0=B2=D1=8F=D0=B7=D0=BD=D0=BE=D0=B9=5D?= Date: Fri, 30 May 2025 11:52:33 +0300 Subject: [PATCH 002/180] [i18n] Russian locale update --- src/lib/i18n/locales/ru-RU/translation.json | 74 ++++++++++----------- 1 file changed, 37 insertions(+), 37 deletions(-) diff --git a/src/lib/i18n/locales/ru-RU/translation.json b/src/lib/i18n/locales/ru-RU/translation.json index 5e7404b387..1968689c47 100644 --- a/src/lib/i18n/locales/ru-RU/translation.json +++ b/src/lib/i18n/locales/ru-RU/translation.json @@ -35,7 +35,7 @@ "Add Connection": "Добавить соединение", "Add Content": "Добавить контент", "Add content here": "Добавить контент сюда", - "Add Custom Parameter": "", + "Add Custom Parameter": "Добавить пользовательский параметр", "Add custom prompt": "Добавьте пользовательский промпт", "Add Files": "Добавить файлы", "Add Group": "Добавить группу", @@ -284,18 +284,18 @@ "Current Model": "Текущая модель", "Current Password": "Текущий пароль", "Custom": "Пользовательский", - "Custom Parameter Name": "", - "Custom Parameter Value": "", + "Custom Parameter Name": "Название пользовательского параметра", + "Custom Parameter Value": "Значение пользовательского параметра", "Danger Zone": "Опасная зона", "Dark": "Темная", "Database": "База данных", "Datalab Marker API": "", - "Datalab Marker API Key required.": "", + "Datalab Marker API Key required.": "Требуется API-ключ Datalab Marker.", "December": "Декабрь", "Default": "По умолчанию", "Default (Open AI)": "По умолчанию (Open AI)", "Default (SentenceTransformers)": "По умолчанию (SentenceTransformers)", - "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.": "Режим по умолчанию работает с более широким спектром моделей, вызывая инструменты один раз перед выполнением. Режим Native использует встроенные в модель возможности вызова инструментов, но требует, чтобы модель изначально поддерживала эту функцию.", + "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.": "Режим по умолчанию работает с более широким спектром моделей, вызывая инструменты один раз перед выполнением. Режим Нативно использует встроенные в модель возможности вызова инструментов, но требует, чтобы модель изначально поддерживала эту функцию.", "Default Model": "Модель по умолчанию", "Default model updated": "Модель по умолчанию обновлена", "Default Models": "Модели по умолчанию", @@ -325,7 +325,7 @@ "Deleted {{deleteModelTag}}": "Удалено {{deleteModelTag}}", "Deleted {{name}}": "Удалено {{name}}", "Deleted User": "Удалённый пользователь", - "Deployment names are required for Azure OpenAI": "Для Azure OpenAI требуются имена развертываний", + "Deployment names are required for Azure OpenAI": "Для Azure OpenAI требуются названия развертываний", "Describe Pictures in Documents": "Опишите изображения в документах", "Describe your knowledge base and objectives": "Опишите свою базу знаний и цели", "Description": "Описание", @@ -337,8 +337,8 @@ "Direct Connections allow users to connect to their own OpenAI compatible API endpoints.": "Прямые подключения позволяют пользователям подключаться к своим собственным конечным точкам API, совместимым с OpenAI.", "Direct Connections settings updated": "Настройки прямых подключений обновлены", "Direct Tool Servers": "Доступ к серверам инструментов", - "Disable Image Extraction": "", - "Disable image extraction from the PDF. If Use LLM is enabled, images will be automatically captioned. Defaults to False.": "", + "Disable Image Extraction": "Отключить извлечение изображений", + "Disable image extraction from the PDF. If Use LLM is enabled, images will be automatically captioned. Defaults to False.": "Отключить извлечение изображений из PDF. Если включена параметр Использовать LLM, изображения будут подписаны автоматически. По умолчанию установлено значение Выкл.", "Disabled": "Отключено", "Discover a function": "Найти функцию", "Discover a model": "Найти модель", @@ -393,7 +393,7 @@ "e.g., 3, 4, 5 (leave blank for default)": "например, 3, 4, 5 (оставьте поле пустым по умолчанию)", "e.g., en-US,ja-JP (leave blank for auto-detect)": "например, en-US,ja-JP (оставьте поле пустым для автоматического определения)", "e.g., westus (leave blank for eastus)": "например, западный (оставьте пустым для восточного)", - "e.g.) en,fr,de": "", + "e.g.) en,fr,de": "например) en,fr,de", "Edit": "Редактировать", "Edit Arena Model": "Изменить модель арены", "Edit Channel": "Редактировать канал", @@ -444,7 +444,7 @@ "Enter Chunk Size": "Введите размер фрагмента", "Enter comma-separated \"token:bias_value\" pairs (example: 5432:100, 413:-100)": "Введите пары \"token:bias_value\", разделенные запятыми (пример: 5432:100, 413:-100).", "Enter content for the pending user info overlay. Leave empty for default.": "Введите содержимое для отложенного отображения информации о пользователе. Оставьте поле пустым для параметра по умолчанию.", - "Enter Datalab Marker API Key": "", + "Enter Datalab Marker API Key": "Введите API-ключ Datalab Marker", "Enter description": "Введите описание", "Enter Docling OCR Engine": "Введите Docling OCR Engine", "Enter Docling OCR Language(s)": "Введите языки для Docling OCR", @@ -476,7 +476,7 @@ "Enter Model ID": "Введите ID модели", "Enter model tag (e.g. {{modelTag}})": "Введите тег модели (например, {{modelTag}})", "Enter Mojeek Search API Key": "Введите ключ API поиска Mojeek", - "Enter name": "Введите название", + "Enter name": "Введите имя", "Enter New Password": "Введите новый пароль", "Enter Number of Steps (e.g. 50)": "Введите количество шагов (например, 50)", "Enter Perplexity API Key": "Введите ключ API Perplexity", @@ -508,7 +508,7 @@ "Enter Tavily Extract Depth": "Укажите глубину извлечения Tavily", "Enter the public URL of your WebUI. This URL will be used to generate links in the notifications.": "Введите общедоступный URL вашего WebUI. Этот URL будет использоваться для создания ссылок в уведомлениях.", "Enter the URL of the function to import": "Введите URL-адрес функции для импорта", - "Enter the URL to import": "", + "Enter the URL to import": "Введите URL-адрес для импорта", "Enter Tika Server URL": "Введите URL-адрес сервера Tika", "Enter timeout in seconds": "Введите время ожидания в секундах", "Enter to Send": "Enter для отправки", @@ -609,11 +609,11 @@ "Fluidly stream large external response chunks": "Плавная потоковая передача больших фрагментов внешних ответов", "Focus chat input": "Фокус ввода чата", "Folder deleted successfully": "Папка успешно удалена", - "Folder name cannot be empty.": "Имя папки не может быть пустым.", - "Folder name updated successfully": "Имя папки успешно обновлено", + "Folder name cannot be empty.": "Название папки не может быть пустым.", + "Folder name updated successfully": "Название папки успешно обновлено", "Followed instructions perfectly": "Идеально соответствует инструкциям", - "Force OCR": "", - "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": "Принудительное OCR", + "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 на всех страницах PDF-файла. Это может привести к ухудшению результатов, если в ваших PDF-файлах есть хороший текст. По умолчанию установлено значение Выкл.", "Forge new paths": "Прокладывайте новые пути", "Form": "Форма", "Format your variables using brackets like this:": "Отформатируйте переменные, используя такие : скобки", @@ -625,10 +625,10 @@ "Function deleted successfully": "Функция успешно удалена", "Function Description": "Описание Функции", "Function ID": "ID Функции", - "Function imported successfully": "", + "Function imported successfully": "Функция успешно импортирована", "Function is now globally disabled": "Функция теперь глобально отключена", "Function is now globally enabled": "Функция теперь глобально включена", - "Function Name": "Имя Функции", + "Function Name": "Название Функции", "Function updated successfully": "Функция успешно обновлена", "Functions": "Функции", "Functions allow arbitrary code execution.": "Функции позволяют выполнять произвольный код.", @@ -742,7 +742,7 @@ "Landing Page Mode": "Режим целевой страницы", "Language": "Язык", "Language Locales": "Языковые особенности", - "Languages": "", + "Languages": "Языки", "Last Active": "Последняя активность", "Last Modified": "Последнее изменение", "Last reply": "Последний ответ", @@ -820,7 +820,7 @@ "Model Filtering": "Фильтрация Моделей", "Model ID": "ID Модели", "Model IDs": "IDs Модели", - "Model Name": "Имя Модели", + "Model Name": "Название Модели", "Model not selected": "Модель не выбрана", "Model Params": "Параметры модели", "Model Permissions": "Разрешения Модели", @@ -844,7 +844,7 @@ "New Function": "Новая функция", "New Note": "Новая заметка", "New Password": "Новый пароль", - "New Tool": "", + "New Tool": "Новый инструмент", "new-channel": "", "No chats found for this user.": "Для этого пользователя не найдено ни одного чата.", "No chats found.": "Не найдено ни одного чата", @@ -918,10 +918,10 @@ "Other": "Прочее", "OUTPUT": "", "Output format": "Формат вывода", - "Output Format": "", + "Output Format": "Формат Вывода", "Overview": "Обзор", "page": "страница", - "Paginate": "", + "Paginate": "Разбивка на страницы", "Parameters": "Параметры", "Password": "Пароль", "Paste Large Text as File": "Вставить большой текст как файл", @@ -986,7 +986,7 @@ "Re-rank models by topic similarity": "Повторное ранжирование моделей по сходству тем", "Read": "Прочитать", "Read Aloud": "Прочитать вслух", - "Reasoning Effort": "Усилие рассуждения", + "Reasoning Effort": "", "Record": "Запись", "Record voice": "Записать голос", "Redirecting you to Open WebUI Community": "Перенаправляем вас в сообщество OpenWebUI", @@ -1060,7 +1060,7 @@ "Searxng Query URL": "URL-адрес запроса Searxng", "See readme.md for instructions": "Смотрите readme.md для инструкций", "See what's new": "Посмотреть, что нового", - "Seed": "Сид", + "Seed": "", "Select a base model": "Выберите базовую модель", "Select a engine": "Выберите движок", "Select a function": "Выберите функцию", @@ -1131,8 +1131,8 @@ "Significantly improves accuracy by using an LLM to enhance tables, forms, inline math, and layout detection. Will increase latency. Defaults to True.": "", "Signing in to {{WEBUI_NAME}}": "Зарегистрироваться в {{WEBUI_NAME}}", "sk-1234": "", - "Skip Cache": "", - "Skip the cache and re-run the inference. Defaults to False.": "", + "Skip Cache": "Пропустить кэширование", + "Skip the cache and re-run the inference. Defaults to False.": "Пропустить кэширование и перезапустить вывод. По умолчанию установлено значение Выкл.", "Sougou Search API sID": "", "Sougou Search API SK": "", "Source": "Источник", @@ -1140,10 +1140,10 @@ "Speech recognition error: {{error}}": "Ошибка распознавания речи: {{error}}", "Speech-to-Text Engine": "Система распознавания речи", "Stop": "Остановить", - "Stop Sequence": "Последовательность остановки", + "Stop Sequence": "", "Stream Chat Response": "Потоковый вывод ответа", - "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": "Удалять существующие OCR", + "Strip existing OCR text from the PDF and re-run OCR. Ignored if Force OCR is enabled. Defaults to False.": "Удалять существующий текст OCR из PDF и перезапустить OCR. Игнорируется если Принудительное OCR включено. По умолчанию установлено значение Выкл.", "STT Model": "Модель распознавания речи", "STT Settings": "Настройки распознавания речи", "Stylized PDF Export": "Стилизованный экспорт в формате PDF", @@ -1168,7 +1168,7 @@ "Tavily API Key": "Ключ API Tavily", "Tavily Extract Depth": "Глубина извлечения Tavily", "Tell us more:": "Пожалуйста, расскажите нам больше:", - "Temperature": "Температура", + "Temperature": "", "Temporary Chat": "Временный чат", "Text Splitter": "Разделитель текста", "Text-to-Speech Engine": "Система синтеза речи", @@ -1184,7 +1184,7 @@ "The leaderboard is currently in beta, and we may adjust the rating calculations as we refine the algorithm.": "В настоящее время таблица лидеров находится в стадии бета-тестирования, и мы можем скорректировать расчеты рейтинга по мере доработки алгоритма.", "The maximum file size in MB. If the file size exceeds this limit, the file will not be uploaded.": "Максимальный размер файла в МБ. Если размер файла превысит это ограничение, файл не будет загружен.", "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.": "Максимальное количество файлов, которые могут быть использованы одновременно в чате. Если количество файлов превысит это ограничение, файлы не будут загружены.", - "The output format for the text. Can be 'json', 'markdown', or 'html'. Defaults to 'markdown'.": "", + "The output format for the text. Can be 'json', 'markdown', or 'html'. Defaults to 'markdown'.": "Формат вывода текста. Может быть 'json', 'markdown', или 'html'. По умолчанию 'markdown'.", "The score should be a value between 0.0 (0%) and 1.0 (100%).": "Оценка должна быть значением между 0,0 (0%) и 1,0 (100%).", "The temperature of the model. Increasing the temperature will make the model answer more creatively.": "Температура модели. При повышении температуры модель будет отвечать более творчески.", "Theme": "Тема", @@ -1218,7 +1218,7 @@ "Title Generation": "Генерация заголовка", "Title Generation Prompt": "Промпт для генерации заголовка", "TLS": "", - "To access the available model names for downloading,": "Чтобы получить доступ к доступным для загрузки именам моделей,", + "To access the available model names for downloading,": "Чтобы получить доступ к доступным для загрузки названиям моделей,", "To access the GGUF models available for downloading,": "Чтобы получить доступ к моделям GGUF, доступным для загрузки,", "To access the WebUI, please reach out to the administrator. Admins can manage user statuses from the Admin Panel.": "Чтобы получить доступ к WebUI, пожалуйста, обратитесь к администратору. Администраторы могут управлять статусами пользователей из панели администратора.", "To attach knowledge base here, add them to the \"Knowledge\" workspace first.": "Чтобы прикрепить сюда базу знаний, сначала добавьте её в \"Знания\" рабочего пространства.", @@ -1239,7 +1239,7 @@ "Tool Description": "Описание Инструмента", "Tool ID": "ID Инструмента", "Tool imported successfully": "Инструмент успешно импортирован", - "Tool Name": "Имя Инструмента", + "Tool Name": "Название Инструмента", "Tool Servers": "Сервер Инструмента", "Tool updated successfully": "Инструмент успешно обновлен", "Tools": "Инструменты", @@ -1292,7 +1292,7 @@ "Use Gravatar": "Использовать Gravatar", "Use groups to group your users and assign permissions.": "Используйте группы, чтобы группировать пользователей и назначать разрешения.", "Use Initials": "Использовать инициалы", - "Use LLM": "", + "Use LLM": "Использовать LLM", "Use no proxy to fetch page contents.": "Не используйте прокси-сервер для получения содержимого страницы.", "Use proxy designated by http_proxy and https_proxy environment variables to fetch page contents.": "Используйте прокси-сервер, обозначенный переменными окружения http_proxy и https_proxy, для получения содержимого страницы.", "user": "пользователь", @@ -1343,8 +1343,8 @@ "What are you working on?": "Над чем вы работаете?", "What’s New in": "Что нового в", "When enabled, the model will respond to each chat message in real-time, generating a response as soon as the user sends a message. This mode is useful for live chat applications, but may impact performance on slower hardware.": "Если эта функция включена, модель будет отвечать на каждое сообщение чата в режиме реального времени, генерируя ответ, как только пользователь отправит сообщение. Этот режим полезен для приложений живого чата, но может повлиять на производительность на более медленном оборудовании.", - "wherever you are": "где бы ты ни был", - "Whether to paginate the output. Each page will be separated by a horizontal rule and page number. Defaults to False.": "", + "wherever you are": "где бы вы не были", + "Whether to paginate the output. Each page will be separated by a horizontal rule and page number. Defaults to False.": "Следует ли разбивать выходные данные на страницы. Каждая страница будет разделена горизонтальной линией и номером страницы. По умолчанию установлено значение Выкл.", "Whisper (Local)": "Whisper (Локально)", "Why?": "Почему?", "Widescreen Mode": "Широкоэкранный режим", From 5555d889d59b41e37d3e5a3daa23b088ecc9a960 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Link=20=5B=D0=A1=D0=B2=D1=8F=D0=B7=D0=BD=D0=BE=D0=B9=5D?= Date: Fri, 30 May 2025 11:55:22 +0300 Subject: [PATCH 003/180] Update translation.json --- src/lib/i18n/locales/ru-RU/translation.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/i18n/locales/ru-RU/translation.json b/src/lib/i18n/locales/ru-RU/translation.json index 1968689c47..3a988eedcc 100644 --- a/src/lib/i18n/locales/ru-RU/translation.json +++ b/src/lib/i18n/locales/ru-RU/translation.json @@ -1343,7 +1343,7 @@ "What are you working on?": "Над чем вы работаете?", "What’s New in": "Что нового в", "When enabled, the model will respond to each chat message in real-time, generating a response as soon as the user sends a message. This mode is useful for live chat applications, but may impact performance on slower hardware.": "Если эта функция включена, модель будет отвечать на каждое сообщение чата в режиме реального времени, генерируя ответ, как только пользователь отправит сообщение. Этот режим полезен для приложений живого чата, но может повлиять на производительность на более медленном оборудовании.", - "wherever you are": "где бы вы не были", + "wherever you are": "где бы вы ни были", "Whether to paginate the output. Each page will be separated by a horizontal rule and page number. Defaults to False.": "Следует ли разбивать выходные данные на страницы. Каждая страница будет разделена горизонтальной линией и номером страницы. По умолчанию установлено значение Выкл.", "Whisper (Local)": "Whisper (Локально)", "Why?": "Почему?", From 4ecf2a868553ba51f4e142fb3ba83c5abb07182d Mon Sep 17 00:00:00 2001 From: PVBLIC Foundation Date: Fri, 30 May 2025 09:33:57 -0700 Subject: [PATCH 004/180] Update pinecone.py May 2025 Latest Pinecone Best Practices --- .../retrieval/vector/dbs/pinecone.py | 54 ++++++++++++++++++- 1 file changed, 52 insertions(+), 2 deletions(-) diff --git a/backend/open_webui/retrieval/vector/dbs/pinecone.py b/backend/open_webui/retrieval/vector/dbs/pinecone.py index 9f8abf4609..982258880b 100644 --- a/backend/open_webui/retrieval/vector/dbs/pinecone.py +++ b/backend/open_webui/retrieval/vector/dbs/pinecone.py @@ -3,10 +3,18 @@ import logging import time # for measuring elapsed time from pinecone import Pinecone, ServerlessSpec +# Add gRPC support for better performance (Pinecone best practice) +try: + from pinecone.grpc import PineconeGRPC + GRPC_AVAILABLE = True +except ImportError: + GRPC_AVAILABLE = False + import asyncio # for async upserts import functools # for partial binding in async tasks import concurrent.futures # for parallel batch upserts +import random # for jitter in retry backoff from open_webui.retrieval.vector.main import ( VectorDBBase, @@ -47,7 +55,24 @@ class PineconeClient(VectorDBBase): self.cloud = PINECONE_CLOUD # Initialize Pinecone client for improved performance - self.client = Pinecone(api_key=self.api_key) + if GRPC_AVAILABLE: + # Use gRPC client for better performance (Pinecone recommendation) + self.client = PineconeGRPC( + api_key=self.api_key, + pool_threads=20, # Improved connection pool size + timeout=30 # Reasonable timeout for operations + ) + self.using_grpc = True + log.info("Using Pinecone gRPC client for optimal performance") + else: + # Fallback to HTTP client with enhanced connection pooling + self.client = Pinecone( + api_key=self.api_key, + pool_threads=20, # Improved connection pool size + timeout=30 # Reasonable timeout for operations + ) + self.using_grpc = False + log.info("Using Pinecone HTTP client (gRPC not available)") # Persistent executor for batch operations self._executor = concurrent.futures.ThreadPoolExecutor(max_workers=5) @@ -91,12 +116,37 @@ class PineconeClient(VectorDBBase): log.info(f"Using existing Pinecone index '{self.index_name}'") # Connect to the index - self.index = self.client.Index(self.index_name) + self.index = self.client.Index( + self.index_name, + pool_threads=20, # Enhanced connection pool for index operations + ) except Exception as e: log.error(f"Failed to initialize Pinecone index: {e}") raise RuntimeError(f"Failed to initialize Pinecone index: {e}") + def _retry_pinecone_operation(self, operation_func, max_retries=3): + """Retry Pinecone operations with exponential backoff for rate limits and network issues.""" + for attempt in range(max_retries): + try: + return operation_func() + except Exception as e: + error_str = str(e).lower() + # Check if it's a retryable error (rate limits, network issues, timeouts) + is_retryable = any(keyword in error_str for keyword in [ + 'rate limit', 'quota', 'timeout', 'network', 'connection', + 'unavailable', 'internal error', '429', '500', '502', '503', '504' + ]) + + if not is_retryable or attempt == max_retries - 1: + # Don't retry for non-retryable errors or on final attempt + raise + + # Exponential backoff with jitter + delay = (2 ** attempt) + random.uniform(0, 1) + log.warning(f"Pinecone operation failed (attempt {attempt + 1}/{max_retries}), retrying in {delay:.2f}s: {e}") + time.sleep(delay) + def _create_points( self, items: List[VectorItem], collection_name_with_prefix: str ) -> List[Dict[str, Any]]: From cbc514e68192885f210a7165b844b0eb91a53c34 Mon Sep 17 00:00:00 2001 From: sanae artoria Date: Sat, 31 May 2025 01:13:38 +0800 Subject: [PATCH 005/180] The translation might be confusing, in some cases for example tooltips over a radio button --- src/lib/i18n/locales/zh-CN/translation.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/i18n/locales/zh-CN/translation.json b/src/lib/i18n/locales/zh-CN/translation.json index 6b28f9e2a7..f55ed9c754 100644 --- a/src/lib/i18n/locales/zh-CN/translation.json +++ b/src/lib/i18n/locales/zh-CN/translation.json @@ -339,7 +339,7 @@ "Direct Tool Servers": "直接连接工具服务器", "Disable Image Extraction": "禁用图像提取", "Disable image extraction from the PDF. If Use LLM is enabled, images will be automatically captioned. Defaults to False.": "禁用从 PDF 中提取图像。若开启 '使用大语言模型(LLM)' 功能,图像将自动添加描述。默认为关闭", - "Disabled": "禁用", + "Disabled": "已禁用", "Discover a function": "发现更多函数", "Discover a model": "发现更多模型", "Discover a prompt": "发现更多提示词", @@ -421,7 +421,7 @@ "Enable Message Rating": "启用回复评价", "Enable Mirostat sampling for controlling perplexity.": "启用 Mirostat 采样以控制困惑度", "Enable New Sign Ups": "允许新用户注册", - "Enabled": "启用", + "Enabled": "已启用", "Endpoint URL": "端点 URL", "Enforce Temporary Chat": "强制临时对话", "Enhance": "润色", From 66bde326230aa1069a6a73b18e00862d5be4316e Mon Sep 17 00:00:00 2001 From: PVBLIC Foundation Date: Fri, 30 May 2025 18:47:23 -0700 Subject: [PATCH 006/180] Update pinecone.py --- .../retrieval/vector/dbs/pinecone.py | 55 +++++++++++++------ 1 file changed, 39 insertions(+), 16 deletions(-) diff --git a/backend/open_webui/retrieval/vector/dbs/pinecone.py b/backend/open_webui/retrieval/vector/dbs/pinecone.py index 982258880b..8291332c0f 100644 --- a/backend/open_webui/retrieval/vector/dbs/pinecone.py +++ b/backend/open_webui/retrieval/vector/dbs/pinecone.py @@ -6,6 +6,7 @@ from pinecone import Pinecone, ServerlessSpec # Add gRPC support for better performance (Pinecone best practice) try: from pinecone.grpc import PineconeGRPC + GRPC_AVAILABLE = True except ImportError: GRPC_AVAILABLE = False @@ -60,7 +61,7 @@ class PineconeClient(VectorDBBase): self.client = PineconeGRPC( api_key=self.api_key, pool_threads=20, # Improved connection pool size - timeout=30 # Reasonable timeout for operations + timeout=30, # Reasonable timeout for operations ) self.using_grpc = True log.info("Using Pinecone gRPC client for optimal performance") @@ -69,7 +70,7 @@ class PineconeClient(VectorDBBase): self.client = Pinecone( api_key=self.api_key, pool_threads=20, # Improved connection pool size - timeout=30 # Reasonable timeout for operations + timeout=30, # Reasonable timeout for operations ) self.using_grpc = False log.info("Using Pinecone HTTP client (gRPC not available)") @@ -133,18 +134,34 @@ class PineconeClient(VectorDBBase): except Exception as e: error_str = str(e).lower() # Check if it's a retryable error (rate limits, network issues, timeouts) - is_retryable = any(keyword in error_str for keyword in [ - 'rate limit', 'quota', 'timeout', 'network', 'connection', - 'unavailable', 'internal error', '429', '500', '502', '503', '504' - ]) - + is_retryable = any( + keyword in error_str + for keyword in [ + "rate limit", + "quota", + "timeout", + "network", + "connection", + "unavailable", + "internal error", + "429", + "500", + "502", + "503", + "504", + ] + ) + if not is_retryable or attempt == max_retries - 1: # Don't retry for non-retryable errors or on final attempt raise - + # Exponential backoff with jitter - delay = (2 ** attempt) + random.uniform(0, 1) - log.warning(f"Pinecone operation failed (attempt {attempt + 1}/{max_retries}), retrying in {delay:.2f}s: {e}") + delay = (2**attempt) + random.uniform(0, 1) + log.warning( + f"Pinecone operation failed (attempt {attempt + 1}/{max_retries}), " + f"retrying in {delay:.2f}s: {e}" + ) time.sleep(delay) def _create_points( @@ -273,7 +290,8 @@ class PineconeClient(VectorDBBase): elapsed = time.time() - start_time log.debug(f"Insert of {len(points)} vectors took {elapsed:.2f} seconds") log.info( - f"Successfully inserted {len(points)} vectors in parallel batches into '{collection_name_with_prefix}'" + f"Successfully inserted {len(points)} vectors in parallel batches " + f"into '{collection_name_with_prefix}'" ) def upsert(self, collection_name: str, items: List[VectorItem]) -> None: @@ -304,7 +322,8 @@ class PineconeClient(VectorDBBase): elapsed = time.time() - start_time log.debug(f"Upsert of {len(points)} vectors took {elapsed:.2f} seconds") log.info( - f"Successfully upserted {len(points)} vectors in parallel batches into '{collection_name_with_prefix}'" + f"Successfully upserted {len(points)} vectors in parallel batches " + f"into '{collection_name_with_prefix}'" ) async def insert_async(self, collection_name: str, items: List[VectorItem]) -> None: @@ -335,7 +354,8 @@ class PineconeClient(VectorDBBase): log.error(f"Error in async insert batch: {result}") raise result log.info( - f"Successfully async inserted {len(points)} vectors in batches into '{collection_name_with_prefix}'" + f"Successfully async inserted {len(points)} vectors in batches " + f"into '{collection_name_with_prefix}'" ) async def upsert_async(self, collection_name: str, items: List[VectorItem]) -> None: @@ -366,7 +386,8 @@ class PineconeClient(VectorDBBase): log.error(f"Error in async upsert batch: {result}") raise result log.info( - f"Successfully async upserted {len(points)} vectors in batches into '{collection_name_with_prefix}'" + f"Successfully async upserted {len(points)} vectors in batches " + f"into '{collection_name_with_prefix}'" ) def search( @@ -507,10 +528,12 @@ class PineconeClient(VectorDBBase): # This is a limitation of Pinecone - be careful with ID uniqueness self.index.delete(ids=batch_ids) log.debug( - f"Deleted batch of {len(batch_ids)} vectors by ID from '{collection_name_with_prefix}'" + f"Deleted batch of {len(batch_ids)} vectors by ID " + f"from '{collection_name_with_prefix}'" ) log.info( - f"Successfully deleted {len(ids)} vectors by ID from '{collection_name_with_prefix}'" + f"Successfully deleted {len(ids)} vectors by ID " + f"from '{collection_name_with_prefix}'" ) elif filter: From cf3635ba25b2f7a2fa02914820fc46eaf3beec18 Mon Sep 17 00:00:00 2001 From: PVBLIC Foundation Date: Fri, 30 May 2025 20:06:29 -0700 Subject: [PATCH 007/180] Update mistral.py 1. Intelligent Error Handling Added _is_retryable_error() method to distinguish retryable vs non-retryable errors Prevents unnecessary retries on client errors (4xx) that won't succeed Caps retry delay at 30 seconds to prevent excessive waiting 2. Optimized Timeout Configuration Upload: Capped at 2 minutes (was using full 5-minute timeout) URL requests: 30 seconds (should be fast) OCR processing: Full timeout (can take time) Cleanup: 30 seconds (should be quick) 3. Enhanced Connection Pool Increased connection limits: 20 total, 10 per host Longer DNS cache TTL (10 minutes vs 5 minutes) Increased keepalive timeout (60s vs 30s) Added async DNS resolver for better performance Granular timeout controls (connect, read, total) 4. Concurrency Control for Batch Processing Added semaphore-based concurrency control (default: 5 concurrent) Prevents API overwhelming while maintaining throughput Configurable concurrency limit per workload 5. Memory Efficient Result Processing Early exit for empty content validation Better error metadata for debugging Added content length tracking Streamlined page processing logic 6. General Performance Improvements Better error logging with truncated responses Optimized metadata creation Improved debug logging efficiency --- .../open_webui/retrieval/loaders/mistral.py | 266 +++++++++++++----- 1 file changed, 200 insertions(+), 66 deletions(-) diff --git a/backend/open_webui/retrieval/loaders/mistral.py b/backend/open_webui/retrieval/loaders/mistral.py index 67641d0509..b00e9d7ce5 100644 --- a/backend/open_webui/retrieval/loaders/mistral.py +++ b/backend/open_webui/retrieval/loaders/mistral.py @@ -20,6 +20,14 @@ class MistralLoader: """ Enhanced Mistral OCR loader with both sync and async support. Loads documents by processing them through the Mistral OCR API. + + Performance Optimizations: + - Differentiated timeouts for different operations + - Intelligent retry logic with exponential backoff + - Memory-efficient file streaming for large files + - Connection pooling and keepalive optimization + - Semaphore-based concurrency control for batch processing + - Enhanced error handling with retryable error classification """ BASE_API_URL = "https://api.mistral.ai/v1" @@ -53,17 +61,40 @@ class MistralLoader: self.max_retries = max_retries self.debug = enable_debug_logging - # Pre-compute file info for performance + # PERFORMANCE OPTIMIZATION: Differentiated timeouts for different operations + # This prevents long-running OCR operations from affecting quick operations + # and improves user experience by failing fast on operations that should be quick + self.upload_timeout = min( + timeout, 120 + ) # Cap upload at 2 minutes - prevents hanging on large files + self.url_timeout = ( + 30 # URL requests should be fast - fail quickly if API is slow + ) + self.ocr_timeout = ( + timeout # OCR can take the full timeout - this is the heavy operation + ) + self.cleanup_timeout = ( + 30 # Cleanup should be quick - don't hang on file deletion + ) + + # PERFORMANCE OPTIMIZATION: Pre-compute file info to avoid repeated filesystem calls + # This avoids multiple os.path.basename() and os.path.getsize() calls during processing self.file_name = os.path.basename(file_path) self.file_size = os.path.getsize(file_path) + # ENHANCEMENT: Added User-Agent for better API tracking and debugging self.headers = { "Authorization": f"Bearer {self.api_key}", - "User-Agent": "OpenWebUI-MistralLoader/2.0", + "User-Agent": "OpenWebUI-MistralLoader/2.0", # Helps API provider track usage } def _debug_log(self, message: str, *args) -> None: - """Conditional debug logging for performance.""" + """ + PERFORMANCE OPTIMIZATION: Conditional debug logging for performance. + + Only processes debug messages when debug mode is enabled, avoiding + string formatting overhead in production environments. + """ if self.debug: log.debug(message, *args) @@ -115,53 +146,118 @@ class MistralLoader: log.error(f"Unexpected error processing response: {e}") raise + def _is_retryable_error(self, error: Exception) -> bool: + """ + ENHANCEMENT: Intelligent error classification for retry logic. + + Determines if an error is retryable based on its type and status code. + This prevents wasting time retrying errors that will never succeed + (like authentication errors) while ensuring transient errors are retried. + + Retryable errors: + - Network connection errors (temporary network issues) + - Timeouts (server might be temporarily overloaded) + - Server errors (5xx status codes - server-side issues) + - Rate limiting (429 status - temporary throttling) + + Non-retryable errors: + - Authentication errors (401, 403 - won't fix with retry) + - Bad request errors (400 - malformed request) + - Not found errors (404 - resource doesn't exist) + """ + if isinstance(error, requests.exceptions.ConnectionError): + return True # Network issues are usually temporary + if isinstance(error, requests.exceptions.Timeout): + return True # Timeouts might resolve on retry + if isinstance(error, requests.exceptions.HTTPError): + # Only retry on server errors (5xx) or rate limits (429) + if hasattr(error, "response") and error.response is not None: + status_code = error.response.status_code + return status_code >= 500 or status_code == 429 + return False + if isinstance( + error, (aiohttp.ClientConnectionError, aiohttp.ServerTimeoutError) + ): + return True # Async network/timeout errors are retryable + if isinstance(error, aiohttp.ClientResponseError): + return error.status >= 500 or error.status == 429 + return False # All other errors are non-retryable + def _retry_request_sync(self, request_func, *args, **kwargs): - """Synchronous retry logic with exponential backoff.""" + """ + ENHANCEMENT: Synchronous retry logic with intelligent error classification. + + Uses exponential backoff with jitter to avoid thundering herd problems. + The wait time increases exponentially but is capped at 30 seconds to + prevent excessive delays. Only retries errors that are likely to succeed + on subsequent attempts. + """ for attempt in range(self.max_retries): try: return request_func(*args, **kwargs) - except (requests.exceptions.RequestException, Exception) as e: - if attempt == self.max_retries - 1: + except Exception as e: + if attempt == self.max_retries - 1 or not self._is_retryable_error(e): raise - wait_time = (2**attempt) + 0.5 + # PERFORMANCE OPTIMIZATION: Exponential backoff with cap + # Prevents overwhelming the server while ensuring reasonable retry delays + wait_time = min((2**attempt) + 0.5, 30) # Cap at 30 seconds log.warning( - f"Request failed (attempt {attempt + 1}/{self.max_retries}): {e}. Retrying in {wait_time}s..." + f"Retryable error (attempt {attempt + 1}/{self.max_retries}): {e}. " + f"Retrying in {wait_time}s..." ) time.sleep(wait_time) async def _retry_request_async(self, request_func, *args, **kwargs): - """Async retry logic with exponential backoff.""" + """ + ENHANCEMENT: Async retry logic with intelligent error classification. + + Async version of retry logic that doesn't block the event loop during + wait periods. Uses the same exponential backoff strategy as sync version. + """ for attempt in range(self.max_retries): try: return await request_func(*args, **kwargs) - except (aiohttp.ClientError, asyncio.TimeoutError) as e: - if attempt == self.max_retries - 1: + except Exception as e: + if attempt == self.max_retries - 1 or not self._is_retryable_error(e): raise - wait_time = (2**attempt) + 0.5 + # PERFORMANCE OPTIMIZATION: Non-blocking exponential backoff + wait_time = min((2**attempt) + 0.5, 30) # Cap at 30 seconds log.warning( - f"Request failed (attempt {attempt + 1}/{self.max_retries}): {e}. Retrying in {wait_time}s..." + f"Retryable error (attempt {attempt + 1}/{self.max_retries}): {e}. " + f"Retrying in {wait_time}s..." ) - await asyncio.sleep(wait_time) + await asyncio.sleep(wait_time) # Non-blocking wait def _upload_file(self) -> str: - """Uploads the file to Mistral for OCR processing (sync version).""" + """ + PERFORMANCE OPTIMIZATION: Enhanced file upload with streaming consideration. + + Uploads the file to Mistral for OCR processing (sync version). + Uses context manager for file handling to ensure proper resource cleanup. + Although streaming is not enabled for this endpoint, the file is opened + in a context manager to minimize memory usage duration. + """ log.info("Uploading file to Mistral API") url = f"{self.BASE_API_URL}/files" - file_name = os.path.basename(self.file_path) def upload_request(): + # MEMORY OPTIMIZATION: Use context manager to minimize file handle lifetime + # This ensures the file is closed immediately after reading, reducing memory usage with open(self.file_path, "rb") as f: - files = {"file": (file_name, f, "application/pdf")} + files = {"file": (self.file_name, f, "application/pdf")} data = {"purpose": "ocr"} + # NOTE: stream=False is required for this endpoint + # The Mistral API doesn't support chunked uploads for this endpoint response = requests.post( url, headers=self.headers, files=files, data=data, - timeout=self.timeout, + timeout=self.upload_timeout, # Use specialized upload timeout + stream=False, # Keep as False for this endpoint ) return self._handle_response(response) @@ -209,7 +305,7 @@ class MistralLoader: url, data=writer, headers=self.headers, - timeout=aiohttp.ClientTimeout(total=self.timeout), + timeout=aiohttp.ClientTimeout(total=self.upload_timeout), ) as response: return await self._handle_response_async(response) @@ -231,7 +327,7 @@ class MistralLoader: def url_request(): response = requests.get( - url, headers=signed_url_headers, params=params, timeout=self.timeout + url, headers=signed_url_headers, params=params, timeout=self.url_timeout ) return self._handle_response(response) @@ -261,7 +357,7 @@ class MistralLoader: url, headers=headers, params=params, - timeout=aiohttp.ClientTimeout(total=self.timeout), + timeout=aiohttp.ClientTimeout(total=self.url_timeout), ) as response: return await self._handle_response_async(response) @@ -294,7 +390,7 @@ class MistralLoader: def ocr_request(): response = requests.post( - url, headers=ocr_headers, json=payload, timeout=self.timeout + url, headers=ocr_headers, json=payload, timeout=self.ocr_timeout ) return self._handle_response(response) @@ -336,7 +432,7 @@ class MistralLoader: url, json=payload, headers=headers, - timeout=aiohttp.ClientTimeout(total=self.timeout), + timeout=aiohttp.ClientTimeout(total=self.ocr_timeout), ) as response: ocr_response = await self._handle_response_async(response) @@ -353,7 +449,9 @@ class MistralLoader: url = f"{self.BASE_API_URL}/files/{file_id}" try: - response = requests.delete(url, headers=self.headers, timeout=30) + response = requests.delete( + url, headers=self.headers, timeout=self.cleanup_timeout + ) delete_response = self._handle_response(response) log.info(f"File deleted successfully: {delete_response}") except Exception as e: @@ -372,7 +470,7 @@ class MistralLoader: url=f"{self.BASE_API_URL}/files/{file_id}", headers=self.headers, timeout=aiohttp.ClientTimeout( - total=30 + total=self.cleanup_timeout ), # Shorter timeout for cleanup ) as response: return await self._handle_response_async(response) @@ -388,29 +486,39 @@ class MistralLoader: async def _get_session(self): """Context manager for HTTP session with optimized settings.""" connector = aiohttp.TCPConnector( - limit=10, # Total connection limit - limit_per_host=5, # Per-host connection limit - ttl_dns_cache=300, # DNS cache TTL + limit=20, # Increased total connection limit for better throughput + limit_per_host=10, # Increased per-host limit for API endpoints + ttl_dns_cache=600, # Longer DNS cache TTL (10 minutes) use_dns_cache=True, - keepalive_timeout=30, + keepalive_timeout=60, # Increased keepalive for connection reuse enable_cleanup_closed=True, + force_close=False, # Allow connection reuse + resolver=aiohttp.AsyncResolver(), # Use async DNS resolver + ) + + timeout = aiohttp.ClientTimeout( + total=self.timeout, + connect=30, # Connection timeout + sock_read=60, # Socket read timeout ) async with aiohttp.ClientSession( connector=connector, - timeout=aiohttp.ClientTimeout(total=self.timeout), + timeout=timeout, headers={"User-Agent": "OpenWebUI-MistralLoader/2.0"}, + raise_for_status=False, # We handle status codes manually ) as session: yield session def _process_results(self, ocr_response: Dict[str, Any]) -> List[Document]: - """Process OCR results into Document objects with enhanced metadata.""" + """Process OCR results into Document objects with enhanced metadata and memory efficiency.""" pages_data = ocr_response.get("pages") if not pages_data: log.warning("No pages found in OCR response.") return [ Document( - page_content="No text content found", metadata={"error": "no_pages"} + page_content="No text content found", + metadata={"error": "no_pages", "file_name": self.file_name}, ) ] @@ -418,41 +526,44 @@ class MistralLoader: total_pages = len(pages_data) skipped_pages = 0 + # Process pages in a memory-efficient way for page_data in pages_data: page_content = page_data.get("markdown") page_index = page_data.get("index") # API uses 0-based index - if page_content is not None and page_index is not None: - # Clean up content efficiently - cleaned_content = ( - page_content.strip() - if isinstance(page_content, str) - else str(page_content) - ) - - if cleaned_content: # Only add non-empty pages - documents.append( - Document( - page_content=cleaned_content, - metadata={ - "page": page_index, # 0-based index from API - "page_label": page_index - + 1, # 1-based label for convenience - "total_pages": total_pages, - "file_name": self.file_name, - "file_size": self.file_size, - "processing_engine": "mistral-ocr", - }, - ) - ) - else: - skipped_pages += 1 - self._debug_log(f"Skipping empty page {page_index}") - else: + if page_content is None or page_index is None: skipped_pages += 1 self._debug_log( - f"Skipping page due to missing 'markdown' or 'index'. Data: {page_data}" + f"Skipping page due to missing 'markdown' or 'index'. Data keys: {list(page_data.keys())}" ) + continue + + # Clean up content efficiently with early exit for empty content + if isinstance(page_content, str): + cleaned_content = page_content.strip() + else: + cleaned_content = str(page_content).strip() + + if not cleaned_content: + skipped_pages += 1 + self._debug_log(f"Skipping empty page {page_index}") + continue + + # Create document with optimized metadata + documents.append( + Document( + page_content=cleaned_content, + metadata={ + "page": page_index, # 0-based index from API + "page_label": page_index + 1, # 1-based label for convenience + "total_pages": total_pages, + "file_name": self.file_name, + "file_size": self.file_size, + "processing_engine": "mistral-ocr", + "content_length": len(cleaned_content), + }, + ) + ) if skipped_pages > 0: log.info( @@ -467,7 +578,11 @@ class MistralLoader: return [ Document( page_content="No valid text content found in document", - metadata={"error": "no_valid_pages", "total_pages": total_pages}, + metadata={ + "error": "no_valid_pages", + "total_pages": total_pages, + "file_name": self.file_name, + }, ) ] @@ -585,12 +700,14 @@ class MistralLoader: @staticmethod async def load_multiple_async( loaders: List["MistralLoader"], + max_concurrent: int = 5, # Limit concurrent requests ) -> List[List[Document]]: """ - Process multiple files concurrently for maximum performance. + Process multiple files concurrently with controlled concurrency. Args: loaders: List of MistralLoader instances + max_concurrent: Maximum number of concurrent requests Returns: List of document lists, one for each loader @@ -598,11 +715,20 @@ class MistralLoader: if not loaders: return [] - log.info(f"Starting concurrent processing of {len(loaders)} files") + log.info( + f"Starting concurrent processing of {len(loaders)} files with max {max_concurrent} concurrent" + ) start_time = time.time() - # Process all files concurrently - tasks = [loader.load_async() for loader in loaders] + # Use semaphore to control concurrency + semaphore = asyncio.Semaphore(max_concurrent) + + async def process_with_semaphore(loader: "MistralLoader") -> List[Document]: + async with semaphore: + return await loader.load_async() + + # Process all files with controlled concurrency + tasks = [process_with_semaphore(loader) for loader in loaders] results = await asyncio.gather(*tasks, return_exceptions=True) # Handle any exceptions in results @@ -624,10 +750,18 @@ class MistralLoader: else: processed_results.append(result) + # MONITORING: Log comprehensive batch processing statistics total_time = time.time() - start_time total_docs = sum(len(docs) for docs in processed_results) + success_count = sum( + 1 for result in results if not isinstance(result, Exception) + ) + failure_count = len(results) - success_count + log.info( - f"Batch processing completed in {total_time:.2f}s, produced {total_docs} total documents" + f"Batch processing completed in {total_time:.2f}s: " + f"{success_count} files succeeded, {failure_count} files failed, " + f"produced {total_docs} total documents" ) return processed_results From e41e375aab0c7e7955b797aa4b53b8c9f4dd9196 Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Sat, 31 May 2025 15:00:27 +0400 Subject: [PATCH 008/180] refac: role update ui --- backend/open_webui/models/users.py | 1 + backend/open_webui/routers/users.py | 38 +++++++++---------- src/lib/apis/users/index.ts | 1 + .../components/admin/Users/UserList.svelte | 38 +------------------ .../admin/Users/UserList/EditUserModal.svelte | 18 +++++++++ 5 files changed, 38 insertions(+), 58 deletions(-) diff --git a/backend/open_webui/models/users.py b/backend/open_webui/models/users.py index 3222aa27a6..a5dd9467bc 100644 --- a/backend/open_webui/models/users.py +++ b/backend/open_webui/models/users.py @@ -95,6 +95,7 @@ class UserRoleUpdateForm(BaseModel): class UserUpdateForm(BaseModel): + role: str name: str email: str profile_image_url: str diff --git a/backend/open_webui/routers/users.py b/backend/open_webui/routers/users.py index 8702ae50ba..4046dc72d8 100644 --- a/backend/open_webui/routers/users.py +++ b/backend/open_webui/routers/users.py @@ -165,22 +165,6 @@ async def update_default_user_permissions( return request.app.state.config.USER_PERMISSIONS -############################ -# UpdateUserRole -############################ - - -@router.post("/update/role", response_model=Optional[UserModel]) -async def update_user_role(form_data: UserRoleUpdateForm, user=Depends(get_admin_user)): - if user.id != form_data.id and form_data.id != Users.get_first_user().id: - return Users.update_user_role_by_id(form_data.id, form_data.role) - - raise HTTPException( - status_code=status.HTTP_403_FORBIDDEN, - detail=ERROR_MESSAGES.ACTION_PROHIBITED, - ) - - ############################ # GetUserSettingsBySessionUser ############################ @@ -333,11 +317,22 @@ async def update_user_by_id( # Prevent modification of the primary admin user by other admins try: first_user = Users.get_first_user() - if first_user and user_id == first_user.id and session_user.id != user_id: - raise HTTPException( - status_code=status.HTTP_403_FORBIDDEN, - detail=ERROR_MESSAGES.ACTION_PROHIBITED, - ) + if first_user: + if user_id == first_user.id: + if session_user.id != user_id: + # If the user trying to update is the primary admin, and they are not the primary admin themselves + raise HTTPException( + status_code=status.HTTP_403_FORBIDDEN, + detail=ERROR_MESSAGES.ACTION_PROHIBITED, + ) + + if form_data.role != "admin": + # If the primary admin is trying to change their own role, prevent it + raise HTTPException( + status_code=status.HTTP_403_FORBIDDEN, + detail=ERROR_MESSAGES.ACTION_PROHIBITED, + ) + except Exception as e: log.error(f"Error checking primary admin status: {e}") raise HTTPException( @@ -365,6 +360,7 @@ async def update_user_by_id( updated_user = Users.update_user_by_id( user_id, { + "role": form_data.role, "name": form_data.name, "email": form_data.email.lower(), "profile_image_url": form_data.profile_image_url, diff --git a/src/lib/apis/users/index.ts b/src/lib/apis/users/index.ts index f8ab88ff53..391bdca56d 100644 --- a/src/lib/apis/users/index.ts +++ b/src/lib/apis/users/index.ts @@ -393,6 +393,7 @@ export const updateUserById = async (token: string, userId: string, user: UserUp }, body: JSON.stringify({ profile_image_url: user.profile_image_url, + role: user.role, email: user.email, name: user.name, password: user.password !== '' ? user.password : undefined diff --git a/src/lib/components/admin/Users/UserList.svelte b/src/lib/components/admin/Users/UserList.svelte index 31eec1fbdd..42f3b9dc1a 100644 --- a/src/lib/components/admin/Users/UserList.svelte +++ b/src/lib/components/admin/Users/UserList.svelte @@ -52,27 +52,6 @@ let showUserChatsModal = false; let showEditUserModal = false; - let showUpdateRoleModal = false; - - const onUpdateRole = (user) => { - if (user.role === 'user') { - updateRoleHandler(user.id, 'admin'); - } else if (user.role === 'pending') { - updateRoleHandler(user.id, 'user'); - } else { - updateRoleHandler(user.id, 'pending'); - } - }; - const updateRoleHandler = async (id, role) => { - const res = await updateUserRole(localStorage.token, id, role).catch((error) => { - toast.error(`${error}`); - return null; - }); - - if (res) { - getUserList(); - } - }; const deleteUserHandler = async (id) => { const res = await deleteUserById(localStorage.token, id).catch((error) => { @@ -133,21 +112,6 @@ }} /> - { - onUpdateRole(selectedUser); - }} - message={$i18n.t(`Are you sure you want to update this user\'s role to **{{ROLE}}**?`, { - ROLE: - selectedUser?.role === 'user' - ? 'admin' - : selectedUser?.role === 'pending' - ? 'user' - : 'pending' - })} -/> - {#key selectedUser} { selectedUser = user; - showUpdateRoleModal = true; + showEditUserModal = !showEditUserModal; }} >
+
+
{$i18n.t('Role')}
+ +
+ +
+
+
{$i18n.t('Email')}
From 4e82c44f3e4cac2ca4a7574967a5caf4058c1ece Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Sat, 31 May 2025 15:04:38 +0400 Subject: [PATCH 009/180] refac --- backend/open_webui/utils/oauth.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/open_webui/utils/oauth.py b/backend/open_webui/utils/oauth.py index de33558596..6c98ed7dfa 100644 --- a/backend/open_webui/utils/oauth.py +++ b/backend/open_webui/utils/oauth.py @@ -538,7 +538,7 @@ class OAuthManager: # Redirect back to the frontend with the JWT token redirect_base_url = request.app.state.config.WEBUI_URL or request.base_url - if redirect_base_url.endswith("/"): + if isinstance(redirect_base_url, str) and redirect_base_url.endswith("/"): redirect_base_url = redirect_base_url[:-1] redirect_url = f"{redirect_base_url}/auth#token={jwt_token}" From 2c15f8e676795fb888ea5391bc6a18a689e47f2c Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Sat, 31 May 2025 15:07:28 +0400 Subject: [PATCH 010/180] refac --- backend/open_webui/config.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/backend/open_webui/config.py b/backend/open_webui/config.py index 0f49483610..0e0c08f2bb 100644 --- a/backend/open_webui/config.py +++ b/backend/open_webui/config.py @@ -901,9 +901,7 @@ TOOL_SERVER_CONNECTIONS = PersistentConfig( #################################### -WEBUI_URL = PersistentConfig( - "WEBUI_URL", "webui.url", os.environ.get("WEBUI_URL", "http://localhost:3000") -) +WEBUI_URL = PersistentConfig("WEBUI_URL", "webui.url", os.environ.get("WEBUI_URL", "")) ENABLE_SIGNUP = PersistentConfig( From 185224e4e11b0fa874051a110d1b3ea34c7893b2 Mon Sep 17 00:00:00 2001 From: expruc Date: Sat, 31 May 2025 21:37:42 +0300 Subject: [PATCH 011/180] Locale: updated he translation --- src/lib/i18n/locales/he-IL/translation.json | 154 ++++++++++---------- 1 file changed, 77 insertions(+), 77 deletions(-) diff --git a/src/lib/i18n/locales/he-IL/translation.json b/src/lib/i18n/locales/he-IL/translation.json index 6af838268e..8bbf25c978 100644 --- a/src/lib/i18n/locales/he-IL/translation.json +++ b/src/lib/i18n/locales/he-IL/translation.json @@ -5,7 +5,7 @@ "(e.g. `sh webui.sh --api`)": "(למשל `sh webui.sh --api`)", "(latest)": "(האחרון)", "(leave blank for to use commercial endpoint)": "", - "{{ models }}": "{{ דגמים }}", + "{{ models }}": "{{ מודלים }}", "{{COUNT}} Available Tools": "", "{{COUNT}} hidden lines": "", "{{COUNT}} Replies": "", @@ -17,39 +17,39 @@ "a user": "משתמש", "About": "אודות", "Accept autocomplete generation / Jump to prompt variable": "", - "Access": "", - "Access Control": "", + "Access": "גישה", + "Access Control": "בקרת גישה", "Accessible to all users": "", "Account": "חשבון", "Account Activation Pending": "", "Accurate information": "מידע מדויק", - "Actions": "", + "Actions": "פעולה", "Activate": "", "Activate this command by typing \"/{{COMMAND}}\" to chat input.": "", - "Active Users": "", + "Active Users": "משתמשים מחוברים", "Add": "הוסף", "Add a model ID": "", "Add a short description about what this model does": "הוסף תיאור קצר אודות אופן הפעולה של מודל זה", "Add a tag": "הוסף תג", "Add Arena Model": "", "Add Connection": "", - "Add Content": "", - "Add content here": "", + "Add Content": "הוסף תוכן", + "Add content here": "הוסף תוכן כאן", "Add Custom Parameter": "", "Add custom prompt": "הוסף פקודה מותאמת אישית", "Add Files": "הוסף קבצים", - "Add Group": "", + "Add Group": "הוסף קבוצה", "Add Memory": "הוסף זיכרון", "Add Model": "הוסף מודל", "Add Reaction": "", - "Add Tag": "", + "Add Tag": "הוסף תג", "Add Tags": "הוסף תגים", "Add text content": "", "Add User": "הוסף משתמש", "Add User Group": "", "Adjusting these settings will apply changes universally to all users.": "התאמת הגדרות אלו תחול על כל המשתמשים.", "admin": "מנהל", - "Admin": "", + "Admin": "מנהל", "Admin Panel": "לוח בקרה למנהל", "Admin Settings": "הגדרות מנהל", "Admins have access to all tools at all times; users need tools assigned per model in the workspace.": "", @@ -57,15 +57,15 @@ "Advanced Params": "פרמטרים מתקדמים", "All": "", "All Documents": "כל המסמכים", - "All models deleted successfully": "", + "All models deleted successfully": "כל המודלים נמחקו בהצלחה", "Allow Call": "", "Allow Chat Controls": "", "Allow Chat Delete": "", "Allow Chat Deletion": "אפשר מחיקת צ'אט", - "Allow Chat Edit": "", + "Allow Chat Edit": "אפשר עריכת צ'אט", "Allow Chat Export": "", - "Allow Chat Share": "", - "Allow File Upload": "", + "Allow Chat Share": "אפשר שיתוף צ'אט", + "Allow File Upload": "אפשר העלאת קובץ", "Allow Multiple Models in Chat": "", "Allow non-local voices": "", "Allow Speech to Text": "", @@ -78,11 +78,11 @@ "Allowed file extensions for upload. Separate multiple extensions with commas. Leave empty for all file types.": "", "Already have an account?": "כבר יש לך חשבון?", "Alternative to the top_p, and aims to ensure a balance of quality and variety. The parameter p represents the minimum probability for a token to be considered, relative to the probability of the most likely token. For example, with p=0.05 and the most likely token having a probability of 0.9, logits with a value less than 0.045 are filtered out.": "", - "Always": "", + "Always": "תמיד", "Always Collapse Code Blocks": "", "Always Expand Details": "", "Always Play Notification Sound": "", - "Amazing": "", + "Amazing": "מדהים", "an assistant": "עוזר", "Analyzed": "", "Analyzing...": "", @@ -124,7 +124,7 @@ "Auth": "", "Authenticate": "", "Authentication": "", - "Auto": "", + "Auto": "אוטומטי", "Auto-Copy Response to Clipboard": "העתקה אוטומטית של תגובה ללוח", "Auto-playback response": "תגובת השמעה אוטומטית", "Autocomplete Generation": "", @@ -134,7 +134,7 @@ "AUTOMATIC1111 Base URL": "כתובת URL בסיסית של AUTOMATIC1111", "AUTOMATIC1111 Base URL is required.": "נדרשת כתובת URL בסיסית של AUTOMATIC1111", "Available list": "", - "Available Tools": "", + "Available Tools": "כלים זמינים", "available!": "זמין!", "Awful": "", "Azure AI Speech": "", @@ -145,7 +145,7 @@ "Base Model (From)": "דגם בסיס (מ)", "before": "לפני", "Being lazy": "להיות עצלן", - "Beta": "", + "Beta": "בטא", "Bing Search V7 Endpoint": "", "Bing Search V7 Subscription Key": "", "Bocha Search API Key": "", @@ -155,10 +155,10 @@ "By {{name}}": "", "Bypass Embedding and Retrieval": "", "Bypass Web Loader": "", - "Calendar": "", + "Calendar": "לוח שנה", "Call": "", "Call feature is not supported when using Web STT engine": "", - "Camera": "", + "Camera": "מצלמה", "Cancel": "בטל", "Capabilities": "יכולות", "Capture": "", @@ -188,18 +188,18 @@ "Ciphers": "", "Citation": "ציטוט", "Citations": "", - "Clear memory": "", - "Clear Memory": "", - "click here": "", + "Clear memory": "נקה זיכרון", + "Clear Memory": "נקה", + "click here": "לחץ פה", "Click here for filter guides.": "", "Click here for help.": "לחץ כאן לעזרה.", "Click here to": "לחץ כאן כדי", "Click here to download user import template file.": "", - "Click here to learn more about faster-whisper and see the available models.": "", - "Click here to see available models.": "", + "Click here to learn more about faster-whisper and see the available models.": "לחץ כאן כדי ללמוד עוד על faster-whisper ולראות מודלים זמינים", + "Click here to see available models.": "לחץ כאן כדי לראות מודלים זמינים", "Click here to select": "לחץ כאן לבחירה", "Click here to select a csv file.": "לחץ כאן לבחירת קובץ csv.", - "Click here to select a py file.": "", + "Click here to select a py file.": "לחץ כאן כדי לבחירת קובץ py", "Click here to upload a workflow.json file.": "", "click here.": "לחץ כאן.", "Click on the user role button to change a user's role.": "לחץ על כפתור תפקיד המשתמש כדי לשנות את תפקיד המשתמש.", @@ -208,9 +208,9 @@ "Clone Chat": "", "Clone of {{TITLE}}": "", "Close": "סגור", - "Code execution": "", - "Code Execution": "", - "Code Execution Engine": "", + "Code execution": "הרצת קוד", + "Code Execution": "הרצת קוד", + "Code Execution Engine": "מנוע הרצת קוד", "Code Execution Timeout": "", "Code formatted successfully": "", "Code Interpreter": "", @@ -218,9 +218,9 @@ "Code Interpreter Prompt Template": "", "Collapse": "", "Collection": "אוסף", - "Color": "", + "Color": "צבע", "ComfyUI": "ComfyUI", - "ComfyUI API Key": "", + "ComfyUI API Key": "מפתח API כל ComfyUI", "ComfyUI Base URL": "כתובת URL בסיסית של ComfyUI", "ComfyUI Base URL is required.": "נדרשת כתובת URL בסיסית של ComfyUI", "ComfyUI Workflow": "", @@ -231,31 +231,31 @@ "Configure": "", "Confirm": "", "Confirm Password": "אשר סיסמה", - "Confirm your action": "", - "Confirm your new password": "", + "Confirm your action": "אשר את הפעולה שלך", + "Confirm your new password": "אשר את הסיסמה החדשה שלך", "Connect to your own OpenAI compatible API endpoints.": "", "Connect to your own OpenAPI compatible external tool servers.": "", - "Connection failed": "", - "Connection successful": "", - "Connection Type": "", + "Connection failed": "החיבור נכשל", + "Connection successful": "החיבור הצליח", + "Connection Type": "סוג חיבור", "Connections": "חיבורים", - "Connections saved successfully": "", + "Connections saved successfully": "החיבור נשמר בהצלחה", "Constrains effort on reasoning for reasoning models. Only applicable to reasoning models from specific providers that support reasoning effort.": "", "Contact Admin for WebUI Access": "", "Content": "תוכן", "Content Extraction Engine": "", "Continue Response": "המשך תגובה", - "Continue with {{provider}}": "", - "Continue with Email": "", - "Continue with LDAP": "", + "Continue with {{provider}}": "המשך עם {{provider}}", + "Continue with Email": "המשך עם מייל", + "Continue with LDAP": "המשך עם LDAP", "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.": "", "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.": "", "Controls": "", "Controls the balance between coherence and diversity of the output. A lower value will result in more focused and coherent text.": "", - "Copied": "", + "Copied": "הועתק", "Copied link to clipboard": "", "Copied shared chat URL to clipboard!": "העתקת כתובת URL של צ'אט משותף ללוח!", - "Copied to clipboard": "", + "Copied to clipboard": "הועתק ללוח", "Copy": "העתק", "Copy Formatted Text": "", "Copy last code block": "העתק את בלוק הקוד האחרון", @@ -270,11 +270,11 @@ "Create Account": "צור חשבון", "Create Admin Account": "", "Create Channel": "", - "Create Group": "", + "Create Group": "יצירת קבוצה", "Create Knowledge": "", "Create new key": "צור מפתח חדש", "Create new secret key": "צור מפתח סודי חדש", - "Create Note": "", + "Create Note": "יצירת פתק", "Create your first note by clicking on the plus button below.": "", "Created at": "נוצר ב", "Created At": "נוצר ב", @@ -642,10 +642,10 @@ "Generate Image": "", "Generate prompt pair": "", "Generating search query": "יצירת שאילתת חיפוש", - "Generating...": "", + "Generating...": "מג'נרט...", "Get started": "", "Get started with {{WEBUI_NAME}}": "", - "Global": "", + "Global": "גלובלי", "Good Response": "תגובה טובה", "Google Drive": "", "Google PSE API Key": "מפתח API של Google PSE", @@ -655,7 +655,7 @@ "Group Description": "", "Group Name": "", "Group updated successfully": "", - "Groups": "", + "Groups": "קבוצות", "Haptic Feedback": "", "Hello, {{name}}": "שלום, {{name}}", "Help": "עזרה", @@ -663,9 +663,9 @@ "Hex Color": "", "Hex Color - Leave empty for default color": "", "Hide": "הסתר", - "Hide Model": "", + "Hide Model": "הסתר מודל", "High Contrast Mode": "", - "Home": "", + "Home": "בית", "Host": "", "How can I help you today?": "כיצד אוכל לעזור לך היום?", "How would you rate this response?": "", @@ -676,7 +676,7 @@ "iframe Sandbox Allow Forms": "", "iframe Sandbox Allow Same Origin": "", "Ignite curiosity": "", - "Image": "", + "Image": "תמונה", "Image Compression": "", "Image Generation": "", "Image Generation (Experimental)": "יצירת תמונות (ניסיוני)", @@ -692,11 +692,11 @@ "Import From Link": "", "Import Functions": "", "Import Models": "ייבוא דגמים", - "Import Notes": "", + "Import Notes": "ייבוא פתקים", "Import Presets": "", "Import Prompt Suggestions": "", - "Import Prompts": "יבוא פקודות", - "Import Tools": "", + "Import Prompts": "ייבוא פקודות", + "Import Tools": "ייבוא כלים", "Include": "", "Include `--api-auth` flag when running stable-diffusion-webui": "", "Include `--api` flag when running stable-diffusion-webui": "כלול את הדגל `--api` בעת הרצת stable-diffusion-webui", @@ -713,7 +713,7 @@ "Invalid JSON file": "", "Invalid JSON schema": "", "Invalid Tag": "תג לא חוקי", - "is typing...": "", + "is typing...": "מקליד...", "January": "ינואר", "Jina API Key": "", "join our Discord for help.": "הצטרף ל-Discord שלנו לעזרה.", @@ -757,7 +757,7 @@ "Leave empty to include all models or select specific models": "", "Leave empty to use the default prompt, or enter a custom prompt": "", "Leave model field empty to use the default model.": "", - "License": "", + "License": "רישיון", "Light": "בהיר", "Listening...": "", "Llama.cpp": "", @@ -795,7 +795,7 @@ "Memory updated successfully": "", "Merge Responses": "", "Merged Response": "תגובה ממוזגת", - "Message rating should be enabled to use this feature": "", + "Message rating should be enabled to use this feature": "דירוג הודעות צריך להיות מאופשר כדי להשתמש בפיצ'ר הזה", "Messages you send after creating your link won't be shared. Users with the URL will be able to view the shared chat.": "הודעות שתשלח לאחר יצירת הקישור לא ישותפו. משתמשים עם כתובת האתר יוכלו לצפות בצ'אט המשותף.", "Microsoft OneDrive": "", "Microsoft OneDrive (personal)": "", @@ -820,9 +820,9 @@ "Model Filtering": "", "Model ID": "מזהה דגם", "Model IDs": "", - "Model Name": "", + "Model Name": "שם המודל", "Model not selected": "לא נבחר מודל", - "Model Params": "פרמס מודל", + "Model Params": "פרמטרי המודל", "Model Permissions": "", "Model unloaded successfully": "", "Model updated successfully": "", @@ -835,22 +835,22 @@ "Mojeek Search API Key": "", "more": "", "More": "עוד", - "My Notes": "", + "My Notes": "הפתקים שלי", "Name": "שם", "Name your knowledge base": "", "Native": "", "New Chat": "צ'אט חדש", - "New Folder": "", - "New Function": "", - "New Note": "", + "New Folder": "תיקייה חדשה", + "New Function": "פונקציה חדשה", + "New Note": "פתק חדש", "New Password": "סיסמה חדשה", - "New Tool": "", + "New Tool": "כלי חדש", "new-channel": "", - "No chats found for this user.": "", - "No chats found.": "", - "No content": "", - "No content found": "", - "No content found in file.": "", + "No chats found for this user.": "לא נמצאו צ'אטים ליוזר הזה.", + "No chats found.": "לא נמצאו צ'אטים", + "No content": "אין תוכן", + "No content found": "תוכן לא נמצא", + "No content found in file.": "לא נמצא תוכן בקובץ.", "No content to speak": "", "No distance available": "", "No feedbacks found": "", @@ -867,14 +867,14 @@ "No results found": "לא נמצאו תוצאות", "No search query generated": "לא נוצרה שאילתת חיפוש", "No source available": "אין מקור זמין", - "No users were found.": "", + "No users were found.": "לא נמצאו יוזרים", "No valves to update": "", "None": "ללא", "Not factually correct": "לא נכון מבחינה עובדתית", "Not helpful": "", - "Note deleted successfully": "", + "Note deleted successfully": "פתק נמחק בהצלחה", "Note: If you set a minimum score, the search will only return documents with a score greater than or equal to the minimum score.": "הערה: אם תקבע ציון מינימלי, החיפוש יחזיר רק מסמכים עם ציון שגבוה או שווה לציון המינימלי.", - "Notes": "", + "Notes": "פתקים", "Notification Sound": "", "Notification Webhook": "", "Notifications": "התראות", @@ -920,7 +920,7 @@ "Output format": "", "Output Format": "", "Overview": "", - "page": "", + "page": "עמוד", "Paginate": "", "Parameters": "", "Password": "סיסמה", @@ -1068,7 +1068,7 @@ "Select a model": "בחר מודל", "Select a pipeline": "בחר קו צינור", "Select a pipeline url": "בחר כתובת URL של קו צינור", - "Select a tool": "", + "Select a tool": "בחר כלי", "Select an auth method": "", "Select an Ollama instance": "", "Select Engine": "", @@ -1188,7 +1188,7 @@ "The score should be a value between 0.0 (0%) and 1.0 (100%).": "ציון צריך להיות ערך בין 0.0 (0%) ל-1.0 (100%)", "The temperature of the model. Increasing the temperature will make the model answer more creatively.": "", "Theme": "נושא", - "Thinking...": "", + "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.": "", "This chat won’t appear in history and your messages will not be saved.": "", @@ -1216,7 +1216,7 @@ "Title Auto-Generation": "יצירת שם אוטומטית", "Title cannot be an empty string.": "שם לא יכול להיות מחרוזת ריקה.", "Title Generation": "", - "Title Generation Prompt": "שאלה ליצירת שם", + "Title Generation Prompt": "פרומפט ליצירת כותרת", "TLS": "", "To access the available model names for downloading,": "כדי לגשת לשמות הדגמים הזמינים להורדה,", "To access the GGUF models available for downloading,": "כדי לגשת לדגמי GGUF הזמינים להורדה,", @@ -1242,7 +1242,7 @@ "Tool Name": "", "Tool Servers": "", "Tool updated successfully": "", - "Tools": "", + "Tools": "כלים", "Tools Access": "", "Tools are a function calling system with arbitrary code execution": "", "Tools Function Calling Prompt": "", From 1ddc784af919954fbbbd54a7aa1c766d6c2d86cc Mon Sep 17 00:00:00 2001 From: Diwakar Singh Maurya Date: Sat, 31 May 2025 19:13:41 +0000 Subject: [PATCH 012/180] feat: Add route for each tab in Settings --- src/lib/components/admin/Settings.svelte | 35 ++++++++++++------- src/routes/(app)/admin/settings/+page.svelte | 6 ++++ .../(app)/admin/settings/audio/+page.svelte | 5 +++ .../settings/code-execution/+page.svelte | 5 +++ .../admin/settings/connections/+page.svelte | 5 +++ .../(app)/admin/settings/db/+page.svelte | 5 +++ .../admin/settings/documents/+page.svelte | 5 +++ .../admin/settings/evaluations/+page.svelte | 5 +++ .../(app)/admin/settings/general/+page.svelte | 5 +++ .../(app)/admin/settings/images/+page.svelte | 5 +++ .../admin/settings/interface/+page.svelte | 5 +++ .../(app)/admin/settings/models/+page.svelte | 5 +++ .../admin/settings/pipelines/+page.svelte | 5 +++ .../(app)/admin/settings/tools/+page.svelte | 5 +++ .../(app)/admin/settings/web/+page.svelte | 5 +++ 15 files changed, 93 insertions(+), 13 deletions(-) create mode 100644 src/routes/(app)/admin/settings/audio/+page.svelte create mode 100644 src/routes/(app)/admin/settings/code-execution/+page.svelte create mode 100644 src/routes/(app)/admin/settings/connections/+page.svelte create mode 100644 src/routes/(app)/admin/settings/db/+page.svelte create mode 100644 src/routes/(app)/admin/settings/documents/+page.svelte create mode 100644 src/routes/(app)/admin/settings/evaluations/+page.svelte create mode 100644 src/routes/(app)/admin/settings/general/+page.svelte create mode 100644 src/routes/(app)/admin/settings/images/+page.svelte create mode 100644 src/routes/(app)/admin/settings/interface/+page.svelte create mode 100644 src/routes/(app)/admin/settings/models/+page.svelte create mode 100644 src/routes/(app)/admin/settings/pipelines/+page.svelte create mode 100644 src/routes/(app)/admin/settings/tools/+page.svelte create mode 100644 src/routes/(app)/admin/settings/web/+page.svelte diff --git a/src/lib/components/admin/Settings.svelte b/src/lib/components/admin/Settings.svelte index c26604d6c0..564d81c851 100644 --- a/src/lib/components/admin/Settings.svelte +++ b/src/lib/components/admin/Settings.svelte @@ -1,5 +1,7 @@ diff --git a/src/routes/(app)/admin/settings/audio/+page.svelte b/src/routes/(app)/admin/settings/audio/+page.svelte new file mode 100644 index 0000000000..a0a86f4356 --- /dev/null +++ b/src/routes/(app)/admin/settings/audio/+page.svelte @@ -0,0 +1,5 @@ + + + diff --git a/src/routes/(app)/admin/settings/code-execution/+page.svelte b/src/routes/(app)/admin/settings/code-execution/+page.svelte new file mode 100644 index 0000000000..a0a86f4356 --- /dev/null +++ b/src/routes/(app)/admin/settings/code-execution/+page.svelte @@ -0,0 +1,5 @@ + + + diff --git a/src/routes/(app)/admin/settings/connections/+page.svelte b/src/routes/(app)/admin/settings/connections/+page.svelte new file mode 100644 index 0000000000..a0a86f4356 --- /dev/null +++ b/src/routes/(app)/admin/settings/connections/+page.svelte @@ -0,0 +1,5 @@ + + + diff --git a/src/routes/(app)/admin/settings/db/+page.svelte b/src/routes/(app)/admin/settings/db/+page.svelte new file mode 100644 index 0000000000..a0a86f4356 --- /dev/null +++ b/src/routes/(app)/admin/settings/db/+page.svelte @@ -0,0 +1,5 @@ + + + diff --git a/src/routes/(app)/admin/settings/documents/+page.svelte b/src/routes/(app)/admin/settings/documents/+page.svelte new file mode 100644 index 0000000000..a0a86f4356 --- /dev/null +++ b/src/routes/(app)/admin/settings/documents/+page.svelte @@ -0,0 +1,5 @@ + + + diff --git a/src/routes/(app)/admin/settings/evaluations/+page.svelte b/src/routes/(app)/admin/settings/evaluations/+page.svelte new file mode 100644 index 0000000000..a0a86f4356 --- /dev/null +++ b/src/routes/(app)/admin/settings/evaluations/+page.svelte @@ -0,0 +1,5 @@ + + + diff --git a/src/routes/(app)/admin/settings/general/+page.svelte b/src/routes/(app)/admin/settings/general/+page.svelte new file mode 100644 index 0000000000..a0a86f4356 --- /dev/null +++ b/src/routes/(app)/admin/settings/general/+page.svelte @@ -0,0 +1,5 @@ + + + diff --git a/src/routes/(app)/admin/settings/images/+page.svelte b/src/routes/(app)/admin/settings/images/+page.svelte new file mode 100644 index 0000000000..a0a86f4356 --- /dev/null +++ b/src/routes/(app)/admin/settings/images/+page.svelte @@ -0,0 +1,5 @@ + + + diff --git a/src/routes/(app)/admin/settings/interface/+page.svelte b/src/routes/(app)/admin/settings/interface/+page.svelte new file mode 100644 index 0000000000..a0a86f4356 --- /dev/null +++ b/src/routes/(app)/admin/settings/interface/+page.svelte @@ -0,0 +1,5 @@ + + + diff --git a/src/routes/(app)/admin/settings/models/+page.svelte b/src/routes/(app)/admin/settings/models/+page.svelte new file mode 100644 index 0000000000..a0a86f4356 --- /dev/null +++ b/src/routes/(app)/admin/settings/models/+page.svelte @@ -0,0 +1,5 @@ + + + diff --git a/src/routes/(app)/admin/settings/pipelines/+page.svelte b/src/routes/(app)/admin/settings/pipelines/+page.svelte new file mode 100644 index 0000000000..a0a86f4356 --- /dev/null +++ b/src/routes/(app)/admin/settings/pipelines/+page.svelte @@ -0,0 +1,5 @@ + + + diff --git a/src/routes/(app)/admin/settings/tools/+page.svelte b/src/routes/(app)/admin/settings/tools/+page.svelte new file mode 100644 index 0000000000..a0a86f4356 --- /dev/null +++ b/src/routes/(app)/admin/settings/tools/+page.svelte @@ -0,0 +1,5 @@ + + + diff --git a/src/routes/(app)/admin/settings/web/+page.svelte b/src/routes/(app)/admin/settings/web/+page.svelte new file mode 100644 index 0000000000..a0a86f4356 --- /dev/null +++ b/src/routes/(app)/admin/settings/web/+page.svelte @@ -0,0 +1,5 @@ + + + From 96d8a52fe3f336ddc2aabbc74fb1cb56b9ba517d Mon Sep 17 00:00:00 2001 From: Peter Dave Hello Date: Sun, 1 Jun 2025 04:08:33 +0800 Subject: [PATCH 013/180] i18n: update and improve zh-TW Traditional Chinese locale --- src/lib/i18n/locales/zh-TW/translation.json | 146 ++++++++++---------- 1 file changed, 73 insertions(+), 73 deletions(-) diff --git a/src/lib/i18n/locales/zh-TW/translation.json b/src/lib/i18n/locales/zh-TW/translation.json index a332e2f72d..a75737b3a4 100644 --- a/src/lib/i18n/locales/zh-TW/translation.json +++ b/src/lib/i18n/locales/zh-TW/translation.json @@ -1,6 +1,6 @@ { - "-1 for no limit, or a positive integer for a specific limit": "-1 表示無限制,或正整數表示特定限制", - "'s', 'm', 'h', 'd', 'w' or '-1' for no expiration.": "'s'、'm'、'h'、'd'、'w' 或 '-1' 表示無到期時間。", + "-1 for no limit, or a positive integer for a specific limit": "-1 表示無限制,正整數表示特定限制", + "'s', 'm', 'h', 'd', 'w' or '-1' for no expiration.": "'s'、'm'、'h'、'd'、'w' , '-1' 表示無到期時間。", "(e.g. `sh webui.sh --api --api-auth username_password`)": "(例如:`sh webui.sh --api --api-auth username_password`)", "(e.g. `sh webui.sh --api`)": "(例如:`sh webui.sh --api`)", "(latest)": "(最新版)", @@ -10,10 +10,10 @@ "{{COUNT}} hidden lines": "已隱藏 {{COUNT}} 行", "{{COUNT}} Replies": "{{COUNT}} 回覆", "{{user}}'s Chats": "{{user}} 的對話", - "{{webUIName}} Backend Required": "需要 {{webUIName}} 後端", + "{{webUIName}} Backend Required": "需要提供 {{webUIName}} 後端", "*Prompt node ID(s) are required for image generation": "* 圖片生成需要提示詞節點 ID", - "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 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": "使用者", "About": "關於", "Accept autocomplete generation / Jump to prompt variable": "接受自動完成生成/跳轉至提示變數", @@ -90,7 +90,7 @@ "and {{COUNT}} more": "和另外 {{COUNT}} 個", "and create a new shared link.": "並建立新的共用連結。", "Android": "Android", - "API Base URL": "API Base URL", + "API Base URL": "API 基底 URL", "API Key": "API 金鑰", "API Key created.": "API 金鑰已建立。", "API Key Endpoint Restrictions": "API 金鑰端點限制", @@ -111,7 +111,7 @@ "Are you sure you want to update this user's role to **{{ROLE}}**?": "您確定要將此使用者的角色更新為「{{ROLE}}」嗎?", "Are you sure?": "您確定嗎?", "Arena Models": "競技場模型", - "Artifacts": "成品", + "Artifacts": "Artifacts", "Ask": "提問", "Ask a question": "提出問題", "Assistant": "助理", @@ -131,8 +131,8 @@ "Autocomplete Generation Input Max Length": "自動完成輸入最大長度", "Automatic1111": "Automatic1111", "AUTOMATIC1111 Api Auth String": "AUTOMATIC1111 API 驗證字串", - "AUTOMATIC1111 Base URL": "AUTOMATIC1111 Base URL", - "AUTOMATIC1111 Base URL is required.": "需要 AUTOMATIC1111 Base URL。", + "AUTOMATIC1111 Base URL": "AUTOMATIC1111 基底 URL", + "AUTOMATIC1111 Base URL is required.": "需要提供 AUTOMATIC1111 基底 URL。", "Available list": "可用清單", "Available Tools": "可用工具", "available!": "可用!", @@ -212,7 +212,7 @@ "Code Execution": "程式碼執行", "Code Execution Engine": "程式碼執行引擎", "Code Execution Timeout": "程式執行超時", - "Code formatted successfully": "程式碼格式化成功", + "Code formatted successfully": "成功格式化程式碼", "Code Interpreter": "程式碼直譯器", "Code Interpreter Engine": "程式碼直譯器引擎", "Code Interpreter Prompt Template": "程式碼直譯器提示詞範本", @@ -221,8 +221,8 @@ "Color": "顏色", "ComfyUI": "ComfyUI", "ComfyUI API Key": "ComfyUI API 金鑰", - "ComfyUI Base URL": "ComfyUI Base URL", - "ComfyUI Base URL is required.": "需要 ComfyUI Base URL。", + "ComfyUI Base URL": "ComfyUI 基底 URL", + "ComfyUI Base URL is required.": "需要提供 ComfyUI 基底 URL。", "ComfyUI Workflow": "ComfyUI 工作流程", "ComfyUI Workflow Nodes": "ComfyUI 工作流程節點", "Command": "命令", @@ -275,7 +275,7 @@ "Create new key": "建立新的金鑰", "Create new secret key": "建立新的金鑰", "Create Note": "新增筆記", - "Create your first note by clicking on the plus button below.": "點擊下方加號按鈕建立您的第一則筆記。", + "Create your first note by clicking on the plus button below.": "點選下方加號按鈕建立您的第一則筆記。", "Created at": "建立於", "Created At": "建立於", "Created by": "建立者", @@ -300,7 +300,7 @@ "Default model updated": "預設模型已更新", "Default Models": "預設模型", "Default permissions": "預設權限", - "Default permissions updated successfully": "預設權限更新成功", + "Default permissions updated successfully": "成功更新預設權限", "Default Prompt Suggestions": "預設提示詞建議", "Default to 389 or 636 if TLS is enabled": "如果啟用了 TLS 則預設為 389 或 636", "Default to ALL": "預設到所有", @@ -338,7 +338,7 @@ "Direct Connections settings updated": "直接連線設定已更新。", "Direct Tool Servers": "直連工具伺服器", "Disable Image Extraction": "停用圖片擷取", - "Disable image extraction from the PDF. If Use LLM is enabled, images will be automatically captioned. Defaults to False.": "停用從 PDF 擷取圖片。若啟用「使用 LLM」,圖片將自動添加說明。預設為 False。", + "Disable image extraction from the PDF. If Use LLM is enabled, images will be automatically captioned. Defaults to False.": "停用從 PDF 擷取圖片。若啟用「使用 LLM」,圖片將自動新增說明。預設為 False。", "Disabled": "已停用", "Discover a function": "發掘函式", "Discover a model": "發掘模型", @@ -355,15 +355,15 @@ "Display Emoji in Call": "在通話中顯示表情符號", "Display the username instead of You in the Chat": "在對話中顯示使用者名稱,而非「您」", "Displays citations in the response": "在回應中顯示引用", - "Dive into knowledge": "深入知識", + "Dive into knowledge": "挖掘知識", "Do not install functions from sources you do not fully trust.": "請勿從您無法完全信任的來源安裝函式。", "Do not install tools from sources you do not fully trust.": "請勿從您無法完全信任的來源安裝工具。", "Docling": "Docling", - "Docling Server URL required.": "Docling 伺服器 URL 為必填。", + "Docling Server URL required.": "需要提供 Docling 伺服器 URL。", "Document": "文件", "Document Intelligence": "Document Intelligence", - "Document Intelligence endpoint and key required.": "需要提供 Document Intelligence 端點及金鑰", - "Documentation": "幫助文件", + "Document Intelligence endpoint and key required.": "需要提供 Document Intelligence 端點及金鑰。", + "Documentation": "說明文件", "Documents": "文件", "does not make any external connections, and your data stays securely on your locally hosted server.": "不會建立任何外部連線,而且您的資料會安全地儲存在您本機伺服器上。", "Domain Filter List": "網域篩選列表", @@ -453,13 +453,13 @@ "Enter Document Intelligence Key": "輸入 Document Intelligence 金鑰", "Enter domains separated by commas (e.g., example.com,site.org)": "輸入網域,以逗號分隔(例如:example.com, site.org)", "Enter Exa API Key": "輸入 Exa API 金鑰", - "Enter External Document Loader API Key": "請輸入外部文件加載器 API 金鑰", - "Enter External Document Loader URL": "請輸入外部文件加載器 URL", + "Enter External Document Loader API Key": "請輸入外部文件載入器 API 金鑰", + "Enter External Document Loader URL": "請輸入外部文件載入器 URL", "Enter External Web Loader API Key": "輸入外部網頁載入器 API 金鑰", "Enter External Web Loader URL": "輸入外部網頁載入器 URL", "Enter External Web Search API Key": "輸入外部網路搜尋 API 金鑰", "Enter External Web Search URL": "輸入外部網路搜尋 URL", - "Enter Firecrawl API Base URL": "輸入 Firecrawl API Base URL", + "Enter Firecrawl API Base URL": "輸入 Firecrawl API 基底 URL", "Enter Firecrawl API Key": "輸入 Firecrawl API 金鑰", "Enter Github Raw URL": "輸入 GitHub Raw URL", "Enter Google PSE API Key": "輸入 Google PSE API 金鑰", @@ -566,7 +566,7 @@ "Export to CSV": "匯出為 CSV", "Export Tools": "匯出工具", "External": "外部", - "External Document Loader URL required.": "需要提供外部文件加載器 URL。", + "External Document Loader URL required.": "需要提供外部文件載入器 URL。", "External Task Model": "外部任務模型", "External Web Loader API Key": "外部網頁載入器 API 金鑰", "External Web Loader URL": "外部網頁載入器 URL", @@ -591,26 +591,26 @@ "Feedbacks": "回饋", "Feel free to add specific details": "歡迎自由新增特定細節", "File": "檔案", - "File added successfully.": "檔案新增成功。", - "File content updated successfully.": "檔案內容更新成功。", + "File added successfully.": "成功新增檔案。", + "File content updated successfully.": "成功更新檔案內容。", "File Mode": "檔案模式", "File not found.": "未找到檔案。", "File removed successfully.": "成功移除檔案。", "File size should not exceed {{maxSize}} MB.": "檔案大小不應超過 {{maxSize}} MB。", "File Upload": "檔案上傳", - "File uploaded successfully": "檔案上傳成功", + "File uploaded successfully": "成功上傳檔案", "Files": "檔案", - "Filter is now globally disabled": "篩選器現在已全域停用", - "Filter is now globally enabled": "篩選器現在已全域啟用", + "Filter is now globally disabled": "篩選器已全域停用", + "Filter is now globally enabled": "篩選器已全域啟用", "Filters": "篩選器", "Fingerprint spoofing detected: Unable to use initials as avatar. Defaulting to default profile image.": "偵測到指紋偽造:無法使用姓名縮寫作為大頭貼。將預設為預設個人檔案圖片。", - "Firecrawl API Base URL": "Firecrawl API Base URL", + "Firecrawl API Base URL": "Firecrawl API 基底 URL", "Firecrawl API Key": "Firecrawl API 金鑰", "Fluidly stream large external response chunks": "流暢地串流大型外部回應區塊", "Focus chat input": "聚焦對話輸入", - "Folder deleted successfully": "資料夾刪除成功", + "Folder deleted successfully": "成功刪除資料夾", "Folder name cannot be empty.": "資料夾名稱不能為空。", - "Folder name updated successfully": "資料夾名稱更新成功", + "Folder name updated successfully": "成功更新資料夾名稱", "Followed instructions perfectly": "完全遵循指示", "Force OCR": "強制執行 OCR", "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.": "強制對 PDF 所有頁面執行 OCR。若原始 PDF 文字品質良好,此功能可能降低準確度。預設為 False。", @@ -625,9 +625,9 @@ "Function deleted successfully": "成功刪除函式", "Function Description": "函式描述", "Function ID": "函式 ID", - "Function imported successfully": "功能已成功匯入", - "Function is now globally disabled": "現在已在全域停用函式", - "Function is now globally enabled": "現在已在全域啟用函式", + "Function imported successfully": "成功匯入函式", + "Function is now globally disabled": "已全域停用函式", + "Function is now globally enabled": "已全域啟用函式", "Function Name": "函式名稱", "Function updated successfully": "成功更新函式", "Functions": "函式", @@ -635,7 +635,7 @@ "Functions imported successfully": "成功匯入函式", "Gemini": "Gemini", "Gemini API Config": "Gemini API 設定", - "Gemini API Key is required.": "必須提供 Gemini API 金鑰", + "Gemini API Key is required.": "需要提供 Gemini API 金鑰。", "General": "一般", "Generate": "生成", "Generate an image": "生成圖片", @@ -650,11 +650,11 @@ "Google Drive": "Google Drive", "Google PSE API Key": "Google PSE API 金鑰", "Google PSE Engine Id": "Google PSE 引擎 ID", - "Group created successfully": "群組建立成功", - "Group deleted successfully": "群組刪除成功", + "Group created successfully": "成功建立群組", + "Group deleted successfully": "成功刪除群組", "Group Description": "群組描述", "Group Name": "群組名稱", - "Group updated successfully": "群組更新成功", + "Group updated successfully": "成功更新群組", "Groups": "群組", "Haptic Feedback": "觸覺回饋", "Hello, {{name}}": "您好,{{name}}", @@ -716,12 +716,12 @@ "is typing...": "正在輸入……", "January": "1 月", "Jina API Key": "Jina API 金鑰", - "join our Discord for help.": "加入我們的 Discord 以尋求協助。", + "join our Discord for help.": "加入我們的 Discord 以取得協助。", "JSON": "JSON", "JSON Preview": "JSON 預覽", "July": "7 月", "June": "6 月", - "Jupyter Auth": "Jupyter Auth", + "Jupyter Auth": "Jupyter 驗證", "Jupyter URL": "Jupyter URL", "JWT Expiration": "JWT 過期時間", "JWT Token": "JWT Token", @@ -731,11 +731,11 @@ "Keyboard shortcuts": "鍵盤快捷鍵", "Knowledge": "知識", "Knowledge Access": "知識存取", - "Knowledge created successfully.": "知識建立成功。", - "Knowledge deleted successfully.": "知識刪除成功。", + "Knowledge created successfully.": "成功建立知識。", + "Knowledge deleted successfully.": "成功刪除知識。", "Knowledge Public Sharing": "知識公開分享", - "Knowledge reset successfully.": "知識重設成功。", - "Knowledge updated successfully": "知識更新成功", + "Knowledge reset successfully.": "成功重設知識。", + "Knowledge updated successfully": "成功更新知識", "Kokoro.js (Browser)": "Kokoro.js (瀏覽器)", "Kokoro.js Dtype": "Kokoro.js Dtype", "Label": "標籤", @@ -782,10 +782,10 @@ "Manage Tool Servers": "管理工具伺服器", "March": "3 月", "Markdown": "Markdown", - "Max Speakers": "最大發言者數", + "Max Speakers": "最大發言者數量", "Max Upload Count": "最大上傳數量", "Max Upload Size": "最大上傳大小", - "Maximum of 3 models can be downloaded simultaneously. Please try again later.": "最多可同時下載 3 個模型。請稍後再試。", + "Maximum of 3 models can be downloaded simultaneously. Please try again later.": "最多同時下載 3 個模型。請稍後再試。", "May": "5 月", "Memories accessible by LLMs will be shown here.": "可被大型語言模型存取的記憶將顯示在此。", "Memory": "記憶", @@ -801,7 +801,7 @@ "Microsoft OneDrive (personal)": "Microsoft OneDrive(個人版)", "Microsoft OneDrive (work/school)": "Microsoft OneDrive(公司版/學校版)", "Mistral OCR": "Mistral OCR", - "Mistral OCR API Key required.": "需要 Mistral OCR API 金鑰。", + "Mistral OCR API Key required.": "需要提供 Mistral OCR API 金鑰。", "Model": "模型", "Model '{{modelName}}' has been successfully downloaded.": "模型「{{modelName}}」已成功下載。", "Model '{{modelTag}}' is already in queue for downloading.": "模型「{{modelTag}}」已在下載佇列中。", @@ -813,7 +813,7 @@ "Model accepts file inputs": "模型支援檔案輸入", "Model accepts image inputs": "模型接受影像輸入", "Model can execute code and perform calculations": "模型可執行程式碼並進行運算", - "Model can generate images based on text prompts": "模型殼基於文字提示詞生成圖像", + "Model can generate images based on text prompts": "模型可基於文字提示詞生成影像", "Model can search the web for information": "模型可透過網路搜尋資訊", "Model created successfully!": "成功建立模型!", "Model filesystem path detected. Model shortname is required for update, cannot continue.": "偵測到模型檔案系統路徑。更新需要模型簡稱,因此無法繼續。", @@ -824,13 +824,13 @@ "Model not selected": "未選取模型", "Model Params": "模型參數", "Model Permissions": "模型權限", - "Model unloaded successfully": "模型卸載成功", + "Model unloaded successfully": "成功卸載模型", "Model updated successfully": "成功更新模型", - "Model(s) do not support file upload": "當前模型不支援檔案上傳", + "Model(s) do not support file upload": "模型不支援檔案上傳", "Modelfile Content": "模型檔案內容", "Models": "模型", "Models Access": "模型存取", - "Models configuration saved successfully": "模型設定儲存成功", + "Models configuration saved successfully": "成功儲存模型設定", "Models Public Sharing": "模型公開分享", "Mojeek Search API Key": "Mojeek 搜尋 API 金鑰", "more": "更多", @@ -841,7 +841,7 @@ "Native": "原生", "New Chat": "新增對話", "New Folder": "新增資料夾", - "New Function": "新增功能", + "New Function": "新增函式", "New Note": "新增筆記", "New Password": "新密碼", "New Tool": "新增工具", @@ -865,14 +865,14 @@ "No models selected": "未選取模型", "No Notes": "尚無筆記", "No results found": "未找到任何結果", - "No search query generated": "未生成搜尋查詢", - "No source available": "無可用源", + "No search query generated": "未產生搜尋查詢", + "No source available": "無可用來源", "No users were found.": "未找到任何使用者", "No valves to update": "無閥門可更新", "None": "無", "Not factually correct": "與事實不符", "Not helpful": "沒有幫助", - "Note deleted successfully": "筆記已成功刪除", + "Note deleted successfully": "已成功刪除筆記", "Note: If you set a minimum score, the search will only return documents with a score greater than or equal to the minimum score.": "注意:如果您設定了最低分數,則搜尋只會回傳分數大於或等於最低分數的檔案。", "Notes": "筆記", "Notification Sound": "通知聲音", @@ -909,10 +909,10 @@ "OpenAI": "OpenAI", "OpenAI API": "OpenAI API", "OpenAI API Config": "OpenAI API 設定", - "OpenAI API Key is required.": "需要 OpenAI API 金鑰。", + "OpenAI API Key is required.": "需要提供 OpenAI API 金鑰。", "OpenAI API settings updated": "OpenAI API 設定已更新", - "OpenAI URL/Key required.": "需要 OpenAI URL 或金鑰。", - "openapi.json URL or Path": "openapi.json URL or Path", + "OpenAI URL/Key required.": "需要提供 OpenAI URL 或金鑰。", + "openapi.json URL or Path": "openapi.json URL 或路徑", "or": "或", "Organize your users": "組織您的使用者", "Other": "其他", @@ -971,9 +971,9 @@ "Prompt (e.g. Tell me a fun fact about the Roman Empire)": "提示詞(例如:告訴我關於羅馬帝國的一些趣事)", "Prompt Autocompletion": "提示詞自動完成", "Prompt Content": "提示詞內容", - "Prompt created successfully": "提示詞建立成功", + "Prompt created successfully": "成功建立提示詞", "Prompt suggestions": "提示詞建議", - "Prompt updated successfully": "提示詞更新成功", + "Prompt updated successfully": "成功更新提示詞", "Prompts": "提示詞", "Prompts Access": "提示詞存取", "Prompts Public Sharing": "提示詞公開分享", @@ -990,15 +990,15 @@ "Record": "錄製", "Record voice": "錄音", "Redirecting you to Open WebUI Community": "正在將您重導向至 Open WebUI 社群", - "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.": "降低生成無意義內容的機率。較高的值(例如:100)會生成更多樣化的答案,而較低的值(例如:10)會更保守。", + "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.": "降低產生無意義內容的機率。較高的值(例如:100)會產生更多樣化的答案,而較低的值(例如:10)會更保守。", "Refer to yourself as \"User\" (e.g., \"User is learning Spanish\")": "以「使用者」稱呼自己(例如:「使用者正在學習西班牙文」)", "References from": "引用來源", "Refused when it shouldn't have": "不應拒絕時拒絕了", - "Regenerate": "重新生成", + "Regenerate": "重新產生回應", "Reindex": "重新索引", "Reindex Knowledge Base Vectors": "重新索引知識庫向量", - "Release Notes": "釋出説明", - "Releases": "釋出說明", + "Release Notes": "版本資訊", + "Releases": "版本資訊", "Relevance": "相關性", "Relevance Threshold": "相關性閾值", "Remove": "移除", @@ -1196,9 +1196,9 @@ "This is an experimental feature, it may not function as expected and is subject to change at any time.": "這是一個實驗性功能,它可能無法如預期運作,並且可能會隨時變更。", "This model is not publicly available. Please select another model.": "此模型未開放公眾使用,請選擇其他模型。", "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.": "此選項控制在重新整理上下文時保留多少 token。例如,如果設定為 2,則會保留對話上下文的最後 2 個 token。保留上下文有助於保持對話的連貫性,但也可能降低對新主題的回應能力。", - "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.": "此選項設定模型在其回應中可以生成的最大 token 數量。增加此限制允許模型提供更長的答案,但也可能增加生成無用或不相關內容的可能性。", + "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.": "此選項設定模型在其回應中可以生成的最大 token 數量。增加此限制允許模型提供更長的答案,但也可能增加產生無用或不相關內容的可能性。", "This option will delete all existing files in the collection and replace them with newly uploaded files.": "此選項將刪除集合中的所有現有檔案,並用新上傳的檔案取代它們。", - "This response was generated by \"{{model}}\"": "此回應由「{{model}}」生成", + "This response was generated by \"{{model}}\"": "此回應由「{{model}}」產生", "This will delete": "這將會刪除", "This will delete {{NAME}} and all its contents.": "這將會刪除 {{NAME}}其所有內容。", "This will delete all models including custom models": "這將刪除所有模型,包括自訂模型", @@ -1208,15 +1208,15 @@ "Thought for {{DURATION}}": "思考時間 {{DURATION}}", "Thought for {{DURATION}} seconds": "思考時間 {{DURATION}} 秒", "Tika": "Tika", - "Tika Server URL required.": "需要 Tika 伺服器 URL。", + "Tika Server URL required.": "需要提供 Tika 伺服器 URL。", "Tiktoken": "Tiktoken", "Tip: Update multiple variable slots consecutively by pressing the tab key in the chat input after each replacement.": "提示:在每次替換後按下對話輸入框中的 Tab 鍵,即可連續更新多個變數欄位。", "Title": "標題", "Title (e.g. Tell me a fun fact)": "標題(例如:告訴我一個有趣的事實)", - "Title Auto-Generation": "自動生成標題", + "Title Auto-Generation": "自動產生標題", "Title cannot be an empty string.": "標題不能是空字串。", - "Title Generation": "生成標題", - "Title Generation Prompt": "生成標題的提示詞", + "Title Generation": "產生標題", + "Title Generation Prompt": "產生標題的提示詞", "TLS": "TLS", "To access the available model names for downloading,": "若要存取可供下載的模型名稱,", "To access the GGUF models available for downloading,": "若要存取可供下載的 GGUF 模型,", @@ -1306,7 +1306,7 @@ "Valid time units:": "有效的時間單位:", "Valves": "閥門", "Valves updated": "閥門已更新", - "Valves updated successfully": "閥門更新成功", + "Valves updated successfully": "成功閥門更新", "variable": "變數", "variable to have them replaced with clipboard content.": "變數,以便將其替換為剪貼簿內容。", "Verify Connection": "驗證連線", @@ -1349,14 +1349,14 @@ "Why?": "為什麼?", "Widescreen Mode": "寬螢幕模式", "Won": "獲勝", - "Works together with top-k. A higher value (e.g., 0.95) will lead to more diverse text, while a lower value (e.g., 0.5) will generate more focused and conservative text.": "與 top-k 一起使用。較高的值(例如:0.95)將生成更多樣化的文字,而較低的值(例如:0.5)將生成更集中和保守的文字。", + "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.": "與 top-k 一起使用。較高的值(例如:0.95)將產生更多樣化的文字,而較低的值(例如:0.5)將生成更集中和保守的文字。", "Workspace": "工作區", "Workspace Permissions": "工作區權限", "Write": "撰寫", "Write a prompt suggestion (e.g. Who are you?)": "撰寫提示詞建議(例如:你是誰?)", "Write a summary in 50 words that summarizes [topic or keyword].": "用 50 字寫一篇總結 [主題或關鍵字] 的摘要。", "Write something...": "寫一些什麼……", - "Yacy Instance URL": "Yacy 實例 URL", + "Yacy Instance URL": "Yacy 執行個體 URL", "Yacy Password": "Yacy 密碼", "Yacy Username": "Yacy 使用者名稱", "Yesterday": "昨天", From 2f3d4622db5d6cbcd576a1201cf6f4ee5b9da1b6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 Jun 2025 02:28:18 +0000 Subject: [PATCH 014/180] build(deps): bump validators from 0.34.0 to 0.35.0 in /backend Bumps [validators](https://github.com/python-validators/validators) from 0.34.0 to 0.35.0. - [Release notes](https://github.com/python-validators/validators/releases) - [Changelog](https://github.com/python-validators/validators/blob/master/CHANGES.md) - [Commits](https://github.com/python-validators/validators/compare/0.34.0...0.35.0) --- updated-dependencies: - dependency-name: validators dependency-version: 0.35.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- backend/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/requirements.txt b/backend/requirements.txt index 9930cd3b68..e8150b0b70 100644 --- a/backend/requirements.txt +++ b/backend/requirements.txt @@ -76,7 +76,7 @@ pandas==2.2.3 openpyxl==3.1.5 pyxlsb==1.0.10 xlrd==2.0.1 -validators==0.34.0 +validators==0.35.0 psutil sentencepiece soundfile==0.13.1 From 1131a61b8eb58f8f02fcde24709dd2df326698b1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 Jun 2025 02:28:27 +0000 Subject: [PATCH 015/180] build(deps): bump azure-ai-documentintelligence in /backend Bumps [azure-ai-documentintelligence](https://github.com/Azure/azure-sdk-for-python) from 1.0.0 to 1.0.2. - [Release notes](https://github.com/Azure/azure-sdk-for-python/releases) - [Changelog](https://github.com/Azure/azure-sdk-for-python/blob/main/doc/esrp_release.md) - [Commits](https://github.com/Azure/azure-sdk-for-python/compare/azure-ai-documentintelligence_1.0.0...azure-ai-documentintelligence_1.0.2) --- updated-dependencies: - dependency-name: azure-ai-documentintelligence dependency-version: 1.0.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- backend/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/requirements.txt b/backend/requirements.txt index 9930cd3b68..97d4208057 100644 --- a/backend/requirements.txt +++ b/backend/requirements.txt @@ -80,7 +80,7 @@ validators==0.34.0 psutil sentencepiece soundfile==0.13.1 -azure-ai-documentintelligence==1.0.0 +azure-ai-documentintelligence==1.0.2 pillow==11.1.0 opencv-python-headless==4.11.0.86 From 3ccd2364ba6bc7bd99757e26b99c3371dfa4a6c6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 Jun 2025 02:28:34 +0000 Subject: [PATCH 016/180] build(deps): bump pillow from 11.1.0 to 11.2.1 in /backend Bumps [pillow](https://github.com/python-pillow/Pillow) from 11.1.0 to 11.2.1. - [Release notes](https://github.com/python-pillow/Pillow/releases) - [Changelog](https://github.com/python-pillow/Pillow/blob/main/CHANGES.rst) - [Commits](https://github.com/python-pillow/Pillow/compare/11.1.0...11.2.1) --- updated-dependencies: - dependency-name: pillow dependency-version: 11.2.1 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- backend/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/requirements.txt b/backend/requirements.txt index 9930cd3b68..d2d870b220 100644 --- a/backend/requirements.txt +++ b/backend/requirements.txt @@ -82,7 +82,7 @@ sentencepiece soundfile==0.13.1 azure-ai-documentintelligence==1.0.0 -pillow==11.1.0 +pillow==11.2.1 opencv-python-headless==4.11.0.86 rapidocr-onnxruntime==1.4.4 rank-bm25==0.2.2 From 26549244fc254ab79023c707939d8d544756218d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 Jun 2025 02:28:37 +0000 Subject: [PATCH 017/180] build(deps): bump uvicorn[standard] from 0.34.0 to 0.34.2 in /backend Bumps [uvicorn[standard]](https://github.com/encode/uvicorn) from 0.34.0 to 0.34.2. - [Release notes](https://github.com/encode/uvicorn/releases) - [Changelog](https://github.com/encode/uvicorn/blob/master/docs/release-notes.md) - [Commits](https://github.com/encode/uvicorn/compare/0.34.0...0.34.2) --- updated-dependencies: - dependency-name: uvicorn[standard] dependency-version: 0.34.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- backend/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/requirements.txt b/backend/requirements.txt index 9930cd3b68..31b7daf513 100644 --- a/backend/requirements.txt +++ b/backend/requirements.txt @@ -1,5 +1,5 @@ fastapi==0.115.7 -uvicorn[standard]==0.34.0 +uvicorn[standard]==0.34.2 pydantic==2.10.6 python-multipart==0.0.20 From 28db03e4f97c6b42ed055d36b3823aa175644810 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 Jun 2025 02:49:05 +0000 Subject: [PATCH 018/180] build(deps): bump katex from 0.16.21 to 0.16.22 Bumps [katex](https://github.com/KaTeX/KaTeX) from 0.16.21 to 0.16.22. - [Release notes](https://github.com/KaTeX/KaTeX/releases) - [Changelog](https://github.com/KaTeX/KaTeX/blob/main/CHANGELOG.md) - [Commits](https://github.com/KaTeX/KaTeX/compare/v0.16.21...v0.16.22) --- updated-dependencies: - dependency-name: katex dependency-version: 0.16.22 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- 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 ae602efa0e..fbe35065e1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -51,7 +51,7 @@ "idb": "^7.1.1", "js-sha256": "^0.10.1", "jspdf": "^3.0.0", - "katex": "^0.16.21", + "katex": "^0.16.22", "kokoro-js": "^1.1.1", "marked": "^9.1.0", "mermaid": "^11.6.0", @@ -7930,9 +7930,9 @@ } }, "node_modules/katex": { - "version": "0.16.21", - "resolved": "https://registry.npmjs.org/katex/-/katex-0.16.21.tgz", - "integrity": "sha512-XvqR7FgOHtWupfMiigNzmh+MgUVmDGU2kXZm899ZkPfcuoPuFxyHmXsgATDpFZDAXCI8tvinaVcDo8PIIJSo4A==", + "version": "0.16.22", + "resolved": "https://registry.npmjs.org/katex/-/katex-0.16.22.tgz", + "integrity": "sha512-XCHRdUw4lf3SKBaJe4EvgqIuWwkPSo9XoeO8GjQW94Bp7TWv9hNhzZjZ+OH9yf1UmLygb7DIT5GSFQiyt16zYg==", "funding": [ "https://opencollective.com/katex", "https://github.com/sponsors/katex" diff --git a/package.json b/package.json index b2a71845ed..8737788a92 100644 --- a/package.json +++ b/package.json @@ -95,7 +95,7 @@ "idb": "^7.1.1", "js-sha256": "^0.10.1", "jspdf": "^3.0.0", - "katex": "^0.16.21", + "katex": "^0.16.22", "kokoro-js": "^1.1.1", "marked": "^9.1.0", "mermaid": "^11.6.0", From d649db4ef7915c7c40b210836d2f277d224264ec Mon Sep 17 00:00:00 2001 From: ER-EPR <38782737+ER-EPR@users.noreply.github.com> Date: Mon, 2 Jun 2025 17:44:17 +0800 Subject: [PATCH 019/180] Add cuda126 image in docker-build.yaml add USE_CUDA_VER=cu126 version to support cuda capability 7.0 and below --- .github/workflows/docker-build.yaml | 157 ++++++++++++++++++++++++++++ 1 file changed, 157 insertions(+) diff --git a/.github/workflows/docker-build.yaml b/.github/workflows/docker-build.yaml index e61a69f33a..02b4461b5a 100644 --- a/.github/workflows/docker-build.yaml +++ b/.github/workflows/docker-build.yaml @@ -210,6 +210,107 @@ jobs: if-no-files-found: error retention-days: 1 + build-cuda126-image: + runs-on: ${{ matrix.platform == 'linux/arm64' && 'ubuntu-24.04-arm' || 'ubuntu-latest' }} + permissions: + contents: read + packages: write + strategy: + fail-fast: false + matrix: + platform: + - linux/amd64 + - linux/arm64 + + steps: + # GitHub Packages requires the entire repository name to be in lowercase + # although the repository owner has a lowercase username, this prevents some people from running actions after forking + - name: Set repository and image name to lowercase + run: | + echo "IMAGE_NAME=${IMAGE_NAME,,}" >>${GITHUB_ENV} + echo "FULL_IMAGE_NAME=ghcr.io/${IMAGE_NAME,,}" >>${GITHUB_ENV} + env: + IMAGE_NAME: '${{ github.repository }}' + + - name: Prepare + run: | + platform=${{ matrix.platform }} + echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV + + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Log in to the Container registry + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract metadata for Docker images (cuda126 tag) + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.FULL_IMAGE_NAME }} + tags: | + type=ref,event=branch + type=ref,event=tag + type=sha,prefix=git- + type=semver,pattern={{version}} + type=semver,pattern={{major}}.{{minor}} + type=raw,enable=${{ github.ref == 'refs/heads/main' }},prefix=,suffix=,value=cuda126 + flavor: | + latest=${{ github.ref == 'refs/heads/main' }} + suffix=-cuda126,onlatest=true + + - name: Extract metadata for Docker cache + id: cache-meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.FULL_IMAGE_NAME }} + tags: | + type=ref,event=branch + ${{ github.ref_type == 'tag' && 'type=raw,value=main' || '' }} + flavor: | + prefix=cache-cuda126-${{ matrix.platform }}- + latest=false + + - name: Build Docker image (cuda126) + uses: docker/build-push-action@v5 + id: build + with: + context: . + push: true + platforms: ${{ matrix.platform }} + labels: ${{ steps.meta.outputs.labels }} + outputs: type=image,name=${{ env.FULL_IMAGE_NAME }},push-by-digest=true,name-canonical=true,push=true + cache-from: type=registry,ref=${{ steps.cache-meta.outputs.tags }} + cache-to: type=registry,ref=${{ steps.cache-meta.outputs.tags }},mode=max + build-args: | + BUILD_HASH=${{ github.sha }} + USE_CUDA=true + USE_CUDA_VER=cu126 + + - name: Export digest + run: | + mkdir -p /tmp/digests + digest="${{ steps.build.outputs.digest }}" + touch "/tmp/digests/${digest#sha256:}" + + - name: Upload digest + uses: actions/upload-artifact@v4 + with: + name: digests-cuda126-${{ env.PLATFORM_PAIR }} + path: /tmp/digests/* + if-no-files-found: error + retention-days: 1 + build-ollama-image: runs-on: ${{ matrix.platform == 'linux/arm64' && 'ubuntu-24.04-arm' || 'ubuntu-latest' }} permissions: @@ -420,6 +521,62 @@ jobs: run: | docker buildx imagetools inspect ${{ env.FULL_IMAGE_NAME }}:${{ steps.meta.outputs.version }} + merge-cuda126-images: + runs-on: ubuntu-latest + needs: [build-cuda126-image] + steps: + # GitHub Packages requires the entire repository name to be in lowercase + # although the repository owner has a lowercase username, this prevents some people from running actions after forking + - name: Set repository and image name to lowercase + run: | + echo "IMAGE_NAME=${IMAGE_NAME,,}" >>${GITHUB_ENV} + echo "FULL_IMAGE_NAME=ghcr.io/${IMAGE_NAME,,}" >>${GITHUB_ENV} + env: + IMAGE_NAME: '${{ github.repository }}' + + - name: Download digests + uses: actions/download-artifact@v4 + with: + pattern: digests-cuda126-* + path: /tmp/digests + merge-multiple: true + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Log in to the Container registry + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract metadata for Docker images (default latest tag) + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.FULL_IMAGE_NAME }} + tags: | + type=ref,event=branch + type=ref,event=tag + type=sha,prefix=git- + type=semver,pattern={{version}} + type=semver,pattern={{major}}.{{minor}} + type=raw,enable=${{ github.ref == 'refs/heads/main' }},prefix=,suffix=,value=cuda126 + flavor: | + latest=${{ github.ref == 'refs/heads/main' }} + suffix=-cuda126,onlatest=true + + - name: Create manifest list and push + working-directory: /tmp/digests + run: | + docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \ + $(printf '${{ env.FULL_IMAGE_NAME }}@sha256:%s ' *) + + - name: Inspect image + run: | + docker buildx imagetools inspect ${{ env.FULL_IMAGE_NAME }}:${{ steps.meta.outputs.version }} + merge-ollama-images: runs-on: ubuntu-latest needs: [build-ollama-image] From 3da1802eec58683e3be223f724b0e52353c13bee Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Mon, 2 Jun 2025 23:25:38 +0400 Subject: [PATCH 020/180] fix: message delete issue --- src/lib/components/chat/Chat.svelte | 2 ++ src/lib/components/common/Loader.svelte | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/lib/components/chat/Chat.svelte b/src/lib/components/chat/Chat.svelte index a37ce5be72..255dc4c1ba 100644 --- a/src/lib/components/chat/Chat.svelte +++ b/src/lib/components/chat/Chat.svelte @@ -239,6 +239,8 @@ }; const showMessage = async (message) => { + await tick(); + const _chatId = JSON.parse(JSON.stringify($chatId)); let _messageId = JSON.parse(JSON.stringify(message.id)); diff --git a/src/lib/components/common/Loader.svelte b/src/lib/components/common/Loader.svelte index ac7ecaf28a..221f6ecf0b 100644 --- a/src/lib/components/common/Loader.svelte +++ b/src/lib/components/common/Loader.svelte @@ -33,7 +33,9 @@ }); onDestroy(() => { - observer.disconnect(); + if (observer) { + observer.disconnect(); + } if (intervalId) { clearInterval(intervalId); From 96e9bfe0e55a0d0b6fea7c1ef1bd6faa3d4e530c Mon Sep 17 00:00:00 2001 From: Dave Date: Tue, 3 Jun 2025 00:19:08 +0200 Subject: [PATCH 021/180] feat: add Perplexity model and search context usage configuration options --- backend/open_webui/config.py | 12 +++++++ backend/open_webui/main.py | 4 +++ .../open_webui/retrieval/web/perplexity.py | 22 +++++++++++-- backend/open_webui/routers/retrieval.py | 19 +++++++---- .../admin/Settings/WebSearch.svelte | 32 +++++++++++++++++++ 5 files changed, 80 insertions(+), 9 deletions(-) diff --git a/backend/open_webui/config.py b/backend/open_webui/config.py index 0e0c08f2bb..c2ca9841cd 100644 --- a/backend/open_webui/config.py +++ b/backend/open_webui/config.py @@ -2442,6 +2442,18 @@ PERPLEXITY_API_KEY = PersistentConfig( os.getenv("PERPLEXITY_API_KEY", ""), ) +PERPLEXITY_MODEL = PersistentConfig( + "PERPLEXITY_MODEL", + "rag.web.search.perplexity_model", + os.getenv("PERPLEXITY_MODEL", "sonar"), +) + +PERPLEXITY_SEARCH_CONTEXT_USAGE = PersistentConfig( + "PERPLEXITY_SEARCH_CONTEXT_USAGE", + "rag.web.search.perplexity_search_context_usage", + os.getenv("PERPLEXITY_SEARCH_CONTEXT_USAGE", "medium"), +) + SOUGOU_API_SID = PersistentConfig( "SOUGOU_API_SID", "rag.web.search.sougou_api_sid", diff --git a/backend/open_webui/main.py b/backend/open_webui/main.py index 6bdcf4957a..545774fc0a 100644 --- a/backend/open_webui/main.py +++ b/backend/open_webui/main.py @@ -268,6 +268,8 @@ from open_webui.config import ( BRAVE_SEARCH_API_KEY, EXA_API_KEY, PERPLEXITY_API_KEY, + PERPLEXITY_MODEL, + PERPLEXITY_SEARCH_CONTEXT_USAGE, SOUGOU_API_SID, SOUGOU_API_SK, KAGI_SEARCH_API_KEY, @@ -771,6 +773,8 @@ app.state.config.BING_SEARCH_V7_ENDPOINT = BING_SEARCH_V7_ENDPOINT app.state.config.BING_SEARCH_V7_SUBSCRIPTION_KEY = BING_SEARCH_V7_SUBSCRIPTION_KEY app.state.config.EXA_API_KEY = EXA_API_KEY app.state.config.PERPLEXITY_API_KEY = PERPLEXITY_API_KEY +app.state.config.PERPLEXITY_MODEL = PERPLEXITY_MODEL +app.state.config.PERPLEXITY_SEARCH_CONTEXT_USAGE = PERPLEXITY_SEARCH_CONTEXT_USAGE app.state.config.SOUGOU_API_SID = SOUGOU_API_SID app.state.config.SOUGOU_API_SK = SOUGOU_API_SK app.state.config.EXTERNAL_WEB_SEARCH_URL = EXTERNAL_WEB_SEARCH_URL diff --git a/backend/open_webui/retrieval/web/perplexity.py b/backend/open_webui/retrieval/web/perplexity.py index e5314eb1f7..551cb2c4b4 100644 --- a/backend/open_webui/retrieval/web/perplexity.py +++ b/backend/open_webui/retrieval/web/perplexity.py @@ -1,10 +1,20 @@ import logging -from typing import Optional, List +from typing import Optional, Literal import requests from open_webui.retrieval.web.main import SearchResult, get_filtered_results from open_webui.env import SRC_LOG_LEVELS +MODELS = Literal[ + "sonar", + "sonar-pro", + "sonar-reasoning", + "sonar-reasoning-pro", + "sonar-deep-research", +] +SEARCH_CONTEXT_USAGE_LEVELS = Literal["low", "medium", "high"] + + log = logging.getLogger(__name__) log.setLevel(SRC_LOG_LEVELS["RAG"]) @@ -14,6 +24,8 @@ def search_perplexity( query: str, count: int, filter_list: Optional[list[str]] = None, + model: MODELS = "sonar", + search_context_usage: SEARCH_CONTEXT_USAGE_LEVELS = "medium", ) -> list[SearchResult]: """Search using Perplexity API and return the results as a list of SearchResult objects. @@ -21,6 +33,9 @@ def search_perplexity( api_key (str): A Perplexity API key query (str): The query to search for count (int): Maximum number of results to return + filter_list (Optional[list[str]]): List of domains to filter results + model (str): The Perplexity model to use (sonar, sonar-pro) + search_context_usage (str): Search context usage level (low, medium, high) """ @@ -33,7 +48,7 @@ def search_perplexity( # Create payload for the API call payload = { - "model": "sonar", + "model": model, "messages": [ { "role": "system", @@ -43,6 +58,9 @@ def search_perplexity( ], "temperature": 0.2, # Lower temperature for more factual responses "stream": False, + "web_search_options": { + "search_context_usage": search_context_usage, + } } headers = { diff --git a/backend/open_webui/routers/retrieval.py b/backend/open_webui/routers/retrieval.py index 343b0513c9..22b264bfad 100644 --- a/backend/open_webui/routers/retrieval.py +++ b/backend/open_webui/routers/retrieval.py @@ -467,6 +467,8 @@ async def get_rag_config(request: Request, user=Depends(get_admin_user)): "BING_SEARCH_V7_SUBSCRIPTION_KEY": request.app.state.config.BING_SEARCH_V7_SUBSCRIPTION_KEY, "EXA_API_KEY": request.app.state.config.EXA_API_KEY, "PERPLEXITY_API_KEY": request.app.state.config.PERPLEXITY_API_KEY, + "PERPLEXITY_MODEL": request.app.state.config.PERPLEXITY_MODEL, + "PERPLEXITY_SEARCH_CONTEXT_USAGE": request.app.state.config.PERPLEXITY_SEARCH_CONTEXT_USAGE, "SOUGOU_API_SID": request.app.state.config.SOUGOU_API_SID, "SOUGOU_API_SK": request.app.state.config.SOUGOU_API_SK, "WEB_LOADER_ENGINE": request.app.state.config.WEB_LOADER_ENGINE, @@ -520,6 +522,8 @@ class WebConfig(BaseModel): BING_SEARCH_V7_SUBSCRIPTION_KEY: Optional[str] = None EXA_API_KEY: Optional[str] = None PERPLEXITY_API_KEY: Optional[str] = None + PERPLEXITY_MODEL: Optional[str] = None + PERPLEXITY_SEARCH_CONTEXT_USAGE: Optional[str] = None SOUGOU_API_SID: Optional[str] = None SOUGOU_API_SK: Optional[str] = None WEB_LOADER_ENGINE: Optional[str] = None @@ -907,6 +911,10 @@ async def update_rag_config( ) request.app.state.config.EXA_API_KEY = form_data.web.EXA_API_KEY request.app.state.config.PERPLEXITY_API_KEY = form_data.web.PERPLEXITY_API_KEY + request.app.state.config.PERPLEXITY_MODEL = form_data.web.PERPLEXITY_MODEL + request.app.state.config.PERPLEXITY_SEARCH_CONTEXT_USAGE = ( + form_data.web.PERPLEXITY_SEARCH_CONTEXT_USAGE + ) request.app.state.config.SOUGOU_API_SID = form_data.web.SOUGOU_API_SID request.app.state.config.SOUGOU_API_SK = form_data.web.SOUGOU_API_SK @@ -1030,6 +1038,8 @@ async def update_rag_config( "BING_SEARCH_V7_SUBSCRIPTION_KEY": request.app.state.config.BING_SEARCH_V7_SUBSCRIPTION_KEY, "EXA_API_KEY": request.app.state.config.EXA_API_KEY, "PERPLEXITY_API_KEY": request.app.state.config.PERPLEXITY_API_KEY, + "PERPLEXITY_MODEL": request.app.state.config.PERPLEXITY_MODEL, + "PERPLEXITY_SEARCH_CONTEXT_USAGE": request.app.state.config.PERPLEXITY_SEARCH_CONTEXT_USAGE, "SOUGOU_API_SID": request.app.state.config.SOUGOU_API_SID, "SOUGOU_API_SK": request.app.state.config.SOUGOU_API_SK, "WEB_LOADER_ENGINE": request.app.state.config.WEB_LOADER_ENGINE, @@ -1740,19 +1750,14 @@ def search_web(request: Request, engine: str, query: str) -> list[SearchResult]: request.app.state.config.WEB_SEARCH_RESULT_COUNT, request.app.state.config.WEB_SEARCH_DOMAIN_FILTER_LIST, ) - elif engine == "exa": - return search_exa( - request.app.state.config.EXA_API_KEY, - query, - request.app.state.config.WEB_SEARCH_RESULT_COUNT, - request.app.state.config.WEB_SEARCH_DOMAIN_FILTER_LIST, - ) elif engine == "perplexity": return search_perplexity( request.app.state.config.PERPLEXITY_API_KEY, query, request.app.state.config.WEB_SEARCH_RESULT_COUNT, request.app.state.config.WEB_SEARCH_DOMAIN_FILTER_LIST, + model=request.app.state.config.PERPLEXITY_MODEL, + search_context_usage=request.app.state.config.PERPLEXITY_SEARCH_CONTEXT_USAGE, ) elif engine == "sougou": if ( diff --git a/src/lib/components/admin/Settings/WebSearch.svelte b/src/lib/components/admin/Settings/WebSearch.svelte index c0712d8ad1..e7630388e9 100644 --- a/src/lib/components/admin/Settings/WebSearch.svelte +++ b/src/lib/components/admin/Settings/WebSearch.svelte @@ -456,6 +456,38 @@ bind:value={webConfig.PERPLEXITY_API_KEY} />
+ + +
+
+ {$i18n.t('Perplexity Model')} +
+ +
+ + +
+
+ {$i18n.t('Search Context Usage')} +
+ +
{:else if webConfig.WEB_SEARCH_ENGINE === 'sougou'}
From 77b357c73b1121faabb3300800a4ccbfc11d373a Mon Sep 17 00:00:00 2001 From: Dave Date: Tue, 3 Jun 2025 00:27:07 +0200 Subject: [PATCH 022/180] fix: update label for search context usage to clarify its purpose --- backend/open_webui/retrieval/web/perplexity.py | 2 +- src/lib/components/admin/Settings/WebSearch.svelte | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/open_webui/retrieval/web/perplexity.py b/backend/open_webui/retrieval/web/perplexity.py index 551cb2c4b4..4e046668fa 100644 --- a/backend/open_webui/retrieval/web/perplexity.py +++ b/backend/open_webui/retrieval/web/perplexity.py @@ -60,7 +60,7 @@ def search_perplexity( "stream": False, "web_search_options": { "search_context_usage": search_context_usage, - } + }, } headers = { diff --git a/src/lib/components/admin/Settings/WebSearch.svelte b/src/lib/components/admin/Settings/WebSearch.svelte index e7630388e9..f0cc97ffe6 100644 --- a/src/lib/components/admin/Settings/WebSearch.svelte +++ b/src/lib/components/admin/Settings/WebSearch.svelte @@ -477,7 +477,7 @@
- {$i18n.t('Search Context Usage')} + {$i18n.t('Perplexity Search Context Usage')}