From 35f3824932833fe77ef3bce54b86803cda4838a6 Mon Sep 17 00:00:00 2001 From: Mazurek Michal Date: Fri, 7 Feb 2025 13:44:47 +0100 Subject: [PATCH 01/27] feat: Implement Document Intelligence as Content Extraction Engine --- backend/open_webui/config.py | 12 +++++++ backend/open_webui/main.py | 4 +++ backend/open_webui/retrieval/loaders/main.py | 22 ++++++++++++ backend/open_webui/routers/retrieval.py | 27 +++++++++++++- backend/requirements.txt | 1 + pyproject.toml | 1 + src/lib/apis/retrieval/index.ts | 6 ++++ .../admin/Settings/Documents.svelte | 36 ++++++++++++++++++- 8 files changed, 107 insertions(+), 2 deletions(-) diff --git a/backend/open_webui/config.py b/backend/open_webui/config.py index bf6f1d025..e46a87cd5 100644 --- a/backend/open_webui/config.py +++ b/backend/open_webui/config.py @@ -1431,6 +1431,18 @@ TIKA_SERVER_URL = PersistentConfig( os.getenv("TIKA_SERVER_URL", "http://tika:9998"), # Default for sidecar deployment ) +DOCUMENT_INTELLIGENCE_ENDPOINT = PersistentConfig( + "DOCUMENT_INTELLIGENCE_ENDPOINT", + "rag.document_intelligence_endpoint", + os.getenv("DOCUMENT_INTELLIGENCE_ENDPOINT", ""), +) + +DOCUMENT_INTELLIGENCE_KEY = PersistentConfig( + "DOCUMENT_INTELLIGENCE_KEY", + "rag.document_intelligence_key", + os.getenv("DOCUMENT_INTELLIGENCE_KEY", ""), +) + RAG_TOP_K = PersistentConfig( "RAG_TOP_K", "rag.top_k", int(os.environ.get("RAG_TOP_K", "3")) ) diff --git a/backend/open_webui/main.py b/backend/open_webui/main.py index 863f58dea..2f1b92b1d 100644 --- a/backend/open_webui/main.py +++ b/backend/open_webui/main.py @@ -154,6 +154,8 @@ from open_webui.config import ( CHUNK_SIZE, CONTENT_EXTRACTION_ENGINE, TIKA_SERVER_URL, + DOCUMENT_INTELLIGENCE_ENDPOINT, + DOCUMENT_INTELLIGENCE_KEY, RAG_TOP_K, RAG_TEXT_SPLITTER, TIKTOKEN_ENCODING_NAME, @@ -478,6 +480,8 @@ app.state.config.ENABLE_RAG_WEB_LOADER_SSL_VERIFICATION = ( app.state.config.CONTENT_EXTRACTION_ENGINE = CONTENT_EXTRACTION_ENGINE app.state.config.TIKA_SERVER_URL = TIKA_SERVER_URL +app.state.config.DOCUMENT_INTELLIGENCE_ENDPOINT = DOCUMENT_INTELLIGENCE_ENDPOINT +app.state.config.DOCUMENT_INTELLIGENCE_KEY = DOCUMENT_INTELLIGENCE_KEY app.state.config.TEXT_SPLITTER = RAG_TEXT_SPLITTER app.state.config.TIKTOKEN_ENCODING_NAME = TIKTOKEN_ENCODING_NAME diff --git a/backend/open_webui/retrieval/loaders/main.py b/backend/open_webui/retrieval/loaders/main.py index a9372f65a..19d590f5c 100644 --- a/backend/open_webui/retrieval/loaders/main.py +++ b/backend/open_webui/retrieval/loaders/main.py @@ -4,6 +4,7 @@ import ftfy import sys from langchain_community.document_loaders import ( + AzureAIDocumentIntelligenceLoader, BSHTMLLoader, CSVLoader, Docx2txtLoader, @@ -147,6 +148,27 @@ class Loader: file_path=file_path, mime_type=file_content_type, ) + elif ( + self.engine == "document_intelligence" + and self.kwargs.get("DOCUMENT_INTELLIGENCE_ENDPOINT") != "" + and self.kwargs.get("DOCUMENT_INTELLIGENCE_KEY") != "" + and ( + file_ext in ["pdf", "xls", "xlsx", "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", + ] + ) + ): + loader = AzureAIDocumentIntelligenceLoader( + file_path=file_path, + api_endpoint=self.kwargs.get("DOCUMENT_INTELLIGENCE_ENDPOINT"), + api_key=self.kwargs.get("DOCUMENT_INTELLIGENCE_KEY"), + ) else: if file_ext == "pdf": loader = PyPDFLoader( diff --git a/backend/open_webui/routers/retrieval.py b/backend/open_webui/routers/retrieval.py index 77f04a4be..4cfcd490d 100644 --- a/backend/open_webui/routers/retrieval.py +++ b/backend/open_webui/routers/retrieval.py @@ -352,6 +352,10 @@ async def get_rag_config(request: Request, user=Depends(get_admin_user)): "content_extraction": { "engine": request.app.state.config.CONTENT_EXTRACTION_ENGINE, "tika_server_url": request.app.state.config.TIKA_SERVER_URL, + "document_intelligence_config": { + "endpoint": request.app.state.config.DOCUMENT_INTELLIGENCE_ENDPOINT, + "key": request.app.state.config.DOCUMENT_INTELLIGENCE_KEY, + }, }, "chunk": { "text_splitter": request.app.state.config.TEXT_SPLITTER, @@ -402,9 +406,15 @@ class FileConfig(BaseModel): max_count: Optional[int] = None +class DocumentIntelligenceConfigForm(BaseModel): + endpoint: str + key: str + + class ContentExtractionConfig(BaseModel): engine: str = "" tika_server_url: Optional[str] = None + document_intelligence_config: Optional[DocumentIntelligenceConfigForm] = None class ChunkParamUpdateForm(BaseModel): @@ -479,13 +489,22 @@ async def update_rag_config( request.app.state.config.FILE_MAX_COUNT = form_data.file.max_count if form_data.content_extraction is not None: - log.info(f"Updating text settings: {form_data.content_extraction}") + log.info( + f"Updating content extraction: {request.app.state.config.CONTENT_EXTRACTION_ENGINE} to {form_data.content_extraction.engine}" + ) request.app.state.config.CONTENT_EXTRACTION_ENGINE = ( form_data.content_extraction.engine ) request.app.state.config.TIKA_SERVER_URL = ( form_data.content_extraction.tika_server_url ) + if form_data.content_extraction.document_intelligence_config is not None: + request.app.state.config.DOCUMENT_INTELLIGENCE_ENDPOINT = ( + form_data.content_extraction.document_intelligence_config.endpoint + ) + request.app.state.config.DOCUMENT_INTELLIGENCE_KEY = ( + form_data.content_extraction.document_intelligence_config.key + ) if form_data.chunk is not None: request.app.state.config.TEXT_SPLITTER = form_data.chunk.text_splitter @@ -564,6 +583,10 @@ async def update_rag_config( "content_extraction": { "engine": request.app.state.config.CONTENT_EXTRACTION_ENGINE, "tika_server_url": request.app.state.config.TIKA_SERVER_URL, + "document_intelligence_config": { + "endpoint": request.app.state.config.DOCUMENT_INTELLIGENCE_ENDPOINT, + "key": request.app.state.config.DOCUMENT_INTELLIGENCE_KEY, + }, }, "chunk": { "text_splitter": request.app.state.config.TEXT_SPLITTER, @@ -887,6 +910,8 @@ def process_file( engine=request.app.state.config.CONTENT_EXTRACTION_ENGINE, TIKA_SERVER_URL=request.app.state.config.TIKA_SERVER_URL, PDF_EXTRACT_IMAGES=request.app.state.config.PDF_EXTRACT_IMAGES, + DOCUMENT_INTELLIGENCE_ENDPOINT=request.app.state.config.DOCUMENT_INTELLIGENCE_ENDPOINT, + DOCUMENT_INTELLIGENCE_KEY=request.app.state.config.DOCUMENT_INTELLIGENCE_KEY, ) docs = loader.load( file.filename, file.meta.get("content_type"), file_path diff --git a/backend/requirements.txt b/backend/requirements.txt index 14ad4b9cd..4a39e77b5 100644 --- a/backend/requirements.txt +++ b/backend/requirements.txt @@ -72,6 +72,7 @@ validators==0.34.0 psutil sentencepiece soundfile==0.13.1 +azure-ai-documentintelligence==1.0.0 opencv-python-headless==4.11.0.86 rapidocr-onnxruntime==1.3.24 diff --git a/pyproject.toml b/pyproject.toml index f121089e8..60d54afd6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -77,6 +77,7 @@ dependencies = [ "psutil", "sentencepiece", "soundfile==0.13.1", + "azure-ai-documentintelligence==1.0.0", "opencv-python-headless==4.11.0.86", "rapidocr-onnxruntime==1.3.24", diff --git a/src/lib/apis/retrieval/index.ts b/src/lib/apis/retrieval/index.ts index c35c37847..ed07ab5d0 100644 --- a/src/lib/apis/retrieval/index.ts +++ b/src/lib/apis/retrieval/index.ts @@ -32,9 +32,15 @@ type ChunkConfigForm = { chunk_overlap: number; }; +type DocumentIntelligenceConfigForm = { + key: string; + endpoint: string; +}; + type ContentExtractConfigForm = { engine: string; tika_server_url: string | null; + document_intelligence_config: DocumentIntelligenceConfigForm | null; }; type YoutubeConfigForm = { diff --git a/src/lib/components/admin/Settings/Documents.svelte b/src/lib/components/admin/Settings/Documents.svelte index d3b7cfa01..e624a51b3 100644 --- a/src/lib/components/admin/Settings/Documents.svelte +++ b/src/lib/components/admin/Settings/Documents.svelte @@ -50,6 +50,9 @@ let contentExtractionEngine = 'default'; let tikaServerUrl = ''; let showTikaServerUrl = false; + let documentIntelligenceEndpoint = ''; + let documentIntelligenceKey = ''; + let showDocumentIntelligenceConfig = false; let textSplitter = ''; let chunkSize = 0; @@ -175,6 +178,13 @@ toast.error($i18n.t('Tika Server URL required.')); return; } + if ( + contentExtractionEngine === 'document_intelligence' && + (documentIntelligenceEndpoint === '' || documentIntelligenceKey === '') + ) { + toast.error($i18n.t('Document Intelligence endpoint and key required.')); + return; + } const res = await updateRAGConfig(localStorage.token, { pdf_extract_images: pdfExtractImages, enable_google_drive_integration: enableGoogleDriveIntegration, @@ -189,7 +199,11 @@ }, content_extraction: { engine: contentExtractionEngine, - tika_server_url: tikaServerUrl + tika_server_url: tikaServerUrl, + document_intelligence_config: { + key: documentIntelligenceKey, + endpoint: documentIntelligenceEndpoint + } } }); @@ -245,6 +259,9 @@ contentExtractionEngine = res.content_extraction.engine; tikaServerUrl = res.content_extraction.tika_server_url; showTikaServerUrl = contentExtractionEngine === 'tika'; + documentIntelligenceEndpoint = res.content_extraction.document_intelligence_config.endpoint; + documentIntelligenceKey = res.content_extraction.document_intelligence_config.key; + showDocumentIntelligenceConfig = contentExtractionEngine === 'document_intelligence'; fileMaxSize = res?.file.max_size ?? ''; fileMaxCount = res?.file.max_count ?? ''; @@ -568,10 +585,12 @@ bind:value={contentExtractionEngine} on:change={(e) => { showTikaServerUrl = e.target.value === 'tika'; + showDocumentIntelligenceConfig = e.target.value === 'document_intelligence'; }} > + @@ -587,6 +606,21 @@ {/if} + + {#if showDocumentIntelligenceConfig} +
+ + + +
+ {/if}
From 08df5f6d7cc43d478d96fd00c801db773e0ebe3d Mon Sep 17 00:00:00 2001 From: Aleix Dorca Date: Thu, 20 Feb 2025 20:24:58 +0100 Subject: [PATCH 02/27] Update catalan translation.json --- src/lib/i18n/locales/ca-ES/translation.json | 100 ++++++++++---------- 1 file changed, 50 insertions(+), 50 deletions(-) diff --git a/src/lib/i18n/locales/ca-ES/translation.json b/src/lib/i18n/locales/ca-ES/translation.json index e99cbbce4..963fdcd60 100644 --- a/src/lib/i18n/locales/ca-ES/translation.json +++ b/src/lib/i18n/locales/ca-ES/translation.json @@ -20,7 +20,7 @@ "Account Activation Pending": "Activació del compte pendent", "Accurate information": "Informació precisa", "Actions": "Accions", - "Activate": "", + "Activate": "Activar", "Activate this command by typing \"/{{COMMAND}}\" to chat input.": "Activa aquest comanda escrivint \"{{COMMAND}}\" en el xat", "Active Users": "Usuaris actius", "Add": "Afegir", @@ -100,7 +100,7 @@ "Audio": "Àudio", "August": "Agost", "Authenticate": "Autenticar", - "Authentication": "", + "Authentication": "Autenticació", "Auto-Copy Response to Clipboard": "Copiar la resposta automàticament al porta-retalls", "Auto-playback response": "Reproduir la resposta automàticament", "Autocomplete Generation": "Generació automàtica", @@ -124,11 +124,11 @@ "Beta": "Beta", "Bing Search V7 Endpoint": "Punt de connexió a Bing Search V7", "Bing Search V7 Subscription Key": "Clau de subscripció a Bing Search V7", - "Bocha Search API Key": "", + "Bocha Search API Key": "Clau API de Bocha Search", "Brave Search API Key": "Clau API de Brave Search", "By {{name}}": "Per {{name}}", "Bypass SSL verification for Websites": "Desactivar la verificació SSL per a l'accés a Internet", - "Calendar": "", + "Calendar": "Calendari", "Call": "Trucada", "Call feature is not supported when using Web STT engine": "La funció de trucada no s'admet quan s'utilitza el motor Web STT", "Camera": "Càmera", @@ -180,13 +180,13 @@ "Clone of {{TITLE}}": "Clon de {{TITLE}}", "Close": "Tancar", "Code execution": "Execució de codi", - "Code Execution": "", - "Code Execution Engine": "", - "Code Execution Timeout": "", + "Code Execution": "Excució 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", "Code Interpreter": "Intèrpret de codi", - "Code Interpreter Engine": "", - "Code Interpreter Prompt Template": "", + "Code Interpreter Engine": "Motor de l'intèrpret de codi", + "Code Interpreter Prompt Template": "Plantilla de la indicació de l'intèrpret de codi", "Collection": "Col·lecció", "Color": "Color", "ComfyUI": "ComfyUI", @@ -203,7 +203,7 @@ "Confirm Password": "Confirmar la contrasenya", "Confirm your action": "Confirma la teva acció", "Confirm your new password": "Confirma la teva nova contrasenya", - "Connect to your own OpenAI compatible API endpoints.": "", + "Connect to your own OpenAI compatible API endpoints.": "Connecta als teus propis punts de connexió de l'API compatible amb OpenAI", "Connections": "Connexions", "Constrains effort on reasoning for reasoning models. Only applicable to reasoning models from specific providers that support reasoning effort. (Default: medium)": "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. (Per defecte: mitjà)", "Contact Admin for WebUI Access": "Posat en contacte amb l'administrador per accedir a WebUI", @@ -215,7 +215,7 @@ "Continue with Email": "Continuar amb el correu", "Continue with LDAP": "Continuar amb 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.": "Controlar com es divideix el text del missatge per a les sol·licituds TTS. 'Puntuació' divideix en frases, 'paràgrafs' divideix en paràgrafs i 'cap' manté el missatge com una cadena única.", - "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. (Default: 1.1)": "", + "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. (Default: 1.1)": "Controlar la repetició de seqüències de tokens en el text generat. Un valor més alt (p. ex., 1,5) penalitzarà les repeticions amb més força, mentre que un valor més baix (p. ex., 1,1) serà més indulgent. A l'1, està desactivat. (Per defecte: 1.1)", "Controls": "Controls", "Controls the balance between coherence and diversity of the output. A lower value will result in more focused and coherent text. (Default: 5.0)": "Controlar l'equilibri entre la coherència i la diversitat de la sortida. Un valor més baix donarà lloc a un text més enfocat i coherent. (Per defecte: 5.0)", "Copied": "Copiat", @@ -227,7 +227,7 @@ "Copy Link": "Copiar l'enllaç", "Copy to clipboard": "Copiar al porta-retalls", "Copying to clipboard was successful!": "La còpia al porta-retalls s'ha realitzat correctament", - "CORS must be properly configured by the provider to allow requests from Open WebUI.": "", + "CORS must be properly configured by the provider to allow requests from Open WebUI.": "CORS ha de ser configurat correctament pel proveïdor per permetre les sol·licituds d'Open WebUI", "Create": "Crear", "Create a knowledge base": "Crear una base de coneixement", "Create a model": "Crear un model", @@ -271,7 +271,7 @@ "Delete folder?": "Eliminar la carpeta?", "Delete function?": "Eliminar funció?", "Delete Message": "Eleiminar el missatge", - "Delete message?": "", + "Delete message?": "Eliminar el missatge?", "Delete prompt?": "Eliminar indicació?", "delete this link": "Eliminar aquest enllaç", "Delete tool?": "Eliminar eina?", @@ -282,15 +282,15 @@ "Describe your knowledge base and objectives": "Descriu la teva base de coneixement i objectius", "Description": "Descripció", "Didn't fully follow instructions": "No s'han seguit les instruccions completament", - "Direct Connections": "", - "Direct Connections allow users to connect to their own OpenAI compatible API endpoints.": "", - "Direct Connections settings updated": "", + "Direct Connections": "Connexions directes", + "Direct Connections allow users to connect to their own OpenAI compatible API endpoints.": "Les connexions directes permeten als usuaris connectar-se als seus propis endpoints d'API compatibles amb OpenAI.", + "Direct Connections settings updated": "Configuració de les connexions directes actualitzada", "Disabled": "Deshabilitat", "Discover a function": "Descobrir una funció", "Discover a model": "Descobrir un model", "Discover a prompt": "Descobrir una indicació", "Discover a tool": "Descobrir una eina", - "Discover how to use Open WebUI and seek support from the community.": "", + "Discover how to use Open WebUI and seek support from the community.": "Descobreix com utilitzar Open WebUI i demana suport a la comunitat.", "Discover wonders": "Descobrir meravelles", "Discover, download, and explore custom functions": "Descobrir, descarregar i explorar funcions personalitzades", "Discover, download, and explore custom prompts": "Descobrir, descarregar i explorar indicacions personalitzades", @@ -315,14 +315,14 @@ "Don't like the style": "No t'agrada l'estil?", "Done": "Fet", "Download": "Descarregar", - "Download as SVG": "", + "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", "Draw": "Dibuixar", "Drop any files here to add to the conversation": "Deixa qualsevol arxiu aquí per afegir-lo a la conversa", "e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "p. ex. '30s','10m'. Les unitats de temps vàlides són 's', 'm', 'h'.", - "e.g. 60": "", + "e.g. 60": "p. ex. 60", "e.g. A filter to remove profanity from text": "p. ex. Un filtre per eliminar paraules malsonants del text", "e.g. My Filter": "p. ex. El meu filtre", "e.g. My Tools": "p. ex. Les meves eines", @@ -346,7 +346,7 @@ "Embedding model set to \"{{embedding_model}}\"": "Model d'incrustació configurat a \"{{embedding_model}}\"", "Enable API Key": "Activar la Clau API", "Enable autocomplete generation for chat messages": "Activar la generació automàtica per als missatges del xat", - "Enable Code Interpreter": "", + "Enable Code Interpreter": "Activar l'intèrpret de codi", "Enable Community Sharing": "Activar l'ús compartit amb la comunitat", "Enable Google Drive": "Activar Google Drive", "Enable Memory Locking (mlock) to prevent model data from being swapped out of RAM. This option locks the model's working set of pages into RAM, ensuring that they will not be swapped out to disk. This can help maintain performance by avoiding page faults and ensuring fast data access.": "Activar el bloqueig de memòria (mlock) per evitar que les dades del model s'intercanviïn fora de la memòria RAM. Aquesta opció bloqueja el conjunt de pàgines de treball del model a la memòria RAM, assegurant-se que no s'intercanviaran al disc. Això pot ajudar a mantenir el rendiment evitant errors de pàgina i garantint un accés ràpid a les dades.", @@ -365,7 +365,7 @@ "Enter Application DN Password": "Introdueix la contrasenya del DN d'aplicació", "Enter Bing Search V7 Endpoint": "Introdueix el punt de connexió de Bing Search V7", "Enter Bing Search V7 Subscription Key": "Introdueix la clau de subscripció de Bing Search V7", - "Enter Bocha Search API Key": "", + "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)", @@ -379,9 +379,9 @@ "Enter Google PSE Engine Id": "Introdueix l'identificador del motor PSE de Google", "Enter Image Size (e.g. 512x512)": "Introdueix la mida de la imatge (p. ex. 512x512)", "Enter Jina API Key": "Introdueix la clau API de Jina", - "Enter Jupyter Password": "", - "Enter Jupyter Token": "", - "Enter Jupyter URL": "", + "Enter Jupyter Password": "Introdueix la contrasenya de Jupyter", + "Enter Jupyter Token": "Introdueix el token de Jupyter", + "Enter Jupyter URL": "Introdueix la URL de Jupyter", "Enter Kagi Search API Key": "Introdueix la clau API de Kagi Search", "Enter language codes": "Introdueix els codis de llenguatge", "Enter Model ID": "Introdueix l'identificador del model", @@ -397,8 +397,8 @@ "Enter SearchApi Engine": "Introdueix el motor SearchApi", "Enter Searxng Query URL": "Introdueix l'URL de consulta de Searxng", "Enter Seed": "Introdueix la llavor", - "Enter SerpApi API Key": "", - "Enter SerpApi Engine": "", + "Enter SerpApi API Key": "Introdueix la clau API SerpApi", + "Enter SerpApi Engine": "Introdueix el motor API SerpApi", "Enter Serper API Key": "Introdueix la clau API Serper", "Enter Serply API Key": "Introdueix la clau API Serply", "Enter Serpstack API Key": "Introdueix la clau API Serpstack", @@ -410,7 +410,7 @@ "Enter Tavily API Key": "Introdueix la clau API de Tavily", "Enter the public URL of your WebUI. This URL will be used to generate links in the notifications.": "Entra la URL pública de WebUI. Aquesta URL s'utilitzarà per generar els enllaços en les notificacions.", "Enter Tika Server URL": "Introdueix l'URL del servidor Tika", - "Enter timeout in seconds": "", + "Enter timeout in seconds": "Entra el temps màxim en segons", "Enter Top K": "Introdueix Top K", "Enter URL (e.g. http://127.0.0.1:7860/)": "Introdueix l'URL (p. ex. http://127.0.0.1:7860/)", "Enter URL (e.g. http://localhost:11434)": "Introdueix l'URL (p. ex. http://localhost:11434)", @@ -458,7 +458,7 @@ "Failed to save models configuration": "No s'ha pogut desar la configuració dels models", "Failed to update settings": "No s'han pogut actualitzar les preferències", "Failed to upload file.": "No s'ha pogut pujar l'arxiu.", - "Features": "", + "Features": "Característiques", "Features Permissions": "Permisos de les característiques", "February": "Febrer", "Feedback History": "Històric de comentaris", @@ -488,7 +488,7 @@ "Form": "Formulari", "Format your variables using brackets like this:": "Formata les teves variables utilitzant claudàtors així:", "Frequency Penalty": "Penalització per freqüència", - "Full Context Mode": "", + "Full Context Mode": "Mode de context complert", "Function": "Funció", "Function Calling": "Crida a funcions", "Function created successfully": "La funció s'ha creat correctament", @@ -503,9 +503,9 @@ "Functions allow arbitrary code execution": "Les funcions permeten l'execució de codi arbitrari", "Functions allow arbitrary code execution.": "Les funcions permeten l'execució de codi arbitrari.", "Functions imported successfully": "Les funcions s'han importat correctament", - "Gemini": "", - "Gemini API Config": "", - "Gemini API Key is required.": "", + "Gemini": "Gemini", + "Gemini API Config": "Configuració de Gemini API", + "Gemini API Key is required.": "La clau API de Gemini és necessària", "General": "General", "General Settings": "Preferències generals", "Generate an image": "Generar una imatge", @@ -532,7 +532,7 @@ "Hex Color": "Color hexadecimal", "Hex Color - Leave empty for default color": "Color hexadecimal - Deixar buit per a color per defecte", "Hide": "Amaga", - "Home": "", + "Home": "Inici", "Host": "Servidor", "How can I help you today?": "Com et puc ajudar avui?", "How would you rate this response?": "Com avaluaries aquesta resposta?", @@ -576,8 +576,8 @@ "JSON Preview": "Vista prèvia del document JSON", "July": "Juliol", "June": "Juny", - "Jupyter Auth": "", - "Jupyter URL": "", + "Jupyter Auth": "Autenticació Jupyter", + "Jupyter URL": "URL de Jupyter", "JWT Expiration": "Caducitat del JWT", "JWT Token": "Token JWT", "Kagi Search API Key": "Clau API de Kagi Search", @@ -607,12 +607,12 @@ "Leave empty to include all models or select specific models": "Deixa-ho en blanc per incloure tots els models o selecciona models específics", "Leave empty to use the default prompt, or enter a custom prompt": "Deixa-ho en blanc per utilitzar la indicació predeterminada o introdueix una indicació personalitzada", "Leave model field empty to use the default model.": "Deixa el camp de model buit per utilitzar el model per defecte.", - "License": "", + "License": "Llicència", "Light": "Clar", "Listening...": "Escoltant...", "Llama.cpp": "Llama.cpp", "LLMs can make mistakes. Verify important information.": "Els models de llenguatge poden cometre errors. Verifica la informació important.", - "Loading Kokoro.js...": "", + "Loading Kokoro.js...": "Carregant Kokoro.js", "Local": "Local", "Local Models": "Models locals", "Lost": "Perdut", @@ -622,7 +622,7 @@ "Make sure to export a workflow.json file as API format from ComfyUI.": "Assegura't d'exportar un fitxer workflow.json com a format API des de ComfyUI.", "Manage": "Gestionar", "Manage Arena Models": "Gestionar els models de l'Arena", - "Manage Direct Connections": "", + "Manage Direct Connections": "Gestionar les connexions directes", "Manage Models": "Gestionar els models", "Manage Ollama": "Gestionar Ollama", "Manage Ollama API Connections": "Gestionar les connexions a l'API d'Ollama", @@ -766,7 +766,7 @@ "Plain text (.txt)": "Text pla (.txt)", "Playground": "Zona de jocs", "Please carefully review the following warnings:": "Si us plau, revisa els següents avisos amb cura:", - "Please do not close the settings page while loading the model.": "", + "Please do not close the settings page while loading the model.": "No tanquis la pàgina de configuració mentre carregues el model.", "Please enter a prompt": "Si us plau, entra una indicació", "Please fill in all fields.": "Emplena tots els camps, si us plau.", "Please select a model first.": "Si us plau, selecciona un model primer", @@ -776,7 +776,7 @@ "Positive attitude": "Actitud positiva", "Prefix ID": "Identificador del prefix", "Prefix ID is used to avoid conflicts with other connections by adding a prefix to the model IDs - leave empty to disable": "L'identificador de prefix s'utilitza per evitar conflictes amb altres connexions afegint un prefix als ID de model; deixa'l en blanc per desactivar-lo.", - "Presence Penalty": "", + "Presence Penalty": "Penalització de presència", "Previous 30 days": "30 dies anteriors", "Previous 7 days": "7 dies anteriors", "Profile Image": "Imatge de perfil", @@ -813,7 +813,7 @@ "Rename": "Canviar el nom", "Reorder Models": "Reordenar els models", "Repeat Last N": "Repeteix els darrers N", - "Repeat Penalty (Ollama)": "", + "Repeat Penalty (Ollama)": "Penalització per repetició (Ollama)", "Reply in Thread": "Respondre al fil", "Request Mode": "Mode de sol·licitud", "Reranking Model": "Model de reavaluació", @@ -876,7 +876,7 @@ "Select a pipeline": "Seleccionar una Pipeline", "Select a pipeline url": "Seleccionar l'URL d'una Pipeline", "Select a tool": "Seleccionar una eina", - "Select an auth method": "", + "Select an auth method": "Seleccionar un mètode d'autenticació", "Select an Ollama instance": "Seleccionar una instància d'Ollama", "Select Engine": "Seleccionar el motor", "Select Knowledge": "Seleccionar coneixement", @@ -889,8 +889,8 @@ "Send message": "Enviar missatge", "Sends `stream_options: { include_usage: true }` in the request.\nSupported providers will return token usage information in the response when set.": "Envia `stream_options: { include_usage: true }` a la sol·licitud.\nEls proveïdors compatibles retornaran la informació d'ús del token a la resposta quan s'estableixi.", "September": "Setembre", - "SerpApi API Key": "", - "SerpApi Engine": "", + "SerpApi API Key": "Clau API de SerpApi", + "SerpApi Engine": "Motor de SerpApi", "Serper API Key": "Clau API de Serper", "Serply API Key": "Clau API de Serply", "Serpstack API Key": "Clau API de Serpstack", @@ -910,8 +910,8 @@ "Set the number of worker threads used for computation. This option controls how many threads are used to process incoming requests concurrently. Increasing this value can improve performance under high concurrency workloads but may also consume more CPU resources.": "Establir el nombre de fils de treball utilitzats per al càlcul. Aquesta opció controla quants fils s'utilitzen per processar les sol·licituds entrants simultàniament. Augmentar aquest valor pot millorar el rendiment amb càrregues de treball de concurrència elevada, però també pot consumir més recursos de CPU.", "Set Voice": "Establir la veu", "Set whisper model": "Establir el model whisper", - "Sets a flat bias against tokens that have appeared at least once. A higher value (e.g., 1.5) will penalize repetitions more strongly, while a lower value (e.g., 0.9) will be more lenient. At 0, it is disabled. (Default: 0)": "", - "Sets a scaling bias against tokens to penalize repetitions, based on how many times they have appeared. A higher value (e.g., 1.5) will penalize repetitions more strongly, while a lower value (e.g., 0.9) will be more lenient. At 0, it is disabled. (Default: 1.1)": "", + "Sets a flat bias against tokens that have appeared at least once. A higher value (e.g., 1.5) will penalize repetitions more strongly, while a lower value (e.g., 0.9) will be more lenient. At 0, it is disabled. (Default: 0)": "Estableix un biaix pla contra tokens que han aparegut almenys una vegada. Un valor més alt (p. ex., 1,5) penalitzarà les repeticions amb més força, mentre que un valor més baix (p. ex., 0,9) serà més indulgent. A 0, està desactivat. (Per defecte: 0)", + "Sets a scaling bias against tokens to penalize repetitions, based on how many times they have appeared. A higher value (e.g., 1.5) will penalize repetitions more strongly, while a lower value (e.g., 0.9) will be more lenient. At 0, it is disabled. (Default: 1.1)": "Estableix un biaix d'escala contra tokens per penalitzar les repeticions, en funció de quantes vegades han aparegut. Un valor més alt (p. ex., 1,5) penalitzarà les repeticions amb més força, mentre que un valor més baix (p. ex., 0,9) serà més indulgent. A 0, està desactivat. (Per defecte: 1.1)", "Sets how far back for the model to look back to prevent repetition. (Default: 64, 0 = disabled, -1 = num_ctx)": "Establir fins a quin punt el model mira enrere per evitar la repetició. (Per defecte: 64, 0 = desactivat, -1 = num_ctx)", "Sets the random number seed to use for generation. Setting this to a specific number will make the model generate the same text for the same prompt. (Default: random)": "Establir la llavor del nombre aleatori que s'utilitzarà per a la generació. Establir-ho a un número específic farà que el model generi el mateix text per a la mateixa sol·licitud. (Per defecte: aleatori)", "Sets the size of the context window used to generate the next token. (Default: 2048)": "Estableix la mida de la finestra de context utilitzada per generar el següent token. (Per defecte: 2048)", @@ -958,7 +958,7 @@ "Tags Generation Prompt": "Indicació per a la generació d'etiquetes", "Tail free sampling is used to reduce the impact of less probable tokens from the output. A higher value (e.g., 2.0) will reduce the impact more, while a value of 1.0 disables this setting. (default: 1)": "El mostreig sense cua s'utilitza per reduir l'impacte de tokens menys probables de la sortida. Un valor més alt (p. ex., 2,0) reduirà més l'impacte, mentre que un valor d'1,0 desactiva aquesta configuració. (per defecte: 1)", "Tap to interrupt": "Prem per interrompre", - "Tasks": "", + "Tasks": "Tasques", "Tavily API Key": "Clau API de Tavily", "Tell us more:": "Dona'ns més informació:", "Temperature": "Temperatura", @@ -1005,7 +1005,7 @@ "Title (e.g. Tell me a fun fact)": "Títol (p. ex. Digues-me quelcom divertit)", "Title Auto-Generation": "Generació automàtica de títol", "Title cannot be an empty string.": "El títol no pot ser una cadena buida.", - "Title Generation": "", + "Title Generation": "Generació de títols", "Title Generation Prompt": "Indicació de generació de títol", "TLS": "TLS", "To access the available model names for downloading,": "Per accedir als noms dels models disponibles per descarregar,", @@ -1062,7 +1062,7 @@ "Updated": "Actualitzat", "Updated at": "Actualitzat el", "Updated At": "Actualitzat el", - "Upgrade to a licensed plan for enhanced capabilities, including custom theming and branding, and dedicated support.": "", + "Upgrade to a licensed plan for enhanced capabilities, including custom theming and branding, and dedicated support.": "Actualitzar a un pla amb llicència per obtenir capacitats millorades, com ara la temàtica personalitzada i la marca, i assistència dedicada.", "Upload": "Pujar", "Upload a GGUF model": "Pujar un model GGUF", "Upload directory": "Pujar directori", @@ -1101,7 +1101,7 @@ "Warning:": "Avís:", "Warning: Enabling this will allow users to upload arbitrary code on the server.": "Avís: Habilitar això permetrà als usuaris penjar codi arbitrari al servidor.", "Warning: If you update or change your embedding model, you will need to re-import all documents.": "Avís: Si s'actualitza o es canvia el model d'incrustació, s'hauran de tornar a importar tots els documents.", - "Warning: Jupyter execution enables arbitrary code execution, posing severe security risks—proceed with extreme caution.": "", + "Warning: Jupyter execution enables arbitrary code execution, posing severe security risks—proceed with extreme caution.": "Avís: l'execució de Jupyter permet l'execució de codi arbitrari, la qual cosa comporta greus riscos de seguretat; procediu amb extrema precaució.", "Web": "Web", "Web API": "Web API", "Web Loader Settings": "Preferències del carregador web", From a8859a81454a55f80c15a72a3c87507ef23459a0 Mon Sep 17 00:00:00 2001 From: Simone Date: Thu, 20 Feb 2025 21:25:32 +0100 Subject: [PATCH 03/27] Fix on ollama to openai conversion - stream can return a single message with content --- backend/open_webui/utils/response.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/open_webui/utils/response.py b/backend/open_webui/utils/response.py index bc47e1e13..8c3f1a58e 100644 --- a/backend/open_webui/utils/response.py +++ b/backend/open_webui/utils/response.py @@ -104,7 +104,7 @@ async def convert_streaming_response_ollama_to_openai(ollama_streaming_response) data = json.loads(data) model = data.get("model", "ollama") - message_content = data.get("message", {}).get("content", "") + message_content = data.get("message", {}).get("content", None) tool_calls = data.get("message", {}).get("tool_calls", None) openai_tool_calls = None @@ -118,7 +118,7 @@ async def convert_streaming_response_ollama_to_openai(ollama_streaming_response) usage = convert_ollama_usage_to_openai(data) data = openai_chat_chunk_message_template( - model, message_content if not done else None, openai_tool_calls, usage + model, message_content, openai_tool_calls, usage ) line = f"data: {json.dumps(data)}\n\n" From 1332a0d3815c46bff5d4bf55c6ce1f6373cdd5f2 Mon Sep 17 00:00:00 2001 From: "Richard (Huangrui) Chu" <65276824+HuangruiChu@users.noreply.github.com> Date: Thu, 20 Feb 2025 16:07:32 -0500 Subject: [PATCH 04/27] Update zh-CN translation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. remove the typo ”‘’" for -1 表示无限制,正整数表示具体限制” 2. Token should be kept rather than translated as "标记" 3. Max Tokens (num_predict) should be "最大Token数量 (num_predict)" 4. "Enter Jupyter Token" here “Token" could be translated as ”令牌“ just as "JWT Token": "JWT 令牌", (line 582) 5. "TLS" which means "Transport Layer Security" should be translated to "传输层安全协议" 6. "Tokens To Keep On Context Refresh (num_keep)" "在语境刷新时需保留的 Token 数量" 7. change token to "Token" in the Chinese translation. --- src/lib/i18n/locales/zh-CN/translation.json | 22 ++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/lib/i18n/locales/zh-CN/translation.json b/src/lib/i18n/locales/zh-CN/translation.json index 66f6a5913..6bf556aa4 100644 --- a/src/lib/i18n/locales/zh-CN/translation.json +++ b/src/lib/i18n/locales/zh-CN/translation.json @@ -1,5 +1,5 @@ { - "-1 for no limit, or a positive integer for a specific limit": "-1 表示无限制,正整数表示具体限制”", + "-1 for no limit, or a positive integer for a specific limit": "-1 表示无限制,正整数表示具体限制", "'s', 'm', 'h', 'd', 'w' or '-1' for no expiration.": "'s', 'm', 'h', 'd', 'w' 或 '-1' 表示无过期时间。", "(e.g. `sh webui.sh --api --api-auth username_password`)": "(例如 `sh webui.sh --api --api-auth username_password`)", "(e.g. `sh webui.sh --api`)": "(例如 `sh webui.sh --api`)", @@ -63,7 +63,7 @@ "Allow Voice Interruption in Call": "允许通话中的打断语音", "Allowed Endpoints": "允许的端点", "Already have an account?": "已经拥有账号了?", - "Alternative to the top_p, and aims to ensure a balance of quality and variety. The parameter p represents the minimum probability for a token to be considered, relative to the probability of the most likely token. For example, with p=0.05 and the most likely token having a probability of 0.9, logits with a value less than 0.045 are filtered out. (Default: 0.0)": "top_p的替代方法,目标是在质量和多样性之间取得平衡。参数p表示一个token相对于最有可能的token所需的最低概率。比如,当p=0.05且最有可能的token概率为0.9时,概率低于0.045的logits会被排除。(默认值:0.0)", + "Alternative to the top_p, and aims to ensure a balance of quality and variety. The parameter p represents the minimum probability for a token to be considered, relative to the probability of the most likely token. For example, with p=0.05 and the most likely token having a probability of 0.9, logits with a value less than 0.045 are filtered out. (Default: 0.0)": "top_p的替代方法,目标是在质量和多样性之间取得平衡。参数p表示一个Token相对于最有可能的Token所需的最低概率。比如,当p=0.05且最有可能的Token概率为0.9时,概率低于0.045的logits会被排除。(默认值:0.0)", "Always": "保持", "Amazing": "很棒", "an assistant": "一个助手", @@ -380,7 +380,7 @@ "Enter Image Size (e.g. 512x512)": "输入图像分辨率 (例如:512x512)", "Enter Jina API Key": "输入 Jina API 密钥", "Enter Jupyter Password": "输入 Jupyter 密码", - "Enter Jupyter Token": "输入 Jupyter Token", + "Enter Jupyter Token": "输入 Jupyter 令牌", "Enter Jupyter URL": "输入 Jupyter URL", "Enter Kagi Search API Key": "输入 Kagi Search API 密钥", "Enter language codes": "输入语言代码", @@ -629,7 +629,7 @@ "Manage OpenAI API Connections": "管理OpenAI API连接", "Manage Pipelines": "管理 Pipeline", "March": "三月", - "Max Tokens (num_predict)": "最多 Token (num_predict)", + "Max Tokens (num_predict)": "最大Token数量 (num_predict)", "Max Upload Count": "最大上传数量", "Max Upload Size": "最大上传大小", "Maximum of 3 models can be downloaded simultaneously. Please try again later.": "最多可以同时下载 3 个模型,请稍后重试。", @@ -910,14 +910,14 @@ "Set the number of worker threads used for computation. This option controls how many threads are used to process incoming requests concurrently. Increasing this value can improve performance under high concurrency workloads but may also consume more CPU resources.": "设置用于计算的工作线程数量。该选项可控制并发处理传入请求的线程数量。增加该值可以提高高并发工作负载下的性能,但也可能消耗更多的 CPU 资源。", "Set Voice": "设置音色", "Set whisper model": "设置 whisper 模型", - "Sets a flat bias against tokens that have appeared at least once. A higher value (e.g., 1.5) will penalize repetitions more strongly, while a lower value (e.g., 0.9) will be more lenient. At 0, it is disabled. (Default: 0)": "这个设置项用于调整对重复 tokens 的抑制强度。当某个 token 至少出现过一次后,系统会通过 flat bias 参数施加惩罚力度:数值越大(如 1.5),抑制重复的效果越强烈;数值较小(如 0.9)则相对宽容。当设为 0 时,系统会完全关闭这个重复抑制功能(默认值为 0)。", - "Sets a scaling bias against tokens to penalize repetitions, based on how many times they have appeared. A higher value (e.g., 1.5) will penalize repetitions more strongly, while a lower value (e.g., 0.9) will be more lenient. At 0, it is disabled. (Default: 1.1)": "这个参数用于通过 scaling bias 机制抑制重复内容:当某些 tokens 重复出现时,系统会根据它们已出现的次数自动施加惩罚。数值越大(如 1.5)惩罚力度越强,能更有效减少重复;数值较小(如 0.9)则允许更多重复。当设为 0 时完全关闭该功能,默认值设置为 1.1 保持适度抑制。", + "Sets a flat bias against tokens that have appeared at least once. A higher value (e.g., 1.5) will penalize repetitions more strongly, while a lower value (e.g., 0.9) will be more lenient. At 0, it is disabled. (Default: 0)": "这个设置项用于调整对重复 Token 的抑制强度。当某个 Token 至少出现过一次后,系统会通过 flat bias 参数施加惩罚力度:数值越大(如 1.5),抑制重复的效果越强烈;数值较小(如 0.9)则相对宽容。当设为 0 时,系统会完全关闭这个重复抑制功能(默认值为 0)。", + "Sets a scaling bias against tokens to penalize repetitions, based on how many times they have appeared. A higher value (e.g., 1.5) will penalize repetitions more strongly, while a lower value (e.g., 0.9) will be more lenient. At 0, it is disabled. (Default: 1.1)": "这个参数用于通过 scaling bias 机制抑制重复内容:当某些 Token 重复出现时,系统会根据它们已出现的次数自动施加惩罚。数值越大(如 1.5)惩罚力度越强,能更有效减少重复;数值较小(如 0.9)则允许更多重复。当设为 0 时完全关闭该功能,默认值设置为 1.1 保持适度抑制。", "Sets how far back for the model to look back to prevent repetition. (Default: 64, 0 = disabled, -1 = num_ctx)": "设置模型回溯多远以防止重复。(默认值:64,0 = 禁用,-1 = num_ctx)", "Sets the random number seed to use for generation. Setting this to a specific number will make the model generate the same text for the same prompt. (Default: random)": "设置 random number seed 可以控制模型生成文本的随机起点。如果指定一个具体数字,当输入相同的提示语时,模型每次都会生成完全相同的文本内容(默认是随机选取 seed)。", "Sets the size of the context window used to generate the next token. (Default: 2048)": "设置用于生成下一个 Token 的上下文大小。(默认值:2048)", "Sets the stop sequences to use. When this pattern is encountered, the LLM will stop generating text and return. Multiple stop patterns may be set by specifying multiple separate stop parameters in a modelfile.": "设置要使用的停止序列。遇到这种模式时,大语言模型将停止生成文本并返回。可以通过在模型文件中指定多个单独的停止参数来设置多个停止模式。", "Settings": "设置", - "Settings saved successfully!": "设置已保存", + "Settings saved successfully!": "设置已成功保存!", "Share": "分享", "Share Chat": "分享对话", "Share to Open WebUI Community": "分享到 OpenWebUI 社区", @@ -956,7 +956,7 @@ "System Prompt": "系统提示词 (System Prompt)", "Tags Generation": "标签生成", "Tags Generation Prompt": "标签生成提示词", - "Tail free sampling is used to reduce the impact of less probable tokens from the output. A higher value (e.g., 2.0) will reduce the impact more, while a value of 1.0 disables this setting. (default: 1)": "Tail free sampling 用于减少输出中可能性较低的标记的影响。数值越大(如 2.0),影响就越小,而数值为 1.0 则会禁用此设置。(默认值:1)", + "Tail free sampling is used to reduce the impact of less probable tokens from the output. A higher value (e.g., 2.0) will reduce the impact more, while a value of 1.0 disables this setting. (default: 1)": "Tail free sampling 用于减少输出中可能性较低的Token的影响。数值越大(如 2.0),影响就越小,而数值为 1.0 则会禁用此设置。(默认值:1)", "Tap to interrupt": "点击以中断", "Tasks": "任务", "Tavily API Key": "Tavily API 密钥", @@ -985,7 +985,7 @@ "This action cannot be undone. Do you wish to continue?": "此操作无法撤销。是否确认继续?", "This ensures that your valuable conversations are securely saved to your backend database. Thank you!": "这将确保您的宝贵对话被安全地保存到后台数据库中。感谢!", "This is an experimental feature, it may not function as expected and is subject to change at any time.": "这是一个实验功能,可能不会如预期那样工作,而且可能随时发生变化。", - "This option controls how many tokens are preserved when refreshing the context. For example, if set to 2, the last 2 tokens of the conversation context will be retained. Preserving context can help maintain the continuity of a conversation, but it may reduce the ability to respond to new topics. (Default: 24)": "该选项控制刷新上下文时保留多少标记。例如,如果设置为 2,就会保留对话上下文的最后 2 个标记。保留上下文有助于保持对话的连续性,但可能会降低回复新话题的能力。(默认值:24)", + "This option controls how many tokens are preserved when refreshing the context. For example, if set to 2, the last 2 tokens of the conversation context will be retained. Preserving context can help maintain the continuity of a conversation, but it may reduce the ability to respond to new topics. (Default: 24)": "该选项控制刷新上下文时保留多少Token。例如,如果设置为 2,就会保留对话上下文的最后 2 个Token。保留上下文有助于保持对话的连续性,但可能会降低回复新话题的能力。(默认值:24)", "This option sets the maximum number of tokens the model can generate in its response. Increasing this limit allows the model to provide longer answers, but it may also increase the likelihood of unhelpful or irrelevant content being generated. (Default: 128)": "此选项设置了模型在回答中可以生成的最大 Token 数。增加这个限制可以让模型提供更长的答案,但也可能增加生成无用或不相关内容的可能性。 (默认值:128)", "This option will delete all existing files in the collection and replace them with newly uploaded files.": "此选项将会删除文件集中所有文件,并用新上传的文件替换。", "This response was generated by \"{{model}}\"": "此回复由 \"{{model}}\" 生成", @@ -1007,7 +1007,7 @@ "Title cannot be an empty string.": "标题不能为空。", "Title Generation": "标题生成", "Title Generation Prompt": "用于自动生成标题的提示词", - "TLS": "TLS", + "TLS": "传输层安全协议", "To access the available model names for downloading,": "要访问可下载的模型名称,", "To access the GGUF models available for downloading,": "要访问可下载的 GGUF 模型,", "To access the WebUI, please reach out to the administrator. Admins can manage user statuses from the Admin Panel.": "请联系管理员以访问。管理员可以在后台管理面板中管理用户状态。", @@ -1022,7 +1022,7 @@ "Toggle settings": "切换设置", "Toggle sidebar": "切换侧边栏", "Token": "Token", - "Tokens To Keep On Context Refresh (num_keep)": "在语境刷新时需保留的 Tokens", + "Tokens To Keep On Context Refresh (num_keep)": "在语境刷新时需保留的 Token 数量", "Too verbose": "过于冗长", "Tool created successfully": "工具创建成功", "Tool deleted successfully": "工具删除成功", From e31f680788910b04a1709271979c1b416f1b839a Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Thu, 20 Feb 2025 20:46:00 -0800 Subject: [PATCH 05/27] refac --- backend/open_webui/routers/tasks.py | 46 +++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/backend/open_webui/routers/tasks.py b/backend/open_webui/routers/tasks.py index 0328cefe0..b63c9732a 100644 --- a/backend/open_webui/routers/tasks.py +++ b/backend/open_webui/routers/tasks.py @@ -20,6 +20,10 @@ from open_webui.utils.auth import get_admin_user, get_verified_user from open_webui.constants import TASKS from open_webui.routers.pipelines import process_pipeline_inlet_filter +from open_webui.utils.filter import ( + get_sorted_filter_ids, + process_filter_functions, +) from open_webui.utils.task import get_task_model_id from open_webui.config import ( @@ -221,6 +225,12 @@ async def generate_title( }, } + # Process the payload through the pipeline + try: + payload = await process_pipeline_inlet_filter(request, payload, user, models) + except Exception as e: + raise e + try: return await generate_chat_completion(request, form_data=payload, user=user) except Exception as e: @@ -290,6 +300,12 @@ async def generate_chat_tags( }, } + # Process the payload through the pipeline + try: + payload = await process_pipeline_inlet_filter(request, payload, user, models) + except Exception as e: + raise e + try: return await generate_chat_completion(request, form_data=payload, user=user) except Exception as e: @@ -356,6 +372,12 @@ async def generate_image_prompt( }, } + # Process the payload through the pipeline + try: + payload = await process_pipeline_inlet_filter(request, payload, user, models) + except Exception as e: + raise e + try: return await generate_chat_completion(request, form_data=payload, user=user) except Exception as e: @@ -433,6 +455,12 @@ async def generate_queries( }, } + # Process the payload through the pipeline + try: + payload = await process_pipeline_inlet_filter(request, payload, user, models) + except Exception as e: + raise e + try: return await generate_chat_completion(request, form_data=payload, user=user) except Exception as e: @@ -514,6 +542,12 @@ async def generate_autocompletion( }, } + # Process the payload through the pipeline + try: + payload = await process_pipeline_inlet_filter(request, payload, user, models) + except Exception as e: + raise e + try: return await generate_chat_completion(request, form_data=payload, user=user) except Exception as e: @@ -584,6 +618,12 @@ async def generate_emoji( }, } + # Process the payload through the pipeline + try: + payload = await process_pipeline_inlet_filter(request, payload, user, models) + except Exception as e: + raise e + try: return await generate_chat_completion(request, form_data=payload, user=user) except Exception as e: @@ -644,6 +684,12 @@ async def generate_moa_response( }, } + # Process the payload through the pipeline + try: + payload = await process_pipeline_inlet_filter(request, payload, user, models) + except Exception as e: + raise e + try: return await generate_chat_completion(request, form_data=payload, user=user) except Exception as e: From 68e33b53867de331ed71682d2dff869c55fb0856 Mon Sep 17 00:00:00 2001 From: Karl Lee <61072264+KarlLee830@users.noreply.github.com> Date: Fri, 21 Feb 2025 14:58:40 +0800 Subject: [PATCH 06/27] i18n: Update Chinese Translation --- src/lib/i18n/locales/zh-CN/translation.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/i18n/locales/zh-CN/translation.json b/src/lib/i18n/locales/zh-CN/translation.json index 6bf556aa4..b0db571ba 100644 --- a/src/lib/i18n/locales/zh-CN/translation.json +++ b/src/lib/i18n/locales/zh-CN/translation.json @@ -995,8 +995,8 @@ "This will delete all models including custom models and cannot be undone.": "这将删除所有模型,包括自定义模型,且无法撤销。", "This will reset the knowledge base and sync all files. Do you wish to continue?": "这将重置知识库并替换所有文件为目录下文件。确认继续?", "Thorough explanation": "解释较为详细", - "Thought for {{DURATION}}": "已推理 持续 {{DURATION}}", - "Thought for {{DURATION}} seconds": "已推理 持续 {{DURATION}} 秒", + "Thought for {{DURATION}}": "思考用时 {{DURATION}}", + "Thought for {{DURATION}} seconds": "思考用时 {{DURATION}} 秒", "Tika": "Tika", "Tika Server URL required.": "请输入 Tika 服务器地址。", "Tiktoken": "Tiktoken", From ab1f2ae914301dfa080a3495813c98901e0c870c Mon Sep 17 00:00:00 2001 From: Olof Larsson Date: Fri, 21 Feb 2025 08:24:48 +0100 Subject: [PATCH 07/27] feat/async-pipes --- backend/open_webui/functions.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/backend/open_webui/functions.py b/backend/open_webui/functions.py index 274be56ec..2f94f701e 100644 --- a/backend/open_webui/functions.py +++ b/backend/open_webui/functions.py @@ -2,6 +2,7 @@ import logging import sys import inspect import json +import asyncio from pydantic import BaseModel from typing import AsyncGenerator, Generator, Iterator @@ -76,11 +77,13 @@ async def get_function_models(request): if hasattr(function_module, "pipes"): sub_pipes = [] - # Check if pipes is a function or a list - + # Handle pipes being a list, sync function, or async function try: if callable(function_module.pipes): - sub_pipes = function_module.pipes() + if asyncio.iscoroutinefunction(function_module.pipes): + sub_pipes = await function_module.pipes() + else: + sub_pipes = function_module.pipes() else: sub_pipes = function_module.pipes except Exception as e: From 2993332b387c4230a4f5676c178bf4ece4384cdf Mon Sep 17 00:00:00 2001 From: huanght Date: Fri, 21 Feb 2025 16:10:11 +0800 Subject: [PATCH 08/27] fix:Quick selection tool lost --- src/lib/components/chat/Chat.svelte | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/lib/components/chat/Chat.svelte b/src/lib/components/chat/Chat.svelte index fcd5177d7..98be06f5c 100644 --- a/src/lib/components/chat/Chat.svelte +++ b/src/lib/components/chat/Chat.svelte @@ -187,15 +187,20 @@ setToolIds(); } + $: if (atSelectedModel || selectedModels) { + setToolIds(); + } + const setToolIds = async () => { if (!$tools) { tools.set(await getTools(localStorage.token)); } - if (selectedModels.length !== 1) { + if (selectedModels.length !== 1 && !atSelectedModel) { return; } - const model = $models.find((m) => m.id === selectedModels[0]); + + const model = atSelectedModel ?? $models.find((m) => m.id === selectedModels[0]); if (model) { selectedToolIds = (model?.info?.meta?.toolIds ?? []).filter((id) => $tools.find((t) => t.id === id) From cdf620e6eedd797a01a984e510bc67761a051b14 Mon Sep 17 00:00:00 2001 From: Coleton M Date: Fri, 21 Feb 2025 04:41:45 -0600 Subject: [PATCH 09/27] Update audio.py to fetch custom URL voices and models --- backend/open_webui/routers/audio.py | 40 ++++++++++++++++++++++++++--- 1 file changed, 37 insertions(+), 3 deletions(-) diff --git a/backend/open_webui/routers/audio.py b/backend/open_webui/routers/audio.py index a970366d1..a8cd3d14a 100644 --- a/backend/open_webui/routers/audio.py +++ b/backend/open_webui/routers/audio.py @@ -679,8 +679,21 @@ def transcription( def get_available_models(request: Request) -> list[dict]: available_models = [] + """if request.app.state.config.TTS_ENGINE == "openai": + available_models = [{"id": "tts-1"}, {"id": "tts-1-hd"}]""" if request.app.state.config.TTS_ENGINE == "openai": - available_models = [{"id": "tts-1"}, {"id": "tts-1-hd"}] + # Use custom endpoint if not using the official OpenAI API URL + if not request.app.state.config.TTS_OPENAI_API_BASE_URL.startswith("https://api.openai.com"): + try: + response = requests.get(f"{request.app.state.config.TTS_OPENAI_API_BASE_URL}/audio/models") + response.raise_for_status() + data = response.json() + available_models = data.get("models", []) + except Exception as e: + log.error(f"Error fetching models from custom endpoint: {str(e)}") + available_models = [] + else: + available_models = [{"id": "tts-1"}, {"id": "tts-1-hd"}] elif request.app.state.config.TTS_ENGINE == "elevenlabs": try: response = requests.get( @@ -710,7 +723,7 @@ async def get_models(request: Request, user=Depends(get_verified_user)): def get_available_voices(request) -> dict: """Returns {voice_id: voice_name} dict""" available_voices = {} - if request.app.state.config.TTS_ENGINE == "openai": + """if request.app.state.config.TTS_ENGINE == "openai": available_voices = { "alloy": "alloy", "echo": "echo", @@ -718,7 +731,28 @@ def get_available_voices(request) -> dict: "onyx": "onyx", "nova": "nova", "shimmer": "shimmer", - } + }""" + if request.app.state.config.TTS_ENGINE == "openai": + # Use custom endpoint if not using the official OpenAI API URL + if not request.app.state.config.TTS_OPENAI_API_BASE_URL.startswith("https://api.openai.com"): + try: + response = requests.get(f"{request.app.state.config.TTS_OPENAI_API_BASE_URL}/audio/voices") + response.raise_for_status() + data = response.json() + voices_list = data.get("voices", []) + available_voices = {voice["id"]: voice["name"] for voice in voices_list} + except Exception as e: + log.error(f"Error fetching voices from custom endpoint: {str(e)}") + available_voices = {} + else: + available_voices = { + "alloy": "alloy", + "echo": "echo", + "fable": "fable", + "onyx": "onyx", + "nova": "nova", + "shimmer": "shimmer", + } elif request.app.state.config.TTS_ENGINE == "elevenlabs": try: available_voices = get_elevenlabs_voices( From f789ad59a9e57b15e8218e7b5d36293051fbf44f Mon Sep 17 00:00:00 2001 From: Synergyst Date: Fri, 21 Feb 2025 04:47:46 -0600 Subject: [PATCH 10/27] Update audio.py Removed original code that was commented out --- backend/open_webui/routers/audio.py | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/backend/open_webui/routers/audio.py b/backend/open_webui/routers/audio.py index a8cd3d14a..12c9dbc6d 100644 --- a/backend/open_webui/routers/audio.py +++ b/backend/open_webui/routers/audio.py @@ -679,8 +679,6 @@ def transcription( def get_available_models(request: Request) -> list[dict]: available_models = [] - """if request.app.state.config.TTS_ENGINE == "openai": - available_models = [{"id": "tts-1"}, {"id": "tts-1-hd"}]""" if request.app.state.config.TTS_ENGINE == "openai": # Use custom endpoint if not using the official OpenAI API URL if not request.app.state.config.TTS_OPENAI_API_BASE_URL.startswith("https://api.openai.com"): @@ -723,15 +721,6 @@ async def get_models(request: Request, user=Depends(get_verified_user)): def get_available_voices(request) -> dict: """Returns {voice_id: voice_name} dict""" available_voices = {} - """if request.app.state.config.TTS_ENGINE == "openai": - available_voices = { - "alloy": "alloy", - "echo": "echo", - "fable": "fable", - "onyx": "onyx", - "nova": "nova", - "shimmer": "shimmer", - }""" if request.app.state.config.TTS_ENGINE == "openai": # Use custom endpoint if not using the official OpenAI API URL if not request.app.state.config.TTS_OPENAI_API_BASE_URL.startswith("https://api.openai.com"): From c4b441de6518d1c8bf0fa59b2c837984158fb9a8 Mon Sep 17 00:00:00 2001 From: Bob McElrath Date: Fri, 21 Feb 2025 09:12:34 -0500 Subject: [PATCH 11/27] Support thinking tags used by Openthinker --- backend/open_webui/utils/middleware.py | 53 ++++++++++++++++++-------- 1 file changed, 37 insertions(+), 16 deletions(-) diff --git a/backend/open_webui/utils/middleware.py b/backend/open_webui/utils/middleware.py index 7ec764fc0..8c82b7074 100644 --- a/backend/open_webui/utils/middleware.py +++ b/backend/open_webui/utils/middleware.py @@ -1127,12 +1127,12 @@ async def process_chat_response( if reasoning_duration is not None: if raw: - content = f'{content}\n<{block["tag"]}>{block["content"]}\n' + content = f'{content}\n<{block["start_tag"]}>{block["content"]}<{block["end_tag"]}>\n' else: content = f'{content}\n
\nThought for {reasoning_duration} seconds\n{reasoning_display_content}\n
\n' else: if raw: - content = f'{content}\n<{block["tag"]}>{block["content"]}\n' + content = f'{content}\n<{block["start_tag"]}>{block["content"]}<{block["end_tag"]}>\n' else: content = f'{content}\n
\nThinking…\n{reasoning_display_content}\n
\n' @@ -1228,9 +1228,9 @@ async def process_chat_response( return attributes if content_blocks[-1]["type"] == "text": - for tag in tags: + for start_tag, end_tag in tags: # Match start tag e.g., or - start_tag_pattern = rf"<{tag}(\s.*?)?>" + start_tag_pattern = rf"<{re.escape(start_tag)}(\s.*?)?>" match = re.search(start_tag_pattern, content) if match: attr_content = ( @@ -1263,7 +1263,8 @@ async def process_chat_response( content_blocks.append( { "type": content_type, - "tag": tag, + "start_tag": start_tag, + "end_tag": end_tag, "attributes": attributes, "content": "", "started_at": time.time(), @@ -1275,9 +1276,10 @@ async def process_chat_response( break elif content_blocks[-1]["type"] == content_type: - tag = content_blocks[-1]["tag"] + start_tag = content_blocks[-1]["start_tag"] + end_tag = content_blocks[-1]["end_tag"] # Match end tag e.g., - end_tag_pattern = rf"" + end_tag_pattern = rf"<{re.escape(end_tag)}>" # Check if the content has the end tag if re.search(end_tag_pattern, content): @@ -1285,7 +1287,7 @@ async def process_chat_response( block_content = content_blocks[-1]["content"] # Strip start and end tags from the content - start_tag_pattern = rf"<{tag}(.*?)>" + start_tag_pattern = rf"<{re.escape(start_tag)}(.*?)>" block_content = re.sub( start_tag_pattern, "", block_content ).strip() @@ -1350,7 +1352,7 @@ async def process_chat_response( # Clean processed content content = re.sub( - rf"<{tag}(.*?)>(.|\n)*?", + rf"<{re.escape(start_tag)}(.*?)>(.|\n)*?<{re.escape(end_tag)}>", "", content, flags=re.DOTALL, @@ -1388,19 +1390,28 @@ async def process_chat_response( # We might want to disable this by default DETECT_REASONING = True + DETECT_SOLUTION = True DETECT_CODE_INTERPRETER = metadata.get("features", {}).get( "code_interpreter", False ) reasoning_tags = [ - "think", - "thinking", - "reason", - "reasoning", - "thought", - "Thought", + ("think", "/think"), + ("thinking", "/thinking"), + ("reason", "/reason"), + ("reasoning", "/reasoning"), + ("thought", "/thought"), + ("Thought", "/Thought"), + ("|begin_of_thought|", "|end_of_thought|") + ] + + code_interpreter_tags = [ + ("code_interpreter", "/code_interpreter") + ] + + solution_tags = [ + ("|begin_of_solution|", "|end_of_solution|") ] - code_interpreter_tags = ["code_interpreter"] try: for event in events: @@ -1533,6 +1544,16 @@ async def process_chat_response( if end: break + if DETECT_SOLUTION: + content, content_blocks, _ = ( + tag_content_handler( + "solution", + solution_tags, + content, + content_blocks, + ) + ) + if ENABLE_REALTIME_CHAT_SAVE: # Save message in the database Chats.upsert_message_to_chat_by_id_and_message_id( From fb3886cf04d1431e7dae6968f607634999404b45 Mon Sep 17 00:00:00 2001 From: Simone Date: Fri, 21 Feb 2025 18:46:10 +0100 Subject: [PATCH 12/27] Added support for stop parameter --- backend/open_webui/utils/payload.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/backend/open_webui/utils/payload.py b/backend/open_webui/utils/payload.py index 51e8d50cc..4c1bbad9a 100644 --- a/backend/open_webui/utils/payload.py +++ b/backend/open_webui/utils/payload.py @@ -230,6 +230,12 @@ def convert_payload_openai_to_ollama(openai_payload: dict) -> dict: "system" ] # To prevent Ollama warning of invalid option provided + # If there is the "stop" parameter in the openai_payload, remap it to the ollama_payload.options + if "stop" in openai_payload: + ollama_options = ollama_payload.get("options", {}) + ollama_options["stop"] = openai_payload.get("stop") + ollama_payload["options"] = ollama_options + if "metadata" in openai_payload: ollama_payload["metadata"] = openai_payload["metadata"] From 613a087387c094e71ee91d29c015195ef401e160 Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Fri, 21 Feb 2025 10:55:03 -0800 Subject: [PATCH 13/27] refac --- backend/open_webui/routers/audio.py | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/backend/open_webui/routers/audio.py b/backend/open_webui/routers/audio.py index 12c9dbc6d..8eb111205 100644 --- a/backend/open_webui/routers/audio.py +++ b/backend/open_webui/routers/audio.py @@ -681,15 +681,19 @@ def get_available_models(request: Request) -> list[dict]: available_models = [] if request.app.state.config.TTS_ENGINE == "openai": # Use custom endpoint if not using the official OpenAI API URL - if not request.app.state.config.TTS_OPENAI_API_BASE_URL.startswith("https://api.openai.com"): + if not request.app.state.config.TTS_OPENAI_API_BASE_URL.startswith( + "https://api.openai.com" + ): try: - response = requests.get(f"{request.app.state.config.TTS_OPENAI_API_BASE_URL}/audio/models") + response = requests.get( + f"{request.app.state.config.TTS_OPENAI_API_BASE_URL}/audio/models" + ) response.raise_for_status() data = response.json() available_models = data.get("models", []) except Exception as e: log.error(f"Error fetching models from custom endpoint: {str(e)}") - available_models = [] + available_models = [{"id": "tts-1"}, {"id": "tts-1-hd"}] else: available_models = [{"id": "tts-1"}, {"id": "tts-1-hd"}] elif request.app.state.config.TTS_ENGINE == "elevenlabs": @@ -723,16 +727,27 @@ def get_available_voices(request) -> dict: available_voices = {} if request.app.state.config.TTS_ENGINE == "openai": # Use custom endpoint if not using the official OpenAI API URL - if not request.app.state.config.TTS_OPENAI_API_BASE_URL.startswith("https://api.openai.com"): + if not request.app.state.config.TTS_OPENAI_API_BASE_URL.startswith( + "https://api.openai.com" + ): try: - response = requests.get(f"{request.app.state.config.TTS_OPENAI_API_BASE_URL}/audio/voices") + response = requests.get( + f"{request.app.state.config.TTS_OPENAI_API_BASE_URL}/audio/voices" + ) response.raise_for_status() data = response.json() voices_list = data.get("voices", []) available_voices = {voice["id"]: voice["name"] for voice in voices_list} except Exception as e: log.error(f"Error fetching voices from custom endpoint: {str(e)}") - available_voices = {} + available_voices = { + "alloy": "alloy", + "echo": "echo", + "fable": "fable", + "onyx": "onyx", + "nova": "nova", + "shimmer": "shimmer", + } else: available_voices = { "alloy": "alloy", From a7d8ed0c6dc6b66ee9d37dc3a7d9812da71a7d7f Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Fri, 21 Feb 2025 12:11:21 -0800 Subject: [PATCH 14/27] refac --- src/lib/components/chat/Chat.svelte | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/lib/components/chat/Chat.svelte b/src/lib/components/chat/Chat.svelte index 98be06f5c..7de69f693 100644 --- a/src/lib/components/chat/Chat.svelte +++ b/src/lib/components/chat/Chat.svelte @@ -1493,7 +1493,10 @@ params?.system ?? $settings?.system ?? '', $user.name, $settings?.userLocation - ? await getAndUpdateUserLocation(localStorage.token) + ? await getAndUpdateUserLocation(localStorage.token).catch((err) => { + console.error(err); + return undefined; + }) : undefined )}${ (responseMessage?.userContext ?? null) @@ -1578,7 +1581,12 @@ variables: { ...getPromptVariables( $user.name, - $settings?.userLocation ? await getAndUpdateUserLocation(localStorage.token) : undefined + $settings?.userLocation + ? await getAndUpdateUserLocation(localStorage.token).catch((err) => { + console.error(err); + return undefined; + }) + : undefined ) }, model_item: $models.find((m) => m.id === model.id), From 642dcd4b702a8e189c0d04e6899218797763a083 Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Fri, 21 Feb 2025 13:19:11 -0800 Subject: [PATCH 15/27] fix: model import --- .../components/workspace/Models/ModelEditor.svelte | 1 - .../(app)/workspace/models/create/+page.svelte | 12 ++++++++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/lib/components/workspace/Models/ModelEditor.svelte b/src/lib/components/workspace/Models/ModelEditor.svelte index 34b5a4b7b..170c37f22 100644 --- a/src/lib/components/workspace/Models/ModelEditor.svelte +++ b/src/lib/components/workspace/Models/ModelEditor.svelte @@ -180,7 +180,6 @@ } if (model) { - console.log(model); name = model.name; await tick(); diff --git a/src/routes/(app)/workspace/models/create/+page.svelte b/src/routes/(app)/workspace/models/create/+page.svelte index fddf8277b..48fd5ccab 100644 --- a/src/routes/(app)/workspace/models/create/+page.svelte +++ b/src/routes/(app)/workspace/models/create/+page.svelte @@ -62,9 +62,17 @@ !['https://openwebui.com', 'https://www.openwebui.com', 'http://localhost:5173'].includes( event.origin ) - ) + ) { return; - model = JSON.parse(event.data); + } + + let data = JSON.parse(event.data); + + if (data?.info) { + data = data.info; + } + + model = data; }); if (window.opener ?? false) { From d50098b62272203b5999edff735e18fff91a2446 Mon Sep 17 00:00:00 2001 From: Jeannot Damoiseaux <62134006+jeannotdamoiseaux@users.noreply.github.com> Date: Fri, 21 Feb 2025 22:25:22 +0100 Subject: [PATCH 16/27] Fix: Ensure `user_oauth_groups` defaults to an empty list to prevent TypeError When the OAuth groups claim does not yield a list, `user_oauth_groups` was previously set to None, causing a TypeError during membership checks. Changed this default to an empty list (`[]`) to ensure the variable is always iterable, preventing errors for non-admin users while logging in. This fix ensures stability in the `update_user_groups` function. --- backend/open_webui/utils/oauth.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/open_webui/utils/oauth.py b/backend/open_webui/utils/oauth.py index 13835e784..0b68be2de 100644 --- a/backend/open_webui/utils/oauth.py +++ b/backend/open_webui/utils/oauth.py @@ -146,7 +146,7 @@ class OAuthManager: nested_claims = oauth_claim.split(".") for nested_claim in nested_claims: claim_data = claim_data.get(nested_claim, {}) - user_oauth_groups = claim_data if isinstance(claim_data, list) else None + user_oauth_groups = claim_data if isinstance(claim_data, list) else [] user_current_groups: list[GroupModel] = Groups.get_groups_by_member_id(user.id) all_available_groups: list[GroupModel] = Groups.get_groups() From b14e75dd6cd4887202940bb99f649540ab3d7a1f Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Fri, 21 Feb 2025 13:40:11 -0800 Subject: [PATCH 17/27] feat: added Trust Proxy Environment switch in Web Search admin settings tab. Co-Authored-By: harry zhou <67385896+harryzhou2000@users.noreply.github.com> --- backend/open_webui/routers/retrieval.py | 1 + src/lib/components/admin/Settings/WebSearch.svelte | 13 +++++++++++++ 2 files changed, 14 insertions(+) diff --git a/backend/open_webui/routers/retrieval.py b/backend/open_webui/routers/retrieval.py index 9611051b3..c2cb68c5d 100644 --- a/backend/open_webui/routers/retrieval.py +++ b/backend/open_webui/routers/retrieval.py @@ -403,6 +403,7 @@ async def get_rag_config(request: Request, user=Depends(get_admin_user)): "bing_search_v7_subscription_key": request.app.state.config.BING_SEARCH_V7_SUBSCRIPTION_KEY, "exa_api_key": request.app.state.config.EXA_API_KEY, "result_count": request.app.state.config.RAG_WEB_SEARCH_RESULT_COUNT, + "trust_env": request.app.state.config.RAG_WEB_SEARCH_TRUST_ENV, "concurrent_requests": request.app.state.config.RAG_WEB_SEARCH_CONCURRENT_REQUESTS, "domain_filter_list": request.app.state.config.RAG_WEB_SEARCH_DOMAIN_FILTER_LIST, }, diff --git a/src/lib/components/admin/Settings/WebSearch.svelte b/src/lib/components/admin/Settings/WebSearch.svelte index 84e9d0e5a..84729117b 100644 --- a/src/lib/components/admin/Settings/WebSearch.svelte +++ b/src/lib/components/admin/Settings/WebSearch.svelte @@ -130,6 +130,19 @@ +
+
{$i18n.t('Trust Proxy Environment')}
+
+ + + +
+
+ {#if webConfig.search.engine !== ''}
{#if webConfig.search.engine === 'searxng'} From 9bada6421e0e2f06a396ffbec501471aed3cc81f Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Fri, 21 Feb 2025 16:39:56 -0800 Subject: [PATCH 18/27] refac: code block image styling --- src/lib/components/chat/Messages/CodeBlock.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/components/chat/Messages/CodeBlock.svelte b/src/lib/components/chat/Messages/CodeBlock.svelte index a5d08356f..06743265a 100644 --- a/src/lib/components/chat/Messages/CodeBlock.svelte +++ b/src/lib/components/chat/Messages/CodeBlock.svelte @@ -514,7 +514,7 @@
{#each files as file} {#if file.type.startsWith('image')} - Output + Output {/if} {/each}
From 7bfa29fa815a8707f011377576c77daa74fa0db4 Mon Sep 17 00:00:00 2001 From: hopeless <99332743+pwnless@users.noreply.github.com> Date: Sat, 22 Feb 2025 12:13:14 +0800 Subject: [PATCH 19/27] Update payload.py Fixes ollama native tool calling because native tool calling content will be str '', and tool call processing will be completely ignored. --- backend/open_webui/utils/payload.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/open_webui/utils/payload.py b/backend/open_webui/utils/payload.py index 4c1bbad9a..869e70895 100644 --- a/backend/open_webui/utils/payload.py +++ b/backend/open_webui/utils/payload.py @@ -124,7 +124,7 @@ def convert_messages_openai_to_ollama(messages: list[dict]) -> list[dict]: tool_call_id = message.get("tool_call_id", None) # Check if the content is a string (just a simple message) - if isinstance(content, str): + if isinstance(content, str) and not tool_calls: # If the content is a string, it's pure text new_message["content"] = content From 50dec1207299c04717b83e7df8826155b01f2a6f Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Fri, 21 Feb 2025 22:15:22 -0800 Subject: [PATCH 20/27] refac --- backend/open_webui/routers/auths.py | 13 ------------- backend/open_webui/utils/oauth.py | 9 --------- 2 files changed, 22 deletions(-) diff --git a/backend/open_webui/routers/auths.py b/backend/open_webui/routers/auths.py index 3fa2ffe2e..b5a5a645a 100644 --- a/backend/open_webui/routers/auths.py +++ b/backend/open_webui/routers/auths.py @@ -252,14 +252,6 @@ async def ldap_auth(request: Request, response: Response, form_data: LdapForm): if not user: try: user_count = Users.get_num_users() - if ( - request.app.state.USER_COUNT - and user_count >= request.app.state.USER_COUNT - ): - raise HTTPException( - status.HTTP_403_FORBIDDEN, - detail=ERROR_MESSAGES.ACCESS_PROHIBITED, - ) role = ( "admin" @@ -439,11 +431,6 @@ async def signup(request: Request, response: Response, form_data: SignupForm): ) user_count = Users.get_num_users() - if request.app.state.USER_COUNT and user_count >= request.app.state.USER_COUNT: - raise HTTPException( - status.HTTP_403_FORBIDDEN, detail=ERROR_MESSAGES.ACCESS_PROHIBITED - ) - if not validate_email_format(form_data.email.lower()): raise HTTPException( status.HTTP_400_BAD_REQUEST, detail=ERROR_MESSAGES.INVALID_EMAIL_FORMAT diff --git a/backend/open_webui/utils/oauth.py b/backend/open_webui/utils/oauth.py index 0b68be2de..2af54c19d 100644 --- a/backend/open_webui/utils/oauth.py +++ b/backend/open_webui/utils/oauth.py @@ -315,15 +315,6 @@ class OAuthManager: if not user: user_count = Users.get_num_users() - if ( - request.app.state.USER_COUNT - and user_count >= request.app.state.USER_COUNT - ): - raise HTTPException( - 403, - detail=ERROR_MESSAGES.ACCESS_PROHIBITED, - ) - # If the user does not exist, check if signups are enabled if auth_manager_config.ENABLE_OAUTH_SIGNUP: # Check if an existing user with the same email already exists From 667d26ca12e9e640759e94f5fe69a8f88464cf16 Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Sat, 22 Feb 2025 01:16:58 -0800 Subject: [PATCH 21/27] refac --- .../components/admin/Functions/FunctionEditor.svelte | 4 ++-- src/lib/components/chat/Messages/CodeBlock.svelte | 4 ++-- src/lib/components/common/CodeEditor.svelte | 11 ++++++++--- .../components/workspace/Tools/ToolkitEditor.svelte | 4 ++-- 4 files changed, 14 insertions(+), 9 deletions(-) diff --git a/src/lib/components/admin/Functions/FunctionEditor.svelte b/src/lib/components/admin/Functions/FunctionEditor.svelte index cbdec2425..fe9b62053 100644 --- a/src/lib/components/admin/Functions/FunctionEditor.svelte +++ b/src/lib/components/admin/Functions/FunctionEditor.svelte @@ -371,10 +371,10 @@ class Pipe: value={content} lang="python" {boilerplate} - on:change={(e) => { + onChange={(e) => { _content = e.detail.value; }} - on:save={async () => { + onSave={async () => { if (formElement) { formElement.requestSubmit(); } diff --git a/src/lib/components/chat/Messages/CodeBlock.svelte b/src/lib/components/chat/Messages/CodeBlock.svelte index 06743265a..4cfaff3e5 100644 --- a/src/lib/components/chat/Messages/CodeBlock.svelte +++ b/src/lib/components/chat/Messages/CodeBlock.svelte @@ -468,10 +468,10 @@ value={code} {id} {lang} - on:save={() => { + onSave={() => { saveCode(); }} - on:change={(e) => { + onChange={(e) => { _code = e.detail.value; }} /> diff --git a/src/lib/components/common/CodeEditor.svelte b/src/lib/components/common/CodeEditor.svelte index 7d9f3a55a..d45c9eb27 100644 --- a/src/lib/components/common/CodeEditor.svelte +++ b/src/lib/components/common/CodeEditor.svelte @@ -21,6 +21,10 @@ export let boilerplate = ''; export let value = ''; + + export let onSave = () => {}; + export let onChange = () => {}; + let _value = ''; $: if (value) { @@ -75,7 +79,7 @@ }); _value = formattedCode; - dispatch('change', { value: _value }); + onChange({ value: _value }); await tick(); toast.success($i18n.t('Code formatted successfully')); @@ -94,7 +98,7 @@ EditorView.updateListener.of((e) => { if (e.docChanged) { _value = e.state.doc.toString(); - dispatch('change', { value: _value }); + onChange({ value: _value }); } }), editorTheme.of([]), @@ -170,7 +174,8 @@ const keydownHandler = async (e) => { if ((e.ctrlKey || e.metaKey) && e.key === 's') { e.preventDefault(); - dispatch('save'); + + onSave(); } // Format code when Ctrl + Shift + F is pressed diff --git a/src/lib/components/workspace/Tools/ToolkitEditor.svelte b/src/lib/components/workspace/Tools/ToolkitEditor.svelte index 60d231763..40d4715c1 100644 --- a/src/lib/components/workspace/Tools/ToolkitEditor.svelte +++ b/src/lib/components/workspace/Tools/ToolkitEditor.svelte @@ -284,10 +284,10 @@ class Tools: value={content} {boilerplate} lang="python" - on:change={(e) => { + onChange={(e) => { _content = e.detail.value; }} - on:save={() => { + onSave={() => { if (formElement) { formElement.requestSubmit(); } From 794919e91d24200f8911d307dc597a57f06843b5 Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Sat, 22 Feb 2025 01:22:17 -0800 Subject: [PATCH 22/27] refac --- .../components/chat/Messages/CodeBlock.svelte | 21 +++++++------------ .../Messages/Markdown/MarkdownTokens.svelte | 4 ++-- 2 files changed, 9 insertions(+), 16 deletions(-) diff --git a/src/lib/components/chat/Messages/CodeBlock.svelte b/src/lib/components/chat/Messages/CodeBlock.svelte index 4cfaff3e5..02879a674 100644 --- a/src/lib/components/chat/Messages/CodeBlock.svelte +++ b/src/lib/components/chat/Messages/CodeBlock.svelte @@ -1,18 +1,9 @@