瀏覽代碼

Merge remote-tracking branch 'upstream/dev' into feat/always-web-search

# Conflicts:
#	src/lib/components/chat/Chat.svelte
hurxxxx 2 月之前
父節點
當前提交
0e3c1ac392
共有 100 個文件被更改,包括 2309 次插入1010 次删除
  1. 70 12
      backend/open_webui/config.py
  2. 1 1
      backend/open_webui/constants.py
  3. 13 6
      backend/open_webui/env.py
  4. 23 6
      backend/open_webui/main.py
  5. 5 1
      backend/open_webui/retrieval/vector/dbs/milvus.py
  6. 74 0
      backend/open_webui/retrieval/web/exa.py
  7. 4 0
      backend/open_webui/retrieval/web/main.py
  8. 15 4
      backend/open_webui/retrieval/web/utils.py
  9. 6 1
      backend/open_webui/routers/auths.py
  10. 8 2
      backend/open_webui/routers/chats.py
  11. 7 10
      backend/open_webui/routers/knowledge.py
  12. 4 4
      backend/open_webui/routers/models.py
  13. 5 4
      backend/open_webui/routers/ollama.py
  14. 4 4
      backend/open_webui/routers/openai.py
  15. 14 0
      backend/open_webui/routers/retrieval.py
  16. 19 3
      backend/open_webui/routers/tasks.py
  17. 1 0
      backend/open_webui/routers/users.py
  18. 5 2
      backend/open_webui/socket/main.py
  19. 686 202
      backend/open_webui/utils/middleware.py
  20. 66 6
      backend/open_webui/utils/misc.py
  21. 9 3
      backend/open_webui/utils/oauth.py
  22. 15 10
      backend/open_webui/utils/payload.py
  23. 9 4
      backend/open_webui/utils/plugin.py
  24. 61 2
      backend/open_webui/utils/response.py
  25. 6 0
      backend/open_webui/utils/task.py
  26. 13 0
      backend/open_webui/utils/tools.py
  27. 8 8
      backend/requirements.txt
  28. 5 4
      package-lock.json
  29. 1 1
      package.json
  30. 8 8
      pyproject.toml
  31. 4 1
      scripts/prepare-pyodide.js
  32. 5 2
      src/lib/apis/chats/index.ts
  33. 3 2
      src/lib/apis/index.ts
  34. 1 1
      src/lib/apis/models/index.ts
  35. 1 1
      src/lib/apis/openai/index.ts
  36. 2 2
      src/lib/components/admin/Settings/Audio.svelte
  37. 16 1
      src/lib/components/admin/Settings/Interface.svelte
  38. 13 1
      src/lib/components/admin/Settings/WebSearch.svelte
  39. 2 1
      src/lib/components/admin/Users/Groups.svelte
  40. 2 1
      src/lib/components/admin/Users/Groups/EditGroupModal.svelte
  41. 10 1
      src/lib/components/admin/Users/Groups/Permissions.svelte
  42. 2 2
      src/lib/components/admin/Users/UserList.svelte
  43. 2 2
      src/lib/components/admin/Users/UserList/EditUserModal.svelte
  44. 2 2
      src/lib/components/admin/Users/UserList/UserChatsModal.svelte
  45. 35 33
      src/lib/components/channel/MessageInput.svelte
  46. 5 9
      src/lib/components/channel/Messages/Message.svelte
  47. 76 68
      src/lib/components/chat/Chat.svelte
  48. 164 80
      src/lib/components/chat/MessageInput.svelte
  49. 48 67
      src/lib/components/chat/MessageInput/InputMenu.svelte
  50. 1 1
      src/lib/components/chat/Messages.svelte
  51. 98 92
      src/lib/components/chat/Messages/CodeBlock.svelte
  52. 8 2
      src/lib/components/chat/Messages/Markdown/MarkdownTokens.svelte
  53. 2 0
      src/lib/components/chat/Messages/Message.svelte
  54. 5 2
      src/lib/components/chat/Messages/MultiResponseMessages.svelte
  55. 35 0
      src/lib/components/chat/Messages/ResponseMessage.svelte
  56. 2 2
      src/lib/components/chat/Messages/UserMessage.svelte
  57. 3 0
      src/lib/components/chat/Placeholder.svelte
  58. 30 0
      src/lib/components/chat/Settings/Advanced/AdvancedParams.svelte
  59. 1 1
      src/lib/components/chat/Settings/Audio.svelte
  60. 1 0
      src/lib/components/chat/Settings/General.svelte
  61. 2 2
      src/lib/components/chat/Settings/Personalization/ManageModal.svelte
  62. 114 36
      src/lib/components/chat/Suggestions.svelte
  63. 6 0
      src/lib/components/common/Collapsible.svelte
  64. 1 1
      src/lib/components/common/FileItem.svelte
  65. 32 51
      src/lib/components/common/Textarea.svelte
  66. 19 0
      src/lib/components/icons/CommandLine.svelte
  67. 11 0
      src/lib/components/icons/CommandLineSolid.svelte
  68. 21 0
      src/lib/components/icons/Photo.svelte
  69. 2 2
      src/lib/components/layout/Help.svelte
  70. 2 2
      src/lib/components/layout/Sidebar/ArchivedChatsModal.svelte
  71. 7 1
      src/lib/components/layout/Sidebar/ChatItem.svelte
  72. 26 1
      src/lib/components/playground/Chat.svelte
  73. 76 0
      src/lib/components/playground/Chat/Message.svelte
  74. 8 65
      src/lib/components/playground/Chat/Messages.svelte
  75. 1 1
      src/lib/components/workspace/Knowledge.svelte
  76. 2 5
      src/lib/components/workspace/Knowledge/CreateKnowledgeBase.svelte
  77. 9 9
      src/lib/components/workspace/Knowledge/KnowledgeBase.svelte
  78. 1 1
      src/lib/components/workspace/Models.svelte
  79. 1 4
      src/lib/components/workspace/Models/ModelEditor.svelte
  80. 1 1
      src/lib/components/workspace/common/ValvesModal.svelte
  81. 10 6
      src/lib/i18n/locales/ar-BH/translation.json
  82. 10 6
      src/lib/i18n/locales/bg-BG/translation.json
  83. 10 6
      src/lib/i18n/locales/bn-BD/translation.json
  84. 10 6
      src/lib/i18n/locales/ca-ES/translation.json
  85. 10 6
      src/lib/i18n/locales/ceb-PH/translation.json
  86. 10 6
      src/lib/i18n/locales/cs-CZ/translation.json
  87. 10 6
      src/lib/i18n/locales/da-DK/translation.json
  88. 10 6
      src/lib/i18n/locales/de-DE/translation.json
  89. 10 6
      src/lib/i18n/locales/dg-DG/translation.json
  90. 10 6
      src/lib/i18n/locales/el-GR/translation.json
  91. 10 6
      src/lib/i18n/locales/en-GB/translation.json
  92. 10 6
      src/lib/i18n/locales/en-US/translation.json
  93. 10 6
      src/lib/i18n/locales/es-ES/translation.json
  94. 10 6
      src/lib/i18n/locales/eu-ES/translation.json
  95. 10 6
      src/lib/i18n/locales/fa-IR/translation.json
  96. 10 6
      src/lib/i18n/locales/fi-FI/translation.json
  97. 11 7
      src/lib/i18n/locales/fr-CA/translation.json
  98. 32 28
      src/lib/i18n/locales/fr-FR/translation.json
  99. 10 6
      src/lib/i18n/locales/he-IL/translation.json
  100. 10 6
      src/lib/i18n/locales/hi-IN/translation.json

+ 70 - 12
backend/open_webui/config.py

@@ -927,6 +927,12 @@ USER_PERMISSIONS_FEATURES_IMAGE_GENERATION = (
     == "true"
     == "true"
 )
 )
 
 
+USER_PERMISSIONS_FEATURES_CODE_INTERPRETER = (
+    os.environ.get("USER_PERMISSIONS_FEATURES_CODE_INTERPRETER", "True").lower()
+    == "true"
+)
+
+
 DEFAULT_USER_PERMISSIONS = {
 DEFAULT_USER_PERMISSIONS = {
     "workspace": {
     "workspace": {
         "models": USER_PERMISSIONS_WORKSPACE_MODELS_ACCESS,
         "models": USER_PERMISSIONS_WORKSPACE_MODELS_ACCESS,
@@ -944,6 +950,7 @@ DEFAULT_USER_PERMISSIONS = {
     "features": {
     "features": {
         "web_search": USER_PERMISSIONS_FEATURES_WEB_SEARCH,
         "web_search": USER_PERMISSIONS_FEATURES_WEB_SEARCH,
         "image_generation": USER_PERMISSIONS_FEATURES_IMAGE_GENERATION,
         "image_generation": USER_PERMISSIONS_FEATURES_IMAGE_GENERATION,
+        "code_interpreter": USER_PERMISSIONS_FEATURES_CODE_INTERPRETER,
     },
     },
 }
 }
 
 
@@ -1094,21 +1101,27 @@ TITLE_GENERATION_PROMPT_TEMPLATE = PersistentConfig(
     os.environ.get("TITLE_GENERATION_PROMPT_TEMPLATE", ""),
     os.environ.get("TITLE_GENERATION_PROMPT_TEMPLATE", ""),
 )
 )
 
 
-DEFAULT_TITLE_GENERATION_PROMPT_TEMPLATE = """Create a concise, 3-5 word title with an emoji as a title for the chat history, in the given language. Suitable Emojis for the summary can be used to enhance understanding but avoid quotation marks or special formatting. RESPOND ONLY WITH THE TITLE TEXT.
-
-Examples of titles:
-📉 Stock Market Trends
-🍪 Perfect Chocolate Chip Recipe
-Evolution of Music Streaming
-Remote Work Productivity Tips
-Artificial Intelligence in Healthcare
-🎮 Video Game Development Insights
-
+DEFAULT_TITLE_GENERATION_PROMPT_TEMPLATE = """### Task:
+Generate a concise, 3-5 word title with an emoji summarizing the chat history.
+### Guidelines:
+- The title should clearly represent the main theme or subject of the conversation.
+- Use emojis that enhance understanding of the topic, but avoid quotation marks or special formatting.
+- Write the title in the chat's primary language; default to English if multilingual.
+- Prioritize accuracy over excessive creativity; keep it clear and simple.
+### Output:
+JSON format: { "title": "your concise title here" }
+### Examples:
+- { "title": "📉 Stock Market Trends" },
+- { "title": "🍪 Perfect Chocolate Chip Recipe" },
+- { "title": "Evolution of Music Streaming" },
+- { "title": "Remote Work Productivity Tips" },
+- { "title": "Artificial Intelligence in Healthcare" },
+- { "title": "🎮 Video Game Development Insights" }
+### Chat History:
 <chat_history>
 <chat_history>
 {{MESSAGES:END:2}}
 {{MESSAGES:END:2}}
 </chat_history>"""
 </chat_history>"""
 
 
-
 TAGS_GENERATION_PROMPT_TEMPLATE = PersistentConfig(
 TAGS_GENERATION_PROMPT_TEMPLATE = PersistentConfig(
     "TAGS_GENERATION_PROMPT_TEMPLATE",
     "TAGS_GENERATION_PROMPT_TEMPLATE",
     "task.tags.prompt_template",
     "task.tags.prompt_template",
@@ -1277,7 +1290,28 @@ TOOLS_FUNCTION_CALLING_PROMPT_TEMPLATE = PersistentConfig(
 )
 )
 
 
 
 
-DEFAULT_TOOLS_FUNCTION_CALLING_PROMPT_TEMPLATE = """Available Tools: {{TOOLS}}\nReturn an empty string if no tools match the query. If a function tool matches, construct and return a JSON object in the format {\"name\": \"functionName\", \"parameters\": {\"requiredFunctionParamKey\": \"requiredFunctionParamValue\"}} using the appropriate tool and its parameters. Only return the object and limit the response to the JSON object without additional text."""
+DEFAULT_TOOLS_FUNCTION_CALLING_PROMPT_TEMPLATE = """Available Tools: {{TOOLS}}
+
+Your task is to choose and return the correct tool(s) from the list of available tools based on the query. Follow these guidelines:
+
+- Return only the JSON object, without any additional text or explanation.
+
+- If no tools match the query, return an empty array: 
+   {
+     "tool_calls": []
+   }
+
+- If one or more tools match the query, construct a JSON response containing a "tool_calls" array with objects that include:
+   - "name": The tool's name.
+   - "parameters": A dictionary of required parameters and their corresponding values.
+
+The format for the JSON response is strictly:
+{
+  "tool_calls": [
+    {"name": "toolName1", "parameters": {"key1": "value1"}},
+    {"name": "toolName2", "parameters": {"key2": "value2"}}
+  ]
+}"""
 
 
 
 
 DEFAULT_EMOJI_GENERATION_PROMPT_TEMPLATE = """Your task is to reflect the speaker's likely facial expression through a fitting emoji. Interpret emotions from the message and reflect their facial expression using fitting, diverse emojis (e.g., 😊, 😢, 😡, 😱).
 DEFAULT_EMOJI_GENERATION_PROMPT_TEMPLATE = """Your task is to reflect the speaker's likely facial expression through a fitting emoji. Interpret emotions from the message and reflect their facial expression using fitting, diverse emojis (e.g., 😊, 😢, 😡, 😱).
@@ -1290,6 +1324,24 @@ Your task is to synthesize these responses into a single, high-quality response.
 
 
 Responses from models: {{responses}}"""
 Responses from models: {{responses}}"""
 
 
+
+DEFAULT_CODE_INTERPRETER_PROMPT = """
+#### Tools Available
+
+1. **Code Interpreter**: `<code_interpreter type="code" lang="python"></code_interpreter>`
+   - You have access to a Python shell that runs directly in the user's browser, enabling fast execution of code for analysis, calculations, or problem-solving.  Use it in this response.
+   - The Python code you write can incorporate a wide array of libraries, handle data manipulation or visualization, perform API calls for web-related tasks, or tackle virtually any computational challenge. Use this flexibility to **think outside the box, craft elegant solutions, and harness Python's full potential**.
+   - To use it, **you must enclose your code within `<code_interpreter type="code" lang="python">` XML tags** and stop right away. If you don't, the code won't execute. Do NOT use triple backticks.
+   - When coding, **always aim to print meaningful outputs** (e.g., results, tables, summaries, or visuals) to better interpret and verify the findings. Avoid relying on implicit outputs; prioritize explicit and clear print statements so the results are effectively communicated to the user.  
+   - After obtaining the printed output, **always provide a concise analysis, interpretation, or next steps to help the user understand the findings or refine the outcome further.**  
+   - If the results are unclear, unexpected, or require validation, refine the code and execute it again as needed. Always aim to deliver meaningful insights from the results, iterating if necessary.  
+   - If a link is provided for an image, audio, or any file, include it in the response exactly as given to ensure the user has access to the original resource.  
+   - All responses should be communicated in the chat's primary language, ensuring seamless understanding. If the chat is multilingual, default to English for clarity.
+   - **If a link to an image, audio, or any file is provided in markdown format, explicitly display it as part of the response to ensure the user can access it easily, do NOT change the link.**
+
+Ensure that the tools are effectively utilized to achieve the highest-quality analysis for the user."""
+
+
 ####################################
 ####################################
 # Vector Database
 # Vector Database
 ####################################
 ####################################
@@ -1319,6 +1371,7 @@ CHROMA_HTTP_SSL = os.environ.get("CHROMA_HTTP_SSL", "false").lower() == "true"
 
 
 MILVUS_URI = os.environ.get("MILVUS_URI", f"{DATA_DIR}/vector_db/milvus.db")
 MILVUS_URI = os.environ.get("MILVUS_URI", f"{DATA_DIR}/vector_db/milvus.db")
 MILVUS_DB = os.environ.get("MILVUS_DB", "default")
 MILVUS_DB = os.environ.get("MILVUS_DB", "default")
+MILVUS_TOKEN = os.environ.get("MILVUS_TOKEN", None)
 
 
 # Qdrant
 # Qdrant
 QDRANT_URI = os.environ.get("QDRANT_URI", None)
 QDRANT_URI = os.environ.get("QDRANT_URI", None)
@@ -1699,6 +1752,11 @@ BING_SEARCH_V7_SUBSCRIPTION_KEY = PersistentConfig(
     os.environ.get("BING_SEARCH_V7_SUBSCRIPTION_KEY", ""),
     os.environ.get("BING_SEARCH_V7_SUBSCRIPTION_KEY", ""),
 )
 )
 
 
+EXA_API_KEY = PersistentConfig(
+    "EXA_API_KEY",
+    "rag.web.search.exa_api_key",
+    os.getenv("EXA_API_KEY", ""),
+)
 
 
 RAG_WEB_SEARCH_RESULT_COUNT = PersistentConfig(
 RAG_WEB_SEARCH_RESULT_COUNT = PersistentConfig(
     "RAG_WEB_SEARCH_RESULT_COUNT",
     "RAG_WEB_SEARCH_RESULT_COUNT",

+ 1 - 1
backend/open_webui/constants.py

@@ -57,7 +57,7 @@ class ERROR_MESSAGES(str, Enum):
     )
     )
 
 
     FILE_NOT_SENT = "FILE_NOT_SENT"
     FILE_NOT_SENT = "FILE_NOT_SENT"
-    FILE_NOT_SUPPORTED = "Oops! It seems like the file format you're trying to upload is not supported. Please upload a file with a supported format (e.g., JPG, PNG, PDF, TXT) and try again."
+    FILE_NOT_SUPPORTED = "Oops! It seems like the file format you're trying to upload is not supported. Please upload a file with a supported format and try again."
 
 
     NOT_FOUND = "We could not find what you're looking for :/"
     NOT_FOUND = "We could not find what you're looking for :/"
     USER_NOT_FOUND = "We could not find what you're looking for :/"
     USER_NOT_FOUND = "We could not find what you're looking for :/"

+ 13 - 6
backend/open_webui/env.py

@@ -358,14 +358,21 @@ WEBUI_SECRET_KEY = os.environ.get(
 
 
 WEBUI_SESSION_COOKIE_SAME_SITE = os.environ.get("WEBUI_SESSION_COOKIE_SAME_SITE", "lax")
 WEBUI_SESSION_COOKIE_SAME_SITE = os.environ.get("WEBUI_SESSION_COOKIE_SAME_SITE", "lax")
 
 
-WEBUI_SESSION_COOKIE_SECURE = os.environ.get("WEBUI_SESSION_COOKIE_SECURE", "false").lower() == "true"
+WEBUI_SESSION_COOKIE_SECURE = (
+    os.environ.get("WEBUI_SESSION_COOKIE_SECURE", "false").lower() == "true"
+)
 
 
-WEBUI_AUTH_COOKIE_SAME_SITE = os.environ.get("WEBUI_AUTH_COOKIE_SAME_SITE", WEBUI_SESSION_COOKIE_SAME_SITE)
+WEBUI_AUTH_COOKIE_SAME_SITE = os.environ.get(
+    "WEBUI_AUTH_COOKIE_SAME_SITE", WEBUI_SESSION_COOKIE_SAME_SITE
+)
 
 
-WEBUI_AUTH_COOKIE_SECURE = os.environ.get(
-    "WEBUI_AUTH_COOKIE_SECURE", 
-    os.environ.get("WEBUI_SESSION_COOKIE_SECURE", "false")
-).lower() == "true"
+WEBUI_AUTH_COOKIE_SECURE = (
+    os.environ.get(
+        "WEBUI_AUTH_COOKIE_SECURE",
+        os.environ.get("WEBUI_SESSION_COOKIE_SECURE", "false"),
+    ).lower()
+    == "true"
+)
 
 
 if WEBUI_AUTH and WEBUI_SECRET_KEY == "":
 if WEBUI_AUTH and WEBUI_SECRET_KEY == "":
     raise ValueError(ERROR_MESSAGES.ENV_VAR_NOT_FOUND)
     raise ValueError(ERROR_MESSAGES.ENV_VAR_NOT_FOUND)

+ 23 - 6
backend/open_webui/main.py

@@ -177,6 +177,7 @@ from open_webui.config import (
     BING_SEARCH_V7_ENDPOINT,
     BING_SEARCH_V7_ENDPOINT,
     BING_SEARCH_V7_SUBSCRIPTION_KEY,
     BING_SEARCH_V7_SUBSCRIPTION_KEY,
     BRAVE_SEARCH_API_KEY,
     BRAVE_SEARCH_API_KEY,
+    EXA_API_KEY,
     KAGI_SEARCH_API_KEY,
     KAGI_SEARCH_API_KEY,
     MOJEEK_SEARCH_API_KEY,
     MOJEEK_SEARCH_API_KEY,
     GOOGLE_PSE_API_KEY,
     GOOGLE_PSE_API_KEY,
@@ -523,6 +524,7 @@ app.state.config.SEARCHAPI_ENGINE = SEARCHAPI_ENGINE
 app.state.config.JINA_API_KEY = JINA_API_KEY
 app.state.config.JINA_API_KEY = JINA_API_KEY
 app.state.config.BING_SEARCH_V7_ENDPOINT = BING_SEARCH_V7_ENDPOINT
 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.BING_SEARCH_V7_SUBSCRIPTION_KEY = BING_SEARCH_V7_SUBSCRIPTION_KEY
+app.state.config.EXA_API_KEY = EXA_API_KEY
 
 
 app.state.config.RAG_WEB_SEARCH_RESULT_COUNT = RAG_WEB_SEARCH_RESULT_COUNT
 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_SEARCH_CONCURRENT_REQUESTS = RAG_WEB_SEARCH_CONCURRENT_REQUESTS
@@ -859,6 +861,7 @@ async def chat_completion(
         if model_id not in request.app.state.MODELS:
         if model_id not in request.app.state.MODELS:
             raise Exception("Model not found")
             raise Exception("Model not found")
         model = request.app.state.MODELS[model_id]
         model = request.app.state.MODELS[model_id]
+        model_info = Models.get_model_by_id(model_id)
 
 
         # Check if user has access to the model
         # Check if user has access to the model
         if not BYPASS_MODEL_ACCESS_CONTROL and user.role == "user":
         if not BYPASS_MODEL_ACCESS_CONTROL and user.role == "user":
@@ -875,12 +878,25 @@ async def chat_completion(
             "tool_ids": form_data.get("tool_ids", None),
             "tool_ids": form_data.get("tool_ids", None),
             "files": form_data.get("files", None),
             "files": form_data.get("files", None),
             "features": form_data.get("features", None),
             "features": form_data.get("features", None),
+            "variables": form_data.get("variables", None),
+            "model": model_info,
+            **(
+                {"function_calling": "native"}
+                if form_data.get("params", {}).get("function_calling") == "native"
+                or (
+                    model_info
+                    and model_info.params.model_dump().get("function_calling")
+                    == "native"
+                )
+                else {}
+            ),
         }
         }
         form_data["metadata"] = metadata
         form_data["metadata"] = metadata
 
 
-        form_data, events = await process_chat_payload(
+        form_data, metadata, events = await process_chat_payload(
             request, form_data, metadata, user, model
             request, form_data, metadata, user, model
         )
         )
+
     except Exception as e:
     except Exception as e:
         raise HTTPException(
         raise HTTPException(
             status_code=status.HTTP_400_BAD_REQUEST,
             status_code=status.HTTP_400_BAD_REQUEST,
@@ -889,6 +905,7 @@ async def chat_completion(
 
 
     try:
     try:
         response = await chat_completion_handler(request, form_data, user)
         response = await chat_completion_handler(request, form_data, user)
+
         return await process_chat_response(
         return await process_chat_response(
             request, response, form_data, user, events, metadata, tasks
             request, response, form_data, user, events, metadata, tasks
         )
         )
@@ -1007,10 +1024,6 @@ async def get_app_config(request: Request):
                 else {}
                 else {}
             ),
             ),
         },
         },
-        "google_drive": {
-            "client_id": GOOGLE_DRIVE_CLIENT_ID.value,
-            "api_key": GOOGLE_DRIVE_API_KEY.value,
-        },
         **(
         **(
             {
             {
                 "default_models": app.state.config.DEFAULT_MODELS,
                 "default_models": app.state.config.DEFAULT_MODELS,
@@ -1030,6 +1043,10 @@ async def get_app_config(request: Request):
                     "max_count": app.state.config.FILE_MAX_COUNT,
                     "max_count": app.state.config.FILE_MAX_COUNT,
                 },
                 },
                 "permissions": {**app.state.config.USER_PERMISSIONS},
                 "permissions": {**app.state.config.USER_PERMISSIONS},
+                "google_drive": {
+                    "client_id": GOOGLE_DRIVE_CLIENT_ID.value,
+                    "api_key": GOOGLE_DRIVE_API_KEY.value,
+                },
             }
             }
             if user is not None
             if user is not None
             else {}
             else {}
@@ -1063,7 +1080,7 @@ async def get_app_version():
 
 
 
 
 @app.get("/api/version/updates")
 @app.get("/api/version/updates")
-async def get_app_latest_release_version():
+async def get_app_latest_release_version(user=Depends(get_verified_user)):
     if OFFLINE_MODE:
     if OFFLINE_MODE:
         log.debug(
         log.debug(
             f"Offline mode is enabled, returning current version as latest version"
             f"Offline mode is enabled, returning current version as latest version"

+ 5 - 1
backend/open_webui/retrieval/vector/dbs/milvus.py

@@ -8,13 +8,17 @@ from open_webui.retrieval.vector.main import VectorItem, SearchResult, GetResult
 from open_webui.config import (
 from open_webui.config import (
     MILVUS_URI,
     MILVUS_URI,
     MILVUS_DB,
     MILVUS_DB,
+    MILVUS_TOKEN,
 )
 )
 
 
 
 
 class MilvusClient:
 class MilvusClient:
     def __init__(self):
     def __init__(self):
         self.collection_prefix = "open_webui"
         self.collection_prefix = "open_webui"
-        self.client = Client(uri=MILVUS_URI, database=MILVUS_DB)
+        if MILVUS_TOKEN is None:
+            self.client = Client(uri=MILVUS_URI, database=MILVUS_DB)
+        else:
+            self.client = Client(uri=MILVUS_URI, database=MILVUS_DB, token=MILVUS_TOKEN)
 
 
     def _result_to_get_result(self, result) -> GetResult:
     def _result_to_get_result(self, result) -> GetResult:
         ids = []
         ids = []

+ 74 - 0
backend/open_webui/retrieval/web/exa.py

@@ -0,0 +1,74 @@
+import logging
+from dataclasses import dataclass
+from typing import Optional
+
+import requests
+from open_webui.env import SRC_LOG_LEVELS
+from open_webui.retrieval.web.main import SearchResult
+
+log = logging.getLogger(__name__)
+log.setLevel(SRC_LOG_LEVELS["RAG"])
+
+EXA_API_BASE = "https://api.exa.ai"
+
+
+@dataclass
+class ExaResult:
+    url: str
+    title: str
+    text: str
+
+
+def search_exa(
+    api_key: str,
+    query: str,
+    count: int,
+    filter_list: Optional[list[str]] = None,
+) -> list[SearchResult]:
+    """Search using Exa Search API and return the results as a list of SearchResult objects.
+
+    Args:
+        api_key (str): A Exa Search API key
+        query (str): The query to search for
+        count (int): Number of results to return
+        filter_list (Optional[list[str]]): List of domains to filter results by
+    """
+    log.info(f"Searching with Exa for query: {query}")
+
+    headers = {"Authorization": f"Bearer {api_key}", "Content-Type": "application/json"}
+
+    payload = {
+        "query": query,
+        "numResults": count or 5,
+        "includeDomains": filter_list,
+        "contents": {"text": True, "highlights": True},
+        "type": "auto",  # Use the auto search type (keyword or neural)
+    }
+
+    try:
+        response = requests.post(f"{EXA_API_BASE}/search", headers=headers, json=payload)
+        response.raise_for_status()
+        data = response.json()
+
+        results = []
+        for result in data["results"]:
+            results.append(
+                ExaResult(
+                    url=result["url"],
+                    title=result["title"],
+                    text=result["text"],
+                )
+            )
+
+        log.info(f"Found {len(results)} results")
+        return [
+            SearchResult(
+                link=result.url,
+                title=result.title,
+                snippet=result.text,
+            )
+            for result in results
+        ]
+    except Exception as e:
+        log.error(f"Error searching Exa: {e}")
+        return []

+ 4 - 0
backend/open_webui/retrieval/web/main.py

@@ -1,3 +1,5 @@
+import validators
+
 from typing import Optional
 from typing import Optional
 from urllib.parse import urlparse
 from urllib.parse import urlparse
 
 
@@ -10,6 +12,8 @@ def get_filtered_results(results, filter_list):
     filtered_results = []
     filtered_results = []
     for result in results:
     for result in results:
         url = result.get("url") or result.get("link", "")
         url = result.get("url") or result.get("link", "")
+        if not validators.url(url):
+            continue
         domain = urlparse(url).netloc
         domain = urlparse(url).netloc
         if any(domain.endswith(filtered_domain) for filtered_domain in filter_list):
         if any(domain.endswith(filtered_domain) for filtered_domain in filter_list):
             filtered_results.append(result)
             filtered_results.append(result)

+ 15 - 4
backend/open_webui/retrieval/web/utils.py

@@ -43,6 +43,17 @@ def validate_url(url: Union[str, Sequence[str]]):
         return False
         return False
 
 
 
 
+def safe_validate_urls(url: Sequence[str]) -> Sequence[str]:
+    valid_urls = []
+    for u in url:
+        try:
+            if validate_url(u):
+                valid_urls.append(u)
+        except ValueError:
+            continue
+    return valid_urls
+
+
 def resolve_hostname(hostname):
 def resolve_hostname(hostname):
     # Get address information
     # Get address information
     addr_info = socket.getaddrinfo(hostname, None)
     addr_info = socket.getaddrinfo(hostname, None)
@@ -86,11 +97,11 @@ def get_web_loader(
     verify_ssl: bool = True,
     verify_ssl: bool = True,
     requests_per_second: int = 2,
     requests_per_second: int = 2,
 ):
 ):
-    # Check if the URL is valid
-    if not validate_url(urls):
-        raise ValueError(ERROR_MESSAGES.INVALID_URL)
+    # Check if the URLs are valid
+    safe_urls = safe_validate_urls([urls] if isinstance(urls, str) else urls)
+
     return SafeWebBaseLoader(
     return SafeWebBaseLoader(
-        urls,
+        safe_urls,
         verify_ssl=verify_ssl,
         verify_ssl=verify_ssl,
         requests_per_second=requests_per_second,
         requests_per_second=requests_per_second,
         continue_on_failure=True,
         continue_on_failure=True,

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

@@ -164,7 +164,7 @@ async def update_password(
 ############################
 ############################
 # LDAP Authentication
 # LDAP Authentication
 ############################
 ############################
-@router.post("/ldap", response_model=SigninResponse)
+@router.post("/ldap", response_model=SessionUserResponse)
 async def ldap_auth(request: Request, response: Response, form_data: LdapForm):
 async def ldap_auth(request: Request, response: Response, form_data: LdapForm):
     ENABLE_LDAP = request.app.state.config.ENABLE_LDAP
     ENABLE_LDAP = request.app.state.config.ENABLE_LDAP
     LDAP_SERVER_LABEL = request.app.state.config.LDAP_SERVER_LABEL
     LDAP_SERVER_LABEL = request.app.state.config.LDAP_SERVER_LABEL
@@ -288,6 +288,10 @@ async def ldap_auth(request: Request, response: Response, form_data: LdapForm):
                     httponly=True,  # Ensures the cookie is not accessible via JavaScript
                     httponly=True,  # Ensures the cookie is not accessible via JavaScript
                 )
                 )
 
 
+                user_permissions = get_permissions(
+                    user.id, request.app.state.config.USER_PERMISSIONS
+                )
+
                 return {
                 return {
                     "token": token,
                     "token": token,
                     "token_type": "Bearer",
                     "token_type": "Bearer",
@@ -296,6 +300,7 @@ async def ldap_auth(request: Request, response: Response, form_data: LdapForm):
                     "name": user.name,
                     "name": user.name,
                     "role": user.role,
                     "role": user.role,
                     "profile_image_url": user.profile_image_url,
                     "profile_image_url": user.profile_image_url,
+                    "permissions": user_permissions,
                 }
                 }
             else:
             else:
                 raise HTTPException(400, detail=ERROR_MESSAGES.INVALID_CRED)
                 raise HTTPException(400, detail=ERROR_MESSAGES.INVALID_CRED)

+ 8 - 2
backend/open_webui/routers/chats.py

@@ -444,15 +444,21 @@ async def pin_chat_by_id(id: str, user=Depends(get_verified_user)):
 ############################
 ############################
 
 
 
 
+class CloneForm(BaseModel):
+    title: Optional[str] = None
+
+
 @router.post("/{id}/clone", response_model=Optional[ChatResponse])
 @router.post("/{id}/clone", response_model=Optional[ChatResponse])
-async def clone_chat_by_id(id: str, user=Depends(get_verified_user)):
+async def clone_chat_by_id(
+    form_data: CloneForm, id: str, user=Depends(get_verified_user)
+):
     chat = Chats.get_chat_by_id_and_user_id(id, user.id)
     chat = Chats.get_chat_by_id_and_user_id(id, user.id)
     if chat:
     if chat:
         updated_chat = {
         updated_chat = {
             **chat.chat,
             **chat.chat,
             "originalChatId": chat.id,
             "originalChatId": chat.id,
             "branchPointMessageId": chat.chat["history"]["currentId"],
             "branchPointMessageId": chat.chat["history"]["currentId"],
-            "title": f"Clone of {chat.title}",
+            "title": form_data.title if form_data.title else f"Clone of {chat.title}",
         }
         }
 
 
         chat = Chats.insert_new_chat(user.id, ChatForm(**{"chat": updated_chat}))
         chat = Chats.insert_new_chat(user.id, ChatForm(**{"chat": updated_chat}))

+ 7 - 10
backend/open_webui/routers/knowledge.py

@@ -264,7 +264,8 @@ def add_file_to_knowledge_by_id(
             detail=ERROR_MESSAGES.NOT_FOUND,
             detail=ERROR_MESSAGES.NOT_FOUND,
         )
         )
 
 
-    if (knowledge.user_id != user.id
+    if (
+        knowledge.user_id != user.id
         and not has_access(user.id, "write", knowledge.access_control)
         and not has_access(user.id, "write", knowledge.access_control)
         and user.role != "admin"
         and user.role != "admin"
     ):
     ):
@@ -349,7 +350,7 @@ def update_file_from_knowledge_by_id(
         knowledge.user_id != user.id
         knowledge.user_id != user.id
         and not has_access(user.id, "write", knowledge.access_control)
         and not has_access(user.id, "write", knowledge.access_control)
         and user.role != "admin"
         and user.role != "admin"
-        ):
+    ):
 
 
         raise HTTPException(
         raise HTTPException(
             status_code=status.HTTP_400_BAD_REQUEST,
             status_code=status.HTTP_400_BAD_REQUEST,
@@ -418,7 +419,7 @@ def remove_file_from_knowledge_by_id(
         knowledge.user_id != user.id
         knowledge.user_id != user.id
         and not has_access(user.id, "write", knowledge.access_control)
         and not has_access(user.id, "write", knowledge.access_control)
         and user.role != "admin"
         and user.role != "admin"
-        ):
+    ):
         raise HTTPException(
         raise HTTPException(
             status_code=status.HTTP_400_BAD_REQUEST,
             status_code=status.HTTP_400_BAD_REQUEST,
             detail=ERROR_MESSAGES.ACCESS_PROHIBITED,
             detail=ERROR_MESSAGES.ACCESS_PROHIBITED,
@@ -441,10 +442,6 @@ def remove_file_from_knowledge_by_id(
     if VECTOR_DB_CLIENT.has_collection(collection_name=file_collection):
     if VECTOR_DB_CLIENT.has_collection(collection_name=file_collection):
         VECTOR_DB_CLIENT.delete_collection(collection_name=file_collection)
         VECTOR_DB_CLIENT.delete_collection(collection_name=file_collection)
 
 
-    # Delete physical file
-    if file.path:
-        Storage.delete_file(file.path)
-
     # Delete file from database
     # Delete file from database
     Files.delete_file_by_id(form_data.file_id)
     Files.delete_file_by_id(form_data.file_id)
 
 
@@ -500,7 +497,7 @@ async def delete_knowledge_by_id(id: str, user=Depends(get_verified_user)):
         knowledge.user_id != user.id
         knowledge.user_id != user.id
         and not has_access(user.id, "write", knowledge.access_control)
         and not has_access(user.id, "write", knowledge.access_control)
         and user.role != "admin"
         and user.role != "admin"
-        ):
+    ):
         raise HTTPException(
         raise HTTPException(
             status_code=status.HTTP_400_BAD_REQUEST,
             status_code=status.HTTP_400_BAD_REQUEST,
             detail=ERROR_MESSAGES.ACCESS_PROHIBITED,
             detail=ERROR_MESSAGES.ACCESS_PROHIBITED,
@@ -563,7 +560,7 @@ async def reset_knowledge_by_id(id: str, user=Depends(get_verified_user)):
         knowledge.user_id != user.id
         knowledge.user_id != user.id
         and not has_access(user.id, "write", knowledge.access_control)
         and not has_access(user.id, "write", knowledge.access_control)
         and user.role != "admin"
         and user.role != "admin"
-        ):
+    ):
         raise HTTPException(
         raise HTTPException(
             status_code=status.HTTP_400_BAD_REQUEST,
             status_code=status.HTTP_400_BAD_REQUEST,
             detail=ERROR_MESSAGES.ACCESS_PROHIBITED,
             detail=ERROR_MESSAGES.ACCESS_PROHIBITED,
@@ -606,7 +603,7 @@ def add_files_to_knowledge_batch(
         knowledge.user_id != user.id
         knowledge.user_id != user.id
         and not has_access(user.id, "write", knowledge.access_control)
         and not has_access(user.id, "write", knowledge.access_control)
         and user.role != "admin"
         and user.role != "admin"
-        ):
+    ):
         raise HTTPException(
         raise HTTPException(
             status_code=status.HTTP_400_BAD_REQUEST,
             status_code=status.HTTP_400_BAD_REQUEST,
             detail=ERROR_MESSAGES.ACCESS_PROHIBITED,
             detail=ERROR_MESSAGES.ACCESS_PROHIBITED,

+ 4 - 4
backend/open_webui/routers/models.py

@@ -184,10 +184,10 @@ async def delete_model_by_id(id: str, user=Depends(get_verified_user)):
         )
         )
 
 
     if (
     if (
-            user.role == "admin"
-            or model.user_id == user.id
-            or has_access(user.id, "write", model.access_control)
-        ):
+        user.role != "admin"
+        and model.user_id != user.id
+        and not has_access(user.id, "write", model.access_control)
+    ):
         raise HTTPException(
         raise HTTPException(
             status_code=status.HTTP_401_UNAUTHORIZED,
             status_code=status.HTTP_401_UNAUTHORIZED,
             detail=ERROR_MESSAGES.UNAUTHORIZED,
             detail=ERROR_MESSAGES.UNAUTHORIZED,

+ 5 - 4
backend/open_webui/routers/ollama.py

@@ -395,7 +395,7 @@ async def get_ollama_tags(
             )
             )
 
 
     if user.role == "user" and not BYPASS_MODEL_ACCESS_CONTROL:
     if user.role == "user" and not BYPASS_MODEL_ACCESS_CONTROL:
-        models["models"] = get_filtered_models(models, user)
+        models["models"] = await get_filtered_models(models, user)
 
 
     return models
     return models
 
 
@@ -939,6 +939,7 @@ async def generate_completion(
 class ChatMessage(BaseModel):
 class ChatMessage(BaseModel):
     role: str
     role: str
     content: str
     content: str
+    tool_calls: Optional[list[dict]] = None
     images: Optional[list[str]] = None
     images: Optional[list[str]] = None
 
 
 
 
@@ -950,6 +951,7 @@ class GenerateChatCompletionForm(BaseModel):
     template: Optional[str] = None
     template: Optional[str] = None
     stream: Optional[bool] = True
     stream: Optional[bool] = True
     keep_alive: Optional[Union[int, str]] = None
     keep_alive: Optional[Union[int, str]] = None
+    tools: Optional[list[dict]] = None
 
 
 
 
 async def get_ollama_url(request: Request, model: str, url_idx: Optional[int] = None):
 async def get_ollama_url(request: Request, model: str, url_idx: Optional[int] = None):
@@ -977,6 +979,7 @@ async def generate_chat_completion(
     if BYPASS_MODEL_ACCESS_CONTROL:
     if BYPASS_MODEL_ACCESS_CONTROL:
         bypass_filter = True
         bypass_filter = True
 
 
+    metadata = form_data.pop("metadata", None)
     try:
     try:
         form_data = GenerateChatCompletionForm(**form_data)
         form_data = GenerateChatCompletionForm(**form_data)
     except Exception as e:
     except Exception as e:
@@ -987,8 +990,6 @@ async def generate_chat_completion(
         )
         )
 
 
     payload = {**form_data.model_dump(exclude_none=True)}
     payload = {**form_data.model_dump(exclude_none=True)}
-    if "metadata" in payload:
-        del payload["metadata"]
 
 
     model_id = payload["model"]
     model_id = payload["model"]
     model_info = Models.get_model_by_id(model_id)
     model_info = Models.get_model_by_id(model_id)
@@ -1006,7 +1007,7 @@ async def generate_chat_completion(
             payload["options"] = apply_model_params_to_body_ollama(
             payload["options"] = apply_model_params_to_body_ollama(
                 params, payload["options"]
                 params, payload["options"]
             )
             )
-            payload = apply_model_system_prompt_to_body(params, payload, user)
+            payload = apply_model_system_prompt_to_body(params, payload, metadata)
 
 
         # Check if user has access to the model
         # Check if user has access to the model
         if not bypass_filter and user.role == "user":
         if not bypass_filter and user.role == "user":

+ 4 - 4
backend/open_webui/routers/openai.py

@@ -489,7 +489,7 @@ async def get_models(
                 raise HTTPException(status_code=500, detail=error_detail)
                 raise HTTPException(status_code=500, detail=error_detail)
 
 
     if user.role == "user" and not BYPASS_MODEL_ACCESS_CONTROL:
     if user.role == "user" and not BYPASS_MODEL_ACCESS_CONTROL:
-        models["data"] = get_filtered_models(models, user)
+        models["data"] = await get_filtered_models(models, user)
 
 
     return models
     return models
 
 
@@ -551,9 +551,9 @@ async def generate_chat_completion(
         bypass_filter = True
         bypass_filter = True
 
 
     idx = 0
     idx = 0
+
     payload = {**form_data}
     payload = {**form_data}
-    if "metadata" in payload:
-        del payload["metadata"]
+    metadata = payload.pop("metadata", None)
 
 
     model_id = form_data.get("model")
     model_id = form_data.get("model")
     model_info = Models.get_model_by_id(model_id)
     model_info = Models.get_model_by_id(model_id)
@@ -566,7 +566,7 @@ async def generate_chat_completion(
 
 
         params = model_info.params.model_dump()
         params = model_info.params.model_dump()
         payload = apply_model_params_to_body_openai(params, payload)
         payload = apply_model_params_to_body_openai(params, payload)
-        payload = apply_model_system_prompt_to_body(params, payload, user)
+        payload = apply_model_system_prompt_to_body(params, payload, metadata)
 
 
         # Check if user has access to the model
         # Check if user has access to the model
         if not bypass_filter and user.role == "user":
         if not bypass_filter and user.role == "user":

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

@@ -55,6 +55,7 @@ from open_webui.retrieval.web.serply import search_serply
 from open_webui.retrieval.web.serpstack import search_serpstack
 from open_webui.retrieval.web.serpstack import search_serpstack
 from open_webui.retrieval.web.tavily import search_tavily
 from open_webui.retrieval.web.tavily import search_tavily
 from open_webui.retrieval.web.bing import search_bing
 from open_webui.retrieval.web.bing import search_bing
+from open_webui.retrieval.web.exa import search_exa
 
 
 
 
 from open_webui.retrieval.utils import (
 from open_webui.retrieval.utils import (
@@ -388,6 +389,7 @@ async def get_rag_config(request: Request, user=Depends(get_admin_user)):
                 "jina_api_key": request.app.state.config.JINA_API_KEY,
                 "jina_api_key": request.app.state.config.JINA_API_KEY,
                 "bing_search_v7_endpoint": request.app.state.config.BING_SEARCH_V7_ENDPOINT,
                 "bing_search_v7_endpoint": request.app.state.config.BING_SEARCH_V7_ENDPOINT,
                 "bing_search_v7_subscription_key": request.app.state.config.BING_SEARCH_V7_SUBSCRIPTION_KEY,
                 "bing_search_v7_subscription_key": request.app.state.config.BING_SEARCH_V7_SUBSCRIPTION_KEY,
+                "exa_api_key": request.app.state.config.EXA_API_KEY,
                 "result_count": request.app.state.config.RAG_WEB_SEARCH_RESULT_COUNT,
                 "result_count": request.app.state.config.RAG_WEB_SEARCH_RESULT_COUNT,
                 "concurrent_requests": request.app.state.config.RAG_WEB_SEARCH_CONCURRENT_REQUESTS,
                 "concurrent_requests": request.app.state.config.RAG_WEB_SEARCH_CONCURRENT_REQUESTS,
             },
             },
@@ -436,6 +438,7 @@ class WebSearchConfig(BaseModel):
     jina_api_key: Optional[str] = None
     jina_api_key: Optional[str] = None
     bing_search_v7_endpoint: Optional[str] = None
     bing_search_v7_endpoint: Optional[str] = None
     bing_search_v7_subscription_key: Optional[str] = None
     bing_search_v7_subscription_key: Optional[str] = None
+    exa_api_key: Optional[str] = None
     result_count: Optional[int] = None
     result_count: Optional[int] = None
     concurrent_requests: Optional[int] = None
     concurrent_requests: Optional[int] = None
 
 
@@ -542,6 +545,8 @@ async def update_rag_config(
             form_data.web.search.bing_search_v7_subscription_key
             form_data.web.search.bing_search_v7_subscription_key
         )
         )
 
 
+        request.app.state.config.EXA_API_KEY = form_data.web.search.exa_api_key
+
         request.app.state.config.RAG_WEB_SEARCH_RESULT_COUNT = (
         request.app.state.config.RAG_WEB_SEARCH_RESULT_COUNT = (
             form_data.web.search.result_count
             form_data.web.search.result_count
         )
         )
@@ -591,6 +596,7 @@ async def update_rag_config(
                 "jina_api_key": request.app.state.config.JINA_API_KEY,
                 "jina_api_key": request.app.state.config.JINA_API_KEY,
                 "bing_search_v7_endpoint": request.app.state.config.BING_SEARCH_V7_ENDPOINT,
                 "bing_search_v7_endpoint": request.app.state.config.BING_SEARCH_V7_ENDPOINT,
                 "bing_search_v7_subscription_key": request.app.state.config.BING_SEARCH_V7_SUBSCRIPTION_KEY,
                 "bing_search_v7_subscription_key": request.app.state.config.BING_SEARCH_V7_SUBSCRIPTION_KEY,
+                "exa_api_key": request.app.state.config.EXA_API_KEY,
                 "result_count": request.app.state.config.RAG_WEB_SEARCH_RESULT_COUNT,
                 "result_count": request.app.state.config.RAG_WEB_SEARCH_RESULT_COUNT,
                 "concurrent_requests": request.app.state.config.RAG_WEB_SEARCH_CONCURRENT_REQUESTS,
                 "concurrent_requests": request.app.state.config.RAG_WEB_SEARCH_CONCURRENT_REQUESTS,
             },
             },
@@ -1099,6 +1105,7 @@ def search_web(request: Request, engine: str, query: str) -> list[SearchResult]:
     - SERPER_API_KEY
     - SERPER_API_KEY
     - SERPLY_API_KEY
     - SERPLY_API_KEY
     - TAVILY_API_KEY
     - TAVILY_API_KEY
+    - EXA_API_KEY
     - SEARCHAPI_API_KEY + SEARCHAPI_ENGINE (by default `google`)
     - SEARCHAPI_API_KEY + SEARCHAPI_ENGINE (by default `google`)
     Args:
     Args:
         query (str): The query to search for
         query (str): The query to search for
@@ -1233,6 +1240,13 @@ def search_web(request: Request, engine: str, query: str) -> list[SearchResult]:
             request.app.state.config.RAG_WEB_SEARCH_RESULT_COUNT,
             request.app.state.config.RAG_WEB_SEARCH_RESULT_COUNT,
             request.app.state.config.RAG_WEB_SEARCH_DOMAIN_FILTER_LIST,
             request.app.state.config.RAG_WEB_SEARCH_DOMAIN_FILTER_LIST,
         )
         )
+    elif engine == "exa":
+        return search_exa(
+            request.app.state.config.EXA_API_KEY,
+            query,
+            request.app.state.config.RAG_WEB_SEARCH_RESULT_COUNT,
+            request.app.state.config.RAG_WEB_SEARCH_DOMAIN_FILTER_LIST,
+        )
     else:
     else:
         raise Exception("No search engine API key found in environment variables")
         raise Exception("No search engine API key found in environment variables")
 
 

+ 19 - 3
backend/open_webui/routers/tasks.py

@@ -4,6 +4,7 @@ from fastapi.responses import JSONResponse, RedirectResponse
 from pydantic import BaseModel
 from pydantic import BaseModel
 from typing import Optional
 from typing import Optional
 import logging
 import logging
+import re
 
 
 from open_webui.utils.chat import generate_chat_completion
 from open_webui.utils.chat import generate_chat_completion
 from open_webui.utils.task import (
 from open_webui.utils.task import (
@@ -89,6 +90,10 @@ async def update_task_config(
         form_data.TITLE_GENERATION_PROMPT_TEMPLATE
         form_data.TITLE_GENERATION_PROMPT_TEMPLATE
     )
     )
 
 
+    request.app.state.config.IMAGE_PROMPT_GENERATION_PROMPT_TEMPLATE = (
+        form_data.IMAGE_PROMPT_GENERATION_PROMPT_TEMPLATE
+    )
+
     request.app.state.config.ENABLE_AUTOCOMPLETE_GENERATION = (
     request.app.state.config.ENABLE_AUTOCOMPLETE_GENERATION = (
         form_data.ENABLE_AUTOCOMPLETE_GENERATION
         form_data.ENABLE_AUTOCOMPLETE_GENERATION
     )
     )
@@ -161,9 +166,20 @@ async def generate_title(
     else:
     else:
         template = DEFAULT_TITLE_GENERATION_PROMPT_TEMPLATE
         template = DEFAULT_TITLE_GENERATION_PROMPT_TEMPLATE
 
 
+    messages = form_data["messages"]
+
+    # Remove reasoning details from the messages
+    for message in messages:
+        message["content"] = re.sub(
+            r"<details\s+type=\"reasoning\"[^>]*>.*?<\/details>",
+            "",
+            message["content"],
+            flags=re.S,
+        ).strip()
+
     content = title_generation_template(
     content = title_generation_template(
         template,
         template,
-        form_data["messages"],
+        messages,
         {
         {
             "name": user.name,
             "name": user.name,
             "location": user.info.get("location") if user.info else None,
             "location": user.info.get("location") if user.info else None,
@@ -175,10 +191,10 @@ async def generate_title(
         "messages": [{"role": "user", "content": content}],
         "messages": [{"role": "user", "content": content}],
         "stream": False,
         "stream": False,
         **(
         **(
-            {"max_tokens": 50}
+            {"max_tokens": 1000}
             if models[task_model_id]["owned_by"] == "ollama"
             if models[task_model_id]["owned_by"] == "ollama"
             else {
             else {
-                "max_completion_tokens": 50,
+                "max_completion_tokens": 1000,
             }
             }
         ),
         ),
         "metadata": {
         "metadata": {

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

@@ -79,6 +79,7 @@ class ChatPermissions(BaseModel):
 class FeaturesPermissions(BaseModel):
 class FeaturesPermissions(BaseModel):
     web_search: bool = True
     web_search: bool = True
     image_generation: bool = True
     image_generation: bool = True
+    code_interpreter: bool = True
 
 
 
 
 class UserPermissions(BaseModel):
 class UserPermissions(BaseModel):

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

@@ -325,7 +325,7 @@ def get_event_emitter(request_info):
 
 
 
 
 def get_event_call(request_info):
 def get_event_call(request_info):
-    async def __event_call__(event_data):
+    async def __event_caller__(event_data):
         response = await sio.call(
         response = await sio.call(
             "chat-events",
             "chat-events",
             {
             {
@@ -337,7 +337,10 @@ def get_event_call(request_info):
         )
         )
         return response
         return response
 
 
-    return __event_call__
+    return __event_caller__
+
+
+get_event_caller = get_event_call
 
 
 
 
 def get_user_id_from_session_pool(sid):
 def get_user_id_from_session_pool(sid):

文件差異過大導致無法顯示
+ 686 - 202
backend/open_webui/utils/middleware.py


+ 66 - 6
backend/open_webui/utils/misc.py

@@ -7,6 +7,18 @@ from pathlib import Path
 from typing import Callable, Optional
 from typing import Callable, Optional
 
 
 
 
+import collections.abc
+
+
+def deep_update(d, u):
+    for k, v in u.items():
+        if isinstance(v, collections.abc.Mapping):
+            d[k] = deep_update(d.get(k, {}), v)
+        else:
+            d[k] = v
+    return d
+
+
 def get_message_list(messages, message_id):
 def get_message_list(messages, message_id):
     """
     """
     Reconstructs a list of messages in order up to the specified message_id.
     Reconstructs a list of messages in order up to the specified message_id.
@@ -20,7 +32,7 @@ def get_message_list(messages, message_id):
     current_message = messages.get(message_id)
     current_message = messages.get(message_id)
 
 
     if not current_message:
     if not current_message:
-        return f"Message ID {message_id} not found in the history."
+        return None
 
 
     # Reconstruct the chain by following the parentId links
     # Reconstruct the chain by following the parentId links
     message_list = []
     message_list = []
@@ -131,6 +143,44 @@ def add_or_update_system_message(content: str, messages: list[dict]):
     return messages
     return messages
 
 
 
 
+def add_or_update_user_message(content: str, messages: list[dict]):
+    """
+    Adds a new user message at the end of the messages list
+    or updates the existing user message at the end.
+
+    :param msg: The message to be added or appended.
+    :param messages: The list of message dictionaries.
+    :return: The updated list of message dictionaries.
+    """
+
+    if messages and messages[-1].get("role") == "user":
+        messages[-1]["content"] = f"{messages[-1]['content']}\n{content}"
+    else:
+        # Insert at the end
+        messages.append({"role": "user", "content": content})
+
+    return messages
+
+
+def append_or_update_assistant_message(content: str, messages: list[dict]):
+    """
+    Adds a new assistant message at the end of the messages list
+    or updates the existing assistant message at the end.
+
+    :param msg: The message to be added or appended.
+    :param messages: The list of message dictionaries.
+    :return: The updated list of message dictionaries.
+    """
+
+    if messages and messages[-1].get("role") == "assistant":
+        messages[-1]["content"] = f"{messages[-1]['content']}\n{content}"
+    else:
+        # Insert at the end
+        messages.append({"role": "assistant", "content": content})
+
+    return messages
+
+
 def openai_chat_message_template(model: str):
 def openai_chat_message_template(model: str):
     return {
     return {
         "id": f"{model}-{str(uuid.uuid4())}",
         "id": f"{model}-{str(uuid.uuid4())}",
@@ -141,15 +191,25 @@ def openai_chat_message_template(model: str):
 
 
 
 
 def openai_chat_chunk_message_template(
 def openai_chat_chunk_message_template(
-    model: str, message: Optional[str] = None, usage: Optional[dict] = None
+    model: str,
+    content: Optional[str] = None,
+    tool_calls: Optional[list[dict]] = None,
+    usage: Optional[dict] = None,
 ) -> dict:
 ) -> dict:
     template = openai_chat_message_template(model)
     template = openai_chat_message_template(model)
     template["object"] = "chat.completion.chunk"
     template["object"] = "chat.completion.chunk"
-    if message:
-        template["choices"][0]["delta"] = {"content": message}
-    else:
+
+    template["choices"][0]["index"] = 0
+    template["choices"][0]["delta"] = {}
+
+    if content:
+        template["choices"][0]["delta"]["content"] = content
+
+    if tool_calls:
+        template["choices"][0]["delta"]["tool_calls"] = tool_calls
+
+    if not content and not tool_calls:
         template["choices"][0]["finish_reason"] = "stop"
         template["choices"][0]["finish_reason"] = "stop"
-        template["choices"][0]["delta"] = {}
 
 
     if usage:
     if usage:
         template["usage"] = usage
         template["usage"] = usage

+ 9 - 3
backend/open_webui/utils/oauth.py

@@ -82,7 +82,8 @@ class OAuthManager:
             oauth_allowed_roles = auth_manager_config.OAUTH_ALLOWED_ROLES
             oauth_allowed_roles = auth_manager_config.OAUTH_ALLOWED_ROLES
             oauth_admin_roles = auth_manager_config.OAUTH_ADMIN_ROLES
             oauth_admin_roles = auth_manager_config.OAUTH_ADMIN_ROLES
             oauth_roles = None
             oauth_roles = None
-            role = "pending"  # Default/fallback role if no matching roles are found
+            # Default/fallback role if no matching roles are found
+            role = auth_manager_config.DEFAULT_USER_ROLE
 
 
             # Next block extracts the roles from the user data, accepting nested claims of any depth
             # Next block extracts the roles from the user data, accepting nested claims of any depth
             if oauth_claim and oauth_allowed_roles and oauth_admin_roles:
             if oauth_claim and oauth_allowed_roles and oauth_admin_roles:
@@ -273,11 +274,16 @@ class OAuthManager:
                         log.error(
                         log.error(
                             f"Error downloading profile image '{picture_url}': {e}"
                             f"Error downloading profile image '{picture_url}': {e}"
                         )
                         )
-                        picture_url = ""
+                        picture_url = "/user.png"
                 if not picture_url:
                 if not picture_url:
                     picture_url = "/user.png"
                     picture_url = "/user.png"
+
                 username_claim = auth_manager_config.OAUTH_USERNAME_CLAIM
                 username_claim = auth_manager_config.OAUTH_USERNAME_CLAIM
 
 
+                name = user_data.get(username_claim)
+                if not isinstance(user, str):
+                    name = email
+
                 role = self.get_user_role(None, user_data)
                 role = self.get_user_role(None, user_data)
 
 
                 user = Auths.insert_new_auth(
                 user = Auths.insert_new_auth(
@@ -285,7 +291,7 @@ class OAuthManager:
                     password=get_password_hash(
                     password=get_password_hash(
                         str(uuid.uuid4())
                         str(uuid.uuid4())
                     ),  # Random password, not used
                     ),  # Random password, not used
-                    name=user_data.get(username_claim, "User"),
+                    name=name,
                     profile_image_url=picture_url,
                     profile_image_url=picture_url,
                     role=role,
                     role=role,
                     oauth_sub=provider_sub,
                     oauth_sub=provider_sub,

+ 15 - 10
backend/open_webui/utils/payload.py

@@ -1,4 +1,4 @@
-from open_webui.utils.task import prompt_template
+from open_webui.utils.task import prompt_variables_template
 from open_webui.utils.misc import (
 from open_webui.utils.misc import (
     add_or_update_system_message,
     add_or_update_system_message,
 )
 )
@@ -7,19 +7,18 @@ from typing import Callable, Optional
 
 
 
 
 # inplace function: form_data is modified
 # inplace function: form_data is modified
-def apply_model_system_prompt_to_body(params: dict, form_data: dict, user) -> dict:
+def apply_model_system_prompt_to_body(
+    params: dict, form_data: dict, metadata: Optional[dict] = None
+) -> dict:
     system = params.get("system", None)
     system = params.get("system", None)
     if not system:
     if not system:
         return form_data
         return form_data
 
 
-    if user:
-        template_params = {
-            "user_name": user.name,
-            "user_location": user.info.get("location") if user.info else None,
-        }
-    else:
-        template_params = {}
-    system = prompt_template(system, **template_params)
+    if metadata:
+        print("apply_model_system_prompt_to_body: metadata", metadata)
+        variables = metadata.get("variables", {})
+        system = prompt_variables_template(system, variables)
+
     form_data["messages"] = add_or_update_system_message(
     form_data["messages"] = add_or_update_system_message(
         system, form_data.get("messages", [])
         system, form_data.get("messages", [])
     )
     )
@@ -155,6 +154,9 @@ def convert_payload_openai_to_ollama(openai_payload: dict) -> dict:
     )
     )
     ollama_payload["stream"] = openai_payload.get("stream", False)
     ollama_payload["stream"] = openai_payload.get("stream", False)
 
 
+    if "tools" in openai_payload:
+        ollama_payload["tools"] = openai_payload["tools"]
+
     if "format" in openai_payload:
     if "format" in openai_payload:
         ollama_payload["format"] = openai_payload["format"]
         ollama_payload["format"] = openai_payload["format"]
 
 
@@ -188,4 +190,7 @@ def convert_payload_openai_to_ollama(openai_payload: dict) -> dict:
     if ollama_options:
     if ollama_options:
         ollama_payload["options"] = ollama_options
         ollama_payload["options"] = ollama_options
 
 
+    if "metadata" in openai_payload:
+        ollama_payload["metadata"] = openai_payload["metadata"]
+
     return ollama_payload
     return ollama_payload

+ 9 - 4
backend/open_webui/utils/plugin.py

@@ -167,9 +167,14 @@ def load_function_module_by_id(function_id, content=None):
 
 
 def install_frontmatter_requirements(requirements):
 def install_frontmatter_requirements(requirements):
     if requirements:
     if requirements:
-        req_list = [req.strip() for req in requirements.split(",")]
-        for req in req_list:
-            log.info(f"Installing requirement: {req}")
-            subprocess.check_call([sys.executable, "-m", "pip", "install", req])
+        try:
+            req_list = [req.strip() for req in requirements.split(",")]
+            for req in req_list:
+                log.info(f"Installing requirement: {req}")
+                subprocess.check_call([sys.executable, "-m", "pip", "install", req])
+        except Exception as e:
+            log.error(f"Error installing package: {req}")
+            raise e
+
     else:
     else:
         log.info("No requirements found in frontmatter.")
         log.info("No requirements found in frontmatter.")

+ 61 - 2
backend/open_webui/utils/response.py

@@ -1,4 +1,5 @@
 import json
 import json
+from uuid import uuid4
 from open_webui.utils.misc import (
 from open_webui.utils.misc import (
     openai_chat_chunk_message_template,
     openai_chat_chunk_message_template,
     openai_chat_completion_message_template,
     openai_chat_completion_message_template,
@@ -9,7 +10,48 @@ def convert_response_ollama_to_openai(ollama_response: dict) -> dict:
     model = ollama_response.get("model", "ollama")
     model = ollama_response.get("model", "ollama")
     message_content = ollama_response.get("message", {}).get("content", "")
     message_content = ollama_response.get("message", {}).get("content", "")
 
 
-    response = openai_chat_completion_message_template(model, message_content)
+    data = ollama_response
+    usage = {
+        "response_token/s": (
+            round(
+                (
+                    (
+                        data.get("eval_count", 0)
+                        / ((data.get("eval_duration", 0) / 10_000_000))
+                    )
+                    * 100
+                ),
+                2,
+            )
+            if data.get("eval_duration", 0) > 0
+            else "N/A"
+        ),
+        "prompt_token/s": (
+            round(
+                (
+                    (
+                        data.get("prompt_eval_count", 0)
+                        / ((data.get("prompt_eval_duration", 0) / 10_000_000))
+                    )
+                    * 100
+                ),
+                2,
+            )
+            if data.get("prompt_eval_duration", 0) > 0
+            else "N/A"
+        ),
+        "total_duration": data.get("total_duration", 0),
+        "load_duration": data.get("load_duration", 0),
+        "prompt_eval_count": data.get("prompt_eval_count", 0),
+        "prompt_eval_duration": data.get("prompt_eval_duration", 0),
+        "eval_count": data.get("eval_count", 0),
+        "eval_duration": data.get("eval_duration", 0),
+        "approximate_total": (lambda s: f"{s // 3600}h{(s % 3600) // 60}m{s % 60}s")(
+            (data.get("total_duration", 0) or 0) // 1_000_000_000
+        ),
+    }
+
+    response = openai_chat_completion_message_template(model, message_content, usage)
     return response
     return response
 
 
 
 
@@ -19,6 +61,23 @@ async def convert_streaming_response_ollama_to_openai(ollama_streaming_response)
 
 
         model = data.get("model", "ollama")
         model = data.get("model", "ollama")
         message_content = data.get("message", {}).get("content", "")
         message_content = data.get("message", {}).get("content", "")
+        tool_calls = data.get("message", {}).get("tool_calls", None)
+        openai_tool_calls = None
+
+        if tool_calls:
+            openai_tool_calls = []
+            for tool_call in tool_calls:
+                openai_tool_call = {
+                    "index": tool_call.get("index", 0),
+                    "id": tool_call.get("id", f"call_{str(uuid4())}"),
+                    "type": "function",
+                    "function": {
+                        "name": tool_call.get("function", {}).get("name", ""),
+                        "arguments": f"{tool_call.get('function', {}).get('arguments', {})}",
+                    },
+                }
+                openai_tool_calls.append(openai_tool_call)
+
         done = data.get("done", False)
         done = data.get("done", False)
 
 
         usage = None
         usage = None
@@ -64,7 +123,7 @@ async def convert_streaming_response_ollama_to_openai(ollama_streaming_response)
             }
             }
 
 
         data = openai_chat_chunk_message_template(
         data = openai_chat_chunk_message_template(
-            model, message_content if not done else None, usage
+            model, message_content if not done else None, openai_tool_calls, usage
         )
         )
 
 
         line = f"data: {json.dumps(data)}\n\n"
         line = f"data: {json.dumps(data)}\n\n"

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

@@ -32,6 +32,12 @@ def get_task_model_id(
     return task_model_id
     return task_model_id
 
 
 
 
+def prompt_variables_template(template: str, variables: dict[str, str]) -> str:
+    for variable, value in variables.items():
+        template = template.replace(variable, value)
+    return template
+
+
 def prompt_template(
 def prompt_template(
     template: str, user_name: Optional[str] = None, user_location: Optional[str] = None
     template: str, user_name: Optional[str] = None, user_location: Optional[str] = None
 ) -> str:
 ) -> str:

+ 13 - 0
backend/open_webui/utils/tools.py

@@ -61,6 +61,12 @@ def get_tools(
             )
             )
 
 
         for spec in tools.specs:
         for spec in tools.specs:
+            # TODO: Fix hack for OpenAI API
+            # Some times breaks OpenAI but others don't. Leaving the comment
+            for val in spec.get("parameters", {}).get("properties", {}).values():
+                if val["type"] == "str":
+                    val["type"] = "string"
+
             # Remove internal parameters
             # Remove internal parameters
             spec["parameters"]["properties"] = {
             spec["parameters"]["properties"] = {
                 key: val
                 key: val
@@ -73,6 +79,13 @@ def get_tools(
             # convert to function that takes only model params and inserts custom params
             # convert to function that takes only model params and inserts custom params
             original_func = getattr(module, function_name)
             original_func = getattr(module, function_name)
             callable = apply_extra_params_to_tool_function(original_func, extra_params)
             callable = apply_extra_params_to_tool_function(original_func, extra_params)
+
+            if callable.__doc__ and callable.__doc__.strip() != "":
+                s = re.split(":(param|return)", callable.__doc__, 1)
+                spec["description"] = s[0]
+            else:
+                spec["description"] = function_name
+
             # TODO: This needs to be a pydantic model
             # TODO: This needs to be a pydantic model
             tool_dict = {
             tool_dict = {
                 "toolkit_id": tool_id,
                 "toolkit_id": tool_id,

+ 8 - 8
backend/requirements.txt

@@ -1,4 +1,4 @@
-fastapi==0.111.0
+fastapi==0.115.7
 uvicorn[standard]==0.30.6
 uvicorn[standard]==0.30.6
 pydantic==2.9.2
 pydantic==2.9.2
 python-multipart==0.0.18
 python-multipart==0.0.18
@@ -11,7 +11,7 @@ python-jose==3.3.0
 passlib[bcrypt]==1.7.4
 passlib[bcrypt]==1.7.4
 
 
 requests==2.32.3
 requests==2.32.3
-aiohttp==3.11.8
+aiohttp==3.11.11
 async-timeout
 async-timeout
 aiocache
 aiocache
 aiofiles
 aiofiles
@@ -57,10 +57,10 @@ einops==0.8.0
 ftfy==6.2.3
 ftfy==6.2.3
 pypdf==4.3.1
 pypdf==4.3.1
 fpdf2==2.8.2
 fpdf2==2.8.2
-pymdown-extensions==10.11.2
+pymdown-extensions==10.14.2
 docx2txt==0.8
 docx2txt==0.8
 python-pptx==1.0.0
 python-pptx==1.0.0
-unstructured==0.15.9
+unstructured==0.16.11
 nltk==3.9.1
 nltk==3.9.1
 Markdown==3.7
 Markdown==3.7
 pypandoc==1.13
 pypandoc==1.13
@@ -71,16 +71,16 @@ xlrd==2.0.1
 validators==0.34.0
 validators==0.34.0
 psutil
 psutil
 sentencepiece
 sentencepiece
-soundfile==0.12.1
+soundfile==0.13.1
 
 
-opencv-python-headless==4.10.0.84
+opencv-python-headless==4.11.0.86
 rapidocr-onnxruntime==1.3.24
 rapidocr-onnxruntime==1.3.24
 rank-bm25==0.2.2
 rank-bm25==0.2.2
 
 
 faster-whisper==1.0.3
 faster-whisper==1.0.3
 
 
 PyJWT[crypto]==2.10.1
 PyJWT[crypto]==2.10.1
-authlib==1.3.2
+authlib==1.4.1
 
 
 black==24.8.0
 black==24.8.0
 langfuse==2.44.0
 langfuse==2.44.0
@@ -89,7 +89,7 @@ pytube==15.0.0
 
 
 extract_msg
 extract_msg
 pydub
 pydub
-duckduckgo-search~=7.2.1
+duckduckgo-search~=7.3.0
 
 
 ## Google Drive
 ## Google Drive
 google-api-python-client
 google-api-python-client

+ 5 - 4
package-lock.json

@@ -56,7 +56,7 @@
 				"prosemirror-schema-list": "^1.4.1",
 				"prosemirror-schema-list": "^1.4.1",
 				"prosemirror-state": "^1.4.3",
 				"prosemirror-state": "^1.4.3",
 				"prosemirror-view": "^1.34.3",
 				"prosemirror-view": "^1.34.3",
-				"pyodide": "^0.26.1",
+				"pyodide": "^0.27.2",
 				"socket.io-client": "^4.2.0",
 				"socket.io-client": "^4.2.0",
 				"sortablejs": "^1.15.2",
 				"sortablejs": "^1.15.2",
 				"svelte-sonner": "^0.3.19",
 				"svelte-sonner": "^0.3.19",
@@ -9366,9 +9366,10 @@
 			}
 			}
 		},
 		},
 		"node_modules/pyodide": {
 		"node_modules/pyodide": {
-			"version": "0.26.1",
-			"resolved": "https://registry.npmjs.org/pyodide/-/pyodide-0.26.1.tgz",
-			"integrity": "sha512-P+Gm88nwZqY7uBgjbQH8CqqU6Ei/rDn7pS1t02sNZsbyLJMyE2OVXjgNuqVT3KqYWnyGREUN0DbBUCJqk8R0ew==",
+			"version": "0.27.2",
+			"resolved": "https://registry.npmjs.org/pyodide/-/pyodide-0.27.2.tgz",
+			"integrity": "sha512-sfA2kiUuQVRpWI4BYnU3sX5PaTTt/xrcVEmRzRcId8DzZXGGtPgCBC0gCqjUTUYSa8ofPaSjXmzESc86yvvCHg==",
+			"license": "Apache-2.0",
 			"dependencies": {
 			"dependencies": {
 				"ws": "^8.5.0"
 				"ws": "^8.5.0"
 			},
 			},

+ 1 - 1
package.json

@@ -98,7 +98,7 @@
 		"prosemirror-schema-list": "^1.4.1",
 		"prosemirror-schema-list": "^1.4.1",
 		"prosemirror-state": "^1.4.3",
 		"prosemirror-state": "^1.4.3",
 		"prosemirror-view": "^1.34.3",
 		"prosemirror-view": "^1.34.3",
-		"pyodide": "^0.26.1",
+		"pyodide": "^0.27.2",
 		"socket.io-client": "^4.2.0",
 		"socket.io-client": "^4.2.0",
 		"sortablejs": "^1.15.2",
 		"sortablejs": "^1.15.2",
 		"svelte-sonner": "^0.3.19",
 		"svelte-sonner": "^0.3.19",

+ 8 - 8
pyproject.toml

@@ -6,7 +6,7 @@ authors = [
 ]
 ]
 license = { file = "LICENSE" }
 license = { file = "LICENSE" }
 dependencies = [
 dependencies = [
-    "fastapi==0.111.0",
+    "fastapi==0.115.7",
     "uvicorn[standard]==0.30.6",
     "uvicorn[standard]==0.30.6",
     "pydantic==2.9.2",
     "pydantic==2.9.2",
     "python-multipart==0.0.18",
     "python-multipart==0.0.18",
@@ -19,7 +19,7 @@ dependencies = [
     "passlib[bcrypt]==1.7.4",
     "passlib[bcrypt]==1.7.4",
 
 
     "requests==2.32.3",
     "requests==2.32.3",
-    "aiohttp==3.11.8",
+    "aiohttp==3.11.11",
     "async-timeout",
     "async-timeout",
     "aiocache",
     "aiocache",
     "aiofiles",
     "aiofiles",
@@ -62,10 +62,10 @@ dependencies = [
     "ftfy==6.2.3",
     "ftfy==6.2.3",
     "pypdf==4.3.1",
     "pypdf==4.3.1",
     "fpdf2==2.8.2",
     "fpdf2==2.8.2",
-    "pymdown-extensions==10.11.2",
+    "pymdown-extensions==10.14.2",
     "docx2txt==0.8",
     "docx2txt==0.8",
     "python-pptx==1.0.0",
     "python-pptx==1.0.0",
-    "unstructured==0.15.9",
+    "unstructured==0.16.11",
     "nltk==3.9.1",
     "nltk==3.9.1",
     "Markdown==3.7",
     "Markdown==3.7",
     "pypandoc==1.13",
     "pypandoc==1.13",
@@ -76,16 +76,16 @@ dependencies = [
     "validators==0.34.0",
     "validators==0.34.0",
     "psutil",
     "psutil",
     "sentencepiece",
     "sentencepiece",
-    "soundfile==0.12.1",
+    "soundfile==0.13.1",
 
 
-    "opencv-python-headless==4.10.0.84",
+    "opencv-python-headless==4.11.0.86",
     "rapidocr-onnxruntime==1.3.24",
     "rapidocr-onnxruntime==1.3.24",
     "rank-bm25==0.2.2",
     "rank-bm25==0.2.2",
 
 
     "faster-whisper==1.0.3",
     "faster-whisper==1.0.3",
 
 
     "PyJWT[crypto]==2.10.1",
     "PyJWT[crypto]==2.10.1",
-    "authlib==1.3.2",
+    "authlib==1.4.1",
 
 
     "black==24.8.0",
     "black==24.8.0",
     "langfuse==2.44.0",
     "langfuse==2.44.0",
@@ -94,7 +94,7 @@ dependencies = [
 
 
     "extract_msg",
     "extract_msg",
     "pydub",
     "pydub",
-    "duckduckgo-search~=7.2.1",
+    "duckduckgo-search~=7.3.0",
 
 
     "google-api-python-client",
     "google-api-python-client",
     "google-auth-httplib2",
     "google-auth-httplib2",

+ 4 - 1
scripts/prepare-pyodide.js

@@ -9,7 +9,10 @@ const packages = [
 	'scikit-learn',
 	'scikit-learn',
 	'scipy',
 	'scipy',
 	'regex',
 	'regex',
-	'seaborn'
+	'sympy',
+	'tiktoken',
+	'seaborn',
+	'pytz'
 ];
 ];
 
 
 import { loadPyodide } from 'pyodide';
 import { loadPyodide } from 'pyodide';

+ 5 - 2
src/lib/apis/chats/index.ts

@@ -580,7 +580,7 @@ export const toggleChatPinnedStatusById = async (token: string, id: string) => {
 	return res;
 	return res;
 };
 };
 
 
-export const cloneChatById = async (token: string, id: string) => {
+export const cloneChatById = async (token: string, id: string, title?: string) => {
 	let error = null;
 	let error = null;
 
 
 	const res = await fetch(`${WEBUI_API_BASE_URL}/chats/${id}/clone`, {
 	const res = await fetch(`${WEBUI_API_BASE_URL}/chats/${id}/clone`, {
@@ -589,7 +589,10 @@ export const cloneChatById = async (token: string, id: string) => {
 			Accept: 'application/json',
 			Accept: 'application/json',
 			'Content-Type': 'application/json',
 			'Content-Type': 'application/json',
 			...(token && { authorization: `Bearer ${token}` })
 			...(token && { authorization: `Bearer ${token}` })
-		}
+		},
+		body: JSON.stringify({
+			...(title && { title: title })
+		})
 	})
 	})
 		.then(async (res) => {
 		.then(async (res) => {
 			if (!res.ok) throw await res.json();
 			if (!res.ok) throw await res.json();

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

@@ -880,13 +880,14 @@ export const getChangelog = async () => {
 	return res;
 	return res;
 };
 };
 
 
-export const getVersionUpdates = async () => {
+export const getVersionUpdates = async (token: string) => {
 	let error = null;
 	let error = null;
 
 
 	const res = await fetch(`${WEBUI_BASE_URL}/api/version/updates`, {
 	const res = await fetch(`${WEBUI_BASE_URL}/api/version/updates`, {
 		method: 'GET',
 		method: 'GET',
 		headers: {
 		headers: {
-			'Content-Type': 'application/json'
+			'Content-Type': 'application/json',
+			Authorization: `Bearer ${token}`
 		}
 		}
 	})
 	})
 		.then(async (res) => {
 		.then(async (res) => {

+ 1 - 1
src/lib/apis/models/index.ts

@@ -219,7 +219,7 @@ export const deleteModelById = async (token: string, id: string) => {
 			return json;
 			return json;
 		})
 		})
 		.catch((err) => {
 		.catch((err) => {
-			error = err;
+			error = err.detail;
 
 
 			console.log(err);
 			console.log(err);
 			return null;
 			return null;

+ 1 - 1
src/lib/apis/openai/index.ts

@@ -322,7 +322,7 @@ export const generateOpenAIChatCompletion = async (
 			return res.json();
 			return res.json();
 		})
 		})
 		.catch((err) => {
 		.catch((err) => {
-			error = `${err?.detail ?? 'Network Problem'}`;
+			error = `${err?.detail ?? err}`;
 			return null;
 			return null;
 		});
 		});
 
 

+ 2 - 2
src/lib/components/admin/Settings/Audio.svelte

@@ -51,7 +51,7 @@
 			models = [];
 			models = [];
 		} else {
 		} else {
 			const res = await _getModels(localStorage.token).catch((e) => {
 			const res = await _getModels(localStorage.token).catch((e) => {
-				toast.error(e);
+				toast.error(`${e}`);
 			});
 			});
 
 
 			if (res) {
 			if (res) {
@@ -74,7 +74,7 @@
 			}, 100);
 			}, 100);
 		} else {
 		} else {
 			const res = await _getVoices(localStorage.token).catch((e) => {
 			const res = await _getVoices(localStorage.token).catch((e) => {
-				toast.error(e);
+				toast.error(`${e}`);
 			});
 			});
 
 
 			if (res) {
 			if (res) {

+ 16 - 1
src/lib/components/admin/Settings/Interface.svelte

@@ -31,7 +31,8 @@
 		ENABLE_TAGS_GENERATION: true,
 		ENABLE_TAGS_GENERATION: true,
 		ENABLE_SEARCH_QUERY_GENERATION: true,
 		ENABLE_SEARCH_QUERY_GENERATION: true,
 		ENABLE_RETRIEVAL_QUERY_GENERATION: true,
 		ENABLE_RETRIEVAL_QUERY_GENERATION: true,
-		QUERY_GENERATION_PROMPT_TEMPLATE: ''
+		QUERY_GENERATION_PROMPT_TEMPLATE: '',
+		TOOLS_FUNCTION_CALLING_PROMPT_TEMPLATE: ''
 	};
 	};
 
 
 	let promptSuggestions = [];
 	let promptSuggestions = [];
@@ -251,6 +252,20 @@
 				</div>
 				</div>
 			</div>
 			</div>
 
 
+			<div class="mt-3">
+				<div class=" mb-2.5 text-xs font-medium">{$i18n.t('Tools Function Calling Prompt')}</div>
+
+				<Tooltip
+					content={$i18n.t('Leave empty to use the default prompt, or enter a custom prompt')}
+					placement="top-start"
+				>
+					<Textarea
+						bind:value={taskConfig.TOOLS_FUNCTION_CALLING_PROMPT_TEMPLATE}
+						placeholder={$i18n.t('Leave empty to use the default prompt, or enter a custom prompt')}
+					/>
+				</Tooltip>
+			</div>
+
 			<hr class=" border-gray-50 dark:border-gray-850 my-3" />
 			<hr class=" border-gray-50 dark:border-gray-850 my-3" />
 
 
 			<div class=" space-y-3 {banners.length > 0 ? ' mb-3' : ''}">
 			<div class=" space-y-3 {banners.length > 0 ? ' mb-3' : ''}">

+ 13 - 1
src/lib/components/admin/Settings/WebSearch.svelte

@@ -25,7 +25,8 @@
 		'duckduckgo',
 		'duckduckgo',
 		'tavily',
 		'tavily',
 		'jina',
 		'jina',
-		'bing'
+		'bing',
+		'exa'
 	];
 	];
 
 
 	let youtubeLanguage = 'en';
 	let youtubeLanguage = 'en';
@@ -261,6 +262,17 @@
 									bind:value={webConfig.search.jina_api_key}
 									bind:value={webConfig.search.jina_api_key}
 								/>
 								/>
 							</div>
 							</div>
+						{:else if webConfig.search.engine === 'exa'}
+							<div>
+								<div class=" self-center text-xs font-medium mb-1">
+									{$i18n.t('Exa API Key')}
+								</div>
+
+								<SensitiveInput
+									placeholder={$i18n.t('Enter Exa API Key')}
+									bind:value={webConfig.search.exa_api_key}
+								/>
+							</div>
 						{:else if webConfig.search.engine === 'bing'}
 						{:else if webConfig.search.engine === 'bing'}
 							<div>
 							<div>
 								<div class=" self-center text-xs font-medium mb-1">
 								<div class=" self-center text-xs font-medium mb-1">

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

@@ -61,7 +61,8 @@
 		},
 		},
 		features: {
 		features: {
 			web_search: true,
 			web_search: true,
-			image_generation: true
+			image_generation: true,
+			code_interpreter: true
 		}
 		}
 	};
 	};
 
 

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

@@ -45,7 +45,8 @@
 		},
 		},
 		features: {
 		features: {
 			web_search: true,
 			web_search: true,
-			image_generation: true
+			image_generation: true,
+			code_interpreter: true
 		}
 		}
 	};
 	};
 	export let userIds = [];
 	export let userIds = [];

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

@@ -22,7 +22,8 @@
 		},
 		},
 		features: {
 		features: {
 			web_search: true,
 			web_search: true,
-			image_generation: true
+			image_generation: true,
+			code_interpreter: true
 		}
 		}
 	};
 	};
 
 
@@ -257,5 +258,13 @@
 
 
 			<Switch bind:state={permissions.features.image_generation} />
 			<Switch bind:state={permissions.features.image_generation} />
 		</div>
 		</div>
+
+		<div class="  flex w-full justify-between my-2 pr-2">
+			<div class=" self-center text-xs font-medium">
+				{$i18n.t('Code Interpreter')}
+			</div>
+
+			<Switch bind:state={permissions.features.code_interpreter} />
+		</div>
 	</div>
 	</div>
 </div>
 </div>

+ 2 - 2
src/lib/components/admin/Users/UserList.svelte

@@ -6,9 +6,9 @@
 
 
 	import dayjs from 'dayjs';
 	import dayjs from 'dayjs';
 	import relativeTime from 'dayjs/plugin/relativeTime';
 	import relativeTime from 'dayjs/plugin/relativeTime';
-    import localizedFormat from 'dayjs/plugin/localizedFormat';
+	import localizedFormat from 'dayjs/plugin/localizedFormat';
 	dayjs.extend(relativeTime);
 	dayjs.extend(relativeTime);
-    dayjs.extend(localizedFormat);
+	dayjs.extend(localizedFormat);
 
 
 	import { toast } from 'svelte-sonner';
 	import { toast } from 'svelte-sonner';
 
 

+ 2 - 2
src/lib/components/admin/Users/UserList/EditUserModal.svelte

@@ -7,11 +7,11 @@
 	import { updateUserById } from '$lib/apis/users';
 	import { updateUserById } from '$lib/apis/users';
 
 
 	import Modal from '$lib/components/common/Modal.svelte';
 	import Modal from '$lib/components/common/Modal.svelte';
-    import localizedFormat from 'dayjs/plugin/localizedFormat';
+	import localizedFormat from 'dayjs/plugin/localizedFormat';
 
 
 	const i18n = getContext('i18n');
 	const i18n = getContext('i18n');
 	const dispatch = createEventDispatcher();
 	const dispatch = createEventDispatcher();
-    dayjs.extend(localizedFormat);
+	dayjs.extend(localizedFormat);
 
 
 	export let show = false;
 	export let show = false;
 	export let selectedUser;
 	export let selectedUser;

+ 2 - 2
src/lib/components/admin/Users/UserList/UserChatsModal.svelte

@@ -2,10 +2,10 @@
 	import { toast } from 'svelte-sonner';
 	import { toast } from 'svelte-sonner';
 	import dayjs from 'dayjs';
 	import dayjs from 'dayjs';
 	import { getContext, createEventDispatcher } from 'svelte';
 	import { getContext, createEventDispatcher } from 'svelte';
-    import localizedFormat from 'dayjs/plugin/localizedFormat';
+	import localizedFormat from 'dayjs/plugin/localizedFormat';
 
 
 	const dispatch = createEventDispatcher();
 	const dispatch = createEventDispatcher();
-    dayjs.extend(localizedFormat);
+	dayjs.extend(localizedFormat);
 
 
 	import { getChatListByUserId, deleteChatById, getArchivedChatList } from '$lib/apis/chats';
 	import { getChatListByUserId, deleteChatById, getArchivedChatList } from '$lib/apis/chats';
 
 

+ 35 - 33
src/lib/components/channel/MessageInput.svelte

@@ -200,7 +200,7 @@
 				files = files.filter((item) => item?.itemId !== tempItemId);
 				files = files.filter((item) => item?.itemId !== tempItemId);
 			}
 			}
 		} catch (e) {
 		} catch (e) {
-			toast.error(e);
+			toast.error(`${e}`);
 			files = files.filter((item) => item?.itemId !== tempItemId);
 			files = files.filter((item) => item?.itemId !== tempItemId);
 		}
 		}
 	};
 	};
@@ -398,7 +398,7 @@
 						dir={$settings?.chatDirection ?? 'LTR'}
 						dir={$settings?.chatDirection ?? 'LTR'}
 					>
 					>
 						{#if files.length > 0}
 						{#if files.length > 0}
-							<div class="mx-1 mt-2.5 mb-1 flex flex-wrap gap-2">
+							<div class="mx-2 mt-2.5 -mb-1 flex flex-wrap gap-2">
 								{#each files as file, fileIdx}
 								{#each files as file, fileIdx}
 									{#if file.type === 'image'}
 									{#if file.type === 'image'}
 										<div class=" relative group">
 										<div class=" relative group">
@@ -411,7 +411,7 @@
 											</div>
 											</div>
 											<div class=" absolute -top-1 -right-1">
 											<div class=" absolute -top-1 -right-1">
 												<button
 												<button
-													class=" bg-gray-400 text-white border border-white rounded-full group-hover:visible invisible transition"
+													class=" bg-white text-black border border-white rounded-full group-hover:visible invisible transition"
 													type="button"
 													type="button"
 													on:click={() => {
 													on:click={() => {
 														files.splice(fileIdx, 1);
 														files.splice(fileIdx, 1);
@@ -453,35 +453,9 @@
 							</div>
 							</div>
 						{/if}
 						{/if}
 
 
-						<div class=" flex">
-							<div class="ml-1 self-end mb-1.5 flex space-x-1">
-								<InputMenu
-									{screenCaptureHandler}
-									uploadFilesHandler={() => {
-										filesInputElement.click();
-									}}
-								>
-									<button
-										class="bg-transparent hover:bg-white/80 text-gray-800 dark:text-white dark:hover:bg-gray-800 transition rounded-full p-2 outline-none focus:outline-none"
-										type="button"
-										aria-label="More"
-									>
-										<svg
-											xmlns="http://www.w3.org/2000/svg"
-											viewBox="0 0 20 20"
-											fill="currentColor"
-											class="size-5"
-										>
-											<path
-												d="M10.75 4.75a.75.75 0 0 0-1.5 0v4.5h-4.5a.75.75 0 0 0 0 1.5h4.5v4.5a.75.75 0 0 0 1.5 0v-4.5h4.5a.75.75 0 0 0 0-1.5h-4.5v-4.5Z"
-											/>
-										</svg>
-									</button>
-								</InputMenu>
-							</div>
-
+						<div class="px-2.5">
 							<div
 							<div
-								class="scrollbar-hidden text-left bg-transparent dark:text-gray-100 outline-none w-full py-2.5 px-1 rounded-xl resize-none h-fit max-h-80 overflow-auto"
+								class="scrollbar-hidden font-primary text-left bg-transparent dark:text-gray-100 outline-none w-full pt-3 px-1 rounded-xl resize-none h-fit max-h-80 overflow-auto"
 							>
 							>
 								<RichTextInput
 								<RichTextInput
 									bind:value={content}
 									bind:value={content}
@@ -528,8 +502,36 @@
 									}}
 									}}
 								/>
 								/>
 							</div>
 							</div>
+						</div>
+
+						<div class=" flex justify-between mb-2.5 mt-1.5 mx-0.5">
+							<div class="ml-1 self-end flex space-x-1">
+								<InputMenu
+									{screenCaptureHandler}
+									uploadFilesHandler={() => {
+										filesInputElement.click();
+									}}
+								>
+									<button
+										class="bg-transparent hover:bg-white/80 text-gray-800 dark:text-white dark:hover:bg-gray-800 transition rounded-full p-1.5 outline-none focus:outline-none"
+										type="button"
+										aria-label="More"
+									>
+										<svg
+											xmlns="http://www.w3.org/2000/svg"
+											viewBox="0 0 20 20"
+											fill="currentColor"
+											class="size-5"
+										>
+											<path
+												d="M10.75 4.75a.75.75 0 0 0-1.5 0v4.5h-4.5a.75.75 0 0 0 0 1.5h4.5v4.5a.75.75 0 0 0 1.5 0v-4.5h4.5a.75.75 0 0 0 0-1.5h-4.5v-4.5Z"
+											/>
+										</svg>
+									</button>
+								</InputMenu>
+							</div>
 
 
-							<div class="self-end mb-1.5 flex space-x-1 mr-1">
+							<div class="self-end flex space-x-1 mr-1">
 								{#if content === ''}
 								{#if content === ''}
 									<Tooltip content={$i18n.t('Record voice')}>
 									<Tooltip content={$i18n.t('Record voice')}>
 										<button
 										<button
@@ -591,7 +593,7 @@
 													xmlns="http://www.w3.org/2000/svg"
 													xmlns="http://www.w3.org/2000/svg"
 													viewBox="0 0 16 16"
 													viewBox="0 0 16 16"
 													fill="currentColor"
 													fill="currentColor"
-													class="size-6"
+													class="size-5"
 												>
 												>
 													<path
 													<path
 														fill-rule="evenodd"
 														fill-rule="evenodd"

+ 5 - 9
src/lib/components/channel/Messages/Message.svelte

@@ -3,12 +3,12 @@
 	import relativeTime from 'dayjs/plugin/relativeTime';
 	import relativeTime from 'dayjs/plugin/relativeTime';
 	import isToday from 'dayjs/plugin/isToday';
 	import isToday from 'dayjs/plugin/isToday';
 	import isYesterday from 'dayjs/plugin/isYesterday';
 	import isYesterday from 'dayjs/plugin/isYesterday';
-    import localizedFormat from 'dayjs/plugin/localizedFormat';
+	import localizedFormat from 'dayjs/plugin/localizedFormat';
 
 
 	dayjs.extend(relativeTime);
 	dayjs.extend(relativeTime);
 	dayjs.extend(isToday);
 	dayjs.extend(isToday);
 	dayjs.extend(isYesterday);
 	dayjs.extend(isYesterday);
-    dayjs.extend(localizedFormat);
+	dayjs.extend(localizedFormat);
 
 
 	import { getContext, onMount } from 'svelte';
 	import { getContext, onMount } from 'svelte';
 	const i18n = getContext<Writable<i18nType>>('i18n');
 	const i18n = getContext<Writable<i18nType>>('i18n');
@@ -155,10 +155,8 @@
 						<div
 						<div
 							class="mt-1.5 flex flex-shrink-0 items-center text-xs self-center invisible group-hover:visible text-gray-500 font-medium first-letter:capitalize"
 							class="mt-1.5 flex flex-shrink-0 items-center text-xs self-center invisible group-hover:visible text-gray-500 font-medium first-letter:capitalize"
 						>
 						>
-							<Tooltip
-								content={dayjs(message.created_at / 1000000).format('LLLL')}
-							>
-								{dayjs(message.created_at / 1000000).format('LT')}
+							<Tooltip content={dayjs(message.created_at / 1000000).format('LLLL')}>
+								{dayjs(message.created_at / 1000000).format('HH:mm')}
 							</Tooltip>
 							</Tooltip>
 						</div>
 						</div>
 					{/if}
 					{/if}
@@ -176,9 +174,7 @@
 							<div
 							<div
 								class=" self-center text-xs invisible group-hover:visible text-gray-400 font-medium first-letter:capitalize ml-0.5 translate-y-[1px]"
 								class=" self-center text-xs invisible group-hover:visible text-gray-400 font-medium first-letter:capitalize ml-0.5 translate-y-[1px]"
 							>
 							>
-								<Tooltip
-									content={dayjs(message.created_at / 1000000).format('LLLL')}
-								>
+								<Tooltip content={dayjs(message.created_at / 1000000).format('LLLL')}>
 									<span class="line-clamp-1">{formatDate(message.created_at / 1000000)}</span>
 									<span class="line-clamp-1">{formatDate(message.created_at / 1000000)}</span>
 								</Tooltip>
 								</Tooltip>
 							</div>
 							</div>

+ 76 - 68
src/lib/components/chat/Chat.svelte

@@ -41,11 +41,13 @@
 		convertMessagesToHistory,
 		convertMessagesToHistory,
 		copyToClipboard,
 		copyToClipboard,
 		getMessageContentParts,
 		getMessageContentParts,
+		createMessagesList,
 		extractSentencesForAudio,
 		extractSentencesForAudio,
 		promptTemplate,
 		promptTemplate,
 		splitStream,
 		splitStream,
 		sleep,
 		sleep,
-		removeDetailsWithReasoning
+		removeDetails,
+		getPromptVariables
 	} from '$lib/utils';
 	} from '$lib/utils';
 
 
 	import { generateChatCompletion } from '$lib/apis/ollama';
 	import { generateChatCompletion } from '$lib/apis/ollama';
@@ -116,7 +118,7 @@
 	let selectedToolIds = [];
 	let selectedToolIds = [];
 	let imageGenerationEnabled = false;
 	let imageGenerationEnabled = false;
 	let webSearchEnabled = $settings?.alwaysOnWebSearch ?? false;
 	let webSearchEnabled = $settings?.alwaysOnWebSearch ?? false;
-
+	let codeInterpreterEnabled = false;
 	let chat = null;
 	let chat = null;
 	let tags = [];
 	let tags = [];
 
 
@@ -224,7 +226,7 @@
 		}
 		}
 
 
 		await tick();
 		await tick();
-		saveChatHandler(_chatId);
+		saveChatHandler(_chatId, history);
 	};
 	};
 
 
 	const chatEventHandler = async (event, cb) => {
 	const chatEventHandler = async (event, cb) => {
@@ -628,7 +630,7 @@
 		} catch (e) {
 		} catch (e) {
 			// Remove the failed doc from the files array
 			// Remove the failed doc from the files array
 			files = files.filter((f) => f.name !== url);
 			files = files.filter((f) => f.name !== url);
-			toast.error(e);
+			toast.error(`${e}`);
 		}
 		}
 	};
 	};
 
 
@@ -825,20 +827,6 @@
 			messagesContainerElement.scrollTop = messagesContainerElement.scrollHeight;
 			messagesContainerElement.scrollTop = messagesContainerElement.scrollHeight;
 		}
 		}
 	};
 	};
-
-	const createMessagesList = (responseMessageId) => {
-		if (responseMessageId === null) {
-			return [];
-		}
-
-		const message = history.messages[responseMessageId];
-		if (message?.parentId) {
-			return [...createMessagesList(message.parentId), message];
-		} else {
-			return [message];
-		}
-	};
-
 	const chatCompletedHandler = async (chatId, modelId, responseMessageId, messages) => {
 	const chatCompletedHandler = async (chatId, modelId, responseMessageId, messages) => {
 		const res = await chatCompleted(localStorage.token, {
 		const res = await chatCompleted(localStorage.token, {
 			model: modelId,
 			model: modelId,
@@ -895,7 +883,7 @@
 	};
 	};
 
 
 	const chatActionHandler = async (chatId, actionId, modelId, responseMessageId, event = null) => {
 	const chatActionHandler = async (chatId, actionId, modelId, responseMessageId, event = null) => {
-		const messages = createMessagesList(responseMessageId);
+		const messages = createMessagesList(history, responseMessageId);
 
 
 		const res = await chatAction(localStorage.token, actionId, {
 		const res = await chatAction(localStorage.token, actionId, {
 			model: modelId,
 			model: modelId,
@@ -964,7 +952,7 @@
 			const modelId = selectedModels[0];
 			const modelId = selectedModels[0];
 			const model = $models.filter((m) => m.id === modelId).at(0);
 			const model = $models.filter((m) => m.id === modelId).at(0);
 
 
-			const messages = createMessagesList(history.currentId);
+			const messages = createMessagesList(history, history.currentId);
 			const parentMessage = messages.length !== 0 ? messages.at(-1) : null;
 			const parentMessage = messages.length !== 0 ? messages.at(-1) : null;
 
 
 			const userMessageId = uuidv4();
 			const userMessageId = uuidv4();
@@ -1009,9 +997,9 @@
 			}
 			}
 
 
 			if (messages.length === 0) {
 			if (messages.length === 0) {
-				await initChatHandler();
+				await initChatHandler(history);
 			} else {
 			} else {
-				await saveChatHandler($chatId);
+				await saveChatHandler($chatId, history);
 			}
 			}
 		}
 		}
 	};
 	};
@@ -1073,9 +1061,9 @@
 		}
 		}
 
 
 		if (messages.length === 0) {
 		if (messages.length === 0) {
-			await initChatHandler();
+			await initChatHandler(history);
 		} else {
 		} else {
-			await saveChatHandler($chatId);
+			await saveChatHandler($chatId, history);
 		}
 		}
 	};
 	};
 
 
@@ -1209,7 +1197,12 @@
 			);
 			);
 
 
 			history.messages[message.id] = message;
 			history.messages[message.id] = message;
-			await chatCompletedHandler(chatId, message.model, message.id, createMessagesList(message.id));
+			await chatCompletedHandler(
+				chatId,
+				message.model,
+				message.id,
+				createMessagesList(history, message.id)
+			);
 		}
 		}
 
 
 		console.log(data);
 		console.log(data);
@@ -1225,7 +1218,7 @@
 	const submitPrompt = async (userPrompt, { _raw = false } = {}) => {
 	const submitPrompt = async (userPrompt, { _raw = false } = {}) => {
 		console.log('submitPrompt', userPrompt, $chatId);
 		console.log('submitPrompt', userPrompt, $chatId);
 
 
-		const messages = createMessagesList(history.currentId);
+		const messages = createMessagesList(history, history.currentId);
 		const _selectedModels = selectedModels.map((modelId) =>
 		const _selectedModels = selectedModels.map((modelId) =>
 			$models.map((m) => m.id).includes(modelId) ? modelId : ''
 			$models.map((m) => m.id).includes(modelId) ? modelId : ''
 		);
 		);
@@ -1273,7 +1266,6 @@
 		}
 		}
 
 
 		prompt = '';
 		prompt = '';
-		await tick();
 
 
 		// Reset chat input textarea
 		// Reset chat input textarea
 		const chatInputElement = document.getElementById('chat-input');
 		const chatInputElement = document.getElementById('chat-input');
@@ -1315,34 +1307,25 @@
 			history.messages[messages.at(-1).id].childrenIds.push(userMessageId);
 			history.messages[messages.at(-1).id].childrenIds.push(userMessageId);
 		}
 		}
 
 
-		// Wait until history/message have been updated
-		await tick();
-
 		// focus on chat input
 		// focus on chat input
 		const chatInput = document.getElementById('chat-input');
 		const chatInput = document.getElementById('chat-input');
 		chatInput?.focus();
 		chatInput?.focus();
 
 
 		saveSessionSelectedModels();
 		saveSessionSelectedModels();
 
 
-		await sendPrompt(userPrompt, userMessageId, { newChat: true });
+		await sendPrompt(history, userPrompt, userMessageId, { newChat: true });
 	};
 	};
 
 
 	const sendPrompt = async (
 	const sendPrompt = async (
+		_history,
 		prompt: string,
 		prompt: string,
 		parentId: string,
 		parentId: string,
 		{ modelId = null, modelIdx = null, newChat = false } = {}
 		{ modelId = null, modelIdx = null, newChat = false } = {}
 	) => {
 	) => {
-		// Create new chat if newChat is true and first user message
-		if (
-			newChat &&
-			history.messages[history.currentId].parentId === null &&
-			history.messages[history.currentId].role === 'user'
-		) {
-			await initChatHandler();
-		} else {
-			await saveChatHandler($chatId);
-		}
+		let _chatId = JSON.parse(JSON.stringify($chatId));
+		_history = JSON.parse(JSON.stringify(_history));
 
 
+		const responseMessageIds: Record<PropertyKey, string> = {};
 		// If modelId is provided, use it, else use selected model
 		// If modelId is provided, use it, else use selected model
 		let selectedModelIds = modelId
 		let selectedModelIds = modelId
 			? [modelId]
 			? [modelId]
@@ -1351,7 +1334,6 @@
 				: selectedModels;
 				: selectedModels;
 
 
 		// Create response messages for each selected model
 		// Create response messages for each selected model
-		const responseMessageIds: Record<PropertyKey, string> = {};
 		for (const [_modelIdx, modelId] of selectedModelIds.entries()) {
 		for (const [_modelIdx, modelId] of selectedModelIds.entries()) {
 			const model = $models.filter((m) => m.id === modelId).at(0);
 			const model = $models.filter((m) => m.id === modelId).at(0);
 
 
@@ -1386,19 +1368,26 @@
 				responseMessageIds[`${modelId}-${modelIdx ? modelIdx : _modelIdx}`] = responseMessageId;
 				responseMessageIds[`${modelId}-${modelIdx ? modelIdx : _modelIdx}`] = responseMessageId;
 			}
 			}
 		}
 		}
+		history = history;
+
+		// Create new chat if newChat is true and first user message
+		if (newChat && _history.messages[_history.currentId].parentId === null) {
+			_chatId = await initChatHandler(_history);
+		}
+
 		await tick();
 		await tick();
 
 
+		_history = JSON.parse(JSON.stringify(history));
 		// Save chat after all messages have been created
 		// Save chat after all messages have been created
-		await saveChatHandler($chatId);
+		await saveChatHandler(_chatId, _history);
 
 
-		const _chatId = JSON.parse(JSON.stringify($chatId));
 		await Promise.all(
 		await Promise.all(
 			selectedModelIds.map(async (modelId, _modelIdx) => {
 			selectedModelIds.map(async (modelId, _modelIdx) => {
 				console.log('modelId', modelId);
 				console.log('modelId', modelId);
 				const model = $models.filter((m) => m.id === modelId).at(0);
 				const model = $models.filter((m) => m.id === modelId).at(0);
 
 
 				if (model) {
 				if (model) {
-					const messages = createMessagesList(parentId);
+					const messages = createMessagesList(_history, parentId);
 					// If there are image files, check if model is vision capable
 					// If there are image files, check if model is vision capable
 					const hasImages = messages.some((message) =>
 					const hasImages = messages.some((message) =>
 						message.files?.some((file) => file.type === 'image')
 						message.files?.some((file) => file.type === 'image')
@@ -1414,7 +1403,7 @@
 
 
 					let responseMessageId =
 					let responseMessageId =
 						responseMessageIds[`${modelId}-${modelIdx ? modelIdx : _modelIdx}`];
 						responseMessageIds[`${modelId}-${modelIdx ? modelIdx : _modelIdx}`];
-					let responseMessage = history.messages[responseMessageId];
+					let responseMessage = _history.messages[responseMessageId];
 
 
 					let userContext = null;
 					let userContext = null;
 					if ($settings?.memory ?? false) {
 					if ($settings?.memory ?? false) {
@@ -1443,7 +1432,7 @@
 					const chatEventEmitter = await getChatEventEmitter(model.id, _chatId);
 					const chatEventEmitter = await getChatEventEmitter(model.id, _chatId);
 
 
 					scrollToBottom();
 					scrollToBottom();
-					await sendPromptSocket(model, responseMessageId, _chatId);
+					await sendPromptSocket(_history, model, responseMessageId, _chatId);
 
 
 					if (chatEventEmitter) clearInterval(chatEventEmitter);
 					if (chatEventEmitter) clearInterval(chatEventEmitter);
 				} else {
 				} else {
@@ -1456,9 +1445,9 @@
 		chats.set(await getChatList(localStorage.token, $currentChatPage));
 		chats.set(await getChatList(localStorage.token, $currentChatPage));
 	};
 	};
 
 
-	const sendPromptSocket = async (model, responseMessageId, _chatId) => {
-		const responseMessage = history.messages[responseMessageId];
-		const userMessage = history.messages[responseMessage.parentId];
+	const sendPromptSocket = async (_history, model, responseMessageId, _chatId) => {
+		const responseMessage = _history.messages[responseMessageId];
+		const userMessage = _history.messages[responseMessage.parentId];
 
 
 		let files = JSON.parse(JSON.stringify(chatFiles));
 		let files = JSON.parse(JSON.stringify(chatFiles));
 		files.push(
 		files.push(
@@ -1506,9 +1495,9 @@
 						}`
 						}`
 					}
 					}
 				: undefined,
 				: undefined,
-			...createMessagesList(responseMessageId).map((message) => ({
+			...createMessagesList(_history, responseMessageId).map((message) => ({
 				...message,
 				...message,
-				content: removeDetailsWithReasoning(message.content)
+				content: removeDetails(message.content, ['reasoning', 'code_interpreter'])
 			}))
 			}))
 		]
 		]
 			.filter((message) => message?.content?.trim())
 			.filter((message) => message?.content?.trim())
@@ -1559,10 +1548,18 @@
 
 
 				files: (files?.length ?? 0) > 0 ? files : undefined,
 				files: (files?.length ?? 0) > 0 ? files : undefined,
 				tool_ids: selectedToolIds.length > 0 ? selectedToolIds : undefined,
 				tool_ids: selectedToolIds.length > 0 ? selectedToolIds : undefined,
+
 				features: {
 				features: {
 					image_generation: imageGenerationEnabled,
 					image_generation: imageGenerationEnabled,
+					code_interpreter: codeInterpreterEnabled,
 					web_search: webSearchEnabled
 					web_search: webSearchEnabled
 				},
 				},
+				variables: {
+					...getPromptVariables(
+						$user.name,
+						$settings?.userLocation ? await getAndUpdateUserLocation(localStorage.token) : undefined
+					)
+				},
 
 
 				session_id: $socket?.id,
 				session_id: $socket?.id,
 				chat_id: $chatId,
 				chat_id: $chatId,
@@ -1592,12 +1589,15 @@
 			},
 			},
 			`${WEBUI_BASE_URL}/api`
 			`${WEBUI_BASE_URL}/api`
 		).catch((error) => {
 		).catch((error) => {
-			console.log(error);
+			toast.error(`${error}`);
+
 			responseMessage.error = {
 			responseMessage.error = {
 				content: error
 				content: error
 			};
 			};
 			responseMessage.done = true;
 			responseMessage.done = true;
+
 			history.messages[responseMessageId] = responseMessage;
 			history.messages[responseMessageId] = responseMessage;
+			history.currentId = responseMessageId;
 			return null;
 			return null;
 		});
 		});
 
 
@@ -1695,7 +1695,7 @@
 		history.currentId = userMessageId;
 		history.currentId = userMessageId;
 
 
 		await tick();
 		await tick();
-		await sendPrompt(userPrompt, userMessageId);
+		await sendPrompt(history, userPrompt, userMessageId);
 	};
 	};
 
 
 	const regenerateResponse = async (message) => {
 	const regenerateResponse = async (message) => {
@@ -1707,11 +1707,11 @@
 
 
 			if ((userMessage?.models ?? [...selectedModels]).length == 1) {
 			if ((userMessage?.models ?? [...selectedModels]).length == 1) {
 				// If user message has only one model selected, sendPrompt automatically selects it for regeneration
 				// If user message has only one model selected, sendPrompt automatically selects it for regeneration
-				await sendPrompt(userPrompt, userMessage.id);
+				await sendPrompt(history, userPrompt, userMessage.id);
 			} else {
 			} else {
 				// If there are multiple models selected, use the model of the response message for regeneration
 				// If there are multiple models selected, use the model of the response message for regeneration
 				// e.g. many model chat
 				// e.g. many model chat
-				await sendPrompt(userPrompt, userMessage.id, {
+				await sendPrompt(history, userPrompt, userMessage.id, {
 					modelId: message.model,
 					modelId: message.model,
 					modelIdx: message.modelIdx
 					modelIdx: message.modelIdx
 				});
 				});
@@ -1733,7 +1733,7 @@
 				.at(0);
 				.at(0);
 
 
 			if (model) {
 			if (model) {
-				await sendPromptSocket(model, responseMessage.id, _chatId);
+				await sendPromptSocket(history, model, responseMessage.id, _chatId);
 			}
 			}
 		}
 		}
 	};
 	};
@@ -1776,7 +1776,7 @@
 					}
 					}
 				}
 				}
 
 
-				await saveChatHandler(_chatId);
+				await saveChatHandler(_chatId, history);
 			} else {
 			} else {
 				console.error(res);
 				console.error(res);
 			}
 			}
@@ -1785,42 +1785,48 @@
 		}
 		}
 	};
 	};
 
 
-	const initChatHandler = async () => {
+	const initChatHandler = async (history) => {
+		let _chatId = $chatId;
+
 		if (!$temporaryChatEnabled) {
 		if (!$temporaryChatEnabled) {
 			chat = await createNewChat(localStorage.token, {
 			chat = await createNewChat(localStorage.token, {
-				id: $chatId,
+				id: _chatId,
 				title: $i18n.t('New Chat'),
 				title: $i18n.t('New Chat'),
 				models: selectedModels,
 				models: selectedModels,
 				system: $settings.system ?? undefined,
 				system: $settings.system ?? undefined,
 				params: params,
 				params: params,
 				history: history,
 				history: history,
-				messages: createMessagesList(history.currentId),
+				messages: createMessagesList(history, history.currentId),
 				tags: [],
 				tags: [],
 				timestamp: Date.now()
 				timestamp: Date.now()
 			});
 			});
 
 
-			currentChatPage.set(1);
+			_chatId = chat.id;
+			await chatId.set(_chatId);
+
 			await chats.set(await getChatList(localStorage.token, $currentChatPage));
 			await chats.set(await getChatList(localStorage.token, $currentChatPage));
-			await chatId.set(chat.id);
+			currentChatPage.set(1);
 
 
-			window.history.replaceState(history.state, '', `/c/${chat.id}`);
+			window.history.replaceState(history.state, '', `/c/${_chatId}`);
 		} else {
 		} else {
+			_chatId = 'local';
 			await chatId.set('local');
 			await chatId.set('local');
 		}
 		}
 		await tick();
 		await tick();
+
+		return _chatId;
 	};
 	};
 
 
-	const saveChatHandler = async (_chatId) => {
+	const saveChatHandler = async (_chatId, history) => {
 		if ($chatId == _chatId) {
 		if ($chatId == _chatId) {
 			if (!$temporaryChatEnabled) {
 			if (!$temporaryChatEnabled) {
 				chat = await updateChatById(localStorage.token, _chatId, {
 				chat = await updateChatById(localStorage.token, _chatId, {
 					models: selectedModels,
 					models: selectedModels,
 					history: history,
 					history: history,
-					messages: createMessagesList(history.currentId),
+					messages: createMessagesList(history, history.currentId),
 					params: params,
 					params: params,
 					files: chatFiles
 					files: chatFiles
 				});
 				});
-
 				currentChatPage.set(1);
 				currentChatPage.set(1);
 				await chats.set(await getChatList(localStorage.token, $currentChatPage));
 				await chats.set(await getChatList(localStorage.token, $currentChatPage));
 			}
 			}
@@ -1924,7 +1930,7 @@
 				{/if}
 				{/if}
 
 
 				<div class="flex flex-col flex-auto z-10 w-full">
 				<div class="flex flex-col flex-auto z-10 w-full">
-					{#if $settings?.landingPageMode === 'chat' || createMessagesList(history.currentId).length > 0}
+					{#if $settings?.landingPageMode === 'chat' || createMessagesList(history, history.currentId).length > 0}
 						<div
 						<div
 							class=" pb-2.5 flex flex-col justify-between w-full flex-auto overflow-auto h-0 max-w-full z-10 scrollbar-hidden"
 							class=" pb-2.5 flex flex-col justify-between w-full flex-auto overflow-auto h-0 max-w-full z-10 scrollbar-hidden"
 							id="messages-container"
 							id="messages-container"
@@ -1964,6 +1970,7 @@
 								bind:autoScroll
 								bind:autoScroll
 								bind:selectedToolIds
 								bind:selectedToolIds
 								bind:imageGenerationEnabled
 								bind:imageGenerationEnabled
+								bind:codeInterpreterEnabled
 								bind:webSearchEnabled
 								bind:webSearchEnabled
 								bind:atSelectedModel
 								bind:atSelectedModel
 								transparentBackground={$settings?.backgroundImageUrl ?? false}
 								transparentBackground={$settings?.backgroundImageUrl ?? false}
@@ -2015,6 +2022,7 @@
 								bind:autoScroll
 								bind:autoScroll
 								bind:selectedToolIds
 								bind:selectedToolIds
 								bind:imageGenerationEnabled
 								bind:imageGenerationEnabled
+								bind:codeInterpreterEnabled
 								bind:webSearchEnabled
 								bind:webSearchEnabled
 								bind:atSelectedModel
 								bind:atSelectedModel
 								transparentBackground={$settings?.backgroundImageUrl ?? false}
 								transparentBackground={$settings?.backgroundImageUrl ?? false}

+ 164 - 80
src/lib/components/chat/MessageInput.svelte

@@ -22,23 +22,27 @@
 	import { blobToFile, compressImage, createMessagesList, findWordIndices } from '$lib/utils';
 	import { blobToFile, compressImage, createMessagesList, findWordIndices } from '$lib/utils';
 	import { transcribeAudio } from '$lib/apis/audio';
 	import { transcribeAudio } from '$lib/apis/audio';
 	import { uploadFile } from '$lib/apis/files';
 	import { uploadFile } from '$lib/apis/files';
-	import { getTools } from '$lib/apis/tools';
+	import { generateAutoCompletion } from '$lib/apis';
+	import { deleteFileById } from '$lib/apis/files';
 
 
 	import { WEBUI_BASE_URL, WEBUI_API_BASE_URL, PASTED_TEXT_CHARACTER_LIMIT } from '$lib/constants';
 	import { WEBUI_BASE_URL, WEBUI_API_BASE_URL, PASTED_TEXT_CHARACTER_LIMIT } from '$lib/constants';
 
 
-	import Tooltip from '../common/Tooltip.svelte';
 	import InputMenu from './MessageInput/InputMenu.svelte';
 	import InputMenu from './MessageInput/InputMenu.svelte';
-	import Headphone from '../icons/Headphone.svelte';
 	import VoiceRecording from './MessageInput/VoiceRecording.svelte';
 	import VoiceRecording from './MessageInput/VoiceRecording.svelte';
-	import FileItem from '../common/FileItem.svelte';
 	import FilesOverlay from './MessageInput/FilesOverlay.svelte';
 	import FilesOverlay from './MessageInput/FilesOverlay.svelte';
 	import Commands from './MessageInput/Commands.svelte';
 	import Commands from './MessageInput/Commands.svelte';
-	import XMark from '../icons/XMark.svelte';
+
 	import RichTextInput from '../common/RichTextInput.svelte';
 	import RichTextInput from '../common/RichTextInput.svelte';
-	import { generateAutoCompletion } from '$lib/apis';
-	import { error, text } from '@sveltejs/kit';
+	import Tooltip from '../common/Tooltip.svelte';
+	import FileItem from '../common/FileItem.svelte';
 	import Image from '../common/Image.svelte';
 	import Image from '../common/Image.svelte';
-	import { deleteFileById } from '$lib/apis/files';
+
+	import XMark from '../icons/XMark.svelte';
+	import Headphone from '../icons/Headphone.svelte';
+	import GlobeAlt from '../icons/GlobeAlt.svelte';
+	import PhotoSolid from '../icons/PhotoSolid.svelte';
+	import Photo from '../icons/Photo.svelte';
+	import CommandLine from '../icons/CommandLine.svelte';
 
 
 	const i18n = getContext('i18n');
 	const i18n = getContext('i18n');
 
 
@@ -65,6 +69,7 @@
 
 
 	export let imageGenerationEnabled = false;
 	export let imageGenerationEnabled = false;
 	export let webSearchEnabled = false;
 	export let webSearchEnabled = false;
+	export let codeInterpreterEnabled = false;
 
 
 	$: onChange({
 	$: onChange({
 		prompt,
 		prompt,
@@ -211,7 +216,7 @@
 				files = files.filter((item) => item?.itemId !== tempItemId);
 				files = files.filter((item) => item?.itemId !== tempItemId);
 			}
 			}
 		} catch (e) {
 		} catch (e) {
-			toast.error(e);
+			toast.error(`${e}`);
 			files = files.filter((item) => item?.itemId !== tempItemId);
 			files = files.filter((item) => item?.itemId !== tempItemId);
 		}
 		}
 	};
 	};
@@ -385,7 +390,7 @@
 				</div>
 				</div>
 
 
 				<div class="w-full relative">
 				<div class="w-full relative">
-					{#if atSelectedModel !== undefined || selectedToolIds.length > 0 || webSearchEnabled || imageGenerationEnabled}
+					{#if atSelectedModel !== undefined || selectedToolIds.length > 0 || webSearchEnabled || imageGenerationEnabled || codeInterpreterEnabled}
 						<div
 						<div
 							class="px-3 pb-0.5 pt-1.5 text-left w-full flex flex-col absolute bottom-0 left-0 right-0 bg-gradient-to-t from-white dark:from-gray-900 z-10"
 							class="px-3 pb-0.5 pt-1.5 text-left w-full flex flex-col absolute bottom-0 left-0 right-0 bg-gradient-to-t from-white dark:from-gray-900 z-10"
 						>
 						>
@@ -421,23 +426,39 @@
 								</div>
 								</div>
 							{/if}
 							{/if}
 
 
+							{#if webSearchEnabled}
+								<div class="flex items-center justify-between w-full">
+									<div class="flex items-center gap-2.5 text-sm dark:text-gray-500">
+										<div class="pl-1">
+											<span class="relative flex size-2">
+												<span
+													class="animate-ping absolute inline-flex h-full w-full rounded-full bg-blue-400 opacity-75"
+												/>
+												<span class="relative inline-flex rounded-full size-2 bg-blue-500" />
+											</span>
+										</div>
+										<div class=" translate-y-[0.5px]">{$i18n.t('Search the internet')}</div>
+									</div>
+								</div>
+							{/if}
+
 							{#if imageGenerationEnabled}
 							{#if imageGenerationEnabled}
 								<div class="flex items-center justify-between w-full">
 								<div class="flex items-center justify-between w-full">
 									<div class="flex items-center gap-2.5 text-sm dark:text-gray-500">
 									<div class="flex items-center gap-2.5 text-sm dark:text-gray-500">
 										<div class="pl-1">
 										<div class="pl-1">
 											<span class="relative flex size-2">
 											<span class="relative flex size-2">
 												<span
 												<span
-													class="animate-ping absolute inline-flex h-full w-full rounded-full bg-green-400 opacity-75"
+													class="animate-ping absolute inline-flex h-full w-full rounded-full bg-teal-400 opacity-75"
 												/>
 												/>
-												<span class="relative inline-flex rounded-full size-2 bg-green-500" />
+												<span class="relative inline-flex rounded-full size-2 bg-teal-500" />
 											</span>
 											</span>
 										</div>
 										</div>
-										<div class=" ">{$i18n.t('Image generation')}</div>
+										<div class=" translate-y-[0.5px]">{$i18n.t('Generate an image')}</div>
 									</div>
 									</div>
 								</div>
 								</div>
 							{/if}
 							{/if}
 
 
-							{#if webSearchEnabled}
+							{#if codeInterpreterEnabled}
 								<div class="flex items-center justify-between w-full">
 								<div class="flex items-center justify-between w-full">
 									<div class="flex items-center gap-2.5 text-sm dark:text-gray-500">
 									<div class="flex items-center gap-2.5 text-sm dark:text-gray-500">
 										<div class="pl-1">
 										<div class="pl-1">
@@ -448,7 +469,7 @@
 												<span class="relative inline-flex rounded-full size-2 bg-green-500" />
 												<span class="relative inline-flex rounded-full size-2 bg-green-500" />
 											</span>
 											</span>
 										</div>
 										</div>
-										<div class=" ">{$i18n.t('Search the web')}</div>
+										<div class=" translate-y-[0.5px]">{$i18n.t('Execute code for analysis')}</div>
 									</div>
 									</div>
 								</div>
 								</div>
 							{/if}
 							{/if}
@@ -568,7 +589,7 @@
 								dir={$settings?.chatDirection ?? 'LTR'}
 								dir={$settings?.chatDirection ?? 'LTR'}
 							>
 							>
 								{#if files.length > 0}
 								{#if files.length > 0}
-									<div class="mx-1 mt-2.5 mb-1 flex items-center flex-wrap gap-2">
+									<div class="mx-2 mt-2.5 -mb-1 flex items-center flex-wrap gap-2">
 										{#each files as file, fileIdx}
 										{#each files as file, fileIdx}
 											{#if file.type === 'image'}
 											{#if file.type === 'image'}
 												<div class=" relative group">
 												<div class=" relative group">
@@ -606,7 +627,7 @@
 													</div>
 													</div>
 													<div class=" absolute -top-1 -right-1">
 													<div class=" absolute -top-1 -right-1">
 														<button
 														<button
-															class=" bg-gray-400 text-white border border-white rounded-full group-hover:visible invisible transition"
+															class=" bg-white text-black border border-white rounded-full group-hover:visible invisible transition"
 															type="button"
 															type="button"
 															on:click={() => {
 															on:click={() => {
 																files.splice(fileIdx, 1);
 																files.splice(fileIdx, 1);
@@ -656,65 +677,10 @@
 									</div>
 									</div>
 								{/if}
 								{/if}
 
 
-								<div class=" flex">
-									<div class="ml-1 self-end mb-1.5 flex space-x-1">
-										<InputMenu
-											bind:imageGenerationEnabled
-											bind:webSearchEnabled
-											bind:selectedToolIds
-											{screenCaptureHandler}
-											uploadFilesHandler={() => {
-												filesInputElement.click();
-											}}
-											uploadGoogleDriveHandler={async () => {
-												try {
-													const fileData = await createPicker();
-													if (fileData) {
-														const file = new File([fileData.blob], fileData.name, {
-															type: fileData.blob.type
-														});
-														await uploadFileHandler(file);
-													} else {
-														console.log('No file was selected from Google Drive');
-													}
-												} catch (error) {
-													console.error('Google Drive Error:', error);
-													toast.error(
-														$i18n.t('Error accessing Google Drive: {{error}}', {
-															error: error.message
-														})
-													);
-												}
-											}}
-											onClose={async () => {
-												await tick();
-
-												const chatInput = document.getElementById('chat-input');
-												chatInput?.focus();
-											}}
-										>
-											<button
-												class="bg-transparent hover:bg-white/80 text-gray-800 dark:text-white dark:hover:bg-gray-800 transition rounded-full p-2 outline-none focus:outline-none"
-												type="button"
-												aria-label="More"
-											>
-												<svg
-													xmlns="http://www.w3.org/2000/svg"
-													viewBox="0 0 20 20"
-													fill="currentColor"
-													class="size-5"
-												>
-													<path
-														d="M10.75 4.75a.75.75 0 0 0-1.5 0v4.5h-4.5a.75.75 0 0 0 0 1.5h4.5v4.5a.75.75 0 0 0 1.5 0v-4.5h4.5a.75.75 0 0 0 0-1.5h-4.5v-4.5Z"
-													/>
-												</svg>
-											</button>
-										</InputMenu>
-									</div>
-
+								<div class="px-2.5">
 									{#if $settings?.richTextInput ?? true}
 									{#if $settings?.richTextInput ?? true}
 										<div
 										<div
-											class="scrollbar-hidden text-left bg-transparent dark:text-gray-100 outline-none w-full py-2.5 px-1 rounded-xl resize-none h-fit max-h-80 overflow-auto"
+											class="scrollbar-hidden text-left bg-transparent dark:text-gray-100 outline-none w-full pt-3 px-1 rounded-xl resize-none h-fit max-h-80 overflow-auto"
 										>
 										>
 											<RichTextInput
 											<RichTextInput
 												bind:this={chatInputElement}
 												bind:this={chatInputElement}
@@ -918,7 +884,7 @@
 										<textarea
 										<textarea
 											id="chat-input"
 											id="chat-input"
 											bind:this={chatInputElement}
 											bind:this={chatInputElement}
-											class="scrollbar-hidden bg-transparent dark:text-gray-100 outline-none w-full py-3 px-1 rounded-xl resize-none h-[48px]"
+											class="scrollbar-hidden bg-transparent dark:text-gray-100 outline-none w-full pt-3 px-1 rounded-xl resize-none"
 											placeholder={placeholder ? placeholder : $i18n.t('Send a Message')}
 											placeholder={placeholder ? placeholder : $i18n.t('Send a Message')}
 											bind:value={prompt}
 											bind:value={prompt}
 											on:keypress={(e) => {
 											on:keypress={(e) => {
@@ -1107,8 +1073,122 @@
 											}}
 											}}
 										/>
 										/>
 									{/if}
 									{/if}
+								</div>
+
+								<div class=" flex justify-between mt-1.5 mb-2.5 mx-0.5">
+									<div class="ml-1 self-end gap-0.5 flex items-center">
+										<InputMenu
+											bind:selectedToolIds
+											{screenCaptureHandler}
+											{inputFilesHandler}
+											uploadFilesHandler={() => {
+												filesInputElement.click();
+											}}
+											uploadGoogleDriveHandler={async () => {
+												try {
+													const fileData = await createPicker();
+													if (fileData) {
+														const file = new File([fileData.blob], fileData.name, {
+															type: fileData.blob.type
+														});
+														await uploadFileHandler(file);
+													} else {
+														console.log('No file was selected from Google Drive');
+													}
+												} catch (error) {
+													console.error('Google Drive Error:', error);
+													toast.error(
+														$i18n.t('Error accessing Google Drive: {{error}}', {
+															error: error.message
+														})
+													);
+												}
+											}}
+											onClose={async () => {
+												await tick();
+
+												const chatInput = document.getElementById('chat-input');
+												chatInput?.focus();
+											}}
+										>
+											<button
+												class="bg-transparent hover:bg-gray-100 text-gray-800 dark:text-white dark:hover:bg-gray-800 transition rounded-full p-1.5 outline-none focus:outline-none"
+												type="button"
+												aria-label="More"
+											>
+												<svg
+													xmlns="http://www.w3.org/2000/svg"
+													viewBox="0 0 20 20"
+													fill="currentColor"
+													class="size-5"
+												>
+													<path
+														d="M10.75 4.75a.75.75 0 0 0-1.5 0v4.5h-4.5a.75.75 0 0 0 0 1.5h4.5v4.5a.75.75 0 0 0 1.5 0v-4.5h4.5a.75.75 0 0 0 0-1.5h-4.5v-4.5Z"
+													/>
+												</svg>
+											</button>
+										</InputMenu>
+
+										{#if $_user}
+											{#if $config?.features?.enable_web_search && ($_user.role === 'admin' || $_user?.permissions?.features?.web_search)}
+												<Tooltip content={$i18n.t('Search the internet')} placement="top">
+													<button
+														on:click|preventDefault={() => (webSearchEnabled = !webSearchEnabled)}
+														type="button"
+														class="px-1.5 sm:px-2.5 py-1.5 flex gap-1.5 items-center text-sm rounded-full font-medium transition-colors duration-300 focus:outline-none max-w-full overflow-hidden {webSearchEnabled
+															? 'bg-blue-100 dark:bg-blue-500/20 text-blue-500 dark:text-blue-400'
+															: 'bg-transparent text-gray-600 dark:text-gray-400 border-gray-200 hover:bg-gray-100 dark:hover:bg-gray-800'}"
+													>
+														<GlobeAlt className="size-5" strokeWidth="1.75" />
+														<span
+															class="hidden sm:block whitespace-nowrap overflow-hidden text-ellipsis translate-y-[0.5px] mr-0.5"
+															>{$i18n.t('Web Search')}</span
+														>
+													</button>
+												</Tooltip>
+											{/if}
+
+											{#if $config?.features?.enable_image_generation && ($_user.role === 'admin' || $_user?.permissions?.features?.image_generation)}
+												<Tooltip content={$i18n.t('Generate an image')} placement="top">
+													<button
+														on:click|preventDefault={() =>
+															(imageGenerationEnabled = !imageGenerationEnabled)}
+														type="button"
+														class="px-1.5 sm:px-2.5 py-1.5 flex gap-1.5 items-center text-sm rounded-full font-medium transition-colors duration-300 focus:outline-none 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 '}"
+													>
+														<Photo className="size-5" strokeWidth="1.75" />
+														<span
+															class="hidden sm:block whitespace-nowrap overflow-hidden text-ellipsis translate-y-[0.5px] mr-0.5"
+															>{$i18n.t('Image')}</span
+														>
+													</button>
+												</Tooltip>
+											{/if}
+
+											{#if $_user.role === 'admin' || $_user?.permissions?.features?.code_interpreter}
+												<Tooltip content={$i18n.t('Execute code for analysis')} placement="top">
+													<button
+														on:click|preventDefault={() =>
+															(codeInterpreterEnabled = !codeInterpreterEnabled)}
+														type="button"
+														class="px-1.5 sm:px-2.5 py-1.5 flex gap-1.5 items-center text-sm rounded-full font-medium transition-colors duration-300 focus:outline-none 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 '}"
+													>
+														<CommandLine className="size-5" strokeWidth="1.75" />
+														<span
+															class="hidden sm:block whitespace-nowrap overflow-hidden text-ellipsis translate-y-[0.5px] mr-0.5"
+															>{$i18n.t('Code Interpreter')}</span
+														>
+													</button>
+												</Tooltip>
+											{/if}
+										{/if}
+									</div>
 
 
-									<div class="self-end mb-1.5 flex space-x-1 mr-1">
+									<div class="self-end flex space-x-1 mr-1">
 										{#if !history?.currentId || history.messages[history.currentId]?.done == true}
 										{#if !history?.currentId || history.messages[history.currentId]?.done == true}
 											<Tooltip content={$i18n.t('Record voice')}>
 											<Tooltip content={$i18n.t('Record voice')}>
 												<button
 												<button
@@ -1163,7 +1243,9 @@
 												<div class=" flex items-center">
 												<div class=" flex items-center">
 													<Tooltip content={$i18n.t('Call')}>
 													<Tooltip content={$i18n.t('Call')}>
 														<button
 														<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-2 self-center"
+															class=" {webSearchEnabled
+																? '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"
 															type="button"
 															on:click={async () => {
 															on:click={async () => {
 																if (selectedModels.length > 1) {
 																if (selectedModels.length > 1) {
@@ -1216,7 +1298,9 @@
 														<button
 														<button
 															id="send-message-button"
 															id="send-message-button"
 															class="{prompt !== ''
 															class="{prompt !== ''
-																? 'bg-black text-white hover:bg-gray-900 dark:bg-white dark:text-black dark:hover:bg-gray-100 '
+																? webSearchEnabled
+																	? '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"
 																: 'text-white bg-gray-200 dark:text-gray-900 dark:bg-gray-700 disabled'} transition rounded-full p-1.5 self-center"
 															type="submit"
 															type="submit"
 															disabled={prompt === ''}
 															disabled={prompt === ''}
@@ -1225,7 +1309,7 @@
 																xmlns="http://www.w3.org/2000/svg"
 																xmlns="http://www.w3.org/2000/svg"
 																viewBox="0 0 16 16"
 																viewBox="0 0 16 16"
 																fill="currentColor"
 																fill="currentColor"
-																class="size-6"
+																class="size-5"
 															>
 															>
 																<path
 																<path
 																	fill-rule="evenodd"
 																	fill-rule="evenodd"
@@ -1250,7 +1334,7 @@
 															xmlns="http://www.w3.org/2000/svg"
 															xmlns="http://www.w3.org/2000/svg"
 															viewBox="0 0 24 24"
 															viewBox="0 0 24 24"
 															fill="currentColor"
 															fill="currentColor"
-															class="size-6"
+															class="size-5"
 														>
 														>
 															<path
 															<path
 																fill-rule="evenodd"
 																fill-rule="evenodd"

+ 48 - 67
src/lib/components/chat/MessageInput/InputMenu.svelte

@@ -15,35 +15,23 @@
 	import WrenchSolid from '$lib/components/icons/WrenchSolid.svelte';
 	import WrenchSolid from '$lib/components/icons/WrenchSolid.svelte';
 	import CameraSolid from '$lib/components/icons/CameraSolid.svelte';
 	import CameraSolid from '$lib/components/icons/CameraSolid.svelte';
 	import PhotoSolid from '$lib/components/icons/PhotoSolid.svelte';
 	import PhotoSolid from '$lib/components/icons/PhotoSolid.svelte';
+	import CommandLineSolid from '$lib/components/icons/CommandLineSolid.svelte';
 
 
 	const i18n = getContext('i18n');
 	const i18n = getContext('i18n');
 
 
 	export let screenCaptureHandler: Function;
 	export let screenCaptureHandler: Function;
 	export let uploadFilesHandler: Function;
 	export let uploadFilesHandler: Function;
+	export let inputFilesHandler: Function;
+
 	export let uploadGoogleDriveHandler: Function;
 	export let uploadGoogleDriveHandler: Function;
 
 
 	export let selectedToolIds: string[] = [];
 	export let selectedToolIds: string[] = [];
 
 
-	export let webSearchEnabled: boolean;
-	export let imageGenerationEnabled: boolean;
-
 	export let onClose: Function;
 	export let onClose: Function;
 
 
 	let tools = {};
 	let tools = {};
 	let show = false;
 	let show = false;
 
 
-	let showImageGeneration = false;
-
-	$: showImageGeneration =
-		$config?.features?.enable_image_generation &&
-		($user.role === 'admin' || $user?.permissions?.features?.image_generation);
-
-	let showWebSearch = false;
-
-	$: showWebSearch =
-		$config?.features?.enable_web_search &&
-		($user.role === 'admin' || $user?.permissions?.features?.web_search);
-
 	$: if (show) {
 	$: if (show) {
 		init();
 		init();
 	}
 	}
@@ -65,8 +53,31 @@
 			return a;
 			return a;
 		}, {});
 		}, {});
 	};
 	};
+
+	const detectMobile = () => {
+		const userAgent = navigator.userAgent || navigator.vendor || window.opera;
+		return /android|iphone|ipad|ipod|windows phone/i.test(userAgent);
+	};
+
+	function handleFileChange(event) {
+		const inputFiles = Array.from(event.target?.files);
+		if (inputFiles && inputFiles.length > 0) {
+			console.log(inputFiles);
+			inputFilesHandler(inputFiles);
+		}
+	}
 </script>
 </script>
 
 
+<!-- Hidden file input used to open the camera on mobile -->
+<input
+	id="camera-input"
+	type="file"
+	accept="image/*"
+	capture="environment"
+	on:change={handleFileChange}
+	style="display: none;"
+/>
+
 <Dropdown
 <Dropdown
 	bind:show
 	bind:show
 	on:change={(e) => {
 	on:change={(e) => {
@@ -132,62 +143,32 @@
 				<hr class="border-black/5 dark:border-white/5 my-1" />
 				<hr class="border-black/5 dark:border-white/5 my-1" />
 			{/if}
 			{/if}
 
 
-			{#if showImageGeneration}
-				<button
-					class="flex w-full justify-between gap-2 items-center px-3 py-2 text-sm font-medium cursor-pointer rounded-xl"
+			<Tooltip
+				content={!fileUploadEnabled ? $i18n.t('You do not have permission to upload files') : ''}
+				className="w-full"
+			>
+				<DropdownMenu.Item
+					class="flex gap-2 items-center px-3 py-2 text-sm  font-medium cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800  rounded-xl {!fileUploadEnabled
+						? 'opacity-50'
+						: ''}"
 					on:click={() => {
 					on:click={() => {
-						imageGenerationEnabled = !imageGenerationEnabled;
-					}}
-				>
-					<div class="flex-1 flex items-center gap-2">
-						<PhotoSolid />
-						<div class=" line-clamp-1">{$i18n.t('Image')}</div>
-					</div>
-
-					<Switch state={imageGenerationEnabled} />
-				</button>
-			{/if}
+						if (fileUploadEnabled) {
+							if (!detectMobile()) {
+								screenCaptureHandler();
+							} else {
+								const cameraInputElement = document.getElementById('camera-input');
 
 
-			{#if showWebSearch}
-				<button
-					class="flex w-full justify-between gap-2 items-center px-3 py-2 text-sm font-medium cursor-pointer rounded-xl"
-					on:click={() => {
-						webSearchEnabled = !webSearchEnabled;
+								if (cameraInputElement) {
+									cameraInputElement.click();
+								}
+							}
+						}
 					}}
 					}}
 				>
 				>
-					<div class="flex-1 flex items-center gap-2">
-						<GlobeAltSolid />
-						<div class=" line-clamp-1">{$i18n.t('Web Search')}</div>
-					</div>
-
-					<Switch state={webSearchEnabled} />
-				</button>
-			{/if}
-
-			{#if showImageGeneration || showWebSearch}
-				<hr class="border-black/5 dark:border-white/5 my-1" />
-			{/if}
-
-			{#if !$mobile}
-				<Tooltip
-					content={!fileUploadEnabled ? $i18n.t('You do not have permission to upload files') : ''}
-					className="w-full"
-				>
-					<DropdownMenu.Item
-						class="flex gap-2 items-center px-3 py-2 text-sm  font-medium cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800  rounded-xl {!fileUploadEnabled
-							? 'opacity-50'
-							: ''}"
-						on:click={() => {
-							if (fileUploadEnabled) {
-								screenCaptureHandler();
-							}
-						}}
-					>
-						<CameraSolid />
-						<div class=" line-clamp-1">{$i18n.t('Capture')}</div>
-					</DropdownMenu.Item>
-				</Tooltip>
-			{/if}
+					<CameraSolid />
+					<div class=" line-clamp-1">{$i18n.t('Capture')}</div>
+				</DropdownMenu.Item>
+			</Tooltip>
 
 
 			<Tooltip
 			<Tooltip
 				content={!fileUploadEnabled ? $i18n.t('You do not have permission to upload files') : ''}
 				content={!fileUploadEnabled ? $i18n.t('You do not have permission to upload files') : ''}

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

@@ -233,7 +233,7 @@
 				history.currentId = userMessageId;
 				history.currentId = userMessageId;
 
 
 				await tick();
 				await tick();
-				await sendPrompt(userPrompt, userMessageId);
+				await sendPrompt(history, userPrompt, userMessageId);
 			} else {
 			} else {
 				// Edit user message
 				// Edit user message
 				history.messages[messageId].content = content;
 				history.messages[messageId].content = content;

+ 98 - 92
src/lib/components/chat/Messages/CodeBlock.svelte

@@ -25,6 +25,7 @@
 	export let token;
 	export let token;
 	export let lang = '';
 	export let lang = '';
 	export let code = '';
 	export let code = '';
+	export let attributes = {};
 
 
 	export let className = 'my-2';
 	export let className = 'my-2';
 	export let editorClassName = '';
 	export let editorClassName = '';
@@ -49,6 +50,7 @@
 	let stdout = null;
 	let stdout = null;
 	let stderr = null;
 	let stderr = null;
 	let result = null;
 	let result = null;
+	let files = null;
 
 
 	let copied = false;
 	let copied = false;
 	let saved = false;
 	let saved = false;
@@ -109,86 +111,7 @@
 	};
 	};
 
 
 	const executePython = async (code) => {
 	const executePython = async (code) => {
-		if (!code.includes('input') && !code.includes('matplotlib')) {
-			executePythonAsWorker(code);
-		} else {
-			result = null;
-			stdout = null;
-			stderr = null;
-
-			executing = true;
-
-			document.pyodideMplTarget = document.getElementById(`plt-canvas-${id}`);
-
-			let pyodide = await loadPyodide({
-				indexURL: '/pyodide/',
-				stdout: (text) => {
-					console.log('Python output:', text);
-
-					if (stdout) {
-						stdout += `${text}\n`;
-					} else {
-						stdout = `${text}\n`;
-					}
-				},
-				stderr: (text) => {
-					console.log('An error occurred:', text);
-					if (stderr) {
-						stderr += `${text}\n`;
-					} else {
-						stderr = `${text}\n`;
-					}
-				},
-				packages: ['micropip']
-			});
-
-			try {
-				const micropip = pyodide.pyimport('micropip');
-
-				// await micropip.set_index_urls('https://pypi.org/pypi/{package_name}/json');
-
-				let packages = [
-					code.includes('requests') ? 'requests' : null,
-					code.includes('bs4') ? 'beautifulsoup4' : null,
-					code.includes('numpy') ? 'numpy' : null,
-					code.includes('pandas') ? 'pandas' : null,
-					code.includes('matplotlib') ? 'matplotlib' : null,
-					code.includes('sklearn') ? 'scikit-learn' : null,
-					code.includes('scipy') ? 'scipy' : null,
-					code.includes('re') ? 'regex' : null,
-					code.includes('seaborn') ? 'seaborn' : null
-				].filter(Boolean);
-
-				console.log(packages);
-				await micropip.install(packages);
-
-				result = await pyodide.runPythonAsync(`from js import prompt
-def input(p):
-    return prompt(p)
-__builtins__.input = input`);
-
-				result = await pyodide.runPython(code);
-
-				if (!result) {
-					result = '[NO OUTPUT]';
-				}
-
-				console.log(result);
-				console.log(stdout);
-				console.log(stderr);
-
-				const pltCanvasElement = document.getElementById(`plt-canvas-${id}`);
-
-				if (pltCanvasElement?.innerHTML !== '') {
-					pltCanvasElement.classList.add('pt-4');
-				}
-			} catch (error) {
-				console.error('Error:', error);
-				stderr = error;
-			}
-
-			executing = false;
-		}
+		executePythonAsWorker(code);
 	};
 	};
 
 
 	const executePythonAsWorker = async (code) => {
 	const executePythonAsWorker = async (code) => {
@@ -206,7 +129,11 @@ __builtins__.input = input`);
 			code.includes('sklearn') ? 'scikit-learn' : null,
 			code.includes('sklearn') ? 'scikit-learn' : null,
 			code.includes('scipy') ? 'scipy' : null,
 			code.includes('scipy') ? 'scipy' : null,
 			code.includes('re') ? 'regex' : null,
 			code.includes('re') ? 'regex' : null,
-			code.includes('seaborn') ? 'seaborn' : null
+			code.includes('seaborn') ? 'seaborn' : null,
+			code.includes('sympy') ? 'sympy' : null,
+			code.includes('tiktoken') ? 'tiktoken' : null,
+			code.includes('matplotlib') ? 'matplotlib' : null,
+			code.includes('pytz') ? 'pytz' : null
 		].filter(Boolean);
 		].filter(Boolean);
 
 
 		console.log(packages);
 		console.log(packages);
@@ -233,7 +160,31 @@ __builtins__.input = input`);
 
 
 			console.log(id, data);
 			console.log(id, data);
 
 
-			data['stdout'] && (stdout = data['stdout']);
+			if (data['stdout']) {
+				stdout = data['stdout'];
+				const stdoutLines = stdout.split('\n');
+
+				for (const [idx, line] of stdoutLines.entries()) {
+					if (line.startsWith('data:image/png;base64')) {
+						if (files) {
+							files.push({
+								type: 'image/png',
+								data: line
+							});
+						} else {
+							files = [
+								{
+									type: 'image/png',
+									data: line
+								}
+							];
+						}
+
+						stdout = stdout.replace(`${line}\n`, ``);
+					}
+				}
+			}
+
 			data['stderr'] && (stderr = data['stderr']);
 			data['stderr'] && (stderr = data['stderr']);
 			data['result'] && (result = data['result']);
 			data['result'] && (result = data['result']);
 
 
@@ -279,6 +230,36 @@ __builtins__.input = input`);
 
 
 	$: dispatch('code', { lang, code });
 	$: dispatch('code', { lang, code });
 
 
+	$: if (attributes) {
+		onAttributesUpdate();
+	}
+
+	const onAttributesUpdate = () => {
+		if (attributes?.output) {
+			// Create a helper function to unescape HTML entities
+			const unescapeHtml = (html) => {
+				const textArea = document.createElement('textarea');
+				textArea.innerHTML = html;
+				return textArea.value;
+			};
+
+			try {
+				// Unescape the HTML-encoded string
+				const unescapedOutput = unescapeHtml(attributes.output);
+
+				// Parse the unescaped string into JSON
+				const output = JSON.parse(unescapedOutput);
+
+				// Assign the parsed values to variables
+				stdout = output.stdout;
+				stderr = output.stderr;
+				result = output.result;
+			} catch (error) {
+				console.error('Error:', error);
+			}
+		}
+	};
+
 	onMount(async () => {
 	onMount(async () => {
 		console.log('codeblock', lang, code);
 		console.log('codeblock', lang, code);
 
 
@@ -376,18 +357,43 @@ __builtins__.input = input`);
 
 
 			<div
 			<div
 				id="plt-canvas-{id}"
 				id="plt-canvas-{id}"
-				class="bg-[#202123] text-white max-w-full overflow-x-auto scrollbar-hidden"
+				class="bg-gray-50 dark:bg-[#202123] dark:text-white max-w-full overflow-x-auto scrollbar-hidden"
 			/>
 			/>
 
 
-			{#if executing}
-				<div class="bg-[#202123] text-white px-4 py-4 rounded-b-lg">
-					<div class=" text-gray-500 text-xs mb-1">STDOUT/STDERR</div>
-					<div class="text-sm">Running...</div>
-				</div>
-			{:else if stdout || stderr || result}
-				<div class="bg-[#202123] text-white px-4 py-4 rounded-b-lg">
-					<div class=" text-gray-500 text-xs mb-1">STDOUT/STDERR</div>
-					<div class="text-sm">{stdout || stderr || result}</div>
+			{#if executing || stdout || stderr || result}
+				<div
+					class="bg-gray-50 dark:bg-[#202123] dark:text-white !rounded-b-lg py-4 px-4 flex flex-col gap-2"
+				>
+					{#if executing}
+						<div class=" ">
+							<div class=" text-gray-500 text-xs mb-1">STDOUT/STDERR</div>
+							<div class="text-sm">Running...</div>
+						</div>
+					{:else}
+						{#if stdout || stderr}
+							<div class=" ">
+								<div class=" text-gray-500 text-xs mb-1">STDOUT/STDERR</div>
+								<div class="text-sm">{stdout || stderr}</div>
+							</div>
+						{/if}
+						{#if result || files}
+							<div class=" ">
+								<div class=" text-gray-500 text-xs mb-1">RESULT</div>
+								{#if result}
+									<div class="text-sm">{`${JSON.stringify(result)}`}</div>
+								{/if}
+								{#if files}
+									<div class="flex flex-col gap-2">
+										{#each files as file}
+											{#if file.type.startsWith('image')}
+												<img src={file.data} alt="Output" />
+											{/if}
+										{/each}
+									</div>
+								{/if}
+							</div>
+						{/if}
+					{/if}
 				</div>
 				</div>
 			{/if}
 			{/if}
 		{/if}
 		{/if}

+ 8 - 2
src/lib/components/chat/Messages/Markdown/MarkdownTokens.svelte

@@ -23,6 +23,7 @@
 	export let id: string;
 	export let id: string;
 	export let tokens: Token[];
 	export let tokens: Token[];
 	export let top = true;
 	export let top = true;
+	export let attributes = {};
 
 
 	export let save = false;
 	export let save = false;
 	export let onSourceClick: Function = () => {};
 	export let onSourceClick: Function = () => {};
@@ -83,6 +84,7 @@
 				{token}
 				{token}
 				lang={token?.lang ?? ''}
 				lang={token?.lang ?? ''}
 				code={token?.text ?? ''}
 				code={token?.text ?? ''}
+				{attributes}
 				{save}
 				{save}
 				on:code={(e) => {
 				on:code={(e) => {
 					dispatch('code', e.detail);
 					dispatch('code', e.detail);
@@ -195,9 +197,13 @@
 			</ul>
 			</ul>
 		{/if}
 		{/if}
 	{:else if token.type === 'details'}
 	{:else if token.type === 'details'}
-		<Collapsible title={token.summary} attributes={token?.attributes} className="w-fit space-y-1">
+		<Collapsible title={token.summary} attributes={token?.attributes} className="w-full space-y-1">
 			<div class=" mb-1.5" slot="content">
 			<div class=" mb-1.5" slot="content">
-				<svelte:self id={`${id}-${tokenIdx}-d`} tokens={marked.lexer(token.text)} />
+				<svelte:self
+					id={`${id}-${tokenIdx}-d`}
+					tokens={marked.lexer(token.text)}
+					attributes={token?.attributes}
+				/>
 			</div>
 			</div>
 		</Collapsible>
 		</Collapsible>
 	{:else if token.type === 'html'}
 	{:else if token.type === 'html'}

+ 2 - 0
src/lib/components/chat/Messages/Message.svelte

@@ -78,6 +78,7 @@
 				{rateMessage}
 				{rateMessage}
 				{actionMessage}
 				{actionMessage}
 				{submitMessage}
 				{submitMessage}
+				{deleteMessage}
 				{continueResponse}
 				{continueResponse}
 				{regenerateResponse}
 				{regenerateResponse}
 				{addMessages}
 				{addMessages}
@@ -95,6 +96,7 @@
 				{rateMessage}
 				{rateMessage}
 				{actionMessage}
 				{actionMessage}
 				{submitMessage}
 				{submitMessage}
+				{deleteMessage}
 				{continueResponse}
 				{continueResponse}
 				{regenerateResponse}
 				{regenerateResponse}
 				{mergeResponses}
 				{mergeResponses}

+ 5 - 2
src/lib/components/chat/Messages/MultiResponseMessages.svelte

@@ -16,9 +16,9 @@
 	import Markdown from './Markdown.svelte';
 	import Markdown from './Markdown.svelte';
 	import Name from './Name.svelte';
 	import Name from './Name.svelte';
 	import Skeleton from './Skeleton.svelte';
 	import Skeleton from './Skeleton.svelte';
-    import localizedFormat from 'dayjs/plugin/localizedFormat';
+	import localizedFormat from 'dayjs/plugin/localizedFormat';
 	const i18n = getContext('i18n');
 	const i18n = getContext('i18n');
-    dayjs.extend(localizedFormat);
+	dayjs.extend(localizedFormat);
 
 
 	export let chatId;
 	export let chatId;
 	export let history;
 	export let history;
@@ -34,6 +34,8 @@
 	export let actionMessage: Function;
 	export let actionMessage: Function;
 
 
 	export let submitMessage: Function;
 	export let submitMessage: Function;
+	export let deleteMessage: Function;
+
 	export let continueResponse: Function;
 	export let continueResponse: Function;
 	export let regenerateResponse: Function;
 	export let regenerateResponse: Function;
 	export let mergeResponses: Function;
 	export let mergeResponses: Function;
@@ -228,6 +230,7 @@
 									{editMessage}
 									{editMessage}
 									{saveMessage}
 									{saveMessage}
 									{rateMessage}
 									{rateMessage}
+									{deleteMessage}
 									{actionMessage}
 									{actionMessage}
 									{submitMessage}
 									{submitMessage}
 									{continueResponse}
 									{continueResponse}

+ 35 - 0
src/lib/components/chat/Messages/ResponseMessage.svelte

@@ -114,6 +114,7 @@
 	export let saveMessage: Function;
 	export let saveMessage: Function;
 	export let rateMessage: Function;
 	export let rateMessage: Function;
 	export let actionMessage: Function;
 	export let actionMessage: Function;
+	export let deleteMessage: Function;
 
 
 	export let submitMessage: Function;
 	export let submitMessage: Function;
 	export let continueResponse: Function;
 	export let continueResponse: Function;
@@ -461,6 +462,10 @@
 		feedbackLoading = false;
 		feedbackLoading = false;
 	};
 	};
 
 
+	const deleteMessageHandler = async () => {
+		deleteMessage(message.id);
+	};
+
 	$: if (!edit) {
 	$: if (!edit) {
 		(async () => {
 		(async () => {
 			await tick();
 			await tick();
@@ -1176,6 +1181,36 @@
 										</button>
 										</button>
 									</Tooltip>
 									</Tooltip>
 
 
+									{#if siblings.length > 1}
+										<Tooltip content={$i18n.t('Delete')} placement="bottom">
+											<button
+												type="button"
+												id="continue-response-button"
+												class="{isLastMessage
+													? 'visible'
+													: 'invisible group-hover:visible'} p-1.5 hover:bg-black/5 dark:hover:bg-white/5 rounded-lg dark:hover:text-white hover:text-black transition regenerate-response-button"
+												on:click={() => {
+													deleteMessageHandler();
+												}}
+											>
+												<svg
+													xmlns="http://www.w3.org/2000/svg"
+													fill="none"
+													viewBox="0 0 24 24"
+													stroke-width="2"
+													stroke="currentColor"
+													class="w-4 h-4"
+												>
+													<path
+														stroke-linecap="round"
+														stroke-linejoin="round"
+														d="m14.74 9-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 0 1-2.244 2.077H8.084a2.25 2.25 0 0 1-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 0 0-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 0 1 3.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 0 0-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 0 0-7.5 0"
+													/>
+												</svg>
+											</button>
+										</Tooltip>
+									{/if}
+
 									{#if isLastMessage}
 									{#if isLastMessage}
 										{#each model?.actions ?? [] as action}
 										{#each model?.actions ?? [] as action}
 											<Tooltip content={action.name} placement="bottom">
 											<Tooltip content={action.name} placement="bottom">

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

@@ -13,10 +13,10 @@
 	import FileItem from '$lib/components/common/FileItem.svelte';
 	import FileItem from '$lib/components/common/FileItem.svelte';
 	import Markdown from './Markdown.svelte';
 	import Markdown from './Markdown.svelte';
 	import Image from '$lib/components/common/Image.svelte';
 	import Image from '$lib/components/common/Image.svelte';
-    import localizedFormat from 'dayjs/plugin/localizedFormat';
+	import localizedFormat from 'dayjs/plugin/localizedFormat';
 
 
 	const i18n = getContext('i18n');
 	const i18n = getContext('i18n');
-    dayjs.extend(localizedFormat);
+	dayjs.extend(localizedFormat);
 
 
 	export let user;
 	export let user;
 
 

+ 3 - 0
src/lib/components/chat/Placeholder.svelte

@@ -35,6 +35,7 @@
 
 
 	export let selectedToolIds = [];
 	export let selectedToolIds = [];
 	export let imageGenerationEnabled = false;
 	export let imageGenerationEnabled = false;
+	export let codeInterpreterEnabled = false;
 	export let webSearchEnabled = false;
 	export let webSearchEnabled = false;
 
 
 	let models = [];
 	let models = [];
@@ -196,6 +197,7 @@
 					bind:autoScroll
 					bind:autoScroll
 					bind:selectedToolIds
 					bind:selectedToolIds
 					bind:imageGenerationEnabled
 					bind:imageGenerationEnabled
+					bind:codeInterpreterEnabled
 					bind:webSearchEnabled
 					bind:webSearchEnabled
 					bind:atSelectedModel
 					bind:atSelectedModel
 					{transparentBackground}
 					{transparentBackground}
@@ -218,6 +220,7 @@
 				suggestionPrompts={models[selectedModelIdx]?.info?.meta?.suggestion_prompts ??
 				suggestionPrompts={models[selectedModelIdx]?.info?.meta?.suggestion_prompts ??
 					$config?.default_prompt_suggestions ??
 					$config?.default_prompt_suggestions ??
 					[]}
 					[]}
+				inputValue={prompt}
 				on:select={(e) => {
 				on:select={(e) => {
 					selectSuggestionPrompt(e.detail);
 					selectSuggestionPrompt(e.detail);
 				}}
 				}}

+ 30 - 0
src/lib/components/chat/Settings/Advanced/AdvancedParams.svelte

@@ -12,6 +12,7 @@
 	export let params = {
 	export let params = {
 		// Advanced
 		// Advanced
 		stream_response: null, // Set stream responses for this model individually
 		stream_response: null, // Set stream responses for this model individually
+		function_calling: null,
 		seed: null,
 		seed: null,
 		stop: null,
 		stop: null,
 		temperature: null,
 		temperature: null,
@@ -81,6 +82,35 @@
 		</Tooltip>
 		</Tooltip>
 	</div>
 	</div>
 
 
+	<div>
+		<Tooltip
+			content={$i18n.t(
+				'Default mode works with a wider range of models by calling tools once before execution. Native mode leverages the model’s built-in tool-calling capabilities, but requires the model to inherently support this feature.'
+			)}
+			placement="top-start"
+			className="inline-tooltip"
+		>
+			<div class=" py-0.5 flex w-full justify-between">
+				<div class=" self-center text-xs font-medium">
+					{$i18n.t('Function Calling')}
+				</div>
+				<button
+					class="p-1 px-3 text-xs flex rounded transition"
+					on:click={() => {
+						params.function_calling = (params?.function_calling ?? null) === null ? 'native' : null;
+					}}
+					type="button"
+				>
+					{#if params.function_calling === 'native'}
+						<span class="ml-2 self-center">{$i18n.t('Native')}</span>
+					{:else}
+						<span class="ml-2 self-center">{$i18n.t('Default')}</span>
+					{/if}
+				</button>
+			</div>
+		</Tooltip>
+	</div>
+
 	<div class=" py-0.5 w-full justify-between">
 	<div class=" py-0.5 w-full justify-between">
 		<Tooltip
 		<Tooltip
 			content={$i18n.t(
 			content={$i18n.t(

+ 1 - 1
src/lib/components/chat/Settings/Audio.svelte

@@ -39,7 +39,7 @@
 			}, 100);
 			}, 100);
 		} else {
 		} else {
 			const res = await _getVoices(localStorage.token).catch((e) => {
 			const res = await _getVoices(localStorage.token).catch((e) => {
-				toast.error(e);
+				toast.error(`${e}`);
 			});
 			});
 
 
 			if (res) {
 			if (res) {

+ 1 - 0
src/lib/components/chat/Settings/General.svelte

@@ -46,6 +46,7 @@
 	let params = {
 	let params = {
 		// Advanced
 		// Advanced
 		stream_response: null,
 		stream_response: null,
+		function_calling: null,
 		seed: null,
 		seed: null,
 		temperature: null,
 		temperature: null,
 		frequency_penalty: null,
 		frequency_penalty: null,

+ 2 - 2
src/lib/components/chat/Settings/Personalization/ManageModal.svelte

@@ -11,10 +11,10 @@
 	import Tooltip from '$lib/components/common/Tooltip.svelte';
 	import Tooltip from '$lib/components/common/Tooltip.svelte';
 	import { error } from '@sveltejs/kit';
 	import { error } from '@sveltejs/kit';
 	import EditMemoryModal from './EditMemoryModal.svelte';
 	import EditMemoryModal from './EditMemoryModal.svelte';
-    import localizedFormat from 'dayjs/plugin/localizedFormat';
+	import localizedFormat from 'dayjs/plugin/localizedFormat';
 
 
 	const i18n = getContext('i18n');
 	const i18n = getContext('i18n');
-    dayjs.extend(localizedFormat);
+	dayjs.extend(localizedFormat);
 
 
 	export let show = false;
 	export let show = false;
 
 

+ 114 - 36
src/lib/components/chat/Suggestions.svelte

@@ -1,53 +1,131 @@
 <script lang="ts">
 <script lang="ts">
+	import Fuse from 'fuse.js';
 	import Bolt from '$lib/components/icons/Bolt.svelte';
 	import Bolt from '$lib/components/icons/Bolt.svelte';
 	import { onMount, getContext, createEventDispatcher } from 'svelte';
 	import { onMount, getContext, createEventDispatcher } from 'svelte';
+	import { WEBUI_NAME } from '$lib/stores';
+	import { WEBUI_VERSION } from '$lib/constants';
 
 
 	const i18n = getContext('i18n');
 	const i18n = getContext('i18n');
 	const dispatch = createEventDispatcher();
 	const dispatch = createEventDispatcher();
 
 
 	export let suggestionPrompts = [];
 	export let suggestionPrompts = [];
 	export let className = '';
 	export let className = '';
+	export let inputValue = '';
 
 
-	let prompts = [];
+	let sortedPrompts = [];
 
 
-	$: prompts = (suggestionPrompts ?? [])
-		.reduce((acc, current) => [...acc, ...[current]], [])
-		.sort(() => Math.random() - 0.5);
+	const fuseOptions = {
+		keys: ['content', 'title'],
+		threshold: 0.5
+	};
+
+	let fuse;
+	let filteredPrompts = [];
+
+	// Initialize Fuse
+	$: fuse = new Fuse(sortedPrompts, fuseOptions);
+
+	// Update the filteredPrompts if inputValue changes
+	// Only increase version if something wirklich geändert hat
+	$: getFilteredPrompts(inputValue);
+
+	// Helper function to check if arrays are the same
+	// (based on unique IDs oder content)
+	function arraysEqual(a, b) {
+		if (a.length !== b.length) return false;
+		for (let i = 0; i < a.length; i++) {
+			if ((a[i].id ?? a[i].content) !== (b[i].id ?? b[i].content)) {
+				return false;
+			}
+		}
+		return true;
+	}
+
+	const getFilteredPrompts = (inputValue) => {
+		const newFilteredPrompts = inputValue.trim()
+			? fuse.search(inputValue.trim()).map((result) => result.item)
+			: sortedPrompts;
+
+		// Compare with the oldFilteredPrompts
+		// If there's a difference, update array + version
+		if (!arraysEqual(filteredPrompts, newFilteredPrompts)) {
+			filteredPrompts = newFilteredPrompts;
+		}
+	};
+
+	$: if (suggestionPrompts) {
+		sortedPrompts = [...(suggestionPrompts ?? [])].sort(() => Math.random() - 0.5);
+		getFilteredPrompts(inputValue);
+	}
 </script>
 </script>
 
 
-{#if prompts.length > 0}
-	<div class="mb-1 flex gap-1 text-sm font-medium items-center text-gray-400 dark:text-gray-600">
+<div class="mb-1 flex gap-1 text-xs font-medium items-center text-gray-400 dark:text-gray-600">
+	{#if filteredPrompts.length > 0}
 		<Bolt />
 		<Bolt />
 		{$i18n.t('Suggested')}
 		{$i18n.t('Suggested')}
-	</div>
-{/if}
-
-<div class=" h-40 max-h-full overflow-auto scrollbar-none {className}">
-	{#each prompts as prompt, promptIdx}
-		<button
-			class="flex flex-col flex-1 shrink-0 w-full justify-between px-3 py-2 rounded-xl bg-transparent hover:bg-black/5 dark:hover:bg-white/5 transition group"
-			on:click={() => {
-				dispatch('select', prompt.content);
-			}}
+	{:else}
+		<!-- Keine Vorschläge -->
+
+		<div
+			class="flex w-full text-center items-center justify-center self-start text-gray-400 dark:text-gray-600"
 		>
 		>
-			<div class="flex flex-col text-left">
-				{#if prompt.title && prompt.title[0] !== ''}
-					<div
-						class="  font-medium dark:text-gray-300 dark:group-hover:text-gray-200 transition line-clamp-1"
-					>
-						{prompt.title[0]}
-					</div>
-					<div class="text-xs text-gray-500 font-normal line-clamp-1">{prompt.title[1]}</div>
-				{:else}
-					<div
-						class="  font-medium dark:text-gray-300 dark:group-hover:text-gray-200 transition line-clamp-1"
-					>
-						{prompt.content}
-					</div>
-
-					<div class="text-xs text-gray-500 font-normal line-clamp-1">Prompt</div>
-				{/if}
-			</div>
-		</button>
-	{/each}
+			{$WEBUI_NAME} ‧ v{WEBUI_VERSION}
+		</div>
+	{/if}
+</div>
+
+<div class="h-40 overflow-auto scrollbar-none {className} items-start">
+	{#if filteredPrompts.length > 0}
+		{#each filteredPrompts as prompt, idx (prompt.id || prompt.content)}
+			<button
+				class="waterfall flex flex-col flex-1 shrink-0 w-full justify-between
+				       px-3 py-2 rounded-xl bg-transparent hover:bg-black/5
+				       dark:hover:bg-white/5 transition group"
+				style="animation-delay: {idx * 60}ms"
+				on:click={() => dispatch('select', prompt.content)}
+			>
+				<div class="flex flex-col text-left">
+					{#if prompt.title && prompt.title[0] !== ''}
+						<div
+							class="font-medium dark:text-gray-300 dark:group-hover:text-gray-200 transition line-clamp-1"
+						>
+							{prompt.title[0]}
+						</div>
+						<div class="text-xs text-gray-500 font-normal line-clamp-1">
+							{prompt.title[1]}
+						</div>
+					{:else}
+						<div
+							class="font-medium dark:text-gray-300 dark:group-hover:text-gray-200 transition line-clamp-1"
+						>
+							{prompt.content}
+						</div>
+						<div class="text-xs text-gray-500 font-normal line-clamp-1">{$i18n.t('Prompt')}</div>
+					{/if}
+				</div>
+			</button>
+		{/each}
+	{/if}
 </div>
 </div>
+
+<style>
+	/* Waterfall animation for the suggestions */
+	@keyframes fadeInUp {
+		0% {
+			opacity: 0;
+			transform: translateY(20px);
+		}
+		100% {
+			opacity: 1;
+			transform: translateY(0);
+		}
+	}
+
+	.waterfall {
+		opacity: 0;
+		animation-name: fadeInUp;
+		animation-duration: 200ms;
+		animation-fill-mode: forwards;
+		animation-timing-function: ease;
+	}
+</style>

+ 6 - 0
src/lib/components/common/Collapsible.svelte

@@ -80,6 +80,12 @@
 						{:else}
 						{:else}
 							{$i18n.t('Thinking...')}
 							{$i18n.t('Thinking...')}
 						{/if}
 						{/if}
+					{:else if attributes?.type === 'code_interpreter'}
+						{#if attributes?.done === 'true'}
+							{$i18n.t('Analyzed')}
+						{:else}
+							{$i18n.t('Analyzing...')}
+						{/if}
 					{:else}
 					{:else}
 						{title}
 						{title}
 					{/if}
 					{/if}

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

@@ -119,7 +119,7 @@
 	{#if dismissible}
 	{#if dismissible}
 		<div class=" absolute -top-1 -right-1">
 		<div class=" absolute -top-1 -right-1">
 			<button
 			<button
-				class=" bg-gray-400 text-white border border-white rounded-full group-hover:visible invisible transition"
+				class=" bg-white text-black border border-white rounded-full group-hover:visible invisible transition"
 				type="button"
 				type="button"
 				on:click|stopPropagation={() => {
 				on:click|stopPropagation={() => {
 					dispatch('dismiss');
 					dispatch('dismiss');

+ 32 - 51
src/lib/components/common/Textarea.svelte

@@ -3,68 +3,49 @@
 
 
 	export let value = '';
 	export let value = '';
 	export let placeholder = '';
 	export let placeholder = '';
+	export let rows = 1;
+	export let required = false;
 	export let className =
 	export let className =
-		'w-full rounded-lg px-3 py-2 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none resize-none h-full';
-
-	export let onKeydown: Function = () => {};
+		'w-full rounded-lg px-3 py-2 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none  h-full';
 
 
 	let textareaElement;
 	let textareaElement;
 
 
-	$: if (textareaElement) {
-		if (textareaElement.innerText !== value && value !== '') {
-			textareaElement.innerText = value ?? '';
-		}
-	}
-
 	// Adjust height on mount and after setting the element.
 	// Adjust height on mount and after setting the element.
 	onMount(async () => {
 	onMount(async () => {
 		await tick();
 		await tick();
+		resize();
+
+		requestAnimationFrame(() => {
+			// setInterveal to cehck until textareaElement is set
+			const interval = setInterval(() => {
+				if (textareaElement) {
+					clearInterval(interval);
+					resize();
+				}
+			}, 100);
+		});
 	});
 	});
 
 
-	// Handle paste event to ensure only plaintext is pasted
-	function handlePaste(event: ClipboardEvent) {
-		event.preventDefault(); // Prevent the default paste action
-		const clipboardData = event.clipboardData?.getData('text/plain'); // Get plaintext from clipboard
-
-		// Insert plaintext into the textarea
-		document.execCommand('insertText', false, clipboardData);
-	}
+	const resize = () => {
+		if (textareaElement) {
+			textareaElement.style.height = '';
+			textareaElement.style.height = `${textareaElement.scrollHeight}px`;
+		}
+	};
 </script>
 </script>
 
 
-<div
-	contenteditable="true"
+<textarea
 	bind:this={textareaElement}
 	bind:this={textareaElement}
-	class="{className} whitespace-pre-wrap relative {value
-		? !value.trim()
-			? 'placeholder'
-			: ''
-		: 'placeholder'}"
-	style="field-sizing: content; -moz-user-select: text !important;"
-	on:input={() => {
-		const text = textareaElement.innerText;
-		if (text === '\n') {
-			value = '';
-			return;
-		}
-
-		value = text;
+	bind:value
+	{placeholder}
+	class={className}
+	style="field-sizing: content;"
+	{rows}
+	{required}
+	on:input={(e) => {
+		resize();
+	}}
+	on:focus={() => {
+		resize();
 	}}
 	}}
-	on:paste={handlePaste}
-	on:keydown={onKeydown}
-	data-placeholder={placeholder}
 />
 />
-
-<style>
-	.placeholder::before {
-		/* absolute */
-		position: absolute;
-		content: attr(data-placeholder);
-		color: #adb5bd;
-		overflow: hidden;
-		display: -webkit-box;
-		-webkit-box-orient: vertical;
-		-webkit-line-clamp: 1;
-		pointer-events: none;
-		touch-action: none;
-	}
-</style>

+ 19 - 0
src/lib/components/icons/CommandLine.svelte

@@ -0,0 +1,19 @@
+<script lang="ts">
+	export let className = 'w-4 h-4';
+	export let strokeWidth = '1.5';
+</script>
+
+<svg
+	xmlns="http://www.w3.org/2000/svg"
+	fill="none"
+	viewBox="0 0 24 24"
+	stroke-width={strokeWidth}
+	stroke="currentColor"
+	class={className}
+>
+	<path
+		stroke-linecap="round"
+		stroke-linejoin="round"
+		d="m6.75 7.5 3 2.25-3 2.25m4.5 0h3m-9 8.25h13.5A2.25 2.25 0 0 0 21 18V6a2.25 2.25 0 0 0-2.25-2.25H5.25A2.25 2.25 0 0 0 3 6v12a2.25 2.25 0 0 0 2.25 2.25Z"
+	/>
+</svg>

+ 11 - 0
src/lib/components/icons/CommandLineSolid.svelte

@@ -0,0 +1,11 @@
+<script lang="ts">
+	export let className = 'size-4';
+</script>
+
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class={className}>
+	<path
+		fill-rule="evenodd"
+		d="M2.25 6a3 3 0 0 1 3-3h13.5a3 3 0 0 1 3 3v12a3 3 0 0 1-3 3H5.25a3 3 0 0 1-3-3V6Zm3.97.97a.75.75 0 0 1 1.06 0l2.25 2.25a.75.75 0 0 1 0 1.06l-2.25 2.25a.75.75 0 0 1-1.06-1.06l1.72-1.72-1.72-1.72a.75.75 0 0 1 0-1.06Zm4.28 4.28a.75.75 0 0 0 0 1.5h3a.75.75 0 0 0 0-1.5h-3Z"
+		clip-rule="evenodd"
+	/>
+</svg>

+ 21 - 0
src/lib/components/icons/Photo.svelte

@@ -0,0 +1,21 @@
+<script lang="ts">
+	export let className = 'size-4';
+	export let strokeWidth = '1.5';
+</script>
+
+<svg
+	class={className}
+	stroke-width={strokeWidth}
+	aria-hidden="true"
+	xmlns="http://www.w3.org/2000/svg"
+	fill="none"
+	viewBox="0 0 24 24"
+>
+	<path
+		stroke="currentColor"
+		stroke-linecap="round"
+		stroke-linejoin="round"
+		stroke-width="2"
+		d="m3 16 5-7 6 6.5m6.5 2.5L16 13l-4.286 6M14 10h.01M4 19h16a1 1 0 0 0 1-1V6a1 1 0 0 0-1-1H4a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1Z"
+	/>
+</svg>

+ 2 - 2
src/lib/components/layout/Help.svelte

@@ -10,7 +10,7 @@
 	let showShortcuts = false;
 	let showShortcuts = false;
 </script>
 </script>
 
 
-<div class=" hidden lg:flex fixed bottom-0 right-0 px-2 py-2 z-20">
+<div class=" hidden lg:flex fixed bottom-0 right-0 px-1 py-1 z-20">
 	<button
 	<button
 		id="show-shortcuts-button"
 		id="show-shortcuts-button"
 		class="hidden"
 		class="hidden"
@@ -29,7 +29,7 @@
 	>
 	>
 		<Tooltip content={$i18n.t('Help')} placement="left">
 		<Tooltip content={$i18n.t('Help')} placement="left">
 			<button
 			<button
-				class="text-gray-600 dark:text-gray-300 bg-gray-300/20 size-5 flex items-center justify-center text-[0.7rem] rounded-full"
+				class="text-gray-600 dark:text-gray-300 bg-gray-300/20 size-4 flex items-center justify-center text-[0.7rem] rounded-full"
 			>
 			>
 				?
 				?
 			</button>
 			</button>

+ 2 - 2
src/lib/components/layout/Sidebar/ArchivedChatsModal.svelte

@@ -4,9 +4,9 @@
 	import { toast } from 'svelte-sonner';
 	import { toast } from 'svelte-sonner';
 	import dayjs from 'dayjs';
 	import dayjs from 'dayjs';
 	import { getContext, createEventDispatcher } from 'svelte';
 	import { getContext, createEventDispatcher } from 'svelte';
-    import localizedFormat from 'dayjs/plugin/localizedFormat';
+	import localizedFormat from 'dayjs/plugin/localizedFormat';
 
 
-    dayjs.extend(localizedFormat);
+	dayjs.extend(localizedFormat);
 
 
 	const dispatch = createEventDispatcher();
 	const dispatch = createEventDispatcher();
 
 

+ 7 - 1
src/lib/components/layout/Sidebar/ChatItem.svelte

@@ -87,7 +87,13 @@
 	};
 	};
 
 
 	const cloneChatHandler = async (id) => {
 	const cloneChatHandler = async (id) => {
-		const res = await cloneChatById(localStorage.token, id).catch((error) => {
+		const res = await cloneChatById(
+			localStorage.token,
+			id,
+			$i18n.t('Clone of {{TITLE}}', {
+				TITLE: title
+			})
+		).catch((error) => {
 			toast.error(`${error}`);
 			toast.error(`${error}`);
 			return null;
 			return null;
 		});
 		});

+ 26 - 1
src/lib/components/playground/Chat.svelte

@@ -33,6 +33,7 @@
 	let loading = false;
 	let loading = false;
 	let stopResponseFlag = false;
 	let stopResponseFlag = false;
 
 
+	let systemTextareaElement: HTMLTextAreaElement;
 	let messagesContainerElement: HTMLDivElement;
 	let messagesContainerElement: HTMLDivElement;
 
 
 	let showSystem = false;
 	let showSystem = false;
@@ -58,8 +59,29 @@
 		console.log('stopResponse');
 		console.log('stopResponse');
 	};
 	};
 
 
+	const resizeSystemTextarea = async () => {
+		await tick();
+		if (systemTextareaElement) {
+			systemTextareaElement.style.height = '';
+			systemTextareaElement.style.height = Math.min(systemTextareaElement.scrollHeight, 555) + 'px';
+		}
+	};
+
+	$: if (showSystem) {
+		resizeSystemTextarea();
+	}
+
 	const chatCompletionHandler = async () => {
 	const chatCompletionHandler = async () => {
+		if (selectedModelId === '') {
+			toast.error($i18n.t('Please select a model.'));
+			return;
+		}
+
 		const model = $models.find((model) => model.id === selectedModelId);
 		const model = $models.find((model) => model.id === selectedModelId);
+		if (!model) {
+			selectedModelId = '';
+			return;
+		}
 
 
 		const [res, controller] = await chatCompletion(
 		const [res, controller] = await chatCompletion(
 			localStorage.token,
 			localStorage.token,
@@ -258,10 +280,13 @@
 					<div slot="content">
 					<div slot="content">
 						<div class="pt-1 px-1.5">
 						<div class="pt-1 px-1.5">
 							<textarea
 							<textarea
-								id="system-textarea"
+								bind:this={systemTextareaElement}
 								class="w-full h-full bg-transparent resize-none outline-none text-sm"
 								class="w-full h-full bg-transparent resize-none outline-none text-sm"
 								bind:value={system}
 								bind:value={system}
 								placeholder={$i18n.t("You're a helpful assistant.")}
 								placeholder={$i18n.t("You're a helpful assistant.")}
+								on:input={() => {
+									resizeSystemTextarea();
+								}}
 								rows="4"
 								rows="4"
 							/>
 							/>
 						</div>
 						</div>

+ 76 - 0
src/lib/components/playground/Chat/Message.svelte

@@ -0,0 +1,76 @@
+<script lang="ts">
+	import { onMount, getContext } from 'svelte';
+
+	const i18n = getContext('i18n');
+
+	export let message;
+	export let idx;
+
+	export let onDelete;
+
+	let textAreaElement: HTMLTextAreaElement;
+
+	onMount(() => {
+		textAreaElement.style.height = '';
+		textAreaElement.style.height = textAreaElement.scrollHeight + 'px';
+	});
+</script>
+
+<div class="flex gap-2 group">
+	<div class="flex items-start pt-1">
+		<div
+			class="px-2 py-1 text-sm font-semibold uppercase min-w-[6rem] text-left rounded-lg transition"
+		>
+			{$i18n.t(message.role)}
+		</div>
+	</div>
+
+	<div class="flex-1">
+		<!-- $i18n.t('a user') -->
+		<!-- $i18n.t('an assistant') -->
+		<textarea
+			id="{message.role}-{idx}-textarea"
+			bind:this={textAreaElement}
+			class="w-full bg-transparent outline-none rounded-lg p-2 text-sm resize-none overflow-hidden"
+			placeholder={$i18n.t(`Enter {{role}} message here`, {
+				role: message.role === 'user' ? $i18n.t('a user') : $i18n.t('an assistant')
+			})}
+			rows="1"
+			on:input={(e) => {
+				textAreaElement.style.height = '';
+				textAreaElement.style.height = textAreaElement.scrollHeight + 'px';
+			}}
+			on:focus={(e) => {
+				textAreaElement.style.height = '';
+				textAreaElement.style.height = textAreaElement.scrollHeight + 'px';
+
+				// e.target.style.height = Math.min(e.target.scrollHeight, 200) + 'px';
+			}}
+			bind:value={message.content}
+		/>
+	</div>
+
+	<div class=" pt-1">
+		<button
+			class=" group-hover:text-gray-500 dark:text-gray-900 dark:hover:text-gray-300 transition"
+			on:click={() => {
+				onDelete();
+			}}
+		>
+			<svg
+				xmlns="http://www.w3.org/2000/svg"
+				fill="none"
+				viewBox="0 0 24 24"
+				stroke-width="2"
+				stroke="currentColor"
+				class="w-5 h-5"
+			>
+				<path
+					stroke-linecap="round"
+					stroke-linejoin="round"
+					d="M15 12H9m12 0a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z"
+				/>
+			</svg>
+		</button>
+	</div>
+</div>

+ 8 - 65
src/lib/components/playground/Chat/Messages.svelte

@@ -1,77 +1,20 @@
 <script lang="ts">
 <script lang="ts">
 	import { onMount, getContext } from 'svelte';
 	import { onMount, getContext } from 'svelte';
+	import Message from './Message.svelte';
 
 
 	const i18n = getContext('i18n');
 	const i18n = getContext('i18n');
 
 
 	export let messages = [];
 	export let messages = [];
-	let textAreaElement: HTMLTextAreaElement;
-	onMount(() => {
-		messages.forEach((message, idx) => {
-			textAreaElement.style.height = '';
-			textAreaElement.style.height = textAreaElement.scrollHeight + 'px';
-		});
-	});
 </script>
 </script>
 
 
 <div class="py-3 space-y-3">
 <div class="py-3 space-y-3">
 	{#each messages as message, idx}
 	{#each messages as message, idx}
-		<div class="flex gap-2 group">
-			<div class="flex items-start pt-1">
-				<div
-					class="px-2 py-1 text-sm font-semibold uppercase min-w-[6rem] text-left rounded-lg transition"
-				>
-					{$i18n.t(message.role)}
-				</div>
-			</div>
-
-			<div class="flex-1">
-				<!-- $i18n.t('a user') -->
-				<!-- $i18n.t('an assistant') -->
-				<textarea
-					id="{message.role}-{idx}-textarea"
-					bind:this={textAreaElement}
-					class="w-full bg-transparent outline-none rounded-lg p-2 text-sm resize-none overflow-hidden"
-					placeholder={$i18n.t(`Enter {{role}} message here`, {
-						role: message.role === 'user' ? $i18n.t('a user') : $i18n.t('an assistant')
-					})}
-					rows="1"
-					on:input={(e) => {
-						textAreaElement.style.height = '';
-						textAreaElement.style.height = textAreaElement.scrollHeight + 'px';
-					}}
-					on:focus={(e) => {
-						textAreaElement.style.height = '';
-						textAreaElement.style.height = textAreaElement.scrollHeight + 'px';
-
-						// e.target.style.height = Math.min(e.target.scrollHeight, 200) + 'px';
-					}}
-					bind:value={message.content}
-				/>
-			</div>
-
-			<div class=" pt-1">
-				<button
-					class=" group-hover:text-gray-500 dark:text-gray-900 dark:hover:text-gray-300 transition"
-					on:click={() => {
-						messages = messages.filter((message, messageIdx) => messageIdx !== idx);
-					}}
-				>
-					<svg
-						xmlns="http://www.w3.org/2000/svg"
-						fill="none"
-						viewBox="0 0 24 24"
-						stroke-width="2"
-						stroke="currentColor"
-						class="w-5 h-5"
-					>
-						<path
-							stroke-linecap="round"
-							stroke-linejoin="round"
-							d="M15 12H9m12 0a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z"
-						/>
-					</svg>
-				</button>
-			</div>
-		</div>
+		<Message
+			{message}
+			{idx}
+			onDelete={() => {
+				messages = messages.filter((message, messageIdx) => messageIdx !== idx);
+			}}
+		/>
 	{/each}
 	{/each}
 </div>
 </div>

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

@@ -54,7 +54,7 @@
 
 
 	const deleteHandler = async (item) => {
 	const deleteHandler = async (item) => {
 		const res = await deleteKnowledgeById(localStorage.token, item.id).catch((e) => {
 		const res = await deleteKnowledgeById(localStorage.token, item.id).catch((e) => {
-			toast.error(e);
+			toast.error(`${e}`);
 		});
 		});
 
 
 		if (res) {
 		if (res) {

+ 2 - 5
src/lib/components/workspace/Knowledge/CreateKnowledgeBase.svelte

@@ -31,7 +31,7 @@
 			description,
 			description,
 			accessControl
 			accessControl
 		).catch((e) => {
 		).catch((e) => {
-			toast.error(e);
+			toast.error(`${e}`);
 		});
 		});
 
 
 		if (res) {
 		if (res) {
@@ -112,10 +112,7 @@
 
 
 		<div class="mt-2">
 		<div class="mt-2">
 			<div class="px-3 py-2 bg-gray-50 dark:bg-gray-950 rounded-lg">
 			<div class="px-3 py-2 bg-gray-50 dark:bg-gray-950 rounded-lg">
-				<AccessControl 
-				bind:accessControl 
-				accessRoles={['read', 'write']}
-				/>
+				<AccessControl bind:accessControl accessRoles={['read', 'write']} />
 			</div>
 			</div>
 		</div>
 		</div>
 
 

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

@@ -149,7 +149,7 @@
 
 
 		try {
 		try {
 			const uploadedFile = await uploadFile(localStorage.token, file).catch((e) => {
 			const uploadedFile = await uploadFile(localStorage.token, file).catch((e) => {
-				toast.error(e);
+				toast.error(`${e}`);
 				return null;
 				return null;
 			});
 			});
 
 
@@ -169,7 +169,7 @@
 				toast.error($i18n.t('Failed to upload file.'));
 				toast.error($i18n.t('Failed to upload file.'));
 			}
 			}
 		} catch (e) {
 		} catch (e) {
-			toast.error(e);
+			toast.error(`${e}`);
 		}
 		}
 	};
 	};
 
 
@@ -339,7 +339,7 @@
 	const syncDirectoryHandler = async () => {
 	const syncDirectoryHandler = async () => {
 		if ((knowledge?.files ?? []).length > 0) {
 		if ((knowledge?.files ?? []).length > 0) {
 			const res = await resetKnowledgeById(localStorage.token, id).catch((e) => {
 			const res = await resetKnowledgeById(localStorage.token, id).catch((e) => {
-				toast.error(e);
+				toast.error(`${e}`);
 			});
 			});
 
 
 			if (res) {
 			if (res) {
@@ -357,7 +357,7 @@
 	const addFileHandler = async (fileId) => {
 	const addFileHandler = async (fileId) => {
 		const updatedKnowledge = await addFileToKnowledgeById(localStorage.token, id, fileId).catch(
 		const updatedKnowledge = await addFileToKnowledgeById(localStorage.token, id, fileId).catch(
 			(e) => {
 			(e) => {
-				toast.error(e);
+				toast.error(`${e}`);
 				return null;
 				return null;
 			}
 			}
 		);
 		);
@@ -386,7 +386,7 @@
 			}
 			}
 		} catch (e) {
 		} catch (e) {
 			console.error('Error in deleteFileHandler:', e);
 			console.error('Error in deleteFileHandler:', e);
-			toast.error(e);
+			toast.error(`${e}`);
 		}
 		}
 	};
 	};
 
 
@@ -395,7 +395,7 @@
 		const content = selectedFile.data.content;
 		const content = selectedFile.data.content;
 
 
 		const res = updateFileDataContentById(localStorage.token, fileId, content).catch((e) => {
 		const res = updateFileDataContentById(localStorage.token, fileId, content).catch((e) => {
-			toast.error(e);
+			toast.error(`${e}`);
 		});
 		});
 
 
 		const updatedKnowledge = await updateFileFromKnowledgeById(
 		const updatedKnowledge = await updateFileFromKnowledgeById(
@@ -403,7 +403,7 @@
 			id,
 			id,
 			fileId
 			fileId
 		).catch((e) => {
 		).catch((e) => {
-			toast.error(e);
+			toast.error(`${e}`);
 		});
 		});
 
 
 		if (res && updatedKnowledge) {
 		if (res && updatedKnowledge) {
@@ -430,7 +430,7 @@
 				description: knowledge.description,
 				description: knowledge.description,
 				access_control: knowledge.access_control
 				access_control: knowledge.access_control
 			}).catch((e) => {
 			}).catch((e) => {
-				toast.error(e);
+				toast.error(`${e}`);
 			});
 			});
 
 
 			if (res) {
 			if (res) {
@@ -522,7 +522,7 @@
 		id = $page.params.id;
 		id = $page.params.id;
 
 
 		const res = await getKnowledgeById(localStorage.token, id).catch((e) => {
 		const res = await getKnowledgeById(localStorage.token, id).catch((e) => {
-			toast.error(e);
+			toast.error(`${e}`);
 			return null;
 			return null;
 		});
 		});
 
 

+ 1 - 1
src/lib/components/workspace/Models.svelte

@@ -60,7 +60,7 @@
 
 
 	const deleteModelHandler = async (model) => {
 	const deleteModelHandler = async (model) => {
 		const res = await deleteModelById(localStorage.token, model.id).catch((e) => {
 		const res = await deleteModelById(localStorage.token, model.id).catch((e) => {
-			toast.error(e);
+			toast.error(`${e}`);
 			return null;
 			return null;
 		});
 		});
 
 

+ 1 - 4
src/lib/components/workspace/Models/ModelEditor.svelte

@@ -531,10 +531,7 @@
 
 
 					<div class="my-2">
 					<div class="my-2">
 						<div class="px-3 py-2 bg-gray-50 dark:bg-gray-950 rounded-lg">
 						<div class="px-3 py-2 bg-gray-50 dark:bg-gray-950 rounded-lg">
-							<AccessControl 
-							bind:accessControl 
-							accessRoles={['read', 'write']}
-							/>
+							<AccessControl bind:accessControl accessRoles={['read', 'write']} />
 						</div>
 						</div>
 					</div>
 					</div>
 
 

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

@@ -136,7 +136,7 @@
 
 
 					<div class="flex justify-end pt-3 text-sm font-medium">
 					<div class="flex justify-end pt-3 text-sm font-medium">
 						<button
 						<button
-							class=" px-4 py-2 bg-emerald-700 hover:bg-emerald-800 text-gray-100 transition rounded-lg flex flex-row space-x-1 items-center {saving
+							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 {saving
 								? ' cursor-not-allowed'
 								? ' cursor-not-allowed'
 								: ''}"
 								: ''}"
 							type="submit"
 							type="submit"

+ 10 - 6
src/lib/i18n/locales/ar-BH/translation.json

@@ -66,6 +66,8 @@
 	"Always-On Web Search": "",
 	"Always-On Web Search": "",
 	"Amazing": "",
 	"Amazing": "",
 	"an assistant": "مساعد",
 	"an assistant": "مساعد",
+	"Analyzed": "",
+	"Analyzing...": "",
 	"and": "و",
 	"and": "و",
 	"and {{COUNT}} more": "",
 	"and {{COUNT}} more": "",
 	"and create a new shared link.": "و أنشئ رابط مشترك جديد.",
 	"and create a new shared link.": "و أنشئ رابط مشترك جديد.",
@@ -170,9 +172,11 @@
 	"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "",
 	"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "",
 	"Clone": "استنساخ",
 	"Clone": "استنساخ",
 	"Clone Chat": "",
 	"Clone Chat": "",
+	"Clone of {{TITLE}}": "",
 	"Close": "أغلق",
 	"Close": "أغلق",
 	"Code execution": "",
 	"Code execution": "",
 	"Code formatted successfully": "",
 	"Code formatted successfully": "",
+	"Code Interpreter": "",
 	"Collection": "مجموعة",
 	"Collection": "مجموعة",
 	"Color": "",
 	"Color": "",
 	"ComfyUI": "ComfyUI",
 	"ComfyUI": "ComfyUI",
@@ -398,6 +402,7 @@
 	"Example: ou=users,dc=foo,dc=example": "",
 	"Example: ou=users,dc=foo,dc=example": "",
 	"Example: sAMAccountName or uid or userPrincipalName": "",
 	"Example: sAMAccountName or uid or userPrincipalName": "",
 	"Exclude": "",
 	"Exclude": "",
+	"Execute code for analysis": "",
 	"Experimental": "تجريبي",
 	"Experimental": "تجريبي",
 	"Explore the cosmos": "",
 	"Explore the cosmos": "",
 	"Export": "تصدير",
 	"Export": "تصدير",
@@ -464,6 +469,7 @@
 	"Functions imported successfully": "",
 	"Functions imported successfully": "",
 	"General": "عام",
 	"General": "عام",
 	"General Settings": "الاعدادات العامة",
 	"General Settings": "الاعدادات العامة",
+	"Generate an image": "",
 	"Generate Image": "",
 	"Generate Image": "",
 	"Generating search query": "إنشاء استعلام بحث",
 	"Generating search query": "إنشاء استعلام بحث",
 	"Get started": "",
 	"Get started": "",
@@ -479,7 +485,6 @@
 	"Group Name": "",
 	"Group Name": "",
 	"Group updated successfully": "",
 	"Group updated successfully": "",
 	"Groups": "",
 	"Groups": "",
-	"h:mm a": "الساعة:الدقائق صباحا/مساء",
 	"Haptic Feedback": "",
 	"Haptic Feedback": "",
 	"has no conversations.": "ليس لديه محادثات.",
 	"has no conversations.": "ليس لديه محادثات.",
 	"Hello, {{name}}": " {{name}} مرحبا",
 	"Hello, {{name}}": " {{name}} مرحبا",
@@ -497,7 +502,6 @@
 	"Ignite curiosity": "",
 	"Ignite curiosity": "",
 	"Image": "",
 	"Image": "",
 	"Image Compression": "",
 	"Image Compression": "",
-	"Image generation": "",
 	"Image Generation": "",
 	"Image Generation": "",
 	"Image Generation (Experimental)": "توليد الصور (تجريبي)",
 	"Image Generation (Experimental)": "توليد الصور (تجريبي)",
 	"Image Generation Engine": "محرك توليد الصور",
 	"Image Generation Engine": "محرك توليد الصور",
@@ -596,9 +600,6 @@
 	"Mirostat": "Mirostat",
 	"Mirostat": "Mirostat",
 	"Mirostat Eta": "Mirostat Eta",
 	"Mirostat Eta": "Mirostat Eta",
 	"Mirostat Tau": "Mirostat Tau",
 	"Mirostat Tau": "Mirostat Tau",
-	"MMMM DD, YYYY": "MMMM DD, YYYY",
-	"MMMM DD, YYYY HH:mm": "MMMM DD, YYYY HH:mm",
-	"MMMM DD, YYYY hh:mm:ss A": "",
 	"Model": "",
 	"Model": "",
 	"Model '{{modelName}}' has been successfully downloaded.": "تم تحميل النموذج '{{modelName}}' بنجاح",
 	"Model '{{modelName}}' has been successfully downloaded.": "تم تحميل النموذج '{{modelName}}' بنجاح",
 	"Model '{{modelTag}}' is already in queue for downloading.": "النموذج '{{modelTag}}' موجود بالفعل في قائمة الانتظار للتحميل",
 	"Model '{{modelTag}}' is already in queue for downloading.": "النموذج '{{modelTag}}' موجود بالفعل في قائمة الانتظار للتحميل",
@@ -719,6 +720,7 @@
 	"Please enter a prompt": "",
 	"Please enter a prompt": "",
 	"Please fill in all fields.": "",
 	"Please fill in all fields.": "",
 	"Please select a model first.": "",
 	"Please select a model first.": "",
+	"Please select a model.": "",
 	"Please select a reason": "",
 	"Please select a reason": "",
 	"Port": "",
 	"Port": "",
 	"Positive attitude": "موقف ايجابي",
 	"Positive attitude": "موقف ايجابي",
@@ -802,7 +804,7 @@
 	"Search options": "",
 	"Search options": "",
 	"Search Prompts": "أبحث حث",
 	"Search Prompts": "أبحث حث",
 	"Search Result Count": "عدد نتائج البحث",
 	"Search Result Count": "عدد نتائج البحث",
-	"Search the web": "",
+	"Search the internet": "",
 	"Search Tools": "",
 	"Search Tools": "",
 	"SearchApi API Key": "",
 	"SearchApi API Key": "",
 	"SearchApi Engine": "",
 	"SearchApi Engine": "",
@@ -972,6 +974,7 @@
 	"Tools": "",
 	"Tools": "",
 	"Tools Access": "",
 	"Tools Access": "",
 	"Tools are a function calling system with arbitrary code execution": "",
 	"Tools are a function calling system with arbitrary code execution": "",
+	"Tools Function Calling Prompt": "",
 	"Tools have a function calling system that allows arbitrary code execution": "",
 	"Tools have a function calling system that allows arbitrary code execution": "",
 	"Tools have a function calling system that allows arbitrary code execution.": "",
 	"Tools have a function calling system that allows arbitrary code execution.": "",
 	"Top K": "Top K",
 	"Top K": "Top K",
@@ -1071,6 +1074,7 @@
 	"You can personalize your interactions with LLMs by adding memories through the 'Manage' button below, making them more helpful and tailored to you.": "",
 	"You can personalize your interactions with LLMs by adding memories through the 'Manage' button below, making them more helpful and tailored to you.": "",
 	"You cannot upload an empty file.": "",
 	"You cannot upload an empty file.": "",
 	"You do not have permission to access this feature.": "",
 	"You do not have permission to access this feature.": "",
+	"You do not have permission to upload files": "",
 	"You do not have permission to upload files.": "",
 	"You do not have permission to upload files.": "",
 	"You have no archived conversations.": "لا تملك محادثات محفوظه",
 	"You have no archived conversations.": "لا تملك محادثات محفوظه",
 	"You have shared this chat": "تم مشاركة هذه المحادثة",
 	"You have shared this chat": "تم مشاركة هذه المحادثة",

+ 10 - 6
src/lib/i18n/locales/bg-BG/translation.json

@@ -66,6 +66,8 @@
 	"Always-On Web Search": "",
 	"Always-On Web Search": "",
 	"Amazing": "",
 	"Amazing": "",
 	"an assistant": "асистент",
 	"an assistant": "асистент",
+	"Analyzed": "",
+	"Analyzing...": "",
 	"and": "и",
 	"and": "и",
 	"and {{COUNT}} more": "",
 	"and {{COUNT}} more": "",
 	"and create a new shared link.": "и създай нов общ линк.",
 	"and create a new shared link.": "и създай нов общ линк.",
@@ -170,9 +172,11 @@
 	"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "",
 	"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "",
 	"Clone": "Клонинг",
 	"Clone": "Клонинг",
 	"Clone Chat": "",
 	"Clone Chat": "",
+	"Clone of {{TITLE}}": "",
 	"Close": "Затвори",
 	"Close": "Затвори",
 	"Code execution": "",
 	"Code execution": "",
 	"Code formatted successfully": "",
 	"Code formatted successfully": "",
+	"Code Interpreter": "",
 	"Collection": "Колекция",
 	"Collection": "Колекция",
 	"Color": "",
 	"Color": "",
 	"ComfyUI": "ComfyUI",
 	"ComfyUI": "ComfyUI",
@@ -398,6 +402,7 @@
 	"Example: ou=users,dc=foo,dc=example": "",
 	"Example: ou=users,dc=foo,dc=example": "",
 	"Example: sAMAccountName or uid or userPrincipalName": "",
 	"Example: sAMAccountName or uid or userPrincipalName": "",
 	"Exclude": "",
 	"Exclude": "",
+	"Execute code for analysis": "",
 	"Experimental": "Експериментално",
 	"Experimental": "Експериментално",
 	"Explore the cosmos": "",
 	"Explore the cosmos": "",
 	"Export": "Износ",
 	"Export": "Износ",
@@ -464,6 +469,7 @@
 	"Functions imported successfully": "",
 	"Functions imported successfully": "",
 	"General": "Основни",
 	"General": "Основни",
 	"General Settings": "Основни Настройки",
 	"General Settings": "Основни Настройки",
+	"Generate an image": "",
 	"Generate Image": "",
 	"Generate Image": "",
 	"Generating search query": "Генериране на заявка за търсене",
 	"Generating search query": "Генериране на заявка за търсене",
 	"Get started": "",
 	"Get started": "",
@@ -479,7 +485,6 @@
 	"Group Name": "",
 	"Group Name": "",
 	"Group updated successfully": "",
 	"Group updated successfully": "",
 	"Groups": "",
 	"Groups": "",
-	"h:mm a": "h:mm a",
 	"Haptic Feedback": "",
 	"Haptic Feedback": "",
 	"has no conversations.": "няма разговори.",
 	"has no conversations.": "няма разговори.",
 	"Hello, {{name}}": "Здравей, {{name}}",
 	"Hello, {{name}}": "Здравей, {{name}}",
@@ -497,7 +502,6 @@
 	"Ignite curiosity": "",
 	"Ignite curiosity": "",
 	"Image": "",
 	"Image": "",
 	"Image Compression": "",
 	"Image Compression": "",
-	"Image generation": "",
 	"Image Generation": "",
 	"Image Generation": "",
 	"Image Generation (Experimental)": "Генерация на изображения (Експериментално)",
 	"Image Generation (Experimental)": "Генерация на изображения (Експериментално)",
 	"Image Generation Engine": "Двигател за генериране на изображения",
 	"Image Generation Engine": "Двигател за генериране на изображения",
@@ -596,9 +600,6 @@
 	"Mirostat": "Mirostat",
 	"Mirostat": "Mirostat",
 	"Mirostat Eta": "Mirostat Eta",
 	"Mirostat Eta": "Mirostat Eta",
 	"Mirostat Tau": "Mirostat Tau",
 	"Mirostat Tau": "Mirostat Tau",
-	"MMMM DD, YYYY": "MMMM DD, YYYY",
-	"MMMM DD, YYYY HH:mm": "MMMM DD, YYYY HH:mm",
-	"MMMM DD, YYYY hh:mm:ss A": "",
 	"Model": "",
 	"Model": "",
 	"Model '{{modelName}}' has been successfully downloaded.": "Моделът '{{modelName}}' беше успешно свален.",
 	"Model '{{modelName}}' has been successfully downloaded.": "Моделът '{{modelName}}' беше успешно свален.",
 	"Model '{{modelTag}}' is already in queue for downloading.": "Моделът '{{modelTag}}' е вече в очакване за сваляне.",
 	"Model '{{modelTag}}' is already in queue for downloading.": "Моделът '{{modelTag}}' е вече в очакване за сваляне.",
@@ -719,6 +720,7 @@
 	"Please enter a prompt": "",
 	"Please enter a prompt": "",
 	"Please fill in all fields.": "",
 	"Please fill in all fields.": "",
 	"Please select a model first.": "",
 	"Please select a model first.": "",
+	"Please select a model.": "",
 	"Please select a reason": "",
 	"Please select a reason": "",
 	"Port": "",
 	"Port": "",
 	"Positive attitude": "Позитивна ативност",
 	"Positive attitude": "Позитивна ативност",
@@ -802,7 +804,7 @@
 	"Search options": "",
 	"Search options": "",
 	"Search Prompts": "Търси Промптове",
 	"Search Prompts": "Търси Промптове",
 	"Search Result Count": "Брой резултати от търсенето",
 	"Search Result Count": "Брой резултати от търсенето",
-	"Search the web": "",
+	"Search the internet": "",
 	"Search Tools": "",
 	"Search Tools": "",
 	"SearchApi API Key": "",
 	"SearchApi API Key": "",
 	"SearchApi Engine": "",
 	"SearchApi Engine": "",
@@ -972,6 +974,7 @@
 	"Tools": "",
 	"Tools": "",
 	"Tools Access": "",
 	"Tools Access": "",
 	"Tools are a function calling system with arbitrary code execution": "",
 	"Tools are a function calling system with arbitrary code execution": "",
+	"Tools Function Calling Prompt": "",
 	"Tools have a function calling system that allows arbitrary code execution": "",
 	"Tools have a function calling system that allows arbitrary code execution": "",
 	"Tools have a function calling system that allows arbitrary code execution.": "",
 	"Tools have a function calling system that allows arbitrary code execution.": "",
 	"Top K": "Top K",
 	"Top K": "Top K",
@@ -1071,6 +1074,7 @@
 	"You can personalize your interactions with LLMs by adding memories through the 'Manage' button below, making them more helpful and tailored to you.": "",
 	"You can personalize your interactions with LLMs by adding memories through the 'Manage' button below, making them more helpful and tailored to you.": "",
 	"You cannot upload an empty file.": "",
 	"You cannot upload an empty file.": "",
 	"You do not have permission to access this feature.": "",
 	"You do not have permission to access this feature.": "",
+	"You do not have permission to upload files": "",
 	"You do not have permission to upload files.": "",
 	"You do not have permission to upload files.": "",
 	"You have no archived conversations.": "Нямате архивирани разговори.",
 	"You have no archived conversations.": "Нямате архивирани разговори.",
 	"You have shared this chat": "Вие сте споделели този чат",
 	"You have shared this chat": "Вие сте споделели този чат",

+ 10 - 6
src/lib/i18n/locales/bn-BD/translation.json

@@ -66,6 +66,8 @@
 	"Always-On Web Search": "",
 	"Always-On Web Search": "",
 	"Amazing": "",
 	"Amazing": "",
 	"an assistant": "একটা এসিস্ট্যান্ট",
 	"an assistant": "একটা এসিস্ট্যান্ট",
+	"Analyzed": "",
+	"Analyzing...": "",
 	"and": "এবং",
 	"and": "এবং",
 	"and {{COUNT}} more": "",
 	"and {{COUNT}} more": "",
 	"and create a new shared link.": "এবং একটি নতুন শেয়ারে লিংক তৈরি করুন.",
 	"and create a new shared link.": "এবং একটি নতুন শেয়ারে লিংক তৈরি করুন.",
@@ -170,9 +172,11 @@
 	"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "",
 	"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "",
 	"Clone": "ক্লোন",
 	"Clone": "ক্লোন",
 	"Clone Chat": "",
 	"Clone Chat": "",
+	"Clone of {{TITLE}}": "",
 	"Close": "বন্ধ",
 	"Close": "বন্ধ",
 	"Code execution": "",
 	"Code execution": "",
 	"Code formatted successfully": "",
 	"Code formatted successfully": "",
+	"Code Interpreter": "",
 	"Collection": "সংগ্রহ",
 	"Collection": "সংগ্রহ",
 	"Color": "",
 	"Color": "",
 	"ComfyUI": "ComfyUI",
 	"ComfyUI": "ComfyUI",
@@ -398,6 +402,7 @@
 	"Example: ou=users,dc=foo,dc=example": "",
 	"Example: ou=users,dc=foo,dc=example": "",
 	"Example: sAMAccountName or uid or userPrincipalName": "",
 	"Example: sAMAccountName or uid or userPrincipalName": "",
 	"Exclude": "",
 	"Exclude": "",
+	"Execute code for analysis": "",
 	"Experimental": "পরিক্ষামূলক",
 	"Experimental": "পরিক্ষামূলক",
 	"Explore the cosmos": "",
 	"Explore the cosmos": "",
 	"Export": "রপ্তানি",
 	"Export": "রপ্তানি",
@@ -464,6 +469,7 @@
 	"Functions imported successfully": "",
 	"Functions imported successfully": "",
 	"General": "সাধারণ",
 	"General": "সাধারণ",
 	"General Settings": "সাধারণ সেটিংসমূহ",
 	"General Settings": "সাধারণ সেটিংসমূহ",
+	"Generate an image": "",
 	"Generate Image": "",
 	"Generate Image": "",
 	"Generating search query": "অনুসন্ধান ক্যোয়ারী তৈরি করা হচ্ছে",
 	"Generating search query": "অনুসন্ধান ক্যোয়ারী তৈরি করা হচ্ছে",
 	"Get started": "",
 	"Get started": "",
@@ -479,7 +485,6 @@
 	"Group Name": "",
 	"Group Name": "",
 	"Group updated successfully": "",
 	"Group updated successfully": "",
 	"Groups": "",
 	"Groups": "",
-	"h:mm a": "h:mm a",
 	"Haptic Feedback": "",
 	"Haptic Feedback": "",
 	"has no conversations.": "কোন কনভার্সেশন আছে না।",
 	"has no conversations.": "কোন কনভার্সেশন আছে না।",
 	"Hello, {{name}}": "হ্যালো, {{name}}",
 	"Hello, {{name}}": "হ্যালো, {{name}}",
@@ -497,7 +502,6 @@
 	"Ignite curiosity": "",
 	"Ignite curiosity": "",
 	"Image": "",
 	"Image": "",
 	"Image Compression": "",
 	"Image Compression": "",
-	"Image generation": "",
 	"Image Generation": "",
 	"Image Generation": "",
 	"Image Generation (Experimental)": "ইমেজ জেনারেশন (পরিক্ষামূলক)",
 	"Image Generation (Experimental)": "ইমেজ জেনারেশন (পরিক্ষামূলক)",
 	"Image Generation Engine": "ইমেজ জেনারেশন ইঞ্জিন",
 	"Image Generation Engine": "ইমেজ জেনারেশন ইঞ্জিন",
@@ -596,9 +600,6 @@
 	"Mirostat": "Mirostat",
 	"Mirostat": "Mirostat",
 	"Mirostat Eta": "Mirostat Eta",
 	"Mirostat Eta": "Mirostat Eta",
 	"Mirostat Tau": "Mirostat Tau",
 	"Mirostat Tau": "Mirostat Tau",
-	"MMMM DD, YYYY": "MMMM DD, YYYY",
-	"MMMM DD, YYYY HH:mm": "MMMM DD, YYYY HH:mm",
-	"MMMM DD, YYYY hh:mm:ss A": "",
 	"Model": "",
 	"Model": "",
 	"Model '{{modelName}}' has been successfully downloaded.": "'{{modelName}}' মডেল সফলভাবে ডাউনলোড হয়েছে।",
 	"Model '{{modelName}}' has been successfully downloaded.": "'{{modelName}}' মডেল সফলভাবে ডাউনলোড হয়েছে।",
 	"Model '{{modelTag}}' is already in queue for downloading.": "{{modelTag}} ডাউনলোডের জন্য আগে থেকেই অপেক্ষমান আছে।",
 	"Model '{{modelTag}}' is already in queue for downloading.": "{{modelTag}} ডাউনলোডের জন্য আগে থেকেই অপেক্ষমান আছে।",
@@ -719,6 +720,7 @@
 	"Please enter a prompt": "",
 	"Please enter a prompt": "",
 	"Please fill in all fields.": "",
 	"Please fill in all fields.": "",
 	"Please select a model first.": "",
 	"Please select a model first.": "",
+	"Please select a model.": "",
 	"Please select a reason": "",
 	"Please select a reason": "",
 	"Port": "",
 	"Port": "",
 	"Positive attitude": "পজিটিভ আক্রমণ",
 	"Positive attitude": "পজিটিভ আক্রমণ",
@@ -802,7 +804,7 @@
 	"Search options": "",
 	"Search options": "",
 	"Search Prompts": "প্রম্পটসমূহ অনুসন্ধান করুন",
 	"Search Prompts": "প্রম্পটসমূহ অনুসন্ধান করুন",
 	"Search Result Count": "অনুসন্ধানের ফলাফল গণনা",
 	"Search Result Count": "অনুসন্ধানের ফলাফল গণনা",
-	"Search the web": "",
+	"Search the internet": "",
 	"Search Tools": "",
 	"Search Tools": "",
 	"SearchApi API Key": "",
 	"SearchApi API Key": "",
 	"SearchApi Engine": "",
 	"SearchApi Engine": "",
@@ -972,6 +974,7 @@
 	"Tools": "",
 	"Tools": "",
 	"Tools Access": "",
 	"Tools Access": "",
 	"Tools are a function calling system with arbitrary code execution": "",
 	"Tools are a function calling system with arbitrary code execution": "",
+	"Tools Function Calling Prompt": "",
 	"Tools have a function calling system that allows arbitrary code execution": "",
 	"Tools have a function calling system that allows arbitrary code execution": "",
 	"Tools have a function calling system that allows arbitrary code execution.": "",
 	"Tools have a function calling system that allows arbitrary code execution.": "",
 	"Top K": "Top K",
 	"Top K": "Top K",
@@ -1071,6 +1074,7 @@
 	"You can personalize your interactions with LLMs by adding memories through the 'Manage' button below, making them more helpful and tailored to you.": "",
 	"You can personalize your interactions with LLMs by adding memories through the 'Manage' button below, making them more helpful and tailored to you.": "",
 	"You cannot upload an empty file.": "",
 	"You cannot upload an empty file.": "",
 	"You do not have permission to access this feature.": "",
 	"You do not have permission to access this feature.": "",
+	"You do not have permission to upload files": "",
 	"You do not have permission to upload files.": "",
 	"You do not have permission to upload files.": "",
 	"You have no archived conversations.": "আপনার কোনও আর্কাইভ করা কথোপকথন নেই।",
 	"You have no archived conversations.": "আপনার কোনও আর্কাইভ করা কথোপকথন নেই।",
 	"You have shared this chat": "আপনি এই চ্যাটটি শেয়ার করেছেন",
 	"You have shared this chat": "আপনি এই চ্যাটটি শেয়ার করেছেন",

+ 10 - 6
src/lib/i18n/locales/ca-ES/translation.json

@@ -66,6 +66,8 @@
 	"Always-On Web Search": "",
 	"Always-On Web Search": "",
 	"Amazing": "Al·lucinant",
 	"Amazing": "Al·lucinant",
 	"an assistant": "un assistent",
 	"an assistant": "un assistent",
+	"Analyzed": "",
+	"Analyzing...": "",
 	"and": "i",
 	"and": "i",
 	"and {{COUNT}} more": "i {{COUNT}} més",
 	"and {{COUNT}} more": "i {{COUNT}} més",
 	"and create a new shared link.": "i crear un nou enllaç compartit.",
 	"and create a new shared link.": "i crear un nou enllaç compartit.",
@@ -170,9 +172,11 @@
 	"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "Permís d'escriptura al porta-retalls denegat. Comprova els ajustos de navegador per donar l'accés necessari.",
 	"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "Permís d'escriptura al porta-retalls denegat. Comprova els ajustos de navegador per donar l'accés necessari.",
 	"Clone": "Clonar",
 	"Clone": "Clonar",
 	"Clone Chat": "Clonar el xat",
 	"Clone Chat": "Clonar el xat",
+	"Clone of {{TITLE}}": "",
 	"Close": "Tancar",
 	"Close": "Tancar",
 	"Code execution": "Execució de codi",
 	"Code execution": "Execució de codi",
 	"Code formatted successfully": "Codi formatat correctament",
 	"Code formatted successfully": "Codi formatat correctament",
+	"Code Interpreter": "",
 	"Collection": "Col·lecció",
 	"Collection": "Col·lecció",
 	"Color": "Color",
 	"Color": "Color",
 	"ComfyUI": "ComfyUI",
 	"ComfyUI": "ComfyUI",
@@ -398,6 +402,7 @@
 	"Example: ou=users,dc=foo,dc=example": "Exemple: ou=users,dc=foo,dc=example",
 	"Example: ou=users,dc=foo,dc=example": "Exemple: ou=users,dc=foo,dc=example",
 	"Example: sAMAccountName or uid or userPrincipalName": "Exemple: sAMAccountName o uid o userPrincipalName",
 	"Example: sAMAccountName or uid or userPrincipalName": "Exemple: sAMAccountName o uid o userPrincipalName",
 	"Exclude": "Excloure",
 	"Exclude": "Excloure",
+	"Execute code for analysis": "",
 	"Experimental": "Experimental",
 	"Experimental": "Experimental",
 	"Explore the cosmos": "Explorar el cosmos",
 	"Explore the cosmos": "Explorar el cosmos",
 	"Export": "Exportar",
 	"Export": "Exportar",
@@ -464,6 +469,7 @@
 	"Functions imported successfully": "Les funcions s'han importat correctament",
 	"Functions imported successfully": "Les funcions s'han importat correctament",
 	"General": "General",
 	"General": "General",
 	"General Settings": "Preferències generals",
 	"General Settings": "Preferències generals",
+	"Generate an image": "",
 	"Generate Image": "Generar imatge",
 	"Generate Image": "Generar imatge",
 	"Generating search query": "Generant consulta",
 	"Generating search query": "Generant consulta",
 	"Get started": "Començar",
 	"Get started": "Començar",
@@ -479,7 +485,6 @@
 	"Group Name": "Nom del grup",
 	"Group Name": "Nom del grup",
 	"Group updated successfully": "Grup actualitzat correctament",
 	"Group updated successfully": "Grup actualitzat correctament",
 	"Groups": "Grups",
 	"Groups": "Grups",
-	"h:mm a": "h:mm a",
 	"Haptic Feedback": "Retorn hàptic",
 	"Haptic Feedback": "Retorn hàptic",
 	"has no conversations.": "no té converses.",
 	"has no conversations.": "no té converses.",
 	"Hello, {{name}}": "Hola, {{name}}",
 	"Hello, {{name}}": "Hola, {{name}}",
@@ -497,7 +502,6 @@
 	"Ignite curiosity": "Despertar la curiositat",
 	"Ignite curiosity": "Despertar la curiositat",
 	"Image": "Imatge",
 	"Image": "Imatge",
 	"Image Compression": "Compressió d'imatges",
 	"Image Compression": "Compressió d'imatges",
-	"Image generation": "Generació d'imatges",
 	"Image Generation": "Generació d'imatges",
 	"Image Generation": "Generació d'imatges",
 	"Image Generation (Experimental)": "Generació d'imatges (Experimental)",
 	"Image Generation (Experimental)": "Generació d'imatges (Experimental)",
 	"Image Generation Engine": "Motor de generació d'imatges",
 	"Image Generation Engine": "Motor de generació d'imatges",
@@ -596,9 +600,6 @@
 	"Mirostat": "Mirostat",
 	"Mirostat": "Mirostat",
 	"Mirostat Eta": "Eta de Mirostat",
 	"Mirostat Eta": "Eta de Mirostat",
 	"Mirostat Tau": "Tau de Mirostat",
 	"Mirostat Tau": "Tau de Mirostat",
-	"MMMM DD, YYYY": "DD de MMMM, YYYY",
-	"MMMM DD, YYYY HH:mm": "DD de MMMM, YYYY HH:mm",
-	"MMMM DD, YYYY hh:mm:ss A": "DD de MMMM, YYYY HH:mm:ss, A",
 	"Model": "Model",
 	"Model": "Model",
 	"Model '{{modelName}}' has been successfully downloaded.": "El model '{{modelName}}' s'ha descarregat correctament.",
 	"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 '{{modelTag}}' is already in queue for downloading.": "El model '{{modelTag}}' ja està en cua per ser descarregat.",
@@ -719,6 +720,7 @@
 	"Please enter a prompt": "Si us plau, entra una indicació",
 	"Please enter a prompt": "Si us plau, entra una indicació",
 	"Please fill in all fields.": "Emplena tots els camps, si us plau.",
 	"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 first.": "Si us plau, selecciona un model primer",
+	"Please select a model.": "",
 	"Please select a reason": "Si us plau, selecciona una raó",
 	"Please select a reason": "Si us plau, selecciona una raó",
 	"Port": "Port",
 	"Port": "Port",
 	"Positive attitude": "Actitud positiva",
 	"Positive attitude": "Actitud positiva",
@@ -802,7 +804,7 @@
 	"Search options": "Opcions de cerca",
 	"Search options": "Opcions de cerca",
 	"Search Prompts": "Cercar indicacions",
 	"Search Prompts": "Cercar indicacions",
 	"Search Result Count": "Recompte de resultats de cerca",
 	"Search Result Count": "Recompte de resultats de cerca",
-	"Search the web": "Cercar la web",
+	"Search the internet": "",
 	"Search Tools": "Cercar eines",
 	"Search Tools": "Cercar eines",
 	"SearchApi API Key": "Clau API de SearchApi",
 	"SearchApi API Key": "Clau API de SearchApi",
 	"SearchApi Engine": "Motor de SearchApi",
 	"SearchApi Engine": "Motor de SearchApi",
@@ -972,6 +974,7 @@
 	"Tools": "Eines",
 	"Tools": "Eines",
 	"Tools Access": "Accés a les eines",
 	"Tools Access": "Accés a les eines",
 	"Tools are a function calling system with arbitrary code execution": "Les eines són un sistema de crida a funcions amb execució de codi arbitrari",
 	"Tools are a function calling system with arbitrary code execution": "Les eines són un sistema de crida a funcions amb execució de codi arbitrari",
+	"Tools Function Calling Prompt": "",
 	"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 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.",
 	"Top K": "Top K",
 	"Top K": "Top K",
@@ -1071,6 +1074,7 @@
 	"You can personalize your interactions with LLMs by adding memories through the 'Manage' button below, making them more helpful and tailored to you.": "Pots personalitzar les teves interaccions amb els models de llenguatge afegint memòries mitjançant el botó 'Gestiona' que hi ha a continuació, fent-les més útils i adaptades a tu.",
 	"You can personalize your interactions with LLMs by adding memories through the 'Manage' button below, making them more helpful and tailored to you.": "Pots personalitzar les teves interaccions amb els models de llenguatge afegint memòries mitjançant el botó 'Gestiona' que hi ha a continuació, fent-les més útils i adaptades a tu.",
 	"You cannot upload an empty file.": "No es pot pujar un ariux buit.",
 	"You cannot upload an empty file.": "No es pot pujar un ariux buit.",
 	"You do not have permission to access this feature.": "No tens permís per accedir a aquesta funcionalitat",
 	"You do not have permission to access this feature.": "No tens permís per accedir a aquesta funcionalitat",
+	"You do not have permission to upload files": "",
 	"You do not have permission to upload files.": "No tens permisos per pujar arxius.",
 	"You do not have permission to upload files.": "No tens permisos per pujar arxius.",
 	"You have no archived conversations.": "No tens converses arxivades.",
 	"You have no archived conversations.": "No tens converses arxivades.",
 	"You have shared this chat": "Has compartit aquest xat",
 	"You have shared this chat": "Has compartit aquest xat",

+ 10 - 6
src/lib/i18n/locales/ceb-PH/translation.json

@@ -66,6 +66,8 @@
 	"Always-On Web Search": "",
 	"Always-On Web Search": "",
 	"Amazing": "",
 	"Amazing": "",
 	"an assistant": "usa ka katabang",
 	"an assistant": "usa ka katabang",
+	"Analyzed": "",
+	"Analyzing...": "",
 	"and": "Ug",
 	"and": "Ug",
 	"and {{COUNT}} more": "",
 	"and {{COUNT}} more": "",
 	"and create a new shared link.": "",
 	"and create a new shared link.": "",
@@ -170,9 +172,11 @@
 	"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "",
 	"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "",
 	"Clone": "",
 	"Clone": "",
 	"Clone Chat": "",
 	"Clone Chat": "",
+	"Clone of {{TITLE}}": "",
 	"Close": "Suod nga",
 	"Close": "Suod nga",
 	"Code execution": "",
 	"Code execution": "",
 	"Code formatted successfully": "",
 	"Code formatted successfully": "",
+	"Code Interpreter": "",
 	"Collection": "Koleksyon",
 	"Collection": "Koleksyon",
 	"Color": "",
 	"Color": "",
 	"ComfyUI": "",
 	"ComfyUI": "",
@@ -398,6 +402,7 @@
 	"Example: ou=users,dc=foo,dc=example": "",
 	"Example: ou=users,dc=foo,dc=example": "",
 	"Example: sAMAccountName or uid or userPrincipalName": "",
 	"Example: sAMAccountName or uid or userPrincipalName": "",
 	"Exclude": "",
 	"Exclude": "",
+	"Execute code for analysis": "",
 	"Experimental": "Eksperimento",
 	"Experimental": "Eksperimento",
 	"Explore the cosmos": "",
 	"Explore the cosmos": "",
 	"Export": "",
 	"Export": "",
@@ -464,6 +469,7 @@
 	"Functions imported successfully": "",
 	"Functions imported successfully": "",
 	"General": "Heneral",
 	"General": "Heneral",
 	"General Settings": "kinatibuk-ang mga setting",
 	"General Settings": "kinatibuk-ang mga setting",
+	"Generate an image": "",
 	"Generate Image": "",
 	"Generate Image": "",
 	"Generating search query": "",
 	"Generating search query": "",
 	"Get started": "",
 	"Get started": "",
@@ -479,7 +485,6 @@
 	"Group Name": "",
 	"Group Name": "",
 	"Group updated successfully": "",
 	"Group updated successfully": "",
 	"Groups": "",
 	"Groups": "",
-	"h:mm a": "",
 	"Haptic Feedback": "",
 	"Haptic Feedback": "",
 	"has no conversations.": "",
 	"has no conversations.": "",
 	"Hello, {{name}}": "Maayong buntag, {{name}}",
 	"Hello, {{name}}": "Maayong buntag, {{name}}",
@@ -497,7 +502,6 @@
 	"Ignite curiosity": "",
 	"Ignite curiosity": "",
 	"Image": "",
 	"Image": "",
 	"Image Compression": "",
 	"Image Compression": "",
-	"Image generation": "",
 	"Image Generation": "",
 	"Image Generation": "",
 	"Image Generation (Experimental)": "Pagmugna og hulagway (Eksperimento)",
 	"Image Generation (Experimental)": "Pagmugna og hulagway (Eksperimento)",
 	"Image Generation Engine": "Makina sa paghimo og imahe",
 	"Image Generation Engine": "Makina sa paghimo og imahe",
@@ -596,9 +600,6 @@
 	"Mirostat": "Mirostat",
 	"Mirostat": "Mirostat",
 	"Mirostat Eta": "Mirostat Eta",
 	"Mirostat Eta": "Mirostat Eta",
 	"Mirostat Tau": "Mirostat Tau",
 	"Mirostat Tau": "Mirostat Tau",
-	"MMMM DD, YYYY": "MMMM DD, YYYY",
-	"MMMM DD, YYYY HH:mm": "",
-	"MMMM DD, YYYY hh:mm:ss A": "",
 	"Model": "",
 	"Model": "",
 	"Model '{{modelName}}' has been successfully downloaded.": "Ang modelo'{{modelName}}' malampuson nga na-download.",
 	"Model '{{modelName}}' has been successfully downloaded.": "Ang modelo'{{modelName}}' malampuson nga na-download.",
 	"Model '{{modelTag}}' is already in queue for downloading.": "Ang modelo'{{modelTag}}' naa na sa pila para ma-download.",
 	"Model '{{modelTag}}' is already in queue for downloading.": "Ang modelo'{{modelTag}}' naa na sa pila para ma-download.",
@@ -719,6 +720,7 @@
 	"Please enter a prompt": "",
 	"Please enter a prompt": "",
 	"Please fill in all fields.": "",
 	"Please fill in all fields.": "",
 	"Please select a model first.": "",
 	"Please select a model first.": "",
+	"Please select a model.": "",
 	"Please select a reason": "",
 	"Please select a reason": "",
 	"Port": "",
 	"Port": "",
 	"Positive attitude": "",
 	"Positive attitude": "",
@@ -802,7 +804,7 @@
 	"Search options": "",
 	"Search options": "",
 	"Search Prompts": "Pangitaa ang mga prompt",
 	"Search Prompts": "Pangitaa ang mga prompt",
 	"Search Result Count": "",
 	"Search Result Count": "",
-	"Search the web": "",
+	"Search the internet": "",
 	"Search Tools": "",
 	"Search Tools": "",
 	"SearchApi API Key": "",
 	"SearchApi API Key": "",
 	"SearchApi Engine": "",
 	"SearchApi Engine": "",
@@ -972,6 +974,7 @@
 	"Tools": "",
 	"Tools": "",
 	"Tools Access": "",
 	"Tools Access": "",
 	"Tools are a function calling system with arbitrary code execution": "",
 	"Tools are a function calling system with arbitrary code execution": "",
+	"Tools Function Calling Prompt": "",
 	"Tools have a function calling system that allows arbitrary code execution": "",
 	"Tools have a function calling system that allows arbitrary code execution": "",
 	"Tools have a function calling system that allows arbitrary code execution.": "",
 	"Tools have a function calling system that allows arbitrary code execution.": "",
 	"Top K": "Top K",
 	"Top K": "Top K",
@@ -1071,6 +1074,7 @@
 	"You can personalize your interactions with LLMs by adding memories through the 'Manage' button below, making them more helpful and tailored to you.": "",
 	"You can personalize your interactions with LLMs by adding memories through the 'Manage' button below, making them more helpful and tailored to you.": "",
 	"You cannot upload an empty file.": "",
 	"You cannot upload an empty file.": "",
 	"You do not have permission to access this feature.": "",
 	"You do not have permission to access this feature.": "",
+	"You do not have permission to upload files": "",
 	"You do not have permission to upload files.": "",
 	"You do not have permission to upload files.": "",
 	"You have no archived conversations.": "",
 	"You have no archived conversations.": "",
 	"You have shared this chat": "",
 	"You have shared this chat": "",

+ 10 - 6
src/lib/i18n/locales/cs-CZ/translation.json

@@ -66,6 +66,8 @@
 	"Always-On Web Search": "",
 	"Always-On Web Search": "",
 	"Amazing": "",
 	"Amazing": "",
 	"an assistant": "asistent",
 	"an assistant": "asistent",
+	"Analyzed": "",
+	"Analyzing...": "",
 	"and": "a",
 	"and": "a",
 	"and {{COUNT}} more": "a {{COUNT}} další/ch",
 	"and {{COUNT}} more": "a {{COUNT}} další/ch",
 	"and create a new shared link.": "a vytvořit nový sdílený odkaz.",
 	"and create a new shared link.": "a vytvořit nový sdílený odkaz.",
@@ -170,9 +172,11 @@
 	"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "Přístup k zápisu do schránky byl zamítnut. Prosím, zkontrolujte nastavení svého prohlížeče a udělte potřebný přístup.",
 	"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "Přístup k zápisu do schránky byl zamítnut. Prosím, zkontrolujte nastavení svého prohlížeče a udělte potřebný přístup.",
 	"Clone": "Klonovat",
 	"Clone": "Klonovat",
 	"Clone Chat": "",
 	"Clone Chat": "",
+	"Clone of {{TITLE}}": "",
 	"Close": "Zavřít",
 	"Close": "Zavřít",
 	"Code execution": "Provádění kódu",
 	"Code execution": "Provádění kódu",
 	"Code formatted successfully": "Kód byl úspěšně naformátován.",
 	"Code formatted successfully": "Kód byl úspěšně naformátován.",
+	"Code Interpreter": "",
 	"Collection": "",
 	"Collection": "",
 	"Color": "Barva",
 	"Color": "Barva",
 	"ComfyUI": "ComfyUI.",
 	"ComfyUI": "ComfyUI.",
@@ -398,6 +402,7 @@
 	"Example: ou=users,dc=foo,dc=example": "",
 	"Example: ou=users,dc=foo,dc=example": "",
 	"Example: sAMAccountName or uid or userPrincipalName": "",
 	"Example: sAMAccountName or uid or userPrincipalName": "",
 	"Exclude": "Vyloučit",
 	"Exclude": "Vyloučit",
+	"Execute code for analysis": "",
 	"Experimental": "Experimentální",
 	"Experimental": "Experimentální",
 	"Explore the cosmos": "",
 	"Explore the cosmos": "",
 	"Export": "Exportovat",
 	"Export": "Exportovat",
@@ -464,6 +469,7 @@
 	"Functions imported successfully": "Funkce byly úspěšně importovány",
 	"Functions imported successfully": "Funkce byly úspěšně importovány",
 	"General": "Obecný",
 	"General": "Obecný",
 	"General Settings": "Obecná nastavení",
 	"General Settings": "Obecná nastavení",
+	"Generate an image": "",
 	"Generate Image": "Vygenerovat obrázek",
 	"Generate Image": "Vygenerovat obrázek",
 	"Generating search query": "Generování vyhledávacího dotazu",
 	"Generating search query": "Generování vyhledávacího dotazu",
 	"Get started": "",
 	"Get started": "",
@@ -479,7 +485,6 @@
 	"Group Name": "",
 	"Group Name": "",
 	"Group updated successfully": "",
 	"Group updated successfully": "",
 	"Groups": "",
 	"Groups": "",
-	"h:mm a": "hh:mm dop./odp.",
 	"Haptic Feedback": "Haptická zpětná vazba",
 	"Haptic Feedback": "Haptická zpětná vazba",
 	"has no conversations.": "nemá žádné konverzace.",
 	"has no conversations.": "nemá žádné konverzace.",
 	"Hello, {{name}}": "Ahoj, {{name}}",
 	"Hello, {{name}}": "Ahoj, {{name}}",
@@ -497,7 +502,6 @@
 	"Ignite curiosity": "",
 	"Ignite curiosity": "",
 	"Image": "",
 	"Image": "",
 	"Image Compression": "",
 	"Image Compression": "",
-	"Image generation": "",
 	"Image Generation": "",
 	"Image Generation": "",
 	"Image Generation (Experimental)": "Generování obrázků (experimentální)",
 	"Image Generation (Experimental)": "Generování obrázků (experimentální)",
 	"Image Generation Engine": "Engine pro generování obrázků",
 	"Image Generation Engine": "Engine pro generování obrázků",
@@ -596,9 +600,6 @@
 	"Mirostat": "Mirostat",
 	"Mirostat": "Mirostat",
 	"Mirostat Eta": "Mirostat Eta",
 	"Mirostat Eta": "Mirostat Eta",
 	"Mirostat Tau": "Mirostat Tau",
 	"Mirostat Tau": "Mirostat Tau",
-	"MMMM DD, YYYY": "MMMM DD, RRRR",
-	"MMMM DD, YYYY HH:mm": "MMMM DD, RRRR HH:mm",
-	"MMMM DD, YYYY hh:mm:ss A": "MMMM DD, YYYY hh:mm:ss A",
 	"Model": "Model",
 	"Model": "Model",
 	"Model '{{modelName}}' has been successfully downloaded.": "Model „{{modelName}}“ byl úspěšně stažen.",
 	"Model '{{modelName}}' has been successfully downloaded.": "Model „{{modelName}}“ byl úspěšně stažen.",
 	"Model '{{modelTag}}' is already in queue for downloading.": "Model '{{modelTag}}' je již zařazen do fronty pro stahování.",
 	"Model '{{modelTag}}' is already in queue for downloading.": "Model '{{modelTag}}' je již zařazen do fronty pro stahování.",
@@ -719,6 +720,7 @@
 	"Please enter a prompt": "Prosím, zadejte zadání.",
 	"Please enter a prompt": "Prosím, zadejte zadání.",
 	"Please fill in all fields.": "Prosím, vyplňte všechna pole.",
 	"Please fill in all fields.": "Prosím, vyplňte všechna pole.",
 	"Please select a model first.": "",
 	"Please select a model first.": "",
+	"Please select a model.": "",
 	"Please select a reason": "Prosím vyberte důvod",
 	"Please select a reason": "Prosím vyberte důvod",
 	"Port": "",
 	"Port": "",
 	"Positive attitude": "Pozitivní přístup",
 	"Positive attitude": "Pozitivní přístup",
@@ -802,7 +804,7 @@
 	"Search options": "",
 	"Search options": "",
 	"Search Prompts": "Vyhledávací dotazy",
 	"Search Prompts": "Vyhledávací dotazy",
 	"Search Result Count": "Počet výsledků hledání",
 	"Search Result Count": "Počet výsledků hledání",
-	"Search the web": "",
+	"Search the internet": "",
 	"Search Tools": "Nástroje pro vyhledávání",
 	"Search Tools": "Nástroje pro vyhledávání",
 	"SearchApi API Key": "Klíč API pro SearchApi",
 	"SearchApi API Key": "Klíč API pro SearchApi",
 	"SearchApi Engine": "Vyhledávací engine API",
 	"SearchApi Engine": "Vyhledávací engine API",
@@ -972,6 +974,7 @@
 	"Tools": "Nástroje",
 	"Tools": "Nástroje",
 	"Tools Access": "",
 	"Tools Access": "",
 	"Tools are a function calling system with arbitrary code execution": "Nástroje jsou systémem pro volání funkcí s vykonáváním libovolného kódu.",
 	"Tools are a function calling system with arbitrary code execution": "Nástroje jsou systémem pro volání funkcí s vykonáváním libovolného kódu.",
+	"Tools Function Calling Prompt": "",
 	"Tools have a function calling system that allows arbitrary code execution": "Nástroje mají systém volání funkcí, který umožňuje libovolné spouštění kódu.",
 	"Tools have a function calling system that allows arbitrary code execution": "Nástroje mají systém volání funkcí, který umožňuje libovolné spouštění kódu.",
 	"Tools have a function calling system that allows arbitrary code execution.": "Nástroje mají systém volání funkcí, který umožňuje spuštění libovolného kódu.",
 	"Tools have a function calling system that allows arbitrary code execution.": "Nástroje mají systém volání funkcí, který umožňuje spuštění libovolného kódu.",
 	"Top K": "Top K",
 	"Top K": "Top K",
@@ -1071,6 +1074,7 @@
 	"You can personalize your interactions with LLMs by adding memories through the 'Manage' button below, making them more helpful and tailored to you.": "Můžete personalizovat své interakce s LLM pomocí přidávání vzpomínek prostřednictvím tlačítka 'Spravovat' níže, což je učiní pro vás užitečnějšími a lépe přizpůsobenými.",
 	"You can personalize your interactions with LLMs by adding memories through the 'Manage' button below, making them more helpful and tailored to you.": "Můžete personalizovat své interakce s LLM pomocí přidávání vzpomínek prostřednictvím tlačítka 'Spravovat' níže, což je učiní pro vás užitečnějšími a lépe přizpůsobenými.",
 	"You cannot upload an empty file.": "Nemůžete nahrát prázdný soubor.",
 	"You cannot upload an empty file.": "Nemůžete nahrát prázdný soubor.",
 	"You do not have permission to access this feature.": "",
 	"You do not have permission to access this feature.": "",
+	"You do not have permission to upload files": "",
 	"You do not have permission to upload files.": "",
 	"You do not have permission to upload files.": "",
 	"You have no archived conversations.": "Nemáte žádné archivované konverzace.",
 	"You have no archived conversations.": "Nemáte žádné archivované konverzace.",
 	"You have shared this chat": "Sdíleli jste tento chat.",
 	"You have shared this chat": "Sdíleli jste tento chat.",

+ 10 - 6
src/lib/i18n/locales/da-DK/translation.json

@@ -66,6 +66,8 @@
 	"Always-On Web Search": "",
 	"Always-On Web Search": "",
 	"Amazing": "",
 	"Amazing": "",
 	"an assistant": "en assistent",
 	"an assistant": "en assistent",
+	"Analyzed": "",
+	"Analyzing...": "",
 	"and": "og",
 	"and": "og",
 	"and {{COUNT}} more": "",
 	"and {{COUNT}} more": "",
 	"and create a new shared link.": "og lav et nyt link til deling",
 	"and create a new shared link.": "og lav et nyt link til deling",
@@ -170,9 +172,11 @@
 	"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "Skriveadgang til udklipsholderen ikke tilladt. Tjek venligst indstillingerne i din browser for at give adgang.",
 	"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "Skriveadgang til udklipsholderen ikke tilladt. Tjek venligst indstillingerne i din browser for at give adgang.",
 	"Clone": "Klon",
 	"Clone": "Klon",
 	"Clone Chat": "",
 	"Clone Chat": "",
+	"Clone of {{TITLE}}": "",
 	"Close": "Luk",
 	"Close": "Luk",
 	"Code execution": "",
 	"Code execution": "",
 	"Code formatted successfully": "Kode formateret korrekt",
 	"Code formatted successfully": "Kode formateret korrekt",
+	"Code Interpreter": "",
 	"Collection": "Samling",
 	"Collection": "Samling",
 	"Color": "",
 	"Color": "",
 	"ComfyUI": "ComfyUI",
 	"ComfyUI": "ComfyUI",
@@ -398,6 +402,7 @@
 	"Example: ou=users,dc=foo,dc=example": "",
 	"Example: ou=users,dc=foo,dc=example": "",
 	"Example: sAMAccountName or uid or userPrincipalName": "",
 	"Example: sAMAccountName or uid or userPrincipalName": "",
 	"Exclude": "",
 	"Exclude": "",
+	"Execute code for analysis": "",
 	"Experimental": "Eksperimentel",
 	"Experimental": "Eksperimentel",
 	"Explore the cosmos": "",
 	"Explore the cosmos": "",
 	"Export": "Eksportér",
 	"Export": "Eksportér",
@@ -464,6 +469,7 @@
 	"Functions imported successfully": "Funktioner importeret.",
 	"Functions imported successfully": "Funktioner importeret.",
 	"General": "Generelt",
 	"General": "Generelt",
 	"General Settings": "Generelle indstillinger",
 	"General Settings": "Generelle indstillinger",
+	"Generate an image": "",
 	"Generate Image": "Generer billede",
 	"Generate Image": "Generer billede",
 	"Generating search query": "Genererer søgeforespørgsel",
 	"Generating search query": "Genererer søgeforespørgsel",
 	"Get started": "",
 	"Get started": "",
@@ -479,7 +485,6 @@
 	"Group Name": "",
 	"Group Name": "",
 	"Group updated successfully": "",
 	"Group updated successfully": "",
 	"Groups": "",
 	"Groups": "",
-	"h:mm a": "h:mm a",
 	"Haptic Feedback": "Haptisk feedback",
 	"Haptic Feedback": "Haptisk feedback",
 	"has no conversations.": "har ingen samtaler.",
 	"has no conversations.": "har ingen samtaler.",
 	"Hello, {{name}}": "Hej {{name}}",
 	"Hello, {{name}}": "Hej {{name}}",
@@ -497,7 +502,6 @@
 	"Ignite curiosity": "",
 	"Ignite curiosity": "",
 	"Image": "",
 	"Image": "",
 	"Image Compression": "",
 	"Image Compression": "",
-	"Image generation": "",
 	"Image Generation": "",
 	"Image Generation": "",
 	"Image Generation (Experimental)": "Billedgenerering (eksperimentel)",
 	"Image Generation (Experimental)": "Billedgenerering (eksperimentel)",
 	"Image Generation Engine": "Billedgenereringsengine",
 	"Image Generation Engine": "Billedgenereringsengine",
@@ -596,9 +600,6 @@
 	"Mirostat": "Mirostat",
 	"Mirostat": "Mirostat",
 	"Mirostat Eta": "Mirostat Eta",
 	"Mirostat Eta": "Mirostat Eta",
 	"Mirostat Tau": "Mirostat Tau",
 	"Mirostat Tau": "Mirostat Tau",
-	"MMMM DD, YYYY": "MMMM DD, YYYY",
-	"MMMM DD, YYYY HH:mm": "MMMM DD, YYYY HH:mm",
-	"MMMM DD, YYYY hh:mm:ss A": "MMMM DD, YYYY hh:mm:ss A",
 	"Model": "",
 	"Model": "",
 	"Model '{{modelName}}' has been successfully downloaded.": "Model '{{modelName}}' er blevet downloadet.",
 	"Model '{{modelName}}' has been successfully downloaded.": "Model '{{modelName}}' er blevet downloadet.",
 	"Model '{{modelTag}}' is already in queue for downloading.": "Model '{{modelTag}}' er allerede i kø til download.",
 	"Model '{{modelTag}}' is already in queue for downloading.": "Model '{{modelTag}}' er allerede i kø til download.",
@@ -719,6 +720,7 @@
 	"Please enter a prompt": "",
 	"Please enter a prompt": "",
 	"Please fill in all fields.": "Udfyld alle felter.",
 	"Please fill in all fields.": "Udfyld alle felter.",
 	"Please select a model first.": "",
 	"Please select a model first.": "",
+	"Please select a model.": "",
 	"Please select a reason": "Vælg en årsag",
 	"Please select a reason": "Vælg en årsag",
 	"Port": "",
 	"Port": "",
 	"Positive attitude": "Positiv holdning",
 	"Positive attitude": "Positiv holdning",
@@ -802,7 +804,7 @@
 	"Search options": "",
 	"Search options": "",
 	"Search Prompts": "Søg i prompts",
 	"Search Prompts": "Søg i prompts",
 	"Search Result Count": "Antal søgeresultater",
 	"Search Result Count": "Antal søgeresultater",
-	"Search the web": "",
+	"Search the internet": "",
 	"Search Tools": "Søg i værktøjer",
 	"Search Tools": "Søg i værktøjer",
 	"SearchApi API Key": "SearchApi API-nøgle",
 	"SearchApi API Key": "SearchApi API-nøgle",
 	"SearchApi Engine": "SearchApi-engine",
 	"SearchApi Engine": "SearchApi-engine",
@@ -972,6 +974,7 @@
 	"Tools": "Værktøjer",
 	"Tools": "Værktøjer",
 	"Tools Access": "",
 	"Tools Access": "",
 	"Tools are a function calling system with arbitrary code execution": "Værktøjer er et funktionkaldssystem med vilkårlig kodeudførelse",
 	"Tools are a function calling system with arbitrary code execution": "Værktøjer er et funktionkaldssystem med vilkårlig kodeudførelse",
+	"Tools Function Calling Prompt": "",
 	"Tools have a function calling system that allows arbitrary code execution": "Værktøjer har et funktionkaldssystem, der tillader vilkårlig kodeudførelse",
 	"Tools have a function calling system that allows arbitrary code execution": "Værktøjer har et funktionkaldssystem, der tillader vilkårlig kodeudførelse",
 	"Tools have a function calling system that allows arbitrary code execution.": "Værktøjer har et funktionkaldssystem, der tillader vilkårlig kodeudførelse.",
 	"Tools have a function calling system that allows arbitrary code execution.": "Værktøjer har et funktionkaldssystem, der tillader vilkårlig kodeudførelse.",
 	"Top K": "Top K",
 	"Top K": "Top K",
@@ -1071,6 +1074,7 @@
 	"You can personalize your interactions with LLMs by adding memories through the 'Manage' button below, making them more helpful and tailored to you.": "Du kan personliggøre dine interaktioner med LLM'er ved at tilføje minder via knappen 'Administrer' nedenfor, hvilket gør dem mere nyttige og skræddersyet til dig.",
 	"You can personalize your interactions with LLMs by adding memories through the 'Manage' button below, making them more helpful and tailored to you.": "Du kan personliggøre dine interaktioner med LLM'er ved at tilføje minder via knappen 'Administrer' nedenfor, hvilket gør dem mere nyttige og skræddersyet til dig.",
 	"You cannot upload an empty file.": "",
 	"You cannot upload an empty file.": "",
 	"You do not have permission to access this feature.": "",
 	"You do not have permission to access this feature.": "",
+	"You do not have permission to upload files": "",
 	"You do not have permission to upload files.": "",
 	"You do not have permission to upload files.": "",
 	"You have no archived conversations.": "Du har ingen arkiverede samtaler.",
 	"You have no archived conversations.": "Du har ingen arkiverede samtaler.",
 	"You have shared this chat": "Du har delt denne chat",
 	"You have shared this chat": "Du har delt denne chat",

+ 10 - 6
src/lib/i18n/locales/de-DE/translation.json

@@ -66,6 +66,8 @@
 	"Always-On Web Search": "",
 	"Always-On Web Search": "",
 	"Amazing": "Fantastisch",
 	"Amazing": "Fantastisch",
 	"an assistant": "ein Assistent",
 	"an assistant": "ein Assistent",
+	"Analyzed": "",
+	"Analyzing...": "",
 	"and": "und",
 	"and": "und",
 	"and {{COUNT}} more": "und {{COUNT}} mehr",
 	"and {{COUNT}} more": "und {{COUNT}} mehr",
 	"and create a new shared link.": "und erstellen Sie einen neuen freigegebenen Link.",
 	"and create a new shared link.": "und erstellen Sie einen neuen freigegebenen Link.",
@@ -170,9 +172,11 @@
 	"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "Schreibberechtigung für die Zwischenablage verweigert. Bitte überprüfen Sie Ihre Browsereinstellungen, um den erforderlichen Zugriff zu erlauben.",
 	"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "Schreibberechtigung für die Zwischenablage verweigert. Bitte überprüfen Sie Ihre Browsereinstellungen, um den erforderlichen Zugriff zu erlauben.",
 	"Clone": "Klonen",
 	"Clone": "Klonen",
 	"Clone Chat": "",
 	"Clone Chat": "",
+	"Clone of {{TITLE}}": "",
 	"Close": "Schließen",
 	"Close": "Schließen",
 	"Code execution": "Codeausführung",
 	"Code execution": "Codeausführung",
 	"Code formatted successfully": "Code erfolgreich formatiert",
 	"Code formatted successfully": "Code erfolgreich formatiert",
+	"Code Interpreter": "",
 	"Collection": "Kollektion",
 	"Collection": "Kollektion",
 	"Color": "Farbe",
 	"Color": "Farbe",
 	"ComfyUI": "ComfyUI",
 	"ComfyUI": "ComfyUI",
@@ -398,6 +402,7 @@
 	"Example: ou=users,dc=foo,dc=example": "Beispiel: ou=users,dc=foo,dc=example",
 	"Example: ou=users,dc=foo,dc=example": "Beispiel: ou=users,dc=foo,dc=example",
 	"Example: sAMAccountName or uid or userPrincipalName": "Beispiel: sAMAccountName or uid or userPrincipalName",
 	"Example: sAMAccountName or uid or userPrincipalName": "Beispiel: sAMAccountName or uid or userPrincipalName",
 	"Exclude": "Ausschließen",
 	"Exclude": "Ausschließen",
+	"Execute code for analysis": "",
 	"Experimental": "Experimentell",
 	"Experimental": "Experimentell",
 	"Explore the cosmos": "Erforschen Sie das Universum",
 	"Explore the cosmos": "Erforschen Sie das Universum",
 	"Export": "Exportieren",
 	"Export": "Exportieren",
@@ -464,6 +469,7 @@
 	"Functions imported successfully": "Funktionen erfolgreich importiert",
 	"Functions imported successfully": "Funktionen erfolgreich importiert",
 	"General": "Allgemein",
 	"General": "Allgemein",
 	"General Settings": "Allgemeine Einstellungen",
 	"General Settings": "Allgemeine Einstellungen",
+	"Generate an image": "",
 	"Generate Image": "Bild erzeugen",
 	"Generate Image": "Bild erzeugen",
 	"Generating search query": "Suchanfrage wird erstellt",
 	"Generating search query": "Suchanfrage wird erstellt",
 	"Get started": "Loslegen",
 	"Get started": "Loslegen",
@@ -479,7 +485,6 @@
 	"Group Name": "Gruppenname",
 	"Group Name": "Gruppenname",
 	"Group updated successfully": "Gruppe erfolgreich aktualisiert",
 	"Group updated successfully": "Gruppe erfolgreich aktualisiert",
 	"Groups": "Gruppen",
 	"Groups": "Gruppen",
-	"h:mm a": "h:mm a",
 	"Haptic Feedback": "Haptisches Feedback",
 	"Haptic Feedback": "Haptisches Feedback",
 	"has no conversations.": "hat keine Unterhaltungen.",
 	"has no conversations.": "hat keine Unterhaltungen.",
 	"Hello, {{name}}": "Hallo, {{name}}",
 	"Hello, {{name}}": "Hallo, {{name}}",
@@ -497,7 +502,6 @@
 	"Ignite curiosity": "Neugier entfachen",
 	"Ignite curiosity": "Neugier entfachen",
 	"Image": "",
 	"Image": "",
 	"Image Compression": "",
 	"Image Compression": "",
-	"Image generation": "",
 	"Image Generation": "",
 	"Image Generation": "",
 	"Image Generation (Experimental)": "Bildgenerierung (experimentell)",
 	"Image Generation (Experimental)": "Bildgenerierung (experimentell)",
 	"Image Generation Engine": "Bildgenerierungs-Engine",
 	"Image Generation Engine": "Bildgenerierungs-Engine",
@@ -596,9 +600,6 @@
 	"Mirostat": "Mirostat",
 	"Mirostat": "Mirostat",
 	"Mirostat Eta": "Mirostat Eta",
 	"Mirostat Eta": "Mirostat Eta",
 	"Mirostat Tau": "Mirostat Tau",
 	"Mirostat Tau": "Mirostat Tau",
-	"MMMM DD, YYYY": "DD MMMM YYYY",
-	"MMMM DD, YYYY HH:mm": "DD MMMM YYYY HH:mm",
-	"MMMM DD, YYYY hh:mm:ss A": "DD MMMM YYYY HH:mm A",
 	"Model": "Modell",
 	"Model": "Modell",
 	"Model '{{modelName}}' has been successfully downloaded.": "Modell '{{modelName}}' wurde erfolgreich heruntergeladen.",
 	"Model '{{modelName}}' has been successfully downloaded.": "Modell '{{modelName}}' wurde erfolgreich heruntergeladen.",
 	"Model '{{modelTag}}' is already in queue for downloading.": "Modell '{{modelTag}}' befindet sich bereits in der Warteschlange zum Herunterladen.",
 	"Model '{{modelTag}}' is already in queue for downloading.": "Modell '{{modelTag}}' befindet sich bereits in der Warteschlange zum Herunterladen.",
@@ -719,6 +720,7 @@
 	"Please enter a prompt": "Bitte geben Sie einen Prompt ein",
 	"Please enter a prompt": "Bitte geben Sie einen Prompt ein",
 	"Please fill in all fields.": "Bitte füllen Sie alle Felder aus.",
 	"Please fill in all fields.": "Bitte füllen Sie alle Felder aus.",
 	"Please select a model first.": "",
 	"Please select a model first.": "",
+	"Please select a model.": "",
 	"Please select a reason": "Bitte wählen Sie einen Grund aus",
 	"Please select a reason": "Bitte wählen Sie einen Grund aus",
 	"Port": "Port",
 	"Port": "Port",
 	"Positive attitude": "Positive Einstellung",
 	"Positive attitude": "Positive Einstellung",
@@ -802,7 +804,7 @@
 	"Search options": "Suchoptionen",
 	"Search options": "Suchoptionen",
 	"Search Prompts": "Prompts durchsuchen...",
 	"Search Prompts": "Prompts durchsuchen...",
 	"Search Result Count": "Anzahl der Suchergebnisse",
 	"Search Result Count": "Anzahl der Suchergebnisse",
-	"Search the web": "Im Web suchen",
+	"Search the internet": "",
 	"Search Tools": "Werkzeuge durchsuchen...",
 	"Search Tools": "Werkzeuge durchsuchen...",
 	"SearchApi API Key": "SearchApi-API-Schlüssel",
 	"SearchApi API Key": "SearchApi-API-Schlüssel",
 	"SearchApi Engine": "SearchApi-Engine",
 	"SearchApi Engine": "SearchApi-Engine",
@@ -972,6 +974,7 @@
 	"Tools": "Werkzeuge",
 	"Tools": "Werkzeuge",
 	"Tools Access": "Werkzeugzugriff",
 	"Tools Access": "Werkzeugzugriff",
 	"Tools are a function calling system with arbitrary code execution": "Wekzeuge sind ein Funktionssystem mit beliebiger Codeausführung",
 	"Tools are a function calling system with arbitrary code execution": "Wekzeuge sind ein Funktionssystem mit beliebiger Codeausführung",
+	"Tools Function Calling Prompt": "",
 	"Tools have a function calling system that allows arbitrary code execution": "Werkezuge verfügen über ein Funktionssystem, das die Ausführung beliebigen Codes ermöglicht",
 	"Tools have a function calling system that allows arbitrary code execution": "Werkezuge verfügen über ein Funktionssystem, das die Ausführung beliebigen Codes ermöglicht",
 	"Tools have a function calling system that allows arbitrary code execution.": "Werkzeuge verfügen über ein Funktionssystem, das die Ausführung beliebigen Codes ermöglicht.",
 	"Tools have a function calling system that allows arbitrary code execution.": "Werkzeuge verfügen über ein Funktionssystem, das die Ausführung beliebigen Codes ermöglicht.",
 	"Top K": "Top K",
 	"Top K": "Top K",
@@ -1071,6 +1074,7 @@
 	"You can personalize your interactions with LLMs by adding memories through the 'Manage' button below, making them more helpful and tailored to you.": "Personalisieren Sie Interaktionen mit LLMs, indem Sie über die Schaltfläche \"Verwalten\" Erinnerungen hinzufügen.",
 	"You can personalize your interactions with LLMs by adding memories through the 'Manage' button below, making them more helpful and tailored to you.": "Personalisieren Sie Interaktionen mit LLMs, indem Sie über die Schaltfläche \"Verwalten\" Erinnerungen hinzufügen.",
 	"You cannot upload an empty file.": "Sie können keine leere Datei hochladen.",
 	"You cannot upload an empty file.": "Sie können keine leere Datei hochladen.",
 	"You do not have permission to access this feature.": "",
 	"You do not have permission to access this feature.": "",
+	"You do not have permission to upload files": "",
 	"You do not have permission to upload files.": "Sie haben keine Berechtigung zum Hochladen von Dateien.",
 	"You do not have permission to upload files.": "Sie haben keine Berechtigung zum Hochladen von Dateien.",
 	"You have no archived conversations.": "Du hast keine archivierten Unterhaltungen.",
 	"You have no archived conversations.": "Du hast keine archivierten Unterhaltungen.",
 	"You have shared this chat": "Sie haben diese Unterhaltung geteilt",
 	"You have shared this chat": "Sie haben diese Unterhaltung geteilt",

+ 10 - 6
src/lib/i18n/locales/dg-DG/translation.json

@@ -66,6 +66,8 @@
 	"Always-On Web Search": "",
 	"Always-On Web Search": "",
 	"Amazing": "",
 	"Amazing": "",
 	"an assistant": "such assistant",
 	"an assistant": "such assistant",
+	"Analyzed": "",
+	"Analyzing...": "",
 	"and": "and",
 	"and": "and",
 	"and {{COUNT}} more": "",
 	"and {{COUNT}} more": "",
 	"and create a new shared link.": "",
 	"and create a new shared link.": "",
@@ -170,9 +172,11 @@
 	"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "",
 	"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "",
 	"Clone": "",
 	"Clone": "",
 	"Clone Chat": "",
 	"Clone Chat": "",
+	"Clone of {{TITLE}}": "",
 	"Close": "Close",
 	"Close": "Close",
 	"Code execution": "",
 	"Code execution": "",
 	"Code formatted successfully": "",
 	"Code formatted successfully": "",
+	"Code Interpreter": "",
 	"Collection": "Collection",
 	"Collection": "Collection",
 	"Color": "",
 	"Color": "",
 	"ComfyUI": "",
 	"ComfyUI": "",
@@ -398,6 +402,7 @@
 	"Example: ou=users,dc=foo,dc=example": "",
 	"Example: ou=users,dc=foo,dc=example": "",
 	"Example: sAMAccountName or uid or userPrincipalName": "",
 	"Example: sAMAccountName or uid or userPrincipalName": "",
 	"Exclude": "",
 	"Exclude": "",
+	"Execute code for analysis": "",
 	"Experimental": "Much Experiment",
 	"Experimental": "Much Experiment",
 	"Explore the cosmos": "",
 	"Explore the cosmos": "",
 	"Export": "",
 	"Export": "",
@@ -464,6 +469,7 @@
 	"Functions imported successfully": "",
 	"Functions imported successfully": "",
 	"General": "Woweral",
 	"General": "Woweral",
 	"General Settings": "General Doge Settings",
 	"General Settings": "General Doge Settings",
+	"Generate an image": "",
 	"Generate Image": "",
 	"Generate Image": "",
 	"Generating search query": "",
 	"Generating search query": "",
 	"Get started": "",
 	"Get started": "",
@@ -479,7 +485,6 @@
 	"Group Name": "",
 	"Group Name": "",
 	"Group updated successfully": "",
 	"Group updated successfully": "",
 	"Groups": "",
 	"Groups": "",
-	"h:mm a": "",
 	"Haptic Feedback": "",
 	"Haptic Feedback": "",
 	"has no conversations.": "",
 	"has no conversations.": "",
 	"Hello, {{name}}": "Much helo, {{name}}",
 	"Hello, {{name}}": "Much helo, {{name}}",
@@ -497,7 +502,6 @@
 	"Ignite curiosity": "",
 	"Ignite curiosity": "",
 	"Image": "",
 	"Image": "",
 	"Image Compression": "",
 	"Image Compression": "",
-	"Image generation": "",
 	"Image Generation": "",
 	"Image Generation": "",
 	"Image Generation (Experimental)": "Image Wow (Much Experiment)",
 	"Image Generation (Experimental)": "Image Wow (Much Experiment)",
 	"Image Generation Engine": "Image Engine",
 	"Image Generation Engine": "Image Engine",
@@ -596,9 +600,6 @@
 	"Mirostat": "Mirostat",
 	"Mirostat": "Mirostat",
 	"Mirostat Eta": "Mirostat Eta",
 	"Mirostat Eta": "Mirostat Eta",
 	"Mirostat Tau": "Mirostat Tau",
 	"Mirostat Tau": "Mirostat Tau",
-	"MMMM DD, YYYY": "MMMM DD, YYYY",
-	"MMMM DD, YYYY HH:mm": "",
-	"MMMM DD, YYYY hh:mm:ss A": "",
 	"Model": "",
 	"Model": "",
 	"Model '{{modelName}}' has been successfully downloaded.": "Model '{{modelName}}' has been successfully downloaded.",
 	"Model '{{modelName}}' has been successfully downloaded.": "Model '{{modelName}}' has been successfully downloaded.",
 	"Model '{{modelTag}}' is already in queue for downloading.": "Model '{{modelTag}}' is already in queue for downloading.",
 	"Model '{{modelTag}}' is already in queue for downloading.": "Model '{{modelTag}}' is already in queue for downloading.",
@@ -719,6 +720,7 @@
 	"Please enter a prompt": "",
 	"Please enter a prompt": "",
 	"Please fill in all fields.": "",
 	"Please fill in all fields.": "",
 	"Please select a model first.": "",
 	"Please select a model first.": "",
+	"Please select a model.": "",
 	"Please select a reason": "",
 	"Please select a reason": "",
 	"Port": "",
 	"Port": "",
 	"Positive attitude": "",
 	"Positive attitude": "",
@@ -802,7 +804,7 @@
 	"Search options": "",
 	"Search options": "",
 	"Search Prompts": "Search Prompts much wow",
 	"Search Prompts": "Search Prompts much wow",
 	"Search Result Count": "",
 	"Search Result Count": "",
-	"Search the web": "",
+	"Search the internet": "",
 	"Search Tools": "",
 	"Search Tools": "",
 	"SearchApi API Key": "",
 	"SearchApi API Key": "",
 	"SearchApi Engine": "",
 	"SearchApi Engine": "",
@@ -972,6 +974,7 @@
 	"Tools": "",
 	"Tools": "",
 	"Tools Access": "",
 	"Tools Access": "",
 	"Tools are a function calling system with arbitrary code execution": "",
 	"Tools are a function calling system with arbitrary code execution": "",
+	"Tools Function Calling Prompt": "",
 	"Tools have a function calling system that allows arbitrary code execution": "",
 	"Tools have a function calling system that allows arbitrary code execution": "",
 	"Tools have a function calling system that allows arbitrary code execution.": "",
 	"Tools have a function calling system that allows arbitrary code execution.": "",
 	"Top K": "Top K very top",
 	"Top K": "Top K very top",
@@ -1071,6 +1074,7 @@
 	"You can personalize your interactions with LLMs by adding memories through the 'Manage' button below, making them more helpful and tailored to you.": "",
 	"You can personalize your interactions with LLMs by adding memories through the 'Manage' button below, making them more helpful and tailored to you.": "",
 	"You cannot upload an empty file.": "",
 	"You cannot upload an empty file.": "",
 	"You do not have permission to access this feature.": "",
 	"You do not have permission to access this feature.": "",
+	"You do not have permission to upload files": "",
 	"You do not have permission to upload files.": "",
 	"You do not have permission to upload files.": "",
 	"You have no archived conversations.": "",
 	"You have no archived conversations.": "",
 	"You have shared this chat": "",
 	"You have shared this chat": "",

+ 10 - 6
src/lib/i18n/locales/el-GR/translation.json

@@ -66,6 +66,8 @@
 	"Always-On Web Search": "",
 	"Always-On Web Search": "",
 	"Amazing": "Καταπληκτικό",
 	"Amazing": "Καταπληκτικό",
 	"an assistant": "ένας βοηθός",
 	"an assistant": "ένας βοηθός",
+	"Analyzed": "",
+	"Analyzing...": "",
 	"and": "και",
 	"and": "και",
 	"and {{COUNT}} more": "και {{COUNT}} ακόμα",
 	"and {{COUNT}} more": "και {{COUNT}} ακόμα",
 	"and create a new shared link.": "και δημιουργήστε έναν νέο κοινόχρηστο σύνδεσμο.",
 	"and create a new shared link.": "και δημιουργήστε έναν νέο κοινόχρηστο σύνδεσμο.",
@@ -170,9 +172,11 @@
 	"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "Άρνηση δικαιώματος εγγραφής στο πρόχειρο. Παρακαλώ ελέγξτε τις ρυθμίσεις του περιηγητή σας για να δώσετε την απαραίτητη πρόσβαση.",
 	"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "Άρνηση δικαιώματος εγγραφής στο πρόχειρο. Παρακαλώ ελέγξτε τις ρυθμίσεις του περιηγητή σας για να δώσετε την απαραίτητη πρόσβαση.",
 	"Clone": "Κλώνος",
 	"Clone": "Κλώνος",
 	"Clone Chat": "",
 	"Clone Chat": "",
+	"Clone of {{TITLE}}": "",
 	"Close": "Κλείσιμο",
 	"Close": "Κλείσιμο",
 	"Code execution": "Εκτέλεση κώδικα",
 	"Code execution": "Εκτέλεση κώδικα",
 	"Code formatted successfully": "Ο κώδικας μορφοποιήθηκε επιτυχώς",
 	"Code formatted successfully": "Ο κώδικας μορφοποιήθηκε επιτυχώς",
+	"Code Interpreter": "",
 	"Collection": "Συλλογή",
 	"Collection": "Συλλογή",
 	"Color": "Χρώμα",
 	"Color": "Χρώμα",
 	"ComfyUI": "ComfyUI",
 	"ComfyUI": "ComfyUI",
@@ -398,6 +402,7 @@
 	"Example: ou=users,dc=foo,dc=example": "Παράδειγμα: ou=users,dc=foo,dc=example",
 	"Example: ou=users,dc=foo,dc=example": "Παράδειγμα: ou=users,dc=foo,dc=example",
 	"Example: sAMAccountName or uid or userPrincipalName": "Παράδειγμα: sAMAccountName ή uid ή userPrincipalName",
 	"Example: sAMAccountName or uid or userPrincipalName": "Παράδειγμα: sAMAccountName ή uid ή userPrincipalName",
 	"Exclude": "Εξαίρεση",
 	"Exclude": "Εξαίρεση",
+	"Execute code for analysis": "",
 	"Experimental": "Πειραματικό",
 	"Experimental": "Πειραματικό",
 	"Explore the cosmos": "Εξερευνήστε το σύμπαν",
 	"Explore the cosmos": "Εξερευνήστε το σύμπαν",
 	"Export": "Εξαγωγή",
 	"Export": "Εξαγωγή",
@@ -464,6 +469,7 @@
 	"Functions imported successfully": "Οι λειτουργίες εισήχθησαν με επιτυχία",
 	"Functions imported successfully": "Οι λειτουργίες εισήχθησαν με επιτυχία",
 	"General": "Γενικά",
 	"General": "Γενικά",
 	"General Settings": "Γενικές Ρυθμίσεις",
 	"General Settings": "Γενικές Ρυθμίσεις",
+	"Generate an image": "",
 	"Generate Image": "Δημιουργία Εικόνας",
 	"Generate Image": "Δημιουργία Εικόνας",
 	"Generating search query": "Γενιά αναζήτησης ερώτησης",
 	"Generating search query": "Γενιά αναζήτησης ερώτησης",
 	"Get started": "Ξεκινήστε",
 	"Get started": "Ξεκινήστε",
@@ -479,7 +485,6 @@
 	"Group Name": "Όνομα Ομάδας",
 	"Group Name": "Όνομα Ομάδας",
 	"Group updated successfully": "Η ομάδα ενημερώθηκε με επιτυχία",
 	"Group updated successfully": "Η ομάδα ενημερώθηκε με επιτυχία",
 	"Groups": "Ομάδες",
 	"Groups": "Ομάδες",
-	"h:mm a": "h:mm π.μ./μ.μ.",
 	"Haptic Feedback": "Ανατροφοδότηση Haptic",
 	"Haptic Feedback": "Ανατροφοδότηση Haptic",
 	"has no conversations.": "δεν έχει συνομιλίες.",
 	"has no conversations.": "δεν έχει συνομιλίες.",
 	"Hello, {{name}}": "Γειά σου, {{name}}",
 	"Hello, {{name}}": "Γειά σου, {{name}}",
@@ -497,7 +502,6 @@
 	"Ignite curiosity": "Ξύπνημα της περιέργειας",
 	"Ignite curiosity": "Ξύπνημα της περιέργειας",
 	"Image": "",
 	"Image": "",
 	"Image Compression": "",
 	"Image Compression": "",
-	"Image generation": "",
 	"Image Generation": "",
 	"Image Generation": "",
 	"Image Generation (Experimental)": "Δημιουργία Εικόνας (Πειραματικό)",
 	"Image Generation (Experimental)": "Δημιουργία Εικόνας (Πειραματικό)",
 	"Image Generation Engine": "Μηχανή Δημιουργίας Εικόνας",
 	"Image Generation Engine": "Μηχανή Δημιουργίας Εικόνας",
@@ -596,9 +600,6 @@
 	"Mirostat": "Mirostat",
 	"Mirostat": "Mirostat",
 	"Mirostat Eta": "Mirostat Eta",
 	"Mirostat Eta": "Mirostat Eta",
 	"Mirostat Tau": "Mirostat Tau",
 	"Mirostat Tau": "Mirostat Tau",
-	"MMMM DD, YYYY": "MMMM DD, YYYY",
-	"MMMM DD, YYYY HH:mm": "MMMM DD, YYYY HH:mm",
-	"MMMM DD, YYYY hh:mm:ss A": "MMMM DD, YYYY hh:mm:ss A",
 	"Model": "Μοντέλο",
 	"Model": "Μοντέλο",
 	"Model '{{modelName}}' has been successfully downloaded.": "Το μοντέλο '{{modelName}}' κατεβάστηκε με επιτυχία.",
 	"Model '{{modelName}}' has been successfully downloaded.": "Το μοντέλο '{{modelName}}' κατεβάστηκε με επιτυχία.",
 	"Model '{{modelTag}}' is already in queue for downloading.": "Το μοντέλο '{{modelTag}}' βρίσκεται ήδη στην ουρά για λήψη.",
 	"Model '{{modelTag}}' is already in queue for downloading.": "Το μοντέλο '{{modelTag}}' βρίσκεται ήδη στην ουρά για λήψη.",
@@ -719,6 +720,7 @@
 	"Please enter a prompt": "Παρακαλώ εισάγετε μια προτροπή",
 	"Please enter a prompt": "Παρακαλώ εισάγετε μια προτροπή",
 	"Please fill in all fields.": "Παρακαλώ συμπληρώστε όλα τα πεδία.",
 	"Please fill in all fields.": "Παρακαλώ συμπληρώστε όλα τα πεδία.",
 	"Please select a model first.": "",
 	"Please select a model first.": "",
+	"Please select a model.": "",
 	"Please select a reason": "Παρακαλώ επιλέξτε έναν λόγο",
 	"Please select a reason": "Παρακαλώ επιλέξτε έναν λόγο",
 	"Port": "Θύρα",
 	"Port": "Θύρα",
 	"Positive attitude": "Θετική στάση",
 	"Positive attitude": "Θετική στάση",
@@ -802,7 +804,7 @@
 	"Search options": "Επιλογές Αναζήτησης",
 	"Search options": "Επιλογές Αναζήτησης",
 	"Search Prompts": "Αναζήτηση Προτροπών",
 	"Search Prompts": "Αναζήτηση Προτροπών",
 	"Search Result Count": "Αριθμός Αποτελεσμάτων Αναζήτησης",
 	"Search Result Count": "Αριθμός Αποτελεσμάτων Αναζήτησης",
-	"Search the web": "Αναζήτηση στο διαδίκτυο",
+	"Search the internet": "",
 	"Search Tools": "Αναζήτηση Εργαλείων",
 	"Search Tools": "Αναζήτηση Εργαλείων",
 	"SearchApi API Key": "Κλειδί API SearchApi",
 	"SearchApi API Key": "Κλειδί API SearchApi",
 	"SearchApi Engine": "Μηχανή SearchApi",
 	"SearchApi Engine": "Μηχανή SearchApi",
@@ -972,6 +974,7 @@
 	"Tools": "Εργαλεία",
 	"Tools": "Εργαλεία",
 	"Tools Access": "Πρόσβαση Εργαλείων",
 	"Tools Access": "Πρόσβαση Εργαλείων",
 	"Tools are a function calling system with arbitrary code execution": "Τα εργαλεία είναι ένα σύστημα κλήσης λειτουργιών με αυθαίρετη εκτέλεση κώδικα",
 	"Tools are a function calling system with arbitrary code execution": "Τα εργαλεία είναι ένα σύστημα κλήσης λειτουργιών με αυθαίρετη εκτέλεση κώδικα",
+	"Tools Function Calling Prompt": "",
 	"Tools have a function calling system that allows arbitrary code execution": "Τα εργαλεία διαθέτουν ένα σύστημα κλήσης λειτουργιών που επιτρέπει την αυθαίρετη εκτέλεση κώδικα",
 	"Tools have a function calling system that allows arbitrary code execution": "Τα εργαλεία διαθέτουν ένα σύστημα κλήσης λειτουργιών που επιτρέπει την αυθαίρετη εκτέλεση κώδικα",
 	"Tools have a function calling system that allows arbitrary code execution.": "Τα εργαλεία διαθέτουν ένα σύστημα κλήσης λειτουργιών που επιτρέπει την αυθαίρετη εκτέλεση κώδικα.",
 	"Tools have a function calling system that allows arbitrary code execution.": "Τα εργαλεία διαθέτουν ένα σύστημα κλήσης λειτουργιών που επιτρέπει την αυθαίρετη εκτέλεση κώδικα.",
 	"Top K": "Top K",
 	"Top K": "Top K",
@@ -1071,6 +1074,7 @@
 	"You can personalize your interactions with LLMs by adding memories through the 'Manage' button below, making them more helpful and tailored to you.": "Μπορείτε να προσωποποιήσετε τις αλληλεπιδράσεις σας με τα LLMs προσθέτοντας αναμνήσεις μέσω του κουμπιού 'Διαχείριση' παρακάτω, κάνοντάς τα πιο χρήσιμα και προσαρμοσμένα σε εσάς.",
 	"You can personalize your interactions with LLMs by adding memories through the 'Manage' button below, making them more helpful and tailored to you.": "Μπορείτε να προσωποποιήσετε τις αλληλεπιδράσεις σας με τα LLMs προσθέτοντας αναμνήσεις μέσω του κουμπιού 'Διαχείριση' παρακάτω, κάνοντάς τα πιο χρήσιμα και προσαρμοσμένα σε εσάς.",
 	"You cannot upload an empty file.": "Δεν μπορείτε να ανεβάσετε ένα κενό αρχείο.",
 	"You cannot upload an empty file.": "Δεν μπορείτε να ανεβάσετε ένα κενό αρχείο.",
 	"You do not have permission to access this feature.": "",
 	"You do not have permission to access this feature.": "",
+	"You do not have permission to upload files": "",
 	"You do not have permission to upload files.": "Δεν έχετε άδεια να ανεβάσετε αρχεία.",
 	"You do not have permission to upload files.": "Δεν έχετε άδεια να ανεβάσετε αρχεία.",
 	"You have no archived conversations.": "Δεν έχετε αρχειοθετημένες συνομιλίες.",
 	"You have no archived conversations.": "Δεν έχετε αρχειοθετημένες συνομιλίες.",
 	"You have shared this chat": "Έχετε μοιραστεί αυτή τη συνομιλία",
 	"You have shared this chat": "Έχετε μοιραστεί αυτή τη συνομιλία",

+ 10 - 6
src/lib/i18n/locales/en-GB/translation.json

@@ -66,6 +66,8 @@
 	"Always-On Web Search": "",
 	"Always-On Web Search": "",
 	"Amazing": "",
 	"Amazing": "",
 	"an assistant": "",
 	"an assistant": "",
+	"Analyzed": "",
+	"Analyzing...": "",
 	"and": "",
 	"and": "",
 	"and {{COUNT}} more": "",
 	"and {{COUNT}} more": "",
 	"and create a new shared link.": "",
 	"and create a new shared link.": "",
@@ -170,9 +172,11 @@
 	"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "",
 	"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "",
 	"Clone": "",
 	"Clone": "",
 	"Clone Chat": "",
 	"Clone Chat": "",
+	"Clone of {{TITLE}}": "",
 	"Close": "",
 	"Close": "",
 	"Code execution": "",
 	"Code execution": "",
 	"Code formatted successfully": "",
 	"Code formatted successfully": "",
+	"Code Interpreter": "",
 	"Collection": "",
 	"Collection": "",
 	"Color": "",
 	"Color": "",
 	"ComfyUI": "",
 	"ComfyUI": "",
@@ -398,6 +402,7 @@
 	"Example: ou=users,dc=foo,dc=example": "",
 	"Example: ou=users,dc=foo,dc=example": "",
 	"Example: sAMAccountName or uid or userPrincipalName": "",
 	"Example: sAMAccountName or uid or userPrincipalName": "",
 	"Exclude": "",
 	"Exclude": "",
+	"Execute code for analysis": "",
 	"Experimental": "",
 	"Experimental": "",
 	"Explore the cosmos": "",
 	"Explore the cosmos": "",
 	"Export": "",
 	"Export": "",
@@ -464,6 +469,7 @@
 	"Functions imported successfully": "",
 	"Functions imported successfully": "",
 	"General": "",
 	"General": "",
 	"General Settings": "",
 	"General Settings": "",
+	"Generate an image": "",
 	"Generate Image": "",
 	"Generate Image": "",
 	"Generating search query": "",
 	"Generating search query": "",
 	"Get started": "",
 	"Get started": "",
@@ -479,7 +485,6 @@
 	"Group Name": "",
 	"Group Name": "",
 	"Group updated successfully": "",
 	"Group updated successfully": "",
 	"Groups": "",
 	"Groups": "",
-	"h:mm a": "",
 	"Haptic Feedback": "",
 	"Haptic Feedback": "",
 	"has no conversations.": "",
 	"has no conversations.": "",
 	"Hello, {{name}}": "",
 	"Hello, {{name}}": "",
@@ -497,7 +502,6 @@
 	"Ignite curiosity": "",
 	"Ignite curiosity": "",
 	"Image": "",
 	"Image": "",
 	"Image Compression": "",
 	"Image Compression": "",
-	"Image generation": "",
 	"Image Generation": "",
 	"Image Generation": "",
 	"Image Generation (Experimental)": "",
 	"Image Generation (Experimental)": "",
 	"Image Generation Engine": "",
 	"Image Generation Engine": "",
@@ -596,9 +600,6 @@
 	"Mirostat": "",
 	"Mirostat": "",
 	"Mirostat Eta": "",
 	"Mirostat Eta": "",
 	"Mirostat Tau": "",
 	"Mirostat Tau": "",
-	"MMMM DD, YYYY": "",
-	"MMMM DD, YYYY HH:mm": "",
-	"MMMM DD, YYYY hh:mm:ss A": "",
 	"Model": "",
 	"Model": "",
 	"Model '{{modelName}}' has been successfully downloaded.": "",
 	"Model '{{modelName}}' has been successfully downloaded.": "",
 	"Model '{{modelTag}}' is already in queue for downloading.": "",
 	"Model '{{modelTag}}' is already in queue for downloading.": "",
@@ -719,6 +720,7 @@
 	"Please enter a prompt": "",
 	"Please enter a prompt": "",
 	"Please fill in all fields.": "",
 	"Please fill in all fields.": "",
 	"Please select a model first.": "",
 	"Please select a model first.": "",
+	"Please select a model.": "",
 	"Please select a reason": "",
 	"Please select a reason": "",
 	"Port": "",
 	"Port": "",
 	"Positive attitude": "",
 	"Positive attitude": "",
@@ -802,7 +804,7 @@
 	"Search options": "",
 	"Search options": "",
 	"Search Prompts": "",
 	"Search Prompts": "",
 	"Search Result Count": "",
 	"Search Result Count": "",
-	"Search the web": "",
+	"Search the internet": "",
 	"Search Tools": "",
 	"Search Tools": "",
 	"SearchApi API Key": "",
 	"SearchApi API Key": "",
 	"SearchApi Engine": "",
 	"SearchApi Engine": "",
@@ -972,6 +974,7 @@
 	"Tools": "",
 	"Tools": "",
 	"Tools Access": "",
 	"Tools Access": "",
 	"Tools are a function calling system with arbitrary code execution": "",
 	"Tools are a function calling system with arbitrary code execution": "",
+	"Tools Function Calling Prompt": "",
 	"Tools have a function calling system that allows arbitrary code execution": "",
 	"Tools have a function calling system that allows arbitrary code execution": "",
 	"Tools have a function calling system that allows arbitrary code execution.": "",
 	"Tools have a function calling system that allows arbitrary code execution.": "",
 	"Top K": "",
 	"Top K": "",
@@ -1071,6 +1074,7 @@
 	"You can personalize your interactions with LLMs by adding memories through the 'Manage' button below, making them more helpful and tailored to you.": "",
 	"You can personalize your interactions with LLMs by adding memories through the 'Manage' button below, making them more helpful and tailored to you.": "",
 	"You cannot upload an empty file.": "",
 	"You cannot upload an empty file.": "",
 	"You do not have permission to access this feature.": "",
 	"You do not have permission to access this feature.": "",
+	"You do not have permission to upload files": "",
 	"You do not have permission to upload files.": "",
 	"You do not have permission to upload files.": "",
 	"You have no archived conversations.": "",
 	"You have no archived conversations.": "",
 	"You have shared this chat": "",
 	"You have shared this chat": "",

+ 10 - 6
src/lib/i18n/locales/en-US/translation.json

@@ -66,6 +66,8 @@
 	"Always-On Web Search": "",
 	"Always-On Web Search": "",
 	"Amazing": "",
 	"Amazing": "",
 	"an assistant": "",
 	"an assistant": "",
+	"Analyzed": "",
+	"Analyzing...": "",
 	"and": "",
 	"and": "",
 	"and {{COUNT}} more": "",
 	"and {{COUNT}} more": "",
 	"and create a new shared link.": "",
 	"and create a new shared link.": "",
@@ -170,9 +172,11 @@
 	"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "",
 	"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "",
 	"Clone": "",
 	"Clone": "",
 	"Clone Chat": "",
 	"Clone Chat": "",
+	"Clone of {{TITLE}}": "",
 	"Close": "",
 	"Close": "",
 	"Code execution": "",
 	"Code execution": "",
 	"Code formatted successfully": "",
 	"Code formatted successfully": "",
+	"Code Interpreter": "",
 	"Collection": "",
 	"Collection": "",
 	"Color": "",
 	"Color": "",
 	"ComfyUI": "",
 	"ComfyUI": "",
@@ -398,6 +402,7 @@
 	"Example: ou=users,dc=foo,dc=example": "",
 	"Example: ou=users,dc=foo,dc=example": "",
 	"Example: sAMAccountName or uid or userPrincipalName": "",
 	"Example: sAMAccountName or uid or userPrincipalName": "",
 	"Exclude": "",
 	"Exclude": "",
+	"Execute code for analysis": "",
 	"Experimental": "",
 	"Experimental": "",
 	"Explore the cosmos": "",
 	"Explore the cosmos": "",
 	"Export": "",
 	"Export": "",
@@ -464,6 +469,7 @@
 	"Functions imported successfully": "",
 	"Functions imported successfully": "",
 	"General": "",
 	"General": "",
 	"General Settings": "",
 	"General Settings": "",
+	"Generate an image": "",
 	"Generate Image": "",
 	"Generate Image": "",
 	"Generating search query": "",
 	"Generating search query": "",
 	"Get started": "",
 	"Get started": "",
@@ -479,7 +485,6 @@
 	"Group Name": "",
 	"Group Name": "",
 	"Group updated successfully": "",
 	"Group updated successfully": "",
 	"Groups": "",
 	"Groups": "",
-	"h:mm a": "",
 	"Haptic Feedback": "",
 	"Haptic Feedback": "",
 	"has no conversations.": "",
 	"has no conversations.": "",
 	"Hello, {{name}}": "",
 	"Hello, {{name}}": "",
@@ -497,7 +502,6 @@
 	"Ignite curiosity": "",
 	"Ignite curiosity": "",
 	"Image": "",
 	"Image": "",
 	"Image Compression": "",
 	"Image Compression": "",
-	"Image generation": "",
 	"Image Generation": "",
 	"Image Generation": "",
 	"Image Generation (Experimental)": "",
 	"Image Generation (Experimental)": "",
 	"Image Generation Engine": "",
 	"Image Generation Engine": "",
@@ -596,9 +600,6 @@
 	"Mirostat": "",
 	"Mirostat": "",
 	"Mirostat Eta": "",
 	"Mirostat Eta": "",
 	"Mirostat Tau": "",
 	"Mirostat Tau": "",
-	"MMMM DD, YYYY": "",
-	"MMMM DD, YYYY HH:mm": "",
-	"MMMM DD, YYYY hh:mm:ss A": "",
 	"Model": "",
 	"Model": "",
 	"Model '{{modelName}}' has been successfully downloaded.": "",
 	"Model '{{modelName}}' has been successfully downloaded.": "",
 	"Model '{{modelTag}}' is already in queue for downloading.": "",
 	"Model '{{modelTag}}' is already in queue for downloading.": "",
@@ -719,6 +720,7 @@
 	"Please enter a prompt": "",
 	"Please enter a prompt": "",
 	"Please fill in all fields.": "",
 	"Please fill in all fields.": "",
 	"Please select a model first.": "",
 	"Please select a model first.": "",
+	"Please select a model.": "",
 	"Please select a reason": "",
 	"Please select a reason": "",
 	"Port": "",
 	"Port": "",
 	"Positive attitude": "",
 	"Positive attitude": "",
@@ -802,7 +804,7 @@
 	"Search options": "",
 	"Search options": "",
 	"Search Prompts": "",
 	"Search Prompts": "",
 	"Search Result Count": "",
 	"Search Result Count": "",
-	"Search the web": "",
+	"Search the internet": "",
 	"Search Tools": "",
 	"Search Tools": "",
 	"SearchApi API Key": "",
 	"SearchApi API Key": "",
 	"SearchApi Engine": "",
 	"SearchApi Engine": "",
@@ -972,6 +974,7 @@
 	"Tools": "",
 	"Tools": "",
 	"Tools Access": "",
 	"Tools Access": "",
 	"Tools are a function calling system with arbitrary code execution": "",
 	"Tools are a function calling system with arbitrary code execution": "",
+	"Tools Function Calling Prompt": "",
 	"Tools have a function calling system that allows arbitrary code execution": "",
 	"Tools have a function calling system that allows arbitrary code execution": "",
 	"Tools have a function calling system that allows arbitrary code execution.": "",
 	"Tools have a function calling system that allows arbitrary code execution.": "",
 	"Top K": "",
 	"Top K": "",
@@ -1071,6 +1074,7 @@
 	"You can personalize your interactions with LLMs by adding memories through the 'Manage' button below, making them more helpful and tailored to you.": "",
 	"You can personalize your interactions with LLMs by adding memories through the 'Manage' button below, making them more helpful and tailored to you.": "",
 	"You cannot upload an empty file.": "",
 	"You cannot upload an empty file.": "",
 	"You do not have permission to access this feature.": "",
 	"You do not have permission to access this feature.": "",
+	"You do not have permission to upload files": "",
 	"You do not have permission to upload files.": "",
 	"You do not have permission to upload files.": "",
 	"You have no archived conversations.": "",
 	"You have no archived conversations.": "",
 	"You have shared this chat": "",
 	"You have shared this chat": "",

+ 10 - 6
src/lib/i18n/locales/es-ES/translation.json

@@ -66,6 +66,8 @@
 	"Always-On Web Search": "",
 	"Always-On Web Search": "",
 	"Amazing": "",
 	"Amazing": "",
 	"an assistant": "un asistente",
 	"an assistant": "un asistente",
+	"Analyzed": "",
+	"Analyzing...": "",
 	"and": "y",
 	"and": "y",
 	"and {{COUNT}} more": "",
 	"and {{COUNT}} more": "",
 	"and create a new shared link.": "y crear un nuevo enlace compartido.",
 	"and create a new shared link.": "y crear un nuevo enlace compartido.",
@@ -170,9 +172,11 @@
 	"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "Permisos de escritura del portapapeles denegados. Por favor, comprueba las configuraciones de tu navegador para otorgar el acceso necesario.",
 	"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "Permisos de escritura del portapapeles denegados. Por favor, comprueba las configuraciones de tu navegador para otorgar el acceso necesario.",
 	"Clone": "Clonar",
 	"Clone": "Clonar",
 	"Clone Chat": "",
 	"Clone Chat": "",
+	"Clone of {{TITLE}}": "",
 	"Close": "Cerrar",
 	"Close": "Cerrar",
 	"Code execution": "",
 	"Code execution": "",
 	"Code formatted successfully": "Se ha formateado correctamente el código.",
 	"Code formatted successfully": "Se ha formateado correctamente el código.",
+	"Code Interpreter": "",
 	"Collection": "Colección",
 	"Collection": "Colección",
 	"Color": "",
 	"Color": "",
 	"ComfyUI": "ComfyUI",
 	"ComfyUI": "ComfyUI",
@@ -398,6 +402,7 @@
 	"Example: ou=users,dc=foo,dc=example": "",
 	"Example: ou=users,dc=foo,dc=example": "",
 	"Example: sAMAccountName or uid or userPrincipalName": "",
 	"Example: sAMAccountName or uid or userPrincipalName": "",
 	"Exclude": "",
 	"Exclude": "",
+	"Execute code for analysis": "",
 	"Experimental": "Experimental",
 	"Experimental": "Experimental",
 	"Explore the cosmos": "",
 	"Explore the cosmos": "",
 	"Export": "Exportar",
 	"Export": "Exportar",
@@ -464,6 +469,7 @@
 	"Functions imported successfully": "Funciones importadas exitosamente",
 	"Functions imported successfully": "Funciones importadas exitosamente",
 	"General": "General",
 	"General": "General",
 	"General Settings": "Opciones Generales",
 	"General Settings": "Opciones Generales",
+	"Generate an image": "",
 	"Generate Image": "Generar imagen",
 	"Generate Image": "Generar imagen",
 	"Generating search query": "Generación de consultas de búsqueda",
 	"Generating search query": "Generación de consultas de búsqueda",
 	"Get started": "",
 	"Get started": "",
@@ -479,7 +485,6 @@
 	"Group Name": "",
 	"Group Name": "",
 	"Group updated successfully": "",
 	"Group updated successfully": "",
 	"Groups": "",
 	"Groups": "",
-	"h:mm a": "h:mm a",
 	"Haptic Feedback": "Retroalimentación háptica",
 	"Haptic Feedback": "Retroalimentación háptica",
 	"has no conversations.": "no tiene conversaciones.",
 	"has no conversations.": "no tiene conversaciones.",
 	"Hello, {{name}}": "Hola, {{name}}",
 	"Hello, {{name}}": "Hola, {{name}}",
@@ -497,7 +502,6 @@
 	"Ignite curiosity": "",
 	"Ignite curiosity": "",
 	"Image": "",
 	"Image": "",
 	"Image Compression": "",
 	"Image Compression": "",
-	"Image generation": "",
 	"Image Generation": "",
 	"Image Generation": "",
 	"Image Generation (Experimental)": "Generación de imágenes (experimental)",
 	"Image Generation (Experimental)": "Generación de imágenes (experimental)",
 	"Image Generation Engine": "Motor de generación de imágenes",
 	"Image Generation Engine": "Motor de generación de imágenes",
@@ -596,9 +600,6 @@
 	"Mirostat": "Mirostat",
 	"Mirostat": "Mirostat",
 	"Mirostat Eta": "Mirostat Eta",
 	"Mirostat Eta": "Mirostat Eta",
 	"Mirostat Tau": "Mirostat Tau",
 	"Mirostat Tau": "Mirostat Tau",
-	"MMMM DD, YYYY": "MMMM DD, YYYY",
-	"MMMM DD, YYYY HH:mm": "MMMM DD, YYYY HH:mm",
-	"MMMM DD, YYYY hh:mm:ss A": "MMMM DD, YYYY hh:mm:ss A",
 	"Model": "",
 	"Model": "",
 	"Model '{{modelName}}' has been successfully downloaded.": "El modelo '{{modelName}}' se ha descargado correctamente.",
 	"Model '{{modelName}}' has been successfully downloaded.": "El modelo '{{modelName}}' se ha descargado correctamente.",
 	"Model '{{modelTag}}' is already in queue for downloading.": "El modelo '{{modelTag}}' ya está en cola para descargar.",
 	"Model '{{modelTag}}' is already in queue for downloading.": "El modelo '{{modelTag}}' ya está en cola para descargar.",
@@ -719,6 +720,7 @@
 	"Please enter a prompt": "",
 	"Please enter a prompt": "",
 	"Please fill in all fields.": "Por favor llene todos los campos.",
 	"Please fill in all fields.": "Por favor llene todos los campos.",
 	"Please select a model first.": "",
 	"Please select a model first.": "",
+	"Please select a model.": "",
 	"Please select a reason": "Por favor seleccione una razón",
 	"Please select a reason": "Por favor seleccione una razón",
 	"Port": "",
 	"Port": "",
 	"Positive attitude": "Actitud positiva",
 	"Positive attitude": "Actitud positiva",
@@ -802,7 +804,7 @@
 	"Search options": "",
 	"Search options": "",
 	"Search Prompts": "Buscar Prompts",
 	"Search Prompts": "Buscar Prompts",
 	"Search Result Count": "Recuento de resultados de búsqueda",
 	"Search Result Count": "Recuento de resultados de búsqueda",
-	"Search the web": "",
+	"Search the internet": "",
 	"Search Tools": "Búsqueda de herramientas",
 	"Search Tools": "Búsqueda de herramientas",
 	"SearchApi API Key": "Clave API de SearchApi",
 	"SearchApi API Key": "Clave API de SearchApi",
 	"SearchApi Engine": "Motor de SearchApi",
 	"SearchApi Engine": "Motor de SearchApi",
@@ -972,6 +974,7 @@
 	"Tools": "Herramientas",
 	"Tools": "Herramientas",
 	"Tools Access": "",
 	"Tools Access": "",
 	"Tools are a function calling system with arbitrary code execution": "Las herramientas son un sistema de llamada de funciones con código arbitrario",
 	"Tools are a function calling system with arbitrary code execution": "Las herramientas son un sistema de llamada de funciones con código arbitrario",
+	"Tools Function Calling Prompt": "",
 	"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 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 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.",
 	"Top K": "Top K",
 	"Top K": "Top K",
@@ -1071,6 +1074,7 @@
 	"You can personalize your interactions with LLMs by adding memories through the 'Manage' button below, making them more helpful and tailored to you.": "Puede personalizar sus interacciones con LLMs añadiendo memorias a través del botón 'Gestionar' debajo, haciendo que sean más útiles y personalizados para usted.",
 	"You can personalize your interactions with LLMs by adding memories through the 'Manage' button below, making them more helpful and tailored to you.": "Puede personalizar sus interacciones con LLMs añadiendo memorias a través del botón 'Gestionar' debajo, haciendo que sean más útiles y personalizados para usted.",
 	"You cannot upload an empty file.": "",
 	"You cannot upload an empty file.": "",
 	"You do not have permission to access this feature.": "",
 	"You do not have permission to access this feature.": "",
+	"You do not have permission to upload files": "",
 	"You do not have permission to upload files.": "",
 	"You do not have permission to upload files.": "",
 	"You have no archived conversations.": "No tiene conversaciones archivadas.",
 	"You have no archived conversations.": "No tiene conversaciones archivadas.",
 	"You have shared this chat": "Usted ha compartido esta conversación",
 	"You have shared this chat": "Usted ha compartido esta conversación",

+ 10 - 6
src/lib/i18n/locales/eu-ES/translation.json

@@ -66,6 +66,8 @@
 	"Always-On Web Search": "",
 	"Always-On Web Search": "",
 	"Amazing": "Harrigarria",
 	"Amazing": "Harrigarria",
 	"an assistant": "laguntzaile bat",
 	"an assistant": "laguntzaile bat",
+	"Analyzed": "",
+	"Analyzing...": "",
 	"and": "eta",
 	"and": "eta",
 	"and {{COUNT}} more": "eta {{COUNT}} gehiago",
 	"and {{COUNT}} more": "eta {{COUNT}} gehiago",
 	"and create a new shared link.": "eta sortu partekatutako esteka berri bat.",
 	"and create a new shared link.": "eta sortu partekatutako esteka berri bat.",
@@ -170,9 +172,11 @@
 	"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "Arbelerako idazteko baimena ukatua. Mesedez, egiaztatu zure nabigatzailearen ezarpenak beharrezko sarbidea emateko.",
 	"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "Arbelerako idazteko baimena ukatua. Mesedez, egiaztatu zure nabigatzailearen ezarpenak beharrezko sarbidea emateko.",
 	"Clone": "Klonatu",
 	"Clone": "Klonatu",
 	"Clone Chat": "",
 	"Clone Chat": "",
+	"Clone of {{TITLE}}": "",
 	"Close": "Itxi",
 	"Close": "Itxi",
 	"Code execution": "Kodearen exekuzioa",
 	"Code execution": "Kodearen exekuzioa",
 	"Code formatted successfully": "Kodea ongi formateatu da",
 	"Code formatted successfully": "Kodea ongi formateatu da",
+	"Code Interpreter": "",
 	"Collection": "Bilduma",
 	"Collection": "Bilduma",
 	"Color": "Kolorea",
 	"Color": "Kolorea",
 	"ComfyUI": "ComfyUI",
 	"ComfyUI": "ComfyUI",
@@ -398,6 +402,7 @@
 	"Example: ou=users,dc=foo,dc=example": "Adibidea: ou=users,dc=foo,dc=example",
 	"Example: ou=users,dc=foo,dc=example": "Adibidea: ou=users,dc=foo,dc=example",
 	"Example: sAMAccountName or uid or userPrincipalName": "Adibidea: sAMAccountName edo uid edo userPrincipalName",
 	"Example: sAMAccountName or uid or userPrincipalName": "Adibidea: sAMAccountName edo uid edo userPrincipalName",
 	"Exclude": "Baztertu",
 	"Exclude": "Baztertu",
+	"Execute code for analysis": "",
 	"Experimental": "Esperimentala",
 	"Experimental": "Esperimentala",
 	"Explore the cosmos": "Esploratu kosmosa",
 	"Explore the cosmos": "Esploratu kosmosa",
 	"Export": "Esportatu",
 	"Export": "Esportatu",
@@ -464,6 +469,7 @@
 	"Functions imported successfully": "Funtzioak ongi inportatu dira",
 	"Functions imported successfully": "Funtzioak ongi inportatu dira",
 	"General": "Orokorra",
 	"General": "Orokorra",
 	"General Settings": "Ezarpen Orokorrak",
 	"General Settings": "Ezarpen Orokorrak",
+	"Generate an image": "",
 	"Generate Image": "Sortu Irudia",
 	"Generate Image": "Sortu Irudia",
 	"Generating search query": "Bilaketa kontsulta sortzen",
 	"Generating search query": "Bilaketa kontsulta sortzen",
 	"Get started": "Hasi",
 	"Get started": "Hasi",
@@ -479,7 +485,6 @@
 	"Group Name": "Taldearen Izena",
 	"Group Name": "Taldearen Izena",
 	"Group updated successfully": "Taldea ongi eguneratu da",
 	"Group updated successfully": "Taldea ongi eguneratu da",
 	"Groups": "Taldeak",
 	"Groups": "Taldeak",
-	"h:mm a": "h:mm a",
 	"Haptic Feedback": "Feedback Haptikoa",
 	"Haptic Feedback": "Feedback Haptikoa",
 	"has no conversations.": "ez du elkarrizketarik.",
 	"has no conversations.": "ez du elkarrizketarik.",
 	"Hello, {{name}}": "Kaixo, {{name}}",
 	"Hello, {{name}}": "Kaixo, {{name}}",
@@ -497,7 +502,6 @@
 	"Ignite curiosity": "Piztu jakin-mina",
 	"Ignite curiosity": "Piztu jakin-mina",
 	"Image": "",
 	"Image": "",
 	"Image Compression": "",
 	"Image Compression": "",
-	"Image generation": "",
 	"Image Generation": "",
 	"Image Generation": "",
 	"Image Generation (Experimental)": "Irudi Sorkuntza (Esperimentala)",
 	"Image Generation (Experimental)": "Irudi Sorkuntza (Esperimentala)",
 	"Image Generation Engine": "Irudi Sorkuntza Motorea",
 	"Image Generation Engine": "Irudi Sorkuntza Motorea",
@@ -596,9 +600,6 @@
 	"Mirostat": "Mirostat",
 	"Mirostat": "Mirostat",
 	"Mirostat Eta": "Mirostat Eta",
 	"Mirostat Eta": "Mirostat Eta",
 	"Mirostat Tau": "Mirostat Tau",
 	"Mirostat Tau": "Mirostat Tau",
-	"MMMM DD, YYYY": "YYYY-ko MMMM-ren DD",
-	"MMMM DD, YYYY HH:mm": "YYYY-ko MMMM-ren DD HH:mm",
-	"MMMM DD, YYYY hh:mm:ss A": "YYYY-ko MMMM-ren DD hh:mm:ss A",
 	"Model": "Modeloa",
 	"Model": "Modeloa",
 	"Model '{{modelName}}' has been successfully downloaded.": "'{{modelName}}' modeloa ongi deskargatu da.",
 	"Model '{{modelName}}' has been successfully downloaded.": "'{{modelName}}' modeloa ongi deskargatu da.",
 	"Model '{{modelTag}}' is already in queue for downloading.": "'{{modelTag}}' modeloa dagoeneko deskarga ilaran dago.",
 	"Model '{{modelTag}}' is already in queue for downloading.": "'{{modelTag}}' modeloa dagoeneko deskarga ilaran dago.",
@@ -719,6 +720,7 @@
 	"Please enter a prompt": "Mesedez, sartu prompt bat",
 	"Please enter a prompt": "Mesedez, sartu prompt bat",
 	"Please fill in all fields.": "Mesedez, bete eremu guztiak.",
 	"Please fill in all fields.": "Mesedez, bete eremu guztiak.",
 	"Please select a model first.": "",
 	"Please select a model first.": "",
+	"Please select a model.": "",
 	"Please select a reason": "Mesedez, hautatu arrazoi bat",
 	"Please select a reason": "Mesedez, hautatu arrazoi bat",
 	"Port": "Ataka",
 	"Port": "Ataka",
 	"Positive attitude": "Jarrera positiboa",
 	"Positive attitude": "Jarrera positiboa",
@@ -802,7 +804,7 @@
 	"Search options": "Bilaketa aukerak",
 	"Search options": "Bilaketa aukerak",
 	"Search Prompts": "Bilatu prompt-ak",
 	"Search Prompts": "Bilatu prompt-ak",
 	"Search Result Count": "Bilaketa emaitzen kopurua",
 	"Search Result Count": "Bilaketa emaitzen kopurua",
-	"Search the web": "Bilatu sarean",
+	"Search the internet": "",
 	"Search Tools": "Bilaketa tresnak",
 	"Search Tools": "Bilaketa tresnak",
 	"SearchApi API Key": "SearchApi API gakoa",
 	"SearchApi API Key": "SearchApi API gakoa",
 	"SearchApi Engine": "SearchApi motorra",
 	"SearchApi Engine": "SearchApi motorra",
@@ -972,6 +974,7 @@
 	"Tools": "Tresnak",
 	"Tools": "Tresnak",
 	"Tools Access": "Tresnen sarbidea",
 	"Tools Access": "Tresnen sarbidea",
 	"Tools are a function calling system with arbitrary code execution": "Tresnak kode arbitrarioa exekutatzeko funtzio deitzeko sistema bat dira",
 	"Tools are a function calling system with arbitrary code execution": "Tresnak kode arbitrarioa exekutatzeko funtzio deitzeko sistema bat dira",
+	"Tools Function Calling Prompt": "",
 	"Tools have a function calling system that allows arbitrary code execution": "Tresnek kode arbitrarioa exekutatzeko aukera ematen duen funtzio deitzeko sistema dute",
 	"Tools have a function calling system that allows arbitrary code execution": "Tresnek kode arbitrarioa exekutatzeko aukera ematen duen funtzio deitzeko sistema dute",
 	"Tools have a function calling system that allows arbitrary code execution.": "Tresnek kode arbitrarioa exekutatzeko aukera ematen duen funtzio deitzeko sistema dute.",
 	"Tools have a function calling system that allows arbitrary code execution.": "Tresnek kode arbitrarioa exekutatzeko aukera ematen duen funtzio deitzeko sistema dute.",
 	"Top K": "Goiko K",
 	"Top K": "Goiko K",
@@ -1071,6 +1074,7 @@
 	"You can personalize your interactions with LLMs by adding memories through the 'Manage' button below, making them more helpful and tailored to you.": "LLMekin dituzun interakzioak pertsonalizatu ditzakezu memoriak gehituz beheko 'Kudeatu' botoiaren bidez, lagungarriagoak eta zuretzat egokituagoak eginez.",
 	"You can personalize your interactions with LLMs by adding memories through the 'Manage' button below, making them more helpful and tailored to you.": "LLMekin dituzun interakzioak pertsonalizatu ditzakezu memoriak gehituz beheko 'Kudeatu' botoiaren bidez, lagungarriagoak eta zuretzat egokituagoak eginez.",
 	"You cannot upload an empty file.": "Ezin duzu fitxategi huts bat kargatu.",
 	"You cannot upload an empty file.": "Ezin duzu fitxategi huts bat kargatu.",
 	"You do not have permission to access this feature.": "",
 	"You do not have permission to access this feature.": "",
+	"You do not have permission to upload files": "",
 	"You do not have permission to upload files.": "Ez duzu fitxategiak kargatzeko baimenik.",
 	"You do not have permission to upload files.": "Ez duzu fitxategiak kargatzeko baimenik.",
 	"You have no archived conversations.": "Ez duzu artxibatutako elkarrizketarik.",
 	"You have no archived conversations.": "Ez duzu artxibatutako elkarrizketarik.",
 	"You have shared this chat": "Txat hau partekatu duzu",
 	"You have shared this chat": "Txat hau partekatu duzu",

+ 10 - 6
src/lib/i18n/locales/fa-IR/translation.json

@@ -66,6 +66,8 @@
 	"Always-On Web Search": "",
 	"Always-On Web Search": "",
 	"Amazing": "",
 	"Amazing": "",
 	"an assistant": "یک دستیار",
 	"an assistant": "یک دستیار",
+	"Analyzed": "",
+	"Analyzing...": "",
 	"and": "و",
 	"and": "و",
 	"and {{COUNT}} more": "و {{COUNT}} مورد دیگر",
 	"and {{COUNT}} more": "و {{COUNT}} مورد دیگر",
 	"and create a new shared link.": "و یک پیوند اشتراک\u200cگذاری جدید ایجاد کنید.",
 	"and create a new shared link.": "و یک پیوند اشتراک\u200cگذاری جدید ایجاد کنید.",
@@ -170,9 +172,11 @@
 	"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "",
 	"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "",
 	"Clone": "کلون",
 	"Clone": "کلون",
 	"Clone Chat": "",
 	"Clone Chat": "",
+	"Clone of {{TITLE}}": "",
 	"Close": "بسته",
 	"Close": "بسته",
 	"Code execution": "",
 	"Code execution": "",
 	"Code formatted successfully": "",
 	"Code formatted successfully": "",
+	"Code Interpreter": "",
 	"Collection": "مجموعه",
 	"Collection": "مجموعه",
 	"Color": "",
 	"Color": "",
 	"ComfyUI": "کومیوآی",
 	"ComfyUI": "کومیوآی",
@@ -398,6 +402,7 @@
 	"Example: ou=users,dc=foo,dc=example": "",
 	"Example: ou=users,dc=foo,dc=example": "",
 	"Example: sAMAccountName or uid or userPrincipalName": "",
 	"Example: sAMAccountName or uid or userPrincipalName": "",
 	"Exclude": "",
 	"Exclude": "",
+	"Execute code for analysis": "",
 	"Experimental": "آزمایشی",
 	"Experimental": "آزمایشی",
 	"Explore the cosmos": "",
 	"Explore the cosmos": "",
 	"Export": "برون\u200cریزی",
 	"Export": "برون\u200cریزی",
@@ -464,6 +469,7 @@
 	"Functions imported successfully": "درون\u200cریزی توابع با موفقیت انجام شد",
 	"Functions imported successfully": "درون\u200cریزی توابع با موفقیت انجام شد",
 	"General": "عمومی",
 	"General": "عمومی",
 	"General Settings": "تنظیمات عمومی",
 	"General Settings": "تنظیمات عمومی",
+	"Generate an image": "",
 	"Generate Image": "",
 	"Generate Image": "",
 	"Generating search query": "در حال تولید پرسوجوی جستجو",
 	"Generating search query": "در حال تولید پرسوجوی جستجو",
 	"Get started": "",
 	"Get started": "",
@@ -479,7 +485,6 @@
 	"Group Name": "",
 	"Group Name": "",
 	"Group updated successfully": "",
 	"Group updated successfully": "",
 	"Groups": "",
 	"Groups": "",
-	"h:mm a": "h:mm a",
 	"Haptic Feedback": "",
 	"Haptic Feedback": "",
 	"has no conversations.": "ندارد.",
 	"has no conversations.": "ندارد.",
 	"Hello, {{name}}": "سلام، {{name}}",
 	"Hello, {{name}}": "سلام، {{name}}",
@@ -497,7 +502,6 @@
 	"Ignite curiosity": "",
 	"Ignite curiosity": "",
 	"Image": "",
 	"Image": "",
 	"Image Compression": "",
 	"Image Compression": "",
-	"Image generation": "",
 	"Image Generation": "",
 	"Image Generation": "",
 	"Image Generation (Experimental)": "تولید تصویر (آزمایشی)",
 	"Image Generation (Experimental)": "تولید تصویر (آزمایشی)",
 	"Image Generation Engine": "موتور تولید تصویر",
 	"Image Generation Engine": "موتور تولید تصویر",
@@ -596,9 +600,6 @@
 	"Mirostat": "Mirostat",
 	"Mirostat": "Mirostat",
 	"Mirostat Eta": "Mirostat Eta",
 	"Mirostat Eta": "Mirostat Eta",
 	"Mirostat Tau": "Mirostat Tau",
 	"Mirostat Tau": "Mirostat Tau",
-	"MMMM DD, YYYY": "MMMM DD, YYYY",
-	"MMMM DD, YYYY HH:mm": "MMMM DD, YYYY HH:mm",
-	"MMMM DD, YYYY hh:mm:ss A": "",
 	"Model": "",
 	"Model": "",
 	"Model '{{modelName}}' has been successfully downloaded.": "مدل '{{modelName}}' با موفقیت دانلود شد.",
 	"Model '{{modelName}}' has been successfully downloaded.": "مدل '{{modelName}}' با موفقیت دانلود شد.",
 	"Model '{{modelTag}}' is already in queue for downloading.": "مدل '{{modelTag}}' در حال حاضر در صف برای دانلود است.",
 	"Model '{{modelTag}}' is already in queue for downloading.": "مدل '{{modelTag}}' در حال حاضر در صف برای دانلود است.",
@@ -719,6 +720,7 @@
 	"Please enter a prompt": "",
 	"Please enter a prompt": "",
 	"Please fill in all fields.": "",
 	"Please fill in all fields.": "",
 	"Please select a model first.": "",
 	"Please select a model first.": "",
+	"Please select a model.": "",
 	"Please select a reason": "",
 	"Please select a reason": "",
 	"Port": "",
 	"Port": "",
 	"Positive attitude": "نظرات مثبت",
 	"Positive attitude": "نظرات مثبت",
@@ -802,7 +804,7 @@
 	"Search options": "",
 	"Search options": "",
 	"Search Prompts": "جستجوی پرامپت\u200cها",
 	"Search Prompts": "جستجوی پرامپت\u200cها",
 	"Search Result Count": "تعداد نتایج جستجو",
 	"Search Result Count": "تعداد نتایج جستجو",
-	"Search the web": "",
+	"Search the internet": "",
 	"Search Tools": "ابزارهای جستجو",
 	"Search Tools": "ابزارهای جستجو",
 	"SearchApi API Key": "",
 	"SearchApi API Key": "",
 	"SearchApi Engine": "",
 	"SearchApi Engine": "",
@@ -972,6 +974,7 @@
 	"Tools": "",
 	"Tools": "",
 	"Tools Access": "",
 	"Tools Access": "",
 	"Tools are a function calling system with arbitrary code execution": "",
 	"Tools are a function calling system with arbitrary code execution": "",
+	"Tools Function Calling Prompt": "",
 	"Tools have a function calling system that allows arbitrary code execution": "",
 	"Tools have a function calling system that allows arbitrary code execution": "",
 	"Tools have a function calling system that allows arbitrary code execution.": "",
 	"Tools have a function calling system that allows arbitrary code execution.": "",
 	"Top K": "Top K",
 	"Top K": "Top K",
@@ -1071,6 +1074,7 @@
 	"You can personalize your interactions with LLMs by adding memories through the 'Manage' button below, making them more helpful and tailored to you.": "",
 	"You can personalize your interactions with LLMs by adding memories through the 'Manage' button below, making them more helpful and tailored to you.": "",
 	"You cannot upload an empty file.": "",
 	"You cannot upload an empty file.": "",
 	"You do not have permission to access this feature.": "",
 	"You do not have permission to access this feature.": "",
+	"You do not have permission to upload files": "",
 	"You do not have permission to upload files.": "",
 	"You do not have permission to upload files.": "",
 	"You have no archived conversations.": "شما هیچ گفتگوی ذخیره شده ندارید.",
 	"You have no archived conversations.": "شما هیچ گفتگوی ذخیره شده ندارید.",
 	"You have shared this chat": "شما این گفتگو را به اشتراک گذاشته اید",
 	"You have shared this chat": "شما این گفتگو را به اشتراک گذاشته اید",

+ 10 - 6
src/lib/i18n/locales/fi-FI/translation.json

@@ -66,6 +66,8 @@
 	"Always-On Web Search": "",
 	"Always-On Web Search": "",
 	"Amazing": "Hämmästyttävä",
 	"Amazing": "Hämmästyttävä",
 	"an assistant": "avustaja",
 	"an assistant": "avustaja",
+	"Analyzed": "",
+	"Analyzing...": "",
 	"and": "ja",
 	"and": "ja",
 	"and {{COUNT}} more": "ja {{COUNT}} muuta",
 	"and {{COUNT}} more": "ja {{COUNT}} muuta",
 	"and create a new shared link.": "ja luo uusi jaettu linkki.",
 	"and create a new shared link.": "ja luo uusi jaettu linkki.",
@@ -170,9 +172,11 @@
 	"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "Leikepöydälle kirjoitusoikeus evätty. Tarkista selaimesi asetukset ja myönnä tarvittavat käyttöoikeudet.",
 	"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "Leikepöydälle kirjoitusoikeus evätty. Tarkista selaimesi asetukset ja myönnä tarvittavat käyttöoikeudet.",
 	"Clone": "Kloonaa",
 	"Clone": "Kloonaa",
 	"Clone Chat": "",
 	"Clone Chat": "",
+	"Clone of {{TITLE}}": "",
 	"Close": "Sulje",
 	"Close": "Sulje",
 	"Code execution": "Koodin suorittaminen",
 	"Code execution": "Koodin suorittaminen",
 	"Code formatted successfully": "Koodin muotoilu onnistui",
 	"Code formatted successfully": "Koodin muotoilu onnistui",
+	"Code Interpreter": "",
 	"Collection": "Kokoelma",
 	"Collection": "Kokoelma",
 	"Color": "Väri",
 	"Color": "Väri",
 	"ComfyUI": "ComfyUI",
 	"ComfyUI": "ComfyUI",
@@ -398,6 +402,7 @@
 	"Example: ou=users,dc=foo,dc=example": "Esimerkki: ou=käyttäjät,dc=foo,dc=example",
 	"Example: ou=users,dc=foo,dc=example": "Esimerkki: ou=käyttäjät,dc=foo,dc=example",
 	"Example: sAMAccountName or uid or userPrincipalName": "Esimerkki: sAMAccountName tai uid tai userPrincipalName",
 	"Example: sAMAccountName or uid or userPrincipalName": "Esimerkki: sAMAccountName tai uid tai userPrincipalName",
 	"Exclude": "Jätä pois",
 	"Exclude": "Jätä pois",
+	"Execute code for analysis": "",
 	"Experimental": "Kokeellinen",
 	"Experimental": "Kokeellinen",
 	"Explore the cosmos": "Tutki avaruutta",
 	"Explore the cosmos": "Tutki avaruutta",
 	"Export": "Vie",
 	"Export": "Vie",
@@ -464,6 +469,7 @@
 	"Functions imported successfully": "Toiminnot tuotu onnistuneesti",
 	"Functions imported successfully": "Toiminnot tuotu onnistuneesti",
 	"General": "Yleinen",
 	"General": "Yleinen",
 	"General Settings": "Yleiset asetukset",
 	"General Settings": "Yleiset asetukset",
+	"Generate an image": "",
 	"Generate Image": "Luo kuva",
 	"Generate Image": "Luo kuva",
 	"Generating search query": "Luodaan hakukyselyä",
 	"Generating search query": "Luodaan hakukyselyä",
 	"Get started": "Aloita",
 	"Get started": "Aloita",
@@ -479,7 +485,6 @@
 	"Group Name": "Ryhmän nimi",
 	"Group Name": "Ryhmän nimi",
 	"Group updated successfully": "Ryhmä päivitetty onnistuneesti",
 	"Group updated successfully": "Ryhmä päivitetty onnistuneesti",
 	"Groups": "Ryhmät",
 	"Groups": "Ryhmät",
-	"h:mm a": "h:mm a",
 	"Haptic Feedback": "Haptinen palaute",
 	"Haptic Feedback": "Haptinen palaute",
 	"has no conversations.": "ei ole keskusteluja.",
 	"has no conversations.": "ei ole keskusteluja.",
 	"Hello, {{name}}": "Hei, {{name}}",
 	"Hello, {{name}}": "Hei, {{name}}",
@@ -497,7 +502,6 @@
 	"Ignite curiosity": "Sytytä uteliaisuus",
 	"Ignite curiosity": "Sytytä uteliaisuus",
 	"Image": "",
 	"Image": "",
 	"Image Compression": "",
 	"Image Compression": "",
-	"Image generation": "",
 	"Image Generation": "",
 	"Image Generation": "",
 	"Image Generation (Experimental)": "Kuvagenerointi (kokeellinen)",
 	"Image Generation (Experimental)": "Kuvagenerointi (kokeellinen)",
 	"Image Generation Engine": "Kuvagenerointimoottori",
 	"Image Generation Engine": "Kuvagenerointimoottori",
@@ -596,9 +600,6 @@
 	"Mirostat": "Mirostat",
 	"Mirostat": "Mirostat",
 	"Mirostat Eta": "Mirostat Eta",
 	"Mirostat Eta": "Mirostat Eta",
 	"Mirostat Tau": "Mirostat Tau",
 	"Mirostat Tau": "Mirostat Tau",
-	"MMMM DD, YYYY": "D. MMMM YYYY",
-	"MMMM DD, YYYY HH:mm": "D. MMMM YYYY, HH:mm",
-	"MMMM DD, YYYY hh:mm:ss A": "D. MMMM YYYY, hh:mm:ss a",
 	"Model": "Malli",
 	"Model": "Malli",
 	"Model '{{modelName}}' has been successfully downloaded.": "Malli '{{modelName}}' ladattiin onnistuneesti.",
 	"Model '{{modelName}}' has been successfully downloaded.": "Malli '{{modelName}}' ladattiin onnistuneesti.",
 	"Model '{{modelTag}}' is already in queue for downloading.": "Malli '{{modelTag}}' on jo jonossa ladattavaksi.",
 	"Model '{{modelTag}}' is already in queue for downloading.": "Malli '{{modelTag}}' on jo jonossa ladattavaksi.",
@@ -719,6 +720,7 @@
 	"Please enter a prompt": "Kirjoita kehote",
 	"Please enter a prompt": "Kirjoita kehote",
 	"Please fill in all fields.": "Täytä kaikki kentät.",
 	"Please fill in all fields.": "Täytä kaikki kentät.",
 	"Please select a model first.": "Valitse ensin malli.",
 	"Please select a model first.": "Valitse ensin malli.",
+	"Please select a model.": "",
 	"Please select a reason": "Valitse syy",
 	"Please select a reason": "Valitse syy",
 	"Port": "Portti",
 	"Port": "Portti",
 	"Positive attitude": "Positiivinen asenne",
 	"Positive attitude": "Positiivinen asenne",
@@ -802,7 +804,7 @@
 	"Search options": "Hakuvaihtoehdot",
 	"Search options": "Hakuvaihtoehdot",
 	"Search Prompts": "Hae kehotteia",
 	"Search Prompts": "Hae kehotteia",
 	"Search Result Count": "Hakutulosten määrä",
 	"Search Result Count": "Hakutulosten määrä",
-	"Search the web": "Etsi verkosta",
+	"Search the internet": "",
 	"Search Tools": "Hae työkaluja",
 	"Search Tools": "Hae työkaluja",
 	"SearchApi API Key": "SearchApi API -avain",
 	"SearchApi API Key": "SearchApi API -avain",
 	"SearchApi Engine": "SearchApi-moottori",
 	"SearchApi Engine": "SearchApi-moottori",
@@ -972,6 +974,7 @@
 	"Tools": "Työkalut",
 	"Tools": "Työkalut",
 	"Tools Access": "Työkalujen käyttöoikeudet",
 	"Tools Access": "Työkalujen käyttöoikeudet",
 	"Tools are a function calling system with arbitrary code execution": "Työkalut ovat toimintokutsuihin perustuva järjestelmä, joka sallii mielivaltaisen koodin suorittamisen",
 	"Tools are a function calling system with arbitrary code execution": "Työkalut ovat toimintokutsuihin perustuva järjestelmä, joka sallii mielivaltaisen koodin suorittamisen",
+	"Tools Function Calling Prompt": "",
 	"Tools have a function calling system that allows arbitrary code execution": "Työkaluilla on toimintokutsuihin perustuva järjestelmä, joka sallii mielivaltaisen koodin suorittamisen",
 	"Tools have a function calling system that allows arbitrary code execution": "Työkaluilla on toimintokutsuihin perustuva järjestelmä, joka sallii mielivaltaisen koodin suorittamisen",
 	"Tools have a function calling system that allows arbitrary code execution.": "Työkalut sallivat mielivaltaisen koodin suorittamisen toimintokutsuilla.",
 	"Tools have a function calling system that allows arbitrary code execution.": "Työkalut sallivat mielivaltaisen koodin suorittamisen toimintokutsuilla.",
 	"Top K": "Top K",
 	"Top K": "Top K",
@@ -1071,6 +1074,7 @@
 	"You can personalize your interactions with LLMs by adding memories through the 'Manage' button below, making them more helpful and tailored to you.": "Voit personoida vuorovaikutustasi LLM-ohjelmien kanssa lisäämällä muistoja 'Hallitse'-painikkeen kautta, jolloin ne ovat hyödyllisempiä ja räätälöityjä sinua varten.",
 	"You can personalize your interactions with LLMs by adding memories through the 'Manage' button below, making them more helpful and tailored to you.": "Voit personoida vuorovaikutustasi LLM-ohjelmien kanssa lisäämällä muistoja 'Hallitse'-painikkeen kautta, jolloin ne ovat hyödyllisempiä ja räätälöityjä sinua varten.",
 	"You cannot upload an empty file.": "Et voi ladata tyhjää tiedostoa.",
 	"You cannot upload an empty file.": "Et voi ladata tyhjää tiedostoa.",
 	"You do not have permission to access this feature.": "",
 	"You do not have permission to access this feature.": "",
+	"You do not have permission to upload files": "",
 	"You do not have permission to upload files.": "Sinulla ei ole lupaa ladata tiedostoja.",
 	"You do not have permission to upload files.": "Sinulla ei ole lupaa ladata tiedostoja.",
 	"You have no archived conversations.": "Sinulla ei ole arkistoituja keskusteluja.",
 	"You have no archived conversations.": "Sinulla ei ole arkistoituja keskusteluja.",
 	"You have shared this chat": "Olet jakanut tämän keskustelun",
 	"You have shared this chat": "Olet jakanut tämän keskustelun",

+ 11 - 7
src/lib/i18n/locales/fr-CA/translation.json

@@ -66,6 +66,8 @@
 	"Always-On Web Search": "",
 	"Always-On Web Search": "",
 	"Amazing": "",
 	"Amazing": "",
 	"an assistant": "un assistant",
 	"an assistant": "un assistant",
+	"Analyzed": "",
+	"Analyzing...": "",
 	"and": "et",
 	"and": "et",
 	"and {{COUNT}} more": "",
 	"and {{COUNT}} more": "",
 	"and create a new shared link.": "et créer un nouveau lien partagé.",
 	"and create a new shared link.": "et créer un nouveau lien partagé.",
@@ -170,9 +172,11 @@
 	"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "L'autorisation d'écriture du presse-papier a été refusée. Veuillez vérifier les paramètres de votre navigateur pour accorder l'accès nécessaire.",
 	"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "L'autorisation d'écriture du presse-papier a été refusée. Veuillez vérifier les paramètres de votre navigateur pour accorder l'accès nécessaire.",
 	"Clone": "Copie conforme",
 	"Clone": "Copie conforme",
 	"Clone Chat": "",
 	"Clone Chat": "",
+	"Clone of {{TITLE}}": "",
 	"Close": "Fermer",
 	"Close": "Fermer",
 	"Code execution": "",
 	"Code execution": "",
 	"Code formatted successfully": "Le code a été formaté avec succès",
 	"Code formatted successfully": "Le code a été formaté avec succès",
+	"Code Interpreter": "",
 	"Collection": "Collection",
 	"Collection": "Collection",
 	"Color": "",
 	"Color": "",
 	"ComfyUI": "ComfyUI",
 	"ComfyUI": "ComfyUI",
@@ -398,6 +402,7 @@
 	"Example: ou=users,dc=foo,dc=example": "",
 	"Example: ou=users,dc=foo,dc=example": "",
 	"Example: sAMAccountName or uid or userPrincipalName": "",
 	"Example: sAMAccountName or uid or userPrincipalName": "",
 	"Exclude": "",
 	"Exclude": "",
+	"Execute code for analysis": "",
 	"Experimental": "Expérimental",
 	"Experimental": "Expérimental",
 	"Explore the cosmos": "",
 	"Explore the cosmos": "",
 	"Export": "Exportation",
 	"Export": "Exportation",
@@ -464,6 +469,7 @@
 	"Functions imported successfully": "Fonctions importées avec succès",
 	"Functions imported successfully": "Fonctions importées avec succès",
 	"General": "Général",
 	"General": "Général",
 	"General Settings": "Paramètres Généraux",
 	"General Settings": "Paramètres Généraux",
+	"Generate an image": "",
 	"Generate Image": "Générer une image",
 	"Generate Image": "Générer une image",
 	"Generating search query": "Génération d'une requête de recherche",
 	"Generating search query": "Génération d'une requête de recherche",
 	"Get started": "",
 	"Get started": "",
@@ -479,7 +485,6 @@
 	"Group Name": "",
 	"Group Name": "",
 	"Group updated successfully": "",
 	"Group updated successfully": "",
 	"Groups": "",
 	"Groups": "",
-	"h:mm a": "h:mm a",
 	"Haptic Feedback": "",
 	"Haptic Feedback": "",
 	"has no conversations.": "n'a aucune conversation.",
 	"has no conversations.": "n'a aucune conversation.",
 	"Hello, {{name}}": "Bonjour, {{name}}.",
 	"Hello, {{name}}": "Bonjour, {{name}}.",
@@ -497,7 +502,6 @@
 	"Ignite curiosity": "",
 	"Ignite curiosity": "",
 	"Image": "",
 	"Image": "",
 	"Image Compression": "",
 	"Image Compression": "",
-	"Image generation": "",
 	"Image Generation": "",
 	"Image Generation": "",
 	"Image Generation (Experimental)": "Génération d'images (expérimental)",
 	"Image Generation (Experimental)": "Génération d'images (expérimental)",
 	"Image Generation Engine": "Moteur de génération d'images",
 	"Image Generation Engine": "Moteur de génération d'images",
@@ -596,9 +600,6 @@
 	"Mirostat": "Mirostat",
 	"Mirostat": "Mirostat",
 	"Mirostat Eta": "Mirostat Eta",
 	"Mirostat Eta": "Mirostat Eta",
 	"Mirostat Tau": "Mirostat Tau",
 	"Mirostat Tau": "Mirostat Tau",
-	"MMMM DD, YYYY": "DD MMMM YYYY",
-	"MMMM DD, YYYY HH:mm": "DD MMMM YYYY HH:mm",
-	"MMMM DD, YYYY hh:mm:ss A": "DD MMMM YYYY HH:mm:ss",
 	"Model": "",
 	"Model": "",
 	"Model '{{modelName}}' has been successfully downloaded.": "Le modèle '{{modelName}}' a été téléchargé avec succès.",
 	"Model '{{modelName}}' has been successfully downloaded.": "Le modèle '{{modelName}}' a été téléchargé avec succès.",
 	"Model '{{modelTag}}' is already in queue for downloading.": "Le modèle '{{modelTag}}' est déjà dans la file d'attente pour le téléchargement.",
 	"Model '{{modelTag}}' is already in queue for downloading.": "Le modèle '{{modelTag}}' est déjà dans la file d'attente pour le téléchargement.",
@@ -719,6 +720,7 @@
 	"Please enter a prompt": "",
 	"Please enter a prompt": "",
 	"Please fill in all fields.": "",
 	"Please fill in all fields.": "",
 	"Please select a model first.": "",
 	"Please select a model first.": "",
+	"Please select a model.": "",
 	"Please select a reason": "",
 	"Please select a reason": "",
 	"Port": "",
 	"Port": "",
 	"Positive attitude": "Attitude positive",
 	"Positive attitude": "Attitude positive",
@@ -802,7 +804,7 @@
 	"Search options": "",
 	"Search options": "",
 	"Search Prompts": "Recherche de prompts",
 	"Search Prompts": "Recherche de prompts",
 	"Search Result Count": "Nombre de résultats de recherche",
 	"Search Result Count": "Nombre de résultats de recherche",
-	"Search the web": "",
+	"Search the internet": "",
 	"Search Tools": "Outils de recherche",
 	"Search Tools": "Outils de recherche",
 	"SearchApi API Key": "",
 	"SearchApi API Key": "",
 	"SearchApi Engine": "",
 	"SearchApi Engine": "",
@@ -888,7 +890,7 @@
 	"Subtitle (e.g. about the Roman Empire)": "Sous-titres (par ex. sur l'Empire romain)",
 	"Subtitle (e.g. about the Roman Empire)": "Sous-titres (par ex. sur l'Empire romain)",
 	"Success": "Réussite",
 	"Success": "Réussite",
 	"Successfully updated.": "Mise à jour réussie.",
 	"Successfully updated.": "Mise à jour réussie.",
-	"Suggested": "Sugéré",
+	"Suggested": "Suggéré",
 	"Support": "",
 	"Support": "",
 	"Support this plugin:": "",
 	"Support this plugin:": "",
 	"Sync directory": "",
 	"Sync directory": "",
@@ -972,6 +974,7 @@
 	"Tools": "Outils",
 	"Tools": "Outils",
 	"Tools Access": "",
 	"Tools Access": "",
 	"Tools are a function calling system with arbitrary code execution": "",
 	"Tools are a function calling system with arbitrary code execution": "",
+	"Tools Function Calling Prompt": "",
 	"Tools have a function calling system that allows arbitrary code execution": "",
 	"Tools have a function calling system that allows arbitrary code execution": "",
 	"Tools have a function calling system that allows arbitrary code execution.": "",
 	"Tools have a function calling system that allows arbitrary code execution.": "",
 	"Top K": "Top K",
 	"Top K": "Top K",
@@ -1071,6 +1074,7 @@
 	"You can personalize your interactions with LLMs by adding memories through the 'Manage' button below, making them more helpful and tailored to you.": "Vous pouvez personnaliser vos interactions avec les LLM en ajoutant des souvenirs via le bouton 'Gérer' ci-dessous, ce qui les rendra plus utiles et adaptés à vos besoins.",
 	"You can personalize your interactions with LLMs by adding memories through the 'Manage' button below, making them more helpful and tailored to you.": "Vous pouvez personnaliser vos interactions avec les LLM en ajoutant des souvenirs via le bouton 'Gérer' ci-dessous, ce qui les rendra plus utiles et adaptés à vos besoins.",
 	"You cannot upload an empty file.": "",
 	"You cannot upload an empty file.": "",
 	"You do not have permission to access this feature.": "",
 	"You do not have permission to access this feature.": "",
+	"You do not have permission to upload files": "",
 	"You do not have permission to upload files.": "",
 	"You do not have permission to upload files.": "",
 	"You have no archived conversations.": "Vous n'avez aucune conversation archivée",
 	"You have no archived conversations.": "Vous n'avez aucune conversation archivée",
 	"You have shared this chat": "Vous avez partagé cette conversation.",
 	"You have shared this chat": "Vous avez partagé cette conversation.",

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

@@ -51,7 +51,7 @@
 	"Advanced Params": "Paramètres avancés",
 	"Advanced Params": "Paramètres avancés",
 	"All Documents": "Tous les documents",
 	"All Documents": "Tous les documents",
 	"All models deleted successfully": "Tous les modèles ont été supprimés avec succès",
 	"All models deleted successfully": "Tous les modèles ont été supprimés avec succès",
-	"Allow Chat Controls": "",
+	"Allow Chat Controls": "Autoriser les contrôles de chat",
 	"Allow Chat Delete": "Autoriser la suppression de la conversation",
 	"Allow Chat Delete": "Autoriser la suppression de la conversation",
 	"Allow Chat Deletion": "Autoriser la suppression de l'historique de chat",
 	"Allow Chat Deletion": "Autoriser la suppression de l'historique de chat",
 	"Allow Chat Edit": "Autoriser la modification de la conversation",
 	"Allow Chat Edit": "Autoriser la modification de la conversation",
@@ -66,6 +66,8 @@
 	"Always-On Web Search": "",
 	"Always-On Web Search": "",
 	"Amazing": "Incroyable",
 	"Amazing": "Incroyable",
 	"an assistant": "un assistant",
 	"an assistant": "un assistant",
+	"Analyzed": "",
+	"Analyzing...": "",
 	"and": "et",
 	"and": "et",
 	"and {{COUNT}} more": "et {{COUNT}} autres",
 	"and {{COUNT}} more": "et {{COUNT}} autres",
 	"and create a new shared link.": "et créer un nouveau lien partagé.",
 	"and create a new shared link.": "et créer un nouveau lien partagé.",
@@ -92,7 +94,7 @@
 	"Assistant": "Assistant",
 	"Assistant": "Assistant",
 	"Attach file": "Joindre un document",
 	"Attach file": "Joindre un document",
 	"Attention to detail": "Attention aux détails",
 	"Attention to detail": "Attention aux détails",
-	"Attribute for Mail": "",
+	"Attribute for Mail": "Attribut pour l'e-mail",
 	"Attribute for Username": "Attribut pour le nom d'utilisateur",
 	"Attribute for Username": "Attribut pour le nom d'utilisateur",
 	"Audio": "Audio",
 	"Audio": "Audio",
 	"August": "Août",
 	"August": "Août",
@@ -170,9 +172,11 @@
 	"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "L'autorisation d'écriture du presse-papier a été refusée. Veuillez vérifier les paramètres de votre navigateur pour accorder l'accès nécessaire.",
 	"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "L'autorisation d'écriture du presse-papier a été refusée. Veuillez vérifier les paramètres de votre navigateur pour accorder l'accès nécessaire.",
 	"Clone": "Cloner",
 	"Clone": "Cloner",
 	"Clone Chat": "Dupliquer le Chat",
 	"Clone Chat": "Dupliquer le Chat",
+	"Clone of {{TITLE}}": "",
 	"Close": "Fermer",
 	"Close": "Fermer",
 	"Code execution": "Exécution de code",
 	"Code execution": "Exécution de code",
 	"Code formatted successfully": "Le code a été formaté avec succès",
 	"Code formatted successfully": "Le code a été formaté avec succès",
+	"Code Interpreter": "",
 	"Collection": "Collection",
 	"Collection": "Collection",
 	"Color": "Couleur",
 	"Color": "Couleur",
 	"ComfyUI": "ComfyUI",
 	"ComfyUI": "ComfyUI",
@@ -190,7 +194,7 @@
 	"Confirm your action": "Confirmer votre action",
 	"Confirm your action": "Confirmer votre action",
 	"Confirm your new password": "Confirmer votre nouveau mot de passe",
 	"Confirm your new password": "Confirmer votre nouveau mot de passe",
 	"Connections": "Connexions",
 	"Connections": "Connexions",
-	"Constrains effort on reasoning for reasoning models. Only applicable to reasoning models from specific providers that support reasoning effort. (Default: medium)": "",
+	"Constrains effort on reasoning for reasoning models. Only applicable to reasoning models from specific providers that support reasoning effort. (Default: medium)": "Contraint l'effort de raisonnement pour les modèles de raisonnement. Applicable uniquement aux modèles de raisonnement de fournisseurs spécifiques qui prennent en charge l'effort de raisonnement. (Par défaut : medium)",
 	"Contact Admin for WebUI Access": "Contacter l'administrateur pour obtenir l'accès à WebUI",
 	"Contact Admin for WebUI Access": "Contacter l'administrateur pour obtenir l'accès à WebUI",
 	"Content": "Contenu",
 	"Content": "Contenu",
 	"Content Extraction": "Extraction du contenu",
 	"Content Extraction": "Extraction du contenu",
@@ -356,7 +360,7 @@
 	"Enter Mojeek Search API Key": "Entrez la clé API Mojeek",
 	"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 Number of Steps (e.g. 50)": "Entrez le nombre d'étapes (par ex. 50)",
 	"Enter proxy URL (e.g. https://user:password@host:port)": "Entrez l'URL du proxy (par ex. https://use:password@host:port)",
 	"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": "",
+	"Enter reasoning effort": "Entrez l'effort de raisonnement",
 	"Enter Sampler (e.g. Euler a)": "Entrez le sampler (par ex. Euler a)",
 	"Enter Sampler (e.g. Euler a)": "Entrez le sampler (par ex. Euler a)",
 	"Enter Scheduler (e.g. Karras)": "Entrez le planificateur (par ex. Karras)",
 	"Enter Scheduler (e.g. Karras)": "Entrez le planificateur (par ex. Karras)",
 	"Enter Score": "Entrez votre score",
 	"Enter Score": "Entrez votre score",
@@ -394,10 +398,11 @@
 	"Evaluations": "Évaluations",
 	"Evaluations": "Évaluations",
 	"Example: (&(objectClass=inetOrgPerson)(uid=%s))": "Exemple: (&(objectClass=inetOrgPerson)(uid=%s))",
 	"Example: (&(objectClass=inetOrgPerson)(uid=%s))": "Exemple: (&(objectClass=inetOrgPerson)(uid=%s))",
 	"Example: ALL": "Exemple: TOUS",
 	"Example: ALL": "Exemple: TOUS",
-	"Example: mail": "",
+	"Example: mail": "Exemple: mail",
 	"Example: ou=users,dc=foo,dc=example": "Exemple: ou=utilisateurs,dc=foo,dc=exemple",
 	"Example: ou=users,dc=foo,dc=example": "Exemple: ou=utilisateurs,dc=foo,dc=exemple",
 	"Example: sAMAccountName or uid or userPrincipalName": "Exemple: sAMAccountName ou uid ou userPrincipalName",
 	"Example: sAMAccountName or uid or userPrincipalName": "Exemple: sAMAccountName ou uid ou userPrincipalName",
 	"Exclude": "Exclure",
 	"Exclude": "Exclure",
+	"Execute code for analysis": "",
 	"Experimental": "Expérimental",
 	"Experimental": "Expérimental",
 	"Explore the cosmos": "Explorer le cosmos",
 	"Explore the cosmos": "Explorer le cosmos",
 	"Export": "Exportation",
 	"Export": "Exportation",
@@ -415,12 +420,12 @@
 	"External Models": "Modèles externes",
 	"External Models": "Modèles externes",
 	"Failed to add file.": "Échec de l'ajout du fichier.",
 	"Failed to add file.": "Échec de l'ajout du fichier.",
 	"Failed to create API Key.": "Échec de la création de la clé API.",
 	"Failed to create API Key.": "Échec de la création de la clé API.",
-	"Failed to fetch models": "",
+	"Failed to fetch models": "Échec de la récupération des modèles",
 	"Failed to read clipboard contents": "Échec de la lecture du contenu du presse-papiers",
 	"Failed to read clipboard contents": "Échec de la lecture du contenu du presse-papiers",
 	"Failed to save models configuration": "Échec de la sauvegarde de la configuration des modèles",
 	"Failed to save models configuration": "Échec de la sauvegarde de la configuration des modèles",
 	"Failed to update settings": "Échec de la mise à jour des paramètres",
 	"Failed to update settings": "Échec de la mise à jour des paramètres",
 	"Failed to upload file.": "Échec du téléchargement du fichier.",
 	"Failed to upload file.": "Échec du téléchargement du fichier.",
-	"Features Permissions": "",
+	"Features Permissions": "Autorisations des fonctionnalités",
 	"February": "Février",
 	"February": "Février",
 	"Feedback History": "Historique des avis",
 	"Feedback History": "Historique des avis",
 	"Feedbacks": "Avis",
 	"Feedbacks": "Avis",
@@ -464,6 +469,7 @@
 	"Functions imported successfully": "Fonctions importées avec succès",
 	"Functions imported successfully": "Fonctions importées avec succès",
 	"General": "Général",
 	"General": "Général",
 	"General Settings": "Paramètres généraux",
 	"General Settings": "Paramètres généraux",
+	"Generate an image": "",
 	"Generate Image": "Générer une image",
 	"Generate Image": "Générer une image",
 	"Generating search query": "Génération d'une requête de recherche",
 	"Generating search query": "Génération d'une requête de recherche",
 	"Get started": "Commencer",
 	"Get started": "Commencer",
@@ -479,7 +485,6 @@
 	"Group Name": "Nom du groupe",
 	"Group Name": "Nom du groupe",
 	"Group updated successfully": "Groupe mis à jour avec succès",
 	"Group updated successfully": "Groupe mis à jour avec succès",
 	"Groups": "Groupes",
 	"Groups": "Groupes",
-	"h:mm a": "h:mm a",
 	"Haptic Feedback": "Retour haptique",
 	"Haptic Feedback": "Retour haptique",
 	"has no conversations.": "n'a aucune conversation.",
 	"has no conversations.": "n'a aucune conversation.",
 	"Hello, {{name}}": "Bonjour, {{name}}.",
 	"Hello, {{name}}": "Bonjour, {{name}}.",
@@ -495,15 +500,14 @@
 	"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.",
 	"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",
 	"ID": "ID",
 	"Ignite curiosity": "Éveiller la curiosité",
 	"Ignite curiosity": "Éveiller la curiosité",
-	"Image": "",
+	"Image": "Image",
 	"Image Compression": "Compression d'image",
 	"Image Compression": "Compression d'image",
-	"Image generation": "",
-	"Image Generation": "",
+	"Image Generation": "Génération d'images",
 	"Image Generation (Experimental)": "Génération d'images (expérimental)",
 	"Image Generation (Experimental)": "Génération d'images (expérimental)",
 	"Image Generation Engine": "Moteur de génération d'images",
 	"Image Generation Engine": "Moteur de génération d'images",
 	"Image Max Compression Size": "Taille maximale de compression d'image",
 	"Image Max Compression Size": "Taille maximale de compression d'image",
-	"Image Prompt Generation": "",
-	"Image Prompt Generation Prompt": "",
+	"Image Prompt Generation": "Génération de prompts d'images",
+	"Image Prompt Generation Prompt": "Prompt de génération de prompts d'images",
 	"Image Settings": "Paramètres de génération d'images",
 	"Image Settings": "Paramètres de génération d'images",
 	"Images": "Images",
 	"Images": "Images",
 	"Import Chats": "Importer les conversations",
 	"Import Chats": "Importer les conversations",
@@ -560,7 +564,7 @@
 	"Leave empty to use the default prompt, or enter a custom prompt": "Laissez vide pour utiliser le prompt par défaut, ou entrez un prompt personnalisé",
 	"Leave empty to use the default prompt, or enter a custom prompt": "Laissez vide pour utiliser le prompt par défaut, ou entrez un prompt personnalisé",
 	"Light": "Clair",
 	"Light": "Clair",
 	"Listening...": "Écoute en cours...",
 	"Listening...": "Écoute en cours...",
-	"Llama.cpp": "",
+	"Llama.cpp": "Llama.cpp",
 	"LLMs can make mistakes. Verify important information.": "Les LLM peuvent faire des erreurs. Vérifiez les informations importantes.",
 	"LLMs can make mistakes. Verify important information.": "Les LLM peuvent faire des erreurs. Vérifiez les informations importantes.",
 	"Local": "Local",
 	"Local": "Local",
 	"Local Models": "Modèles locaux",
 	"Local Models": "Modèles locaux",
@@ -571,7 +575,7 @@
 	"Make sure to export a workflow.json file as API format from ComfyUI.": "Veillez à exporter un fichier workflow.json au format API depuis ComfyUI.",
 	"Make sure to export a workflow.json file as API format from ComfyUI.": "Veillez à exporter un fichier workflow.json au format API depuis ComfyUI.",
 	"Manage": "Gérer",
 	"Manage": "Gérer",
 	"Manage Arena Models": "Gérer les modèles d'arène",
 	"Manage Arena Models": "Gérer les modèles d'arène",
-	"Manage Models": "",
+	"Manage Models": "Gérer les modèles",
 	"Manage Ollama": "Gérer Ollama",
 	"Manage Ollama": "Gérer Ollama",
 	"Manage Ollama API Connections": "Gérer les connexions API Ollama",
 	"Manage Ollama API Connections": "Gérer les connexions API Ollama",
 	"Manage OpenAI API Connections": "Gérer les connexions API OpenAI",
 	"Manage OpenAI API Connections": "Gérer les connexions API OpenAI",
@@ -596,9 +600,6 @@
 	"Mirostat": "Mirostat",
 	"Mirostat": "Mirostat",
 	"Mirostat Eta": "Mirostat Eta",
 	"Mirostat Eta": "Mirostat Eta",
 	"Mirostat Tau": "Mirostat Tau",
 	"Mirostat Tau": "Mirostat Tau",
-	"MMMM DD, YYYY": "DD MMMM YYYY",
-	"MMMM DD, YYYY HH:mm": "DD MMMM YYYY HH:mm",
-	"MMMM DD, YYYY hh:mm:ss A": "DD MMMM YYYY HH:mm:ss",
 	"Model": "Modèle",
 	"Model": "Modèle",
 	"Model '{{modelName}}' has been successfully downloaded.": "Le modèle '{{modelName}}' a été téléchargé avec succès.",
 	"Model '{{modelName}}' has been successfully downloaded.": "Le modèle '{{modelName}}' a été téléchargé avec succès.",
 	"Model '{{modelTag}}' is already in queue for downloading.": "Le modèle '{{modelTag}}' est déjà dans la file d'attente pour le téléchargement.",
 	"Model '{{modelTag}}' is already in queue for downloading.": "Le modèle '{{modelTag}}' est déjà dans la file d'attente pour le téléchargement.",
@@ -637,7 +638,7 @@
 	"No files found.": "Aucun fichier trouvé.",
 	"No files found.": "Aucun fichier trouvé.",
 	"No groups with access, add a group to grant access": "Aucun groupe n'a accès, ajoutez un groupe pour accorder l'accès",
 	"No groups with access, add a group to grant access": "Aucun groupe n'a accès, ajoutez un groupe pour accorder l'accès",
 	"No HTML, CSS, or JavaScript content found.": "Aucun contenu HTML, CSS ou JavaScript trouvé.",
 	"No HTML, CSS, or JavaScript content found.": "Aucun contenu HTML, CSS ou JavaScript trouvé.",
-	"No inference engine with management support found": "",
+	"No inference engine with management support found": "Aucun moteur d'inférence avec support trouvé",
 	"No knowledge found": "Aucune connaissance trouvée",
 	"No knowledge found": "Aucune connaissance trouvée",
 	"No model IDs": "Aucun ID de modèle",
 	"No model IDs": "Aucun ID de modèle",
 	"No models found": "Aucun modèle trouvé",
 	"No models found": "Aucun modèle trouvé",
@@ -719,6 +720,7 @@
 	"Please enter a prompt": "Veuillez saisir un prompt",
 	"Please enter a prompt": "Veuillez saisir un prompt",
 	"Please fill in all fields.": "Veuillez remplir tous les champs.",
 	"Please fill in all fields.": "Veuillez remplir tous les champs.",
 	"Please select a model first.": "Veuillez d'abord sélectionner un modèle.",
 	"Please select a model first.": "Veuillez d'abord sélectionner un modèle.",
+	"Please select a model.": "",
 	"Please select a reason": "Veuillez sélectionner une raison",
 	"Please select a reason": "Veuillez sélectionner une raison",
 	"Port": "Port",
 	"Port": "Port",
 	"Positive attitude": "Attitude positive",
 	"Positive attitude": "Attitude positive",
@@ -742,9 +744,9 @@
 	"RAG Template": "Modèle RAG",
 	"RAG Template": "Modèle RAG",
 	"Rating": "Note",
 	"Rating": "Note",
 	"Re-rank models by topic similarity": "Reclasser les modèles par similarité de sujet",
 	"Re-rank models by topic similarity": "Reclasser les modèles par similarité de sujet",
-	"Read": "",
+	"Read": "Lire",
 	"Read Aloud": "Lire à haute voix",
 	"Read Aloud": "Lire à haute voix",
-	"Reasoning Effort": "",
+	"Reasoning Effort": "Effort de raisonnement",
 	"Record voice": "Enregistrer la voix",
 	"Record voice": "Enregistrer la voix",
 	"Redirecting you to OpenWebUI Community": "Redirection vers la communauté OpenWebUI",
 	"Redirecting you to OpenWebUI Community": "Redirection vers la communauté OpenWebUI",
 	"Reduces the probability of generating nonsense. A higher value (e.g. 100) will give more diverse answers, while a lower value (e.g. 10) will be more conservative. (Default: 40)": "Réduit la probabilité de générer des non-sens. Une valeur plus élevée (par exemple 100) donnera des réponses plus diversifiées, tandis qu'une valeur plus basse (par exemple 10) sera plus conservatrice. (Par défaut : 40)",
 	"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. (Default: 40)": "Réduit la probabilité de générer des non-sens. Une valeur plus élevée (par exemple 100) donnera des réponses plus diversifiées, tandis qu'une valeur plus basse (par exemple 10) sera plus conservatrice. (Par défaut : 40)",
@@ -802,7 +804,7 @@
 	"Search options": "Options de recherche",
 	"Search options": "Options de recherche",
 	"Search Prompts": "Rechercher des prompts",
 	"Search Prompts": "Rechercher des prompts",
 	"Search Result Count": "Nombre de résultats de recherche",
 	"Search Result Count": "Nombre de résultats de recherche",
-	"Search the web": "Rechercher sur le web",
+	"Search the internet": "",
 	"Search Tools": "Rechercher des outils",
 	"Search Tools": "Rechercher des outils",
 	"SearchApi API Key": "Clé API SearchApi",
 	"SearchApi API Key": "Clé API SearchApi",
 	"SearchApi Engine": "Moteur de recherche SearchApi",
 	"SearchApi Engine": "Moteur de recherche SearchApi",
@@ -821,7 +823,7 @@
 	"Select a pipeline": "Sélectionnez un pipeline",
 	"Select a pipeline": "Sélectionnez un pipeline",
 	"Select a pipeline url": "Sélectionnez l'URL du pipeline",
 	"Select a pipeline url": "Sélectionnez l'URL du pipeline",
 	"Select a tool": "Sélectionnez un outil",
 	"Select a tool": "Sélectionnez un outil",
-	"Select an Ollama instance": "",
+	"Select an Ollama instance": "Sélectionnez une instance Ollama",
 	"Select Engine": "Sélectionnez le moteur",
 	"Select Engine": "Sélectionnez le moteur",
 	"Select Knowledge": "Sélectionnez une connaissance",
 	"Select Knowledge": "Sélectionnez une connaissance",
 	"Select model": "Sélectionner un modèle",
 	"Select model": "Sélectionner un modèle",
@@ -848,7 +850,7 @@
 	"Set Scheduler": "Définir le planificateur",
 	"Set Scheduler": "Définir le planificateur",
 	"Set Steps": "Définir le nombre d'étapes",
 	"Set Steps": "Définir le nombre d'étapes",
 	"Set Task Model": "Définir le modèle de tâche",
 	"Set Task Model": "Définir le modèle de tâche",
-	"Set the number of layers, which will be off-loaded to GPU. Increasing this value can significantly improve performance for models that are optimized for GPU acceleration but may also consume more power and GPU resources.": "",
+	"Set the number of layers, which will be off-loaded to GPU. Increasing this value can significantly improve performance for models that are optimized for GPU acceleration but may also consume more power and GPU resources.": "Définir le nombre de couches qui seront déchargées sur le GPU. Augmenter cette valeur peut améliorer considérablement les performances pour les modèles optimisés pour l'accélération GPU, mais peut également consommer plus d'énergie et de ressources GPU.",
 	"Set the number of worker threads used for computation. This option controls how many threads are used to process incoming requests concurrently. Increasing this value can improve performance under high concurrency workloads but may also consume more CPU resources.": "Définir le nombre de threads de travail utilisés pour le calcul. Cette option contrôle combien de threads sont utilisés pour traiter les demandes entrantes simultanément. L'augmentation de cette valeur peut améliorer les performances sous de fortes charges de travail concurrentes mais peut également consommer plus de ressources CPU.",
 	"Set the number of worker threads used for computation. This option controls how many threads are used to process incoming requests concurrently. Increasing this value can improve performance under high concurrency workloads but may also consume more CPU resources.": "Définir le nombre de threads de travail utilisés pour le calcul. Cette option contrôle combien de threads sont utilisés pour traiter les demandes entrantes simultanément. L'augmentation de cette valeur peut améliorer les performances sous de fortes charges de travail concurrentes mais peut également consommer plus de ressources CPU.",
 	"Set Voice": "Choisir la voix",
 	"Set Voice": "Choisir la voix",
 	"Set whisper model": "Choisir le modèle Whisper",
 	"Set whisper model": "Choisir le modèle Whisper",
@@ -913,7 +915,7 @@
 	"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.  (Default: 512)": "La taille de lot détermine combien de demandes de texte sont traitées ensemble en une fois. Une taille de lot plus grande peut augmenter les performances et la vitesse du modèle, mais elle nécessite également plus de mémoire. (Par défaut : 512)",
 	"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.  (Default: 512)": "La taille de lot détermine combien de demandes de texte sont traitées ensemble en une fois. Une taille de lot plus grande peut augmenter les performances et la vitesse du modèle, mais elle nécessite également plus de mémoire. (Par défaut : 512)",
 	"The developers behind this plugin are passionate volunteers from the community. If you find this plugin helpful, please consider contributing to its development.": "Les développeurs de ce plugin sont des bénévoles passionnés issus de la communauté. Si vous trouvez ce plugin utile, merci de contribuer à son développement.",
 	"The developers behind this plugin are passionate volunteers from the community. If you find this plugin helpful, please consider contributing to its development.": "Les développeurs de ce plugin sont des bénévoles passionnés issus de la communauté. Si vous trouvez ce plugin utile, merci de contribuer à son développement.",
 	"The evaluation leaderboard is based on the Elo rating system and is updated in real-time.": "Le classement d'évaluation est basé sur le système de notation Elo et est mis à jour en temps réel.",
 	"The evaluation leaderboard is based on the Elo rating system and is updated in real-time.": "Le classement d'évaluation est basé sur le système de notation Elo et est mis à jour en temps réel.",
-	"The LDAP attribute that maps to the mail that users use to sign in.": "",
+	"The LDAP attribute that maps to the mail that users use to sign in.": "L'attribut LDAP qui correspond à l'adresse e-mail que les utilisateurs utilisent pour se connecter.",
 	"The LDAP attribute that maps to the username that users use to sign in.": "L'attribut LDAP qui correspond au nom d'utilisateur que les utilisateurs utilisent pour se connecter.",
 	"The LDAP attribute that maps to the username that users use to sign in.": "L'attribut LDAP qui correspond au nom d'utilisateur que les utilisateurs utilisent pour se connecter.",
 	"The leaderboard is currently in beta, and we may adjust the rating calculations as we refine the algorithm.": "Le classement est actuellement en version bêta et nous pouvons ajuster les calculs de notation à mesure que nous peaufinons l'algorithme.",
 	"The leaderboard is currently in beta, and we may adjust the rating calculations as we refine the algorithm.": "Le classement est actuellement en version bêta et nous pouvons ajuster les calculs de notation à mesure que nous peaufinons l'algorithme.",
 	"The maximum file size in MB. If the file size exceeds this limit, the file will not be uploaded.": "La taille maximale du fichier en Mo. Si la taille du fichier dépasse cette limite, le fichier ne sera pas téléchargé.",
 	"The maximum file size in MB. If the file size exceeds this limit, the file will not be uploaded.": "La taille maximale du fichier en Mo. Si la taille du fichier dépasse cette limite, le fichier ne sera pas téléchargé.",
@@ -935,7 +937,7 @@
 	"This will delete all models including custom models and cannot be undone.": "Cela supprimera tous les modèles, y compris les modèles personnalisés, et ne peut pas être annulé.",
 	"This will delete all models including custom models and cannot be undone.": "Cela supprimera tous les modèles, y compris les modèles personnalisés, et ne peut pas être annulé.",
 	"This will reset the knowledge base and sync all files. Do you wish to continue?": "Cela réinitialisera la base de connaissances et synchronisera tous les fichiers. Souhaitez-vous continuer ?",
 	"This will reset the knowledge base and sync all files. Do you wish to continue?": "Cela réinitialisera la base de connaissances et synchronisera tous les fichiers. Souhaitez-vous continuer ?",
 	"Thorough explanation": "Explication approfondie",
 	"Thorough explanation": "Explication approfondie",
-	"Thought for {{DURATION}}": "",
+	"Thought for {{DURATION}}": "Réflexion de {{DURATION}}",
 	"Tika": "Tika",
 	"Tika": "Tika",
 	"Tika Server URL required.": "URL du serveur Tika requise.",
 	"Tika Server URL required.": "URL du serveur Tika requise.",
 	"Tiktoken": "Tiktoken",
 	"Tiktoken": "Tiktoken",
@@ -972,6 +974,7 @@
 	"Tools": "Outils",
 	"Tools": "Outils",
 	"Tools Access": "Accès aux outils",
 	"Tools Access": "Accès aux outils",
 	"Tools are a function calling system with arbitrary code execution": "Les outils sont un système d'appel de fonction avec exécution de code arbitraire",
 	"Tools are a function calling system with arbitrary code execution": "Les outils sont un système d'appel de fonction avec exécution de code arbitraire",
+	"Tools Function Calling Prompt": "",
 	"Tools have a function calling system that allows arbitrary code execution": "Les outils ont un système d'appel de fonction qui permet l'exécution de code arbitraire",
 	"Tools have a function calling system that allows arbitrary code execution": "Les outils ont un système d'appel de fonction qui permet l'exécution de code arbitraire",
 	"Tools have a function calling system that allows arbitrary code execution.": "Les outils ont un système d'appel de fonction qui permet l'exécution de code arbitraire.",
 	"Tools have a function calling system that allows arbitrary code execution.": "Les outils ont un système d'appel de fonction qui permet l'exécution de code arbitraire.",
 	"Top K": "Top K",
 	"Top K": "Top K",
@@ -1060,7 +1063,7 @@
 	"Works together with top-k. A higher value (e.g., 0.95) will lead to more diverse text, while a lower value (e.g., 0.5) will generate more focused and conservative text. (Default: 0.9)": "Fonctionne avec le top-k. Une valeur plus élevée (par ex. 0.95) donnera un texte plus diversifié, tandis qu'une valeur plus basse (par ex. 0.5) générera un texte plus concentré et conservateur. (Par défaut : 0.9)",
 	"Works together with top-k. A higher value (e.g., 0.95) will lead to more diverse text, while a lower value (e.g., 0.5) will generate more focused and conservative text. (Default: 0.9)": "Fonctionne avec le top-k. Une valeur plus élevée (par ex. 0.95) donnera un texte plus diversifié, tandis qu'une valeur plus basse (par ex. 0.5) générera un texte plus concentré et conservateur. (Par défaut : 0.9)",
 	"Workspace": "Espace de travail",
 	"Workspace": "Espace de travail",
 	"Workspace Permissions": "Autorisations de l'espace de travail",
 	"Workspace Permissions": "Autorisations de l'espace de travail",
-	"Write": "",
+	"Write": "Écrire",
 	"Write a prompt suggestion (e.g. Who are you?)": "Écrivez une suggestion de prompt (par exemple : Qui êtes-vous ?)",
 	"Write a prompt suggestion (e.g. Who are you?)": "Écrivez une suggestion de prompt (par exemple : Qui êtes-vous ?)",
 	"Write a summary in 50 words that summarizes [topic or keyword].": "Rédigez un résumé de 50 mots qui résume [sujet ou mot-clé].",
 	"Write a summary in 50 words that summarizes [topic or keyword].": "Rédigez un résumé de 50 mots qui résume [sujet ou mot-clé].",
 	"Write something...": "Écrivez quelque chose...",
 	"Write something...": "Écrivez quelque chose...",
@@ -1070,7 +1073,8 @@
 	"You can only chat with a maximum of {{maxCount}} file(s) at a time.": "Vous ne pouvez discuter qu'avec un maximum de {{maxCount}} fichier(s) à la fois.",
 	"You can only chat with a maximum of {{maxCount}} file(s) at a time.": "Vous ne pouvez discuter qu'avec un maximum de {{maxCount}} fichier(s) à la fois.",
 	"You can personalize your interactions with LLMs by adding memories through the 'Manage' button below, making them more helpful and tailored to you.": "Vous pouvez personnaliser vos interactions avec les LLM en ajoutant des mémoires à l'aide du bouton « Gérer » ci-dessous, ce qui les rendra plus utiles et mieux adaptées à vos besoins.",
 	"You can personalize your interactions with LLMs by adding memories through the 'Manage' button below, making them more helpful and tailored to you.": "Vous pouvez personnaliser vos interactions avec les LLM en ajoutant des mémoires à l'aide du bouton « Gérer » ci-dessous, ce qui les rendra plus utiles et mieux adaptées à vos besoins.",
 	"You cannot upload an empty file.": "Vous ne pouvez pas envoyer un fichier vide.",
 	"You cannot upload an empty file.": "Vous ne pouvez pas envoyer un fichier vide.",
-	"You do not have permission to access this feature.": "",
+	"You do not have permission to access this feature.": "Vous n'avez pas la permission d'accéder à cette fonctionnalité.",
+	"You do not have permission to upload files": "",
 	"You do not have permission to upload files.": "Vous n'avez pas la permission de télécharger des fichiers.",
 	"You do not have permission to upload files.": "Vous n'avez pas la permission de télécharger des fichiers.",
 	"You have no archived conversations.": "Vous n'avez aucune conversation archivée.",
 	"You have no archived conversations.": "Vous n'avez aucune conversation archivée.",
 	"You have shared this chat": "Vous avez partagé cette conversation.",
 	"You have shared this chat": "Vous avez partagé cette conversation.",

+ 10 - 6
src/lib/i18n/locales/he-IL/translation.json

@@ -66,6 +66,8 @@
 	"Always-On Web Search": "",
 	"Always-On Web Search": "",
 	"Amazing": "",
 	"Amazing": "",
 	"an assistant": "עוזר",
 	"an assistant": "עוזר",
+	"Analyzed": "",
+	"Analyzing...": "",
 	"and": "וגם",
 	"and": "וגם",
 	"and {{COUNT}} more": "",
 	"and {{COUNT}} more": "",
 	"and create a new shared link.": "וצור קישור משותף חדש.",
 	"and create a new shared link.": "וצור קישור משותף חדש.",
@@ -170,9 +172,11 @@
 	"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "",
 	"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "",
 	"Clone": "שיבוט",
 	"Clone": "שיבוט",
 	"Clone Chat": "",
 	"Clone Chat": "",
+	"Clone of {{TITLE}}": "",
 	"Close": "סגור",
 	"Close": "סגור",
 	"Code execution": "",
 	"Code execution": "",
 	"Code formatted successfully": "",
 	"Code formatted successfully": "",
+	"Code Interpreter": "",
 	"Collection": "אוסף",
 	"Collection": "אוסף",
 	"Color": "",
 	"Color": "",
 	"ComfyUI": "ComfyUI",
 	"ComfyUI": "ComfyUI",
@@ -398,6 +402,7 @@
 	"Example: ou=users,dc=foo,dc=example": "",
 	"Example: ou=users,dc=foo,dc=example": "",
 	"Example: sAMAccountName or uid or userPrincipalName": "",
 	"Example: sAMAccountName or uid or userPrincipalName": "",
 	"Exclude": "",
 	"Exclude": "",
+	"Execute code for analysis": "",
 	"Experimental": "ניסיוני",
 	"Experimental": "ניסיוני",
 	"Explore the cosmos": "",
 	"Explore the cosmos": "",
 	"Export": "ייצא",
 	"Export": "ייצא",
@@ -464,6 +469,7 @@
 	"Functions imported successfully": "",
 	"Functions imported successfully": "",
 	"General": "כללי",
 	"General": "כללי",
 	"General Settings": "הגדרות כלליות",
 	"General Settings": "הגדרות כלליות",
+	"Generate an image": "",
 	"Generate Image": "",
 	"Generate Image": "",
 	"Generating search query": "יצירת שאילתת חיפוש",
 	"Generating search query": "יצירת שאילתת חיפוש",
 	"Get started": "",
 	"Get started": "",
@@ -479,7 +485,6 @@
 	"Group Name": "",
 	"Group Name": "",
 	"Group updated successfully": "",
 	"Group updated successfully": "",
 	"Groups": "",
 	"Groups": "",
-	"h:mm a": "h:mm a",
 	"Haptic Feedback": "",
 	"Haptic Feedback": "",
 	"has no conversations.": "אין שיחות.",
 	"has no conversations.": "אין שיחות.",
 	"Hello, {{name}}": "שלום, {{name}}",
 	"Hello, {{name}}": "שלום, {{name}}",
@@ -497,7 +502,6 @@
 	"Ignite curiosity": "",
 	"Ignite curiosity": "",
 	"Image": "",
 	"Image": "",
 	"Image Compression": "",
 	"Image Compression": "",
-	"Image generation": "",
 	"Image Generation": "",
 	"Image Generation": "",
 	"Image Generation (Experimental)": "יצירת תמונות (ניסיוני)",
 	"Image Generation (Experimental)": "יצירת תמונות (ניסיוני)",
 	"Image Generation Engine": "מנוע יצירת תמונות",
 	"Image Generation Engine": "מנוע יצירת תמונות",
@@ -596,9 +600,6 @@
 	"Mirostat": "Mirostat",
 	"Mirostat": "Mirostat",
 	"Mirostat Eta": "Mirostat Eta",
 	"Mirostat Eta": "Mirostat Eta",
 	"Mirostat Tau": "Mirostat Tau",
 	"Mirostat Tau": "Mirostat Tau",
-	"MMMM DD, YYYY": "DD בMMMM, YYYY",
-	"MMMM DD, YYYY HH:mm": "DD בMMMM, YYYY HH:mm",
-	"MMMM DD, YYYY hh:mm:ss A": "",
 	"Model": "",
 	"Model": "",
 	"Model '{{modelName}}' has been successfully downloaded.": "המודל '{{modelName}}' הורד בהצלחה.",
 	"Model '{{modelName}}' has been successfully downloaded.": "המודל '{{modelName}}' הורד בהצלחה.",
 	"Model '{{modelTag}}' is already in queue for downloading.": "המודל '{{modelTag}}' כבר בתור להורדה.",
 	"Model '{{modelTag}}' is already in queue for downloading.": "המודל '{{modelTag}}' כבר בתור להורדה.",
@@ -719,6 +720,7 @@
 	"Please enter a prompt": "",
 	"Please enter a prompt": "",
 	"Please fill in all fields.": "",
 	"Please fill in all fields.": "",
 	"Please select a model first.": "",
 	"Please select a model first.": "",
+	"Please select a model.": "",
 	"Please select a reason": "",
 	"Please select a reason": "",
 	"Port": "",
 	"Port": "",
 	"Positive attitude": "גישה חיובית",
 	"Positive attitude": "גישה חיובית",
@@ -802,7 +804,7 @@
 	"Search options": "",
 	"Search options": "",
 	"Search Prompts": "חפש פקודות",
 	"Search Prompts": "חפש פקודות",
 	"Search Result Count": "ספירת תוצאות חיפוש",
 	"Search Result Count": "ספירת תוצאות חיפוש",
-	"Search the web": "",
+	"Search the internet": "",
 	"Search Tools": "",
 	"Search Tools": "",
 	"SearchApi API Key": "",
 	"SearchApi API Key": "",
 	"SearchApi Engine": "",
 	"SearchApi Engine": "",
@@ -972,6 +974,7 @@
 	"Tools": "",
 	"Tools": "",
 	"Tools Access": "",
 	"Tools Access": "",
 	"Tools are a function calling system with arbitrary code execution": "",
 	"Tools are a function calling system with arbitrary code execution": "",
+	"Tools Function Calling Prompt": "",
 	"Tools have a function calling system that allows arbitrary code execution": "",
 	"Tools have a function calling system that allows arbitrary code execution": "",
 	"Tools have a function calling system that allows arbitrary code execution.": "",
 	"Tools have a function calling system that allows arbitrary code execution.": "",
 	"Top K": "Top K",
 	"Top K": "Top K",
@@ -1071,6 +1074,7 @@
 	"You can personalize your interactions with LLMs by adding memories through the 'Manage' button below, making them more helpful and tailored to you.": "",
 	"You can personalize your interactions with LLMs by adding memories through the 'Manage' button below, making them more helpful and tailored to you.": "",
 	"You cannot upload an empty file.": "",
 	"You cannot upload an empty file.": "",
 	"You do not have permission to access this feature.": "",
 	"You do not have permission to access this feature.": "",
+	"You do not have permission to upload files": "",
 	"You do not have permission to upload files.": "",
 	"You do not have permission to upload files.": "",
 	"You have no archived conversations.": "אין לך שיחות בארכיון.",
 	"You have no archived conversations.": "אין לך שיחות בארכיון.",
 	"You have shared this chat": "שיתפת את השיחה הזו",
 	"You have shared this chat": "שיתפת את השיחה הזו",

+ 10 - 6
src/lib/i18n/locales/hi-IN/translation.json

@@ -66,6 +66,8 @@
 	"Always-On Web Search": "",
 	"Always-On Web Search": "",
 	"Amazing": "",
 	"Amazing": "",
 	"an assistant": "एक सहायक",
 	"an assistant": "एक सहायक",
+	"Analyzed": "",
+	"Analyzing...": "",
 	"and": "और",
 	"and": "और",
 	"and {{COUNT}} more": "",
 	"and {{COUNT}} more": "",
 	"and create a new shared link.": "और एक नई साझा लिंक बनाएं.",
 	"and create a new shared link.": "और एक नई साझा लिंक बनाएं.",
@@ -170,9 +172,11 @@
 	"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "",
 	"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "",
 	"Clone": "क्लोन",
 	"Clone": "क्लोन",
 	"Clone Chat": "",
 	"Clone Chat": "",
+	"Clone of {{TITLE}}": "",
 	"Close": "बंद करना",
 	"Close": "बंद करना",
 	"Code execution": "",
 	"Code execution": "",
 	"Code formatted successfully": "",
 	"Code formatted successfully": "",
+	"Code Interpreter": "",
 	"Collection": "संग्रह",
 	"Collection": "संग्रह",
 	"Color": "",
 	"Color": "",
 	"ComfyUI": "ComfyUI",
 	"ComfyUI": "ComfyUI",
@@ -398,6 +402,7 @@
 	"Example: ou=users,dc=foo,dc=example": "",
 	"Example: ou=users,dc=foo,dc=example": "",
 	"Example: sAMAccountName or uid or userPrincipalName": "",
 	"Example: sAMAccountName or uid or userPrincipalName": "",
 	"Exclude": "",
 	"Exclude": "",
+	"Execute code for analysis": "",
 	"Experimental": "प्रयोगात्मक",
 	"Experimental": "प्रयोगात्मक",
 	"Explore the cosmos": "",
 	"Explore the cosmos": "",
 	"Export": "निर्यातित माल",
 	"Export": "निर्यातित माल",
@@ -464,6 +469,7 @@
 	"Functions imported successfully": "",
 	"Functions imported successfully": "",
 	"General": "सामान्य",
 	"General": "सामान्य",
 	"General Settings": "सामान्य सेटिंग्स",
 	"General Settings": "सामान्य सेटिंग्स",
+	"Generate an image": "",
 	"Generate Image": "",
 	"Generate Image": "",
 	"Generating search query": "खोज क्वेरी जनरेट करना",
 	"Generating search query": "खोज क्वेरी जनरेट करना",
 	"Get started": "",
 	"Get started": "",
@@ -479,7 +485,6 @@
 	"Group Name": "",
 	"Group Name": "",
 	"Group updated successfully": "",
 	"Group updated successfully": "",
 	"Groups": "",
 	"Groups": "",
-	"h:mm a": "h:mm a",
 	"Haptic Feedback": "",
 	"Haptic Feedback": "",
 	"has no conversations.": "कोई बातचीत नहीं है",
 	"has no conversations.": "कोई बातचीत नहीं है",
 	"Hello, {{name}}": "नमस्ते, {{name}}",
 	"Hello, {{name}}": "नमस्ते, {{name}}",
@@ -497,7 +502,6 @@
 	"Ignite curiosity": "",
 	"Ignite curiosity": "",
 	"Image": "",
 	"Image": "",
 	"Image Compression": "",
 	"Image Compression": "",
-	"Image generation": "",
 	"Image Generation": "",
 	"Image Generation": "",
 	"Image Generation (Experimental)": "छवि निर्माण (प्रायोगिक)",
 	"Image Generation (Experimental)": "छवि निर्माण (प्रायोगिक)",
 	"Image Generation Engine": "छवि निर्माण इंजन",
 	"Image Generation Engine": "छवि निर्माण इंजन",
@@ -596,9 +600,6 @@
 	"Mirostat": "मिरोस्टा",
 	"Mirostat": "मिरोस्टा",
 	"Mirostat Eta": "मिरोस्टा ईटा",
 	"Mirostat Eta": "मिरोस्टा ईटा",
 	"Mirostat Tau": "मिरोस्तात ताऊ",
 	"Mirostat Tau": "मिरोस्तात ताऊ",
-	"MMMM DD, YYYY": "MMMM DD, YYYY",
-	"MMMM DD, YYYY HH:mm": "MMMM DD, YYYY HH:mm",
-	"MMMM DD, YYYY hh:mm:ss A": "",
 	"Model": "",
 	"Model": "",
 	"Model '{{modelName}}' has been successfully downloaded.": "मॉडल '{{modelName}}' सफलतापूर्वक डाउनलोड हो गया है।",
 	"Model '{{modelName}}' has been successfully downloaded.": "मॉडल '{{modelName}}' सफलतापूर्वक डाउनलोड हो गया है।",
 	"Model '{{modelTag}}' is already in queue for downloading.": "मॉडल '{{modelTag}}' पहले से ही डाउनलोड करने के लिए कतार में है।",
 	"Model '{{modelTag}}' is already in queue for downloading.": "मॉडल '{{modelTag}}' पहले से ही डाउनलोड करने के लिए कतार में है।",
@@ -719,6 +720,7 @@
 	"Please enter a prompt": "",
 	"Please enter a prompt": "",
 	"Please fill in all fields.": "",
 	"Please fill in all fields.": "",
 	"Please select a model first.": "",
 	"Please select a model first.": "",
+	"Please select a model.": "",
 	"Please select a reason": "",
 	"Please select a reason": "",
 	"Port": "",
 	"Port": "",
 	"Positive attitude": "सकारात्मक रवैया",
 	"Positive attitude": "सकारात्मक रवैया",
@@ -802,7 +804,7 @@
 	"Search options": "",
 	"Search options": "",
 	"Search Prompts": "प्रॉम्प्ट खोजें",
 	"Search Prompts": "प्रॉम्प्ट खोजें",
 	"Search Result Count": "खोज परिणामों की संख्या",
 	"Search Result Count": "खोज परिणामों की संख्या",
-	"Search the web": "",
+	"Search the internet": "",
 	"Search Tools": "",
 	"Search Tools": "",
 	"SearchApi API Key": "",
 	"SearchApi API Key": "",
 	"SearchApi Engine": "",
 	"SearchApi Engine": "",
@@ -972,6 +974,7 @@
 	"Tools": "",
 	"Tools": "",
 	"Tools Access": "",
 	"Tools Access": "",
 	"Tools are a function calling system with arbitrary code execution": "",
 	"Tools are a function calling system with arbitrary code execution": "",
+	"Tools Function Calling Prompt": "",
 	"Tools have a function calling system that allows arbitrary code execution": "",
 	"Tools have a function calling system that allows arbitrary code execution": "",
 	"Tools have a function calling system that allows arbitrary code execution.": "",
 	"Tools have a function calling system that allows arbitrary code execution.": "",
 	"Top K": "शीर्ष  K",
 	"Top K": "शीर्ष  K",
@@ -1071,6 +1074,7 @@
 	"You can personalize your interactions with LLMs by adding memories through the 'Manage' button below, making them more helpful and tailored to you.": "",
 	"You can personalize your interactions with LLMs by adding memories through the 'Manage' button below, making them more helpful and tailored to you.": "",
 	"You cannot upload an empty file.": "",
 	"You cannot upload an empty file.": "",
 	"You do not have permission to access this feature.": "",
 	"You do not have permission to access this feature.": "",
+	"You do not have permission to upload files": "",
 	"You do not have permission to upload files.": "",
 	"You do not have permission to upload files.": "",
 	"You have no archived conversations.": "आपको कोई अंकित चैट नहीं है।",
 	"You have no archived conversations.": "आपको कोई अंकित चैट नहीं है।",
 	"You have shared this chat": "आपने इस चैट को शेयर किया है",
 	"You have shared this chat": "आपने इस चैट को शेयर किया है",

部分文件因文件數量過多而無法顯示