From e50cde80f3e49a7f2e1f86b05d19e61eddc629eb Mon Sep 17 00:00:00 2001 From: Classic298 <27028174+Classic298@users.noreply.github.com> Date: Fri, 13 Jun 2025 13:05:33 +0200 Subject: [PATCH 001/116] Fix search unicode error (#11) --- backend/open_webui/models/chats.py | 79 +++++++++++++----------------- 1 file changed, 34 insertions(+), 45 deletions(-) diff --git a/backend/open_webui/models/chats.py b/backend/open_webui/models/chats.py index 0ac53a0233..71cdd0befb 100644 --- a/backend/open_webui/models/chats.py +++ b/backend/open_webui/models/chats.py @@ -12,6 +12,7 @@ from pydantic import BaseModel, ConfigDict from sqlalchemy import BigInteger, Boolean, Column, String, Text, JSON from sqlalchemy import or_, func, select, and_, text from sqlalchemy.sql import exists +from sqlalchemy.sql.expression import bindparam #################### # Chat DB Schema @@ -232,6 +233,10 @@ class ChatTable: if chat is None: return None + # Sanitize message content for null characters before upserting + if isinstance(message.get("content"), str): + message["content"] = message["content"].replace("\x00", "") + chat = chat.chat history = chat.get("history", {}) @@ -580,7 +585,7 @@ class ChatTable: """ Filters chats based on a search query using Python, allowing pagination using skip and limit. """ - search_text = search_text.lower().strip() + search_text = search_text.replace("\u0000", "").lower().strip() if not search_text: return self.get_chat_list_by_user_id( @@ -614,24 +619,22 @@ class ChatTable: dialect_name = db.bind.dialect.name if dialect_name == "sqlite": # SQLite case: using JSON1 extension for JSON searching + sqlite_content_sql = ( + "EXISTS (" + " SELECT 1 " + " FROM json_each(Chat.chat, '$.messages') AS message " + " WHERE LOWER(message.value->>'content') LIKE '%' || :content_key || '%'" + ")" + ) + sqlite_content_clause = text(sqlite_content_sql) query = query.filter( - ( - Chat.title.ilike( - f"%{search_text}%" - ) # Case-insensitive search in title - | text( - """ - EXISTS ( - SELECT 1 - FROM json_each(Chat.chat, '$.messages') AS message - WHERE LOWER(message.value->>'content') LIKE '%' || :search_text || '%' - ) - """ - ) - ).params(search_text=search_text) + or_( + Chat.title.ilike(bindparam('title_key')), + sqlite_content_clause + ).params(title_key=f"%{search_text}%", content_key=search_text) ) - # Check if there are any tags to filter, it should have all the tags + # Tag filtering if "none" in tag_ids: query = query.filter( text( @@ -648,13 +651,7 @@ class ChatTable: and_( *[ text( - f""" - EXISTS ( - SELECT 1 - FROM json_each(Chat.meta, '$.tags') AS tag - WHERE tag.value = :tag_id_{tag_idx} - ) - """ + f"EXISTS (SELECT 1 FROM json_each(Chat.meta, '$.tags') AS tag WHERE tag.value = :tag_id_{tag_idx})" ).params(**{f"tag_id_{tag_idx}": tag_id}) for tag_idx, tag_id in enumerate(tag_ids) ] @@ -663,24 +660,22 @@ class ChatTable: elif dialect_name == "postgresql": # PostgreSQL relies on proper JSON query for search + postgres_content_sql = ( + "EXISTS (" + " SELECT 1 " + " FROM json_array_elements(Chat.chat->'messages') AS message " + " WHERE LOWER(message->>'content') LIKE '%' || :content_key || '%'" + ")" + ) + postgres_content_clause = text(postgres_content_sql) query = query.filter( - ( - Chat.title.ilike( - f"%{search_text}%" - ) # Case-insensitive search in title - | text( - """ - EXISTS ( - SELECT 1 - FROM json_array_elements(Chat.chat->'messages') AS message - WHERE LOWER(message->>'content') LIKE '%' || :search_text || '%' - ) - """ - ) - ).params(search_text=search_text) + or_( + Chat.title.ilike(bindparam('title_key')), + postgres_content_clause + ).params(title_key=f"%{search_text}%", content_key=search_text) ) - # Check if there are any tags to filter, it should have all the tags + # Tag filtering if "none" in tag_ids: query = query.filter( text( @@ -697,13 +692,7 @@ class ChatTable: and_( *[ text( - f""" - EXISTS ( - SELECT 1 - FROM json_array_elements_text(Chat.meta->'tags') AS tag - WHERE tag = :tag_id_{tag_idx} - ) - """ + f"EXISTS (SELECT 1 FROM json_array_elements_text(Chat.meta->'tags') AS tag WHERE tag = :tag_id_{tag_idx})" ).params(**{f"tag_id_{tag_idx}": tag_id}) for tag_idx, tag_id in enumerate(tag_ids) ] From 033e5c1e002626ce20eb562147b323b6fbed33c5 Mon Sep 17 00:00:00 2001 From: Classic298 <27028174+Classic298@users.noreply.github.com> Date: Fri, 13 Jun 2025 13:07:41 +0200 Subject: [PATCH 002/116] Update chats.py --- backend/open_webui/models/chats.py | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/backend/open_webui/models/chats.py b/backend/open_webui/models/chats.py index 71cdd0befb..92da599b3b 100644 --- a/backend/open_webui/models/chats.py +++ b/backend/open_webui/models/chats.py @@ -634,7 +634,7 @@ class ChatTable: ).params(title_key=f"%{search_text}%", content_key=search_text) ) - # Tag filtering + # Check if there are any tags to filter, it should have all the tags if "none" in tag_ids: query = query.filter( text( @@ -651,7 +651,13 @@ class ChatTable: and_( *[ text( - f"EXISTS (SELECT 1 FROM json_each(Chat.meta, '$.tags') AS tag WHERE tag.value = :tag_id_{tag_idx})" + f""" + EXISTS ( + SELECT 1 + FROM json_each(Chat.meta, '$.tags') AS tag + WHERE tag.value = :tag_id_{tag_idx} + ) + """ ).params(**{f"tag_id_{tag_idx}": tag_id}) for tag_idx, tag_id in enumerate(tag_ids) ] @@ -675,7 +681,7 @@ class ChatTable: ).params(title_key=f"%{search_text}%", content_key=search_text) ) - # Tag filtering + # Check if there are any tags to filter, it should have all the tags if "none" in tag_ids: query = query.filter( text( @@ -692,7 +698,13 @@ class ChatTable: and_( *[ text( - f"EXISTS (SELECT 1 FROM json_array_elements_text(Chat.meta->'tags') AS tag WHERE tag = :tag_id_{tag_idx})" + f""" + EXISTS ( + SELECT 1 + FROM json_array_elements_text(Chat.meta->'tags') AS tag + WHERE tag = :tag_id_{tag_idx} + ) + """ ).params(**{f"tag_id_{tag_idx}": tag_id}) for tag_idx, tag_id in enumerate(tag_ids) ] From f0e60c28df7ccae4daecca36825eb3e505263623 Mon Sep 17 00:00:00 2001 From: guenhter Date: Mon, 23 Jun 2025 11:48:03 +0200 Subject: [PATCH 003/116] fix: use correct password autocomplete for signup Password manager act based on the 'autocomplet' attribute of the password fields. If the attribut is set to "current-password" they try to fill the password with an existing one. If it is set to "new-password" they try to support the user by generating a new password. For signup in owui, the password was always set to "current-password", so the password manager never proposed a password on signup. --- src/routes/auth/+page.svelte | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/routes/auth/+page.svelte b/src/routes/auth/+page.svelte index c4dcfcd811..c7e114fd0a 100644 --- a/src/routes/auth/+page.svelte +++ b/src/routes/auth/+page.svelte @@ -302,8 +302,8 @@ id="password" class="my-0.5 w-full text-sm outline-hidden bg-transparent" placeholder={$i18n.t('Enter Your Password')} - autocomplete="current-password" - name="current-password" + autocomplete={mode === 'signup' ? 'new-password' : 'current-password'} + name="password" required /> From 708010a16af7eea1c906ecb3b3cc35c384f7b070 Mon Sep 17 00:00:00 2001 From: LHCLYT Date: Mon, 23 Jun 2025 12:43:14 +0200 Subject: [PATCH 004/116] feat(i18n): update Spanish translations Updated several Spanish translation strings to improve grammar, consistency, and clarity. Includes fixes for technical terms and overall localization quality. --- src/lib/i18n/locales/es-ES/translation.json | 28 ++++++++++----------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/lib/i18n/locales/es-ES/translation.json b/src/lib/i18n/locales/es-ES/translation.json index 37381fb79b..aa5d436704 100644 --- a/src/lib/i18n/locales/es-ES/translation.json +++ b/src/lib/i18n/locales/es-ES/translation.json @@ -4,7 +4,7 @@ "(e.g. `sh webui.sh --api --api-auth username_password`)": "(p.ej. `sh webui.sh --api --api-auth username_password`)", "(e.g. `sh webui.sh --api`)": "(p.ej. `sh webui.sh --api`)", "(latest)": "(último)", - "(leave blank for to use commercial endpoint)": "(dejar vacío para endpoints commerciales", + "(leave blank for to use commercial endpoint)": "(dejar vacío para usar el endpoint comercial)", "{{ models }}": "{{ models }}", "{{COUNT}} Available Tools": "{{COUNT}} herramientas disponibles", "{{COUNT}} hidden lines": "{{COUNT}} líneas ocultas", @@ -57,12 +57,12 @@ "Advanced Params": "Parámetros Avanzados", "All": "Todos", "All Documents": "Todos los Documentos", - "All models deleted successfully": "Todos los modelos borrados correctamnete", + "All models deleted successfully": "Todos los modelos borrados correctamente", "Allow Call": "Permitir Llamada", "Allow Chat Controls": "Permitir Controles del Chat", "Allow Chat Delete": "Permitir Borrar Chat", "Allow Chat Deletion": "Permitir Borrado de Chat", - "Allow Chat Edit": "Pemritir Editar Chat", + "Allow Chat Edit": "Permitir Editar Chat", "Allow Chat Export": "Permitir Exportar Chat", "Allow Chat Share": "Permitir Compartir Chat", "Allow Chat System Prompt": "", @@ -174,7 +174,7 @@ "Chart new frontiers": "Trazar nuevas fronteras", "Chat": "Chat", "Chat Background Image": "Imágen de Fondo del Chat", - "Chat Bubble UI": "Interface de Chat tipo Burbuja", + "Chat Bubble UI": "Interfaz de Chat en Burbuja", "Chat Controls": "Controles de chat", "Chat direction": "Dirección de Chat", "Chat Overview": "Vista General del Chat", @@ -393,10 +393,10 @@ "e.g. my_filter": "p.ej. mi_filtro", "e.g. my_tools": "p.ej. mis_herramientas", "e.g. pdf, docx, txt": "p.ej. pdf, docx, txt ...", - "e.g. Tools for performing various operations": "p.ej. Herramientas para realizar varias operaciones", + "e.g. Tools for performing various operations": "p.ej. Herramientas para realizar diversas operaciones", "e.g., 3, 4, 5 (leave blank for default)": "p.ej. , 3, 4, 5 ...", "e.g., audio/wav,audio/mpeg (leave blank for defaults)": "", - "e.g., en-US,ja-JP (leave blank for auto-detect)": "p. ej., en-US,ja-JP (dejar en blanco para detectar automáticamente)", + "e.g., en-US,ja-JP (leave blank for auto-detect)": "p.ej., en-US,ja-JP (dejar en blanco para detectar automáticamente)", "e.g., westus (leave blank for eastus)": "p.ej. ,oeste (dejar vacío para este)", "e.g.) en,fr,de": "p.ej. ) en,es,fr,de", "Edit": "Editar", @@ -723,7 +723,7 @@ "Install from Github URL": "Instalar desde la URL de Github", "Instant Auto-Send After Voice Transcription": "AutoEnvio Instantaneo tras la Transcripción de Voz", "Integration": "Integración", - "Interface": "Interface", + "Interface": "Interfaz", "Invalid file content": "Contenido de archivo inválido", "Invalid file format.": "Formato de archivo inválido.", "Invalid JSON file": "Archivo JSON inválido", @@ -786,7 +786,7 @@ "Lost": "Perdido", "LTR": "LTR", "Made by Open WebUI Community": "Creado por la Comunidad Open-WebUI", - "Make password visible in the user interface": "Hacer visible la contraseña en la interface del usuario.", + "Make password visible in the user interface": "Hacer visible la contraseña en la interfaz del usuario.", "Make sure to enclose them with": "Asegúrate de delimitarlos con", "Make sure to export a workflow.json file as API format from ComfyUI.": "Asegúrate de exportar un archivo workflow.json en formato API desde ComfyUI.", "Manage": "Gestionar", @@ -918,7 +918,7 @@ "Oops! Looks like the URL is invalid. Please double-check and try again.": "¡vaya! Parece que la URL es inválida. Por favor, revisala y reintenta de nuevo.", "Oops! There are files still uploading. Please wait for the upload to complete.": "¡vaya! Todavía hay archivos subiendose. Por favor, espera a que se complete la subida.", "Oops! There was an error in the previous response.": "¡vaya! Hubo un error en la respuesta previa.", - "Oops! You're using an unsupported method (frontend only). Please serve the WebUI from the backend.": "¡vaya! Estás usando un método no soportado (solo interface frontal-frontend). Por favor sirve WebUI desde el interface trasero (servidor backend).", + "Oops! You're using an unsupported method (frontend only). Please serve the WebUI from the backend.": "¡vaya! Estás usando un método no soportado (solo interfaz frontal-frontend). Por favor sirve WebUI desde el interfaz trasero (servidor backend).", "Open file": "Abrir archivo", "Open in full screen": "Abrir en pantalla completa", "Open modal to configure connection": "Abrir modal para configurar la conexión", @@ -1165,7 +1165,7 @@ "Signing in to {{WEBUI_NAME}}": "Iniciando Sesión en {{WEBUI_NAME}}", "sk-1234": "sk-1234", "Skip Cache": "Evitar Caché", - "Skip the cache and re-run the inference. Defaults to False.": "Evitar caché y reiniciar la interface. Valor predeterminado Falso", + "Skip the cache and re-run the inference. Defaults to False.": "Evitar caché y reiniciar la interfaz. Valor predeterminado Falso", "Sougou Search API sID": "API sID de Sougou Search", "Sougou Search API SK": "SK API de Sougou Search", "Source": "Fuente", @@ -1249,7 +1249,7 @@ "This will reset the knowledge base and sync all files. Do you wish to continue?": "Esto reinicializará la base de conocimientos y sincronizará todos los archivos. ¿Desea continuar?", "Thorough explanation": "Explicación exhaustiva", "Thought for {{DURATION}}": "Pensando durante {{DURATION}}", - "Thought for {{DURATION}} seconds": "Persando durante {{DURATION}} segundos", + "Thought for {{DURATION}} seconds": "Pensando durante {{DURATION}} segundos", "Tika": "Tika", "Tika Server URL required.": "URL del Servidor Tika necesaria", "Tiktoken": "Tiktoken", @@ -1320,7 +1320,7 @@ "Updated": "Actualizado", "Updated at": "Actualizado el", "Updated At": "Actualizado El", - "Upgrade to a licensed plan for enhanced capabilities, including custom theming and branding, and dedicated support.": "Mejore a un plan con licencia para tener capacidades mejoradas, incluyendo personalización de marca e interface, y soporte dedicado.", + "Upgrade to a licensed plan for enhanced capabilities, including custom theming and branding, and dedicated support.": "Mejore a un plan con licencia para tener capacidades mejoradas, incluyendo personalización de marca e interfaz, y soporte dedicado.", "Upload": "Subir", "Upload a GGUF model": "Subir un modelo GGUF", "Upload Audio": "Subir Audio", @@ -1400,9 +1400,9 @@ "Write a prompt suggestion (e.g. Who are you?)": "Escribe una sugerencia de prompt (p.ej. ¿quién eres?)", "Write a summary in 50 words that summarizes [topic or keyword].": "Escribe un resumen en 50 palabras que resuma [tema o palabra clave].", "Write something...": "Escribe algo...", - "Yacy Instance URL": "URL de la instancia Yary", + "Yacy Instance URL": "URL de la instancia Yacy", "Yacy Password": "Contraseña de Yacy", - "Yacy Username": "Usuario de Yary", + "Yacy Username": "Usuario de Yacy", "Yesterday": "Ayer", "You": "Tu", "You are currently using a trial license. Please contact support to upgrade your license.": "Actualmente estás utilizando una licencia de prueba. Por favor, para actualizar su licencia contacta con soporte.", From e237600e512f47016a87031f321fed49768a458e Mon Sep 17 00:00:00 2001 From: LHCLYT Date: Mon, 23 Jun 2025 12:53:49 +0200 Subject: [PATCH 005/116] feat(i18n): update Catalan translations Updated several Catalan translation strings to improve grammar, consistency, and clarity. Includes fixes for technical terms and overall localization quality. --- src/lib/i18n/locales/ca-ES/translation.json | 30 ++++++++++----------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/lib/i18n/locales/ca-ES/translation.json b/src/lib/i18n/locales/ca-ES/translation.json index bc32b22e94..f08b3fd544 100644 --- a/src/lib/i18n/locales/ca-ES/translation.json +++ b/src/lib/i18n/locales/ca-ES/translation.json @@ -213,7 +213,7 @@ "Close modal": "", "Close settings modal": "", "Code execution": "Execució de codi", - "Code Execution": "Excució de Codi", + "Code Execution": "Execució de Codi", "Code Execution Engine": "Motor d'execució de codi", "Code Execution Timeout": "Temps màxim d'execució de codi", "Code formatted successfully": "Codi formatat correctament", @@ -245,7 +245,7 @@ "Connections": "Connexions", "Connections saved successfully": "Les connexions s'han desat correctament", "Constrains effort on reasoning for reasoning models. Only applicable to reasoning models from specific providers that support reasoning effort.": "Restringeix l'esforç de raonament dels models de raonament. Només aplicable a models de raonament de proveïdors específics que donen suport a l'esforç de raonament.", - "Contact Admin for WebUI Access": "Posat en contacte amb l'administrador per accedir a WebUI", + "Contact Admin for WebUI Access": "Posa't en contacte amb l'administrador per accedir a WebUI", "Content": "Contingut", "Content Extraction Engine": "Motor d'extracció de contingut", "Continue Response": "Continuar la resposta", @@ -319,7 +319,7 @@ "Delete chat?": "Eliminar el xat?", "Delete folder?": "Eliminar la carpeta?", "Delete function?": "Eliminar funció?", - "Delete Message": "Eleiminar el missatge", + "Delete Message": "Eliminar el missatge", "Delete message?": "Eliminar el missatge?", "Delete note?": "Eliminar la nota?", "Delete prompt?": "Eliminar indicació?", @@ -380,9 +380,9 @@ "Download as SVG": "Descarrega com a SVG", "Download canceled": "Descàrrega cancel·lada", "Download Database": "Descarregar la base de dades", - "Drag and drop a file to upload or select a file to view": "Arrossegar un arxiu per pujar o escull un arxiu a veure", + "Drag and drop a file to upload or select a file to view": "Arrossega un fitxer per pujar-lo o selecciona'n un per visualitzar-lo", "Draw": "Dibuixar", - "Drop any files here to upload": "Arrosega qualsevol arxius a pujar", + "Drop any files here to upload": "Arrossega aquí qualsevol fitxer per pujar-lo", "e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "p. ex. '30s','10m'. Les unitats de temps vàlides són 's', 'm', 'h'.", "e.g. \"json\" or a JSON schema": "p. ex. \"json\" o un esquema JSON", "e.g. 60": "p. ex. 60", @@ -444,7 +444,7 @@ "Enter Bocha Search API Key": "Introdueix la clau API de Bocha Search", "Enter Brave Search API Key": "Introdueix la clau API de Brave Search", "Enter certificate path": "Introdueix el camí del certificat", - "Enter CFG Scale (e.g. 7.0)": "Entra l'escala CFG (p.ex. 7.0)", + "Enter CFG Scale (e.g. 7.0)": "Entra l'escala CFG (p. ex. 7.0)", "Enter Chunk Overlap": "Introdueix la mida de solapament de blocs", "Enter Chunk Size": "Introdueix la mida del bloc", "Enter comma-separated \"token:bias_value\" pairs (example: 5432:100, 413:-100)": "Introdueix parelles de \"token:valor de biaix\" separats per comes (exemple: 5432:100, 413:-100)", @@ -490,8 +490,8 @@ "Enter Playwright WebSocket URL": "Introdueix la URL de Playwright WebSocket", "Enter proxy URL (e.g. https://user:password@host:port)": "Entra l'URL (p. ex. https://user:password@host:port)", "Enter reasoning effort": "Introdueix l'esforç de raonament", - "Enter Sampler (e.g. Euler a)": "Introdueix el mostrejador (p.ex. Euler a)", - "Enter Scheduler (e.g. Karras)": "Entra el programador (p.ex. Karras)", + "Enter Sampler (e.g. Euler a)": "Introdueix el mostrejador (p. ex. Euler a)", + "Enter Scheduler (e.g. Karras)": "Entra el programador (p. ex. Karras)", "Enter Score": "Introdueix la puntuació", "Enter SearchApi API Key": "Introdueix la clau API SearchApi", "Enter SearchApi Engine": "Introdueix el motor SearchApi", @@ -655,7 +655,7 @@ "Generating search query": "Generant consulta", "Generating...": "Generant...", "Get started": "Començar", - "Get started with {{WEBUI_NAME}}": "Començar amb {{WEBUI_NAME}}", + "Get started with {{WEBUI_NAME}}": "Comença amb {{WEBUI_NAME}}", "Global": "Global", "Good Response": "Bona resposta", "Google Drive": "Google Drive", @@ -998,7 +998,7 @@ "Private": "Privat", "Profile Image": "Imatge de perfil", "Prompt": "Indicació", - "Prompt (e.g. Tell me a fun fact about the Roman Empire)": "Indicació (p.ex. Digues-me quelcom divertit sobre l'Imperi Romà)", + "Prompt (e.g. Tell me a fun fact about the Roman Empire)": "Indicació (p. ex. Digues-me quelcom divertit sobre l'Imperi Romà)", "Prompt Autocompletion": "Completar automàticament la indicació", "Prompt Content": "Contingut de la indicació", "Prompt created successfully": "Indicació creada correctament", @@ -1123,9 +1123,9 @@ "Set CFG Scale": "Establir l'escala CFG", "Set Default Model": "Establir el model predeterminat", "Set embedding model": "Establir el model d'incrustació", - "Set embedding model (e.g. {{model}})": "Establir el model d'incrustació (p.ex. {{model}})", + "Set embedding model (e.g. {{model}})": "Establir el model d'incrustació (p. ex. {{model}})", "Set Image Size": "Establir la mida de la image", - "Set reranking model (e.g. {{model}})": "Establir el model de reavaluació (p.ex. {{model}})", + "Set reranking model (e.g. {{model}})": "Establir el model de reavaluació (p. ex. {{model}})", "Set Sampler": "Establir el mostrejador", "Set Scheduler": "Establir el programador", "Set Steps": "Establir el nombre de passos", @@ -1349,8 +1349,8 @@ "Utilize": "Utilitzar", "Valid time units:": "Unitats de temps vàlides:", "Valves": "Valves", - "Valves updated": "Valves actualitzat", - "Valves updated successfully": "Valves actualitat correctament", + "Valves updated": "Valves actualitzades", + "Valves updated successfully": "Valves actualitzades correctament", "variable": "variable", "variable to have them replaced with clipboard content.": "variable per tenir-les reemplaçades amb el contingut del porta-retalls.", "Verify Connection": "Verificar la connexió", @@ -1408,7 +1408,7 @@ "You are currently using a trial license. Please contact support to upgrade your license.": "Actualment esteu utilitzant una llicència de prova. Poseu-vos en contacte amb el servei d'assistència per actualitzar la vostra llicència.", "You can only chat with a maximum of {{maxCount}} file(s) at a time.": "Només pots xatejar amb un màxim de {{maxCount}} fitxers alhora.", "You can personalize your interactions with LLMs by adding memories through the 'Manage' button below, making them more helpful and tailored to you.": "Pots personalitzar les teves interaccions amb els models de llenguatge afegint memòries mitjançant el botó 'Gestiona' que hi ha a continuació, fent-les més útils i adaptades a tu.", - "You cannot upload an empty file.": "No es pot pujar un ariux buit.", + "You cannot upload an empty file.": "No es pot pujar un arxiu buit.", "You do not have permission to upload files.": "No tens permisos per pujar arxius.", "You have no archived conversations.": "No tens converses arxivades.", "You have shared this chat": "Has compartit aquest xat", From 74ae9ab8973da1ae21768841293464ae6d435959 Mon Sep 17 00:00:00 2001 From: Doris Lam Date: Mon, 23 Jun 2025 18:43:33 -0700 Subject: [PATCH 006/116] fix opensearch race condition, use keyword search instead of full text search in filter query --- backend/open_webui/retrieval/vector/dbs/opensearch.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/backend/open_webui/retrieval/vector/dbs/opensearch.py b/backend/open_webui/retrieval/vector/dbs/opensearch.py index 60ef2d906c..7e16df3cfb 100644 --- a/backend/open_webui/retrieval/vector/dbs/opensearch.py +++ b/backend/open_webui/retrieval/vector/dbs/opensearch.py @@ -157,10 +157,10 @@ class OpenSearchClient(VectorDBBase): for field, value in filter.items(): query_body["query"]["bool"]["filter"].append( - {"match": {"metadata." + str(field): value}} + {"term": {"metadata." + str(field) + ".keyword": value}} ) - size = limit if limit else 10 + size = limit if limit else 10000 try: result = self.client.search( @@ -206,6 +206,7 @@ class OpenSearchClient(VectorDBBase): for item in batch ] bulk(self.client, actions) + self.client.indices.refresh(self._get_index_name(collection_name)) def upsert(self, collection_name: str, items: list[VectorItem]): self._create_index_if_not_exists( @@ -228,6 +229,7 @@ class OpenSearchClient(VectorDBBase): for item in batch ] bulk(self.client, actions) + self.client.indices.refresh(self._get_index_name(collection_name)) def delete( self, @@ -251,11 +253,12 @@ class OpenSearchClient(VectorDBBase): } for field, value in filter.items(): query_body["query"]["bool"]["filter"].append( - {"match": {"metadata." + str(field): value}} + {"term": {"metadata." + str(field) + ".keyword": value}} ) self.client.delete_by_query( index=self._get_index_name(collection_name), body=query_body ) + self.client.indices.refresh(self._get_index_name(collection_name)) def reset(self): indices = self.client.indices.get(index=f"{self.index_prefix}_*") From 559c25fa3c708df7ed46f5dd12aa574c04e9d5b8 Mon Sep 17 00:00:00 2001 From: Bo Bendtsen Date: Tue, 24 Jun 2025 12:45:48 +0200 Subject: [PATCH 007/116] Updated da-DK translation --- src/lib/i18n/locales/da-DK/translation.json | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/lib/i18n/locales/da-DK/translation.json b/src/lib/i18n/locales/da-DK/translation.json index 3856d4052e..7ab09e7096 100644 --- a/src/lib/i18n/locales/da-DK/translation.json +++ b/src/lib/i18n/locales/da-DK/translation.json @@ -858,14 +858,14 @@ "Native": "", "New Chat": "Ny chat", "New Folder": "Ny mappe", - "New Function": "", + "New Function": "Ny funktion", "New Note": "Ny note", "New Password": "Ny adgangskode", - "New Tool": "", + "New Tool": "Nyt værktøj", "new-channel": "", "Next message": "Næste besked", - "No chats found for this user.": "", - "No chats found.": "", + "No chats found for this user.": "Ingen besked-tråde fundet for denne bruger.", + "No chats found.": "Ingen besked-tråde fundet.", "No content": "Intet indhold", "No content found": "Intet indhold fundet", "No content found in file.": "Intet indhold fundet i fil.", @@ -900,7 +900,7 @@ "OAuth ID": "OAuth-ID", "October": "Oktober", "Off": "Fra", - "Okay, Let's Go!": "Okay, lad os gå!", + "Okay, Let's Go!": "Okay, lad os komme i gang!", "OLED Dark": "OLED Mørk", "Ollama": "Ollama", "Ollama API": "Ollama API", @@ -1258,7 +1258,7 @@ "Title (e.g. Tell me a fun fact)": "Titel (f.eks. Fortæl mig en sjov kendsgerning)", "Title Auto-Generation": "Automatisk titelgenerering", "Title cannot be an empty string.": "Titel kan ikke være en tom streng.", - "Title Generation": "Titel Generation", + "Title Generation": "Titel-generation", "Title Generation Prompt": "Prompt til titelgenerering", "TLS": "TLS", "To access the available model names for downloading,": "For at få adgang til de tilgængelige modelnavne til download,", @@ -1331,7 +1331,7 @@ "Upload Progress": "Uploadfremdrift", "URL": "URL", "URL Mode": "URL-tilstand", - "Usage": "", + "Usage": "Forbrug", "Use '#' in the prompt input to load and include your knowledge.": "Brug '#' i promptinput for at indlæse og inkludere din viden.", "Use Gravatar": "Brug Gravatar", "Use groups to group your users and assign permissions.": "Brug grupper til at gruppere dine brugere og tildele rettigheder.", @@ -1340,7 +1340,7 @@ "Use no proxy to fetch page contents.": "Brug ingen proxy til at hente sideindhold.", "Use proxy designated by http_proxy and https_proxy environment variables to fetch page contents.": "", "user": "bruger", - "User": "Bruger:", + "User": "Bruger", "User location successfully retrieved.": "Brugerplacering hentet.", "User Webhooks": "Bruger Webhooks", "Username": "Brugernavn", From c77c4ff3f76d558868308244359037eb72aedb5c Mon Sep 17 00:00:00 2001 From: Taehong Gu Date: Wed, 25 Jun 2025 00:11:06 +0900 Subject: [PATCH 008/116] build: add docker-compose.otel.yaml for OpenTelemetry --- docker-compose.otel.yaml | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 docker-compose.otel.yaml diff --git a/docker-compose.otel.yaml b/docker-compose.otel.yaml new file mode 100644 index 0000000000..436a54cbe3 --- /dev/null +++ b/docker-compose.otel.yaml @@ -0,0 +1,24 @@ +services: + grafana: + image: grafana/otel-lgtm:latest + container_name: lgtm + ports: + - "3000:3000" # Grafana UI + - "4317:4317" # OTLP/gRPC + - "4318:4318" # OTLP/HTTP + restart: unless-stopped + + open-webui: + image: ghcr.io/open-webui/open-webui:main + container_name: open-webui + depends_on: [grafana] + environment: + - ENABLE_OTEL=true + - OTEL_EXPORTER_OTLP_ENDPOINT=http://grafana:4317 + - OTEL_SERVICE_NAME=open-webui + ports: + - "8088:8080" + networks: [default] + +networks: + default: From 1875d7d7261e6cbe6343c947555fcc8b1c4a52df Mon Sep 17 00:00:00 2001 From: c0mplex10 Date: Wed, 25 Jun 2025 01:40:23 +0300 Subject: [PATCH 009/116] i18n: Improve Turkish translations --- src/lib/i18n/locales/tr-TR/translation.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/lib/i18n/locales/tr-TR/translation.json b/src/lib/i18n/locales/tr-TR/translation.json index df4a19364b..77722af2ac 100644 --- a/src/lib/i18n/locales/tr-TR/translation.json +++ b/src/lib/i18n/locales/tr-TR/translation.json @@ -16,7 +16,7 @@ "A task model is used when performing tasks such as generating titles for chats and web search queries": "Bir görev modeli, sohbetler ve web arama sorguları için başlık oluşturma gibi görevleri yerine getirirken kullanılır", "a user": "bir kullanıcı", "About": "Hakkında", - "Accept autocomplete generation / Jump to prompt variable": "", + "Accept autocomplete generation / Jump to prompt variable": "Otomatik tamamlama üretimini kabul et / İstem değişkenine atla", "Access": "Erişim", "Access Control": "Erişim Kontrolü", "Accessible to all users": "Tüm kullanıcılara erişilebilir", @@ -632,7 +632,7 @@ "Forwards system user session credentials to authenticate": "", "Full Context Mode": "", "Function": "Fonksiyon", - "Function Calling": "", + "Function Calling": "Fonksiyon Çağırma", "Function created successfully": "Fonksiyon başarıyla oluşturuldu", "Function deleted successfully": "Fonksiyon başarıyla silindi", "Function Description": "Fonksiyon Açıklaması", @@ -1177,7 +1177,7 @@ "Stop": "Durdur", "Stop Generating": "", "Stop Sequence": "Diziyi Durdur", - "Stream Chat Response": "SAkış Sohbet Yanıtı", + "Stream Chat Response": "Akış Sohbet Yanıtı", "Strip Existing OCR": "", "Strip existing OCR text from the PDF and re-run OCR. Ignored if Force OCR is enabled. Defaults to False.": "", "STT Model": "STT Modeli", @@ -1386,7 +1386,7 @@ "What are you trying to achieve?": "Ne yapmaya çalışıyorsunuz?", "What are you working on?": "Üzerinde çalıştığınız nedir?", "What's New in": "Yenilikler:", - "When enabled, the model will respond to each chat message in real-time, generating a response as soon as the user sends a message. This mode is useful for live chat applications, but may impact performance on slower hardware.": "", + "When enabled, the model will respond to each chat message in real-time, generating a response as soon as the user sends a message. This mode is useful for live chat applications, but may impact performance on slower hardware.": "Etkinleştirildiğinde, model her sohbet mesajına gerçek zamanlı olarak yanıt verecek ve kullanıcı bir mesaj gönderdiği anda bir yanıt üretecektir. Bu mod canlı sohbet uygulamaları için yararlıdır, ancak daha yavaş donanımlarda performansı etkileyebilir.", "wherever you are": "nerede olursanız olun", "Whether to paginate the output. Each page will be separated by a horizontal rule and page number. Defaults to False.": "", "Whisper (Local)": "Whisper (Yerel)", From ac5567f78d4691df472bf783df7022ccc0e30f51 Mon Sep 17 00:00:00 2001 From: zhangtyzzz <61079484+zhangtyzzz@users.noreply.github.com> Date: Wed, 25 Jun 2025 09:11:58 +0800 Subject: [PATCH 010/116] Update brave.py to use the correct field fixing issues caused by incorrect field names. --- backend/open_webui/retrieval/web/brave.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/open_webui/retrieval/web/brave.py b/backend/open_webui/retrieval/web/brave.py index 3075db990f..e732d07a55 100644 --- a/backend/open_webui/retrieval/web/brave.py +++ b/backend/open_webui/retrieval/web/brave.py @@ -36,7 +36,7 @@ def search_brave( return [ SearchResult( - link=result["url"], title=result.get("title"), snippet=result.get("snippet") + link=result["url"], title=result.get("title"), snippet=result.get("description") ) for result in results[:count] ] From 45d7726ee0a8b85ececd87adaadc614253d1bef9 Mon Sep 17 00:00:00 2001 From: Zachar Hankewycz Date: Tue, 24 Jun 2025 21:24:53 -0400 Subject: [PATCH 011/116] Restore exa --- backend/open_webui/routers/retrieval.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/backend/open_webui/routers/retrieval.py b/backend/open_webui/routers/retrieval.py index ee6f99fbb5..3037da8420 100644 --- a/backend/open_webui/routers/retrieval.py +++ b/backend/open_webui/routers/retrieval.py @@ -1784,6 +1784,13 @@ def search_web(request: Request, engine: str, query: str) -> list[SearchResult]: request.app.state.config.WEB_SEARCH_RESULT_COUNT, request.app.state.config.WEB_SEARCH_DOMAIN_FILTER_LIST, ) + elif engine == "exa": + return search_exa( + request.app.state.config.EXA_API_KEY, + query, + request.app.state.config.WEB_SEARCH_RESULT_COUNT, + request.app.state.config.WEB_SEARCH_DOMAIN_FILTER_LIST, + ) elif engine == "perplexity": return search_perplexity( request.app.state.config.PERPLEXITY_API_KEY, From 1f123eb100a4424726e044d35aca7a8a5659c39f Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Wed, 25 Jun 2025 12:20:08 +0400 Subject: [PATCH 012/116] refac --- backend/open_webui/retrieval/utils.py | 55 +++++++++++++++----------- backend/open_webui/utils/middleware.py | 24 ++++++----- 2 files changed, 46 insertions(+), 33 deletions(-) diff --git a/backend/open_webui/retrieval/utils.py b/backend/open_webui/retrieval/utils.py index 00dd683063..0a0f0dabab 100644 --- a/backend/open_webui/retrieval/utils.py +++ b/backend/open_webui/retrieval/utils.py @@ -460,20 +460,19 @@ def get_sources_from_files( ) extracted_collections = [] - relevant_contexts = [] + query_results = [] for file in files: - - context = None + query_result = None if file.get("docs"): # BYPASS_WEB_SEARCH_EMBEDDING_AND_RETRIEVAL - context = { + query_result = { "documents": [[doc.get("content") for doc in file.get("docs")]], "metadatas": [[doc.get("metadata") for doc in file.get("docs")]], } elif file.get("context") == "full": # Manual Full Mode Toggle - context = { + query_result = { "documents": [[file.get("file").get("data", {}).get("content")]], "metadatas": [[{"file_id": file.get("id"), "name": file.get("name")}]], } @@ -500,7 +499,7 @@ def get_sources_from_files( } ) - context = { + query_result = { "documents": [documents], "metadatas": [metadatas], } @@ -508,7 +507,7 @@ def get_sources_from_files( elif file.get("id"): file_object = Files.get_file_by_id(file.get("id")) if file_object: - context = { + query_result = { "documents": [[file_object.data.get("content", "")]], "metadatas": [ [ @@ -521,7 +520,7 @@ def get_sources_from_files( ], } elif file.get("file").get("data"): - context = { + query_result = { "documents": [[file.get("file").get("data", {}).get("content")]], "metadatas": [ [file.get("file").get("data", {}).get("metadata", {})] @@ -549,19 +548,27 @@ def get_sources_from_files( if full_context: try: - context = get_all_items_from_collections(collection_names) + query_result = get_all_items_from_collections(collection_names) except Exception as e: log.exception(e) else: try: - context = None + query_result = None if file.get("type") == "text": - context = file["content"] + # Not sure when this is used, but it seems to be a fallback + query_result = { + "documents": [ + [file.get("file").get("data", {}).get("content")] + ], + "metadatas": [ + [file.get("file").get("data", {}).get("meta", {})] + ], + } else: if hybrid_search: try: - context = query_collection_with_hybrid_search( + query_result = query_collection_with_hybrid_search( collection_names=collection_names, queries=queries, embedding_function=embedding_function, @@ -577,8 +584,8 @@ def get_sources_from_files( " non hybrid search as fallback." ) - if (not hybrid_search) or (context is None): - context = query_collection( + if (not hybrid_search) or (query_result is None): + query_result = query_collection( collection_names=collection_names, queries=queries, embedding_function=embedding_function, @@ -589,24 +596,24 @@ def get_sources_from_files( extracted_collections.extend(collection_names) - if context: + if query_result: if "data" in file: del file["data"] - relevant_contexts.append({**context, "file": file}) + query_results.append({**query_result, "file": file}) sources = [] - for context in relevant_contexts: + for query_result in query_results: try: - if "documents" in context: - if "metadatas" in context: + if "documents" in query_result: + if "metadatas" in query_result: source = { - "source": context["file"], - "document": context["documents"][0], - "metadata": context["metadatas"][0], + "source": query_result["file"], + "document": query_result["documents"][0], + "metadata": query_result["metadatas"][0], } - if "distances" in context and context["distances"]: - source["distances"] = context["distances"][0] + if "distances" in query_result and query_result["distances"]: + source["distances"] = query_result["distances"][0] sources.append(source) except Exception as e: diff --git a/backend/open_webui/utils/middleware.py b/backend/open_webui/utils/middleware.py index 6bad97b1f4..45b8dd5f89 100644 --- a/backend/open_webui/utils/middleware.py +++ b/backend/open_webui/utils/middleware.py @@ -718,6 +718,10 @@ def apply_params_to_form_data(form_data, model): async def process_chat_payload(request, form_data, user, metadata, model): + # Pipeline Inlet -> Filter Inlet -> Chat Memory -> Chat Web Search -> Chat Image Generation + # -> Chat Code Interpreter (Form Data Update) -> (Default) Chat Tools Function Calling + # -> Chat Files + form_data = apply_params_to_form_data(form_data, model) log.debug(f"form_data: {form_data}") @@ -911,7 +915,6 @@ async def process_chat_payload(request, form_data, user, metadata, model): request, form_data, extra_params, user, models, tools_dict ) sources.extend(flags.get("sources", [])) - except Exception as e: log.exception(e) @@ -924,24 +927,27 @@ async def process_chat_payload(request, form_data, user, metadata, model): # If context is not empty, insert it into the messages if len(sources) > 0: context_string = "" - citation_idx = {} + citation_idx_map = {} + for source in sources: if "document" in source: - for doc_context, doc_meta in zip( + for document_text, document_metadata in zip( source["document"], source["metadata"] ): source_name = source.get("source", {}).get("name", None) - citation_id = ( - doc_meta.get("source", None) + source_id = ( + document_metadata.get("source", None) or source.get("source", {}).get("id", None) or "N/A" ) - if citation_id not in citation_idx: - citation_idx[citation_id] = len(citation_idx) + 1 + + if source_id not in citation_idx_map: + citation_idx_map[source_id] = len(citation_idx_map) + 1 + context_string += ( - f'{doc_context}\n" + + f">{document_text}\n" ) context_string = context_string.strip() From 0ba84a670d362880e6b17e47efe3609fbbe2fbe1 Mon Sep 17 00:00:00 2001 From: Sine Jespersen Date: Wed, 25 Jun 2025 10:34:40 +0200 Subject: [PATCH 013/116] add aria-pressed and aria-label to toggle button to make it accessible --- src/lib/components/workspace/Models/ModelEditor.svelte | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/lib/components/workspace/Models/ModelEditor.svelte b/src/lib/components/workspace/Models/ModelEditor.svelte index b53e5c68c6..dfecdd0df4 100644 --- a/src/lib/components/workspace/Models/ModelEditor.svelte +++ b/src/lib/components/workspace/Models/ModelEditor.svelte @@ -497,6 +497,10 @@ @@ -524,29 +516,7 @@ {#if loading}
- +
{/if} diff --git a/src/lib/components/AddServerModal.svelte b/src/lib/components/AddServerModal.svelte index fa3cab44c3..30f40173ea 100644 --- a/src/lib/components/AddServerModal.svelte +++ b/src/lib/components/AddServerModal.svelte @@ -15,6 +15,8 @@ import { getToolServerData } from '$lib/apis'; import { verifyToolServerConnection } from '$lib/apis/configs'; import AccessControl from './workspace/common/AccessControl.svelte'; + import Spinner from '$lib/components/common/Spinner.svelte'; + import XMark from '$lib/components/icons/XMark.svelte'; export let onSubmit: Function = () => {}; export let onDelete: Function = () => {}; @@ -168,17 +170,7 @@ show = false; }} > - + @@ -389,30 +381,7 @@ {#if loading}
- +
{/if} diff --git a/src/lib/components/ChangelogModal.svelte b/src/lib/components/ChangelogModal.svelte index 21f0f5a06f..67097276e2 100644 --- a/src/lib/components/ChangelogModal.svelte +++ b/src/lib/components/ChangelogModal.svelte @@ -9,6 +9,7 @@ import Modal from './common/Modal.svelte'; import { updateUserSettings } from '$lib/apis/users'; + import XMark from '$lib/components/icons/XMark.svelte'; const i18n = getContext('i18n'); @@ -36,18 +37,9 @@ localStorage.version = $config.version; show = false; }} + aria-label={$i18n.t('Close')} > - -

