From c6d80496abe7fb5d3a122332025334f04b5a989a Mon Sep 17 00:00:00 2001 From: silentoplayz Date: Sat, 23 Aug 2025 15:08:07 -0400 Subject: [PATCH 001/127] feat: improve ollama model management experience This commit introduces several improvements to the Ollama model management modal: - Adds a cancel button to the model pulling operation, using the existing 'x' button pattern. - Adds a cancel button to the "Update All" models operation, allowing the user to cancel the update for the currently processing model. - Cleans up toast notifications when updating all models. A single toast is now shown at the beginning and a summary toast at the end, preventing notification spam. - Refactors the `ManageOllama.svelte` component to support these new cancellation features. - Adds tooltips to all buttons in the modal to improve clarity. - Disables buttons when their corresponding input fields are empty to prevent accidental clicks. --- .../Models/Manage/ManageOllama.svelte | 374 +++++++++++------- 1 file changed, 225 insertions(+), 149 deletions(-) diff --git a/src/lib/components/admin/Settings/Models/Manage/ManageOllama.svelte b/src/lib/components/admin/Settings/Models/Manage/ManageOllama.svelte index fbfcf93677..29682a7a37 100644 --- a/src/lib/components/admin/Settings/Models/Manage/ManageOllama.svelte +++ b/src/lib/components/admin/Settings/Models/Manage/ManageOllama.svelte @@ -36,6 +36,8 @@ let updateModelId = null; let updateProgress = null; + let updateModelsControllers = {}; + let updateCancelled = false; let showExperimentalOllama = false; const MAX_PARALLEL_DOWNLOADS = 3; @@ -65,17 +67,31 @@ let deleteModelTag = ''; const updateModelsHandler = async () => { + updateCancelled = false; + toast.info('Checking for model updates...'); + for (const model of ollamaModels) { + if (updateCancelled) { + break; + } + console.debug(model); updateModelId = model.id; const [res, controller] = await pullModel(localStorage.token, model.id, urlIdx).catch( (error) => { - toast.error(`${error}`); - return null; + if (error.name !== 'AbortError') { + toast.error(`${error}`); + } + return [null, null]; } ); + updateModelsControllers = { + ...updateModelsControllers, + [model.id]: controller + }; + if (res) { const reader = res.body .pipeThrough(new TextDecoderStream()) @@ -108,19 +124,28 @@ } else { updateProgress = 100; } - } else { - toast.success(data.status); } } } } } catch (err) { - console.error(err); + if (err.name !== 'AbortError') { + console.error(err); + } + break; } } } + + delete updateModelsControllers[model.id]; + updateModelsControllers = { ...updateModelsControllers }; } + if (updateCancelled) { + toast.info('Model update cancelled'); + } else { + toast.success('All models are up to date'); + } updateModelId = null; updateProgress = null; }; @@ -143,10 +168,13 @@ return; } + modelTransferring = true; const [res, controller] = await pullModel(localStorage.token, sanitizedModelTag, urlIdx).catch( (error) => { - toast.error(`${error}`); - return null; + if (error.name !== 'AbortError') { + toast.error(`${error}`); + } + return [null, null]; } ); @@ -202,8 +230,6 @@ } }); } else { - toast.success(data.status); - MODEL_DOWNLOAD_POOL.set({ ...$MODEL_DOWNLOAD_POOL, [sanitizedModelTag]: { @@ -216,19 +242,23 @@ } } } catch (err) { - console.error(err); - if (typeof err !== 'string') { - err = err.message; - } + if (err.name !== 'AbortError') { + console.error(err); + if (typeof err !== 'string') { + err = err.message; + } - toast.error(`${err}`); - // opts.callback({ success: false, error, modelName: opts.modelName }); + toast.error(`${err}`); + // opts.callback({ success: false, error, modelName: opts.modelName }); + } else { + break; + } } } console.log($MODEL_DOWNLOAD_POOL[sanitizedModelTag]); - if ($MODEL_DOWNLOAD_POOL[sanitizedModelTag].done) { + if ($MODEL_DOWNLOAD_POOL[sanitizedModelTag]?.done) { toast.success( $i18n.t(`Model '{{modelName}}' has been successfully downloaded.`, { modelName: sanitizedModelTag @@ -425,6 +455,14 @@ ); }; + const cancelUpdateModelHandler = async (model: string) => { + const controller = updateModelsControllers[model]; + if (controller) { + controller.abort(); + updateCancelled = true; + } + }; + const cancelModelPullHandler = async (model: string) => { const { reader, abortController } = $MODEL_DOWNLOAD_POOL[model]; if (abortController) { @@ -437,7 +475,7 @@ ...$MODEL_DOWNLOAD_POOL }); await deleteModel(localStorage.token, model); - toast.success($i18n.t('{{model}} download has been canceled', { model: model })); + toast.success(`${model} download has been canceled`); } }; @@ -605,59 +643,61 @@ bind:value={modelTag} /> - + {/if} + +
@@ -670,8 +710,35 @@
{#if updateModelId} -
- Updating "{updateModelId}" {updateProgress ? `(${updateProgress}%)` : ''} +
+
Updating "{updateModelId}" {updateProgress ? `(${updateProgress}%)` : ''}
+ + + +
{/if} @@ -754,25 +821,28 @@ {/each}
- + + + + + @@ -799,27 +869,31 @@
- + + + + + +
@@ -936,57 +1010,59 @@ {#if (modelUploadMode === 'file' && modelInputFile && modelInputFile.length > 0) || (modelUploadMode === 'url' && modelFileUrl !== '')} - + {/if} + + {/if} From 8df74dde80271d7b0083e73677c90401910c5516 Mon Sep 17 00:00:00 2001 From: silentoplayz Date: Sat, 23 Aug 2025 15:19:35 -0400 Subject: [PATCH 002/127] fix --- .../components/admin/Settings/Models/Manage/ManageOllama.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/components/admin/Settings/Models/Manage/ManageOllama.svelte b/src/lib/components/admin/Settings/Models/Manage/ManageOllama.svelte index 29682a7a37..6c73b7a4a0 100644 --- a/src/lib/components/admin/Settings/Models/Manage/ManageOllama.svelte +++ b/src/lib/components/admin/Settings/Models/Manage/ManageOllama.svelte @@ -475,7 +475,7 @@ ...$MODEL_DOWNLOAD_POOL }); await deleteModel(localStorage.token, model); - toast.success(`${model} download has been canceled`); + toast.success($i18n.t('{{model}} download has been canceled', { model: model })); } }; From 3b1197a1bb8d33ac0fe072c0970fd4cdd64cdcbc Mon Sep 17 00:00:00 2001 From: Shirasawa <764798966@qq.com> Date: Mon, 29 Sep 2025 07:47:11 +0000 Subject: [PATCH 003/127] i18n: improve Chinese translation --- src/lib/i18n/locales/zh-CN/translation.json | 20 ++++++++++---------- src/lib/i18n/locales/zh-TW/translation.json | 10 +++++----- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/lib/i18n/locales/zh-CN/translation.json b/src/lib/i18n/locales/zh-CN/translation.json index 3fc0de3fe7..e2520c8945 100644 --- a/src/lib/i18n/locales/zh-CN/translation.json +++ b/src/lib/i18n/locales/zh-CN/translation.json @@ -36,7 +36,7 @@ "Accurate information": "信息准确", "Action": "操作", "Action not found": "找不到对应的操作项", - "Action Required for Chat Log Storage": "需要操作以保存聊天记录", + "Action Required for Chat Log Storage": "需要操作以保存对话记录", "Actions": "操作", "Activate": "激活", "Activate this command by typing \"/{{COMMAND}}\" to chat input.": "在对话框中输入 \"/{{COMMAND}}\" 激活此命令", @@ -75,7 +75,7 @@ "Advanced Params": "高级参数", "AI": "AI", "All": "全部", - "All chats have been unarchived.": "", + "All chats have been unarchived.": "已成功将所有对话取消归档。", "All Documents": "所有文档", "All models deleted successfully": "所有模型删除成功", "Allow Call": "允许语音通话", @@ -431,7 +431,7 @@ "Discover, download, and explore custom tools": "发现、下载并探索更多自定义工具", "Discover, download, and explore model presets": "发现、下载并探索更多模型预设", "Display": "显示", - "Display chat title in tab": "", + "Display chat title in tab": "在浏览器标签页中显示对话标题", "Display Emoji in Call": "在通话中显示 Emoji", "Display Multi-model Responses in Tabs": "以标签页的形式展示多个模型的回答", "Display the username instead of You in the Chat": "在对话中显示用户名而不是“你”", @@ -693,7 +693,7 @@ "Failed to extract content from the file.": "文件内容提取失败", "Failed to fetch models": "获取模型失败", "Failed to generate title": "生成标题失败", - "Failed to import models": "", + "Failed to import models": "导入模型配置失败", "Failed to load chat preview": "对话预览加载失败", "Failed to load file content.": "文件内容加载失败", "Failed to move chat": "移动对话失败", @@ -1019,7 +1019,7 @@ "Models": "模型", "Models Access": "访问模型列表", "Models configuration saved successfully": "模型配置保存成功", - "Models imported successfully": "", + "Models imported successfully": "已成功导入模型配置", "Models Public Sharing": "模型公开共享", "Mojeek Search API Key": "Mojeek Search API 密钥", "More": "更多", @@ -1243,7 +1243,7 @@ "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)则更加保守。", "Refer to yourself as \"User\" (e.g., \"User is learning Spanish\")": "使用\"User\" (用户) 来指代自己(例如:“User 正在学习西班牙语”)", - "Reference Chats": "引用其他聊天", + "Reference Chats": "引用其他对话", "Refused when it shouldn't have": "拒绝了我的要求", "Regenerate": "重新生成", "Regenerate Menu": "显示重新生成选项菜单", @@ -1317,10 +1317,10 @@ "Search Chats": "搜索对话", "Search Collection": "搜索内容", "Search Filters": "搜索过滤器", - "search for archived chats": "搜索已归档的聊天", + "search for archived chats": "搜索已归档的对话", "search for folders": "搜索分组", - "search for pinned chats": "搜索已置顶的聊天", - "search for shared chats": "搜索已共享的聊天", + "search for pinned chats": "搜索已置顶的对话", + "search for shared chats": "搜索已共享的对话", "search for tags": "搜索标签", "Search Functions": "搜索函数", "Search In Models": "搜索模型", @@ -1524,7 +1524,7 @@ "This chat won't appear in history and your messages will not be saved.": "此对话将不会出现在历史记录中,且您的消息不会被保存", "This ensures that your valuable conversations are securely saved to your backend database. Thank you!": "这将确保您宝贵的对话数据被安全地保存到后台数据库中。谢谢!", "This feature is experimental and may be modified or discontinued without notice.": "此功能为实验性功能,可能会在未经通知的情况下修改或停用。", - "This is a default user permission and will remain enabled.": "", + "This is a default user permission and will remain enabled.": "此权限已在默认用户配置中启用,当前会始终生效。", "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 long the model will stay loaded into memory following the request (default: 5m)": "此选项用于控制模型在收到请求后,保持常驻内存的时长(默认:5 分钟)", diff --git a/src/lib/i18n/locales/zh-TW/translation.json b/src/lib/i18n/locales/zh-TW/translation.json index 0f31944e66..b4bb01d0b9 100644 --- a/src/lib/i18n/locales/zh-TW/translation.json +++ b/src/lib/i18n/locales/zh-TW/translation.json @@ -75,7 +75,7 @@ "Advanced Params": "進階參數", "AI": "AI", "All": "全部", - "All chats have been unarchived.": "", + "All chats have been unarchived.": "已成功將所有對話解除封存。", "All Documents": "所有檔案", "All models deleted successfully": "成功刪除所有模型", "Allow Call": "允許通話", @@ -431,7 +431,7 @@ "Discover, download, and explore custom tools": "發掘、下載及探索自訂工具", "Discover, download, and explore model presets": "發掘、下載及探索模型預設集", "Display": "顯示", - "Display chat title in tab": "", + "Display chat title in tab": "在瀏覽器分頁標籤上顯示對話標題", "Display Emoji in Call": "在通話中顯示表情符號", "Display Multi-model Responses in Tabs": "以標籤頁的形式展示多個模型的回應", "Display the username instead of You in the Chat": "在對話中顯示使用者名稱,而非「您」", @@ -693,7 +693,7 @@ "Failed to extract content from the file.": "檔案內容擷取失敗", "Failed to fetch models": "取得模型失敗", "Failed to generate title": "產生標題失敗", - "Failed to import models": "", + "Failed to import models": "匯入模型失敗", "Failed to load chat preview": "對話預覽載入失敗", "Failed to load file content.": "載入檔案內容失敗。", "Failed to move chat": "移動對話失敗", @@ -1019,7 +1019,7 @@ "Models": "模型", "Models Access": "模型存取", "Models configuration saved successfully": "成功儲存模型設定", - "Models imported successfully": "", + "Models imported successfully": "已成功匯入模型", "Models Public Sharing": "模型公開分享", "Mojeek Search API Key": "Mojeek 搜尋 API 金鑰", "More": "更多", @@ -1524,7 +1524,7 @@ "This chat won't appear in history and your messages will not be saved.": "此對話不會出現在歷史記錄中,且您的訊息將不被儲存。", "This ensures that your valuable conversations are securely saved to your backend database. Thank you!": "這確保您寶貴的對話會安全地儲存到您的後端資料庫。謝謝!", "This feature is experimental and may be modified or discontinued without notice.": "此功能為實驗性功能,可能會在未經通知的情況下修改或停用。", - "This is a default user permission and will remain enabled.": "", + "This is a default user permission and will remain enabled.": "此權限已在預設使用者設定中啟用,並將持續生效。", "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 long the model will stay loaded into memory following the request (default: 5m)": "此選項控制模型請求後在記憶體中保持載入狀態的時長(預設:5 分鐘)", From 3389f0eece512f91ee6daa358f9feaee1b2cd66f Mon Sep 17 00:00:00 2001 From: Shirasawa <764798966@qq.com> Date: Mon, 29 Sep 2025 08:16:50 +0000 Subject: [PATCH 004/127] =?UTF-8?q?fix:=20handle=20non=E2=80=91UTF8=20char?= =?UTF-8?q?s=20in=20third=E2=80=91party=20responses=20without=20error?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/open_webui/utils/middleware.py | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/backend/open_webui/utils/middleware.py b/backend/open_webui/utils/middleware.py index e4bf1195ff..445a456752 100644 --- a/backend/open_webui/utils/middleware.py +++ b/backend/open_webui/utils/middleware.py @@ -147,7 +147,7 @@ def process_tool_result( if isinstance(tool_result, HTMLResponse): content_disposition = tool_result.headers.get("Content-Disposition", "") if "inline" in content_disposition: - content = tool_result.body.decode("utf-8") + content = tool_result.body.decode("utf-8", "replace") tool_result_embeds.append(content) if 200 <= tool_result.status_code < 300: @@ -175,7 +175,7 @@ def process_tool_result( "message": f"{tool_function_name}: Unexpected status code {tool_result.status_code} from embedded UI result.", } else: - tool_result = tool_result.body.decode("utf-8") + tool_result = tool_result.body.decode("utf-8", "replace") elif (tool_type == "external" and isinstance(tool_result, tuple)) or ( direct_tool and isinstance(tool_result, list) and len(tool_result) == 2 @@ -283,7 +283,7 @@ async def chat_completion_tools_handler( content = None if hasattr(response, "body_iterator"): async for chunk in response.body_iterator: - data = json.loads(chunk.decode("utf-8")) + data = json.loads(chunk.decode("utf-8", "replace")) content = data["choices"][0]["message"]["content"] # Cleanup any remaining background tasks if necessary @@ -1642,7 +1642,9 @@ async def process_chat_response( response.body, bytes ): try: - response_data = json.loads(response.body.decode("utf-8")) + response_data = json.loads( + response.body.decode("utf-8", "replace") + ) except json.JSONDecodeError: response_data = { "error": {"detail": "Invalid JSON response"} @@ -2276,7 +2278,11 @@ async def process_chat_response( last_delta_data = None async for line in response.body_iterator: - line = line.decode("utf-8") if isinstance(line, bytes) else line + line = ( + line.decode("utf-8", "replace") + if isinstance(line, bytes) + else line + ) data = line # Skip empty lines From a395af31b1b8eec8ebe2da9b557a65ae5ceef4ac Mon Sep 17 00:00:00 2001 From: Jan Kessler Date: Mon, 29 Sep 2025 11:43:17 +0200 Subject: [PATCH 005/127] German translation of new strings in i18n --- src/lib/i18n/locales/de-DE/translation.json | 48 ++++++++++----------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/src/lib/i18n/locales/de-DE/translation.json b/src/lib/i18n/locales/de-DE/translation.json index b98033617e..df3bce398f 100644 --- a/src/lib/i18n/locales/de-DE/translation.json +++ b/src/lib/i18n/locales/de-DE/translation.json @@ -75,7 +75,7 @@ "Advanced Params": "Erweiterte Parameter", "AI": "KI", "All": "Alle", - "All chats have been unarchived.": "", + "All chats have been unarchived.": "Alle Chats wurden aus dem Archiv wiederhergestellt.", "All Documents": "Alle Dokumente", "All models deleted successfully": "Alle Modelle erfolgreich gelöscht", "Allow Call": "Anruffunktion erlauben", @@ -214,7 +214,7 @@ "Capture Audio": "Audio aufzeichnen", "Certificate Path": "Zertifikatpfad", "Change Password": "Passwort ändern", - "Channel": "", + "Channel": "Kanal", "Channel deleted successfully": "Kanal erfolgreich gelöscht", "Channel Name": "Kanalname", "Channel updated successfully": "Kanal erfolgreich aktualisiert", @@ -431,7 +431,7 @@ "Discover, download, and explore custom tools": "Entdecken und beziehen Sie benutzerdefinierte Werkzeuge", "Discover, download, and explore model presets": "Entdecken und beziehen Sie benutzerdefinierte Modellvorlagen", "Display": "Anzeigen", - "Display chat title in tab": "", + "Display chat title in tab": "Chat-Titel im Tab anzeigen", "Display Emoji in Call": "Emojis im Anruf anzeigen", "Display Multi-model Responses in Tabs": "Multi-Modell-Antworten in Tabs anzeigen", "Display the username instead of You in the Chat": "Soll \"Sie\" durch Ihren Benutzernamen ersetzt werden?", @@ -490,7 +490,7 @@ "Edit Memory": "Erinnerungen bearbeiten", "Edit User": "Benutzer bearbeiten", "Edit User Group": "Benutzergruppe bearbeiten", - "edited": "", + "edited": "bearbeitet", "Edited": "Bearbeitet", "Editing": "Bearbeite", "Eject": "Auswerfen", @@ -634,7 +634,7 @@ "Enter Your Role": "Geben Sie Ihre Rolle ein", "Enter Your Username": "Geben Sie Ihren Benutzernamen ein", "Enter your webhook URL": "Geben Sie Ihre Webhook-URL ein", - "Entra ID": "", + "Entra ID": "Entra ID", "Error": "Fehler", "ERROR": "FEHLER", "Error accessing directory": "Fehler beim Zugriff auf das Verzeichnis", @@ -693,7 +693,7 @@ "Failed to extract content from the file.": "Fehler beim extrahieren des Inhalts aus der Datei.", "Failed to fetch models": "Fehler beim Abrufen der Modelle", "Failed to generate title": "Fehler beim generieren des Titels", - "Failed to import models": "", + "Failed to import models": "Fehler beim Importieren der Modelle", "Failed to load chat preview": "Chat-Vorschau konnte nicht geladen werden", "Failed to load file content.": "Fehler beim Laden des Dateiinhalts.", "Failed to move chat": "Chat konnte nicht verschoben werden", @@ -847,7 +847,7 @@ "Import Presets": "Voreinstellungen importieren", "Import Prompt Suggestions": "Prompt-Vorschläge importieren", "Import Prompts": "Prompts importieren", - "Import successful": "", + "Import successful": "Import erfolgreich", "Import Tools": "Werkzeuge importieren", "Important Update": "Wichtiges Update", "In order to force OCR, performing OCR must be enabled.": "Um die OCR erzwingen zu können, muss die Durchführung der OCR aktiviert sein.", @@ -885,7 +885,7 @@ "join our Discord for help.": "Treten Sie unserem Discord bei, um Hilfe zu erhalten.", "JSON": "JSON", "JSON Preview": "JSON-Vorschau", - "JSON Spec": "", + "JSON Spec": "JSON-Spezifikation", "July": "Juli", "June": "Juni", "Jupyter Auth": "Jupyter-Authentifizierung", @@ -930,7 +930,7 @@ "Leave empty to include all models or select specific models": "Leer lassen, um alle Modelle einzuschließen oder spezifische Modelle auszuwählen", "Leave empty to use the default prompt, or enter a custom prompt": "Leer lassen, um den Standardprompt zu verwenden, oder geben Sie einen benutzerdefinierten Prompt ein", "Leave model field empty to use the default model.": "Leer lassen, um das Standardmodell zu verwenden.", - "Legacy": "", + "Legacy": "Legacy", "lexical": "lexikalisch", "License": "Lizenz", "Lift List": "Liste anheben", @@ -969,7 +969,7 @@ "Max Upload Size": "Maximale Uploadgröße", "Maximum of 3 models can be downloaded simultaneously. Please try again later.": "Es können maximal 3 Modelle gleichzeitig heruntergeladen werden. Bitte versuchen Sie es später erneut.", "May": "Mai", - "MCP": "", + "MCP": "MCP", "MCP support is experimental and its specification changes often, which can lead to incompatibilities. OpenAPI specification support is directly maintained by the Open WebUI team, making it the more reliable option for compatibility.": "Die MCP-Unterstützung ist experimentell und ihre Spezifikation ändert sich häufig, was zu Inkompatibilitäten führen kann. Die Unterstützung der OpenAPI-Spezifikation wird direkt vom Open‑WebUI‑Team gepflegt und ist daher die verlässlichere Option in Bezug auf Kompatibilität.", "Medium": "Mittel", "Memories accessible by LLMs will be shown here.": "Erinnerungen, die für Modelle zugänglich sind, werden hier angezeigt.", @@ -1019,7 +1019,7 @@ "Models": "Modelle", "Models Access": "Modell-Zugriff", "Models configuration saved successfully": "Modellkonfiguration erfolgreich gespeichert", - "Models imported successfully": "", + "Models imported successfully": "Modelle erfolgreich importiert", "Models Public Sharing": "Öffentliche Freigabe von Modellen", "Mojeek Search API Key": "Mojeek Search API-Schlüssel", "More": "Mehr", @@ -1079,13 +1079,13 @@ "Note deleted successfully": "Notiz erfolgreich gelöscht", "Note: If you set a minimum score, the search will only return documents with a score greater than or equal to the minimum score.": "Hinweis: Wenn Sie eine Mindestpunktzahl festlegen, werden in der Suche nur Dokumente mit einer Punktzahl größer oder gleich der Mindestpunktzahl zurückgegeben.", "Notes": "Notizen", - "Notes Public Sharing": "", + "Notes Public Sharing": "Öffentliche Freigabe von Notizen", "Notification Sound": "Benachrichtigungston", "Notification Webhook": "Benachrichtigungs-Webhook", "Notifications": "Benachrichtigungen", "November": "November", "OAuth": "OAuth", - "OAuth 2.1": "", + "OAuth 2.1": "OAuth 2.1", "OAuth ID": "OAuth-ID", "October": "Oktober", "Off": "Aus", @@ -1128,8 +1128,8 @@ "OpenAI API Key is required.": "OpenAI-API-Schlüssel erforderlich.", "OpenAI API settings updated": "OpenAI-API-Einstellungen aktualisiert", "OpenAI URL/Key required.": "OpenAI-URL/Schlüssel erforderlich.", - "OpenAPI": "", - "OpenAPI Spec": "", + "OpenAPI": "OpenAPI", + "OpenAPI Spec": "OpenAPI-Spezifikation", "openapi.json URL or Path": "openapi.json-URL oder Pfad", "Optional": "Optional", "Options for running a local vision-language model in the picture description. The parameters refer to a model hosted on Hugging Face. This parameter is mutually exclusive with picture_description_api.": "Optionen zum Ausführen eines lokalen Vision-Language-Modells in der Bildbeschreibung. Die Parameter beziehen sich auf ein Modell, das auf Hugging Face gehostet wird. Dieser Parameter ist gegenseitig ausschließend mit picture_description_api.", @@ -1187,7 +1187,7 @@ "Please enter a message or attach a file.": "Bitte geben Sie eine Nachricht ein oder hängen Sie eine Datei an.", "Please enter a prompt": "Bitte geben Sie einen Prompt ein", "Please enter a valid ID": "Bitte geben Sie eine gültige ID ein", - "Please enter a valid JSON spec": "", + "Please enter a valid JSON spec": "Bitte geben Sie eine gültige JSON-Spezifikation ein", "Please enter a valid path": "Bitte geben Sie einen gültigen Pfad ein", "Please enter a valid URL": "Bitte geben Sie eine gültige URL ein", "Please enter a valid URL.": "Bitte geben Sie eine gültige URL ein", @@ -1197,7 +1197,7 @@ "Please select a model first.": "Bitte wählen Sie zuerst ein Modell aus.", "Please select a model.": "Bitte wählen Sie ein Modell aus.", "Please select a reason": "Bitte wählen Sie einen Grund aus", - "Please select a valid JSON file": "", + "Please select a valid JSON file": "Bitte wählen Sie eine gültige JSON-Datei aus", "Please wait until all files are uploaded.": "Bitte warten Sie, bis alle Dateien hochgeladen sind.", "Port": "Port", "Positive attitude": "Positive Einstellung", @@ -1268,10 +1268,10 @@ "Remove this tag from list": "Diesen Tag von der Liste entfernen", "Rename": "Umbenennen", "Reorder Models": "Modelle neu anordnen", - "Reply": "", + "Reply": "Antworten", "Reply in Thread": "Im Thread antworten", "Reply to thread...": "Im Thread antworten...", - "Replying to {{NAME}}": "", + "Replying to {{NAME}}": "{{NAME}} antworten", "required": "benötigt", "Reranking Engine": "Reranking-Engine", "Reranking Model": "Reranking-Modell", @@ -1458,7 +1458,7 @@ "Stop Sequence": "Stop-Sequenz", "Stream Chat Response": "Chat-Antwort streamen", "Stream Delta Chunk Size": "Stream-Delta-Chunk-Größe", - "Streamable HTTP": "", + "Streamable HTTP": "Streambares HTTP", "Strikethrough": "Durchgestrichen", "Strip Existing OCR": "Vorhandenes OCR entfernen", "Strip existing OCR text from the PDF and re-run OCR. Ignored if Force OCR is enabled. Defaults to False.": "Vorhandenen OCR-Text aus der PDF entfernen und OCR erneut ausführen. Wird ignoriert, wenn OCR erzwingen aktiviert ist. Standardmäßig deaktiviert.", @@ -1516,7 +1516,7 @@ "The score should be a value between 0.0 (0%) and 1.0 (100%).": "Die Punktzahl sollte ein Wert zwischen 0.0 (0 %) und 1.0 (100 %) sein.", "The stream delta chunk size for the model. Increasing the chunk size will make the model respond with larger pieces of text at once.": "Die Stream-Delta-Chunk-Größe für das Modell. Eine Erhöhung der Chunk-Größe führt dazu, dass das Modell mit größeren Textabschnitten auf einmal antwortet.", "The temperature of the model. Increasing the temperature will make the model answer more creatively.": "Die Temperatur des Modells. Eine Erhöhung der Temperatur führt zu kreativeren Antworten des Modells.", - "The Weight of BM25 Hybrid Search. 0 more semantic, 1 more lexical. Default 0.5": "", + "The Weight of BM25 Hybrid Search. 0 more semantic, 1 more lexical. Default 0.5": "Das Gewicht der BM25-Hybridsuche. 0 stärker semantisch, 1 stärker lexikalisch. Standard 0.5", "The width in pixels to compress images to. Leave empty for no compression.": "Die Breite in Pixeln, auf die Bilder komprimiert werden sollen. Leer lassen, um keine Komprimierung durchzuführen.", "Theme": "Design", "Thinking...": "Denke nach...", @@ -1525,7 +1525,7 @@ "This chat won't appear in history and your messages will not be saved.": "Diese Unterhaltung erscheint nicht in Ihrem Chat-Verlauf. Alle Nachrichten sind privat und werden nicht gespeichert.", "This ensures that your valuable conversations are securely saved to your backend database. Thank you!": "Dies stellt sicher, dass Ihre wertvollen Chats sicher in Ihrer Backend-Datenbank gespeichert werden. Vielen Dank!", "This feature is experimental and may be modified or discontinued without notice.": "Diese Funktion ist experimentell und kann ohne Ankündigung geändert oder entfernt werden.", - "This is a default user permission and will remain enabled.": "", + "This is a default user permission and will remain enabled.": "Dies ist eine Standardbenutzerberechtigung und bleibt aktiviert.", "This is an experimental feature, it may not function as expected and is subject to change at any time.": "Dies ist eine experimentelle Funktion, sie funktioniert möglicherweise nicht wie erwartet und kann jederzeit geändert werden.", "This model is not publicly available. Please select another model.": "Dieses Modell ist nicht öffentlich verfügbar. Bitte wählen Sie ein anderes Modell aus.", "This option controls how long the model will stay loaded into memory following the request (default: 5m)": "Diese Option steuert, wie lange das Modell nach der Anfrage im Speicher verbleibt (Standard: 5m)", @@ -1603,8 +1603,8 @@ "Unarchive All Archived Chats": "Alle archivierten Chats wiederherstellen", "Unarchive Chat": "Chat wiederherstellen", "Underline": "Unterstreichen", - "Unknown": "", - "Unknown User": "", + "Unknown": "Unbekannt", + "Unknown User": "Unbekannter Benutzer", "Unloads {{FROM_NOW}}": "Entlädt {{FROM_NOW}}", "Unlock mysteries": "Geheimnisse entsperren", "Unpin": "Lösen", From c9c0dd367f3a09e01edb61b62256779d5bb75904 Mon Sep 17 00:00:00 2001 From: Jan Kessler Date: Mon, 29 Sep 2025 11:58:58 +0200 Subject: [PATCH 006/127] log web search queries only with level 'debug' instead of 'info' --- backend/open_webui/routers/retrieval.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/open_webui/routers/retrieval.py b/backend/open_webui/routers/retrieval.py index d322addfa6..0d4c3c98fc 100644 --- a/backend/open_webui/routers/retrieval.py +++ b/backend/open_webui/routers/retrieval.py @@ -2047,7 +2047,7 @@ async def process_web_search( result_items = [] try: - logging.info( + logging.debug( f"trying to web search with {request.app.state.config.WEB_SEARCH_ENGINE, form_data.queries}" ) From 0a928d6e9d0036bbfadc58729e72b4d2dabf5bdb Mon Sep 17 00:00:00 2001 From: Jacob Leksan Date: Mon, 29 Sep 2025 12:50:01 -0400 Subject: [PATCH 007/127] Tool calls now only include text and dont inlcude other content like image b64 --- backend/open_webui/utils/middleware.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/backend/open_webui/utils/middleware.py b/backend/open_webui/utils/middleware.py index e4bf1195ff..b6a1bf3221 100644 --- a/backend/open_webui/utils/middleware.py +++ b/backend/open_webui/utils/middleware.py @@ -84,6 +84,7 @@ from open_webui.utils.misc import ( get_system_message, prepend_to_first_user_message_content, convert_logit_bias_input_to_json, + get_content_from_message, ) from open_webui.utils.tools import get_tools from open_webui.utils.plugin import load_function_module_by_id @@ -298,7 +299,7 @@ async def chat_completion_tools_handler( recent_messages = messages[-4:] if len(messages) > 4 else messages chat_history = "\n".join( - f"{message['role'].upper()}: \"\"\"{message['content']}\"\"\"" + f"{message['role'].upper()}: \"\"\"{get_content_from_message(message)}\"\"\"" for message in recent_messages ) From 2a3f57bc61740edd533304086dfdfb5486c6b903 Mon Sep 17 00:00:00 2001 From: Classic298 <27028174+Classic298@users.noreply.github.com> Date: Mon, 29 Sep 2025 22:12:49 +0200 Subject: [PATCH 008/127] fix onedrive --- src/lib/utils/onedrive-file-picker.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/lib/utils/onedrive-file-picker.ts b/src/lib/utils/onedrive-file-picker.ts index e55882e081..78477e80db 100644 --- a/src/lib/utils/onedrive-file-picker.ts +++ b/src/lib/utils/onedrive-file-picker.ts @@ -3,7 +3,8 @@ import { v4 as uuidv4 } from 'uuid'; class OneDriveConfig { private static instance: OneDriveConfig; - private clientId: string = ''; + private clientIdPersonal: string = ''; + private clientIdBusiness: string = ''; private sharepointUrl: string = ''; private sharepointTenantId: string = ''; private msalInstance: PublicClientApplication | null = null; @@ -49,8 +50,8 @@ class OneDriveConfig { this.sharepointUrl = config.onedrive?.sharepoint_url; this.sharepointTenantId = config.onedrive?.sharepoint_tenant_id; - if (!this.newClientIdPersonal && !this.newClientIdBusiness) { - throw new Error('OneDrive client ID not configured'); + if (!this.clientIdPersonal && !this.clientIdBusiness) { + throw new Error('OneDrive personal or business client ID not configured'); } } From 73f32a88e318172d1b089274f23d9819a2110c82 Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Mon, 29 Sep 2025 21:30:19 -0500 Subject: [PATCH 009/127] fix: discovery url --- backend/open_webui/routers/configs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/open_webui/routers/configs.py b/backend/open_webui/routers/configs.py index f19fbeedd0..46fbb41b99 100644 --- a/backend/open_webui/routers/configs.py +++ b/backend/open_webui/routers/configs.py @@ -213,7 +213,7 @@ async def verify_tool_servers_config( ) async with aiohttp.ClientSession() as session: async with session.get( - discovery_urls[0] + discovery_url ) as oauth_server_metadata_response: if oauth_server_metadata_response.status == 200: try: From 887772db22e44c63fd8dcbcfc319464b407f00e8 Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Mon, 29 Sep 2025 21:36:07 -0500 Subject: [PATCH 010/127] fix: default permissions not being loaded --- src/lib/components/admin/Users/Groups.svelte | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/lib/components/admin/Users/Groups.svelte b/src/lib/components/admin/Users/Groups.svelte index 3d72c96dc8..2b262767ef 100644 --- a/src/lib/components/admin/Users/Groups.svelte +++ b/src/lib/components/admin/Users/Groups.svelte @@ -57,12 +57,6 @@ const setGroups = async () => { const allGroups = await getGroups(localStorage.token); - const userGroup = allGroups.find((g) => g.name.toLowerCase() === 'user'); - - if (userGroup) { - defaultPermissions = userGroup.permissions; - } - groups = allGroups.filter((g) => g.name.toLowerCase() !== 'user'); }; @@ -110,6 +104,7 @@ total = res.total; } + defaultPermissions = await getUserDefaultPermissions(localStorage.token); await setGroups(); loaded = true; }); From 58efa18f96919ab32ad776c92e12f091c97038d9 Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Mon, 29 Sep 2025 21:37:38 -0500 Subject: [PATCH 011/127] fix: ai hallucination --- src/lib/components/admin/Users/Groups.svelte | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/lib/components/admin/Users/Groups.svelte b/src/lib/components/admin/Users/Groups.svelte index 2b262767ef..fcf5128c7b 100644 --- a/src/lib/components/admin/Users/Groups.svelte +++ b/src/lib/components/admin/Users/Groups.svelte @@ -56,8 +56,7 @@ let showDefaultPermissionsModal = false; const setGroups = async () => { - const allGroups = await getGroups(localStorage.token); - groups = allGroups.filter((g) => g.name.toLowerCase() !== 'user'); + groups = await getGroups(localStorage.token); }; const addGroupHandler = async (group) => { From fcc3d9ed2b1b291043c8b2247494f5e46c793b9d Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Mon, 29 Sep 2025 22:45:38 -0500 Subject: [PATCH 012/127] fix: non rich text input copy --- src/lib/components/common/RichTextInput.svelte | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/lib/components/common/RichTextInput.svelte b/src/lib/components/common/RichTextInput.svelte index dae14f9682..a97b53741b 100644 --- a/src/lib/components/common/RichTextInput.svelte +++ b/src/lib/components/common/RichTextInput.svelte @@ -1035,10 +1035,15 @@ if (!event.clipboardData) return false; if (richText) return false; // Let ProseMirror handle normal copy in rich text mode - const plain = editor.getText(); - const html = editor.getHTML(); + const { state } = view; + const { from, to } = state.selection; - event.clipboardData.setData('text/plain', plain.replaceAll('\n\n', '\n')); + // Only take the selected text & HTML, not the full doc + const plain = state.doc.textBetween(from, to, '\n'); + const slice = state.doc.cut(from, to); + const html = editor.schema ? editor.getHTML(slice) : editor.getHTML(); // depending on your editor API + + event.clipboardData.setData('text/plain', plain); event.clipboardData.setData('text/html', html); event.preventDefault(); From 88a6fe3c7a3e8fab3d569ae12d3bf9c0f73c7d07 Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Mon, 29 Sep 2025 22:59:10 -0500 Subject: [PATCH 013/127] refac: rm print statements --- backend/open_webui/utils/middleware.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/backend/open_webui/utils/middleware.py b/backend/open_webui/utils/middleware.py index 48da091170..2667252b37 100644 --- a/backend/open_webui/utils/middleware.py +++ b/backend/open_webui/utils/middleware.py @@ -1411,10 +1411,6 @@ async def process_chat_payload(request, form_data, user, metadata, model): } ) - print("Final form_data:", form_data) - print("Final metadata:", metadata) - print("Final events:", events) - return form_data, metadata, events From 8c662c65a9c5db655ba7982866f75f06a58753e8 Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Mon, 29 Sep 2025 22:59:28 -0500 Subject: [PATCH 014/127] refac: disable direct models from model editors --- src/lib/components/workspace/Models/ModelEditor.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/components/workspace/Models/ModelEditor.svelte b/src/lib/components/workspace/Models/ModelEditor.svelte index c846414d4b..012c4d4dbd 100644 --- a/src/lib/components/workspace/Models/ModelEditor.svelte +++ b/src/lib/components/workspace/Models/ModelEditor.svelte @@ -526,7 +526,7 @@ - {#each $models.filter((m) => (model ? m.id !== model.id : true) && !m?.preset && m?.owned_by !== 'arena') as model} + {#each $models.filter((m) => (model ? m.id !== model.id : true) && !m?.preset && m?.owned_by !== 'arena' && !(m?.direct ?? false)) as model} {/each} From 01a5b97415d777b1814e8ff4502539429150647d Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Mon, 29 Sep 2025 23:05:24 -0500 Subject: [PATCH 015/127] refac/fix: do not process xlsx files with azure doc intelligence --- backend/open_webui/retrieval/loaders/main.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/backend/open_webui/retrieval/loaders/main.py b/backend/open_webui/retrieval/loaders/main.py index 45f3d8c941..b3d90cc8f3 100644 --- a/backend/open_webui/retrieval/loaders/main.py +++ b/backend/open_webui/retrieval/loaders/main.py @@ -346,11 +346,9 @@ class Loader: self.engine == "document_intelligence" and self.kwargs.get("DOCUMENT_INTELLIGENCE_ENDPOINT") != "" and ( - file_ext in ["pdf", "xls", "xlsx", "docx", "ppt", "pptx"] + file_ext in ["pdf", "docx", "ppt", "pptx"] or file_content_type in [ - "application/vnd.ms-excel", - "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "application/vnd.openxmlformats-officedocument.wordprocessingml.document", "application/vnd.ms-powerpoint", "application/vnd.openxmlformats-officedocument.presentationml.presentation", From 0d0d28641d58de427d702afdbbd2a33017fd21d6 Mon Sep 17 00:00:00 2001 From: Classic298 <27028174+Classic298@users.noreply.github.com> Date: Tue, 30 Sep 2025 08:36:31 +0200 Subject: [PATCH 016/127] Update pull_request_template.md --- .github/pull_request_template.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index fa82ae26a1..0ec871f328 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -4,14 +4,15 @@ **Before submitting, make sure you've checked the following:** -- [ ] **Target branch:** Please verify that the pull request targets the `dev` branch. +- [ ] **Target branch:** Verify that the pull request targets the `dev` branch. Not targeting the `dev` branch may lead to immediate closure of the PR. - [ ] **Description:** Provide a concise description of the changes made in this pull request. - [ ] **Changelog:** Ensure a changelog entry following the format of [Keep a Changelog](https://keepachangelog.com/) is added at the bottom of the PR description. -- [ ] **Documentation:** Have you updated relevant documentation [Open WebUI Docs](https://github.com/open-webui/docs), or other documentation sources? +- [ ] **Documentation:** If necessary, update relevant documentation [Open WebUI Docs](https://github.com/open-webui/docs) like environment variables, the tutorials, or other documentation sources. - [ ] **Dependencies:** Are there any new dependencies? Have you updated the dependency versions in the documentation? -- [ ] **Testing:** Have you written and run sufficient tests to validate the changes? +- [ ] **Testing:** Perform manual tests to verify the implemented fix/feature works as intended AND does not break any other functionality. Take this as an opportunity to make screenshots of the feature/fix and include it in the PR description. +- [ ] **Agentic AI Code:**: Confirm this Pull Request is **not written by any AI Agent** or has at least gone through additional human review **and** manual testing. If any AI Agent is the co-author of this PR, it may lead to immediate closure of the PR. - [ ] **Code review:** Have you performed a self-review of your code, addressing any coding standard issues and ensuring adherence to the project's coding standards? -- [ ] **Prefix:** To clearly categorize this pull request, prefix the pull request title using one of the following: +- [ ] **Title Prefix:** To clearly categorize this pull request, prefix the pull request title using one of the following: - **BREAKING CHANGE**: Significant changes that may affect compatibility - **build**: Changes that affect the build system or external dependencies - **ci**: Changes to our continuous integration processes or workflows From 7259905114ab18f5b07750872170dbd6e53cdc9d Mon Sep 17 00:00:00 2001 From: Classic298 <27028174+Classic298@users.noreply.github.com> Date: Tue, 30 Sep 2025 10:51:39 +0200 Subject: [PATCH 017/127] Update generated image translation in DE-de --- src/lib/i18n/locales/de-DE/translation.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/i18n/locales/de-DE/translation.json b/src/lib/i18n/locales/de-DE/translation.json index df3bce398f..aa12d704b1 100644 --- a/src/lib/i18n/locales/de-DE/translation.json +++ b/src/lib/i18n/locales/de-DE/translation.json @@ -777,7 +777,7 @@ "Generate an image": "Bild erzeugen", "Generate Image": "Bild erzeugen", "Generate prompt pair": "Prompt-Paar generieren", - "Generated Image": "Erzeugtes Bild", + "Generated Image": "Generiertes Bild", "Generating search query": "Suchanfrage wird erstellt", "Generating...": "Generiere...", "Get information on {{name}} in the UI": "Informationen zu {{name}} in der Benutzeroberfläche abrufen", From e96fb673676bd4a140744bc3f52dd73815b69092 Mon Sep 17 00:00:00 2001 From: sinejespersen Date: Tue, 30 Sep 2025 12:49:12 +0200 Subject: [PATCH 018/127] added missing danish translations --- src/lib/i18n/locales/da-DK/translation.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/lib/i18n/locales/da-DK/translation.json b/src/lib/i18n/locales/da-DK/translation.json index ef386d0edf..3914462cd1 100644 --- a/src/lib/i18n/locales/da-DK/translation.json +++ b/src/lib/i18n/locales/da-DK/translation.json @@ -1227,7 +1227,7 @@ "Pull a model from Ollama.com": "Hent en model fra Ollama.com", "pypdfium2": "", "Query Generation Prompt": "Forespørgsel genererings prompt", - "Querying": "", + "Querying": "Undersøger", "Quick Actions": "", "RAG Template": "RAG-skabelon", "Rating": "Rating", @@ -1289,10 +1289,10 @@ "RESULT": "Resultat", "Retrieval": "Hentning", "Retrieval Query Generation": "Hentnings forespørgsel generering", - "Retrieved {{count}} sources": "", + "Retrieved {{count}} sources": "Fandt en kildehenvisning", "Retrieved {{count}} sources_one": "", "Retrieved {{count}} sources_other": "", - "Retrieved 1 source": "", + "Retrieved 1 source": "Fandt en kildehenvisning", "Rich Text Input for Chat": "Rich text input til chat", "RK": "RK", "Role": "Rolle", @@ -1564,7 +1564,7 @@ "To select toolkits here, add them to the \"Tools\" workspace first.": "For at vælge værktøjssæt her skal du først tilføje dem til \"Værktøjer\"-arbejdsområdet.", "Toast notifications for new updates": "Toast-notifikationer for nye opdateringer", "Today": "I dag", - "Today at {{LOCALIZED_TIME}}": "", + "Today at {{LOCALIZED_TIME}}": "I dag {{LOCALIZED_TIME}}", "Toggle search": "Skift søgning", "Toggle settings": "Skift indstillinger", "Toggle sidebar": "Skift sidebjælke", From 07cc807bc9a2c47172ea780a4786818465135734 Mon Sep 17 00:00:00 2001 From: Selene Blok Date: Tue, 30 Sep 2025 16:43:38 +0200 Subject: [PATCH 019/127] feat(onedrive): Enable search and "My Organization" pivot --- src/lib/utils/onedrive-file-picker.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/lib/utils/onedrive-file-picker.ts b/src/lib/utils/onedrive-file-picker.ts index 78477e80db..e1d6df0392 100644 --- a/src/lib/utils/onedrive-file-picker.ts +++ b/src/lib/utils/onedrive-file-picker.ts @@ -176,6 +176,9 @@ interface PickerParams { origin: string; channelId: string; }; + search: { + enabled: boolean; + } typesAndSources: { mode: string; pivots: Record; @@ -204,11 +207,15 @@ function getPickerParams(): PickerParams { origin: window?.location?.origin || '', channelId }, + search: { + enabled: true + }, typesAndSources: { mode: 'files', pivots: { oneDrive: true, - recent: true + recent: true, + myOrganization: config.getAuthorityType() === "organizations", } } }; From 5c059e604ba73d0710834270e27a9be3e4ec38fb Mon Sep 17 00:00:00 2001 From: Selene Blok Date: Tue, 30 Sep 2025 16:49:53 +0200 Subject: [PATCH 020/127] style(onedrive): Formatting fix --- src/lib/utils/onedrive-file-picker.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lib/utils/onedrive-file-picker.ts b/src/lib/utils/onedrive-file-picker.ts index e1d6df0392..136d239294 100644 --- a/src/lib/utils/onedrive-file-picker.ts +++ b/src/lib/utils/onedrive-file-picker.ts @@ -51,7 +51,7 @@ class OneDriveConfig { this.sharepointTenantId = config.onedrive?.sharepoint_tenant_id; if (!this.clientIdPersonal && !this.clientIdBusiness) { - throw new Error('OneDrive personal or business client ID not configured'); + throw new Error('OneDrive personal or business client ID not configured'); } } @@ -178,7 +178,7 @@ interface PickerParams { }; search: { enabled: boolean; - } + }; typesAndSources: { mode: string; pivots: Record; @@ -215,7 +215,7 @@ function getPickerParams(): PickerParams { pivots: { oneDrive: true, recent: true, - myOrganization: config.getAuthorityType() === "organizations", + myOrganization: config.getAuthorityType() === 'organizations' } } }; From 80cbdbb535293bba4ad6dba122a289952901bd9e Mon Sep 17 00:00:00 2001 From: silentoplayz Date: Tue, 30 Sep 2025 16:09:55 -0400 Subject: [PATCH 021/127] feat: Implement toggling for vertical and horizontal flow layouts This commit introduces the necessary logic and UI controls to allow users to switch the Flow component layout between vertical and horizontal orientations. * **`Flow.svelte` Refactoring:** * Updates logic for calculating level offsets and node positions to consistently respect the current flow orientation. * Adds a control panel using `` and `` components. * Provides user interface elements to easily switch the flow layout between horizontal and vertical orientations. --- src/lib/components/chat/Overview.svelte | 23 +++++++++++++------- src/lib/components/chat/Overview/Flow.svelte | 22 ++++++++++++++++--- 2 files changed, 34 insertions(+), 11 deletions(-) diff --git a/src/lib/components/chat/Overview.svelte b/src/lib/components/chat/Overview.svelte index 7dbebee1d9..5e0dd46be1 100644 --- a/src/lib/components/chat/Overview.svelte +++ b/src/lib/components/chat/Overview.svelte @@ -29,12 +29,14 @@ const nodes = writable([]); const edges = writable([]); + let layoutDirection = 'vertical'; + const nodeTypes = { custom: CustomNode }; $: if (history) { - drawFlow(); + drawFlow(layoutDirection); } $: if (history && history.currentId) { @@ -51,11 +53,11 @@ selectedMessageId = null; }; - const drawFlow = async () => { + const drawFlow = async (direction) => { const nodeList = []; const edgeList = []; - const levelOffset = 150; // Vertical spacing between layers - const siblingOffset = 250; // Horizontal spacing between nodes at the same layer + const levelOffset = direction === 'vertical' ? 150 : 300; + const siblingOffset = direction === 'vertical' ? 250 : 150; // Map to keep track of node positions at each level let positionMap = new Map(); @@ -84,9 +86,8 @@ // Adjust positions based on siblings count to centralize vertical spacing Object.keys(history.messages).forEach((id) => { const pos = positionMap.get(id); - const xOffset = pos.position * siblingOffset; - const y = pos.level * levelOffset; - const x = xOffset; + const x = direction === 'vertical' ? pos.position * siblingOffset : pos.level * levelOffset; + const y = direction === 'vertical' ? pos.level * levelOffset : pos.position * siblingOffset; nodeList.push({ id: pos.id, @@ -126,8 +127,13 @@ ); }; + const setLayoutDirection = (direction) => { + layoutDirection = direction; + drawFlow(layoutDirection); + }; + onMount(() => { - drawFlow(); + drawFlow(layoutDirection); nodesInitialized.subscribe(async (initialized) => { if (initialized) { @@ -188,6 +194,7 @@ {nodes} {nodeTypes} {edges} + {setLayoutDirection} on:nodeclick={(e) => { console.log(e.detail.node.data); dispatch('nodeclick', e.detail); diff --git a/src/lib/components/chat/Overview/Flow.svelte b/src/lib/components/chat/Overview/Flow.svelte index f7ff307567..4eeb497f63 100644 --- a/src/lib/components/chat/Overview/Flow.svelte +++ b/src/lib/components/chat/Overview/Flow.svelte @@ -4,11 +4,20 @@ const dispatch = createEventDispatcher(); import { theme } from '$lib/stores'; - import { Background, Controls, SvelteFlow, BackgroundVariant } from '@xyflow/svelte'; + import { + Background, + Controls, + SvelteFlow, + BackgroundVariant, + ControlButton + } from '@xyflow/svelte'; + import BarsArrowUp from '$lib/components/icons/BarsArrowUp.svelte'; + import Bars3BottomLeft from '$lib/components/icons/Bars3BottomLeft.svelte'; export let nodes; export let nodeTypes; export let edges; + export let setLayoutDirection; - + + setLayoutDirection('vertical')} title="Vertical Layout"> + + + setLayoutDirection('horizontal')} title="Horizontal Layout"> + + + - + \ No newline at end of file From 6cc7a8b0522cf7432a5c4c7d12601d651ef26ed3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Oct 2025 02:09:11 +0000 Subject: [PATCH 022/127] build(deps): bump pydantic from 2.11.7 to 2.11.9 in /backend Bumps [pydantic](https://github.com/pydantic/pydantic) from 2.11.7 to 2.11.9. - [Release notes](https://github.com/pydantic/pydantic/releases) - [Changelog](https://github.com/pydantic/pydantic/blob/v2.11.9/HISTORY.md) - [Commits](https://github.com/pydantic/pydantic/compare/v2.11.7...v2.11.9) --- updated-dependencies: - dependency-name: pydantic dependency-version: 2.11.9 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- backend/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/requirements.txt b/backend/requirements.txt index 27e0c24cb7..b53c36895b 100644 --- a/backend/requirements.txt +++ b/backend/requirements.txt @@ -1,6 +1,6 @@ fastapi==0.115.7 uvicorn[standard]==0.35.0 -pydantic==2.11.7 +pydantic==2.11.9 python-multipart==0.0.20 itsdangerous==2.2.0 From d9a723b0abf26cfc44146519bb9264282000a0c2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Oct 2025 02:09:14 +0000 Subject: [PATCH 023/127] build(deps): bump black from 25.1.0 to 25.9.0 in /backend Bumps [black](https://github.com/psf/black) from 25.1.0 to 25.9.0. - [Release notes](https://github.com/psf/black/releases) - [Changelog](https://github.com/psf/black/blob/main/CHANGES.md) - [Commits](https://github.com/psf/black/compare/25.1.0...25.9.0) --- updated-dependencies: - dependency-name: black dependency-version: 25.9.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- backend/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/requirements.txt b/backend/requirements.txt index 27e0c24cb7..c187377aaf 100644 --- a/backend/requirements.txt +++ b/backend/requirements.txt @@ -105,7 +105,7 @@ onnxruntime==1.20.1 faster-whisper==1.1.1 -black==25.1.0 +black==25.9.0 youtube-transcript-api==1.2.2 pytube==15.0.0 From 17af98126a76fc71015cbaf531ca1edeff920c50 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Oct 2025 02:09:20 +0000 Subject: [PATCH 024/127] build(deps): bump markdown from 3.8.2 to 3.9 in /backend Bumps [markdown](https://github.com/Python-Markdown/markdown) from 3.8.2 to 3.9. - [Release notes](https://github.com/Python-Markdown/markdown/releases) - [Changelog](https://github.com/Python-Markdown/markdown/blob/master/docs/changelog.md) - [Commits](https://github.com/Python-Markdown/markdown/compare/3.8.2...3.9.0) --- updated-dependencies: - dependency-name: markdown dependency-version: '3.9' dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- backend/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/requirements.txt b/backend/requirements.txt index 27e0c24cb7..42a2f103ce 100644 --- a/backend/requirements.txt +++ b/backend/requirements.txt @@ -84,7 +84,7 @@ docx2txt==0.8 python-pptx==1.0.2 unstructured==0.16.17 nltk==3.9.1 -Markdown==3.8.2 +Markdown==3.9 pypandoc==1.15 pandas==2.2.3 openpyxl==3.1.5 From 242bcc8643889e95747120093f1545955b6da9e1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Oct 2025 02:09:24 +0000 Subject: [PATCH 025/127] build(deps): bump chromadb from 1.0.20 to 1.1.0 in /backend Bumps [chromadb](https://github.com/chroma-core/chroma) from 1.0.20 to 1.1.0. - [Release notes](https://github.com/chroma-core/chroma/releases) - [Changelog](https://github.com/chroma-core/chroma/blob/main/RELEASE_PROCESS.md) - [Commits](https://github.com/chroma-core/chroma/compare/1.0.20...1.1.0) --- updated-dependencies: - dependency-name: chromadb dependency-version: 1.1.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- backend/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/requirements.txt b/backend/requirements.txt index 27e0c24cb7..97e7e47ccb 100644 --- a/backend/requirements.txt +++ b/backend/requirements.txt @@ -57,7 +57,7 @@ langchain==0.3.27 langchain-community==0.3.29 fake-useragent==2.2.0 -chromadb==1.0.20 +chromadb==1.1.0 opensearch-py==2.8.0 pymilvus==2.5.0 From b0e3afc0be972f4d546a982dae5a153230a2fe77 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Oct 2025 02:25:40 +0000 Subject: [PATCH 026/127] build(deps): bump opentelemetry-api from 1.36.0 to 1.37.0 Bumps [opentelemetry-api](https://github.com/open-telemetry/opentelemetry-python) from 1.36.0 to 1.37.0. - [Release notes](https://github.com/open-telemetry/opentelemetry-python/releases) - [Changelog](https://github.com/open-telemetry/opentelemetry-python/blob/main/CHANGELOG.md) - [Commits](https://github.com/open-telemetry/opentelemetry-python/compare/v1.36.0...v1.37.0) --- updated-dependencies: - dependency-name: opentelemetry-api dependency-version: 1.37.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- backend/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/requirements.txt b/backend/requirements.txt index 27e0c24cb7..ea928d47eb 100644 --- a/backend/requirements.txt +++ b/backend/requirements.txt @@ -139,7 +139,7 @@ firecrawl-py==1.12.0 tencentcloud-sdk-python==3.0.1336 ## Trace -opentelemetry-api==1.36.0 +opentelemetry-api==1.37.0 opentelemetry-sdk==1.36.0 opentelemetry-exporter-otlp==1.36.0 opentelemetry-instrumentation==0.57b0 From a8a0a2655dc841edca4b481105ebb6db5ea27346 Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Tue, 30 Sep 2025 22:04:22 -0500 Subject: [PATCH 027/127] refac: ollama embed form data --- backend/open_webui/routers/ollama.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/backend/open_webui/routers/ollama.py b/backend/open_webui/routers/ollama.py index bf11ffa0dd..64b0687afa 100644 --- a/backend/open_webui/routers/ollama.py +++ b/backend/open_webui/routers/ollama.py @@ -1020,6 +1020,10 @@ class GenerateEmbedForm(BaseModel): options: Optional[dict] = None keep_alive: Optional[Union[int, str]] = None + model_config = ConfigDict( + extra="allow", + ) + @router.post("/api/embed") @router.post("/api/embed/{url_idx}") From b1c196ed8387020a6d034c072604c2493d169ae9 Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Wed, 1 Oct 2025 12:39:49 -0500 Subject: [PATCH 028/127] fix: non rich text handling --- src/lib/components/common/RichTextInput.svelte | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/lib/components/common/RichTextInput.svelte b/src/lib/components/common/RichTextInput.svelte index a97b53741b..0586b4a4f1 100644 --- a/src/lib/components/common/RichTextInput.svelte +++ b/src/lib/components/common/RichTextInput.svelte @@ -789,11 +789,19 @@ ) .replace(/\u00a0/g, ' '); - onChange({ - html: htmlValue, - json: jsonValue, - md: mdValue - }); + if (richText) { + onChange({ + html: htmlValue, + json: jsonValue, + md: mdValue + }); + } else { + // Plain text path: preserve \t and \n exactly + const doc = editor.view.state.doc; + const plain = doc.textBetween(0, doc.content.size, '\n\n', '\n'); // keeps \t intact + value = plain; + onChange({ html: null, json: null, md: plain }); + } if (json) { value = jsonValue; From e493562735089462ad6cc30060778eb79ed302a6 Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Wed, 1 Oct 2025 15:15:24 -0500 Subject: [PATCH 029/127] fix: oauth client registration --- backend/open_webui/config.py | 20 ++++++++++---------- backend/open_webui/utils/oauth.py | 10 +++++++++- 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/backend/open_webui/config.py b/backend/open_webui/config.py index 3a4309438a..fc60455e3e 100644 --- a/backend/open_webui/config.py +++ b/backend/open_webui/config.py @@ -605,8 +605,8 @@ def load_oauth_providers(): OAUTH_PROVIDERS.clear() if GOOGLE_CLIENT_ID.value and GOOGLE_CLIENT_SECRET.value: - def google_oauth_register(client: OAuth): - client.register( + def google_oauth_register(oauth: OAuth): + return oauth.register( name="google", client_id=GOOGLE_CLIENT_ID.value, client_secret=GOOGLE_CLIENT_SECRET.value, @@ -633,8 +633,8 @@ def load_oauth_providers(): and MICROSOFT_CLIENT_TENANT_ID.value ): - def microsoft_oauth_register(client: OAuth): - client.register( + def microsoft_oauth_register(oauth: OAuth): + return oauth.register( name="microsoft", client_id=MICROSOFT_CLIENT_ID.value, client_secret=MICROSOFT_CLIENT_SECRET.value, @@ -658,8 +658,8 @@ def load_oauth_providers(): if GITHUB_CLIENT_ID.value and GITHUB_CLIENT_SECRET.value: - def github_oauth_register(client: OAuth): - client.register( + def github_oauth_register(oauth: OAuth): + return oauth.register( name="github", client_id=GITHUB_CLIENT_ID.value, client_secret=GITHUB_CLIENT_SECRET.value, @@ -690,7 +690,7 @@ def load_oauth_providers(): and OPENID_PROVIDER_URL.value ): - def oidc_oauth_register(client: OAuth): + def oidc_oauth_register(oauth: OAuth): client_kwargs = { "scope": OAUTH_SCOPES.value, **( @@ -716,7 +716,7 @@ def load_oauth_providers(): % ("S256", OAUTH_CODE_CHALLENGE_METHOD.value) ) - client.register( + return oauth.register( name="oidc", client_id=OAUTH_CLIENT_ID.value, client_secret=OAUTH_CLIENT_SECRET.value, @@ -733,8 +733,8 @@ def load_oauth_providers(): if FEISHU_CLIENT_ID.value and FEISHU_CLIENT_SECRET.value: - def feishu_oauth_register(client: OAuth): - client.register( + def feishu_oauth_register(oauth: OAuth): + return oauth.register( name="feishu", client_id=FEISHU_CLIENT_ID.value, client_secret=FEISHU_CLIENT_SECRET.value, diff --git a/backend/open_webui/utils/oauth.py b/backend/open_webui/utils/oauth.py index 6cf91e3f12..b660267e73 100644 --- a/backend/open_webui/utils/oauth.py +++ b/backend/open_webui/utils/oauth.py @@ -615,8 +615,16 @@ class OAuthManager: self.app = app self._clients = {} + for _, provider_config in OAUTH_PROVIDERS.items(): - provider_config["register"](self.oauth) + if "register" not in provider_config: + log.error( + f"OAuth provider {provider_config['name']} missing register function" + ) + continue + + client = provider_config["register"](self.oauth) + self._clients[provider_config["name"]] = client def get_client(self, provider_name): if provider_name not in self._clients: From 0330dc31592d20ccb33fa11015e55aaf811202b8 Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Wed, 1 Oct 2025 15:35:37 -0500 Subject: [PATCH 030/127] refac --- backend/open_webui/utils/oauth.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/backend/open_webui/utils/oauth.py b/backend/open_webui/utils/oauth.py index b660267e73..c598e05090 100644 --- a/backend/open_webui/utils/oauth.py +++ b/backend/open_webui/utils/oauth.py @@ -616,15 +616,13 @@ class OAuthManager: self._clients = {} - for _, provider_config in OAUTH_PROVIDERS.items(): + for name, provider_config in OAUTH_PROVIDERS.items(): if "register" not in provider_config: - log.error( - f"OAuth provider {provider_config['name']} missing register function" - ) + log.error(f"OAuth provider {name} missing register function") continue client = provider_config["register"](self.oauth) - self._clients[provider_config["name"]] = client + self._clients[name] = client def get_client(self, provider_name): if provider_name not in self._clients: From f5a4d27e57585b7a9c706d4c5bb0beac722fd54a Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Wed, 1 Oct 2025 18:13:27 -0500 Subject: [PATCH 031/127] chore: dep bump --- backend/requirements.txt | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/backend/requirements.txt b/backend/requirements.txt index 2eaaf0631b..5d4ab1a3c8 100644 --- a/backend/requirements.txt +++ b/backend/requirements.txt @@ -140,13 +140,13 @@ tencentcloud-sdk-python==3.0.1336 ## Trace opentelemetry-api==1.37.0 -opentelemetry-sdk==1.36.0 -opentelemetry-exporter-otlp==1.36.0 -opentelemetry-instrumentation==0.57b0 -opentelemetry-instrumentation-fastapi==0.57b0 -opentelemetry-instrumentation-sqlalchemy==0.57b0 -opentelemetry-instrumentation-redis==0.57b0 -opentelemetry-instrumentation-requests==0.57b0 -opentelemetry-instrumentation-logging==0.57b0 -opentelemetry-instrumentation-httpx==0.57b0 -opentelemetry-instrumentation-aiohttp-client==0.57b0 +opentelemetry-sdk==1.37.0 +opentelemetry-exporter-otlp==1.37.0 +opentelemetry-instrumentation==0.58b0 +opentelemetry-instrumentation-fastapi==0.58b0 +opentelemetry-instrumentation-sqlalchemy==0.58b0 +opentelemetry-instrumentation-redis==0.58b0 +opentelemetry-instrumentation-requests==0.58b0 +opentelemetry-instrumentation-logging==0.58b0 +opentelemetry-instrumentation-httpx==0.58b0 +opentelemetry-instrumentation-aiohttp-client==0.58b0 From 7563a62dfe03778d2cb7c43798bd8dccfcbdec48 Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Wed, 1 Oct 2025 18:14:55 -0500 Subject: [PATCH 032/127] chore: fastapi bump --- backend/requirements.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/requirements.txt b/backend/requirements.txt index 5d4ab1a3c8..b1414e42b7 100644 --- a/backend/requirements.txt +++ b/backend/requirements.txt @@ -1,5 +1,5 @@ -fastapi==0.115.7 -uvicorn[standard]==0.35.0 +fastapi==0.118.0 +uvicorn[standard]==0.37.0 pydantic==2.11.9 python-multipart==0.0.20 itsdangerous==2.2.0 From ebce0578e6bf8f04073a4c1674bcc16548e8ba42 Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Wed, 1 Oct 2025 19:19:56 -0500 Subject: [PATCH 033/127] chore/refac: bump bcrypt and remove passlib --- backend/open_webui/utils/auth.py | 25 ++++++++++++++----------- backend/requirements.txt | 3 +-- pyproject.toml | 3 +-- 3 files changed, 16 insertions(+), 15 deletions(-) diff --git a/backend/open_webui/utils/auth.py b/backend/open_webui/utils/auth.py index f941ef9263..e34803ade1 100644 --- a/backend/open_webui/utils/auth.py +++ b/backend/open_webui/utils/auth.py @@ -6,7 +6,7 @@ import hmac import hashlib import requests import os - +import bcrypt from cryptography.hazmat.primitives.ciphers.aead import AESGCM from cryptography.hazmat.primitives.asymmetric import ed25519 @@ -38,11 +38,8 @@ from open_webui.env import ( from fastapi import BackgroundTasks, Depends, HTTPException, Request, Response, status from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer -from passlib.context import CryptContext -logging.getLogger("passlib").setLevel(logging.ERROR) - log = logging.getLogger(__name__) log.setLevel(SRC_LOG_LEVELS["OAUTH"]) @@ -155,19 +152,25 @@ def get_license_data(app, key): bearer_security = HTTPBearer(auto_error=False) -pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto") -def verify_password(plain_password, hashed_password): +def get_password_hash(password: str) -> str: + """Hash a password using bcrypt""" + return bcrypt.hashpw(password.encode("utf-8"), bcrypt.gensalt()).decode("utf-8") + + +def verify_password(plain_password: str, hashed_password: str) -> bool: + """Verify a password against its hash""" return ( - pwd_context.verify(plain_password, hashed_password) if hashed_password else None + bcrypt.checkpw( + plain_password.encode("utf-8"), + hashed_password.encode("utf-8"), + ) + if hashed_password + else None ) -def get_password_hash(password): - return pwd_context.hash(password) - - def create_token(data: dict, expires_delta: Union[timedelta, None] = None) -> str: payload = data.copy() diff --git a/backend/requirements.txt b/backend/requirements.txt index b1414e42b7..23e1300b49 100644 --- a/backend/requirements.txt +++ b/backend/requirements.txt @@ -6,9 +6,8 @@ itsdangerous==2.2.0 python-socketio==5.13.0 python-jose==3.4.0 -passlib[bcrypt]==1.7.4 cryptography -bcrypt==4.3.0 +bcrypt==5.0.0 argon2-cffi==25.1.0 PyJWT[crypto]==2.10.1 authlib==1.6.3 diff --git a/pyproject.toml b/pyproject.toml index 7378d3d287..47fde0b9cc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -14,9 +14,8 @@ dependencies = [ "python-socketio==5.13.0", "python-jose==3.4.0", - "passlib[bcrypt]==1.7.4", "cryptography", - "bcrypt==4.3.0", + "bcrypt==5.0.0", "argon2-cffi==25.1.0", "PyJWT[crypto]==2.10.1", "authlib==1.6.3", From 6ddb44985e8625fe49df7a872ca8ea32f956454e Mon Sep 17 00:00:00 2001 From: Cyp Date: Thu, 2 Oct 2025 09:28:29 +0900 Subject: [PATCH 034/127] Improving Korean Translation --- src/lib/i18n/locales/ko-KR/translation.json | 92 ++++++++++----------- 1 file changed, 46 insertions(+), 46 deletions(-) diff --git a/src/lib/i18n/locales/ko-KR/translation.json b/src/lib/i18n/locales/ko-KR/translation.json index 3760d14987..43d431cafc 100644 --- a/src/lib/i18n/locales/ko-KR/translation.json +++ b/src/lib/i18n/locales/ko-KR/translation.json @@ -11,7 +11,7 @@ "{{ models }}": "{{ models }}", "{{COUNT}} Available Tools": "사용 가능한 도구 {{COUNT}}개", "{{COUNT}} characters": "{{COUNT}} 문자", - "{{COUNT}} extracted lines": "", + "{{COUNT}} extracted lines": "추출된 줄 {{COUNT}}개", "{{COUNT}} hidden lines": "숨겨진 줄 {{COUNT}}개", "{{COUNT}} Replies": "답글 {{COUNT}}개", "{{COUNT}} Sources": "{{COUNT}}개의 소스", @@ -75,7 +75,7 @@ "Advanced Params": "고급 매개변수", "AI": "", "All": "전체", - "All chats have been unarchived.": "", + "All chats have been unarchived.": "모든 채팅이 보관되지 않았습니다.", "All Documents": "모든 문서", "All models deleted successfully": "성공적으로 모든 모델이 삭제되었습니다", "Allow Call": "음성 통화 허용", @@ -88,13 +88,13 @@ "Allow Chat Share": "채팅 공유 허용", "Allow Chat System Prompt": "채팅 시스템 프롬프트 허용", "Allow Chat Valves": "채팅 밸브 허용", - "Allow Continue Response": "", + "Allow Continue Response": "계속 응답 허용", "Allow Delete Messages": "", "Allow File Upload": "파일 업로드 허용", "Allow Multiple Models in Chat": "채팅에서 여러 모델 허용", "Allow non-local voices": "외부 음성 허용", "Allow Rate Response": "", - "Allow Regenerate Response": "", + "Allow Regenerate Response": "응답 재생성 허용", "Allow Speech to Text": "음성 텍스트 변환 허용", "Allow Temporary Chat": "임시 채팅 허용", "Allow Text to Speech": "텍스트 음성 변환 허용", @@ -139,7 +139,7 @@ "archived-chat-export": "보관된 채팅 내보내기", "Are you sure you want to clear all memories? This action cannot be undone.": "정말 모든 메모리를 지우시겠습니까? 이 작업은 되돌릴 수 없습니다.", "Are you sure you want to delete this channel?": "정말 이 채널을 삭제하시겠습니까?", - "Are you sure you want to delete this message?": "정말 이 메세지를 삭제하시겠습니까?", + "Are you sure you want to delete this message?": "정말 이 메시지를 삭제하시겠습니까?", "Are you sure you want to unarchive all archived chats?": "정말 보관된 모든 채팅을 보관 해제하시겠습니까?", "Are you sure?": "확실합니까?", "Arena Models": "Arena 모델", @@ -150,7 +150,7 @@ "Attach file from knowledge": "지식 기반에서 파일 첨부", "Attach Knowledge": "지식 기반 첨부", "Attach Notes": "노트 첨부", - "Attach Webpage": "", + "Attach Webpage": "웹페이지 첨부", "Attention to detail": "세부 사항에 대한 주의", "Attribute for Mail": "메일 속성", "Attribute for Username": "사용자 이름 속성", @@ -287,7 +287,7 @@ "ComfyUI Base URL is required.": "ComfyUI 기본 URL이 필요합니다.", "ComfyUI Workflow": "ComfyUI 워크플로", "ComfyUI Workflow Nodes": "ComfyUI 워크플로 노드", - "Comma separated Node Ids (e.g. 1 or 1,2)": "쉼표로 구분된 노드 아이디(예: 1 또는 1,2)", + "Comma separated Node Ids (e.g. 1 or 1,2)": "쉼표로 구분된 노드 아이디 (예: 1 또는 1,2)", "Command": "명령", "Comment": "주석", "Completions": "완성됨", @@ -316,7 +316,7 @@ "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.": "TTS 요청에 메시지가 어떻게 나눠지는지 제어하십시오. 'Punctuation'는 문장으로 나누고, 'paragraphs'은 문단으로 나누고, '없음'은 메세지를 하나의 문자열로 인식합니다.", + "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.": "TTS 요청에 메시지가 어떻게 나눠지는지 제어하십시오. 'Punctuation'는 문장으로 나누고, 'paragraphs'은 문단으로 나누고, '없음'은 메시지를 하나의 문자열로 인식합니다.", "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.": "생성된 텍스트에서 토큰 시퀀스의 반복을 제어합니다. 높은 값(예: 1.5)은 반복에 더 강한 페널티를 부과하고, 낮은 값(예: 1.1)은 더 관대합니다. 1일 경우 비활성화됩니다.", "Controls": "제어", "Controls the balance between coherence and diversity of the output. A lower value will result in more focused and coherent text.": "출력의 일관성과 다양성 간의 균형을 제어합니다. 낮은 값은 더 집중되고 일관성 있는 텍스트를 생성합니다.", @@ -431,7 +431,7 @@ "Discover, download, and explore custom tools": "사용자 정의 도구 검색, 다운로드 및 탐색", "Discover, download, and explore model presets": "모델 사전 설정 검색, 다운로드 및 탐색", "Display": "표시", - "Display chat title in tab": "", + "Display chat title in tab": "탭에 채팅 목록 표시", "Display Emoji in Call": "음성기능에서 이모지 표시", "Display Multi-model Responses in Tabs": "탭에 여러 모델 응답 표시", "Display the username instead of You in the Chat": "채팅에서 '당신' 대신 사용자 이름 표시", @@ -490,7 +490,7 @@ "Edit Memory": "메모리 편집", "Edit User": "사용자 편집", "Edit User Group": "사용자 그룹 편집", - "edited": "", + "edited": "수정됨", "Edited": "수정됨", "Editing": "수정중", "Eject": "추출", @@ -514,7 +514,7 @@ "Enable New Sign Ups": "새 회원가입 활성화", "Enable, disable, or customize the reasoning tags used by the model. \"Enabled\" uses default tags, \"Disabled\" turns off reasoning tags, and \"Custom\" lets you specify your own start and end tags.": "", "Enabled": "활성화됨", - "End Tag": "", + "End Tag": "종료 태그", "Endpoint URL": "엔드포인트 URL", "Enforce Temporary Chat": "임시 채팅 강제 적용", "Enhance": "향상", @@ -537,7 +537,7 @@ "Enter comma-separated \"token:bias_value\" pairs (example: 5432:100, 413:-100)": "쉼표로 구분된 \\\"토큰:편향_값\\\" 쌍 입력 (예: 5432:100, 413:-100)", "Enter Config in JSON format": "JSON 형식의 설정 입력", "Enter content for the pending user info overlay. Leave empty for default.": "대기 중인 사용자 정보 오버레이에 들어갈 내용을 입력하세요. 비워두면 기본값이 사용됩니다.", - "Enter coordinates (e.g. 51.505, -0.09)": "", + "Enter coordinates (e.g. 51.505, -0.09)": "좌표 입력 (예: 51.505, -0.09)", "Enter Datalab Marker API Base URL": "Datalab Marker API URL 입력", "Enter Datalab Marker API Key": "Datalab Marker API 키 입력", "Enter description": "설명 입력", @@ -560,8 +560,8 @@ "Enter Github Raw URL": "Github Raw URL 입력", "Enter Google PSE API Key": "Google PSE API 키 입력", "Enter Google PSE Engine Id": "Google PSE 엔진 ID 입력", - "Enter hex color (e.g. #FF0000)": "", - "Enter ID": "", + "Enter hex color (e.g. #FF0000)": "색상 hex 입력 (예: #FF0000)", + "Enter ID": "ID 입력", "Enter Image Size (e.g. 512x512)": "이미지 크기 입력(예: 512x512)", "Enter Jina API Key": "Jina API 키 입력", "Enter JSON config (e.g., {\"disable_links\": true})": "JSON 설정 입력 (예: {\"disable_links\": true})", @@ -578,7 +578,7 @@ "Enter name": "이름 입력", "Enter New Password": "새로운 비밀번호 입력", "Enter Number of Steps (e.g. 50)": "단계 수 입력(예: 50)", - "Enter Ollama Cloud API Key": "", + "Enter Ollama Cloud API Key": "Ollama 클라우드 API 키 입력", "Enter Perplexity API Key": "Perplexity API 키 입력", "Enter Playwright Timeout": "Playwright 시간 초과 입력", "Enter Playwright WebSocket URL": "Playwright WebSocket URL 입력", @@ -626,7 +626,7 @@ "Enter Your Email": "이메일 입력", "Enter Your Full Name": "전체 이름 입력", "Enter your gender": "성별 입력", - "Enter your message": "메세지 입력", + "Enter your message": "메시지 입력", "Enter your name": "이름 입력", "Enter Your Name": "이름 입력", "Enter your new password": "새로운 비밀번호를 입력해 주세요", @@ -696,7 +696,7 @@ "Failed to import models": "", "Failed to load chat preview": "채팅 미리보기 로드 실패", "Failed to load file content.": "파일 내용 로드 실패.", - "Failed to move chat": "", + "Failed to move chat": "채팅 이동 실패", "Failed to read clipboard contents": "클립보드 내용 가져오기를 실패하였습니다.", "Failed to save connections": "연결 저장 실패", "Failed to save conversation": "대화 저장 실패", @@ -866,11 +866,11 @@ "Insert": "삽입", "Insert Follow-Up Prompt to Input": "후속 질문을 메시지 입력란에 삽입(자동 전송 없이)", "Insert Prompt as Rich Text": "프롬프트를 서식 있는 텍스트로 삽입", - "Insert Suggestion Prompt to Input": "", + "Insert Suggestion Prompt to Input": "입력할 제안 프롬프트 삽입", "Install from Github URL": "Github URL에서 설치", "Instant Auto-Send After Voice Transcription": "음성 변환 후 즉시 자동 전송", "Integration": "통합", - "Integrations": "", + "Integrations": "통합", "Interface": "인터페이스", "Invalid file content": "잘못된 파일 내용", "Invalid file format.": "잘못된 파일 형식", @@ -896,7 +896,7 @@ "Keep Follow-Up Prompts in Chat": "채팅마다 후속 질문이 항상 보이도록 유지", "Keep in Sidebar": "사이드바에 유지", "Key": "키", - "Key is required": "", + "Key is required": "키가 필요합니다", "Keyboard shortcuts": "키보드 단축키", "Knowledge": "지식 기반", "Knowledge Access": "지식 기반 접근", @@ -960,7 +960,7 @@ "Manage OpenAI API Connections": "OpenAI API 연결 관리", "Manage Pipelines": "파이프라인 관리", "Manage Tool Servers": "도구 서버 관리", - "Manage your account information.": "", + "Manage your account information.": "계정 정보를 관리하세요.", "March": "3월", "Markdown": "마크다운", "Markdown (Header)": "", @@ -1228,30 +1228,30 @@ "pypdfium2": "", "Query Generation Prompt": "쿼리 생성 프롬프트", "Querying": "", - "Quick Actions": "", + "Quick Actions": "빠른 작업", "RAG Template": "RAG 템플릿", "Rating": "평가", "Re-rank models by topic similarity": "주제 유사성으로 모델을 재정렬하기", "Read": "읽기", "Read Aloud": "읽어주기", - "Read more →": "", + "Read more →": "더 읽기 →", "Reason": "근거", "Reasoning Effort": "추론 난이도", - "Reasoning Tags": "", + "Reasoning Tags": "추론 태그", "Record": "녹음", "Record voice": "음성 녹음", "Redirecting you to Open WebUI Community": "OpenWebUI 커뮤니티로 리디렉션 중", "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\")": "스스로를 \"사용자\" 라고 지칭하세요. (예: \"사용자는 영어를 배우고 있습니다\")", - "Reference Chats": "", + "Reference Chats": "채팅 참조", "Refused when it shouldn't have": "허용되지 않았지만 허용되어야 합니다.", "Regenerate": "재생성", - "Regenerate Menu": "", - "Register Again": "", - "Register Client": "", - "Registered": "", - "Registration failed": "", - "Registration successful": "", + "Regenerate Menu": "메뉴 재생성", + "Register Again": "재등록", + "Register Client": "클라이언트 등록", + "Registered": "등록됨", + "Registration failed": "등록 실패", + "Registration successful": "등록 성공", "Reindex": "재색인", "Reindex Knowledge Base Vectors": "전체 지식 베이스 재색인", "Release Notes": "릴리스 노트", @@ -1317,7 +1317,7 @@ "Search Chats": "채팅 검색", "Search Collection": "컬렉션 검색", "Search Filters": "필터 검색", - "search for archived chats": "아카이브된 채팅 검색", + "search for archived chats": "보관된 채팅 검색", "search for folders": "폴더 검색", "search for pinned chats": "고정된 채팅 검색", "search for shared chats": "공유된 채팅 검색", @@ -1335,7 +1335,7 @@ "SearchApi API Key": "SearchApi API 키", "SearchApi Engine": "SearchApi 엔진", "Searched {{count}} sites": "{{count}}개 사이트 검색됨", - "Searching": "", + "Searching": "검색 중", "Searching \"{{searchQuery}}\"": "\"{{searchQuery}}\" 검색 중", "Searching Knowledge for \"{{searchQuery}}\"": "\"{{searchQuery}}\"에 대한 지식 기반 검색 중", "Searching the web": "웹에서 검색 중...", @@ -1343,7 +1343,7 @@ "See readme.md for instructions": "설명은 readme.md를 참조하세요.", "See what's new": "새로운 기능 보기", "Seed": "시드", - "Select": "", + "Select": "선택", "Select a base model": "기본 모델 선택", "Select a base model (e.g. llama3, gpt-4o)": "기본 모델 선택 (예: llama3, gpt-4o)", "Select a conversation to preview": "대화를 선택하여 미리 보기", @@ -1357,18 +1357,18 @@ "Select a pipeline": "파이프라인 선택", "Select a pipeline url": "파이프라인 URL 선택", "Select a reranking model engine": "", - "Select a role": "", + "Select a role": "역할 선택", "Select a theme": "테마 선택", "Select a tool": "도구 선택", - "Select a voice": "", + "Select a voice": "음성 선택", "Select an auth method": "인증 방법 선택", - "Select an embedding model engine": "", - "Select an engine": "", + "Select an embedding model engine": "임베딩 모델 엔진 선택", + "Select an engine": "엔진 선택", "Select an Ollama instance": "Ollama 인스턴스 선택", "Select an output format": "출력 형식 선택", "Select dtype": "dtype 선택", "Select Engine": "엔진 선택", - "Select how to split message text for TTS requests": "", + "Select how to split message text for TTS requests": "TTS 요청에 대한 메시지 텍스트 분할 방법 선택", "Select Knowledge": "지식 기반 선택", "Select only one model to call": "음성 기능을 위해서는 모델을 하나만 선택해야 합니다.", "Selected model(s) do not support image inputs": "선택한 모델은 이미지 입력을 지원하지 않습니다.", @@ -1410,7 +1410,7 @@ "Share": "공유", "Share Chat": "채팅 공유", "Share to Open WebUI Community": "OpenWebUI 커뮤니티에 공유", - "Share your background and interests": "", + "Share your background and interests": "당신의 배경과 관심사를 공유하세요", "Sharing Permissions": "권한 공유", "Shortcuts with an asterisk (*) are situational and only active under specific conditions.": "별표(*) 단축키는 특정 조건에서만 활성화됩니다.", "Show": "보기", @@ -1434,7 +1434,7 @@ "sk-1234": "", "Skip Cache": "캐시 무시", "Skip the cache and re-run the inference. Defaults to False.": "캐시를 무시하고 추론을 다시 실행합니다. 기본값은 False입니다.", - "Something went wrong :/": "무언가 잘못 되었습니다", + "Something went wrong :/": "무언가 잘못 되었습니다 :/", "Sonar": "", "Sonar Deep Research": "", "Sonar Pro": "", @@ -1464,7 +1464,7 @@ "STT Model": "STT 모델", "STT Settings": "STT 설정", "Stylized PDF Export": "서식이 적용된 PDF 내보내기", - "Subtitle (e.g. about the Roman Empire)": "자막 (예: 로마 황제)", + "Subtitle (e.g. about the Roman Empire)": "자막 (예: 로마 제국에 대하여)", "Success": "성공", "Successfully imported {{userCount}} users.": "성공적으로 {{userCount}}명의 사용자를 가져왔습니다.", "Successfully updated.": "성공적으로 업데이트되었습니다.", @@ -1492,7 +1492,7 @@ "Tell us more:": "더 알려주세요:", "Temperature": "온도", "Temporary Chat": "임시 채팅", - "Temporary Chat by Default": "", + "Temporary Chat by Default": "임시 채팅을 기본값으로", "Text Splitter": "텍스트 나누기", "Text-to-Speech": "텍스트-음성 변환", "Text-to-Speech Engine": "텍스트-음성 변환 엔진", @@ -1515,7 +1515,7 @@ "The score should be a value between 0.0 (0%) and 1.0 (100%).": "점수는 0.0(0%)에서 1.0(100%) 사이의 값이어야 합니다.", "The stream delta chunk size for the model. Increasing the chunk size will make the model respond with larger pieces of text at once.": "", "The temperature of the model. Increasing the temperature will make the model answer more creatively.": "모델의 온도. 온도를 높이면 모델이 더 창의적으로 답변할 수 있습니다.", - "The Weight of BM25 Hybrid Search. 0 more semantic, 1 more lexical. Default 0.5": "", + "The Weight of BM25 Hybrid Search. 0 more semantic, 1 more lexical. Default 0.5": "BM25 하이브리드 검색의 가중치. 0에 가까울수록 의미(semantic) 기반, 1에 가까울수록 어휘(lexical) 기반. 기본값 0.5", "The width in pixels to compress images to. Leave empty for no compression.": "이미지를 압축할 픽셀 너비입니다. 압축하지 않으려면 비워 두세요.", "Theme": "테마", "Thinking...": "생각 중...", @@ -1651,7 +1651,7 @@ "Using Focused Retrieval": "집중 검색 사용", "Using the default arena model with all models. Click the plus button to add custom models.": "모든 모델은 기본 아레나 모델을 사용중입니다. 플러스 버튼을 눌러 커스텀 모델을 추가하세요.", "Valid time units:": "유효 시간 단위:", - "Validate certificate": "", + "Validate certificate": "인증서 검증", "Valves": "밸브", "Valves updated": "밸브 업데이트됨", "Valves updated successfully": "성공적으로 밸브가 업데이트되었습니다", @@ -1681,7 +1681,7 @@ "Web Search in Chat": "채팅에서 웹 검색", "Web Search Query Generation": "웹 검색 쿼리 생성", "Webhook URL": "웹훅 URL", - "Webpage URL": "", + "Webpage URL": "웹페이지 URL", "WebUI Settings": "WebUI 설정", "WebUI URL": "", "WebUI will make requests to \"{{url}}\"": "WebUI가 \"{{url}}\"로 요청을 보냅니다", From 03498bd2fd6bb413da4587e73ffc04930c8cc3fc Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Wed, 1 Oct 2025 19:31:47 -0500 Subject: [PATCH 035/127] refac --- src/lib/components/chat/Overview/Flow.svelte | 8 ++++--- .../components/icons/AlignHorizontal.svelte | 21 +++++++++++++++++++ src/lib/components/icons/AlignVertical.svelte | 21 +++++++++++++++++++ 3 files changed, 47 insertions(+), 3 deletions(-) create mode 100644 src/lib/components/icons/AlignHorizontal.svelte create mode 100644 src/lib/components/icons/AlignVertical.svelte diff --git a/src/lib/components/chat/Overview/Flow.svelte b/src/lib/components/chat/Overview/Flow.svelte index 4eeb497f63..d3963e22ae 100644 --- a/src/lib/components/chat/Overview/Flow.svelte +++ b/src/lib/components/chat/Overview/Flow.svelte @@ -13,6 +13,8 @@ } from '@xyflow/svelte'; import BarsArrowUp from '$lib/components/icons/BarsArrowUp.svelte'; import Bars3BottomLeft from '$lib/components/icons/Bars3BottomLeft.svelte'; + import AlignVertical from '$lib/components/icons/AlignVertical.svelte'; + import AlignHorizontal from '$lib/components/icons/AlignHorizontal.svelte'; export let nodes; export let nodeTypes; @@ -42,11 +44,11 @@ > setLayoutDirection('vertical')} title="Vertical Layout"> - + setLayoutDirection('horizontal')} title="Horizontal Layout"> - + - \ No newline at end of file + diff --git a/src/lib/components/icons/AlignHorizontal.svelte b/src/lib/components/icons/AlignHorizontal.svelte new file mode 100644 index 0000000000..2cd28d5578 --- /dev/null +++ b/src/lib/components/icons/AlignHorizontal.svelte @@ -0,0 +1,21 @@ + + + diff --git a/src/lib/components/icons/AlignVertical.svelte b/src/lib/components/icons/AlignVertical.svelte new file mode 100644 index 0000000000..92db5b83e8 --- /dev/null +++ b/src/lib/components/icons/AlignVertical.svelte @@ -0,0 +1,21 @@ + + + From dc3911be14f6c430a204c12a1f6ca73d03f5ac7e Mon Sep 17 00:00:00 2001 From: Cyp Date: Thu, 2 Oct 2025 09:36:37 +0900 Subject: [PATCH 036/127] Improving Korean Translation --- src/lib/i18n/locales/ko-KR/translation.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/lib/i18n/locales/ko-KR/translation.json b/src/lib/i18n/locales/ko-KR/translation.json index 43d431cafc..28d8971efd 100644 --- a/src/lib/i18n/locales/ko-KR/translation.json +++ b/src/lib/i18n/locales/ko-KR/translation.json @@ -75,7 +75,7 @@ "Advanced Params": "고급 매개변수", "AI": "", "All": "전체", - "All chats have been unarchived.": "모든 채팅이 보관되지 않았습니다.", + "All chats have been unarchived.": "모든 채팅이 보관 해제되었습니다.", "All Documents": "모든 문서", "All models deleted successfully": "성공적으로 모든 모델이 삭제되었습니다", "Allow Call": "음성 통화 허용", @@ -89,7 +89,7 @@ "Allow Chat System Prompt": "채팅 시스템 프롬프트 허용", "Allow Chat Valves": "채팅 밸브 허용", "Allow Continue Response": "계속 응답 허용", - "Allow Delete Messages": "", + "Allow Delete Messages": "메시지 삭제 허용", "Allow File Upload": "파일 업로드 허용", "Allow Multiple Models in Chat": "채팅에서 여러 모델 허용", "Allow non-local voices": "외부 음성 허용", @@ -111,12 +111,12 @@ "Always Play Notification Sound": "항상 알림 소리 재생", "Amazing": "놀라움", "an assistant": "어시스턴트", - "An error occurred while fetching the explanation": "", + "An error occurred while fetching the explanation": "설명을 가져오는 동안 오류가 발생했습니다.", "Analytics": "분석", "Analyzed": "분석됨", "Analyzing...": "분석 중...", "and": "그리고", - "and {{COUNT}} more": "그리고 {{COUNT}} 더", + "and {{COUNT}} more": "그리고 {{COUNT}}개 더", "and create a new shared link.": "새로운 공유 링크를 생성합니다.", "Android": "안드로이드", "API": "", From 6c8c3257fd8d4a462feb15214933caf1822eb513 Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Wed, 1 Oct 2025 19:57:19 -0500 Subject: [PATCH 037/127] feat: PWA share_target implementation Co-Authored-By: gjveld <19951982+gjveld@users.noreply.github.com> --- backend/open_webui/main.py | 32 +++++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/backend/open_webui/main.py b/backend/open_webui/main.py index 904399af14..5d269513f5 100644 --- a/backend/open_webui/main.py +++ b/backend/open_webui/main.py @@ -8,6 +8,7 @@ import shutil import sys import time import random +import re from uuid import uuid4 @@ -1170,12 +1171,32 @@ class RedirectMiddleware(BaseHTTPMiddleware): path = request.url.path query_params = dict(parse_qs(urlparse(str(request.url)).query)) + redirect_params = {} + # Check for the specific watch path and the presence of 'v' parameter if path.endswith("/watch") and "v" in query_params: # Extract the first 'v' parameter - video_id = query_params["v"][0] - encoded_video_id = urlencode({"youtube": video_id}) - redirect_url = f"/?{encoded_video_id}" + youtube_video_id = query_params["v"][0] + redirect_params["youtube"] = youtube_video_id + + if "shared" in query_params and len(query_params["shared"]) > 0: + # PWA share_target support + + text = query_params["shared"][0] + if text: + urls = re.match(r"https://\S+", text) + if urls: + from open_webui.retrieval.loaders.youtube import _parse_video_id + + if youtube_video_id := _parse_video_id(urls[0]): + redirect_params["youtube"] = youtube_video_id + else: + redirect_params["load-url"] = urls[0] + else: + redirect_params["q"] = text + + if redirect_params: + redirect_url = f"/?{urlencode(redirect_params)}" return RedirectResponse(url=redirect_url) # Proceed with the normal flow of other requests @@ -2004,6 +2025,11 @@ async def get_manifest_json(): "purpose": "maskable", }, ], + "share_target": { + "action": "/", + "method": "GET", + "params": {"text": "shared"}, + }, } From 557367cf48b75a533277d554df815661df5ddd50 Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Wed, 1 Oct 2025 20:02:27 -0500 Subject: [PATCH 038/127] refac: message input mobile detection behaviour --- src/lib/components/channel/MessageInput.svelte | 12 ++++++------ src/lib/components/chat/MessageInput.svelte | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/lib/components/channel/MessageInput.svelte b/src/lib/components/channel/MessageInput.svelte index 6954066fd9..02323a5f20 100644 --- a/src/lib/components/channel/MessageInput.svelte +++ b/src/lib/components/channel/MessageInput.svelte @@ -876,12 +876,12 @@ richText={$settings?.richTextInput ?? true} showFormattingToolbar={$settings?.showFormattingToolbar ?? false} shiftEnter={!($settings?.ctrlEnterToSend ?? false) && - (!$mobile || - !( - 'ontouchstart' in window || - navigator.maxTouchPoints > 0 || - navigator.msMaxTouchPoints > 0 - ))} + !$mobile && + !( + 'ontouchstart' in window || + navigator.maxTouchPoints > 0 || + navigator.msMaxTouchPoints > 0 + )} largeTextAsFile={$settings?.largeTextAsFile ?? false} floatingMenuPlacement={'top-start'} {suggestions} diff --git a/src/lib/components/chat/MessageInput.svelte b/src/lib/components/chat/MessageInput.svelte index 4bcdbc30ca..31aa1ab0f9 100644 --- a/src/lib/components/chat/MessageInput.svelte +++ b/src/lib/components/chat/MessageInput.svelte @@ -1201,12 +1201,12 @@ floatingMenuPlacement={'top-start'} insertPromptAsRichText={$settings?.insertPromptAsRichText ?? false} shiftEnter={!($settings?.ctrlEnterToSend ?? false) && - (!$mobile || - !( - 'ontouchstart' in window || - navigator.maxTouchPoints > 0 || - navigator.msMaxTouchPoints > 0 - ))} + !$mobile && + !( + 'ontouchstart' in window || + navigator.maxTouchPoints > 0 || + navigator.msMaxTouchPoints > 0 + )} placeholder={placeholder ? placeholder : $i18n.t('Send a Message')} largeTextAsFile={($settings?.largeTextAsFile ?? false) && !shiftKey} autocomplete={$config?.features?.enable_autocomplete_generation && From e2ca7b8632dc38903147da71948f10ddc520666f Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Wed, 1 Oct 2025 20:50:04 -0500 Subject: [PATCH 039/127] feat: model_ids per folder --- src/lib/components/chat/Chat.svelte | 69 +++++++++++++++---- src/lib/components/chat/Placeholder.svelte | 9 +-- .../chat/Placeholder/FolderTitle.svelte | 22 ++++-- .../components/common/RichTextInput.svelte | 6 -- .../layout/Sidebar/RecursiveFolder.svelte | 10 +-- 5 files changed, 79 insertions(+), 37 deletions(-) diff --git a/src/lib/components/chat/Chat.svelte b/src/lib/components/chat/Chat.svelte index 8b45ee6b22..2be7b0b1a9 100644 --- a/src/lib/components/chat/Chat.svelte +++ b/src/lib/components/chat/Chat.svelte @@ -91,6 +91,7 @@ import Sidebar from '../icons/Sidebar.svelte'; import { getFunctions } from '$lib/apis/functions'; import Image from '../common/Image.svelte'; + import { updateFolderById } from '$lib/apis/folders'; export let chatIdProp = ''; @@ -499,7 +500,24 @@ } }; + const savedModelIds = async () => { + if ($selectedFolder && $selectedFolder?.data?.model_ids !== selectedModels) { + const res = await updateFolderById(localStorage.token, $selectedFolder.id, { + data: { + model_ids: selectedModels + } + }); + } + }; + + $: if (selectedModels !== null) { + savedModelIds(); + } + let pageSubscribe = null; + let showControlsSubscribe = null; + let selectedFolderSubscribe = null; + onMount(async () => { loading = true; console.log('mounted'); @@ -548,7 +566,7 @@ } catch (e) {} } - showControls.subscribe(async (value) => { + showControlsSubscribe = showControls.subscribe(async (value) => { if (controlPane && !$mobile) { try { if (value) { @@ -569,17 +587,32 @@ } }); + selectedFolderSubscribe = selectedFolder.subscribe(async (folder) => { + if ( + folder?.data?.model_ids && + JSON.stringify(selectedModels) !== JSON.stringify(folder.data.model_ids) + ) { + selectedModels = folder.data.model_ids; + + console.log('Set selectedModels from folder data:', selectedModels); + } + }); + const chatInput = document.getElementById('chat-input'); chatInput?.focus(); - - chats.subscribe(() => {}); }); onDestroy(() => { - pageSubscribe(); - chatIdUnsubscriber?.(); - window.removeEventListener('message', onMessageHandler); - $socket?.off('chat-events', chatEventHandler); + try { + pageSubscribe(); + showControlsSubscribe(); + selectedFolderSubscribe(); + chatIdUnsubscriber?.(); + window.removeEventListener('message', onMessageHandler); + $socket?.off('chat-events', chatEventHandler); + } catch (e) { + console.error(e); + } }); // File upload functions @@ -780,6 +813,7 @@ ////////////////////////// const initNewChat = async () => { + console.log('initNewChat'); if ($user?.role !== 'admin' && $user?.permissions?.chat?.temporary_enforced) { await temporaryChatEnabled.set(true); } @@ -830,17 +864,22 @@ $models.map((m) => m.id).includes(modelId) ); } else { - if (sessionStorage.selectedModels) { - selectedModels = JSON.parse(sessionStorage.selectedModels); - sessionStorage.removeItem('selectedModels'); + if ($selectedFolder?.data?.model_ids) { + selectedModels = $selectedFolder?.data?.model_ids; } else { - if ($settings?.models) { - selectedModels = $settings?.models; - } else if ($config?.default_models) { - console.log($config?.default_models.split(',') ?? ''); - selectedModels = $config?.default_models.split(','); + if (sessionStorage.selectedModels) { + selectedModels = JSON.parse(sessionStorage.selectedModels); + sessionStorage.removeItem('selectedModels'); + } else { + if ($settings?.models) { + selectedModels = $settings?.models; + } else if ($config?.default_models) { + console.log($config?.default_models.split(',') ?? ''); + selectedModels = $config?.default_models.split(','); + } } } + selectedModels = selectedModels.filter((modelId) => availableModels.includes(modelId)); } diff --git a/src/lib/components/chat/Placeholder.svelte b/src/lib/components/chat/Placeholder.svelte index bf4986c590..509ef9f2ba 100644 --- a/src/lib/components/chat/Placeholder.svelte +++ b/src/lib/components/chat/Placeholder.svelte @@ -7,6 +7,9 @@ const dispatch = createEventDispatcher(); + import { getChatList } from '$lib/apis/chats'; + import { updateFolderById } from '$lib/apis/folders'; + import { config, user, @@ -25,7 +28,6 @@ import MessageInput from './MessageInput.svelte'; import FolderPlaceholder from './Placeholder/FolderPlaceholder.svelte'; import FolderTitle from './Placeholder/FolderTitle.svelte'; - import { getChatList } from '$lib/apis/chats'; const i18n = getContext('i18n'); @@ -58,7 +60,6 @@ export let toolServers = []; let models = []; - let selectedModelIdx = 0; $: if (selectedModels.length > 0) { @@ -66,8 +67,6 @@ } $: models = selectedModels.map((id) => $_models.find((m) => m.id === id)); - - onMount(() => {});
@@ -91,8 +90,6 @@ { - selectedFolder.set(folder); - await chats.set(await getChatList(localStorage.token, $currentChatPage)); currentChatPage.set(1); }} diff --git a/src/lib/components/chat/Placeholder/FolderTitle.svelte b/src/lib/components/chat/Placeholder/FolderTitle.svelte index 16a1c42836..533b627d0d 100644 --- a/src/lib/components/chat/Placeholder/FolderTitle.svelte +++ b/src/lib/components/chat/Placeholder/FolderTitle.svelte @@ -11,7 +11,7 @@ import { selectedFolder } from '$lib/stores'; - import { deleteFolderById, updateFolderById } from '$lib/apis/folders'; + import { deleteFolderById, getFolderById, updateFolderById } from '$lib/apis/folders'; import { getChatsByFolderId } from '$lib/apis/chats'; import FolderModal from '$lib/components/layout/Sidebar/Folders/FolderModal.svelte'; @@ -61,8 +61,14 @@ } toast.success($i18n.t('Folder updated successfully')); - selectedFolder.set(folder); - onUpdate(folder); + + const _folder = await getFolderById(localStorage.token, folder.id).catch((error) => { + toast.error(`${error}`); + return null; + }); + + await selectedFolder.set(_folder); + onUpdate(_folder); } }; @@ -80,8 +86,14 @@ folder.meta = { ...folder.meta, icon: iconName }; toast.success($i18n.t('Folder updated successfully')); - selectedFolder.set(folder); - onUpdate(folder); + + const _folder = await getFolderById(localStorage.token, folder.id).catch((error) => { + toast.error(`${error}`); + return null; + }); + + await selectedFolder.set(_folder); + onUpdate(_folder); } }; diff --git a/src/lib/components/common/RichTextInput.svelte b/src/lib/components/common/RichTextInput.svelte index 0586b4a4f1..6e770b7627 100644 --- a/src/lib/components/common/RichTextInput.svelte +++ b/src/lib/components/common/RichTextInput.svelte @@ -672,16 +672,10 @@ } } - console.log('content', content); - if (collaboration && documentId && socket && user) { const { SocketIOCollaborationProvider } = await import('./RichTextInput/Collaboration'); provider = new SocketIOCollaborationProvider(documentId, socket, user, content); } - - console.log(bubbleMenuElement, floatingMenuElement); - console.log(suggestions); - editor = new Editor({ element: element, extensions: [ diff --git a/src/lib/components/layout/Sidebar/RecursiveFolder.svelte b/src/lib/components/layout/Sidebar/RecursiveFolder.svelte index 67fbf9e455..5187bba242 100644 --- a/src/lib/components/layout/Sidebar/RecursiveFolder.svelte +++ b/src/lib/components/layout/Sidebar/RecursiveFolder.svelte @@ -335,7 +335,7 @@ }); if (folder) { - selectedFolder.set(folder); + await selectedFolder.set(folder); } } dispatch('update'); @@ -376,7 +376,7 @@ }); if (folder) { - selectedFolder.set(folder); + await selectedFolder.set(folder); } } } else { @@ -488,17 +488,17 @@ } clickTimer = setTimeout(async () => { - await goto('/'); - const folder = await getFolderById(localStorage.token, folderId).catch((error) => { toast.error(`${error}`); return null; }); if (folder) { - selectedFolder.set(folder); + await selectedFolder.set(folder); } + await goto('/'); + if ($mobile) { showSidebar.set(!$showSidebar); } From 6afbcac9d01f145f69c954646979dcd52d94cfd1 Mon Sep 17 00:00:00 2001 From: joaoback <156559121+joaoback@users.noreply.github.com> Date: Wed, 1 Oct 2025 23:05:56 -0300 Subject: [PATCH 040/127] Update translation.json (pt-BR) inclusion of new translations of items that have been added --- src/lib/i18n/locales/pt-BR/translation.json | 36 ++++++++++----------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/src/lib/i18n/locales/pt-BR/translation.json b/src/lib/i18n/locales/pt-BR/translation.json index 07e83e30cc..064a835fd6 100644 --- a/src/lib/i18n/locales/pt-BR/translation.json +++ b/src/lib/i18n/locales/pt-BR/translation.json @@ -75,7 +75,7 @@ "Advanced Params": "Parâmetros Avançados", "AI": "IA", "All": "Tudo", - "All chats have been unarchived.": "", + "All chats have been unarchived.": "Todos os chats foram desarquivados.", "All Documents": "Todos os Documentos", "All models deleted successfully": "Todos os modelos foram excluídos com sucesso", "Allow Call": "Permitir chamada", @@ -431,12 +431,12 @@ "Discover, download, and explore custom tools": "Descubra, baixe e explore ferramentas personalizadas", "Discover, download, and explore model presets": "Descubra, baixe e explore predefinições de modelos", "Display": "Exibir", - "Display chat title in tab": "", + "Display chat title in tab": "Exibir título do chat na aba", "Display Emoji in Call": "Exibir Emoji na Chamada", "Display Multi-model Responses in Tabs": "Exibir respostas de vários modelos em guias", "Display the username instead of You in the Chat": "Exibir o nome de usuário em vez de Você no Chat", "Displays citations in the response": "Exibir citações na resposta", - "Displays status updates (e.g., web search progress) in the response": "", + "Displays status updates (e.g., web search progress) in the response": "Exibe atualizações de status (por exemplo, progresso da pesquisa na web) na resposta", "Dive into knowledge": "Explorar base de conhecimento", "dlparse_v1": "", "dlparse_v2": "", @@ -512,7 +512,7 @@ "Enable Message Rating": "Ativar Avaliação de Mensagens", "Enable Mirostat sampling for controlling perplexity.": "", "Enable New Sign Ups": "Ativar Novos Cadastros", - "Enable, disable, or customize the reasoning tags used by the model. \"Enabled\" uses default tags, \"Disabled\" turns off reasoning tags, and \"Custom\" lets you specify your own start and end tags.": "", + "Enable, disable, or customize the reasoning tags used by the model. \"Enabled\" uses default tags, \"Disabled\" turns off reasoning tags, and \"Custom\" lets you specify your own start and end tags.": "Habilite, desabilite ou personalize as tags de raciocínio usadas pelo modelo. \"Enabled\" usa tags padrão, \"Disabled\" desativa as tags de raciocínio e \"Custom\" permite que você especifique suas próprias tags de início e fim.", "Enabled": "Ativado", "End Tag": "Tag final", "Endpoint URL": "", @@ -578,7 +578,7 @@ "Enter name": "Digite o nome", "Enter New Password": "Digite uma nova senha", "Enter Number of Steps (e.g. 50)": "Digite o Número de Passos (por exemplo, 50)", - "Enter Ollama Cloud API Key": "", + "Enter Ollama Cloud API Key": "Insira a chave da API do Ollama Cloud", "Enter Perplexity API Key": "Insira a chave da API Perplexity", "Enter Playwright Timeout": "", "Enter Playwright WebSocket URL": "", @@ -693,7 +693,7 @@ "Failed to extract content from the file.": "Falha ao extrair conteúdo do arquivo.", "Failed to fetch models": "Falha ao buscar modelos", "Failed to generate title": "Falha ao gerar título", - "Failed to import models": "", + "Failed to import models": "Falha ao importar modelos", "Failed to load chat preview": "Falha ao carregar a pré-visualização do chat", "Failed to load file content.": "Falha ao carregar o conteúdo do arquivo.", "Failed to move chat": "Falha ao mover o chat", @@ -819,7 +819,7 @@ "Hybrid Search": "Pesquisa Híbrida", "I acknowledge that I have read and I understand the implications of my action. I am aware of the risks associated with executing arbitrary code and I have verified the trustworthiness of the source.": "Eu reconheço que li e entendi as implicações da minha ação. Estou ciente dos riscos associados à execução de código arbitrário e verifiquei a confiabilidade da fonte.", "ID": "", - "ID cannot contain \":\" or \"|\" characters": "", + "ID cannot contain \":\" or \"|\" characters": "O ID não pode conter caracteres \":\" ou \"|\"", "iframe Sandbox Allow Forms": "", "iframe Sandbox Allow Same Origin": "", "Ignite curiosity": "Desperte a curiosidade", @@ -847,7 +847,7 @@ "Import Presets": "Importar Presets", "Import Prompt Suggestions": "Importar Sugestões de Prompt", "Import Prompts": "Importar Prompts", - "Import successful": "", + "Import successful": "Importação bem-sucedida", "Import Tools": "Importar Ferramentas", "Important Update": "Atualização importante", "In order to force OCR, performing OCR must be enabled.": "Para forçar o OCR, a execução do OCR deve estar habilitada.", @@ -885,7 +885,7 @@ "join our Discord for help.": "junte-se ao nosso Discord para ajudar.", "JSON": "JSON", "JSON Preview": "Pré-visualização JSON", - "JSON Spec": "", + "JSON Spec": "Especificação JSON", "July": "Julho", "June": "Junho", "Jupyter Auth": "", @@ -1079,7 +1079,7 @@ "Note deleted successfully": "Nota excluída com sucesso", "Note: If you set a minimum score, the search will only return documents with a score greater than or equal to the minimum score.": "Nota: Se você definir uma pontuação mínima, a pesquisa retornará apenas documentos com pontuação igual ou superior à pontuação mínima.", "Notes": "Notas", - "Notes Public Sharing": "", + "Notes Public Sharing": "Compartilhamento Público das Notas", "Notification Sound": "Som de notificação", "Notification Webhook": "Webhook de notificação", "Notifications": "Notificações", @@ -1187,7 +1187,7 @@ "Please enter a message or attach a file.": "Por favor, insira uma mensagem ou anexe um arquivo.", "Please enter a prompt": "Por favor, digite um prompt", "Please enter a valid ID": "Por favor, insira um ID válido", - "Please enter a valid JSON spec": "", + "Please enter a valid JSON spec": "Por favor, insira uma especificação JSON válida", "Please enter a valid path": "Por favor, insira um caminho válido", "Please enter a valid URL": "Por favor, insira uma URL válido", "Please enter a valid URL.": "Por favor, insira uma URL válida", @@ -1197,7 +1197,7 @@ "Please select a model first.": "Selecione um modelo primeiro.", "Please select a model.": "Selecione um modelo.", "Please select a reason": "Por favor, seleccione uma razão", - "Please select a valid JSON file": "", + "Please select a valid JSON file": "Selecione um arquivo JSON válido", "Please wait until all files are uploaded.": "Aguarde até que todos os arquivos sejam enviados.", "Port": "Porta", "Positive attitude": "Atitude positiva", @@ -1268,10 +1268,10 @@ "Remove this tag from list": "Remover esta tag da lista", "Rename": "Renomear", "Reorder Models": "Reordenar modelos", - "Reply": "", + "Reply": "Responder", "Reply in Thread": "Responder no tópico", "Reply to thread...": "Responder ao tópico...", - "Replying to {{NAME}}": "", + "Replying to {{NAME}}": "Respondendo para {{NAME}}", "required": "obrigatório", "Reranking Engine": "Motor de Reclassificação", "Reranking Model": "Modelo de Reclassificação", @@ -1517,7 +1517,7 @@ "The score should be a value between 0.0 (0%) and 1.0 (100%).": "A pontuação deve ser um valor entre 0.0 (0%) e 1.0 (100%).", "The stream delta chunk size for the model. Increasing the chunk size will make the model respond with larger pieces of text at once.": "O tamanho do bloco delta do fluxo para o modelo. Aumentar o tamanho do bloco fará com que o modelo responda com trechos maiores de texto de uma só vez.", "The temperature of the model. Increasing the temperature will make the model answer more creatively.": "A temperatura do modelo. Aumentar a temperatura fará com que o modelo responda de forma mais criativa.", - "The Weight of BM25 Hybrid Search. 0 more semantic, 1 more lexical. Default 0.5": "", + "The Weight of BM25 Hybrid Search. 0 more semantic, 1 more lexical. Default 0.5": "O Peso da Busca Híbrida BM25. 0 a mais semântico, 1 a mais lexical. Padrão 0,5", "The width in pixels to compress images to. Leave empty for no compression.": "A largura em pixels para compactar as imagens. Deixe em branco para não compactar.", "Theme": "Tema", "Thinking...": "Pensando...", @@ -1526,7 +1526,7 @@ "This chat won't appear in history and your messages will not be saved.": "Este chat não aparecerá no histórico e suas mensagens não serão salvas.", "This ensures that your valuable conversations are securely saved to your backend database. Thank you!": "Isso garante que suas conversas valiosas sejam salvas com segurança no banco de dados do backend. Obrigado!", "This feature is experimental and may be modified or discontinued without notice.": "Este recurso é experimental e pode ser modificado ou descontinuado sem aviso prévio.", - "This is a default user permission and will remain enabled.": "", + "This is a default user permission and will remain enabled.": "Esta é uma permissão de usuário padrão e permanecerá ativada.", "This is an experimental feature, it may not function as expected and is subject to change at any time.": "Esta é uma funcionalidade experimental, pode não funcionar como esperado e está sujeita a alterações a qualquer momento.", "This model is not publicly available. Please select another model.": "Este modelo não está disponível publicamente. Selecione outro modelo.", "This option controls how long the model will stay loaded into memory following the request (default: 5m)": "Esta opção controla por quanto tempo o modelo permanecerá carregado na memória após a solicitação (padrão: 5m)", @@ -1605,7 +1605,7 @@ "Unarchive Chat": "Desarquivar Chat", "Underline": "Sublinhado", "Unknown": "Desconhecido", - "Unknown User": "", + "Unknown User": "Usuário desconhecido", "Unloads {{FROM_NOW}}": "Descarrega {{FROM_NOW}}", "Unlock mysteries": "Desvendar mistérios", "Unpin": "Desfixar", @@ -1683,7 +1683,7 @@ "Web Search in Chat": "Pesquisa na Web no Chat", "Web Search Query Generation": "Geração de consulta de pesquisa na Web", "Webhook URL": "URL do Webhook", - "Webpage URL": "", + "Webpage URL": "URL da página da web", "WebUI Settings": "Configurações da WebUI", "WebUI URL": "", "WebUI will make requests to \"{{url}}\"": "A WebUI fará requisições para \"{{url}}\"", From 97faeccebf064f8d29e4dee00d89f4eec5be7ca7 Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Wed, 1 Oct 2025 21:56:32 -0500 Subject: [PATCH 041/127] refac --- src/lib/components/chat/Chat.svelte | 9 +- src/lib/components/chat/MessageInput.svelte | 2 - .../chat/Messages/ResponseMessage.svelte | 2 - src/lib/components/common/CodeEditor.svelte | 1 - src/lib/components/layout/Sidebar.svelte | 99 +++++++++---------- .../layout/Sidebar/RecursiveFolder.svelte | 9 +- 6 files changed, 60 insertions(+), 62 deletions(-) diff --git a/src/lib/components/chat/Chat.svelte b/src/lib/components/chat/Chat.svelte index 2be7b0b1a9..819da85ba1 100644 --- a/src/lib/components/chat/Chat.svelte +++ b/src/lib/components/chat/Chat.svelte @@ -220,10 +220,15 @@ } const saveSessionSelectedModels = () => { - if (selectedModels.length === 0 || (selectedModels.length === 1 && selectedModels[0] === '')) { + const selectedModelsString = JSON.stringify(selectedModels); + if ( + selectedModels.length === 0 || + (selectedModels.length === 1 && selectedModels[0] === '') || + sessionStorage.selectedModels === selectedModelsString + ) { return; } - sessionStorage.selectedModels = JSON.stringify(selectedModels); + sessionStorage.selectedModels = selectedModelsString; console.log('saveSessionSelectedModels', selectedModels, sessionStorage.selectedModels); }; diff --git a/src/lib/components/chat/MessageInput.svelte b/src/lib/components/chat/MessageInput.svelte index 31aa1ab0f9..bedcd13985 100644 --- a/src/lib/components/chat/MessageInput.svelte +++ b/src/lib/components/chat/MessageInput.svelte @@ -885,8 +885,6 @@ }) } ]; - - console.log(suggestions); loaded = true; window.setTimeout(() => { diff --git a/src/lib/components/chat/Messages/ResponseMessage.svelte b/src/lib/components/chat/Messages/ResponseMessage.svelte index f807bd0fab..2a721e1428 100644 --- a/src/lib/components/chat/Messages/ResponseMessage.svelte +++ b/src/lib/components/chat/Messages/ResponseMessage.svelte @@ -576,8 +576,6 @@ await tick(); if (buttonsContainerElement) { - console.log(buttonsContainerElement); - buttonsContainerElement.addEventListener('wheel', function (event) { if (buttonsContainerElement.scrollWidth <= buttonsContainerElement.clientWidth) { // If the container is not scrollable, horizontal scroll diff --git a/src/lib/components/common/CodeEditor.svelte b/src/lib/components/common/CodeEditor.svelte index 44826fda00..50c2a7cd18 100644 --- a/src/lib/components/common/CodeEditor.svelte +++ b/src/lib/components/common/CodeEditor.svelte @@ -250,7 +250,6 @@ print("${endTag}") }; onMount(() => { - console.log(value); if (value === '') { value = boilerplate; } diff --git a/src/lib/components/layout/Sidebar.svelte b/src/lib/components/layout/Sidebar.svelte index 3e9bb38f0a..0294bd8091 100644 --- a/src/lib/components/layout/Sidebar.svelte +++ b/src/lib/components/layout/Sidebar.svelte @@ -125,13 +125,6 @@ }); } } - - await tick(); - for (const folderId in folders) { - if (folders[folderId] && folders[folderId].is_expanded) { - folderRegistry[folderId]?.setFolderItems(); - } - } }; const createFolder = async ({ name, data }) => { @@ -185,14 +178,15 @@ const initChatList = async () => { // Reset pagination variables - tags.set(await getAllTags(localStorage.token)); - pinnedChats.set(await getPinnedChatList(localStorage.token)); - initFolders(); - + console.log('initChatList'); currentChatPage.set(1); allChatsLoaded = false; await chats.set(await getChatList(localStorage.token, $currentChatPage)); + const _chats = await getChatList(localStorage.token, $currentChatPage); + await chats.set(_chats); + })() + ]); // Enable pagination scrollPaginationEnabled.set(true); @@ -342,57 +336,52 @@ selectedChatId = null; }; + let unsubscribers = []; onMount(async () => { showPinnedChat = localStorage?.showPinnedChat ? localStorage.showPinnedChat === 'true' : true; + await showSidebar.set(!$mobile ? localStorage.sidebar === 'true' : false); - mobile.subscribe((value) => { - if ($showSidebar && value) { - showSidebar.set(false); - } - - if ($showSidebar && !value) { - const navElement = document.getElementsByTagName('nav')[0]; - if (navElement) { - navElement.style['-webkit-app-region'] = 'drag'; + unsubscribers = [ + mobile.subscribe((value) => { + if ($showSidebar && value) { + showSidebar.set(false); } - } - if (!$showSidebar && !value) { - showSidebar.set(true); - } - }); - - showSidebar.set(!$mobile ? localStorage.sidebar === 'true' : false); - showSidebar.subscribe(async (value) => { - localStorage.sidebar = value; - - // nav element is not available on the first render - const navElement = document.getElementsByTagName('nav')[0]; - - if (navElement) { - if ($mobile) { - if (!value) { + if ($showSidebar && !value) { + const navElement = document.getElementsByTagName('nav')[0]; + if (navElement) { navElement.style['-webkit-app-region'] = 'drag'; - } else { - navElement.style['-webkit-app-region'] = 'no-drag'; } - } else { - navElement.style['-webkit-app-region'] = 'drag'; } - } - if (!value) { - await initChannels(); - await initChatList(); - } - }); + if (!$showSidebar && !value) { + showSidebar.set(true); + } + }), + showSidebar.subscribe(async (value) => { + localStorage.sidebar = value; - chats.subscribe((value) => { - initFolders(); - }); + // nav element is not available on the first render + const navElement = document.getElementsByTagName('nav')[0]; - await initChannels(); - await initChatList(); + if (navElement) { + if ($mobile) { + if (!value) { + navElement.style['-webkit-app-region'] = 'drag'; + } else { + navElement.style['-webkit-app-region'] = 'no-drag'; + } + } else { + navElement.style['-webkit-app-region'] = 'drag'; + } + } + + if (value) { + await initChannels(); + await initChatList(); + } + }) + ]; window.addEventListener('keydown', onKeyDown); window.addEventListener('keyup', onKeyUp); @@ -411,6 +400,14 @@ }); onDestroy(() => { + if (unsubscribers && unsubscribers.length > 0) { + unsubscribers.forEach((unsubscriber) => { + if (unsubscriber) { + unsubscriber(); + } + }); + } + window.removeEventListener('keydown', onKeyDown); window.removeEventListener('keyup', onKeyUp); diff --git a/src/lib/components/layout/Sidebar/RecursiveFolder.svelte b/src/lib/components/layout/Sidebar/RecursiveFolder.svelte index 5187bba242..a748352d2f 100644 --- a/src/lib/components/layout/Sidebar/RecursiveFolder.svelte +++ b/src/lib/components/layout/Sidebar/RecursiveFolder.svelte @@ -246,11 +246,12 @@ }; onMount(async () => { - folderRegistry[folderId] = { - setFolderItems: () => setFolderItems() - }; - open = folders[folderId].is_expanded; + folderRegistry[folderId] = { + setFolderItems: () => { + setFolderItems(); + } + }; if (folderElement) { folderElement.addEventListener('dragover', onDragOver); folderElement.addEventListener('drop', onDrop); From d87a2315ce5fa151a3f91fbfec0fa3c245e40cfd Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Wed, 1 Oct 2025 21:56:37 -0500 Subject: [PATCH 042/127] refac --- src/lib/components/layout/Sidebar.svelte | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/lib/components/layout/Sidebar.svelte b/src/lib/components/layout/Sidebar.svelte index 0294bd8091..3cc9706ea3 100644 --- a/src/lib/components/layout/Sidebar.svelte +++ b/src/lib/components/layout/Sidebar.svelte @@ -182,7 +182,20 @@ currentChatPage.set(1); allChatsLoaded = false; - await chats.set(await getChatList(localStorage.token, $currentChatPage)); + initFolders(); + await Promise.all([ + await (async () => { + console.log('Init tags'); + const _tags = await getAllTags(localStorage.token); + tags.set(_tags); + })(), + await (async () => { + console.log('Init pinned chats'); + const _pinnedChats = await getPinnedChatList(localStorage.token); + pinnedChats.set(_pinnedChats); + })(), + await (async () => { + console.log('Init chat list'); const _chats = await getChatList(localStorage.token, $currentChatPage); await chats.set(_chats); })() From 9677871ce7a12ef75af50b9ceaceb4678d6c844d Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Wed, 1 Oct 2025 21:57:34 -0500 Subject: [PATCH 043/127] refac --- src/lib/components/chat/Chat.svelte | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/lib/components/chat/Chat.svelte b/src/lib/components/chat/Chat.svelte index 819da85ba1..c7c9b4ebda 100644 --- a/src/lib/components/chat/Chat.svelte +++ b/src/lib/components/chat/Chat.svelte @@ -506,7 +506,10 @@ }; const savedModelIds = async () => { - if ($selectedFolder && $selectedFolder?.data?.model_ids !== selectedModels) { + if ( + $selectedFolder && + JSON.stringify($selectedFolder?.data?.model_ids) !== JSON.stringify(selectedModels) + ) { const res = await updateFolderById(localStorage.token, $selectedFolder.id, { data: { model_ids: selectedModels From af34e414e10dd62d03923c74f66163410cec6809 Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Wed, 1 Oct 2025 22:05:42 -0500 Subject: [PATCH 044/127] refac --- src/lib/components/chat/Chat.svelte | 1 + src/lib/components/chat/ModelSelector.svelte | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/lib/components/chat/Chat.svelte b/src/lib/components/chat/Chat.svelte index c7c9b4ebda..05f787bcec 100644 --- a/src/lib/components/chat/Chat.svelte +++ b/src/lib/components/chat/Chat.svelte @@ -508,6 +508,7 @@ const savedModelIds = async () => { if ( $selectedFolder && + selectedModels.filter((modelId) => modelId !== '').length > 0 && JSON.stringify($selectedFolder?.data?.model_ids) !== JSON.stringify(selectedModels) ) { const res = await updateFolderById(localStorage.token, $selectedFolder.id, { diff --git a/src/lib/components/chat/ModelSelector.svelte b/src/lib/components/chat/ModelSelector.svelte index 1fcb817af5..588352e725 100644 --- a/src/lib/components/chat/ModelSelector.svelte +++ b/src/lib/components/chat/ModelSelector.svelte @@ -39,9 +39,13 @@ }; $: if (selectedModels.length > 0 && $models.length > 0) { - selectedModels = selectedModels.map((model) => + const _selectedModels = selectedModels.map((model) => $models.map((m) => m.id).includes(model) ? model : '' ); + + if (JSON.stringify(_selectedModels) !== JSON.stringify(selectedModels)) { + selectedModels = _selectedModels; + } } From 3a601e0fc39aef341e4051ce6ae1da83361cd050 Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Wed, 1 Oct 2025 22:49:25 -0500 Subject: [PATCH 045/127] refac/fix: temp chat --- backend/open_webui/main.py | 32 +-- backend/open_webui/socket/main.py | 14 +- backend/open_webui/utils/middleware.py | 205 +++++++++++-------- src/lib/components/chat/Chat.svelte | 4 +- src/lib/components/chat/Navbar.svelte | 2 +- src/lib/components/layout/Navbar/Menu.svelte | 2 +- 6 files changed, 145 insertions(+), 114 deletions(-) diff --git a/backend/open_webui/main.py b/backend/open_webui/main.py index 5d269513f5..5dad3d7904 100644 --- a/backend/open_webui/main.py +++ b/backend/open_webui/main.py @@ -1495,7 +1495,7 @@ async def chat_completion( } if metadata.get("chat_id") and (user and user.role != "admin"): - if metadata["chat_id"] != "local": + if not metadata["chat_id"].startswith("local:"): chat = Chats.get_chat_by_id_and_user_id(metadata["chat_id"], user.id) if chat is None: raise HTTPException( @@ -1522,13 +1522,14 @@ async def chat_completion( response = await chat_completion_handler(request, form_data, user) if metadata.get("chat_id") and metadata.get("message_id"): try: - Chats.upsert_message_to_chat_by_id_and_message_id( - metadata["chat_id"], - metadata["message_id"], - { - "model": model_id, - }, - ) + if not metadata["chat_id"].startswith("local:"): + Chats.upsert_message_to_chat_by_id_and_message_id( + metadata["chat_id"], + metadata["message_id"], + { + "model": model_id, + }, + ) except: pass @@ -1549,13 +1550,14 @@ async def chat_completion( if metadata.get("chat_id") and metadata.get("message_id"): # Update the chat message with the error try: - Chats.upsert_message_to_chat_by_id_and_message_id( - metadata["chat_id"], - metadata["message_id"], - { - "error": {"content": str(e)}, - }, - ) + if not metadata["chat_id"].startswith("local:"): + Chats.upsert_message_to_chat_by_id_and_message_id( + metadata["chat_id"], + metadata["message_id"], + { + "error": {"content": str(e)}, + }, + ) event_emitter = get_event_emitter(metadata) await event_emitter( diff --git a/backend/open_webui/socket/main.py b/backend/open_webui/socket/main.py index e481571df4..657533c714 100644 --- a/backend/open_webui/socket/main.py +++ b/backend/open_webui/socket/main.py @@ -653,12 +653,15 @@ def get_event_emitter(request_info, update_db=True): ) ) + chat_id = request_info.get("chat_id", None) + message_id = request_info.get("message_id", None) + emit_tasks = [ sio.emit( "chat-events", { - "chat_id": request_info.get("chat_id", None), - "message_id": request_info.get("message_id", None), + "chat_id": chat_id, + "message_id": message_id, "data": event_data, }, to=session_id, @@ -667,8 +670,11 @@ def get_event_emitter(request_info, update_db=True): ] await asyncio.gather(*emit_tasks) - - if update_db: + if ( + update_db + and message_id + and not request_info.get("chat_id", "").startswith("local:") + ): if "type" in event_data and event_data["type"] == "status": Chats.add_message_status_to_chat_by_id_and_message_id( request_info["chat_id"], diff --git a/backend/open_webui/utils/middleware.py b/backend/open_webui/utils/middleware.py index 2667252b37..377ba54dc3 100644 --- a/backend/open_webui/utils/middleware.py +++ b/backend/open_webui/utils/middleware.py @@ -80,6 +80,7 @@ from open_webui.utils.misc import ( add_or_update_system_message, add_or_update_user_message, get_last_user_message, + get_last_user_message_item, get_last_assistant_message, get_system_message, prepend_to_first_user_message_content, @@ -1418,10 +1419,13 @@ async def process_chat_response( request, response, form_data, user, metadata, model, events, tasks ): async def background_tasks_handler(): - messages_map = Chats.get_messages_map_by_chat_id(metadata["chat_id"]) - message = messages_map.get(metadata["message_id"]) if messages_map else None + message = None + messages = [] + + if "chat_id" in metadata and not metadata["chat_id"].startswith("local:"): + messages_map = Chats.get_messages_map_by_chat_id(metadata["chat_id"]) + message = messages_map.get(metadata["message_id"]) if messages_map else None - if message: message_list = get_message_list(messages_map, metadata["message_id"]) # Remove details tags and files from the messages. @@ -1454,12 +1458,21 @@ async def process_chat_response( "content": content, } ) + else: + # Local temp chat, get the model and message from the form_data + message = get_last_user_message_item(form_data.get("messages", [])) + messages = form_data.get("messages", []) + if message: + message["model"] = form_data.get("model") + if message and "model" in message: if tasks and messages: if ( TASKS.FOLLOW_UP_GENERATION in tasks and tasks[TASKS.FOLLOW_UP_GENERATION] ): + + print("Generating follow ups") res = await generate_follow_ups( request, { @@ -1490,15 +1503,6 @@ async def process_chat_response( follow_ups = json.loads(follow_ups_string).get( "follow_ups", [] ) - - Chats.upsert_message_to_chat_by_id_and_message_id( - metadata["chat_id"], - metadata["message_id"], - { - "followUps": follow_ups, - }, - ) - await event_emitter( { "type": "chat:message:follow_ups", @@ -1507,17 +1511,93 @@ async def process_chat_response( }, } ) + + if not metadata.get("chat_id", "").startswith("local:"): + Chats.upsert_message_to_chat_by_id_and_message_id( + metadata["chat_id"], + metadata["message_id"], + { + "followUps": follow_ups, + }, + ) + except Exception as e: pass - if TASKS.TITLE_GENERATION in tasks: - user_message = get_last_user_message(messages) - if user_message and len(user_message) > 100: - user_message = user_message[:100] + "..." + if not metadata.get("chat_id", "").startswith( + "local:" + ): # Only update titles and tags for non-temp chats + if ( + TASKS.TITLE_GENERATION in tasks + and tasks[TASKS.TITLE_GENERATION] + ): + user_message = get_last_user_message(messages) + if user_message and len(user_message) > 100: + user_message = user_message[:100] + "..." - if tasks[TASKS.TITLE_GENERATION]: + if tasks[TASKS.TITLE_GENERATION]: - res = await generate_title( + res = await generate_title( + request, + { + "model": message["model"], + "messages": messages, + "chat_id": metadata["chat_id"], + }, + user, + ) + + if res and isinstance(res, dict): + if len(res.get("choices", [])) == 1: + title_string = ( + res.get("choices", [])[0] + .get("message", {}) + .get( + "content", + message.get("content", user_message), + ) + ) + else: + title_string = "" + + title_string = title_string[ + title_string.find("{") : title_string.rfind("}") + 1 + ] + + try: + title = json.loads(title_string).get( + "title", user_message + ) + except Exception as e: + title = "" + + if not title: + title = messages[0].get("content", user_message) + + Chats.update_chat_title_by_id( + metadata["chat_id"], title + ) + + await event_emitter( + { + "type": "chat:title", + "data": title, + } + ) + elif len(messages) == 2: + title = messages[0].get("content", user_message) + + Chats.update_chat_title_by_id(metadata["chat_id"], title) + + await event_emitter( + { + "type": "chat:title", + "data": message.get("content", user_message), + } + ) + + if TASKS.TAGS_GENERATION in tasks and tasks[TASKS.TAGS_GENERATION]: + res = await generate_chat_tags( request, { "model": message["model"], @@ -1529,89 +1609,32 @@ async def process_chat_response( if res and isinstance(res, dict): if len(res.get("choices", [])) == 1: - title_string = ( + tags_string = ( res.get("choices", [])[0] .get("message", {}) - .get( - "content", message.get("content", user_message) - ) + .get("content", "") ) else: - title_string = "" + tags_string = "" - title_string = title_string[ - title_string.find("{") : title_string.rfind("}") + 1 + tags_string = tags_string[ + tags_string.find("{") : tags_string.rfind("}") + 1 ] try: - title = json.loads(title_string).get( - "title", user_message + tags = json.loads(tags_string).get("tags", []) + Chats.update_chat_tags_by_id( + metadata["chat_id"], tags, user + ) + + await event_emitter( + { + "type": "chat:tags", + "data": tags, + } ) except Exception as e: - title = "" - - if not title: - title = messages[0].get("content", user_message) - - Chats.update_chat_title_by_id(metadata["chat_id"], title) - - await event_emitter( - { - "type": "chat:title", - "data": title, - } - ) - elif len(messages) == 2: - title = messages[0].get("content", user_message) - - Chats.update_chat_title_by_id(metadata["chat_id"], title) - - await event_emitter( - { - "type": "chat:title", - "data": message.get("content", user_message), - } - ) - - if TASKS.TAGS_GENERATION in tasks and tasks[TASKS.TAGS_GENERATION]: - res = await generate_chat_tags( - request, - { - "model": message["model"], - "messages": messages, - "chat_id": metadata["chat_id"], - }, - user, - ) - - if res and isinstance(res, dict): - if len(res.get("choices", [])) == 1: - tags_string = ( - res.get("choices", [])[0] - .get("message", {}) - .get("content", "") - ) - else: - tags_string = "" - - tags_string = tags_string[ - tags_string.find("{") : tags_string.rfind("}") + 1 - ] - - try: - tags = json.loads(tags_string).get("tags", []) - Chats.update_chat_tags_by_id( - metadata["chat_id"], tags, user - ) - - await event_emitter( - { - "type": "chat:tags", - "data": tags, - } - ) - except Exception as e: - pass + pass event_emitter = None event_caller = None diff --git a/src/lib/components/chat/Chat.svelte b/src/lib/components/chat/Chat.svelte index 05f787bcec..ebdb15c4e3 100644 --- a/src/lib/components/chat/Chat.svelte +++ b/src/lib/components/chat/Chat.svelte @@ -2207,8 +2207,8 @@ selectedFolder.set(null); } else { - _chatId = 'local'; - await chatId.set('local'); + _chatId = `local:${$socket?.id}`; // Use socket id for temporary chat + await chatId.set(_chatId); } await tick(); diff --git a/src/lib/components/chat/Navbar.svelte b/src/lib/components/chat/Navbar.svelte index c8939892dd..755e98e7af 100644 --- a/src/lib/components/chat/Navbar.svelte +++ b/src/lib/components/chat/Navbar.svelte @@ -248,7 +248,7 @@
- {#if $temporaryChatEnabled && $chatId === 'local'} + {#if $temporaryChatEnabled && ($chatId ?? '').startsWith('local:')}
{$i18n.t('Temporary Chat')}
diff --git a/src/lib/components/layout/Navbar/Menu.svelte b/src/lib/components/layout/Navbar/Menu.svelte index 9e4fda3b20..65448b2c6a 100644 --- a/src/lib/components/layout/Navbar/Menu.svelte +++ b/src/lib/components/layout/Navbar/Menu.svelte @@ -232,7 +232,7 @@ if (chat.id) { let chatObj = null; - if (chat.id === 'local' || $temporaryChatEnabled) { + if ((chat?.id ?? '').startsWith('local') || $temporaryChatEnabled) { chatObj = chat; } else { chatObj = await getChatById(localStorage.token, chat.id); From 028f29556fd261a7054fbf917a833031f79a6188 Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Wed, 1 Oct 2025 22:51:57 -0500 Subject: [PATCH 046/127] refac --- src/lib/components/layout/Navbar/Menu.svelte | 24 +++++++++----------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/src/lib/components/layout/Navbar/Menu.svelte b/src/lib/components/layout/Navbar/Menu.svelte index 65448b2c6a..19170e5b7b 100644 --- a/src/lib/components/layout/Navbar/Menu.svelte +++ b/src/lib/components/layout/Navbar/Menu.svelte @@ -431,9 +431,9 @@
{$i18n.t('Copy')}
-
+ {#if !$temporaryChatEnabled && chat?.id} +
- {#if chat?.id} - {/if} - { - archiveChatHandler(); - }} - > - -
{$i18n.t('Archive')}
-
+ { + archiveChatHandler(); + }} + > + +
{$i18n.t('Archive')}
+
- {#if !$temporaryChatEnabled}
From c0d3e702969b37c0c90fa6e193219ece4baed827 Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Wed, 1 Oct 2025 22:58:04 -0500 Subject: [PATCH 047/127] refac: stop task --- backend/open_webui/tasks.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/backend/open_webui/tasks.py b/backend/open_webui/tasks.py index a15e8ac146..3e31438281 100644 --- a/backend/open_webui/tasks.py +++ b/backend/open_webui/tasks.py @@ -164,7 +164,10 @@ async def stop_task(redis, task_id: str): # Task successfully canceled return {"status": True, "message": f"Task {task_id} successfully stopped."} - return {"status": False, "message": f"Failed to stop task {task_id}."} + if task.cancelled() or task.done(): + return {"status": True, "message": f"Task {task_id} successfully cancelled."} + + return {"status": True, "message": f"Cancellation requested for {task_id}."} async def stop_item_tasks(redis: Redis, item_id: str): From 2e75c6dbdf0658e6689fba1e477f8021a3e85d54 Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Wed, 1 Oct 2025 23:01:08 -0500 Subject: [PATCH 048/127] refac/fix: azure audio escape --- backend/open_webui/routers/audio.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/backend/open_webui/routers/audio.py b/backend/open_webui/routers/audio.py index 100610a83a..e0aee2f726 100644 --- a/backend/open_webui/routers/audio.py +++ b/backend/open_webui/routers/audio.py @@ -3,6 +3,7 @@ import json import logging import os import uuid +import html from functools import lru_cache from pydub import AudioSegment from pydub.silence import split_on_silence @@ -458,7 +459,7 @@ async def speech(request: Request, user=Depends(get_verified_user)): try: data = f""" - {payload["input"]} + {html.escape(payload["input"])} """ timeout = aiohttp.ClientTimeout(total=AIOHTTP_CLIENT_TIMEOUT) async with aiohttp.ClientSession( From 39675434f6686c9e3793319a4991a63980fb7e4a Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Thu, 2 Oct 2025 01:29:44 -0500 Subject: [PATCH 049/127] refac: external tool validation --- src/lib/components/AddToolServerModal.svelte | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/lib/components/AddToolServerModal.svelte b/src/lib/components/AddToolServerModal.svelte index 08489348b3..21ce63f014 100644 --- a/src/lib/components/AddToolServerModal.svelte +++ b/src/lib/components/AddToolServerModal.svelte @@ -98,9 +98,16 @@ return; } - if (path === '') { - toast.error($i18n.t('Please enter a valid path')); - return; + if (['openapi', ''].includes(type)) { + if (spec_type === 'json' && spec === '') { + toast.error($i18n.t('Please enter a valid JSON spec')); + return; + } + + if (spec_type === 'url' && path === '') { + toast.error($i18n.t('Please enter a valid path')); + return; + } } if (direct) { From 6ff392edc05b3548925b67af5ff8e90e62d7e1a1 Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Thu, 2 Oct 2025 01:58:06 -0500 Subject: [PATCH 050/127] refac/enh: start.sh additional args support --- backend/start.sh | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/backend/start.sh b/backend/start.sh index c32498aa45..bc57b2bb03 100755 --- a/backend/start.sh +++ b/backend/start.sh @@ -70,5 +70,18 @@ if [ -n "$SPACE_ID" ]; then fi PYTHON_CMD=$(command -v python3 || command -v python) +UVICORN_WORKERS="${UVICORN_WORKERS:-1}" -WEBUI_SECRET_KEY="$WEBUI_SECRET_KEY" exec "$PYTHON_CMD" -m uvicorn open_webui.main:app --host "$HOST" --port "$PORT" --forwarded-allow-ips '*' --workers "${UVICORN_WORKERS:-1}" +# If script is called with arguments, use them; otherwise use default workers +if [ "$#" -gt 0 ]; then + ARGS=("$@") +else + ARGS=(--workers "$UVICORN_WORKERS") +fi + +# Run uvicorn +exec "$PYTHON_CMD" -m uvicorn open_webui.main:app \ + --host "$HOST" \ + --port "$PORT" \ + --forwarded-allow-ips '*' \ + "${ARGS[@]}" \ No newline at end of file From a57bb6a7d444098758993355e0f3db39e48c69dc Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Thu, 2 Oct 2025 01:59:10 -0500 Subject: [PATCH 051/127] refac --- backend/start.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/start.sh b/backend/start.sh index bc57b2bb03..31e87c9557 100755 --- a/backend/start.sh +++ b/backend/start.sh @@ -80,7 +80,7 @@ else fi # Run uvicorn -exec "$PYTHON_CMD" -m uvicorn open_webui.main:app \ +WEBUI_SECRET_KEY="$WEBUI_SECRET_KEY" exec "$PYTHON_CMD" -m uvicorn open_webui.main:app \ --host "$HOST" \ --port "$PORT" \ --forwarded-allow-ips '*' \ From b6538b2cddbe83b4a3a2d03d97e374f5bad61964 Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Thu, 2 Oct 2025 02:19:01 -0500 Subject: [PATCH 052/127] refac: styling --- .../chat/Messages/ResponseMessage/RegenerateMenu.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/components/chat/Messages/ResponseMessage/RegenerateMenu.svelte b/src/lib/components/chat/Messages/ResponseMessage/RegenerateMenu.svelte index ba822b77a1..8adca15283 100644 --- a/src/lib/components/chat/Messages/ResponseMessage/RegenerateMenu.svelte +++ b/src/lib/components/chat/Messages/ResponseMessage/RegenerateMenu.svelte @@ -29,7 +29,7 @@
Date: Thu, 2 Oct 2025 02:21:21 -0500 Subject: [PATCH 053/127] refac/fix: direct connection floating action buttons --- backend/open_webui/utils/chat.py | 6 +++++- .../chat/ContentRenderer/FloatingButtons.svelte | 16 +++++++++++----- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/backend/open_webui/utils/chat.py b/backend/open_webui/utils/chat.py index 83483f391b..8b6a0b9da2 100644 --- a/backend/open_webui/utils/chat.py +++ b/backend/open_webui/utils/chat.py @@ -80,6 +80,7 @@ async def generate_direct_chat_completion( event_caller = get_event_call(metadata) channel = f"{user_id}:{session_id}:{request_id}" + logging.info(f"WebSocket channel: {channel}") if form_data.get("stream"): q = asyncio.Queue() @@ -121,7 +122,10 @@ async def generate_direct_chat_completion( yield f"data: {json.dumps(data)}\n\n" elif isinstance(data, str): - yield data + if "data:" in data: + yield f"{data}\n\n" + else: + yield f"data: {data}\n\n" except Exception as e: log.debug(f"Error in event generator: {e}") pass diff --git a/src/lib/components/chat/ContentRenderer/FloatingButtons.svelte b/src/lib/components/chat/ContentRenderer/FloatingButtons.svelte index 389e17adc0..03c9429588 100644 --- a/src/lib/components/chat/ContentRenderer/FloatingButtons.svelte +++ b/src/lib/components/chat/ContentRenderer/FloatingButtons.svelte @@ -13,6 +13,7 @@ import LightBulb from '$lib/components/icons/LightBulb.svelte'; import Markdown from '../Messages/Markdown.svelte'; import Skeleton from '../Messages/Skeleton.svelte'; + import { chatId, models, socket } from '$lib/stores'; export let id = ''; export let messageId = ''; @@ -118,6 +119,9 @@ let res; [res, controller] = await chatCompletion(localStorage.token, { model: model, + model_item: $models.find((m) => m.id === model), + session_id: $socket?.id, + chat_id: $chatId, messages: [ ...messages, { @@ -246,11 +250,11 @@ {#if responseContent === null} {#if !floatingInput}
{#each actions as action}
{/if} {:else} -
+
@@ -331,7 +337,7 @@
{#if !responseContent || responseContent?.trim() === ''} From a1fc99c66fc62fcea98cdbef2e23ddbc7a7465b5 Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Thu, 2 Oct 2025 02:57:54 -0500 Subject: [PATCH 054/127] refac/fix: system prompt duplication --- backend/open_webui/utils/middleware.py | 2 +- backend/open_webui/utils/misc.py | 8 ++++++++ backend/open_webui/utils/payload.py | 19 +++++++++++++++---- 3 files changed, 24 insertions(+), 5 deletions(-) diff --git a/backend/open_webui/utils/middleware.py b/backend/open_webui/utils/middleware.py index 377ba54dc3..1ae340ae7b 100644 --- a/backend/open_webui/utils/middleware.py +++ b/backend/open_webui/utils/middleware.py @@ -1004,7 +1004,7 @@ async def process_chat_payload(request, form_data, user, metadata, model): if system_message: try: form_data = apply_system_prompt_to_body( - system_message.get("content"), form_data, metadata, user + system_message.get("content"), form_data, metadata, user, replace=True ) except: pass diff --git a/backend/open_webui/utils/misc.py b/backend/open_webui/utils/misc.py index 81a4142ea0..8977cf17d4 100644 --- a/backend/open_webui/utils/misc.py +++ b/backend/open_webui/utils/misc.py @@ -136,6 +136,14 @@ def update_message_content(message: dict, content: str, append: bool = True) -> return message +def replace_system_message_content(content: str, messages: list[dict]) -> dict: + for message in messages: + if message["role"] == "system": + message["content"] = content + break + return messages + + def add_or_update_system_message( content: str, messages: list[dict], append: bool = False ): diff --git a/backend/open_webui/utils/payload.py b/backend/open_webui/utils/payload.py index 8cb36b3759..4a431dcab3 100644 --- a/backend/open_webui/utils/payload.py +++ b/backend/open_webui/utils/payload.py @@ -2,6 +2,7 @@ from open_webui.utils.task import prompt_template, prompt_variables_template from open_webui.utils.misc import ( deep_update, add_or_update_system_message, + replace_system_message_content, ) from typing import Callable, Optional @@ -10,7 +11,11 @@ import json # inplace function: form_data is modified def apply_system_prompt_to_body( - system: Optional[str], form_data: dict, metadata: Optional[dict] = None, user=None + system: Optional[str], + form_data: dict, + metadata: Optional[dict] = None, + user=None, + replace: bool = False, ) -> dict: if not system: return form_data @@ -24,9 +29,15 @@ def apply_system_prompt_to_body( # Legacy (API Usage) system = prompt_template(system, user) - form_data["messages"] = add_or_update_system_message( - system, form_data.get("messages", []) - ) + if replace: + form_data["messages"] = replace_system_message_content( + system, form_data.get("messages", []) + ) + else: + form_data["messages"] = add_or_update_system_message( + system, form_data.get("messages", []) + ) + return form_data From 5d5b42d3f5c4b657896c2b3b3a34f3e13d510a07 Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Thu, 2 Oct 2025 03:52:29 -0500 Subject: [PATCH 055/127] refac/enh: openai tts additional params support --- backend/open_webui/config.py | 13 ++++++++ backend/open_webui/main.py | 13 +++++--- backend/open_webui/routers/audio.py | 15 +++++++-- .../components/admin/Settings/Audio.svelte | 33 +++++++++++++++++-- 4 files changed, 65 insertions(+), 9 deletions(-) diff --git a/backend/open_webui/config.py b/backend/open_webui/config.py index fc60455e3e..f17b9f94b9 100644 --- a/backend/open_webui/config.py +++ b/backend/open_webui/config.py @@ -3361,6 +3361,19 @@ AUDIO_TTS_OPENAI_API_KEY = PersistentConfig( os.getenv("AUDIO_TTS_OPENAI_API_KEY", OPENAI_API_KEY), ) +audio_tts_openai_params = os.getenv("AUDIO_TTS_OPENAI_PARAMS", "") +try: + audio_tts_openai_params = json.loads(audio_tts_openai_params) +except json.JSONDecodeError: + audio_tts_openai_params = {} + +AUDIO_TTS_OPENAI_PARAMS = PersistentConfig( + "AUDIO_TTS_OPENAI_PARAMS", + "audio.tts.openai.params", + audio_tts_openai_params, +) + + AUDIO_TTS_API_KEY = PersistentConfig( "AUDIO_TTS_API_KEY", "audio.tts.api_key", diff --git a/backend/open_webui/main.py b/backend/open_webui/main.py index 5dad3d7904..a68c943966 100644 --- a/backend/open_webui/main.py +++ b/backend/open_webui/main.py @@ -175,13 +175,14 @@ from open_webui.config import ( AUDIO_STT_AZURE_LOCALES, AUDIO_STT_AZURE_BASE_URL, AUDIO_STT_AZURE_MAX_SPEAKERS, - AUDIO_TTS_API_KEY, AUDIO_TTS_ENGINE, AUDIO_TTS_MODEL, + AUDIO_TTS_VOICE, AUDIO_TTS_OPENAI_API_BASE_URL, AUDIO_TTS_OPENAI_API_KEY, + AUDIO_TTS_OPENAI_PARAMS, + AUDIO_TTS_API_KEY, AUDIO_TTS_SPLIT_ON, - AUDIO_TTS_VOICE, AUDIO_TTS_AZURE_SPEECH_REGION, AUDIO_TTS_AZURE_SPEECH_BASE_URL, AUDIO_TTS_AZURE_SPEECH_OUTPUT_FORMAT, @@ -1096,11 +1097,15 @@ app.state.config.AUDIO_STT_AZURE_LOCALES = AUDIO_STT_AZURE_LOCALES app.state.config.AUDIO_STT_AZURE_BASE_URL = AUDIO_STT_AZURE_BASE_URL app.state.config.AUDIO_STT_AZURE_MAX_SPEAKERS = AUDIO_STT_AZURE_MAX_SPEAKERS -app.state.config.TTS_OPENAI_API_BASE_URL = AUDIO_TTS_OPENAI_API_BASE_URL -app.state.config.TTS_OPENAI_API_KEY = AUDIO_TTS_OPENAI_API_KEY app.state.config.TTS_ENGINE = AUDIO_TTS_ENGINE + app.state.config.TTS_MODEL = AUDIO_TTS_MODEL app.state.config.TTS_VOICE = AUDIO_TTS_VOICE + +app.state.config.TTS_OPENAI_API_BASE_URL = AUDIO_TTS_OPENAI_API_BASE_URL +app.state.config.TTS_OPENAI_API_KEY = AUDIO_TTS_OPENAI_API_KEY +app.state.config.TTS_OPENAI_PARAMS = AUDIO_TTS_OPENAI_PARAMS + app.state.config.TTS_API_KEY = AUDIO_TTS_API_KEY app.state.config.TTS_SPLIT_ON = AUDIO_TTS_SPLIT_ON diff --git a/backend/open_webui/routers/audio.py b/backend/open_webui/routers/audio.py index e0aee2f726..cb7a57b5b7 100644 --- a/backend/open_webui/routers/audio.py +++ b/backend/open_webui/routers/audio.py @@ -154,6 +154,7 @@ def set_faster_whisper_model(model: str, auto_update: bool = False): class TTSConfigForm(BaseModel): OPENAI_API_BASE_URL: str OPENAI_API_KEY: str + OPENAI_PARAMS: Optional[dict] = None API_KEY: str ENGINE: str MODEL: str @@ -190,6 +191,7 @@ async def get_audio_config(request: Request, user=Depends(get_admin_user)): "tts": { "OPENAI_API_BASE_URL": request.app.state.config.TTS_OPENAI_API_BASE_URL, "OPENAI_API_KEY": request.app.state.config.TTS_OPENAI_API_KEY, + "OPENAI_PARAMS": request.app.state.config.TTS_OPENAI_PARAMS, "API_KEY": request.app.state.config.TTS_API_KEY, "ENGINE": request.app.state.config.TTS_ENGINE, "MODEL": request.app.state.config.TTS_MODEL, @@ -222,6 +224,7 @@ async def update_audio_config( ): request.app.state.config.TTS_OPENAI_API_BASE_URL = form_data.tts.OPENAI_API_BASE_URL request.app.state.config.TTS_OPENAI_API_KEY = form_data.tts.OPENAI_API_KEY + request.app.state.config.TTS_OPENAI_PARAMS = form_data.tts.OPENAI_PARAMS request.app.state.config.TTS_API_KEY = form_data.tts.API_KEY request.app.state.config.TTS_ENGINE = form_data.tts.ENGINE request.app.state.config.TTS_MODEL = form_data.tts.MODEL @@ -262,12 +265,13 @@ async def update_audio_config( return { "tts": { - "OPENAI_API_BASE_URL": request.app.state.config.TTS_OPENAI_API_BASE_URL, - "OPENAI_API_KEY": request.app.state.config.TTS_OPENAI_API_KEY, - "API_KEY": request.app.state.config.TTS_API_KEY, "ENGINE": request.app.state.config.TTS_ENGINE, "MODEL": request.app.state.config.TTS_MODEL, "VOICE": request.app.state.config.TTS_VOICE, + "OPENAI_API_BASE_URL": request.app.state.config.TTS_OPENAI_API_BASE_URL, + "OPENAI_API_KEY": request.app.state.config.TTS_OPENAI_API_KEY, + "OPENAI_PARAMS": request.app.state.config.TTS_OPENAI_PARAMS, + "API_KEY": request.app.state.config.TTS_API_KEY, "SPLIT_ON": request.app.state.config.TTS_SPLIT_ON, "AZURE_SPEECH_REGION": request.app.state.config.TTS_AZURE_SPEECH_REGION, "AZURE_SPEECH_BASE_URL": request.app.state.config.TTS_AZURE_SPEECH_BASE_URL, @@ -337,6 +341,11 @@ async def speech(request: Request, user=Depends(get_verified_user)): async with aiohttp.ClientSession( timeout=timeout, trust_env=True ) as session: + payload = { + **payload, + **(request.app.state.config.TTS_OPENAI_PARAMS or {}), + } + r = await session.post( url=f"{request.app.state.config.TTS_OPENAI_API_BASE_URL}/audio/speech", json=payload, diff --git a/src/lib/components/admin/Settings/Audio.svelte b/src/lib/components/admin/Settings/Audio.svelte index 10c8a6adb2..588b6bed7c 100644 --- a/src/lib/components/admin/Settings/Audio.svelte +++ b/src/lib/components/admin/Settings/Audio.svelte @@ -19,6 +19,7 @@ import type { Writable } from 'svelte/store'; import type { i18n as i18nType } from 'i18next'; + import Textarea from '$lib/components/common/Textarea.svelte'; const i18n = getContext>('i18n'); @@ -31,6 +32,7 @@ let TTS_ENGINE = ''; let TTS_MODEL = ''; let TTS_VOICE = ''; + let TTS_OPENAI_PARAMS = ''; let TTS_SPLIT_ON: TTS_RESPONSE_SPLIT = TTS_RESPONSE_SPLIT.PUNCTUATION; let TTS_AZURE_SPEECH_REGION = ''; let TTS_AZURE_SPEECH_BASE_URL = ''; @@ -98,18 +100,28 @@ }; const updateConfigHandler = async () => { + let openaiParams = {}; + try { + openaiParams = TTS_OPENAI_PARAMS ? JSON.parse(TTS_OPENAI_PARAMS) : {}; + TTS_OPENAI_PARAMS = JSON.stringify(openaiParams, null, 2); + } catch (e) { + toast.error($i18n.t('Invalid JSON format for Parameters')); + return; + } + const res = await updateAudioConfig(localStorage.token, { tts: { OPENAI_API_BASE_URL: TTS_OPENAI_API_BASE_URL, OPENAI_API_KEY: TTS_OPENAI_API_KEY, + OPENAI_PARAMS: openaiParams, API_KEY: TTS_API_KEY, ENGINE: TTS_ENGINE, MODEL: TTS_MODEL, VOICE: TTS_VOICE, - SPLIT_ON: TTS_SPLIT_ON, AZURE_SPEECH_REGION: TTS_AZURE_SPEECH_REGION, AZURE_SPEECH_BASE_URL: TTS_AZURE_SPEECH_BASE_URL, - AZURE_SPEECH_OUTPUT_FORMAT: TTS_AZURE_SPEECH_OUTPUT_FORMAT + AZURE_SPEECH_OUTPUT_FORMAT: TTS_AZURE_SPEECH_OUTPUT_FORMAT, + SPLIT_ON: TTS_SPLIT_ON }, stt: { OPENAI_API_BASE_URL: STT_OPENAI_API_BASE_URL, @@ -146,6 +158,7 @@ console.log(res); TTS_OPENAI_API_BASE_URL = res.tts.OPENAI_API_BASE_URL; TTS_OPENAI_API_KEY = res.tts.OPENAI_API_KEY; + TTS_OPENAI_PARAMS = JSON.stringify(res?.tts?.OPENAI_PARAMS ?? '', null, 2); TTS_API_KEY = res.tts.API_KEY; TTS_ENGINE = res.tts.ENGINE; @@ -612,6 +625,22 @@
+ +
+
+
{$i18n.t('Additional Parameters')}
+
+
+