Explorar el Código

Merge pull request #12715 from open-webui/dev

0.6.3
Tim Jaeryang Baek hace 3 semanas
padre
commit
8b0e565e2c
Se han modificado 100 ficheros con 3948 adiciones y 2337 borrados
  1. 40 0
      CHANGELOG.md
  2. 8 1
      backend/open_webui/__init__.py
  3. 121 55
      backend/open_webui/config.py
  4. 1 0
      backend/open_webui/constants.py
  5. 36 0
      backend/open_webui/env.py
  6. 89 54
      backend/open_webui/main.py
  7. 3 2
      backend/open_webui/models/memories.py
  8. 1 1
      backend/open_webui/retrieval/loaders/youtube.py
  9. 14 0
      backend/open_webui/retrieval/utils.py
  10. 8 8
      backend/open_webui/retrieval/web/duckduckgo.py
  11. 60 0
      backend/open_webui/retrieval/web/sougou.py
  12. 25 23
      backend/open_webui/retrieval/web/utils.py
  13. 150 14
      backend/open_webui/routers/audio.py
  14. 12 3
      backend/open_webui/routers/auths.py
  15. 6 1
      backend/open_webui/routers/chats.py
  16. 42 0
      backend/open_webui/routers/files.py
  17. 68 0
      backend/open_webui/routers/knowledge.py
  18. 3 1
      backend/open_webui/routers/memories.py
  19. 413 393
      backend/open_webui/routers/retrieval.py
  20. 14 16
      backend/open_webui/routers/tools.py
  21. 1 0
      backend/open_webui/routers/users.py
  22. 5 11
      backend/open_webui/socket/main.py
  23. 24 4
      backend/open_webui/tasks.py
  24. 52 48
      backend/open_webui/utils/middleware.py
  25. 6 3
      backend/open_webui/utils/models.py
  26. 1 1
      backend/open_webui/utils/plugin.py
  27. 13 64
      backend/open_webui/utils/redis.py
  28. 2 0
      backend/open_webui/utils/task.py
  29. 71 27
      backend/open_webui/utils/tools.py
  30. 20 17
      backend/requirements.txt
  31. 3 3
      backend/start.sh
  32. 4 3
      backend/start_windows.bat
  33. 2 2
      docker-compose.playwright.yaml
  34. 62 28
      package-lock.json
  35. 5 3
      package.json
  36. 11 7
      pyproject.toml
  37. 22 4
      src/app.css
  38. 1 1
      src/app.html
  39. 43 3
      src/lib/apis/index.ts
  40. 28 0
      src/lib/apis/knowledge/index.ts
  41. 3 30
      src/lib/apis/retrieval/index.ts
  42. 1 1
      src/lib/components/AddServerModal.svelte
  43. 42 1
      src/lib/components/admin/Settings/Audio.svelte
  44. 568 609
      src/lib/components/admin/Settings/Documents.svelte
  45. 9 1
      src/lib/components/admin/Settings/Models.svelte
  46. 241 91
      src/lib/components/admin/Settings/WebSearch.svelte
  47. 1 0
      src/lib/components/admin/Users/Groups.svelte
  48. 9 0
      src/lib/components/admin/Users/Groups/Permissions.svelte
  49. 25 0
      src/lib/components/admin/Users/UserList.svelte
  50. 113 105
      src/lib/components/chat/Artifacts.svelte
  51. 62 87
      src/lib/components/chat/Chat.svelte
  52. 3 3
      src/lib/components/chat/ChatControls.svelte
  53. 1 1
      src/lib/components/chat/ChatPlaceholder.svelte
  54. 99 107
      src/lib/components/chat/MessageInput.svelte
  55. 9 1
      src/lib/components/chat/MessageInput/Commands/Knowledge.svelte
  56. 4 1
      src/lib/components/chat/MessageInput/VoiceRecording.svelte
  57. 11 3
      src/lib/components/chat/Messages/Citations.svelte
  58. 9 1
      src/lib/components/chat/Messages/CitationsModal.svelte
  59. 2 1
      src/lib/components/chat/Messages/ContentRenderer.svelte
  60. 7 5
      src/lib/components/chat/Messages/Markdown/AlertRenderer.svelte
  61. 4 1
      src/lib/components/chat/Messages/ResponseMessage.svelte
  62. 1 1
      src/lib/components/chat/Messages/UserMessage.svelte
  63. 52 50
      src/lib/components/chat/ModelSelector.svelte
  64. 64 59
      src/lib/components/chat/ModelSelector/Selector.svelte
  65. 170 115
      src/lib/components/chat/Navbar.svelte
  66. 86 1
      src/lib/components/chat/Settings/Interface.svelte
  67. 1 1
      src/lib/components/chat/ToolServersModal.svelte
  68. 11 1
      src/lib/components/common/CodeEditor.svelte
  69. 1 1
      src/lib/components/common/Collapsible.svelte
  70. 12 8
      src/lib/components/common/FileItem.svelte
  71. 1 1
      src/lib/components/layout/Sidebar/ChannelModal.svelte
  72. 2 0
      src/lib/components/layout/Sidebar/ChatItem.svelte
  73. 1 1
      src/lib/components/workspace/Knowledge/CreateKnowledgeBase.svelte
  74. 9 1
      src/lib/components/workspace/Knowledge/KnowledgeBase.svelte
  75. 1 1
      src/lib/components/workspace/Prompts/PromptEditor.svelte
  76. 18 13
      src/lib/components/workspace/Tools/ToolkitEditor.svelte
  77. 1 1
      src/lib/components/workspace/common/AccessControl.svelte
  78. 1 1
      src/lib/components/workspace/common/AccessControlModal.svelte
  79. 28 2
      src/lib/i18n/locales/ar-BH/translation.json
  80. 28 2
      src/lib/i18n/locales/ar/translation.json
  81. 28 2
      src/lib/i18n/locales/bg-BG/translation.json
  82. 28 2
      src/lib/i18n/locales/bn-BD/translation.json
  83. 28 2
      src/lib/i18n/locales/bo-TB/translation.json
  84. 65 39
      src/lib/i18n/locales/ca-ES/translation.json
  85. 28 2
      src/lib/i18n/locales/ceb-PH/translation.json
  86. 28 2
      src/lib/i18n/locales/cs-CZ/translation.json
  87. 28 2
      src/lib/i18n/locales/da-DK/translation.json
  88. 28 2
      src/lib/i18n/locales/de-DE/translation.json
  89. 28 2
      src/lib/i18n/locales/dg-DG/translation.json
  90. 28 2
      src/lib/i18n/locales/el-GR/translation.json
  91. 28 2
      src/lib/i18n/locales/en-GB/translation.json
  92. 28 2
      src/lib/i18n/locales/en-US/translation.json
  93. 178 152
      src/lib/i18n/locales/es-ES/translation.json
  94. 28 2
      src/lib/i18n/locales/et-EE/translation.json
  95. 28 2
      src/lib/i18n/locales/eu-ES/translation.json
  96. 28 2
      src/lib/i18n/locales/fa-IR/translation.json
  97. 28 2
      src/lib/i18n/locales/fi-FI/translation.json
  98. 28 2
      src/lib/i18n/locales/fr-CA/translation.json
  99. 28 2
      src/lib/i18n/locales/fr-FR/translation.json
  100. 28 2
      src/lib/i18n/locales/he-IL/translation.json

+ 40 - 0
CHANGELOG.md

@@ -5,6 +5,46 @@ All notable changes to this project will be documented in this file.
 The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
 and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
 
+## [0.6.3] - 2025-04-12
+
+### Added
+
+- 🧪 **Auto-Artifact Detection Toggle**: Automatically detects artifacts in results—but now you can disable this behavior under advanced settings for full control.
+- 🖼️ **Widescreen Mode for Shared Chats**: Shared link conversations now support widescreen layouts—perfect for presentations or easier review across wider displays.
+- 🔁 **Reindex Knowledge Files on Demand**: Admins can now trigger reindexing of all knowledge files after changing embeddings—ensuring immediate alignment with new models for optimal RAG performance.
+- 📄 **OpenAPI YAML Format Support**: External tools can now use YAML-format OpenAPI specs—making integration simpler for developers familiar with YAML-based configurations.
+- 💬 **Message Content Copy Behavior**: Copy action now excludes 'details' tags—streamlining clipboard content when sharing or pasting summaries elsewhere.
+- 🧭 **Sougou Web Search Integration**: New search engine option added—enhancing global relevance and diversity of search sources for multilingual users.
+- 🧰 **Frontend Web Loader Engine Configuration**: Admins can now set preferred web loader engine for RAG workflows directly from the frontend—offering more control across setups.
+- 👥 **Multi-Model Chat Permission Control**: Admins can manage access to multi-model chats per user group—allowing tighter governance in team environments.
+- 🧱 **Persistent Configuration Can Be Disabled**: New environment variable lets advanced users and hosts turn off persistent configs—ideal for volatile or stateless deployments.
+- 🧠 **Elixir Code Highlighting Support**: Elixir syntax is now beautifully rendered in code blocks—perfect for developers using this language in AI or automation projects.
+- 🌐 **PWA External Manifest URL Support**: You can now define an external manifest.json—integrate Open WebUI seamlessly in managed or proxy-based PWA environments like Cloudflare Zero Trust.
+- 🧪 **Azure AI Speech-to-Text Provider Integration**: Easily transcribe large audio files (up to 200MB) with high accuracy using Microsoft's Azure STT—fully configurable in Audio Settings.
+- 🔏 **PKCE (Code Challenge Method) Support for OIDC**: Enhance your OIDC login security with Proof Key for Code Exchange—ideal for zero-trust and native client apps.
+- ✨ **General UI/UX Enhancements**: Numerous refinements across layout, styling, and tool interactions—reducing visual noise and improving overall usability across key workflows.
+- 🌍 **Translation Updates Across Multiple Languages**: Refined Catalan, Russian, Chinese (Simplified & Traditional), Hungarian, and Spanish translations for clearer navigation and instructions globally.
+
+### Fixed
+
+- 💥 **Chat Completion Error with Missing Models Resolved**: Fixed internal server error when referencing a model that doesn’t exist—ensuring graceful fallback and clear error guidance.
+- 🔧 **Correct Knowledge Base Citations Restored**: Citations generated by RAG workflows now show accurate references—ensuring verifiability in outputs from sourced content.
+- 🎙️ **Broken OGG/WebM Audio Upload Handling for OpenAI Fixed**: Uploading OGG or WebM files now converts properly to WAV before transcription—restoring accurate AI speech recognition workflows.
+- 🔐 **Tool Server 'Session' Authentication Restored**: Previously broken session auth on external tool servers is now fully functional—ensuring secure and seamless access to connected tools.
+- 🌐 **Folder-Based Chat Rename Now Updates Correctly**: Renaming chats in folders now reflects instantly everywhere—improving chat organization and clarity.
+- 📜 **KaTeX Overflow Displays Fixed**: Math expressions now stay neatly within message bounds—preserving layout consistency even with long formulas.
+- 🚫 **Stopping Ongoing Chat Fixed**: You can now return to an active (ongoing) chat and stop generation at any time—ensuring full control over sessions.
+- 🔧 **TOOL_SERVERS / TOOL_SERVER_CONNECTIONS Indexing Issue Fixed**: Fixed a mismatch between tool lists and their access paths—restoring full function and preventing confusion in tool management.
+- 🔐 **LDAP Login Handles Multiple Emails**: When LDAP returns multiple email attributes, the first valid one is now used—ensuring login success and account consistency.
+- 🧩 **Model Visibility Toggle Fix**: Toggling model visibility now works even for untouched models—letting admins smoothly manage user access across base models.
+- ⚙️ **Cross-Origin manifest.json Now Loads Properly**: Compatibility issues with Cloudflare Zero Trust (and others) resolved, allowing manifest.json to load behind authenticated proxies.
+
+### Changed
+
+- 🔒 **Default Access Scopes Set to Private for All Resources**: Models, tools, and knowledge are now private by default when created—ensuring better baseline security and visibility controls.
+- 🧱 **General Backend Refactoring for Stability**: Numerous invisible improvements enhance backend scalability, security, and maintainability—powering upcoming features with a stronger foundation.
+- 🧩 **Stable Dependency Upgrades**: Updated key platform libraries—Chromadb (0.6.3), pgvector (0.4.0), Azure Identity (1.21.0), and Youtube Transcript API (1.0.3)—for improved compatibility, functionality, and security.
+
 ## [0.6.2] - 2025-04-06
 
 ### Added

+ 8 - 1
backend/open_webui/__init__.py

@@ -73,8 +73,15 @@ def serve(
             os.environ["LD_LIBRARY_PATH"] = ":".join(LD_LIBRARY_PATH)
 
     import open_webui.main  # we need set environment variables before importing main
+    from open_webui.env import UVICORN_WORKERS  # Import the workers setting
 
-    uvicorn.run(open_webui.main.app, host=host, port=port, forwarded_allow_ips="*")
+    uvicorn.run(
+        open_webui.main.app,
+        host=host,
+        port=port,
+        forwarded_allow_ips="*",
+        workers=UVICORN_WORKERS,
+    )
 
 
 @app.command()

+ 121 - 55
backend/open_webui/config.py

@@ -201,6 +201,10 @@ def save_config(config):
 
 T = TypeVar("T")
 
+ENABLE_PERSISTENT_CONFIG = (
+    os.environ.get("ENABLE_PERSISTENT_CONFIG", "True").lower() == "true"
+)
+
 
 class PersistentConfig(Generic[T]):
     def __init__(self, env_name: str, config_path: str, env_value: T):
@@ -208,7 +212,7 @@ class PersistentConfig(Generic[T]):
         self.config_path = config_path
         self.env_value = env_value
         self.config_value = get_config_value(config_path)
-        if self.config_value is not None:
+        if self.config_value is not None and ENABLE_PERSISTENT_CONFIG:
             log.info(f"'{env_name}' loaded from the latest database entry")
             self.value = self.config_value
         else:
@@ -456,6 +460,12 @@ OAUTH_SCOPES = PersistentConfig(
     os.environ.get("OAUTH_SCOPES", "openid email profile"),
 )
 
+OAUTH_CODE_CHALLENGE_METHOD = PersistentConfig(
+    "OAUTH_CODE_CHALLENGE_METHOD",
+    "oauth.oidc.code_challenge_method",
+    os.environ.get("OAUTH_CODE_CHALLENGE_METHOD", None),
+)
+
 OAUTH_PROVIDER_NAME = PersistentConfig(
     "OAUTH_PROVIDER_NAME",
     "oauth.oidc.provider_name",
@@ -560,7 +570,7 @@ def load_oauth_providers():
                 name="microsoft",
                 client_id=MICROSOFT_CLIENT_ID.value,
                 client_secret=MICROSOFT_CLIENT_SECRET.value,
-                server_metadata_url=f"https://login.microsoftonline.com/{MICROSOFT_CLIENT_TENANT_ID.value}/v2.0/.well-known/openid-configuration",
+                server_metadata_url=f"https://login.microsoftonline.com/{MICROSOFT_CLIENT_TENANT_ID.value}/v2.0/.well-known/openid-configuration?appid={MICROSOFT_CLIENT_ID.value}",
                 client_kwargs={
                     "scope": MICROSOFT_OAUTH_SCOPE.value,
                 },
@@ -601,14 +611,27 @@ def load_oauth_providers():
     ):
 
         def oidc_oauth_register(client):
+            client_kwargs = {
+                "scope": OAUTH_SCOPES.value,
+            }
+
+            if (
+                OAUTH_CODE_CHALLENGE_METHOD.value
+                and OAUTH_CODE_CHALLENGE_METHOD.value == "S256"
+            ):
+                client_kwargs["code_challenge_method"] = "S256"
+            elif OAUTH_CODE_CHALLENGE_METHOD.value:
+                raise Exception(
+                    'Code challenge methods other than "%s" not supported. Given: "%s"'
+                    % ("S256", OAUTH_CODE_CHALLENGE_METHOD.value)
+                )
+
             client.register(
                 name="oidc",
                 client_id=OAUTH_CLIENT_ID.value,
                 client_secret=OAUTH_CLIENT_SECRET.value,
                 server_metadata_url=OPENID_PROVIDER_URL.value,
-                client_kwargs={
-                    "scope": OAUTH_SCOPES.value,
-                },
+                client_kwargs=client_kwargs,
                 redirect_uri=OPENID_REDIRECT_URI.value,
             )
 
@@ -1039,6 +1062,10 @@ USER_PERMISSIONS_CHAT_EDIT = (
     os.environ.get("USER_PERMISSIONS_CHAT_EDIT", "True").lower() == "true"
 )
 
+USER_PERMISSIONS_CHAT_MULTIPLE_MODELS = (
+    os.environ.get("USER_PERMISSIONS_CHAT_MULTIPLE_MODELS", "True").lower() == "true"
+)
+
 USER_PERMISSIONS_CHAT_TEMPORARY = (
     os.environ.get("USER_PERMISSIONS_CHAT_TEMPORARY", "True").lower() == "true"
 )
@@ -1048,6 +1075,7 @@ USER_PERMISSIONS_CHAT_TEMPORARY_ENFORCED = (
     == "true"
 )
 
+
 USER_PERMISSIONS_FEATURES_DIRECT_TOOL_SERVERS = (
     os.environ.get("USER_PERMISSIONS_FEATURES_DIRECT_TOOL_SERVERS", "False").lower()
     == "true"
@@ -1086,6 +1114,7 @@ DEFAULT_USER_PERMISSIONS = {
         "file_upload": USER_PERMISSIONS_CHAT_FILE_UPLOAD,
         "delete": USER_PERMISSIONS_CHAT_DELETE,
         "edit": USER_PERMISSIONS_CHAT_EDIT,
+        "multiple_models": USER_PERMISSIONS_CHAT_MULTIPLE_MODELS,
         "temporary": USER_PERMISSIONS_CHAT_TEMPORARY,
         "temporary_enforced": USER_PERMISSIONS_CHAT_TEMPORARY_ENFORCED,
     },
@@ -1806,12 +1835,6 @@ RAG_FILE_MAX_SIZE = PersistentConfig(
     ),
 )
 
-ENABLE_RAG_WEB_LOADER_SSL_VERIFICATION = PersistentConfig(
-    "ENABLE_RAG_WEB_LOADER_SSL_VERIFICATION",
-    "rag.enable_web_loader_ssl_verification",
-    os.environ.get("ENABLE_RAG_WEB_LOADER_SSL_VERIFICATION", "True").lower() == "true",
-)
-
 RAG_EMBEDDING_ENGINE = PersistentConfig(
     "RAG_EMBEDDING_ENGINE",
     "rag.embedding_engine",
@@ -1976,16 +1999,20 @@ YOUTUBE_LOADER_PROXY_URL = PersistentConfig(
 )
 
 
-ENABLE_RAG_WEB_SEARCH = PersistentConfig(
-    "ENABLE_RAG_WEB_SEARCH",
+####################################
+# Web Search (RAG)
+####################################
+
+ENABLE_WEB_SEARCH = PersistentConfig(
+    "ENABLE_WEB_SEARCH",
     "rag.web.search.enable",
-    os.getenv("ENABLE_RAG_WEB_SEARCH", "False").lower() == "true",
+    os.getenv("ENABLE_WEB_SEARCH", "False").lower() == "true",
 )
 
-RAG_WEB_SEARCH_ENGINE = PersistentConfig(
-    "RAG_WEB_SEARCH_ENGINE",
+WEB_SEARCH_ENGINE = PersistentConfig(
+    "WEB_SEARCH_ENGINE",
     "rag.web.search.engine",
-    os.getenv("RAG_WEB_SEARCH_ENGINE", ""),
+    os.getenv("WEB_SEARCH_ENGINE", ""),
 )
 
 BYPASS_WEB_SEARCH_EMBEDDING_AND_RETRIEVAL = PersistentConfig(
@@ -1994,10 +2021,18 @@ BYPASS_WEB_SEARCH_EMBEDDING_AND_RETRIEVAL = PersistentConfig(
     os.getenv("BYPASS_WEB_SEARCH_EMBEDDING_AND_RETRIEVAL", "False").lower() == "true",
 )
 
+
+WEB_SEARCH_RESULT_COUNT = PersistentConfig(
+    "WEB_SEARCH_RESULT_COUNT",
+    "rag.web.search.result_count",
+    int(os.getenv("WEB_SEARCH_RESULT_COUNT", "3")),
+)
+
+
 # You can provide a list of your own websites to filter after performing a web search.
 # This ensures the highest level of safety and reliability of the information sources.
-RAG_WEB_SEARCH_DOMAIN_FILTER_LIST = PersistentConfig(
-    "RAG_WEB_SEARCH_DOMAIN_FILTER_LIST",
+WEB_SEARCH_DOMAIN_FILTER_LIST = PersistentConfig(
+    "WEB_SEARCH_DOMAIN_FILTER_LIST",
     "rag.web.search.domain.filter_list",
     [
         # "wikipedia.com",
@@ -2006,6 +2041,30 @@ RAG_WEB_SEARCH_DOMAIN_FILTER_LIST = PersistentConfig(
     ],
 )
 
+WEB_SEARCH_CONCURRENT_REQUESTS = PersistentConfig(
+    "WEB_SEARCH_CONCURRENT_REQUESTS",
+    "rag.web.search.concurrent_requests",
+    int(os.getenv("WEB_SEARCH_CONCURRENT_REQUESTS", "10")),
+)
+
+WEB_LOADER_ENGINE = PersistentConfig(
+    "WEB_LOADER_ENGINE",
+    "rag.web.loader.engine",
+    os.environ.get("WEB_LOADER_ENGINE", ""),
+)
+
+ENABLE_WEB_LOADER_SSL_VERIFICATION = PersistentConfig(
+    "ENABLE_WEB_LOADER_SSL_VERIFICATION",
+    "rag.web.loader.ssl_verification",
+    os.environ.get("ENABLE_WEB_LOADER_SSL_VERIFICATION", "True").lower() == "true",
+)
+
+WEB_SEARCH_TRUST_ENV = PersistentConfig(
+    "WEB_SEARCH_TRUST_ENV",
+    "rag.web.search.trust_env",
+    os.getenv("WEB_SEARCH_TRUST_ENV", "False").lower() == "true",
+)
+
 
 SEARXNG_QUERY_URL = PersistentConfig(
     "SEARXNG_QUERY_URL",
@@ -2073,18 +2132,6 @@ SERPLY_API_KEY = PersistentConfig(
     os.getenv("SERPLY_API_KEY", ""),
 )
 
-TAVILY_API_KEY = PersistentConfig(
-    "TAVILY_API_KEY",
-    "rag.web.search.tavily_api_key",
-    os.getenv("TAVILY_API_KEY", ""),
-)
-
-TAVILY_EXTRACT_DEPTH = PersistentConfig(
-    "TAVILY_EXTRACT_DEPTH",
-    "rag.web.search.tavily_extract_depth",
-    os.getenv("TAVILY_EXTRACT_DEPTH", "basic"),
-)
-
 JINA_API_KEY = PersistentConfig(
     "JINA_API_KEY",
     "rag.web.search.jina_api_key",
@@ -2141,54 +2188,55 @@ PERPLEXITY_API_KEY = PersistentConfig(
     os.getenv("PERPLEXITY_API_KEY", ""),
 )
 
-RAG_WEB_SEARCH_RESULT_COUNT = PersistentConfig(
-    "RAG_WEB_SEARCH_RESULT_COUNT",
-    "rag.web.search.result_count",
-    int(os.getenv("RAG_WEB_SEARCH_RESULT_COUNT", "3")),
+SOUGOU_API_SID = PersistentConfig(
+    "SOUGOU_API_SID",
+    "rag.web.search.sougou_api_sid",
+    os.getenv("SOUGOU_API_SID", ""),
 )
 
-RAG_WEB_SEARCH_CONCURRENT_REQUESTS = PersistentConfig(
-    "RAG_WEB_SEARCH_CONCURRENT_REQUESTS",
-    "rag.web.search.concurrent_requests",
-    int(os.getenv("RAG_WEB_SEARCH_CONCURRENT_REQUESTS", "10")),
+SOUGOU_API_SK = PersistentConfig(
+    "SOUGOU_API_SK",
+    "rag.web.search.sougou_api_sk",
+    os.getenv("SOUGOU_API_SK", ""),
 )
 
-RAG_WEB_LOADER_ENGINE = PersistentConfig(
-    "RAG_WEB_LOADER_ENGINE",
-    "rag.web.loader.engine",
-    os.environ.get("RAG_WEB_LOADER_ENGINE", "safe_web"),
+TAVILY_API_KEY = PersistentConfig(
+    "TAVILY_API_KEY",
+    "rag.web.search.tavily_api_key",
+    os.getenv("TAVILY_API_KEY", ""),
 )
 
-RAG_WEB_SEARCH_TRUST_ENV = PersistentConfig(
-    "RAG_WEB_SEARCH_TRUST_ENV",
-    "rag.web.search.trust_env",
-    os.getenv("RAG_WEB_SEARCH_TRUST_ENV", "False").lower() == "true",
+TAVILY_EXTRACT_DEPTH = PersistentConfig(
+    "TAVILY_EXTRACT_DEPTH",
+    "rag.web.search.tavily_extract_depth",
+    os.getenv("TAVILY_EXTRACT_DEPTH", "basic"),
 )
 
-PLAYWRIGHT_WS_URI = PersistentConfig(
-    "PLAYWRIGHT_WS_URI",
-    "rag.web.loader.engine.playwright.ws.uri",
-    os.environ.get("PLAYWRIGHT_WS_URI", None),
+PLAYWRIGHT_WS_URL = PersistentConfig(
+    "PLAYWRIGHT_WS_URL",
+    "rag.web.loader.playwright_ws_url",
+    os.environ.get("PLAYWRIGHT_WS_URL", ""),
 )
 
 PLAYWRIGHT_TIMEOUT = PersistentConfig(
     "PLAYWRIGHT_TIMEOUT",
-    "rag.web.loader.engine.playwright.timeout",
-    int(os.environ.get("PLAYWRIGHT_TIMEOUT", "10")),
+    "rag.web.loader.playwright_timeout",
+    int(os.environ.get("PLAYWRIGHT_TIMEOUT", "10000")),
 )
 
 FIRECRAWL_API_KEY = PersistentConfig(
     "FIRECRAWL_API_KEY",
-    "firecrawl.api_key",
+    "rag.web.loader.firecrawl_api_key",
     os.environ.get("FIRECRAWL_API_KEY", ""),
 )
 
 FIRECRAWL_API_BASE_URL = PersistentConfig(
     "FIRECRAWL_API_BASE_URL",
-    "firecrawl.api_url",
+    "rag.web.loader.firecrawl_api_url",
     os.environ.get("FIRECRAWL_API_BASE_URL", "https://api.firecrawl.dev"),
 )
 
+
 ####################################
 # Images
 ####################################
@@ -2472,6 +2520,24 @@ AUDIO_STT_MODEL = PersistentConfig(
     os.getenv("AUDIO_STT_MODEL", ""),
 )
 
+AUDIO_STT_AZURE_API_KEY = PersistentConfig(
+    "AUDIO_STT_AZURE_API_KEY",
+    "audio.stt.azure.api_key",
+    os.getenv("AUDIO_STT_AZURE_API_KEY", ""),
+)
+
+AUDIO_STT_AZURE_REGION = PersistentConfig(
+    "AUDIO_STT_AZURE_REGION",
+    "audio.stt.azure.region",
+    os.getenv("AUDIO_STT_AZURE_REGION", ""),
+)
+
+AUDIO_STT_AZURE_LOCALES = PersistentConfig(
+    "AUDIO_STT_AZURE_LOCALES",
+    "audio.stt.azure.locales",
+    os.getenv("AUDIO_STT_AZURE_LOCALES", ""),
+)
+
 AUDIO_TTS_OPENAI_API_BASE_URL = PersistentConfig(
     "AUDIO_TTS_OPENAI_API_BASE_URL",
     "audio.tts.openai.api_base_url",

+ 1 - 0
backend/open_webui/constants.py

@@ -31,6 +31,7 @@ class ERROR_MESSAGES(str, Enum):
     USERNAME_TAKEN = (
         "Uh-oh! This username is already registered. Please choose another username."
     )
+    PASSWORD_TOO_LONG = "Uh-oh! The password you entered is too long. Please make sure your password is less than 72 bytes long."
     COMMAND_TAKEN = "Uh-oh! This command is already registered. Please choose another command string."
     FILE_EXISTS = "Uh-oh! This file is already registered. Please choose another file."
 

+ 36 - 0
backend/open_webui/env.py

@@ -326,6 +326,20 @@ REDIS_URL = os.environ.get("REDIS_URL", "")
 REDIS_SENTINEL_HOSTS = os.environ.get("REDIS_SENTINEL_HOSTS", "")
 REDIS_SENTINEL_PORT = os.environ.get("REDIS_SENTINEL_PORT", "26379")
 
+####################################
+# UVICORN WORKERS
+####################################
+
+# Number of uvicorn worker processes for handling requests
+UVICORN_WORKERS = os.environ.get("UVICORN_WORKERS", "1")
+try:
+    UVICORN_WORKERS = int(UVICORN_WORKERS)
+    if UVICORN_WORKERS < 1:
+        UVICORN_WORKERS = 1
+except ValueError:
+    UVICORN_WORKERS = 1
+    log.info(f"Invalid UVICORN_WORKERS value, defaulting to {UVICORN_WORKERS}")
+
 ####################################
 # WEBUI_AUTH (Required for security)
 ####################################
@@ -408,6 +422,21 @@ else:
     except Exception:
         AIOHTTP_CLIENT_TIMEOUT_MODEL_LIST = 10
 
+
+AIOHTTP_CLIENT_TIMEOUT_TOOL_SERVER_DATA = os.environ.get(
+    "AIOHTTP_CLIENT_TIMEOUT_TOOL_SERVER_DATA", "10"
+)
+
+if AIOHTTP_CLIENT_TIMEOUT_TOOL_SERVER_DATA == "":
+    AIOHTTP_CLIENT_TIMEOUT_TOOL_SERVER_DATA = None
+else:
+    try:
+        AIOHTTP_CLIENT_TIMEOUT_TOOL_SERVER_DATA = int(
+            AIOHTTP_CLIENT_TIMEOUT_TOOL_SERVER_DATA
+        )
+    except Exception:
+        AIOHTTP_CLIENT_TIMEOUT_TOOL_SERVER_DATA = 10
+
 ####################################
 # OFFLINE_MODE
 ####################################
@@ -460,3 +489,10 @@ OTEL_TRACES_SAMPLER = os.environ.get(
 
 PIP_OPTIONS = os.getenv("PIP_OPTIONS", "").split()
 PIP_PACKAGE_INDEX_OPTIONS = os.getenv("PIP_PACKAGE_INDEX_OPTIONS", "").split()
+
+
+####################################
+# PROGRESSIVE WEB APP OPTIONS
+####################################
+
+EXTERNAL_PWA_MANIFEST_URL = os.environ.get("EXTERNAL_PWA_MANIFEST_URL")

+ 89 - 54
backend/open_webui/main.py

@@ -148,6 +148,9 @@ from open_webui.config import (
     AUDIO_STT_MODEL,
     AUDIO_STT_OPENAI_API_BASE_URL,
     AUDIO_STT_OPENAI_API_KEY,
+    AUDIO_STT_AZURE_API_KEY,
+    AUDIO_STT_AZURE_REGION,
+    AUDIO_STT_AZURE_LOCALES,
     AUDIO_TTS_API_KEY,
     AUDIO_TTS_ENGINE,
     AUDIO_TTS_MODEL,
@@ -157,11 +160,11 @@ from open_webui.config import (
     AUDIO_TTS_VOICE,
     AUDIO_TTS_AZURE_SPEECH_REGION,
     AUDIO_TTS_AZURE_SPEECH_OUTPUT_FORMAT,
-    PLAYWRIGHT_WS_URI,
+    PLAYWRIGHT_WS_URL,
     PLAYWRIGHT_TIMEOUT,
     FIRECRAWL_API_BASE_URL,
     FIRECRAWL_API_KEY,
-    RAG_WEB_LOADER_ENGINE,
+    WEB_LOADER_ENGINE,
     WHISPER_MODEL,
     DEEPGRAM_API_KEY,
     WHISPER_MODEL_AUTO_UPDATE,
@@ -202,12 +205,13 @@ from open_webui.config import (
     YOUTUBE_LOADER_LANGUAGE,
     YOUTUBE_LOADER_PROXY_URL,
     # Retrieval (Web Search)
-    RAG_WEB_SEARCH_ENGINE,
+    ENABLE_WEB_SEARCH,
+    WEB_SEARCH_ENGINE,
     BYPASS_WEB_SEARCH_EMBEDDING_AND_RETRIEVAL,
-    RAG_WEB_SEARCH_RESULT_COUNT,
-    RAG_WEB_SEARCH_CONCURRENT_REQUESTS,
-    RAG_WEB_SEARCH_TRUST_ENV,
-    RAG_WEB_SEARCH_DOMAIN_FILTER_LIST,
+    WEB_SEARCH_RESULT_COUNT,
+    WEB_SEARCH_CONCURRENT_REQUESTS,
+    WEB_SEARCH_TRUST_ENV,
+    WEB_SEARCH_DOMAIN_FILTER_LIST,
     JINA_API_KEY,
     SEARCHAPI_API_KEY,
     SEARCHAPI_ENGINE,
@@ -225,6 +229,8 @@ from open_webui.config import (
     BRAVE_SEARCH_API_KEY,
     EXA_API_KEY,
     PERPLEXITY_API_KEY,
+    SOUGOU_API_SID,
+    SOUGOU_API_SK,
     KAGI_SEARCH_API_KEY,
     MOJEEK_SEARCH_API_KEY,
     BOCHA_SEARCH_API_KEY,
@@ -235,8 +241,7 @@ from open_webui.config import (
     ONEDRIVE_CLIENT_ID,
     ENABLE_RAG_HYBRID_SEARCH,
     ENABLE_RAG_LOCAL_WEB_FETCH,
-    ENABLE_RAG_WEB_LOADER_SSL_VERIFICATION,
-    ENABLE_RAG_WEB_SEARCH,
+    ENABLE_WEB_LOADER_SSL_VERIFICATION,
     ENABLE_GOOGLE_DRIVE_INTEGRATION,
     ENABLE_ONEDRIVE_INTEGRATION,
     UPLOAD_DIR,
@@ -340,6 +345,7 @@ from open_webui.env import (
     RESET_CONFIG_ON_START,
     OFFLINE_MODE,
     ENABLE_OTEL,
+    EXTERNAL_PWA_MANIFEST_URL,
 )
 
 
@@ -366,7 +372,11 @@ from open_webui.utils.auth import (
 from open_webui.utils.oauth import OAuthManager
 from open_webui.utils.security_headers import SecurityHeadersMiddleware
 
-from open_webui.tasks import stop_task, list_tasks  # Import from tasks.py
+from open_webui.tasks import (
+    list_task_ids_by_chat_id,
+    stop_task,
+    list_tasks,
+)  # Import from tasks.py
 
 from open_webui.utils.redis import get_sentinels_from_env
 
@@ -426,6 +436,7 @@ async def lifespan(app: FastAPI):
 
 
 app = FastAPI(
+    title="Open WebUI",
     docs_url="/docs" if ENV == "dev" else None,
     openapi_url="/openapi.json" if ENV == "dev" else None,
     redoc_url=None,
@@ -564,6 +575,7 @@ app.state.config.LDAP_CIPHERS = LDAP_CIPHERS
 
 app.state.AUTH_TRUSTED_EMAIL_HEADER = WEBUI_AUTH_TRUSTED_EMAIL_HEADER
 app.state.AUTH_TRUSTED_NAME_HEADER = WEBUI_AUTH_TRUSTED_NAME_HEADER
+app.state.EXTERNAL_PWA_MANIFEST_URL = EXTERNAL_PWA_MANIFEST_URL
 
 app.state.USER_COUNT = None
 app.state.TOOLS = {}
@@ -586,9 +598,7 @@ app.state.config.FILE_MAX_COUNT = RAG_FILE_MAX_COUNT
 app.state.config.RAG_FULL_CONTEXT = RAG_FULL_CONTEXT
 app.state.config.BYPASS_EMBEDDING_AND_RETRIEVAL = BYPASS_EMBEDDING_AND_RETRIEVAL
 app.state.config.ENABLE_RAG_HYBRID_SEARCH = ENABLE_RAG_HYBRID_SEARCH
-app.state.config.ENABLE_RAG_WEB_LOADER_SSL_VERIFICATION = (
-    ENABLE_RAG_WEB_LOADER_SSL_VERIFICATION
-)
+app.state.config.ENABLE_WEB_LOADER_SSL_VERIFICATION = ENABLE_WEB_LOADER_SSL_VERIFICATION
 
 app.state.config.CONTENT_EXTRACTION_ENGINE = CONTENT_EXTRACTION_ENGINE
 app.state.config.TIKA_SERVER_URL = TIKA_SERVER_URL
@@ -621,12 +631,16 @@ app.state.config.YOUTUBE_LOADER_LANGUAGE = YOUTUBE_LOADER_LANGUAGE
 app.state.config.YOUTUBE_LOADER_PROXY_URL = YOUTUBE_LOADER_PROXY_URL
 
 
-app.state.config.ENABLE_RAG_WEB_SEARCH = ENABLE_RAG_WEB_SEARCH
-app.state.config.RAG_WEB_SEARCH_ENGINE = RAG_WEB_SEARCH_ENGINE
+app.state.config.ENABLE_WEB_SEARCH = ENABLE_WEB_SEARCH
+app.state.config.WEB_SEARCH_ENGINE = WEB_SEARCH_ENGINE
+app.state.config.WEB_SEARCH_DOMAIN_FILTER_LIST = WEB_SEARCH_DOMAIN_FILTER_LIST
+app.state.config.WEB_SEARCH_RESULT_COUNT = WEB_SEARCH_RESULT_COUNT
+app.state.config.WEB_SEARCH_CONCURRENT_REQUESTS = WEB_SEARCH_CONCURRENT_REQUESTS
+app.state.config.WEB_LOADER_ENGINE = WEB_LOADER_ENGINE
+app.state.config.WEB_SEARCH_TRUST_ENV = WEB_SEARCH_TRUST_ENV
 app.state.config.BYPASS_WEB_SEARCH_EMBEDDING_AND_RETRIEVAL = (
     BYPASS_WEB_SEARCH_EMBEDDING_AND_RETRIEVAL
 )
-app.state.config.RAG_WEB_SEARCH_DOMAIN_FILTER_LIST = RAG_WEB_SEARCH_DOMAIN_FILTER_LIST
 
 app.state.config.ENABLE_GOOGLE_DRIVE_INTEGRATION = ENABLE_GOOGLE_DRIVE_INTEGRATION
 app.state.config.ENABLE_ONEDRIVE_INTEGRATION = ENABLE_ONEDRIVE_INTEGRATION
@@ -651,12 +665,11 @@ app.state.config.BING_SEARCH_V7_ENDPOINT = BING_SEARCH_V7_ENDPOINT
 app.state.config.BING_SEARCH_V7_SUBSCRIPTION_KEY = BING_SEARCH_V7_SUBSCRIPTION_KEY
 app.state.config.EXA_API_KEY = EXA_API_KEY
 app.state.config.PERPLEXITY_API_KEY = PERPLEXITY_API_KEY
+app.state.config.SOUGOU_API_SID = SOUGOU_API_SID
+app.state.config.SOUGOU_API_SK = SOUGOU_API_SK
+
 
-app.state.config.RAG_WEB_SEARCH_RESULT_COUNT = RAG_WEB_SEARCH_RESULT_COUNT
-app.state.config.RAG_WEB_SEARCH_CONCURRENT_REQUESTS = RAG_WEB_SEARCH_CONCURRENT_REQUESTS
-app.state.config.RAG_WEB_LOADER_ENGINE = RAG_WEB_LOADER_ENGINE
-app.state.config.RAG_WEB_SEARCH_TRUST_ENV = RAG_WEB_SEARCH_TRUST_ENV
-app.state.config.PLAYWRIGHT_WS_URI = PLAYWRIGHT_WS_URI
+app.state.config.PLAYWRIGHT_WS_URL = PLAYWRIGHT_WS_URL
 app.state.config.PLAYWRIGHT_TIMEOUT = PLAYWRIGHT_TIMEOUT
 app.state.config.FIRECRAWL_API_BASE_URL = FIRECRAWL_API_BASE_URL
 app.state.config.FIRECRAWL_API_KEY = FIRECRAWL_API_KEY
@@ -778,6 +791,10 @@ app.state.config.STT_MODEL = AUDIO_STT_MODEL
 app.state.config.WHISPER_MODEL = WHISPER_MODEL
 app.state.config.DEEPGRAM_API_KEY = DEEPGRAM_API_KEY
 
+app.state.config.AUDIO_STT_AZURE_API_KEY = AUDIO_STT_AZURE_API_KEY
+app.state.config.AUDIO_STT_AZURE_REGION = AUDIO_STT_AZURE_REGION
+app.state.config.AUDIO_STT_AZURE_LOCALES = AUDIO_STT_AZURE_LOCALES
+
 app.state.config.TTS_OPENAI_API_BASE_URL = AUDIO_TTS_OPENAI_API_BASE_URL
 app.state.config.TTS_OPENAI_API_KEY = AUDIO_TTS_OPENAI_API_KEY
 app.state.config.TTS_ENGINE = AUDIO_TTS_ENGINE
@@ -1053,6 +1070,7 @@ async def chat_completion(
     model_item = form_data.pop("model_item", {})
     tasks = form_data.pop("background_tasks", None)
 
+    metadata = {}
     try:
         if not model_item.get("direct", False):
             model_id = form_data.get("model", None)
@@ -1108,13 +1126,15 @@ async def chat_completion(
 
     except Exception as e:
         log.debug(f"Error processing chat payload: {e}")
-        Chats.upsert_message_to_chat_by_id_and_message_id(
-            metadata["chat_id"],
-            metadata["message_id"],
-            {
-                "error": {"content": str(e)},
-            },
-        )
+        if metadata.get("chat_id") and metadata.get("message_id"):
+            # Update the chat message with the error
+            Chats.upsert_message_to_chat_by_id_and_message_id(
+                metadata["chat_id"],
+                metadata["message_id"],
+                {
+                    "error": {"content": str(e)},
+                },
+            )
 
         raise HTTPException(
             status_code=status.HTTP_400_BAD_REQUEST,
@@ -1180,7 +1200,7 @@ async def chat_action(
 @app.post("/api/tasks/stop/{task_id}")
 async def stop_task_endpoint(task_id: str, user=Depends(get_verified_user)):
     try:
-        result = await stop_task(task_id)  # Use the function from tasks.py
+        result = await stop_task(task_id)
         return result
     except ValueError as e:
         raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e))
@@ -1188,7 +1208,19 @@ async def stop_task_endpoint(task_id: str, user=Depends(get_verified_user)):
 
 @app.get("/api/tasks")
 async def list_tasks_endpoint(user=Depends(get_verified_user)):
-    return {"tasks": list_tasks()}  # Use the function from tasks.py
+    return {"tasks": list_tasks()}
+
+
+@app.get("/api/tasks/chat/{chat_id}")
+async def list_tasks_by_chat_id_endpoint(chat_id: str, user=Depends(get_verified_user)):
+    chat = Chats.get_chat_by_id(chat_id)
+    if chat is None or chat.user_id != user.id:
+        return {"task_ids": []}
+
+    task_ids = list_task_ids_by_chat_id(chat_id)
+
+    print(f"Task IDs for chat {chat_id}: {task_ids}")
+    return {"task_ids": task_ids}
 
 
 ##################################
@@ -1244,7 +1276,7 @@ async def get_app_config(request: Request):
                 {
                     "enable_direct_connections": app.state.config.ENABLE_DIRECT_CONNECTIONS,
                     "enable_channels": app.state.config.ENABLE_CHANNELS,
-                    "enable_web_search": app.state.config.ENABLE_RAG_WEB_SEARCH,
+                    "enable_web_search": app.state.config.ENABLE_WEB_SEARCH,
                     "enable_code_execution": app.state.config.ENABLE_CODE_EXECUTION,
                     "enable_code_interpreter": app.state.config.ENABLE_CODE_INTERPRETER,
                     "enable_image_generation": app.state.config.ENABLE_IMAGE_GENERATION,
@@ -1390,29 +1422,32 @@ async def oauth_callback(provider: str, request: Request, response: Response):
 
 @app.get("/manifest.json")
 async def get_manifest_json():
-    return {
-        "name": app.state.WEBUI_NAME,
-        "short_name": app.state.WEBUI_NAME,
-        "description": "Open WebUI is an open, extensible, user-friendly interface for AI that adapts to your workflow.",
-        "start_url": "/",
-        "display": "standalone",
-        "background_color": "#343541",
-        "orientation": "natural",
-        "icons": [
-            {
-                "src": "/static/logo.png",
-                "type": "image/png",
-                "sizes": "500x500",
-                "purpose": "any",
-            },
-            {
-                "src": "/static/logo.png",
-                "type": "image/png",
-                "sizes": "500x500",
-                "purpose": "maskable",
-            },
-        ],
-    }
+    if app.state.EXTERNAL_PWA_MANIFEST_URL:
+        return requests.get(app.state.EXTERNAL_PWA_MANIFEST_URL).json()
+    else:
+        return {
+            "name": app.state.WEBUI_NAME,
+            "short_name": app.state.WEBUI_NAME,
+            "description": "Open WebUI is an open, extensible, user-friendly interface for AI that adapts to your workflow.",
+            "start_url": "/",
+            "display": "standalone",
+            "background_color": "#343541",
+            "orientation": "natural",
+            "icons": [
+                {
+                    "src": "/static/logo.png",
+                    "type": "image/png",
+                    "sizes": "500x500",
+                    "purpose": "any",
+                },
+                {
+                    "src": "/static/logo.png",
+                    "type": "image/png",
+                    "sizes": "500x500",
+                    "purpose": "maskable",
+                },
+            ],
+        }
 
 
 @app.get("/opensearch.xml")

+ 3 - 2
backend/open_webui/models/memories.py

@@ -63,14 +63,15 @@ class MemoriesTable:
             else:
                 return None
 
-    def update_memory_by_id(
+    def update_memory_by_id_and_user_id(
         self,
         id: str,
+        user_id: str,
         content: str,
     ) -> Optional[MemoryModel]:
         with get_db() as db:
             try:
-                db.query(Memory).filter_by(id=id).update(
+                db.query(Memory).filter_by(id=id, user_id=user_id).update(
                     {"content": content, "updated_at": int(time.time())}
                 )
                 db.commit()

+ 1 - 1
backend/open_webui/retrieval/loaders/youtube.py

@@ -110,7 +110,7 @@ class YoutubeLoader:
 
         transcript = " ".join(
             map(
-                lambda transcript_piece: transcript_piece["text"].strip(" "),
+                lambda transcript_piece: transcript_piece.text.strip(" "),
                 transcript_pieces,
             )
         )

+ 14 - 0
backend/open_webui/retrieval/utils.py

@@ -77,6 +77,7 @@ def query_doc(
     collection_name: str, query_embedding: list[float], k: int, user: UserModel = None
 ):
     try:
+        log.debug(f"query_doc:doc {collection_name}")
         result = VECTOR_DB_CLIENT.search(
             collection_name=collection_name,
             vectors=[query_embedding],
@@ -94,6 +95,7 @@ def query_doc(
 
 def get_doc(collection_name: str, user: UserModel = None):
     try:
+        log.debug(f"get_doc:doc {collection_name}")
         result = VECTOR_DB_CLIENT.get(collection_name=collection_name)
 
         if result:
@@ -116,6 +118,7 @@ def query_doc_with_hybrid_search(
     r: float,
 ) -> dict:
     try:
+        log.debug(f"query_doc_with_hybrid_search:doc {collection_name}")
         bm25_retriever = BM25Retriever.from_texts(
             texts=collection_result.documents[0],
             metadatas=collection_result.metadatas[0],
@@ -168,6 +171,7 @@ def query_doc_with_hybrid_search(
         )
         return result
     except Exception as e:
+        log.exception(f"Error querying doc {collection_name} with hybrid search: {e}")
         raise e
 
 
@@ -257,6 +261,7 @@ def query_collection(
 ) -> dict:
     results = []
     for query in queries:
+        log.debug(f"query_collection:query {query}")
         query_embedding = embedding_function(query, prefix=RAG_EMBEDDING_QUERY_PREFIX)
         for collection_name in collection_names:
             if collection_name:
@@ -292,6 +297,9 @@ def query_collection_with_hybrid_search(
     collection_results = {}
     for collection_name in collection_names:
         try:
+            log.debug(
+                f"query_collection_with_hybrid_search:VECTOR_DB_CLIENT.get:collection {collection_name}"
+            )
             collection_results[collection_name] = VECTOR_DB_CLIENT.get(
                 collection_name=collection_name
             )
@@ -613,6 +621,9 @@ def generate_openai_batch_embeddings(
     user: UserModel = None,
 ) -> Optional[list[list[float]]]:
     try:
+        log.debug(
+            f"generate_openai_batch_embeddings:model {model} batch size: {len(texts)}"
+        )
         json_data = {"input": texts, "model": model}
         if isinstance(RAG_EMBEDDING_PREFIX_FIELD_NAME, str) and isinstance(prefix, str):
             json_data[RAG_EMBEDDING_PREFIX_FIELD_NAME] = prefix
@@ -655,6 +666,9 @@ def generate_ollama_batch_embeddings(
     user: UserModel = None,
 ) -> Optional[list[list[float]]]:
     try:
+        log.debug(
+            f"generate_ollama_batch_embeddings:model {model} batch size: {len(texts)}"
+        )
         json_data = {"input": texts, "model": model}
         if isinstance(RAG_EMBEDDING_PREFIX_FIELD_NAME, str) and isinstance(prefix, str):
             json_data[RAG_EMBEDDING_PREFIX_FIELD_NAME] = prefix

+ 8 - 8
backend/open_webui/retrieval/web/duckduckgo.py

@@ -3,6 +3,7 @@ from typing import Optional
 
 from open_webui.retrieval.web.main import SearchResult, get_filtered_results
 from duckduckgo_search import DDGS
+from duckduckgo_search.exceptions import RatelimitException
 from open_webui.env import SRC_LOG_LEVELS
 
 log = logging.getLogger(__name__)
@@ -22,16 +23,15 @@ def search_duckduckgo(
         list[SearchResult]: A list of search results
     """
     # Use the DDGS context manager to create a DDGS object
+    search_results = []
     with DDGS() as ddgs:
         # Use the ddgs.text() method to perform the search
-        ddgs_gen = ddgs.text(
-            query, safesearch="moderate", max_results=count, backend="api"
-        )
-        # Check if there are search results
-        if ddgs_gen:
-            # Convert the search results into a list
-            search_results = [r for r in ddgs_gen]
-
+        try:
+            search_results = ddgs.text(
+                query, safesearch="moderate", max_results=count, backend="lite"
+            )
+        except RatelimitException as e:
+            log.error(f"RatelimitException: {e}")
     if filter_list:
         search_results = get_filtered_results(search_results, filter_list)
 

+ 60 - 0
backend/open_webui/retrieval/web/sougou.py

@@ -0,0 +1,60 @@
+import logging
+import json
+from typing import Optional, List
+
+
+from open_webui.retrieval.web.main import SearchResult, get_filtered_results
+from open_webui.env import SRC_LOG_LEVELS
+
+log = logging.getLogger(__name__)
+log.setLevel(SRC_LOG_LEVELS["RAG"])
+
+
+def search_sougou(
+    sougou_api_sid: str,
+    sougou_api_sk: str,
+    query: str,
+    count: int,
+    filter_list: Optional[List[str]] = None,
+) -> List[SearchResult]:
+    from tencentcloud.common.common_client import CommonClient
+    from tencentcloud.common import credential
+    from tencentcloud.common.exception.tencent_cloud_sdk_exception import (
+        TencentCloudSDKException,
+    )
+    from tencentcloud.common.profile.client_profile import ClientProfile
+    from tencentcloud.common.profile.http_profile import HttpProfile
+
+    try:
+        cred = credential.Credential(sougou_api_sid, sougou_api_sk)
+        http_profile = HttpProfile()
+        http_profile.endpoint = "tms.tencentcloudapi.com"
+        client_profile = ClientProfile()
+        client_profile.http_profile = http_profile
+        params = json.dumps({"Query": query, "Cnt": 20})
+        common_client = CommonClient(
+            "tms", "2020-12-29", cred, "", profile=client_profile
+        )
+        results = [
+            json.loads(page)
+            for page in common_client.call_json("SearchPro", json.loads(params))[
+                "Response"
+            ]["Pages"]
+        ]
+        sorted_results = sorted(
+            results, key=lambda x: x.get("scour", 0.0), reverse=True
+        )
+        if filter_list:
+            sorted_results = get_filtered_results(sorted_results, filter_list)
+
+        return [
+            SearchResult(
+                link=result.get("url"),
+                title=result.get("title"),
+                snippet=result.get("passage"),
+            )
+            for result in sorted_results[:count]
+        ]
+    except TencentCloudSDKException as err:
+        log.error(f"Error in Sougou search: {err}")
+        return []

+ 25 - 23
backend/open_webui/retrieval/web/utils.py

@@ -28,9 +28,9 @@ from open_webui.retrieval.loaders.tavily import TavilyLoader
 from open_webui.constants import ERROR_MESSAGES
 from open_webui.config import (
     ENABLE_RAG_LOCAL_WEB_FETCH,
-    PLAYWRIGHT_WS_URI,
+    PLAYWRIGHT_WS_URL,
     PLAYWRIGHT_TIMEOUT,
-    RAG_WEB_LOADER_ENGINE,
+    WEB_LOADER_ENGINE,
     FIRECRAWL_API_BASE_URL,
     FIRECRAWL_API_KEY,
     TAVILY_API_KEY,
@@ -584,13 +584,6 @@ class SafeWebBaseLoader(WebBaseLoader):
         return [document async for document in self.alazy_load()]
 
 
-RAG_WEB_LOADER_ENGINES = defaultdict(lambda: SafeWebBaseLoader)
-RAG_WEB_LOADER_ENGINES["playwright"] = SafePlaywrightURLLoader
-RAG_WEB_LOADER_ENGINES["safe_web"] = SafeWebBaseLoader
-RAG_WEB_LOADER_ENGINES["firecrawl"] = SafeFireCrawlLoader
-RAG_WEB_LOADER_ENGINES["tavily"] = SafeTavilyLoader
-
-
 def get_web_loader(
     urls: Union[str, Sequence[str]],
     verify_ssl: bool = True,
@@ -608,27 +601,36 @@ def get_web_loader(
         "trust_env": trust_env,
     }
 
-    if RAG_WEB_LOADER_ENGINE.value == "playwright":
+    if WEB_LOADER_ENGINE.value == "" or WEB_LOADER_ENGINE.value == "safe_web":
+        WebLoaderClass = SafeWebBaseLoader
+    if WEB_LOADER_ENGINE.value == "playwright":
+        WebLoaderClass = SafePlaywrightURLLoader
         web_loader_args["playwright_timeout"] = PLAYWRIGHT_TIMEOUT.value * 1000
-        if PLAYWRIGHT_WS_URI.value:
-            web_loader_args["playwright_ws_url"] = PLAYWRIGHT_WS_URI.value
+        if PLAYWRIGHT_WS_URL.value:
+            web_loader_args["playwright_ws_url"] = PLAYWRIGHT_WS_URL.value
 
-    if RAG_WEB_LOADER_ENGINE.value == "firecrawl":
+    if WEB_LOADER_ENGINE.value == "firecrawl":
+        WebLoaderClass = SafeFireCrawlLoader
         web_loader_args["api_key"] = FIRECRAWL_API_KEY.value
         web_loader_args["api_url"] = FIRECRAWL_API_BASE_URL.value
 
-    if RAG_WEB_LOADER_ENGINE.value == "tavily":
+    if WEB_LOADER_ENGINE.value == "tavily":
+        WebLoaderClass = SafeTavilyLoader
         web_loader_args["api_key"] = TAVILY_API_KEY.value
         web_loader_args["extract_depth"] = TAVILY_EXTRACT_DEPTH.value
 
-    # Create the appropriate WebLoader based on the configuration
-    WebLoaderClass = RAG_WEB_LOADER_ENGINES[RAG_WEB_LOADER_ENGINE.value]
-    web_loader = WebLoaderClass(**web_loader_args)
+    if WebLoaderClass:
+        web_loader = WebLoaderClass(**web_loader_args)
 
-    log.debug(
-        "Using RAG_WEB_LOADER_ENGINE %s for %s URLs",
-        web_loader.__class__.__name__,
-        len(safe_urls),
-    )
+        log.debug(
+            "Using WEB_LOADER_ENGINE %s for %s URLs",
+            web_loader.__class__.__name__,
+            len(safe_urls),
+        )
 
-    return web_loader
+        return web_loader
+    else:
+        raise ValueError(
+            f"Invalid WEB_LOADER_ENGINE: {WEB_LOADER_ENGINE.value}. "
+            "Please set it to 'safe_web', 'playwright', 'firecrawl', or 'tavily'."
+        )

+ 150 - 14
backend/open_webui/routers/audio.py

@@ -50,6 +50,8 @@ router = APIRouter()
 # Constants
 MAX_FILE_SIZE_MB = 25
 MAX_FILE_SIZE = MAX_FILE_SIZE_MB * 1024 * 1024  # Convert MB to bytes
+AZURE_MAX_FILE_SIZE_MB = 200
+AZURE_MAX_FILE_SIZE = AZURE_MAX_FILE_SIZE_MB * 1024 * 1024  # Convert MB to bytes
 
 log = logging.getLogger(__name__)
 log.setLevel(SRC_LOG_LEVELS["AUDIO"])
@@ -68,8 +70,8 @@ from pydub import AudioSegment
 from pydub.utils import mediainfo
 
 
-def is_mp4_audio(file_path):
-    """Check if the given file is an MP4 audio file."""
+def get_audio_format(file_path):
+    """Check if the given file needs to be converted to a different format."""
     if not os.path.isfile(file_path):
         log.error(f"File not found: {file_path}")
         return False
@@ -80,13 +82,17 @@ def is_mp4_audio(file_path):
         and info.get("codec_type") == "audio"
         and info.get("codec_tag_string") == "mp4a"
     ):
-        return True
-    return False
+        return "mp4"
+    elif info.get("format_name") == "ogg":
+        return "ogg"
+    elif info.get("format_name") == "matroska,webm":
+        return "webm"
+    return None
 
 
-def convert_mp4_to_wav(file_path, output_path):
-    """Convert MP4 audio file to WAV format."""
-    audio = AudioSegment.from_file(file_path, format="mp4")
+def convert_audio_to_wav(file_path, output_path, conversion_type):
+    """Convert MP4/OGG audio file to WAV format."""
+    audio = AudioSegment.from_file(file_path, format=conversion_type)
     audio.export(output_path, format="wav")
     log.info(f"Converted {file_path} to {output_path}")
 
@@ -141,6 +147,9 @@ class STTConfigForm(BaseModel):
     MODEL: str
     WHISPER_MODEL: str
     DEEPGRAM_API_KEY: str
+    AZURE_API_KEY: str
+    AZURE_REGION: str
+    AZURE_LOCALES: str
 
 
 class AudioConfigUpdateForm(BaseModel):
@@ -169,6 +178,9 @@ async def get_audio_config(request: Request, user=Depends(get_admin_user)):
             "MODEL": request.app.state.config.STT_MODEL,
             "WHISPER_MODEL": request.app.state.config.WHISPER_MODEL,
             "DEEPGRAM_API_KEY": request.app.state.config.DEEPGRAM_API_KEY,
+            "AZURE_API_KEY": request.app.state.config.AUDIO_STT_AZURE_API_KEY,
+            "AZURE_REGION": request.app.state.config.AUDIO_STT_AZURE_REGION,
+            "AZURE_LOCALES": request.app.state.config.AUDIO_STT_AZURE_LOCALES,
         },
     }
 
@@ -195,6 +207,9 @@ async def update_audio_config(
     request.app.state.config.STT_MODEL = form_data.stt.MODEL
     request.app.state.config.WHISPER_MODEL = form_data.stt.WHISPER_MODEL
     request.app.state.config.DEEPGRAM_API_KEY = form_data.stt.DEEPGRAM_API_KEY
+    request.app.state.config.AUDIO_STT_AZURE_API_KEY = form_data.stt.AZURE_API_KEY
+    request.app.state.config.AUDIO_STT_AZURE_REGION = form_data.stt.AZURE_REGION
+    request.app.state.config.AUDIO_STT_AZURE_LOCALES = form_data.stt.AZURE_LOCALES
 
     if request.app.state.config.STT_ENGINE == "":
         request.app.state.faster_whisper_model = set_faster_whisper_model(
@@ -220,6 +235,9 @@ async def update_audio_config(
             "MODEL": request.app.state.config.STT_MODEL,
             "WHISPER_MODEL": request.app.state.config.WHISPER_MODEL,
             "DEEPGRAM_API_KEY": request.app.state.config.DEEPGRAM_API_KEY,
+            "AZURE_API_KEY": request.app.state.config.AUDIO_STT_AZURE_API_KEY,
+            "AZURE_REGION": request.app.state.config.AUDIO_STT_AZURE_REGION,
+            "AZURE_LOCALES": request.app.state.config.AUDIO_STT_AZURE_LOCALES,
         },
     }
 
@@ -312,7 +330,7 @@ async def speech(request: Request, user=Depends(get_verified_user)):
                 detail = f"External: {e}"
 
             raise HTTPException(
-                status_code=getattr(r, "status", 500),
+                status_code=getattr(r, "status", 500) if r else 500,
                 detail=detail if detail else "Open WebUI: Server Connection Error",
             )
 
@@ -366,7 +384,7 @@ async def speech(request: Request, user=Depends(get_verified_user)):
                 detail = f"External: {e}"
 
             raise HTTPException(
-                status_code=getattr(r, "status", 500),
+                status_code=getattr(r, "status", 500) if r else 500,
                 detail=detail if detail else "Open WebUI: Server Connection Error",
             )
 
@@ -422,7 +440,7 @@ async def speech(request: Request, user=Depends(get_verified_user)):
                 detail = f"External: {e}"
 
             raise HTTPException(
-                status_code=getattr(r, "status", 500),
+                status_code=getattr(r, "status", 500) if r else 500,
                 detail=detail if detail else "Open WebUI: Server Connection Error",
             )
 
@@ -496,10 +514,15 @@ def transcribe(request: Request, file_path):
         log.debug(data)
         return data
     elif request.app.state.config.STT_ENGINE == "openai":
-        if is_mp4_audio(file_path):
-            os.rename(file_path, file_path.replace(".wav", ".mp4"))
-            # Convert MP4 audio file to WAV format
-            convert_mp4_to_wav(file_path.replace(".wav", ".mp4"), file_path)
+        audio_format = get_audio_format(file_path)
+        if audio_format:
+            os.rename(file_path, file_path.replace(".wav", f".{audio_format}"))
+            # Convert unsupported audio file to WAV format
+            convert_audio_to_wav(
+                file_path.replace(".wav", f".{audio_format}"),
+                file_path,
+                audio_format,
+            )
 
         r = None
         try:
@@ -598,6 +621,119 @@ def transcribe(request: Request, file_path):
                     detail = f"External: {e}"
             raise Exception(detail if detail else "Open WebUI: Server Connection Error")
 
+    elif request.app.state.config.STT_ENGINE == "azure":
+        # Check file exists and size
+        if not os.path.exists(file_path):
+            raise HTTPException(status_code=400, detail="Audio file not found")
+
+        # Check file size (Azure has a larger limit of 200MB)
+        file_size = os.path.getsize(file_path)
+        if file_size > AZURE_MAX_FILE_SIZE:
+            raise HTTPException(
+                status_code=400,
+                detail=f"File size exceeds Azure's limit of {AZURE_MAX_FILE_SIZE_MB}MB",
+            )
+
+        api_key = request.app.state.config.AUDIO_STT_AZURE_API_KEY
+        region = request.app.state.config.AUDIO_STT_AZURE_REGION
+        locales = request.app.state.config.AUDIO_STT_AZURE_LOCALES
+
+        # IF NO LOCALES, USE DEFAULTS
+        if len(locales) < 2:
+            locales = [
+                "en-US",
+                "es-ES",
+                "es-MX",
+                "fr-FR",
+                "hi-IN",
+                "it-IT",
+                "de-DE",
+                "en-GB",
+                "en-IN",
+                "ja-JP",
+                "ko-KR",
+                "pt-BR",
+                "zh-CN",
+            ]
+            locales = ",".join(locales)
+
+        if not api_key or not region:
+            raise HTTPException(
+                status_code=400,
+                detail="Azure API key and region are required for Azure STT",
+            )
+
+        r = None
+        try:
+            # Prepare the request
+            data = {
+                "definition": json.dumps(
+                    {
+                        "locales": locales.split(","),
+                        "diarization": {"maxSpeakers": 3, "enabled": True},
+                    }
+                    if locales
+                    else {}
+                )
+            }
+            url = f"https://{region}.api.cognitive.microsoft.com/speechtotext/transcriptions:transcribe?api-version=2024-11-15"
+
+            # Use context manager to ensure file is properly closed
+            with open(file_path, "rb") as audio_file:
+                r = requests.post(
+                    url=url,
+                    files={"audio": audio_file},
+                    data=data,
+                    headers={
+                        "Ocp-Apim-Subscription-Key": api_key,
+                    },
+                )
+
+            r.raise_for_status()
+            response = r.json()
+
+            # Extract transcript from response
+            if not response.get("combinedPhrases"):
+                raise ValueError("No transcription found in response")
+
+            # Get the full transcript from combinedPhrases
+            transcript = response["combinedPhrases"][0].get("text", "").strip()
+            if not transcript:
+                raise ValueError("Empty transcript in response")
+
+            data = {"text": transcript}
+
+            # Save transcript to json file (consistent with other providers)
+            transcript_file = f"{file_dir}/{id}.json"
+            with open(transcript_file, "w") as f:
+                json.dump(data, f)
+
+            log.debug(data)
+            return data
+
+        except (KeyError, IndexError, ValueError) as e:
+            log.exception("Error parsing Azure response")
+            raise HTTPException(
+                status_code=500,
+                detail=f"Failed to parse Azure response: {str(e)}",
+            )
+        except requests.exceptions.RequestException as e:
+            log.exception(e)
+            detail = None
+
+            try:
+                if r is not None and r.status_code != 200:
+                    res = r.json()
+                    if "error" in res:
+                        detail = f"External: {res['error'].get('message', '')}"
+            except Exception:
+                detail = f"External: {e}"
+
+            raise HTTPException(
+                status_code=getattr(r, "status_code", 500) if r else 500,
+                detail=detail if detail else "Open WebUI: Server Connection Error",
+            )
+
 
 def compress_audio(file_path):
     if os.path.getsize(file_path) > MAX_FILE_SIZE:

+ 12 - 3
backend/open_webui/routers/auths.py

@@ -230,11 +230,13 @@ async def ldap_auth(request: Request, response: Response, form_data: LdapForm):
 
         entry = connection_app.entries[0]
         username = str(entry[f"{LDAP_ATTRIBUTE_FOR_USERNAME}"]).lower()
-        email = str(entry[f"{LDAP_ATTRIBUTE_FOR_MAIL}"])
-        if not email or email == "" or email == "[]":
+        email = entry[f"{LDAP_ATTRIBUTE_FOR_MAIL}"]
+        if not email:
             raise HTTPException(400, "User does not have a valid email address.")
-        else:
+        elif isinstance(email, str):
             email = email.lower()
+        elif isinstance(email, list):
+            email = email[0].lower()
 
         cn = str(entry["cn"])
         user_dn = entry.entry_dn
@@ -454,6 +456,13 @@ async def signup(request: Request, response: Response, form_data: SignupForm):
             # Disable signup after the first user is created
             request.app.state.config.ENABLE_SIGNUP = False
 
+        # The password passed to bcrypt must be 72 bytes or fewer. If it is longer, it will be truncated before hashing.
+        if len(form_data.password.encode("utf-8")) > 72:
+            raise HTTPException(
+                status.HTTP_400_BAD_REQUEST,
+                detail=ERROR_MESSAGES.PASSWORD_TOO_LONG,
+            )
+
         hashed = get_password_hash(form_data.password)
         user = Auths.insert_new_auth(
             form_data.email.lower(),

+ 6 - 1
backend/open_webui/routers/chats.py

@@ -579,7 +579,12 @@ async def clone_chat_by_id(
 
 @router.post("/{id}/clone/shared", response_model=Optional[ChatResponse])
 async def clone_shared_chat_by_id(id: str, user=Depends(get_verified_user)):
-    chat = Chats.get_chat_by_share_id(id)
+
+    if user.role == "admin":
+        chat = Chats.get_chat_by_id(id)
+    else:
+        chat = Chats.get_chat_by_share_id(id)
+
     if chat:
         updated_chat = {
             **chat.chat,

+ 42 - 0
backend/open_webui/routers/files.py

@@ -1,6 +1,7 @@
 import logging
 import os
 import uuid
+from fnmatch import fnmatch
 from pathlib import Path
 from typing import Optional
 from urllib.parse import quote
@@ -177,6 +178,47 @@ async def list_files(user=Depends(get_verified_user), content: bool = Query(True
     return files
 
 
+############################
+# Search Files
+############################
+
+
+@router.get("/search", response_model=list[FileModelResponse])
+async def search_files(
+    filename: str = Query(
+        ...,
+        description="Filename pattern to search for. Supports wildcards such as '*.txt'",
+    ),
+    content: bool = Query(True),
+    user=Depends(get_verified_user),
+):
+    """
+    Search for files by filename with support for wildcard patterns.
+    """
+    # Get files according to user role
+    if user.role == "admin":
+        files = Files.get_files()
+    else:
+        files = Files.get_files_by_user_id(user.id)
+
+    # Get matching files
+    matching_files = [
+        file for file in files if fnmatch(file.filename.lower(), filename.lower())
+    ]
+
+    if not matching_files:
+        raise HTTPException(
+            status_code=status.HTTP_404_NOT_FOUND,
+            detail="No files found matching the pattern.",
+        )
+
+    if not content:
+        for file in matching_files:
+            del file.data["content"]
+
+    return matching_files
+
+
 ############################
 # Delete All Files
 ############################

+ 68 - 0
backend/open_webui/routers/knowledge.py

@@ -161,6 +161,74 @@ async def create_new_knowledge(
         )
 
 
+############################
+# ReindexKnowledgeFiles
+############################
+
+
+@router.post("/reindex", response_model=bool)
+async def reindex_knowledge_files(request: Request, user=Depends(get_verified_user)):
+    if user.role != "admin":
+        raise HTTPException(
+            status_code=status.HTTP_401_UNAUTHORIZED,
+            detail=ERROR_MESSAGES.UNAUTHORIZED,
+        )
+
+    knowledge_bases = Knowledges.get_knowledge_bases()
+
+    log.info(f"Starting reindexing for {len(knowledge_bases)} knowledge bases")
+
+    for knowledge_base in knowledge_bases:
+        try:
+            files = Files.get_files_by_ids(knowledge_base.data.get("file_ids", []))
+
+            try:
+                if VECTOR_DB_CLIENT.has_collection(collection_name=knowledge_base.id):
+                    VECTOR_DB_CLIENT.delete_collection(
+                        collection_name=knowledge_base.id
+                    )
+            except Exception as e:
+                log.error(f"Error deleting collection {knowledge_base.id}: {str(e)}")
+                raise HTTPException(
+                    status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
+                    detail=f"Error deleting vector DB collection",
+                )
+
+            failed_files = []
+            for file in files:
+                try:
+                    process_file(
+                        request,
+                        ProcessFileForm(
+                            file_id=file.id, collection_name=knowledge_base.id
+                        ),
+                        user=user,
+                    )
+                except Exception as e:
+                    log.error(
+                        f"Error processing file {file.filename} (ID: {file.id}): {str(e)}"
+                    )
+                    failed_files.append({"file_id": file.id, "error": str(e)})
+                    continue
+
+        except Exception as e:
+            log.error(f"Error processing knowledge base {knowledge_base.id}: {str(e)}")
+            raise HTTPException(
+                status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
+                detail=f"Error processing knowledge base",
+            )
+
+        if failed_files:
+            log.warning(
+                f"Failed to process {len(failed_files)} files in knowledge base {knowledge_base.id}"
+            )
+            for failed in failed_files:
+                log.warning(f"File ID: {failed['file_id']}, Error: {failed['error']}")
+
+    log.info("Reindexing completed successfully")
+    return True
+
+
 ############################
 # GetKnowledgeById
 ############################

+ 3 - 1
backend/open_webui/routers/memories.py

@@ -153,7 +153,9 @@ async def update_memory_by_id(
     form_data: MemoryUpdateModel,
     user=Depends(get_verified_user),
 ):
-    memory = Memories.update_memory_by_id(memory_id, form_data.content)
+    memory = Memories.update_memory_by_id_and_user_id(
+        memory_id, user.id, form_data.content
+    )
     if memory is None:
         raise HTTPException(status_code=404, detail="Memory not found")
 

La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 413 - 393
backend/open_webui/routers/retrieval.py


+ 14 - 16
backend/open_webui/routers/tools.py

@@ -10,11 +10,11 @@ from open_webui.models.tools import (
     ToolUserResponse,
     Tools,
 )
-from open_webui.utils.plugin import load_tools_module_by_id, replace_imports
+from open_webui.utils.plugin import load_tool_module_by_id, replace_imports
 from open_webui.config import CACHE_DIR
 from open_webui.constants import ERROR_MESSAGES
 from fastapi import APIRouter, Depends, HTTPException, Request, status
-from open_webui.utils.tools import get_tools_specs
+from open_webui.utils.tools import get_tool_specs
 from open_webui.utils.auth import get_admin_user, get_verified_user
 from open_webui.utils.access_control import has_access, has_permission
 from open_webui.env import SRC_LOG_LEVELS
@@ -45,7 +45,7 @@ async def get_tools(request: Request, user=Depends(get_verified_user)):
         )
 
     tools = Tools.get_tools()
-    for idx, server in enumerate(request.app.state.TOOL_SERVERS):
+    for server in request.app.state.TOOL_SERVERS:
         tools.append(
             ToolUserResponse(
                 **{
@@ -60,7 +60,7 @@ async def get_tools(request: Request, user=Depends(get_verified_user)):
                         .get("description", ""),
                     },
                     "access_control": request.app.state.config.TOOL_SERVER_CONNECTIONS[
-                        idx
+                        server["idx"]
                     ]
                     .get("config", {})
                     .get("access_control", None),
@@ -137,15 +137,15 @@ async def create_new_tools(
     if tools is None:
         try:
             form_data.content = replace_imports(form_data.content)
-            tools_module, frontmatter = load_tools_module_by_id(
+            tool_module, frontmatter = load_tool_module_by_id(
                 form_data.id, content=form_data.content
             )
             form_data.meta.manifest = frontmatter
 
             TOOLS = request.app.state.TOOLS
-            TOOLS[form_data.id] = tools_module
+            TOOLS[form_data.id] = tool_module
 
-            specs = get_tools_specs(TOOLS[form_data.id])
+            specs = get_tool_specs(TOOLS[form_data.id])
             tools = Tools.insert_new_tool(user.id, form_data, specs)
 
             tool_cache_dir = CACHE_DIR / "tools" / form_data.id
@@ -226,15 +226,13 @@ async def update_tools_by_id(
 
     try:
         form_data.content = replace_imports(form_data.content)
-        tools_module, frontmatter = load_tools_module_by_id(
-            id, content=form_data.content
-        )
+        tool_module, frontmatter = load_tool_module_by_id(id, content=form_data.content)
         form_data.meta.manifest = frontmatter
 
         TOOLS = request.app.state.TOOLS
-        TOOLS[id] = tools_module
+        TOOLS[id] = tool_module
 
-        specs = get_tools_specs(TOOLS[id])
+        specs = get_tool_specs(TOOLS[id])
 
         updated = {
             **form_data.model_dump(exclude={"id"}),
@@ -332,7 +330,7 @@ async def get_tools_valves_spec_by_id(
         if id in request.app.state.TOOLS:
             tools_module = request.app.state.TOOLS[id]
         else:
-            tools_module, _ = load_tools_module_by_id(id)
+            tools_module, _ = load_tool_module_by_id(id)
             request.app.state.TOOLS[id] = tools_module
 
         if hasattr(tools_module, "Valves"):
@@ -375,7 +373,7 @@ async def update_tools_valves_by_id(
     if id in request.app.state.TOOLS:
         tools_module = request.app.state.TOOLS[id]
     else:
-        tools_module, _ = load_tools_module_by_id(id)
+        tools_module, _ = load_tool_module_by_id(id)
         request.app.state.TOOLS[id] = tools_module
 
     if not hasattr(tools_module, "Valves"):
@@ -431,7 +429,7 @@ async def get_tools_user_valves_spec_by_id(
         if id in request.app.state.TOOLS:
             tools_module = request.app.state.TOOLS[id]
         else:
-            tools_module, _ = load_tools_module_by_id(id)
+            tools_module, _ = load_tool_module_by_id(id)
             request.app.state.TOOLS[id] = tools_module
 
         if hasattr(tools_module, "UserValves"):
@@ -455,7 +453,7 @@ async def update_tools_user_valves_by_id(
         if id in request.app.state.TOOLS:
             tools_module = request.app.state.TOOLS[id]
         else:
-            tools_module, _ = load_tools_module_by_id(id)
+            tools_module, _ = load_tool_module_by_id(id)
             request.app.state.TOOLS[id] = tools_module
 
         if hasattr(tools_module, "UserValves"):

+ 1 - 0
backend/open_webui/routers/users.py

@@ -88,6 +88,7 @@ class ChatPermissions(BaseModel):
     file_upload: bool = True
     delete: bool = True
     edit: bool = True
+    multiple_models: bool = True
     temporary: bool = True
     temporary_enforced: bool = False
 

+ 5 - 11
backend/open_webui/socket/main.py

@@ -9,9 +9,8 @@ from open_webui.models.users import Users, UserNameResponse
 from open_webui.models.channels import Channels
 from open_webui.models.chats import Chats
 from open_webui.utils.redis import (
-    parse_redis_sentinel_url,
     get_sentinels_from_env,
-    AsyncRedisSentinelManager,
+    get_sentinel_url_from_env,
 )
 
 from open_webui.env import (
@@ -38,15 +37,10 @@ log.setLevel(SRC_LOG_LEVELS["SOCKET"])
 
 if WEBSOCKET_MANAGER == "redis":
     if WEBSOCKET_SENTINEL_HOSTS:
-        redis_config = parse_redis_sentinel_url(WEBSOCKET_REDIS_URL)
-        mgr = AsyncRedisSentinelManager(
-            WEBSOCKET_SENTINEL_HOSTS.split(","),
-            sentinel_port=int(WEBSOCKET_SENTINEL_PORT),
-            redis_port=redis_config["port"],
-            service=redis_config["service"],
-            db=redis_config["db"],
-            username=redis_config["username"],
-            password=redis_config["password"],
+        mgr = socketio.AsyncRedisManager(
+            get_sentinel_url_from_env(
+                WEBSOCKET_REDIS_URL, WEBSOCKET_SENTINEL_HOSTS, WEBSOCKET_SENTINEL_PORT
+            )
         )
     else:
         mgr = socketio.AsyncRedisManager(WEBSOCKET_REDIS_URL)

+ 24 - 4
backend/open_webui/tasks.py

@@ -5,16 +5,23 @@ from uuid import uuid4
 
 # A dictionary to keep track of active tasks
 tasks: Dict[str, asyncio.Task] = {}
+chat_tasks = {}
 
 
-def cleanup_task(task_id: str):
+def cleanup_task(task_id: str, id=None):
     """
     Remove a completed or canceled task from the global `tasks` dictionary.
     """
     tasks.pop(task_id, None)  # Remove the task if it exists
 
+    # If an ID is provided, remove the task from the chat_tasks dictionary
+    if id and task_id in chat_tasks.get(id, []):
+        chat_tasks[id].remove(task_id)
+        if not chat_tasks[id]:  # If no tasks left for this ID, remove the entry
+            chat_tasks.pop(id, None)
 
-def create_task(coroutine):
+
+def create_task(coroutine, id=None):
     """
     Create a new asyncio task and add it to the global task dictionary.
     """
@@ -22,9 +29,15 @@ def create_task(coroutine):
     task = asyncio.create_task(coroutine)  # Create the task
 
     # Add a done callback for cleanup
-    task.add_done_callback(lambda t: cleanup_task(task_id))
-
+    task.add_done_callback(lambda t: cleanup_task(task_id, id))
     tasks[task_id] = task
+
+    # If an ID is provided, associate the task with that ID
+    if chat_tasks.get(id):
+        chat_tasks[id].append(task_id)
+    else:
+        chat_tasks[id] = [task_id]
+
     return task_id, task
 
 
@@ -42,6 +55,13 @@ def list_tasks():
     return list(tasks.keys())
 
 
+def list_task_ids_by_chat_id(id):
+    """
+    List all tasks associated with a specific ID.
+    """
+    return chat_tasks.get(id, [])
+
+
 async def stop_task(task_id: str):
     """
     Cancel a running task and remove it from the global task list.

+ 52 - 48
backend/open_webui/utils/middleware.py

@@ -235,46 +235,30 @@ async def chat_completion_tools_handler(
                 if isinstance(tool_result, str):
                     tool = tools[tool_function_name]
                     tool_id = tool.get("tool_id", "")
+
+                    tool_name = (
+                        f"{tool_id}/{tool_function_name}"
+                        if tool_id
+                        else f"{tool_function_name}"
+                    )
                     if tool.get("metadata", {}).get("citation", False) or tool.get(
                         "direct", False
                     ):
-
+                        # Citation is enabled for this tool
                         sources.append(
                             {
                                 "source": {
-                                    "name": (
-                                        f"TOOL:" + f"{tool_id}/{tool_function_name}"
-                                        if tool_id
-                                        else f"{tool_function_name}"
-                                    ),
+                                    "name": (f"TOOL:{tool_name}"),
                                 },
-                                "document": [tool_result, *tool_result_files],
-                                "metadata": [
-                                    {
-                                        "source": (
-                                            f"TOOL:" + f"{tool_id}/{tool_function_name}"
-                                            if tool_id
-                                            else f"{tool_function_name}"
-                                        )
-                                    }
-                                ],
+                                "document": [tool_result],
+                                "metadata": [{"source": (f"TOOL:{tool_name}")}],
                             }
                         )
                     else:
-                        sources.append(
-                            {
-                                "source": {},
-                                "document": [tool_result, *tool_result_files],
-                                "metadata": [
-                                    {
-                                        "source": (
-                                            f"TOOL:" + f"{tool_id}/{tool_function_name}"
-                                            if tool_id
-                                            else f"{tool_function_name}"
-                                        )
-                                    }
-                                ],
-                            }
+                        # Citation is not enabled for this tool
+                        body["messages"] = add_or_update_user_message(
+                            f"\nTool `{tool_name}` Output: {tool_result}",
+                            body["messages"],
                         )
 
                     if (
@@ -897,12 +881,16 @@ 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 = ""
-        for source_idx, source in enumerate(sources):
+        citated_file_idx = {}
+        for _, source in enumerate(sources, 1):
             if "document" in source:
-                for doc_idx, doc_context in enumerate(source["document"]):
-                    context_string += (
-                        f'<source id="{source_idx + 1}">{doc_context}</source>\n'
-                    )
+                for doc_context, doc_meta in zip(
+                    source["document"], source["metadata"]
+                ):
+                    file_id = doc_meta.get("file_id")
+                    if file_id not in citated_file_idx:
+                        citated_file_idx[file_id] = len(citated_file_idx) + 1
+                    context_string += f'<source id="{citated_file_idx[file_id]}">{doc_context}</source>\n'
 
         context_string = context_string.strip()
         prompt = get_last_user_message(form_data["messages"])
@@ -1609,6 +1597,9 @@ async def process_chat_response(
                             )
 
                             if data:
+                                if "event" in data:
+                                    await event_emitter(data.get("event", {}))
+
                                 if "selected_model_id" in data:
                                     model_id = data["selected_model_id"]
                                     Chats.upsert_message_to_chat_by_id_and_message_id(
@@ -1653,14 +1644,27 @@ async def process_chat_response(
                                             )
 
                                             if tool_call_index is not None:
-                                                if (
-                                                    len(response_tool_calls)
-                                                    <= tool_call_index
-                                                ):
+                                                # Check if the tool call already exists
+                                                current_response_tool_call = None
+                                                for (
+                                                    response_tool_call
+                                                ) in response_tool_calls:
+                                                    if (
+                                                        response_tool_call.get("index")
+                                                        == tool_call_index
+                                                    ):
+                                                        current_response_tool_call = (
+                                                            response_tool_call
+                                                        )
+                                                        break
+
+                                                if current_response_tool_call is None:
+                                                    # Add the new tool call
                                                     response_tool_calls.append(
                                                         delta_tool_call
                                                     )
                                                 else:
+                                                    # Update the existing tool call
                                                     delta_name = delta_tool_call.get(
                                                         "function", {}
                                                     ).get("name")
@@ -1671,16 +1675,14 @@ async def process_chat_response(
                                                     )
 
                                                     if delta_name:
-                                                        response_tool_calls[
-                                                            tool_call_index
-                                                        ]["function"][
-                                                            "name"
-                                                        ] += delta_name
+                                                        current_response_tool_call[
+                                                            "function"
+                                                        ]["name"] += delta_name
 
                                                     if delta_arguments:
-                                                        response_tool_calls[
-                                                            tool_call_index
-                                                        ]["function"][
+                                                        current_response_tool_call[
+                                                            "function"
+                                                        ][
                                                             "arguments"
                                                         ] += delta_arguments
 
@@ -2243,7 +2245,9 @@ async def process_chat_response(
                 await response.background()
 
         # background_tasks.add_task(post_response_handler, response, events)
-        task_id, _ = create_task(post_response_handler(response, events))
+        task_id, _ = create_task(
+            post_response_handler(response, events), id=metadata["chat_id"]
+        )
         return {"status": True, "task_id": task_id}
 
     else:

+ 6 - 3
backend/open_webui/utils/models.py

@@ -114,9 +114,12 @@ async def get_all_models(request, user: UserModel = None):
     for custom_model in custom_models:
         if custom_model.base_model_id is None:
             for model in models:
-                if (
-                    custom_model.id == model["id"]
-                    or custom_model.id == model["id"].split(":")[0]
+                if custom_model.id == model["id"] or (
+                    model.get("owned_by") == "ollama"
+                    and custom_model.id
+                    == model["id"].split(":")[
+                        0
+                    ]  # Ollama may return model ids in different formats (e.g., 'llama3' vs. 'llama3:7b')
                 ):
                     if custom_model.is_active:
                         model["name"] = custom_model.name

+ 1 - 1
backend/open_webui/utils/plugin.py

@@ -68,7 +68,7 @@ def replace_imports(content):
     return content
 
 
-def load_tools_module_by_id(tool_id, content=None):
+def load_tool_module_by_id(tool_id, content=None):
 
     if content is None:
         tool = Tools.get_tool_by_id(tool_id)

+ 13 - 64
backend/open_webui/utils/redis.py

@@ -4,7 +4,7 @@ from redis import asyncio as aioredis
 from urllib.parse import urlparse
 
 
-def parse_redis_sentinel_url(redis_url):
+def parse_redis_service_url(redis_url):
     parsed_url = urlparse(redis_url)
     if parsed_url.scheme != "redis":
         raise ValueError("Invalid Redis URL scheme. Must be 'redis'.")
@@ -20,7 +20,7 @@ def parse_redis_sentinel_url(redis_url):
 
 def get_redis_connection(redis_url, redis_sentinels, decode_responses=True):
     if redis_sentinels:
-        redis_config = parse_redis_sentinel_url(redis_url)
+        redis_config = parse_redis_service_url(redis_url)
         sentinel = redis.sentinel.Sentinel(
             redis_sentinels,
             port=redis_config["port"],
@@ -45,65 +45,14 @@ def get_sentinels_from_env(sentinel_hosts_env, sentinel_port_env):
     return []
 
 
-class AsyncRedisSentinelManager(socketio.AsyncRedisManager):
-    def __init__(
-        self,
-        sentinel_hosts,
-        sentinel_port=26379,
-        redis_port=6379,
-        service="mymaster",
-        db=0,
-        username=None,
-        password=None,
-        channel="socketio",
-        write_only=False,
-        logger=None,
-        redis_options=None,
-    ):
-        """
-        Initialize the Redis Sentinel Manager.
-        This implementation mostly replicates the __init__ of AsyncRedisManager and
-        overrides _redis_connect() with a version that uses Redis Sentinel
-
-        :param sentinel_hosts: List of Sentinel hosts
-        :param sentinel_port: Sentinel Port
-        :param redis_port: Redis Port (currently unsupported by aioredis!)
-        :param service: Master service name in Sentinel
-        :param db: Redis database to use
-        :param username: Redis username (if any) (currently unsupported by aioredis!)
-        :param password: Redis password (if any)
-        :param channel: The channel name on which the server sends and receives
-                        notifications. Must be the same in all the servers.
-        :param write_only: If set to ``True``, only initialize to emit events. The
-                           default of ``False`` initializes the class for emitting
-                           and receiving.
-        :param redis_options: additional keyword arguments to be passed to
-                              ``aioredis.from_url()``.
-        """
-        self._sentinels = [(host, sentinel_port) for host in sentinel_hosts]
-        self._redis_port = redis_port
-        self._service = service
-        self._db = db
-        self._username = username
-        self._password = password
-        self._channel = channel
-        self.redis_options = redis_options or {}
-
-        # connect and call grandparent constructor
-        self._redis_connect()
-        super(socketio.AsyncRedisManager, self).__init__(
-            channel=channel, write_only=write_only, logger=logger
-        )
-
-    def _redis_connect(self):
-        """Establish connections to Redis through Sentinel."""
-        sentinel = aioredis.sentinel.Sentinel(
-            self._sentinels,
-            port=self._redis_port,
-            db=self._db,
-            password=self._password,
-            **self.redis_options,
-        )
-
-        self.redis = sentinel.master_for(self._service)
-        self.pubsub = self.redis.pubsub(ignore_subscribe_messages=True)
+def get_sentinel_url_from_env(redis_url, sentinel_hosts_env, sentinel_port_env):
+    redis_config = parse_redis_service_url(redis_url)
+    username = redis_config["username"] or ""
+    password = redis_config["password"] or ""
+    auth_part = ""
+    if username or password:
+        auth_part = f"{username}:{password}@"
+    hosts_part = ",".join(
+        f"{host}:{sentinel_port_env}" for host in sentinel_hosts_env.split(",")
+    )
+    return f"redis+sentinel://{auth_part}{hosts_part}/{redis_config['db']}/{redis_config['service']}"

+ 2 - 0
backend/open_webui/utils/task.py

@@ -152,6 +152,8 @@ def rag_template(template: str, context: str, query: str):
     if template.strip() == "":
         template = DEFAULT_RAG_TEMPLATE
 
+    template = prompt_template(template)
+
     if "[context]" not in template and "{{CONTEXT}}" not in template:
         log.debug(
             "WARNING: The RAG template does not contain the '[context]' or '{{CONTEXT}}' placeholder."

+ 71 - 27
backend/open_webui/utils/tools.py

@@ -4,19 +4,39 @@ import re
 import inspect
 import aiohttp
 import asyncio
-
-from typing import Any, Awaitable, Callable, get_type_hints, Dict, List, Union, Optional
+import yaml
+
+from pydantic import BaseModel
+from pydantic.fields import FieldInfo
+from typing import (
+    Any,
+    Awaitable,
+    Callable,
+    get_type_hints,
+    get_args,
+    get_origin,
+    Dict,
+    List,
+    Tuple,
+    Union,
+    Optional,
+    Type,
+)
 from functools import update_wrapper, partial
 
 
 from fastapi import Request
 from pydantic import BaseModel, Field, create_model
-from langchain_core.utils.function_calling import convert_to_openai_function
+
+from langchain_core.utils.function_calling import (
+    convert_to_openai_function as convert_pydantic_model_to_openai_function_spec,
+)
 
 
 from open_webui.models.tools import Tools
 from open_webui.models.users import UserModel
-from open_webui.utils.plugin import load_tools_module_by_id
+from open_webui.utils.plugin import load_tool_module_by_id
+from open_webui.env import AIOHTTP_CLIENT_TIMEOUT_TOOL_SERVER_DATA
 
 import copy
 
@@ -55,7 +75,12 @@ def get_tools(
                 tool_server_connection = (
                     request.app.state.config.TOOL_SERVER_CONNECTIONS[server_idx]
                 )
-                tool_server_data = request.app.state.TOOL_SERVERS[server_idx]
+                tool_server_data = None
+                for server in request.app.state.TOOL_SERVERS:
+                    if server["idx"] == server_idx:
+                        tool_server_data = server
+                        break
+                assert tool_server_data is not None
                 specs = tool_server_data.get("specs", [])
 
                 for spec in specs:
@@ -112,7 +137,7 @@ def get_tools(
         else:
             module = request.app.state.TOOLS.get(tool_id, None)
             if module is None:
-                module, _ = load_tools_module_by_id(tool_id)
+                module, _ = load_tool_module_by_id(tool_id)
                 request.app.state.TOOLS[tool_id] = module
 
             extra_params["__id__"] = tool_id
@@ -233,7 +258,7 @@ def parse_docstring(docstring):
     return param_descriptions
 
 
-def function_to_pydantic_model(func: Callable) -> type[BaseModel]:
+def convert_function_to_pydantic_model(func: Callable) -> type[BaseModel]:
     """
     Converts a Python function's type hints and docstring to a Pydantic model,
     including support for nested types, default values, and descriptions.
@@ -250,45 +275,57 @@ def function_to_pydantic_model(func: Callable) -> type[BaseModel]:
     parameters = signature.parameters
 
     docstring = func.__doc__
-    descriptions = parse_docstring(docstring)
 
-    tool_description = parse_description(docstring)
+    description = parse_description(docstring)
+    function_descriptions = parse_docstring(docstring)
 
     field_defs = {}
     for name, param in parameters.items():
+
         type_hint = type_hints.get(name, Any)
         default_value = param.default if param.default is not param.empty else ...
-        description = descriptions.get(name, None)
-        if not description:
+
+        description = function_descriptions.get(name, None)
+
+        if description:
+            field_defs[name] = type_hint, Field(default_value, description=description)
+        else:
             field_defs[name] = type_hint, default_value
-            continue
-        field_defs[name] = type_hint, Field(default_value, description=description)
 
     model = create_model(func.__name__, **field_defs)
-    model.__doc__ = tool_description
+    model.__doc__ = description
 
     return model
 
 
-def get_callable_attributes(tool: object) -> list[Callable]:
+def get_functions_from_tool(tool: object) -> list[Callable]:
     return [
         getattr(tool, func)
         for func in dir(tool)
-        if callable(getattr(tool, func))
-        and not func.startswith("__")
-        and not inspect.isclass(getattr(tool, func))
+        if callable(
+            getattr(tool, func)
+        )  # checks if the attribute is callable (a method or function).
+        and not func.startswith(
+            "__"
+        )  # filters out special (dunder) methods like init, str, etc. — these are usually built-in functions of an object that you might not need to use directly.
+        and not inspect.isclass(
+            getattr(tool, func)
+        )  # ensures that the callable is not a class itself, just a method or function.
     ]
 
 
-def get_tools_specs(tool_class: object) -> list[dict]:
-    function_model_list = map(
-        function_to_pydantic_model, get_callable_attributes(tool_class)
+def get_tool_specs(tool_module: object) -> list[dict]:
+    function_models = map(
+        convert_function_to_pydantic_model, get_functions_from_tool(tool_module)
     )
-    return [
-        convert_to_openai_function(function_model)
-        for function_model in function_model_list
+
+    specs = [
+        convert_pydantic_model_to_openai_function_spec(function_model)
+        for function_model in function_models
     ]
 
+    return specs
+
 
 def resolve_schema(schema, components):
     """
@@ -393,14 +430,21 @@ async def get_tool_server_data(token: str, url: str) -> Dict[str, Any]:
 
     error = None
     try:
-        async with aiohttp.ClientSession() as session:
+        timeout = aiohttp.ClientTimeout(total=AIOHTTP_CLIENT_TIMEOUT_TOOL_SERVER_DATA)
+        async with aiohttp.ClientSession(timeout=timeout) as session:
             async with session.get(url, headers=headers) as response:
                 if response.status != 200:
                     error_body = await response.json()
                     raise Exception(error_body)
-                res = await response.json()
+
+                # Check if URL ends with .yaml or .yml to determine format
+                if url.lower().endswith((".yaml", ".yml")):
+                    text_content = await response.text()
+                    res = yaml.safe_load(text_content)
+                else:
+                    res = await response.json()
     except Exception as err:
-        print("Error:", err)
+        log.exception(f"Could not fetch tool server spec from {url}")
         if isinstance(err, dict) and "detail" in err:
             error = err["detail"]
         else:

+ 20 - 17
backend/requirements.txt

@@ -3,7 +3,7 @@ uvicorn[standard]==0.34.0
 pydantic==2.10.6
 python-multipart==0.0.20
 
-python-socketio==5.11.3
+python-socketio==5.13.0
 python-jose==3.4.0
 passlib[bcrypt]==1.7.4
 
@@ -18,7 +18,7 @@ alembic==1.14.0
 peewee==3.17.9
 peewee-migrate==1.12.2
 psycopg2-binary==2.9.9
-pgvector==0.3.5
+pgvector==0.4.0
 PyMySQL==1.1.1
 bcrypt==4.3.0
 
@@ -44,7 +44,7 @@ langchain==0.3.19
 langchain-community==0.3.18
 
 fake-useragent==2.1.0
-chromadb==0.6.2
+chromadb==0.6.3
 pymilvus==2.5.0
 qdrant-client~=1.12.0
 opensearch-py==2.8.0
@@ -93,12 +93,12 @@ authlib==1.4.1
 
 black==25.1.0
 langfuse==2.44.0
-youtube-transcript-api==0.6.3
+youtube-transcript-api==1.0.3
 pytube==15.0.0
 
 extract_msg
 pydub
-duckduckgo-search~=7.3.2
+duckduckgo-search~=8.0.0
 
 ## Google Drive
 google-api-python-client
@@ -113,7 +113,7 @@ pytest-docker~=3.1.1
 googleapis-common-protos==1.63.2
 google-cloud-storage==2.19.0
 
-azure-identity==1.20.0
+azure-identity==1.21.0
 azure-storage-blob==12.24.1
 
 
@@ -123,15 +123,18 @@ ldap3==2.9.1
 ## Firecrawl
 firecrawl-py==1.12.0
 
+# Sougou API SDK(Tencentcloud SDK)
+tencentcloud-sdk-python==3.0.1336
+
 ## Trace
-opentelemetry-api==1.30.0
-opentelemetry-sdk==1.30.0
-opentelemetry-exporter-otlp==1.30.0
-opentelemetry-instrumentation==0.51b0
-opentelemetry-instrumentation-fastapi==0.51b0
-opentelemetry-instrumentation-sqlalchemy==0.51b0
-opentelemetry-instrumentation-redis==0.51b0
-opentelemetry-instrumentation-requests==0.51b0
-opentelemetry-instrumentation-logging==0.51b0
-opentelemetry-instrumentation-httpx==0.51b0
-opentelemetry-instrumentation-aiohttp-client==0.51b0
+opentelemetry-api==1.31.1
+opentelemetry-sdk==1.31.1
+opentelemetry-exporter-otlp==1.31.1
+opentelemetry-instrumentation==0.52b1
+opentelemetry-instrumentation-fastapi==0.52b1
+opentelemetry-instrumentation-sqlalchemy==0.52b1
+opentelemetry-instrumentation-redis==0.52b1
+opentelemetry-instrumentation-requests==0.52b1
+opentelemetry-instrumentation-logging==0.52b1
+opentelemetry-instrumentation-httpx==0.52b1
+opentelemetry-instrumentation-aiohttp-client==0.52b1

+ 3 - 3
backend/start.sh

@@ -4,8 +4,8 @@ SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
 cd "$SCRIPT_DIR" || exit
 
 # Add conditional Playwright browser installation
-if [[ "${RAG_WEB_LOADER_ENGINE,,}" == "playwright" ]]; then
-    if [[ -z "${PLAYWRIGHT_WS_URI}" ]]; then
+if [[ "${WEB_LOADER_ENGINE,,}" == "playwright" ]]; then
+    if [[ -z "${PLAYWRIGHT_WS_URL}" ]]; then
         echo "Installing Playwright browsers..."
         playwright install chromium
         playwright install-deps chromium
@@ -65,4 +65,4 @@ if [ -n "$SPACE_ID" ]; then
   export WEBUI_URL=${SPACE_HOST}
 fi
 
-WEBUI_SECRET_KEY="$WEBUI_SECRET_KEY" exec uvicorn open_webui.main:app --host "$HOST" --port "$PORT" --forwarded-allow-ips '*'
+WEBUI_SECRET_KEY="$WEBUI_SECRET_KEY" exec uvicorn open_webui.main:app --host "$HOST" --port "$PORT" --forwarded-allow-ips '*' --workers "${UVICORN_WORKERS:-1}"

+ 4 - 3
backend/start_windows.bat

@@ -7,8 +7,8 @@ SET "SCRIPT_DIR=%~dp0"
 cd /d "%SCRIPT_DIR%" || exit /b
 
 :: Add conditional Playwright browser installation
-IF /I "%RAG_WEB_LOADER_ENGINE%" == "playwright" (
-    IF "%PLAYWRIGHT_WS_URI%" == "" (
+IF /I "%WEB_LOADER_ENGINE%" == "playwright" (
+    IF "%PLAYWRIGHT_WS_URL%" == "" (
         echo Installing Playwright browsers...
         playwright install chromium
         playwright install-deps chromium
@@ -41,5 +41,6 @@ IF "%WEBUI_SECRET_KEY%%WEBUI_JWT_SECRET_KEY%" == " " (
 
 :: Execute uvicorn
 SET "WEBUI_SECRET_KEY=%WEBUI_SECRET_KEY%"
-uvicorn open_webui.main:app --host "%HOST%" --port "%PORT%" --forwarded-allow-ips '*' --ws auto
+IF "%UVICORN_WORKERS%"=="" SET UVICORN_WORKERS=1
+uvicorn open_webui.main:app --host "%HOST%" --port "%PORT%" --forwarded-allow-ips '*' --workers %UVICORN_WORKERS% --ws auto
 :: For ssl user uvicorn open_webui.main:app --host "%HOST%" --port "%PORT%" --forwarded-allow-ips '*' --ssl-keyfile "key.pem" --ssl-certfile "cert.pem" --ws auto

+ 2 - 2
docker-compose.playwright.yaml

@@ -6,5 +6,5 @@ services:
 
   open-webui:
     environment:
-      - 'RAG_WEB_LOADER_ENGINE=playwright'
-      - 'PLAYWRIGHT_WS_URI=ws://playwright:3000'
+      - 'WEB_LOADER_ENGINE=playwright'
+      - 'PLAYWRIGHT_WS_URL=ws://playwright:3000'

+ 62 - 28
package-lock.json

@@ -1,12 +1,12 @@
 {
 	"name": "open-webui",
-	"version": "0.6.2",
+	"version": "0.6.3",
 	"lockfileVersion": 3,
 	"requires": true,
 	"packages": {
 		"": {
 			"name": "open-webui",
-			"version": "0.6.2",
+			"version": "0.6.3",
 			"dependencies": {
 				"@azure/msal-browser": "^4.5.0",
 				"@codemirror/lang-javascript": "^6.2.2",
@@ -23,12 +23,13 @@
 				"@tiptap/extension-highlight": "^2.10.0",
 				"@tiptap/extension-placeholder": "^2.10.0",
 				"@tiptap/extension-typography": "^2.10.0",
-				"@tiptap/pm": "^2.10.0",
+				"@tiptap/pm": "^2.11.7",
 				"@tiptap/starter-kit": "^2.10.0",
 				"@xyflow/svelte": "^0.1.19",
 				"async": "^3.2.5",
 				"bits-ui": "^0.19.7",
 				"codemirror": "^6.0.1",
+				"codemirror-lang-elixir": "^4.0.0",
 				"codemirror-lang-hcl": "^0.0.0-beta.2",
 				"crc-32": "^1.2.2",
 				"dayjs": "^1.11.10",
@@ -69,7 +70,8 @@
 				"turndown": "^7.2.0",
 				"undici": "^7.3.0",
 				"uuid": "^9.0.1",
-				"vite-plugin-static-copy": "^2.2.0"
+				"vite-plugin-static-copy": "^2.2.0",
+				"yaml": "^2.7.1"
 			},
 			"devDependencies": {
 				"@sveltejs/adapter-auto": "3.2.2",
@@ -3042,9 +3044,9 @@
 			}
 		},
 		"node_modules/@tiptap/pm": {
-			"version": "2.10.0",
-			"resolved": "https://registry.npmjs.org/@tiptap/pm/-/pm-2.10.0.tgz",
-			"integrity": "sha512-ohshlWf4MlW6D3rQkNQnhmiQ2w4pwRoQcJmTPt8UJoIDGkeKmZh494fQp4Aeh80XuGd81SsCv//1HJeyaeHJYQ==",
+			"version": "2.11.7",
+			"resolved": "https://registry.npmjs.org/@tiptap/pm/-/pm-2.11.7.tgz",
+			"integrity": "sha512-7gEEfz2Q6bYKXM07vzLUD0vqXFhC5geWRA6LCozTiLdVFDdHWiBrvb2rtkL5T7mfLq03zc1QhH7rI3F6VntOEA==",
 			"license": "MIT",
 			"dependencies": {
 				"prosemirror-changeset": "^2.2.1",
@@ -3061,10 +3063,10 @@
 				"prosemirror-schema-basic": "^1.2.3",
 				"prosemirror-schema-list": "^1.4.1",
 				"prosemirror-state": "^1.4.3",
-				"prosemirror-tables": "^1.6.1",
+				"prosemirror-tables": "^1.6.4",
 				"prosemirror-trailing-node": "^3.0.0",
 				"prosemirror-transform": "^1.10.2",
-				"prosemirror-view": "^1.36.0"
+				"prosemirror-view": "^1.37.0"
 			},
 			"funding": {
 				"type": "github",
@@ -4625,6 +4627,15 @@
 				"@codemirror/view": "^6.0.0"
 			}
 		},
+		"node_modules/codemirror-lang-elixir": {
+			"version": "4.0.0",
+			"resolved": "https://registry.npmjs.org/codemirror-lang-elixir/-/codemirror-lang-elixir-4.0.0.tgz",
+			"integrity": "sha512-mzFesxo/t6KOxwnkqVd34R/q7yk+sMtHh6vUKGAvjwHmpL7bERHB+vQAsmU/nqrndkwVeJEHWGw/z/ybfdiudA==",
+			"dependencies": {
+				"@codemirror/language": "^6.0.0",
+				"lezer-elixir": "^1.0.0"
+			}
+		},
 		"node_modules/codemirror-lang-hcl": {
 			"version": "0.0.0-beta.2",
 			"resolved": "https://registry.npmjs.org/codemirror-lang-hcl/-/codemirror-lang-hcl-0.0.0-beta.2.tgz",
@@ -7611,6 +7622,15 @@
 				"node": ">= 0.8.0"
 			}
 		},
+		"node_modules/lezer-elixir": {
+			"version": "1.1.2",
+			"resolved": "https://registry.npmjs.org/lezer-elixir/-/lezer-elixir-1.1.2.tgz",
+			"integrity": "sha512-K3yPMJcNhqCL6ugr5NkgOC1g37rcOM38XZezO9lBXy0LwWFd8zdWXfmRbY829vZVk0OGCQoI02yDWp9FF2OWZA==",
+			"dependencies": {
+				"@lezer/highlight": "^1.2.0",
+				"@lezer/lr": "^1.3.0"
+			}
+		},
 		"node_modules/lightningcss": {
 			"version": "1.29.1",
 			"resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.29.1.tgz",
@@ -9495,6 +9515,16 @@
 				"node": ">=10"
 			}
 		},
+		"node_modules/postcss-load-config/node_modules/yaml": {
+			"version": "1.10.2",
+			"resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz",
+			"integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==",
+			"dev": true,
+			"license": "ISC",
+			"engines": {
+				"node": ">= 6"
+			}
+		},
 		"node_modules/postcss-safe-parser": {
 			"version": "6.0.0",
 			"resolved": "https://registry.npmjs.org/postcss-safe-parser/-/postcss-safe-parser-6.0.0.tgz",
@@ -9783,9 +9813,10 @@
 			}
 		},
 		"node_modules/prosemirror-model": {
-			"version": "1.23.0",
-			"resolved": "https://registry.npmjs.org/prosemirror-model/-/prosemirror-model-1.23.0.tgz",
-			"integrity": "sha512-Q/fgsgl/dlOAW9ILu4OOhYWQbc7TQd4BwKH/RwmUjyVf8682Be4zj3rOYdLnYEcGzyg8LL9Q5IWYKD8tdToreQ==",
+			"version": "1.25.0",
+			"resolved": "https://registry.npmjs.org/prosemirror-model/-/prosemirror-model-1.25.0.tgz",
+			"integrity": "sha512-/8XUmxWf0pkj2BmtqZHYJipTBMHIdVjuvFzMvEoxrtyGNmfvdhBiRwYt/eFwy2wA9DtBW3RLqvZnjurEkHaFCw==",
+			"license": "MIT",
 			"dependencies": {
 				"orderedmap": "^2.0.0"
 			}
@@ -9819,16 +9850,16 @@
 			}
 		},
 		"node_modules/prosemirror-tables": {
-			"version": "1.6.1",
-			"resolved": "https://registry.npmjs.org/prosemirror-tables/-/prosemirror-tables-1.6.1.tgz",
-			"integrity": "sha512-p8WRJNA96jaNQjhJolmbxTzd6M4huRE5xQ8OxjvMhQUP0Nzpo4zz6TztEiwk6aoqGBhz9lxRWR1yRZLlpQN98w==",
+			"version": "1.6.4",
+			"resolved": "https://registry.npmjs.org/prosemirror-tables/-/prosemirror-tables-1.6.4.tgz",
+			"integrity": "sha512-TkDY3Gw52gRFRfRn2f4wJv5WOgAOXLJA2CQJYIJ5+kdFbfj3acR4JUW6LX2e1hiEBiUwvEhzH5a3cZ5YSztpIA==",
 			"license": "MIT",
 			"dependencies": {
-				"prosemirror-keymap": "^1.1.2",
-				"prosemirror-model": "^1.8.1",
-				"prosemirror-state": "^1.3.1",
-				"prosemirror-transform": "^1.2.1",
-				"prosemirror-view": "^1.13.3"
+				"prosemirror-keymap": "^1.2.2",
+				"prosemirror-model": "^1.24.1",
+				"prosemirror-state": "^1.4.3",
+				"prosemirror-transform": "^1.10.2",
+				"prosemirror-view": "^1.37.2"
 			}
 		},
 		"node_modules/prosemirror-trailing-node": {
@@ -9856,9 +9887,9 @@
 			}
 		},
 		"node_modules/prosemirror-view": {
-			"version": "1.36.0",
-			"resolved": "https://registry.npmjs.org/prosemirror-view/-/prosemirror-view-1.36.0.tgz",
-			"integrity": "sha512-U0GQd5yFvV5qUtT41X1zCQfbw14vkbbKwLlQXhdylEmgpYVHkefXYcC4HHwWOfZa3x6Y8wxDLUBv7dxN5XQ3nA==",
+			"version": "1.39.1",
+			"resolved": "https://registry.npmjs.org/prosemirror-view/-/prosemirror-view-1.39.1.tgz",
+			"integrity": "sha512-GhLxH1xwnqa5VjhJ29LfcQITNDp+f1jzmMPXQfGW9oNrF0lfjPzKvV5y/bjIQkyKpwCX3Fp+GA4dBpMMk8g+ZQ==",
 			"license": "MIT",
 			"dependencies": {
 				"prosemirror-model": "^1.20.0",
@@ -13011,12 +13042,15 @@
 			}
 		},
 		"node_modules/yaml": {
-			"version": "1.10.2",
-			"resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz",
-			"integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==",
-			"dev": true,
+			"version": "2.7.1",
+			"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.1.tgz",
+			"integrity": "sha512-10ULxpnOCQXxJvBgxsn9ptjq6uviG/htZKk9veJGhlqn3w/DxQ631zFF+nlQXLwmImeS5amR2dl2U8sg6U9jsQ==",
+			"license": "ISC",
+			"bin": {
+				"yaml": "bin.mjs"
+			},
 			"engines": {
-				"node": ">= 6"
+				"node": ">= 14"
 			}
 		},
 		"node_modules/yauzl": {

+ 5 - 3
package.json

@@ -1,6 +1,6 @@
 {
 	"name": "open-webui",
-	"version": "0.6.2",
+	"version": "0.6.3",
 	"private": true,
 	"scripts": {
 		"dev": "npm run pyodide:fetch && vite dev --host",
@@ -66,13 +66,14 @@
 		"@tiptap/extension-highlight": "^2.10.0",
 		"@tiptap/extension-placeholder": "^2.10.0",
 		"@tiptap/extension-typography": "^2.10.0",
-		"@tiptap/pm": "^2.10.0",
+		"@tiptap/pm": "^2.11.7",
 		"@tiptap/starter-kit": "^2.10.0",
 		"@xyflow/svelte": "^0.1.19",
 		"async": "^3.2.5",
 		"bits-ui": "^0.19.7",
 		"codemirror": "^6.0.1",
 		"codemirror-lang-hcl": "^0.0.0-beta.2",
+		"codemirror-lang-elixir": "^4.0.0",
 		"crc-32": "^1.2.2",
 		"dayjs": "^1.11.10",
 		"dompurify": "^3.1.6",
@@ -112,7 +113,8 @@
 		"turndown": "^7.2.0",
 		"undici": "^7.3.0",
 		"uuid": "^9.0.1",
-		"vite-plugin-static-copy": "^2.2.0"
+		"vite-plugin-static-copy": "^2.2.0",
+		"yaml": "^2.7.1"
 	},
 	"engines": {
 		"node": ">=18.13.0 <=22.x.x",

+ 11 - 7
pyproject.toml

@@ -9,9 +9,9 @@ dependencies = [
     "fastapi==0.115.7",
     "uvicorn[standard]==0.34.0",
     "pydantic==2.10.6",
-    "python-multipart==0.0.18",
+    "python-multipart==0.0.20",
 
-    "python-socketio==5.11.3",
+    "python-socketio==5.13.0",
     "python-jose==3.4.0",
     "passlib[bcrypt]==1.7.4",
 
@@ -26,7 +26,7 @@ dependencies = [
     "peewee==3.17.9",
     "peewee-migrate==1.12.2",
     "psycopg2-binary==2.9.9",
-    "pgvector==0.3.5",
+    "pgvector==0.4.0",
     "PyMySQL==1.1.1",
     "bcrypt==4.3.0",
 
@@ -52,7 +52,7 @@ dependencies = [
     "langchain-community==0.3.18",
 
     "fake-useragent==2.1.0",
-    "chromadb==0.6.2",
+    "chromadb==0.6.3",
     "pymilvus==2.5.0",
     "qdrant-client~=1.12.0",
     "opensearch-py==2.8.0",
@@ -99,12 +99,12 @@ dependencies = [
 
     "black==25.1.0",
     "langfuse==2.44.0",
-    "youtube-transcript-api==0.6.3",
+    "youtube-transcript-api==1.0.3",
     "pytube==15.0.0",
 
     "extract_msg",
     "pydub",
-    "duckduckgo-search~=7.3.2",
+    "duckduckgo-search~=8.0.0",
 
     "google-api-python-client",
     "google-auth-httplib2",
@@ -113,7 +113,6 @@ dependencies = [
     "docker~=7.1.0",
     "pytest~=8.3.2",
     "pytest-docker~=3.1.1",
-    "moto[s3]>=5.0.26",
 
     "googleapis-common-protos==1.63.2",
     "google-cloud-storage==2.19.0",
@@ -125,7 +124,12 @@ dependencies = [
 
     "firecrawl-py==1.12.0",
 
+    "tencentcloud-sdk-python==3.0.1336",
+
     "gcp-storage-emulator>=2024.8.3",
+
+    "moto[s3]>=5.0.26",
+
 ]
 readme = "README.md"
 requires-python = ">= 3.11, < 3.13.0a1"

+ 22 - 4
src/app.css

@@ -91,10 +91,6 @@ textarea::placeholder {
 	-webkit-app-region: no-drag;
 }
 
-iframe {
-	@apply rounded-lg;
-}
-
 li p {
 	display: inline;
 }
@@ -222,6 +218,28 @@ input[type='number'] {
 	-moz-appearance: textfield; /* Firefox */
 }
 
+.katex-display {
+	@apply overflow-y-hidden overflow-x-auto max-w-full;
+}
+
+.katex-display::-webkit-scrollbar {
+	height: 0.4rem;
+	width: 0.4rem;
+}
+
+.katex-display:active::-webkit-scrollbar-thumb,
+.katex-display:focus::-webkit-scrollbar-thumb,
+.katex-display:hover::-webkit-scrollbar-thumb {
+	visibility: visible;
+}
+.katex-display::-webkit-scrollbar-thumb {
+	visibility: hidden;
+}
+
+.katex-display::-webkit-scrollbar-corner {
+	display: none;
+}
+
 .cm-editor {
 	height: 100%;
 	width: 100%;

+ 1 - 1
src/app.html

@@ -9,7 +9,7 @@
 		<link rel="apple-touch-icon" sizes="180x180" href="/static/apple-touch-icon.png" />
 		<meta name="apple-mobile-web-app-title" content="Open WebUI" />
 
-		<link rel="manifest" href="/manifest.json" />
+		<link rel="manifest" href="/manifest.json" crossorigin="use-credentials" />
 		<meta
 			name="viewport"
 			content="width=device-width, initial-scale=1, maximum-scale=1, viewport-fit=cover"

+ 43 - 3
src/lib/apis/index.ts

@@ -2,6 +2,7 @@ import { WEBUI_API_BASE_URL, WEBUI_BASE_URL } from '$lib/constants';
 import { convertOpenApiToToolPayload } from '$lib/utils';
 import { getOpenAIModelsDirect } from './openai';
 
+import { parse } from 'yaml';
 import { toast } from 'svelte-sonner';
 
 export const getModels = async (
@@ -259,10 +260,10 @@ export const stopTask = async (token: string, id: string) => {
 	return res;
 };
 
-export const getToolServerData = async (token: string, url: string) => {
+export const getTaskIdsByChatId = async (token: string, chat_id: string) => {
 	let error = null;
 
-	const res = await fetch(`${url}`, {
+	const res = await fetch(`${WEBUI_BASE_URL}/api/tasks/chat/${chat_id}`, {
 		method: 'GET',
 		headers: {
 			Accept: 'application/json',
@@ -288,6 +289,45 @@ export const getToolServerData = async (token: string, url: string) => {
 		throw error;
 	}
 
+	return res;
+};
+
+export const getToolServerData = async (token: string, url: string) => {
+	let error = null;
+
+	const res = await fetch(`${url}`, {
+		method: 'GET',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			...(token && { authorization: `Bearer ${token}` })
+		}
+	})
+		.then(async (res) => {
+			// Check if URL ends with .yaml or .yml to determine format
+			if (url.toLowerCase().endsWith('.yaml') || url.toLowerCase().endsWith('.yml')) {
+				if (!res.ok) throw await res.text();
+				const text = await res.text();
+				return parse(text);
+			} else {
+				if (!res.ok) throw await res.json();
+				return res.json();
+			}
+		})
+		.catch((err) => {
+			console.log(err);
+			if ('detail' in err) {
+				error = err.detail;
+			} else {
+				error = err;
+			}
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
 	const data = {
 		openapi: res,
 		info: res.info,
@@ -305,7 +345,7 @@ export const getToolServersData = async (i18n, servers: object[]) => {
 				.filter((server) => server?.config?.enable)
 				.map(async (server) => {
 					const data = await getToolServerData(
-						server?.key,
+						(server?.auth_type ?? 'bearer') === 'bearer' ? server?.key : localStorage.token,
 						server?.url + '/' + (server?.path ?? 'openapi.json')
 					).catch((err) => {
 						toast.error(

+ 28 - 0
src/lib/apis/knowledge/index.ts

@@ -345,3 +345,31 @@ export const deleteKnowledgeById = async (token: string, id: string) => {
 
 	return res;
 };
+
+export const reindexKnowledgeFiles = async (token: string) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/knowledge/reindex`, {
+		method: 'POST',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			authorization: `Bearer ${token}`
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			error = err.detail;
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};

+ 3 - 30
src/lib/apis/retrieval/index.ts

@@ -50,9 +50,9 @@ type YoutubeConfigForm = {
 };
 
 type RAGConfigForm = {
-	pdf_extract_images?: boolean;
-	enable_google_drive_integration?: boolean;
-	enable_onedrive_integration?: boolean;
+	PDF_EXTRACT_IMAGES?: boolean;
+	ENABLE_GOOGLE_DRIVE_INTEGRATION?: boolean;
+	ENABLE_ONEDRIVE_INTEGRATION?: boolean;
 	chunk?: ChunkConfigForm;
 	content_extraction?: ContentExtractConfigForm;
 	web_loader_ssl_verification?: boolean;
@@ -89,33 +89,6 @@ export const updateRAGConfig = async (token: string, payload: RAGConfigForm) =>
 	return res;
 };
 
-export const getRAGTemplate = async (token: string) => {
-	let error = null;
-
-	const res = await fetch(`${RETRIEVAL_API_BASE_URL}/template`, {
-		method: 'GET',
-		headers: {
-			'Content-Type': 'application/json',
-			Authorization: `Bearer ${token}`
-		}
-	})
-		.then(async (res) => {
-			if (!res.ok) throw await res.json();
-			return res.json();
-		})
-		.catch((err) => {
-			console.log(err);
-			error = err.detail;
-			return null;
-		});
-
-	if (error) {
-		throw error;
-	}
-
-	return res?.template ?? '';
-};
-
 export const getQuerySettings = async (token: string) => {
 	let error = null;
 

+ 1 - 1
src/lib/components/AddServerModal.svelte

@@ -35,7 +35,7 @@
 	let auth_type = 'bearer';
 	let key = '';
 
-	let accessControl = null;
+	let accessControl = {};
 
 	let enable = true;
 

+ 42 - 1
src/lib/components/admin/Settings/Audio.svelte

@@ -39,6 +39,9 @@
 	let STT_ENGINE = '';
 	let STT_MODEL = '';
 	let STT_WHISPER_MODEL = '';
+	let STT_AZURE_API_KEY = '';
+	let STT_AZURE_REGION = '';
+	let STT_AZURE_LOCALES = '';
 	let STT_DEEPGRAM_API_KEY = '';
 
 	let STT_WHISPER_MODEL_LOADING = false;
@@ -108,7 +111,10 @@
 				ENGINE: STT_ENGINE,
 				MODEL: STT_MODEL,
 				WHISPER_MODEL: STT_WHISPER_MODEL,
-				DEEPGRAM_API_KEY: STT_DEEPGRAM_API_KEY
+				DEEPGRAM_API_KEY: STT_DEEPGRAM_API_KEY,
+				AZURE_API_KEY: STT_AZURE_API_KEY,
+				AZURE_REGION: STT_AZURE_REGION,
+				AZURE_LOCALES: STT_AZURE_LOCALES
 			}
 		});
 
@@ -148,6 +154,9 @@
 			STT_ENGINE = res.stt.ENGINE;
 			STT_MODEL = res.stt.MODEL;
 			STT_WHISPER_MODEL = res.stt.WHISPER_MODEL;
+			STT_AZURE_API_KEY = res.stt.AZURE_API_KEY;
+			STT_AZURE_REGION = res.stt.AZURE_REGION;
+			STT_AZURE_LOCALES = res.stt.AZURE_LOCALES;
 			STT_DEEPGRAM_API_KEY = res.stt.DEEPGRAM_API_KEY;
 		}
 
@@ -180,6 +189,7 @@
 							<option value="openai">OpenAI</option>
 							<option value="web">{$i18n.t('Web API')}</option>
 							<option value="deepgram">Deepgram</option>
+							<option value="azure">Azure AI Speech</option>
 						</select>
 					</div>
 				</div>
@@ -248,6 +258,37 @@
 							</a>
 						</div>
 					</div>
+				{:else if STT_ENGINE === 'azure'}
+					<div>
+						<div class="mt-1 flex gap-2 mb-1">
+							<SensitiveInput
+								placeholder={$i18n.t('API Key')}
+								bind:value={STT_AZURE_API_KEY}
+								required
+							/>
+							<input
+								class="flex-1 w-full rounded-lg py-2 pl-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-hidden"
+								placeholder={$i18n.t('Azure Region')}
+								bind:value={STT_AZURE_REGION}
+								required
+							/>
+						</div>
+
+						<hr class="border-gray-100 dark:border-gray-850 my-2" />
+
+						<div>
+							<div class=" mb-1.5 text-sm font-medium">{$i18n.t('Language Locales')}</div>
+							<div class="flex w-full">
+								<div class="flex-1">
+									<input
+										class="w-full rounded-lg py-2 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-hidden"
+										bind:value={STT_AZURE_LOCALES}
+										placeholder={$i18n.t('e.g., en-US,ja-JP (leave blank for auto-detect)')}
+									/>
+								</div>
+							</div>
+						</div>
+					</div>
 				{:else if STT_ENGINE === ''}
 					<div>
 						<div class=" mb-1.5 text-sm font-medium">{$i18n.t('STT Model')}</div>

+ 568 - 609
src/lib/components/admin/Settings/Documents.svelte

@@ -13,61 +13,36 @@
 		updateEmbeddingConfig,
 		getRerankingConfig,
 		updateRerankingConfig,
-		resetUploadDir,
 		getRAGConfig,
 		updateRAGConfig
 	} from '$lib/apis/retrieval';
 
-	import { knowledge, models } from '$lib/stores';
-	import { getKnowledgeBases } from '$lib/apis/knowledge';
-	import { uploadDir, deleteAllFiles, deleteFileById } from '$lib/apis/files';
+	import { reindexKnowledgeFiles } from '$lib/apis/knowledge';
+	import { deleteAllFiles } from '$lib/apis/files';
 
 	import ResetUploadDirConfirmDialog from '$lib/components/common/ConfirmDialog.svelte';
 	import ResetVectorDBConfirmDialog from '$lib/components/common/ConfirmDialog.svelte';
+	import ReindexKnowledgeFilesConfirmDialog from '$lib/components/common/ConfirmDialog.svelte';
 	import SensitiveInput from '$lib/components/common/SensitiveInput.svelte';
 	import Tooltip from '$lib/components/common/Tooltip.svelte';
 	import Switch from '$lib/components/common/Switch.svelte';
 	import Textarea from '$lib/components/common/Textarea.svelte';
+	import Spinner from '$lib/components/common/Spinner.svelte';
 
 	const i18n = getContext('i18n');
 
-	let scanDirLoading = false;
 	let updateEmbeddingModelLoading = false;
 	let updateRerankingModelLoading = false;
 
 	let showResetConfirm = false;
 	let showResetUploadDirConfirm = false;
+	let showReindexConfirm = false;
 
 	let embeddingEngine = '';
 	let embeddingModel = '';
 	let embeddingBatchSize = 1;
 	let rerankingModel = '';
 
-	let fileMaxSize = null;
-	let fileMaxCount = null;
-
-	let contentExtractionEngine = 'default';
-	let tikaServerUrl = '';
-	let showTikaServerUrl = false;
-	let doclingServerUrl = '';
-	let showDoclingServerUrl = false;
-	let documentIntelligenceEndpoint = '';
-	let documentIntelligenceKey = '';
-	let showDocumentIntelligenceConfig = false;
-	let mistralApiKey = '';
-	let showMistralOcrConfig = false;
-
-	let textSplitter = '';
-	let chunkSize = 0;
-	let chunkOverlap = 0;
-	let pdfExtractImages = true;
-
-	let RAG_FULL_CONTEXT = false;
-	let BYPASS_EMBEDDING_AND_RETRIEVAL = false;
-
-	let enableGoogleDriveIntegration = false;
-	let enableOneDriveIntegration = false;
-
 	let OpenAIUrl = '';
 	let OpenAIKey = '';
 
@@ -82,6 +57,8 @@
 		hybrid: false
 	};
 
+	let RAGConfig = null;
+
 	const embeddingModelUpdateHandler = async () => {
 		if (embeddingEngine === '' && embeddingModel.split('/').length - 1 > 1) {
 			toast.error(
@@ -176,65 +153,40 @@
 	};
 
 	const submitHandler = async () => {
-		if (contentExtractionEngine === 'tika' && tikaServerUrl === '') {
+		if (RAGConfig.CONTENT_EXTRACTION_ENGINE === 'tika' && RAGConfig.TIKA_SERVER_URL === '') {
 			toast.error($i18n.t('Tika Server URL required.'));
 			return;
 		}
-		if (contentExtractionEngine === 'docling' && doclingServerUrl === '') {
+		if (RAGConfig.CONTENT_EXTRACTION_ENGINE === 'docling' && RAGConfig.DOCLING_SERVER_URL === '') {
 			toast.error($i18n.t('Docling Server URL required.'));
 			return;
 		}
+
 		if (
-			contentExtractionEngine === 'document_intelligence' &&
-			(documentIntelligenceEndpoint === '' || documentIntelligenceKey === '')
+			RAGConfig.CONTENT_EXTRACTION_ENGINE === 'document_intelligence' &&
+			(RAGConfig.DOCUMENT_INTELLIGENCE_ENDPOINT === '' ||
+				RAGConfig.DOCUMENT_INTELLIGENCE_KEY === '')
 		) {
 			toast.error($i18n.t('Document Intelligence endpoint and key required.'));
 			return;
 		}
-		if (contentExtractionEngine === 'mistral_ocr' && mistralApiKey === '') {
+		if (
+			RAGConfig.CONTENT_EXTRACTION_ENGINE === 'mistral_ocr' &&
+			RAGConfig.MISTRAL_OCR_API_KEY === ''
+		) {
 			toast.error($i18n.t('Mistral OCR API Key required.'));
 			return;
 		}
 
-		if (!BYPASS_EMBEDDING_AND_RETRIEVAL) {
+		if (!RAGConfig.BYPASS_EMBEDDING_AND_RETRIEVAL) {
 			await embeddingModelUpdateHandler();
 
-			if (querySettings.hybrid) {
+			if (RAGConfig.ENABLE_RAG_HYBRID_SEARCH) {
 				await rerankingModelUpdateHandler();
 			}
 		}
 
-		const res = await updateRAGConfig(localStorage.token, {
-			pdf_extract_images: pdfExtractImages,
-			enable_google_drive_integration: enableGoogleDriveIntegration,
-			enable_onedrive_integration: enableOneDriveIntegration,
-			file: {
-				max_size: fileMaxSize === '' ? null : fileMaxSize,
-				max_count: fileMaxCount === '' ? null : fileMaxCount
-			},
-			RAG_FULL_CONTEXT: RAG_FULL_CONTEXT,
-			BYPASS_EMBEDDING_AND_RETRIEVAL: BYPASS_EMBEDDING_AND_RETRIEVAL,
-			chunk: {
-				text_splitter: textSplitter,
-				chunk_overlap: chunkOverlap,
-				chunk_size: chunkSize
-			},
-			content_extraction: {
-				engine: contentExtractionEngine,
-				tika_server_url: tikaServerUrl,
-				docling_server_url: doclingServerUrl,
-				document_intelligence_config: {
-					key: documentIntelligenceKey,
-					endpoint: documentIntelligenceEndpoint
-				},
-				mistral_ocr_config: {
-					api_key: mistralApiKey
-				}
-			}
-		});
-
-		await updateQuerySettings(localStorage.token, querySettings);
-
+		const res = await updateRAGConfig(localStorage.token, RAGConfig);
 		dispatch('save');
 	};
 
@@ -262,46 +214,11 @@
 		}
 	};
 
-	const toggleHybridSearch = async () => {
-		querySettings = await updateQuerySettings(localStorage.token, querySettings);
-	};
-
 	onMount(async () => {
 		await setEmbeddingConfig();
 		await setRerankingConfig();
 
-		querySettings = await getQuerySettings(localStorage.token);
-
-		const res = await getRAGConfig(localStorage.token);
-
-		if (res) {
-			pdfExtractImages = res.pdf_extract_images;
-
-			textSplitter = res.chunk.text_splitter;
-			chunkSize = res.chunk.chunk_size;
-			chunkOverlap = res.chunk.chunk_overlap;
-
-			RAG_FULL_CONTEXT = res.RAG_FULL_CONTEXT;
-			BYPASS_EMBEDDING_AND_RETRIEVAL = res.BYPASS_EMBEDDING_AND_RETRIEVAL;
-
-			contentExtractionEngine = res.content_extraction.engine;
-			tikaServerUrl = res.content_extraction.tika_server_url;
-			doclingServerUrl = res.content_extraction.docling_server_url;
-
-			showTikaServerUrl = contentExtractionEngine === 'tika';
-			showDoclingServerUrl = contentExtractionEngine === 'docling';
-			documentIntelligenceEndpoint = res.content_extraction.document_intelligence_config.endpoint;
-			documentIntelligenceKey = res.content_extraction.document_intelligence_config.key;
-			showDocumentIntelligenceConfig = contentExtractionEngine === 'document_intelligence';
-			mistralApiKey = res.content_extraction.mistral_ocr_config.api_key;
-			showMistralOcrConfig = contentExtractionEngine === 'mistral_ocr';
-
-			fileMaxSize = res?.file.max_size ?? '';
-			fileMaxCount = res?.file.max_count ?? '';
-
-			enableGoogleDriveIntegration = res.enable_google_drive_integration;
-			enableOneDriveIntegration = res.enable_onedrive_integration;
-		}
+		RAGConfig = await getRAGConfig(localStorage.token);
 	});
 </script>
 
@@ -333,345 +250,113 @@
 	}}
 />
 
+<ReindexKnowledgeFilesConfirmDialog
+	bind:show={showReindexConfirm}
+	on:confirm={async () => {
+		const res = await reindexKnowledgeFiles(localStorage.token).catch((error) => {
+			toast.error(`${error}`);
+			return null;
+		});
+
+		if (res) {
+			toast.success($i18n.t('Success'));
+		}
+	}}
+/>
+
 <form
 	class="flex flex-col h-full justify-between space-y-3 text-sm"
 	on:submit|preventDefault={() => {
 		submitHandler();
 	}}
 >
-	<div class=" space-y-2.5 overflow-y-scroll scrollbar-hidden h-full pr-1.5">
-		<div class="">
-			<div class="mb-3">
-				<div class=" mb-2.5 text-base font-medium">{$i18n.t('General')}</div>
-
-				<hr class=" border-gray-100 dark:border-gray-850 my-2" />
-
-				<div class="mb-2.5 flex flex-col w-full justify-between">
-					<div class="flex w-full justify-between">
-						<div class="self-center text-xs font-medium">
-							{$i18n.t('Content Extraction Engine')}
-						</div>
-						<div class="">
-							<select
-								class="dark:bg-gray-900 w-fit pr-8 rounded-sm px-2 text-xs bg-transparent outline-hidden text-right"
-								bind:value={contentExtractionEngine}
-							>
-								<option value="">{$i18n.t('Default')}</option>
-								<option value="tika">{$i18n.t('Tika')}</option>
-								<option value="docling">{$i18n.t('Docling')}</option>
-								<option value="document_intelligence">{$i18n.t('Document Intelligence')}</option>
-								<option value="mistral_ocr">{$i18n.t('Mistral OCR')}</option>
-							</select>
-						</div>
-					</div>
-					{#if contentExtractionEngine === 'tika'}
-						<div class="flex w-full mt-1">
-							<div class="flex-1 mr-2">
-								<input
-									class="flex-1 w-full rounded-lg text-sm bg-transparent outline-hidden"
-									placeholder={$i18n.t('Enter Tika Server URL')}
-									bind:value={tikaServerUrl}
-								/>
-							</div>
-						</div>
-					{:else if contentExtractionEngine === 'docling'}
-						<div class="flex w-full mt-1">
-							<input
-								class="flex-1 w-full rounded-lg text-sm bg-transparent outline-hidden"
-								placeholder={$i18n.t('Enter Docling Server URL')}
-								bind:value={doclingServerUrl}
-							/>
-						</div>
-					{:else if contentExtractionEngine === 'document_intelligence'}
-						<div class="my-0.5 flex gap-2 pr-2">
-							<input
-								class="flex-1 w-full rounded-lg text-sm bg-transparent outline-hidden"
-								placeholder={$i18n.t('Enter Document Intelligence Endpoint')}
-								bind:value={documentIntelligenceEndpoint}
-							/>
-							<SensitiveInput
-								placeholder={$i18n.t('Enter Document Intelligence Key')}
-								bind:value={documentIntelligenceKey}
-							/>
-						</div>
-					{:else if contentExtractionEngine === 'mistral_ocr'}
-						<div class="my-0.5 flex gap-2 pr-2">
-							<SensitiveInput
-								placeholder={$i18n.t('Enter Mistral API Key')}
-								bind:value={mistralApiKey}
-							/>
-						</div>
-					{/if}
-				</div>
-
-				{#if contentExtractionEngine === ''}
-					<div class="  mb-2.5 flex w-full justify-between">
-						<div class=" self-center text-xs font-medium">
-							{$i18n.t('PDF Extract Images (OCR)')}
-						</div>
-						<div class="flex items-center relative">
-							<Switch bind:state={pdfExtractImages} />
-						</div>
-					</div>
-				{/if}
-
-				<div class="  mb-2.5 flex w-full justify-between">
-					<div class=" self-center text-xs font-medium">
-						<Tooltip content={$i18n.t('Full Context Mode')} placement="top-start">
-							{$i18n.t('Bypass Embedding and Retrieval')}
-						</Tooltip>
-					</div>
-					<div class="flex items-center relative">
-						<Tooltip
-							content={BYPASS_EMBEDDING_AND_RETRIEVAL
-								? $i18n.t(
-										'Inject the entire content as context for comprehensive processing, this is recommended for complex queries.'
-									)
-								: $i18n.t(
-										'Default to segmented retrieval for focused and relevant content extraction, this is recommended for most cases.'
-									)}
-						>
-							<Switch bind:state={BYPASS_EMBEDDING_AND_RETRIEVAL} />
-						</Tooltip>
-					</div>
-				</div>
-
-				{#if !BYPASS_EMBEDDING_AND_RETRIEVAL}
-					<div class="  mb-2.5 flex w-full justify-between">
-						<div class=" self-center text-xs font-medium">{$i18n.t('Text Splitter')}</div>
-						<div class="flex items-center relative">
-							<select
-								class="dark:bg-gray-900 w-fit pr-8 rounded-sm px-2 text-xs bg-transparent outline-hidden text-right"
-								bind:value={textSplitter}
-							>
-								<option value="">{$i18n.t('Default')} ({$i18n.t('Character')})</option>
-								<option value="token">{$i18n.t('Token')} ({$i18n.t('Tiktoken')})</option>
-							</select>
-						</div>
-					</div>
-
-					<div class="  mb-2.5 flex w-full justify-between">
-						<div class=" flex gap-1.5 w-full">
-							<div class="  w-full justify-between">
-								<div class="self-center text-xs font-medium min-w-fit mb-1">
-									{$i18n.t('Chunk Size')}
-								</div>
-								<div class="self-center">
-									<input
-										class=" w-full rounded-lg py-1.5 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-hidden"
-										type="number"
-										placeholder={$i18n.t('Enter Chunk Size')}
-										bind:value={chunkSize}
-										autocomplete="off"
-										min="0"
-									/>
-								</div>
-							</div>
-
-							<div class="w-full">
-								<div class=" self-center text-xs font-medium min-w-fit mb-1">
-									{$i18n.t('Chunk Overlap')}
-								</div>
-
-								<div class="self-center">
-									<input
-										class="w-full rounded-lg py-1.5 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-hidden"
-										type="number"
-										placeholder={$i18n.t('Enter Chunk Overlap')}
-										bind:value={chunkOverlap}
-										autocomplete="off"
-										min="0"
-									/>
-								</div>
-							</div>
-						</div>
-					</div>
-				{/if}
-			</div>
-
-			{#if !BYPASS_EMBEDDING_AND_RETRIEVAL}
+	{#if RAGConfig}
+		<div class=" space-y-2.5 overflow-y-scroll scrollbar-hidden h-full pr-1.5">
+			<div class="">
 				<div class="mb-3">
-					<div class=" mb-2.5 text-base font-medium">{$i18n.t('Embedding')}</div>
+					<div class=" mb-2.5 text-base font-medium">{$i18n.t('General')}</div>
 
 					<hr class=" border-gray-100 dark:border-gray-850 my-2" />
 
-					<div class="  mb-2.5 flex flex-col w-full justify-between">
+					<div class="mb-2.5 flex flex-col w-full justify-between">
 						<div class="flex w-full justify-between">
-							<div class=" self-center text-xs font-medium">
-								{$i18n.t('Embedding Model Engine')}
+							<div class="self-center text-xs font-medium">
+								{$i18n.t('Content Extraction Engine')}
 							</div>
-							<div class="flex items-center relative">
+							<div class="">
 								<select
-									class="dark:bg-gray-900 w-fit pr-8 rounded-sm px-2 p-1 text-xs bg-transparent outline-hidden text-right"
-									bind:value={embeddingEngine}
-									placeholder="Select an embedding model engine"
-									on:change={(e) => {
-										if (e.target.value === 'ollama') {
-											embeddingModel = '';
-										} else if (e.target.value === 'openai') {
-											embeddingModel = 'text-embedding-3-small';
-										} else if (e.target.value === '') {
-											embeddingModel = 'sentence-transformers/all-MiniLM-L6-v2';
-										}
-									}}
+									class="dark:bg-gray-900 w-fit pr-8 rounded-sm px-2 text-xs bg-transparent outline-hidden text-right"
+									bind:value={RAGConfig.CONTENT_EXTRACTION_ENGINE}
 								>
-									<option value="">{$i18n.t('Default (SentenceTransformers)')}</option>
-									<option value="ollama">{$i18n.t('Ollama')}</option>
-									<option value="openai">{$i18n.t('OpenAI')}</option>
+									<option value="">{$i18n.t('Default')}</option>
+									<option value="tika">{$i18n.t('Tika')}</option>
+									<option value="docling">{$i18n.t('Docling')}</option>
+									<option value="document_intelligence">{$i18n.t('Document Intelligence')}</option>
+									<option value="mistral_ocr">{$i18n.t('Mistral OCR')}</option>
 								</select>
 							</div>
 						</div>
 
-						{#if embeddingEngine === 'openai'}
-							<div class="my-0.5 flex gap-2 pr-2">
+						{#if RAGConfig.CONTENT_EXTRACTION_ENGINE === ''}
+							<div class="flex w-full mt-1">
+								<div class="flex-1 flex justify-between">
+									<div class=" self-center text-xs font-medium">
+										{$i18n.t('PDF Extract Images (OCR)')}
+									</div>
+									<div class="flex items-center relative">
+										<Switch bind:state={RAGConfig.PDF_EXTRACT_IMAGES} />
+									</div>
+								</div>
+							</div>
+						{:else if RAGConfig.CONTENT_EXTRACTION_ENGINE === 'tika'}
+							<div class="flex w-full mt-1">
+								<div class="flex-1 mr-2">
+									<input
+										class="flex-1 w-full rounded-lg text-sm bg-transparent outline-hidden"
+										placeholder={$i18n.t('Enter Tika Server URL')}
+										bind:value={RAGConfig.TIKA_SERVER_URL}
+									/>
+								</div>
+							</div>
+						{:else if RAGConfig.CONTENT_EXTRACTION_ENGINE === 'docling'}
+							<div class="flex w-full mt-1">
 								<input
 									class="flex-1 w-full rounded-lg text-sm bg-transparent outline-hidden"
-									placeholder={$i18n.t('API Base URL')}
-									bind:value={OpenAIUrl}
-									required
+									placeholder={$i18n.t('Enter Docling Server URL')}
+									bind:value={RAGConfig.DOCLING_SERVER_URL}
 								/>
-
-								<SensitiveInput placeholder={$i18n.t('API Key')} bind:value={OpenAIKey} />
 							</div>
-						{:else if embeddingEngine === 'ollama'}
+						{:else if RAGConfig.CONTENT_EXTRACTION_ENGINE === 'document_intelligence'}
 							<div class="my-0.5 flex gap-2 pr-2">
 								<input
 									class="flex-1 w-full rounded-lg text-sm bg-transparent outline-hidden"
-									placeholder={$i18n.t('API Base URL')}
-									bind:value={OllamaUrl}
-									required
+									placeholder={$i18n.t('Enter Document Intelligence Endpoint')}
+									bind:value={RAGConfig.DOCUMENT_INTELLIGENCE_ENDPOINT}
 								/>
-
 								<SensitiveInput
-									placeholder={$i18n.t('API Key')}
-									bind:value={OllamaKey}
-									required={false}
+									placeholder={$i18n.t('Enter Document Intelligence Key')}
+									bind:value={RAGConfig.DOCUMENT_INTELLIGENCE_KEY}
 								/>
 							</div>
-						{/if}
-					</div>
-
-					<div class="  mb-2.5 flex flex-col w-full">
-						<div class=" mb-1 text-xs font-medium">{$i18n.t('Embedding Model')}</div>
-
-						<div class="">
-							{#if embeddingEngine === 'ollama'}
-								<div class="flex w-full">
-									<div class="flex-1 mr-2">
-										<input
-											class="flex-1 w-full rounded-lg text-sm bg-transparent outline-hidden"
-											bind:value={embeddingModel}
-											placeholder={$i18n.t('Set embedding model')}
-											required
-										/>
-									</div>
-								</div>
-							{:else}
-								<div class="flex w-full">
-									<div class="flex-1 mr-2">
-										<input
-											class="flex-1 w-full rounded-lg text-sm bg-transparent outline-hidden"
-											placeholder={$i18n.t('Set embedding model (e.g. {{model}})', {
-												model: embeddingModel.slice(-40)
-											})}
-											bind:value={embeddingModel}
-										/>
-									</div>
-
-									{#if embeddingEngine === ''}
-										<button
-											class="px-2.5 bg-transparent text-gray-800 dark:bg-transparent dark:text-gray-100 rounded-lg transition"
-											on:click={() => {
-												embeddingModelUpdateHandler();
-											}}
-											disabled={updateEmbeddingModelLoading}
-										>
-											{#if updateEmbeddingModelLoading}
-												<div class="self-center">
-													<svg
-														class=" w-4 h-4"
-														viewBox="0 0 24 24"
-														fill="currentColor"
-														xmlns="http://www.w3.org/2000/svg"
-													>
-														<style>
-															.spinner_ajPY {
-																transform-origin: center;
-																animation: spinner_AtaB 0.75s infinite linear;
-															}
-
-															@keyframes spinner_AtaB {
-																100% {
-																	transform: rotate(360deg);
-																}
-															}
-														</style>
-														<path
-															d="M12,1A11,11,0,1,0,23,12,11,11,0,0,0,12,1Zm0,19a8,8,0,1,1,8-8A8,8,0,0,1,12,20Z"
-															opacity=".25"
-														/>
-														<path
-															d="M10.14,1.16a11,11,0,0,0-9,8.92A1.59,1.59,0,0,0,2.46,12,1.52,1.52,0,0,0,4.11,10.7a8,8,0,0,1,6.66-6.61A1.42,1.42,0,0,0,12,2.69h0A1.57,1.57,0,0,0,10.14,1.16Z"
-															class="spinner_ajPY"
-														/>
-													</svg>
-												</div>
-											{:else}
-												<svg
-													xmlns="http://www.w3.org/2000/svg"
-													viewBox="0 0 16 16"
-													fill="currentColor"
-													class="w-4 h-4"
-												>
-													<path
-														d="M8.75 2.75a.75.75 0 0 0-1.5 0v5.69L5.03 6.22a.75.75 0 0 0-1.06 1.06l3.5 3.5a.75.75 0 0 0 1.06 0l3.5-3.5a.75.75 0 0 0-1.06-1.06L8.75 8.44V2.75Z"
-													/>
-													<path
-														d="M3.5 9.75a.75.75 0 0 0-1.5 0v1.5A2.75 2.75 0 0 0 4.75 14h6.5A2.75 2.75 0 0 0 14 11.25v-1.5a.75.75 0 0 0-1.5 0v1.5c0 .69-.56 1.25-1.25 1.25h-6.5c-.69 0-1.25-.56-1.25-1.25v-1.5Z"
-													/>
-												</svg>
-											{/if}
-										</button>
-									{/if}
-								</div>
-							{/if}
-						</div>
-
-						<div class="mt-1 mb-1 text-xs text-gray-400 dark:text-gray-500">
-							{$i18n.t(
-								'Warning: If you update or change your embedding model, you will need to re-import all documents.'
-							)}
-						</div>
-					</div>
-
-					{#if embeddingEngine === 'ollama' || embeddingEngine === 'openai'}
-						<div class="  mb-2.5 flex w-full justify-between">
-							<div class=" self-center text-xs font-medium">{$i18n.t('Embedding Batch Size')}</div>
-
-							<div class="">
-								<input
-									bind:value={embeddingBatchSize}
-									type="number"
-									class=" bg-transparent text-center w-14 outline-none"
-									min="-2"
-									max="16000"
-									step="1"
+						{:else if RAGConfig.CONTENT_EXTRACTION_ENGINE === 'mistral_ocr'}
+							<div class="my-0.5 flex gap-2 pr-2">
+								<SensitiveInput
+									placeholder={$i18n.t('Enter Mistral API Key')}
+									bind:value={RAGConfig.MISTRAL_OCR_API_KEY}
 								/>
 							</div>
-						</div>
-					{/if}
-				</div>
-
-				<div class="mb-3">
-					<div class=" mb-2.5 text-base font-medium">{$i18n.t('Retrieval')}</div>
-
-					<hr class=" border-gray-100 dark:border-gray-850 my-2" />
+						{/if}
+					</div>
 
 					<div class="  mb-2.5 flex w-full justify-between">
-						<div class=" self-center text-xs font-medium">{$i18n.t('Full Context Mode')}</div>
+						<div class=" self-center text-xs font-medium">
+							<Tooltip content={$i18n.t('Full Context Mode')} placement="top-start">
+								{$i18n.t('Bypass Embedding and Retrieval')}
+							</Tooltip>
+						</div>
 						<div class="flex items-center relative">
 							<Tooltip
-								content={RAG_FULL_CONTEXT
+								content={RAGConfig.BYPASS_EMBEDDING_AND_RETRIEVAL
 									? $i18n.t(
 											'Inject the entire content as context for comprehensive processing, this is recommended for complex queries.'
 										)
@@ -679,286 +364,560 @@
 											'Default to segmented retrieval for focused and relevant content extraction, this is recommended for most cases.'
 										)}
 							>
-								<Switch bind:state={RAG_FULL_CONTEXT} />
+								<Switch bind:state={RAGConfig.BYPASS_EMBEDDING_AND_RETRIEVAL} />
 							</Tooltip>
 						</div>
 					</div>
 
-					{#if !RAG_FULL_CONTEXT}
+					{#if !RAGConfig.BYPASS_EMBEDDING_AND_RETRIEVAL}
 						<div class="  mb-2.5 flex w-full justify-between">
-							<div class=" self-center text-xs font-medium">{$i18n.t('Hybrid Search')}</div>
+							<div class=" self-center text-xs font-medium">{$i18n.t('Text Splitter')}</div>
 							<div class="flex items-center relative">
-								<Switch
-									bind:state={querySettings.hybrid}
-									on:change={() => {
-										toggleHybridSearch();
-									}}
-								/>
+								<select
+									class="dark:bg-gray-900 w-fit pr-8 rounded-sm px-2 text-xs bg-transparent outline-hidden text-right"
+									bind:value={RAGConfig.TEXT_SPLITTER}
+								>
+									<option value="">{$i18n.t('Default')} ({$i18n.t('Character')})</option>
+									<option value="token">{$i18n.t('Token')} ({$i18n.t('Tiktoken')})</option>
+								</select>
 							</div>
 						</div>
 
-						{#if querySettings.hybrid === true}
-							<div class="  mb-2.5 flex flex-col w-full">
-								<div class=" mb-1 text-xs font-medium">{$i18n.t('Reranking Model')}</div>
+						<div class="  mb-2.5 flex w-full justify-between">
+							<div class=" flex gap-1.5 w-full">
+								<div class="  w-full justify-between">
+									<div class="self-center text-xs font-medium min-w-fit mb-1">
+										{$i18n.t('Chunk Size')}
+									</div>
+									<div class="self-center">
+										<input
+											class=" w-full rounded-lg py-1.5 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-hidden"
+											type="number"
+											placeholder={$i18n.t('Enter Chunk Size')}
+											bind:value={RAGConfig.CHUNK_SIZE}
+											autocomplete="off"
+											min="0"
+										/>
+									</div>
+								</div>
+
+								<div class="w-full">
+									<div class=" self-center text-xs font-medium min-w-fit mb-1">
+										{$i18n.t('Chunk Overlap')}
+									</div>
 
-								<div class="">
+									<div class="self-center">
+										<input
+											class="w-full rounded-lg py-1.5 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-hidden"
+											type="number"
+											placeholder={$i18n.t('Enter Chunk Overlap')}
+											bind:value={RAGConfig.CHUNK_OVERLAP}
+											autocomplete="off"
+											min="0"
+										/>
+									</div>
+								</div>
+							</div>
+						</div>
+					{/if}
+				</div>
+
+				{#if !RAGConfig.BYPASS_EMBEDDING_AND_RETRIEVAL}
+					<div class="mb-3">
+						<div class=" mb-2.5 text-base font-medium">{$i18n.t('Embedding')}</div>
+
+						<hr class=" border-gray-100 dark:border-gray-850 my-2" />
+
+						<div class="  mb-2.5 flex flex-col w-full justify-between">
+							<div class="flex w-full justify-between">
+								<div class=" self-center text-xs font-medium">
+									{$i18n.t('Embedding Model Engine')}
+								</div>
+								<div class="flex items-center relative">
+									<select
+										class="dark:bg-gray-900 w-fit pr-8 rounded-sm px-2 p-1 text-xs bg-transparent outline-hidden text-right"
+										bind:value={embeddingEngine}
+										placeholder="Select an embedding model engine"
+										on:change={(e) => {
+											if (e.target.value === 'ollama') {
+												embeddingModel = '';
+											} else if (e.target.value === 'openai') {
+												embeddingModel = 'text-embedding-3-small';
+											} else if (e.target.value === '') {
+												embeddingModel = 'sentence-transformers/all-MiniLM-L6-v2';
+											}
+										}}
+									>
+										<option value="">{$i18n.t('Default (SentenceTransformers)')}</option>
+										<option value="ollama">{$i18n.t('Ollama')}</option>
+										<option value="openai">{$i18n.t('OpenAI')}</option>
+									</select>
+								</div>
+							</div>
+
+							{#if embeddingEngine === 'openai'}
+								<div class="my-0.5 flex gap-2 pr-2">
+									<input
+										class="flex-1 w-full rounded-lg text-sm bg-transparent outline-hidden"
+										placeholder={$i18n.t('API Base URL')}
+										bind:value={OpenAIUrl}
+										required
+									/>
+
+									<SensitiveInput placeholder={$i18n.t('API Key')} bind:value={OpenAIKey} />
+								</div>
+							{:else if embeddingEngine === 'ollama'}
+								<div class="my-0.5 flex gap-2 pr-2">
+									<input
+										class="flex-1 w-full rounded-lg text-sm bg-transparent outline-hidden"
+										placeholder={$i18n.t('API Base URL')}
+										bind:value={OllamaUrl}
+										required
+									/>
+
+									<SensitiveInput
+										placeholder={$i18n.t('API Key')}
+										bind:value={OllamaKey}
+										required={false}
+									/>
+								</div>
+							{/if}
+						</div>
+
+						<div class="  mb-2.5 flex flex-col w-full">
+							<div class=" mb-1 text-xs font-medium">{$i18n.t('Embedding Model')}</div>
+
+							<div class="">
+								{#if embeddingEngine === 'ollama'}
+									<div class="flex w-full">
+										<div class="flex-1 mr-2">
+											<input
+												class="flex-1 w-full rounded-lg text-sm bg-transparent outline-hidden"
+												bind:value={embeddingModel}
+												placeholder={$i18n.t('Set embedding model')}
+												required
+											/>
+										</div>
+									</div>
+								{:else}
 									<div class="flex w-full">
 										<div class="flex-1 mr-2">
 											<input
 												class="flex-1 w-full rounded-lg text-sm bg-transparent outline-hidden"
-												placeholder={$i18n.t('Set reranking model (e.g. {{model}})', {
-													model: 'BAAI/bge-reranker-v2-m3'
+												placeholder={$i18n.t('Set embedding model (e.g. {{model}})', {
+													model: embeddingModel.slice(-40)
 												})}
-												bind:value={rerankingModel}
+												bind:value={embeddingModel}
 											/>
 										</div>
-										<button
-											class="px-2.5 bg-transparent text-gray-800 dark:bg-transparent dark:text-gray-100 rounded-lg transition"
-											on:click={() => {
-												rerankingModelUpdateHandler();
-											}}
-											disabled={updateRerankingModelLoading}
-										>
-											{#if updateRerankingModelLoading}
-												<div class="self-center">
+
+										{#if embeddingEngine === ''}
+											<button
+												class="px-2.5 bg-transparent text-gray-800 dark:bg-transparent dark:text-gray-100 rounded-lg transition"
+												on:click={() => {
+													embeddingModelUpdateHandler();
+												}}
+												disabled={updateEmbeddingModelLoading}
+											>
+												{#if updateEmbeddingModelLoading}
+													<div class="self-center">
+														<svg
+															class=" w-4 h-4"
+															viewBox="0 0 24 24"
+															fill="currentColor"
+															xmlns="http://www.w3.org/2000/svg"
+														>
+															<style>
+																.spinner_ajPY {
+																	transform-origin: center;
+																	animation: spinner_AtaB 0.75s infinite linear;
+																}
+
+																@keyframes spinner_AtaB {
+																	100% {
+																		transform: rotate(360deg);
+																	}
+																}
+															</style>
+															<path
+																d="M12,1A11,11,0,1,0,23,12,11,11,0,0,0,12,1Zm0,19a8,8,0,1,1,8-8A8,8,0,0,1,12,20Z"
+																opacity=".25"
+															/>
+															<path
+																d="M10.14,1.16a11,11,0,0,0-9,8.92A1.59,1.59,0,0,0,2.46,12,1.52,1.52,0,0,0,4.11,10.7a8,8,0,0,1,6.66-6.61A1.42,1.42,0,0,0,12,2.69h0A1.57,1.57,0,0,0,10.14,1.16Z"
+																class="spinner_ajPY"
+															/>
+														</svg>
+													</div>
+												{:else}
 													<svg
-														class=" w-4 h-4"
-														viewBox="0 0 24 24"
-														fill="currentColor"
 														xmlns="http://www.w3.org/2000/svg"
+														viewBox="0 0 16 16"
+														fill="currentColor"
+														class="w-4 h-4"
 													>
-														<style>
-															.spinner_ajPY {
-																transform-origin: center;
-																animation: spinner_AtaB 0.75s infinite linear;
-															}
-
-															@keyframes spinner_AtaB {
-																100% {
-																	transform: rotate(360deg);
-																}
-															}
-														</style>
 														<path
-															d="M12,1A11,11,0,1,0,23,12,11,11,0,0,0,12,1Zm0,19a8,8,0,1,1,8-8A8,8,0,0,1,12,20Z"
-															opacity=".25"
+															d="M8.75 2.75a.75.75 0 0 0-1.5 0v5.69L5.03 6.22a.75.75 0 0 0-1.06 1.06l3.5 3.5a.75.75 0 0 0 1.06 0l3.5-3.5a.75.75 0 0 0-1.06-1.06L8.75 8.44V2.75Z"
 														/>
 														<path
-															d="M10.14,1.16a11,11,0,0,0-9,8.92A1.59,1.59,0,0,0,2.46,12,1.52,1.52,0,0,0,4.11,10.7a8,8,0,0,1,6.66-6.61A1.42,1.42,0,0,0,12,2.69h0A1.57,1.57,0,0,0,10.14,1.16Z"
-															class="spinner_ajPY"
+															d="M3.5 9.75a.75.75 0 0 0-1.5 0v1.5A2.75 2.75 0 0 0 4.75 14h6.5A2.75 2.75 0 0 0 14 11.25v-1.5a.75.75 0 0 0-1.5 0v1.5c0 .69-.56 1.25-1.25 1.25h-6.5c-.69 0-1.25-.56-1.25-1.25v-1.5Z"
 														/>
 													</svg>
-												</div>
-											{:else}
-												<svg
-													xmlns="http://www.w3.org/2000/svg"
-													viewBox="0 0 16 16"
-													fill="currentColor"
-													class="w-4 h-4"
-												>
-													<path
-														d="M8.75 2.75a.75.75 0 0 0-1.5 0v5.69L5.03 6.22a.75.75 0 0 0-1.06 1.06l3.5 3.5a.75.75 0 0 0 1.06 0l3.5-3.5a.75.75 0 0 0-1.06-1.06L8.75 8.44V2.75Z"
-													/>
-													<path
-														d="M3.5 9.75a.75.75 0 0 0-1.5 0v1.5A2.75 2.75 0 0 0 4.75 14h6.5A2.75 2.75 0 0 0 14 11.25v-1.5a.75.75 0 0 0-1.5 0v1.5c0 .69-.56 1.25-1.25 1.25h-6.5c-.69 0-1.25-.56-1.25-1.25v-1.5Z"
-													/>
-												</svg>
-											{/if}
-										</button>
+												{/if}
+											</button>
+										{/if}
 									</div>
+								{/if}
+							</div>
+
+							<div class="mt-1 mb-1 text-xs text-gray-400 dark:text-gray-500">
+								{$i18n.t(
+									'Warning: If you update or change your embedding model, you will need to re-import all documents.'
+								)}
+							</div>
+						</div>
+
+						{#if embeddingEngine === 'ollama' || embeddingEngine === 'openai'}
+							<div class="  mb-2.5 flex w-full justify-between">
+								<div class=" self-center text-xs font-medium">
+									{$i18n.t('Embedding Batch Size')}
+								</div>
+
+								<div class="">
+									<input
+										bind:value={embeddingBatchSize}
+										type="number"
+										class=" bg-transparent text-center w-14 outline-none"
+										min="-2"
+										max="16000"
+										step="1"
+									/>
 								</div>
 							</div>
 						{/if}
+					</div>
+
+					<div class="mb-3">
+						<div class=" mb-2.5 text-base font-medium">{$i18n.t('Retrieval')}</div>
+
+						<hr class=" border-gray-100 dark:border-gray-850 my-2" />
 
 						<div class="  mb-2.5 flex w-full justify-between">
-							<div class=" self-center text-xs font-medium">{$i18n.t('Top K')}</div>
+							<div class=" self-center text-xs font-medium">{$i18n.t('Full Context Mode')}</div>
 							<div class="flex items-center relative">
-								<input
-									class="flex-1 w-full rounded-lg text-sm bg-transparent outline-hidden"
-									type="number"
-									placeholder={$i18n.t('Enter Top K')}
-									bind:value={querySettings.k}
-									autocomplete="off"
-									min="0"
-								/>
+								<Tooltip
+									content={RAGConfig.RAG_FULL_CONTEXT
+										? $i18n.t(
+												'Inject the entire content as context for comprehensive processing, this is recommended for complex queries.'
+											)
+										: $i18n.t(
+												'Default to segmented retrieval for focused and relevant content extraction, this is recommended for most cases.'
+											)}
+								>
+									<Switch bind:state={RAGConfig.RAG_FULL_CONTEXT} />
+								</Tooltip>
 							</div>
 						</div>
 
-						{#if querySettings.hybrid === true}
-							<div class="mb-2.5 flex w-full justify-between">
-								<div class="self-center text-xs font-medium">{$i18n.t('Top K Reranker')}</div>
+						{#if !RAGConfig.RAG_FULL_CONTEXT}
+							<div class="  mb-2.5 flex w-full justify-between">
+								<div class=" self-center text-xs font-medium">{$i18n.t('Hybrid Search')}</div>
+								<div class="flex items-center relative">
+									<Switch
+										bind:state={RAGConfig.ENABLE_RAG_HYBRID_SEARCH}
+										on:change={() => {
+											submitHandler();
+										}}
+									/>
+								</div>
+							</div>
+
+							{#if RAGConfig.ENABLE_RAG_HYBRID_SEARCH === true}
+								<div class="  mb-2.5 flex flex-col w-full">
+									<div class=" mb-1 text-xs font-medium">{$i18n.t('Reranking Model')}</div>
+
+									<div class="">
+										<div class="flex w-full">
+											<div class="flex-1 mr-2">
+												<input
+													class="flex-1 w-full rounded-lg text-sm bg-transparent outline-hidden"
+													placeholder={$i18n.t('Set reranking model (e.g. {{model}})', {
+														model: 'BAAI/bge-reranker-v2-m3'
+													})}
+													bind:value={rerankingModel}
+												/>
+											</div>
+											<button
+												class="px-2.5 bg-transparent text-gray-800 dark:bg-transparent dark:text-gray-100 rounded-lg transition"
+												on:click={() => {
+													rerankingModelUpdateHandler();
+												}}
+												disabled={updateRerankingModelLoading}
+											>
+												{#if updateRerankingModelLoading}
+													<div class="self-center">
+														<svg
+															class=" w-4 h-4"
+															viewBox="0 0 24 24"
+															fill="currentColor"
+															xmlns="http://www.w3.org/2000/svg"
+														>
+															<style>
+																.spinner_ajPY {
+																	transform-origin: center;
+																	animation: spinner_AtaB 0.75s infinite linear;
+																}
+
+																@keyframes spinner_AtaB {
+																	100% {
+																		transform: rotate(360deg);
+																	}
+																}
+															</style>
+															<path
+																d="M12,1A11,11,0,1,0,23,12,11,11,0,0,0,12,1Zm0,19a8,8,0,1,1,8-8A8,8,0,0,1,12,20Z"
+																opacity=".25"
+															/>
+															<path
+																d="M10.14,1.16a11,11,0,0,0-9,8.92A1.59,1.59,0,0,0,2.46,12,1.52,1.52,0,0,0,4.11,10.7a8,8,0,0,1,6.66-6.61A1.42,1.42,0,0,0,12,2.69h0A1.57,1.57,0,0,0,10.14,1.16Z"
+																class="spinner_ajPY"
+															/>
+														</svg>
+													</div>
+												{:else}
+													<svg
+														xmlns="http://www.w3.org/2000/svg"
+														viewBox="0 0 16 16"
+														fill="currentColor"
+														class="w-4 h-4"
+													>
+														<path
+															d="M8.75 2.75a.75.75 0 0 0-1.5 0v5.69L5.03 6.22a.75.75 0 0 0-1.06 1.06l3.5 3.5a.75.75 0 0 0 1.06 0l3.5-3.5a.75.75 0 0 0-1.06-1.06L8.75 8.44V2.75Z"
+														/>
+														<path
+															d="M3.5 9.75a.75.75 0 0 0-1.5 0v1.5A2.75 2.75 0 0 0 4.75 14h6.5A2.75 2.75 0 0 0 14 11.25v-1.5a.75.75 0 0 0-1.5 0v1.5c0 .69-.56 1.25-1.25 1.25h-6.5c-.69 0-1.25-.56-1.25-1.25v-1.5Z"
+														/>
+													</svg>
+												{/if}
+											</button>
+										</div>
+									</div>
+								</div>
+							{/if}
+
+							<div class="  mb-2.5 flex w-full justify-between">
+								<div class=" self-center text-xs font-medium">{$i18n.t('Top K')}</div>
 								<div class="flex items-center relative">
 									<input
 										class="flex-1 w-full rounded-lg text-sm bg-transparent outline-hidden"
 										type="number"
-										placeholder={$i18n.t('Enter Top K Reranker')}
-										bind:value={querySettings.k_reranker}
+										placeholder={$i18n.t('Enter Top K')}
+										bind:value={RAGConfig.TOP_K}
 										autocomplete="off"
 										min="0"
 									/>
 								</div>
 							</div>
-						{/if}
 
-						{#if querySettings.hybrid === true}
-							<div class="  mb-2.5 flex flex-col w-full justify-between">
-								<div class=" flex w-full justify-between">
-									<div class=" self-center text-xs font-medium">{$i18n.t('Minimum Score')}</div>
+							{#if RAGConfig.ENABLE_RAG_HYBRID_SEARCH === true}
+								<div class="mb-2.5 flex w-full justify-between">
+									<div class="self-center text-xs font-medium">{$i18n.t('Top K Reranker')}</div>
 									<div class="flex items-center relative">
 										<input
 											class="flex-1 w-full rounded-lg text-sm bg-transparent outline-hidden"
 											type="number"
-											step="0.01"
-											placeholder={$i18n.t('Enter Score')}
-											bind:value={querySettings.r}
+											placeholder={$i18n.t('Enter Top K Reranker')}
+											bind:value={RAGConfig.TOP_K_RERANKER}
 											autocomplete="off"
-											min="0.0"
-											title={$i18n.t(
-												'The score should be a value between 0.0 (0%) and 1.0 (100%).'
-											)}
+											min="0"
 										/>
 									</div>
 								</div>
-								<div class="mt-1 text-xs text-gray-400 dark:text-gray-500">
-									{$i18n.t(
-										'Note: If you set a minimum score, the search will only return documents with a score greater than or equal to the minimum score.'
-									)}
+							{/if}
+
+							{#if RAGConfig.ENABLE_RAG_HYBRID_SEARCH === true}
+								<div class="  mb-2.5 flex flex-col w-full justify-between">
+									<div class=" flex w-full justify-between">
+										<div class=" self-center text-xs font-medium">
+											{$i18n.t('Relevance Threshold')}
+										</div>
+										<div class="flex items-center relative">
+											<input
+												class="flex-1 w-full rounded-lg text-sm bg-transparent outline-hidden"
+												type="number"
+												step="0.01"
+												placeholder={$i18n.t('Enter Score')}
+												bind:value={RAGConfig.RELEVANCE_THRESHOLD}
+												autocomplete="off"
+												min="0.0"
+												title={$i18n.t(
+													'The score should be a value between 0.0 (0%) and 1.0 (100%).'
+												)}
+											/>
+										</div>
+									</div>
+									<div class="mt-1 text-xs text-gray-400 dark:text-gray-500">
+										{$i18n.t(
+											'Note: If you set a minimum score, the search will only return documents with a score greater than or equal to the minimum score.'
+										)}
+									</div>
 								</div>
-							</div>
+							{/if}
 						{/if}
-					{/if}
 
-					<div class="  mb-2.5 flex flex-col w-full justify-between">
-						<div class=" mb-1 text-xs font-medium">{$i18n.t('RAG Template')}</div>
-						<div class="flex w-full items-center relative">
+						<div class="  mb-2.5 flex flex-col w-full justify-between">
+							<div class=" mb-1 text-xs font-medium">{$i18n.t('RAG Template')}</div>
+							<div class="flex w-full items-center relative">
+								<Tooltip
+									content={$i18n.t(
+										'Leave empty to use the default prompt, or enter a custom prompt'
+									)}
+									placement="top-start"
+									className="w-full"
+								>
+									<Textarea
+										bind:value={RAGConfig.RAG_TEMPLATE}
+										placeholder={$i18n.t(
+											'Leave empty to use the default prompt, or enter a custom prompt'
+										)}
+									/>
+								</Tooltip>
+							</div>
+						</div>
+					</div>
+				{/if}
+
+				<div class="mb-3">
+					<div class=" mb-2.5 text-base font-medium">{$i18n.t('Files')}</div>
+
+					<hr class=" border-gray-100 dark:border-gray-850 my-2" />
+
+					<div class="  mb-2.5 flex w-full justify-between">
+						<div class=" self-center text-xs font-medium">{$i18n.t('Max Upload Size')}</div>
+						<div class="flex items-center relative">
 							<Tooltip
-								content={$i18n.t('Leave empty to use the default prompt, or enter a custom prompt')}
+								content={$i18n.t(
+									'The maximum file size in MB. If the file size exceeds this limit, the file will not be uploaded.'
+								)}
 								placement="top-start"
-								className="w-full"
 							>
-								<Textarea
-									bind:value={querySettings.template}
-									placeholder={$i18n.t(
-										'Leave empty to use the default prompt, or enter a custom prompt'
-									)}
+								<input
+									class="flex-1 w-full rounded-lg text-sm bg-transparent outline-hidden"
+									type="number"
+									placeholder={$i18n.t('Leave empty for unlimited')}
+									bind:value={RAGConfig.FILE_MAX_SIZE}
+									autocomplete="off"
+									min="0"
 								/>
 							</Tooltip>
 						</div>
 					</div>
-				</div>
-			{/if}
-
-			<div class="mb-3">
-				<div class=" mb-2.5 text-base font-medium">{$i18n.t('Files')}</div>
-
-				<hr class=" border-gray-100 dark:border-gray-850 my-2" />
-
-				<div class="  mb-2.5 flex w-full justify-between">
-					<div class=" self-center text-xs font-medium">{$i18n.t('Max Upload Size')}</div>
-					<div class="flex items-center relative">
-						<Tooltip
-							content={$i18n.t(
-								'The maximum file size in MB. If the file size exceeds this limit, the file will not be uploaded.'
-							)}
-							placement="top-start"
-						>
-							<input
-								class="flex-1 w-full rounded-lg text-sm bg-transparent outline-hidden"
-								type="number"
-								placeholder={$i18n.t('Leave empty for unlimited')}
-								bind:value={fileMaxSize}
-								autocomplete="off"
-								min="0"
-							/>
-						</Tooltip>
-					</div>
-				</div>
 
-				<div class="  mb-2.5 flex w-full justify-between">
-					<div class=" self-center text-xs font-medium">{$i18n.t('Max Upload Count')}</div>
-					<div class="flex items-center relative">
-						<Tooltip
-							content={$i18n.t(
-								'The maximum number of files that can be used at once in chat. If the number of files exceeds this limit, the files will not be uploaded.'
-							)}
-							placement="top-start"
-						>
-							<input
-								class="flex-1 w-full rounded-lg text-sm bg-transparent outline-hidden"
-								type="number"
-								placeholder={$i18n.t('Leave empty for unlimited')}
-								bind:value={fileMaxCount}
-								autocomplete="off"
-								min="0"
-							/>
-						</Tooltip>
+					<div class="  mb-2.5 flex w-full justify-between">
+						<div class=" self-center text-xs font-medium">{$i18n.t('Max Upload Count')}</div>
+						<div class="flex items-center relative">
+							<Tooltip
+								content={$i18n.t(
+									'The maximum number of files that can be used at once in chat. If the number of files exceeds this limit, the files will not be uploaded.'
+								)}
+								placement="top-start"
+							>
+								<input
+									class="flex-1 w-full rounded-lg text-sm bg-transparent outline-hidden"
+									type="number"
+									placeholder={$i18n.t('Leave empty for unlimited')}
+									bind:value={RAGConfig.FILE_MAX_COUNT}
+									autocomplete="off"
+									min="0"
+								/>
+							</Tooltip>
+						</div>
 					</div>
 				</div>
-			</div>
 
-			<div class="mb-3">
-				<div class=" mb-2.5 text-base font-medium">{$i18n.t('Integration')}</div>
+				<div class="mb-3">
+					<div class=" mb-2.5 text-base font-medium">{$i18n.t('Integration')}</div>
 
-				<hr class=" border-gray-100 dark:border-gray-850 my-2" />
+					<hr class=" border-gray-100 dark:border-gray-850 my-2" />
 
-				<div class="  mb-2.5 flex w-full justify-between">
-					<div class=" self-center text-xs font-medium">{$i18n.t('Google Drive')}</div>
-					<div class="flex items-center relative">
-						<Switch bind:state={enableGoogleDriveIntegration} />
+					<div class="  mb-2.5 flex w-full justify-between">
+						<div class=" self-center text-xs font-medium">{$i18n.t('Google Drive')}</div>
+						<div class="flex items-center relative">
+							<Switch bind:state={RAGConfig.ENABLE_GOOGLE_DRIVE_INTEGRATION} />
+						</div>
 					</div>
-				</div>
 
-				<div class="  mb-2.5 flex w-full justify-between">
-					<div class=" self-center text-xs font-medium">{$i18n.t('OneDrive')}</div>
-					<div class="flex items-center relative">
-						<Switch bind:state={enableOneDriveIntegration} />
+					<div class="  mb-2.5 flex w-full justify-between">
+						<div class=" self-center text-xs font-medium">{$i18n.t('OneDrive')}</div>
+						<div class="flex items-center relative">
+							<Switch bind:state={RAGConfig.ENABLE_ONEDRIVE_INTEGRATION} />
+						</div>
 					</div>
 				</div>
-			</div>
 
-			<div class="mb-3">
-				<div class=" mb-2.5 text-base font-medium">{$i18n.t('Danger Zone')}</div>
-
-				<hr class=" border-gray-100 dark:border-gray-850 my-2" />
-
-				<div class="  mb-2.5 flex w-full justify-between">
-					<div class=" self-center text-xs font-medium">{$i18n.t('Reset Upload Directory')}</div>
-					<div class="flex items-center relative">
-						<button
-							class="text-xs"
-							on:click={() => {
-								showResetUploadDirConfirm = true;
-							}}
-						>
-							{$i18n.t('Reset')}
-						</button>
+				<div class="mb-3">
+					<div class=" mb-2.5 text-base font-medium">{$i18n.t('Danger Zone')}</div>
+
+					<hr class=" border-gray-100 dark:border-gray-850 my-2" />
+
+					<div class="  mb-2.5 flex w-full justify-between">
+						<div class=" self-center text-xs font-medium">{$i18n.t('Reset Upload Directory')}</div>
+						<div class="flex items-center relative">
+							<button
+								class="text-xs"
+								on:click={() => {
+									showResetUploadDirConfirm = true;
+								}}
+							>
+								{$i18n.t('Reset')}
+							</button>
+						</div>
 					</div>
-				</div>
 
-				<div class="  mb-2.5 flex w-full justify-between">
-					<div class=" self-center text-xs font-medium">
-						{$i18n.t('Reset Vector Storage/Knowledge')}
+					<div class="  mb-2.5 flex w-full justify-between">
+						<div class=" self-center text-xs font-medium">
+							{$i18n.t('Reset Vector Storage/Knowledge')}
+						</div>
+						<div class="flex items-center relative">
+							<button
+								class="text-xs"
+								on:click={() => {
+									showResetConfirm = true;
+								}}
+							>
+								{$i18n.t('Reset')}
+							</button>
+						</div>
 					</div>
-					<div class="flex items-center relative">
-						<button
-							class="text-xs"
-							on:click={() => {
-								showResetConfirm = true;
-							}}
-						>
-							{$i18n.t('Reset')}
-						</button>
+					<div class="  mb-2.5 flex w-full justify-between">
+						<div class=" self-center text-xs font-medium">
+							{$i18n.t('Reindex Knowledge Base Vectors')}
+						</div>
+						<div class="flex items-center relative">
+							<button
+								class="text-xs"
+								on:click={() => {
+									showReindexConfirm = true;
+								}}
+							>
+								{$i18n.t('Reindex')}
+							</button>
+						</div>
 					</div>
 				</div>
 			</div>
 		</div>
-	</div>
-	<div class="flex justify-end pt-3 text-sm font-medium">
-		<button
-			class="px-3.5 py-1.5 text-sm font-medium bg-black hover:bg-gray-900 text-white dark:bg-white dark:text-black dark:hover:bg-gray-100 transition rounded-full"
-			type="submit"
-		>
-			{$i18n.t('Save')}
-		</button>
-	</div>
+		<div class="flex justify-end pt-3 text-sm font-medium">
+			<button
+				class="px-3.5 py-1.5 text-sm font-medium bg-black hover:bg-gray-900 text-white dark:bg-white dark:text-black dark:hover:bg-gray-100 transition rounded-full"
+				type="submit"
+			>
+				{$i18n.t('Save')}
+			</button>
+		</div>
+	{:else}
+		<div class="flex items-center justify-center h-full">
+			<Spinner />
+		</div>
+	{/if}
 </form>

+ 9 - 1
src/lib/components/admin/Settings/Models.svelte

@@ -108,7 +108,15 @@
 				toast.success($i18n.t('Model updated successfully'));
 			}
 		} else {
-			const res = await createNewModel(localStorage.token, model).catch((error) => {
+			const res = await createNewModel(localStorage.token, {
+				meta: {},
+				id: model.id,
+				name: model.name,
+				base_model_id: null,
+				params: {},
+				access_control: {},
+				...model
+			}).catch((error) => {
 				return null;
 			});
 

+ 241 - 91
src/lib/components/admin/Settings/WebSearch.svelte

@@ -12,7 +12,6 @@
 
 	export let saveHandler: Function;
 
-	let webConfig = null;
 	let webSearchEngines = [
 		'searxng',
 		'google_pse',
@@ -30,34 +29,38 @@
 		'jina',
 		'bing',
 		'exa',
-		'perplexity'
+		'perplexity',
+		'sougou'
 	];
+	let webLoaderEngines = ['playwright', 'firecrawl', 'tavily'];
 
-	let youtubeLanguage = 'en';
-	let youtubeTranslation = null;
-	let youtubeProxyUrl = '';
+	let webConfig = null;
 
 	const submitHandler = async () => {
 		// Convert domain filter string to array before sending
-		if (webConfig.search.domain_filter_list) {
-			webConfig.search.domain_filter_list = webConfig.search.domain_filter_list
-				.split(',')
+		if (webConfig.WEB_SEARCH_DOMAIN_FILTER_LIST) {
+			webConfig.WEB_SEARCH_DOMAIN_FILTER_LIST = webConfig.WEB_SEARCH_DOMAIN_FILTER_LIST.split(',')
 				.map((domain) => domain.trim())
 				.filter((domain) => domain.length > 0);
 		} else {
-			webConfig.search.domain_filter_list = [];
+			webConfig.WEB_SEARCH_DOMAIN_FILTER_LIST = [];
+		}
+
+		// Convert Youtube loader language string to array before sending
+		if (webConfig.YOUTUBE_LOADER_LANGUAGE) {
+			webConfig.YOUTUBE_LOADER_LANGUAGE = webConfig.YOUTUBE_LOADER_LANGUAGE.split(',')
+				.map((lang) => lang.trim())
+				.filter((lang) => lang.length > 0);
+		} else {
+			webConfig.YOUTUBE_LOADER_LANGUAGE = [];
 		}
 
 		const res = await updateRAGConfig(localStorage.token, {
-			web: webConfig,
-			youtube: {
-				language: youtubeLanguage.split(',').map((lang) => lang.trim()),
-				translation: youtubeTranslation,
-				proxy_url: youtubeProxyUrl
-			}
+			web: webConfig
 		});
 
-		webConfig.search.domain_filter_list = webConfig.search.domain_filter_list.join(', ');
+		webConfig.WEB_SEARCH_DOMAIN_FILTER_LIST = webConfig.WEB_SEARCH_DOMAIN_FILTER_LIST.join(',');
+		webConfig.YOUTUBE_LOADER_LANGUAGE = webConfig.YOUTUBE_LOADER_LANGUAGE.join(',');
 	};
 
 	onMount(async () => {
@@ -65,14 +68,13 @@
 
 		if (res) {
 			webConfig = res.web;
+
 			// Convert array back to comma-separated string for display
-			if (webConfig?.search?.domain_filter_list) {
-				webConfig.search.domain_filter_list = webConfig.search.domain_filter_list.join(', ');
+			if (webConfig?.WEB_SEARCH_DOMAIN_FILTER_LIST) {
+				webConfig.WEB_SEARCH_DOMAIN_FILTER_LIST = webConfig.WEB_SEARCH_DOMAIN_FILTER_LIST.join(',');
 			}
 
-			youtubeLanguage = res.youtube.language.join(',');
-			youtubeTranslation = res.youtube.translation;
-			youtubeProxyUrl = res.youtube.proxy_url;
+			webConfig.YOUTUBE_LOADER_LANGUAGE = webConfig.YOUTUBE_LOADER_LANGUAGE.join(',');
 		}
 	});
 </script>
@@ -97,7 +99,7 @@
 							{$i18n.t('Web Search')}
 						</div>
 						<div class="flex items-center relative">
-							<Switch bind:state={webConfig.search.enabled} />
+							<Switch bind:state={webConfig.ENABLE_WEB_SEARCH} />
 						</div>
 					</div>
 
@@ -108,7 +110,7 @@
 						<div class="flex items-center relative">
 							<select
 								class="dark:bg-gray-900 w-fit pr-8 rounded-sm px-2 p-1 text-xs bg-transparent outline-hidden text-right"
-								bind:value={webConfig.search.engine}
+								bind:value={webConfig.WEB_SEARCH_ENGINE}
 								placeholder={$i18n.t('Select a engine')}
 								required
 							>
@@ -120,8 +122,8 @@
 						</div>
 					</div>
 
-					{#if webConfig.search.engine !== ''}
-						{#if webConfig.search.engine === 'searxng'}
+					{#if webConfig.WEB_SEARCH_ENGINE !== ''}
+						{#if webConfig.WEB_SEARCH_ENGINE === 'searxng'}
 							<div class="mb-2.5 flex w-full flex-col">
 								<div>
 									<div class=" self-center text-xs font-medium mb-1">
@@ -134,14 +136,14 @@
 												class="w-full rounded-lg py-2 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-hidden"
 												type="text"
 												placeholder={$i18n.t('Enter Searxng Query URL')}
-												bind:value={webConfig.search.searxng_query_url}
+												bind:value={webConfig.SEARXNG_QUERY_URL}
 												autocomplete="off"
 											/>
 										</div>
 									</div>
 								</div>
 							</div>
-						{:else if webConfig.search.engine === 'google_pse'}
+						{:else if webConfig.WEB_SEARCH_ENGINE === 'google_pse'}
 							<div class="mb-2.5 flex w-full flex-col">
 								<div>
 									<div class=" self-center text-xs font-medium mb-1">
@@ -150,7 +152,7 @@
 
 									<SensitiveInput
 										placeholder={$i18n.t('Enter Google PSE API Key')}
-										bind:value={webConfig.search.google_pse_api_key}
+										bind:value={webConfig.GOOGLE_PSE_API_KEY}
 									/>
 								</div>
 								<div class="mt-1.5">
@@ -164,14 +166,14 @@
 												class="w-full rounded-lg py-2 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-hidden"
 												type="text"
 												placeholder={$i18n.t('Enter Google PSE Engine Id')}
-												bind:value={webConfig.search.google_pse_engine_id}
+												bind:value={webConfig.GOOGLE_PSE_ENGINE_ID}
 												autocomplete="off"
 											/>
 										</div>
 									</div>
 								</div>
 							</div>
-						{:else if webConfig.search.engine === 'brave'}
+						{:else if webConfig.WEB_SEARCH_ENGINE === 'brave'}
 							<div class="mb-2.5 flex w-full flex-col">
 								<div>
 									<div class=" self-center text-xs font-medium mb-1">
@@ -180,11 +182,11 @@
 
 									<SensitiveInput
 										placeholder={$i18n.t('Enter Brave Search API Key')}
-										bind:value={webConfig.search.brave_search_api_key}
+										bind:value={webConfig.BRAVE_SEARCH_API_KEY}
 									/>
 								</div>
 							</div>
-						{:else if webConfig.search.engine === 'kagi'}
+						{:else if webConfig.WEB_SEARCH_ENGINE === 'kagi'}
 							<div class="mb-2.5 flex w-full flex-col">
 								<div>
 									<div class=" self-center text-xs font-medium mb-1">
@@ -193,12 +195,12 @@
 
 									<SensitiveInput
 										placeholder={$i18n.t('Enter Kagi Search API Key')}
-										bind:value={webConfig.search.kagi_search_api_key}
+										bind:value={webConfig.KAGI_SEARCH_API_KEY}
 									/>
 								</div>
 								.
 							</div>
-						{:else if webConfig.search.engine === 'mojeek'}
+						{:else if webConfig.WEB_SEARCH_ENGINE === 'mojeek'}
 							<div class="mb-2.5 flex w-full flex-col">
 								<div>
 									<div class=" self-center text-xs font-medium mb-1">
@@ -207,11 +209,11 @@
 
 									<SensitiveInput
 										placeholder={$i18n.t('Enter Mojeek Search API Key')}
-										bind:value={webConfig.search.mojeek_search_api_key}
+										bind:value={webConfig.MOJEEK_SEARCH_API_KEY}
 									/>
 								</div>
 							</div>
-						{:else if webConfig.search.engine === 'bocha'}
+						{:else if webConfig.WEB_SEARCH_ENGINE === 'bocha'}
 							<div class="mb-2.5 flex w-full flex-col">
 								<div>
 									<div class=" self-center text-xs font-medium mb-1">
@@ -220,11 +222,11 @@
 
 									<SensitiveInput
 										placeholder={$i18n.t('Enter Bocha Search API Key')}
-										bind:value={webConfig.search.bocha_search_api_key}
+										bind:value={webConfig.BOCHA_SEARCH_API_KEY}
 									/>
 								</div>
 							</div>
-						{:else if webConfig.search.engine === 'serpstack'}
+						{:else if webConfig.WEB_SEARCH_ENGINE === 'serpstack'}
 							<div class="mb-2.5 flex w-full flex-col">
 								<div>
 									<div class=" self-center text-xs font-medium mb-1">
@@ -233,11 +235,11 @@
 
 									<SensitiveInput
 										placeholder={$i18n.t('Enter Serpstack API Key')}
-										bind:value={webConfig.search.serpstack_api_key}
+										bind:value={webConfig.SERPSTACK_API_KEY}
 									/>
 								</div>
 							</div>
-						{:else if webConfig.search.engine === 'serper'}
+						{:else if webConfig.WEB_SEARCH_ENGINE === 'serper'}
 							<div class="mb-2.5 flex w-full flex-col">
 								<div>
 									<div class=" self-center text-xs font-medium mb-1">
@@ -246,11 +248,11 @@
 
 									<SensitiveInput
 										placeholder={$i18n.t('Enter Serper API Key')}
-										bind:value={webConfig.search.serper_api_key}
+										bind:value={webConfig.SERPER_API_KEY}
 									/>
 								</div>
 							</div>
-						{:else if webConfig.search.engine === 'serply'}
+						{:else if webConfig.WEB_SEARCH_ENGINE === 'serply'}
 							<div class="mb-2.5 flex w-full flex-col">
 								<div>
 									<div class=" self-center text-xs font-medium mb-1">
@@ -259,11 +261,24 @@
 
 									<SensitiveInput
 										placeholder={$i18n.t('Enter Serply API Key')}
-										bind:value={webConfig.search.serply_api_key}
+										bind:value={webConfig.SERPLY_API_KEY}
+									/>
+								</div>
+							</div>
+						{:else if webConfig.WEB_SEARCH_ENGINE === 'tavily'}
+							<div class="mb-2.5 flex w-full flex-col">
+								<div>
+									<div class=" self-center text-xs font-medium mb-1">
+										{$i18n.t('Tavily API Key')}
+									</div>
+
+									<SensitiveInput
+										placeholder={$i18n.t('Enter Tavily API Key')}
+										bind:value={webConfig.TAVILY_API_KEY}
 									/>
 								</div>
 							</div>
-						{:else if webConfig.search.engine === 'searchapi'}
+						{:else if webConfig.WEB_SEARCH_ENGINE === 'searchapi'}
 							<div class="mb-2.5 flex w-full flex-col">
 								<div>
 									<div class=" self-center text-xs font-medium mb-1">
@@ -272,7 +287,7 @@
 
 									<SensitiveInput
 										placeholder={$i18n.t('Enter SearchApi API Key')}
-										bind:value={webConfig.search.searchapi_api_key}
+										bind:value={webConfig.SEARCHAPI_API_KEY}
 									/>
 								</div>
 								<div class="mt-1.5">
@@ -286,14 +301,14 @@
 												class="w-full rounded-lg py-2 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-hidden"
 												type="text"
 												placeholder={$i18n.t('Enter SearchApi Engine')}
-												bind:value={webConfig.search.searchapi_engine}
+												bind:value={webConfig.SEARCHAPI_ENGINE}
 												autocomplete="off"
 											/>
 										</div>
 									</div>
 								</div>
 							</div>
-						{:else if webConfig.search.engine === 'serpapi'}
+						{:else if webConfig.WEB_SEARCH_ENGINE === 'serpapi'}
 							<div class="mb-2.5 flex w-full flex-col">
 								<div>
 									<div class=" self-center text-xs font-medium mb-1">
@@ -302,7 +317,7 @@
 
 									<SensitiveInput
 										placeholder={$i18n.t('Enter SerpApi API Key')}
-										bind:value={webConfig.search.serpapi_api_key}
+										bind:value={webConfig.SERPAPI_API_KEY}
 									/>
 								</div>
 								<div class="mt-1.5">
@@ -316,40 +331,58 @@
 												class="w-full rounded-lg py-2 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-hidden"
 												type="text"
 												placeholder={$i18n.t('Enter SerpApi Engine')}
-												bind:value={webConfig.search.serpapi_engine}
+												bind:value={webConfig.SERPAPI_ENGINE}
 												autocomplete="off"
 											/>
 										</div>
 									</div>
 								</div>
 							</div>
-						{:else if webConfig.search.engine === 'tavily'}
+						{:else if webConfig.WEB_SEARCH_ENGINE === 'jina'}
 							<div class="mb-2.5 flex w-full flex-col">
 								<div>
 									<div class=" self-center text-xs font-medium mb-1">
-										{$i18n.t('Tavily API Key')}
+										{$i18n.t('Jina API Key')}
 									</div>
 
 									<SensitiveInput
-										placeholder={$i18n.t('Enter Tavily API Key')}
-										bind:value={webConfig.search.tavily_api_key}
+										placeholder={$i18n.t('Enter Jina API Key')}
+										bind:value={webConfig.JINA_API_KEY}
 									/>
 								</div>
 							</div>
-						{:else if webConfig.search.engine === 'jina'}
+						{:else if webConfig.WEB_SEARCH_ENGINE === 'bing'}
 							<div class="mb-2.5 flex w-full flex-col">
 								<div>
 									<div class=" self-center text-xs font-medium mb-1">
-										{$i18n.t('Jina API Key')}
+										{$i18n.t('Bing Search V7 Endpoint')}
+									</div>
+
+									<div class="flex w-full">
+										<div class="flex-1">
+											<input
+												class="w-full rounded-lg py-2 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-hidden"
+												type="text"
+												placeholder={$i18n.t('Enter Bing Search V7 Endpoint')}
+												bind:value={webConfig.BING_SEARCH_V7_ENDPOINT}
+												autocomplete="off"
+											/>
+										</div>
+									</div>
+								</div>
+
+								<div class="mt-2">
+									<div class=" self-center text-xs font-medium mb-1">
+										{$i18n.t('Bing Search V7 Subscription Key')}
 									</div>
 
 									<SensitiveInput
-										placeholder={$i18n.t('Enter Jina API Key')}
-										bind:value={webConfig.search.jina_api_key}
+										placeholder={$i18n.t('Enter Bing Search V7 Subscription Key')}
+										bind:value={webConfig.BING_SEARCH_V7_SUBSCRIPTION_KEY}
 									/>
 								</div>
 							</div>
-						{:else if webConfig.search.engine === 'exa'}
+						{:else if webConfig.WEB_SEARCH_ENGINE === 'exa'}
 							<div class="mb-2.5 flex w-full flex-col">
 								<div>
 									<div class=" self-center text-xs font-medium mb-1">
@@ -358,11 +391,11 @@
 
 									<SensitiveInput
 										placeholder={$i18n.t('Enter Exa API Key')}
-										bind:value={webConfig.search.exa_api_key}
+										bind:value={webConfig.EXA_API_KEY}
 									/>
 								</div>
 							</div>
-						{:else if webConfig.search.engine === 'perplexity'}
+						{:else if webConfig.WEB_SEARCH_ENGINE === 'perplexity'}
 							<div>
 								<div class=" self-center text-xs font-medium mb-1">
 									{$i18n.t('Perplexity API Key')}
@@ -370,44 +403,38 @@
 
 								<SensitiveInput
 									placeholder={$i18n.t('Enter Perplexity API Key')}
-									bind:value={webConfig.search.perplexity_api_key}
+									bind:value={webConfig.PERPLEXITY_API_KEY}
 								/>
 							</div>
-						{:else if webConfig.search.engine === 'bing'}
+						{:else if webConfig.WEB_SEARCH_ENGINE === 'sougou'}
 							<div class="mb-2.5 flex w-full flex-col">
 								<div>
 									<div class=" self-center text-xs font-medium mb-1">
-										{$i18n.t('Bing Search V7 Endpoint')}
+										{$i18n.t('Sougou Search API sID')}
 									</div>
 
-									<div class="flex w-full">
-										<div class="flex-1">
-											<input
-												class="w-full rounded-lg py-2 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-hidden"
-												type="text"
-												placeholder={$i18n.t('Enter Bing Search V7 Endpoint')}
-												bind:value={webConfig.search.bing_search_v7_endpoint}
-												autocomplete="off"
-											/>
-										</div>
-									</div>
+									<SensitiveInput
+										placeholder={$i18n.t('Enter Sougou Search API sID')}
+										bind:value={webConfig.SOUGOU_API_SID}
+									/>
 								</div>
-
-								<div class="mt-2">
+							</div>
+							<div class="mb-2.5 flex w-full flex-col">
+								<div>
 									<div class=" self-center text-xs font-medium mb-1">
-										{$i18n.t('Bing Search V7 Subscription Key')}
+										{$i18n.t('Sougou Search API SK')}
 									</div>
 
 									<SensitiveInput
-										placeholder={$i18n.t('Enter Bing Search V7 Subscription Key')}
-										bind:value={webConfig.search.bing_search_v7_subscription_key}
+										placeholder={$i18n.t('Enter Sougou Search API SK')}
+										bind:value={webConfig.SOUGOU_API_SK}
 									/>
 								</div>
 							</div>
 						{/if}
 					{/if}
 
-					{#if webConfig.search.enabled}
+					{#if webConfig.ENABLE_WEB_SEARCH}
 						<div class="mb-2.5 flex w-full flex-col">
 							<div class="flex gap-2">
 								<div class="w-full">
@@ -418,7 +445,7 @@
 									<input
 										class="w-full rounded-lg py-2 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-hidden"
 										placeholder={$i18n.t('Search Result Count')}
-										bind:value={webConfig.search.result_count}
+										bind:value={webConfig.WEB_SEARCH_RESULT_COUNT}
 										required
 									/>
 								</div>
@@ -431,7 +458,7 @@
 									<input
 										class="w-full rounded-lg py-2 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-hidden"
 										placeholder={$i18n.t('Concurrent Requests')}
-										bind:value={webConfig.search.concurrent_requests}
+										bind:value={webConfig.WEB_SEARCH_CONCURRENT_REQUESTS}
 										required
 									/>
 								</div>
@@ -448,7 +475,7 @@
 								placeholder={$i18n.t(
 									'Enter domains separated by commas (e.g., example.com,site.org)'
 								)}
-								bind:value={webConfig.search.domain_filter_list}
+								bind:value={webConfig.WEB_SEARCH_DOMAIN_FILTER_LIST}
 							/>
 						</div>
 					{/if}
@@ -480,11 +507,13 @@
 						</div>
 						<div class="flex items-center relative">
 							<Tooltip
-								content={webConfig.search.trust_env
-									? 'Use proxy designated by http_proxy and https_proxy environment variables to fetch page contents'
-									: 'Use no proxy to fetch page contents.'}
+								content={webConfig.WEB_SEARCH_TRUST_ENV
+									? $i18n.t(
+											'Use proxy designated by http_proxy and https_proxy environment variables to fetch page contents.'
+										)
+									: $i18n.t('Use no proxy to fetch page contents.')}
 							>
-								<Switch bind:state={webConfig.search.trust_env} />
+								<Switch bind:state={webConfig.WEB_SEARCH_TRUST_ENV} />
 							</Tooltip>
 						</div>
 					</div>
@@ -497,13 +526,134 @@
 
 					<div class="  mb-2.5 flex w-full justify-between">
 						<div class=" self-center text-xs font-medium">
-							{$i18n.t('Bypass SSL verification for Websites')}
+							{$i18n.t('Web Loader Engine')}
 						</div>
 						<div class="flex items-center relative">
-							<Switch bind:state={webConfig.ENABLE_RAG_WEB_LOADER_SSL_VERIFICATION} />
+							<select
+								class="dark:bg-gray-900 w-fit pr-8 rounded-sm px-2 p-1 text-xs bg-transparent outline-hidden text-right"
+								bind:value={webConfig.WEB_LOADER_ENGINE}
+								placeholder={$i18n.t('Select a engine')}
+							>
+								<option value="">{$i18n.t('Default')}</option>
+								{#each webLoaderEngines as engine}
+									<option value={engine}>{engine}</option>
+								{/each}
+							</select>
 						</div>
 					</div>
 
+					{#if webConfig.WEB_LOADER_ENGINE === '' || webConfig.WEB_LOADER_ENGINE === 'safe_web'}
+						<div class="  mb-2.5 flex w-full justify-between">
+							<div class=" self-center text-xs font-medium">
+								{$i18n.t('Verify SSL Certificate')}
+							</div>
+							<div class="flex items-center relative">
+								<Switch bind:state={webConfig.ENABLE_WEB_LOADER_SSL_VERIFICATION} />
+							</div>
+						</div>
+					{:else if webConfig.WEB_LOADER_ENGINE === 'playwright'}
+						<div class="mb-2.5 flex w-full flex-col">
+							<div>
+								<div class=" self-center text-xs font-medium mb-1">
+									{$i18n.t('Playwright WebSocket URL')}
+								</div>
+
+								<div class="flex w-full">
+									<div class="flex-1">
+										<input
+											class="w-full rounded-lg py-2 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-hidden"
+											type="text"
+											placeholder={$i18n.t('Enter Playwright WebSocket URL')}
+											bind:value={webConfig.PLAYWRIGHT_WS_URL}
+											autocomplete="off"
+										/>
+									</div>
+								</div>
+							</div>
+
+							<div class="mt-2">
+								<div class=" self-center text-xs font-medium mb-1">
+									{$i18n.t('Playwright Timeout (ms)')}
+								</div>
+
+								<div class="flex w-full">
+									<div class="flex-1">
+										<input
+											class="w-full rounded-lg py-2 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-hidden"
+											placeholder={$i18n.t('Enter Playwright Timeout')}
+											bind:value={webConfig.PLAYWRIGHT_TIMEOUT}
+											autocomplete="off"
+										/>
+									</div>
+								</div>
+							</div>
+						</div>
+					{:else if webConfig.WEB_LOADER_ENGINE === 'firecrawl'}
+						<div class="mb-2.5 flex w-full flex-col">
+							<div>
+								<div class=" self-center text-xs font-medium mb-1">
+									{$i18n.t('Firecrawl API Base URL')}
+								</div>
+
+								<div class="flex w-full">
+									<div class="flex-1">
+										<input
+											class="w-full rounded-lg py-2 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-hidden"
+											type="text"
+											placeholder={$i18n.t('Enter Firecrawl API Base URL')}
+											bind:value={webConfig.FIRECRAWL_API_BASE_URL}
+											autocomplete="off"
+										/>
+									</div>
+								</div>
+							</div>
+
+							<div class="mt-2">
+								<div class=" self-center text-xs font-medium mb-1">
+									{$i18n.t('Firecrawl API Key')}
+								</div>
+
+								<SensitiveInput
+									placeholder={$i18n.t('Enter Firecrawl API Key')}
+									bind:value={webConfig.FIRECRAWL_API_KEY}
+								/>
+							</div>
+						</div>
+					{:else if webConfig.WEB_LOADER_ENGINE === 'tavily'}
+						<div class="mb-2.5 flex w-full flex-col">
+							<div>
+								<div class=" self-center text-xs font-medium mb-1">
+									{$i18n.t('Tavily Extract Depth')}
+								</div>
+
+								<div class="flex w-full">
+									<div class="flex-1">
+										<input
+											class="w-full rounded-lg py-2 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-hidden"
+											type="text"
+											placeholder={$i18n.t('Enter Tavily Extract Depth')}
+											bind:value={webConfig.TAVILY_EXTRACT_DEPTH}
+											autocomplete="off"
+										/>
+									</div>
+								</div>
+							</div>
+
+							{#if webConfig.WEB_SEARCH_ENGINE !== 'tavily'}
+								<div class="mt-2">
+									<div class=" self-center text-xs font-medium mb-1">
+										{$i18n.t('Tavily API Key')}
+									</div>
+
+									<SensitiveInput
+										placeholder={$i18n.t('Enter Tavily API Key')}
+										bind:value={webConfig.TAVILY_API_KEY}
+									/>
+								</div>
+							{/if}
+						</div>
+					{/if}
+
 					<div class="  mb-2.5 flex w-full justify-between">
 						<div class=" self-center text-xs font-medium">
 							{$i18n.t('Youtube Language')}
@@ -513,7 +663,7 @@
 								class="flex-1 w-full rounded-lg text-sm bg-transparent outline-hidden"
 								type="text"
 								placeholder={$i18n.t('Enter language codes')}
-								bind:value={youtubeLanguage}
+								bind:value={webConfig.YOUTUBE_LOADER_LANGUAGE}
 								autocomplete="off"
 							/>
 						</div>
@@ -528,7 +678,7 @@
 								class="flex-1 w-full rounded-lg text-sm bg-transparent outline-hidden"
 								type="text"
 								placeholder={$i18n.t('Enter proxy URL (e.g. https://user:password@host:port)')}
-								bind:value={youtubeProxyUrl}
+								bind:value={webConfig.YOUTUBE_LOADER_PROXY_URL}
 								autocomplete="off"
 							/>
 						</div>

+ 1 - 0
src/lib/components/admin/Users/Groups.svelte

@@ -63,6 +63,7 @@
 			file_upload: true,
 			delete: true,
 			edit: true,
+			multiple_models: true,
 			temporary: true,
 			temporary_enforced: false
 		},

+ 9 - 0
src/lib/components/admin/Users/Groups/Permissions.svelte

@@ -24,6 +24,7 @@
 			delete: true,
 			edit: true,
 			file_upload: true,
+			multiple_models: true,
 			temporary: true,
 			temporary_enforced: false
 		},
@@ -272,6 +273,14 @@
 			<Switch bind:state={permissions.chat.edit} />
 		</div>
 
+		<div class="  flex w-full justify-between my-2 pr-2">
+			<div class=" self-center text-xs font-medium">
+				{$i18n.t('Allow Multiple Models in Chat')}
+			</div>
+
+			<Switch bind:state={permissions.chat.multiple_models} />
+		</div>
+
 		<div class="  flex w-full justify-between my-2 pr-2">
 			<div class=" self-center text-xs font-medium">
 				{$i18n.t('Allow Temporary Chat')}

+ 25 - 0
src/lib/components/admin/Users/UserList.svelte

@@ -29,6 +29,7 @@
 	import ChevronDown from '$lib/components/icons/ChevronDown.svelte';
 	import About from '$lib/components/chat/Settings/About.svelte';
 	import Banner from '$lib/components/common/Banner.svelte';
+	import Markdown from '$lib/components/chat/Messages/Markdown.svelte';
 
 	const i18n = getContext('i18n');
 
@@ -486,3 +487,27 @@
 </div>
 
 <Pagination bind:page count={users.length} />
+
+{#if !$config?.license_metadata}
+	{#if users.length > 50}
+		<div class="text-sm">
+			<Markdown
+				content={`
+> [!NOTE]
+> # **Hey there! 👋**
+>
+> It looks like you have over 50 users — that usually falls under organizational usage.
+> 
+> Open WebUI is proudly open source and completely free, with no hidden limits — and we'd love to keep it that way. 🌱  
+>
+> By supporting the project through sponsorship or an enterprise license, you’re not only helping us stay independent, you’re also helping us ship new features faster, improve stability, and grow the project for the long haul. With an *enterprise license*, you also get additional perks like dedicated support, customization options, and more — all at a fraction of what it would cost to build and maintain internally.  
+> 
+> Your support helps us stay independent and continue building great tools for everyone. 💛
+> 
+> - 👉 **[Click here to learn more about enterprise licensing](https://docs.openwebui.com/enterprise)**
+> - 👉 *[Click here to sponsor the project on GitHub](https://github.com/sponsors/tjbck)*
+`}
+			/>
+		</div>
+	{/if}
+{/if}

+ 113 - 105
src/lib/components/chat/Artifacts.svelte

@@ -4,7 +4,7 @@
 	const i18n = getContext('i18n');
 	const dispatch = createEventDispatcher();
 
-	import { chatId, showArtifacts, showControls } from '$lib/stores';
+	import { chatId, settings, showArtifacts, showControls } from '$lib/stores';
 	import XMark from '../icons/XMark.svelte';
 	import { copyToClipboard, createMessagesList } from '$lib/utils';
 	import ArrowsPointingOut from '../icons/ArrowsPointingOut.svelte';
@@ -184,35 +184,117 @@
 </script>
 
 <div class=" w-full h-full relative flex flex-col bg-gray-50 dark:bg-gray-850">
-	<div class="w-full h-full flex-1 relative">
+	<div class="w-full h-full flex flex-col flex-1 relative">
+		{#if contents.length > 0}
+			<div
+				class="pointer-events-auto z-20 flex justify-between items-center p-2.5 font-primar text-gray-900 dark:text-white"
+			>
+				<button
+					class="self-center pointer-events-auto p-1 rounded-full bg-white dark:bg-gray-850"
+					on:click={() => {
+						showArtifacts.set(false);
+					}}
+				>
+					<ArrowLeft className="size-3.5  text-gray-900 dark:text-white" />
+				</button>
+
+				<div class="flex-1 flex items-center justify-between">
+					<div class="flex items-center space-x-2">
+						<div class="flex items-center gap-0.5 self-center min-w-fit" dir="ltr">
+							<button
+								class="self-center p-1 hover:bg-black/5 dark:hover:bg-white/5 dark:hover:text-white hover:text-black rounded-md transition disabled:cursor-not-allowed"
+								on:click={() => navigateContent('prev')}
+								disabled={contents.length <= 1}
+							>
+								<svg
+									xmlns="http://www.w3.org/2000/svg"
+									fill="none"
+									viewBox="0 0 24 24"
+									stroke="currentColor"
+									stroke-width="2.5"
+									class="size-3.5"
+								>
+									<path
+										stroke-linecap="round"
+										stroke-linejoin="round"
+										d="M15.75 19.5 8.25 12l7.5-7.5"
+									/>
+								</svg>
+							</button>
+
+							<div class="text-xs self-center dark:text-gray-100 min-w-fit">
+								{$i18n.t('Version {{selectedVersion}} of {{totalVersions}}', {
+									selectedVersion: selectedContentIdx + 1,
+									totalVersions: contents.length
+								})}
+							</div>
+
+							<button
+								class="self-center p-1 hover:bg-black/5 dark:hover:bg-white/5 dark:hover:text-white hover:text-black rounded-md transition disabled:cursor-not-allowed"
+								on:click={() => navigateContent('next')}
+								disabled={contents.length <= 1}
+							>
+								<svg
+									xmlns="http://www.w3.org/2000/svg"
+									fill="none"
+									viewBox="0 0 24 24"
+									stroke="currentColor"
+									stroke-width="2.5"
+									class="size-3.5"
+								>
+									<path
+										stroke-linecap="round"
+										stroke-linejoin="round"
+										d="m8.25 4.5 7.5 7.5-7.5 7.5"
+									/>
+								</svg>
+							</button>
+						</div>
+					</div>
+
+					<div class="flex items-center gap-1">
+						<button
+							class="copy-code-button bg-none border-none text-xs bg-gray-50 hover:bg-gray-100 dark:bg-gray-850 dark:hover:bg-gray-800 transition rounded-md px-1.5 py-0.5"
+							on:click={() => {
+								copyToClipboard(contents[selectedContentIdx].content);
+								copied = true;
+
+								setTimeout(() => {
+									copied = false;
+								}, 2000);
+							}}>{copied ? $i18n.t('Copied') : $i18n.t('Copy')}</button
+						>
+
+						{#if contents[selectedContentIdx].type === 'iframe'}
+							<Tooltip content={$i18n.t('Open in full screen')}>
+								<button
+									class=" bg-none border-none text-xs bg-gray-50 hover:bg-gray-100 dark:bg-gray-850 dark:hover:bg-gray-800 transition rounded-md p-0.5"
+									on:click={showFullScreen}
+								>
+									<ArrowsPointingOut className="size-3.5" />
+								</button>
+							</Tooltip>
+						{/if}
+					</div>
+				</div>
+
+				<button
+					class="self-center pointer-events-auto p-1 rounded-full bg-white dark:bg-gray-850"
+					on:click={() => {
+						dispatch('close');
+						showControls.set(false);
+						showArtifacts.set(false);
+					}}
+				>
+					<XMark className="size-3.5 text-gray-900 dark:text-white" />
+				</button>
+			</div>
+		{/if}
+
 		{#if overlay}
 			<div class=" absolute top-0 left-0 right-0 bottom-0 z-10"></div>
 		{/if}
 
-		<div class="absolute pointer-events-none z-50 w-full flex items-center justify-start p-4">
-			<button
-				class="self-center pointer-events-auto p-1 rounded-full bg-white dark:bg-gray-850"
-				on:click={() => {
-					showArtifacts.set(false);
-				}}
-			>
-				<ArrowLeft className="size-3.5  text-gray-900 dark:text-white" />
-			</button>
-		</div>
-
-		<div class=" absolute pointer-events-none z-50 w-full flex items-center justify-end p-4">
-			<button
-				class="self-center pointer-events-auto p-1 rounded-full bg-white dark:bg-gray-850"
-				on:click={() => {
-					dispatch('close');
-					showControls.set(false);
-					showArtifacts.set(false);
-				}}
-			>
-				<XMark className="size-3.5 text-gray-900 dark:text-white" />
-			</button>
-		</div>
-
 		<div class="flex-1 w-full h-full">
 			<div class=" h-full flex flex-col">
 				{#if contents.length > 0}
@@ -223,7 +305,11 @@
 								title="Content"
 								srcdoc={contents[selectedContentIdx].content}
 								class="w-full border-0 h-full rounded-none"
-								sandbox="allow-scripts allow-forms allow-same-origin"
+								sandbox="allow-scripts{($settings?.iframeSandboxAllowForms ?? false)
+									? ' allow-forms'
+									: ''}{($settings?.iframeSandboxAllowSameOrigin ?? false)
+									? ' allow-same-origin'
+									: ''}"
 								on:load={iframeLoadHandler}
 							></iframe>
 						{:else if contents[selectedContentIdx].type === 'svg'}
@@ -241,82 +327,4 @@
 			</div>
 		</div>
 	</div>
-
-	{#if contents.length > 0}
-		<div class="flex justify-between items-center p-2.5 font-primar text-gray-900 dark:text-white">
-			<div class="flex items-center space-x-2">
-				<div class="flex items-center gap-0.5 self-center min-w-fit" dir="ltr">
-					<button
-						class="self-center p-1 hover:bg-black/5 dark:hover:bg-white/5 dark:hover:text-white hover:text-black rounded-md transition disabled:cursor-not-allowed"
-						on:click={() => navigateContent('prev')}
-						disabled={contents.length <= 1}
-					>
-						<svg
-							xmlns="http://www.w3.org/2000/svg"
-							fill="none"
-							viewBox="0 0 24 24"
-							stroke="currentColor"
-							stroke-width="2.5"
-							class="size-3.5"
-						>
-							<path
-								stroke-linecap="round"
-								stroke-linejoin="round"
-								d="M15.75 19.5 8.25 12l7.5-7.5"
-							/>
-						</svg>
-					</button>
-
-					<div class="text-xs self-center dark:text-gray-100 min-w-fit">
-						{$i18n.t('Version {{selectedVersion}} of {{totalVersions}}', {
-							selectedVersion: selectedContentIdx + 1,
-							totalVersions: contents.length
-						})}
-					</div>
-
-					<button
-						class="self-center p-1 hover:bg-black/5 dark:hover:bg-white/5 dark:hover:text-white hover:text-black rounded-md transition disabled:cursor-not-allowed"
-						on:click={() => navigateContent('next')}
-						disabled={contents.length <= 1}
-					>
-						<svg
-							xmlns="http://www.w3.org/2000/svg"
-							fill="none"
-							viewBox="0 0 24 24"
-							stroke="currentColor"
-							stroke-width="2.5"
-							class="size-3.5"
-						>
-							<path stroke-linecap="round" stroke-linejoin="round" d="m8.25 4.5 7.5 7.5-7.5 7.5" />
-						</svg>
-					</button>
-				</div>
-			</div>
-
-			<div class="flex items-center gap-1">
-				<button
-					class="copy-code-button bg-none border-none text-xs bg-gray-50 hover:bg-gray-100 dark:bg-gray-850 dark:hover:bg-gray-800 transition rounded-md px-1.5 py-0.5"
-					on:click={() => {
-						copyToClipboard(contents[selectedContentIdx].content);
-						copied = true;
-
-						setTimeout(() => {
-							copied = false;
-						}, 2000);
-					}}>{copied ? $i18n.t('Copied') : $i18n.t('Copy')}</button
-				>
-
-				{#if contents[selectedContentIdx].type === 'iframe'}
-					<Tooltip content={$i18n.t('Open in full screen')}>
-						<button
-							class=" bg-none border-none text-xs bg-gray-50 hover:bg-gray-100 dark:bg-gray-850 dark:hover:bg-gray-800 transition rounded-md p-0.5"
-							on:click={showFullScreen}
-						>
-							<ArrowsPointingOut className="size-3.5" />
-						</button>
-					</Tooltip>
-				{/if}
-			</div>
-		</div>
-	{/if}
 </div>

+ 62 - 87
src/lib/components/chat/Chat.svelte

@@ -74,7 +74,8 @@
 		generateQueries,
 		chatAction,
 		generateMoACompletion,
-		stopTask
+		stopTask,
+		getTaskIdsByChatId
 	} from '$lib/apis';
 	import { getTools } from '$lib/apis/tools';
 
@@ -130,7 +131,7 @@
 		currentId: null
 	};
 
-	let taskId = null;
+	let taskIds = null;
 
 	// Chat Input
 	let prompt = '';
@@ -401,6 +402,7 @@
 		if (!$chatId) {
 			chatIdUnsubscriber = chatId.subscribe(async (value) => {
 				if (!value) {
+					await tick(); // Wait for DOM updates
 					await initNewChat();
 				}
 			});
@@ -817,8 +819,21 @@
 				await tick();
 
 				if (history.currentId) {
-					history.messages[history.currentId].done = true;
+					for (const message of Object.values(history.messages)) {
+						if (message.role === 'assistant') {
+							message.done = true;
+						}
+					}
+				}
+
+				const taskRes = await getTaskIdsByChatId(localStorage.token, $chatId).catch((error) => {
+					return null;
+				});
+
+				if (taskRes) {
+					taskIds = taskRes.task_ids;
 				}
+
 				await tick();
 
 				return true;
@@ -890,7 +905,7 @@
 			}
 		}
 
-		taskId = null;
+		taskIds = null;
 	};
 
 	const chatActionHandler = async (chatId, actionId, modelId, responseMessageId, event = null) => {
@@ -1648,7 +1663,11 @@
 			if (res.error) {
 				await handleOpenAIError(res.error, responseMessage);
 			} else {
-				taskId = res.task_id;
+				if (taskIds) {
+					taskIds.push(res.task_id);
+				} else {
+					taskIds = [res.task_id];
+				}
 			}
 		}
 
@@ -1699,23 +1718,26 @@
 	};
 
 	const stopResponse = async () => {
-		if (taskId) {
-			const res = await stopTask(localStorage.token, taskId).catch((error) => {
-				toast.error(`${error}`);
-				return null;
-			});
+		if (taskIds) {
+			for (const taskId of taskIds) {
+				const res = await stopTask(localStorage.token, taskId).catch((error) => {
+					toast.error(`${error}`);
+					return null;
+				});
+			}
 
-			if (res) {
-				taskId = null;
+			taskIds = null;
 
-				const responseMessage = history.messages[history.currentId];
-				responseMessage.done = true;
+			const responseMessage = history.messages[history.currentId];
+			// Set all response messages to done
+			for (const messageId of history.messages[responseMessage.parentId].childrenIds) {
+				history.messages[messageId].done = true;
+			}
 
-				history.messages[history.currentId] = responseMessage;
+			history.messages[history.currentId] = responseMessage;
 
-				if (autoScroll) {
-					scrollToBottom();
-				}
+			if (autoScroll) {
+				scrollToBottom();
 			}
 		}
 	};
@@ -1927,7 +1949,7 @@
 		: ' '} w-full max-w-full flex flex-col"
 	id="chat-container"
 >
-	{#if chatIdProp === '' || (!loading && chatIdProp)}
+	{#if !loading}
 		{#if $settings?.backgroundImageUrl ?? null}
 			<div
 				class="absolute {$showSidebar
@@ -1941,75 +1963,27 @@
 			/>
 		{/if}
 
-		<Navbar
-			bind:this={navbarElement}
-			chat={{
-				id: $chatId,
-				chat: {
-					title: $chatTitle,
-					models: selectedModels,
-					system: $settings.system ?? undefined,
-					params: params,
-					history: history,
-					timestamp: Date.now()
-				}
-			}}
-			title={$chatTitle}
-			bind:selectedModels
-			shareEnabled={!!history.currentId}
-			{initNewChat}
-		/>
-
 		<PaneGroup direction="horizontal" class="w-full h-full">
-			<Pane defaultSize={50} class="h-full flex w-full relative">
-				{#if !history.currentId && !$chatId && selectedModels.length <= 1 && ($banners.length > 0 || ($config?.license_metadata?.type ?? null) === 'trial' || (($config?.license_metadata?.seats ?? null) !== null && $config?.user_count > $config?.license_metadata?.seats))}
-					<div class="absolute top-12 left-0 right-0 w-full z-30">
-						<div class=" flex flex-col gap-1 w-full">
-							{#if ($config?.license_metadata?.type ?? null) === 'trial'}
-								<Banner
-									banner={{
-										type: 'info',
-										title: 'Trial License',
-										content: $i18n.t(
-											'You are currently using a trial license. Please contact support to upgrade your license.'
-										)
-									}}
-								/>
-							{/if}
-
-							{#if ($config?.license_metadata?.seats ?? null) !== null && $config?.user_count > $config?.license_metadata?.seats}
-								<Banner
-									banner={{
-										type: 'error',
-										title: 'License Error',
-										content: $i18n.t(
-											'Exceeded the number of seats in your license. Please contact support to increase the number of seats.'
-										)
-									}}
-								/>
-							{/if}
-
-							{#each $banners.filter( (b) => (b.dismissible ? !JSON.parse(localStorage.getItem('dismissedBannerIds') ?? '[]').includes(b.id) : true) ) as banner}
-								<Banner
-									{banner}
-									on:dismiss={(e) => {
-										const bannerId = e.detail;
-
-										localStorage.setItem(
-											'dismissedBannerIds',
-											JSON.stringify(
-												[
-													bannerId,
-													...JSON.parse(localStorage.getItem('dismissedBannerIds') ?? '[]')
-												].filter((id) => $banners.find((b) => b.id === id))
-											)
-										);
-									}}
-								/>
-							{/each}
-						</div>
-					</div>
-				{/if}
+			<Pane defaultSize={50} class="h-full flex relative max-w-full flex-col">
+				<Navbar
+					bind:this={navbarElement}
+					chat={{
+						id: $chatId,
+						chat: {
+							title: $chatTitle,
+							models: selectedModels,
+							system: $settings.system ?? undefined,
+							params: params,
+							history: history,
+							timestamp: Date.now()
+						}
+					}}
+					{history}
+					title={$chatTitle}
+					bind:selectedModels
+					shareEnabled={!!history.currentId}
+					{initNewChat}
+				/>
 
 				<div class="flex flex-col flex-auto z-10 w-full @container">
 					{#if $settings?.landingPageMode === 'chat' || createMessagesList(history, history.currentId).length > 0}
@@ -2047,6 +2021,7 @@
 						<div class=" pb-[1rem]">
 							<MessageInput
 								{history}
+								{taskIds}
 								{selectedModels}
 								bind:files
 								bind:prompt

+ 3 - 3
src/lib/components/chat/ChatControls.svelte

@@ -223,14 +223,14 @@
 				showControls.set(false);
 			}}
 			collapsible={true}
-			class="pt-8"
+			class=" z-10 "
 		>
 			{#if $showControls}
-				<div class="pr-4 pb-8 flex max-h-full min-h-full">
+				<div class="flex max-h-full min-h-full">
 					<div
 						class="w-full {($showOverview || $showArtifacts) && !$showCallOverlay
 							? ' '
-							: 'px-4 py-4 bg-white dark:shadow-lg dark:bg-gray-850  border border-gray-100 dark:border-gray-850'}  rounded-xl z-40 pointer-events-auto overflow-y-auto scrollbar-hidden"
+							: 'px-4 py-4 bg-white dark:shadow-lg dark:bg-gray-850  border border-gray-100 dark:border-gray-850'} z-40 pointer-events-auto overflow-y-auto scrollbar-hidden"
 					>
 						{#if $showCallOverlay}
 							<div class="w-full h-full flex justify-center">

+ 1 - 1
src/lib/components/chat/ChatPlaceholder.svelte

@@ -56,7 +56,7 @@
 									($i18n.language === 'dg-DG'
 										? `/doge.png`
 										: `${WEBUI_BASE_URL}/static/favicon.png`)}
-								class=" size-[2.7rem] rounded-full border-[1px] border-gray-200 dark:border-none"
+								class=" size-[2.7rem] rounded-full border-[1px] border-gray-100 dark:border-none"
 								alt="logo"
 								draggable="false"
 							/>

+ 99 - 107
src/lib/components/chat/MessageInput.svelte

@@ -71,6 +71,7 @@
 	$: selectedModelIds = atSelectedModel !== undefined ? [atSelectedModel.id] : selectedModels;
 
 	export let history;
+	export let taskIds = null;
 
 	export let prompt = '';
 	export let files = [];
@@ -509,7 +510,7 @@
 							}}
 						>
 							<div
-								class="flex-1 flex flex-col relative w-full shadow-lg rounded-3xl border border-gray-100 dark:border-gray-850 hover:border-gray-200 focus-within:border-gray-200 hover:dark:border-gray-800 focus-within:dark:border-gray-800 transition px-1 bg-white/90 dark:bg-gray-400/5 dark:text-gray-100"
+								class="flex-1 flex flex-col relative w-full shadow-lg rounded-3xl border border-gray-50 dark:border-gray-850 hover:border-gray-100 focus-within:border-gray-100 hover:dark:border-gray-800 focus-within:dark:border-gray-800 transition px-1 bg-white/90 dark:bg-gray-400/5 dark:text-gray-100"
 								dir={$settings?.chatDirection ?? 'auto'}
 							>
 								{#if files.length > 0}
@@ -1033,7 +1034,7 @@
 									{/if}
 								</div>
 
-								<div class=" flex justify-between mt-1.5 mb-2.5 mx-0.5 max-w-full" dir="ltr">
+								<div class=" flex justify-between mt-1 mb-2.5 mx-0.5 max-w-full" dir="ltr">
 									<div class="ml-1 self-end flex items-center flex-1 max-w-[80%] gap-0.5">
 										<InputMenu
 											bind:selectedToolIds
@@ -1102,7 +1103,7 @@
 											</button>
 										</InputMenu>
 
-										<div class="flex gap-[2px] items-center overflow-x-auto scrollbar-none flex-1">
+										<div class="flex gap-1 items-center overflow-x-auto scrollbar-none flex-1">
 											{#if toolServers.length + selectedToolIds.length > 0}
 												<Tooltip
 													content={$i18n.t('{{COUNT}} Available Tools', {
@@ -1132,10 +1133,10 @@
 														<button
 															on:click|preventDefault={() => (webSearchEnabled = !webSearchEnabled)}
 															type="button"
-															class="px-1.5 @xl:px-2.5 py-1.5 flex gap-1.5 items-center text-sm rounded-full font-medium transition-colors duration-300 focus:outline-hidden max-w-full overflow-hidden {webSearchEnabled ||
+															class="px-1.5 @xl:px-2.5 py-1.5 flex gap-1.5 items-center text-sm rounded-full font-medium transition-colors duration-300 focus:outline-hidden max-w-full overflow-hidden border {webSearchEnabled ||
 															($settings?.webSearch ?? false) === 'always'
-																? 'bg-blue-100 dark:bg-blue-500/20 text-blue-500 dark:text-blue-400'
-																: 'bg-transparent text-gray-600 dark:text-gray-300 border-gray-200 hover:bg-gray-100 dark:hover:bg-gray-800'}"
+																? 'bg-blue-100 dark:bg-blue-500/20 border-blue-400/20 text-blue-500 dark:text-blue-400'
+																: 'bg-transparent border-transparent text-gray-600 dark:text-gray-300 border-gray-200 hover:bg-gray-100 dark:hover:bg-gray-800'}"
 														>
 															<GlobeAlt className="size-5" strokeWidth="1.75" />
 															<span
@@ -1152,9 +1153,9 @@
 															on:click|preventDefault={() =>
 																(imageGenerationEnabled = !imageGenerationEnabled)}
 															type="button"
-															class="px-1.5 @xl:px-2.5 py-1.5 flex gap-1.5 items-center text-sm rounded-full font-medium transition-colors duration-300 focus:outline-hidden max-w-full overflow-hidden {imageGenerationEnabled
-																? 'bg-gray-100 dark:bg-gray-500/20 text-gray-600 dark:text-gray-400'
-																: 'bg-transparent text-gray-600 dark:text-gray-300 border-gray-200 hover:bg-gray-100 dark:hover:bg-gray-800 '}"
+															class="px-1.5 @xl:px-2.5 py-1.5 flex gap-1.5 items-center text-sm rounded-full font-medium transition-colors duration-300 focus:outline-hidden max-w-full overflow-hidden border {imageGenerationEnabled
+																? 'bg-gray-50 dark:bg-gray-400/10 border-gray-100 dark:border-gray-700 text-gray-600 dark:text-gray-400'
+																: 'bg-transparent border-transparent text-gray-600 dark:text-gray-300  hover:bg-gray-100 dark:hover:bg-gray-800 '}"
 														>
 															<Photo className="size-5" strokeWidth="1.75" />
 															<span
@@ -1171,9 +1172,9 @@
 															on:click|preventDefault={() =>
 																(codeInterpreterEnabled = !codeInterpreterEnabled)}
 															type="button"
-															class="px-1.5 @xl:px-2.5 py-1.5 flex gap-1.5 items-center text-sm rounded-full font-medium transition-colors duration-300 focus:outline-hidden max-w-full overflow-hidden {codeInterpreterEnabled
-																? 'bg-gray-100 dark:bg-gray-500/20 text-gray-600 dark:text-gray-400'
-																: 'bg-transparent text-gray-600 dark:text-gray-300 border-gray-200 hover:bg-gray-100 dark:hover:bg-gray-800 '}"
+															class="px-1.5 @xl:px-2.5 py-1.5 flex gap-1.5 items-center text-sm rounded-full font-medium transition-colors duration-300 focus:outline-hidden max-w-full overflow-hidden border {codeInterpreterEnabled
+																? 'bg-gray-50 dark:bg-gray-400/10 border-gray-100  dark:border-gray-700 text-gray-600 dark:text-gray-400  '
+																: 'bg-transparent border-transparent text-gray-600 dark:text-gray-300  hover:bg-gray-100 dark:hover:bg-gray-800 '}"
 														>
 															<CommandLine className="size-5" strokeWidth="1.75" />
 															<span
@@ -1237,121 +1238,112 @@
 											</Tooltip>
 										{/if}
 
-										{#if !history.currentId || history.messages[history.currentId]?.done == true}
-											{#if prompt === '' && files.length === 0}
-												<div class=" flex items-center">
-													<Tooltip content={$i18n.t('Call')}>
-														<button
-															class=" {webSearchEnabled ||
-															($settings?.webSearch ?? false) === 'always'
-																? 'bg-blue-500 text-white hover:bg-blue-400 '
-																: 'bg-black text-white hover:bg-gray-900 dark:bg-white dark:text-black dark:hover:bg-gray-100'} transition rounded-full p-1.5 self-center"
-															type="button"
-															on:click={async () => {
-																if (selectedModels.length > 1) {
-																	toast.error($i18n.t('Select only one model to call'));
+										{#if (taskIds && taskIds.length > 0) || (history.currentId && history.messages[history.currentId]?.done != true)}
+											<div class=" flex items-center">
+												<Tooltip content={$i18n.t('Stop')}>
+													<button
+														class="bg-white hover:bg-gray-100 text-gray-800 dark:bg-gray-700 dark:text-white dark:hover:bg-gray-800 transition rounded-full p-1.5"
+														on:click={() => {
+															stopResponse();
+														}}
+													>
+														<svg
+															xmlns="http://www.w3.org/2000/svg"
+															viewBox="0 0 24 24"
+															fill="currentColor"
+															class="size-5"
+														>
+															<path
+																fill-rule="evenodd"
+																d="M2.25 12c0-5.385 4.365-9.75 9.75-9.75s9.75 4.365 9.75 9.75-4.365 9.75-9.75 9.75S2.25 17.385 2.25 12zm6-2.438c0-.724.588-1.312 1.313-1.312h4.874c.725 0 1.313.588 1.313 1.313v4.874c0 .725-.588 1.313-1.313 1.313H9.564a1.312 1.312 0 01-1.313-1.313V9.564z"
+																clip-rule="evenodd"
+															/>
+														</svg>
+													</button>
+												</Tooltip>
+											</div>
+										{:else if prompt === '' && files.length === 0}
+											<div class=" flex items-center">
+												<Tooltip content={$i18n.t('Call')}>
+													<button
+														class=" bg-black text-white hover:bg-gray-900 dark:bg-white dark:text-black dark:hover:bg-gray-100 transition rounded-full p-1.5 self-center"
+														type="button"
+														on:click={async () => {
+															if (selectedModels.length > 1) {
+																toast.error($i18n.t('Select only one model to call'));
 
-																	return;
-																}
+																return;
+															}
 
-																if ($config.audio.stt.engine === 'web') {
-																	toast.error(
-																		$i18n.t(
-																			'Call feature is not supported when using Web STT engine'
-																		)
-																	);
+															if ($config.audio.stt.engine === 'web') {
+																toast.error(
+																	$i18n.t('Call feature is not supported when using Web STT engine')
+																);
 
-																	return;
-																}
-																// check if user has access to getUserMedia
-																try {
-																	let stream = await navigator.mediaDevices.getUserMedia({
-																		audio: true
-																	});
-																	// If the user grants the permission, proceed to show the call overlay
+																return;
+															}
+															// check if user has access to getUserMedia
+															try {
+																let stream = await navigator.mediaDevices.getUserMedia({
+																	audio: true
+																});
+																// If the user grants the permission, proceed to show the call overlay
 
-																	if (stream) {
-																		const tracks = stream.getTracks();
-																		tracks.forEach((track) => track.stop());
-																	}
+																if (stream) {
+																	const tracks = stream.getTracks();
+																	tracks.forEach((track) => track.stop());
+																}
 
-																	stream = null;
+																stream = null;
 
-																	if ($settings.audio?.tts?.engine === 'browser-kokoro') {
-																		// If the user has not initialized the TTS worker, initialize it
-																		if (!$TTSWorker) {
-																			await TTSWorker.set(
-																				new KokoroWorker({
-																					dtype: $settings.audio?.tts?.engineConfig?.dtype ?? 'fp32'
-																				})
-																			);
+																if ($settings.audio?.tts?.engine === 'browser-kokoro') {
+																	// If the user has not initialized the TTS worker, initialize it
+																	if (!$TTSWorker) {
+																		await TTSWorker.set(
+																			new KokoroWorker({
+																				dtype: $settings.audio?.tts?.engineConfig?.dtype ?? 'fp32'
+																			})
+																		);
 
-																			await $TTSWorker.init();
-																		}
+																		await $TTSWorker.init();
 																	}
-
-																	showCallOverlay.set(true);
-																	showControls.set(true);
-																} catch (err) {
-																	// If the user denies the permission or an error occurs, show an error message
-																	toast.error(
-																		$i18n.t('Permission denied when accessing media devices')
-																	);
 																}
-															}}
-															aria-label="Call"
-														>
-															<Headphone className="size-5" />
-														</button>
-													</Tooltip>
-												</div>
-											{:else}
-												<div class=" flex items-center">
-													<Tooltip content={$i18n.t('Send message')}>
-														<button
-															id="send-message-button"
-															class="{!(prompt === '' && files.length === 0)
-																? webSearchEnabled || ($settings?.webSearch ?? false) === 'always'
-																	? 'bg-blue-500 text-white hover:bg-blue-400 '
-																	: 'bg-black text-white hover:bg-gray-900 dark:bg-white dark:text-black dark:hover:bg-gray-100 '
-																: 'text-white bg-gray-200 dark:text-gray-900 dark:bg-gray-700 disabled'} transition rounded-full p-1.5 self-center"
-															type="submit"
-															disabled={prompt === '' && files.length === 0}
-														>
-															<svg
-																xmlns="http://www.w3.org/2000/svg"
-																viewBox="0 0 16 16"
-																fill="currentColor"
-																class="size-5"
-															>
-																<path
-																	fill-rule="evenodd"
-																	d="M8 14a.75.75 0 0 1-.75-.75V4.56L4.03 7.78a.75.75 0 0 1-1.06-1.06l4.5-4.5a.75.75 0 0 1 1.06 0l4.5 4.5a.75.75 0 0 1-1.06 1.06L8.75 4.56v8.69A.75.75 0 0 1 8 14Z"
-																	clip-rule="evenodd"
-																/>
-															</svg>
-														</button>
-													</Tooltip>
-												</div>
-											{/if}
+
+																showCallOverlay.set(true);
+																showControls.set(true);
+															} catch (err) {
+																// If the user denies the permission or an error occurs, show an error message
+																toast.error(
+																	$i18n.t('Permission denied when accessing media devices')
+																);
+															}
+														}}
+														aria-label="Call"
+													>
+														<Headphone className="size-5" />
+													</button>
+												</Tooltip>
+											</div>
 										{:else}
 											<div class=" flex items-center">
-												<Tooltip content={$i18n.t('Stop')}>
+												<Tooltip content={$i18n.t('Send message')}>
 													<button
-														class="bg-white hover:bg-gray-100 text-gray-800 dark:bg-gray-700 dark:text-white dark:hover:bg-gray-800 transition rounded-full p-1.5"
-														on:click={() => {
-															stopResponse();
-														}}
+														id="send-message-button"
+														class="{!(prompt === '' && files.length === 0)
+															? 'bg-black text-white hover:bg-gray-900 dark:bg-white dark:text-black dark:hover:bg-gray-100 '
+															: 'text-white bg-gray-200 dark:text-gray-900 dark:bg-gray-700 disabled'} transition rounded-full p-1.5 self-center"
+														type="submit"
+														disabled={prompt === '' && files.length === 0}
 													>
 														<svg
 															xmlns="http://www.w3.org/2000/svg"
-															viewBox="0 0 24 24"
+															viewBox="0 0 16 16"
 															fill="currentColor"
 															class="size-5"
 														>
 															<path
 																fill-rule="evenodd"
-																d="M2.25 12c0-5.385 4.365-9.75 9.75-9.75s9.75 4.365 9.75 9.75-4.365 9.75-9.75 9.75S2.25 17.385 2.25 12zm6-2.438c0-.724.588-1.312 1.313-1.312h4.874c.725 0 1.313.588 1.313 1.313v4.874c0 .725-.588 1.313-1.313 1.313H9.564a1.312 1.312 0 01-1.313-1.313V9.564z"
+																d="M8 14a.75.75 0 0 1-.75-.75V4.56L4.03 7.78a.75.75 0 0 1-1.06-1.06l4.5-4.5a.75.75 0 0 1 1.06 0l4.5 4.5a.75.75 0 0 1-1.06 1.06L8.75 4.56v8.69A.75.75 0 0 1 8 14Z"
 																clip-rule="evenodd"
 															/>
 														</svg>

+ 9 - 1
src/lib/components/chat/MessageInput/Commands/Knowledge.svelte

@@ -154,6 +154,14 @@
 			keys: ['name', 'description']
 		});
 	});
+
+	const decodeString = (str: string) => {
+		try {
+			return decodeURIComponent(str);
+		} catch (e) {
+			return str;
+		}
+	};
 </script>
 
 {#if filteredItems.length > 0 || prompt.split(' ')?.at(0)?.substring(1).startsWith('http')}
@@ -210,7 +218,7 @@
 									{/if}
 
 									<div class="line-clamp-1">
-										{decodeURIComponent(item?.name)}
+										{decodeString(item?.name)}
 									</div>
 								</div>
 

+ 4 - 1
src/lib/components/chat/MessageInput/VoiceRecording.svelte

@@ -159,7 +159,7 @@
 	};
 
 	const startRecording = async () => {
-		startDurationCounter();
+		loading = true;
 
 		stream = await navigator.mediaDevices.getUserMedia({
 			audio: {
@@ -171,6 +171,9 @@
 		mediaRecorder = new MediaRecorder(stream);
 		mediaRecorder.onstart = () => {
 			console.log('Recording started');
+			loading = false;
+			startDurationCounter();
+
 			audioChunks = [];
 			analyseAudio(stream);
 		};

+ 11 - 3
src/lib/components/chat/Messages/Citations.svelte

@@ -87,6 +87,14 @@
 		showRelevance = calculateShowRelevance(citations);
 		showPercentage = shouldShowPercentage(citations);
 	}
+
+	const decodeString = (str: string) => {
+		try {
+			return decodeURIComponent(str);
+		} catch (e) {
+			return str;
+		}
+	};
 </script>
 
 <CitationsModal
@@ -117,7 +125,7 @@
 						<div
 							class="flex-1 mx-1 truncate text-black/60 hover:text-black dark:text-white/60 dark:hover:text-white transition"
 						>
-							{decodeURIComponent(citation.source.name)}
+							{decodeString(citation.source.name)}
 						</div>
 					</button>
 				{/each}
@@ -157,7 +165,7 @@
 											</div>
 										{/if}
 										<div class="flex-1 mx-1 truncate">
-											{decodeURIComponent(citation.source.name)}
+											{decodeString(citation.source.name)}
 										</div>
 									</button>
 								{/each}
@@ -194,7 +202,7 @@
 									</div>
 								{/if}
 								<div class="flex-1 mx-1 truncate">
-									{decodeURIComponent(citation.source.name)}
+									{decodeString(citation.source.name)}
 								</div>
 							</button>
 						{/each}

+ 9 - 1
src/lib/components/chat/Messages/CitationsModal.svelte

@@ -45,6 +45,14 @@
 			);
 		}
 	}
+
+	const decodeString = (str: string) => {
+		try {
+			return decodeURIComponent(str);
+		} catch (e) {
+			return str;
+		}
+	};
 </script>
 
 <Modal size="lg" bind:show>
@@ -99,7 +107,7 @@
 												: `#`}
 										target="_blank"
 									>
-										{decodeURIComponent(document?.metadata?.name ?? document.source.name)}
+										{decodeString(document?.metadata?.name ?? document.source.name)}
 									</a>
 									{#if document?.metadata?.page}
 										<span class="text-xs text-gray-500 dark:text-gray-400">

+ 2 - 1
src/lib/components/chat/Messages/ContentRenderer.svelte

@@ -4,7 +4,7 @@
 	const dispatch = createEventDispatcher();
 
 	import Markdown from './Markdown.svelte';
-	import { chatId, mobile, showArtifacts, showControls, showOverview } from '$lib/stores';
+	import { chatId, mobile, settings, showArtifacts, showControls, showOverview } from '$lib/stores';
 	import FloatingButtons from '../ContentRenderer/FloatingButtons.svelte';
 	import { createMessagesList } from '$lib/utils';
 
@@ -161,6 +161,7 @@
 			const { lang, code } = e.detail;
 
 			if (
+				($settings?.detectArtifacts ?? true) &&
 				(['html', 'svg'].includes(lang) || (lang === 'xml' && code.includes('svg'))) &&
 				!$mobile &&
 				$chatId

+ 7 - 5
src/lib/components/chat/Messages/Markdown/AlertRenderer.svelte

@@ -99,10 +99,12 @@ Renders the following Markdown as alerts:
 > Example warning
 
 -->
-<div class={`border-l-2 pl-2 ${alertStyles[alert.type].border}`}>
-	<p class={alertStyles[alert.type].text}>
+<div class={`border-l-4 pl-2.5 ${alertStyles[alert.type].border} my-0.5`}>
+	<div class="{alertStyles[alert.type].text} items-center flex gap-1 py-1.5">
 		<svelte:component this={alertStyles[alert.type].icon} className="inline-block size-4" />
-		<b>{alert.type}</b>
-	</p>
-	<MarkdownTokens id={`${id}-${tokenIdx}`} tokens={alert.tokens} {onTaskClick} {onSourceClick} />
+		<span class=" font-medium">{alert.type}</span>
+	</div>
+	<div class="pb-2">
+		<MarkdownTokens id={`${id}-${tokenIdx}`} tokens={alert.tokens} {onTaskClick} {onSourceClick} />
+	</div>
 </div>

+ 4 - 1
src/lib/components/chat/Messages/ResponseMessage.svelte

@@ -24,7 +24,9 @@
 		getMessageContentParts,
 		sanitizeResponseContent,
 		createMessagesList,
-		formatDate
+		formatDate,
+		removeDetails,
+		removeAllDetails
 	} from '$lib/utils';
 	import { WEBUI_BASE_URL } from '$lib/constants';
 
@@ -152,6 +154,7 @@
 	let showRateComment = false;
 
 	const copyToClipboard = async (text) => {
+		text = removeAllDetails(text);
 		const res = await _copyToClipboard(text);
 		if (res) {
 			toast.success($i18n.t('Copying to clipboard was successful!'));

+ 1 - 1
src/lib/components/chat/Messages/UserMessage.svelte

@@ -192,7 +192,7 @@
 							<div>
 								<button
 									id="save-edit-message-button"
-									class=" px-4 py-2 bg-gray-50 hover:bg-gray-100 dark:bg-gray-800 dark:hover:bg-gray-700 border dark:border-gray-700 text-gray-700 dark:text-gray-200 transition rounded-3xl"
+									class=" px-4 py-2 bg-gray-50 hover:bg-gray-100 dark:bg-gray-800 dark:hover:bg-gray-700 border border-gray-100 dark:border-gray-700 text-gray-700 dark:text-gray-200 transition rounded-3xl"
 									on:click={() => {
 										editMessageConfirmHandler(false);
 									}}

+ 52 - 50
src/lib/components/chat/ModelSelector.svelte

@@ -54,58 +54,60 @@
 				</div>
 			</div>
 
-			{#if selectedModelIdx === 0}
-				<div
-					class="  self-center mx-1 disabled:text-gray-600 disabled:hover:text-gray-600 -translate-y-[0.5px]"
-				>
-					<Tooltip content={$i18n.t('Add Model')}>
-						<button
-							class=" "
-							{disabled}
-							on:click={() => {
-								selectedModels = [...selectedModels, ''];
-							}}
-							aria-label="Add Model"
-						>
-							<svg
-								xmlns="http://www.w3.org/2000/svg"
-								fill="none"
-								viewBox="0 0 24 24"
-								stroke-width="2"
-								stroke="currentColor"
-								class="size-3.5"
+			{#if $user?.role === 'admin' || ($user?.permissions?.chat?.multiple_models ?? true)}
+				{#if selectedModelIdx === 0}
+					<div
+						class="  self-center mx-1 disabled:text-gray-600 disabled:hover:text-gray-600 -translate-y-[0.5px]"
+					>
+						<Tooltip content={$i18n.t('Add Model')}>
+							<button
+								class=" "
+								{disabled}
+								on:click={() => {
+									selectedModels = [...selectedModels, ''];
+								}}
+								aria-label="Add Model"
 							>
-								<path stroke-linecap="round" stroke-linejoin="round" d="M12 6v12m6-6H6" />
-							</svg>
-						</button>
-					</Tooltip>
-				</div>
-			{:else}
-				<div
-					class="  self-center mx-1 disabled:text-gray-600 disabled:hover:text-gray-600 -translate-y-[0.5px]"
-				>
-					<Tooltip content={$i18n.t('Remove Model')}>
-						<button
-							{disabled}
-							on:click={() => {
-								selectedModels.splice(selectedModelIdx, 1);
-								selectedModels = selectedModels;
-							}}
-							aria-label="Remove Model"
-						>
-							<svg
-								xmlns="http://www.w3.org/2000/svg"
-								fill="none"
-								viewBox="0 0 24 24"
-								stroke-width="2"
-								stroke="currentColor"
-								class="size-3"
+								<svg
+									xmlns="http://www.w3.org/2000/svg"
+									fill="none"
+									viewBox="0 0 24 24"
+									stroke-width="2"
+									stroke="currentColor"
+									class="size-3.5"
+								>
+									<path stroke-linecap="round" stroke-linejoin="round" d="M12 6v12m6-6H6" />
+								</svg>
+							</button>
+						</Tooltip>
+					</div>
+				{:else}
+					<div
+						class="  self-center mx-1 disabled:text-gray-600 disabled:hover:text-gray-600 -translate-y-[0.5px]"
+					>
+						<Tooltip content={$i18n.t('Remove Model')}>
+							<button
+								{disabled}
+								on:click={() => {
+									selectedModels.splice(selectedModelIdx, 1);
+									selectedModels = selectedModels;
+								}}
+								aria-label="Remove Model"
 							>
-								<path stroke-linecap="round" stroke-linejoin="round" d="M19.5 12h-15" />
-							</svg>
-						</button>
-					</Tooltip>
-				</div>
+								<svg
+									xmlns="http://www.w3.org/2000/svg"
+									fill="none"
+									viewBox="0 0 24 24"
+									stroke-width="2"
+									stroke="currentColor"
+									class="size-3"
+								>
+									<path stroke-linecap="round" stroke-linejoin="round" d="M19.5 12h-15" />
+								</svg>
+							</button>
+						</Tooltip>
+					</div>
+				{/if}
 			{/if}
 		</div>
 	{/each}

+ 64 - 59
src/lib/components/chat/ModelSelector/Selector.svelte

@@ -84,47 +84,49 @@
 		}
 	);
 
-	$: filteredItems = searchValue
-		? fuse
-				.search(searchValue)
-				.map((e) => {
-					return e.item;
-				})
-				.filter((item) => {
-					if (selectedTag === '') {
-						return true;
-					}
-					return (item.model?.tags ?? []).map((tag) => tag.name).includes(selectedTag);
-				})
-				.filter((item) => {
-					if (selectedConnectionType === '') {
-						return true;
-					} else if (selectedConnectionType === 'ollama') {
-						return item.model?.owned_by === 'ollama';
-					} else if (selectedConnectionType === 'openai') {
-						return item.model?.owned_by === 'openai';
-					} else if (selectedConnectionType === 'direct') {
-						return item.model?.direct;
-					}
-				})
-		: items
-				.filter((item) => {
-					if (selectedTag === '') {
-						return true;
-					}
-					return (item.model?.tags ?? []).map((tag) => tag.name).includes(selectedTag);
-				})
-				.filter((item) => {
-					if (selectedConnectionType === '') {
-						return true;
-					} else if (selectedConnectionType === 'ollama') {
-						return item.model?.owned_by === 'ollama';
-					} else if (selectedConnectionType === 'openai') {
-						return item.model?.owned_by === 'openai';
-					} else if (selectedConnectionType === 'direct') {
-						return item.model?.direct;
-					}
-				});
+	$: filteredItems = (
+		searchValue
+			? fuse
+					.search(searchValue)
+					.map((e) => {
+						return e.item;
+					})
+					.filter((item) => {
+						if (selectedTag === '') {
+							return true;
+						}
+						return (item.model?.tags ?? []).map((tag) => tag.name).includes(selectedTag);
+					})
+					.filter((item) => {
+						if (selectedConnectionType === '') {
+							return true;
+						} else if (selectedConnectionType === 'ollama') {
+							return item.model?.owned_by === 'ollama';
+						} else if (selectedConnectionType === 'openai') {
+							return item.model?.owned_by === 'openai';
+						} else if (selectedConnectionType === 'direct') {
+							return item.model?.direct;
+						}
+					})
+			: items
+					.filter((item) => {
+						if (selectedTag === '') {
+							return true;
+						}
+						return (item.model?.tags ?? []).map((tag) => tag.name).includes(selectedTag);
+					})
+					.filter((item) => {
+						if (selectedConnectionType === '') {
+							return true;
+						} else if (selectedConnectionType === 'ollama') {
+							return item.model?.owned_by === 'ollama';
+						} else if (selectedConnectionType === 'openai') {
+							return item.model?.owned_by === 'openai';
+						} else if (selectedConnectionType === 'direct') {
+							return item.model?.direct;
+						}
+					})
+	).filter((item) => !(item.model?.info?.meta?.hidden ?? false));
 
 	$: if (selectedTag || selectedConnectionType) {
 		resetView();
@@ -282,7 +284,10 @@
 		ollamaVersion = await getOllamaVersion(localStorage.token).catch((error) => false);
 
 		if (items) {
-			tags = items.flatMap((item) => item.model?.tags ?? []).map((tag) => tag.name);
+			tags = items
+				.filter((item) => !(item.model?.info?.meta?.hidden ?? false))
+				.flatMap((item) => item.model?.tags ?? [])
+				.map((tag) => tag.name);
 
 			// Remove duplicates and sort
 			tags = Array.from(new Set(tags)).sort((a, b) => a.localeCompare(b));
@@ -373,7 +378,7 @@
 				</div>
 			{/if}
 
-			<div class="px-3 mb-2 max-h-64 overflow-y-auto scrollbar-hidden group relative">
+			<div class="px-3 max-h-64 overflow-y-auto scrollbar-hidden group relative">
 				{#if tags && items.filter((item) => !(item.model?.info?.meta?.hidden ?? false)).length > 0}
 					<div
 						class=" flex w-full sticky top-0 z-10 bg-white dark:bg-gray-850 overflow-x-auto scrollbar-none"
@@ -388,18 +393,20 @@
 							class="flex gap-1 w-fit text-center text-sm font-medium rounded-full bg-transparent px-1.5 pb-0.5"
 							bind:this={tagsContainerElement}
 						>
-							<button
-								class="min-w-fit outline-none p-1.5 {selectedTag === '' &&
-								selectedConnectionType === ''
-									? ''
-									: 'text-gray-300 dark:text-gray-600 hover:text-gray-700 dark:hover:text-white'} transition capitalize"
-								on:click={() => {
-									selectedConnectionType = '';
-									selectedTag = '';
-								}}
-							>
-								{$i18n.t('All')}
-							</button>
+							{#if (items.find((item) => item.model?.owned_by === 'ollama') && items.find((item) => item.model?.owned_by === 'openai')) || items.find((item) => item.model?.direct) || tags.length > 0}
+								<button
+									class="min-w-fit outline-none p-1.5 {selectedTag === '' &&
+									selectedConnectionType === ''
+										? ''
+										: 'text-gray-300 dark:text-gray-600 hover:text-gray-700 dark:hover:text-white'} transition capitalize"
+									on:click={() => {
+										selectedConnectionType = '';
+										selectedTag = '';
+									}}
+								>
+									{$i18n.t('All')}
+								</button>
+							{/if}
 
 							{#if items.find((item) => item.model?.owned_by === 'ollama') && items.find((item) => item.model?.owned_by === 'openai')}
 								<button
@@ -457,7 +464,7 @@
 					</div>
 				{/if}
 
-				{#each filteredItems.filter((item) => !(item.model?.info?.meta?.hidden ?? false)) as item, index}
+				{#each filteredItems as item, index}
 					<button
 						aria-label="model-item"
 						class="flex w-full text-left font-medium line-clamp-1 select-none items-center rounded-button py-2 pl-3 pr-1.5 text-sm text-gray-700 dark:text-gray-100 outline-hidden transition-all duration-75 hover:bg-gray-100 dark:hover:bg-gray-800 rounded-lg cursor-pointer data-highlighted:bg-muted {index ===
@@ -739,9 +746,7 @@
 			</div>
 
 			{#if showTemporaryChatControl}
-				<hr class="border-gray-100 dark:border-gray-800" />
-
-				<div class="flex items-center mx-2 my-2">
+				<div class="flex items-center mx-2 mb-2">
 					<button
 						class="flex justify-between w-full font-medium line-clamp-1 select-none items-center rounded-button py-2 px-3 text-sm text-gray-700 dark:text-gray-100 outline-hidden transition-all duration-75 hover:bg-gray-100 dark:hover:bg-gray-800 rounded-lg cursor-pointer data-highlighted:bg-muted"
 						on:click={async () => {

+ 170 - 115
src/lib/components/chat/Navbar.svelte

@@ -4,7 +4,9 @@
 
 	import {
 		WEBUI_NAME,
+		banners,
 		chatId,
+		config,
 		mobile,
 		settings,
 		showArchivedChats,
@@ -26,6 +28,7 @@
 	import AdjustmentsHorizontal from '../icons/AdjustmentsHorizontal.svelte';
 
 	import PencilSquare from '../icons/PencilSquare.svelte';
+	import Banner from '../common/Banner.svelte';
 
 	const i18n = getContext('i18n');
 
@@ -34,6 +37,7 @@
 	export let shareEnabled: boolean = false;
 
 	export let chat;
+	export let history;
 	export let selectedModels;
 	export let showModelSelector = true;
 
@@ -43,136 +47,187 @@
 
 <ShareChatModal bind:show={showShareChatModal} chatId={$chatId} />
 
-<nav class="sticky top-0 z-30 w-full px-1.5 py-1.5 -mb-8 flex items-center drag-region">
-	<div
-		class=" bg-linear-to-b via-50% from-white via-white to-transparent dark:from-gray-900 dark:via-gray-900 dark:to-transparent pointer-events-none absolute inset-0 -bottom-7 z-[-1]"
-	></div>
-
-	<div class=" flex max-w-full w-full mx-auto px-1 pt-0.5 bg-transparent">
-		<div class="flex items-center w-full max-w-full">
-			<div
-				class="{$showSidebar
-					? 'md:hidden'
-					: ''} mr-1 self-start flex flex-none items-center text-gray-600 dark:text-gray-400"
-			>
-				<button
-					id="sidebar-toggle-button"
-					class="cursor-pointer px-2 py-2 flex rounded-xl hover:bg-gray-50 dark:hover:bg-gray-850 transition"
-					on:click={() => {
-						showSidebar.set(!$showSidebar);
-					}}
-					aria-label="Toggle Sidebar"
+<nav class="sticky top-0 z-30 w-full py-1.5 -mb-8 flex flex-col items-center drag-region">
+	<div class="flex items-center w-full px-1.5">
+		<div
+			class=" bg-linear-to-b via-50% from-white via-white to-transparent dark:from-gray-900 dark:via-gray-900 dark:to-transparent pointer-events-none absolute inset-0 -bottom-7 z-[-1]"
+		></div>
+
+		<div class=" flex max-w-full w-full mx-auto px-1 pt-0.5 bg-transparent">
+			<div class="flex items-center w-full max-w-full">
+				<div
+					class="{$showSidebar
+						? 'md:hidden'
+						: ''} mr-1 self-start flex flex-none items-center text-gray-600 dark:text-gray-400"
 				>
-					<div class=" m-auto self-center">
-						<MenuLines />
-					</div>
-				</button>
-			</div>
+					<button
+						id="sidebar-toggle-button"
+						class="cursor-pointer px-2 py-2 flex rounded-xl hover:bg-gray-50 dark:hover:bg-gray-850 transition"
+						on:click={() => {
+							showSidebar.set(!$showSidebar);
+						}}
+						aria-label="Toggle Sidebar"
+					>
+						<div class=" m-auto self-center">
+							<MenuLines />
+						</div>
+					</button>
+				</div>
 
-			<div
-				class="flex-1 overflow-hidden max-w-full py-0.5
+				<div
+					class="flex-1 overflow-hidden max-w-full py-0.5
 			{$showSidebar ? 'ml-1' : ''}
 			"
-			>
-				{#if showModelSelector}
-					<ModelSelector bind:selectedModels showSetDefault={!shareEnabled} />
-				{/if}
-			</div>
-
-			<div class="self-start flex flex-none items-center text-gray-600 dark:text-gray-400">
-				<!-- <div class="md:hidden flex self-center w-[1px] h-5 mx-2 bg-gray-300 dark:bg-stone-700" /> -->
-				{#if shareEnabled && chat && (chat.id || $temporaryChatEnabled)}
-					<Menu
-						{chat}
-						{shareEnabled}
-						shareHandler={() => {
-							showShareChatModal = !showShareChatModal;
-						}}
-						downloadHandler={() => {
-							showDownloadChatModal = !showDownloadChatModal;
-						}}
-					>
+				>
+					{#if showModelSelector}
+						<ModelSelector bind:selectedModels showSetDefault={!shareEnabled} />
+					{/if}
+				</div>
+
+				<div class="self-start flex flex-none items-center text-gray-600 dark:text-gray-400">
+					<!-- <div class="md:hidden flex self-center w-[1px] h-5 mx-2 bg-gray-300 dark:bg-stone-700" /> -->
+					{#if shareEnabled && chat && (chat.id || $temporaryChatEnabled)}
+						<Menu
+							{chat}
+							{shareEnabled}
+							shareHandler={() => {
+								showShareChatModal = !showShareChatModal;
+							}}
+							downloadHandler={() => {
+								showDownloadChatModal = !showDownloadChatModal;
+							}}
+						>
+							<button
+								class="flex cursor-pointer px-2 py-2 rounded-xl hover:bg-gray-50 dark:hover:bg-gray-850 transition"
+								id="chat-context-menu-button"
+							>
+								<div class=" m-auto self-center">
+									<svg
+										xmlns="http://www.w3.org/2000/svg"
+										fill="none"
+										viewBox="0 0 24 24"
+										stroke-width="1.5"
+										stroke="currentColor"
+										class="size-5"
+									>
+										<path
+											stroke-linecap="round"
+											stroke-linejoin="round"
+											d="M6.75 12a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0ZM12.75 12a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0ZM18.75 12a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0Z"
+										/>
+									</svg>
+								</div>
+							</button>
+						</Menu>
+					{/if}
+
+					<Tooltip content={$i18n.t('Controls')}>
 						<button
-							class="flex cursor-pointer px-2 py-2 rounded-xl hover:bg-gray-50 dark:hover:bg-gray-850 transition"
-							id="chat-context-menu-button"
+							class=" flex cursor-pointer px-2 py-2 rounded-xl hover:bg-gray-50 dark:hover:bg-gray-850 transition"
+							on:click={async () => {
+								await showControls.set(!$showControls);
+							}}
+							aria-label="Controls"
 						>
 							<div class=" m-auto self-center">
-								<svg
-									xmlns="http://www.w3.org/2000/svg"
-									fill="none"
-									viewBox="0 0 24 24"
-									stroke-width="1.5"
-									stroke="currentColor"
-									class="size-5"
-								>
-									<path
-										stroke-linecap="round"
-										stroke-linejoin="round"
-										d="M6.75 12a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0ZM12.75 12a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0ZM18.75 12a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0Z"
-									/>
-								</svg>
+								<AdjustmentsHorizontal className=" size-5" strokeWidth="0.5" />
 							</div>
 						</button>
-					</Menu>
-				{/if}
-
-				<Tooltip content={$i18n.t('Controls')}>
-					<button
-						class=" flex cursor-pointer px-2 py-2 rounded-xl hover:bg-gray-50 dark:hover:bg-gray-850 transition"
-						on:click={async () => {
-							await showControls.set(!$showControls);
-						}}
-						aria-label="Controls"
-					>
-						<div class=" m-auto self-center">
-							<AdjustmentsHorizontal className=" size-5" strokeWidth="0.5" />
-						</div>
-					</button>
-				</Tooltip>
+					</Tooltip>
 
-				<Tooltip content={$i18n.t('New Chat')}>
-					<button
-						id="new-chat-button"
-						class=" flex {$showSidebar
-							? 'md:hidden'
-							: ''} cursor-pointer px-2 py-2 rounded-xl text-gray-600 dark:text-gray-400 hover:bg-gray-50 dark:hover:bg-gray-850 transition"
-						on:click={() => {
-							initNewChat();
-						}}
-						aria-label="New Chat"
-					>
-						<div class=" m-auto self-center">
-							<PencilSquare className=" size-5" strokeWidth="2" />
-						</div>
-					</button>
-				</Tooltip>
-
-				{#if $user !== undefined && $user !== null}
-					<UserMenu
-						className="max-w-[200px]"
-						role={$user?.role}
-						on:show={(e) => {
-							if (e.detail === 'archived-chat') {
-								showArchivedChats.set(true);
-							}
-						}}
-					>
+					<Tooltip content={$i18n.t('New Chat')}>
 						<button
-							class="select-none flex rounded-xl p-1.5 w-full hover:bg-gray-50 dark:hover:bg-gray-850 transition"
-							aria-label="User Menu"
+							id="new-chat-button"
+							class=" flex {$showSidebar
+								? 'md:hidden'
+								: ''} cursor-pointer px-2 py-2 rounded-xl text-gray-600 dark:text-gray-400 hover:bg-gray-50 dark:hover:bg-gray-850 transition"
+							on:click={() => {
+								initNewChat();
+							}}
+							aria-label="New Chat"
 						>
-							<div class=" self-center">
-								<img
-									src={$user?.profile_image_url}
-									class="size-6 object-cover rounded-full"
-									alt="User profile"
-									draggable="false"
-								/>
+							<div class=" m-auto self-center">
+								<PencilSquare className=" size-5" strokeWidth="2" />
 							</div>
 						</button>
-					</UserMenu>
-				{/if}
+					</Tooltip>
+
+					{#if $user !== undefined && $user !== null}
+						<UserMenu
+							className="max-w-[200px]"
+							role={$user?.role}
+							on:show={(e) => {
+								if (e.detail === 'archived-chat') {
+									showArchivedChats.set(true);
+								}
+							}}
+						>
+							<button
+								class="select-none flex rounded-xl p-1.5 w-full hover:bg-gray-50 dark:hover:bg-gray-850 transition"
+								aria-label="User Menu"
+							>
+								<div class=" self-center">
+									<img
+										src={$user?.profile_image_url}
+										class="size-6 object-cover rounded-full"
+										alt="User profile"
+										draggable="false"
+									/>
+								</div>
+							</button>
+						</UserMenu>
+					{/if}
+				</div>
 			</div>
 		</div>
 	</div>
+
+	{#if !history.currentId && !$chatId && ($banners.length > 0 || ($config?.license_metadata?.type ?? null) === 'trial' || (($config?.license_metadata?.seats ?? null) !== null && $config?.user_count > $config?.license_metadata?.seats))}
+		<div class=" w-full z-30 mt-5">
+			<div class=" flex flex-col gap-1 w-full">
+				{#if ($config?.license_metadata?.type ?? null) === 'trial'}
+					<Banner
+						banner={{
+							type: 'info',
+							title: 'Trial License',
+							content: $i18n.t(
+								'You are currently using a trial license. Please contact support to upgrade your license.'
+							)
+						}}
+					/>
+				{/if}
+
+				{#if ($config?.license_metadata?.seats ?? null) !== null && $config?.user_count > $config?.license_metadata?.seats}
+					<Banner
+						banner={{
+							type: 'error',
+							title: 'License Error',
+							content: $i18n.t(
+								'Exceeded the number of seats in your license. Please contact support to increase the number of seats.'
+							)
+						}}
+					/>
+				{/if}
+
+				{#each $banners.filter( (b) => (b.dismissible ? !JSON.parse(localStorage.getItem('dismissedBannerIds') ?? '[]').includes(b.id) : true) ) as banner}
+					<Banner
+						{banner}
+						on:dismiss={(e) => {
+							const bannerId = e.detail;
+
+							localStorage.setItem(
+								'dismissedBannerIds',
+								JSON.stringify(
+									[
+										bannerId,
+										...JSON.parse(localStorage.getItem('dismissedBannerIds') ?? '[]')
+									].filter((id) => $banners.find((b) => b.id === id))
+								)
+							);
+						}}
+					/>
+				{/each}
+			</div>
+		</div>
+	{/if}
 </nav>

+ 86 - 1
src/lib/components/chat/Settings/Interface.svelte

@@ -32,6 +32,8 @@
 	let showUsername = false;
 	let notificationSound = true;
 
+	let detectArtifacts = true;
+
 	let richTextInput = true;
 	let promptAutocomplete = false;
 
@@ -61,6 +63,9 @@
 
 	let webSearch = null;
 
+	let iframeSandboxAllowSameOrigin = false;
+	let iframeSandboxAllowForms = false;
+
 	const toggleExpandDetails = () => {
 		expandDetails = !expandDetails;
 		saveSettings({ expandDetails });
@@ -176,6 +181,11 @@
 		saveSettings({ autoTags });
 	};
 
+	const toggleDetectArtifacts = async () => {
+		detectArtifacts = !detectArtifacts;
+		saveSettings({ detectArtifacts });
+	};
+
 	const toggleRichTextInput = async () => {
 		richTextInput = !richTextInput;
 		saveSettings({ richTextInput });
@@ -238,10 +248,21 @@
 		saveSettings({ webSearch: webSearch });
 	};
 
+	const toggleIframeSandboxAllowSameOrigin = async () => {
+		iframeSandboxAllowSameOrigin = !iframeSandboxAllowSameOrigin;
+		saveSettings({ iframeSandboxAllowSameOrigin });
+	};
+
+	const toggleIframeSandboxAllowForms = async () => {
+		iframeSandboxAllowForms = !iframeSandboxAllowForms;
+		saveSettings({ iframeSandboxAllowForms });
+	};
+
 	onMount(async () => {
 		titleAutoGenerate = $settings?.title?.auto ?? true;
 		autoTags = $settings.autoTags ?? true;
 
+		detectArtifacts = $settings.detectArtifacts ?? true;
 		responseAutoCopy = $settings.responseAutoCopy ?? false;
 
 		showUsername = $settings.showUsername ?? false;
@@ -537,6 +558,28 @@
 				</div>
 			</div>
 
+			<div>
+				<div class=" py-0.5 flex w-full justify-between">
+					<div class=" self-center text-xs">
+						{$i18n.t('Detect Artifacts Automatically')}
+					</div>
+
+					<button
+						class="p-1 px-3 text-xs flex rounded-sm transition"
+						on:click={() => {
+							toggleDetectArtifacts();
+						}}
+						type="button"
+					>
+						{#if detectArtifacts === true}
+							<span class="ml-2 self-center">{$i18n.t('On')}</span>
+						{:else}
+							<span class="ml-2 self-center">{$i18n.t('Off')}</span>
+						{/if}
+					</button>
+				</div>
+			</div>
+
 			<div>
 				<div class=" py-0.5 flex w-full justify-between">
 					<div class=" self-center text-xs">
@@ -716,7 +759,9 @@
 
 			<div>
 				<div class=" py-0.5 flex w-full justify-between">
-					<div class=" self-center text-xs">{$i18n.t('Haptic Feedback')}</div>
+					<div class=" self-center text-xs">
+						{$i18n.t('Haptic Feedback')} ({$i18n.t('Android')})
+					</div>
 
 					<button
 						class="p-1 px-3 text-xs flex rounded-sm transition"
@@ -820,6 +865,46 @@
 				</div>
 			</div>
 
+			<div>
+				<div class=" py-0.5 flex w-full justify-between">
+					<div class=" self-center text-xs">{$i18n.t('iframe Sandbox Allow Same Origin')}</div>
+
+					<button
+						class="p-1 px-3 text-xs flex rounded-sm transition"
+						on:click={() => {
+							toggleIframeSandboxAllowSameOrigin();
+						}}
+						type="button"
+					>
+						{#if iframeSandboxAllowSameOrigin === true}
+							<span class="ml-2 self-center">{$i18n.t('On')}</span>
+						{:else}
+							<span class="ml-2 self-center">{$i18n.t('Off')}</span>
+						{/if}
+					</button>
+				</div>
+			</div>
+
+			<div>
+				<div class=" py-0.5 flex w-full justify-between">
+					<div class=" self-center text-xs">{$i18n.t('iframe Sandbox Allow Forms')}</div>
+
+					<button
+						class="p-1 px-3 text-xs flex rounded-sm transition"
+						on:click={() => {
+							toggleIframeSandboxAllowForms();
+						}}
+						type="button"
+					>
+						{#if iframeSandboxAllowForms === true}
+							<span class="ml-2 self-center">{$i18n.t('On')}</span>
+						{:else}
+							<span class="ml-2 self-center">{$i18n.t('Off')}</span>
+						{/if}
+					</button>
+				</div>
+			</div>
+
 			<div class=" my-1.5 text-sm font-medium">{$i18n.t('Voice')}</div>
 
 			<div>

+ 1 - 1
src/lib/components/chat/ToolServersModal.svelte

@@ -15,7 +15,7 @@
 
 	let selectedTools = [];
 
-	$: selectedTools = $tools.filter((tool) => selectedToolIds.includes(tool.id));
+	$: selectedTools = ($tools ?? []).filter((tool) => selectedToolIds.includes(tool.id));
 
 	const i18n = getContext('i18n');
 </script>

+ 11 - 1
src/lib/components/common/CodeEditor.svelte

@@ -98,6 +98,16 @@
 			}
 		})
 	);
+	languages.push(
+		LanguageDescription.of({
+			name: 'Elixir',
+			extensions: ['ex', 'exs'],
+			load() {
+				return import('codemirror-lang-elixir').then((m) => m.elixir());
+			}
+		})
+	);
+
 	const getLang = async () => {
 		const language = languages.find((l) => l.alias.includes(lang));
 		return await language?.load();
@@ -232,4 +242,4 @@
 	});
 </script>
 
-<div id="code-textarea-{id}" class="h-full w-full" />
+<div id="code-textarea-{id}" class="h-full w-full text-sm" />

+ 1 - 1
src/lib/components/common/Collapsible.svelte

@@ -73,7 +73,7 @@
 				return JSON.stringify(parsed, null, 2);
 			} else {
 				// It's a primitive value like a number, boolean, etc.
-				return String(parsed);
+				return `${JSON.stringify(String(parsed))}`;
 			}
 		} catch (e) {
 			// Not valid JSON, return as-is

+ 12 - 8
src/lib/components/common/FileItem.svelte

@@ -28,6 +28,14 @@
 	import { deleteFileById } from '$lib/apis/files';
 
 	let showModal = false;
+
+	const decodeString = (str: string) => {
+		try {
+			return decodeURIComponent(str);
+		} catch (e) {
+			return str;
+		}
+	};
 </script>
 
 {#if item}
@@ -82,7 +90,7 @@
 	{#if !small}
 		<div class="flex flex-col justify-center -space-y-0.5 px-2.5 w-full">
 			<div class=" dark:text-gray-100 text-sm font-medium line-clamp-1 mb-1">
-				{decodeURIComponent(name)}
+				{decodeString(name)}
 			</div>
 
 			<div class=" flex justify-between text-gray-500 text-xs line-clamp-1">
@@ -101,11 +109,7 @@
 			</div>
 		</div>
 	{:else}
-		<Tooltip
-			content={decodeURIComponent(name)}
-			className="flex flex-col w-full"
-			placement="top-start"
-		>
+		<Tooltip content={decodeString(name)} className="flex flex-col w-full" placement="top-start">
 			<div class="flex flex-col justify-center -space-y-0.5 px-2.5 w-full">
 				<div class=" dark:text-gray-100 text-sm flex justify-between items-center">
 					{#if loading}
@@ -113,7 +117,7 @@
 							<Spinner className="size-4" />
 						</div>
 					{/if}
-					<div class="font-medium line-clamp-1 flex-1">{decodeURIComponent(name)}</div>
+					<div class="font-medium line-clamp-1 flex-1">{decodeString(name)}</div>
 					<div class="text-gray-500 text-xs capitalize shrink-0">{formatFileSize(size)}</div>
 				</div>
 			</div>
@@ -123,7 +127,7 @@
 	{#if dismissible}
 		<div class=" absolute -top-1 -right-1">
 			<button
-				class=" bg-white text-black border border-white rounded-full group-hover:visible invisible transition"
+				class=" bg-white text-black border border-gray-50 rounded-full group-hover:visible invisible transition"
 				type="button"
 				on:click|stopPropagation={() => {
 					dispatch('dismiss');

+ 1 - 1
src/lib/components/layout/Sidebar/ChannelModal.svelte

@@ -19,7 +19,7 @@
 	export let edit = false;
 
 	let name = '';
-	let accessControl = null;
+	let accessControl = {};
 
 	let loading = false;
 

+ 2 - 0
src/lib/components/layout/Sidebar/ChatItem.svelte

@@ -83,6 +83,8 @@
 			currentChatPage.set(1);
 			await chats.set(await getChatList(localStorage.token, $currentChatPage));
 			await pinnedChats.set(await getPinnedChatList(localStorage.token));
+
+			dispatch('change');
 		}
 	};
 

+ 1 - 1
src/lib/components/workspace/Knowledge/CreateKnowledgeBase.svelte

@@ -12,7 +12,7 @@
 
 	let name = '';
 	let description = '';
-	let accessControl = null;
+	let accessControl = {};
 
 	const submitHandler = async () => {
 		loading = true;

+ 9 - 1
src/lib/components/workspace/Knowledge/KnowledgeBase.svelte

@@ -547,6 +547,14 @@
 		dropZone?.removeEventListener('drop', onDrop);
 		dropZone?.removeEventListener('dragleave', onDragLeave);
 	});
+
+	const decodeString = (str: string) => {
+		try {
+			return decodeURIComponent(str);
+		} catch (e) {
+			return str;
+		}
+	};
 </script>
 
 {#if dragged}
@@ -698,7 +706,7 @@
 										href={selectedFile.id ? `/api/v1/files/${selectedFile.id}/content` : '#'}
 										target="_blank"
 									>
-										{decodeURIComponent(selectedFile?.meta?.name)}
+										{decodeString(selectedFile?.meta?.name)}
 									</a>
 								</div>
 

+ 1 - 1
src/lib/components/workspace/Prompts/PromptEditor.svelte

@@ -21,7 +21,7 @@
 	let command = '';
 	let content = '';
 
-	let accessControl = null;
+	let accessControl = {};
 
 	let showAccessControlModal = false;
 

+ 18 - 13
src/lib/components/workspace/Tools/ToolkitEditor.svelte

@@ -30,7 +30,7 @@
 		description: ''
 	};
 	export let content = '';
-	export let accessControl = null;
+	export let accessControl = {};
 
 	let _content = '';
 
@@ -50,22 +50,20 @@
 	let boilerplate = `import os
 import requests
 from datetime import datetime
-
+from pydantic import BaseModel, Field
 
 class Tools:
     def __init__(self):
         pass
 
-    # Add your custom tools using pure Python code here, make sure to add type hints
-    # Use Sphinx-style docstrings to document your tools, they will be used for generating tools specifications
-    # Please refer to function_calling_filter_pipeline.py file from pipelines project for an example
-
+    # Add your custom tools using pure Python code here, make sure to add type hints and descriptions
+	
     def get_user_name_and_email_and_id(self, __user__: dict = {}) -> str:
         """
         Get the user name, Email and ID from the user object.
         """
 
-        # Do not include :param for __user__ in the docstring as it should not be shown in the tool's specification
+        # Do not include a descrption for __user__ as it should not be shown in the tool's specification
         # The session user object will be passed as a parameter when the function is called
 
         print(__user__)
@@ -86,7 +84,6 @@ class Tools:
     def get_current_time(self) -> str:
         """
         Get the current time in a more human-readable format.
-        :return: The current time.
         """
 
         now = datetime.now()
@@ -97,10 +94,14 @@ class Tools:
 
         return f"Current Date and Time = {current_date}, {current_time}"
 
-    def calculator(self, equation: str) -> str:
+    def calculator(
+        self,
+        equation: str = Field(
+            ..., description="The mathematical equation to calculate."
+        ),
+    ) -> str:
         """
         Calculate the result of an equation.
-        :param equation: The equation to calculate.
         """
 
         # Avoid using eval in production code
@@ -112,12 +113,16 @@ class Tools:
             print(e)
             return "Invalid equation"
 
-    def get_current_weather(self, city: str) -> str:
+    def get_current_weather(
+        self,
+        city: str = Field(
+            "New York, NY", description="Get the current weather for a given city."
+        ),
+    ) -> str:
         """
         Get the current weather for a given city.
-        :param city: The name of the city to get the weather for.
-        :return: The current weather information or an error message.
         """
+
         api_key = os.getenv("OPENWEATHER_API_KEY")
         if not api_key:
             return (

+ 1 - 1
src/lib/components/workspace/common/AccessControl.svelte

@@ -13,7 +13,7 @@
 	export let onChange: Function = () => {};
 
 	export let accessRoles = ['read'];
-	export let accessControl = null;
+	export let accessControl = {};
 
 	export let allowPublic = true;
 

+ 1 - 1
src/lib/components/workspace/common/AccessControlModal.svelte

@@ -6,7 +6,7 @@
 	import AccessControl from './AccessControl.svelte';
 
 	export let show = false;
-	export let accessControl = null;
+	export let accessControl = {};
 	export let accessRoles = ['read'];
 	export let allowPublic = true;
 

+ 28 - 2
src/lib/i18n/locales/ar-BH/translation.json

@@ -62,6 +62,7 @@
 	"Allow Chat Deletion": "يستطيع حذف المحادثات",
 	"Allow Chat Edit": "",
 	"Allow File Upload": "",
+	"Allow Multiple Models in Chat": "",
 	"Allow non-local voices": "",
 	"Allow Temporary Chat": "",
 	"Allow User Location": "",
@@ -79,6 +80,7 @@
 	"and": "و",
 	"and {{COUNT}} more": "",
 	"and create a new shared link.": "و أنشئ رابط مشترك جديد.",
+	"Android": "",
 	"API Base URL": "API الرابط الرئيسي",
 	"API Key": "API مفتاح",
 	"API Key created.": "API تم أنشاء المفتاح",
@@ -141,7 +143,6 @@
 	"Brave Search API Key": "مفتاح واجهة برمجة تطبيقات البحث الشجاع",
 	"By {{name}}": "",
 	"Bypass Embedding and Retrieval": "",
-	"Bypass SSL verification for Websites": "تجاوز التحقق من SSL للموقع",
 	"Calendar": "",
 	"Call": "",
 	"Call feature is not supported when using Web STT engine": "",
@@ -303,6 +304,7 @@
 	"Deleted User": "",
 	"Describe your knowledge base and objectives": "",
 	"Description": "وصف",
+	"Detect Artifacts Automatically": "",
 	"Didn't fully follow instructions": "لم أتبع التعليمات بشكل كامل",
 	"Direct": "",
 	"Direct Connections": "",
@@ -358,6 +360,7 @@
 	"e.g. my_filter": "",
 	"e.g. my_tools": "",
 	"e.g. Tools for performing various operations": "",
+	"e.g., en-US,ja-JP (leave blank for auto-detect)": "",
 	"Edit": "تعديل",
 	"Edit Arena Model": "",
 	"Edit Channel": "",
@@ -407,6 +410,8 @@
 	"Enter Document Intelligence Key": "",
 	"Enter domains separated by commas (e.g., example.com,site.org)": "",
 	"Enter Exa API Key": "",
+	"Enter Firecrawl API Base URL": "",
+	"Enter Firecrawl API Key": "",
 	"Enter Github Raw URL": "أدخل عنوان URL ل Github Raw",
 	"Enter Google PSE API Key": "أدخل مفتاح واجهة برمجة تطبيقات PSE من Google",
 	"Enter Google PSE Engine Id": "أدخل معرف محرك PSE من Google",
@@ -424,6 +429,8 @@
 	"Enter Mojeek Search API Key": "",
 	"Enter Number of Steps (e.g. 50)": "(e.g. 50) أدخل عدد الخطوات",
 	"Enter Perplexity API Key": "",
+	"Enter Playwright Timeout": "",
+	"Enter Playwright WebSocket URL": "",
 	"Enter proxy URL (e.g. https://user:password@host:port)": "",
 	"Enter reasoning effort": "",
 	"Enter Sampler (e.g. Euler a)": "",
@@ -441,10 +448,13 @@
 	"Enter server host": "",
 	"Enter server label": "",
 	"Enter server port": "",
+	"Enter Sougou Search API sID": "",
+	"Enter Sougou Search API SK": "",
 	"Enter stop sequence": "أدخل تسلسل التوقف",
 	"Enter system prompt": "",
 	"Enter system prompt here": "",
 	"Enter Tavily API Key": "",
+	"Enter Tavily Extract Depth": "",
 	"Enter the public URL of your WebUI. This URL will be used to generate links in the notifications.": "",
 	"Enter Tika Server URL": "",
 	"Enter timeout in seconds": "",
@@ -525,6 +535,8 @@
 	"Filter is now globally enabled": "",
 	"Filters": "",
 	"Fingerprint spoofing detected: Unable to use initials as avatar. Defaulting to default profile image.": "تم اكتشاف انتحال بصمة الإصبع: غير قادر على استخدام الأحرف الأولى كصورة رمزية. الافتراضي لصورة الملف الشخصي الافتراضية.",
+	"Firecrawl API Base URL": "",
+	"Firecrawl API Key": "",
 	"Fluidly stream large external response chunks": "دفق قطع الاستجابة الخارجية الكبيرة بسلاسة",
 	"Focus chat input": "التركيز على إدخال الدردشة",
 	"Folder deleted successfully": "",
@@ -589,6 +601,8 @@
 	"Hybrid Search": "البحث الهجين",
 	"I acknowledge that I have read and I understand the implications of my action. I am aware of the risks associated with executing arbitrary code and I have verified the trustworthiness of the source.": "",
 	"ID": "",
+	"iframe Sandbox Allow Forms": "",
+	"iframe Sandbox Allow Same Origin": "",
 	"Ignite curiosity": "",
 	"Image": "",
 	"Image Compression": "",
@@ -649,6 +663,7 @@
 	"Label": "",
 	"Landing Page Mode": "",
 	"Language": "اللغة",
+	"Language Locales": "",
 	"Last Active": "آخر نشاط",
 	"Last Modified": "",
 	"Last reply": "",
@@ -702,7 +717,6 @@
 	"Message rating should be enabled to use this feature": "",
 	"Messages you send after creating your link won't be shared. Users with the URL will be able to view the shared chat.": "لن تتم مشاركة الرسائل التي ترسلها بعد إنشاء الرابط الخاص بك. سيتمكن المستخدمون الذين لديهم عنوان URL من عرض الدردشة المشتركة",
 	"Min P": "",
-	"Minimum Score": "الحد الأدنى من النقاط",
 	"Mirostat": "Mirostat",
 	"Mirostat Eta": "Mirostat Eta",
 	"Mirostat Tau": "Mirostat Tau",
@@ -833,6 +847,8 @@
 	"Pipelines Valves": "صمامات خطوط الأنابيب",
 	"Plain text (.txt)": "نص عادي (.txt)",
 	"Playground": "مكان التجربة",
+	"Playwright Timeout (ms)": "",
+	"Playwright WebSocket URL": "",
 	"Please carefully review the following warnings:": "",
 	"Please do not close the settings page while loading the model.": "",
 	"Please enter a prompt": "",
@@ -878,8 +894,11 @@
 	"References from": "",
 	"Refused when it shouldn't have": "رفض عندما لا ينبغي أن يكون",
 	"Regenerate": "تجديد",
+	"Reindex": "",
+	"Reindex Knowledge Base Vectors": "",
 	"Release Notes": "ملاحظات الإصدار",
 	"Relevance": "",
+	"Relevance Threshold": "",
 	"Remove": "إزالة",
 	"Remove Model": "حذف الموديل",
 	"Rename": "إعادة تسمية",
@@ -1009,6 +1028,8 @@
 	"Sign up to {{WEBUI_NAME}}": "",
 	"Signing in to {{WEBUI_NAME}}": "",
 	"sk-1234": "",
+	"Sougou Search API sID": "",
+	"Sougou Search API SK": "",
 	"Source": "المصدر",
 	"Speech Playback Speed": "",
 	"Speech recognition error: {{error}}": "{{error}} خطأ في التعرف على الكلام",
@@ -1036,6 +1057,7 @@
 	"Tap to interrupt": "",
 	"Tasks": "",
 	"Tavily API Key": "",
+	"Tavily Extract Depth": "",
 	"Tell us more:": "أخبرنا المزيد:",
 	"Temperature": "درجة حرارة",
 	"Template": "نموذج",
@@ -1158,6 +1180,8 @@
 	"Use Gravatar": "Gravatar أستخدم",
 	"Use groups to group your users and assign permissions.": "",
 	"Use Initials": "Initials أستخدم",
+	"Use no proxy to fetch page contents.": "",
+	"Use proxy designated by http_proxy and https_proxy environment variables to fetch page contents.": "",
 	"use_mlock (Ollama)": "use_mlock (أولاما)",
 	"use_mmap (Ollama)": "use_mmap (أولاما)",
 	"user": "مستخدم",
@@ -1175,6 +1199,7 @@
 	"variable": "المتغير",
 	"variable to have them replaced with clipboard content.": "متغير لاستبدالها بمحتوى الحافظة.",
 	"Verify Connection": "",
+	"Verify SSL Certificate": "",
 	"Version": "إصدار",
 	"Version {{selectedVersion}} of {{totalVersions}}": "",
 	"View Replies": "",
@@ -1189,6 +1214,7 @@
 	"Warning: Jupyter execution enables arbitrary code execution, posing severe security risks—proceed with extreme caution.": "",
 	"Web": "Web",
 	"Web API": "",
+	"Web Loader Engine": "",
 	"Web Search": "بحث الويب",
 	"Web Search Engine": "محرك بحث الويب",
 	"Web Search in Chat": "",

+ 28 - 2
src/lib/i18n/locales/ar/translation.json

@@ -62,6 +62,7 @@
 	"Allow Chat Deletion": "السماح بحذف المحادثة",
 	"Allow Chat Edit": "السماح بتعديل المحادثة",
 	"Allow File Upload": "السماح بتحميل الملفات",
+	"Allow Multiple Models in Chat": "",
 	"Allow non-local voices": "السماح بالأصوات غير المحلية",
 	"Allow Temporary Chat": "السماح بالمحادثة المؤقتة",
 	"Allow User Location": "السماح بتحديد موقع المستخدم",
@@ -79,6 +80,7 @@
 	"and": "و",
 	"and {{COUNT}} more": "و{{COUNT}} المزيد",
 	"and create a new shared link.": "وإنشاء رابط مشترك جديد.",
+	"Android": "",
 	"API Base URL": "الرابط الأساسي لواجهة API",
 	"API Key": "مفتاح واجهة برمجة التطبيقات (API)",
 	"API Key created.": "تم إنشاء مفتاح واجهة API.",
@@ -141,7 +143,6 @@
 	"Brave Search API Key": "مفتاح API لـ Brave Search",
 	"By {{name}}": "بواسطة {{name}}",
 	"Bypass Embedding and Retrieval": "تجاوز التضمين والاسترجاع",
-	"Bypass SSL verification for Websites": "تجاوز التحقق من SSL للمواقع",
 	"Calendar": "التقويم",
 	"Call": "مكالمة",
 	"Call feature is not supported when using Web STT engine": "ميزة الاتصال غير مدعومة عند استخدام محرك Web STT",
@@ -303,6 +304,7 @@
 	"Deleted User": "مستخدم محذوف",
 	"Describe your knowledge base and objectives": "صف قاعدة معرفتك وأهدافك",
 	"Description": "وصف",
+	"Detect Artifacts Automatically": "",
 	"Didn't fully follow instructions": "لم أتبع التعليمات بشكل كامل",
 	"Direct": "",
 	"Direct Connections": "الاتصالات المباشرة",
@@ -358,6 +360,7 @@
 	"e.g. my_filter": "مثال: my_filter",
 	"e.g. my_tools": "مثال: my_tools",
 	"e.g. Tools for performing various operations": "مثال: أدوات لتنفيذ عمليات متنوعة",
+	"e.g., en-US,ja-JP (leave blank for auto-detect)": "",
 	"Edit": "تعديل",
 	"Edit Arena Model": "تعديل نموذج Arena",
 	"Edit Channel": "تعديل القناة",
@@ -407,6 +410,8 @@
 	"Enter Document Intelligence Key": "أدخل مفتاح تحليل المستندات",
 	"Enter domains separated by commas (e.g., example.com,site.org)": "أدخل النطاقات مفصولة بفواصل (مثال: example.com,site.org)",
 	"Enter Exa API Key": "أدخل مفتاح API لـ Exa",
+	"Enter Firecrawl API Base URL": "",
+	"Enter Firecrawl API Key": "",
 	"Enter Github Raw URL": "أدخل عنوان URL ل Github Raw",
 	"Enter Google PSE API Key": "أدخل مفتاح واجهة برمجة تطبيقات PSE من Google",
 	"Enter Google PSE Engine Id": "أدخل معرف محرك PSE من Google",
@@ -424,6 +429,8 @@
 	"Enter Mojeek Search API Key": "أدخل مفتاح API لـ Mojeek Search",
 	"Enter Number of Steps (e.g. 50)": "(e.g. 50) أدخل عدد الخطوات",
 	"Enter Perplexity API Key": "أدخل مفتاح API لـ Perplexity",
+	"Enter Playwright Timeout": "",
+	"Enter Playwright WebSocket URL": "",
 	"Enter proxy URL (e.g. https://user:password@host:port)": "أدخل عنوان البروكسي (مثال: https://user:password@host:port)",
 	"Enter reasoning effort": "أدخل مستوى الجهد في الاستدلال",
 	"Enter Sampler (e.g. Euler a)": "أدخل العينة (مثال: Euler a)",
@@ -441,10 +448,13 @@
 	"Enter server host": "أدخل مضيف الخادم",
 	"Enter server label": "أدخل تسمية الخادم",
 	"Enter server port": "أدخل منفذ الخادم",
+	"Enter Sougou Search API sID": "",
+	"Enter Sougou Search API SK": "",
 	"Enter stop sequence": "أدخل تسلسل التوقف",
 	"Enter system prompt": "أدخل موجه النظام",
 	"Enter system prompt here": "",
 	"Enter Tavily API Key": "أدخل مفتاح API لـ Tavily",
+	"Enter Tavily Extract Depth": "",
 	"Enter the public URL of your WebUI. This URL will be used to generate links in the notifications.": "أدخل الرابط العلني لـ WebUI الخاص بك. سيتم استخدام هذا الرابط لإنشاء روابط داخل الإشعارات.",
 	"Enter Tika Server URL": "أدخل رابط خادم Tika",
 	"Enter timeout in seconds": "أدخل المهلة بالثواني",
@@ -525,6 +535,8 @@
 	"Filter is now globally enabled": "تم الآن تفعيل الفلتر على مستوى النظام",
 	"Filters": "الفلاتر",
 	"Fingerprint spoofing detected: Unable to use initials as avatar. Defaulting to default profile image.": "تم اكتشاف انتحال بصمة الإصبع: غير قادر على استخدام الأحرف الأولى كصورة رمزية. الافتراضي لصورة الملف الشخصي الافتراضية.",
+	"Firecrawl API Base URL": "",
+	"Firecrawl API Key": "",
 	"Fluidly stream large external response chunks": "دفق قطع الاستجابة الخارجية الكبيرة بسلاسة",
 	"Focus chat input": "التركيز على إدخال الدردشة",
 	"Folder deleted successfully": "تم حذف المجلد بنجاح",
@@ -589,6 +601,8 @@
 	"Hybrid Search": "البحث الهجين",
 	"I acknowledge that I have read and I understand the implications of my action. I am aware of the risks associated with executing arbitrary code and I have verified the trustworthiness of the source.": "أقر بأنني قرأت وفهمت تبعات هذا الإجراء. أنا على دراية بالمخاطر المرتبطة بتنفيذ كود عشوائي وقد تحققت من موثوقية المصدر.",
 	"ID": "المعرّف",
+	"iframe Sandbox Allow Forms": "",
+	"iframe Sandbox Allow Same Origin": "",
 	"Ignite curiosity": "أشعل الفضول",
 	"Image": "صورة",
 	"Image Compression": "ضغط الصور",
@@ -649,6 +663,7 @@
 	"Label": "التسمية",
 	"Landing Page Mode": "وضع الصفحة الرئيسية",
 	"Language": "اللغة",
+	"Language Locales": "",
 	"Last Active": "آخر نشاط",
 	"Last Modified": "آخر تعديل",
 	"Last reply": "آخر رد",
@@ -702,7 +717,6 @@
 	"Message rating should be enabled to use this feature": "يجب تفعيل تقييم الرسائل لاستخدام هذه الميزة",
 	"Messages you send after creating your link won't be shared. Users with the URL will be able to view the shared chat.": "لن تتم مشاركة الرسائل التي ترسلها بعد إنشاء الرابط الخاص بك. سيتمكن المستخدمون الذين لديهم عنوان URL من عرض الدردشة المشتركة",
 	"Min P": "الحد الأدنى P",
-	"Minimum Score": "الحد الأدنى من النقاط",
 	"Mirostat": "Mirostat",
 	"Mirostat Eta": "Mirostat Eta",
 	"Mirostat Tau": "Mirostat Tau",
@@ -833,6 +847,8 @@
 	"Pipelines Valves": "صمامات خطوط الأنابيب",
 	"Plain text (.txt)": "نص عادي (.txt)",
 	"Playground": "مكان التجربة",
+	"Playwright Timeout (ms)": "",
+	"Playwright WebSocket URL": "",
 	"Please carefully review the following warnings:": "يرجى مراجعة التحذيرات التالية بعناية:",
 	"Please do not close the settings page while loading the model.": "الرجاء عدم إغلاق صفحة الإعدادات أثناء تحميل النموذج.",
 	"Please enter a prompt": "الرجاء إدخال توجيه",
@@ -878,8 +894,11 @@
 	"References from": "مراجع من",
 	"Refused when it shouldn't have": "رفض عندما لا ينبغي أن يكون",
 	"Regenerate": "تجديد",
+	"Reindex": "",
+	"Reindex Knowledge Base Vectors": "",
 	"Release Notes": "ملاحظات الإصدار",
 	"Relevance": "الصلة",
+	"Relevance Threshold": "",
 	"Remove": "إزالة",
 	"Remove Model": "حذف الموديل",
 	"Rename": "إعادة تسمية",
@@ -1009,6 +1028,8 @@
 	"Sign up to {{WEBUI_NAME}}": "سجّل في {{WEBUI_NAME}}",
 	"Signing in to {{WEBUI_NAME}}": "جارٍ تسجيل الدخول إلى {{WEBUI_NAME}}",
 	"sk-1234": "sk-1234",
+	"Sougou Search API sID": "",
+	"Sougou Search API SK": "",
 	"Source": "المصدر",
 	"Speech Playback Speed": "سرعة تشغيل الصوت",
 	"Speech recognition error: {{error}}": "{{error}} خطأ في التعرف على الكلام",
@@ -1036,6 +1057,7 @@
 	"Tap to interrupt": "اضغط للمقاطعة",
 	"Tasks": "المهام",
 	"Tavily API Key": "مفتاح API لـ Tavily",
+	"Tavily Extract Depth": "",
 	"Tell us more:": "أخبرنا المزيد:",
 	"Temperature": "درجة حرارة",
 	"Template": "نموذج",
@@ -1158,6 +1180,8 @@
 	"Use Gravatar": "Gravatar أستخدم",
 	"Use groups to group your users and assign permissions.": "استخدم المجموعات لتجميع المستخدمين وتحديد الصلاحيات.",
 	"Use Initials": "Initials أستخدم",
+	"Use no proxy to fetch page contents.": "",
+	"Use proxy designated by http_proxy and https_proxy environment variables to fetch page contents.": "",
 	"use_mlock (Ollama)": "use_mlock (أولاما)",
 	"use_mmap (Ollama)": "use_mmap (أولاما)",
 	"user": "مستخدم",
@@ -1175,6 +1199,7 @@
 	"variable": "المتغير",
 	"variable to have them replaced with clipboard content.": "متغير لاستبدالها بمحتوى الحافظة.",
 	"Verify Connection": "",
+	"Verify SSL Certificate": "",
 	"Version": "إصدار",
 	"Version {{selectedVersion}} of {{totalVersions}}": "الإصدار {{selectedVersion}} من {{totalVersions}}",
 	"View Replies": "عرض الردود",
@@ -1189,6 +1214,7 @@
 	"Warning: Jupyter execution enables arbitrary code execution, posing severe security risks—proceed with extreme caution.": "تحذير: تنفيذ كود Jupyter يتيح تنفيذ كود عشوائي مما يشكل مخاطر أمنية جسيمة—تابع بحذر شديد.",
 	"Web": "Web",
 	"Web API": "واجهة برمجة التطبيقات (API)",
+	"Web Loader Engine": "",
 	"Web Search": "بحث الويب",
 	"Web Search Engine": "محرك بحث الويب",
 	"Web Search in Chat": "بحث ويب داخل المحادثة",

+ 28 - 2
src/lib/i18n/locales/bg-BG/translation.json

@@ -62,6 +62,7 @@
 	"Allow Chat Deletion": "Позволи Изтриване на Чат",
 	"Allow Chat Edit": "Разреши редактиране на чат",
 	"Allow File Upload": "Разреши качване на файлове",
+	"Allow Multiple Models in Chat": "",
 	"Allow non-local voices": "Разреши нелокални гласове",
 	"Allow Temporary Chat": "Разреши временен чат",
 	"Allow User Location": "Разреши местоположение на потребителя",
@@ -79,6 +80,7 @@
 	"and": "и",
 	"and {{COUNT}} more": "и още {{COUNT}}",
 	"and create a new shared link.": "и създай нов общ линк.",
+	"Android": "",
 	"API Base URL": "API Базов URL",
 	"API Key": "API Ключ",
 	"API Key created.": "API Ключ създаден.",
@@ -141,7 +143,6 @@
 	"Brave Search API Key": "API ключ за Brave Search",
 	"By {{name}}": "От {{name}}",
 	"Bypass Embedding and Retrieval": "",
-	"Bypass SSL verification for Websites": "Изключване на SSL проверката за сайтове",
 	"Calendar": "Календар",
 	"Call": "Обаждане",
 	"Call feature is not supported when using Web STT engine": "Функцията за обаждане не се поддържа при използване на Web STT двигател",
@@ -303,6 +304,7 @@
 	"Deleted User": "Изтрит потребител",
 	"Describe your knowledge base and objectives": "Опишете вашата база от знания и цели",
 	"Description": "Описание",
+	"Detect Artifacts Automatically": "",
 	"Didn't fully follow instructions": "Не следва напълно инструкциите",
 	"Direct": "",
 	"Direct Connections": "Директни връзки",
@@ -358,6 +360,7 @@
 	"e.g. my_filter": "напр. моят_филтър",
 	"e.g. my_tools": "напр. моите_инструменти",
 	"e.g. Tools for performing various operations": "напр. Инструменти за извършване на различни операции",
+	"e.g., en-US,ja-JP (leave blank for auto-detect)": "",
 	"Edit": "Редактиране",
 	"Edit Arena Model": "Редактиране на Arena модел",
 	"Edit Channel": "Редактиране на канал",
@@ -407,6 +410,8 @@
 	"Enter Document Intelligence Key": "",
 	"Enter domains separated by commas (e.g., example.com,site.org)": "Въведете домейни, разделени със запетаи (напр. example.com,site.org)",
 	"Enter Exa API Key": "Въведете API ключ за Exa",
+	"Enter Firecrawl API Base URL": "",
+	"Enter Firecrawl API Key": "",
 	"Enter Github Raw URL": "Въведете URL адрес на Github Raw",
 	"Enter Google PSE API Key": "Въведете API ключ за Google PSE",
 	"Enter Google PSE Engine Id": "Въведете идентификатор на двигателя на Google PSE",
@@ -424,6 +429,8 @@
 	"Enter Mojeek Search API Key": "Въведете API ключ за Mojeek Search",
 	"Enter Number of Steps (e.g. 50)": "Въведете брой стъпки (напр. 50)",
 	"Enter Perplexity API Key": "",
+	"Enter Playwright Timeout": "",
+	"Enter Playwright WebSocket URL": "",
 	"Enter proxy URL (e.g. https://user:password@host:port)": "Въведете URL адрес на прокси (напр. https://потребител:парола@хост:порт)",
 	"Enter reasoning effort": "Въведете усилие за разсъждение",
 	"Enter Sampler (e.g. Euler a)": "Въведете семплер (напр. Euler a)",
@@ -441,10 +448,13 @@
 	"Enter server host": "Въведете хост на сървъра",
 	"Enter server label": "Въведете етикет на сървъра",
 	"Enter server port": "Въведете порт на сървъра",
+	"Enter Sougou Search API sID": "",
+	"Enter Sougou Search API SK": "",
 	"Enter stop sequence": "Въведете стоп последователност",
 	"Enter system prompt": "Въведете системен промпт",
 	"Enter system prompt here": "",
 	"Enter Tavily API Key": "Въведете API ключ за Tavily",
+	"Enter Tavily Extract Depth": "",
 	"Enter the public URL of your WebUI. This URL will be used to generate links in the notifications.": "Въведете публичния URL адрес на вашия WebUI. Този URL адрес ще бъде използван за генериране на връзки в известията.",
 	"Enter Tika Server URL": "Въведете URL адрес на Tika сървър",
 	"Enter timeout in seconds": "",
@@ -525,6 +535,8 @@
 	"Filter is now globally enabled": "Филтърът вече е глобално активиран",
 	"Filters": "Филтри",
 	"Fingerprint spoofing detected: Unable to use initials as avatar. Defaulting to default profile image.": "Потвърждаване на отпечатък: Не може да се използва инициализационна буква като аватар. Потребителят се връща към стандартна аватарка.",
+	"Firecrawl API Base URL": "",
+	"Firecrawl API Key": "",
 	"Fluidly stream large external response chunks": "Плавно предаване на големи части от външен отговор",
 	"Focus chat input": "Фокусиране на чат вход",
 	"Folder deleted successfully": "Папката е изтрита успешно",
@@ -589,6 +601,8 @@
 	"Hybrid Search": "Хибридно търсене",
 	"I acknowledge that I have read and I understand the implications of my action. I am aware of the risks associated with executing arbitrary code and I have verified the trustworthiness of the source.": "Потвърждавам, че съм прочел и разбирам последствията от моето действие. Наясно съм с рисковете, свързани с изпълнението на произволен код, и съм проверил надеждността на източника.",
 	"ID": "ID",
+	"iframe Sandbox Allow Forms": "",
+	"iframe Sandbox Allow Same Origin": "",
 	"Ignite curiosity": "Запалете любопитството",
 	"Image": "Изображение",
 	"Image Compression": "Компресия на изображения",
@@ -649,6 +663,7 @@
 	"Label": "Етикет",
 	"Landing Page Mode": "Режим на начална страница",
 	"Language": "Език",
+	"Language Locales": "",
 	"Last Active": "Последни активни",
 	"Last Modified": "Последно модифицирано",
 	"Last reply": "Последен отговор",
@@ -702,7 +717,6 @@
 	"Message rating should be enabled to use this feature": "Оценяването на съобщения трябва да бъде активирано, за да използвате тази функция",
 	"Messages you send after creating your link won't be shared. Users with the URL will be able to view the shared chat.": "Съобщенията, които изпращате след създаването на връзката, няма да бъдат споделяни. Потребителите с URL адреса ще могат да видят споделения чат.",
 	"Min P": "Мин P",
-	"Minimum Score": "Минимална оценка",
 	"Mirostat": "Mirostat",
 	"Mirostat Eta": "Mirostat Eta",
 	"Mirostat Tau": "Mirostat Tau",
@@ -833,6 +847,8 @@
 	"Pipelines Valves": "Клапани на пайплайни",
 	"Plain text (.txt)": "Обикновен текст (.txt)",
 	"Playground": "Плейграунд",
+	"Playwright Timeout (ms)": "",
+	"Playwright WebSocket URL": "",
 	"Please carefully review the following warnings:": "Моля, внимателно прегледайте следните предупреждения:",
 	"Please do not close the settings page while loading the model.": "Моля, не затваряйте страницата с настройки, докато моделът се зарежда.",
 	"Please enter a prompt": "Моля, въведете промпт",
@@ -878,8 +894,11 @@
 	"References from": "Препратки от",
 	"Refused when it shouldn't have": "Отказано, когато не трябва да бъде",
 	"Regenerate": "Регенериране",
+	"Reindex": "",
+	"Reindex Knowledge Base Vectors": "",
 	"Release Notes": "Бележки по изданието",
 	"Relevance": "Релевантност",
+	"Relevance Threshold": "",
 	"Remove": "Изтриване",
 	"Remove Model": "Изтриване на модела",
 	"Rename": "Преименуване",
@@ -1009,6 +1028,8 @@
 	"Sign up to {{WEBUI_NAME}}": "Регистрирайте се в {{WEBUI_NAME}}",
 	"Signing in to {{WEBUI_NAME}}": "Вписване в {{WEBUI_NAME}}",
 	"sk-1234": "sk-1234",
+	"Sougou Search API sID": "",
+	"Sougou Search API SK": "",
 	"Source": "Източник",
 	"Speech Playback Speed": "Скорост на възпроизвеждане на речта",
 	"Speech recognition error: {{error}}": "Грешка при разпознаване на реч: {{error}}",
@@ -1036,6 +1057,7 @@
 	"Tap to interrupt": "Докоснете за прекъсване",
 	"Tasks": "Задачи",
 	"Tavily API Key": "Tavily API Ключ",
+	"Tavily Extract Depth": "",
 	"Tell us more:": "Повече информация:",
 	"Temperature": "Температура",
 	"Template": "Шаблон",
@@ -1158,6 +1180,8 @@
 	"Use Gravatar": "Използвайте Gravatar",
 	"Use groups to group your users and assign permissions.": "Използвайте групи, за да групирате вашите потребители и да присвоите разрешения.",
 	"Use Initials": "Използвайте инициали",
+	"Use no proxy to fetch page contents.": "",
+	"Use proxy designated by http_proxy and https_proxy environment variables to fetch page contents.": "",
 	"use_mlock (Ollama)": "use_mlock (Ollama)",
 	"use_mmap (Ollama)": "use_mmap (Ollama)",
 	"user": "потребител",
@@ -1175,6 +1199,7 @@
 	"variable": "променлива",
 	"variable to have them replaced with clipboard content.": "променлива, за да бъдат заменени със съдържанието от клипборда.",
 	"Verify Connection": "",
+	"Verify SSL Certificate": "",
 	"Version": "Версия",
 	"Version {{selectedVersion}} of {{totalVersions}}": "Версия {{selectedVersion}} от {{totalVersions}}",
 	"View Replies": "Преглед на отговорите",
@@ -1189,6 +1214,7 @@
 	"Warning: Jupyter execution enables arbitrary code execution, posing severe security risks—proceed with extreme caution.": "Предупреждение: Изпълнението на Jupyter позволява произволно изпълнение на код, което представлява сериозни рискове за сигурността—продължете с изключително внимание.",
 	"Web": "Уеб",
 	"Web API": "Уеб API",
+	"Web Loader Engine": "",
 	"Web Search": "Търсене в уеб",
 	"Web Search Engine": "Уеб търсачка",
 	"Web Search in Chat": "Уеб търсене в чата",

+ 28 - 2
src/lib/i18n/locales/bn-BD/translation.json

@@ -62,6 +62,7 @@
 	"Allow Chat Deletion": "চ্যাট ডিলিট করতে দিন",
 	"Allow Chat Edit": "",
 	"Allow File Upload": "",
+	"Allow Multiple Models in Chat": "",
 	"Allow non-local voices": "",
 	"Allow Temporary Chat": "",
 	"Allow User Location": "",
@@ -79,6 +80,7 @@
 	"and": "এবং",
 	"and {{COUNT}} more": "",
 	"and create a new shared link.": "এবং একটি নতুন শেয়ারে লিংক তৈরি করুন.",
+	"Android": "",
 	"API Base URL": "এপিআই বেজ ইউআরএল",
 	"API Key": "এপিআই কোড",
 	"API Key created.": "একটি এপিআই কোড তৈরি করা হয়েছে.",
@@ -141,7 +143,6 @@
 	"Brave Search API Key": "সাহসী অনুসন্ধান API কী",
 	"By {{name}}": "",
 	"Bypass Embedding and Retrieval": "",
-	"Bypass SSL verification for Websites": "ওয়েবসাইটের জন্য SSL যাচাই বাতিল করুন",
 	"Calendar": "",
 	"Call": "",
 	"Call feature is not supported when using Web STT engine": "",
@@ -303,6 +304,7 @@
 	"Deleted User": "",
 	"Describe your knowledge base and objectives": "",
 	"Description": "বিবরণ",
+	"Detect Artifacts Automatically": "",
 	"Didn't fully follow instructions": "ইনস্ট্রাকশন সম্পূর্ণ অনুসরণ করা হয়নি",
 	"Direct": "",
 	"Direct Connections": "",
@@ -358,6 +360,7 @@
 	"e.g. my_filter": "",
 	"e.g. my_tools": "",
 	"e.g. Tools for performing various operations": "",
+	"e.g., en-US,ja-JP (leave blank for auto-detect)": "",
 	"Edit": "এডিট করুন",
 	"Edit Arena Model": "",
 	"Edit Channel": "",
@@ -407,6 +410,8 @@
 	"Enter Document Intelligence Key": "",
 	"Enter domains separated by commas (e.g., example.com,site.org)": "",
 	"Enter Exa API Key": "",
+	"Enter Firecrawl API Base URL": "",
+	"Enter Firecrawl API Key": "",
 	"Enter Github Raw URL": "গিটহাব কাঁচা URL লিখুন",
 	"Enter Google PSE API Key": "গুগল পিএসই এপিআই কী লিখুন",
 	"Enter Google PSE Engine Id": "গুগল পিএসই ইঞ্জিন আইডি লিখুন",
@@ -424,6 +429,8 @@
 	"Enter Mojeek Search API Key": "",
 	"Enter Number of Steps (e.g. 50)": "ধাপের সংখ্যা দিন (যেমন: 50)",
 	"Enter Perplexity API Key": "",
+	"Enter Playwright Timeout": "",
+	"Enter Playwright WebSocket URL": "",
 	"Enter proxy URL (e.g. https://user:password@host:port)": "",
 	"Enter reasoning effort": "",
 	"Enter Sampler (e.g. Euler a)": "",
@@ -441,10 +448,13 @@
 	"Enter server host": "",
 	"Enter server label": "",
 	"Enter server port": "",
+	"Enter Sougou Search API sID": "",
+	"Enter Sougou Search API SK": "",
 	"Enter stop sequence": "স্টপ সিকোয়েন্স লিখুন",
 	"Enter system prompt": "",
 	"Enter system prompt here": "",
 	"Enter Tavily API Key": "",
+	"Enter Tavily Extract Depth": "",
 	"Enter the public URL of your WebUI. This URL will be used to generate links in the notifications.": "",
 	"Enter Tika Server URL": "",
 	"Enter timeout in seconds": "",
@@ -525,6 +535,8 @@
 	"Filter is now globally enabled": "",
 	"Filters": "",
 	"Fingerprint spoofing detected: Unable to use initials as avatar. Defaulting to default profile image.": "ফিঙ্গারপ্রিন্ট স্পুফিং ধরা পড়েছে: অ্যাভাটার হিসেবে নামের আদ্যক্ষর ব্যবহার করা যাচ্ছে না। ডিফল্ট প্রোফাইল পিকচারে ফিরিয়ে নেয়া হচ্ছে।",
+	"Firecrawl API Base URL": "",
+	"Firecrawl API Key": "",
 	"Fluidly stream large external response chunks": "বড় এক্সটার্নাল রেসপন্স চাঙ্কগুলো মসৃণভাবে প্রবাহিত করুন",
 	"Focus chat input": "চ্যাট ইনপুট ফোকাস করুন",
 	"Folder deleted successfully": "",
@@ -589,6 +601,8 @@
 	"Hybrid Search": "হাইব্রিড অনুসন্ধান",
 	"I acknowledge that I have read and I understand the implications of my action. I am aware of the risks associated with executing arbitrary code and I have verified the trustworthiness of the source.": "",
 	"ID": "",
+	"iframe Sandbox Allow Forms": "",
+	"iframe Sandbox Allow Same Origin": "",
 	"Ignite curiosity": "",
 	"Image": "",
 	"Image Compression": "",
@@ -649,6 +663,7 @@
 	"Label": "",
 	"Landing Page Mode": "",
 	"Language": "ভাষা",
+	"Language Locales": "",
 	"Last Active": "সর্বশেষ সক্রিয়",
 	"Last Modified": "",
 	"Last reply": "",
@@ -702,7 +717,6 @@
 	"Message rating should be enabled to use this feature": "",
 	"Messages you send after creating your link won't be shared. Users with the URL will be able to view the shared chat.": "আপনার লিঙ্ক তৈরি করার পরে আপনার পাঠানো বার্তাগুলি শেয়ার করা হবে না। ইউআরএল ব্যবহারকারীরা শেয়ার করা চ্যাট দেখতে পারবেন।",
 	"Min P": "",
-	"Minimum Score": "Minimum Score",
 	"Mirostat": "Mirostat",
 	"Mirostat Eta": "Mirostat Eta",
 	"Mirostat Tau": "Mirostat Tau",
@@ -833,6 +847,8 @@
 	"Pipelines Valves": "পাইপলাইন ভালভ",
 	"Plain text (.txt)": "প্লায়েন টেক্সট (.txt)",
 	"Playground": "খেলাঘর",
+	"Playwright Timeout (ms)": "",
+	"Playwright WebSocket URL": "",
 	"Please carefully review the following warnings:": "",
 	"Please do not close the settings page while loading the model.": "",
 	"Please enter a prompt": "",
@@ -878,8 +894,11 @@
 	"References from": "",
 	"Refused when it shouldn't have": "যদি উপযুক্ত নয়, তবে রেজিগেনেট করা হচ্ছে",
 	"Regenerate": "রেজিগেনেট করুন",
+	"Reindex": "",
+	"Reindex Knowledge Base Vectors": "",
 	"Release Notes": "রিলিজ নোটসমূহ",
 	"Relevance": "",
+	"Relevance Threshold": "",
 	"Remove": "রিমুভ করুন",
 	"Remove Model": "মডেল রিমুভ করুন",
 	"Rename": "রেনেম",
@@ -1009,6 +1028,8 @@
 	"Sign up to {{WEBUI_NAME}}": "",
 	"Signing in to {{WEBUI_NAME}}": "",
 	"sk-1234": "",
+	"Sougou Search API sID": "",
+	"Sougou Search API SK": "",
 	"Source": "উৎস",
 	"Speech Playback Speed": "",
 	"Speech recognition error: {{error}}": "স্পিচ রিকগনিশনে সমস্যা: {{error}}",
@@ -1036,6 +1057,7 @@
 	"Tap to interrupt": "",
 	"Tasks": "",
 	"Tavily API Key": "",
+	"Tavily Extract Depth": "",
 	"Tell us more:": "আরও বলুন:",
 	"Temperature": "তাপমাত্রা",
 	"Template": "টেম্পলেট",
@@ -1158,6 +1180,8 @@
 	"Use Gravatar": "Gravatar ব্যবহার করুন",
 	"Use groups to group your users and assign permissions.": "",
 	"Use Initials": "নামের আদ্যক্ষর ব্যবহার করুন",
+	"Use no proxy to fetch page contents.": "",
+	"Use proxy designated by http_proxy and https_proxy environment variables to fetch page contents.": "",
 	"use_mlock (Ollama)": "use_mlock (ওলামা)",
 	"use_mmap (Ollama)": "use_mmap (ওলামা)",
 	"user": "ব্যবহারকারী",
@@ -1175,6 +1199,7 @@
 	"variable": "ভেরিয়েবল",
 	"variable to have them replaced with clipboard content.": "ক্লিপবোর্ডের কন্টেন্ট দিয়ে যেই ভেরিয়েবল রিপ্লেস করা যাবে।",
 	"Verify Connection": "",
+	"Verify SSL Certificate": "",
 	"Version": "ভার্সন",
 	"Version {{selectedVersion}} of {{totalVersions}}": "",
 	"View Replies": "",
@@ -1189,6 +1214,7 @@
 	"Warning: Jupyter execution enables arbitrary code execution, posing severe security risks—proceed with extreme caution.": "",
 	"Web": "ওয়েব",
 	"Web API": "",
+	"Web Loader Engine": "",
 	"Web Search": "ওয়েব অনুসন্ধান",
 	"Web Search Engine": "ওয়েব সার্চ ইঞ্জিন",
 	"Web Search in Chat": "",

+ 28 - 2
src/lib/i18n/locales/bo-TB/translation.json

@@ -62,6 +62,7 @@
 	"Allow Chat Deletion": "ཁ་བརྡ་བསུབ་པར་གནང་བ་སྤྲོད་པ།",
 	"Allow Chat Edit": "ཁ་བརྡ་ཞུ་དག་ལ་གནང་བ་སྤྲོད་པ།",
 	"Allow File Upload": "ཡིག་ཆ་སྤར་བར་གནང་བ་སྤྲོད་པ།",
+	"Allow Multiple Models in Chat": "",
 	"Allow non-local voices": "ས་གནས་མིན་པའི་སྐད་གདངས་ལ་གནང་བ་སྤྲོད་པ།",
 	"Allow Temporary Chat": "གནས་སྐབས་ཁ་བརྡར་གནང་བ་སྤྲོད་པ།",
 	"Allow User Location": "བེད་སྤྱོད་མཁན་གནས་ཡུལ་ལ་གནང་བ་སྤྲོད་པ།",
@@ -79,6 +80,7 @@
 	"and": "དང་།",
 	"and {{COUNT}} more": "ད་དུང་ {{COUNT}}",
 	"and create a new shared link.": "དང་མཉམ་སྤྱོད་སྦྲེལ་ཐག་གསར་པ་ཞིག་བཟོ་བ།",
+	"Android": "",
 	"API Base URL": "API གཞི་རྩའི་ URL",
 	"API Key": "API ལྡེ་མིག",
 	"API Key created.": "API ལྡེ་མིག་བཟོས་ཟིན།",
@@ -141,7 +143,6 @@
 	"Brave Search API Key": "Brave Search API ལྡེ་མིག",
 	"By {{name}}": "{{name}} ཡིས།",
 	"Bypass Embedding and Retrieval": "ཚུད་འཇུག་དང་ལེན་ཚུར་སྒྲུབ་ལས་བརྒལ་བ།",
-	"Bypass SSL verification for Websites": "དྲ་ཚིགས་ཀྱི་ SSL ར་སྤྲོད་བརྒལ་བ།",
 	"Calendar": "ལོ་ཐོ།",
 	"Call": "སྐད་འབོད།",
 	"Call feature is not supported when using Web STT engine": "Web STT མ་ལག་སྤྱོད་སྐབས་སྐད་འབོད་ཀྱི་ཁྱད་ཆོས་ལ་རྒྱབ་སྐྱོར་མེད།",
@@ -303,6 +304,7 @@
 	"Deleted User": "བེད་སྤྱོད་མཁན་བསུབས་ཟིན།",
 	"Describe your knowledge base and objectives": "ཁྱེད་ཀྱི་ཤེས་བྱའི་རྟེན་གཞི་དང་དམིགས་ཡུལ་འགྲེལ་བཤད་བྱེད་པ།",
 	"Description": "འགྲེལ་བཤད།",
+	"Detect Artifacts Automatically": "",
 	"Didn't fully follow instructions": "ལམ་སྟོན་ཡོངས་སུ་མ་བསྒྲུབས།",
 	"Direct": "ཐད་ཀར།",
 	"Direct Connections": "ཐད་ཀར་སྦྲེལ་མཐུད།",
@@ -358,6 +360,7 @@
 	"e.g. my_filter": "དཔེར་ན། my_filter",
 	"e.g. my_tools": "དཔེར་ན། my_tools",
 	"e.g. Tools for performing various operations": "དཔེར་ན། ལས་ཀ་སྣ་ཚོགས་སྒྲུབ་བྱེད་ཀྱི་ལག་ཆ།",
+	"e.g., en-US,ja-JP (leave blank for auto-detect)": "",
 	"Edit": "ཞུ་དག",
 	"Edit Arena Model": "Arena དཔེ་དབྱིབས་ཞུ་དག",
 	"Edit Channel": "བགྲོ་གླེང་ཞུ་དག",
@@ -407,6 +410,8 @@
 	"Enter Document Intelligence Key": "ཡིག་ཆའི་རིག་ནུས་ལྡེ་མིག་འཇུག་པ།",
 	"Enter domains separated by commas (e.g., example.com,site.org)": "ཚེག་བསྐུངས་ཀྱིས་ལོགས་སུ་བཀར་བའི་ཁྱབ་ཁོངས་འཇུག་པ། (དཔེར་ན། example.com,site.org)",
 	"Enter Exa API Key": "Exa API ལྡེ་མིག་འཇུག་པ།",
+	"Enter Firecrawl API Base URL": "",
+	"Enter Firecrawl API Key": "",
 	"Enter Github Raw URL": "Github Raw URL འཇུག་པ།",
 	"Enter Google PSE API Key": "Google PSE API ལྡེ་མིག་འཇུག་པ།",
 	"Enter Google PSE Engine Id": "Google PSE Engine Id འཇུག་པ།",
@@ -424,6 +429,8 @@
 	"Enter Mojeek Search API Key": "Mojeek Search API ལྡེ་མིག་འཇུག་པ།",
 	"Enter Number of Steps (e.g. 50)": "གོམ་གྲངས་འཇུག་པ། (དཔེར་ན། ༥༠)",
 	"Enter Perplexity API Key": "Perplexity API ལྡེ་མིག་འཇུག་པ།",
+	"Enter Playwright Timeout": "",
+	"Enter Playwright WebSocket URL": "",
 	"Enter proxy URL (e.g. https://user:password@host:port)": "Proxy URL འཇུག་པ། (དཔེར་ན། https://user:password@host:port)",
 	"Enter reasoning effort": "རྒྱུ་མཚན་འདྲེན་པའི་འབད་བརྩོན་འཇུག་པ།",
 	"Enter Sampler (e.g. Euler a)": "Sampler འཇུག་པ། (དཔེར་ན། Euler a)",
@@ -441,10 +448,13 @@
 	"Enter server host": "སར་བར་གྱི་ Host འཇུག་པ།",
 	"Enter server label": "སར་བར་གྱི་བྱང་རྟགས་འཇུག་པ།",
 	"Enter server port": "སར་བར་གྱི་ Port འཇུག་པ།",
+	"Enter Sougou Search API sID": "",
+	"Enter Sougou Search API SK": "",
 	"Enter stop sequence": "མཚམས་འཇོག་རིམ་པ་འཇུག་པ།",
 	"Enter system prompt": "མ་ལག་གི་འགུལ་སློང་འཇུག་པ།",
 	"Enter system prompt here": "",
 	"Enter Tavily API Key": "Tavily API ལྡེ་མིག་འཇུག་པ།",
+	"Enter Tavily Extract Depth": "",
 	"Enter the public URL of your WebUI. This URL will be used to generate links in the notifications.": "ཁྱེད་ཀྱི་ WebUI ཡི་སྤྱི་སྤྱོད་ URL འཇུག་པ། URL འདི་བརྡ་ཁྱབ་ནང་སྦྲེལ་ཐག་བཟོ་བར་བེད་སྤྱོད་བྱེད་ངེས།",
 	"Enter Tika Server URL": "Tika Server URL འཇུག་པ།",
 	"Enter timeout in seconds": "སྐར་ཆའི་ནང་དུས་ཚོད་བཀག་པ་འཇུག་པ།",
@@ -525,6 +535,8 @@
 	"Filter is now globally enabled": "འཚག་མ་དེ་ད་ལྟ་འཛམ་གླིང་ཡོངས་ནས་སྒུལ་བསྐྱོད་བྱས་ཡོད།",
 	"Filters": "འཚག་མ།",
 	"Fingerprint spoofing detected: Unable to use initials as avatar. Defaulting to default profile image.": "མཛུབ་ཐེལ་རྫུན་བཟོ་རྙེད་སོང་།: མིང་གི་ཡིག་འབྲུ་མགོ་མ་སྐུ་ཚབ་ཏུ་བེད་སྤྱོད་གཏོང་མི་ཐུབ། སྔོན་སྒྲིག་ཕྱི་ཐག་པར་རིས་ལ་སྔོན་སྒྲིག་བྱེད་བཞིན་པ།",
+	"Firecrawl API Base URL": "",
+	"Firecrawl API Key": "",
 	"Fluidly stream large external response chunks": "ཕྱི་རོལ་གྱི་ལན་གྱི་དུམ་བུ་ཆེན་པོ་རྒྱུན་བཞིན་རྒྱུག་པ།",
 	"Focus chat input": "ཁ་བརྡའི་ནང་འཇུག་ལ་དམིགས་པ།",
 	"Folder deleted successfully": "ཡིག་སྣོད་ལེགས་པར་བསུབས་ཟིན།",
@@ -589,6 +601,8 @@
 	"Hybrid Search": "Hybrid འཚོལ་བཤེར།",
 	"I acknowledge that I have read and I understand the implications of my action. I am aware of the risks associated with executing arbitrary code and I have verified the trustworthiness of the source.": "ངས་ངའི་བྱ་སྤྱོད་ཀྱི་ཤུགས་རྐྱེན་ཀློག་པ་དང་གོ་རྟོགས་སྤྲད་ཡོད་པ་ཁས་ལེན་བྱེད། ངས་གང་འདོད་ཀྱི་ཀོཌ་ལག་བསྟར་དང་འབྲེལ་བའི་ཉེན་ཁ་ཤེས་ཀྱི་ཡོད། དེ་མིན་ངས་འབྱུང་ཁུངས་ཀྱི་ཡིད་རྟོན་རུང་བའི་རང་བཞིན་ར་སྤྲོད་བྱས་ཡོད།",
 	"ID": "ID",
+	"iframe Sandbox Allow Forms": "",
+	"iframe Sandbox Allow Same Origin": "",
 	"Ignite curiosity": "ཤེས་འདོད་སློང་བ།",
 	"Image": "པར།",
 	"Image Compression": "པར་བསྡུ་སྐུམ།",
@@ -649,6 +663,7 @@
 	"Label": "བྱང་རྟགས།",
 	"Landing Page Mode": "འབབ་ཤོག་མ་དཔེ།",
 	"Language": "སྐད་ཡིག",
+	"Language Locales": "",
 	"Last Active": "མཐའ་མའི་ལས་བྱེད།",
 	"Last Modified": "མཐའ་མའི་བཟོ་བཅོས།",
 	"Last reply": "ལན་མཐའ་མ།",
@@ -702,7 +717,6 @@
 	"Message rating should be enabled to use this feature": "ཁྱད་ཆོས་འདི་བེད་སྤྱོད་གཏོང་བར་འཕྲིན་ལ་སྐར་མ་སྤྲོད་པ་སྒུལ་བསྐྱོད་བྱེད་དགོས།",
 	"Messages you send after creating your link won't be shared. Users with the URL will be able to view the shared chat.": "ཁྱེད་ཀྱི་སྦྲེལ་ཐག་བཟོས་རྗེས་ཁྱེད་ཀྱིས་བསྐུར་བའི་འཕྲིན་དག་མཉམ་སྤྱོད་བྱེད་མི་འགྱུར། URL ཡོད་པའི་བེད་སྤྱོད་མཁན་ཚོས་མཉམ་སྤྱོད་ཁ་བརྡ་ལྟ་ཐུབ་ངེས།",
 	"Min P": "P ཉུང་ཤོས།",
-	"Minimum Score": "སྐར་མ་ཉུང་ཤོས།",
 	"Mirostat": "Mirostat",
 	"Mirostat Eta": "Mirostat Eta",
 	"Mirostat Tau": "Mirostat Tau",
@@ -833,6 +847,8 @@
 	"Pipelines Valves": "རྒྱུ་ལམ་གྱི་ Valve",
 	"Plain text (.txt)": "ཡིག་རྐྱང་རྐྱང་པ། (.txt)",
 	"Playground": "རྩེད་ཐང་།",
+	"Playwright Timeout (ms)": "",
+	"Playwright WebSocket URL": "",
 	"Please carefully review the following warnings:": "གཤམ་གསལ་ཉེན་བརྡ་དག་ལ་ཞིབ་ཚགས་ངང་བལྟ་ཞིབ་བྱེད་རོགས།:",
 	"Please do not close the settings page while loading the model.": "དཔེ་དབྱིབས་ནང་འཇུག་བྱེད་སྐབས་སྒྲིག་འགོད་ཤོག་ངོས་ཁ་མ་རྒྱག་རོགས།",
 	"Please enter a prompt": "འགུལ་སློང་ཞིག་འཇུག་རོགས།",
@@ -878,8 +894,11 @@
 	"References from": "ནས་ལུང་འདྲེན།",
 	"Refused when it shouldn't have": "མི་དགོས་དུས་ཁས་མ་བླངས།",
 	"Regenerate": "བསྐྱར་བཟོ།",
+	"Reindex": "",
+	"Reindex Knowledge Base Vectors": "",
 	"Release Notes": "འགྲེམས་སྤེལ་མཆན་བུ།",
 	"Relevance": "འབྲེལ་ཡོད་རང་བཞིན།",
+	"Relevance Threshold": "",
 	"Remove": "འདོར་བ།",
 	"Remove Model": "དཔེ་དབྱིབས་འདོར་བ།",
 	"Rename": "མིང་བསྐྱར་འདོགས།",
@@ -1009,6 +1028,8 @@
 	"Sign up to {{WEBUI_NAME}}": "{{WEBUI_NAME}} ལ་ཐོ་འགོད།",
 	"Signing in to {{WEBUI_NAME}}": "{{WEBUI_NAME}} ལ་ནང་འཛུལ་བྱེད་བཞིན་པ།",
 	"sk-1234": "sk-༡༢༣༤",
+	"Sougou Search API sID": "",
+	"Sougou Search API SK": "",
 	"Source": "འབྱུང་ཁུངས།",
 	"Speech Playback Speed": "གཏམ་བཤད་ཕྱིར་གཏོང་གི་མྱུར་ཚད།",
 	"Speech recognition error: {{error}}": "གཏམ་བཤད་ངོས་འཛིན་ནོར་འཁྲུལ།: {{error}}",
@@ -1036,6 +1057,7 @@
 	"Tap to interrupt": "བར་ཆད་བྱེད་པར་མནན་པ།",
 	"Tasks": "ལས་འགན།",
 	"Tavily API Key": "Tavily API ལྡེ་མིག",
+	"Tavily Extract Depth": "",
 	"Tell us more:": "ང་ཚོ་ལ་མང་ཙམ་ཤོད།:",
 	"Temperature": "དྲོད་ཚད།",
 	"Template": "མ་དཔེ།",
@@ -1158,6 +1180,8 @@
 	"Use Gravatar": "Gravatar བེད་སྤྱོད།",
 	"Use groups to group your users and assign permissions.": "ཁྱེད་ཀྱི་བེད་སྤྱོད་མཁན་ཚོགས་པ་བཟོ་བ་དང་དབང་ཚད་སྤྲོད་པར་ཚོགས་པ་བེད་སྤྱོད་གཏོང་བ།",
 	"Use Initials": "མིང་གི་ཡིག་འབྲུ་མགོ་མ་བེད་སྤྱོད།",
+	"Use no proxy to fetch page contents.": "",
+	"Use proxy designated by http_proxy and https_proxy environment variables to fetch page contents.": "",
 	"use_mlock (Ollama)": "use_mlock (Ollama)",
 	"use_mmap (Ollama)": "use_mmap (Ollama)",
 	"user": "བེད་སྤྱོད་མཁན།",
@@ -1175,6 +1199,7 @@
 	"variable": "འགྱུར་ཚད།",
 	"variable to have them replaced with clipboard content.": "འགྱུར་ཚད་དེ་དག་སྦྱར་སྡེར་གྱི་ནང་དོན་གྱིས་ཚབ་བྱེད་པར་ཡོད་པ།",
 	"Verify Connection": "སྦྲེལ་མཐུད་ར་སྤྲོད།",
+	"Verify SSL Certificate": "",
 	"Version": "པར་གཞི།",
 	"Version {{selectedVersion}} of {{totalVersions}}": "པར་གཞི་ {{selectedVersion}} ། {{totalVersions}} ནས།",
 	"View Replies": "ལན་ལྟ་བ།",
@@ -1189,6 +1214,7 @@
 	"Warning: Jupyter execution enables arbitrary code execution, posing severe security risks—proceed with extreme caution.": "ཉེན་བརྡ།: Jupyter ལག་བསྟར་གྱིས་གང་འདོད་ཀྱི་ཀོཌ་ལག་བསྟར་སྒུལ་བསྐྱོད་བྱས་ནས། བདེ་འཇགས་ཀྱི་ཉེན་ཁ་ཚབས་ཆེན་བཟོ་གི་ཡོད།—ཧ་ཅང་གཟབ་ནན་གྱིས་སྔོན་སྐྱོད་བྱེད་རོགས།",
 	"Web": "དྲ་བ།",
 	"Web API": "Web API",
+	"Web Loader Engine": "",
 	"Web Search": "དྲ་བའི་འཚོལ་བཤེར།",
 	"Web Search Engine": "དྲ་བའི་འཚོལ་བཤེར་འཕྲུལ་འཁོར།",
 	"Web Search in Chat": "ཁ་བརྡའི་ནང་དྲ་བའི་འཚོལ་བཤེར།",

+ 65 - 39
src/lib/i18n/locales/ca-ES/translation.json

@@ -6,7 +6,7 @@
 	"(latest)": "(últim)",
 	"(Ollama)": "(Ollama)",
 	"{{ models }}": "{{ models }}",
-	"{{COUNT}} Available Tools": "",
+	"{{COUNT}} Available Tools": "{{COUNT}} eines disponibles",
 	"{{COUNT}} hidden lines": "{{COUNT}} línies ocultes",
 	"{{COUNT}} Replies": "{{COUNT}} respostes",
 	"{{user}}'s Chats": "Els xats de {{user}}",
@@ -62,6 +62,7 @@
 	"Allow Chat Deletion": "Permetre la supressió del xat",
 	"Allow Chat Edit": "Permetre editar el xat",
 	"Allow File Upload": "Permetre la pujada d'arxius",
+	"Allow Multiple Models in Chat": "",
 	"Allow non-local voices": "Permetre veus no locals",
 	"Allow Temporary Chat": "Permetre el xat temporal",
 	"Allow User Location": "Permetre la ubicació de l'usuari",
@@ -79,6 +80,7 @@
 	"and": "i",
 	"and {{COUNT}} more": "i {{COUNT}} més",
 	"and create a new shared link.": "i crear un nou enllaç compartit.",
+	"Android": "",
 	"API Base URL": "URL Base de l'API",
 	"API Key": "clau API",
 	"API Key created.": "clau API creada.",
@@ -108,10 +110,10 @@
 	"Attribute for Username": "Atribut per al Nom d'usuari",
 	"Audio": "Àudio",
 	"August": "Agost",
-	"Auth": "",
+	"Auth": "Autenticació",
 	"Authenticate": "Autenticar",
 	"Authentication": "Autenticació",
-	"Auto": "",
+	"Auto": "Automàtic",
 	"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",
@@ -121,7 +123,7 @@
 	"AUTOMATIC1111 Base URL": "URL Base d'AUTOMATIC1111",
 	"AUTOMATIC1111 Base URL is required.": "Es requereix l'URL Base d'AUTOMATIC1111.",
 	"Available list": "Llista de disponibles",
-	"Available Tools": "",
+	"Available Tools": "Eines disponibles",
 	"available!": "disponible!",
 	"Awful": "Terrible",
 	"Azure AI Speech": "Azure AI Speech",
@@ -141,7 +143,6 @@
 	"Brave Search API Key": "Clau API de Brave Search",
 	"By {{name}}": "Per {{name}}",
 	"Bypass Embedding and Retrieval": "Desactivar l'Embedding i el Retrieval",
-	"Bypass SSL verification for Websites": "Desactivar la verificació SSL per a l'accés a Internet",
 	"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",
@@ -220,10 +221,10 @@
 	"Confirm your new password": "Confirma la teva nova contrasenya",
 	"Connect to your own OpenAI compatible API endpoints.": "Connecta als teus propis punts de connexió de l'API compatible amb OpenAI",
 	"Connect to your own OpenAPI compatible external tool servers.": "Connecta als teus propis servidors d'eines externs compatibles amb OpenAPI",
-	"Connection failed": "",
-	"Connection successful": "",
+	"Connection failed": "La connexió ha fallat",
+	"Connection successful": "Connexió correcta",
 	"Connections": "Connexions",
-	"Connections saved successfully": "",
+	"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",
 	"Content": "Contingut",
@@ -303,12 +304,13 @@
 	"Deleted User": "Usuari eliminat",
 	"Describe your knowledge base and objectives": "Descriu la teva base de coneixement i objectius",
 	"Description": "Descripció",
+	"Detect Artifacts Automatically": "",
 	"Didn't fully follow instructions": "No s'han seguit les instruccions completament",
 	"Direct": "Directe",
 	"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",
-	"Direct Tool Servers": "",
+	"Direct Tool Servers": "Servidors d'eines directes",
 	"Disabled": "Deshabilitat",
 	"Discover a function": "Descobrir una funció",
 	"Discover a model": "Descobrir un model",
@@ -358,6 +360,7 @@
 	"e.g. my_filter": "p. ex. els_meus_filtres",
 	"e.g. my_tools": "p. ex. les_meves_eines",
 	"e.g. Tools for performing various operations": "p. ex. Eines per dur a terme operacions",
+	"e.g., en-US,ja-JP (leave blank for auto-detect)": "",
 	"Edit": "Editar",
 	"Edit Arena Model": "Editar model de l'Arena",
 	"Edit Channel": "Editar el canal",
@@ -385,7 +388,7 @@
 	"Enable Mirostat sampling for controlling perplexity.": "Permetre el mostreig de Mirostat per controlar la perplexitat",
 	"Enable New Sign Ups": "Permetre nous registres",
 	"Enabled": "Habilitat",
-	"Enforce Temporary Chat": "",
+	"Enforce Temporary Chat": "Forçar els xats temporals",
 	"Ensure your CSV file includes 4 columns in this order: Name, Email, Password, Role.": "Assegura't que els teus fitxers CSV inclouen 4 columnes en aquest ordre: Nom, Correu electrònic, Contrasenya, Rol.",
 	"Enter {{role}} message here": "Introdueix aquí el missatge de {{role}}",
 	"Enter a detail about yourself for your LLMs to recall": "Introdueix un detall sobre tu què els teus models de llenguatge puguin recordar",
@@ -407,6 +410,8 @@
 	"Enter Document Intelligence Key": "Introdueix la clau de Document Intelligence",
 	"Enter domains separated by commas (e.g., example.com,site.org)": "Introdueix els dominis separats per comes (p. ex. example.com,site.org)",
 	"Enter Exa API Key": "Introdueix la clau API de d'EXA",
+	"Enter Firecrawl API Base URL": "",
+	"Enter Firecrawl API Key": "",
 	"Enter Github Raw URL": "Introdueix l'URL en brut de Github",
 	"Enter Google PSE API Key": "Introdueix la clau API de Google PSE",
 	"Enter Google PSE Engine Id": "Introdueix l'identificador del motor PSE de Google",
@@ -418,12 +423,14 @@
 	"Enter Kagi Search API Key": "Introdueix la clau API de Kagi Search",
 	"Enter Key Behavior": "Introdueix el comportament de clau",
 	"Enter language codes": "Introdueix els codis de llenguatge",
-	"Enter Mistral API Key": "",
+	"Enter Mistral API Key": "Entra la clau API de Mistral",
 	"Enter Model ID": "Introdueix l'identificador del model",
 	"Enter model tag (e.g. {{modelTag}})": "Introdueix l'etiqueta del model (p. ex. {{modelTag}})",
 	"Enter Mojeek Search API Key": "Introdueix la clau API de Mojeek Search",
 	"Enter Number of Steps (e.g. 50)": "Introdueix el nombre de passos (p. ex. 50)",
 	"Enter Perplexity API Key": "Introdueix la clau API de Perplexity",
+	"Enter Playwright Timeout": "",
+	"Enter Playwright WebSocket URL": "",
 	"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)",
@@ -441,10 +448,13 @@
 	"Enter server host": "Introdueix el servidor",
 	"Enter server label": "Introdueix l'etiqueta del servidor",
 	"Enter server port": "Introdueix el port del servidor",
+	"Enter Sougou Search API sID": "",
+	"Enter Sougou Search API SK": "",
 	"Enter stop sequence": "Introdueix la seqüència de parada",
 	"Enter system prompt": "Introdueix la indicació de sistema",
-	"Enter system prompt here": "",
+	"Enter system prompt here": "Entra la indicació de sistema aquí",
 	"Enter Tavily API Key": "Introdueix la clau API de Tavily",
+	"Enter Tavily Extract Depth": "",
 	"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": "Entra el temps màxim en segons",
@@ -457,7 +467,7 @@
 	"Enter Your Email": "Introdueix el teu correu electrònic",
 	"Enter Your Full Name": "Introdueix el teu nom complet",
 	"Enter your message": "Introdueix el teu missatge",
-	"Enter your name": "",
+	"Enter your name": "Entra el teu nom",
 	"Enter your new password": "Introdueix la teva nova contrasenya",
 	"Enter Your Password": "Introdueix la teva contrasenya",
 	"Enter Your Role": "Introdueix el teu rol",
@@ -477,7 +487,7 @@
 	"Exceeded the number of seats in your license. Please contact support to increase the number of seats.": "S'ha superat el nombre de places a la vostra llicència. Poseu-vos en contacte amb el servei d'assistència per augmentar el nombre de places.",
 	"Exclude": "Excloure",
 	"Execute code for analysis": "Executar el codi per analitzar-lo",
-	"Executing **{{NAME}}**...": "",
+	"Executing **{{NAME}}**...": "Executant **{{NAME}}**...",
 	"Expand": "Expandir",
 	"Experimental": "Experimental",
 	"Explain": "Explicar",
@@ -502,7 +512,7 @@
 	"Failed to create API Key.": "No s'ha pogut crear la clau API.",
 	"Failed to fetch models": "No s'han pogut obtenir els models",
 	"Failed to read clipboard contents": "No s'ha pogut llegir el contingut del porta-retalls",
-	"Failed to save connections": "",
+	"Failed to save connections": "No s'han pogut desar les connexions",
 	"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.",
@@ -525,6 +535,8 @@
 	"Filter is now globally enabled": "El filtre ha estat activat globalment",
 	"Filters": "Filtres",
 	"Fingerprint spoofing detected: Unable to use initials as avatar. Defaulting to default profile image.": "S'ha detectat la suplantació d'identitat de l'empremta digital: no es poden utilitzar les inicials com a avatar. S'estableix la imatge de perfil predeterminada.",
+	"Firecrawl API Base URL": "",
+	"Firecrawl API Key": "",
 	"Fluidly stream large external response chunks": "Transmetre amb fluïdesa grans trossos de resposta externa",
 	"Focus chat input": "Estableix el focus a l'entrada del xat",
 	"Folder deleted successfully": "Carpeta eliminada correctament",
@@ -535,7 +547,7 @@
 	"Forge new paths": "Crea nous camins",
 	"Form": "Formulari",
 	"Format your variables using brackets like this:": "Formata les teves variables utilitzant claudàtors així:",
-	"Forwards system user session credentials to authenticate": "",
+	"Forwards system user session credentials to authenticate": "Envia les credencials de l'usuari del sistema per autenticar",
 	"Frequency Penalty": "Penalització per freqüència",
 	"Full Context Mode": "Mode de context complert",
 	"Function": "Funció",
@@ -581,7 +593,7 @@
 	"Hex Color": "Color hexadecimal",
 	"Hex Color - Leave empty for default color": "Color hexadecimal - Deixar buit per a color per defecte",
 	"Hide": "Amaga",
-	"Hide Model": "",
+	"Hide Model": "Amagar el model",
 	"Home": "Inici",
 	"Host": "Servidor",
 	"How can I help you today?": "Com et puc ajudar avui?",
@@ -589,6 +601,8 @@
 	"Hybrid Search": "Cerca híbrida",
 	"I acknowledge that I have read and I understand the implications of my action. I am aware of the risks associated with executing arbitrary code and I have verified the trustworthiness of the source.": "Afirmo que he llegit i entenc les implicacions de la meva acció. Soc conscient dels riscos associats a l'execució de codi arbitrari i he verificat la fiabilitat de la font.",
 	"ID": "ID",
+	"iframe Sandbox Allow Forms": "",
+	"iframe Sandbox Allow Same Origin": "",
 	"Ignite curiosity": "Despertar la curiositat",
 	"Image": "Imatge",
 	"Image Compression": "Compressió d'imatges",
@@ -641,7 +655,7 @@
 	"Knowledge Access": "Accés al coneixement",
 	"Knowledge created successfully.": "Coneixement creat correctament.",
 	"Knowledge deleted successfully.": "Coneixement eliminat correctament.",
-	"Knowledge Public Sharing": "",
+	"Knowledge Public Sharing": "Compartir públicament el Coneixement",
 	"Knowledge reset successfully.": "Coneixement restablert correctament.",
 	"Knowledge updated successfully": "Coneixement actualitzat correctament.",
 	"Kokoro.js (Browser)": "Kokoro.js (Navegador)",
@@ -649,13 +663,14 @@
 	"Label": "Etiqueta",
 	"Landing Page Mode": "Mode de la pàgina d'entrada",
 	"Language": "Idioma",
+	"Language Locales": "",
 	"Last Active": "Activitat recent",
 	"Last Modified": "Modificació",
 	"Last reply": "Darrera resposta",
 	"LDAP": "LDAP",
 	"LDAP server updated": "Servidor LDAP actualitzat",
 	"Leaderboard": "Tauler de classificació",
-	"Learn more about OpenAPI tool servers.": "",
+	"Learn more about OpenAPI tool servers.": "Aprèn més sobre els servidors d'eines OpenAPI",
 	"Leave empty for unlimited": "Deixar-ho buit per il·limitat",
 	"Leave empty to include all models from \"{{url}}/api/tags\" endpoint": "Deixar-ho buit per incloure tots els models del punt de connexió \"{{url}}/api/tags\"",
 	"Leave empty to include all models from \"{{url}}/models\" endpoint": "Deixar-ho buit per incloure tots els models del punt de connexió \"{{url}}/models\"",
@@ -702,20 +717,19 @@
 	"Message rating should be enabled to use this feature": "La classificació dels missatges s'hauria d'activar per utilitzar aquesta funció",
 	"Messages you send after creating your link won't be shared. Users with the URL will be able to view the shared chat.": "Els missatges enviats després de crear el teu enllaç no es compartiran. Els usuaris amb l'URL podran veure el xat compartit.",
 	"Min P": "Min P",
-	"Minimum Score": "Puntuació mínima",
 	"Mirostat": "Mirostat",
 	"Mirostat Eta": "Eta de Mirostat",
 	"Mirostat Tau": "Tau de Mirostat",
-	"Mistral OCR": "",
-	"Mistral OCR API Key required.": "",
+	"Mistral OCR": "Mistral OCR",
+	"Mistral OCR API Key required.": "És necessària la clau API de Mistral OCR",
 	"Model": "Model",
 	"Model '{{modelName}}' has been successfully downloaded.": "El model '{{modelName}}' s'ha descarregat correctament.",
 	"Model '{{modelTag}}' is already in queue for downloading.": "El model '{{modelTag}}' ja està en cua per ser descarregat.",
 	"Model {{modelId}} not found": "No s'ha trobat el model {{modelId}}",
 	"Model {{modelName}} is not vision capable": "El model {{modelName}} no és capaç de visió",
 	"Model {{name}} is now {{status}}": "El model {{name}} ara és {{status}}",
-	"Model {{name}} is now hidden": "",
-	"Model {{name}} is now visible": "",
+	"Model {{name}} is now hidden": "El model {{name}} està ara amagat",
+	"Model {{name}} is now visible": "El model {{name}} està ara visible",
 	"Model accepts image inputs": "El model accepta entrades d'imatge",
 	"Model created successfully!": "Model creat correctament",
 	"Model filesystem path detected. Model shortname is required for update, cannot continue.": "S'ha detectat el camí del sistema de fitxers del model. És necessari un nom curt del model per actualitzar, no es pot continuar.",
@@ -731,7 +745,7 @@
 	"Models": "Models",
 	"Models Access": "Accés als models",
 	"Models configuration saved successfully": "La configuració dels models s'ha desat correctament",
-	"Models Public Sharing": "",
+	"Models Public Sharing": "Compartició pública de models",
 	"Mojeek Search API Key": "Clau API de Mojeek Search",
 	"more": "més",
 	"More": "Més",
@@ -794,7 +808,7 @@
 	"Open file": "Obrir arxiu",
 	"Open in full screen": "Obrir en pantalla complerta",
 	"Open new chat": "Obre un xat nou",
-	"Open WebUI can use tools provided by any OpenAPI server.": "",
+	"Open WebUI can use tools provided by any OpenAPI server.": "Open WebUI pot utilitzar eines de servidors OpenAPI.",
 	"Open WebUI uses faster-whisper internally.": "Open WebUI utilitza faster-whisper internament.",
 	"Open WebUI uses SpeechT5 and CMU Arctic speaker embeddings.": "Open WebUI utilitza incrustacions de SpeechT5 i CMU Arctic.",
 	"Open WebUI version (v{{OPEN_WEBUI_VERSION}}) is lower than required version (v{{REQUIRED_VERSION}})": "La versió d'Open WebUI (v{{OPEN_WEBUI_VERSION}}) és inferior a la versió requerida (v{{REQUIRED_VERSION}})",
@@ -804,7 +818,7 @@
 	"OpenAI API Key is required.": "Es requereix la clau API d'OpenAI.",
 	"OpenAI API settings updated": "Configuració de l'API d'OpenAI actualitzada",
 	"OpenAI URL/Key required.": "URL/Clau d'OpenAI requerides.",
-	"openapi.json Path": "",
+	"openapi.json Path": "openapi.json",
 	"or": "o",
 	"Organize your users": "Organitza els teus usuaris",
 	"Other": "Altres",
@@ -833,11 +847,13 @@
 	"Pipelines Valves": "Vàlvules de les Pipelines",
 	"Plain text (.txt)": "Text pla (.txt)",
 	"Playground": "Zona de jocs",
+	"Playwright Timeout (ms)": "",
+	"Playwright WebSocket URL": "",
 	"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.": "No tanquis la pàgina de configuració mentre carregues el model.",
 	"Please enter a prompt": "Si us plau, entra una indicació",
-	"Please enter a valid path": "",
-	"Please enter a valid URL": "",
+	"Please enter a valid path": "Si us plau, entra un camí vàlid",
+	"Please enter a valid URL": "Si us plau, entra una URL vàlida",
 	"Please fill in all fields.": "Emplena tots els camps, si us plau.",
 	"Please select a model first.": "Si us plau, selecciona un model primer",
 	"Please select a model.": "Si us plau, selecciona un model.",
@@ -853,14 +869,14 @@
 	"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 Autocompletion": "",
+	"Prompt Autocompletion": "Completar automàticament la indicació",
 	"Prompt Content": "Contingut de la indicació",
 	"Prompt created successfully": "Indicació creada correctament",
 	"Prompt suggestions": "Suggeriments d'indicacions",
 	"Prompt updated successfully": "Indicació actualitzada correctament",
 	"Prompts": "Indicacions",
 	"Prompts Access": "Accés a les indicacions",
-	"Prompts Public Sharing": "",
+	"Prompts Public Sharing": "Compartició pública de indicacions",
 	"Public": "Públic",
 	"Pull \"{{searchValue}}\" from Ollama.com": "Obtenir \"{{searchValue}}\" de Ollama.com",
 	"Pull a model from Ollama.com": "Obtenir un model d'Ollama.com",
@@ -878,8 +894,11 @@
 	"References from": "Referències de",
 	"Refused when it shouldn't have": "Refusat quan no hauria d'haver estat",
 	"Regenerate": "Regenerar",
+	"Reindex": "",
+	"Reindex Knowledge Base Vectors": "",
 	"Release Notes": "Notes de la versió",
 	"Relevance": "Rellevància",
+	"Relevance Threshold": "",
 	"Remove": "Eliminar",
 	"Remove Model": "Eliminar el model",
 	"Rename": "Canviar el nom",
@@ -993,11 +1012,11 @@
 	"Share": "Compartir",
 	"Share Chat": "Compartir el xat",
 	"Share to Open WebUI Community": "Compartir amb la comunitat OpenWebUI",
-	"Sharing Permissions": "",
+	"Sharing Permissions": "Compartir els permisos",
 	"Show": "Mostrar",
 	"Show \"What's New\" modal on login": "Veure 'Què hi ha de nou' a l'entrada",
 	"Show Admin Details in Account Pending Overlay": "Mostrar els detalls de l'administrador a la superposició del compte pendent",
-	"Show Model": "",
+	"Show Model": "Mostrar el model",
 	"Show shortcuts": "Mostrar dreceres",
 	"Show your support!": "Mostra el teu suport!",
 	"Showcased creativity": "Creativitat mostrada",
@@ -1009,6 +1028,8 @@
 	"Sign up to {{WEBUI_NAME}}": "Registrar-se a {{WEBUI_NAME}}",
 	"Signing in to {{WEBUI_NAME}}": "Iniciant sessió a {{WEBUI_NAME}}",
 	"sk-1234": "sk-1234",
+	"Sougou Search API sID": "",
+	"Sougou Search API SK": "",
 	"Source": "Font",
 	"Speech Playback Speed": "Velocitat de la parla",
 	"Speech recognition error: {{error}}": "Error de reconeixement de veu: {{error}}",
@@ -1036,6 +1057,7 @@
 	"Tap to interrupt": "Prem per interrompre",
 	"Tasks": "Tasques",
 	"Tavily API Key": "Clau API de Tavily",
+	"Tavily Extract Depth": "",
 	"Tell us more:": "Dona'ns més informació:",
 	"Temperature": "Temperatura",
 	"Template": "Plantilla",
@@ -1060,7 +1082,7 @@
 	"Thinking...": "Pensant...",
 	"This action cannot be undone. Do you wish to continue?": "Aquesta acció no es pot desfer. Vols continuar?",
 	"This channel was created on {{createdAt}}. This is the very beginning of the {{channelName}} channel.": "Aquest canal es va crear el dia {{createdAt}}. Aquest és el començament del canal {{channelName}}.",
-	"This chat won’t appear in history and your messages will not be saved.": "",
+	"This chat won’t appear in history and your messages will not be saved.": "Aquest xat no apareixerà a l'historial i els teus missatges no es desaran.",
 	"This ensures that your valuable conversations are securely saved to your backend database. Thank you!": "Això assegura que les teves converses valuoses queden desades de manera segura a la teva base de dades. Gràcies!",
 	"This is an experimental feature, it may not function as expected and is subject to change at any time.": "Aquesta és una funció experimental, és possible que no funcioni com s'espera i està subjecta a canvis en qualsevol moment.",
 	"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.": "Aquesta opció controla quants tokens es conserven en actualitzar el context. Per exemple, si s'estableix en 2, es conservaran els darrers 2 tokens del context de conversa. Preservar el context pot ajudar a mantenir la continuïtat d'una conversa, però pot reduir la capacitat de respondre a nous temes.",
@@ -1108,7 +1130,7 @@
 	"Tool ID": "ID de l'eina",
 	"Tool imported successfully": "Eina importada correctament",
 	"Tool Name": "Nom de l'eina",
-	"Tool Servers": "",
+	"Tool Servers": "Servidors d'eines",
 	"Tool updated successfully": "Eina actualitzada correctament",
 	"Tools": "Eines",
 	"Tools Access": "Accés a les eines",
@@ -1116,7 +1138,7 @@
 	"Tools Function Calling Prompt": "Indicació per a la crida de funcions",
 	"Tools have a function calling system that allows arbitrary code execution": "Les eines disposen d'un sistema de crida a funcions que permet execució de codi arbitrari",
 	"Tools have a function calling system that allows arbitrary code execution.": "Les eines disposen d'un sistema de crida a funcions que permet execució de codi arbitrari.",
-	"Tools Public Sharing": "",
+	"Tools Public Sharing": "Compartició pública d'eines",
 	"Top K": "Top K",
 	"Top K Reranker": "Top K Reranker",
 	"Top P": "Top P",
@@ -1158,12 +1180,14 @@
 	"Use Gravatar": "Utilitzar Gravatar",
 	"Use groups to group your users and assign permissions.": "Utilitza grups per agrupar els usuaris i assignar permisos.",
 	"Use Initials": "Utilitzar inicials",
+	"Use no proxy to fetch page contents.": "",
+	"Use proxy designated by http_proxy and https_proxy environment variables to fetch page contents.": "",
 	"use_mlock (Ollama)": "use_mlock (Ollama)",
 	"use_mmap (Ollama)": "use_mmap (Ollama)",
 	"user": "usuari",
 	"User": "Usuari",
 	"User location successfully retrieved.": "Ubicació de l'usuari obtinguda correctament",
-	"User Webhooks": "",
+	"User Webhooks": "Webhooks d'usuari",
 	"Username": "Nom d'usuari",
 	"Users": "Usuaris",
 	"Using the default arena model with all models. Click the plus button to add custom models.": "S'utilitza el model d'Arena predeterminat amb tots els models. Clica el botó més per afegir models personalitzats.",
@@ -1175,10 +1199,11 @@
 	"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ó",
+	"Verify SSL Certificate": "",
 	"Version": "Versió",
 	"Version {{selectedVersion}} of {{totalVersions}}": "Versió {{selectedVersion}} de {{totalVersions}}",
 	"View Replies": "Veure les respostes",
-	"View Result from **{{NAME}}**": "",
+	"View Result from **{{NAME}}**": "Veure el resultat de **{{NAME}}**",
 	"Visibility": "Visibilitat",
 	"Voice": "Veu",
 	"Voice Input": "Entrada de veu",
@@ -1189,6 +1214,7 @@
 	"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 Engine": "",
 	"Web Search": "Cerca la web",
 	"Web Search Engine": "Motor de cerca de la web",
 	"Web Search in Chat": "Cerca a internet al xat",
@@ -1196,7 +1222,7 @@
 	"Webhook URL": "URL del webhook",
 	"WebUI Settings": "Preferències de WebUI",
 	"WebUI URL": "URL de WebUI",
-	"WebUI will make requests to \"{{url}}\"": "",
+	"WebUI will make requests to \"{{url}}\"": "WebUI farà peticions a \"{{url}}\"",
 	"WebUI will make requests to \"{{url}}/api/chat\"": "WebUI farà peticions a \"{{url}}/api/chat\"",
 	"WebUI will make requests to \"{{url}}/chat/completions\"": "WebUI farà peticions a \"{{url}}/chat/completions\"",
 	"What are you trying to achieve?": "Què intentes aconseguir?",

+ 28 - 2
src/lib/i18n/locales/ceb-PH/translation.json

@@ -62,6 +62,7 @@
 	"Allow Chat Deletion": "Tugoti nga mapapas ang mga chat",
 	"Allow Chat Edit": "",
 	"Allow File Upload": "",
+	"Allow Multiple Models in Chat": "",
 	"Allow non-local voices": "",
 	"Allow Temporary Chat": "",
 	"Allow User Location": "",
@@ -79,6 +80,7 @@
 	"and": "Ug",
 	"and {{COUNT}} more": "",
 	"and create a new shared link.": "",
+	"Android": "",
 	"API Base URL": "API Base URL",
 	"API Key": "yawe sa API",
 	"API Key created.": "",
@@ -141,7 +143,6 @@
 	"Brave Search API Key": "",
 	"By {{name}}": "",
 	"Bypass Embedding and Retrieval": "",
-	"Bypass SSL verification for Websites": "",
 	"Calendar": "",
 	"Call": "",
 	"Call feature is not supported when using Web STT engine": "",
@@ -303,6 +304,7 @@
 	"Deleted User": "",
 	"Describe your knowledge base and objectives": "",
 	"Description": "Deskripsyon",
+	"Detect Artifacts Automatically": "",
 	"Didn't fully follow instructions": "",
 	"Direct": "",
 	"Direct Connections": "",
@@ -358,6 +360,7 @@
 	"e.g. my_filter": "",
 	"e.g. my_tools": "",
 	"e.g. Tools for performing various operations": "",
+	"e.g., en-US,ja-JP (leave blank for auto-detect)": "",
 	"Edit": "",
 	"Edit Arena Model": "",
 	"Edit Channel": "",
@@ -407,6 +410,8 @@
 	"Enter Document Intelligence Key": "",
 	"Enter domains separated by commas (e.g., example.com,site.org)": "",
 	"Enter Exa API Key": "",
+	"Enter Firecrawl API Base URL": "",
+	"Enter Firecrawl API Key": "",
 	"Enter Github Raw URL": "",
 	"Enter Google PSE API Key": "",
 	"Enter Google PSE Engine Id": "",
@@ -424,6 +429,8 @@
 	"Enter Mojeek Search API Key": "",
 	"Enter Number of Steps (e.g. 50)": "Pagsulod sa gidaghanon sa mga lakang (e.g. 50)",
 	"Enter Perplexity API Key": "",
+	"Enter Playwright Timeout": "",
+	"Enter Playwright WebSocket URL": "",
 	"Enter proxy URL (e.g. https://user:password@host:port)": "",
 	"Enter reasoning effort": "",
 	"Enter Sampler (e.g. Euler a)": "",
@@ -441,10 +448,13 @@
 	"Enter server host": "",
 	"Enter server label": "",
 	"Enter server port": "",
+	"Enter Sougou Search API sID": "",
+	"Enter Sougou Search API SK": "",
 	"Enter stop sequence": "Pagsulod sa katapusan nga han-ay",
 	"Enter system prompt": "",
 	"Enter system prompt here": "",
 	"Enter Tavily API Key": "",
+	"Enter Tavily Extract Depth": "",
 	"Enter the public URL of your WebUI. This URL will be used to generate links in the notifications.": "",
 	"Enter Tika Server URL": "",
 	"Enter timeout in seconds": "",
@@ -525,6 +535,8 @@
 	"Filter is now globally enabled": "",
 	"Filters": "",
 	"Fingerprint spoofing detected: Unable to use initials as avatar. Defaulting to default profile image.": "",
+	"Firecrawl API Base URL": "",
+	"Firecrawl API Key": "",
 	"Fluidly stream large external response chunks": "Hapsay nga paghatud sa daghang mga tipik sa eksternal nga mga tubag",
 	"Focus chat input": "Pag-focus sa entry sa diskusyon",
 	"Folder deleted successfully": "",
@@ -589,6 +601,8 @@
 	"Hybrid Search": "",
 	"I acknowledge that I have read and I understand the implications of my action. I am aware of the risks associated with executing arbitrary code and I have verified the trustworthiness of the source.": "",
 	"ID": "",
+	"iframe Sandbox Allow Forms": "",
+	"iframe Sandbox Allow Same Origin": "",
 	"Ignite curiosity": "",
 	"Image": "",
 	"Image Compression": "",
@@ -649,6 +663,7 @@
 	"Label": "",
 	"Landing Page Mode": "",
 	"Language": "Pinulongan",
+	"Language Locales": "",
 	"Last Active": "",
 	"Last Modified": "",
 	"Last reply": "",
@@ -702,7 +717,6 @@
 	"Message rating should be enabled to use this feature": "",
 	"Messages you send after creating your link won't be shared. Users with the URL will be able to view the shared chat.": "",
 	"Min P": "",
-	"Minimum Score": "",
 	"Mirostat": "Mirostat",
 	"Mirostat Eta": "Mirostat Eta",
 	"Mirostat Tau": "Mirostat Tau",
@@ -833,6 +847,8 @@
 	"Pipelines Valves": "",
 	"Plain text (.txt)": "",
 	"Playground": "Dulaanan",
+	"Playwright Timeout (ms)": "",
+	"Playwright WebSocket URL": "",
 	"Please carefully review the following warnings:": "",
 	"Please do not close the settings page while loading the model.": "",
 	"Please enter a prompt": "",
@@ -878,8 +894,11 @@
 	"References from": "",
 	"Refused when it shouldn't have": "",
 	"Regenerate": "",
+	"Reindex": "",
+	"Reindex Knowledge Base Vectors": "",
 	"Release Notes": "Release Notes",
 	"Relevance": "",
+	"Relevance Threshold": "",
 	"Remove": "",
 	"Remove Model": "",
 	"Rename": "",
@@ -1009,6 +1028,8 @@
 	"Sign up to {{WEBUI_NAME}}": "",
 	"Signing in to {{WEBUI_NAME}}": "",
 	"sk-1234": "",
+	"Sougou Search API sID": "",
+	"Sougou Search API SK": "",
 	"Source": "Tinubdan",
 	"Speech Playback Speed": "",
 	"Speech recognition error: {{error}}": "Sayop sa pag-ila sa tingog: {{error}}",
@@ -1036,6 +1057,7 @@
 	"Tap to interrupt": "",
 	"Tasks": "",
 	"Tavily API Key": "",
+	"Tavily Extract Depth": "",
 	"Tell us more:": "",
 	"Temperature": "Temperatura",
 	"Template": "Modelo",
@@ -1158,6 +1180,8 @@
 	"Use Gravatar": "Paggamit sa Gravatar",
 	"Use groups to group your users and assign permissions.": "",
 	"Use Initials": "",
+	"Use no proxy to fetch page contents.": "",
+	"Use proxy designated by http_proxy and https_proxy environment variables to fetch page contents.": "",
 	"use_mlock (Ollama)": "",
 	"use_mmap (Ollama)": "",
 	"user": "tiggamit",
@@ -1175,6 +1199,7 @@
 	"variable": "variable",
 	"variable to have them replaced with clipboard content.": "variable aron pulihan kini sa mga sulud sa clipboard.",
 	"Verify Connection": "",
+	"Verify SSL Certificate": "",
 	"Version": "Bersyon",
 	"Version {{selectedVersion}} of {{totalVersions}}": "",
 	"View Replies": "",
@@ -1189,6 +1214,7 @@
 	"Warning: Jupyter execution enables arbitrary code execution, posing severe security risks—proceed with extreme caution.": "",
 	"Web": "Web",
 	"Web API": "",
+	"Web Loader Engine": "",
 	"Web Search": "",
 	"Web Search Engine": "",
 	"Web Search in Chat": "",

+ 28 - 2
src/lib/i18n/locales/cs-CZ/translation.json

@@ -62,6 +62,7 @@
 	"Allow Chat Deletion": "Povolit odstranění chatu",
 	"Allow Chat Edit": "Povolit úpravu chatu",
 	"Allow File Upload": "Povolit nahrávat soubory",
+	"Allow Multiple Models in Chat": "",
 	"Allow non-local voices": "Povolit ne-místní hlasy",
 	"Allow Temporary Chat": "Povolit dočasný chat",
 	"Allow User Location": "Povolit uživatelskou polohu",
@@ -79,6 +80,7 @@
 	"and": "a",
 	"and {{COUNT}} more": "a {{COUNT}} další/ch",
 	"and create a new shared link.": "a vytvořit nový sdílený odkaz.",
+	"Android": "",
 	"API Base URL": "Základní URL adresa API",
 	"API Key": "Klíč API",
 	"API Key created.": "API klíč byl vytvořen.",
@@ -141,7 +143,6 @@
 	"Brave Search API Key": "Klíč API pro Brave Search",
 	"By {{name}}": "",
 	"Bypass Embedding and Retrieval": "",
-	"Bypass SSL verification for Websites": "Obcházení ověření SSL pro webové stránky",
 	"Calendar": "",
 	"Call": "Volání",
 	"Call feature is not supported when using Web STT engine": "Funkce pro volání není podporována při použití Web STT engine.",
@@ -303,6 +304,7 @@
 	"Deleted User": "",
 	"Describe your knowledge base and objectives": "",
 	"Description": "Popis",
+	"Detect Artifacts Automatically": "",
 	"Didn't fully follow instructions": "Nenásledovali jste přesně všechny instrukce.",
 	"Direct": "",
 	"Direct Connections": "",
@@ -358,6 +360,7 @@
 	"e.g. my_filter": "",
 	"e.g. my_tools": "",
 	"e.g. Tools for performing various operations": "",
+	"e.g., en-US,ja-JP (leave blank for auto-detect)": "",
 	"Edit": "Upravit",
 	"Edit Arena Model": "Upravit Arena Model",
 	"Edit Channel": "",
@@ -407,6 +410,8 @@
 	"Enter Document Intelligence Key": "",
 	"Enter domains separated by commas (e.g., example.com,site.org)": "",
 	"Enter Exa API Key": "",
+	"Enter Firecrawl API Base URL": "",
+	"Enter Firecrawl API Key": "",
 	"Enter Github Raw URL": "Zadejte URL adresu Github Raw",
 	"Enter Google PSE API Key": "Zadejte klíč rozhraní API Google PSE",
 	"Enter Google PSE Engine Id": "Zadejte ID vyhledávacího mechanismu Google PSE",
@@ -424,6 +429,8 @@
 	"Enter Mojeek Search API Key": "",
 	"Enter Number of Steps (e.g. 50)": "Zadejte počet kroků (např. 50)",
 	"Enter Perplexity API Key": "",
+	"Enter Playwright Timeout": "",
+	"Enter Playwright WebSocket URL": "",
 	"Enter proxy URL (e.g. https://user:password@host:port)": "",
 	"Enter reasoning effort": "",
 	"Enter Sampler (e.g. Euler a)": "Zadejte vzorkovač (např. Euler a)",
@@ -441,10 +448,13 @@
 	"Enter server host": "",
 	"Enter server label": "",
 	"Enter server port": "",
+	"Enter Sougou Search API sID": "",
+	"Enter Sougou Search API SK": "",
 	"Enter stop sequence": "Zadejte ukončovací sekvenci",
 	"Enter system prompt": "Vložte systémový prompt",
 	"Enter system prompt here": "",
 	"Enter Tavily API Key": "Zadejte API klíč Tavily",
+	"Enter Tavily Extract Depth": "",
 	"Enter the public URL of your WebUI. This URL will be used to generate links in the notifications.": "",
 	"Enter Tika Server URL": "Zadejte URL serveru Tika",
 	"Enter timeout in seconds": "",
@@ -525,6 +535,8 @@
 	"Filter is now globally enabled": "Filtr je nyní globálně povolen.",
 	"Filters": "Filtry",
 	"Fingerprint spoofing detected: Unable to use initials as avatar. Defaulting to default profile image.": "Detekováno padělání otisku prstu: Není možné použít iniciály jako avatar. Používá se výchozí profilový obrázek.",
+	"Firecrawl API Base URL": "",
+	"Firecrawl API Key": "",
 	"Fluidly stream large external response chunks": "Plynule streamujte velké externí části odpovědí",
 	"Focus chat input": "Zaměřte se na vstup chatu",
 	"Folder deleted successfully": "Složka byla úspěšně smazána",
@@ -589,6 +601,8 @@
 	"Hybrid Search": "Hybridní vyhledávání",
 	"I acknowledge that I have read and I understand the implications of my action. I am aware of the risks associated with executing arbitrary code and I have verified the trustworthiness of the source.": "Beru na vědomí, že jsem si přečetl a chápu důsledky svých činů. Jsem si vědom rizik spojených s vykonáváním libovolného kódu a ověřil jsem důvěryhodnost zdroje.",
 	"ID": "ID",
+	"iframe Sandbox Allow Forms": "",
+	"iframe Sandbox Allow Same Origin": "",
 	"Ignite curiosity": "",
 	"Image": "",
 	"Image Compression": "",
@@ -649,6 +663,7 @@
 	"Label": "",
 	"Landing Page Mode": "Režim vstupní stránky",
 	"Language": "Jazyk",
+	"Language Locales": "",
 	"Last Active": "Naposledy aktivní",
 	"Last Modified": "Poslední změna",
 	"Last reply": "",
@@ -702,7 +717,6 @@
 	"Message rating should be enabled to use this feature": "Hodnocení zpráv musí být povoleno, aby bylo možné tuto funkci používat.",
 	"Messages you send after creating your link won't be shared. Users with the URL will be able to view the shared chat.": "Zprávy, které odešlete po vytvoření odkazu, nebudou sdíleny. Uživatelé s URL budou moci zobrazit sdílený chat.",
 	"Min P": "Min P",
-	"Minimum Score": "Minimální skóre",
 	"Mirostat": "Mirostat",
 	"Mirostat Eta": "Mirostat Eta",
 	"Mirostat Tau": "Mirostat Tau",
@@ -833,6 +847,8 @@
 	"Pipelines Valves": "",
 	"Plain text (.txt)": "Čistý text (.txt)",
 	"Playground": "",
+	"Playwright Timeout (ms)": "",
+	"Playwright WebSocket URL": "",
 	"Please carefully review the following warnings:": "Prosím, pečlivě si přečtěte následující upozornění:",
 	"Please do not close the settings page while loading the model.": "",
 	"Please enter a prompt": "Prosím, zadejte zadání.",
@@ -878,8 +894,11 @@
 	"References from": "Reference z",
 	"Refused when it shouldn't have": "Odmítnuto, když nemělo být.",
 	"Regenerate": "Regenerovat",
+	"Reindex": "",
+	"Reindex Knowledge Base Vectors": "",
 	"Release Notes": "Záznamy o vydání",
 	"Relevance": "Relevance",
+	"Relevance Threshold": "",
 	"Remove": "Odebrat",
 	"Remove Model": "Odebrat model",
 	"Rename": "Přejmenovat",
@@ -1009,6 +1028,8 @@
 	"Sign up to {{WEBUI_NAME}}": "Zaregistrujte se na {{WEBUI_NAME}}",
 	"Signing in to {{WEBUI_NAME}}": "Přihlašování do {{WEBUI_NAME}}",
 	"sk-1234": "",
+	"Sougou Search API sID": "",
+	"Sougou Search API SK": "",
 	"Source": "Zdroj",
 	"Speech Playback Speed": "Rychlost přehrávání řeči",
 	"Speech recognition error: {{error}}": "Chyba rozpoznávání řeči: {{error}}",
@@ -1036,6 +1057,7 @@
 	"Tap to interrupt": "Klepněte pro přerušení",
 	"Tasks": "",
 	"Tavily API Key": "Klíč API pro Tavily",
+	"Tavily Extract Depth": "",
 	"Tell us more:": "Řekněte nám více.",
 	"Temperature": "",
 	"Template": "Šablona",
@@ -1158,6 +1180,8 @@
 	"Use Gravatar": "Použití Gravatar",
 	"Use groups to group your users and assign permissions.": "",
 	"Use Initials": "Použijte iniciály",
+	"Use no proxy to fetch page contents.": "",
+	"Use proxy designated by http_proxy and https_proxy environment variables to fetch page contents.": "",
 	"use_mlock (Ollama)": "use_mlock (Ollama)",
 	"use_mmap (Ollama)": "use_mmap (Ollama)",
 	"user": "uživatel",
@@ -1175,6 +1199,7 @@
 	"variable": "proměnná",
 	"variable to have them replaced with clipboard content.": "proměnnou, aby byl jejich obsah nahrazen obsahem schránky.",
 	"Verify Connection": "",
+	"Verify SSL Certificate": "",
 	"Version": "Verze",
 	"Version {{selectedVersion}} of {{totalVersions}}": "Verze {{selectedVersion}} z {{totalVersions}}",
 	"View Replies": "",
@@ -1189,6 +1214,7 @@
 	"Warning: Jupyter execution enables arbitrary code execution, posing severe security risks—proceed with extreme caution.": "",
 	"Web": "Web",
 	"Web API": "Webové API",
+	"Web Loader Engine": "",
 	"Web Search": "Vyhledávání na webu",
 	"Web Search Engine": "Webový vyhledávač",
 	"Web Search in Chat": "",

+ 28 - 2
src/lib/i18n/locales/da-DK/translation.json

@@ -62,6 +62,7 @@
 	"Allow Chat Deletion": "Tillad sletning af chats",
 	"Allow Chat Edit": "",
 	"Allow File Upload": "",
+	"Allow Multiple Models in Chat": "",
 	"Allow non-local voices": "Tillad ikke-lokale stemmer",
 	"Allow Temporary Chat": "Tillad midlertidig chat",
 	"Allow User Location": "Tillad bruger-lokation",
@@ -79,6 +80,7 @@
 	"and": "og",
 	"and {{COUNT}} more": "",
 	"and create a new shared link.": "og lav et nyt link til deling",
+	"Android": "",
 	"API Base URL": "API Base URL",
 	"API Key": "API nøgle",
 	"API Key created.": "API nøgle lavet",
@@ -141,7 +143,6 @@
 	"Brave Search API Key": "Brave Search API nøgle",
 	"By {{name}}": "",
 	"Bypass Embedding and Retrieval": "",
-	"Bypass SSL verification for Websites": "Forbigå SSL verifikation på websider",
 	"Calendar": "",
 	"Call": "Opkald",
 	"Call feature is not supported when using Web STT engine": "Opkaldsfunktion er ikke understøttet for Web STT engine",
@@ -303,6 +304,7 @@
 	"Deleted User": "",
 	"Describe your knowledge base and objectives": "",
 	"Description": "Beskrivelse",
+	"Detect Artifacts Automatically": "",
 	"Didn't fully follow instructions": "Fulgte ikke instruktioner",
 	"Direct": "",
 	"Direct Connections": "",
@@ -358,6 +360,7 @@
 	"e.g. my_filter": "",
 	"e.g. my_tools": "",
 	"e.g. Tools for performing various operations": "",
+	"e.g., en-US,ja-JP (leave blank for auto-detect)": "",
 	"Edit": "Rediger",
 	"Edit Arena Model": "",
 	"Edit Channel": "",
@@ -407,6 +410,8 @@
 	"Enter Document Intelligence Key": "",
 	"Enter domains separated by commas (e.g., example.com,site.org)": "",
 	"Enter Exa API Key": "",
+	"Enter Firecrawl API Base URL": "",
+	"Enter Firecrawl API Key": "",
 	"Enter Github Raw URL": "Indtast Github Raw URL",
 	"Enter Google PSE API Key": "Indtast Google PSE API-nøgle",
 	"Enter Google PSE Engine Id": "Indtast Google PSE Engine ID",
@@ -424,6 +429,8 @@
 	"Enter Mojeek Search API Key": "",
 	"Enter Number of Steps (e.g. 50)": "Indtast antal trin (f.eks. 50)",
 	"Enter Perplexity API Key": "",
+	"Enter Playwright Timeout": "",
+	"Enter Playwright WebSocket URL": "",
 	"Enter proxy URL (e.g. https://user:password@host:port)": "",
 	"Enter reasoning effort": "",
 	"Enter Sampler (e.g. Euler a)": "Indtast sampler (f.eks. Euler a)",
@@ -441,10 +448,13 @@
 	"Enter server host": "",
 	"Enter server label": "",
 	"Enter server port": "",
+	"Enter Sougou Search API sID": "",
+	"Enter Sougou Search API SK": "",
 	"Enter stop sequence": "Indtast stopsekvens",
 	"Enter system prompt": "Indtast systemprompt",
 	"Enter system prompt here": "",
 	"Enter Tavily API Key": "Indtast Tavily API-nøgle",
+	"Enter Tavily Extract Depth": "",
 	"Enter the public URL of your WebUI. This URL will be used to generate links in the notifications.": "",
 	"Enter Tika Server URL": "Indtast Tika Server URL",
 	"Enter timeout in seconds": "",
@@ -525,6 +535,8 @@
 	"Filter is now globally enabled": "Filter er nu globalt aktiveret",
 	"Filters": "Filtre",
 	"Fingerprint spoofing detected: Unable to use initials as avatar. Defaulting to default profile image.": "Fingeraftryksspoofing registreret: Kan ikke bruge initialer som avatar. Bruger standard profilbillede.",
+	"Firecrawl API Base URL": "",
+	"Firecrawl API Key": "",
 	"Fluidly stream large external response chunks": "Stream store eksterne svar chunks flydende",
 	"Focus chat input": "Fokuser på chatinput",
 	"Folder deleted successfully": "",
@@ -589,6 +601,8 @@
 	"Hybrid Search": "Hybrid søgning",
 	"I acknowledge that I have read and I understand the implications of my action. I am aware of the risks associated with executing arbitrary code and I have verified the trustworthiness of the source.": "Jeg anerkender, at jeg har læst og forstået konsekvenserne af min handling. Jeg er opmærksom på de risici, der er forbundet med at udføre vilkårlig kode, og jeg har verificeret kildens troværdighed.",
 	"ID": "",
+	"iframe Sandbox Allow Forms": "",
+	"iframe Sandbox Allow Same Origin": "",
 	"Ignite curiosity": "",
 	"Image": "",
 	"Image Compression": "",
@@ -649,6 +663,7 @@
 	"Label": "",
 	"Landing Page Mode": "Landing Page-tilstand",
 	"Language": "Sprog",
+	"Language Locales": "",
 	"Last Active": "Sidst aktiv",
 	"Last Modified": "Sidst ændret",
 	"Last reply": "",
@@ -702,7 +717,6 @@
 	"Message rating should be enabled to use this feature": "",
 	"Messages you send after creating your link won't be shared. Users with the URL will be able to view the shared chat.": "Beskeder, du sender efter at have oprettet dit link, deles ikke. Brugere med URL'en vil kunne se den delte chat.",
 	"Min P": "Min P",
-	"Minimum Score": "Minimumscore",
 	"Mirostat": "Mirostat",
 	"Mirostat Eta": "Mirostat Eta",
 	"Mirostat Tau": "Mirostat Tau",
@@ -833,6 +847,8 @@
 	"Pipelines Valves": "Pipelines-ventiler",
 	"Plain text (.txt)": "Almindelig tekst (.txt)",
 	"Playground": "Legeplads",
+	"Playwright Timeout (ms)": "",
+	"Playwright WebSocket URL": "",
 	"Please carefully review the following warnings:": "Gennemgå omhyggeligt følgende advarsler:",
 	"Please do not close the settings page while loading the model.": "",
 	"Please enter a prompt": "",
@@ -878,8 +894,11 @@
 	"References from": "",
 	"Refused when it shouldn't have": "Afvist, når den ikke burde have været det",
 	"Regenerate": "Regenerer",
+	"Reindex": "",
+	"Reindex Knowledge Base Vectors": "",
 	"Release Notes": "Udgivelsesnoter",
 	"Relevance": "",
+	"Relevance Threshold": "",
 	"Remove": "Fjern",
 	"Remove Model": "Fjern model",
 	"Rename": "Omdøb",
@@ -1009,6 +1028,8 @@
 	"Sign up to {{WEBUI_NAME}}": "Tilmeld dig {{WEBUI_NAME}}",
 	"Signing in to {{WEBUI_NAME}}": "Logger ind på {{WEBUI_NAME}}",
 	"sk-1234": "",
+	"Sougou Search API sID": "",
+	"Sougou Search API SK": "",
 	"Source": "Kilde",
 	"Speech Playback Speed": "Talehastighed",
 	"Speech recognition error: {{error}}": "Talegenkendelsesfejl: {{error}}",
@@ -1036,6 +1057,7 @@
 	"Tap to interrupt": "Tryk for at afbryde",
 	"Tasks": "",
 	"Tavily API Key": "Tavily API-nøgle",
+	"Tavily Extract Depth": "",
 	"Tell us more:": "Fortæl os mere:",
 	"Temperature": "Temperatur",
 	"Template": "Skabelon",
@@ -1158,6 +1180,8 @@
 	"Use Gravatar": "Brug Gravatar",
 	"Use groups to group your users and assign permissions.": "",
 	"Use Initials": "Brug initialer",
+	"Use no proxy to fetch page contents.": "",
+	"Use proxy designated by http_proxy and https_proxy environment variables to fetch page contents.": "",
 	"use_mlock (Ollama)": "use_mlock (Ollama)",
 	"use_mmap (Ollama)": "use_mmap (Ollama)",
 	"user": "bruger",
@@ -1175,6 +1199,7 @@
 	"variable": "variabel",
 	"variable to have them replaced with clipboard content.": "variabel for at få dem erstattet med indholdet af udklipsholderen.",
 	"Verify Connection": "",
+	"Verify SSL Certificate": "",
 	"Version": "Version",
 	"Version {{selectedVersion}} of {{totalVersions}}": "Version {{selectedVersion}} af {{totalVersions}}",
 	"View Replies": "",
@@ -1189,6 +1214,7 @@
 	"Warning: Jupyter execution enables arbitrary code execution, posing severe security risks—proceed with extreme caution.": "",
 	"Web": "Web",
 	"Web API": "Web API",
+	"Web Loader Engine": "",
 	"Web Search": "Websøgning",
 	"Web Search Engine": "Websøgemaskine",
 	"Web Search in Chat": "",

+ 28 - 2
src/lib/i18n/locales/de-DE/translation.json

@@ -62,6 +62,7 @@
 	"Allow Chat Deletion": "Löschen von Chats erlauben",
 	"Allow Chat Edit": "Bearbeiten von Chats erlauben",
 	"Allow File Upload": "Hochladen von Dateien erlauben",
+	"Allow Multiple Models in Chat": "",
 	"Allow non-local voices": "Nicht-lokale Stimmen erlauben",
 	"Allow Temporary Chat": "Temporäre Chats erlauben",
 	"Allow User Location": "Standort freigeben",
@@ -79,6 +80,7 @@
 	"and": "und",
 	"and {{COUNT}} more": "und {{COUNT}} mehr",
 	"and create a new shared link.": "und erstellen Sie einen neuen freigegebenen Link.",
+	"Android": "",
 	"API Base URL": "API-Basis-URL",
 	"API Key": "API-Schlüssel",
 	"API Key created.": "API-Schlüssel erstellt.",
@@ -141,7 +143,6 @@
 	"Brave Search API Key": "Brave Search API-Schlüssel",
 	"By {{name}}": "Von {{name}}",
 	"Bypass Embedding and Retrieval": "Embedding und Retrieval umgehen",
-	"Bypass SSL verification for Websites": "SSL-Überprüfung für Webseiten umgehen",
 	"Calendar": "Kalender",
 	"Call": "Anrufen",
 	"Call feature is not supported when using Web STT engine": "Die Anruffunktion wird nicht unterstützt, wenn die Web-STT-Engine verwendet wird.",
@@ -303,6 +304,7 @@
 	"Deleted User": "Benutzer gelöscht",
 	"Describe your knowledge base and objectives": "Beschreibe deinen Wissensspeicher und deine Ziele",
 	"Description": "Beschreibung",
+	"Detect Artifacts Automatically": "",
 	"Didn't fully follow instructions": "Nicht genau den Answeisungen gefolgt",
 	"Direct": "Direkt",
 	"Direct Connections": "Direktverbindungen",
@@ -358,6 +360,7 @@
 	"e.g. my_filter": "z. B. mein_filter",
 	"e.g. my_tools": "z. B. meine_werkzeuge",
 	"e.g. Tools for performing various operations": "z. B. Werkzeuge für verschiedene Operationen",
+	"e.g., en-US,ja-JP (leave blank for auto-detect)": "",
 	"Edit": "Bearbeiten",
 	"Edit Arena Model": "Arena-Modell bearbeiten",
 	"Edit Channel": "Kanal bearbeiten",
@@ -407,6 +410,8 @@
 	"Enter Document Intelligence Key": "",
 	"Enter domains separated by commas (e.g., example.com,site.org)": "Geben Sie die Domains durch Kommas separiert ein (z.B. example.com,site.org)",
 	"Enter Exa API Key": "Geben Sie den Exa-API-Schlüssel ein",
+	"Enter Firecrawl API Base URL": "",
+	"Enter Firecrawl API Key": "",
 	"Enter Github Raw URL": "Geben Sie die Github Raw-URL ein",
 	"Enter Google PSE API Key": "Geben Sie den Google PSE-API-Schlüssel ein",
 	"Enter Google PSE Engine Id": "Geben Sie die Google PSE-Engine-ID ein",
@@ -424,6 +429,8 @@
 	"Enter Mojeek Search API Key": "Geben Sie den Mojeek Search API-Schlüssel ein",
 	"Enter Number of Steps (e.g. 50)": "Geben Sie die Anzahl an Schritten ein (z. B. 50)",
 	"Enter Perplexity API Key": "Geben Sie den Perplexity API-Key ein",
+	"Enter Playwright Timeout": "",
+	"Enter Playwright WebSocket URL": "",
 	"Enter proxy URL (e.g. https://user:password@host:port)": "Geben sie die Proxy-URL ein (z. B. https://user:password@host:port)",
 	"Enter reasoning effort": "Geben Sie den Schlussfolgerungsaufwand ein",
 	"Enter Sampler (e.g. Euler a)": "Geben Sie den Sampler ein (z. B. Euler a)",
@@ -441,10 +448,13 @@
 	"Enter server host": "Geben Sie den Server-Host ein",
 	"Enter server label": "Geben Sie das Server-Label ein",
 	"Enter server port": "Geben Sie den Server-Port ein",
+	"Enter Sougou Search API sID": "",
+	"Enter Sougou Search API SK": "",
 	"Enter stop sequence": "Stop-Sequenz eingeben",
 	"Enter system prompt": "Systemprompt eingeben",
 	"Enter system prompt here": "",
 	"Enter Tavily API Key": "Geben Sie den Tavily-API-Schlüssel ein",
+	"Enter Tavily Extract Depth": "",
 	"Enter the public URL of your WebUI. This URL will be used to generate links in the notifications.": "Geben sie die öffentliche URL Ihrer WebUI ein. Diese URL wird verwendet, um Links in den Benachrichtigungen zu generieren.",
 	"Enter Tika Server URL": "Geben Sie die Tika-Server-URL ein",
 	"Enter timeout in seconds": "Geben Sie den Timeout in Sekunden ein",
@@ -525,6 +535,8 @@
 	"Filter is now globally enabled": "Filter ist jetzt global aktiviert",
 	"Filters": "Filter",
 	"Fingerprint spoofing detected: Unable to use initials as avatar. Defaulting to default profile image.": "Fingerabdruck-Spoofing erkannt: Initialen können nicht als Avatar verwendet werden. Standard-Avatar wird verwendet.",
+	"Firecrawl API Base URL": "",
+	"Firecrawl API Key": "",
 	"Fluidly stream large external response chunks": "Nahtlose Übertragung großer externer Antwortabschnitte",
 	"Focus chat input": "Chat-Eingabe fokussieren",
 	"Folder deleted successfully": "Ordner erfolgreich gelöscht",
@@ -589,6 +601,8 @@
 	"Hybrid Search": "Hybride Suche",
 	"I acknowledge that I have read and I understand the implications of my action. I am aware of the risks associated with executing arbitrary code and I have verified the trustworthiness of the source.": "Ich bestätige, dass ich gelesen habe und die Auswirkungen meiner Aktion verstehe. Mir sind die Risiken bewusst, die mit der Ausführung beliebigen Codes verbunden sind, und ich habe die Vertrauenswürdigkeit der Quelle überprüft.",
 	"ID": "ID",
+	"iframe Sandbox Allow Forms": "",
+	"iframe Sandbox Allow Same Origin": "",
 	"Ignite curiosity": "Neugier entfachen",
 	"Image": "Bild",
 	"Image Compression": "Bildkomprimierung",
@@ -649,6 +663,7 @@
 	"Label": "Label",
 	"Landing Page Mode": "Startseitenmodus",
 	"Language": "Sprache",
+	"Language Locales": "",
 	"Last Active": "Zuletzt aktiv",
 	"Last Modified": "Zuletzt bearbeitet",
 	"Last reply": "Letzte Antwort",
@@ -702,7 +717,6 @@
 	"Message rating should be enabled to use this feature": "Antwortbewertung muss aktiviert sein, um diese Funktion zu verwenden",
 	"Messages you send after creating your link won't be shared. Users with the URL will be able to view the shared chat.": "Nachrichten, die Sie nach der Erstellung Ihres Links senden, werden nicht geteilt. Nutzer mit der URL können den freigegebenen Chat einsehen.",
 	"Min P": "Min P",
-	"Minimum Score": "Mindestpunktzahl",
 	"Mirostat": "Mirostat",
 	"Mirostat Eta": "Mirostat Eta",
 	"Mirostat Tau": "Mirostat Tau",
@@ -833,6 +847,8 @@
 	"Pipelines Valves": "Pipeline Valves",
 	"Plain text (.txt)": "Nur Text (.txt)",
 	"Playground": "Testumgebung",
+	"Playwright Timeout (ms)": "",
+	"Playwright WebSocket URL": "",
 	"Please carefully review the following warnings:": "Bitte überprüfen Sie die folgenden Warnungen sorgfältig:",
 	"Please do not close the settings page while loading the model.": "Bitte schließen die Einstellungen-Seite nicht, während das Modell lädt.",
 	"Please enter a prompt": "Bitte geben Sie einen Prompt ein",
@@ -878,8 +894,11 @@
 	"References from": "Referenzen aus",
 	"Refused when it shouldn't have": "Abgelehnt, obwohl es nicht hätte abgelehnt werden sollen",
 	"Regenerate": "Neu generieren",
+	"Reindex": "",
+	"Reindex Knowledge Base Vectors": "",
 	"Release Notes": "Veröffentlichungshinweise",
 	"Relevance": "Relevanz",
+	"Relevance Threshold": "",
 	"Remove": "Entfernen",
 	"Remove Model": "Modell entfernen",
 	"Rename": "Umbenennen",
@@ -1009,6 +1028,8 @@
 	"Sign up to {{WEBUI_NAME}}": "Bei {{WEBUI_NAME}} registrieren",
 	"Signing in to {{WEBUI_NAME}}": "Wird bei {{WEBUI_NAME}} angemeldet",
 	"sk-1234": "sk-1234",
+	"Sougou Search API sID": "",
+	"Sougou Search API SK": "",
 	"Source": "Quelle",
 	"Speech Playback Speed": "Sprachwiedergabegeschwindigkeit",
 	"Speech recognition error: {{error}}": "Spracherkennungsfehler: {{error}}",
@@ -1036,6 +1057,7 @@
 	"Tap to interrupt": "Zum Unterbrechen tippen",
 	"Tasks": "Aufgaben",
 	"Tavily API Key": "Tavily-API-Schlüssel",
+	"Tavily Extract Depth": "",
 	"Tell us more:": "Erzähl uns mehr",
 	"Temperature": "Temperatur",
 	"Template": "Vorlage",
@@ -1158,6 +1180,8 @@
 	"Use Gravatar": "Gravatar verwenden",
 	"Use groups to group your users and assign permissions.": "Nutzen Sie Gruppen, um Ihre Benutzer zu gruppieren und Berechtigungen zuzuweisen.",
 	"Use Initials": "Initialen verwenden",
+	"Use no proxy to fetch page contents.": "",
+	"Use proxy designated by http_proxy and https_proxy environment variables to fetch page contents.": "",
 	"use_mlock (Ollama)": "use_mlock (Ollama)",
 	"use_mmap (Ollama)": "use_mmap (Ollama)",
 	"user": "Benutzer",
@@ -1175,6 +1199,7 @@
 	"variable": "Variable",
 	"variable to have them replaced with clipboard content.": "Variable, um den Inhalt der Zwischenablage beim Nutzen des Prompts zu ersetzen.",
 	"Verify Connection": "Verbindung verifizieren",
+	"Verify SSL Certificate": "",
 	"Version": "Version",
 	"Version {{selectedVersion}} of {{totalVersions}}": "Version {{selectedVersion}} von {{totalVersions}}",
 	"View Replies": "Antworten anzeigen",
@@ -1189,6 +1214,7 @@
 	"Warning: Jupyter execution enables arbitrary code execution, posing severe security risks—proceed with extreme caution.": "",
 	"Web": "Web",
 	"Web API": "Web-API",
+	"Web Loader Engine": "",
 	"Web Search": "Websuche",
 	"Web Search Engine": "Suchmaschine",
 	"Web Search in Chat": "Websuche im Chat",

+ 28 - 2
src/lib/i18n/locales/dg-DG/translation.json

@@ -62,6 +62,7 @@
 	"Allow Chat Deletion": "Allow Delete Chats",
 	"Allow Chat Edit": "",
 	"Allow File Upload": "",
+	"Allow Multiple Models in Chat": "",
 	"Allow non-local voices": "",
 	"Allow Temporary Chat": "",
 	"Allow User Location": "",
@@ -79,6 +80,7 @@
 	"and": "and",
 	"and {{COUNT}} more": "",
 	"and create a new shared link.": "",
+	"Android": "",
 	"API Base URL": "API Base URL",
 	"API Key": "API Key",
 	"API Key created.": "",
@@ -141,7 +143,6 @@
 	"Brave Search API Key": "",
 	"By {{name}}": "",
 	"Bypass Embedding and Retrieval": "",
-	"Bypass SSL verification for Websites": "",
 	"Calendar": "",
 	"Call": "",
 	"Call feature is not supported when using Web STT engine": "",
@@ -303,6 +304,7 @@
 	"Deleted User": "",
 	"Describe your knowledge base and objectives": "",
 	"Description": "Description",
+	"Detect Artifacts Automatically": "",
 	"Didn't fully follow instructions": "",
 	"Direct": "",
 	"Direct Connections": "",
@@ -358,6 +360,7 @@
 	"e.g. my_filter": "",
 	"e.g. my_tools": "",
 	"e.g. Tools for performing various operations": "",
+	"e.g., en-US,ja-JP (leave blank for auto-detect)": "",
 	"Edit": "",
 	"Edit Arena Model": "",
 	"Edit Channel": "",
@@ -407,6 +410,8 @@
 	"Enter Document Intelligence Key": "",
 	"Enter domains separated by commas (e.g., example.com,site.org)": "",
 	"Enter Exa API Key": "",
+	"Enter Firecrawl API Base URL": "",
+	"Enter Firecrawl API Key": "",
 	"Enter Github Raw URL": "",
 	"Enter Google PSE API Key": "",
 	"Enter Google PSE Engine Id": "",
@@ -424,6 +429,8 @@
 	"Enter Mojeek Search API Key": "",
 	"Enter Number of Steps (e.g. 50)": "Enter Number of Steps (e.g. 50)",
 	"Enter Perplexity API Key": "",
+	"Enter Playwright Timeout": "",
+	"Enter Playwright WebSocket URL": "",
 	"Enter proxy URL (e.g. https://user:password@host:port)": "",
 	"Enter reasoning effort": "",
 	"Enter Sampler (e.g. Euler a)": "",
@@ -441,10 +448,13 @@
 	"Enter server host": "",
 	"Enter server label": "",
 	"Enter server port": "",
+	"Enter Sougou Search API sID": "",
+	"Enter Sougou Search API SK": "",
 	"Enter stop sequence": "Enter stop bark",
 	"Enter system prompt": "",
 	"Enter system prompt here": "",
 	"Enter Tavily API Key": "",
+	"Enter Tavily Extract Depth": "",
 	"Enter the public URL of your WebUI. This URL will be used to generate links in the notifications.": "",
 	"Enter Tika Server URL": "",
 	"Enter timeout in seconds": "",
@@ -525,6 +535,8 @@
 	"Filter is now globally enabled": "",
 	"Filters": "",
 	"Fingerprint spoofing detected: Unable to use initials as avatar. Defaulting to default profile image.": "Fingerprint dogeing: Unable to use initials as avatar. Defaulting to default doge image.",
+	"Firecrawl API Base URL": "",
+	"Firecrawl API Key": "",
 	"Fluidly stream large external response chunks": "Fluidly wow big chunks",
 	"Focus chat input": "Focus chat bork",
 	"Folder deleted successfully": "",
@@ -589,6 +601,8 @@
 	"Hybrid Search": "",
 	"I acknowledge that I have read and I understand the implications of my action. I am aware of the risks associated with executing arbitrary code and I have verified the trustworthiness of the source.": "",
 	"ID": "",
+	"iframe Sandbox Allow Forms": "",
+	"iframe Sandbox Allow Same Origin": "",
 	"Ignite curiosity": "",
 	"Image": "",
 	"Image Compression": "",
@@ -649,6 +663,7 @@
 	"Label": "",
 	"Landing Page Mode": "",
 	"Language": "Doge Speak",
+	"Language Locales": "",
 	"Last Active": "",
 	"Last Modified": "",
 	"Last reply": "",
@@ -702,7 +717,6 @@
 	"Message rating should be enabled to use this feature": "",
 	"Messages you send after creating your link won't be shared. Users with the URL will be able to view the shared chat.": "",
 	"Min P": "",
-	"Minimum Score": "",
 	"Mirostat": "Mirostat",
 	"Mirostat Eta": "Mirostat Eta",
 	"Mirostat Tau": "Mirostat Tau",
@@ -833,6 +847,8 @@
 	"Pipelines Valves": "",
 	"Plain text (.txt)": "Plain text (.txt)",
 	"Playground": "Playground",
+	"Playwright Timeout (ms)": "",
+	"Playwright WebSocket URL": "",
 	"Please carefully review the following warnings:": "",
 	"Please do not close the settings page while loading the model.": "",
 	"Please enter a prompt": "",
@@ -878,8 +894,11 @@
 	"References from": "",
 	"Refused when it shouldn't have": "",
 	"Regenerate": "",
+	"Reindex": "",
+	"Reindex Knowledge Base Vectors": "",
 	"Release Notes": "Release Borks",
 	"Relevance": "",
+	"Relevance Threshold": "",
 	"Remove": "",
 	"Remove Model": "",
 	"Rename": "",
@@ -1009,6 +1028,8 @@
 	"Sign up to {{WEBUI_NAME}}": "",
 	"Signing in to {{WEBUI_NAME}}": "",
 	"sk-1234": "",
+	"Sougou Search API sID": "",
+	"Sougou Search API SK": "",
 	"Source": "Source",
 	"Speech Playback Speed": "",
 	"Speech recognition error: {{error}}": "Speech recognition error: {{error}} so error",
@@ -1036,6 +1057,7 @@
 	"Tap to interrupt": "",
 	"Tasks": "",
 	"Tavily API Key": "",
+	"Tavily Extract Depth": "",
 	"Tell us more:": "",
 	"Temperature": "Temperature very temp",
 	"Template": "Template much template",
@@ -1158,6 +1180,8 @@
 	"Use Gravatar": "Use Gravatar much avatar",
 	"Use groups to group your users and assign permissions.": "",
 	"Use Initials": "Use Initials much initial",
+	"Use no proxy to fetch page contents.": "",
+	"Use proxy designated by http_proxy and https_proxy environment variables to fetch page contents.": "",
 	"use_mlock (Ollama)": "",
 	"use_mmap (Ollama)": "",
 	"user": "user much user",
@@ -1175,6 +1199,7 @@
 	"variable": "variable very variable",
 	"variable to have them replaced with clipboard content.": "variable to have them replaced with clipboard content. Very replace.",
 	"Verify Connection": "",
+	"Verify SSL Certificate": "",
 	"Version": "Version much version",
 	"Version {{selectedVersion}} of {{totalVersions}}": "",
 	"View Replies": "",
@@ -1189,6 +1214,7 @@
 	"Warning: Jupyter execution enables arbitrary code execution, posing severe security risks—proceed with extreme caution.": "",
 	"Web": "Web very web",
 	"Web API": "",
+	"Web Loader Engine": "",
 	"Web Search": "",
 	"Web Search Engine": "",
 	"Web Search in Chat": "",

+ 28 - 2
src/lib/i18n/locales/el-GR/translation.json

@@ -62,6 +62,7 @@
 	"Allow Chat Deletion": "Επιτρέπεται η Διαγραφή Συνομιλίας",
 	"Allow Chat Edit": "Επιτρέπεται η Επεξεργασία Συνομιλίας",
 	"Allow File Upload": "Επιτρέπεται η Αποστολή Αρχείων",
+	"Allow Multiple Models in Chat": "",
 	"Allow non-local voices": "Επιτρέπονται μη τοπικές φωνές",
 	"Allow Temporary Chat": "Επιτρέπεται η Προσωρινή Συνομιλία",
 	"Allow User Location": "Επιτρέπεται η Τοποθεσία Χρήστη",
@@ -79,6 +80,7 @@
 	"and": "και",
 	"and {{COUNT}} more": "και {{COUNT}} ακόμα",
 	"and create a new shared link.": "και δημιουργήστε έναν νέο κοινόχρηστο σύνδεσμο.",
+	"Android": "",
 	"API Base URL": "API Βασικό URL",
 	"API Key": "Κλειδί API",
 	"API Key created.": "Το κλειδί API δημιουργήθηκε.",
@@ -141,7 +143,6 @@
 	"Brave Search API Key": "Κλειδί API Brave Search",
 	"By {{name}}": "Από {{name}}",
 	"Bypass Embedding and Retrieval": "",
-	"Bypass SSL verification for Websites": "Παράκαμψη επαλήθευσης SSL για Ιστότοπους",
 	"Calendar": "",
 	"Call": "Κλήση",
 	"Call feature is not supported when using Web STT engine": "Η λειτουργία κλήσης δεν υποστηρίζεται όταν χρησιμοποιείται η μηχανή Web STT",
@@ -303,6 +304,7 @@
 	"Deleted User": "Διαγράφηκε ο Χρήστης",
 	"Describe your knowledge base and objectives": "Περιγράψτε τη βάση γνώσης και τους στόχους σας",
 	"Description": "Περιγραφή",
+	"Detect Artifacts Automatically": "",
 	"Didn't fully follow instructions": "Δεν ακολούθησε πλήρως τις οδηγίες",
 	"Direct": "",
 	"Direct Connections": "",
@@ -358,6 +360,7 @@
 	"e.g. my_filter": "π.χ. my_filter",
 	"e.g. my_tools": "π.χ. my_tools",
 	"e.g. Tools for performing various operations": "π.χ. Εργαλεία για την εκτέλεση διάφορων λειτουργιών",
+	"e.g., en-US,ja-JP (leave blank for auto-detect)": "",
 	"Edit": "Επεξεργασία",
 	"Edit Arena Model": "Επεξεργασία Μοντέλου Arena",
 	"Edit Channel": "",
@@ -407,6 +410,8 @@
 	"Enter Document Intelligence Key": "",
 	"Enter domains separated by commas (e.g., example.com,site.org)": "",
 	"Enter Exa API Key": "",
+	"Enter Firecrawl API Base URL": "",
+	"Enter Firecrawl API Key": "",
 	"Enter Github Raw URL": "Εισάγετε το Github Raw URL",
 	"Enter Google PSE API Key": "Εισάγετε το Κλειδί API Google PSE",
 	"Enter Google PSE Engine Id": "Εισάγετε το Αναγνωριστικό Μηχανής Google PSE",
@@ -424,6 +429,8 @@
 	"Enter Mojeek Search API Key": "Εισάγετε το Κλειδί API Mojeek Search",
 	"Enter Number of Steps (e.g. 50)": "Εισάγετε τον Αριθμό Βημάτων (π.χ. 50)",
 	"Enter Perplexity API Key": "",
+	"Enter Playwright Timeout": "",
+	"Enter Playwright WebSocket URL": "",
 	"Enter proxy URL (e.g. https://user:password@host:port)": "",
 	"Enter reasoning effort": "",
 	"Enter Sampler (e.g. Euler a)": "Εισάγετε τον Sampler (π.χ. Euler a)",
@@ -441,10 +448,13 @@
 	"Enter server host": "Εισάγετε τον διακομιστή host",
 	"Enter server label": "Εισάγετε την ετικέτα διακομιστή",
 	"Enter server port": "Εισάγετε την θύρα διακομιστή",
+	"Enter Sougou Search API sID": "",
+	"Enter Sougou Search API SK": "",
 	"Enter stop sequence": "Εισάγετε τη σειρά παύσης",
 	"Enter system prompt": "Εισάγετε την προτροπή συστήματος",
 	"Enter system prompt here": "",
 	"Enter Tavily API Key": "Εισάγετε το Κλειδί API Tavily",
+	"Enter Tavily Extract Depth": "",
 	"Enter the public URL of your WebUI. This URL will be used to generate links in the notifications.": "",
 	"Enter Tika Server URL": "Εισάγετε το URL διακομιστή Tika",
 	"Enter timeout in seconds": "",
@@ -525,6 +535,8 @@
 	"Filter is now globally enabled": "Το φίλτρο είναι τώρα καθολικά ενεργοποιημένο",
 	"Filters": "Φίλτρα",
 	"Fingerprint spoofing detected: Unable to use initials as avatar. Defaulting to default profile image.": "Εντοπίστηκε spoofing δακτυλικού αποτυπώματος: Αδυναμία χρήσης αρχικών ως avatar. Χρήση της προεπιλεγμένης εικόνας προφίλ.",
+	"Firecrawl API Base URL": "",
+	"Firecrawl API Key": "",
 	"Fluidly stream large external response chunks": "Ροή μεγάλων εξωτερικών τμημάτων απάντησης ομαλά",
 	"Focus chat input": "Εστίαση στο πεδίο συνομιλίας",
 	"Folder deleted successfully": "Ο φάκελος διαγράφηκε με επιτυχία",
@@ -589,6 +601,8 @@
 	"Hybrid Search": "Υβριδική Αναζήτηση",
 	"I acknowledge that I have read and I understand the implications of my action. I am aware of the risks associated with executing arbitrary code and I have verified the trustworthiness of the source.": "Αναγνωρίζω ότι έχω διαβάσει και κατανοώ τις συνέπειες της ενέργειάς μου. Γνωρίζω τους κινδύνους που σχετίζονται με την εκτέλεση αυθαίρετου κώδικα και έχω επαληθεύσει την αξιοπιστία της πηγής.",
 	"ID": "ID",
+	"iframe Sandbox Allow Forms": "",
+	"iframe Sandbox Allow Same Origin": "",
 	"Ignite curiosity": "Ξύπνημα της περιέργειας",
 	"Image": "",
 	"Image Compression": "",
@@ -649,6 +663,7 @@
 	"Label": "Ετικέτα",
 	"Landing Page Mode": "Λειτουργία Σελίδας Άφιξης",
 	"Language": "Γλώσσα",
+	"Language Locales": "",
 	"Last Active": "Τελευταία Ενεργή",
 	"Last Modified": "Τελευταία Τροποποίηση",
 	"Last reply": "",
@@ -702,7 +717,6 @@
 	"Message rating should be enabled to use this feature": "Η αξιολόγηση μηνυμάτων πρέπει να είναι ενεργοποιημένη για να χρησιμοποιήσετε αυτή τη λειτουργία",
 	"Messages you send after creating your link won't be shared. Users with the URL will be able to view the shared chat.": "Τα μηνύματα που στέλνετε μετά τη δημιουργία του συνδέσμου σας δεν θα κοινοποιηθούν. Οι χρήστες με το URL θα μπορούν να δουν τη συνομιλία που μοιραστήκατε.",
 	"Min P": "Min P",
-	"Minimum Score": "Ελάχιστο Score",
 	"Mirostat": "Mirostat",
 	"Mirostat Eta": "Mirostat Eta",
 	"Mirostat Tau": "Mirostat Tau",
@@ -833,6 +847,8 @@
 	"Pipelines Valves": "Βαλβίδες Συναρτήσεων",
 	"Plain text (.txt)": "Απλό κείμενο (.txt)",
 	"Playground": "Γήπεδο παιχνιδιών",
+	"Playwright Timeout (ms)": "",
+	"Playwright WebSocket URL": "",
 	"Please carefully review the following warnings:": "Παρακαλώ αναθεωρήστε προσεκτικά τις ακόλουθες προειδοποιήσεις:",
 	"Please do not close the settings page while loading the model.": "",
 	"Please enter a prompt": "Παρακαλώ εισάγετε μια προτροπή",
@@ -878,8 +894,11 @@
 	"References from": "Αναφορές από",
 	"Refused when it shouldn't have": "Αρνήθηκε όταν δεν έπρεπε",
 	"Regenerate": "Αναγεννήστε",
+	"Reindex": "",
+	"Reindex Knowledge Base Vectors": "",
 	"Release Notes": "Σημειώσεις Έκδοσης",
 	"Relevance": "Σχετικότητα",
+	"Relevance Threshold": "",
 	"Remove": "Αφαίρεση",
 	"Remove Model": "Αφαίρεση Μοντέλου",
 	"Rename": "Μετονομασία",
@@ -1009,6 +1028,8 @@
 	"Sign up to {{WEBUI_NAME}}": "Εγγραφή στο {{WEBUI_NAME}}",
 	"Signing in to {{WEBUI_NAME}}": "Σύνδεση στο {{WEBUI_NAME}}",
 	"sk-1234": "",
+	"Sougou Search API sID": "",
+	"Sougou Search API SK": "",
 	"Source": "Πηγή",
 	"Speech Playback Speed": "Ταχύτητα Αναπαραγωγής Ομιλίας",
 	"Speech recognition error: {{error}}": "Σφάλμα αναγνώρισης ομιλίας: {{error}}",
@@ -1036,6 +1057,7 @@
 	"Tap to interrupt": "Πατήστε για παύση",
 	"Tasks": "",
 	"Tavily API Key": "Κλειδί API Tavily",
+	"Tavily Extract Depth": "",
 	"Tell us more:": "Πείτε μας περισσότερα:",
 	"Temperature": "Temperature",
 	"Template": "Πρότυπο",
@@ -1158,6 +1180,8 @@
 	"Use Gravatar": "Χρησιμοποιήστε Gravatar",
 	"Use groups to group your users and assign permissions.": "Χρησιμοποιήστε ομάδες για να ομαδοποιήσετε τους χρήστες σας και να αναθέσετε δικαιώματα.",
 	"Use Initials": "Χρησιμοποιήστε Αρχικά",
+	"Use no proxy to fetch page contents.": "",
+	"Use proxy designated by http_proxy and https_proxy environment variables to fetch page contents.": "",
 	"use_mlock (Ollama)": "use_mlock (Ollama)",
 	"use_mmap (Ollama)": "use_mmap (Ollama)",
 	"user": "user",
@@ -1175,6 +1199,7 @@
 	"variable": "μεταβλητή",
 	"variable to have them replaced with clipboard content.": "μεταβλητή να αντικατασταθούν με το περιεχόμενο του πρόχειρου.",
 	"Verify Connection": "",
+	"Verify SSL Certificate": "",
 	"Version": "Έκδοση",
 	"Version {{selectedVersion}} of {{totalVersions}}": "Έκδοση {{selectedVersion}} από {{totalVersions}}",
 	"View Replies": "",
@@ -1189,6 +1214,7 @@
 	"Warning: Jupyter execution enables arbitrary code execution, posing severe security risks—proceed with extreme caution.": "",
 	"Web": "Διαδίκτυο",
 	"Web API": "Web API",
+	"Web Loader Engine": "",
 	"Web Search": "Αναζήτηση στο Διαδίκτυο",
 	"Web Search Engine": "Μηχανή Αναζήτησης στο Διαδίκτυο",
 	"Web Search in Chat": "",

+ 28 - 2
src/lib/i18n/locales/en-GB/translation.json

@@ -62,6 +62,7 @@
 	"Allow Chat Deletion": "",
 	"Allow Chat Edit": "",
 	"Allow File Upload": "",
+	"Allow Multiple Models in Chat": "",
 	"Allow non-local voices": "",
 	"Allow Temporary Chat": "",
 	"Allow User Location": "",
@@ -79,6 +80,7 @@
 	"and": "",
 	"and {{COUNT}} more": "",
 	"and create a new shared link.": "",
+	"Android": "",
 	"API Base URL": "",
 	"API Key": "",
 	"API Key created.": "",
@@ -141,7 +143,6 @@
 	"Brave Search API Key": "",
 	"By {{name}}": "",
 	"Bypass Embedding and Retrieval": "",
-	"Bypass SSL verification for Websites": "",
 	"Calendar": "",
 	"Call": "",
 	"Call feature is not supported when using Web STT engine": "",
@@ -303,6 +304,7 @@
 	"Deleted User": "",
 	"Describe your knowledge base and objectives": "",
 	"Description": "",
+	"Detect Artifacts Automatically": "",
 	"Didn't fully follow instructions": "",
 	"Direct": "",
 	"Direct Connections": "",
@@ -358,6 +360,7 @@
 	"e.g. my_filter": "",
 	"e.g. my_tools": "",
 	"e.g. Tools for performing various operations": "",
+	"e.g., en-US,ja-JP (leave blank for auto-detect)": "",
 	"Edit": "",
 	"Edit Arena Model": "",
 	"Edit Channel": "",
@@ -407,6 +410,8 @@
 	"Enter Document Intelligence Key": "",
 	"Enter domains separated by commas (e.g., example.com,site.org)": "",
 	"Enter Exa API Key": "",
+	"Enter Firecrawl API Base URL": "",
+	"Enter Firecrawl API Key": "",
 	"Enter Github Raw URL": "",
 	"Enter Google PSE API Key": "",
 	"Enter Google PSE Engine Id": "",
@@ -424,6 +429,8 @@
 	"Enter Mojeek Search API Key": "",
 	"Enter Number of Steps (e.g. 50)": "",
 	"Enter Perplexity API Key": "",
+	"Enter Playwright Timeout": "",
+	"Enter Playwright WebSocket URL": "",
 	"Enter proxy URL (e.g. https://user:password@host:port)": "",
 	"Enter reasoning effort": "",
 	"Enter Sampler (e.g. Euler a)": "",
@@ -441,10 +448,13 @@
 	"Enter server host": "",
 	"Enter server label": "",
 	"Enter server port": "",
+	"Enter Sougou Search API sID": "",
+	"Enter Sougou Search API SK": "",
 	"Enter stop sequence": "",
 	"Enter system prompt": "",
 	"Enter system prompt here": "",
 	"Enter Tavily API Key": "",
+	"Enter Tavily Extract Depth": "",
 	"Enter the public URL of your WebUI. This URL will be used to generate links in the notifications.": "",
 	"Enter Tika Server URL": "",
 	"Enter timeout in seconds": "",
@@ -525,6 +535,8 @@
 	"Filter is now globally enabled": "",
 	"Filters": "",
 	"Fingerprint spoofing detected: Unable to use initials as avatar. Defaulting to default profile image.": "",
+	"Firecrawl API Base URL": "",
+	"Firecrawl API Key": "",
 	"Fluidly stream large external response chunks": "",
 	"Focus chat input": "",
 	"Folder deleted successfully": "",
@@ -589,6 +601,8 @@
 	"Hybrid Search": "",
 	"I acknowledge that I have read and I understand the implications of my action. I am aware of the risks associated with executing arbitrary code and I have verified the trustworthiness of the source.": "",
 	"ID": "",
+	"iframe Sandbox Allow Forms": "",
+	"iframe Sandbox Allow Same Origin": "",
 	"Ignite curiosity": "",
 	"Image": "",
 	"Image Compression": "",
@@ -649,6 +663,7 @@
 	"Label": "",
 	"Landing Page Mode": "",
 	"Language": "",
+	"Language Locales": "",
 	"Last Active": "",
 	"Last Modified": "",
 	"Last reply": "",
@@ -702,7 +717,6 @@
 	"Message rating should be enabled to use this feature": "",
 	"Messages you send after creating your link won't be shared. Users with the URL will be able to view the shared chat.": "",
 	"Min P": "",
-	"Minimum Score": "",
 	"Mirostat": "",
 	"Mirostat Eta": "",
 	"Mirostat Tau": "",
@@ -833,6 +847,8 @@
 	"Pipelines Valves": "",
 	"Plain text (.txt)": "",
 	"Playground": "",
+	"Playwright Timeout (ms)": "",
+	"Playwright WebSocket URL": "",
 	"Please carefully review the following warnings:": "",
 	"Please do not close the settings page while loading the model.": "",
 	"Please enter a prompt": "",
@@ -878,8 +894,11 @@
 	"References from": "",
 	"Refused when it shouldn't have": "",
 	"Regenerate": "",
+	"Reindex": "",
+	"Reindex Knowledge Base Vectors": "",
 	"Release Notes": "",
 	"Relevance": "",
+	"Relevance Threshold": "",
 	"Remove": "",
 	"Remove Model": "",
 	"Rename": "",
@@ -1009,6 +1028,8 @@
 	"Sign up to {{WEBUI_NAME}}": "",
 	"Signing in to {{WEBUI_NAME}}": "",
 	"sk-1234": "",
+	"Sougou Search API sID": "",
+	"Sougou Search API SK": "",
 	"Source": "",
 	"Speech Playback Speed": "",
 	"Speech recognition error: {{error}}": "",
@@ -1036,6 +1057,7 @@
 	"Tap to interrupt": "",
 	"Tasks": "",
 	"Tavily API Key": "",
+	"Tavily Extract Depth": "",
 	"Tell us more:": "",
 	"Temperature": "",
 	"Template": "",
@@ -1158,6 +1180,8 @@
 	"Use Gravatar": "",
 	"Use groups to group your users and assign permissions.": "",
 	"Use Initials": "",
+	"Use no proxy to fetch page contents.": "",
+	"Use proxy designated by http_proxy and https_proxy environment variables to fetch page contents.": "",
 	"use_mlock (Ollama)": "",
 	"use_mmap (Ollama)": "",
 	"user": "",
@@ -1175,6 +1199,7 @@
 	"variable": "",
 	"variable to have them replaced with clipboard content.": "",
 	"Verify Connection": "",
+	"Verify SSL Certificate": "",
 	"Version": "",
 	"Version {{selectedVersion}} of {{totalVersions}}": "",
 	"View Replies": "",
@@ -1189,6 +1214,7 @@
 	"Warning: Jupyter execution enables arbitrary code execution, posing severe security risks—proceed with extreme caution.": "",
 	"Web": "",
 	"Web API": "",
+	"Web Loader Engine": "",
 	"Web Search": "",
 	"Web Search Engine": "",
 	"Web Search in Chat": "",

+ 28 - 2
src/lib/i18n/locales/en-US/translation.json

@@ -62,6 +62,7 @@
 	"Allow Chat Deletion": "",
 	"Allow Chat Edit": "",
 	"Allow File Upload": "",
+	"Allow Multiple Models in Chat": "",
 	"Allow non-local voices": "",
 	"Allow Temporary Chat": "",
 	"Allow User Location": "",
@@ -79,6 +80,7 @@
 	"and": "",
 	"and {{COUNT}} more": "",
 	"and create a new shared link.": "",
+	"Android": "",
 	"API Base URL": "",
 	"API Key": "",
 	"API Key created.": "",
@@ -141,7 +143,6 @@
 	"Brave Search API Key": "",
 	"By {{name}}": "",
 	"Bypass Embedding and Retrieval": "",
-	"Bypass SSL verification for Websites": "",
 	"Calendar": "",
 	"Call": "",
 	"Call feature is not supported when using Web STT engine": "",
@@ -303,6 +304,7 @@
 	"Deleted User": "",
 	"Describe your knowledge base and objectives": "",
 	"Description": "",
+	"Detect Artifacts Automatically": "",
 	"Didn't fully follow instructions": "",
 	"Direct": "",
 	"Direct Connections": "",
@@ -358,6 +360,7 @@
 	"e.g. my_filter": "",
 	"e.g. my_tools": "",
 	"e.g. Tools for performing various operations": "",
+	"e.g., en-US,ja-JP (leave blank for auto-detect)": "",
 	"Edit": "",
 	"Edit Arena Model": "",
 	"Edit Channel": "",
@@ -407,6 +410,8 @@
 	"Enter Document Intelligence Key": "",
 	"Enter domains separated by commas (e.g., example.com,site.org)": "",
 	"Enter Exa API Key": "",
+	"Enter Firecrawl API Base URL": "",
+	"Enter Firecrawl API Key": "",
 	"Enter Github Raw URL": "",
 	"Enter Google PSE API Key": "",
 	"Enter Google PSE Engine Id": "",
@@ -424,6 +429,8 @@
 	"Enter Mojeek Search API Key": "",
 	"Enter Number of Steps (e.g. 50)": "",
 	"Enter Perplexity API Key": "",
+	"Enter Playwright Timeout": "",
+	"Enter Playwright WebSocket URL": "",
 	"Enter proxy URL (e.g. https://user:password@host:port)": "",
 	"Enter reasoning effort": "",
 	"Enter Sampler (e.g. Euler a)": "",
@@ -441,10 +448,13 @@
 	"Enter server host": "",
 	"Enter server label": "",
 	"Enter server port": "",
+	"Enter Sougou Search API sID": "",
+	"Enter Sougou Search API SK": "",
 	"Enter stop sequence": "",
 	"Enter system prompt": "",
 	"Enter system prompt here": "",
 	"Enter Tavily API Key": "",
+	"Enter Tavily Extract Depth": "",
 	"Enter the public URL of your WebUI. This URL will be used to generate links in the notifications.": "",
 	"Enter Tika Server URL": "",
 	"Enter timeout in seconds": "",
@@ -525,6 +535,8 @@
 	"Filter is now globally enabled": "",
 	"Filters": "",
 	"Fingerprint spoofing detected: Unable to use initials as avatar. Defaulting to default profile image.": "",
+	"Firecrawl API Base URL": "",
+	"Firecrawl API Key": "",
 	"Fluidly stream large external response chunks": "",
 	"Focus chat input": "",
 	"Folder deleted successfully": "",
@@ -589,6 +601,8 @@
 	"Hybrid Search": "",
 	"I acknowledge that I have read and I understand the implications of my action. I am aware of the risks associated with executing arbitrary code and I have verified the trustworthiness of the source.": "",
 	"ID": "",
+	"iframe Sandbox Allow Forms": "",
+	"iframe Sandbox Allow Same Origin": "",
 	"Ignite curiosity": "",
 	"Image": "",
 	"Image Compression": "",
@@ -649,6 +663,7 @@
 	"Label": "",
 	"Landing Page Mode": "",
 	"Language": "",
+	"Language Locales": "",
 	"Last Active": "",
 	"Last Modified": "",
 	"Last reply": "",
@@ -702,7 +717,6 @@
 	"Message rating should be enabled to use this feature": "",
 	"Messages you send after creating your link won't be shared. Users with the URL will be able to view the shared chat.": "",
 	"Min P": "",
-	"Minimum Score": "",
 	"Mirostat": "",
 	"Mirostat Eta": "",
 	"Mirostat Tau": "",
@@ -833,6 +847,8 @@
 	"Pipelines Valves": "",
 	"Plain text (.txt)": "",
 	"Playground": "",
+	"Playwright Timeout (ms)": "",
+	"Playwright WebSocket URL": "",
 	"Please carefully review the following warnings:": "",
 	"Please do not close the settings page while loading the model.": "",
 	"Please enter a prompt": "",
@@ -878,8 +894,11 @@
 	"References from": "",
 	"Refused when it shouldn't have": "",
 	"Regenerate": "",
+	"Reindex": "",
+	"Reindex Knowledge Base Vectors": "",
 	"Release Notes": "",
 	"Relevance": "",
+	"Relevance Threshold": "",
 	"Remove": "",
 	"Remove Model": "",
 	"Rename": "",
@@ -1009,6 +1028,8 @@
 	"Sign up to {{WEBUI_NAME}}": "",
 	"Signing in to {{WEBUI_NAME}}": "",
 	"sk-1234": "",
+	"Sougou Search API sID": "",
+	"Sougou Search API SK": "",
 	"Source": "",
 	"Speech Playback Speed": "",
 	"Speech recognition error: {{error}}": "",
@@ -1036,6 +1057,7 @@
 	"Tap to interrupt": "",
 	"Tasks": "",
 	"Tavily API Key": "",
+	"Tavily Extract Depth": "",
 	"Tell us more:": "",
 	"Temperature": "",
 	"Template": "",
@@ -1158,6 +1180,8 @@
 	"Use Gravatar": "",
 	"Use groups to group your users and assign permissions.": "",
 	"Use Initials": "",
+	"Use no proxy to fetch page contents.": "",
+	"Use proxy designated by http_proxy and https_proxy environment variables to fetch page contents.": "",
 	"use_mlock (Ollama)": "",
 	"use_mmap (Ollama)": "",
 	"user": "",
@@ -1175,6 +1199,7 @@
 	"variable": "",
 	"variable to have them replaced with clipboard content.": "",
 	"Verify Connection": "",
+	"Verify SSL Certificate": "",
 	"Version": "",
 	"Version {{selectedVersion}} of {{totalVersions}}": "",
 	"View Replies": "",
@@ -1189,6 +1214,7 @@
 	"Warning: Jupyter execution enables arbitrary code execution, posing severe security risks—proceed with extreme caution.": "",
 	"Web": "",
 	"Web API": "",
+	"Web Loader Engine": "",
 	"Web Search": "",
 	"Web Search Engine": "",
 	"Web Search in Chat": "",

+ 178 - 152
src/lib/i18n/locales/es-ES/translation.json

@@ -14,19 +14,19 @@
 	"*Prompt node ID(s) are required for image generation": "Los ID de nodo son requeridos para la generación de imágenes",
 	"A new version (v{{LATEST_VERSION}}) is now available.": "Nueva versión (v{{LATEST_VERSION}}) disponible.",
 	"A task model is used when performing tasks such as generating titles for chats and web search queries": "El modelo de tareas realiza tareas como la generación de títulos para chats y consultas de búsqueda web",
-	"a user": "un usuario",
+	"a user": "un/a usuari@",
 	"About": "Acerca de",
-	"Accept autocomplete generation / Jump to prompt variable": "Aceptar generación de autocompletado / Saltar a la variable de Indicadión (prompt)",
+	"Accept autocomplete generation / Jump to prompt variable": "Aceptar generación de autocompletado / Saltar a indicador variable",
 	"Access": "Acceso",
 	"Access Control": "Control de Acceso",
-	"Accessible to all users": "Accesible para todos los usuarios",
+	"Accessible to all users": "Accesible para todos l@s usuari@s",
 	"Account": "Cuenta",
 	"Account Activation Pending": "Activación de cuenta Pendiente",
 	"Accurate information": "Información precisa",
 	"Actions": "Acciones",
 	"Activate": "Activar",
 	"Activate this command by typing \"/{{COMMAND}}\" to chat input.": "Activar este comando escribiendo \"/{{COMMAND}}\" en el chat",
-	"Active Users": "Usuarios activos",
+	"Active Users": "Usuari@s activos",
 	"Add": "Añadir",
 	"Add a model ID": "Añadir un ID de modelo",
 	"Add a short description about what this model does": "Añadir una breve descripción sobre lo que hace este modelo",
@@ -35,7 +35,7 @@
 	"Add Connection": "Añadir Conexión",
 	"Add Content": "Añadir Contenido",
 	"Add content here": "Añadir contenido aquí",
-	"Add custom prompt": "Añadir un indicador(prompt) personalizado",
+	"Add custom prompt": "Añadir un indicador personalizado",
 	"Add Files": "Añadir Ficheros",
 	"Add Group": "Añadir Grupo",
 	"Add Memory": "Añadir Memoria",
@@ -44,14 +44,14 @@
 	"Add Tag": "Añadir etiqueta",
 	"Add Tags": "Añadir etiquetas",
 	"Add text content": "Añade contenido de texto",
-	"Add User": "Añadir Usuario",
-	"Add User Group": "Añadir Grupo de Usuario",
-	"Adjusting these settings will apply changes universally to all users.": "El ajuste de estas opciones se aplicará globalmente a todos los usuarios.",
+	"Add User": "Añadir Usuari@",
+	"Add User Group": "Añadir Grupo de Usuari@",
+	"Adjusting these settings will apply changes universally to all users.": "El ajuste de estas opciones se aplicará globalmente a todos l@s usuari@s.",
 	"admin": "admin",
 	"Admin": "Admin",
-	"Admin Panel": "Panel de Admin",
+	"Admin Panel": "Administración",
 	"Admin Settings": "Ajustes de Admin",
-	"Admins have access to all tools at all times; users need tools assigned per model in the workspace.": "Los Admins tienen acceso a todas las herramientas en todo momento; los usuarios necesitan, en el área de trabajo, que los modelos tengan asignadas las herramentas.",
+	"Admins have access to all tools at all times; users need tools assigned per model in the workspace.": "Los Admins tienen acceso a todas las herramientas en todo momento; l@s usuari@s necesitan, en el área de trabajo, que los modelos tengan asignadas las herramentas.",
 	"Advanced Parameters": "Parámetros Avanzados",
 	"Advanced Params": "Param. Avanz.",
 	"All": "Todos",
@@ -62,9 +62,10 @@
 	"Allow Chat Deletion": "Permitir Borrado de Chat",
 	"Allow Chat Edit": "Pemritir Editar Chat",
 	"Allow File Upload": "Permitir Subida de Ficheros",
+	"Allow Multiple Models in Chat": "",
 	"Allow non-local voices": "Permitir voces no locales",
 	"Allow Temporary Chat": "Permitir Chat Temporal",
-	"Allow User Location": "Permitir Ubicación del Usuario",
+	"Allow User Location": "Permitir Ubicación de Usuari@",
 	"Allow Voice Interruption in Call": "Permitir Interrupción de Voz en Llamada",
 	"Allowed Endpoints": "Endpoints Permitidos",
 	"Already have an account?": "¿Ya tienes una cuenta?",
@@ -79,14 +80,15 @@
 	"and": "y",
 	"and {{COUNT}} more": "y {{COUNT}} más",
 	"and create a new shared link.": "y crear un nuevo enlace compartido.",
+	"Android": "",
 	"API Base URL": "URL Base API",
 	"API Key": "Clave API ",
 	"API Key created.": "Clave API creada.",
-	"API Key Endpoint Restrictions": "Clave API Restricciones de Endpoint",
+	"API Key Endpoint Restrictions": "Clave API para Endpoints Restringidos",
 	"API keys": "Claves API",
 	"Application DN": "Aplicacion DN",
 	"Application DN Password": "Contraseña Aplicacion DN",
-	"applies to all users with the \"user\" role": "se aplica a todos los usuarios con el rol \"user\" ",
+	"applies to all users with the \"user\" role": "se aplica a todos l@s usuari@s con el rol \"user\" ",
 	"April": "Abril",
 	"Archive": "Archivar",
 	"Archive All Chats": "Archivar Todos los Chats",
@@ -105,15 +107,15 @@
 	"Attach file from knowledge": "Adjuntar fichero desde el conocimiento",
 	"Attention to detail": "Atención al detalle",
 	"Attribute for Mail": "Atributo para Correo",
-	"Attribute for Username": "Atributo para Nombre de Usuario",
+	"Attribute for Username": "Atributo para Nombre de Usuari@",
 	"Audio": "Audio",
 	"August": "Agosto",
-	"Auth": "",
+	"Auth": "Autorización",
 	"Authenticate": "Autentificar",
 	"Authentication": "Autentificación",
-	"Auto": "",
-	"Auto-Copy Response to Clipboard": "Auto-Copiar respuesta al Portapapeles",
-	"Auto-playback response": "Auto-Reproducir Respuesta",
+	"Auto": "Auto",
+	"Auto-Copy Response to Clipboard": "AutoCopiado de respuesta al Portapapeles",
+	"Auto-playback response": "AutoReproducir Respuesta",
 	"Autocomplete Generation": "Generación de Autocompletado",
 	"Autocomplete Generation Input Max Length": "Max. Longitud de Entrada en Generación de Autocompletado",
 	"Automatic1111": "AUTOMATIC1111",
@@ -141,7 +143,6 @@
 	"Brave Search API Key": "Clave API de Brave Search",
 	"By {{name}}": "Por {{name}}",
 	"Bypass Embedding and Retrieval": "Evitar Incrustración y Recuperación",
-	"Bypass SSL verification for Websites": "Evitar Verificación SSL para sitios web",
 	"Calendar": "Calendario",
 	"Call": "Llamada",
 	"Call feature is not supported when using Web STT engine": "La característica Llamada no está soportada cuando se usa el motor Web STT",
@@ -158,19 +159,19 @@
 	"Chart new frontiers": "Trazar nuevas fronteras",
 	"Chat": "Chat",
 	"Chat Background Image": "Imágen de Fondo del Chat",
-	"Chat Bubble UI": "Interface Burbuja de Chat",
+	"Chat Bubble UI": "Interface del Chat tipo Burbuja",
 	"Chat Controls": "Controles del chat",
 	"Chat direction": "Dirección del Chat",
 	"Chat Overview": "Vista General del Chat",
 	"Chat Permissions": "Permisos del Chat",
-	"Chat Tags Auto-Generation": "Auto-Generación de Etiquetas de Chat",
+	"Chat Tags Auto-Generation": "AutoGeneración de Etiquetas de Chat",
 	"Chats": "Chats",
 	"Check Again": "Verifica de nuevo",
 	"Check for updates": "Buscar actualizaciones",
 	"Checking for updates...": "Buscando actualizaciones...",
 	"Choose a model before saving...": "Escoge un modelo antes de guardar...",
 	"Chunk Overlap": "Superposición de Fragmentos",
-	"Chunk Size": "Tamaño de Fragmentos",
+	"Chunk Size": "Tamaño de los Fragmentos",
 	"Ciphers": "Cifrado",
 	"Citation": "Cita",
 	"Clear memory": "Liberar memoria",
@@ -179,7 +180,7 @@
 	"Click here for filter guides.": "Pulsar aquí para guías de filtros",
 	"Click here for help.": "Pulsar aquí para Ayuda.",
 	"Click here to": "Pulsa aquí para",
-	"Click here to download user import template file.": "Pulsa aquí para descargar la plantilla de importación del usuario.",
+	"Click here to download user import template file.": "Pulsa aquí para descargar la plantilla de importación de usuari@.",
 	"Click here to learn more about faster-whisper and see the available models.": "Pulsa aquí para saber más sobre faster-whisper y ver modelos disponibles.",
 	"Click here to see available models.": "Pulsa aquí para ver modelos disponibles.",
 	"Click here to select": "Pulsa aquí para seleccionar",
@@ -187,7 +188,7 @@
 	"Click here to select a py file.": "Pulsa aquí para seleccionar un fichero Python (.py)",
 	"Click here to upload a workflow.json file.": "Pulsa aquí para subir un fichero workflow.json",
 	"click here.": "Pulsa aquí.",
-	"Click on the user role button to change a user's role.": "Pulsa en el botón rol del usuario para cambiar su rol.",
+	"Click on the user role button to change a user's role.": "Pulsa en el botón rol de usuari@ para cambiar su rol.",
 	"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "Permisos de escritura del portapapeles denegado. Por favor, comprueba la configuración de tu navegador para otorgar el permiso necesario.",
 	"Clone": "Clonar",
 	"Clone Chat": "Clonar Chat",
@@ -199,20 +200,20 @@
 	"Code Execution Timeout": "Tiempo",
 	"Code formatted successfully": "Se ha formateado correctamente el código.",
 	"Code Interpreter": "Interprete de Código",
-	"Code Interpreter Engine": "Motor Interpretador de Código",
-	"Code Interpreter Prompt Template": "Plantilla de Prompt del Interpretador de Código",
+	"Code Interpreter Engine": "Motor del Interprete de Código",
+	"Code Interpreter Prompt Template": "Plantilla del Indicador del Interprete de Código",
 	"Collapse": "Plegar",
 	"Collection": "Colección",
 	"Color": "Color",
 	"ComfyUI": "ComfyUI",
-	"ComfyUI API Key": "ComfyUI Clave API",
-	"ComfyUI Base URL": "ComfyUI URL Base",
-	"ComfyUI Base URL is required.": "ComfyUI URL Base es necesaria.",
-	"ComfyUI Workflow": "ComfyUI Flujo de Trabajo",
-	"ComfyUI Workflow Nodes": "Comfy Nodos del Flujo de Trabajo",
+	"ComfyUI API Key": "Clave API de ComfyUI",
+	"ComfyUI Base URL": "URL Base de ComfyUI",
+	"ComfyUI Base URL is required.": "La URL Base de ComfyUI es necesaria.",
+	"ComfyUI Workflow": "Flujo de Trabajo de ComfyUI",
+	"ComfyUI Workflow Nodes": "Nodos del Flujo de Trabajo de ComfyUI",
 	"Command": "Comando",
 	"Completions": "Cumplimientos",
-	"Concurrent Requests": "Solicitudes Concurrentes",
+	"Concurrent Requests": "Número de Solicitudes Concurrentes",
 	"Configure": "Configurar",
 	"Confirm": "Confirmar",
 	"Confirm Password": "Confirma Contraseña",
@@ -220,10 +221,10 @@
 	"Confirm your new password": "Confirma tu nueva contraseña",
 	"Connect to your own OpenAI compatible API endpoints.": "Conectar a tus propios endpoints compatibles API OpenAI.",
 	"Connect to your own OpenAPI compatible external tool servers.": "Conectar a tus propios endpoints externos de herramientas compatibles API OpenAI.",
-	"Connection failed": "",
-	"Connection successful": "",
+	"Connection failed": "Conexión fallida",
+	"Connection successful": "Conexión realizada",
 	"Connections": "Conexiones",
-	"Connections saved successfully": "",
+	"Connections saved successfully": "Conexiones grabadas correctamente",
 	"Constrains effort on reasoning for reasoning models. Only applicable to reasoning models from specific providers that support reasoning effort.": "Limita el esfuerzo de razonamiento para los modelos de razonamiento. Solo aplicable a modelos de razonamiento de proveedores específicos que soportan el esfuerzo de razonamiento.",
 	"Contact Admin for WebUI Access": "Contacta con Admin para obtener acceso a WebUI",
 	"Content": "Contenido",
@@ -261,7 +262,7 @@
 	"Created At": "Creado En",
 	"Created by": "Creado por",
 	"CSV Import": "Importar CSV",
-	"Ctrl+Enter to Send": "Ctrl+Enter para Enviar",
+	"Ctrl+Enter to Send": "'Ctrl+Enter' para Enviar",
 	"Current Model": "Modelo Actual",
 	"Current Password": "Contraseña Actual",
 	"Custom": "Personalizado",
@@ -278,11 +279,11 @@
 	"Default Models": "Modelos Predeterminados",
 	"Default permissions": "Permisos Predeterminados",
 	"Default permissions updated successfully": "Permisos predeterminados actualizados correctamente",
-	"Default Prompt Suggestions": "Sugerencias de Indicador(prompt) Predetermonadas",
+	"Default Prompt Suggestions": "Sugerencias Predeterminadas de Indicador",
 	"Default to 389 or 636 if TLS is enabled": "Predeterminado a 389, o 636 si TLS está habilitado",
 	"Default to ALL": "Predeterminado a TODOS",
-	"Default to segmented retrieval for focused and relevant content extraction, this is recommended for most cases.": "Predeterminada una segmentación de la recuperación para una extracción de contenido centrado y relevante, recomendado para la mayoría de los casos.",
-	"Default User Role": "Rol Predeterminado Usuarios",
+	"Default to segmented retrieval for focused and relevant content extraction, this is recommended for most cases.": "Por defecto está predeterminada una segmentación de la recuperación para una extracción de contenido centrado y relevante, recomendado para la mayoría de los casos.",
+	"Default User Role": "Rol Predeterminado de l@s Usuari@s Nuev@s",
 	"Delete": "Borrar",
 	"Delete a model": "Borrar un modelo",
 	"Delete All Chats": "Borrar todos los chats",
@@ -294,36 +295,37 @@
 	"Delete function?": "Borrar la función?",
 	"Delete Message": "Borrar mensaje",
 	"Delete message?": "¿Borrar mensaje?",
-	"Delete prompt?": "¿Borrar el indicador(prompt)?",
+	"Delete prompt?": "¿Borrar el indicador?",
 	"delete this link": "Borrar este enlace",
 	"Delete tool?": "¿Borrar la herramienta?",
-	"Delete User": "Borrar Usuario",
+	"Delete User": "Borrar Usuari@",
 	"Deleted {{deleteModelTag}}": "{{deleteModelTag}} Borrado",
 	"Deleted {{name}}": "{{nombre}} Borrado",
-	"Deleted User": "Usuario Borrado",
+	"Deleted User": "Usuari@ Borrado",
 	"Describe your knowledge base and objectives": "Describe tu Base de Conocimientos y sus objetivos",
 	"Description": "Descripción",
+	"Detect Artifacts Automatically": "",
 	"Didn't fully follow instructions": "No seguiste completamente las instrucciones",
 	"Direct": "Directo",
 	"Direct Connections": "Conexiones Directas",
-	"Direct Connections allow users to connect to their own OpenAI compatible API endpoints.": "Las Conexiones Directas permiten a los usuarios conectar a sus propios endpoints compatibles API OpenAI.",
+	"Direct Connections allow users to connect to their own OpenAI compatible API endpoints.": "Las Conexiones Directas permiten a l@s usuari@s conectar a sus propios endpoints compatibles API OpenAI.",
 	"Direct Connections settings updated": "Se actualizaron las configuraciones de las Conexiones Directas",
-	"Direct Tool Servers": "",
+	"Direct Tool Servers": "Servidores de Herramientas Directos",
 	"Disabled": "Deshabilitado",
-	"Discover a function": "Descubre una Función",
-	"Discover a model": "Descubre un Modelo",
-	"Discover a prompt": "Descubre un Indicador(prompt)",
-	"Discover a tool": "Descubre una Herramienta",
+	"Discover a function": "Descubrir Funciónes",
+	"Discover a model": "Descubrir Modelos",
+	"Discover a prompt": "Descubrir Indicadores",
+	"Discover a tool": "Descubrir Herramientas",
 	"Discover how to use Open WebUI and seek support from the community.": "Descubre cómo usar Open WebUI y busca Soporte Comunitario.",
 	"Discover wonders": "Descubre Maravillas",
 	"Discover, download, and explore custom functions": "Descubre, descarga y explora funciones personalizadas",
-	"Discover, download, and explore custom prompts": "Descubre, descarga, y explora indicadores(prompts) personalizados",
+	"Discover, download, and explore custom prompts": "Descubre, descarga, y explora indicadores personalizados",
 	"Discover, download, and explore custom tools": "Descubre, descarga y explora herramientas personalizadas",
 	"Discover, download, and explore model presets": "Descubre, descarga y explora modelos con preajustados",
 	"Dismissible": "Desestimable",
 	"Display": "Mostrar",
 	"Display Emoji in Call": "Muestra chirimbolitos(Emojis) en Llamada",
-	"Display the username instead of You in the Chat": "Mostrar en el chat el nombre de usuario en lugar del genérico Tu/Usted",
+	"Display the username instead of You in the Chat": "Mostrar en el chat el nombre de usuari@ en lugar del genérico Tu/Usted",
 	"Displays citations in the response": "Mostrar citas en la respuesta",
 	"Dive into knowledge": "Sumérgete en el conocimiento",
 	"Do not install functions from sources you do not fully trust.": "¡No instalar funciones de fuentes en las que que no se confíe totalmente!",
@@ -358,32 +360,33 @@
 	"e.g. my_filter": "p.ej. mi_filtro",
 	"e.g. my_tools": "p.ej. mis_herramientas",
 	"e.g. Tools for performing various operations": "p.ej. Herramientas para realizar varias operaciones",
+	"e.g., en-US,ja-JP (leave blank for auto-detect)": "",
 	"Edit": "Editar",
 	"Edit Arena Model": "Editar Modelo en Arena",
 	"Edit Channel": "Editar Canal",
 	"Edit Connection": "Editar Conexión",
 	"Edit Default Permissions": "Editar Permisos Predeterminados",
 	"Edit Memory": "Editar Memoria",
-	"Edit User": "Editar Usuario",
-	"Edit User Group": "Editar Grupo de Usuario",
+	"Edit User": "Editar Usuari@",
+	"Edit User Group": "Editar Grupo de Usuari@",
 	"ElevenLabs": "ElevenLabs",
 	"Email": "Email",
 	"Embark on adventures": "Embarcate en aventuras",
-	"Embedding": "Incrustado",
-	"Embedding Batch Size": "Tamaño Lote Incrustación",
-	"Embedding Model": "Modelo de Incruswtación",
+	"Embedding": "Incrustación",
+	"Embedding Batch Size": "Tamaño del Lote de Incrustación",
+	"Embedding Model": "Modelo de Incrustación",
 	"Embedding Model Engine": "Motor del Modelo de Incrustación",
 	"Embedding model set to \"{{embedding_model}}\"": "Modelo de Incrustación configurado a \"{{embedding_model}}\"",
-	"Enable API Key": "Habilitar clave API",
+	"Enable API Key": "Habilitar Clave API",
 	"Enable autocomplete generation for chat messages": "Habilitar generación de autocompletado para mensajes de chat",
 	"Enable Code Execution": "Habilitar Ejecución de Código",
 	"Enable Code Interpreter": "Habilitar Interprete de Código",
 	"Enable Community Sharing": "Habilitar Compartir con la Comunidad",
 	"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.": "Habilitar bloqueo de memoria (mlock) para prevenir que los datos del modelo se intercambien fuera de la RAM. Esta opción bloquea el conjunto de páginas de trabajo del modelo en RAM, asegurando que no se intercambiarán fuera a disco. Esto puede ayudar a mantener el rendimiento evitando fallos de página y asegurando un acceso rápido a los datos.",
 	"Enable Memory Mapping (mmap) to load model data. This option allows the system to use disk storage as an extension of RAM by treating disk files as if they were in RAM. This can improve model performance by allowing for faster data access. However, it may not work correctly with all systems and can consume a significant amount of disk space.": "Habilitar Mapeado de Memoria (mmap) para cargar datos del modelo. Esta opción permite al sistema usar el almacenamiento del disco como una extensión de la RAM al tratar los archivos en disco como si estuvieran en la RAM. Esto puede mejorar el rendimiento del modelo al permitir un acceso más rápido a los datos. Sin embargo, puede no funcionar correctamente con todos los sistemas y puede consumir una cantidad significativa de espacio en disco.",
-	"Enable Message Rating": "Habilitar la calificación de los Mensajes",
-	"Enable Mirostat sampling for controlling perplexity.": "Habilitar muestreo Mirostat para controlar la perplejidad.",
-	"Enable New Sign Ups": "Habilitar Registros de Nuevos Usuarios",
+	"Enable Message Rating": "Habilitar Calificación de los Mensajes",
+	"Enable Mirostat sampling for controlling perplexity.": "Algoritmo de decodificación de texto neuronal que controla activamente el proceso generativo para mantener la perplejidad del texto generado en un valor deseado. Previene las trampas de aburrimiento (por excesivas repeticiones) y de incoherencia (por generación de excesivo texto).",
+	"Enable New Sign Ups": "Habilitar Registros de Nuev@s Usuari@s",
 	"Enabled": "Habilitado",
 	"Enforce Temporary Chat": "",
 	"Ensure your CSV file includes 4 columns in this order: Name, Email, Password, Role.": "Asegúrese de que su archivo CSV incluya 4 columnas en este orden: Nombre, Correo Electrónico, Contraseña, Rol.",
@@ -398,7 +401,7 @@
 	"Enter Brave Search API Key": "Ingresar la Clave API de Brave Search",
 	"Enter certificate path": "Ingresar la ruta del certificado",
 	"Enter CFG Scale (e.g. 7.0)": "Ingresa escala CFG (p.ej., 7.0)",
-	"Enter Chunk Overlap": "Ingresar Superposición de Fragmentos",
+	"Enter Chunk Overlap": "Ingresar Superposición de los Fragmentos",
 	"Enter Chunk Size": "Ingresar el Tamaño del Fragmento",
 	"Enter comma-seperated \"token:bias_value\" pairs (example: 5432:100, 413:-100)": "Ingresar pares \"token:valor_sesgo\" separados por comas (ejemplo: 5432:100, 413:-100)",
 	"Enter description": "Ingresar Descripción",
@@ -407,6 +410,8 @@
 	"Enter Document Intelligence Key": "Ingresar Clave de Azure Document Intelligence",
 	"Enter domains separated by commas (e.g., example.com,site.org)": "Ingresar dominios separados por comas (p.ej., ejemplo.com,sitio.org)",
 	"Enter Exa API Key": "Ingresar Clave API de Exa",
+	"Enter Firecrawl API Base URL": "",
+	"Enter Firecrawl API Key": "",
 	"Enter Github Raw URL": "Ingresar URL Github en Bruto(raw)",
 	"Enter Google PSE API Key": "Ingresar Clave API de Google PSE",
 	"Enter Google PSE Engine Id": "Ingresa ID del Motor PSE de Google",
@@ -416,14 +421,16 @@
 	"Enter Jupyter Token": "Ingresar Token de Jupyter",
 	"Enter Jupyter URL": "Ingresar URL de Jupyter",
 	"Enter Kagi Search API Key": "Ingresar Clave API de Kagi Search",
-	"Enter Key Behavior": "Ingresar Clave de Comportamiento",
+	"Enter Key Behavior": "Comportamiento de la Tecla de Envío",
 	"Enter language codes": "Ingresar Códigos de Idioma",
-	"Enter Mistral API Key": "",
+	"Enter Mistral API Key": "Ingresar Clave API de Mistral",
 	"Enter Model ID": "Ingresar ID del Modelo",
 	"Enter model tag (e.g. {{modelTag}})": "Ingresar la etiqueta del modelo (p.ej. {{modelTag}})",
 	"Enter Mojeek Search API Key": "Ingresar Clave API de Mojeek Search",
 	"Enter Number of Steps (e.g. 50)": "Ingresar Número de Pasos (p.ej., 50)",
 	"Enter Perplexity API Key": "Ingresar Clave API de Perplexity",
+	"Enter Playwright Timeout": "",
+	"Enter Playwright WebSocket URL": "",
 	"Enter proxy URL (e.g. https://user:password@host:port)": "Ingresar URL del proxy (p.ej. https://user:password@host:port)",
 	"Enter reasoning effort": "Ingresar esfuerzo de razonamiento",
 	"Enter Sampler (e.g. Euler a)": "Ingresar Muestreador (p.ej., Euler a)",
@@ -441,14 +448,17 @@
 	"Enter server host": "Ingresar host del servidor",
 	"Enter server label": "Ingresar etiqueta del servidor",
 	"Enter server port": "Ingresar puerto del servidor",
+	"Enter Sougou Search API sID": "",
+	"Enter Sougou Search API SK": "",
 	"Enter stop sequence": "Ingresar secuencia de parada",
-	"Enter system prompt": "Ingresar Indicador(prompt) del sistema",
-	"Enter system prompt here": "",
+	"Enter system prompt": "Ingresar Indicador del sistema",
+	"Enter system prompt here": "Ingresa aquí el indicador del sistema",
 	"Enter Tavily API Key": "Ingresar Clave API de Tavily",
+	"Enter Tavily Extract Depth": "",
 	"Enter the public URL of your WebUI. This URL will be used to generate links in the notifications.": "Ingresar URL pública de su WebUI. Esta URL se usará para generar enlaces en las notificaciones.",
 	"Enter Tika Server URL": "Ingresar URL del servidor Tika",
 	"Enter timeout in seconds": "Ingresar timeout en segundos",
-	"Enter to Send": "Ingresar Enviar a",
+	"Enter to Send": "'Enter' para Enviar",
 	"Enter Top K": "Ingresar Top K",
 	"Enter Top K Reranker": "Ingresar Top K Reclasificador",
 	"Enter URL (e.g. http://127.0.0.1:7860/)": "Ingresar URL (p.ej., http://127.0.0.1:7860/)",
@@ -457,12 +467,12 @@
 	"Enter Your Email": "Ingresa tu correo electrónico",
 	"Enter Your Full Name": "Ingresa su nombre completo",
 	"Enter your message": "Ingresa tu mensaje",
-	"Enter your name": "",
+	"Enter your name": "Ingresa tu nombre",
 	"Enter your new password": "Ingresa tu contraseña nueva",
 	"Enter Your Password": "Ingresa tu contraseña",
 	"Enter Your Role": "Ingresa tu rol",
-	"Enter Your Username": "Ingresa tu nombre de usuario",
-	"Enter your webhook URL": "Ingresa tu URL de EngancheWeb(webhook)",
+	"Enter Your Username": "Ingresa tu nombre de usuari@",
+	"Enter your webhook URL": "Ingresa tu URL de enganchesWeb(webhook)",
 	"Error": "Error",
 	"ERROR": "ERROR",
 	"Error accessing Google Drive: {{error}}": "Error accediendo a Google Drive: {{error}}",
@@ -485,14 +495,14 @@
 	"Explore the cosmos": "Explora el cosmos",
 	"Export": "Exportar",
 	"Export All Archived Chats": "Exportar Todos los Chats Archivados",
-	"Export All Chats (All Users)": "Exportar Todos los Chats (Todos los Usuarios)",
+	"Export All Chats (All Users)": "Exportar Todos los Chats (Todos l@s Usuari@s)",
 	"Export chat (.json)": "Exportar chat (.json)",
 	"Export Chats": "Exportar Chats",
 	"Export Config to JSON File": "Exportar Configuración a archivo JSON",
 	"Export Functions": "Exportar Funciones",
 	"Export Models": "Exportar Modelos",
 	"Export Presets": "Exportar Preajustes",
-	"Export Prompts": "Exportar Indicadores(prompts)",
+	"Export Prompts": "Exportar Indicadores",
 	"Export to CSV": "Exportar a CSV",
 	"Export Tools": "Exportar Herramientas",
 	"External": "Externo",
@@ -502,7 +512,7 @@
 	"Failed to create API Key.": "Fallo al crear la Clave API.",
 	"Failed to fetch models": "Fallo al obtener los modelos",
 	"Failed to read clipboard contents": "Fallo al leer el contenido del portapapeles",
-	"Failed to save connections": "",
+	"Failed to save connections": "Fallo al grabar las conexiones",
 	"Failed to save models configuration": "Fallo al guardar la configuración de los modelos",
 	"Failed to update settings": "Fallo al actualizar los ajustes",
 	"Failed to upload file.": "Fallo al subir el archivo.",
@@ -525,6 +535,8 @@
 	"Filter is now globally enabled": "El filtro ahora está habilitado globalmente",
 	"Filters": "Filtros",
 	"Fingerprint spoofing detected: Unable to use initials as avatar. Defaulting to default profile image.": "Se detectó suplantación de huellas: No se pueden usar las iniciales como avatar. Se establece la imagen de perfil predeterminada.",
+	"Firecrawl API Base URL": "",
+	"Firecrawl API Key": "",
 	"Fluidly stream large external response chunks": "Transmisión fluida de fragmentos de grandes respuestas externas",
 	"Focus chat input": "Enfoque entrada del chat",
 	"Folder deleted successfully": "Carpeta bollada correctamente",
@@ -535,13 +547,13 @@
 	"Forge new paths": "Forjar nuevos caminos",
 	"Form": "Formulario",
 	"Format your variables using brackets like this:": "Formatea tus variables usando corchetes así:",
-	"Forwards system user session credentials to authenticate": "",
+	"Forwards system user session credentials to authenticate": "Reenvío de las credenciales de la sesión del usuario del sistema para autenticación",
 	"Frequency Penalty": "Penalización de Frecuencia",
 	"Full Context Mode": "Modo Contexto Completo",
 	"Function": "Función",
-	"Function Calling": "Llamada de Función",
-	"Function created successfully": "Función creada exitosamente",
-	"Function deleted successfully": "Función borrada exitosamente",
+	"Function Calling": "Modo de Llamada a Funciones (Herramientas)",
+	"Function created successfully": "Función creada correctamente",
+	"Function deleted successfully": "Función borrada correctamente",
 	"Function Description": "Descripción de la Función",
 	"Function ID": "ID de la Función",
 	"Function is now globally disabled": "La Función ahora está deshabilitada globalmente",
@@ -558,7 +570,7 @@
 	"General": "General",
 	"Generate an image": "Generar una imagen",
 	"Generate Image": "Generar imagen",
-	"Generate prompt pair": "Generar par de indicadores(prompt)",
+	"Generate prompt pair": "Generar par de indicadores",
 	"Generating search query": "Generando consulta de búsqueda",
 	"Get started": "Empezar",
 	"Get started with {{WEBUI_NAME}}": "Empezar con {{WEBUI_NAME}}",
@@ -573,7 +585,7 @@
 	"Group Name": "Nombre del Grupo",
 	"Group updated successfully": "Grupo actualizado correctamente",
 	"Groups": "Grupos",
-	"Haptic Feedback": "Realimentación háptica",
+	"Haptic Feedback": "Realimentación Háptica",
 	"has no conversations.": "no tiene conversaciones.",
 	"Hello, {{name}}": "Hola, {{name}}",
 	"Help": "Ayuda",
@@ -581,7 +593,7 @@
 	"Hex Color": "Color Hex",
 	"Hex Color - Leave empty for default color": "Color Hex - Deja vacío para el color predeterminado",
 	"Hide": "Esconder",
-	"Hide Model": "",
+	"Hide Model": "Ocultar Modelo",
 	"Home": "Inicio",
 	"Host": "Host",
 	"How can I help you today?": "¿Cómo puedo ayudarte hoy?",
@@ -589,6 +601,8 @@
 	"Hybrid Search": "Búsqueda Híbrida",
 	"I acknowledge that I have read and I understand the implications of my action. I am aware of the risks associated with executing arbitrary code and I have verified the trustworthiness of the source.": "Aseguro que he leído y entiendo las implicaciones de mi acción. Soy consciente de los riesgos asociados con la ejecución de código arbitrario y he verificado la confiabilidad de la fuente.",
 	"ID": "ID",
+	"iframe Sandbox Allow Forms": "",
+	"iframe Sandbox Allow Same Origin": "",
 	"Ignite curiosity": "Encender la curiosidad",
 	"Image": "Imagen",
 	"Image Compression": "Compresión de Imagen",
@@ -596,16 +610,16 @@
 	"Image Generation (Experimental)": "Generación de Imagen (experimental)",
 	"Image Generation Engine": "Motor de Generación de Imagen",
 	"Image Max Compression Size": "Tamaño Máximo de Compresión de Imagen",
-	"Image Prompt Generation": "Indicador(prompt) para Generación de Imagen",
-	"Image Prompt Generation Prompt": "Indicador para la Generación de Inficador de Imagen",
-	"Image Settings": "Configuración de la Imágen",
+	"Image Prompt Generation": "Indicador para Generación de Imagen",
+	"Image Prompt Generation Prompt": "Indicador para la Generación de Imagen",
+	"Image Settings": "Configuración de Imágen",
 	"Images": "Imágenes",
 	"Import Chats": "Importar Chats",
 	"Import Config from JSON File": "Importar Config desde Archivo JSON",
 	"Import Functions": "Importar Funciones",
 	"Import Models": "Importar Modelos",
 	"Import Presets": "Importar Preajustes",
-	"Import Prompts": "Importar Indicadores(prompts)",
+	"Import Prompts": "Importar Indicadores",
 	"Import Tools": "Importar Herramientas",
 	"Include": "Incluir",
 	"Include `--api-auth` flag when running stable-diffusion-webui": "Incluir el señalizador `--api-auth` al ejecutar stable-diffusion-webui",
@@ -615,7 +629,7 @@
 	"Inject the entire content as context for comprehensive processing, this is recommended for complex queries.": "Inyecta el contenido completo como contexto para un procesado comprensivo, recomendado para consultas complejas.",
 	"Input commands": "Ingresar comandos",
 	"Install from Github URL": "Instalar desde la URL de Github",
-	"Instant Auto-Send After Voice Transcription": "Auto-Envio Instantaneo tras la Transcripción de Voz",
+	"Instant Auto-Send After Voice Transcription": "AutoEnvio Instantaneo tras la Transcripción de Voz",
 	"Integration": "Integración",
 	"Interface": "Interface",
 	"Invalid file format.": "Formato de archivo Inválido.",
@@ -631,8 +645,8 @@
 	"June": "Junio",
 	"Jupyter Auth": "Autenticación de Jupyter",
 	"Jupyter URL": "URL de Jupyter",
-	"JWT Expiration": "Expiración del JWT",
-	"JWT Token": "Token JWT",
+	"JWT Expiration": "Expiración del JSON Web Token (JWT)",
+	"JWT Token": "JSON Web Token",
 	"Kagi Search API Key": "Clave API de Kagi Search",
 	"Keep Alive": "Mantener Vivo",
 	"Key": "Clave",
@@ -648,19 +662,20 @@
 	"Kokoro.js Dtype": "Kokoro.js DType",
 	"Label": "Etiqueta",
 	"Landing Page Mode": "Modo Página Inicial",
-	"Language": "Lenguaje",
+	"Language": "Idioma",
+	"Language Locales": "",
 	"Last Active": "Última Actividad",
 	"Last Modified": "Último Modificación",
 	"Last reply": "Última Respuesta",
 	"LDAP": "LDAP",
 	"LDAP server updated": "Servidor LDAP actualizado",
 	"Leaderboard": "Tabla Clasificatoria",
-	"Learn more about OpenAPI tool servers.": "",
+	"Learn more about OpenAPI tool servers.": "Saber más sobre los servidores de herramientas OpenAPI",
 	"Leave empty for unlimited": "Dejar vacío para ilimitado",
 	"Leave empty to include all models from \"{{url}}/api/tags\" endpoint": "Dejar vacío para incluir todos los modelos desde el endpoint \"{{url}}/api/tags\"",
 	"Leave empty to include all models from \"{{url}}/models\" endpoint": "Dejar vacío para incluir todos los modelos desde el endpoint \"{{url}}/models\"",
 	"Leave empty to include all models or select specific models": "Dejar vacío para incluir todos los modelos o Seleccionar modelos específicos",
-	"Leave empty to use the default prompt, or enter a custom prompt": "Dejar vacío para usar el indicador(prompt) predeterminado, o Ingresar un indicador(prompt) personalizado",
+	"Leave empty to use the default prompt, or enter a custom prompt": "Dejar vacío para usar el indicador predeterminado, o Ingresar un indicador personalizado",
 	"Leave model field empty to use the default model.": "Dejar vacío el campo modelo para usar el modelo predeterminado.",
 	"License": "Licencia",
 	"Light": "Claro",
@@ -676,7 +691,7 @@
 	"Lost": "Perdido",
 	"LTR": "LTR",
 	"Made by Open WebUI Community": "Creado por la Comunidad Open-WebUI",
-	"Make sure to enclose them with": "Asegúrate de adjuntarlos con",
+	"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",
 	"Manage Direct Connections": "Gestionar Conexiones Directas",
@@ -700,14 +715,13 @@
 	"Memory updated successfully": "Memoria actualizada correctamente",
 	"Merge Responses": "Fusionar Respuestas",
 	"Message rating should be enabled to use this feature": "Para usar esta función debe estar habilitada la calificación de mensajes",
-	"Messages you send after creating your link won't be shared. Users with the URL will be able to view the shared chat.": "Los mensajes que envíe después de la creación del enlace no se compartirán. Los usuarios con la URL del enlace podrán ver el chat compartido.",
+	"Messages you send after creating your link won't be shared. Users with the URL will be able to view the shared chat.": "Los mensajes que envíe después de la creación del enlace no se compartirán. L@s usuari@s con la URL del enlace podrán ver el chat compartido.",
 	"Min P": "Min P",
-	"Minimum Score": "Puntuación Mínima",
 	"Mirostat": "Mirostat",
 	"Mirostat Eta": "Mirostat Eta",
 	"Mirostat Tau": "Mirostat Tau",
-	"Mistral OCR": "",
-	"Mistral OCR API Key required.": "",
+	"Mistral OCR": "OCR Mistral",
+	"Mistral OCR API Key required.": "Clave API de Mistral OCR requerida",
 	"Model": "Modelo",
 	"Model '{{modelName}}' has been successfully downloaded.": "Modelo '{{modelName}}' se ha descargado correctamente.",
 	"Model '{{modelTag}}' is already in queue for downloading.": "Modelo '{{modelTag}}' ya está en cola para descargar.",
@@ -759,15 +773,15 @@
 	"No results found": "No se encontraron resultados",
 	"No search query generated": "No se generó ninguna consulta de búsqueda",
 	"No source available": "No hay fuente disponible",
-	"No users were found.": "No se encontraron usuarios.",
+	"No users were found.": "No se encontraron usuari@s.",
 	"No valves to update": "No hay válvulas para actualizar",
 	"None": "Ninguno",
 	"Not factually correct": "No es correcto en todos los aspectos",
 	"Not helpful": "No aprovechable",
 	"Note: If you set a minimum score, the search will only return documents with a score greater than or equal to the minimum score.": "Nota: Si estableces una puntuación mínima, la búsqueda sólo devolverá documentos con una puntuación mayor o igual a la puntuación mínima establecida.",
 	"Notes": "Notas",
-	"Notification Sound": "Notificación Sonido",
-	"Notification Webhook": "Notificación EngancheWeb(webhook)",
+	"Notification Sound": "Notificación Sonora",
+	"Notification Webhook": "Notificación Enganchada (webhook)",
 	"Notifications": "Notificaciones",
 	"November": "Noviembre",
 	"num_gpu (Ollama)": "num_gpu (capas Ollama)",
@@ -786,7 +800,7 @@
 	"Only alphanumeric characters and hyphens are allowed": "Sólo están permitidos caracteres alfanuméricos y guiones",
 	"Only alphanumeric characters and hyphens are allowed in the command string.": "Sólo están permitidos en la cadena de comandos caracteres alfanuméricos y guiones.",
 	"Only collections can be edited, create a new knowledge base to edit/add documents.": "Solo se pueden editar las colecciones, para añadir/editar documentos hay que crear una nueva base de conocimientos",
-	"Only select users and groups with permission can access": "Solo pueden acceder los usuarios y grupos con permiso",
+	"Only select users and groups with permission can access": "Solo pueden acceder l@s usuari@s y grupos con permiso",
 	"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.",
@@ -794,7 +808,7 @@
 	"Open file": "Abrir archivo",
 	"Open in full screen": "Abrir en pantalla completa",
 	"Open new chat": "Abrir nuevo chat",
-	"Open WebUI can use tools provided by any OpenAPI server.": "",
+	"Open WebUI can use tools provided by any OpenAPI server.": "Open-WebUI puede usar herramientas proporcionadas por cualquier servidor OpenAPI",
 	"Open WebUI uses faster-whisper internally.": "Open-WebUI usa faster-whisper internamente.",
 	"Open WebUI uses SpeechT5 and CMU Arctic speaker embeddings.": "Open-WebUI usa SpeechT5 y la incrustración de locutores de CMU Arctic.",
 	"Open WebUI version (v{{OPEN_WEBUI_VERSION}}) is lower than required version (v{{REQUIRED_VERSION}})": "La versión de Open-WebUI (v{{OPEN_WEBUI_VERSION}}) es inferior a la versión  (v{{REQUIRED_VERSION}}) requerida",
@@ -804,16 +818,16 @@
 	"OpenAI API Key is required.": "Clave API de OpenAI requerida.",
 	"OpenAI API settings updated": "Ajustes de API OpenAI actualizados",
 	"OpenAI URL/Key required.": "URL/Clave de OpenAI requerida.",
-	"openapi.json Path": "",
+	"openapi.json Path": "Ruta a openapi.json",
 	"or": "o",
-	"Organize your users": "Organiza tus usuarios",
+	"Organize your users": "Organiza tus usuari@s",
 	"Other": "Otro",
 	"OUTPUT": "SALIDA",
 	"Output format": "Formato de salida",
 	"Overview": "Vista General",
 	"page": "página",
 	"Password": "Contraseña",
-	"Paste Large Text as File": "Pegar el texto largo como archivo",
+	"Paste Large Text as File": "Pegar el Texto Largo como Archivo",
 	"PDF document (.pdf)": "Documento PDF (.pdf)",
 	"PDF Extract Images (OCR)": "Extraer imágenes del PDF (OCR)",
 	"pending": "pendiente",
@@ -829,15 +843,17 @@
 	"Pipeline deleted successfully": "Tubería borrada correctamente",
 	"Pipeline downloaded successfully": "Tubería descargada correctamente",
 	"Pipelines": "Tuberías",
-	"Pipelines Not Detected": "Tuberías(pipelines) No Detectada",
+	"Pipelines Not Detected": "Servicio de Tuberías (Pipelines) No Detectado",
 	"Pipelines Valves": "Válvulas de Tuberías",
 	"Plain text (.txt)": "Texto plano (.txt)",
-	"Playground": "Recreo",
+	"Playground": "Zona de Pruebas",
+	"Playwright Timeout (ms)": "",
+	"Playwright WebSocket URL": "",
 	"Please carefully review the following warnings:": "Por favor revisar cuidadosamente los siguientes avisos:",
 	"Please do not close the settings page while loading the model.": "Por favor no cerrar la página de ajustes mientras se está descargando el modelo.",
-	"Please enter a prompt": "Por favor ingresar un indicador(prompt)",
-	"Please enter a valid path": "",
-	"Please enter a valid URL": "",
+	"Please enter a prompt": "Por favor ingresar un indicador",
+	"Please enter a valid path": "Por favor, ingresa una ruta válida",
+	"Please enter a valid URL": "Por favor, ingresa una URL válida",
 	"Please fill in all fields.": "Por favor rellenar todos los campos.",
 	"Please select a model first.": "Por favor primero seleccionar un modelo.",
 	"Please select a model.": "Por favor seleccionar un modelo.",
@@ -851,20 +867,20 @@
 	"Previous 7 days": "7 días previos",
 	"Private": "Privado",
 	"Profile Image": "Imagen del Perfil",
-	"Prompt": "Indicador(prompt)",
-	"Prompt (e.g. Tell me a fun fact about the Roman Empire)": "Indicador(prompt) (p.ej. Cuéntame una cosa divertida sobre el Imperio Romano)",
-	"Prompt Autocompletion": "Autocompletado del Indicador(prompt)",
-	"Prompt Content": "Contenido del Indicador(prompt)",
-	"Prompt created successfully": "Indicador(prompt) creado exitosamente",
-	"Prompt suggestions": "Indicadores(prompts) Sugeridos",
-	"Prompt updated successfully": "Indicador(prompt) actualizado correctamente",
-	"Prompts": "Indicadores(prompts)",
-	"Prompts Access": "Acceso a Indicadores(prompts)",
+	"Prompt": "Indicador",
+	"Prompt (e.g. Tell me a fun fact about the Roman Empire)": "Indicador (p.ej. Cuéntame una cosa divertida sobre el Imperio Romano)",
+	"Prompt Autocompletion": "Autocompletado del Indicador",
+	"Prompt Content": "Contenido del Indicador",
+	"Prompt created successfully": "Indicador creado exitosamente",
+	"Prompt suggestions": "Indicadores Sugeridos",
+	"Prompt updated successfully": "Indicador actualizado correctamente",
+	"Prompts": "Indicadores",
+	"Prompts Access": "Acceso a Indicadores",
 	"Prompts Public Sharing": "",
 	"Public": "Público",
 	"Pull \"{{searchValue}}\" from Ollama.com": "Extraer \"{{searchValue}}\" desde Ollama.com",
 	"Pull a model from Ollama.com": "Extraer un modelo desde Ollama.com",
-	"Query Generation Prompt": "Consulta de Generación del Indicador(prompt)",
+	"Query Generation Prompt": "Indicador para la Consulta de Generación",
 	"RAG Template": "Plantilla del RAG",
 	"Rating": "Calificación",
 	"Re-rank models by topic similarity": "Reclasificar modelos por similitud temática",
@@ -874,17 +890,20 @@
 	"Record voice": "Grabar voz",
 	"Redirecting you to Open WebUI Community": "Redireccionando a la Comunidad Open-WebUI",
 	"Reduces the probability of generating nonsense. A higher value (e.g. 100) will give more diverse answers, while a lower value (e.g. 10) will be more conservative.": "Reduce la probabilidad de generación sin sentido. Un valor más alto (p.ej. 100) dará respuestas más diversas, mientras que un valor más bajo (p.ej. 10) será más conservador.",
-	"Refer to yourself as \"User\" (e.g., \"User is learning Spanish\")": "Referir a ti mismo como \"Usuario\" (p.ej. \"Usuario está aprendiendo Español\")",
+	"Refer to yourself as \"User\" (e.g., \"User is learning Spanish\")": "Referir a ti mismo como \"Usuari@\" (p.ej. \"Usuari@ está aprendiendo Español\")",
 	"References from": "Referencias desde",
 	"Refused when it shouldn't have": "Rechazado cuando no debería haberlo hecho",
 	"Regenerate": "Regenerar",
+	"Reindex": "Reindexar",
+	"Reindex Knowledge Base Vectors": "Reindexar Base Vectorial de Conocimiento",
 	"Release Notes": "Notas de la Versión",
 	"Relevance": "Relevancia",
+	"Relevance Threshold": "",
 	"Remove": "Eliminar",
 	"Remove Model": "Eliminar Modelo",
 	"Rename": "Renombrar",
 	"Reorder Models": "Reordenar Modelos",
-	"Repeat Last N": "Repetir Últimas N",
+	"Repeat Last N": "Repetición - Últimos N",
 	"Repeat Penalty (Ollama)": "Penalización Repetición (Ollama)",
 	"Reply in Thread": "Responder en Hilo",
 	"Request Mode": "Modo de Petición",
@@ -928,8 +947,8 @@
 	"Search Knowledge": "Buscar Conocimiento",
 	"Search Models": "Buscar Modelos",
 	"Search options": "Opciones de Búsqueda",
-	"Search Prompts": "Buscar Indicadores(prompts)",
-	"Search Result Count": "Recuento de resultados de búsqueda",
+	"Search Prompts": "Buscar Indicadores",
+	"Search Result Count": "Número de resultados de la búsqueda",
 	"Search the internet": "Buscar en internet",
 	"Search Tools": "Buscar Herramientas",
 	"SearchApi API Key": "Clave API de SearchApi",
@@ -984,7 +1003,7 @@
 	"Set whisper model": "Establecer modelo whisper (transcripción)",
 	"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.": "Establece un sesgo plano contra los tokens que han aparecido al menos una vez. Un valor más alto (p.ej. 1.5) penalizará las repeticiones más fuertemente, mientras que un valor más bajo (p.ej. 0.9) será más indulgente. En 0, está deshabilitado.",
 	"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.": "Establece un sesgo escalado contra los tokens para penalizar las repeticiones, basado en cuántas veces han aparecido. Un valor más alto (por ejemplo, 1.5) penalizará las repeticiones más fuertemente, mientras que un valor más bajo (por ejemplo, 0.9) será más indulgente. En 0, está deshabilitado.",
-	"Sets how far back for the model to look back to prevent repetition.": "Establece cuánto debe mirar atrás el modelo para prevenir la repetición.",
+	"Sets how far back for the model to look back to prevent repetition.": "Establece cuántos tokens debe mirar atrás el modelo para prevenir la repetición. ",
 	"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.": "Establece la semilla de números aleatorios a usar para la generación. Establecer esto en un número específico hará que el modelo genere el mismo texto para el mismo indicador(prompt).",
 	"Sets the size of the context window used to generate the next token.": "Establece el tamaño de la ventana del contexto utilizada para generar el siguiente token.",
 	"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.": "Establece las secuencias de parada a usar. Cuando se encuentre este patrón, el LLM dejará de generar texto y retornará. Se pueden establecer varios patrones de parada especificando separadamente múltiples parámetros de parada en un archivo de modelo.",
@@ -996,7 +1015,7 @@
 	"Sharing Permissions": "",
 	"Show": "Mostrar",
 	"Show \"What's New\" modal on login": "Mostrar modal \"Qué hay de Nuevo\" al iniciar sesión",
-	"Show Admin Details in Account Pending Overlay": "Mostrar Detalles Admin en la Sobrecapa Cuenta Pendiente",
+	"Show Admin Details in Account Pending Overlay": "Mostrar Detalles Admin en la sobrecapa de 'Cuenta Pendiente'",
 	"Show Model": "",
 	"Show shortcuts": "Mostrar Atajos",
 	"Show your support!": "¡Muestra tu apoyo!",
@@ -1009,15 +1028,17 @@
 	"Sign up to {{WEBUI_NAME}}": "Crear una Cuenta en {{WEBUI_NAME}}",
 	"Signing in to {{WEBUI_NAME}}": "Iniciando Sesión en {{WEBUI_NAME}}",
 	"sk-1234": "sk-1234",
+	"Sougou Search API sID": "",
+	"Sougou Search API SK": "",
 	"Source": "Fuente",
 	"Speech Playback Speed": "Velocidad de Reproducción de Voz",
 	"Speech recognition error: {{error}}": "Error en reconocimiento de voz: {{error}}",
 	"Speech-to-Text Engine": "Motor Voz a Texto(STT)",
 	"Stop": "Detener",
-	"Stop Sequence": "Detener Secuencia",
-	"Stream Chat Response": "Transmitir Respuesta del Chat",
+	"Stop Sequence": "Secuencia de Parada",
+	"Stream Chat Response": "Transmisión Directa de la  Respuesta del Chat",
 	"STT Model": "Modelo STT",
-	"STT Settings": "Ajustes STT",
+	"STT Settings": "Ajustes Voz a Texto (STT)",
 	"Subtitle (e.g. about the Roman Empire)": "Subtítulo (p.ej. sobre el Imperio Romano)",
 	"Success": "Correcto",
 	"Successfully updated.": "Actualizado correctamente.",
@@ -1027,15 +1048,16 @@
 	"Sync directory": "Sincroniza Directorio",
 	"System": "Sistema",
 	"System Instructions": "Instrucciones del sistema",
-	"System Prompt": "Indicador(prompt) del sistema",
+	"System Prompt": "Indicador del sistema",
 	"Tags": "Etiquetas",
 	"Tags Generation": "Generación de Etiquetas",
-	"Tags Generation Prompt": "Indicador(prompt) para la Generación de Etiquetas",
+	"Tags Generation Prompt": "Indicador para la Generación de Etiquetas",
 	"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.": "El Muestreo de cola libre(TFS_Z) es usado para reducir el impacto de los tokens menos probables en la salida. Un valor más alto (p.ej. 2.0) reduce más fuertemente el impacto, mientras que un valor de 1.0 deshabilita este ajuste.",
 	"Talk to model": "Hablar con el modelo",
 	"Tap to interrupt": "Toca para interrumpir",
 	"Tasks": "Tareas",
 	"Tavily API Key": "Clave API de Tavily",
+	"Tavily Extract Depth": "",
 	"Tell us more:": "Dinos algo más:",
 	"Temperature": "Temperatura",
 	"Template": "Plantilla",
@@ -1045,12 +1067,12 @@
 	"Tfs Z": "TFS Z",
 	"Thanks for your feedback!": "¡Gracias por tu realimentación!",
 	"The Application Account DN you bind with for search": "Cuenta DN de la aplicación vinculada para búsqueda",
-	"The base to search for users": "La base para buscar usuarios",
+	"The base to search for users": "La base para buscar usuari@s",
 	"The batch size determines how many text requests are processed together at once. A higher batch size can increase the performance and speed of the model, but it also requires more memory.": "El tamaño de lote determina cuántas solicitudes de texto se procesan juntas de una vez. Un tamaño de lote más alto puede aumentar el rendimiento y la velocidad del modelo, pero también requiere más memoria.",
 	"The developers behind this plugin are passionate volunteers from the community. If you find this plugin helpful, please consider contributing to its development.": "L@s desarrolladores de este complemento son apasionad@s voluntari@s de la comunidad. Si este complemento te es útil, por favor considera contribuir a su desarrollo.",
 	"The evaluation leaderboard is based on the Elo rating system and is updated in real-time.": "La tabla clasificatoria de evaluación se basa en el sistema de clasificación Elo y se actualiza en tiempo real.",
-	"The LDAP attribute that maps to the mail that users use to sign in.": "El atributo LDAP que mapea el correo que los usuarios utilizan para iniciar sesión.",
-	"The LDAP attribute that maps to the username that users use to sign in.": "El atributo LDAP que mapea el nombre de usuario que los usuarios utilizan para iniciar sesión.",
+	"The LDAP attribute that maps to the mail that users use to sign in.": "El atributo LDAP que mapea el correo que l@s usuari@s utilizan para iniciar sesión.",
+	"The LDAP attribute that maps to the username that users use to sign in.": "El atributo LDAP que mapea el nombre de usuari@ que l@s usuari@s utilizan para iniciar sesión.",
 	"The leaderboard is currently in beta, and we may adjust the rating calculations as we refine the algorithm.": "La tabla clasificatoria está actualmente en beta, por lo que los cálculos de clasificación pueden reajustarse a medida que se refina el algoritmo.",
 	"The maximum file size in MB. If the file size exceeds this limit, the file will not be uploaded.": "El tamaño máximo del archivo en MB. Si el tamaño del archivo supera este límite, el archivo no se subirá.",
 	"The maximum number of files that can be used at once in chat. If the number of files exceeds this limit, the files will not be uploaded.": "El número máximo de archivos que se pueden utilizar a la vez en el chat. Si se supera este límite, los archivos no se subirán.",
@@ -1060,10 +1082,10 @@
 	"Thinking...": "Pensando...",
 	"This action cannot be undone. Do you wish to continue?": "Esta acción no se puede deshacer. ¿Desea continuar?",
 	"This channel was created on {{createdAt}}. This is the very beginning of the {{channelName}} channel.": "Este canal fue creado el {{createdAt}}. Este es el comienzo del canal {{channelName}}.",
-	"This chat won’t appear in history and your messages will not be saved.": "",
+	"This chat won’t appear in history and your messages will not be saved.": "Este chat no aparecerá en el historial y los mensajes no se guardarán.",
 	"This ensures that your valuable conversations are securely saved to your backend database. Thank you!": "Esto garantiza que sus valiosas conversaciones se guardan de forma segura en tu base de datos del servidor trasero (backend). ¡Gracias!",
 	"This is an experimental feature, it may not function as expected and is subject to change at any time.": "Esta es una característica experimental, por lo que puede no funcionar como se esperaba y está sujeta a cambios en cualquier momento.",
-	"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.": "Esta opción controla cuántos tokens se conservan cuando se actualiza el contexto. Por ejemplo, si se establece en 2, se conservarán los últimos 2 tokens del contexto de la conversación. Conservar el contexto puede ayudar a mantener la continuidad de una conversación, pero puede reducir la habilidad para responder a nuevos temas.",
+	"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.": "Esta opción controla cuántos tokens se conservan cuando se actualiza el contexto. Por ejemplo, si se establece en 2, se conservarán los primeros 2 tokens del contexto de la conversación. Conservar el contexto puede ayudar a mantener la continuidad de una conversación, pero puede reducir la habilidad para responder a nuevos temas.",
 	"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.": "Esta opción establece el número máximo de tokens que el modelo puede generar en sus respuestas. Aumentar este límite permite al modelo proporcionar respuestas más largas, pero también puede aumentar la probabilidad de que se genere contenido inútil o irrelevante.",
 	"This option will delete all existing files in the collection and replace them with newly uploaded files.": "Esta opción eliminará todos los archivos existentes en la colección y los reemplazará con los nuevos archivos subidos.",
 	"This response was generated by \"{{model}}\"": "Esta respuesta fue generada por \"{{model}}\"",
@@ -1084,11 +1106,11 @@
 	"Title Auto-Generation": "AutoGeneración de Títulos",
 	"Title cannot be an empty string.": "El título no puede ser una cadena vacía.",
 	"Title Generation": "Generación de Títulos",
-	"Title Generation Prompt": "Indicador(prompt) para la Generación de Título",
+	"Title Generation Prompt": "Indicador para la Generación de Título",
 	"TLS": "TLS",
 	"To access the available model names for downloading,": "Para acceder a los nombres de modelos disponibles para descargar,",
 	"To access the GGUF models available for downloading,": "Para acceder a los modelos GGUF disponibles para descargar,",
-	"To access the WebUI, please reach out to the administrator. Admins can manage user statuses from the Admin Panel.": "Para acceder a WebUI, por favor contacte con Admins. L@s administradores pueden gestionar los estados de l@s usuarios desde el panel de administración.",
+	"To access the WebUI, please reach out to the administrator. Admins can manage user statuses from the Admin Panel.": "Para acceder a WebUI, por favor contacte con Admins. L@s administradores pueden gestionar los estados de l@s usuari@s esde el panel de administración.",
 	"To attach knowledge base here, add them to the \"Knowledge\" workspace first.": "Para adjuntar la base de conocimientos aquí, primero añadirla a \"Conocimiento\" en el área de trabajo.",
 	"To learn more about available endpoints, visit our documentation.": "Para aprender más sobre los endpoints disponibles, visite nuestra documentación.",
 	"To protect your privacy, only ratings, model IDs, tags, and metadata are shared from your feedback—your chat logs remain private and are not included.": "Para proteger tu privacidad, de tu realimentación solo se comparten las calificaciones, IDs de modelo, etiquetas y metadatos; tus chat registrados permanecen privados y no se incluyen.",
@@ -1108,12 +1130,12 @@
 	"Tool ID": "ID de la Herramienta",
 	"Tool imported successfully": "Herramienta importada correctamente",
 	"Tool Name": "Nombre de la Herramienta",
-	"Tool Servers": "",
+	"Tool Servers": "Servidores de Herraientas",
 	"Tool updated successfully": "Herramienta actualizada correctamente",
 	"Tools": "Herramientas",
 	"Tools Access": "Acceso a Herramientas",
 	"Tools are a function calling system with arbitrary code execution": "Las herramientas son un sistema de llamada de funciones con ejecución de código arbitrario",
-	"Tools Function Calling Prompt": "Indicador(prompt) para la Función de Llamada a las Herramientas",
+	"Tools Function Calling Prompt": "Indicador para la Función de Llamada a las Herramientas",
 	"Tools have a function calling system that allows arbitrary code execution": "Las herramientas tienen un sistema de llamadas de funciones que permite la ejecución de código arbitrario",
 	"Tools have a function calling system that allows arbitrary code execution.": "Las herramientas tienen un sistema de llamada de funciones que permite la ejecución de código arbitrario.",
 	"Tools Public Sharing": "",
@@ -1123,9 +1145,9 @@
 	"Transformers": "Transformadores",
 	"Trouble accessing Ollama?": "¿Problemas para acceder a Ollama?",
 	"Trust Proxy Environment": "Entorno Proxy Confiable",
-	"TTS Model": "TTS Modelo",
-	"TTS Settings": "TTS Ajustes",
-	"TTS Voice": "TTS Voz",
+	"TTS Model": "Modelo TTS",
+	"TTS Settings": "Ajustes Texto a Voz (TTS)",
+	"TTS Voice": "Voz TTS",
 	"Type": "Tipo",
 	"Type Hugging Face Resolve (Download) URL": "Escribir la URL de Hugging Face Resolve (Descarga)",
 	"Uh-oh! There was an issue with the response.": "¡Vaya! Hubo un problema con la respuesta.",
@@ -1154,18 +1176,20 @@
 	"Upload Progress": "Progreso de la Subida",
 	"URL": "URL",
 	"URL Mode": "Modo URL",
-	"Use '#' in the prompt input to load and include your knowledge.": "Utilizar '#' en el indicador(prompt) para cargar e incluir tu conocimiento.",
+	"Use '#' in the prompt input to load and include your knowledge.": "Utilizar '#' en el indicador para cargar e incluir tu conocimiento.",
 	"Use Gravatar": "Usar Gravatar",
-	"Use groups to group your users and assign permissions.": "Usar grupos para agrupar a usuarios y asignar permisos.",
+	"Use groups to group your users and assign permissions.": "Usar grupos para agrupar a usuari@s y asignar permisos.",
 	"Use Initials": "Usar Iniciales",
+	"Use no proxy to fetch page contents.": "No usar proxy para extraer contenidos",
+	"Use proxy designated by http_proxy and https_proxy environment variables to fetch page contents.": "Usar el proxy asignado en las variables del entorno http_proxy y/o https_proxy para extraer contenido",
 	"use_mlock (Ollama)": "use_mlock (Ollama)",
 	"use_mmap (Ollama)": "use_mmap (Ollama)",
-	"user": "usuario",
-	"User": "Usuario",
-	"User location successfully retrieved.": "Ubicación del usuario obtenida correctamente.",
-	"User Webhooks": "Usuario EnganchesWeb(webhooks)",
-	"Username": "Nombre de Usuario",
-	"Users": "Usuarios",
+	"user": "usuari@",
+	"User": "Usuari@",
+	"User location successfully retrieved.": "Ubicación de usuari@ obtenida correctamente.",
+	"User Webhooks": "Usuari@ EnganchesWeb(webhooks)",
+	"Username": "Nombre de Usuari@",
+	"Users": "Usuari@s",
 	"Using the default arena model with all models. Click the plus button to add custom models.": "Usando el modelo de arena predeterminado con todos los modelos. Pulsar en el botón + para agregar modelos personalizados.",
 	"Utilize": "Utilizar",
 	"Valid time units:": "Unidades de tiempo válidas:",
@@ -1173,8 +1197,9 @@
 	"Valves updated": "Válvulas actualizadas",
 	"Valves updated successfully": "Válvulas actualizados correctamente",
 	"variable": "variable",
-	"variable to have them replaced with clipboard content.": "variable para ser reemplazada con el contenido del portapapeles.",
+	"variable to have them replaced with clipboard content.": "hace que la variable sea reemplazada con el contenido del portapapeles.",
 	"Verify Connection": "Verificar Conexión",
+	"Verify SSL Certificate": "",
 	"Version": "Versión",
 	"Version {{selectedVersion}} of {{totalVersions}}": "Versión {{selectedVersion}} de {{totalVersions}}",
 	"View Replies": "Ver Respuestas",
@@ -1184,16 +1209,17 @@
 	"Voice Input": "Entrada de Voz",
 	"Warning": "Aviso",
 	"Warning:": "Aviso:",
-	"Warning: Enabling this will allow users to upload arbitrary code on the server.": "Aviso: Habilitar esto permitirá a los usuarios subir código arbitrario al servidor.",
+	"Warning: Enabling this will allow users to upload arbitrary code on the server.": "Aviso: Habilitar esto permitirá a l@s usuari@s subir código arbitrario al servidor.",
 	"Warning: If you update or change your embedding model, you will need to re-import all documents.": "Aviso: Si actualizas o cambias el modelo de incrustacción, necesitarás re-importar todos los documentos.",
 	"Warning: Jupyter execution enables arbitrary code execution, posing severe security risks—proceed with extreme caution.": "Aviso: La ejecución Jupyter habilita la ejecución de código arbitrario, planteando graves riesgos de seguridad; Proceder con extrema precaución.",
 	"Web": "Web",
 	"Web API": "API Web",
+	"Web Loader Engine": "",
 	"Web Search": "Búsqueda Web",
 	"Web Search Engine": "Motor Búsqueda Web",
 	"Web Search in Chat": "Búsqueda Web en Chat",
 	"Web Search Query Generation": "Generación de Consulta Búsqueda Web",
-	"Webhook URL": "URL EngancheWeb(Webhook)",
+	"Webhook URL": "URL EnganchesWeb(Webhook)",
 	"WebUI Settings": "WebUI Ajustes",
 	"WebUI URL": "WebUI URL",
 	"WebUI will make requests to \"{{url}}\"": "",
@@ -1202,7 +1228,7 @@
 	"What are you trying to achieve?": "¿Qué estás tratando de conseguir?",
 	"What are you working on?": "¿En qué estás trabajando?",
 	"What’s New in": "Que hay de Nuevo en",
-	"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.": "Cuando está habilitado, el modelo responderá a cada mensaje de chat en tiempo real, generando una respuesta tan pronto como el usuario envíe un mensaje. Este modo es útil para aplicaciones de chat en vivo, pero puede afectar al rendimiento en equipos más lentos.",
+	"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.": "Cuando está habilitado, el modelo responderá a cada mensaje de chat en tiempo real, generando una respuesta tan pronto como se envíe un mensaje. Este modo es útil para aplicaciones de chat en vivo, pero puede afectar al rendimiento en equipos más lentos.",
 	"wherever you are": "dondequiera que estés",
 	"Whisper (Local)": "Whisper (Local)",
 	"Why?": "¿Por qué?",
@@ -1212,7 +1238,7 @@
 	"Workspace": "Espacio de Trabajo",
 	"Workspace Permissions": "Permisos del Espacio de Trabajo",
 	"Write": "Escribir",
-	"Write a prompt suggestion (e.g. Who are you?)": "Escribe una sugerencia de Indicador(prompt) (p.ej. ¿quién eres?)",
+	"Write a prompt suggestion (e.g. Who are you?)": "Escribe una sugerencia de indicador (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...",
 	"Write your model template content here": "Escribe el contenido de la plantilla de tu modelo aquí",

+ 28 - 2
src/lib/i18n/locales/et-EE/translation.json

@@ -62,6 +62,7 @@
 	"Allow Chat Deletion": "Luba vestluse kustutamine",
 	"Allow Chat Edit": "Luba vestluse muutmine",
 	"Allow File Upload": "Luba failide üleslaadimine",
+	"Allow Multiple Models in Chat": "",
 	"Allow non-local voices": "Luba mitte-lokaalsed hääled",
 	"Allow Temporary Chat": "Luba ajutine vestlus",
 	"Allow User Location": "Luba kasutaja asukoht",
@@ -79,6 +80,7 @@
 	"and": "ja",
 	"and {{COUNT}} more": "ja veel {{COUNT}}",
 	"and create a new shared link.": "ja looge uus jagatud link.",
+	"Android": "",
 	"API Base URL": "API baas-URL",
 	"API Key": "API võti",
 	"API Key created.": "API võti loodud.",
@@ -141,7 +143,6 @@
 	"Brave Search API Key": "Brave Search API võti",
 	"By {{name}}": "Autor: {{name}}",
 	"Bypass Embedding and Retrieval": "Möödaminek sisestamisest ja taastamisest",
-	"Bypass SSL verification for Websites": "Möödaminek veebisaitide SSL-kontrollimisest",
 	"Calendar": "Kalender",
 	"Call": "Kõne",
 	"Call feature is not supported when using Web STT engine": "Kõnefunktsioon ei ole Web STT mootorit kasutades toetatud",
@@ -303,6 +304,7 @@
 	"Deleted User": "Kustutatud kasutaja",
 	"Describe your knowledge base and objectives": "Kirjeldage oma teadmiste baasi ja eesmärke",
 	"Description": "Kirjeldus",
+	"Detect Artifacts Automatically": "",
 	"Didn't fully follow instructions": "Ei järginud täielikult juhiseid",
 	"Direct": "",
 	"Direct Connections": "Otsesed ühendused",
@@ -358,6 +360,7 @@
 	"e.g. my_filter": "nt minu_filter",
 	"e.g. my_tools": "nt minu_toriistad",
 	"e.g. Tools for performing various operations": "nt tööriistad mitmesuguste operatsioonide teostamiseks",
+	"e.g., en-US,ja-JP (leave blank for auto-detect)": "",
 	"Edit": "Muuda",
 	"Edit Arena Model": "Muuda Areena mudelit",
 	"Edit Channel": "Muuda kanalit",
@@ -407,6 +410,8 @@
 	"Enter Document Intelligence Key": "Sisestage dokumendi intelligentsuse võti",
 	"Enter domains separated by commas (e.g., example.com,site.org)": "Sisestage domeenid komadega eraldatult (nt example.com,site.org)",
 	"Enter Exa API Key": "Sisestage Exa API võti",
+	"Enter Firecrawl API Base URL": "",
+	"Enter Firecrawl API Key": "",
 	"Enter Github Raw URL": "Sisestage Github toorURL",
 	"Enter Google PSE API Key": "Sisestage Google PSE API võti",
 	"Enter Google PSE Engine Id": "Sisestage Google PSE mootori ID",
@@ -424,6 +429,8 @@
 	"Enter Mojeek Search API Key": "Sisestage Mojeek Search API võti",
 	"Enter Number of Steps (e.g. 50)": "Sisestage sammude arv (nt 50)",
 	"Enter Perplexity API Key": "Sisestage Perplexity API võti",
+	"Enter Playwright Timeout": "",
+	"Enter Playwright WebSocket URL": "",
 	"Enter proxy URL (e.g. https://user:password@host:port)": "Sisestage puhverserveri URL (nt https://kasutaja:parool@host:port)",
 	"Enter reasoning effort": "Sisestage arutluspingutus",
 	"Enter Sampler (e.g. Euler a)": "Sisestage valimismeetod (nt Euler a)",
@@ -441,10 +448,13 @@
 	"Enter server host": "Sisestage serveri host",
 	"Enter server label": "Sisestage serveri silt",
 	"Enter server port": "Sisestage serveri port",
+	"Enter Sougou Search API sID": "",
+	"Enter Sougou Search API SK": "",
 	"Enter stop sequence": "Sisestage lõpetamise järjestus",
 	"Enter system prompt": "Sisestage süsteemi vihjed",
 	"Enter system prompt here": "",
 	"Enter Tavily API Key": "Sisestage Tavily API võti",
+	"Enter Tavily Extract Depth": "",
 	"Enter the public URL of your WebUI. This URL will be used to generate links in the notifications.": "Sisestage oma WebUI avalik URL. Seda URL-i kasutatakse teadaannetes linkide genereerimiseks.",
 	"Enter Tika Server URL": "Sisestage Tika serveri URL",
 	"Enter timeout in seconds": "Sisestage aegumine sekundites",
@@ -525,6 +535,8 @@
 	"Filter is now globally enabled": "Filter on nüüd globaalselt lubatud",
 	"Filters": "Filtrid",
 	"Fingerprint spoofing detected: Unable to use initials as avatar. Defaulting to default profile image.": "Tuvastati sõrmejälje võltsimine: initsiaalide kasutamine avatarina pole võimalik. Kasutatakse vaikimisi profiilikujutist.",
+	"Firecrawl API Base URL": "",
+	"Firecrawl API Key": "",
 	"Fluidly stream large external response chunks": "Suurte väliste vastuste tükkide sujuv voogedastus",
 	"Focus chat input": "Fokuseeri vestluse sisendile",
 	"Folder deleted successfully": "Kaust edukalt kustutatud",
@@ -589,6 +601,8 @@
 	"Hybrid Search": "Hübriidotsing",
 	"I acknowledge that I have read and I understand the implications of my action. I am aware of the risks associated with executing arbitrary code and I have verified the trustworthiness of the source.": "Kinnitan, et olen lugenud ja mõistan oma tegevuse tagajärgi. Olen teadlik suvalise koodi käivitamisega seotud riskidest ja olen kontrollinud allika usaldusväärsust.",
 	"ID": "ID",
+	"iframe Sandbox Allow Forms": "",
+	"iframe Sandbox Allow Same Origin": "",
 	"Ignite curiosity": "Süüta uudishimu",
 	"Image": "Pilt",
 	"Image Compression": "Pildi tihendamine",
@@ -649,6 +663,7 @@
 	"Label": "Silt",
 	"Landing Page Mode": "Maandumislehe režiim",
 	"Language": "Keel",
+	"Language Locales": "",
 	"Last Active": "Viimati aktiivne",
 	"Last Modified": "Viimati muudetud",
 	"Last reply": "Viimane vastus",
@@ -702,7 +717,6 @@
 	"Message rating should be enabled to use this feature": "Selle funktsiooni kasutamiseks peaks sõnumite hindamine olema lubatud",
 	"Messages you send after creating your link won't be shared. Users with the URL will be able to view the shared chat.": "Teie saadetud sõnumeid pärast lingi loomist ei jagata. Kasutajad, kellel on URL, saavad vaadata jagatud vestlust.",
 	"Min P": "Min P",
-	"Minimum Score": "Minimaalne skoor",
 	"Mirostat": "Mirostat",
 	"Mirostat Eta": "Mirostat Eta",
 	"Mirostat Tau": "Mirostat Tau",
@@ -833,6 +847,8 @@
 	"Pipelines Valves": "Torustike klapid",
 	"Plain text (.txt)": "Lihttekst (.txt)",
 	"Playground": "Mänguväljak",
+	"Playwright Timeout (ms)": "",
+	"Playwright WebSocket URL": "",
 	"Please carefully review the following warnings:": "Palun vaadake hoolikalt läbi järgmised hoiatused:",
 	"Please do not close the settings page while loading the model.": "Palun ärge sulgege seadete lehte mudeli laadimise ajal.",
 	"Please enter a prompt": "Palun sisestage vihje",
@@ -878,8 +894,11 @@
 	"References from": "Viited allikast",
 	"Refused when it shouldn't have": "Keeldus, kui ei oleks pidanud",
 	"Regenerate": "Regenereeri",
+	"Reindex": "",
+	"Reindex Knowledge Base Vectors": "",
 	"Release Notes": "Väljalaskemärkmed",
 	"Relevance": "Asjakohasus",
+	"Relevance Threshold": "",
 	"Remove": "Eemalda",
 	"Remove Model": "Eemalda mudel",
 	"Rename": "Nimeta ümber",
@@ -1009,6 +1028,8 @@
 	"Sign up to {{WEBUI_NAME}}": "Registreeru {{WEBUI_NAME}}",
 	"Signing in to {{WEBUI_NAME}}": "Sisselogimine {{WEBUI_NAME}}",
 	"sk-1234": "sk-1234",
+	"Sougou Search API sID": "",
+	"Sougou Search API SK": "",
 	"Source": "Allikas",
 	"Speech Playback Speed": "Kõne taasesituse kiirus",
 	"Speech recognition error: {{error}}": "Kõnetuvastuse viga: {{error}}",
@@ -1036,6 +1057,7 @@
 	"Tap to interrupt": "Puuduta katkestamiseks",
 	"Tasks": "Ülesanded",
 	"Tavily API Key": "Tavily API võti",
+	"Tavily Extract Depth": "",
 	"Tell us more:": "Räägi meile lähemalt:",
 	"Temperature": "Temperatuur",
 	"Template": "Mall",
@@ -1158,6 +1180,8 @@
 	"Use Gravatar": "Kasuta Gravatari",
 	"Use groups to group your users and assign permissions.": "Kasutage gruppe oma kasutajate grupeerimiseks ja õiguste määramiseks.",
 	"Use Initials": "Kasuta initsiaale",
+	"Use no proxy to fetch page contents.": "",
+	"Use proxy designated by http_proxy and https_proxy environment variables to fetch page contents.": "",
 	"use_mlock (Ollama)": "use_mlock (Ollama)",
 	"use_mmap (Ollama)": "use_mmap (Ollama)",
 	"user": "kasutaja",
@@ -1175,6 +1199,7 @@
 	"variable": "muutuja",
 	"variable to have them replaced with clipboard content.": "muutuja, et need asendataks lõikelaua sisuga.",
 	"Verify Connection": "",
+	"Verify SSL Certificate": "",
 	"Version": "Versioon",
 	"Version {{selectedVersion}} of {{totalVersions}}": "Versioon {{selectedVersion}} / {{totalVersions}}",
 	"View Replies": "Vaata vastuseid",
@@ -1189,6 +1214,7 @@
 	"Warning: Jupyter execution enables arbitrary code execution, posing severe security risks—proceed with extreme caution.": "Hoiatus: Jupyter täitmine võimaldab suvalise koodi käivitamist, mis kujutab endast tõsist turvariski - jätkake äärmise ettevaatusega.",
 	"Web": "Veeb",
 	"Web API": "Veebi API",
+	"Web Loader Engine": "",
 	"Web Search": "Veebiotsing",
 	"Web Search Engine": "Veebi otsingumootor",
 	"Web Search in Chat": "Veebiotsing vestluses",

+ 28 - 2
src/lib/i18n/locales/eu-ES/translation.json

@@ -62,6 +62,7 @@
 	"Allow Chat Deletion": "Baimendu Txata Ezabatzea",
 	"Allow Chat Edit": "Baimendu Txata Editatzea",
 	"Allow File Upload": "Baimendu Fitxategiak Igotzea",
+	"Allow Multiple Models in Chat": "",
 	"Allow non-local voices": "Baimendu urruneko ahotsak",
 	"Allow Temporary Chat": "Baimendu Behin-behineko Txata",
 	"Allow User Location": "Baimendu Erabiltzailearen Kokapena",
@@ -79,6 +80,7 @@
 	"and": "eta",
 	"and {{COUNT}} more": "eta {{COUNT}} gehiago",
 	"and create a new shared link.": "eta sortu partekatutako esteka berri bat.",
+	"Android": "",
 	"API Base URL": "API Oinarri URLa",
 	"API Key": "API Gakoa",
 	"API Key created.": "API Gakoa sortu da.",
@@ -141,7 +143,6 @@
 	"Brave Search API Key": "Brave Bilaketa API Gakoa",
 	"By {{name}}": "{{name}}-k",
 	"Bypass Embedding and Retrieval": "",
-	"Bypass SSL verification for Websites": "Saihestu SSL egiaztapena Webguneentzat",
 	"Calendar": "",
 	"Call": "Deia",
 	"Call feature is not supported when using Web STT engine": "Dei funtzioa ez da onartzen Web STT motorra erabiltzean",
@@ -303,6 +304,7 @@
 	"Deleted User": "Ezabatutako Erabiltzailea",
 	"Describe your knowledge base and objectives": "Deskribatu zure ezagutza-basea eta helburuak",
 	"Description": "Deskribapena",
+	"Detect Artifacts Automatically": "",
 	"Didn't fully follow instructions": "Ez ditu jarraibideak guztiz jarraitu",
 	"Direct": "",
 	"Direct Connections": "",
@@ -358,6 +360,7 @@
 	"e.g. my_filter": "adib. nire_iragazkia",
 	"e.g. my_tools": "adib. nire_tresnak",
 	"e.g. Tools for performing various operations": "adib. Hainbat eragiketa egiteko tresnak",
+	"e.g., en-US,ja-JP (leave blank for auto-detect)": "",
 	"Edit": "Editatu",
 	"Edit Arena Model": "Editatu Arena Eredua",
 	"Edit Channel": "",
@@ -407,6 +410,8 @@
 	"Enter Document Intelligence Key": "",
 	"Enter domains separated by commas (e.g., example.com,site.org)": "",
 	"Enter Exa API Key": "",
+	"Enter Firecrawl API Base URL": "",
+	"Enter Firecrawl API Key": "",
 	"Enter Github Raw URL": "Sartu Github Raw URLa",
 	"Enter Google PSE API Key": "Sartu Google PSE API Gakoa",
 	"Enter Google PSE Engine Id": "Sartu Google PSE Motor IDa",
@@ -424,6 +429,8 @@
 	"Enter Mojeek Search API Key": "Sartu Mojeek Bilaketa API Gakoa",
 	"Enter Number of Steps (e.g. 50)": "Sartu Urrats Kopurua (adib. 50)",
 	"Enter Perplexity API Key": "",
+	"Enter Playwright Timeout": "",
+	"Enter Playwright WebSocket URL": "",
 	"Enter proxy URL (e.g. https://user:password@host:port)": "",
 	"Enter reasoning effort": "",
 	"Enter Sampler (e.g. Euler a)": "Sartu Sampler-a (adib. Euler a)",
@@ -441,10 +448,13 @@
 	"Enter server host": "Sartu zerbitzariaren ostalaria",
 	"Enter server label": "Sartu zerbitzariaren etiketa",
 	"Enter server port": "Sartu zerbitzariaren portua",
+	"Enter Sougou Search API sID": "",
+	"Enter Sougou Search API SK": "",
 	"Enter stop sequence": "Sartu gelditze sekuentzia",
 	"Enter system prompt": "Sartu sistema prompta",
 	"Enter system prompt here": "",
 	"Enter Tavily API Key": "Sartu Tavily API Gakoa",
+	"Enter Tavily Extract Depth": "",
 	"Enter the public URL of your WebUI. This URL will be used to generate links in the notifications.": "",
 	"Enter Tika Server URL": "Sartu Tika Zerbitzari URLa",
 	"Enter timeout in seconds": "",
@@ -525,6 +535,8 @@
 	"Filter is now globally enabled": "Iragazkia orain globalki gaituta dago",
 	"Filters": "Iragazkiak",
 	"Fingerprint spoofing detected: Unable to use initials as avatar. Defaulting to default profile image.": "Hatz-marka faltsutzea detektatu da: Ezin dira inizialak avatar gisa erabili. Profil irudi lehenetsia erabiliko da.",
+	"Firecrawl API Base URL": "",
+	"Firecrawl API Key": "",
 	"Fluidly stream large external response chunks": "Modu jariagarrian transmititu kanpoko erantzun zati handiak",
 	"Focus chat input": "Fokuratu txataren sarrera",
 	"Folder deleted successfully": "Karpeta ongi ezabatu da",
@@ -589,6 +601,8 @@
 	"Hybrid Search": "Bilaketa Hibridoa",
 	"I acknowledge that I have read and I understand the implications of my action. I am aware of the risks associated with executing arbitrary code and I have verified the trustworthiness of the source.": "Onartzen dut irakurri dudala eta nire ekintzaren ondorioak ulertzen ditudala. Kode arbitrarioa exekutatzearekin lotutako arriskuez jabetzen naiz eta iturriaren fidagarritasuna egiaztatu dut.",
 	"ID": "IDa",
+	"iframe Sandbox Allow Forms": "",
+	"iframe Sandbox Allow Same Origin": "",
 	"Ignite curiosity": "Piztu jakin-mina",
 	"Image": "",
 	"Image Compression": "",
@@ -649,6 +663,7 @@
 	"Label": "Etiketa",
 	"Landing Page Mode": "Hasiera Orriaren Modua",
 	"Language": "Hizkuntza",
+	"Language Locales": "",
 	"Last Active": "Azken Aktibitatea",
 	"Last Modified": "Azken Aldaketa",
 	"Last reply": "",
@@ -702,7 +717,6 @@
 	"Message rating should be enabled to use this feature": "Mezuen balorazioa gaitu behar da funtzionalitate hau erabiltzeko",
 	"Messages you send after creating your link won't be shared. Users with the URL will be able to view the shared chat.": "Esteka sortu ondoren bidaltzen dituzun mezuak ez dira partekatuko. URLa duten erabiltzaileek partekatutako txata ikusi ahal izango dute.",
 	"Min P": "Min P",
-	"Minimum Score": "Puntuazio minimoa",
 	"Mirostat": "Mirostat",
 	"Mirostat Eta": "Mirostat Eta",
 	"Mirostat Tau": "Mirostat Tau",
@@ -833,6 +847,8 @@
 	"Pipelines Valves": "Pipeline balbulak",
 	"Plain text (.txt)": "Testu laua (.txt)",
 	"Playground": "Jolaslekua",
+	"Playwright Timeout (ms)": "",
+	"Playwright WebSocket URL": "",
 	"Please carefully review the following warnings:": "Mesedez, berrikusi arretaz hurrengo oharrak:",
 	"Please do not close the settings page while loading the model.": "",
 	"Please enter a prompt": "Mesedez, sartu prompt bat",
@@ -878,8 +894,11 @@
 	"References from": "Erreferentziak hemendik",
 	"Refused when it shouldn't have": "Ukatu duenean ukatu behar ez zuenean",
 	"Regenerate": "Bersortu",
+	"Reindex": "",
+	"Reindex Knowledge Base Vectors": "",
 	"Release Notes": "Bertsio oharrak",
 	"Relevance": "Garrantzia",
+	"Relevance Threshold": "",
 	"Remove": "Kendu",
 	"Remove Model": "Kendu modeloa",
 	"Rename": "Berrizendatu",
@@ -1009,6 +1028,8 @@
 	"Sign up to {{WEBUI_NAME}}": "Erregistratu {{WEBUI_NAME}}-n",
 	"Signing in to {{WEBUI_NAME}}": "{{WEBUI_NAME}}-n saioa hasten",
 	"sk-1234": "",
+	"Sougou Search API sID": "",
+	"Sougou Search API SK": "",
 	"Source": "Iturria",
 	"Speech Playback Speed": "Ahots erreprodukzio abiadura",
 	"Speech recognition error: {{error}}": "Ahots ezagutze errorea: {{error}}",
@@ -1036,6 +1057,7 @@
 	"Tap to interrupt": "Ukitu eteteko",
 	"Tasks": "",
 	"Tavily API Key": "Tavily API gakoa",
+	"Tavily Extract Depth": "",
 	"Tell us more:": "Kontatu gehiago:",
 	"Temperature": "Tenperatura",
 	"Template": "Txantiloia",
@@ -1158,6 +1180,8 @@
 	"Use Gravatar": "Erabili Gravatar",
 	"Use groups to group your users and assign permissions.": "Erabili taldeak zure erabiltzaileak taldekatu eta baimenak esleitzeko.",
 	"Use Initials": "Erabili inizialak",
+	"Use no proxy to fetch page contents.": "",
+	"Use proxy designated by http_proxy and https_proxy environment variables to fetch page contents.": "",
 	"use_mlock (Ollama)": "use_mlock (Ollama)",
 	"use_mmap (Ollama)": "use_mmap (Ollama)",
 	"user": "erabiltzailea",
@@ -1175,6 +1199,7 @@
 	"variable": "aldagaia",
 	"variable to have them replaced with clipboard content.": "aldagaia arbeleko edukiarekin ordezkatzeko.",
 	"Verify Connection": "",
+	"Verify SSL Certificate": "",
 	"Version": "Bertsioa",
 	"Version {{selectedVersion}} of {{totalVersions}}": "{{totalVersions}}-tik {{selectedVersion}}. bertsioa",
 	"View Replies": "",
@@ -1189,6 +1214,7 @@
 	"Warning: Jupyter execution enables arbitrary code execution, posing severe security risks—proceed with extreme caution.": "",
 	"Web": "Weba",
 	"Web API": "Web APIa",
+	"Web Loader Engine": "",
 	"Web Search": "Web bilaketa",
 	"Web Search Engine": "Web bilaketa motorra",
 	"Web Search in Chat": "",

+ 28 - 2
src/lib/i18n/locales/fa-IR/translation.json

@@ -62,6 +62,7 @@
 	"Allow Chat Deletion": "اجازهٔ حذف گفتگو",
 	"Allow Chat Edit": "",
 	"Allow File Upload": "",
+	"Allow Multiple Models in Chat": "",
 	"Allow non-local voices": "",
 	"Allow Temporary Chat": "اجازهٔ گفتگوی موقتی",
 	"Allow User Location": "اجازهٔ موقعیت مکانی کاربر",
@@ -79,6 +80,7 @@
 	"and": "و",
 	"and {{COUNT}} more": "و {{COUNT}} مورد دیگر",
 	"and create a new shared link.": "و یک پیوند اشتراک\u200cگذاری جدید ایجاد کنید.",
+	"Android": "",
 	"API Base URL": "نشانی پایهٔ API",
 	"API Key": "کلید API",
 	"API Key created.": "کلید API ساخته شد.",
@@ -141,7 +143,6 @@
 	"Brave Search API Key": "کلید API جستجوی شجاع",
 	"By {{name}}": "",
 	"Bypass Embedding and Retrieval": "",
-	"Bypass SSL verification for Websites": "عبور از تأیید SSL برای وب سایت ها",
 	"Calendar": "",
 	"Call": "",
 	"Call feature is not supported when using Web STT engine": "",
@@ -303,6 +304,7 @@
 	"Deleted User": "",
 	"Describe your knowledge base and objectives": "",
 	"Description": "توضیحات",
+	"Detect Artifacts Automatically": "",
 	"Didn't fully follow instructions": "نمی تواند دستورالعمل را کامل پیگیری کند",
 	"Direct": "",
 	"Direct Connections": "",
@@ -358,6 +360,7 @@
 	"e.g. my_filter": "",
 	"e.g. my_tools": "",
 	"e.g. Tools for performing various operations": "",
+	"e.g., en-US,ja-JP (leave blank for auto-detect)": "",
 	"Edit": "ویرایش",
 	"Edit Arena Model": "",
 	"Edit Channel": "",
@@ -407,6 +410,8 @@
 	"Enter Document Intelligence Key": "",
 	"Enter domains separated by commas (e.g., example.com,site.org)": "",
 	"Enter Exa API Key": "",
+	"Enter Firecrawl API Base URL": "",
+	"Enter Firecrawl API Key": "",
 	"Enter Github Raw URL": "ادرس Github Raw را وارد کنید",
 	"Enter Google PSE API Key": "کلید API گوگل PSE را وارد کنید",
 	"Enter Google PSE Engine Id": "شناسه موتور PSE گوگل را وارد کنید",
@@ -424,6 +429,8 @@
 	"Enter Mojeek Search API Key": "",
 	"Enter Number of Steps (e.g. 50)": "تعداد گام ها را وارد کنید (مثال: 50)",
 	"Enter Perplexity API Key": "",
+	"Enter Playwright Timeout": "",
+	"Enter Playwright WebSocket URL": "",
 	"Enter proxy URL (e.g. https://user:password@host:port)": "",
 	"Enter reasoning effort": "",
 	"Enter Sampler (e.g. Euler a)": "",
@@ -441,10 +448,13 @@
 	"Enter server host": "",
 	"Enter server label": "",
 	"Enter server port": "",
+	"Enter Sougou Search API sID": "",
+	"Enter Sougou Search API SK": "",
 	"Enter stop sequence": "توالی توقف را وارد کنید",
 	"Enter system prompt": "",
 	"Enter system prompt here": "",
 	"Enter Tavily API Key": "",
+	"Enter Tavily Extract Depth": "",
 	"Enter the public URL of your WebUI. This URL will be used to generate links in the notifications.": "",
 	"Enter Tika Server URL": "",
 	"Enter timeout in seconds": "",
@@ -525,6 +535,8 @@
 	"Filter is now globally enabled": "",
 	"Filters": "",
 	"Fingerprint spoofing detected: Unable to use initials as avatar. Defaulting to default profile image.": "فانگ سرفیس شناسایی شد: نمی توان از نمایه شما به عنوان آواتار استفاده کرد. پیش فرض به عکس پروفایل پیش فرض برگشت داده شد.",
+	"Firecrawl API Base URL": "",
+	"Firecrawl API Key": "",
 	"Fluidly stream large external response chunks": "تکه های پاسخ خارجی بزرگ را به صورت سیال پخش کنید",
 	"Focus chat input": "فوکوس کردن ورودی گپ",
 	"Folder deleted successfully": "",
@@ -589,6 +601,8 @@
 	"Hybrid Search": "جستجوی همزمان",
 	"I acknowledge that I have read and I understand the implications of my action. I am aware of the risks associated with executing arbitrary code and I have verified the trustworthiness of the source.": "",
 	"ID": "",
+	"iframe Sandbox Allow Forms": "",
+	"iframe Sandbox Allow Same Origin": "",
 	"Ignite curiosity": "",
 	"Image": "",
 	"Image Compression": "",
@@ -649,6 +663,7 @@
 	"Label": "",
 	"Landing Page Mode": "",
 	"Language": "زبان",
+	"Language Locales": "",
 	"Last Active": "آخرین فعال",
 	"Last Modified": "",
 	"Last reply": "",
@@ -702,7 +717,6 @@
 	"Message rating should be enabled to use this feature": "",
 	"Messages you send after creating your link won't be shared. Users with the URL will be able to view the shared chat.": "پیام های شما بعد از ایجاد لینک شما به اشتراک نمی گردد. کاربران با لینک URL می توانند چت اشتراک را مشاهده کنند.",
 	"Min P": "",
-	"Minimum Score": "نماد کمینه",
 	"Mirostat": "Mirostat",
 	"Mirostat Eta": "Mirostat Eta",
 	"Mirostat Tau": "Mirostat Tau",
@@ -833,6 +847,8 @@
 	"Pipelines Valves": "شیرالات خطوط لوله",
 	"Plain text (.txt)": "متن ساده (.txt)",
 	"Playground": "زمین بازی",
+	"Playwright Timeout (ms)": "",
+	"Playwright WebSocket URL": "",
 	"Please carefully review the following warnings:": "",
 	"Please do not close the settings page while loading the model.": "",
 	"Please enter a prompt": "",
@@ -878,8 +894,11 @@
 	"References from": "",
 	"Refused when it shouldn't have": "رد شده زمانی که باید نباشد",
 	"Regenerate": "ری\u200cسازی",
+	"Reindex": "",
+	"Reindex Knowledge Base Vectors": "",
 	"Release Notes": "یادداشت\u200cهای انتشار",
 	"Relevance": "ارتباط",
+	"Relevance Threshold": "",
 	"Remove": "حذف",
 	"Remove Model": "حذف مدل",
 	"Rename": "تغییر نام",
@@ -1009,6 +1028,8 @@
 	"Sign up to {{WEBUI_NAME}}": "",
 	"Signing in to {{WEBUI_NAME}}": "",
 	"sk-1234": "",
+	"Sougou Search API sID": "",
+	"Sougou Search API SK": "",
 	"Source": "منبع",
 	"Speech Playback Speed": "",
 	"Speech recognition error: {{error}}": "خطای تشخیص گفتار: {{error}}",
@@ -1036,6 +1057,7 @@
 	"Tap to interrupt": "",
 	"Tasks": "",
 	"Tavily API Key": "",
+	"Tavily Extract Depth": "",
 	"Tell us more:": "بیشتر بگویید:",
 	"Temperature": "دما",
 	"Template": "الگو",
@@ -1158,6 +1180,8 @@
 	"Use Gravatar": "استفاده از گراواتار",
 	"Use groups to group your users and assign permissions.": "",
 	"Use Initials": "استفاده از سرواژه",
+	"Use no proxy to fetch page contents.": "",
+	"Use proxy designated by http_proxy and https_proxy environment variables to fetch page contents.": "",
 	"use_mlock (Ollama)": "use_mlock (اولاما)",
 	"use_mmap (Ollama)": "use_mmap (اولاما)",
 	"user": "کاربر",
@@ -1175,6 +1199,7 @@
 	"variable": "متغیر",
 	"variable to have them replaced with clipboard content.": "متغیر برای جایگزینی آنها با محتوای بریده\u200cدان.",
 	"Verify Connection": "",
+	"Verify SSL Certificate": "",
 	"Version": "نسخه",
 	"Version {{selectedVersion}} of {{totalVersions}}": "نسخهٔ {{selectedVersion}} از {{totalVersions}}",
 	"View Replies": "",
@@ -1189,6 +1214,7 @@
 	"Warning: Jupyter execution enables arbitrary code execution, posing severe security risks—proceed with extreme caution.": "",
 	"Web": "وب",
 	"Web API": "",
+	"Web Loader Engine": "",
 	"Web Search": "جستجوی وب",
 	"Web Search Engine": "موتور جستجوی وب",
 	"Web Search in Chat": "",

+ 28 - 2
src/lib/i18n/locales/fi-FI/translation.json

@@ -62,6 +62,7 @@
 	"Allow Chat Deletion": "Salli keskustelujen poisto",
 	"Allow Chat Edit": "Salli keskustelujen muokkaus",
 	"Allow File Upload": "Salli tiedostojen lataus",
+	"Allow Multiple Models in Chat": "",
 	"Allow non-local voices": "Salli ei-paikalliset äänet",
 	"Allow Temporary Chat": "Salli väliaikaiset keskustelut",
 	"Allow User Location": "Salli käyttäjän sijainti",
@@ -79,6 +80,7 @@
 	"and": "ja",
 	"and {{COUNT}} more": "ja {{COUNT}} muuta",
 	"and create a new shared link.": "ja luo uusi jaettu linkki.",
+	"Android": "",
 	"API Base URL": "API:n verkko-osoite",
 	"API Key": "API-avain",
 	"API Key created.": "API-avain luotu.",
@@ -141,7 +143,6 @@
 	"Brave Search API Key": "Brave Search API -avain",
 	"By {{name}}": "Tekijä {{name}}",
 	"Bypass Embedding and Retrieval": "Ohita upotus ja haku",
-	"Bypass SSL verification for Websites": "Ohita SSL-varmennus verkkosivustoille",
 	"Calendar": "Kalenteri",
 	"Call": "Puhelu",
 	"Call feature is not supported when using Web STT engine": "Puhelutoimintoa ei tueta käytettäessä web-puheentunnistusmoottoria",
@@ -303,6 +304,7 @@
 	"Deleted User": "Käyttäjä poistettu",
 	"Describe your knowledge base and objectives": "Kuvaa tietokantasi ja tavoitteesi",
 	"Description": "Kuvaus",
+	"Detect Artifacts Automatically": "",
 	"Didn't fully follow instructions": "Ei noudattanut ohjeita täysin",
 	"Direct": "Suora",
 	"Direct Connections": "Suorat yhteydet",
@@ -358,6 +360,7 @@
 	"e.g. my_filter": "esim. oma_suodatin",
 	"e.g. my_tools": "esim. omat_työkalut",
 	"e.g. Tools for performing various operations": "esim. työkaluja erilaisten toimenpiteiden suorittamiseen",
+	"e.g., en-US,ja-JP (leave blank for auto-detect)": "",
 	"Edit": "Muokkaa",
 	"Edit Arena Model": "Muokkaa Arena-mallia",
 	"Edit Channel": "Muokkaa kanavaa",
@@ -407,6 +410,8 @@
 	"Enter Document Intelligence Key": "Kirjoiuta asiakirja tiedustelun avain",
 	"Enter domains separated by commas (e.g., example.com,site.org)": "Verkko-osoitteet erotetaan pilkulla (esim. esimerkki.com,sivu.org)",
 	"Enter Exa API Key": "Kirjoita Exa API -avain",
+	"Enter Firecrawl API Base URL": "",
+	"Enter Firecrawl API Key": "",
 	"Enter Github Raw URL": "Kirjoita Github Raw -verkko-osoite",
 	"Enter Google PSE API Key": "Kirjoita Google PSE API -avain",
 	"Enter Google PSE Engine Id": "Kirjoita Google PSE -moottorin tunnus",
@@ -424,6 +429,8 @@
 	"Enter Mojeek Search API Key": "Kirjoita Mojeek Search API -avain",
 	"Enter Number of Steps (e.g. 50)": "Kirjoita askelten määrä (esim. 50)",
 	"Enter Perplexity API Key": "Aseta Perplexity API-avain",
+	"Enter Playwright Timeout": "",
+	"Enter Playwright WebSocket URL": "",
 	"Enter proxy URL (e.g. https://user:password@host:port)": "Kirjoita välityspalvelimen verkko-osoite (esim. https://käyttäjä:salasana@host:portti)",
 	"Enter reasoning effort": "",
 	"Enter Sampler (e.g. Euler a)": "Kirjoita näytteistäjä (esim. Euler a)",
@@ -441,10 +448,13 @@
 	"Enter server host": "Kirjoita palvelimen isäntänimi",
 	"Enter server label": "Kirjoita palvelimen tunniste",
 	"Enter server port": "Kirjoita palvelimen portti",
+	"Enter Sougou Search API sID": "",
+	"Enter Sougou Search API SK": "",
 	"Enter stop sequence": "Kirjoita lopetussekvenssi",
 	"Enter system prompt": "Kirjoita järjestelmäkehote",
 	"Enter system prompt here": "Kirjoita järjestelmäkehote tähän",
 	"Enter Tavily API Key": "Kirjoita Tavily API -avain",
+	"Enter Tavily Extract Depth": "",
 	"Enter the public URL of your WebUI. This URL will be used to generate links in the notifications.": "Kirjoita julkinen WebUI verkko-osoitteesi. Verkko-osoitetta käytetään osoitteiden luontiin ilmoituksissa.",
 	"Enter Tika Server URL": "Kirjoita Tika Server URL",
 	"Enter timeout in seconds": "Aseta aikakatkaisu sekunneissa",
@@ -525,6 +535,8 @@
 	"Filter is now globally enabled": "Suodatin on nyt otettu käyttöön globaalisti",
 	"Filters": "Suodattimet",
 	"Fingerprint spoofing detected: Unable to use initials as avatar. Defaulting to default profile image.": "Sormenjäljen väärentäminen havaittu: Alkukirjaimia ei voi käyttää avatarina. Käytetään oletusprofiilikuvaa.",
+	"Firecrawl API Base URL": "",
+	"Firecrawl API Key": "",
 	"Fluidly stream large external response chunks": "Virtaa suuria ulkoisia vastausosia joustavasti",
 	"Focus chat input": "Fokusoi syöttökenttään",
 	"Folder deleted successfully": "Kansio poistettu onnistuneesti",
@@ -589,6 +601,8 @@
 	"Hybrid Search": "Hybridihaku",
 	"I acknowledge that I have read and I understand the implications of my action. I am aware of the risks associated with executing arbitrary code and I have verified the trustworthiness of the source.": "Vahvistan, että olen lukenut ja ymmärrän toimintani seuraukset. Olen tietoinen mielivaltaisen koodin suorittamiseen liittyvistä riskeistä ja olen varmistanut lähteen luotettavuuden.",
 	"ID": "Tunnus",
+	"iframe Sandbox Allow Forms": "",
+	"iframe Sandbox Allow Same Origin": "",
 	"Ignite curiosity": "Sytytä uteliaisuus",
 	"Image": "Kuva",
 	"Image Compression": "Kuvan pakkaus",
@@ -649,6 +663,7 @@
 	"Label": "Tunniste",
 	"Landing Page Mode": "Etusivun tila",
 	"Language": "Kieli",
+	"Language Locales": "",
 	"Last Active": "Viimeksi aktiivinen",
 	"Last Modified": "Viimeksi muokattu",
 	"Last reply": "Viimeksi vastattu",
@@ -702,7 +717,6 @@
 	"Message rating should be enabled to use this feature": "Tämän toiminnon käyttämiseksi viestiarviointi on otettava käyttöön",
 	"Messages you send after creating your link won't be shared. Users with the URL will be able to view the shared chat.": "Linkin luomisen jälkeen lähettämäsi viestit eivät ole jaettuja. Käyttäjät, joilla on verkko-osoite, voivat tarkastella jaettua keskustelua.",
 	"Min P": "Min P",
-	"Minimum Score": "Vähimmäispisteet",
 	"Mirostat": "Mirostat",
 	"Mirostat Eta": "Mirostat Eta",
 	"Mirostat Tau": "Mirostat Tau",
@@ -833,6 +847,8 @@
 	"Pipelines Valves": "Putkistojen venttiilit",
 	"Plain text (.txt)": "Pelkkä teksti (.txt)",
 	"Playground": "Leikkipaikka",
+	"Playwright Timeout (ms)": "",
+	"Playwright WebSocket URL": "",
 	"Please carefully review the following warnings:": "Tarkista huolellisesti seuraavat varoitukset:",
 	"Please do not close the settings page while loading the model.": "Älä sulje asetussivua mallin latautuessa.",
 	"Please enter a prompt": "Kirjoita kehote",
@@ -878,8 +894,11 @@
 	"References from": "Viitteet lähteistä",
 	"Refused when it shouldn't have": "Kieltäytyi, vaikka ei olisi pitänyt",
 	"Regenerate": "Uudelleentuota",
+	"Reindex": "",
+	"Reindex Knowledge Base Vectors": "",
 	"Release Notes": "Julkaisutiedot",
 	"Relevance": "Relevanssi",
+	"Relevance Threshold": "",
 	"Remove": "Poista",
 	"Remove Model": "Poista malli",
 	"Rename": "Nimeä uudelleen",
@@ -1009,6 +1028,8 @@
 	"Sign up to {{WEBUI_NAME}}": "Rekisteröidy palveluun {{WEBUI_NAME}}",
 	"Signing in to {{WEBUI_NAME}}": "Kirjaudutaan sisään palveluun {{WEBUI_NAME}}",
 	"sk-1234": "",
+	"Sougou Search API sID": "",
+	"Sougou Search API SK": "",
 	"Source": "Lähde",
 	"Speech Playback Speed": "Puhetoiston nopeus",
 	"Speech recognition error: {{error}}": "Puheentunnistusvirhe: {{error}}",
@@ -1036,6 +1057,7 @@
 	"Tap to interrupt": "Napauta keskeyttääksesi",
 	"Tasks": "Tehtävät",
 	"Tavily API Key": "Tavily API -avain",
+	"Tavily Extract Depth": "",
 	"Tell us more:": "Kerro lisää:",
 	"Temperature": "Lämpötila",
 	"Template": "Malli",
@@ -1158,6 +1180,8 @@
 	"Use Gravatar": "Käytä Gravataria",
 	"Use groups to group your users and assign permissions.": "Käytä ryhmiä jäsentääksesi käyttäjiä ja antaaksesi käyttöoikeuksia.",
 	"Use Initials": "Käytä alkukirjaimia",
+	"Use no proxy to fetch page contents.": "",
+	"Use proxy designated by http_proxy and https_proxy environment variables to fetch page contents.": "",
 	"use_mlock (Ollama)": "use_mlock (Ollama)",
 	"use_mmap (Ollama)": "use_mmap (Ollama)",
 	"user": "käyttäjä",
@@ -1175,6 +1199,7 @@
 	"variable": "muuttuja",
 	"variable to have them replaced with clipboard content.": "muuttuja korvataan leikepöydän sisällöllä.",
 	"Verify Connection": "Tarkista yhteys",
+	"Verify SSL Certificate": "",
 	"Version": "Versio",
 	"Version {{selectedVersion}} of {{totalVersions}}": "Versio {{selectedVersion}} / {{totalVersions}}",
 	"View Replies": "Näytä vastaukset",
@@ -1189,6 +1214,7 @@
 	"Warning: Jupyter execution enables arbitrary code execution, posing severe security risks—proceed with extreme caution.": "Varoitus: Jupyter käyttö voi mahdollistaa mielivaltaiseen koodin suorittamiseen, mikä voi aiheuttaa tietoturvariskejä - käytä äärimmäisen varoen.",
 	"Web": "Web",
 	"Web API": "Web-API",
+	"Web Loader Engine": "",
 	"Web Search": "Verkkohaku",
 	"Web Search Engine": "Hakukoneet",
 	"Web Search in Chat": "Verkkohaku keskustelussa",

+ 28 - 2
src/lib/i18n/locales/fr-CA/translation.json

@@ -62,6 +62,7 @@
 	"Allow Chat Deletion": "Autoriser la suppression de l'historique de chat",
 	"Allow Chat Edit": "",
 	"Allow File Upload": "",
+	"Allow Multiple Models in Chat": "",
 	"Allow non-local voices": "Autoriser les voix non locales",
 	"Allow Temporary Chat": "",
 	"Allow User Location": "Autoriser l'emplacement de l'utilisateur",
@@ -79,6 +80,7 @@
 	"and": "et",
 	"and {{COUNT}} more": "",
 	"and create a new shared link.": "et créer un nouveau lien partagé.",
+	"Android": "",
 	"API Base URL": "URL de base de l'API",
 	"API Key": "Clé d'API",
 	"API Key created.": "Clé d'API générée.",
@@ -141,7 +143,6 @@
 	"Brave Search API Key": "Clé API Brave Search",
 	"By {{name}}": "",
 	"Bypass Embedding and Retrieval": "",
-	"Bypass SSL verification for Websites": "Bypasser la vérification SSL pour les sites web",
 	"Calendar": "",
 	"Call": "Appeler",
 	"Call feature is not supported when using Web STT engine": "La fonction d'appel n'est pas prise en charge lors de l'utilisation du moteur Web STT",
@@ -303,6 +304,7 @@
 	"Deleted User": "",
 	"Describe your knowledge base and objectives": "",
 	"Description": "Description",
+	"Detect Artifacts Automatically": "",
 	"Didn't fully follow instructions": "N'a pas entièrement respecté les instructions",
 	"Direct": "",
 	"Direct Connections": "",
@@ -358,6 +360,7 @@
 	"e.g. my_filter": "",
 	"e.g. my_tools": "",
 	"e.g. Tools for performing various operations": "",
+	"e.g., en-US,ja-JP (leave blank for auto-detect)": "",
 	"Edit": "Modifier",
 	"Edit Arena Model": "",
 	"Edit Channel": "",
@@ -407,6 +410,8 @@
 	"Enter Document Intelligence Key": "",
 	"Enter domains separated by commas (e.g., example.com,site.org)": "",
 	"Enter Exa API Key": "",
+	"Enter Firecrawl API Base URL": "",
+	"Enter Firecrawl API Key": "",
 	"Enter Github Raw URL": "Entrez l'URL brute de GitHub",
 	"Enter Google PSE API Key": "Entrez la clé API Google PSE",
 	"Enter Google PSE Engine Id": "Entrez l'identifiant du moteur Google PSE",
@@ -424,6 +429,8 @@
 	"Enter Mojeek Search API Key": "",
 	"Enter Number of Steps (e.g. 50)": "Entrez le nombre de pas (par ex. 50)",
 	"Enter Perplexity API Key": "",
+	"Enter Playwright Timeout": "",
+	"Enter Playwright WebSocket URL": "",
 	"Enter proxy URL (e.g. https://user:password@host:port)": "",
 	"Enter reasoning effort": "",
 	"Enter Sampler (e.g. Euler a)": "",
@@ -441,10 +448,13 @@
 	"Enter server host": "",
 	"Enter server label": "",
 	"Enter server port": "",
+	"Enter Sougou Search API sID": "",
+	"Enter Sougou Search API SK": "",
 	"Enter stop sequence": "Entrez la séquence d'arrêt",
 	"Enter system prompt": "",
 	"Enter system prompt here": "",
 	"Enter Tavily API Key": "Entrez la clé API Tavily",
+	"Enter Tavily Extract Depth": "",
 	"Enter the public URL of your WebUI. This URL will be used to generate links in the notifications.": "",
 	"Enter Tika Server URL": "",
 	"Enter timeout in seconds": "",
@@ -525,6 +535,8 @@
 	"Filter is now globally enabled": "Le filtre est désormais activé globalement",
 	"Filters": "Filtres",
 	"Fingerprint spoofing detected: Unable to use initials as avatar. Defaulting to default profile image.": "Spoofing détecté : impossible d'utiliser les initiales comme avatar. Retour à l'image de profil par défaut.",
+	"Firecrawl API Base URL": "",
+	"Firecrawl API Key": "",
 	"Fluidly stream large external response chunks": "Diffuser de manière fluide de larges portions de réponses externes",
 	"Focus chat input": "Se concentrer sur le chat en entrée",
 	"Folder deleted successfully": "",
@@ -589,6 +601,8 @@
 	"Hybrid Search": "Recherche hybride",
 	"I acknowledge that I have read and I understand the implications of my action. I am aware of the risks associated with executing arbitrary code and I have verified the trustworthiness of the source.": "",
 	"ID": "",
+	"iframe Sandbox Allow Forms": "",
+	"iframe Sandbox Allow Same Origin": "",
 	"Ignite curiosity": "",
 	"Image": "",
 	"Image Compression": "",
@@ -649,6 +663,7 @@
 	"Label": "",
 	"Landing Page Mode": "",
 	"Language": "Langue",
+	"Language Locales": "",
 	"Last Active": "Dernière activité",
 	"Last Modified": "Dernière modification",
 	"Last reply": "",
@@ -702,7 +717,6 @@
 	"Message rating should be enabled to use this feature": "",
 	"Messages you send after creating your link won't be shared. Users with the URL will be able to view the shared chat.": "Les messages que vous envoyez après avoir créé votre lien ne seront pas partagés. Les utilisateurs disposant de l'URL pourront voir le chat partagé.",
 	"Min P": "",
-	"Minimum Score": "Score minimal",
 	"Mirostat": "Mirostat",
 	"Mirostat Eta": "Mirostat Eta",
 	"Mirostat Tau": "Mirostat Tau",
@@ -833,6 +847,8 @@
 	"Pipelines Valves": "Vannes de Pipelines",
 	"Plain text (.txt)": "Texte simple (.txt)",
 	"Playground": "Aire de jeux",
+	"Playwright Timeout (ms)": "",
+	"Playwright WebSocket URL": "",
 	"Please carefully review the following warnings:": "",
 	"Please do not close the settings page while loading the model.": "",
 	"Please enter a prompt": "",
@@ -878,8 +894,11 @@
 	"References from": "",
 	"Refused when it shouldn't have": "Refusé alors qu'il n'aurait pas dû l'être",
 	"Regenerate": "Regénérer",
+	"Reindex": "",
+	"Reindex Knowledge Base Vectors": "",
 	"Release Notes": "Notes de publication",
 	"Relevance": "",
+	"Relevance Threshold": "",
 	"Remove": "Retirer",
 	"Remove Model": "Retirer le modèle",
 	"Rename": "Renommer",
@@ -1009,6 +1028,8 @@
 	"Sign up to {{WEBUI_NAME}}": "",
 	"Signing in to {{WEBUI_NAME}}": "",
 	"sk-1234": "",
+	"Sougou Search API sID": "",
+	"Sougou Search API SK": "",
 	"Source": "Source",
 	"Speech Playback Speed": "",
 	"Speech recognition error: {{error}}": "Erreur de reconnaissance vocale\u00a0: {{error}}",
@@ -1036,6 +1057,7 @@
 	"Tap to interrupt": "Appuyez pour interrompre",
 	"Tasks": "",
 	"Tavily API Key": "Clé API Tavily",
+	"Tavily Extract Depth": "",
 	"Tell us more:": "Dites-nous en plus à ce sujet : ",
 	"Temperature": "Température",
 	"Template": "Template",
@@ -1158,6 +1180,8 @@
 	"Use Gravatar": "Utilisez Gravatar",
 	"Use groups to group your users and assign permissions.": "",
 	"Use Initials": "Utiliser les initiales",
+	"Use no proxy to fetch page contents.": "",
+	"Use proxy designated by http_proxy and https_proxy environment variables to fetch page contents.": "",
 	"use_mlock (Ollama)": "use_mlock (Ollama)",
 	"use_mmap (Ollama)": "utiliser mmap (Ollama)",
 	"user": "utilisateur",
@@ -1175,6 +1199,7 @@
 	"variable": "variable",
 	"variable to have them replaced with clipboard content.": "variable pour qu'elles soient remplacées par le contenu du presse-papiers.",
 	"Verify Connection": "",
+	"Verify SSL Certificate": "",
 	"Version": "Version améliorée",
 	"Version {{selectedVersion}} of {{totalVersions}}": "",
 	"View Replies": "",
@@ -1189,6 +1214,7 @@
 	"Warning: Jupyter execution enables arbitrary code execution, posing severe security risks—proceed with extreme caution.": "",
 	"Web": "Web",
 	"Web API": "API Web",
+	"Web Loader Engine": "",
 	"Web Search": "Recherche Web",
 	"Web Search Engine": "Moteur de recherche Web",
 	"Web Search in Chat": "",

+ 28 - 2
src/lib/i18n/locales/fr-FR/translation.json

@@ -62,6 +62,7 @@
 	"Allow Chat Deletion": "Autoriser la suppression de l'historique de chat",
 	"Allow Chat Edit": "Autoriser la modification de la conversation",
 	"Allow File Upload": "Autoriser le téléchargement de fichiers",
+	"Allow Multiple Models in Chat": "",
 	"Allow non-local voices": "Autoriser les voix non locales",
 	"Allow Temporary Chat": "Autoriser le chat éphémère",
 	"Allow User Location": "Autoriser l'emplacement de l'utilisateur",
@@ -79,6 +80,7 @@
 	"and": "et",
 	"and {{COUNT}} more": "et {{COUNT}} autres",
 	"and create a new shared link.": "et créer un nouveau lien partagé.",
+	"Android": "",
 	"API Base URL": "URL de base de l'API",
 	"API Key": "Clé d'API",
 	"API Key created.": "Clé d'API générée.",
@@ -141,7 +143,6 @@
 	"Brave Search API Key": "Clé API Brave Search",
 	"By {{name}}": "Par {{name}}",
 	"Bypass Embedding and Retrieval": "",
-	"Bypass SSL verification for Websites": "Bypasser la vérification SSL pour les sites web",
 	"Calendar": "",
 	"Call": "Appeler",
 	"Call feature is not supported when using Web STT engine": "La fonction d'appel n'est pas prise en charge lors de l'utilisation du moteur Web STT",
@@ -303,6 +304,7 @@
 	"Deleted User": "Utilisateur supprimé",
 	"Describe your knowledge base and objectives": "Décrivez votre base de connaissances et vos objectifs",
 	"Description": "Description",
+	"Detect Artifacts Automatically": "",
 	"Didn't fully follow instructions": "N'a pas entièrement respecté les instructions",
 	"Direct": "",
 	"Direct Connections": "",
@@ -358,6 +360,7 @@
 	"e.g. my_filter": "par ex. mon_filtre",
 	"e.g. my_tools": "par ex. mes_outils",
 	"e.g. Tools for performing various operations": "par ex. Outils pour effectuer diverses opérations",
+	"e.g., en-US,ja-JP (leave blank for auto-detect)": "",
 	"Edit": "Modifier",
 	"Edit Arena Model": "Modifier le modèle d'arène",
 	"Edit Channel": "Modifier le canal",
@@ -407,6 +410,8 @@
 	"Enter Document Intelligence Key": "",
 	"Enter domains separated by commas (e.g., example.com,site.org)": "",
 	"Enter Exa API Key": "",
+	"Enter Firecrawl API Base URL": "",
+	"Enter Firecrawl API Key": "",
 	"Enter Github Raw URL": "Entrez l'URL brute de GitHub",
 	"Enter Google PSE API Key": "Entrez la clé API Google PSE",
 	"Enter Google PSE Engine Id": "Entrez l'identifiant du moteur Google PSE",
@@ -424,6 +429,8 @@
 	"Enter Mojeek Search API Key": "Entrez la clé API Mojeek",
 	"Enter Number of Steps (e.g. 50)": "Entrez le nombre d'étapes (par ex. 50)",
 	"Enter Perplexity API Key": "",
+	"Enter Playwright Timeout": "",
+	"Enter Playwright WebSocket URL": "",
 	"Enter proxy URL (e.g. https://user:password@host:port)": "Entrez l'URL du proxy (par ex. https://use:password@host:port)",
 	"Enter reasoning effort": "Entrez l'effort de raisonnement",
 	"Enter Sampler (e.g. Euler a)": "Entrez le sampler (par ex. Euler a)",
@@ -441,10 +448,13 @@
 	"Enter server host": "Entrez l'hôte du serveur",
 	"Enter server label": "Entrez l'étiquette du serveur",
 	"Enter server port": "Entrez le port du serveur",
+	"Enter Sougou Search API sID": "",
+	"Enter Sougou Search API SK": "",
 	"Enter stop sequence": "Entrez la séquence d'arrêt",
 	"Enter system prompt": "Entrez le prompt système",
 	"Enter system prompt here": "",
 	"Enter Tavily API Key": "Entrez la clé API Tavily",
+	"Enter Tavily Extract Depth": "",
 	"Enter the public URL of your WebUI. This URL will be used to generate links in the notifications.": "Entrez l'URL publique de votre WebUI. Cette URL sera utilisée pour générer des liens dans les notifications.",
 	"Enter Tika Server URL": "Entrez l'URL du serveur Tika",
 	"Enter timeout in seconds": "",
@@ -525,6 +535,8 @@
 	"Filter is now globally enabled": "Le filtre est désormais activé globalement",
 	"Filters": "Filtres",
 	"Fingerprint spoofing detected: Unable to use initials as avatar. Defaulting to default profile image.": "Spoofing détecté : impossible d'utiliser les initiales comme avatar. Retour à l'image de profil par défaut.",
+	"Firecrawl API Base URL": "",
+	"Firecrawl API Key": "",
 	"Fluidly stream large external response chunks": "Streaming fluide de gros chunks de réponses externes",
 	"Focus chat input": "Mettre le focus sur le champ de chat",
 	"Folder deleted successfully": "Dossier supprimé avec succès",
@@ -589,6 +601,8 @@
 	"Hybrid Search": "Recherche hybride",
 	"I acknowledge that I have read and I understand the implications of my action. I am aware of the risks associated with executing arbitrary code and I have verified the trustworthiness of the source.": "Je reconnais avoir lu et compris les implications de mes actions. Je suis conscient des risques associés à l'exécution d'un code arbitraire et j'ai vérifié la fiabilité de la source.",
 	"ID": "ID",
+	"iframe Sandbox Allow Forms": "",
+	"iframe Sandbox Allow Same Origin": "",
 	"Ignite curiosity": "Éveiller la curiosité",
 	"Image": "Image",
 	"Image Compression": "Compression d'image",
@@ -649,6 +663,7 @@
 	"Label": "Étiquette",
 	"Landing Page Mode": "Mode de la page d'accueil",
 	"Language": "Langue",
+	"Language Locales": "",
 	"Last Active": "Dernière activité",
 	"Last Modified": "Dernière modification",
 	"Last reply": "Déernière réponse",
@@ -702,7 +717,6 @@
 	"Message rating should be enabled to use this feature": "L'évaluation des messages doit être activée pour pouvoir utiliser cette fonctionnalité",
 	"Messages you send after creating your link won't be shared. Users with the URL will be able to view the shared chat.": "Les messages que vous envoyez après avoir créé votre lien ne seront pas partagés. Les utilisateurs disposant de l'URL pourront voir la conversation partagée.",
 	"Min P": "P min",
-	"Minimum Score": "Score minimal",
 	"Mirostat": "Mirostat",
 	"Mirostat Eta": "Mirostat Eta",
 	"Mirostat Tau": "Mirostat Tau",
@@ -833,6 +847,8 @@
 	"Pipelines Valves": "Vannes de pipelines",
 	"Plain text (.txt)": "Texte (.txt)",
 	"Playground": "Playground",
+	"Playwright Timeout (ms)": "",
+	"Playwright WebSocket URL": "",
 	"Please carefully review the following warnings:": "Veuillez lire attentivement les avertissements suivants :",
 	"Please do not close the settings page while loading the model.": "Veuillez ne pas fermer les paramètres pendant le chargement du modèle.",
 	"Please enter a prompt": "Veuillez saisir un prompt",
@@ -878,8 +894,11 @@
 	"References from": "Références de",
 	"Refused when it shouldn't have": "Refusé alors qu'il n'aurait pas dû l'être",
 	"Regenerate": "Regénérer",
+	"Reindex": "",
+	"Reindex Knowledge Base Vectors": "",
 	"Release Notes": "Notes de mise à jour",
 	"Relevance": "Pertinence",
+	"Relevance Threshold": "",
 	"Remove": "Retirer",
 	"Remove Model": "Retirer le modèle",
 	"Rename": "Renommer",
@@ -1009,6 +1028,8 @@
 	"Sign up to {{WEBUI_NAME}}": "Inscrivez-vous à {{WEBUI_NAME}}",
 	"Signing in to {{WEBUI_NAME}}": "Connexion à {{WEBUI_NAME}}",
 	"sk-1234": "sk-1234",
+	"Sougou Search API sID": "",
+	"Sougou Search API SK": "",
 	"Source": "Source",
 	"Speech Playback Speed": "Vitesse de lecture de la parole",
 	"Speech recognition error: {{error}}": "Erreur de reconnaissance vocale : {{error}}",
@@ -1036,6 +1057,7 @@
 	"Tap to interrupt": "Appuyez pour interrompre",
 	"Tasks": "Tâches",
 	"Tavily API Key": "Clé API Tavily",
+	"Tavily Extract Depth": "",
 	"Tell us more:": "Dites-nous en plus à ce sujet : ",
 	"Temperature": "Température",
 	"Template": "Template",
@@ -1158,6 +1180,8 @@
 	"Use Gravatar": "Utiliser Gravatar",
 	"Use groups to group your users and assign permissions.": "Utilisez des groupes pour regrouper vos utilisateurs et attribuer des permissions.",
 	"Use Initials": "Utiliser les initiales",
+	"Use no proxy to fetch page contents.": "",
+	"Use proxy designated by http_proxy and https_proxy environment variables to fetch page contents.": "",
 	"use_mlock (Ollama)": "Utiliser mlock (Ollama)",
 	"use_mmap (Ollama)": "Utiliser mmap (Ollama)",
 	"user": "utilisateur",
@@ -1175,6 +1199,7 @@
 	"variable": "variable",
 	"variable to have them replaced with clipboard content.": "variable pour qu'elles soient remplacées par le contenu du presse-papiers.",
 	"Verify Connection": "",
+	"Verify SSL Certificate": "",
 	"Version": "Version:",
 	"Version {{selectedVersion}} of {{totalVersions}}": "Version {{selectedVersion}} de {{totalVersions}}",
 	"View Replies": "Voir les réponses",
@@ -1189,6 +1214,7 @@
 	"Warning: Jupyter execution enables arbitrary code execution, posing severe security risks—proceed with extreme caution.": "",
 	"Web": "Web",
 	"Web API": "API Web",
+	"Web Loader Engine": "",
 	"Web Search": "Recherche Web",
 	"Web Search Engine": "Moteur de recherche Web",
 	"Web Search in Chat": "Recherche web depuis le chat",

+ 28 - 2
src/lib/i18n/locales/he-IL/translation.json

@@ -62,6 +62,7 @@
 	"Allow Chat Deletion": "אפשר מחיקת צ'אט",
 	"Allow Chat Edit": "",
 	"Allow File Upload": "",
+	"Allow Multiple Models in Chat": "",
 	"Allow non-local voices": "",
 	"Allow Temporary Chat": "",
 	"Allow User Location": "",
@@ -79,6 +80,7 @@
 	"and": "וגם",
 	"and {{COUNT}} more": "",
 	"and create a new shared link.": "וצור קישור משותף חדש.",
+	"Android": "",
 	"API Base URL": "כתובת URL בסיסית ל-API",
 	"API Key": "מפתח API",
 	"API Key created.": "מפתח API נוצר.",
@@ -141,7 +143,6 @@
 	"Brave Search API Key": "מפתח API של חיפוש אמיץ",
 	"By {{name}}": "",
 	"Bypass Embedding and Retrieval": "",
-	"Bypass SSL verification for Websites": "עקוף אימות SSL עבור אתרים",
 	"Calendar": "",
 	"Call": "",
 	"Call feature is not supported when using Web STT engine": "",
@@ -303,6 +304,7 @@
 	"Deleted User": "",
 	"Describe your knowledge base and objectives": "",
 	"Description": "תיאור",
+	"Detect Artifacts Automatically": "",
 	"Didn't fully follow instructions": "לא עקב אחרי ההוראות באופן מלא",
 	"Direct": "",
 	"Direct Connections": "",
@@ -358,6 +360,7 @@
 	"e.g. my_filter": "",
 	"e.g. my_tools": "",
 	"e.g. Tools for performing various operations": "",
+	"e.g., en-US,ja-JP (leave blank for auto-detect)": "",
 	"Edit": "ערוך",
 	"Edit Arena Model": "",
 	"Edit Channel": "",
@@ -407,6 +410,8 @@
 	"Enter Document Intelligence Key": "",
 	"Enter domains separated by commas (e.g., example.com,site.org)": "",
 	"Enter Exa API Key": "",
+	"Enter Firecrawl API Base URL": "",
+	"Enter Firecrawl API Key": "",
 	"Enter Github Raw URL": "הזן כתובת URL של Github Raw",
 	"Enter Google PSE API Key": "הזן מפתח API של Google PSE",
 	"Enter Google PSE Engine Id": "הזן את מזהה מנוע PSE של Google",
@@ -424,6 +429,8 @@
 	"Enter Mojeek Search API Key": "",
 	"Enter Number of Steps (e.g. 50)": "הזן מספר שלבים (למשל 50)",
 	"Enter Perplexity API Key": "",
+	"Enter Playwright Timeout": "",
+	"Enter Playwright WebSocket URL": "",
 	"Enter proxy URL (e.g. https://user:password@host:port)": "",
 	"Enter reasoning effort": "",
 	"Enter Sampler (e.g. Euler a)": "",
@@ -441,10 +448,13 @@
 	"Enter server host": "",
 	"Enter server label": "",
 	"Enter server port": "",
+	"Enter Sougou Search API sID": "",
+	"Enter Sougou Search API SK": "",
 	"Enter stop sequence": "הזן רצף עצירה",
 	"Enter system prompt": "",
 	"Enter system prompt here": "",
 	"Enter Tavily API Key": "",
+	"Enter Tavily Extract Depth": "",
 	"Enter the public URL of your WebUI. This URL will be used to generate links in the notifications.": "",
 	"Enter Tika Server URL": "",
 	"Enter timeout in seconds": "",
@@ -525,6 +535,8 @@
 	"Filter is now globally enabled": "",
 	"Filters": "",
 	"Fingerprint spoofing detected: Unable to use initials as avatar. Defaulting to default profile image.": "התגלתה הזיית טביעת אצבע: לא ניתן להשתמש בראשי תיבות כאווטאר. משתמש בתמונת פרופיל ברירת מחדל.",
+	"Firecrawl API Base URL": "",
+	"Firecrawl API Key": "",
 	"Fluidly stream large external response chunks": "שידור נתונים חיצוניים בקצב רציף",
 	"Focus chat input": "מיקוד הקלט לצ'אט",
 	"Folder deleted successfully": "",
@@ -589,6 +601,8 @@
 	"Hybrid Search": "חיפוש היברידי",
 	"I acknowledge that I have read and I understand the implications of my action. I am aware of the risks associated with executing arbitrary code and I have verified the trustworthiness of the source.": "",
 	"ID": "",
+	"iframe Sandbox Allow Forms": "",
+	"iframe Sandbox Allow Same Origin": "",
 	"Ignite curiosity": "",
 	"Image": "",
 	"Image Compression": "",
@@ -649,6 +663,7 @@
 	"Label": "",
 	"Landing Page Mode": "",
 	"Language": "שפה",
+	"Language Locales": "",
 	"Last Active": "פעיל לאחרונה",
 	"Last Modified": "",
 	"Last reply": "",
@@ -702,7 +717,6 @@
 	"Message rating should be enabled to use this feature": "",
 	"Messages you send after creating your link won't be shared. Users with the URL will be able to view the shared chat.": "הודעות שתשלח לאחר יצירת הקישור לא ישותפו. משתמשים עם כתובת האתר יוכלו לצפות בצ'אט המשותף.",
 	"Min P": "",
-	"Minimum Score": "ציון מינימלי",
 	"Mirostat": "Mirostat",
 	"Mirostat Eta": "Mirostat Eta",
 	"Mirostat Tau": "Mirostat Tau",
@@ -833,6 +847,8 @@
 	"Pipelines Valves": "צינורות שסתומים",
 	"Plain text (.txt)": "טקסט פשוט (.txt)",
 	"Playground": "אזור משחקים",
+	"Playwright Timeout (ms)": "",
+	"Playwright WebSocket URL": "",
 	"Please carefully review the following warnings:": "",
 	"Please do not close the settings page while loading the model.": "",
 	"Please enter a prompt": "",
@@ -878,8 +894,11 @@
 	"References from": "",
 	"Refused when it shouldn't have": "נדחה כאשר לא היה צריך",
 	"Regenerate": "הפק מחדש",
+	"Reindex": "",
+	"Reindex Knowledge Base Vectors": "",
 	"Release Notes": "הערות שחרור",
 	"Relevance": "",
+	"Relevance Threshold": "",
 	"Remove": "הסר",
 	"Remove Model": "הסר מודל",
 	"Rename": "שנה שם",
@@ -1009,6 +1028,8 @@
 	"Sign up to {{WEBUI_NAME}}": "",
 	"Signing in to {{WEBUI_NAME}}": "",
 	"sk-1234": "",
+	"Sougou Search API sID": "",
+	"Sougou Search API SK": "",
 	"Source": "מקור",
 	"Speech Playback Speed": "",
 	"Speech recognition error: {{error}}": "שגיאת תחקור שמע: {{error}}",
@@ -1036,6 +1057,7 @@
 	"Tap to interrupt": "",
 	"Tasks": "",
 	"Tavily API Key": "",
+	"Tavily Extract Depth": "",
 	"Tell us more:": "תרשמו יותר:",
 	"Temperature": "טמפרטורה",
 	"Template": "תבנית",
@@ -1158,6 +1180,8 @@
 	"Use Gravatar": "שימוש ב Gravatar",
 	"Use groups to group your users and assign permissions.": "",
 	"Use Initials": "שימוש ב initials",
+	"Use no proxy to fetch page contents.": "",
+	"Use proxy designated by http_proxy and https_proxy environment variables to fetch page contents.": "",
 	"use_mlock (Ollama)": "use_mlock (אולמה)",
 	"use_mmap (Ollama)": "use_mmap (Ollama)",
 	"user": "משתמש",
@@ -1175,6 +1199,7 @@
 	"variable": "משתנה",
 	"variable to have them replaced with clipboard content.": "משתנה להחליפו ב- clipboard תוכן.",
 	"Verify Connection": "",
+	"Verify SSL Certificate": "",
 	"Version": "גרסה",
 	"Version {{selectedVersion}} of {{totalVersions}}": "",
 	"View Replies": "",
@@ -1189,6 +1214,7 @@
 	"Warning: Jupyter execution enables arbitrary code execution, posing severe security risks—proceed with extreme caution.": "",
 	"Web": "רשת",
 	"Web API": "",
+	"Web Loader Engine": "",
 	"Web Search": "חיפוש באינטרנט",
 	"Web Search Engine": "מנוע חיפוש באינטרנט",
 	"Web Search in Chat": "",

Algunos archivos no se mostraron porque demasiados archivos cambiaron en este cambio