{$i18n.t('Close')}

- - +
diff --git a/src/lib/components/ImportModal.svelte b/src/lib/components/ImportModal.svelte index e0a64a7a58..3e8eb15f7f 100644 --- a/src/lib/components/ImportModal.svelte +++ b/src/lib/components/ImportModal.svelte @@ -3,7 +3,9 @@ import { getContext, onMount } from 'svelte'; const i18n = getContext('i18n'); + import Spinner from '$lib/components/common/Spinner.svelte'; import Modal from '$lib/components/common/Modal.svelte'; + import XMark from '$lib/components/icons/XMark.svelte'; import { extractFrontmatter } from '$lib/utils'; export let show = false; @@ -69,16 +71,7 @@ show = false; }} > - - - +
@@ -120,29 +113,7 @@ {#if loading}
- +
{/if} diff --git a/src/lib/components/admin/Evaluations/FeedbackModal.svelte b/src/lib/components/admin/Evaluations/FeedbackModal.svelte index f6242327e9..cac64f0e60 100644 --- a/src/lib/components/admin/Evaluations/FeedbackModal.svelte +++ b/src/lib/components/admin/Evaluations/FeedbackModal.svelte @@ -2,6 +2,7 @@ import Modal from '$lib/components/common/Modal.svelte'; import { getContext } from 'svelte'; const i18n = getContext('i18n'); + import XMark from '$lib/components/icons/XMark.svelte'; // Import the icon export let show = false; export let selectedFeedback = null; @@ -22,16 +23,7 @@ {$i18n.t('Feedback Details')} diff --git a/src/lib/components/admin/Evaluations/LeaderboardModal.svelte b/src/lib/components/admin/Evaluations/LeaderboardModal.svelte index f0c1f012a0..f9a9eacb07 100644 --- a/src/lib/components/admin/Evaluations/LeaderboardModal.svelte +++ b/src/lib/components/admin/Evaluations/LeaderboardModal.svelte @@ -6,6 +6,7 @@ export let feedbacks = []; export let onClose: () => void = () => {}; const i18n = getContext('i18n'); + import XMark from '$lib/components/icons/XMark.svelte'; const close = () => { show = false; @@ -37,16 +38,7 @@ {model.name}
diff --git a/src/lib/components/admin/Settings/Audio.svelte b/src/lib/components/admin/Settings/Audio.svelte index da3912a514..9eeeac8ea6 100644 --- a/src/lib/components/admin/Settings/Audio.svelte +++ b/src/lib/components/admin/Settings/Audio.svelte @@ -12,6 +12,7 @@ } from '$lib/apis/audio'; import { config, settings } from '$lib/stores'; + import Spinner from '$lib/components/common/Spinner.svelte'; import SensitiveInput from '$lib/components/common/SensitiveInput.svelte'; import { TTS_RESPONSE_SPLIT } from '$lib/types'; @@ -373,33 +374,7 @@ > {#if STT_WHISPER_MODEL_LOADING}
- - - - - +
{:else} - - - +
diff --git a/src/lib/components/admin/Settings/Documents.svelte b/src/lib/components/admin/Settings/Documents.svelte index e05abf686c..79ed003096 100644 --- a/src/lib/components/admin/Settings/Documents.svelte +++ b/src/lib/components/admin/Settings/Documents.svelte @@ -808,33 +808,7 @@ > {#if updateEmbeddingModelLoading}
- - - - - +
{:else} - - - + @@ -406,29 +399,7 @@ {#if loading}
- +
{/if} diff --git a/src/lib/components/admin/Settings/Images.svelte b/src/lib/components/admin/Settings/Images.svelte index 003b991a0b..b327c6e615 100644 --- a/src/lib/components/admin/Settings/Images.svelte +++ b/src/lib/components/admin/Settings/Images.svelte @@ -13,6 +13,7 @@ updateConfig, verifyConfigUrl } from '$lib/apis/images'; + import Spinner from '$lib/components/common/Spinner.svelte'; import SensitiveInput from '$lib/components/common/SensitiveInput.svelte'; import Switch from '$lib/components/common/Switch.svelte'; import Tooltip from '$lib/components/common/Tooltip.svelte'; @@ -711,29 +712,7 @@ {#if loading}
- +
{/if} diff --git a/src/lib/components/admin/Settings/Interface/Banners.svelte b/src/lib/components/admin/Settings/Interface/Banners.svelte index 00d422cedf..b22e940f0d 100644 --- a/src/lib/components/admin/Settings/Interface/Banners.svelte +++ b/src/lib/components/admin/Settings/Interface/Banners.svelte @@ -2,6 +2,7 @@ import Switch from '$lib/components/common/Switch.svelte'; import Tooltip from '$lib/components/common/Tooltip.svelte'; import EllipsisVertical from '$lib/components/icons/EllipsisVertical.svelte'; + import XMark from '$lib/components/icons/XMark.svelte'; import Sortable from 'sortablejs'; import { getContext } from 'svelte'; const i18n = getContext('i18n'); @@ -85,16 +86,7 @@ banners = banners; }} > - - - + {/each} diff --git a/src/lib/components/admin/Settings/Models/ConfigureModelsModal.svelte b/src/lib/components/admin/Settings/Models/ConfigureModelsModal.svelte index 7bc0e8abdd..e81c74907b 100644 --- a/src/lib/components/admin/Settings/Models/ConfigureModelsModal.svelte +++ b/src/lib/components/admin/Settings/Models/ConfigureModelsModal.svelte @@ -18,6 +18,7 @@ import Plus from '$lib/components/icons/Plus.svelte'; import ChevronUp from '$lib/components/icons/ChevronUp.svelte'; import ChevronDown from '$lib/components/icons/ChevronDown.svelte'; + import XMark from '$lib/components/icons/XMark.svelte'; export let show = false; export let initHandler = () => {}; @@ -129,16 +130,7 @@ show = false; }} > - - - + @@ -278,29 +270,7 @@ {#if loading}
- +
{/if} diff --git a/src/lib/components/admin/Settings/Models/ManageModelsModal.svelte b/src/lib/components/admin/Settings/Models/ManageModelsModal.svelte index 117009ddaa..1915778b42 100644 --- a/src/lib/components/admin/Settings/Models/ManageModelsModal.svelte +++ b/src/lib/components/admin/Settings/Models/ManageModelsModal.svelte @@ -7,6 +7,7 @@ import { user } from '$lib/stores'; + import XMark from '$lib/components/icons/XMark.svelte'; import Modal from '$lib/components/common/Modal.svelte'; import ManageOllama from './Manage/ManageOllama.svelte'; import { getOllamaConfig } from '$lib/apis/ollama'; @@ -48,16 +49,7 @@ show = false; }} > - - - + diff --git a/src/lib/components/admin/Users/Groups/AddGroupModal.svelte b/src/lib/components/admin/Users/Groups/AddGroupModal.svelte index 12cddf3981..50f6a7f5b4 100644 --- a/src/lib/components/admin/Users/Groups/AddGroupModal.svelte +++ b/src/lib/components/admin/Users/Groups/AddGroupModal.svelte @@ -3,8 +3,10 @@ import { getContext, onMount } from 'svelte'; const i18n = getContext('i18n'); + import Spinner from '$lib/components/common/Spinner.svelte'; import Modal from '$lib/components/common/Modal.svelte'; import Textarea from '$lib/components/common/Textarea.svelte'; + import XMark from '$lib/components/icons/XMark.svelte'; export let onSubmit: Function = () => {}; export let show = false; @@ -45,16 +47,7 @@ show = false; }} > - - - + @@ -111,29 +104,7 @@ {#if loading}
- +
{/if} diff --git a/src/lib/components/admin/Users/Groups/EditGroupModal.svelte b/src/lib/components/admin/Users/Groups/EditGroupModal.svelte index 8cc353064c..ab2ba97fc1 100644 --- a/src/lib/components/admin/Users/Groups/EditGroupModal.svelte +++ b/src/lib/components/admin/Users/Groups/EditGroupModal.svelte @@ -3,6 +3,7 @@ import { getContext, onMount } from 'svelte'; const i18n = getContext('i18n'); + import Spinner from '$lib/components/common/Spinner.svelte'; import Modal from '$lib/components/common/Modal.svelte'; import Display from './Display.svelte'; import Permissions from './Permissions.svelte'; @@ -10,6 +11,7 @@ import UserPlusSolid from '$lib/components/icons/UserPlusSolid.svelte'; import WrenchSolid from '$lib/components/icons/WrenchSolid.svelte'; import ConfirmDialog from '$lib/components/common/ConfirmDialog.svelte'; + import XMark from '$lib/components/icons/XMark.svelte'; export let onSubmit: Function = () => {}; export let onDelete: Function = () => {}; @@ -124,16 +126,7 @@ show = false; }} > - - - + @@ -305,29 +298,7 @@ {#if loading}
- +
{/if} diff --git a/src/lib/components/admin/Users/UserList/AddUserModal.svelte b/src/lib/components/admin/Users/UserList/AddUserModal.svelte index cad799eb54..fda50ec296 100644 --- a/src/lib/components/admin/Users/UserList/AddUserModal.svelte +++ b/src/lib/components/admin/Users/UserList/AddUserModal.svelte @@ -6,8 +6,10 @@ import { WEBUI_BASE_URL } from '$lib/constants'; + import Spinner from '$lib/components/common/Spinner.svelte'; import Modal from '$lib/components/common/Modal.svelte'; import { generateInitialsImage } from '$lib/utils'; + import XMark from '$lib/components/icons/XMark.svelte'; const i18n = getContext('i18n'); const dispatch = createEventDispatcher(); @@ -132,16 +134,7 @@ show = false; }} > - - - + @@ -293,29 +286,7 @@ {#if loading}
- +
{/if} diff --git a/src/lib/components/admin/Users/UserList/EditUserModal.svelte b/src/lib/components/admin/Users/UserList/EditUserModal.svelte index 929b6d5070..23ec1b10a9 100644 --- a/src/lib/components/admin/Users/UserList/EditUserModal.svelte +++ b/src/lib/components/admin/Users/UserList/EditUserModal.svelte @@ -8,6 +8,7 @@ import Modal from '$lib/components/common/Modal.svelte'; import localizedFormat from 'dayjs/plugin/localizedFormat'; + import XMark from '$lib/components/icons/XMark.svelte'; const i18n = getContext('i18n'); const dispatch = createEventDispatcher(); @@ -54,16 +55,7 @@ show = false; }} > - - - + diff --git a/src/lib/components/chat/MessageInput/VoiceRecording.svelte b/src/lib/components/chat/MessageInput/VoiceRecording.svelte index 020892bea5..3ae16d9f06 100644 --- a/src/lib/components/chat/MessageInput/VoiceRecording.svelte +++ b/src/lib/components/chat/MessageInput/VoiceRecording.svelte @@ -5,6 +5,7 @@ import { blobToFile, calculateSHA256, extractCurlyBraceWords } from '$lib/utils'; import { transcribeAudio } from '$lib/apis/audio'; + import XMark from '$lib/components/icons/XMark.svelte'; import dayjs from 'dayjs'; import LocalizedFormat from 'dayjs/plugin/localizedFormat'; @@ -406,16 +407,7 @@ onCancel(); }} > - - - + diff --git a/src/lib/components/chat/Messages/CitationsModal.svelte b/src/lib/components/chat/Messages/CitationsModal.svelte index bd21fe1965..09d8fcc873 100644 --- a/src/lib/components/chat/Messages/CitationsModal.svelte +++ b/src/lib/components/chat/Messages/CitationsModal.svelte @@ -4,6 +4,8 @@ import Tooltip from '$lib/components/common/Tooltip.svelte'; import { WEBUI_API_BASE_URL } from '$lib/constants'; + import XMark from '$lib/components/icons/XMark.svelte'; + const i18n = getContext('i18n'); export let show = false; @@ -67,16 +69,7 @@ show = false; }} > - - - + diff --git a/src/lib/components/chat/Messages/CodeExecutionModal.svelte b/src/lib/components/chat/Messages/CodeExecutionModal.svelte index 8d8f81927a..da42227403 100644 --- a/src/lib/components/chat/Messages/CodeExecutionModal.svelte +++ b/src/lib/components/chat/Messages/CodeExecutionModal.svelte @@ -4,6 +4,7 @@ import Modal from '$lib/components/common/Modal.svelte'; import Spinner from '$lib/components/common/Spinner.svelte'; import Badge from '$lib/components/common/Badge.svelte'; + import XMark from '$lib/components/icons/XMark.svelte'; const i18n = getContext('i18n'); export let show = false; @@ -49,16 +50,7 @@ codeExecution = null; }} > - - - + diff --git a/src/lib/components/chat/Messages/RateComment.svelte b/src/lib/components/chat/Messages/RateComment.svelte index 2ffd2d8a2b..b23efadc30 100644 --- a/src/lib/components/chat/Messages/RateComment.svelte +++ b/src/lib/components/chat/Messages/RateComment.svelte @@ -4,6 +4,7 @@ import { createEventDispatcher, onMount, getContext } from 'svelte'; import { config, models } from '$lib/stores'; import Tags from '$lib/components/common/Tags.svelte'; + import XMark from '$lib/components/icons/XMark.svelte'; const i18n = getContext('i18n'); @@ -123,16 +124,7 @@ show = false; }} > - - - + diff --git a/src/lib/components/chat/ModelSelector/Selector.svelte b/src/lib/components/chat/ModelSelector/Selector.svelte index d586553ac2..98e039e9e4 100644 --- a/src/lib/components/chat/ModelSelector/Selector.svelte +++ b/src/lib/components/chat/ModelSelector/Selector.svelte @@ -7,6 +7,7 @@ import relativeTime from 'dayjs/plugin/relativeTime'; dayjs.extend(relativeTime); + import Spinner from '$lib/components/common/Spinner.svelte'; import { flyAndScale } from '$lib/utils/transitions'; import { createEventDispatcher, onMount, getContext, tick } from 'svelte'; import { goto } from '$app/navigation'; @@ -550,29 +551,7 @@ >
- +
diff --git a/src/lib/components/chat/Settings/Personalization/AddMemoryModal.svelte b/src/lib/components/chat/Settings/Personalization/AddMemoryModal.svelte index a5bdb4bfca..205e8243fe 100644 --- a/src/lib/components/chat/Settings/Personalization/AddMemoryModal.svelte +++ b/src/lib/components/chat/Settings/Personalization/AddMemoryModal.svelte @@ -4,6 +4,8 @@ import Modal from '$lib/components/common/Modal.svelte'; import { addNewMemory, updateMemoryById } from '$lib/apis/memories'; import { toast } from 'svelte-sonner'; + import Spinner from '$lib/components/common/Spinner.svelte'; + import XMark from '$lib/components/icons/XMark.svelte'; const dispatch = createEventDispatcher(); @@ -46,16 +48,7 @@ show = false; }} > - - - +
@@ -93,29 +86,7 @@ {#if loading}
- +
{/if} diff --git a/src/lib/components/chat/Settings/Personalization/EditMemoryModal.svelte b/src/lib/components/chat/Settings/Personalization/EditMemoryModal.svelte index f9ab87efe6..cca3d9b467 100644 --- a/src/lib/components/chat/Settings/Personalization/EditMemoryModal.svelte +++ b/src/lib/components/chat/Settings/Personalization/EditMemoryModal.svelte @@ -4,7 +4,9 @@ import { updateMemoryById } from '$lib/apis/memories'; + import Spinner from '$lib/components/common/Spinner.svelte'; import Modal from '$lib/components/common/Modal.svelte'; + import XMark from '$lib/components/icons/XMark.svelte'; const dispatch = createEventDispatcher(); @@ -56,16 +58,7 @@ show = false; }} > - - - +
@@ -103,29 +96,7 @@ {#if loading}
- +
{/if} diff --git a/src/lib/components/chat/ShareChatModal.svelte b/src/lib/components/chat/ShareChatModal.svelte index f3f6213248..382d81ba77 100644 --- a/src/lib/components/chat/ShareChatModal.svelte +++ b/src/lib/components/chat/ShareChatModal.svelte @@ -8,6 +8,7 @@ import Modal from '../common/Modal.svelte'; import Link from '../icons/Link.svelte'; + import XMark from '$lib/components/icons/XMark.svelte'; export let chatId; @@ -90,16 +91,7 @@ show = false; }} > - - - + diff --git a/src/lib/components/chat/ShortcutsModal.svelte b/src/lib/components/chat/ShortcutsModal.svelte index b3409c902a..1879d5ab3d 100644 --- a/src/lib/components/chat/ShortcutsModal.svelte +++ b/src/lib/components/chat/ShortcutsModal.svelte @@ -4,6 +4,7 @@ import Tooltip from '../common/Tooltip.svelte'; const i18n = getContext('i18n'); + import XMark from '$lib/components/icons/XMark.svelte'; export let show = false; @@ -18,16 +19,7 @@ show = false; }} > - - - + diff --git a/src/lib/components/chat/ToolServersModal.svelte b/src/lib/components/chat/ToolServersModal.svelte index 24a86e48b8..c93390fda1 100644 --- a/src/lib/components/chat/ToolServersModal.svelte +++ b/src/lib/components/chat/ToolServersModal.svelte @@ -9,6 +9,7 @@ import Modal from '../common/Modal.svelte'; import Link from '../icons/Link.svelte'; import Collapsible from '../common/Collapsible.svelte'; + import XMark from '$lib/components/icons/XMark.svelte'; export let show = false; export let selectedToolIds = []; @@ -30,16 +31,7 @@ show = false; }} > - - - + diff --git a/src/lib/components/common/FileItem.svelte b/src/lib/components/common/FileItem.svelte index 75befff736..5134f1309f 100644 --- a/src/lib/components/common/FileItem.svelte +++ b/src/lib/components/common/FileItem.svelte @@ -6,6 +6,7 @@ import GarbageBin from '../icons/GarbageBin.svelte'; import Spinner from './Spinner.svelte'; import Tooltip from './Tooltip.svelte'; + import XMark from '$lib/components/icons/XMark.svelte'; const i18n = getContext('i18n'); const dispatch = createEventDispatcher(); @@ -133,16 +134,7 @@ dispatch('dismiss'); }} > - - - +