Browse Source

Merge remote-tracking branch 'upstream/dev' into feat/oauth

Jun Siang Cheah 10 months ago
parent
commit
f26d80dcae
81 changed files with 4060 additions and 1162 deletions
  1. 3 0
      backend/apps/rag/main.py
  2. 41 0
      backend/apps/rag/search/jina_search.py
  3. 50 0
      backend/apps/webui/internal/migrations/016_add_valves_and_is_active.py
  4. 7 3
      backend/apps/webui/main.py
  5. 116 9
      backend/apps/webui/models/functions.py
  6. 72 0
      backend/apps/webui/models/tools.py
  7. 23 0
      backend/apps/webui/routers/files.py
  8. 219 3
      backend/apps/webui/routers/functions.py
  9. 192 4
      backend/apps/webui/routers/tools.py
  10. 41 3
      backend/apps/webui/utils.py
  11. 6 0
      backend/config.py
  12. 264 108
      backend/main.py
  13. 3 1
      backend/requirements.txt
  14. 4 1
      backend/utils/tools.py
  15. 4 0
      src/app.css
  16. 230 0
      src/lib/apis/functions/index.ts
  17. 198 0
      src/lib/apis/tools/index.ts
  18. 2 1
      src/lib/components/admin/Settings/WebSearch.svelte
  19. 0 43
      src/lib/components/admin/SettingsModal.svelte
  20. 49 1
      src/lib/components/chat/Chat.svelte
  21. 4 2
      src/lib/components/chat/MessageInput.svelte
  22. 1 1
      src/lib/components/chat/MessageInput/CallOverlay.svelte
  23. 1 1
      src/lib/components/chat/Messages/Placeholder.svelte
  24. 27 0
      src/lib/components/chat/Settings/Interface.svelte
  25. 2 2
      src/lib/components/chat/Settings/Personalization.svelte
  26. 245 0
      src/lib/components/chat/Settings/Valves.svelte
  27. 48 16
      src/lib/components/chat/SettingsModal.svelte
  28. 19 0
      src/lib/components/icons/Heart.svelte
  29. 0 0
      src/lib/components/playground/Playground.svelte
  30. 6 2
      src/lib/components/workspace/Documents.svelte
  31. 172 118
      src/lib/components/workspace/Functions.svelte
  32. 117 0
      src/lib/components/workspace/Functions/FunctionMenu.svelte
  33. 13 9
      src/lib/components/workspace/Models.svelte
  34. 49 77
      src/lib/components/workspace/Prompts.svelte
  35. 92 0
      src/lib/components/workspace/Prompts/PromptMenu.svelte
  36. 158 112
      src/lib/components/workspace/Tools.svelte
  37. 117 0
      src/lib/components/workspace/Tools/ToolMenu.svelte
  38. 102 0
      src/lib/components/workspace/common/ManifestModal.svelte
  39. 256 0
      src/lib/components/workspace/common/ValvesModal.svelte
  40. 11 0
      src/lib/i18n/locales/ar-BH/translation.json
  41. 11 0
      src/lib/i18n/locales/bg-BG/translation.json
  42. 11 0
      src/lib/i18n/locales/bn-BD/translation.json
  43. 403 392
      src/lib/i18n/locales/ca-ES/translation.json
  44. 11 0
      src/lib/i18n/locales/ceb-PH/translation.json
  45. 11 0
      src/lib/i18n/locales/de-DE/translation.json
  46. 11 0
      src/lib/i18n/locales/dg-DG/translation.json
  47. 11 0
      src/lib/i18n/locales/en-GB/translation.json
  48. 11 0
      src/lib/i18n/locales/en-US/translation.json
  49. 11 0
      src/lib/i18n/locales/es-ES/translation.json
  50. 11 0
      src/lib/i18n/locales/fa-IR/translation.json
  51. 11 0
      src/lib/i18n/locales/fi-FI/translation.json
  52. 11 0
      src/lib/i18n/locales/fr-CA/translation.json
  53. 11 0
      src/lib/i18n/locales/fr-FR/translation.json
  54. 11 0
      src/lib/i18n/locales/he-IL/translation.json
  55. 11 0
      src/lib/i18n/locales/hi-IN/translation.json
  56. 11 0
      src/lib/i18n/locales/hr-HR/translation.json
  57. 11 0
      src/lib/i18n/locales/it-IT/translation.json
  58. 11 0
      src/lib/i18n/locales/ja-JP/translation.json
  59. 11 0
      src/lib/i18n/locales/ka-GE/translation.json
  60. 11 0
      src/lib/i18n/locales/ko-KR/translation.json
  61. 11 0
      src/lib/i18n/locales/lt-LT/translation.json
  62. 11 0
      src/lib/i18n/locales/nb-NO/translation.json
  63. 11 0
      src/lib/i18n/locales/nl-NL/translation.json
  64. 11 0
      src/lib/i18n/locales/pa-IN/translation.json
  65. 11 0
      src/lib/i18n/locales/pl-PL/translation.json
  66. 11 0
      src/lib/i18n/locales/pt-BR/translation.json
  67. 11 0
      src/lib/i18n/locales/pt-PT/translation.json
  68. 11 0
      src/lib/i18n/locales/ru-RU/translation.json
  69. 11 0
      src/lib/i18n/locales/sr-RS/translation.json
  70. 11 0
      src/lib/i18n/locales/sv-SE/translation.json
  71. 11 0
      src/lib/i18n/locales/tk-TW/translation.json
  72. 11 0
      src/lib/i18n/locales/tr-TR/translation.json
  73. 11 0
      src/lib/i18n/locales/uk-UA/translation.json
  74. 11 0
      src/lib/i18n/locales/vi-VN/translation.json
  75. 17 6
      src/lib/i18n/locales/zh-CN/translation.json
  76. 251 240
      src/lib/i18n/locales/zh-TW/translation.json
  77. 27 4
      src/lib/utils/index.ts
  78. 6 1
      src/routes/(app)/+layout.svelte
  79. 1 1
      src/routes/(app)/playground/+page.svelte
  80. 1 1
      src/routes/(app)/workspace/+layout.svelte
  81. 27 0
      src/routes/+layout.svelte

+ 3 - 0
backend/apps/rag/main.py

@@ -77,6 +77,7 @@ from apps.rag.search.serpstack import search_serpstack
 from apps.rag.search.serply import search_serply
 from apps.rag.search.duckduckgo import search_duckduckgo
 from apps.rag.search.tavily import search_tavily
+from apps.rag.search.jina_search import search_jina
 
 from utils.misc import (
     calculate_sha256,
@@ -856,6 +857,8 @@ def search_web(engine: str, query: str) -> list[SearchResult]:
             )
         else:
             raise Exception("No TAVILY_API_KEY found in environment variables")
+    elif engine == "jina":
+        return search_jina(query, app.state.config.RAG_WEB_SEARCH_RESULT_COUNT)
     else:
         raise Exception("No search engine API key found in environment variables")
 

+ 41 - 0
backend/apps/rag/search/jina_search.py

@@ -0,0 +1,41 @@
+import logging
+import requests
+from yarl import URL
+
+from apps.rag.search.main import SearchResult
+from config import SRC_LOG_LEVELS
+
+log = logging.getLogger(__name__)
+log.setLevel(SRC_LOG_LEVELS["RAG"])
+
+
+def search_jina(query: str, count: int) -> list[SearchResult]:
+    """
+    Search using Jina's Search API and return the results as a list of SearchResult objects.
+    Args:
+        query (str): The query to search for
+        count (int): The number of results to return
+
+    Returns:
+        List[SearchResult]: A list of search results
+    """
+    jina_search_endpoint = "https://s.jina.ai/"
+    headers = {
+        "Accept": "application/json",
+    }
+    url = str(URL(jina_search_endpoint + query))
+    response = requests.get(url, headers=headers)
+    response.raise_for_status()
+    data = response.json()
+
+    results = []
+    for result in data["data"][:count]:
+        results.append(
+            SearchResult(
+                link=result["url"],
+                title=result.get("title"),
+                snippet=result.get("content"),
+            )
+        )
+
+    return results

+ 50 - 0
backend/apps/webui/internal/migrations/016_add_valves_and_is_active.py

@@ -0,0 +1,50 @@
+"""Peewee migrations -- 009_add_models.py.
+
+Some examples (model - class or model name)::
+
+    > Model = migrator.orm['table_name']            # Return model in current state by name
+    > Model = migrator.ModelClass                   # Return model in current state by name
+
+    > migrator.sql(sql)                             # Run custom SQL
+    > migrator.run(func, *args, **kwargs)           # Run python function with the given args
+    > migrator.create_model(Model)                  # Create a model (could be used as decorator)
+    > migrator.remove_model(model, cascade=True)    # Remove a model
+    > migrator.add_fields(model, **fields)          # Add fields to a model
+    > migrator.change_fields(model, **fields)       # Change fields
+    > migrator.remove_fields(model, *field_names, cascade=True)
+    > migrator.rename_field(model, old_field_name, new_field_name)
+    > migrator.rename_table(model, new_table_name)
+    > migrator.add_index(model, *col_names, unique=False)
+    > migrator.add_not_null(model, *field_names)
+    > migrator.add_default(model, field_name, default)
+    > migrator.add_constraint(model, name, sql)
+    > migrator.drop_index(model, *col_names)
+    > migrator.drop_not_null(model, *field_names)
+    > migrator.drop_constraints(model, *constraints)
+
+"""
+
+from contextlib import suppress
+
+import peewee as pw
+from peewee_migrate import Migrator
+
+
+with suppress(ImportError):
+    import playhouse.postgres_ext as pw_pext
+
+
+def migrate(migrator: Migrator, database: pw.Database, *, fake=False):
+    """Write your migrations here."""
+
+    migrator.add_fields("tool", valves=pw.TextField(null=True))
+    migrator.add_fields("function", valves=pw.TextField(null=True))
+    migrator.add_fields("function", is_active=pw.BooleanField(default=False))
+
+
+def rollback(migrator: Migrator, database: pw.Database, *, fake=False):
+    """Write your rollback migrations here."""
+
+    migrator.remove_fields("tool", "valves")
+    migrator.remove_fields("function", "valves")
+    migrator.remove_fields("function", "is_active")

+ 7 - 3
backend/apps/webui/main.py

@@ -105,13 +105,15 @@ async def get_status():
 
 
 async def get_pipe_models():
-    pipes = Functions.get_functions_by_type("pipe")
+    pipes = Functions.get_functions_by_type("pipe", active_only=True)
     pipe_models = []
 
     for pipe in pipes:
         # Check if function is already loaded
         if pipe.id not in app.state.FUNCTIONS:
-            function_module, function_type = load_function_module_by_id(pipe.id)
+            function_module, function_type, frontmatter = load_function_module_by_id(
+                pipe.id
+            )
             app.state.FUNCTIONS[pipe.id] = function_module
         else:
             function_module = app.state.FUNCTIONS[pipe.id]
@@ -132,7 +134,9 @@ async def get_pipe_models():
                     manifold_pipe_name = p["name"]
 
                     if hasattr(function_module, "name"):
-                        manifold_pipe_name = f"{pipe.name}{manifold_pipe_name}"
+                        manifold_pipe_name = (
+                            f"{function_module.name}{manifold_pipe_name}"
+                        )
 
                     pipe_models.append(
                         {

+ 116 - 9
backend/apps/webui/models/functions.py

@@ -5,8 +5,11 @@ from typing import List, Union, Optional
 import time
 import logging
 from apps.webui.internal.db import DB, JSONField
+from apps.webui.models.users import Users
 
 import json
+import copy
+
 
 from config import SRC_LOG_LEVELS
 
@@ -25,6 +28,8 @@ class Function(Model):
     type = TextField()
     content = TextField()
     meta = JSONField()
+    valves = JSONField()
+    is_active = BooleanField(default=False)
     updated_at = BigIntegerField()
     created_at = BigIntegerField()
 
@@ -34,6 +39,7 @@ class Function(Model):
 
 class FunctionMeta(BaseModel):
     description: Optional[str] = None
+    manifest: Optional[dict] = {}
 
 
 class FunctionModel(BaseModel):
@@ -43,6 +49,7 @@ class FunctionModel(BaseModel):
     type: str
     content: str
     meta: FunctionMeta
+    is_active: bool = False
     updated_at: int  # timestamp in epoch
     created_at: int  # timestamp in epoch
 
@@ -58,6 +65,7 @@ class FunctionResponse(BaseModel):
     type: str
     name: str
     meta: FunctionMeta
+    is_active: bool
     updated_at: int  # timestamp in epoch
     created_at: int  # timestamp in epoch
 
@@ -69,6 +77,10 @@ class FunctionForm(BaseModel):
     meta: FunctionMeta
 
 
+class FunctionValves(BaseModel):
+    valves: Optional[dict] = None
+
+
 class FunctionsTable:
     def __init__(self, db):
         self.db = db
@@ -104,16 +116,98 @@ class FunctionsTable:
         except:
             return None
 
-    def get_functions(self) -> List[FunctionModel]:
-        return [
-            FunctionModel(**model_to_dict(function)) for function in Function.select()
-        ]
+    def get_functions(self, active_only=False) -> List[FunctionModel]:
+        if active_only:
+            return [
+                FunctionModel(**model_to_dict(function))
+                for function in Function.select().where(Function.is_active == True)
+            ]
+        else:
+            return [
+                FunctionModel(**model_to_dict(function))
+                for function in Function.select()
+            ]
+
+    def get_functions_by_type(
+        self, type: str, active_only=False
+    ) -> List[FunctionModel]:
+        if active_only:
+            return [
+                FunctionModel(**model_to_dict(function))
+                for function in Function.select().where(
+                    Function.type == type, Function.is_active == True
+                )
+            ]
+        else:
+            return [
+                FunctionModel(**model_to_dict(function))
+                for function in Function.select().where(Function.type == type)
+            ]
+
+    def get_function_valves_by_id(self, id: str) -> Optional[dict]:
+        try:
+            function = Function.get(Function.id == id)
+            return function.valves if function.valves else {}
+        except Exception as e:
+            print(f"An error occurred: {e}")
+            return None
 
-    def get_functions_by_type(self, type: str) -> List[FunctionModel]:
-        return [
-            FunctionModel(**model_to_dict(function))
-            for function in Function.select().where(Function.type == type)
-        ]
+    def update_function_valves_by_id(
+        self, id: str, valves: dict
+    ) -> Optional[FunctionValves]:
+        try:
+            query = Function.update(
+                **{"valves": valves},
+                updated_at=int(time.time()),
+            ).where(Function.id == id)
+            query.execute()
+
+            function = Function.get(Function.id == id)
+            return FunctionValves(**model_to_dict(function))
+        except:
+            return None
+
+    def get_user_valves_by_id_and_user_id(
+        self, id: str, user_id: str
+    ) -> Optional[dict]:
+        try:
+            user = Users.get_user_by_id(user_id)
+            user_settings = user.settings.model_dump()
+
+            # Check if user has "functions" and "valves" settings
+            if "functions" not in user_settings:
+                user_settings["functions"] = {}
+            if "valves" not in user_settings["functions"]:
+                user_settings["functions"]["valves"] = {}
+
+            return user_settings["functions"]["valves"].get(id, {})
+        except Exception as e:
+            print(f"An error occurred: {e}")
+            return None
+
+    def update_user_valves_by_id_and_user_id(
+        self, id: str, user_id: str, valves: dict
+    ) -> Optional[dict]:
+        try:
+            user = Users.get_user_by_id(user_id)
+            user_settings = user.settings.model_dump()
+
+            # Check if user has "functions" and "valves" settings
+            if "functions" not in user_settings:
+                user_settings["functions"] = {}
+            if "valves" not in user_settings["functions"]:
+                user_settings["functions"]["valves"] = {}
+
+            user_settings["functions"]["valves"][id] = valves
+
+            # Update the user settings in the database
+            query = Users.update_user_by_id(user_id, {"settings": user_settings})
+            query.execute()
+
+            return user_settings["functions"]["valves"][id]
+        except Exception as e:
+            print(f"An error occurred: {e}")
+            return None
 
     def update_function_by_id(self, id: str, updated: dict) -> Optional[FunctionModel]:
         try:
@@ -128,6 +222,19 @@ class FunctionsTable:
         except:
             return None
 
+    def deactivate_all_functions(self) -> Optional[bool]:
+        try:
+            query = Function.update(
+                **{"is_active": False},
+                updated_at=int(time.time()),
+            )
+
+            query.execute()
+
+            return True
+        except:
+            return None
+
     def delete_function_by_id(self, id: str) -> bool:
         try:
             query = Function.delete().where((Function.id == id))

+ 72 - 0
backend/apps/webui/models/tools.py

@@ -5,8 +5,11 @@ from typing import List, Union, Optional
 import time
 import logging
 from apps.webui.internal.db import DB, JSONField
+from apps.webui.models.users import Users
 
 import json
+import copy
+
 
 from config import SRC_LOG_LEVELS
 
@@ -25,6 +28,7 @@ class Tool(Model):
     content = TextField()
     specs = JSONField()
     meta = JSONField()
+    valves = JSONField()
     updated_at = BigIntegerField()
     created_at = BigIntegerField()
 
@@ -34,6 +38,7 @@ class Tool(Model):
 
 class ToolMeta(BaseModel):
     description: Optional[str] = None
+    manifest: Optional[dict] = {}
 
 
 class ToolModel(BaseModel):
@@ -68,6 +73,10 @@ class ToolForm(BaseModel):
     meta: ToolMeta
 
 
+class ToolValves(BaseModel):
+    valves: Optional[dict] = None
+
+
 class ToolsTable:
     def __init__(self, db):
         self.db = db
@@ -106,6 +115,69 @@ class ToolsTable:
     def get_tools(self) -> List[ToolModel]:
         return [ToolModel(**model_to_dict(tool)) for tool in Tool.select()]
 
+    def get_tool_valves_by_id(self, id: str) -> Optional[dict]:
+        try:
+            tool = Tool.get(Tool.id == id)
+            return tool.valves if tool.valves else {}
+        except Exception as e:
+            print(f"An error occurred: {e}")
+            return None
+
+    def update_tool_valves_by_id(self, id: str, valves: dict) -> Optional[ToolValves]:
+        try:
+            query = Tool.update(
+                **{"valves": valves},
+                updated_at=int(time.time()),
+            ).where(Tool.id == id)
+            query.execute()
+
+            tool = Tool.get(Tool.id == id)
+            return ToolValves(**model_to_dict(tool))
+        except:
+            return None
+
+    def get_user_valves_by_id_and_user_id(
+        self, id: str, user_id: str
+    ) -> Optional[dict]:
+        try:
+            user = Users.get_user_by_id(user_id)
+            user_settings = user.settings.model_dump()
+
+            # Check if user has "tools" and "valves" settings
+            if "tools" not in user_settings:
+                user_settings["tools"] = {}
+            if "valves" not in user_settings["tools"]:
+                user_settings["tools"]["valves"] = {}
+
+            return user_settings["tools"]["valves"].get(id, {})
+        except Exception as e:
+            print(f"An error occurred: {e}")
+            return None
+
+    def update_user_valves_by_id_and_user_id(
+        self, id: str, user_id: str, valves: dict
+    ) -> Optional[dict]:
+        try:
+            user = Users.get_user_by_id(user_id)
+            user_settings = user.settings.model_dump()
+
+            # Check if user has "tools" and "valves" settings
+            if "tools" not in user_settings:
+                user_settings["tools"] = {}
+            if "valves" not in user_settings["tools"]:
+                user_settings["tools"]["valves"] = {}
+
+            user_settings["tools"]["valves"][id] = valves
+
+            # Update the user settings in the database
+            query = Users.update_user_by_id(user_id, {"settings": user_settings})
+            query.execute()
+
+            return user_settings["tools"]["valves"][id]
+        except Exception as e:
+            print(f"An error occurred: {e}")
+            return None
+
     def update_tool_by_id(self, id: str, updated: dict) -> Optional[ToolModel]:
         try:
             query = Tool.update(

+ 23 - 0
backend/apps/webui/routers/files.py

@@ -194,6 +194,29 @@ async def get_file_content_by_id(id: str, user=Depends(get_verified_user)):
         )
 
 
+@router.get("/{id}/content/{file_name}", response_model=Optional[FileModel])
+async def get_file_content_by_id(id: str, user=Depends(get_verified_user)):
+    file = Files.get_file_by_id(id)
+
+    if file:
+        file_path = Path(file.meta["path"])
+
+        # Check if the file already exists in the cache
+        if file_path.is_file():
+            print(f"file_path: {file_path}")
+            return FileResponse(file_path)
+        else:
+            raise HTTPException(
+                status_code=status.HTTP_404_NOT_FOUND,
+                detail=ERROR_MESSAGES.NOT_FOUND,
+            )
+    else:
+        raise HTTPException(
+            status_code=status.HTTP_404_NOT_FOUND,
+            detail=ERROR_MESSAGES.NOT_FOUND,
+        )
+
+
 ############################
 # Delete File By Id
 ############################

+ 219 - 3
backend/apps/webui/routers/functions.py

@@ -69,7 +69,10 @@ async def create_new_function(
             with open(function_path, "w") as function_file:
                 function_file.write(form_data.content)
 
-            function_module, function_type = load_function_module_by_id(form_data.id)
+            function_module, function_type, frontmatter = load_function_module_by_id(
+                form_data.id
+            )
+            form_data.meta.manifest = frontmatter
 
             FUNCTIONS = request.app.state.FUNCTIONS
             FUNCTIONS[form_data.id] = function_module
@@ -117,13 +120,40 @@ async def get_function_by_id(id: str, user=Depends(get_admin_user)):
         )
 
 
+############################
+# ToggleFunctionById
+############################
+
+
+@router.post("/id/{id}/toggle", response_model=Optional[FunctionModel])
+async def toggle_function_by_id(id: str, user=Depends(get_admin_user)):
+    function = Functions.get_function_by_id(id)
+    if function:
+        function = Functions.update_function_by_id(
+            id, {"is_active": not function.is_active}
+        )
+
+        if function:
+            return function
+        else:
+            raise HTTPException(
+                status_code=status.HTTP_400_BAD_REQUEST,
+                detail=ERROR_MESSAGES.DEFAULT("Error updating function"),
+            )
+    else:
+        raise HTTPException(
+            status_code=status.HTTP_401_UNAUTHORIZED,
+            detail=ERROR_MESSAGES.NOT_FOUND,
+        )
+
+
 ############################
 # UpdateFunctionById
 ############################
 
 
 @router.post("/id/{id}/update", response_model=Optional[FunctionModel])
-async def update_toolkit_by_id(
+async def update_function_by_id(
     request: Request, id: str, form_data: FunctionForm, user=Depends(get_admin_user)
 ):
     function_path = os.path.join(FUNCTIONS_DIR, f"{id}.py")
@@ -132,7 +162,8 @@ async def update_toolkit_by_id(
         with open(function_path, "w") as function_file:
             function_file.write(form_data.content)
 
-        function_module, function_type = load_function_module_by_id(id)
+        function_module, function_type, frontmatter = load_function_module_by_id(id)
+        form_data.meta.manifest = frontmatter
 
         FUNCTIONS = request.app.state.FUNCTIONS
         FUNCTIONS[id] = function_module
@@ -178,3 +209,188 @@ async def delete_function_by_id(
         os.remove(function_path)
 
     return result
+
+
+############################
+# GetFunctionValves
+############################
+
+
+@router.get("/id/{id}/valves", response_model=Optional[dict])
+async def get_function_valves_by_id(id: str, user=Depends(get_admin_user)):
+    function = Functions.get_function_by_id(id)
+    if function:
+        try:
+            valves = Functions.get_function_valves_by_id(id)
+            return valves
+        except Exception as e:
+            raise HTTPException(
+                status_code=status.HTTP_400_BAD_REQUEST,
+                detail=ERROR_MESSAGES.DEFAULT(e),
+            )
+    else:
+        raise HTTPException(
+            status_code=status.HTTP_401_UNAUTHORIZED,
+            detail=ERROR_MESSAGES.NOT_FOUND,
+        )
+
+
+############################
+# GetFunctionValvesSpec
+############################
+
+
+@router.get("/id/{id}/valves/spec", response_model=Optional[dict])
+async def get_function_valves_spec_by_id(
+    request: Request, id: str, user=Depends(get_admin_user)
+):
+    function = Functions.get_function_by_id(id)
+    if function:
+        if id in request.app.state.FUNCTIONS:
+            function_module = request.app.state.FUNCTIONS[id]
+        else:
+            function_module, function_type, frontmatter = load_function_module_by_id(id)
+            request.app.state.FUNCTIONS[id] = function_module
+
+        if hasattr(function_module, "Valves"):
+            Valves = function_module.Valves
+            return Valves.schema()
+        return None
+    else:
+        raise HTTPException(
+            status_code=status.HTTP_401_UNAUTHORIZED,
+            detail=ERROR_MESSAGES.NOT_FOUND,
+        )
+
+
+############################
+# UpdateFunctionValves
+############################
+
+
+@router.post("/id/{id}/valves/update", response_model=Optional[dict])
+async def update_function_valves_by_id(
+    request: Request, id: str, form_data: dict, user=Depends(get_admin_user)
+):
+    function = Functions.get_function_by_id(id)
+    if function:
+
+        if id in request.app.state.FUNCTIONS:
+            function_module = request.app.state.FUNCTIONS[id]
+        else:
+            function_module, function_type, frontmatter = load_function_module_by_id(id)
+            request.app.state.FUNCTIONS[id] = function_module
+
+        if hasattr(function_module, "Valves"):
+            Valves = function_module.Valves
+
+            try:
+                form_data = {k: v for k, v in form_data.items() if v is not None}
+                valves = Valves(**form_data)
+                Functions.update_function_valves_by_id(id, valves.model_dump())
+                return valves.model_dump()
+            except Exception as e:
+                print(e)
+                raise HTTPException(
+                    status_code=status.HTTP_400_BAD_REQUEST,
+                    detail=ERROR_MESSAGES.DEFAULT(e),
+                )
+        else:
+            raise HTTPException(
+                status_code=status.HTTP_401_UNAUTHORIZED,
+                detail=ERROR_MESSAGES.NOT_FOUND,
+            )
+
+    else:
+        raise HTTPException(
+            status_code=status.HTTP_401_UNAUTHORIZED,
+            detail=ERROR_MESSAGES.NOT_FOUND,
+        )
+
+
+############################
+# FunctionUserValves
+############################
+
+
+@router.get("/id/{id}/valves/user", response_model=Optional[dict])
+async def get_function_user_valves_by_id(id: str, user=Depends(get_verified_user)):
+    function = Functions.get_function_by_id(id)
+    if function:
+        try:
+            user_valves = Functions.get_user_valves_by_id_and_user_id(id, user.id)
+            return user_valves
+        except Exception as e:
+            raise HTTPException(
+                status_code=status.HTTP_400_BAD_REQUEST,
+                detail=ERROR_MESSAGES.DEFAULT(e),
+            )
+    else:
+        raise HTTPException(
+            status_code=status.HTTP_401_UNAUTHORIZED,
+            detail=ERROR_MESSAGES.NOT_FOUND,
+        )
+
+
+@router.get("/id/{id}/valves/user/spec", response_model=Optional[dict])
+async def get_function_user_valves_spec_by_id(
+    request: Request, id: str, user=Depends(get_verified_user)
+):
+    function = Functions.get_function_by_id(id)
+    if function:
+        if id in request.app.state.FUNCTIONS:
+            function_module = request.app.state.FUNCTIONS[id]
+        else:
+            function_module, function_type, frontmatter = load_function_module_by_id(id)
+            request.app.state.FUNCTIONS[id] = function_module
+
+        if hasattr(function_module, "UserValves"):
+            UserValves = function_module.UserValves
+            return UserValves.schema()
+        return None
+    else:
+        raise HTTPException(
+            status_code=status.HTTP_401_UNAUTHORIZED,
+            detail=ERROR_MESSAGES.NOT_FOUND,
+        )
+
+
+@router.post("/id/{id}/valves/user/update", response_model=Optional[dict])
+async def update_function_user_valves_by_id(
+    request: Request, id: str, form_data: dict, user=Depends(get_verified_user)
+):
+    function = Functions.get_function_by_id(id)
+
+    if function:
+        if id in request.app.state.FUNCTIONS:
+            function_module = request.app.state.FUNCTIONS[id]
+        else:
+            function_module, function_type, frontmatter = load_function_module_by_id(id)
+            request.app.state.FUNCTIONS[id] = function_module
+
+        if hasattr(function_module, "UserValves"):
+            UserValves = function_module.UserValves
+
+            try:
+                form_data = {k: v for k, v in form_data.items() if v is not None}
+                user_valves = UserValves(**form_data)
+                Functions.update_user_valves_by_id_and_user_id(
+                    id, user.id, user_valves.model_dump()
+                )
+                return user_valves.model_dump()
+            except Exception as e:
+                print(e)
+                raise HTTPException(
+                    status_code=status.HTTP_400_BAD_REQUEST,
+                    detail=ERROR_MESSAGES.DEFAULT(e),
+                )
+        else:
+            raise HTTPException(
+                status_code=status.HTTP_401_UNAUTHORIZED,
+                detail=ERROR_MESSAGES.NOT_FOUND,
+            )
+    else:
+        raise HTTPException(
+            status_code=status.HTTP_401_UNAUTHORIZED,
+            detail=ERROR_MESSAGES.NOT_FOUND,
+        )

+ 192 - 4
backend/apps/webui/routers/tools.py

@@ -6,10 +6,12 @@ from fastapi import APIRouter
 from pydantic import BaseModel
 import json
 
+
+from apps.webui.models.users import Users
 from apps.webui.models.tools import Tools, ToolForm, ToolModel, ToolResponse
 from apps.webui.utils import load_toolkit_module_by_id
 
-from utils.utils import get_current_user, get_admin_user
+from utils.utils import get_admin_user, get_verified_user
 from utils.tools import get_tools_specs
 from constants import ERROR_MESSAGES
 
@@ -32,7 +34,7 @@ router = APIRouter()
 
 
 @router.get("/", response_model=List[ToolResponse])
-async def get_toolkits(user=Depends(get_current_user)):
+async def get_toolkits(user=Depends(get_verified_user)):
     toolkits = [toolkit for toolkit in Tools.get_tools()]
     return toolkits
 
@@ -72,7 +74,8 @@ async def create_new_toolkit(
             with open(toolkit_path, "w") as tool_file:
                 tool_file.write(form_data.content)
 
-            toolkit_module = load_toolkit_module_by_id(form_data.id)
+            toolkit_module, frontmatter = load_toolkit_module_by_id(form_data.id)
+            form_data.meta.manifest = frontmatter
 
             TOOLS = request.app.state.TOOLS
             TOOLS[form_data.id] = toolkit_module
@@ -136,7 +139,8 @@ async def update_toolkit_by_id(
         with open(toolkit_path, "w") as tool_file:
             tool_file.write(form_data.content)
 
-        toolkit_module = load_toolkit_module_by_id(id)
+        toolkit_module, frontmatter = load_toolkit_module_by_id(id)
+        form_data.meta.manifest = frontmatter
 
         TOOLS = request.app.state.TOOLS
         TOOLS[id] = toolkit_module
@@ -185,3 +189,187 @@ async def delete_toolkit_by_id(request: Request, id: str, user=Depends(get_admin
         os.remove(toolkit_path)
 
     return result
+
+
+############################
+# GetToolValves
+############################
+
+
+@router.get("/id/{id}/valves", response_model=Optional[dict])
+async def get_toolkit_valves_by_id(id: str, user=Depends(get_admin_user)):
+    toolkit = Tools.get_tool_by_id(id)
+    if toolkit:
+        try:
+            valves = Tools.get_tool_valves_by_id(id)
+            return valves
+        except Exception as e:
+            raise HTTPException(
+                status_code=status.HTTP_400_BAD_REQUEST,
+                detail=ERROR_MESSAGES.DEFAULT(e),
+            )
+    else:
+        raise HTTPException(
+            status_code=status.HTTP_401_UNAUTHORIZED,
+            detail=ERROR_MESSAGES.NOT_FOUND,
+        )
+
+
+############################
+# GetToolValvesSpec
+############################
+
+
+@router.get("/id/{id}/valves/spec", response_model=Optional[dict])
+async def get_toolkit_valves_spec_by_id(
+    request: Request, id: str, user=Depends(get_admin_user)
+):
+    toolkit = Tools.get_tool_by_id(id)
+    if toolkit:
+        if id in request.app.state.TOOLS:
+            toolkit_module = request.app.state.TOOLS[id]
+        else:
+            toolkit_module, frontmatter = load_toolkit_module_by_id(id)
+            request.app.state.TOOLS[id] = toolkit_module
+
+        if hasattr(toolkit_module, "Valves"):
+            Valves = toolkit_module.Valves
+            return Valves.schema()
+        return None
+    else:
+        raise HTTPException(
+            status_code=status.HTTP_401_UNAUTHORIZED,
+            detail=ERROR_MESSAGES.NOT_FOUND,
+        )
+
+
+############################
+# UpdateToolValves
+############################
+
+
+@router.post("/id/{id}/valves/update", response_model=Optional[dict])
+async def update_toolkit_valves_by_id(
+    request: Request, id: str, form_data: dict, user=Depends(get_admin_user)
+):
+    toolkit = Tools.get_tool_by_id(id)
+    if toolkit:
+        if id in request.app.state.TOOLS:
+            toolkit_module = request.app.state.TOOLS[id]
+        else:
+            toolkit_module, frontmatter = load_toolkit_module_by_id(id)
+            request.app.state.TOOLS[id] = toolkit_module
+
+        if hasattr(toolkit_module, "Valves"):
+            Valves = toolkit_module.Valves
+
+            try:
+                form_data = {k: v for k, v in form_data.items() if v is not None}
+                valves = Valves(**form_data)
+                Tools.update_tool_valves_by_id(id, valves.model_dump())
+                return valves.model_dump()
+            except Exception as e:
+                print(e)
+                raise HTTPException(
+                    status_code=status.HTTP_400_BAD_REQUEST,
+                    detail=ERROR_MESSAGES.DEFAULT(e),
+                )
+        else:
+            raise HTTPException(
+                status_code=status.HTTP_401_UNAUTHORIZED,
+                detail=ERROR_MESSAGES.NOT_FOUND,
+            )
+
+    else:
+        raise HTTPException(
+            status_code=status.HTTP_401_UNAUTHORIZED,
+            detail=ERROR_MESSAGES.NOT_FOUND,
+        )
+
+
+############################
+# ToolUserValves
+############################
+
+
+@router.get("/id/{id}/valves/user", response_model=Optional[dict])
+async def get_toolkit_user_valves_by_id(id: str, user=Depends(get_verified_user)):
+    toolkit = Tools.get_tool_by_id(id)
+    if toolkit:
+        try:
+            user_valves = Tools.get_user_valves_by_id_and_user_id(id, user.id)
+            return user_valves
+        except Exception as e:
+            raise HTTPException(
+                status_code=status.HTTP_400_BAD_REQUEST,
+                detail=ERROR_MESSAGES.DEFAULT(e),
+            )
+    else:
+        raise HTTPException(
+            status_code=status.HTTP_401_UNAUTHORIZED,
+            detail=ERROR_MESSAGES.NOT_FOUND,
+        )
+
+
+@router.get("/id/{id}/valves/user/spec", response_model=Optional[dict])
+async def get_toolkit_user_valves_spec_by_id(
+    request: Request, id: str, user=Depends(get_verified_user)
+):
+    toolkit = Tools.get_tool_by_id(id)
+    if toolkit:
+        if id in request.app.state.TOOLS:
+            toolkit_module = request.app.state.TOOLS[id]
+        else:
+            toolkit_module, frontmatter = load_toolkit_module_by_id(id)
+            request.app.state.TOOLS[id] = toolkit_module
+
+        if hasattr(toolkit_module, "UserValves"):
+            UserValves = toolkit_module.UserValves
+            return UserValves.schema()
+        return None
+    else:
+        raise HTTPException(
+            status_code=status.HTTP_401_UNAUTHORIZED,
+            detail=ERROR_MESSAGES.NOT_FOUND,
+        )
+
+
+@router.post("/id/{id}/valves/user/update", response_model=Optional[dict])
+async def update_toolkit_user_valves_by_id(
+    request: Request, id: str, form_data: dict, user=Depends(get_verified_user)
+):
+    toolkit = Tools.get_tool_by_id(id)
+
+    if toolkit:
+        if id in request.app.state.TOOLS:
+            toolkit_module = request.app.state.TOOLS[id]
+        else:
+            toolkit_module, frontmatter = load_toolkit_module_by_id(id)
+            request.app.state.TOOLS[id] = toolkit_module
+
+        if hasattr(toolkit_module, "UserValves"):
+            UserValves = toolkit_module.UserValves
+
+            try:
+                form_data = {k: v for k, v in form_data.items() if v is not None}
+                user_valves = UserValves(**form_data)
+                Tools.update_user_valves_by_id_and_user_id(
+                    id, user.id, user_valves.model_dump()
+                )
+                return user_valves.model_dump()
+            except Exception as e:
+                print(e)
+                raise HTTPException(
+                    status_code=status.HTTP_400_BAD_REQUEST,
+                    detail=ERROR_MESSAGES.DEFAULT(e),
+                )
+        else:
+            raise HTTPException(
+                status_code=status.HTTP_401_UNAUTHORIZED,
+                detail=ERROR_MESSAGES.NOT_FOUND,
+            )
+    else:
+        raise HTTPException(
+            status_code=status.HTTP_401_UNAUTHORIZED,
+            detail=ERROR_MESSAGES.NOT_FOUND,
+        )

+ 41 - 3
backend/apps/webui/utils.py

@@ -1,19 +1,56 @@
 from importlib import util
 import os
+import re
 
 from config import TOOLS_DIR, FUNCTIONS_DIR
 
 
+def extract_frontmatter(file_path):
+    """
+    Extract frontmatter as a dictionary from the specified file path.
+    """
+    frontmatter = {}
+    frontmatter_started = False
+    frontmatter_ended = False
+    frontmatter_pattern = re.compile(r"^\s*([a-z_]+):\s*(.*)\s*$", re.IGNORECASE)
+
+    try:
+        with open(file_path, "r", encoding="utf-8") as file:
+            for line in file:
+                if '"""' in line:
+                    if not frontmatter_started:
+                        frontmatter_started = True
+                        continue  # skip the line with the opening triple quotes
+                    else:
+                        frontmatter_ended = True
+                        break
+
+                if frontmatter_started and not frontmatter_ended:
+                    match = frontmatter_pattern.match(line)
+                    if match:
+                        key, value = match.groups()
+                        frontmatter[key.strip()] = value.strip()
+    except FileNotFoundError:
+        print(f"Error: The file {file_path} does not exist.")
+        return {}
+    except Exception as e:
+        print(f"An error occurred: {e}")
+        return {}
+
+    return frontmatter
+
+
 def load_toolkit_module_by_id(toolkit_id):
     toolkit_path = os.path.join(TOOLS_DIR, f"{toolkit_id}.py")
     spec = util.spec_from_file_location(toolkit_id, toolkit_path)
     module = util.module_from_spec(spec)
+    frontmatter = extract_frontmatter(toolkit_path)
 
     try:
         spec.loader.exec_module(module)
         print(f"Loaded module: {module.__name__}")
         if hasattr(module, "Tools"):
-            return module.Tools()
+            return module.Tools(), frontmatter
         else:
             raise Exception("No Tools class found")
     except Exception as e:
@@ -28,14 +65,15 @@ def load_function_module_by_id(function_id):
 
     spec = util.spec_from_file_location(function_id, function_path)
     module = util.module_from_spec(spec)
+    frontmatter = extract_frontmatter(function_path)
 
     try:
         spec.loader.exec_module(module)
         print(f"Loaded module: {module.__name__}")
         if hasattr(module, "Pipe"):
-            return module.Pipe(), "pipe"
+            return module.Pipe(), "pipe", frontmatter
         elif hasattr(module, "Filter"):
-            return module.Filter(), "filter"
+            return module.Filter(), "filter", frontmatter
         else:
             raise Exception("No Function class found")
     except Exception as e:

+ 6 - 0
backend/config.py

@@ -167,6 +167,12 @@ for version in soup.find_all("h2"):
 CHANGELOG = changelog_json
 
 
+####################################
+# SAFE_MODE
+####################################
+
+SAFE_MODE = os.environ.get("SAFE_MODE", "false").lower() == "true"
+
 ####################################
 # WEBUI_BUILD_HASH
 ####################################

+ 264 - 108
backend/main.py

@@ -62,9 +62,7 @@ from apps.webui.models.functions import Functions
 from apps.webui.models.users import Users
 
 from apps.webui.utils import load_toolkit_module_by_id, load_function_module_by_id
-from apps.webui.utils import load_toolkit_module_by_id
 
-from utils.misc import parse_duration
 from utils.utils import (
     get_admin_user,
     get_verified_user,
@@ -82,6 +80,7 @@ from utils.misc import (
     get_last_user_message,
     add_or_update_system_message,
     stream_message_template,
+    parse_duration,
 )
 
 from apps.rag.utils import get_rag_context, rag_template
@@ -113,6 +112,7 @@ from config import (
     SEARCH_QUERY_GENERATION_PROMPT_TEMPLATE,
     SEARCH_QUERY_PROMPT_LENGTH_THRESHOLD,
     TOOLS_FUNCTION_CALLING_PROMPT_TEMPLATE,
+    SAFE_MODE,
     OAUTH_PROVIDERS,
     ENABLE_OAUTH_SIGNUP,
     OAUTH_MERGE_ACCOUNTS_BY_EMAIL,
@@ -124,6 +124,11 @@ from config import (
 from constants import ERROR_MESSAGES, WEBHOOK_MESSAGES
 from utils.webhook import post_webhook
 
+if SAFE_MODE:
+    print("SAFE MODE ENABLED")
+    Functions.deactivate_all_functions()
+
+
 logging.basicConfig(stream=sys.stdout, level=GLOBAL_LOG_LEVEL)
 log = logging.getLogger(__name__)
 log.setLevel(SRC_LOG_LEVELS["MAIN"])
@@ -271,7 +276,7 @@ async def get_function_call_response(
                 if tool_id in webui_app.state.TOOLS:
                     toolkit_module = webui_app.state.TOOLS[tool_id]
                 else:
-                    toolkit_module = load_toolkit_module_by_id(tool_id)
+                    toolkit_module, frontmatter = load_toolkit_module_by_id(tool_id)
                     webui_app.state.TOOLS[tool_id] = toolkit_module
 
                 file_handler = False
@@ -280,6 +285,14 @@ async def get_function_call_response(
                     file_handler = True
                     print("file_handler: ", file_handler)
 
+                if hasattr(toolkit_module, "valves") and hasattr(
+                    toolkit_module, "Valves"
+                ):
+                    valves = Tools.get_tool_valves_by_id(tool_id)
+                    toolkit_module.valves = toolkit_module.Valves(
+                        **(valves if valves else {})
+                    )
+
                 function = getattr(toolkit_module, result["name"])
                 function_result = None
                 try:
@@ -289,16 +302,24 @@ async def get_function_call_response(
 
                     if "__user__" in sig.parameters:
                         # Call the function with the '__user__' parameter included
-                        params = {
-                            **params,
-                            "__user__": {
-                                "id": user.id,
-                                "email": user.email,
-                                "name": user.name,
-                                "role": user.role,
-                            },
+                        __user__ = {
+                            "id": user.id,
+                            "email": user.email,
+                            "name": user.name,
+                            "role": user.role,
                         }
 
+                        try:
+                            if hasattr(toolkit_module, "UserValves"):
+                                __user__["valves"] = toolkit_module.UserValves(
+                                    **Tools.get_user_valves_by_id_and_user_id(
+                                        tool_id, user.id
+                                    )
+                                )
+                        except Exception as e:
+                            print(e)
+
+                        params = {**params, "__user__": __user__}
                     if "__messages__" in sig.parameters:
                         # Call the function with the '__messages__' parameter included
                         params = {
@@ -386,54 +407,94 @@ class ChatCompletionMiddleware(BaseHTTPMiddleware):
                 )
             model = app.state.MODELS[model_id]
 
+            def get_priority(function_id):
+                function = Functions.get_function_by_id(function_id)
+                if function is not None and hasattr(function, "valves"):
+                    return (function.valves if function.valves else {}).get(
+                        "priority", 0
+                    )
+                return 0
+
+            filter_ids = [
+                function.id
+                for function in Functions.get_functions_by_type(
+                    "filter", active_only=True
+                )
+            ]
             # Check if the model has any filters
             if "info" in model and "meta" in model["info"]:
-                for filter_id in model["info"]["meta"].get("filterIds", []):
-                    filter = Functions.get_function_by_id(filter_id)
-                    if filter:
-                        if filter_id in webui_app.state.FUNCTIONS:
-                            function_module = webui_app.state.FUNCTIONS[filter_id]
-                        else:
-                            function_module, function_type = load_function_module_by_id(
-                                filter_id
-                            )
-                            webui_app.state.FUNCTIONS[filter_id] = function_module
+                filter_ids.extend(model["info"]["meta"].get("filterIds", []))
+                filter_ids = list(set(filter_ids))
+
+            filter_ids.sort(key=get_priority)
+            for filter_id in filter_ids:
+                filter = Functions.get_function_by_id(filter_id)
+                if filter:
+                    if filter_id in webui_app.state.FUNCTIONS:
+                        function_module = webui_app.state.FUNCTIONS[filter_id]
+                    else:
+                        function_module, function_type, frontmatter = (
+                            load_function_module_by_id(filter_id)
+                        )
+                        webui_app.state.FUNCTIONS[filter_id] = function_module
+
+                    # Check if the function has a file_handler variable
+                    if hasattr(function_module, "file_handler"):
+                        skip_files = function_module.file_handler
+
+                    if hasattr(function_module, "valves") and hasattr(
+                        function_module, "Valves"
+                    ):
+                        valves = Functions.get_function_valves_by_id(filter_id)
+                        function_module.valves = function_module.Valves(
+                            **(valves if valves else {})
+                        )
 
-                        # Check if the function has a file_handler variable
-                        if hasattr(function_module, "file_handler"):
-                            skip_files = function_module.file_handler
+                    try:
+                        if hasattr(function_module, "inlet"):
+                            inlet = function_module.inlet
 
-                        try:
-                            if hasattr(function_module, "inlet"):
-                                inlet = function_module.inlet
-
-                                if inspect.iscoroutinefunction(inlet):
-                                    data = await inlet(
-                                        data,
-                                        {
-                                            "id": user.id,
-                                            "email": user.email,
-                                            "name": user.name,
-                                            "role": user.role,
-                                        },
-                                    )
-                                else:
-                                    data = inlet(
-                                        data,
-                                        {
-                                            "id": user.id,
-                                            "email": user.email,
-                                            "name": user.name,
-                                            "role": user.role,
-                                        },
-                                    )
+                            # Get the signature of the function
+                            sig = inspect.signature(inlet)
+                            params = {"body": data}
 
-                        except Exception as e:
-                            print(f"Error: {e}")
-                            return JSONResponse(
-                                status_code=status.HTTP_400_BAD_REQUEST,
-                                content={"detail": str(e)},
-                            )
+                            if "__user__" in sig.parameters:
+                                __user__ = {
+                                    "id": user.id,
+                                    "email": user.email,
+                                    "name": user.name,
+                                    "role": user.role,
+                                }
+
+                                try:
+                                    if hasattr(function_module, "UserValves"):
+                                        __user__["valves"] = function_module.UserValves(
+                                            **Functions.get_user_valves_by_id_and_user_id(
+                                                filter_id, user.id
+                                            )
+                                        )
+                                except Exception as e:
+                                    print(e)
+
+                                params = {**params, "__user__": __user__}
+
+                            if "__id__" in sig.parameters:
+                                params = {
+                                    **params,
+                                    "__id__": filter_id,
+                                }
+
+                            if inspect.iscoroutinefunction(inlet):
+                                data = await inlet(**params)
+                            else:
+                                data = inlet(**params)
+
+                    except Exception as e:
+                        print(f"Error: {e}")
+                        return JSONResponse(
+                            status_code=status.HTTP_400_BAD_REQUEST,
+                            content={"detail": str(e)},
+                        )
 
             # Set the task model
             task_model_id = data["model"]
@@ -857,12 +918,6 @@ async def generate_chat_completions(form_data: dict, user=Depends(get_verified_u
 
     pipe = model.get("pipe")
     if pipe:
-        form_data["user"] = {
-            "id": user.id,
-            "email": user.email,
-            "name": user.name,
-            "role": user.role,
-        }
 
         async def job():
             pipe_id = form_data["model"]
@@ -870,14 +925,62 @@ async def generate_chat_completions(form_data: dict, user=Depends(get_verified_u
                 pipe_id, sub_pipe_id = pipe_id.split(".", 1)
             print(pipe_id)
 
-            pipe = webui_app.state.FUNCTIONS[pipe_id].pipe
+            # Check if function is already loaded
+            if pipe_id not in webui_app.state.FUNCTIONS:
+                function_module, function_type, frontmatter = (
+                    load_function_module_by_id(pipe_id)
+                )
+                webui_app.state.FUNCTIONS[pipe_id] = function_module
+            else:
+                function_module = webui_app.state.FUNCTIONS[pipe_id]
+
+            if hasattr(function_module, "valves") and hasattr(
+                function_module, "Valves"
+            ):
+
+                valves = Functions.get_function_valves_by_id(pipe_id)
+                function_module.valves = function_module.Valves(
+                    **(valves if valves else {})
+                )
+
+            pipe = function_module.pipe
+
+            # Get the signature of the function
+            sig = inspect.signature(pipe)
+            params = {"body": form_data}
+
+            if "__user__" in sig.parameters:
+                __user__ = {
+                    "id": user.id,
+                    "email": user.email,
+                    "name": user.name,
+                    "role": user.role,
+                }
+
+                try:
+                    if hasattr(function_module, "UserValves"):
+                        __user__["valves"] = function_module.UserValves(
+                            **Functions.get_user_valves_by_id_and_user_id(
+                                pipe_id, user.id
+                            )
+                        )
+                except Exception as e:
+                    print(e)
+
+                params = {**params, "__user__": __user__}
+
             if form_data["stream"]:
 
                 async def stream_content():
-                    if inspect.iscoroutinefunction(pipe):
-                        res = await pipe(body=form_data)
-                    else:
-                        res = pipe(body=form_data)
+                    try:
+                        if inspect.iscoroutinefunction(pipe):
+                            res = await pipe(**params)
+                        else:
+                            res = pipe(**params)
+                    except Exception as e:
+                        print(f"Error: {e}")
+                        yield f"data: {json.dumps({'error': {'detail':str(e)}})}\n\n"
+                        return
 
                     if isinstance(res, str):
                         message = stream_message_template(form_data["model"], res)
@@ -922,10 +1025,20 @@ async def generate_chat_completions(form_data: dict, user=Depends(get_verified_u
                     stream_content(), media_type="text/event-stream"
                 )
             else:
+
+                try:
+                    if inspect.iscoroutinefunction(pipe):
+                        res = await pipe(**params)
+                    else:
+                        res = pipe(**params)
+                except Exception as e:
+                    print(f"Error: {e}")
+                    return {"error": {"detail": str(e)}}
+
                 if inspect.iscoroutinefunction(pipe):
-                    res = await pipe(body=form_data)
+                    res = await pipe(**params)
                 else:
-                    res = pipe(body=form_data)
+                    res = pipe(**params)
 
                 if isinstance(res, dict):
                     return res
@@ -1008,7 +1121,12 @@ async def chat_completed(form_data: dict, user=Depends(get_verified_user)):
                     f"{url}/{filter['id']}/filter/outlet",
                     headers=headers,
                     json={
-                        "user": {"id": user.id, "name": user.name, "role": user.role},
+                        "user": {
+                            "id": user.id,
+                            "name": user.name,
+                            "email": user.email,
+                            "role": user.role,
+                        },
                         "body": data,
                     },
                 )
@@ -1033,49 +1151,88 @@ async def chat_completed(form_data: dict, user=Depends(get_verified_user)):
             else:
                 pass
 
+    def get_priority(function_id):
+        function = Functions.get_function_by_id(function_id)
+        if function is not None and hasattr(function, "valves"):
+            return (function.valves if function.valves else {}).get("priority", 0)
+        return 0
+
+    filter_ids = [
+        function.id
+        for function in Functions.get_functions_by_type("filter", active_only=True)
+    ]
     # Check if the model has any filters
     if "info" in model and "meta" in model["info"]:
-        for filter_id in model["info"]["meta"].get("filterIds", []):
-            filter = Functions.get_function_by_id(filter_id)
-            if filter:
-                if filter_id in webui_app.state.FUNCTIONS:
-                    function_module = webui_app.state.FUNCTIONS[filter_id]
-                else:
-                    function_module, function_type = load_function_module_by_id(
-                        filter_id
-                    )
-                    webui_app.state.FUNCTIONS[filter_id] = function_module
+        filter_ids.extend(model["info"]["meta"].get("filterIds", []))
+        filter_ids = list(set(filter_ids))
 
-                try:
-                    if hasattr(function_module, "outlet"):
-                        outlet = function_module.outlet
-                        if inspect.iscoroutinefunction(outlet):
-                            data = await outlet(
-                                data,
-                                {
-                                    "id": user.id,
-                                    "email": user.email,
-                                    "name": user.name,
-                                    "role": user.role,
-                                },
-                            )
-                        else:
-                            data = outlet(
-                                data,
-                                {
-                                    "id": user.id,
-                                    "email": user.email,
-                                    "name": user.name,
-                                    "role": user.role,
-                                },
-                            )
+    # Sort filter_ids by priority, using the get_priority function
+    filter_ids.sort(key=get_priority)
 
-                except Exception as e:
-                    print(f"Error: {e}")
-                    return JSONResponse(
-                        status_code=status.HTTP_400_BAD_REQUEST,
-                        content={"detail": str(e)},
-                    )
+    for filter_id in filter_ids:
+        filter = Functions.get_function_by_id(filter_id)
+        if filter:
+            if filter_id in webui_app.state.FUNCTIONS:
+                function_module = webui_app.state.FUNCTIONS[filter_id]
+            else:
+                function_module, function_type, frontmatter = (
+                    load_function_module_by_id(filter_id)
+                )
+                webui_app.state.FUNCTIONS[filter_id] = function_module
+
+            if hasattr(function_module, "valves") and hasattr(
+                function_module, "Valves"
+            ):
+                valves = Functions.get_function_valves_by_id(filter_id)
+                function_module.valves = function_module.Valves(
+                    **(valves if valves else {})
+                )
+
+            try:
+                if hasattr(function_module, "outlet"):
+                    outlet = function_module.outlet
+
+                    # Get the signature of the function
+                    sig = inspect.signature(outlet)
+                    params = {"body": data}
+
+                    if "__user__" in sig.parameters:
+                        __user__ = {
+                            "id": user.id,
+                            "email": user.email,
+                            "name": user.name,
+                            "role": user.role,
+                        }
+
+                        try:
+                            if hasattr(function_module, "UserValves"):
+                                __user__["valves"] = function_module.UserValves(
+                                    **Functions.get_user_valves_by_id_and_user_id(
+                                        filter_id, user.id
+                                    )
+                                )
+                        except Exception as e:
+                            print(e)
+
+                        params = {**params, "__user__": __user__}
+
+                    if "__id__" in sig.parameters:
+                        params = {
+                            **params,
+                            "__id__": filter_id,
+                        }
+
+                    if inspect.iscoroutinefunction(outlet):
+                        data = await outlet(**params)
+                    else:
+                        data = outlet(**params)
+
+            except Exception as e:
+                print(f"Error: {e}")
+                return JSONResponse(
+                    status_code=status.HTTP_400_BAD_REQUEST,
+                    content={"detail": str(e)},
+                )
 
     return data
 
@@ -1989,7 +2146,6 @@ async def get_manifest_json():
         "start_url": "/",
         "display": "standalone",
         "background_color": "#343541",
-        "theme_color": "#343541",
         "orientation": "portrait-primary",
         "icons": [{"src": "/static/logo.png", "type": "image/png", "sizes": "500x500"}],
     }

+ 3 - 1
backend/requirements.txt

@@ -17,7 +17,9 @@ peewee-migrate==1.12.2
 psycopg2-binary==2.9.9
 PyMySQL==1.1.1
 bcrypt==4.1.3
-
+SQLAlchemy
+pymongo
+redis
 boto3==1.34.110
 
 argon2-cffi==23.1.0

+ 4 - 1
backend/utils/tools.py

@@ -20,7 +20,9 @@ def get_tools_specs(tools) -> List[dict]:
     function_list = [
         {"name": func, "function": getattr(tools, func)}
         for func in dir(tools)
-        if callable(getattr(tools, func)) and not func.startswith("__")
+        if callable(getattr(tools, func))
+        and not func.startswith("__")
+        and not inspect.isclass(getattr(tools, func))
     ]
 
     specs = []
@@ -65,6 +67,7 @@ def get_tools_specs(tools) -> List[dict]:
                             function
                         ).parameters.items()
                         if param.default is param.empty
+                        and not (name.startswith("__") and name.endswith("__"))
                     ],
                 },
             }

+ 4 - 0
src/app.css

@@ -32,6 +32,10 @@ math {
 	@apply underline;
 }
 
+iframe {
+	@apply rounded-lg;
+}
+
 ol > li {
 	counter-increment: list-number;
 	display: block;

+ 230 - 0
src/lib/apis/functions/index.ts

@@ -191,3 +191,233 @@ export const deleteFunctionById = async (token: string, id: string) => {
 
 	return res;
 };
+
+export const toggleFunctionById = async (token: string, id: string) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/functions/id/${id}/toggle`, {
+		method: 'POST',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			authorization: `Bearer ${token}`
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.then((json) => {
+			return json;
+		})
+		.catch((err) => {
+			error = err.detail;
+
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const getFunctionValvesById = async (token: string, id: string) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/functions/id/${id}/valves`, {
+		method: 'GET',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			authorization: `Bearer ${token}`
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.then((json) => {
+			return json;
+		})
+		.catch((err) => {
+			error = err.detail;
+
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const getFunctionValvesSpecById = async (token: string, id: string) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/functions/id/${id}/valves/spec`, {
+		method: 'GET',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			authorization: `Bearer ${token}`
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.then((json) => {
+			return json;
+		})
+		.catch((err) => {
+			error = err.detail;
+
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const updateFunctionValvesById = async (token: string, id: string, valves: object) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/functions/id/${id}/valves/update`, {
+		method: 'POST',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			authorization: `Bearer ${token}`
+		},
+		body: JSON.stringify({
+			...valves
+		})
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.then((json) => {
+			return json;
+		})
+		.catch((err) => {
+			error = err.detail;
+
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const getUserValvesById = async (token: string, id: string) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/functions/id/${id}/valves/user`, {
+		method: 'GET',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			authorization: `Bearer ${token}`
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.then((json) => {
+			return json;
+		})
+		.catch((err) => {
+			error = err.detail;
+
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const getUserValvesSpecById = async (token: string, id: string) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/functions/id/${id}/valves/user/spec`, {
+		method: 'GET',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			authorization: `Bearer ${token}`
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.then((json) => {
+			return json;
+		})
+		.catch((err) => {
+			error = err.detail;
+
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const updateUserValvesById = async (token: string, id: string, valves: object) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/functions/id/${id}/valves/user/update`, {
+		method: 'POST',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			authorization: `Bearer ${token}`
+		},
+		body: JSON.stringify({
+			...valves
+		})
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.then((json) => {
+			return json;
+		})
+		.catch((err) => {
+			error = err.detail;
+
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};

+ 198 - 0
src/lib/apis/tools/index.ts

@@ -191,3 +191,201 @@ export const deleteToolById = async (token: string, id: string) => {
 
 	return res;
 };
+
+export const getToolValvesById = async (token: string, id: string) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/tools/id/${id}/valves`, {
+		method: 'GET',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			authorization: `Bearer ${token}`
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.then((json) => {
+			return json;
+		})
+		.catch((err) => {
+			error = err.detail;
+
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const getToolValvesSpecById = async (token: string, id: string) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/tools/id/${id}/valves/spec`, {
+		method: 'GET',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			authorization: `Bearer ${token}`
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.then((json) => {
+			return json;
+		})
+		.catch((err) => {
+			error = err.detail;
+
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const updateToolValvesById = async (token: string, id: string, valves: object) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/tools/id/${id}/valves/update`, {
+		method: 'POST',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			authorization: `Bearer ${token}`
+		},
+		body: JSON.stringify({
+			...valves
+		})
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.then((json) => {
+			return json;
+		})
+		.catch((err) => {
+			error = err.detail;
+
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const getUserValvesById = async (token: string, id: string) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/tools/id/${id}/valves/user`, {
+		method: 'GET',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			authorization: `Bearer ${token}`
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.then((json) => {
+			return json;
+		})
+		.catch((err) => {
+			error = err.detail;
+
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const getUserValvesSpecById = async (token: string, id: string) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/tools/id/${id}/valves/user/spec`, {
+		method: 'GET',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			authorization: `Bearer ${token}`
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.then((json) => {
+			return json;
+		})
+		.catch((err) => {
+			error = err.detail;
+
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const updateUserValvesById = async (token: string, id: string, valves: object) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/tools/id/${id}/valves/user/update`, {
+		method: 'POST',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			authorization: `Bearer ${token}`
+		},
+		body: JSON.stringify({
+			...valves
+		})
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.then((json) => {
+			return json;
+		})
+		.catch((err) => {
+			error = err.detail;
+
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};

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

@@ -19,7 +19,8 @@
 		'serper',
 		'serply',
 		'duckduckgo',
-		'tavily'
+		'tavily',
+		'jina'
 	];
 
 	let youtubeLanguage = 'en';

+ 0 - 43
src/lib/components/admin/SettingsModal.svelte

@@ -1,43 +0,0 @@
-<script>
-	import { getContext } from 'svelte';
-	import Modal from '../common/Modal.svelte';
-	import Database from './Settings/Database.svelte';
-
-	import General from './Settings/General.svelte';
-	import Users from './Settings/Users.svelte';
-
-	import Banners from '$lib/components/admin/Settings/Banners.svelte';
-	import { toast } from 'svelte-sonner';
-	import Pipelines from './Settings/Pipelines.svelte';
-
-	const i18n = getContext('i18n');
-
-	export let show = false;
-
-	let selectedTab = 'general';
-</script>
-
-<Modal bind:show>
-	<div>
-		<div class=" flex justify-between dark:text-gray-300 px-5 pt-4 pb-2">
-			<div class=" text-lg font-medium self-center">{$i18n.t('Admin Settings')}</div>
-			<button
-				class="self-center"
-				on:click={() => {
-					show = false;
-				}}
-			>
-				<svg
-					xmlns="http://www.w3.org/2000/svg"
-					viewBox="0 0 20 20"
-					fill="currentColor"
-					class="w-5 h-5"
-				>
-					<path
-						d="M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z"
-					/>
-				</svg>
-			</button>
-		</div>
-	</div>
-</Modal>

+ 49 - 1
src/lib/components/chat/Chat.svelte

@@ -127,6 +127,42 @@
 	}
 
 	onMount(async () => {
+		const onMessageHandler = async (event) => {
+			if (event.origin === window.origin) {
+				// Replace with your iframe's origin
+				console.log('Message received from iframe:', event.data);
+				if (event.data.type === 'input:prompt') {
+					console.log(event.data.text);
+
+					const inputElement = document.getElementById('chat-textarea');
+
+					if (inputElement) {
+						prompt = event.data.text;
+						inputElement.focus();
+					}
+				}
+
+				if (event.data.type === 'action:submit') {
+					console.log(event.data.text);
+
+					if (prompt !== '') {
+						await tick();
+						submitPrompt(prompt);
+					}
+				}
+
+				if (event.data.type === 'input:prompt:submit') {
+					console.log(event.data.text);
+
+					if (prompt !== '') {
+						await tick();
+						submitPrompt(event.data.text);
+					}
+				}
+			}
+		};
+		window.addEventListener('message', onMessageHandler);
+
 		if (!$chatId) {
 			chatId.subscribe(async (value) => {
 				if (!value) {
@@ -138,6 +174,10 @@
 				await goto('/');
 			}
 		}
+
+		return () => {
+			window.removeEventListener('message', onMessageHandler);
+		};
 	});
 
 	//////////////////////////
@@ -600,10 +640,14 @@
 			files = model.info.meta.knowledge;
 		}
 		const lastUserMessage = messages.filter((message) => message.role === 'user').at(-1);
+
 		files = [
 			...files,
 			...(lastUserMessage?.files?.filter((item) =>
 				['doc', 'file', 'collection', 'web_search_results'].includes(item.type)
+			) ?? []),
+			...(responseMessage?.files?.filter((item) =>
+				['doc', 'file', 'collection', 'web_search_results'].includes(item.type)
 			) ?? [])
 		].filter(
 			// Remove duplicates
@@ -844,6 +888,9 @@
 			...files,
 			...(lastUserMessage?.files?.filter((item) =>
 				['doc', 'file', 'collection', 'web_search_results'].includes(item.type)
+			) ?? []),
+			...(responseMessage?.files?.filter((item) =>
+				['doc', 'file', 'collection', 'web_search_results'].includes(item.type)
 			) ?? [])
 		].filter(
 			// Remove duplicates
@@ -1213,6 +1260,7 @@
 
 	const getWebSearchResults = async (model: string, parentId: string, responseId: string) => {
 		const responseMessage = history.messages[responseId];
+		const userMessage = history.messages[parentId];
 
 		responseMessage.statusHistory = [
 			{
@@ -1223,7 +1271,7 @@
 		];
 		messages = messages;
 
-		const prompt = history.messages[parentId].content;
+		const prompt = userMessage.content;
 		let searchQuery = await generateSearchQuery(localStorage.token, model, messages, prompt).catch(
 			(error) => {
 				console.log(error);

+ 4 - 2
src/lib/components/chat/MessageInput.svelte

@@ -321,9 +321,11 @@
 		<div class="flex flex-col max-w-6xl px-2.5 md:px-6 w-full">
 			<div class="relative">
 				{#if autoScroll === false && messages.length > 0}
-					<div class=" absolute -top-12 left-0 right-0 flex justify-center z-30">
+					<div
+						class=" absolute -top-12 left-0 right-0 flex justify-center z-30 pointer-events-none"
+					>
 						<button
-							class=" bg-white border border-gray-100 dark:border-none dark:bg-white/20 p-1.5 rounded-full"
+							class=" bg-white border border-gray-100 dark:border-none dark:bg-white/20 p-1.5 rounded-full pointer-events-auto"
 							on:click={() => {
 								autoScroll = true;
 								scrollToBottom();

+ 1 - 1
src/lib/components/chat/MessageInput/CallOverlay.svelte

@@ -271,7 +271,7 @@
 					return;
 				}
 
-				if (assistantSpeaking) {
+				if (assistantSpeaking && !($settings?.voiceInterruption ?? false)) {
 					// Mute the audio if the assistant is speaking
 					analyser.maxDecibels = 0;
 					analyser.minDecibels = -1;

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

@@ -33,7 +33,7 @@
 </script>
 
 {#key mounted}
-	<div class="m-auto w-full max-w-6xl px-8 lg:px-24 pb-10">
+	<div class="m-auto w-full max-w-6xl px-8 lg:px-20 pb-10">
 		<div class="flex justify-start">
 			<div class="flex -space-x-4 mb-1" in:fade={{ duration: 200 }}>
 				{#each models as model, modelIdx}

+ 27 - 0
src/lib/components/chat/Settings/Interface.svelte

@@ -32,6 +32,7 @@
 	let chatDirection: 'LTR' | 'RTL' = 'LTR';
 
 	let showEmojiInCall = false;
+	let voiceInterruption = false;
 
 	const toggleSplitLargeChunks = async () => {
 		splitLargeChunks = !splitLargeChunks;
@@ -58,6 +59,11 @@
 		saveSettings({ showEmojiInCall: showEmojiInCall });
 	};
 
+	const toggleVoiceInterruption = async () => {
+		voiceInterruption = !voiceInterruption;
+		saveSettings({ voiceInterruption: voiceInterruption });
+	};
+
 	const toggleUserLocation = async () => {
 		userLocation = !userLocation;
 
@@ -128,6 +134,7 @@
 		showUsername = $settings.showUsername ?? false;
 
 		showEmojiInCall = $settings.showEmojiInCall ?? false;
+		voiceInterruption = $settings.voiceInterruption ?? false;
 
 		chatBubble = $settings.chatBubble ?? true;
 		widescreenMode = $settings.widescreenMode ?? false;
@@ -399,6 +406,26 @@
 
 			<div class=" my-1.5 text-sm font-medium">{$i18n.t('Voice')}</div>
 
+			<div>
+				<div class=" py-0.5 flex w-full justify-between">
+					<div class=" self-center text-xs">{$i18n.t('Allow Voice Interruption in Call')}</div>
+
+					<button
+						class="p-1 px-3 text-xs flex rounded transition"
+						on:click={() => {
+							toggleVoiceInterruption();
+						}}
+						type="button"
+					>
+						{#if voiceInterruption === true}
+							<span class="ml-2 self-center">{$i18n.t('On')}</span>
+						{:else}
+							<span class="ml-2 self-center">{$i18n.t('Off')}</span>
+						{/if}
+					</button>
+				</div>
+			</div>
+
 			<div>
 				<div class=" py-0.5 flex w-full justify-between">
 					<div class=" self-center text-xs">{$i18n.t('Display Emoji in Call')}</div>

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

@@ -31,7 +31,7 @@
 		dispatch('save');
 	}}
 >
-	<div class="  pr-1.5 overflow-y-scroll max-h-[25rem]">
+	<div class="  pr-1.5 py-1 overflow-y-scroll max-h-[25rem]">
 		<div>
 			<div class="flex items-center justify-between mb-1">
 				<Tooltip
@@ -46,7 +46,7 @@
 					</div>
 				</Tooltip>
 
-				<div class="mt-1">
+				<div class="">
 					<Switch
 						bind:state={enableMemory}
 						on:change={async () => {

+ 245 - 0
src/lib/components/chat/Settings/Valves.svelte

@@ -0,0 +1,245 @@
+<script lang="ts">
+	import { toast } from 'svelte-sonner';
+
+	import { config, functions, models, settings, tools, user } from '$lib/stores';
+	import { createEventDispatcher, onMount, getContext, tick } from 'svelte';
+
+	import {
+		getUserValvesSpecById as getToolUserValvesSpecById,
+		getUserValvesById as getToolUserValvesById,
+		updateUserValvesById as updateToolUserValvesById
+	} from '$lib/apis/tools';
+	import {
+		getUserValvesSpecById as getFunctionUserValvesSpecById,
+		getUserValvesById as getFunctionUserValvesById,
+		updateUserValvesById as updateFunctionUserValvesById
+	} from '$lib/apis/functions';
+
+	import ManageModal from './Personalization/ManageModal.svelte';
+	import Tooltip from '$lib/components/common/Tooltip.svelte';
+	import Spinner from '$lib/components/common/Spinner.svelte';
+
+	const dispatch = createEventDispatcher();
+
+	const i18n = getContext('i18n');
+
+	export let saveSettings: Function;
+
+	let tab = 'tools';
+	let selectedId = '';
+
+	let loading = false;
+
+	let valvesSpec = null;
+	let valves = {};
+
+	const getUserValves = async () => {
+		loading = true;
+		if (tab === 'tools') {
+			valves = await getToolUserValvesById(localStorage.token, selectedId);
+			valvesSpec = await getToolUserValvesSpecById(localStorage.token, selectedId);
+		} else if (tab === 'functions') {
+			valves = await getFunctionUserValvesById(localStorage.token, selectedId);
+			valvesSpec = await getFunctionUserValvesSpecById(localStorage.token, selectedId);
+		}
+
+		if (valvesSpec) {
+			// Convert array to string
+			for (const property in valvesSpec.properties) {
+				if (valvesSpec.properties[property]?.type === 'array') {
+					valves[property] = (valves[property] ?? []).join(',');
+				}
+			}
+		}
+
+		loading = false;
+	};
+
+	const submitHandler = async () => {
+		if (valvesSpec) {
+			// Convert string to array
+			for (const property in valvesSpec.properties) {
+				if (valvesSpec.properties[property]?.type === 'array') {
+					valves[property] = (valves[property] ?? '').split(',').map((v) => v.trim());
+				}
+			}
+
+			if (tab === 'tools') {
+				const res = await updateToolUserValvesById(localStorage.token, selectedId, valves).catch(
+					(error) => {
+						toast.error(error);
+						return null;
+					}
+				);
+
+				if (res) {
+					toast.success('Valves updated');
+					valves = res;
+				}
+			} else if (tab === 'functions') {
+				const res = await updateFunctionUserValvesById(
+					localStorage.token,
+					selectedId,
+					valves
+				).catch((error) => {
+					toast.error(error);
+					return null;
+				});
+
+				if (res) {
+					toast.success('Valves updated');
+					valves = res;
+				}
+			}
+		}
+	};
+
+	$: if (tab) {
+		selectedId = '';
+	}
+
+	$: if (selectedId) {
+		getUserValves();
+	}
+</script>
+
+<form
+	class="flex flex-col h-full justify-between space-y-3 text-sm"
+	on:submit|preventDefault={() => {
+		submitHandler();
+		dispatch('save');
+	}}
+>
+	<div class="flex flex-col pr-1.5 overflow-y-scroll max-h-[25rem]">
+		<div>
+			<div class="flex items-center justify-between mb-2">
+				<Tooltip content="">
+					<div class="text-sm font-medium">
+						{$i18n.t('Manage Valves')}
+					</div>
+				</Tooltip>
+
+				<div class=" self-end">
+					<select
+						class=" dark:bg-gray-900 w-fit pr-8 rounded text-xs bg-transparent outline-none text-right"
+						bind:value={tab}
+						placeholder="Select"
+					>
+						<option value="tools">{$i18n.t('Tools')}</option>
+						<option value="functions">{$i18n.t('Functions')}</option>
+					</select>
+				</div>
+			</div>
+		</div>
+
+		<div class="space-y-1">
+			<div class="flex gap-2">
+				<div class="flex-1">
+					<select
+						class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
+						bind:value={selectedId}
+						on:change={async () => {
+							await tick();
+						}}
+					>
+						{#if tab === 'tools'}
+							<option value="" selected disabled class="bg-gray-100 dark:bg-gray-700"
+								>{$i18n.t('Select a tool')}</option
+							>
+
+							{#each $tools as tool, toolIdx}
+								<option value={tool.id} class="bg-gray-100 dark:bg-gray-700">{tool.name}</option>
+							{/each}
+						{:else if tab === 'functions'}
+							<option value="" selected disabled class="bg-gray-100 dark:bg-gray-700"
+								>{$i18n.t('Select a function')}</option
+							>
+
+							{#each $functions as func, funcIdx}
+								<option value={func.id} class="bg-gray-100 dark:bg-700">{func.name}</option>
+							{/each}
+						{/if}
+					</select>
+				</div>
+			</div>
+		</div>
+
+		{#if selectedId}
+			<hr class="dark:border-gray-800 my-3 w-full" />
+
+			<div>
+				{#if !loading}
+					{#if valvesSpec}
+						{#each Object.keys(valvesSpec.properties) as property, idx}
+							<div class=" py-0.5 w-full justify-between">
+								<div class="flex w-full justify-between">
+									<div class=" self-center text-xs font-medium">
+										{valvesSpec.properties[property].title}
+
+										{#if (valvesSpec?.required ?? []).includes(property)}
+											<span class=" text-gray-500">*required</span>
+										{/if}
+									</div>
+
+									<button
+										class="p-1 px-3 text-xs flex rounded transition"
+										type="button"
+										on:click={() => {
+											valves[property] = (valves[property] ?? null) === null ? '' : null;
+										}}
+									>
+										{#if (valves[property] ?? null) === null}
+											<span class="ml-2 self-center">
+												{#if (valvesSpec?.required ?? []).includes(property)}
+													{$i18n.t('None')}
+												{:else}
+													{$i18n.t('Default')}
+												{/if}
+											</span>
+										{:else}
+											<span class="ml-2 self-center"> {$i18n.t('Custom')} </span>
+										{/if}
+									</button>
+								</div>
+
+								{#if (valves[property] ?? null) !== null}
+									<div class="flex mt-0.5 mb-1.5 space-x-2">
+										<div class=" flex-1">
+											<input
+												class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
+												type="text"
+												placeholder={valvesSpec.properties[property].title}
+												bind:value={valves[property]}
+												autocomplete="off"
+												required
+											/>
+										</div>
+									</div>
+								{/if}
+
+								{#if (valvesSpec.properties[property]?.description ?? null) !== null}
+									<div class="text-xs text-gray-500">
+										{valvesSpec.properties[property].description}
+									</div>
+								{/if}
+							</div>
+						{/each}
+					{:else}
+						<div>No valves</div>
+					{/if}
+				{:else}
+					<Spinner className="size-5" />
+				{/if}
+			</div>
+		{/if}
+	</div>
+
+	<div class="flex justify-end text-sm font-medium">
+		<button
+			class=" px-4 py-2 bg-emerald-700 hover:bg-emerald-800 text-gray-100 transition rounded-lg"
+			type="submit"
+		>
+			{$i18n.t('Save')}
+		</button>
+	</div>
+</form>

+ 48 - 16
src/lib/components/chat/SettingsModal.svelte

@@ -16,6 +16,7 @@
 	import Personalization from './Settings/Personalization.svelte';
 	import { updateUserSettings } from '$lib/apis/users';
 	import { goto } from '$app/navigation';
+	import Valves from './Settings/Valves.svelte';
 
 	const i18n = getContext('i18n');
 
@@ -65,8 +66,8 @@
 				<button
 					class="px-2.5 py-2.5 min-w-fit rounded-lg flex-1 md:flex-none flex text-right transition {selectedTab ===
 					'general'
-						? 'bg-gray-200 dark:bg-gray-700'
-						: ' hover:bg-gray-300 dark:hover:bg-gray-800'}"
+						? 'bg-gray-200 dark:bg-gray-800'
+						: ' hover:bg-gray-100 dark:hover:bg-gray-850'}"
 					on:click={() => {
 						selectedTab = 'general';
 					}}
@@ -91,8 +92,8 @@
 				<button
 					class="px-2.5 py-2.5 min-w-fit rounded-lg flex-1 md:flex-none flex text-right transition {selectedTab ===
 					'interface'
-						? 'bg-gray-200 dark:bg-gray-700'
-						: ' hover:bg-gray-300 dark:hover:bg-gray-800'}"
+						? 'bg-gray-200 dark:bg-gray-800'
+						: ' hover:bg-gray-100 dark:hover:bg-gray-850'}"
 					on:click={() => {
 						selectedTab = 'interface';
 					}}
@@ -117,8 +118,8 @@
 				<button
 					class="px-2.5 py-2.5 min-w-fit rounded-lg flex-1 md:flex-none flex text-right transition {selectedTab ===
 					'personalization'
-						? 'bg-gray-200 dark:bg-gray-700'
-						: ' hover:bg-gray-300 dark:hover:bg-gray-800'}"
+						? 'bg-gray-200 dark:bg-gray-800'
+						: ' hover:bg-gray-100 dark:hover:bg-gray-850'}"
 					on:click={() => {
 						selectedTab = 'personalization';
 					}}
@@ -132,8 +133,8 @@
 				<button
 					class="px-2.5 py-2.5 min-w-fit rounded-lg flex-1 md:flex-none flex text-right transition {selectedTab ===
 					'audio'
-						? 'bg-gray-200 dark:bg-gray-700'
-						: ' hover:bg-gray-300 dark:hover:bg-gray-800'}"
+						? 'bg-gray-200 dark:bg-gray-800'
+						: ' hover:bg-gray-100 dark:hover:bg-gray-850'}"
 					on:click={() => {
 						selectedTab = 'audio';
 					}}
@@ -156,11 +157,35 @@
 					<div class=" self-center">{$i18n.t('Audio')}</div>
 				</button>
 
+				<button
+					class="px-2.5 py-2.5 min-w-fit rounded-lg flex-1 md:flex-none flex text-right transition {selectedTab ===
+					'valves'
+						? 'bg-gray-200 dark:bg-gray-800'
+						: ' hover:bg-gray-100 dark:hover:bg-gray-850'}"
+					on:click={() => {
+						selectedTab = 'valves';
+					}}
+				>
+					<div class=" self-center mr-2">
+						<svg
+							xmlns="http://www.w3.org/2000/svg"
+							viewBox="0 0 24 24"
+							fill="currentColor"
+							class="size-4"
+						>
+							<path
+								d="M18.75 12.75h1.5a.75.75 0 0 0 0-1.5h-1.5a.75.75 0 0 0 0 1.5ZM12 6a.75.75 0 0 1 .75-.75h7.5a.75.75 0 0 1 0 1.5h-7.5A.75.75 0 0 1 12 6ZM12 18a.75.75 0 0 1 .75-.75h7.5a.75.75 0 0 1 0 1.5h-7.5A.75.75 0 0 1 12 18ZM3.75 6.75h1.5a.75.75 0 1 0 0-1.5h-1.5a.75.75 0 0 0 0 1.5ZM5.25 18.75h-1.5a.75.75 0 0 1 0-1.5h1.5a.75.75 0 0 1 0 1.5ZM3 12a.75.75 0 0 1 .75-.75h7.5a.75.75 0 0 1 0 1.5h-7.5A.75.75 0 0 1 3 12ZM9 3.75a2.25 2.25 0 1 0 0 4.5 2.25 2.25 0 0 0 0-4.5ZM12.75 12a2.25 2.25 0 1 1 4.5 0 2.25 2.25 0 0 1-4.5 0ZM9 15.75a2.25 2.25 0 1 0 0 4.5 2.25 2.25 0 0 0 0-4.5Z"
+							/>
+						</svg>
+					</div>
+					<div class=" self-center">{$i18n.t('Valves')}</div>
+				</button>
+
 				<button
 					class="px-2.5 py-2.5 min-w-fit rounded-lg flex-1 md:flex-none flex text-right transition {selectedTab ===
 					'chats'
-						? 'bg-gray-200 dark:bg-gray-700'
-						: ' hover:bg-gray-300 dark:hover:bg-gray-800'}"
+						? 'bg-gray-200 dark:bg-gray-800'
+						: ' hover:bg-gray-100 dark:hover:bg-gray-850'}"
 					on:click={() => {
 						selectedTab = 'chats';
 					}}
@@ -185,8 +210,8 @@
 				<button
 					class="px-2.5 py-2.5 min-w-fit rounded-lg flex-1 md:flex-none flex text-right transition {selectedTab ===
 					'account'
-						? 'bg-gray-200 dark:bg-gray-700'
-						: ' hover:bg-gray-300 dark:hover:bg-gray-800'}"
+						? 'bg-gray-200 dark:bg-gray-800'
+						: ' hover:bg-gray-100 dark:hover:bg-gray-850'}"
 					on:click={() => {
 						selectedTab = 'account';
 					}}
@@ -212,8 +237,8 @@
 					<button
 						class="px-2.5 py-2.5 min-w-fit rounded-lg flex-1 md:flex-none flex text-right transition {selectedTab ===
 						'admin'
-							? 'bg-gray-200 dark:bg-gray-700'
-							: ' hover:bg-gray-300 dark:hover:bg-gray-800'}"
+							? 'bg-gray-200 dark:bg-gray-800'
+							: ' hover:bg-gray-100 dark:hover:bg-gray-850'}"
 						on:click={async () => {
 							await goto('/admin/settings');
 							show = false;
@@ -240,8 +265,8 @@
 				<button
 					class="px-2.5 py-2.5 min-w-fit rounded-lg flex-1 md:flex-none flex text-right transition {selectedTab ===
 					'about'
-						? 'bg-gray-200 dark:bg-gray-700'
-						: ' hover:bg-gray-300 dark:hover:bg-gray-800'}"
+						? 'bg-gray-200 dark:bg-gray-800'
+						: ' hover:bg-gray-100 dark:hover:bg-gray-850'}"
 					on:click={() => {
 						selectedTab = 'about';
 					}}
@@ -293,6 +318,13 @@
 							toast.success($i18n.t('Settings saved successfully!'));
 						}}
 					/>
+				{:else if selectedTab === 'valves'}
+					<Valves
+						{saveSettings}
+						on:save={() => {
+							toast.success($i18n.t('Settings saved successfully!'));
+						}}
+					/>
 				{:else if selectedTab === 'chats'}
 					<Chats {saveSettings} />
 				{:else if selectedTab === 'account'}

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

@@ -0,0 +1,19 @@
+<script lang="ts">
+	export let className = 'size-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="M21 8.25c0-2.485-2.099-4.5-4.688-4.5-1.935 0-3.597 1.126-4.312 2.733-.715-1.607-2.377-2.733-4.313-2.733C5.1 3.75 3 5.765 3 8.25c0 7.22 9 12 9 12s9-4.78 9-12Z"
+	/>
+</svg>

+ 0 - 0
src/lib/components/workspace/Playground.svelte → src/lib/components/playground/Playground.svelte


+ 6 - 2
src/lib/components/workspace/Documents.svelte

@@ -538,7 +538,9 @@
 				documentsImportInputElement.click();
 			}}
 		>
-			<div class=" self-center mr-2 font-medium">{$i18n.t('Import Documents Mapping')}</div>
+			<div class=" self-center mr-2 font-medium line-clamp-1">
+				{$i18n.t('Import Documents Mapping')}
+			</div>
 
 			<div class=" self-center">
 				<svg
@@ -565,7 +567,9 @@
 				saveAs(blob, `documents-mapping-export-${Date.now()}.json`);
 			}}
 		>
-			<div class=" self-center mr-2 font-medium">{$i18n.t('Export Documents Mapping')}</div>
+			<div class=" self-center mr-2 font-medium line-clamp-1">
+				{$i18n.t('Export Documents Mapping')}
+			</div>
 
 			<div class=" self-center">
 				<svg

+ 172 - 118
src/lib/components/workspace/Functions.svelte

@@ -13,13 +13,20 @@
 		deleteFunctionById,
 		exportFunctions,
 		getFunctionById,
-		getFunctions
+		getFunctions,
+		toggleFunctionById
 	} from '$lib/apis/functions';
 
 	import ArrowDownTray from '../icons/ArrowDownTray.svelte';
 	import Tooltip from '../common/Tooltip.svelte';
 	import ConfirmDialog from '../common/ConfirmDialog.svelte';
 	import { getModels } from '$lib/apis';
+	import FunctionMenu from './Functions/FunctionMenu.svelte';
+	import EllipsisHorizontal from '../icons/EllipsisHorizontal.svelte';
+	import Switch from '../common/Switch.svelte';
+	import ValvesModal from './common/ValvesModal.svelte';
+	import ManifestModal from './common/ManifestModal.svelte';
+	import Heart from '../icons/Heart.svelte';
 
 	const i18n = getContext('i18n');
 
@@ -28,6 +35,58 @@
 
 	let showConfirm = false;
 	let query = '';
+
+	let showManifestModal = false;
+	let showValvesModal = false;
+	let selectedFunction = null;
+
+	const shareHandler = async (tool) => {
+		console.log(tool);
+	};
+
+	const cloneHandler = async (func) => {
+		const _function = await getFunctionById(localStorage.token, func.id).catch((error) => {
+			toast.error(error);
+			return null;
+		});
+
+		if (_function) {
+			sessionStorage.function = JSON.stringify({
+				..._function,
+				id: `${_function.id}_clone`,
+				name: `${_function.name} (Clone)`
+			});
+			goto('/workspace/functions/create');
+		}
+	};
+
+	const exportHandler = async (func) => {
+		const _function = await getFunctionById(localStorage.token, func.id).catch((error) => {
+			toast.error(error);
+			return null;
+		});
+
+		if (_function) {
+			let blob = new Blob([JSON.stringify([_function])], {
+				type: 'application/json'
+			});
+			saveAs(blob, `function-${_function.id}-export-${Date.now()}.json`);
+		}
+	};
+
+	const deleteHandler = async (func) => {
+		const res = await deleteFunctionById(localStorage.token, func.id).catch((error) => {
+			toast.error(error);
+			return null;
+		});
+
+		if (res) {
+			toast.success('Function deleted successfully');
+
+			functions.set(await getFunctions(localStorage.token));
+			models.set(await getModels(localStorage.token));
+		}
+	};
 </script>
 
 <svelte:head>
@@ -87,18 +146,14 @@
 	{#each $functions.filter((f) => query === '' || f.name
 				.toLowerCase()
 				.includes(query.toLowerCase()) || f.id.toLowerCase().includes(query.toLowerCase())) as func}
-		<button
+		<div
 			class=" flex space-x-4 cursor-pointer w-full px-3 py-2 dark:hover:bg-white/5 hover:bg-black/5 rounded-xl"
-			type="button"
-			on:click={() => {
-				goto(`/workspace/functions/edit?id=${encodeURIComponent(func.id)}`);
-			}}
 		>
-			<div class=" flex flex-1 space-x-4 cursor-pointer w-full">
-				<a
-					href={`/workspace/functions/edit?id=${encodeURIComponent(func.id)}`}
-					class="flex items-center text-left"
-				>
+			<a
+				class=" flex flex-1 space-x-3.5 cursor-pointer w-full"
+				href={`/workspace/functions/edit?id=${encodeURIComponent(func.id)}`}
+			>
+				<div class="flex items-center text-left">
 					<div class=" flex-1 self-center pl-1">
 						<div class=" font-semibold flex items-center gap-1.5">
 							<div
@@ -107,67 +162,52 @@
 								{func.type}
 							</div>
 
-							<div>
+							{#if func?.meta?.manifest?.version}
+								<div
+									class="text-xs font-black px-1 rounded line-clamp-1 bg-gray-500/20 text-gray-700 dark:text-gray-200"
+								>
+									v{func?.meta?.manifest?.version ?? ''}
+								</div>
+							{/if}
+
+							<div class=" line-clamp-1">
 								{func.name}
 							</div>
 						</div>
 
 						<div class="flex gap-1.5 px-1">
-							<div class=" text-gray-500 text-xs font-medium">{func.id}</div>
+							<div class=" text-gray-500 text-xs font-medium flex-shrink-0">{func.id}</div>
 
 							<div class=" text-xs overflow-hidden text-ellipsis line-clamp-1">
 								{func.meta.description}
 							</div>
 						</div>
 					</div>
-				</a>
-			</div>
-			<div class="flex flex-row space-x-1 self-center">
-				<Tooltip content="Edit">
-					<a
-						class="self-center w-fit text-sm px-2 py-2 dark:text-gray-300 dark:hover:text-white hover:bg-black/5 dark:hover:bg-white/5 rounded-xl"
-						type="button"
-						href={`/workspace/functions/edit?id=${encodeURIComponent(func.id)}`}
-					>
-						<svg
-							xmlns="http://www.w3.org/2000/svg"
-							fill="none"
-							viewBox="0 0 24 24"
-							stroke-width="1.5"
-							stroke="currentColor"
-							class="w-4 h-4"
+				</div>
+			</a>
+			<div class="flex flex-row gap-0.5 self-center">
+				{#if func?.meta?.manifest?.funding_url ?? false}
+					<Tooltip content="Support">
+						<button
+							class="self-center w-fit text-sm px-2 py-2 dark:text-gray-300 dark:hover:text-white hover:bg-black/5 dark:hover:bg-white/5 rounded-xl"
+							type="button"
+							on:click={() => {
+								selectedFunction = func;
+								showManifestModal = true;
+							}}
 						>
-							<path
-								stroke-linecap="round"
-								stroke-linejoin="round"
-								d="M16.862 4.487l1.687-1.688a1.875 1.875 0 112.652 2.652L6.832 19.82a4.5 4.5 0 01-1.897 1.13l-2.685.8.8-2.685a4.5 4.5 0 011.13-1.897L16.863 4.487zm0 0L19.5 7.125"
-							/>
-						</svg>
-					</a>
-				</Tooltip>
+							<Heart />
+						</button>
+					</Tooltip>
+				{/if}
 
-				<Tooltip content="Clone">
+				<Tooltip content="Valves">
 					<button
 						class="self-center w-fit text-sm px-2 py-2 dark:text-gray-300 dark:hover:text-white hover:bg-black/5 dark:hover:bg-white/5 rounded-xl"
 						type="button"
-						on:click={async (e) => {
-							e.stopPropagation();
-
-							const _function = await getFunctionById(localStorage.token, func.id).catch(
-								(error) => {
-									toast.error(error);
-									return null;
-								}
-							);
-
-							if (_function) {
-								sessionStorage.function = JSON.stringify({
-									..._function,
-									id: `${_function.id}_clone`,
-									name: `${_function.name} (Clone)`
-								});
-								goto('/workspace/functions/create');
-							}
+						on:click={() => {
+							selectedFunction = func;
+							showValvesModal = true;
 						}}
 					>
 						<svg
@@ -176,81 +216,59 @@
 							viewBox="0 0 24 24"
 							stroke-width="1.5"
 							stroke="currentColor"
-							class="w-4 h-4"
+							class="size-4"
 						>
 							<path
 								stroke-linecap="round"
 								stroke-linejoin="round"
-								d="M15.75 17.25v3.375c0 .621-.504 1.125-1.125 1.125h-9.75a1.125 1.125 0 0 1-1.125-1.125V7.875c0-.621.504-1.125 1.125-1.125H6.75a9.06 9.06 0 0 1 1.5.124m7.5 10.376h3.375c.621 0 1.125-.504 1.125-1.125V11.25c0-4.46-3.243-8.161-7.5-8.876a9.06 9.06 0 0 0-1.5-.124H9.375c-.621 0-1.125.504-1.125 1.125v3.5m7.5 10.375H9.375a1.125 1.125 0 0 1-1.125-1.125v-9.25m12 6.625v-1.875a3.375 3.375 0 0 0-3.375-3.375h-1.5a1.125 1.125 0 0 1-1.125-1.125v-1.5a3.375 3.375 0 0 0-3.375-3.375H9.75"
+								d="M9.594 3.94c.09-.542.56-.94 1.11-.94h2.593c.55 0 1.02.398 1.11.94l.213 1.281c.063.374.313.686.645.87.074.04.147.083.22.127.325.196.72.257 1.075.124l1.217-.456a1.125 1.125 0 0 1 1.37.49l1.296 2.247a1.125 1.125 0 0 1-.26 1.431l-1.003.827c-.293.241-.438.613-.43.992a7.723 7.723 0 0 1 0 .255c-.008.378.137.75.43.991l1.004.827c.424.35.534.955.26 1.43l-1.298 2.247a1.125 1.125 0 0 1-1.369.491l-1.217-.456c-.355-.133-.75-.072-1.076.124a6.47 6.47 0 0 1-.22.128c-.331.183-.581.495-.644.869l-.213 1.281c-.09.543-.56.94-1.11.94h-2.594c-.55 0-1.019-.398-1.11-.94l-.213-1.281c-.062-.374-.312-.686-.644-.87a6.52 6.52 0 0 1-.22-.127c-.325-.196-.72-.257-1.076-.124l-1.217.456a1.125 1.125 0 0 1-1.369-.49l-1.297-2.247a1.125 1.125 0 0 1 .26-1.431l1.004-.827c.292-.24.437-.613.43-.991a6.932 6.932 0 0 1 0-.255c.007-.38-.138-.751-.43-.992l-1.004-.827a1.125 1.125 0 0 1-.26-1.43l1.297-2.247a1.125 1.125 0 0 1 1.37-.491l1.216.456c.356.133.751.072 1.076-.124.072-.044.146-.086.22-.128.332-.183.582-.495.644-.869l.214-1.28Z"
+							/>
+							<path
+								stroke-linecap="round"
+								stroke-linejoin="round"
+								d="M15 12a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z"
 							/>
 						</svg>
 					</button>
 				</Tooltip>
 
-				<Tooltip content="Export">
+				<FunctionMenu
+					editHandler={() => {
+						goto(`/workspace/functions/edit?id=${encodeURIComponent(func.id)}`);
+					}}
+					shareHandler={() => {
+						shareHandler(func);
+					}}
+					cloneHandler={() => {
+						cloneHandler(func);
+					}}
+					exportHandler={() => {
+						exportHandler(func);
+					}}
+					deleteHandler={async () => {
+						deleteHandler(func);
+					}}
+					onClose={() => {}}
+				>
 					<button
-						class="self-center w-fit text-sm px-2 py-2 dark:text-gray-300 dark:hover:text-white hover:bg-black/5 dark:hover:bg-white/5 rounded-xl"
+						class="self-center w-fit text-sm p-1.5 dark:text-gray-300 dark:hover:text-white hover:bg-black/5 dark:hover:bg-white/5 rounded-xl"
 						type="button"
-						on:click={async (e) => {
-							e.stopPropagation();
-
-							const _function = await getFunctionById(localStorage.token, func.id).catch(
-								(error) => {
-									toast.error(error);
-									return null;
-								}
-							);
-
-							if (_function) {
-								let blob = new Blob([JSON.stringify([_function])], {
-									type: 'application/json'
-								});
-								saveAs(blob, `function-${_function.id}-export-${Date.now()}.json`);
-							}
-						}}
 					>
-						<ArrowDownTray />
+						<EllipsisHorizontal className="size-5" />
 					</button>
-				</Tooltip>
-
-				<Tooltip content="Delete">
-					<button
-						class="self-center w-fit text-sm px-2 py-2 dark:text-gray-300 dark:hover:text-white hover:bg-black/5 dark:hover:bg-white/5 rounded-xl"
-						type="button"
-						on:click={async (e) => {
-							e.stopPropagation();
-
-							const res = await deleteFunctionById(localStorage.token, func.id).catch((error) => {
-								toast.error(error);
-								return null;
-							});
-
-							if (res) {
-								toast.success('Function deleted successfully');
-
-								functions.set(await getFunctions(localStorage.token));
-								models.set(await getModels(localStorage.token));
-							}
+				</FunctionMenu>
+
+				<div class=" self-center mx-1">
+					<Switch
+						bind:state={func.is_active}
+						on:change={async (e) => {
+							toggleFunctionById(localStorage.token, func.id);
+							models.set(await getModels(localStorage.token));
 						}}
-					>
-						<svg
-							xmlns="http://www.w3.org/2000/svg"
-							fill="none"
-							viewBox="0 0 24 24"
-							stroke-width="1.5"
-							stroke="currentColor"
-							class="w-4 h-4"
-						>
-							<path
-								stroke-linecap="round"
-								stroke-linejoin="round"
-								d="M14.74 9l-.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 01-2.244 2.077H8.084a2.25 2.25 0 01-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 00-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 013.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 00-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 00-7.5 0"
-							/>
-						</svg>
-					</button>
-				</Tooltip>
+					/>
+				</div>
 			</div>
-		</button>
+		</div>
 	{/each}
 </div>
 
@@ -281,7 +299,7 @@
 				functionsImportInputElement.click();
 			}}
 		>
-			<div class=" self-center mr-2 font-medium">{$i18n.t('Import Functions')}</div>
+			<div class=" self-center mr-2 font-medium line-clamp-1">{$i18n.t('Import Functions')}</div>
 
 			<div class=" self-center">
 				<svg
@@ -315,7 +333,7 @@
 				}
 			}}
 		>
-			<div class=" self-center mr-2 font-medium">{$i18n.t('Export Functions')}</div>
+			<div class=" self-center mr-2 font-medium line-clamp-1">{$i18n.t('Export Functions')}</div>
 
 			<div class=" self-center">
 				<svg
@@ -335,6 +353,42 @@
 	</div>
 </div>
 
+<div class=" my-16">
+	<div class=" text-lg font-semibold mb-3 line-clamp-1">
+		{$i18n.t('Made by OpenWebUI Community')}
+	</div>
+
+	<a
+		class=" flex space-x-4 cursor-pointer w-full mb-2 px-3 py-2"
+		href="https://openwebui.com/"
+		target="_blank"
+	>
+		<div class=" self-center w-10 flex-shrink-0">
+			<div
+				class="w-full h-10 flex justify-center rounded-full bg-transparent dark:bg-gray-700 border border-dashed border-gray-200"
+			>
+				<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="w-6">
+					<path
+						fill-rule="evenodd"
+						d="M12 3.75a.75.75 0 01.75.75v6.75h6.75a.75.75 0 010 1.5h-6.75v6.75a.75.75 0 01-1.5 0v-6.75H4.5a.75.75 0 010-1.5h6.75V4.5a.75.75 0 01.75-.75z"
+						clip-rule="evenodd"
+					/>
+				</svg>
+			</div>
+		</div>
+
+		<div class=" self-center">
+			<div class=" font-bold line-clamp-1">{$i18n.t('Discover a function')}</div>
+			<div class=" text-sm line-clamp-1">
+				{$i18n.t('Discover, download, and explore custom functions')}
+			</div>
+		</div>
+	</a>
+</div>
+
+<ManifestModal bind:show={showManifestModal} manifest={selectedFunction?.meta?.manifest ?? {}} />
+<ValvesModal bind:show={showValvesModal} type="function" id={selectedFunction?.id ?? null} />
+
 <ConfirmDialog
 	bind:show={showConfirm}
 	on:confirm={() => {

+ 117 - 0
src/lib/components/workspace/Functions/FunctionMenu.svelte

@@ -0,0 +1,117 @@
+<script lang="ts">
+	import { DropdownMenu } from 'bits-ui';
+	import { flyAndScale } from '$lib/utils/transitions';
+	import { getContext } from 'svelte';
+
+	import Dropdown from '$lib/components/common/Dropdown.svelte';
+	import GarbageBin from '$lib/components/icons/GarbageBin.svelte';
+	import Pencil from '$lib/components/icons/Pencil.svelte';
+	import Tooltip from '$lib/components/common/Tooltip.svelte';
+	import Tags from '$lib/components/chat/Tags.svelte';
+	import Share from '$lib/components/icons/Share.svelte';
+	import ArchiveBox from '$lib/components/icons/ArchiveBox.svelte';
+	import DocumentDuplicate from '$lib/components/icons/DocumentDuplicate.svelte';
+	import ArrowDownTray from '$lib/components/icons/ArrowDownTray.svelte';
+
+	const i18n = getContext('i18n');
+
+	export let editHandler: Function;
+	export let shareHandler: Function;
+	export let cloneHandler: Function;
+	export let exportHandler: Function;
+	export let deleteHandler: Function;
+	export let onClose: Function;
+
+	let show = false;
+</script>
+
+<Dropdown
+	bind:show
+	on:change={(e) => {
+		if (e.detail === false) {
+			onClose();
+		}
+	}}
+>
+	<Tooltip content={$i18n.t('More')}>
+		<slot />
+	</Tooltip>
+
+	<div slot="content">
+		<DropdownMenu.Content
+			class="w-full max-w-[160px] rounded-xl px-1 py-1.5 border border-gray-300/30 dark:border-gray-700/50 z-50 bg-white dark:bg-gray-850 dark:text-white shadow"
+			sideOffset={-2}
+			side="bottom"
+			align="start"
+			transition={flyAndScale}
+		>
+			<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-md"
+				on:click={() => {
+					editHandler();
+				}}
+			>
+				<svg
+					xmlns="http://www.w3.org/2000/svg"
+					fill="none"
+					viewBox="0 0 24 24"
+					stroke-width="1.5"
+					stroke="currentColor"
+					class="w-4 h-4"
+				>
+					<path
+						stroke-linecap="round"
+						stroke-linejoin="round"
+						d="M16.862 4.487l1.687-1.688a1.875 1.875 0 112.652 2.652L6.832 19.82a4.5 4.5 0 01-1.897 1.13l-2.685.8.8-2.685a4.5 4.5 0 011.13-1.897L16.863 4.487zm0 0L19.5 7.125"
+					/>
+				</svg>
+
+				<div class="flex items-center">{$i18n.t('Edit')}</div>
+			</DropdownMenu.Item>
+
+			<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-md"
+				on:click={() => {
+					shareHandler();
+				}}
+			>
+				<Share />
+				<div class="flex items-center">{$i18n.t('Share')}</div>
+			</DropdownMenu.Item>
+
+			<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-md"
+				on:click={() => {
+					cloneHandler();
+				}}
+			>
+				<DocumentDuplicate />
+
+				<div class="flex items-center">{$i18n.t('Clone')}</div>
+			</DropdownMenu.Item>
+
+			<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-md"
+				on:click={() => {
+					exportHandler();
+				}}
+			>
+				<ArrowDownTray />
+
+				<div class="flex items-center">{$i18n.t('Export')}</div>
+			</DropdownMenu.Item>
+
+			<hr class="border-gray-100 dark:border-gray-800 my-1" />
+
+			<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-md"
+				on:click={() => {
+					deleteHandler();
+				}}
+			>
+				<GarbageBin strokeWidth="2" />
+				<div class="flex items-center">{$i18n.t('Delete')}</div>
+			</DropdownMenu.Item>
+		</DropdownMenu.Content>
+	</div>
+</Dropdown>

+ 13 - 9
src/lib/components/workspace/Models.svelte

@@ -256,7 +256,7 @@
 <hr class=" dark:border-gray-850 my-2.5" />
 
 <a class=" flex space-x-4 cursor-pointer w-full mb-2 px-3 py-2" href="/workspace/models/create">
-	<div class=" self-center w-10">
+	<div class=" self-center w-10 flex-shrink-0">
 		<div
 			class="w-full h-10 flex justify-center rounded-full bg-transparent dark:bg-gray-700 border border-dashed border-gray-200"
 		>
@@ -271,8 +271,8 @@
 	</div>
 
 	<div class=" self-center">
-		<div class=" font-bold">{$i18n.t('Create a model')}</div>
-		<div class=" text-sm">{$i18n.t('Customize models for a specific purpose')}</div>
+		<div class=" font-bold line-clamp-1">{$i18n.t('Create a model')}</div>
+		<div class=" text-sm line-clamp-1">{$i18n.t('Customize models for a specific purpose')}</div>
 	</div>
 </a>
 
@@ -412,7 +412,7 @@
 				modelsImportInputElement.click();
 			}}
 		>
-			<div class=" self-center mr-2 font-medium">{$i18n.t('Import Models')}</div>
+			<div class=" self-center mr-2 font-medium line-clamp-1">{$i18n.t('Import Models')}</div>
 
 			<div class=" self-center">
 				<svg
@@ -436,7 +436,7 @@
 				downloadModels($models);
 			}}
 		>
-			<div class=" self-center mr-2 font-medium">{$i18n.t('Export Models')}</div>
+			<div class=" self-center mr-2 font-medium line-clamp-1">{$i18n.t('Export Models')}</div>
 
 			<div class=" self-center">
 				<svg
@@ -494,14 +494,16 @@
 </div>
 
 <div class=" my-16">
-	<div class=" text-lg font-semibold mb-3">{$i18n.t('Made by OpenWebUI Community')}</div>
+	<div class=" text-lg font-semibold mb-3 line-clamp-1">
+		{$i18n.t('Made by OpenWebUI Community')}
+	</div>
 
 	<a
 		class=" flex space-x-4 cursor-pointer w-full mb-2 px-3 py-2"
 		href="https://openwebui.com/"
 		target="_blank"
 	>
-		<div class=" self-center w-10">
+		<div class=" self-center w-10 flex-shrink-0">
 			<div
 				class="w-full h-10 flex justify-center rounded-full bg-transparent dark:bg-gray-700 border border-dashed border-gray-200"
 			>
@@ -516,8 +518,10 @@
 		</div>
 
 		<div class=" self-center">
-			<div class=" font-bold">{$i18n.t('Discover a model')}</div>
-			<div class=" text-sm">{$i18n.t('Discover, download, and explore model presets')}</div>
+			<div class=" font-bold line-clamp-1">{$i18n.t('Discover a model')}</div>
+			<div class=" text-sm line-clamp-1">
+				{$i18n.t('Discover, download, and explore model presets')}
+			</div>
 		</div>
 	</a>
 </div>

+ 49 - 77
src/lib/components/workspace/Prompts.svelte

@@ -8,13 +8,16 @@
 	import { createNewPrompt, deletePromptByCommand, getPrompts } from '$lib/apis/prompts';
 	import { error } from '@sveltejs/kit';
 	import { goto } from '$app/navigation';
+	import PromptMenu from './Prompts/PromptMenu.svelte';
+	import EllipsisHorizontal from '../icons/EllipsisHorizontal.svelte';
 
 	const i18n = getContext('i18n');
 
 	let importFiles = '';
 	let query = '';
 	let promptsImportInputElement: HTMLInputElement;
-	const sharePrompt = async (prompt) => {
+
+	const shareHandler = async (prompt) => {
 		toast.success($i18n.t('Redirecting you to OpenWebUI Community'));
 
 		const url = 'https://openwebui.com';
@@ -32,7 +35,20 @@
 		);
 	};
 
-	const deletePrompt = async (command) => {
+	const cloneHandler = async (prompt) => {
+		sessionStorage.prompt = JSON.stringify(prompt);
+		goto('/workspace/prompts/create');
+	};
+
+	const exportHandler = async (prompt) => {
+		let blob = new Blob([JSON.stringify([prompt])], {
+			type: 'application/json'
+		});
+		saveAs(blob, `prompt-export-${Date.now()}.json`);
+	};
+
+	const deleteHandler = async (prompt) => {
+		const command = prompt.command;
 		await deletePromptByCommand(localStorage.token, command);
 		await prompts.set(await getPrompts(localStorage.token));
 	};
@@ -99,14 +115,14 @@
 			<div class=" flex flex-1 space-x-4 cursor-pointer w-full">
 				<a href={`/workspace/prompts/edit?command=${encodeURIComponent(prompt.command)}`}>
 					<div class=" flex-1 self-center pl-5">
-						<div class=" font-bold">{prompt.command}</div>
+						<div class=" font-bold line-clamp-1">{prompt.command}</div>
 						<div class=" text-xs overflow-hidden text-ellipsis line-clamp-1">
 							{prompt.title}
 						</div>
 					</div>
 				</a>
 			</div>
-			<div class="flex flex-row space-x-1 self-center">
+			<div class="flex flex-row gap-0.5 self-center">
 				<a
 					class="self-center w-fit text-sm px-2 py-2 dark:text-gray-300 dark:hover:text-white hover:bg-black/5 dark:hover:bg-white/5 rounded-xl"
 					type="button"
@@ -128,76 +144,28 @@
 					</svg>
 				</a>
 
-				<button
-					class="self-center w-fit text-sm px-2 py-2 dark:text-gray-300 dark:hover:text-white hover:bg-black/5 dark:hover:bg-white/5 rounded-xl"
-					type="button"
-					on:click={() => {
-						// console.log(modelfile);
-						sessionStorage.prompt = JSON.stringify(prompt);
-						goto('/workspace/prompts/create');
+				<PromptMenu
+					shareHandler={() => {
+						shareHandler(prompt);
 					}}
-				>
-					<svg
-						xmlns="http://www.w3.org/2000/svg"
-						fill="none"
-						viewBox="0 0 24 24"
-						stroke-width="1.5"
-						stroke="currentColor"
-						class="w-4 h-4"
-					>
-						<path
-							stroke-linecap="round"
-							stroke-linejoin="round"
-							d="M15.75 17.25v3.375c0 .621-.504 1.125-1.125 1.125h-9.75a1.125 1.125 0 0 1-1.125-1.125V7.875c0-.621.504-1.125 1.125-1.125H6.75a9.06 9.06 0 0 1 1.5.124m7.5 10.376h3.375c.621 0 1.125-.504 1.125-1.125V11.25c0-4.46-3.243-8.161-7.5-8.876a9.06 9.06 0 0 0-1.5-.124H9.375c-.621 0-1.125.504-1.125 1.125v3.5m7.5 10.375H9.375a1.125 1.125 0 0 1-1.125-1.125v-9.25m12 6.625v-1.875a3.375 3.375 0 0 0-3.375-3.375h-1.5a1.125 1.125 0 0 1-1.125-1.125v-1.5a3.375 3.375 0 0 0-3.375-3.375H9.75"
-						/>
-					</svg>
-				</button>
-
-				<button
-					class="self-center w-fit text-sm px-2 py-2 dark:text-gray-300 dark:hover:text-white hover:bg-black/5 dark:hover:bg-white/5 rounded-xl"
-					type="button"
-					on:click={() => {
-						sharePrompt(prompt);
+					cloneHandler={() => {
+						cloneHandler(prompt);
 					}}
-				>
-					<svg
-						xmlns="http://www.w3.org/2000/svg"
-						fill="none"
-						viewBox="0 0 24 24"
-						stroke-width="1.5"
-						stroke="currentColor"
-						class="w-4 h-4"
-					>
-						<path
-							stroke-linecap="round"
-							stroke-linejoin="round"
-							d="M7.217 10.907a2.25 2.25 0 100 2.186m0-2.186c.18.324.283.696.283 1.093s-.103.77-.283 1.093m0-2.186l9.566-5.314m-9.566 7.5l9.566 5.314m0 0a2.25 2.25 0 103.935 2.186 2.25 2.25 0 00-3.935-2.186zm0-12.814a2.25 2.25 0 103.933-2.185 2.25 2.25 0 00-3.933 2.185z"
-						/>
-					</svg>
-				</button>
-
-				<button
-					class="self-center w-fit text-sm px-2 py-2 dark:text-gray-300 dark:hover:text-white hover:bg-black/5 dark:hover:bg-white/5 rounded-xl"
-					type="button"
-					on:click={() => {
-						deletePrompt(prompt.command);
+					exportHandler={() => {
+						exportHandler(prompt);
+					}}
+					deleteHandler={async () => {
+						deleteHandler(prompt);
 					}}
+					onClose={() => {}}
 				>
-					<svg
-						xmlns="http://www.w3.org/2000/svg"
-						fill="none"
-						viewBox="0 0 24 24"
-						stroke-width="1.5"
-						stroke="currentColor"
-						class="w-4 h-4"
+					<button
+						class="self-center w-fit text-sm p-1.5 dark:text-gray-300 dark:hover:text-white hover:bg-black/5 dark:hover:bg-white/5 rounded-xl"
+						type="button"
 					>
-						<path
-							stroke-linecap="round"
-							stroke-linejoin="round"
-							d="M14.74 9l-.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 01-2.244 2.077H8.084a2.25 2.25 0 01-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 00-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 013.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 00-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 00-7.5 0"
-						/>
-					</svg>
-				</button>
+						<EllipsisHorizontal className="size-5" />
+					</button>
+				</PromptMenu>
 			</div>
 		</div>
 	{/each}
@@ -245,7 +213,7 @@
 				promptsImportInputElement.click();
 			}}
 		>
-			<div class=" self-center mr-2 font-medium">{$i18n.t('Import Prompts')}</div>
+			<div class=" self-center mr-2 font-medium line-clamp-1">{$i18n.t('Import Prompts')}</div>
 
 			<div class=" self-center">
 				<svg
@@ -273,7 +241,7 @@
 				saveAs(blob, `prompts-export-${Date.now()}.json`);
 			}}
 		>
-			<div class=" self-center mr-2 font-medium">{$i18n.t('Export Prompts')}</div>
+			<div class=" self-center mr-2 font-medium line-clamp-1">{$i18n.t('Export Prompts')}</div>
 
 			<div class=" self-center">
 				<svg
@@ -302,14 +270,16 @@
 </div>
 
 <div class=" my-16">
-	<div class=" text-lg font-semibold mb-3">{$i18n.t('Made by OpenWebUI Community')}</div>
+	<div class=" text-lg font-semibold mb-3 line-clamp-1">
+		{$i18n.t('Made by OpenWebUI Community')}
+	</div>
 
 	<a
-		class=" flex space-x-4 cursor-pointer w-full mb-3 px-3 py-2"
-		href="https://openwebui.com/?type=prompts"
+		class=" flex space-x-4 cursor-pointer w-full mb-2 px-3 py-2"
+		href="https://openwebui.com/"
 		target="_blank"
 	>
-		<div class=" self-center w-10">
+		<div class=" self-center w-10 flex-shrink-0">
 			<div
 				class="w-full h-10 flex justify-center rounded-full bg-transparent dark:bg-gray-700 border border-dashed border-gray-200"
 			>
@@ -324,8 +294,10 @@
 		</div>
 
 		<div class=" self-center">
-			<div class=" font-bold">{$i18n.t('Discover a prompt')}</div>
-			<div class=" text-sm">{$i18n.t('Discover, download, and explore custom prompts')}</div>
+			<div class=" font-bold line-clamp-1">{$i18n.t('Discover a prompt')}</div>
+			<div class=" text-sm line-clamp-1">
+				{$i18n.t('Discover, download, and explore custom prompts')}
+			</div>
 		</div>
 	</a>
 </div>

+ 92 - 0
src/lib/components/workspace/Prompts/PromptMenu.svelte

@@ -0,0 +1,92 @@
+<script lang="ts">
+	import { DropdownMenu } from 'bits-ui';
+	import { flyAndScale } from '$lib/utils/transitions';
+	import { getContext } from 'svelte';
+
+	import Dropdown from '$lib/components/common/Dropdown.svelte';
+	import GarbageBin from '$lib/components/icons/GarbageBin.svelte';
+	import Pencil from '$lib/components/icons/Pencil.svelte';
+	import Tooltip from '$lib/components/common/Tooltip.svelte';
+	import Tags from '$lib/components/chat/Tags.svelte';
+	import Share from '$lib/components/icons/Share.svelte';
+	import ArchiveBox from '$lib/components/icons/ArchiveBox.svelte';
+	import DocumentDuplicate from '$lib/components/icons/DocumentDuplicate.svelte';
+	import ArrowDownTray from '$lib/components/icons/ArrowDownTray.svelte';
+
+	const i18n = getContext('i18n');
+
+	export let shareHandler: Function;
+	export let cloneHandler: Function;
+	export let exportHandler: Function;
+	export let deleteHandler: Function;
+	export let onClose: Function;
+
+	let show = false;
+</script>
+
+<Dropdown
+	bind:show
+	on:change={(e) => {
+		if (e.detail === false) {
+			onClose();
+		}
+	}}
+>
+	<Tooltip content={$i18n.t('More')}>
+		<slot />
+	</Tooltip>
+
+	<div slot="content">
+		<DropdownMenu.Content
+			class="w-full max-w-[160px] rounded-xl px-1 py-1.5 border border-gray-300/30 dark:border-gray-700/50 z-50 bg-white dark:bg-gray-850 dark:text-white shadow"
+			sideOffset={-2}
+			side="bottom"
+			align="start"
+			transition={flyAndScale}
+		>
+			<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-md"
+				on:click={() => {
+					shareHandler();
+				}}
+			>
+				<Share />
+				<div class="flex items-center">{$i18n.t('Share')}</div>
+			</DropdownMenu.Item>
+
+			<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-md"
+				on:click={() => {
+					cloneHandler();
+				}}
+			>
+				<DocumentDuplicate />
+
+				<div class="flex items-center">{$i18n.t('Clone')}</div>
+			</DropdownMenu.Item>
+
+			<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-md"
+				on:click={() => {
+					exportHandler();
+				}}
+			>
+				<ArrowDownTray />
+
+				<div class="flex items-center">{$i18n.t('Export')}</div>
+			</DropdownMenu.Item>
+
+			<hr class="border-gray-100 dark:border-gray-800 my-1" />
+
+			<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-md"
+				on:click={() => {
+					deleteHandler();
+				}}
+			>
+				<GarbageBin strokeWidth="2" />
+				<div class="flex items-center">{$i18n.t('Delete')}</div>
+			</DropdownMenu.Item>
+		</DropdownMenu.Content>
+	</div>
+</Dropdown>

+ 158 - 112
src/lib/components/workspace/Tools.svelte

@@ -18,6 +18,11 @@
 	import ArrowDownTray from '../icons/ArrowDownTray.svelte';
 	import Tooltip from '../common/Tooltip.svelte';
 	import ConfirmDialog from '../common/ConfirmDialog.svelte';
+	import ToolMenu from './Tools/ToolMenu.svelte';
+	import EllipsisHorizontal from '../icons/EllipsisHorizontal.svelte';
+	import ValvesModal from './common/ValvesModal.svelte';
+	import ManifestModal from './common/ManifestModal.svelte';
+	import Heart from '../icons/Heart.svelte';
 
 	const i18n = getContext('i18n');
 
@@ -26,6 +31,56 @@
 
 	let showConfirm = false;
 	let query = '';
+
+	let showManifestModal = false;
+	let showValvesModal = false;
+	let selectedTool = null;
+
+	const shareHandler = async (tool) => {
+		console.log(tool);
+	};
+
+	const cloneHandler = async (tool) => {
+		const _tool = await getToolById(localStorage.token, tool.id).catch((error) => {
+			toast.error(error);
+			return null;
+		});
+
+		if (_tool) {
+			sessionStorage.tool = JSON.stringify({
+				..._tool,
+				id: `${_tool.id}_clone`,
+				name: `${_tool.name} (Clone)`
+			});
+			goto('/workspace/tools/create');
+		}
+	};
+
+	const exportHandler = async (tool) => {
+		const _tool = await getToolById(localStorage.token, tool.id).catch((error) => {
+			toast.error(error);
+			return null;
+		});
+
+		if (_tool) {
+			let blob = new Blob([JSON.stringify([_tool])], {
+				type: 'application/json'
+			});
+			saveAs(blob, `tool-${_tool.id}-export-${Date.now()}.json`);
+		}
+	};
+
+	const deleteHandler = async (tool) => {
+		const res = await deleteToolById(localStorage.token, tool.id).catch((error) => {
+			toast.error(error);
+			return null;
+		});
+
+		if (res) {
+			toast.success('Tool deleted successfully');
+			tools.set(await getTools(localStorage.token));
+		}
+	};
 </script>
 
 <svelte:head>
@@ -85,18 +140,14 @@
 	{#each $tools.filter((t) => query === '' || t.name
 				.toLowerCase()
 				.includes(query.toLowerCase()) || t.id.toLowerCase().includes(query.toLowerCase())) as tool}
-		<button
+		<div
 			class=" flex space-x-4 cursor-pointer w-full px-3 py-2 dark:hover:bg-white/5 hover:bg-black/5 rounded-xl"
-			type="button"
-			on:click={() => {
-				goto(`/workspace/tools/edit?id=${encodeURIComponent(tool.id)}`);
-			}}
 		>
-			<div class=" flex flex-1 space-x-4 cursor-pointer w-full">
-				<a
-					href={`/workspace/tools/edit?id=${encodeURIComponent(tool.id)}`}
-					class="flex items-center text-left"
-				>
+			<a
+				class=" flex flex-1 space-x-3.5 cursor-pointer w-full"
+				href={`/workspace/tools/edit?id=${encodeURIComponent(tool.id)}`}
+			>
+				<div class="flex items-center text-left">
 					<div class=" flex-1 self-center pl-1">
 						<div class=" font-semibold flex items-center gap-1.5">
 							<div
@@ -105,65 +156,52 @@
 								TOOL
 							</div>
 
-							<div>
+							{#if tool?.meta?.manifest?.version}
+								<div
+									class="text-xs font-black px-1 rounded line-clamp-1 bg-gray-500/20 text-gray-700 dark:text-gray-200"
+								>
+									v{tool?.meta?.manifest?.version ?? ''}
+								</div>
+							{/if}
+
+							<div class="line-clamp-1">
 								{tool.name}
 							</div>
 						</div>
 
 						<div class="flex gap-1.5 px-1">
-							<div class=" text-gray-500 text-xs font-medium">{tool.id}</div>
+							<div class=" text-gray-500 text-xs font-medium flex-shrink-0">{tool.id}</div>
 
 							<div class=" text-xs overflow-hidden text-ellipsis line-clamp-1">
 								{tool.meta.description}
 							</div>
 						</div>
 					</div>
-				</a>
-			</div>
-			<div class="flex flex-row space-x-1 self-center">
-				<Tooltip content="Edit">
-					<a
-						class="self-center w-fit text-sm px-2 py-2 dark:text-gray-300 dark:hover:text-white hover:bg-black/5 dark:hover:bg-white/5 rounded-xl"
-						type="button"
-						href={`/workspace/tools/edit?id=${encodeURIComponent(tool.id)}`}
-					>
-						<svg
-							xmlns="http://www.w3.org/2000/svg"
-							fill="none"
-							viewBox="0 0 24 24"
-							stroke-width="1.5"
-							stroke="currentColor"
-							class="w-4 h-4"
+				</div>
+			</a>
+			<div class="flex flex-row gap-0.5 self-center">
+				{#if tool?.meta?.manifest?.funding_url ?? false}
+					<Tooltip content="Support">
+						<button
+							class="self-center w-fit text-sm px-2 py-2 dark:text-gray-300 dark:hover:text-white hover:bg-black/5 dark:hover:bg-white/5 rounded-xl"
+							type="button"
+							on:click={() => {
+								selectedTool = tool;
+								showManifestModal = true;
+							}}
 						>
-							<path
-								stroke-linecap="round"
-								stroke-linejoin="round"
-								d="M16.862 4.487l1.687-1.688a1.875 1.875 0 112.652 2.652L6.832 19.82a4.5 4.5 0 01-1.897 1.13l-2.685.8.8-2.685a4.5 4.5 0 011.13-1.897L16.863 4.487zm0 0L19.5 7.125"
-							/>
-						</svg>
-					</a>
-				</Tooltip>
+							<Heart />
+						</button>
+					</Tooltip>
+				{/if}
 
-				<Tooltip content="Clone">
+				<Tooltip content="Valves">
 					<button
 						class="self-center w-fit text-sm px-2 py-2 dark:text-gray-300 dark:hover:text-white hover:bg-black/5 dark:hover:bg-white/5 rounded-xl"
 						type="button"
-						on:click={async (e) => {
-							e.stopPropagation();
-
-							const _tool = await getToolById(localStorage.token, tool.id).catch((error) => {
-								toast.error(error);
-								return null;
-							});
-
-							if (_tool) {
-								sessionStorage.tool = JSON.stringify({
-									..._tool,
-									id: `${_tool.id}_clone`,
-									name: `${_tool.name} (Clone)`
-								});
-								goto('/workspace/tools/create');
-							}
+						on:click={() => {
+							selectedTool = tool;
+							showValvesModal = true;
 						}}
 					>
 						<svg
@@ -172,77 +210,49 @@
 							viewBox="0 0 24 24"
 							stroke-width="1.5"
 							stroke="currentColor"
-							class="w-4 h-4"
+							class="size-4"
 						>
 							<path
 								stroke-linecap="round"
 								stroke-linejoin="round"
-								d="M15.75 17.25v3.375c0 .621-.504 1.125-1.125 1.125h-9.75a1.125 1.125 0 0 1-1.125-1.125V7.875c0-.621.504-1.125 1.125-1.125H6.75a9.06 9.06 0 0 1 1.5.124m7.5 10.376h3.375c.621 0 1.125-.504 1.125-1.125V11.25c0-4.46-3.243-8.161-7.5-8.876a9.06 9.06 0 0 0-1.5-.124H9.375c-.621 0-1.125.504-1.125 1.125v3.5m7.5 10.375H9.375a1.125 1.125 0 0 1-1.125-1.125v-9.25m12 6.625v-1.875a3.375 3.375 0 0 0-3.375-3.375h-1.5a1.125 1.125 0 0 1-1.125-1.125v-1.5a3.375 3.375 0 0 0-3.375-3.375H9.75"
+								d="M9.594 3.94c.09-.542.56-.94 1.11-.94h2.593c.55 0 1.02.398 1.11.94l.213 1.281c.063.374.313.686.645.87.074.04.147.083.22.127.325.196.72.257 1.075.124l1.217-.456a1.125 1.125 0 0 1 1.37.49l1.296 2.247a1.125 1.125 0 0 1-.26 1.431l-1.003.827c-.293.241-.438.613-.43.992a7.723 7.723 0 0 1 0 .255c-.008.378.137.75.43.991l1.004.827c.424.35.534.955.26 1.43l-1.298 2.247a1.125 1.125 0 0 1-1.369.491l-1.217-.456c-.355-.133-.75-.072-1.076.124a6.47 6.47 0 0 1-.22.128c-.331.183-.581.495-.644.869l-.213 1.281c-.09.543-.56.94-1.11.94h-2.594c-.55 0-1.019-.398-1.11-.94l-.213-1.281c-.062-.374-.312-.686-.644-.87a6.52 6.52 0 0 1-.22-.127c-.325-.196-.72-.257-1.076-.124l-1.217.456a1.125 1.125 0 0 1-1.369-.49l-1.297-2.247a1.125 1.125 0 0 1 .26-1.431l1.004-.827c.292-.24.437-.613.43-.991a6.932 6.932 0 0 1 0-.255c.007-.38-.138-.751-.43-.992l-1.004-.827a1.125 1.125 0 0 1-.26-1.43l1.297-2.247a1.125 1.125 0 0 1 1.37-.491l1.216.456c.356.133.751.072 1.076-.124.072-.044.146-.086.22-.128.332-.183.582-.495.644-.869l.214-1.28Z"
+							/>
+							<path
+								stroke-linecap="round"
+								stroke-linejoin="round"
+								d="M15 12a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z"
 							/>
 						</svg>
 					</button>
 				</Tooltip>
 
-				<Tooltip content="Export">
-					<button
-						class="self-center w-fit text-sm px-2 py-2 dark:text-gray-300 dark:hover:text-white hover:bg-black/5 dark:hover:bg-white/5 rounded-xl"
-						type="button"
-						on:click={async (e) => {
-							e.stopPropagation();
-
-							const _tool = await getToolById(localStorage.token, tool.id).catch((error) => {
-								toast.error(error);
-								return null;
-							});
-
-							if (_tool) {
-								let blob = new Blob([JSON.stringify([_tool])], {
-									type: 'application/json'
-								});
-								saveAs(blob, `tool-${_tool.id}-export-${Date.now()}.json`);
-							}
-						}}
-					>
-						<ArrowDownTray />
-					</button>
-				</Tooltip>
-
-				<Tooltip content="Delete">
+				<ToolMenu
+					editHandler={() => {
+						goto(`/workspace/tools/edit?id=${encodeURIComponent(tool.id)}`);
+					}}
+					shareHandler={() => {
+						shareHandler(tool);
+					}}
+					cloneHandler={() => {
+						cloneHandler(tool);
+					}}
+					exportHandler={() => {
+						exportHandler(tool);
+					}}
+					deleteHandler={async () => {
+						deleteHandler(tool);
+					}}
+					onClose={() => {}}
+				>
 					<button
-						class="self-center w-fit text-sm px-2 py-2 dark:text-gray-300 dark:hover:text-white hover:bg-black/5 dark:hover:bg-white/5 rounded-xl"
+						class="self-center w-fit text-sm p-1.5 dark:text-gray-300 dark:hover:text-white hover:bg-black/5 dark:hover:bg-white/5 rounded-xl"
 						type="button"
-						on:click={async (e) => {
-							e.stopPropagation();
-
-							const res = await deleteToolById(localStorage.token, tool.id).catch((error) => {
-								toast.error(error);
-								return null;
-							});
-
-							if (res) {
-								toast.success('Tool deleted successfully');
-								tools.set(await getTools(localStorage.token));
-							}
-						}}
 					>
-						<svg
-							xmlns="http://www.w3.org/2000/svg"
-							fill="none"
-							viewBox="0 0 24 24"
-							stroke-width="1.5"
-							stroke="currentColor"
-							class="w-4 h-4"
-						>
-							<path
-								stroke-linecap="round"
-								stroke-linejoin="round"
-								d="M14.74 9l-.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 01-2.244 2.077H8.084a2.25 2.25 0 01-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 00-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 013.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 00-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 00-7.5 0"
-							/>
-						</svg>
+						<EllipsisHorizontal className="size-5" />
 					</button>
-				</Tooltip>
+				</ToolMenu>
 			</div>
-		</button>
+		</div>
 	{/each}
 </div>
 
@@ -273,7 +283,7 @@
 				toolsImportInputElement.click();
 			}}
 		>
-			<div class=" self-center mr-2 font-medium">{$i18n.t('Import Tools')}</div>
+			<div class=" self-center mr-2 font-medium line-clamp-1">{$i18n.t('Import Tools')}</div>
 
 			<div class=" self-center">
 				<svg
@@ -307,7 +317,7 @@
 				}
 			}}
 		>
-			<div class=" self-center mr-2 font-medium">{$i18n.t('Export Tools')}</div>
+			<div class=" self-center mr-2 font-medium line-clamp-1">{$i18n.t('Export Tools')}</div>
 
 			<div class=" self-center">
 				<svg
@@ -327,6 +337,42 @@
 	</div>
 </div>
 
+<div class=" my-16">
+	<div class=" text-lg font-semibold mb-3 line-clamp-1">
+		{$i18n.t('Made by OpenWebUI Community')}
+	</div>
+
+	<a
+		class=" flex space-x-4 cursor-pointer w-full mb-2 px-3 py-2"
+		href="https://openwebui.com/"
+		target="_blank"
+	>
+		<div class=" self-center w-10 flex-shrink-0">
+			<div
+				class="w-full h-10 flex justify-center rounded-full bg-transparent dark:bg-gray-700 border border-dashed border-gray-200"
+			>
+				<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="w-6">
+					<path
+						fill-rule="evenodd"
+						d="M12 3.75a.75.75 0 01.75.75v6.75h6.75a.75.75 0 010 1.5h-6.75v6.75a.75.75 0 01-1.5 0v-6.75H4.5a.75.75 0 010-1.5h6.75V4.5a.75.75 0 01.75-.75z"
+						clip-rule="evenodd"
+					/>
+				</svg>
+			</div>
+		</div>
+
+		<div class=" self-center">
+			<div class=" font-bold line-clamp-1">{$i18n.t('Discover a tool')}</div>
+			<div class=" text-sm line-clamp-1">
+				{$i18n.t('Discover, download, and explore custom tools')}
+			</div>
+		</div>
+	</a>
+</div>
+
+<ValvesModal bind:show={showValvesModal} type="tool" id={selectedTool?.id ?? null} />
+<ManifestModal bind:show={showManifestModal} manifest={selectedTool?.meta?.manifest ?? {}} />
+
 <ConfirmDialog
 	bind:show={showConfirm}
 	on:confirm={() => {

+ 117 - 0
src/lib/components/workspace/Tools/ToolMenu.svelte

@@ -0,0 +1,117 @@
+<script lang="ts">
+	import { DropdownMenu } from 'bits-ui';
+	import { flyAndScale } from '$lib/utils/transitions';
+	import { getContext } from 'svelte';
+
+	import Dropdown from '$lib/components/common/Dropdown.svelte';
+	import GarbageBin from '$lib/components/icons/GarbageBin.svelte';
+	import Pencil from '$lib/components/icons/Pencil.svelte';
+	import Tooltip from '$lib/components/common/Tooltip.svelte';
+	import Tags from '$lib/components/chat/Tags.svelte';
+	import Share from '$lib/components/icons/Share.svelte';
+	import ArchiveBox from '$lib/components/icons/ArchiveBox.svelte';
+	import DocumentDuplicate from '$lib/components/icons/DocumentDuplicate.svelte';
+	import ArrowDownTray from '$lib/components/icons/ArrowDownTray.svelte';
+
+	const i18n = getContext('i18n');
+
+	export let editHandler: Function;
+	export let shareHandler: Function;
+	export let cloneHandler: Function;
+	export let exportHandler: Function;
+	export let deleteHandler: Function;
+	export let onClose: Function;
+
+	let show = false;
+</script>
+
+<Dropdown
+	bind:show
+	on:change={(e) => {
+		if (e.detail === false) {
+			onClose();
+		}
+	}}
+>
+	<Tooltip content={$i18n.t('More')}>
+		<slot />
+	</Tooltip>
+
+	<div slot="content">
+		<DropdownMenu.Content
+			class="w-full max-w-[160px] rounded-xl px-1 py-1.5 border border-gray-300/30 dark:border-gray-700/50 z-50 bg-white dark:bg-gray-850 dark:text-white shadow"
+			sideOffset={-2}
+			side="bottom"
+			align="start"
+			transition={flyAndScale}
+		>
+			<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-md"
+				on:click={() => {
+					editHandler();
+				}}
+			>
+				<svg
+					xmlns="http://www.w3.org/2000/svg"
+					fill="none"
+					viewBox="0 0 24 24"
+					stroke-width="1.5"
+					stroke="currentColor"
+					class="w-4 h-4"
+				>
+					<path
+						stroke-linecap="round"
+						stroke-linejoin="round"
+						d="M16.862 4.487l1.687-1.688a1.875 1.875 0 112.652 2.652L6.832 19.82a4.5 4.5 0 01-1.897 1.13l-2.685.8.8-2.685a4.5 4.5 0 011.13-1.897L16.863 4.487zm0 0L19.5 7.125"
+					/>
+				</svg>
+
+				<div class="flex items-center">{$i18n.t('Edit')}</div>
+			</DropdownMenu.Item>
+
+			<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-md"
+				on:click={() => {
+					shareHandler();
+				}}
+			>
+				<Share />
+				<div class="flex items-center">{$i18n.t('Share')}</div>
+			</DropdownMenu.Item>
+
+			<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-md"
+				on:click={() => {
+					cloneHandler();
+				}}
+			>
+				<DocumentDuplicate />
+
+				<div class="flex items-center">{$i18n.t('Clone')}</div>
+			</DropdownMenu.Item>
+
+			<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-md"
+				on:click={() => {
+					exportHandler();
+				}}
+			>
+				<ArrowDownTray />
+
+				<div class="flex items-center">{$i18n.t('Export')}</div>
+			</DropdownMenu.Item>
+
+			<hr class="border-gray-100 dark:border-gray-800 my-1" />
+
+			<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-md"
+				on:click={() => {
+					deleteHandler();
+				}}
+			>
+				<GarbageBin strokeWidth="2" />
+				<div class="flex items-center">{$i18n.t('Delete')}</div>
+			</DropdownMenu.Item>
+		</DropdownMenu.Content>
+	</div>
+</Dropdown>

+ 102 - 0
src/lib/components/workspace/common/ManifestModal.svelte

@@ -0,0 +1,102 @@
+<script lang="ts">
+	import { toast } from 'svelte-sonner';
+	import { createEventDispatcher } from 'svelte';
+	import { onMount, getContext } from 'svelte';
+
+	import Modal from '../../common/Modal.svelte';
+
+	const i18n = getContext('i18n');
+	const dispatch = createEventDispatcher();
+
+	export let show = false;
+	export let manifest = {};
+</script>
+
+<Modal size="sm" bind:show>
+	<div>
+		<div class=" flex justify-between dark:text-gray-300 px-5 pt-4 pb-2">
+			<div class=" text-lg font-medium self-center">{$i18n.t('Show your support!')}</div>
+			<button
+				class="self-center"
+				on:click={() => {
+					show = false;
+				}}
+			>
+				<svg
+					xmlns="http://www.w3.org/2000/svg"
+					viewBox="0 0 20 20"
+					fill="currentColor"
+					class="w-5 h-5"
+				>
+					<path
+						d="M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z"
+					/>
+				</svg>
+			</button>
+		</div>
+
+		<div class="flex flex-col md:flex-row w-full px-5 pb-4 md:space-x-4 dark:text-gray-200">
+			<div class=" flex flex-col w-full sm:flex-row sm:justify-center sm:space-x-6">
+				<form
+					class="flex flex-col w-full"
+					on:submit|preventDefault={() => {
+						show = false;
+					}}
+				>
+					<div class="px-1 text-sm">
+						<div class=" my-2">
+							The developers behind this plugin are passionate volunteers from the community. If you
+							find this plugin helpful, please consider contributing to its development.
+						</div>
+
+						<div class=" my-2">
+							Your entire contribution will go directly to the plugin developer; Open WebUI does not
+							take any percentage. However, the chosen funding platform might have its own fees.
+						</div>
+
+						<hr class=" dark:border-gray-800 my-3" />
+
+						<div class="my-2">
+							Support this plugin: <a
+								href={manifest.funding_url}
+								target="_blank"
+								class=" underline text-blue-400 hover:text-blue-300">{manifest.funding_url}</a
+							>
+						</div>
+					</div>
+
+					<div class="flex justify-end pt-3 text-sm font-medium">
+						<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"
+							type="submit"
+						>
+							{$i18n.t('Done')}
+						</button>
+					</div>
+				</form>
+			</div>
+		</div>
+	</div>
+</Modal>
+
+<style>
+	input::-webkit-outer-spin-button,
+	input::-webkit-inner-spin-button {
+		/* display: none; <- Crashes Chrome on hover */
+		-webkit-appearance: none;
+		margin: 0; /* <-- Apparently some margin are still there even though it's hidden */
+	}
+
+	.tabs::-webkit-scrollbar {
+		display: none; /* for Chrome, Safari and Opera */
+	}
+
+	.tabs {
+		-ms-overflow-style: none; /* IE and Edge */
+		scrollbar-width: none; /* Firefox */
+	}
+
+	input[type='number'] {
+		-moz-appearance: textfield; /* Firefox */
+	}
+</style>

+ 256 - 0
src/lib/components/workspace/common/ValvesModal.svelte

@@ -0,0 +1,256 @@
+<script lang="ts">
+	import { toast } from 'svelte-sonner';
+	import { createEventDispatcher } from 'svelte';
+	import { onMount, getContext } from 'svelte';
+	import { addUser } from '$lib/apis/auths';
+
+	import Modal from '../../common/Modal.svelte';
+	import {
+		getFunctionValvesById,
+		getFunctionValvesSpecById,
+		updateFunctionValvesById
+	} from '$lib/apis/functions';
+	import { getToolValvesById, getToolValvesSpecById, updateToolValvesById } from '$lib/apis/tools';
+	import Spinner from '../../common/Spinner.svelte';
+
+	const i18n = getContext('i18n');
+	const dispatch = createEventDispatcher();
+
+	export let show = false;
+
+	export let type = 'tool';
+	export let id = null;
+
+	let saving = false;
+	let loading = false;
+
+	let valvesSpec = null;
+	let valves = {};
+
+	const submitHandler = async () => {
+		saving = true;
+
+		if (valvesSpec) {
+			// Convert string to array
+			for (const property in valvesSpec.properties) {
+				if (valvesSpec.properties[property]?.type === 'array') {
+					valves[property] = (valves[property] ?? '').split(',').map((v) => v.trim());
+				}
+			}
+
+			let res = null;
+
+			if (type === 'tool') {
+				res = await updateToolValvesById(localStorage.token, id, valves).catch((error) => {
+					toast.error(error);
+				});
+			} else if (type === 'function') {
+				res = await updateFunctionValvesById(localStorage.token, id, valves).catch((error) => {
+					toast.error(error);
+				});
+			}
+
+			if (res) {
+				toast.success('Valves updated successfully');
+			}
+		}
+
+		saving = false;
+	};
+
+	const initHandler = async () => {
+		loading = true;
+		valves = {};
+		valvesSpec = null;
+
+		if (type === 'tool') {
+			valves = await getToolValvesById(localStorage.token, id);
+			valvesSpec = await getToolValvesSpecById(localStorage.token, id);
+		} else if (type === 'function') {
+			valves = await getFunctionValvesById(localStorage.token, id);
+			valvesSpec = await getFunctionValvesSpecById(localStorage.token, id);
+		}
+
+		if (!valves) {
+			valves = {};
+		}
+
+		if (valvesSpec) {
+			// Convert array to string
+			for (const property in valvesSpec.properties) {
+				if (valvesSpec.properties[property]?.type === 'array') {
+					valves[property] = (valves[property] ?? []).join(',');
+				}
+			}
+		}
+
+		loading = false;
+	};
+
+	$: if (show) {
+		initHandler();
+	}
+</script>
+
+<Modal size="sm" bind:show>
+	<div>
+		<div class=" flex justify-between dark:text-gray-300 px-5 pt-4 pb-2">
+			<div class=" text-lg font-medium self-center">{$i18n.t('Valves')}</div>
+			<button
+				class="self-center"
+				on:click={() => {
+					show = false;
+				}}
+			>
+				<svg
+					xmlns="http://www.w3.org/2000/svg"
+					viewBox="0 0 20 20"
+					fill="currentColor"
+					class="w-5 h-5"
+				>
+					<path
+						d="M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z"
+					/>
+				</svg>
+			</button>
+		</div>
+
+		<div class="flex flex-col md:flex-row w-full px-5 pb-4 md:space-x-4 dark:text-gray-200">
+			<div class=" flex flex-col w-full sm:flex-row sm:justify-center sm:space-x-6">
+				<form
+					class="flex flex-col w-full"
+					on:submit|preventDefault={() => {
+						submitHandler();
+					}}
+				>
+					<div class="px-1">
+						{#if !loading}
+							{#if valvesSpec}
+								{#each Object.keys(valvesSpec.properties) as property, idx}
+									<div class=" py-0.5 w-full justify-between">
+										<div class="flex w-full justify-between">
+											<div class=" self-center text-xs font-medium">
+												{valvesSpec.properties[property].title}
+
+												{#if (valvesSpec?.required ?? []).includes(property)}
+													<span class=" text-gray-500">*required</span>
+												{/if}
+											</div>
+
+											<button
+												class="p-1 px-3 text-xs flex rounded transition"
+												type="button"
+												on:click={() => {
+													valves[property] = (valves[property] ?? null) === null ? '' : null;
+												}}
+											>
+												{#if (valves[property] ?? null) === null}
+													<span class="ml-2 self-center">
+														{#if (valvesSpec?.required ?? []).includes(property)}
+															{$i18n.t('None')}
+														{:else}
+															{$i18n.t('Default')}
+														{/if}
+													</span>
+												{:else}
+													<span class="ml-2 self-center"> {$i18n.t('Custom')} </span>
+												{/if}
+											</button>
+										</div>
+
+										{#if (valves[property] ?? null) !== null}
+											<div class="flex mt-0.5 mb-1.5 space-x-2">
+												<div class=" flex-1">
+													<input
+														class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
+														type="text"
+														placeholder={valvesSpec.properties[property].title}
+														bind:value={valves[property]}
+														autocomplete="off"
+														required
+													/>
+												</div>
+											</div>
+										{/if}
+
+										{#if (valvesSpec.properties[property]?.description ?? null) !== null}
+											<div class="text-xs text-gray-500">
+												{valvesSpec.properties[property].description}
+											</div>
+										{/if}
+									</div>
+								{/each}
+							{:else}
+								<div class="text-sm">No valves</div>
+							{/if}
+						{:else}
+							<Spinner className="size-5" />
+						{/if}
+					</div>
+
+					<div class="flex justify-end pt-3 text-sm font-medium">
+						<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
+								? ' cursor-not-allowed'
+								: ''}"
+							type="submit"
+							disabled={saving}
+						>
+							{$i18n.t('Save')}
+
+							{#if saving}
+								<div class="ml-2 self-center">
+									<svg
+										class=" w-4 h-4"
+										viewBox="0 0 24 24"
+										fill="currentColor"
+										xmlns="http://www.w3.org/2000/svg"
+										><style>
+											.spinner_ajPY {
+												transform-origin: center;
+												animation: spinner_AtaB 0.75s infinite linear;
+											}
+											@keyframes spinner_AtaB {
+												100% {
+													transform: rotate(360deg);
+												}
+											}
+										</style><path
+											d="M12,1A11,11,0,1,0,23,12,11,11,0,0,0,12,1Zm0,19a8,8,0,1,1,8-8A8,8,0,0,1,12,20Z"
+											opacity=".25"
+										/><path
+											d="M10.14,1.16a11,11,0,0,0-9,8.92A1.59,1.59,0,0,0,2.46,12,1.52,1.52,0,0,0,4.11,10.7a8,8,0,0,1,6.66-6.61A1.42,1.42,0,0,0,12,2.69h0A1.57,1.57,0,0,0,10.14,1.16Z"
+											class="spinner_ajPY"
+										/></svg
+									>
+								</div>
+							{/if}
+						</button>
+					</div>
+				</form>
+			</div>
+		</div>
+	</div>
+</Modal>
+
+<style>
+	input::-webkit-outer-spin-button,
+	input::-webkit-inner-spin-button {
+		/* display: none; <- Crashes Chrome on hover */
+		-webkit-appearance: none;
+		margin: 0; /* <-- Apparently some margin are still there even though it's hidden */
+	}
+
+	.tabs::-webkit-scrollbar {
+		display: none; /* for Chrome, Safari and Opera */
+	}
+
+	.tabs {
+		-ms-overflow-style: none; /* IE and Edge */
+		scrollbar-width: none; /* Firefox */
+	}
+
+	input[type='number'] {
+		-moz-appearance: textfield; /* Firefox */
+	}
+</style>

+ 11 - 0
src/lib/i18n/locales/ar-BH/translation.json

@@ -43,6 +43,7 @@
 	"Allow Chat Deletion": "يستطيع حذف المحادثات",
 	"Allow non-local voices": "",
 	"Allow User Location": "",
+	"Allow Voice Interruption in Call": "",
 	"alphanumeric characters and hyphens": "الأحرف الأبجدية الرقمية والواصلات",
 	"Already have an account?": "هل تملك حساب ؟",
 	"an assistant": "مساعد",
@@ -165,9 +166,13 @@
 	"Deleted {{name}}": "حذف {{name}}",
 	"Description": "وصف",
 	"Didn't fully follow instructions": "لم أتبع التعليمات بشكل كامل",
+	"Discover a function": "",
 	"Discover a model": "اكتشف نموذجا",
 	"Discover a prompt": "اكتشاف موجه",
+	"Discover a tool": "",
+	"Discover, download, and explore custom functions": "",
 	"Discover, download, and explore custom prompts": "اكتشاف وتنزيل واستكشاف المطالبات المخصصة",
+	"Discover, download, and explore custom tools": "",
 	"Discover, download, and explore model presets": "اكتشاف وتنزيل واستكشاف الإعدادات المسبقة للنموذج",
 	"Dismissible": "",
 	"Display Emoji in Call": "",
@@ -180,6 +185,7 @@
 	"Don't Allow": "لا تسمح بذلك",
 	"Don't have an account?": "ليس لديك حساب؟",
 	"Don't like the style": "لا أحب النمط",
+	"Done": "",
 	"Download": "تحميل",
 	"Download canceled": "تم اللغاء التحميل",
 	"Download Database": "تحميل قاعدة البيانات",
@@ -312,6 +318,7 @@
 	"Manage Models": "إدارة النماذج",
 	"Manage Ollama Models": "Ollama إدارة موديلات ",
 	"Manage Pipelines": "إدارة خطوط الأنابيب",
+	"Manage Valves": "",
 	"March": "مارس",
 	"Max Tokens (num_predict)": "ماكس توكنز (num_predict)",
 	"Maximum of 3 models can be downloaded simultaneously. Please try again later.": "يمكن تنزيل 3 نماذج كحد أقصى في وقت واحد. الرجاء معاودة المحاولة في وقت لاحق.",
@@ -463,10 +470,12 @@
 	"Seed": "Seed",
 	"Select a base model": "حدد نموذجا أساسيا",
 	"Select a engine": "",
+	"Select a function": "",
 	"Select a mode": "أختار موديل",
 	"Select a model": "أختار الموديل",
 	"Select a pipeline": "حدد مسارا",
 	"Select a pipeline url": "حدد عنوان URL لخط الأنابيب",
+	"Select a tool": "",
 	"Select an Ollama instance": "أختار سيرفر ",
 	"Select Documents": "",
 	"Select model": " أختار موديل",
@@ -499,6 +508,7 @@
 	"Show Admin Details in Account Pending Overlay": "",
 	"Show Model": "",
 	"Show shortcuts": "إظهار الاختصارات",
+	"Show your support!": "",
 	"Showcased creativity": "أظهر الإبداع",
 	"sidebar": "الشريط الجانبي",
 	"Sign in": "تسجيل الدخول",
@@ -587,6 +597,7 @@
 	"Users": "المستخدمين",
 	"Utilize": "يستخدم",
 	"Valid time units:": "وحدات زمنية صالحة:",
+	"Valves": "",
 	"variable": "المتغير",
 	"variable to have them replaced with clipboard content.": "متغير لاستبدالها بمحتوى الحافظة.",
 	"Version": "إصدار",

+ 11 - 0
src/lib/i18n/locales/bg-BG/translation.json

@@ -43,6 +43,7 @@
 	"Allow Chat Deletion": "Позволи Изтриване на Чат",
 	"Allow non-local voices": "",
 	"Allow User Location": "",
+	"Allow Voice Interruption in Call": "",
 	"alphanumeric characters and hyphens": "алфанумерични знаци и тире",
 	"Already have an account?": "Вече имате акаунт? ",
 	"an assistant": "асистент",
@@ -165,9 +166,13 @@
 	"Deleted {{name}}": "Изтрито {{име}}",
 	"Description": "Описание",
 	"Didn't fully follow instructions": "Не следва инструкциите",
+	"Discover a function": "",
 	"Discover a model": "Открийте модел",
 	"Discover a prompt": "Откриване на промпт",
+	"Discover a tool": "",
+	"Discover, download, and explore custom functions": "",
 	"Discover, download, and explore custom prompts": "Откриване, сваляне и преглед на персонализирани промптове",
+	"Discover, download, and explore custom tools": "",
 	"Discover, download, and explore model presets": "Откриване, сваляне и преглед на пресетове на модели",
 	"Dismissible": "",
 	"Display Emoji in Call": "",
@@ -180,6 +185,7 @@
 	"Don't Allow": "Не Позволявай",
 	"Don't have an account?": "Нямате акаунт?",
 	"Don't like the style": "Не харесваш стила?",
+	"Done": "",
 	"Download": "Изтегляне отменено",
 	"Download canceled": "Изтегляне отменено",
 	"Download Database": "Сваляне на база данни",
@@ -312,6 +318,7 @@
 	"Manage Models": "Управление на Моделите",
 	"Manage Ollama Models": "Управление на Ollama Моделите",
 	"Manage Pipelines": "Управление на тръбопроводи",
+	"Manage Valves": "",
 	"March": "Март",
 	"Max Tokens (num_predict)": "Макс токени (num_predict)",
 	"Maximum of 3 models can be downloaded simultaneously. Please try again later.": "Максимум 3 модели могат да бъдат сваляни едновременно. Моля, опитайте отново по-късно.",
@@ -459,10 +466,12 @@
 	"Seed": "Seed",
 	"Select a base model": "Изберете базов модел",
 	"Select a engine": "",
+	"Select a function": "",
 	"Select a mode": "Изберете режим",
 	"Select a model": "Изберете модел",
 	"Select a pipeline": "Изберете тръбопровод",
 	"Select a pipeline url": "Избор на URL адрес на канал",
+	"Select a tool": "",
 	"Select an Ollama instance": "Изберете Ollama инстанция",
 	"Select Documents": "",
 	"Select model": "Изберете модел",
@@ -495,6 +504,7 @@
 	"Show Admin Details in Account Pending Overlay": "",
 	"Show Model": "",
 	"Show shortcuts": "Покажи",
+	"Show your support!": "",
 	"Showcased creativity": "Показана креативност",
 	"sidebar": "sidebar",
 	"Sign in": "Вписване",
@@ -583,6 +593,7 @@
 	"Users": "Потребители",
 	"Utilize": "Използване",
 	"Valid time units:": "Валидни единици за време:",
+	"Valves": "",
 	"variable": "променлива",
 	"variable to have them replaced with clipboard content.": "променливи да се заменят съдържанието от клипборд.",
 	"Version": "Версия",

+ 11 - 0
src/lib/i18n/locales/bn-BD/translation.json

@@ -43,6 +43,7 @@
 	"Allow Chat Deletion": "চ্যাট ডিলিট করতে দিন",
 	"Allow non-local voices": "",
 	"Allow User Location": "",
+	"Allow Voice Interruption in Call": "",
 	"alphanumeric characters and hyphens": "ইংরেজি অক্ষর, সংখ্যা এবং হাইফেন",
 	"Already have an account?": "আগে থেকেই একাউন্ট আছে?",
 	"an assistant": "একটা এসিস্ট্যান্ট",
@@ -165,9 +166,13 @@
 	"Deleted {{name}}": "{{name}} মোছা হয়েছে",
 	"Description": "বিবরণ",
 	"Didn't fully follow instructions": "ইনস্ট্রাকশন সম্পূর্ণ অনুসরণ করা হয়নি",
+	"Discover a function": "",
 	"Discover a model": "একটি মডেল আবিষ্কার করুন",
 	"Discover a prompt": "একটি প্রম্পট খুঁজে বের করুন",
+	"Discover a tool": "",
+	"Discover, download, and explore custom functions": "",
 	"Discover, download, and explore custom prompts": "কাস্টম প্রম্পটগুলো আবিস্কার, ডাউনলোড এবং এক্সপ্লোর করুন",
+	"Discover, download, and explore custom tools": "",
 	"Discover, download, and explore model presets": "মডেল প্রিসেটগুলো আবিস্কার, ডাউনলোড এবং এক্সপ্লোর করুন",
 	"Dismissible": "",
 	"Display Emoji in Call": "",
@@ -180,6 +185,7 @@
 	"Don't Allow": "অনুমোদন দেবেন না",
 	"Don't have an account?": "একাউন্ট নেই?",
 	"Don't like the style": "স্টাইল পছন্দ করেন না",
+	"Done": "",
 	"Download": "ডাউনলোড",
 	"Download canceled": "ডাউনলোড বাতিল করা হয়েছে",
 	"Download Database": "ডেটাবেজ ডাউনলোড করুন",
@@ -312,6 +318,7 @@
 	"Manage Models": "মডেলসমূহ ব্যবস্থাপনা করুন",
 	"Manage Ollama Models": "Ollama মডেলসূহ ব্যবস্থাপনা করুন",
 	"Manage Pipelines": "পাইপলাইন পরিচালনা করুন",
+	"Manage Valves": "",
 	"March": "মার্চ",
 	"Max Tokens (num_predict)": "সর্বোচ্চ টোকেন (num_predict)",
 	"Maximum of 3 models can be downloaded simultaneously. Please try again later.": "একসঙ্গে সর্বোচ্চ তিনটি মডেল ডাউনলোড করা যায়। দয়া করে পরে আবার চেষ্টা করুন।",
@@ -459,10 +466,12 @@
 	"Seed": "সীড",
 	"Select a base model": "একটি বেস মডেল নির্বাচন করুন",
 	"Select a engine": "",
+	"Select a function": "",
 	"Select a mode": "একটি মডেল নির্বাচন করুন",
 	"Select a model": "একটি মডেল নির্বাচন করুন",
 	"Select a pipeline": "একটি পাইপলাইন নির্বাচন করুন",
 	"Select a pipeline url": "একটি পাইপলাইন URL নির্বাচন করুন",
+	"Select a tool": "",
 	"Select an Ollama instance": "একটি Ollama ইন্সট্যান্স নির্বাচন করুন",
 	"Select Documents": "",
 	"Select model": "মডেল নির্বাচন করুন",
@@ -495,6 +504,7 @@
 	"Show Admin Details in Account Pending Overlay": "",
 	"Show Model": "",
 	"Show shortcuts": "শর্টকাটগুলো দেখান",
+	"Show your support!": "",
 	"Showcased creativity": "সৃজনশীলতা প্রদর্শন",
 	"sidebar": "সাইডবার",
 	"Sign in": "সাইন ইন",
@@ -583,6 +593,7 @@
 	"Users": "ব্যাবহারকারীগণ",
 	"Utilize": "ইউটিলাইজ",
 	"Valid time units:": "সময়ের গ্রহণযোগ্য এককসমূহ:",
+	"Valves": "",
 	"variable": "ভেরিয়েবল",
 	"variable to have them replaced with clipboard content.": "ক্লিপবোর্ডের কন্টেন্ট দিয়ে যেই ভেরিয়েবল রিপ্লেস করা যাবে।",
 	"Version": "ভার্সন",

+ 403 - 392
src/lib/i18n/locales/ca-ES/translation.json

@@ -1,620 +1,631 @@
 {
-	"'s', 'm', 'h', 'd', 'w' or '-1' for no expiration.": "'s', 'm', 'h', 'd', 'w' or '-1' per no caduca mai.",
+	"'s', 'm', 'h', 'd', 'w' or '-1' for no expiration.": "'s', 'm', 'h', 'd', 'w' o '-1' perquè no caduqui mai.",
 	"(Beta)": "(Beta)",
 	"(e.g. `sh webui.sh --api`)": "(p. ex. `sh webui.sh --api`)",
 	"(latest)": "(últim)",
 	"{{ models }}": "{{ models }}",
-	"{{ owner }}: You cannot delete a base model": "{{ propietari }}: No es pot suprimir un model base",
+	"{{ owner }}: You cannot delete a base model": "{{ owner }}: No es pot eliminar un model base",
 	"{{modelName}} is thinking...": "{{modelName}} està pensant...",
-	"{{user}}'s Chats": "{{user}}'s Chats",
-	"{{webUIName}} Backend Required": "Es requereix Backend de {{webUIName}}",
-	"A task model is used when performing tasks such as generating titles for chats and web search queries": "Un model de tasca s'utilitza quan es realitzen tasques com ara generar títols per a xats i consultes de cerca web",
+	"{{user}}'s Chats": "Els xats de {{user}}",
+	"{{webUIName}} Backend Required": "El Backend de {{webUIName}} és necessari",
+	"A task model is used when performing tasks such as generating titles for chats and web search queries": "Un model de tasca s'utilitza quan es realitzen tasques com ara generar títols per a xats i consultes de cerca per a la web",
 	"a user": "un usuari",
 	"About": "Sobre",
 	"Account": "Compte",
-	"Account Activation Pending": "",
+	"Account Activation Pending": "Activació del compte pendent",
 	"Accurate information": "Informació precisa",
-	"Active Users": "",
+	"Active Users": "Usuaris actius",
 	"Add": "Afegir",
-	"Add a model id": "Afegir un identificador de model",
-	"Add a short description about what this model does": "Afegiu una breu descripció sobre què fa aquest model",
-	"Add a short title for this prompt": "Afegeix un títol curt per aquest prompt",
+	"Add a model id": "Afegeix un identificador de model",
+	"Add a short description about what this model does": "Afegeix una breu descripció sobre què fa aquest model",
+	"Add a short title for this prompt": "Afegeix un títol curt per a aquesta indicació",
 	"Add a tag": "Afegeix una etiqueta",
-	"Add custom prompt": "Afegir un prompt personalitzat",
-	"Add Docs": "Afegeix Documents",
-	"Add Files": "Afegeix Arxius",
-	"Add Memory": "Afegir Memòria",
-	"Add message": "Afegeix missatge",
-	"Add Model": "Afegir Model",
-	"Add Tags": "afegeix etiquetes",
-	"Add User": "Afegir Usuari",
-	"Adjusting these settings will apply changes universally to all users.": "Ajustar aquests paràmetres aplicarà canvis de manera universal a tots els usuaris.",
+	"Add custom prompt": "Afegir una indicació personalitzada",
+	"Add Docs": "Afegir documents",
+	"Add Files": "Afegir arxius",
+	"Add Memory": "Afegir memòria",
+	"Add message": "Afegir un missatge",
+	"Add Model": "Afegir un model",
+	"Add Tags": "Afegir etiquetes",
+	"Add User": "Afegir un usuari",
+	"Adjusting these settings will apply changes universally to all users.": "Si ajustes aquesta configuració, els canvis s'aplicaran de manera universal a tots els usuaris.",
 	"admin": "administrador",
-	"Admin": "",
-	"Admin Panel": "Panell d'Administració",
-	"Admin Settings": "Configuració d'Administració",
-	"Admins have access to all tools at all times; users need tools assigned per model in the workspace.": "",
-	"Advanced Parameters": "Paràmetres Avançats",
+	"Admin": "Administrador",
+	"Admin Panel": "Panell d'administració",
+	"Admin Settings": "Configuració d'administració",
+	"Admins have access to all tools at all times; users need tools assigned per model in the workspace.": "Els administradors tenen accés a totes les eines en tot moment; els usuaris necessiten eines assignades per model a l'espai de treball.",
+	"Advanced Parameters": "Paràmetres avançats",
 	"Advanced Params": "Paràmetres avançats",
 	"all": "tots",
-	"All Documents": "Tots els Documents",
-	"All Users": "Tots els Usuaris",
-	"Allow": "Permet",
-	"Allow Chat Deletion": "Permet la Supressió del Xat",
-	"Allow non-local voices": "",
-	"Allow User Location": "",
+	"All Documents": "Tots els documents",
+	"All Users": "Tots els usuaris",
+	"Allow": "Permetre",
+	"Allow Chat Deletion": "Permetre la supressió del xat",
+	"Allow non-local voices": "Permetre veus no locals",
+	"Allow User Location": "Permetre la ubicació de l'usuari",
+	"Allow Voice Interruption in Call": "",
 	"alphanumeric characters and hyphens": "caràcters alfanumèrics i guions",
 	"Already have an account?": "Ja tens un compte?",
 	"an assistant": "un assistent",
 	"and": "i",
 	"and create a new shared link.": "i crear un nou enllaç compartit.",
 	"API Base URL": "URL Base de l'API",
-	"API Key": "Clau de l'API",
-	"API Key created.": "Clau de l'API creada.",
+	"API Key": "clau API",
+	"API Key created.": "clau API creada.",
 	"API keys": "Claus de l'API",
 	"April": "Abril",
 	"Archive": "Arxiu",
 	"Archive All Chats": "Arxiva tots els xats",
-	"Archived Chats": "Arxiu d'historial de xat",
+	"Archived Chats": "Xats arxivats",
 	"are allowed - Activate this command by typing": "estan permesos - Activa aquesta comanda escrivint",
 	"Are you sure?": "Estàs segur?",
 	"Attach file": "Adjuntar arxiu",
-	"Attention to detail": "Detall atent",
+	"Attention to detail": "Atenció al detall",
 	"Audio": "Àudio",
 	"August": "Agost",
-	"Auto-playback response": "Resposta de reproducció automàtica",
-	"AUTOMATIC1111 Base URL": "URL Base AUTOMATIC1111",
-	"AUTOMATIC1111 Base URL is required.": "Es requereix l'URL Base AUTOMATIC1111.",
+	"Auto-playback response": "Reproduir la resposta automàticament",
+	"AUTOMATIC1111 Base URL": "URL Base d'AUTOMATIC1111",
+	"AUTOMATIC1111 Base URL is required.": "Es requereix l'URL Base d'AUTOMATIC1111.",
 	"available!": "disponible!",
 	"Back": "Enrere",
-	"Bad Response": "Resposta Erroni",
+	"Bad Response": "Resposta errònia",
 	"Banners": "Banners",
 	"Base Model (From)": "Model base (des de)",
-	"Batch Size (num_batch)": "",
+	"Batch Size (num_batch)": "Mida del lot (num_batch)",
 	"before": "abans",
-	"Being lazy": "Ser l'estupidez",
-	"Brave Search API Key": "Clau API Brave Search",
-	"Bypass SSL verification for Websites": "Desactivar la verificació SSL per a l'accés a l'Internet",
-	"Call": "",
-	"Call feature is not supported when using Web STT engine": "",
-	"Camera": "",
-	"Cancel": "Cancel·la",
+	"Being lazy": "Essent mandrós",
+	"Brave Search API Key": "Clau API de Brave Search",
+	"Bypass SSL verification for Websites": "Desactivar la verificació SSL per a l'accés a Internet",
+	"Call": "Trucada",
+	"Call feature is not supported when using Web STT engine": "La funció de trucada no s'admet quan s'utilitza el motor Web STT",
+	"Camera": "Càmera",
+	"Cancel": "Cancel·lar",
 	"Capabilities": "Capacitats",
-	"Change Password": "Canvia la Contrasenya",
+	"Change Password": "Canviar la contrasenya",
 	"Chat": "Xat",
-	"Chat Background Image": "",
+	"Chat Background Image": "Imatge de fons del xat",
 	"Chat Bubble UI": "Chat Bubble UI",
-	"Chat direction": "Direcció del Xat",
-	"Chat History": "Històric del Xat",
-	"Chat History is off for this browser.": "L'historial de xat està desactivat per a aquest navegador.",
+	"Chat direction": "Direcció del xat",
+	"Chat History": "Històric del xat",
+	"Chat History is off for this browser.": "L'historic del xat està desactivat per a aquest navegador.",
 	"Chats": "Xats",
-	"Check Again": "Comprova-ho de Nou",
-	"Check for updates": "Comprova si hi ha actualitzacions",
+	"Check Again": "Comprovar-ho de nou",
+	"Check for updates": "Comprovar si hi ha actualitzacions",
 	"Checking for updates...": "Comprovant actualitzacions...",
-	"Choose a model before saving...": "Tria un model abans de guardar...",
-	"Chunk Overlap": "Solapament de Blocs",
-	"Chunk Params": "Paràmetres de Blocs",
-	"Chunk Size": "Mida del Bloc",
-	"Citation": "Citació",
-	"Clear memory": "",
-	"Click here for help.": "Fes clic aquí per ajuda.",
-	"Click here to": "Fes clic aquí per",
-	"Click here to download user import template file.": "",
-	"Click here to select": "Fes clic aquí per seleccionar",
-	"Click here to select a csv file.": "Fes clic aquí per seleccionar un fitxer csv.",
-	"Click here to select a py file.": "",
-	"Click here to select documents.": "Fes clic aquí per seleccionar documents.",
-	"click here.": "fes clic aquí.",
-	"Click on the user role button to change a user's role.": "Fes clic al botó de rol d'usuari per canviar el rol d'un usuari.",
-	"Clone": "Clon",
-	"Close": "Tanca",
+	"Choose a model before saving...": "Triar un model abans de desar...",
+	"Chunk Overlap": "Solapament de blocs",
+	"Chunk Params": "Paràmetres dels blocs",
+	"Chunk Size": "Mida del bloc",
+	"Citation": "Cita",
+	"Clear memory": "Esborrar la memòria",
+	"Click here for help.": "Clica aquí per obtenir ajuda.",
+	"Click here to": "Clic aquí per",
+	"Click here to download user import template file.": "Fes clic aquí per descarregar l'arxiu de plantilla d'importació d'usuaris",
+	"Click here to select": "Clica aquí per seleccionar",
+	"Click here to select a csv file.": "Clica aquí per seleccionar un fitxer csv.",
+	"Click here to select a py file.": "Clica aquí per seleccionar un fitxer py.",
+	"Click here to select documents.": "Clica aquí per seleccionar documents.",
+	"click here.": "clica aquí.",
+	"Click on the user role button to change a user's role.": "Clica sobre el botó de rol d'usuari per canviar el rol d'un usuari.",
+	"Clone": "Clonar",
+	"Close": "Tancar",
 	"Collection": "Col·lecció",
 	"ComfyUI": "ComfyUI",
 	"ComfyUI Base URL": "URL base de ComfyUI",
-	"ComfyUI Base URL is required.": "URL base de ComfyUI és obligatòria.",
+	"ComfyUI Base URL is required.": "L'URL base de ComfyUI és obligatòria.",
 	"Command": "Comanda",
-	"Concurrent Requests": "Sol·licituds simultànies",
-	"Confirm": "",
-	"Confirm Password": "Confirma la Contrasenya",
-	"Confirm your action": "",
+	"Concurrent Requests": "Peticions simultànies",
+	"Confirm": "Confirmar",
+	"Confirm Password": "Confirmar la contrasenya",
+	"Confirm your action": "Confirma la teva acció",
 	"Connections": "Connexions",
-	"Contact Admin for WebUI Access": "",
+	"Contact Admin for WebUI Access": "Posat en contacte amb l'administrador per accedir a WebUI",
 	"Content": "Contingut",
-	"Context Length": "Longitud del Context",
-	"Continue Response": "Continua la Resposta",
+	"Context Length": "Mida del context",
+	"Continue Response": "Continuar la resposta",
 	"Continue with {{provider}}": "",
 	"Copied shared chat URL to clipboard!": "S'ha copiat l'URL compartida al porta-retalls!",
 	"Copy": "Copiar",
-	"Copy last code block": "Copia l'últim bloc de codi",
-	"Copy last response": "Copia l'última resposta",
+	"Copy last code block": "Copiar l'últim bloc de codi",
+	"Copy last response": "Copiar l'última resposta",
 	"Copy Link": "Copiar l'enllaç",
-	"Copying to clipboard was successful!": "La còpia al porta-retalls ha estat exitosa!",
+	"Copying to clipboard was successful!": "La còpia al porta-retalls s'ha realitzat amb èxit!",
 	"Create a model": "Crear un model",
-	"Create Account": "Crea un Compte",
-	"Create new key": "Crea una nova clau",
-	"Create new secret key": "Crea una nova clau secreta",
+	"Create Account": "Crear un compte",
+	"Create new key": "Crear una nova clau",
+	"Create new secret key": "Crear una nova clau secreta",
 	"Created at": "Creat el",
 	"Created At": "Creat el",
-	"Created by": "",
-	"CSV Import": "",
-	"Current Model": "Model Actual",
-	"Current Password": "Contrasenya Actual",
+	"Created by": "Creat per",
+	"CSV Import": "Importar CSV",
+	"Current Model": "Model actual",
+	"Current Password": "Contrasenya actual",
 	"Custom": "Personalitzat",
 	"Customize models for a specific purpose": "Personalitzar models per a un propòsit específic",
 	"Dark": "Fosc",
-	"Dashboard": "",
-	"Database": "Base de Dades",
+	"Dashboard": "Tauler",
+	"Database": "Base de dades",
 	"December": "Desembre",
 	"Default": "Per defecte",
 	"Default (Automatic1111)": "Per defecte (Automatic1111)",
 	"Default (SentenceTransformers)": "Per defecte (SentenceTransformers)",
 	"Default Model": "Model per defecte",
 	"Default model updated": "Model per defecte actualitzat",
-	"Default Prompt Suggestions": "Suggeriments de Prompt Per Defecte",
-	"Default User Role": "Rol d'Usuari Per Defecte",
-	"delete": "esborra",
-	"Delete": "Esborra",
-	"Delete a model": "Esborra un model",
-	"Delete All Chats": "Suprimir tots els xats",
-	"Delete chat": "Esborra xat",
-	"Delete Chat": "Esborra Xat",
-	"Delete chat?": "",
-	"delete this link": "Esborra aquest enllaç",
-	"Delete User": "Esborra Usuari",
-	"Deleted {{deleteModelTag}}": "Esborrat {{deleteModelTag}}",
-	"Deleted {{name}}": "Suprimit {{nom}}",
+	"Default Prompt Suggestions": "Suggeriments d'indicació per defecte",
+	"Default User Role": "Rol d'usuari per defecte",
+	"delete": "eliminar",
+	"Delete": "Eliminar",
+	"Delete a model": "Eliminar un model",
+	"Delete All Chats": "Eliminar tots els xats",
+	"Delete chat": "Eliminar xat",
+	"Delete Chat": "Eliminar xat",
+	"Delete chat?": "Eliminar el xat?",
+	"delete this link": "Eliminar aquest enllaç",
+	"Delete User": "Eliminar usuari",
+	"Deleted {{deleteModelTag}}": "S'ha eliminat {{deleteModelTag}}",
+	"Deleted {{name}}": "S'ha eliminat {{name}}",
 	"Description": "Descripció",
-	"Didn't fully follow instructions": "No s'ha completat els instruccions",
-	"Discover a model": "Descobreix un model",
-	"Discover a prompt": "Descobreix un prompt",
-	"Discover, download, and explore custom prompts": "Descobreix, descarrega i explora prompts personalitzats",
-	"Discover, download, and explore model presets": "Descobreix, descarrega i explora presets de models",
-	"Dismissible": "",
-	"Display Emoji in Call": "",
-	"Display the username instead of You in the Chat": "Mostra el nom d'usuari en lloc de 'Tu' al Xat",
+	"Didn't fully follow instructions": "No s'han seguit les instruccions completament",
+	"Discover a function": "",
+	"Discover a model": "Descobrir un model",
+	"Discover a prompt": "Descobrir una indicació",
+	"Discover a tool": "",
+	"Discover, download, and explore custom functions": "",
+	"Discover, download, and explore custom prompts": "Descobrir, descarregar i explorar indicacions personalitzades",
+	"Discover, download, and explore custom tools": "",
+	"Discover, download, and explore model presets": "Descobrir, descarregar i explorar models preconfigurats",
+	"Dismissible": "Descartable",
+	"Display Emoji in Call": "Mostrar emojis a la trucada",
+	"Display the username instead of You in the Chat": "Mostrar el nom d'usuari en lloc de 'Tu' al xat",
 	"Document": "Document",
-	"Document Settings": "Configuració de Documents",
-	"Documentation": "",
+	"Document Settings": "Configuració de documents",
+	"Documentation": "Documentació",
 	"Documents": "Documents",
 	"does not make any external connections, and your data stays securely on your locally hosted server.": "no realitza connexions externes, i les teves dades romanen segures al teu servidor allotjat localment.",
-	"Don't Allow": "No Permetre",
+	"Don't Allow": "No permetre",
 	"Don't have an account?": "No tens un compte?",
 	"Don't like the style": "No t'agrada l'estil?",
+	"Done": "",
 	"Download": "Descarregar",
 	"Download canceled": "Descàrrega cancel·lada",
-	"Download Database": "Descarrega Base de Dades",
+	"Download Database": "Descarregar la base de dades",
 	"Drop any files here to add to the conversation": "Deixa qualsevol arxiu aquí per afegir-lo a la conversa",
 	"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "p. ex. '30s','10m'. Les unitats de temps vàlides són 's', 'm', 'h'.",
 	"Edit": "Editar",
-	"Edit Doc": "Edita Document",
-	"Edit Memory": "",
-	"Edit User": "Edita Usuari",
+	"Edit Doc": "Editar el document",
+	"Edit Memory": "Editar la memòria",
+	"Edit User": "Editar l'usuari",
 	"Email": "Correu electrònic",
-	"Embedding Batch Size": "",
-	"Embedding Model": "Model d'embutiment",
-	"Embedding Model Engine": "Motor de model d'embutiment",
-	"Embedding model set to \"{{embedding_model}}\"": "Model d'embutiment configurat a \"{{embedding_model}}\"",
-	"Enable Chat History": "Activa Historial de Xat",
-	"Enable Community Sharing": "Activar l'ús compartit de la comunitat",
-	"Enable New Sign Ups": "Permet Noves Inscripcions",
-	"Enable Web Search": "Activa la cerca web",
-	"Ensure your CSV file includes 4 columns in this order: Name, Email, Password, Role.": "Assegura't que el fitxer CSV inclou 4 columnes en aquest ordre: Nom, Correu Electrònic, Contrasenya, Rol.",
+	"Embedding Batch Size": "Mida del lot d'incrustació",
+	"Embedding Model": "Model d'incrustació",
+	"Embedding Model Engine": "Motor de model d'incrustació",
+	"Embedding model set to \"{{embedding_model}}\"": "Model d'incrustació configurat a \"{{embedding_model}}\"",
+	"Enable Chat History": "Activar l'historial de xats",
+	"Enable Community Sharing": "Activar l'ús compartit amb la comunitat",
+	"Enable New Sign Ups": "Permetre nous registres",
+	"Enable Web Search": "Activar la cerca web",
+	"Ensure your CSV file includes 4 columns in this order: Name, Email, Password, Role.": "Assegura't que els teus fitxers CSV inclouen 4 columnes en aquest ordre: Nom, Correu electrònic, Contrasenya, Rol.",
 	"Enter {{role}} message here": "Introdueix aquí el missatge de {{role}}",
-	"Enter a detail about yourself for your LLMs to recall": "Introdueix un detall sobre tu per que els LLMs puguin recordar-te",
-	"Enter Brave Search API Key": "Introduïu la clau de l'API Brave Search",
-	"Enter Chunk Overlap": "Introdueix el Solapament de Blocs",
-	"Enter Chunk Size": "Introdueix la Mida del Bloc",
-	"Enter Github Raw URL": "Introduïu l'URL en brut de Github",
-	"Enter Google PSE API Key": "Introduïu la clau de l'API de Google PSE",
-	"Enter Google PSE Engine Id": "Introduïu l'identificador del motor PSE de Google",
-	"Enter Image Size (e.g. 512x512)": "Introdueix la Mida de la Imatge (p. ex. 512x512)",
+	"Enter a detail about yourself for your LLMs to recall": "Introdueix un detall sobre tu què els teus models de llenguatge puguin recordar",
+	"Enter Brave Search API Key": "Introdueix la clau API de Brave Search",
+	"Enter Chunk Overlap": "Introdueix la mida de solapament de blocs",
+	"Enter Chunk Size": "Introdueix la mida del bloc",
+	"Enter Github Raw URL": "Introdueix l'URL en brut de Github",
+	"Enter Google PSE API Key": "Introdueix la clau API de Google PSE",
+	"Enter Google PSE Engine Id": "Introdueix l'identificador del motor PSE de Google",
+	"Enter Image Size (e.g. 512x512)": "Introdueix la mida de la imatge (p. ex. 512x512)",
 	"Enter language codes": "Introdueix els codis de llenguatge",
 	"Enter model tag (e.g. {{modelTag}})": "Introdueix l'etiqueta del model (p. ex. {{modelTag}})",
-	"Enter Number of Steps (e.g. 50)": "Introdueix el Nombre de Passos (p. ex. 50)",
-	"Enter Score": "Introdueix el Puntuació",
-	"Enter Searxng Query URL": "Introduïu l'URL de consulta de Searxng",
-	"Enter Serper API Key": "Introduïu la clau de l'API Serper",
-	"Enter Serply API Key": "",
-	"Enter Serpstack API Key": "Introduïu la clau de l'API Serpstack",
+	"Enter Number of Steps (e.g. 50)": "Introdueix el nombre de passos (p. ex. 50)",
+	"Enter Score": "Introdueix la puntuació",
+	"Enter Searxng Query URL": "Introdueix l'URL de consulta de Searxng",
+	"Enter Serper API Key": "Introdueix la clau API Serper",
+	"Enter Serply API Key": "Introdueix la clau API Serply",
+	"Enter Serpstack API Key": "Introdueix la clau API Serpstack",
 	"Enter stop sequence": "Introdueix la seqüència de parada",
-	"Enter Tavily API Key": "",
+	"Enter Tavily API Key": "Introdueix la clau API de Tavily",
 	"Enter Top K": "Introdueix Top K",
 	"Enter URL (e.g. http://127.0.0.1:7860/)": "Introdueix l'URL (p. ex. http://127.0.0.1:7860/)",
 	"Enter URL (e.g. http://localhost:11434)": "Introdueix l'URL (p. ex. http://localhost:11434)",
-	"Enter Your Email": "Introdueix el Teu Correu Electrònic",
-	"Enter Your Full Name": "Introdueix el Teu Nom Complet",
-	"Enter Your Password": "Introdueix la Teva Contrasenya",
-	"Enter Your Role": "Introdueix el Teu Ròl",
+	"Enter Your Email": "Introdueix el teu correu electrònic",
+	"Enter Your Full Name": "Introdueix el teu nom complet",
+	"Enter Your Password": "Introdueix la teva contrasenya",
+	"Enter Your Role": "Introdueix el teu rol",
 	"Error": "Error",
 	"Experimental": "Experimental",
 	"Export": "Exportar",
-	"Export All Chats (All Users)": "Exporta Tots els Xats (Tots els Usuaris)",
-	"Export chat (.json)": "",
-	"Export Chats": "Exporta Xats",
-	"Export Documents Mapping": "Exporta el Mapatge de Documents",
-	"Export Functions": "",
-	"Export Models": "Models d'exportació",
-	"Export Prompts": "Exporta Prompts",
-	"Export Tools": "",
-	"External Models": "",
-	"Failed to create API Key.": "No s'ha pogut crear la clau d'API.",
+	"Export All Chats (All Users)": "Exportar tots els xats (Tots els usuaris)",
+	"Export chat (.json)": "Exportar el xat (.json)",
+	"Export Chats": "Exportar els xats",
+	"Export Documents Mapping": "Exportar el mapatge de documents",
+	"Export Functions": "Exportar funcions",
+	"Export Models": "Exportar els models",
+	"Export Prompts": "Exportar les indicacions",
+	"Export Tools": "Exportar les eines",
+	"External Models": "Models externs",
+	"Failed to create API Key.": "No s'ha pogut crear la clau API.",
 	"Failed to read clipboard contents": "No s'ha pogut llegir el contingut del porta-retalls",
-	"Failed to update settings": "",
+	"Failed to update settings": "No s'ha pogut actualitzar la configuració",
 	"February": "Febrer",
-	"Feel free to add specific details": "Siusplau, afegeix detalls específics",
-	"File": "",
-	"File Mode": "Mode Arxiu",
-	"File not found.": "Arxiu no trobat.",
-	"Filters": "",
-	"Fingerprint spoofing detected: Unable to use initials as avatar. Defaulting to default profile image.": "S'ha detectat la suplantació d'identitat d'empremtes digitals: no es poden utilitzar les inicials com a avatar. Per defecte a la imatge de perfil predeterminada.",
-	"Fluidly stream large external response chunks": "Transmita con fluidez grandes fragmentos de respuesta externa",
-	"Focus chat input": "Enfoca l'entrada del xat",
-	"Followed instructions perfectly": "Siguiu les instruccions perfeicte",
-	"Form": "",
+	"Feel free to add specific details": "Sent-te lliure d'afegir detalls específics",
+	"File": "Arxiu",
+	"File Mode": "Mode d'arxiu",
+	"File not found.": "No s'ha trobat l'arxiu.",
+	"Filters": "Filtres",
+	"Fingerprint spoofing detected: Unable to use initials as avatar. Defaulting to default profile image.": "S'ha detectat la suplantació d'identitat de l'empremta digital: no es poden utilitzar les inicials com a avatar. S'estableix la imatge de perfil predeterminada.",
+	"Fluidly stream large external response chunks": "Transmetre amb fluïdesa grans trossos de resposta externa",
+	"Focus chat input": "Estableix el focus a l'entrada del xat",
+	"Followed instructions perfectly": "S'han seguit les instruccions perfectament",
+	"Form": "Formulari",
 	"Format your variables using square brackets like this:": "Formata les teves variables utilitzant claudàtors així:",
-	"Frequency Penalty": "Pena de freqüència",
-	"Functions": "",
+	"Frequency Penalty": "Penalització per freqüència",
+	"Functions": "Funcions",
 	"General": "General",
-	"General Settings": "Configuració General",
-	"Generate Image": "",
-	"Generating search query": "Generació de consultes de cerca",
-	"Generation Info": "Informació de Generació",
-	"Good Response": "Resposta bona",
-	"Google PSE API Key": "Clau de l'API PSE de Google",
+	"General Settings": "Configuració general",
+	"Generate Image": "Generar imatge",
+	"Generating search query": "Generant consulta",
+	"Generation Info": "Informació sobre la generació",
+	"Good Response": "Bona resposta",
+	"Google PSE API Key": "Clau API PSE de Google",
 	"Google PSE Engine Id": "Identificador del motor PSE de Google",
 	"h:mm a": "h:mm a",
 	"has no conversations.": "no té converses.",
 	"Hello, {{name}}": "Hola, {{name}}",
 	"Help": "Ajuda",
 	"Hide": "Amaga",
-	"Hide Model": "",
+	"Hide Model": "Amagar el model",
 	"How can I help you today?": "Com et puc ajudar avui?",
-	"Hybrid Search": "Cerca Hibrida",
-	"Image Generation (Experimental)": "Generació d'Imatges (Experimental)",
-	"Image Generation Engine": "Motor de Generació d'Imatges",
-	"Image Settings": "Configuració d'Imatges",
+	"Hybrid Search": "Cerca brida",
+	"Image Generation (Experimental)": "Generació d'imatges (Experimental)",
+	"Image Generation Engine": "Motor de generació d'imatges",
+	"Image Settings": "Configuració d'imatges",
 	"Images": "Imatges",
-	"Import Chats": "Importa Xats",
-	"Import Documents Mapping": "Importa el Mapa de Documents",
-	"Import Functions": "",
-	"Import Models": "Models d'importació",
-	"Import Prompts": "Importa Prompts",
-	"Import Tools": "",
-	"Include `--api` flag when running stable-diffusion-webui": "Inclou la bandera `--api` quan executis stable-diffusion-webui",
+	"Import Chats": "Importar xats",
+	"Import Documents Mapping": "Importar el mapatge de documents",
+	"Import Functions": "Importar funcions",
+	"Import Models": "Importar models",
+	"Import Prompts": "Importar indicacions",
+	"Import Tools": "Importar eines",
+	"Include `--api` flag when running stable-diffusion-webui": "Inclou `--api` quan executis stable-diffusion-webui",
 	"Info": "Informació",
-	"Input commands": "Entra ordres",
-	"Install from Github URL": "Instal·leu des de l'URL de Github",
-	"Instant Auto-Send After Voice Transcription": "",
+	"Input commands": "Entra comandes",
+	"Install from Github URL": "Instal·lar des de l'URL de Github",
+	"Instant Auto-Send After Voice Transcription": "Enviament automàtic després de la transcripció de veu",
 	"Interface": "Interfície",
-	"Invalid Tag": "Etiqueta Inválida",
+	"Invalid Tag": "Etiqueta no vàlida",
 	"January": "Gener",
-	"join our Discord for help.": "uneix-te al nostre Discord per ajuda.",
+	"join our Discord for help.": "uneix-te al nostre Discord per obtenir ajuda.",
 	"JSON": "JSON",
-	"JSON Preview": "Vista prèvia de JSON",
+	"JSON Preview": "Vista prèvia del document JSON",
 	"July": "Juliol",
 	"June": "Juny",
-	"JWT Expiration": "Expiració de JWT",
+	"JWT Expiration": "Caducitat del JWT",
 	"JWT Token": "Token JWT",
-	"Keep Alive": "Mantén Actiu",
-	"Keyboard shortcuts": "Dreceres de Teclat",
-	"Knowledge": "",
+	"Keep Alive": "Manté actiu",
+	"Keyboard shortcuts": "Dreceres de teclat",
+	"Knowledge": "Coneixement",
 	"Language": "Idioma",
-	"Last Active": "Últim Actiu",
-	"Last Modified": "",
+	"Last Active": "Activitat recent",
+	"Last Modified": "Modificació",
 	"Light": "Clar",
-	"Listening...": "",
-	"LLMs can make mistakes. Verify important information.": "Els LLMs poden cometre errors. Verifica la informació important.",
-	"Local Models": "",
+	"Listening...": "Escoltant...",
+	"LLMs can make mistakes. Verify important information.": "Els models de llenguatge poden cometre errors. Verifica la informació important.",
+	"Local Models": "Models locals",
 	"LTR": "LTR",
 	"Made by OpenWebUI Community": "Creat per la Comunitat OpenWebUI",
 	"Make sure to enclose them with": "Assegura't d'envoltar-los amb",
-	"Manage": "",
-	"Manage Models": "Gestiona Models",
-	"Manage Ollama Models": "Gestiona Models Ollama",
-	"Manage Pipelines": "Gestionar canonades",
+	"Manage": "Gestionar",
+	"Manage Models": "Gestionar els models",
+	"Manage Ollama Models": "Gestionar els models Ollama",
+	"Manage Pipelines": "Gestionar les Pipelines",
+	"Manage Valves": "",
 	"March": "Març",
-	"Max Tokens (num_predict)": "Max Fitxes (num_predict)",
+	"Max Tokens (num_predict)": "Nombre màxim de Tokens (num_predict)",
 	"Maximum of 3 models can be downloaded simultaneously. Please try again later.": "Es poden descarregar un màxim de 3 models simultàniament. Si us plau, prova-ho més tard.",
 	"May": "Maig",
-	"Memories accessible by LLMs will be shown here.": "Els memòries accessible per a LLMs es mostraran aquí.",
+	"Memories accessible by LLMs will be shown here.": "Les memòries accessibles pels models de llenguatge es mostraran aquí.",
 	"Memory": "Memòria",
-	"Messages you send after creating your link won't be shared. Users with the URL will be able to view the shared chat.": "Els missatges que envieu després de crear el vostre enllaç no es compartiran. Els usuaris amb l'URL podran veure el xat compartit.",
+	"Messages you send after creating your link won't be shared. Users with the URL will be able to view the shared chat.": "Els missatges enviats després de crear el teu enllaç no es compartiran. Els usuaris amb l'URL podran veure el xat compartit.",
 	"Minimum Score": "Puntuació mínima",
 	"Mirostat": "Mirostat",
 	"Mirostat Eta": "Eta 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": "",
+	"MMMM DD, YYYY hh:mm:ss A": "DD de MMMM, YYYY HH:mm:ss, A",
 	"Model '{{modelName}}' has been successfully downloaded.": "El model '{{modelName}}' s'ha descarregat amb èxit.",
 	"Model '{{modelTag}}' is already in queue for downloading.": "El model '{{modelTag}}' ja està en cua per ser descarregat.",
-	"Model {{modelId}} not found": "Model {{modelId}} no trobat",
+	"Model {{modelId}} not found": "No s'ha trobat el model {{modelId}}",
 	"Model {{modelName}} is not vision capable": "El model {{modelName}} no és capaç de visió",
-	"Model {{name}} is now {{status}}": "El model {{nom}} ara és {{estat}}",
-	"Model filesystem path detected. Model shortname is required for update, cannot continue.": "S'ha detectat el camí del sistema de fitxers del model. És necessari un nom curt del model per a actualitzar, no es pot continuar.",
+	"Model {{name}} is now {{status}}": "El model {{name}} ara és {{status}}",
+	"Model filesystem path detected. Model shortname is required for update, cannot continue.": "S'ha detectat el camí del sistema de fitxers del model. És necessari un nom curt del model per actualitzar, no es pot continuar.",
 	"Model ID": "Identificador del model",
 	"Model not selected": "Model no seleccionat",
 	"Model Params": "Paràmetres del model",
-	"Model Whitelisting": "Llista Blanca de Models",
-	"Model(s) Whitelisted": "Model(s) a la Llista Blanca",
-	"Modelfile Content": "Contingut del Fitxer de Model",
+	"Model Whitelisting": "Llista blanca de models",
+	"Model(s) Whitelisted": "Model(s) a la llista blanca",
+	"Modelfile Content": "Contingut del Modelfile",
 	"Models": "Models",
 	"More": "Més",
 	"Name": "Nom",
-	"Name Tag": "Etiqueta de Nom",
-	"Name your model": "Posa un nom al model",
-	"New Chat": "Xat Nou",
-	"New Password": "Nova Contrasenya",
-	"No documents found": "",
+	"Name Tag": "Etiqueta de nom",
+	"Name your model": "Posa un nom al teu model",
+	"New Chat": "Nou xat",
+	"New Password": "Nova contrasenya",
+	"No documents found": "No s'han trobat documents",
 	"No results found": "No s'han trobat resultats",
-	"No search query generated": "No es genera cap consulta de cerca",
+	"No search query generated": "No s'ha generat cap consulta",
 	"No source available": "Sense font disponible",
 	"None": "Cap",
-	"Not factually correct": "No està clarament correcte",
-	"Note: If you set a minimum score, the search will only return documents with a score greater than or equal to the minimum score.": "Nota: Si establiscs una puntuació mínima, la cerca només retornarà documents amb una puntuació major o igual a la puntuació mínima.",
-	"Notifications": "Notificacions d'Escriptori",
+	"Not factually correct": "No és clarament correcte",
+	"Note: If you set a minimum score, the search will only return documents with a score greater than or equal to the minimum score.": "Nota: Si s'estableix una puntuació mínima, la cerca només retornarà documents amb una puntuació major o igual a la puntuació mínima.",
+	"Notifications": "Notificacions",
 	"November": "Novembre",
 	"num_thread (Ollama)": "num_thread (Ollama)",
 	"OAuth ID": "",
 	"October": "Octubre",
 	"Off": "Desactivat",
-	"Okay, Let's Go!": "D'acord, Anem!",
+	"Okay, Let's Go!": "D'acord, som-hi!",
 	"OLED Dark": "OLED Fosc",
 	"Ollama": "Ollama",
 	"Ollama API": "API d'Ollama",
-	"Ollama API disabled": "L'API d'Ollama desactivada",
-	"Ollama API is disabled": "",
+	"Ollama API disabled": "API d'Ollama desactivada",
+	"Ollama API is disabled": "L'API d'Ollama està desactivada",
 	"Ollama Version": "Versió d'Ollama",
 	"On": "Activat",
 	"Only": "Només",
-	"Only alphanumeric characters and hyphens are allowed in the command string.": "Només es permeten caràcters alfanumèrics i guions en la cadena de comandes.",
-	"Oops! Hold tight! Your files are still in the processing oven. We're cooking them up to perfection. Please be patient and we'll let you know once they're ready.": "Ui! Aguanta! Els teus fitxers encara estan en el forn de processament. Els estem cuinant a la perfecció. Si us plau, tingues paciència i t'avisarem quan estiguin llestos.",
-	"Oops! Looks like the URL is invalid. Please double-check and try again.": "Ui! Sembla que l'URL és invàlida. Si us plau, revisa-ho i prova de nou.",
-	"Oops! There was an error in the previous response. Please try again or contact admin.": "",
+	"Only alphanumeric characters and hyphens are allowed in the command string.": "Només es permeten caràcters alfanumèrics i guions en la comanda.",
+	"Oops! Hold tight! Your files are still in the processing oven. We're cooking them up to perfection. Please be patient and we'll let you know once they're ready.": "Ep! Un moment! Els teus fitxers encara s'estan processant. Els estem cuinant a la perfecció. Si us plau, tingues paciència i t'avisarem quan estiguin preparats.",
+	"Oops! Looks like the URL is invalid. Please double-check and try again.": "Ui! Sembla que l'URL no és vàlida. Si us plau, revisa-la i torna-ho a provar.",
+	"Oops! There was an error in the previous response. Please try again or contact admin.": "Ui! Hi ha hagut un error en la resposta anterior. Torna a provar-ho o contacta amb un administrador",
 	"Oops! You're using an unsupported method (frontend only). Please serve the WebUI from the backend.": "Ui! Estàs utilitzant un mètode no suportat (només frontend). Si us plau, serveix la WebUI des del backend.",
 	"Open": "Obre",
 	"Open AI": "Open AI",
 	"Open AI (Dall-E)": "Open AI (Dall-E)",
-	"Open new chat": "Obre un nou xat",
+	"Open new chat": "Obre un xat nou",
 	"OpenAI": "OpenAI",
 	"OpenAI API": "API d'OpenAI",
 	"OpenAI API Config": "Configuració de l'API d'OpenAI",
-	"OpenAI API Key is required.": "Es requereix la Clau API d'OpenAI.",
+	"OpenAI API Key is required.": "Es requereix la clau API d'OpenAI.",
 	"OpenAI URL/Key required.": "URL/Clau d'OpenAI requerides.",
 	"or": "o",
 	"Other": "Altres",
 	"Password": "Contrasenya",
 	"PDF document (.pdf)": "Document PDF (.pdf)",
-	"PDF Extract Images (OCR)": "Extreu Imatges de PDF (OCR)",
+	"PDF Extract Images (OCR)": "Extreu imatges del PDF (OCR)",
 	"pending": "pendent",
-	"Permission denied when accessing media devices": "",
-	"Permission denied when accessing microphone": "",
+	"Permission denied when accessing media devices": "Permís denegat en accedir a dispositius multimèdia",
+	"Permission denied when accessing microphone": "Permís denegat en accedir al micròfon",
 	"Permission denied when accessing microphone: {{error}}": "Permís denegat en accedir al micròfon: {{error}}",
 	"Personalization": "Personalització",
-	"Pipelines": "Canonades",
-	"Pipelines Valves": "Vàlvules de canonades",
+	"Pipelines": "Pipelines",
+	"Pipelines Valves": "Vàlvules de les Pipelines",
 	"Plain text (.txt)": "Text pla (.txt)",
-	"Playground": "Zona de Jocs",
-	"Positive attitude": "Attitudin positiva",
+	"Playground": "Zona de jocs",
+	"Positive attitude": "Actitud positiva",
 	"Previous 30 days": "30 dies anteriors",
 	"Previous 7 days": "7 dies anteriors",
 	"Profile Image": "Imatge de perfil",
-	"Prompt": "Prompt",
-	"Prompt (e.g. Tell me a fun fact about the Roman Empire)": "Prompt (p.ex. diu-me un fàcte divertit sobre l'Imperi Roman)",
-	"Prompt Content": "Contingut del Prompt",
-	"Prompt suggestions": "Suggeriments de Prompt",
-	"Prompts": "Prompts",
-	"Pull \"{{searchValue}}\" from Ollama.com": "Treu \"{{searchValue}}\" de Ollama.com",
-	"Pull a model from Ollama.com": "Treu un model d'Ollama.com",
-	"Query Params": "Paràmetres de Consulta",
+	"Prompt": "Indicació",
+	"Prompt (e.g. Tell me a fun fact about the Roman Empire)": "Indicació (p.ex. Digues-me quelcom divertit sobre l'Imperi Romà)",
+	"Prompt Content": "Contingut de la indicació",
+	"Prompt suggestions": "Suggeriments d'indicacions",
+	"Prompts": "Indicacions",
+	"Pull \"{{searchValue}}\" from Ollama.com": "Obtenir \"{{searchValue}}\" de Ollama.com",
+	"Pull a model from Ollama.com": "Obtenir un model d'Ollama.com",
+	"Query Params": "Paràmetres de consulta",
 	"RAG Template": "Plantilla RAG",
-	"Read Aloud": "Llegiu al voltant",
-	"Record voice": "Enregistra veu",
-	"Redirecting you to OpenWebUI Community": "Redirigint-te a la Comunitat OpenWebUI",
-	"Refer to yourself as \"User\" (e.g., \"User is learning Spanish\")": "",
-	"Refused when it shouldn't have": "Refusat quan no hauria d'haver-ho",
+	"Read Aloud": "Llegir en veu alta",
+	"Record voice": "Enregistrar la veu",
+	"Redirecting you to OpenWebUI Community": "Redirigint-te a la comunitat OpenWebUI",
+	"Refer to yourself as \"User\" (e.g., \"User is learning Spanish\")": "Fes referència a tu mateix com a \"Usuari\" (p. ex., \"L'usuari està aprenent espanyol\")",
+	"Refused when it shouldn't have": "Refusat quan no hauria d'haver estat",
 	"Regenerate": "Regenerar",
-	"Release Notes": "Notes de la Versió",
-	"Remove": "Elimina",
-	"Remove Model": "Elimina Model",
-	"Rename": "Canvia el nom",
-	"Repeat Last N": "Repeteix Últim N",
-	"Request Mode": "Mode de Sol·licitud",
-	"Reranking Model": "Model de Reranking desactivat",
-	"Reranking model disabled": "Model de Reranking desactivat",
-	"Reranking model set to \"{{reranking_model}}\"": "Model de Reranking establert a \"{{reranking_model}}\"",
-	"Reset": "",
-	"Reset Upload Directory": "",
-	"Reset Vector Storage": "Reinicia l'Emmagatzematge de Vectors",
-	"Response AutoCopy to Clipboard": "Resposta AutoCopiar al Portapapers",
+	"Release Notes": "Notes de la versió",
+	"Remove": "Eliminar",
+	"Remove Model": "Eliminar el model",
+	"Rename": "Canviar el nom",
+	"Repeat Last N": "Repeteix els darrers N",
+	"Request Mode": "Mode de sol·licitud",
+	"Reranking Model": "Model de reavaluació",
+	"Reranking model disabled": "Model de reavaluació desactivat",
+	"Reranking model set to \"{{reranking_model}}\"": "Model de reavaluació establert a \"{{reranking_model}}\"",
+	"Reset": "Restableix",
+	"Reset Upload Directory": "Restableix el directori de pujades",
+	"Reset Vector Storage": "Restableix l'emmagatzematge de vectors",
+	"Response AutoCopy to Clipboard": "Copiar la resposta automàticament al porta-retalls",
 	"Role": "Rol",
 	"Rosé Pine": "Rosé Pine",
 	"Rosé Pine Dawn": "Albada Rosé Pine",
 	"RTL": "RTL",
-	"Running": "",
-	"Save": "Guarda",
-	"Save & Create": "Guarda i Crea",
-	"Save & Update": "Guarda i Actualitza",
-	"Saving chat logs directly to your browser's storage is no longer supported. Please take a moment to download and delete your chat logs by clicking the button below. Don't worry, you can easily re-import your chat logs to the backend through": "Guardar registres de xat directament a l'emmagatzematge del teu navegador ja no és suportat. Si us plau, pren un moment per descarregar i eliminar els teus registres de xat fent clic al botó de sota. No et preocupis, pots reimportar fàcilment els teus registres de xat al backend a través de",
-	"Scan": "Escaneja",
-	"Scan complete!": "Escaneig completat!",
-	"Scan for documents from {{path}}": "Escaneja documents des de {{path}}",
-	"Search": "Cerca",
-	"Search a model": "Cerca un model",
+	"Running": "S'està executant",
+	"Save": "Desar",
+	"Save & Create": "Desar i crear",
+	"Save & Update": "Desar i actualitzar",
+	"Saving chat logs directly to your browser's storage is no longer supported. Please take a moment to download and delete your chat logs by clicking the button below. Don't worry, you can easily re-import your chat logs to the backend through": "Desar els registres de xat directament a l'emmagatzematge del teu navegador ja no està suportat. Si us plau, descarregr i elimina els registres de xat fent clic al botó de sota. No et preocupis, pots tornar a importar fàcilment els teus registres de xat al backend a través de",
+	"Scan": "Escanejar",
+	"Scan complete!": "Escaneigr completat!",
+	"Scan for documents from {{path}}": "Escanejar documents des de {{path}}",
+	"Search": "Cercar",
+	"Search a model": "Cercar un model",
 	"Search Chats": "Cercar xats",
-	"Search Documents": "Cerca Documents",
-	"Search Functions": "",
-	"Search Models": "Models de cerca",
-	"Search Prompts": "Cerca Prompts",
-	"Search Query Generation Prompt": "",
-	"Search Query Generation Prompt Length Threshold": "",
+	"Search Documents": "Cercar documents",
+	"Search Functions": "Cercar funcions",
+	"Search Models": "Cercar models",
+	"Search Prompts": "Cercar indicacions",
+	"Search Query Generation Prompt": "Indicació de cerca de generació de consultes",
+	"Search Query Generation Prompt Length Threshold": "Mida màxima de la indicació de cerca de generació de consultes",
 	"Search Result Count": "Recompte de resultats de cerca",
-	"Search Tools": "",
-	"Searched {{count}} sites_one": "Cercat {{count}} sites_one",
-	"Searched {{count}} sites_many": "Cercat {{recompte}} sites_many",
-	"Searched {{count}} sites_other": "Cercat {{recompte}} sites_other",
-	"Searching \"{{searchQuery}}\"": "",
-	"Searxng Query URL": "Searxng URL de consulta",
-	"See readme.md for instructions": "Consulta el readme.md per a instruccions",
-	"See what's new": "Veure novetats",
+	"Search Tools": "Cercar eines",
+	"Searched {{count}} sites_one": "S'ha cercat {{count}} una pàgina",
+	"Searched {{count}} sites_many": "S'han cercat {{count}} pàgines",
+	"Searched {{count}} sites_other": "S'han cercat {{count}} pàgines",
+	"Searching \"{{searchQuery}}\"": "Cercant \"{{searchQuery}}\"",
+	"Searxng Query URL": "URL de consulta de Searxng",
+	"See readme.md for instructions": "Consulta l'arxiu readme.md per obtenir instruccions",
+	"See what's new": "Veure què hi ha de nou",
 	"Seed": "Llavor",
 	"Select a base model": "Seleccionar un model base",
-	"Select a engine": "",
-	"Select a mode": "Selecciona un mode",
-	"Select a model": "Selecciona un model",
-	"Select a pipeline": "Seleccioneu una canonada",
-	"Select a pipeline url": "Seleccionar un URL de canonada",
-	"Select an Ollama instance": "Selecciona una instància d'Ollama",
-	"Select Documents": "",
-	"Select model": "Selecciona un model",
-	"Select only one model to call": "",
-	"Selected model(s) do not support image inputs": "Els models seleccionats no admeten l'entrada d'imatges",
-	"Send": "Envia",
-	"Send a Message": "Envia un Missatge",
-	"Send message": "Envia missatge",
+	"Select a engine": "Seleccionar un motor",
+	"Select a function": "",
+	"Select a mode": "Seleccionar un mode",
+	"Select a model": "Seleccionar un model",
+	"Select a pipeline": "Seleccionar una Pipeline",
+	"Select a pipeline url": "Seleccionar l'URL d'una Pipeline",
+	"Select a tool": "",
+	"Select an Ollama instance": "Seleccionar una instància d'Ollama",
+	"Select Documents": "Seleccionar documents",
+	"Select model": "Seleccionar un model",
+	"Select only one model to call": "Seleccionar només un model per trucar",
+	"Selected model(s) do not support image inputs": "El(s) model(s) seleccionats no admeten l'entrada d'imatges",
+	"Send": "Enviar",
+	"Send a Message": "Enviar un missatge",
+	"Send message": "Enviar missatge",
 	"September": "Setembre",
-	"Serper API Key": "Clau API Serper",
-	"Serply API Key": "",
-	"Serpstack API Key": "Serpstack API Key",
+	"Serper API Key": "Clau API de Serper",
+	"Serply API Key": "Clau API de Serply",
+	"Serpstack API Key": "Clau API de Serpstack",
 	"Server connection verified": "Connexió al servidor verificada",
-	"Set as default": "Estableix com a predeterminat",
-	"Set Default Model": "Estableix Model Predeterminat",
-	"Set embedding model (e.g. {{model}})": "Estableix el model d'emboscada (p.ex. {{model}})",
-	"Set Image Size": "Estableix Mida de la Imatge",
-	"Set reranking model (e.g. {{model}})": "Estableix el model de reranking (p.ex. {{model}})",
-	"Set Steps": "Estableix Passos",
-	"Set Task Model": "Defineix el model de tasca",
-	"Set Voice": "Estableix Veu",
+	"Set as default": "Establir com a predeterminat",
+	"Set Default Model": "Establir el model predeterminat",
+	"Set embedding model (e.g. {{model}})": "Establir el model d'incrustació (p.ex. {{model}})",
+	"Set Image Size": "Establir la mida de la image",
+	"Set reranking model (e.g. {{model}})": "Establir el model de reavaluació (p.ex. {{model}})",
+	"Set Steps": "Establir el nombre de passos",
+	"Set Task Model": "Establir el model de tasca",
+	"Set Voice": "Establir la veu",
 	"Settings": "Configuracions",
-	"Settings saved successfully!": "Configuracions guardades amb èxit!",
-	"Settings updated successfully": "",
+	"Settings saved successfully!": "Les configuracions s'han desat amb èxit!",
+	"Settings updated successfully": "Les configuracions s'han actualitzat amb èxit",
 	"Share": "Compartir",
-	"Share Chat": "Compartir el Chat",
-	"Share to OpenWebUI Community": "Comparteix amb la Comunitat OpenWebUI",
-	"short-summary": "resum curt",
-	"Show": "Mostra",
-	"Show Admin Details in Account Pending Overlay": "",
-	"Show Model": "",
-	"Show shortcuts": "Mostra dreceres",
-	"Showcased creativity": "Mostra la creativitat",
+	"Share Chat": "Compartir el xat",
+	"Share to OpenWebUI Community": "Compartir amb la comunitat OpenWebUI",
+	"short-summary": "resum breu",
+	"Show": "Mostrar",
+	"Show Admin Details in Account Pending Overlay": "Mostrar els detalls de l'administrador a la superposició del compte pendent",
+	"Show Model": "Mostrar el model",
+	"Show shortcuts": "Mostrar dreceres",
+	"Show your support!": "",
+	"Showcased creativity": "Creativitat mostrada",
 	"sidebar": "barra lateral",
-	"Sign in": "Inicia sessió",
-	"Sign Out": "Tanca sessió",
-	"Sign up": "Registra't",
+	"Sign in": "Iniciar sessió",
+	"Sign Out": "Tancar sessió",
+	"Sign up": "Registrar-se",
 	"Signing in": "Iniciant sessió",
 	"Source": "Font",
 	"Speech recognition error: {{error}}": "Error de reconeixement de veu: {{error}}",
-	"Speech-to-Text Engine": "Motor de Veu a Text",
-	"Stop Sequence": "Atura Seqüència",
-	"STT Model": "",
-	"STT Settings": "Configuracions STT",
-	"Submit": "Envia",
+	"Speech-to-Text Engine": "Motor de veu a text",
+	"Stop Sequence": "Atura la seqüència",
+	"STT Model": "Model SST",
+	"STT Settings": "Configuracions de STT",
+	"Submit": "Enviar",
 	"Subtitle (e.g. about the Roman Empire)": "Subtítol (per exemple, sobre l'Imperi Romà)",
 	"Success": "Èxit",
 	"Successfully updated.": "Actualitzat amb èxit.",
 	"Suggested": "Suggerit",
 	"System": "Sistema",
-	"System Prompt": "Prompt del Sistema",
+	"System Prompt": "Indicació del Sistema",
 	"Tags": "Etiquetes",
-	"Tap to interrupt": "",
-	"Tavily API Key": "",
-	"Tell us more:": "Dóna'ns més informació:",
+	"Tap to interrupt": "Prem per interrompre",
+	"Tavily API Key": "Clau API de Tavily",
+	"Tell us more:": "Dona'ns més informació:",
 	"Temperature": "Temperatura",
 	"Template": "Plantilla",
-	"Text Completion": "Completació de Text",
-	"Text-to-Speech Engine": "Motor de Text a Veu",
+	"Text Completion": "Completament de text",
+	"Text-to-Speech Engine": "Motor de text a veu",
 	"Tfs Z": "Tfs Z",
 	"Thanks for your feedback!": "Gràcies pel teu comentari!",
-	"The score should be a value between 0.0 (0%) and 1.0 (100%).": "El puntuatge ha de ser un valor entre 0.0 (0%) i 1.0 (100%).",
+	"The score should be a value between 0.0 (0%) and 1.0 (100%).": "El valor de puntuació hauria de ser entre 0.0 (0%) i 1.0 (100%).",
 	"Theme": "Tema",
-	"Thinking...": "",
-	"This action cannot be undone. Do you wish to continue?": "",
-	"This ensures that your valuable conversations are securely saved to your backend database. Thank you!": "Això assegura que les teves converses valuoses queden segurament guardades a la teva base de dades backend. Gràcies!",
-	"This is an experimental feature, it may not function as expected and is subject to change at any time.": "",
+	"Thinking...": "Pensant...",
+	"This action cannot be undone. Do you wish to continue?": "Aquesta acció no es pot desfer. Vols continuar?",
+	"This ensures that your valuable conversations are securely saved to your backend database. Thank you!": "Això assegura que les teves converses valuoses queden desades de manera segura a la teva base de dades. Gràcies!",
+	"This is an experimental feature, it may not function as expected and is subject to change at any time.": "Aquesta és una funció experimental, és possible que no funcioni com s'espera i està subjecta a canvis en qualsevol moment.",
 	"This setting does not sync across browsers or devices.": "Aquesta configuració no es sincronitza entre navegadors ni dispositius.",
-	"This will delete": "",
-	"Thorough explanation": "Explacació exhaustiva",
-	"Tip: Update multiple variable slots consecutively by pressing the tab key in the chat input after each replacement.": "Consell: Actualitza diversos espais de variables consecutivament prement la tecla de tabulació en l'entrada del xat després de cada reemplaçament.",
+	"This will delete": "Això eliminarà",
+	"Thorough explanation": "Explicació en detall",
+	"Tip: Update multiple variable slots consecutively by pressing the tab key in the chat input after each replacement.": "Consell: Actualitza les diverses variables consecutivament prement la tecla de tabulació en l'entrada del xat després de cada reemplaçament.",
 	"Title": "Títol",
-	"Title (e.g. Tell me a fun fact)": "Títol (p. ex. diu-me un fet divertit)",
-	"Title Auto-Generation": "Auto-Generació de Títol",
+	"Title (e.g. Tell me a fun fact)": "Títol (p. ex. Digues-me quelcom divertit)",
+	"Title Auto-Generation": "Generació automàtica de títol",
 	"Title cannot be an empty string.": "El títol no pot ser una cadena buida.",
-	"Title Generation Prompt": "Prompt de Generació de Títol",
+	"Title Generation Prompt": "Indicació de generació de títol",
 	"to": "a",
 	"To access the available model names for downloading,": "Per accedir als noms dels models disponibles per descarregar,",
 	"To access the GGUF models available for downloading,": "Per accedir als models GGUF disponibles per descarregar,",
-	"To access the WebUI, please reach out to the administrator. Admins can manage user statuses from the Admin Panel.": "",
-	"To add documents here, upload them to the \"Documents\" workspace first.": "",
+	"To access the WebUI, please reach out to the administrator. Admins can manage user statuses from the Admin Panel.": "Per accedir a la WebUI, poseu-vos en contacte amb l'administrador. Els administradors poden gestionar els estats dels usuaris des del tauler d'administració.",
+	"To add documents here, upload them to the \"Documents\" workspace first.": "Per afegir documents aquí, puja-ls primer a l'espai de treball \"Documents\".",
 	"to chat input.": "a l'entrada del xat.",
-	"To select filters here, add them to the \"Functions\" workspace first.": "",
-	"To select toolkits here, add them to the \"Tools\" workspace first.": "",
+	"To select filters here, add them to the \"Functions\" workspace first.": "Per seleccionar filtres aquí, afegeix-los primer a l'espai de treball \"Funcions\".",
+	"To select toolkits here, add them to the \"Tools\" workspace first.": "Per seleccionar kits d'eines aquí, afegeix-los primer a l'espai de treball \"Eines\".",
 	"Today": "Avui",
-	"Toggle settings": "Commuta configuracions",
-	"Toggle sidebar": "Commuta barra lateral",
-	"Tokens To Keep On Context Refresh (num_keep)": "",
-	"Tools": "",
+	"Toggle settings": "Alterna configuracions",
+	"Toggle sidebar": "Alterna la barra lateral",
+	"Tokens To Keep On Context Refresh (num_keep)": "Tokens a mantenir en l'actualització del context (num_keep)",
+	"Tools": "Eines",
 	"Top K": "Top K",
 	"Top P": "Top P",
-	"Trouble accessing Ollama?": "Problemes accedint a Ollama?",
-	"TTS Model": "",
+	"Trouble accessing Ollama?": "Problemes en accedir a Ollama?",
+	"TTS Model": "Model TTS",
 	"TTS Settings": "Configuracions TTS",
-	"TTS Voice": "",
+	"TTS Voice": "Veu TTS",
 	"Type": "Tipus",
-	"Type Hugging Face Resolve (Download) URL": "Escriu URL de Resolució (Descàrrega) de Hugging Face",
-	"Uh-oh! There was an issue connecting to {{provider}}.": "Uf! Hi va haver un problema connectant-se a {{provider}}.",
-	"UI": "",
+	"Type Hugging Face Resolve (Download) URL": "Escriu l'URL de Resolució (Descàrrega) de Hugging Face",
+	"Uh-oh! There was an issue connecting to {{provider}}.": "Oh! Hi ha hagut un problema connectant a {{provider}}.",
+	"UI": "UI",
 	"Unknown file type '{{file_type}}'. Proceeding with the file upload anyway.": "",
-	"Update": "",
-	"Update and Copy Link": "Actualitza i Copia enllaç",
-	"Update password": "Actualitza contrasenya",
-	"Updated at": "",
-	"Upload": "",
-	"Upload a GGUF model": "Puja un model GGUF",
+	"Update": "Actualitzar",
+	"Update and Copy Link": "Actualitzar i copiar l'enllaç",
+	"Update password": "Actualitzar la contrasenya",
+	"Updated at": "Actualitzat",
+	"Upload": "Pujar",
+	"Upload a GGUF model": "Pujar un model GGUF",
 	"Upload Files": "Pujar fitxers",
-	"Upload Pipeline": "",
-	"Upload Progress": "Progrés de Càrrega",
+	"Upload Pipeline": "Pujar una Pipeline",
+	"Upload Progress": "Progrés de càrrega",
 	"URL Mode": "Mode URL",
-	"Use '#' in the prompt input to load and select your documents.": "Utilitza '#' a l'entrada del prompt per carregar i seleccionar els teus documents.",
-	"Use Gravatar": "Utilitza Gravatar",
-	"Use Initials": "Utilitza Inicials",
+	"Use '#' in the prompt input to load and select your documents.": "Utilitza '#' a l'entrada de la indicació per carregar i seleccionar els teus documents.",
+	"Use Gravatar": "Utilitzar Gravatar",
+	"Use Initials": "Utilitzar inicials",
 	"use_mlock (Ollama)": "use_mlock (Ollama)",
 	"use_mmap (Ollama)": "use_mmap (Ollama)",
 	"user": "usuari",
-	"User Permissions": "Permisos d'Usuari",
+	"User Permissions": "Permisos d'usuari",
 	"Users": "Usuaris",
-	"Utilize": "Utilitza",
+	"Utilize": "Utilitzar",
 	"Valid time units:": "Unitats de temps vàlides:",
+	"Valves": "",
 	"variable": "variable",
 	"variable to have them replaced with clipboard content.": "variable per tenir-les reemplaçades amb el contingut del porta-retalls.",
 	"Version": "Versió",
-	"Voice": "",
-	"Warning": "Advertiment",
-	"Warning: If you update or change your embedding model, you will need to re-import all documents.": "Avís: Si actualitzeu o canvieu el model d'incrustació, haureu de tornar a importar tots els documents.",
+	"Voice": "Veu",
+	"Warning": "Avís",
+	"Warning: If you update or change your embedding model, you will need to re-import all documents.": "Avís: Si s'actualitza o es canvia el model d'incrustació, s'hauran de tornar a importar tots els documents.",
 	"Web": "Web",
-	"Web API": "",
+	"Web API": "Web API",
 	"Web Loader Settings": "Configuració del carregador web",
 	"Web Params": "Paràmetres web",
-	"Web Search": "Cercador web",
-	"Web Search Engine": "Cercador web",
+	"Web Search": "Cerca la web",
+	"Web Search Engine": "Motor de cerca de la web",
 	"Webhook URL": "URL del webhook",
-	"WebUI Settings": "Configuració de WebUI",
+	"WebUI Settings": "Configuracions de WebUI",
 	"WebUI will make requests to": "WebUI farà peticions a",
-	"What’s New in": "Què hi ha de Nou en",
+	"What’s New in": "Què hi ha de nou a",
 	"When history is turned off, new chats on this browser won't appear in your history on any of your devices.": "Quan l'historial està desactivat, els nous xats en aquest navegador no apareixeran en el teu historial en cap dels teus dispositius.",
-	"Whisper (Local)": "",
-	"Widescreen Mode": "",
-	"Workspace": "Treball",
-	"Write a prompt suggestion (e.g. Who are you?)": "Escriu una suggerència de prompt (p. ex. Qui ets tu?)",
+	"Whisper (Local)": "Whisper (local)",
+	"Widescreen Mode": "Mode de pantalla ampla",
+	"Workspace": "Espai de treball",
+	"Write a prompt suggestion (e.g. Who are you?)": "Escriu una suggerència d'indicació (p. ex. Qui ets?)",
 	"Write a summary in 50 words that summarizes [topic or keyword].": "Escriu un resum en 50 paraules que resumeixi [tema o paraula clau].",
-	"Yesterday": "Ayer",
+	"Yesterday": "Ahir",
 	"You": "Tu",
-	"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.": "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 clone a base model": "No es pot clonar un model base",
 	"You have no archived conversations.": "No tens converses arxivades.",
 	"You have shared this chat": "Has compartit aquest xat",
 	"You're a helpful assistant.": "Ets un assistent útil.",
 	"You're now logged in.": "Ara estàs connectat.",
-	"Your account status is currently pending activation.": "",
+	"Your account status is currently pending activation.": "El compte està actualment pendent d'activació",
 	"Youtube": "Youtube",
 	"Youtube Loader Settings": "Configuració del carregador de Youtube"
 }

+ 11 - 0
src/lib/i18n/locales/ceb-PH/translation.json

@@ -43,6 +43,7 @@
 	"Allow Chat Deletion": "Tugoti nga mapapas ang mga chat",
 	"Allow non-local voices": "",
 	"Allow User Location": "",
+	"Allow Voice Interruption in Call": "",
 	"alphanumeric characters and hyphens": "alphanumeric nga mga karakter ug hyphen",
 	"Already have an account?": "Naa na kay account ?",
 	"an assistant": "usa ka katabang",
@@ -165,9 +166,13 @@
 	"Deleted {{name}}": "",
 	"Description": "Deskripsyon",
 	"Didn't fully follow instructions": "",
+	"Discover a function": "",
 	"Discover a model": "",
 	"Discover a prompt": "Pagkaplag usa ka prompt",
+	"Discover a tool": "",
+	"Discover, download, and explore custom functions": "",
 	"Discover, download, and explore custom prompts": "Pagdiskubre, pag-download ug pagsuhid sa mga naandan nga pag-aghat",
+	"Discover, download, and explore custom tools": "",
 	"Discover, download, and explore model presets": "Pagdiskobre, pag-download, ug pagsuhid sa mga preset sa template",
 	"Dismissible": "",
 	"Display Emoji in Call": "",
@@ -180,6 +185,7 @@
 	"Don't Allow": "Dili tugotan",
 	"Don't have an account?": "Wala kay account ?",
 	"Don't like the style": "",
+	"Done": "",
 	"Download": "",
 	"Download canceled": "",
 	"Download Database": "I-download ang database",
@@ -312,6 +318,7 @@
 	"Manage Models": "Pagdumala sa mga templates",
 	"Manage Ollama Models": "Pagdumala sa mga modelo sa Ollama",
 	"Manage Pipelines": "",
+	"Manage Valves": "",
 	"March": "",
 	"Max Tokens (num_predict)": "",
 	"Maximum of 3 models can be downloaded simultaneously. Please try again later.": "Ang labing taas nga 3 nga mga disenyo mahimong ma-download nga dungan. ",
@@ -459,10 +466,12 @@
 	"Seed": "Binhi",
 	"Select a base model": "",
 	"Select a engine": "",
+	"Select a function": "",
 	"Select a mode": "Pagpili og mode",
 	"Select a model": "Pagpili og modelo",
 	"Select a pipeline": "",
 	"Select a pipeline url": "",
+	"Select a tool": "",
 	"Select an Ollama instance": "Pagpili usa ka pananglitan sa Ollama",
 	"Select Documents": "",
 	"Select model": "Pagpili og modelo",
@@ -495,6 +504,7 @@
 	"Show Admin Details in Account Pending Overlay": "",
 	"Show Model": "",
 	"Show shortcuts": "Ipakita ang mga shortcut",
+	"Show your support!": "",
 	"Showcased creativity": "",
 	"sidebar": "lateral bar",
 	"Sign in": "Para maka log in",
@@ -583,6 +593,7 @@
 	"Users": "Mga tiggamit",
 	"Utilize": "Sa paggamit",
 	"Valid time units:": "Balido nga mga yunit sa oras:",
+	"Valves": "",
 	"variable": "variable",
 	"variable to have them replaced with clipboard content.": "variable aron pulihan kini sa mga sulud sa clipboard.",
 	"Version": "Bersyon",

+ 11 - 0
src/lib/i18n/locales/de-DE/translation.json

@@ -43,6 +43,7 @@
 	"Allow Chat Deletion": "Chat Löschung erlauben",
 	"Allow non-local voices": "Nicht-lokale Stimmen erlauben",
 	"Allow User Location": "",
+	"Allow Voice Interruption in Call": "",
 	"alphanumeric characters and hyphens": "alphanumerische Zeichen und Bindestriche",
 	"Already have an account?": "Hast du vielleicht schon ein Account?",
 	"an assistant": "ein Assistent",
@@ -165,9 +166,13 @@
 	"Deleted {{name}}": "Gelöscht {{name}}",
 	"Description": "Beschreibung",
 	"Didn't fully follow instructions": "Nicht genau den Answeisungen gefolgt",
+	"Discover a function": "",
 	"Discover a model": "Entdecken Sie ein Modell",
 	"Discover a prompt": "Einen Prompt entdecken",
+	"Discover a tool": "",
+	"Discover, download, and explore custom functions": "",
 	"Discover, download, and explore custom prompts": "Benutzerdefinierte Prompts entdecken, herunterladen und erkunden",
+	"Discover, download, and explore custom tools": "",
 	"Discover, download, and explore model presets": "Modellvorgaben entdecken, herunterladen und erkunden",
 	"Dismissible": "ausblendbar",
 	"Display Emoji in Call": "",
@@ -180,6 +185,7 @@
 	"Don't Allow": "Nicht erlauben",
 	"Don't have an account?": "Hast du vielleicht noch kein Konto?",
 	"Don't like the style": "Dir gefällt der Style nicht",
+	"Done": "",
 	"Download": "Herunterladen",
 	"Download canceled": "Download abgebrochen",
 	"Download Database": "Datenbank herunterladen",
@@ -312,6 +318,7 @@
 	"Manage Models": "Modelle verwalten",
 	"Manage Ollama Models": "Ollama-Modelle verwalten",
 	"Manage Pipelines": "Verwalten von Pipelines",
+	"Manage Valves": "",
 	"March": "März",
 	"Max Tokens (num_predict)": "Max. Token (num_predict)",
 	"Maximum of 3 models can be downloaded simultaneously. Please try again later.": "Es können maximal 3 Modelle gleichzeitig heruntergeladen werden. Bitte versuche es später erneut.",
@@ -459,10 +466,12 @@
 	"Seed": "Seed",
 	"Select a base model": "Wählen Sie ein Basismodell",
 	"Select a engine": "Wähle eine Engine",
+	"Select a function": "",
 	"Select a mode": "Einen Modus auswählen",
 	"Select a model": "Ein Modell auswählen",
 	"Select a pipeline": "Wählen Sie eine Pipeline aus",
 	"Select a pipeline url": "Auswählen einer Pipeline-URL",
+	"Select a tool": "",
 	"Select an Ollama instance": "Eine Ollama Instanz auswählen",
 	"Select Documents": "",
 	"Select model": "Modell auswählen",
@@ -495,6 +504,7 @@
 	"Show Admin Details in Account Pending Overlay": "Admin-Details im Account-Pending-Overlay anzeigen",
 	"Show Model": "",
 	"Show shortcuts": "Verknüpfungen anzeigen",
+	"Show your support!": "",
 	"Showcased creativity": "Kreativität zur Schau gestellt",
 	"sidebar": "Seitenleiste",
 	"Sign in": "Anmelden",
@@ -583,6 +593,7 @@
 	"Users": "Benutzer",
 	"Utilize": "Nutze die",
 	"Valid time units:": "Gültige Zeiteinheiten:",
+	"Valves": "",
 	"variable": "Variable",
 	"variable to have them replaced with clipboard content.": "Variable, um den Inhalt der Zwischenablage beim Nutzen des Prompts zu ersetzen.",
 	"Version": "Version",

+ 11 - 0
src/lib/i18n/locales/dg-DG/translation.json

@@ -43,6 +43,7 @@
 	"Allow Chat Deletion": "Allow Delete Chats",
 	"Allow non-local voices": "",
 	"Allow User Location": "",
+	"Allow Voice Interruption in Call": "",
 	"alphanumeric characters and hyphens": "so alpha, many hyphen",
 	"Already have an account?": "Such account exists?",
 	"an assistant": "such assistant",
@@ -165,9 +166,13 @@
 	"Deleted {{name}}": "",
 	"Description": "Description",
 	"Didn't fully follow instructions": "",
+	"Discover a function": "",
 	"Discover a model": "",
 	"Discover a prompt": "Discover a prompt",
+	"Discover a tool": "",
+	"Discover, download, and explore custom functions": "",
 	"Discover, download, and explore custom prompts": "Discover, download, and explore custom prompts",
+	"Discover, download, and explore custom tools": "",
 	"Discover, download, and explore model presets": "Discover, download, and explore model presets",
 	"Dismissible": "",
 	"Display Emoji in Call": "",
@@ -180,6 +185,7 @@
 	"Don't Allow": "Don't Allow",
 	"Don't have an account?": "No account? Much sad.",
 	"Don't like the style": "",
+	"Done": "",
 	"Download": "",
 	"Download canceled": "",
 	"Download Database": "Download Database",
@@ -312,6 +318,7 @@
 	"Manage Models": "Manage Wowdels",
 	"Manage Ollama Models": "Manage Ollama Wowdels",
 	"Manage Pipelines": "",
+	"Manage Valves": "",
 	"March": "",
 	"Max Tokens (num_predict)": "",
 	"Maximum of 3 models can be downloaded simultaneously. Please try again later.": "Maximum of 3 models can be downloaded simultaneously. Please try again later.",
@@ -461,10 +468,12 @@
 	"Seed": "Seed very plant",
 	"Select a base model": "",
 	"Select a engine": "",
+	"Select a function": "",
 	"Select a mode": "Select a mode very choose",
 	"Select a model": "Select a model much choice",
 	"Select a pipeline": "",
 	"Select a pipeline url": "",
+	"Select a tool": "",
 	"Select an Ollama instance": "Select an Ollama instance very choose",
 	"Select Documents": "",
 	"Select model": "Select model much choice",
@@ -497,6 +506,7 @@
 	"Show Admin Details in Account Pending Overlay": "",
 	"Show Model": "",
 	"Show shortcuts": "Show shortcuts much shortcut",
+	"Show your support!": "",
 	"Showcased creativity": "",
 	"sidebar": "sidebar much side",
 	"Sign in": "Sign in very sign",
@@ -585,6 +595,7 @@
 	"Users": "Users much users",
 	"Utilize": "Utilize very use",
 	"Valid time units:": "Valid time units: much time",
+	"Valves": "",
 	"variable": "variable very variable",
 	"variable to have them replaced with clipboard content.": "variable to have them replaced with clipboard content. Very replace.",
 	"Version": "Version much version",

+ 11 - 0
src/lib/i18n/locales/en-GB/translation.json

@@ -43,6 +43,7 @@
 	"Allow Chat Deletion": "",
 	"Allow non-local voices": "",
 	"Allow User Location": "",
+	"Allow Voice Interruption in Call": "",
 	"alphanumeric characters and hyphens": "",
 	"Already have an account?": "",
 	"an assistant": "",
@@ -165,9 +166,13 @@
 	"Deleted {{name}}": "",
 	"Description": "",
 	"Didn't fully follow instructions": "",
+	"Discover a function": "",
 	"Discover a model": "",
 	"Discover a prompt": "",
+	"Discover a tool": "",
+	"Discover, download, and explore custom functions": "",
 	"Discover, download, and explore custom prompts": "",
+	"Discover, download, and explore custom tools": "",
 	"Discover, download, and explore model presets": "",
 	"Dismissible": "",
 	"Display Emoji in Call": "",
@@ -180,6 +185,7 @@
 	"Don't Allow": "",
 	"Don't have an account?": "",
 	"Don't like the style": "",
+	"Done": "",
 	"Download": "",
 	"Download canceled": "",
 	"Download Database": "",
@@ -312,6 +318,7 @@
 	"Manage Models": "",
 	"Manage Ollama Models": "",
 	"Manage Pipelines": "",
+	"Manage Valves": "",
 	"March": "",
 	"Max Tokens (num_predict)": "",
 	"Maximum of 3 models can be downloaded simultaneously. Please try again later.": "",
@@ -459,10 +466,12 @@
 	"Seed": "",
 	"Select a base model": "",
 	"Select a engine": "",
+	"Select a function": "",
 	"Select a mode": "",
 	"Select a model": "",
 	"Select a pipeline": "",
 	"Select a pipeline url": "",
+	"Select a tool": "",
 	"Select an Ollama instance": "",
 	"Select Documents": "",
 	"Select model": "",
@@ -495,6 +504,7 @@
 	"Show Admin Details in Account Pending Overlay": "",
 	"Show Model": "",
 	"Show shortcuts": "",
+	"Show your support!": "",
 	"Showcased creativity": "",
 	"sidebar": "",
 	"Sign in": "",
@@ -583,6 +593,7 @@
 	"Users": "",
 	"Utilize": "",
 	"Valid time units:": "",
+	"Valves": "",
 	"variable": "",
 	"variable to have them replaced with clipboard content.": "",
 	"Version": "",

+ 11 - 0
src/lib/i18n/locales/en-US/translation.json

@@ -43,6 +43,7 @@
 	"Allow Chat Deletion": "",
 	"Allow non-local voices": "",
 	"Allow User Location": "",
+	"Allow Voice Interruption in Call": "",
 	"alphanumeric characters and hyphens": "",
 	"Already have an account?": "",
 	"an assistant": "",
@@ -165,9 +166,13 @@
 	"Deleted {{name}}": "",
 	"Description": "",
 	"Didn't fully follow instructions": "",
+	"Discover a function": "",
 	"Discover a model": "",
 	"Discover a prompt": "",
+	"Discover a tool": "",
+	"Discover, download, and explore custom functions": "",
 	"Discover, download, and explore custom prompts": "",
+	"Discover, download, and explore custom tools": "",
 	"Discover, download, and explore model presets": "",
 	"Dismissible": "",
 	"Display Emoji in Call": "",
@@ -180,6 +185,7 @@
 	"Don't Allow": "",
 	"Don't have an account?": "",
 	"Don't like the style": "",
+	"Done": "",
 	"Download": "",
 	"Download canceled": "",
 	"Download Database": "",
@@ -312,6 +318,7 @@
 	"Manage Models": "",
 	"Manage Ollama Models": "",
 	"Manage Pipelines": "",
+	"Manage Valves": "",
 	"March": "",
 	"Max Tokens (num_predict)": "",
 	"Maximum of 3 models can be downloaded simultaneously. Please try again later.": "",
@@ -459,10 +466,12 @@
 	"Seed": "",
 	"Select a base model": "",
 	"Select a engine": "",
+	"Select a function": "",
 	"Select a mode": "",
 	"Select a model": "",
 	"Select a pipeline": "",
 	"Select a pipeline url": "",
+	"Select a tool": "",
 	"Select an Ollama instance": "",
 	"Select Documents": "",
 	"Select model": "",
@@ -495,6 +504,7 @@
 	"Show Admin Details in Account Pending Overlay": "",
 	"Show Model": "",
 	"Show shortcuts": "",
+	"Show your support!": "",
 	"Showcased creativity": "",
 	"sidebar": "",
 	"Sign in": "",
@@ -583,6 +593,7 @@
 	"Users": "",
 	"Utilize": "",
 	"Valid time units:": "",
+	"Valves": "",
 	"variable": "",
 	"variable to have them replaced with clipboard content.": "",
 	"Version": "",

+ 11 - 0
src/lib/i18n/locales/es-ES/translation.json

@@ -43,6 +43,7 @@
 	"Allow Chat Deletion": "Permitir Borrar Chats",
 	"Allow non-local voices": "",
 	"Allow User Location": "",
+	"Allow Voice Interruption in Call": "",
 	"alphanumeric characters and hyphens": "caracteres alfanuméricos y guiones",
 	"Already have an account?": "¿Ya tienes una cuenta?",
 	"an assistant": "un asistente",
@@ -165,9 +166,13 @@
 	"Deleted {{name}}": "Eliminado {{nombre}}",
 	"Description": "Descripción",
 	"Didn't fully follow instructions": "No siguió las instrucciones",
+	"Discover a function": "",
 	"Discover a model": "Descubrir un modelo",
 	"Discover a prompt": "Descubre un Prompt",
+	"Discover a tool": "",
+	"Discover, download, and explore custom functions": "",
 	"Discover, download, and explore custom prompts": "Descubre, descarga, y explora Prompts personalizados",
+	"Discover, download, and explore custom tools": "",
 	"Discover, download, and explore model presets": "Descubre, descarga y explora ajustes preestablecidos de modelos",
 	"Dismissible": "",
 	"Display Emoji in Call": "",
@@ -180,6 +185,7 @@
 	"Don't Allow": "No Permitir",
 	"Don't have an account?": "¿No tienes una cuenta?",
 	"Don't like the style": "No te gusta el estilo?",
+	"Done": "",
 	"Download": "Descargar",
 	"Download canceled": "Descarga cancelada",
 	"Download Database": "Descarga la Base de Datos",
@@ -312,6 +318,7 @@
 	"Manage Models": "Administrar Modelos",
 	"Manage Ollama Models": "Administrar Modelos Ollama",
 	"Manage Pipelines": "Administrar canalizaciones",
+	"Manage Valves": "",
 	"March": "Marzo",
 	"Max Tokens (num_predict)": "Máximo de fichas (num_predict)",
 	"Maximum of 3 models can be downloaded simultaneously. Please try again later.": "Se pueden descargar un máximo de 3 modelos simultáneamente. Por favor, inténtelo de nuevo más tarde.",
@@ -460,10 +467,12 @@
 	"Seed": "Seed",
 	"Select a base model": "Seleccionar un modelo base",
 	"Select a engine": "",
+	"Select a function": "",
 	"Select a mode": "Selecciona un modo",
 	"Select a model": "Selecciona un modelo",
 	"Select a pipeline": "Selección de una canalización",
 	"Select a pipeline url": "Selección de una dirección URL de canalización",
+	"Select a tool": "",
 	"Select an Ollama instance": "Seleccione una instancia de Ollama",
 	"Select Documents": "",
 	"Select model": "Selecciona un modelo",
@@ -496,6 +505,7 @@
 	"Show Admin Details in Account Pending Overlay": "",
 	"Show Model": "",
 	"Show shortcuts": "Mostrar atajos",
+	"Show your support!": "",
 	"Showcased creativity": "Mostrar creatividad",
 	"sidebar": "barra lateral",
 	"Sign in": "Iniciar sesión",
@@ -584,6 +594,7 @@
 	"Users": "Usuarios",
 	"Utilize": "Utilizar",
 	"Valid time units:": "Unidades válidas de tiempo:",
+	"Valves": "",
 	"variable": "variable",
 	"variable to have them replaced with clipboard content.": "variable para reemplazarlos con el contenido del portapapeles.",
 	"Version": "Versión",

+ 11 - 0
src/lib/i18n/locales/fa-IR/translation.json

@@ -43,6 +43,7 @@
 	"Allow Chat Deletion": "اجازه حذف گپ",
 	"Allow non-local voices": "",
 	"Allow User Location": "",
+	"Allow Voice Interruption in Call": "",
 	"alphanumeric characters and hyphens": "حروف الفبایی و خط فاصله",
 	"Already have an account?": "از قبل حساب کاربری دارید؟",
 	"an assistant": "یک دستیار",
@@ -165,9 +166,13 @@
 	"Deleted {{name}}": "حذف شده {{name}}",
 	"Description": "توضیحات",
 	"Didn't fully follow instructions": "نمی تواند دستورالعمل را کامل پیگیری کند",
+	"Discover a function": "",
 	"Discover a model": "کشف یک مدل",
 	"Discover a prompt": "یک اعلان را کشف کنید",
+	"Discover a tool": "",
+	"Discover, download, and explore custom functions": "",
 	"Discover, download, and explore custom prompts": "پرامپت\u200cهای سفارشی را کشف، دانلود و کاوش کنید",
+	"Discover, download, and explore custom tools": "",
 	"Discover, download, and explore model presets": "پیش تنظیمات مدل را کشف، دانلود و کاوش کنید",
 	"Dismissible": "",
 	"Display Emoji in Call": "",
@@ -180,6 +185,7 @@
 	"Don't Allow": "اجازه نده",
 	"Don't have an account?": "حساب کاربری ندارید؟",
 	"Don't like the style": "نظری ندارید؟",
+	"Done": "",
 	"Download": "دانلود کن",
 	"Download canceled": "دانلود لغو شد",
 	"Download Database": "دانلود پایگاه داده",
@@ -312,6 +318,7 @@
 	"Manage Models": "مدیریت مدل\u200cها",
 	"Manage Ollama Models": "مدیریت مدل\u200cهای اولاما",
 	"Manage Pipelines": "مدیریت خطوط لوله",
+	"Manage Valves": "",
 	"March": "مارچ",
 	"Max Tokens (num_predict)": "توکنهای بیشینه (num_predict)",
 	"Maximum of 3 models can be downloaded simultaneously. Please try again later.": "حداکثر 3 مدل را می توان به طور همزمان دانلود کرد. لطفاً بعداً دوباره امتحان کنید.",
@@ -459,10 +466,12 @@
 	"Seed": "Seed",
 	"Select a base model": "انتخاب یک مدل پایه",
 	"Select a engine": "",
+	"Select a function": "",
 	"Select a mode": "یک حالت انتخاب کنید",
 	"Select a model": "انتخاب یک مدل",
 	"Select a pipeline": "انتخاب یک خط لوله",
 	"Select a pipeline url": "یک ادرس خط لوله را انتخاب کنید",
+	"Select a tool": "",
 	"Select an Ollama instance": "انتخاب یک نمونه از اولاما",
 	"Select Documents": "",
 	"Select model": "انتخاب یک مدل",
@@ -495,6 +504,7 @@
 	"Show Admin Details in Account Pending Overlay": "",
 	"Show Model": "",
 	"Show shortcuts": "نمایش میانبرها",
+	"Show your support!": "",
 	"Showcased creativity": "ایده\u200cآفرینی",
 	"sidebar": "نوار کناری",
 	"Sign in": "ورود",
@@ -583,6 +593,7 @@
 	"Users": "کاربران",
 	"Utilize": "استفاده کنید",
 	"Valid time units:": "واحدهای زمانی معتبر:",
+	"Valves": "",
 	"variable": "متغیر",
 	"variable to have them replaced with clipboard content.": "متغیر برای جایگزینی آنها با محتوای کلیپ بورد.",
 	"Version": "نسخه",

+ 11 - 0
src/lib/i18n/locales/fi-FI/translation.json

@@ -43,6 +43,7 @@
 	"Allow Chat Deletion": "Salli keskustelujen poisto",
 	"Allow non-local voices": "",
 	"Allow User Location": "",
+	"Allow Voice Interruption in Call": "",
 	"alphanumeric characters and hyphens": "kirjaimia, numeroita ja väliviivoja",
 	"Already have an account?": "Onko sinulla jo tili?",
 	"an assistant": "avustaja",
@@ -165,9 +166,13 @@
 	"Deleted {{name}}": "Poistettu {{nimi}}",
 	"Description": "Kuvaus",
 	"Didn't fully follow instructions": "Ei noudattanut ohjeita täysin",
+	"Discover a function": "",
 	"Discover a model": "Tutustu malliin",
 	"Discover a prompt": "Löydä kehote",
+	"Discover a tool": "",
+	"Discover, download, and explore custom functions": "",
 	"Discover, download, and explore custom prompts": "Löydä ja lataa mukautettuja kehotteita",
+	"Discover, download, and explore custom tools": "",
 	"Discover, download, and explore model presets": "Löydä ja lataa mallien esiasetuksia",
 	"Dismissible": "",
 	"Display Emoji in Call": "",
@@ -180,6 +185,7 @@
 	"Don't Allow": "Älä salli",
 	"Don't have an account?": "Eikö sinulla ole tiliä?",
 	"Don't like the style": "En pidä tyylistä",
+	"Done": "",
 	"Download": "Lataa",
 	"Download canceled": "Lataus peruutettu",
 	"Download Database": "Lataa tietokanta",
@@ -312,6 +318,7 @@
 	"Manage Models": "Hallitse malleja",
 	"Manage Ollama Models": "Hallitse Ollama-malleja",
 	"Manage Pipelines": "Hallitse putkia",
+	"Manage Valves": "",
 	"March": "maaliskuu",
 	"Max Tokens (num_predict)": "Tokenien enimmäismäärä (num_predict)",
 	"Maximum of 3 models can be downloaded simultaneously. Please try again later.": "Enintään 3 mallia voidaan ladata samanaikaisesti. Yritä myöhemmin uudelleen.",
@@ -459,10 +466,12 @@
 	"Seed": "Siemen",
 	"Select a base model": "Valitse perusmalli",
 	"Select a engine": "",
+	"Select a function": "",
 	"Select a mode": "Valitse tila",
 	"Select a model": "Valitse malli",
 	"Select a pipeline": "Valitse putki",
 	"Select a pipeline url": "Valitse putken URL-osoite",
+	"Select a tool": "",
 	"Select an Ollama instance": "Valitse Ollama-instanssi",
 	"Select Documents": "",
 	"Select model": "Valitse malli",
@@ -495,6 +504,7 @@
 	"Show Admin Details in Account Pending Overlay": "",
 	"Show Model": "",
 	"Show shortcuts": "Näytä pikanäppäimet",
+	"Show your support!": "",
 	"Showcased creativity": "Näytti luovuutta",
 	"sidebar": "sivupalkki",
 	"Sign in": "Kirjaudu sisään",
@@ -583,6 +593,7 @@
 	"Users": "Käyttäjät",
 	"Utilize": "Käytä",
 	"Valid time units:": "Kelvolliset aikayksiköt:",
+	"Valves": "",
 	"variable": "muuttuja",
 	"variable to have them replaced with clipboard content.": "muuttuja korvataan leikepöydän sisällöllä.",
 	"Version": "Versio",

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

@@ -43,6 +43,7 @@
 	"Allow Chat Deletion": "Autoriser la suppression des discussions",
 	"Allow non-local voices": "",
 	"Allow User Location": "",
+	"Allow Voice Interruption in Call": "",
 	"alphanumeric characters and hyphens": "caractères alphanumériques et tirets",
 	"Already have an account?": "Vous avez déjà un compte ?",
 	"an assistant": "un assistant",
@@ -165,9 +166,13 @@
 	"Deleted {{name}}": "Supprimé {{nom}}",
 	"Description": "Description",
 	"Didn't fully follow instructions": "Ne suit pas les instructions",
+	"Discover a function": "",
 	"Discover a model": "Découvrez un modèle",
 	"Discover a prompt": "Découvrir un prompt",
+	"Discover a tool": "",
+	"Discover, download, and explore custom functions": "",
 	"Discover, download, and explore custom prompts": "Découvrir, télécharger et explorer des prompts personnalisés",
+	"Discover, download, and explore custom tools": "",
 	"Discover, download, and explore model presets": "Découvrir, télécharger et explorer des préconfigurations de modèles",
 	"Dismissible": "",
 	"Display Emoji in Call": "",
@@ -180,6 +185,7 @@
 	"Don't Allow": "Ne pas autoriser",
 	"Don't have an account?": "Vous n'avez pas de compte ?",
 	"Don't like the style": "Vous n'aimez pas le style ?",
+	"Done": "",
 	"Download": "Télécharger",
 	"Download canceled": "Téléchargement annulé",
 	"Download Database": "Télécharger la base de données",
@@ -312,6 +318,7 @@
 	"Manage Models": "Gérer les modèles",
 	"Manage Ollama Models": "Gérer les modèles Ollama",
 	"Manage Pipelines": "Gérer les pipelines",
+	"Manage Valves": "",
 	"March": "Mars",
 	"Max Tokens (num_predict)": "Max Tokens (num_predict)",
 	"Maximum of 3 models can be downloaded simultaneously. Please try again later.": "Un maximum de 3 modèles peut être téléchargé simultanément. Veuillez réessayer plus tard.",
@@ -460,10 +467,12 @@
 	"Seed": "Graine",
 	"Select a base model": "Sélectionner un modèle de base",
 	"Select a engine": "",
+	"Select a function": "",
 	"Select a mode": "Sélectionnez un mode",
 	"Select a model": "Sélectionnez un modèle",
 	"Select a pipeline": "Sélectionner un pipeline",
 	"Select a pipeline url": "Sélectionnez une URL de pipeline",
+	"Select a tool": "",
 	"Select an Ollama instance": "Sélectionner une instance Ollama",
 	"Select Documents": "",
 	"Select model": "Sélectionnez un modèle",
@@ -496,6 +505,7 @@
 	"Show Admin Details in Account Pending Overlay": "",
 	"Show Model": "",
 	"Show shortcuts": "Afficher les raccourcis",
+	"Show your support!": "",
 	"Showcased creativity": "Créativité affichée",
 	"sidebar": "barre latérale",
 	"Sign in": "Se connecter",
@@ -584,6 +594,7 @@
 	"Users": "Utilisateurs",
 	"Utilize": "Utiliser",
 	"Valid time units:": "Unités de temps valides :",
+	"Valves": "",
 	"variable": "variable",
 	"variable to have them replaced with clipboard content.": "variable pour les remplacer par le contenu du presse-papiers.",
 	"Version": "Version",

+ 11 - 0
src/lib/i18n/locales/fr-FR/translation.json

@@ -43,6 +43,7 @@
 	"Allow Chat Deletion": "Autoriser la suppression du chat",
 	"Allow non-local voices": "",
 	"Allow User Location": "",
+	"Allow Voice Interruption in Call": "",
 	"alphanumeric characters and hyphens": "caractères alphanumériques et tirets",
 	"Already have an account?": "Vous avez déjà un compte ?",
 	"an assistant": "un assistant",
@@ -165,9 +166,13 @@
 	"Deleted {{name}}": "{{name}} supprimé",
 	"Description": "Description",
 	"Didn't fully follow instructions": "N'a pas suivi entièrement les instructions",
+	"Discover a function": "",
 	"Discover a model": "Découvrir un modèle",
 	"Discover a prompt": "Découvrir un prompt",
+	"Discover a tool": "",
+	"Discover, download, and explore custom functions": "",
 	"Discover, download, and explore custom prompts": "Découvrir, télécharger et explorer des prompts personnalisés",
+	"Discover, download, and explore custom tools": "",
 	"Discover, download, and explore model presets": "Découvrir, télécharger et explorer des préconfigurations de modèles",
 	"Dismissible": "",
 	"Display Emoji in Call": "",
@@ -180,6 +185,7 @@
 	"Don't Allow": "Ne pas autoriser",
 	"Don't have an account?": "Vous n'avez pas de compte ?",
 	"Don't like the style": "N'aime pas le style",
+	"Done": "",
 	"Download": "Télécharger",
 	"Download canceled": "Téléchargement annulé",
 	"Download Database": "Télécharger la base de données",
@@ -312,6 +318,7 @@
 	"Manage Models": "Gérer les modèles",
 	"Manage Ollama Models": "Gérer les modèles Ollama",
 	"Manage Pipelines": "Gérer les pipelines",
+	"Manage Valves": "",
 	"March": "Mars",
 	"Max Tokens (num_predict)": "Tokens maximaux (num_predict)",
 	"Maximum of 3 models can be downloaded simultaneously. Please try again later.": "Un maximum de 3 modèles peut être téléchargé simultanément. Veuillez réessayer plus tard.",
@@ -460,10 +467,12 @@
 	"Seed": "Graine",
 	"Select a base model": "Sélectionner un modèle de base",
 	"Select a engine": "",
+	"Select a function": "",
 	"Select a mode": "Sélectionner un mode",
 	"Select a model": "Sélectionner un modèle",
 	"Select a pipeline": "Sélectionner un pipeline",
 	"Select a pipeline url": "Sélectionnez une URL de pipeline",
+	"Select a tool": "",
 	"Select an Ollama instance": "Sélectionner une instance Ollama",
 	"Select Documents": "",
 	"Select model": "Sélectionner un modèle",
@@ -496,6 +505,7 @@
 	"Show Admin Details in Account Pending Overlay": "",
 	"Show Model": "",
 	"Show shortcuts": "Afficher les raccourcis",
+	"Show your support!": "",
 	"Showcased creativity": "Créativité affichée",
 	"sidebar": "barre latérale",
 	"Sign in": "Se connecter",
@@ -584,6 +594,7 @@
 	"Users": "Utilisateurs",
 	"Utilize": "Utiliser",
 	"Valid time units:": "Unités de temps valides :",
+	"Valves": "",
 	"variable": "variable",
 	"variable to have them replaced with clipboard content.": "variable pour les remplacer par le contenu du presse-papiers.",
 	"Version": "Version",

+ 11 - 0
src/lib/i18n/locales/he-IL/translation.json

@@ -43,6 +43,7 @@
 	"Allow Chat Deletion": "אפשר מחיקת צ'אט",
 	"Allow non-local voices": "",
 	"Allow User Location": "",
+	"Allow Voice Interruption in Call": "",
 	"alphanumeric characters and hyphens": "תווים אלפאנומריים ומקפים",
 	"Already have an account?": "כבר יש לך חשבון?",
 	"an assistant": "עוזר",
@@ -165,9 +166,13 @@
 	"Deleted {{name}}": "נמחק {{name}}",
 	"Description": "תיאור",
 	"Didn't fully follow instructions": "לא עקב אחרי ההוראות באופן מלא",
+	"Discover a function": "",
 	"Discover a model": "גלה מודל",
 	"Discover a prompt": "גלה פקודה",
+	"Discover a tool": "",
+	"Discover, download, and explore custom functions": "",
 	"Discover, download, and explore custom prompts": "גלה, הורד, וחקור פקודות מותאמות אישית",
+	"Discover, download, and explore custom tools": "",
 	"Discover, download, and explore model presets": "גלה, הורד, וחקור הגדרות מודל מוגדרות מראש",
 	"Dismissible": "",
 	"Display Emoji in Call": "",
@@ -180,6 +185,7 @@
 	"Don't Allow": "אל תאפשר",
 	"Don't have an account?": "אין לך חשבון?",
 	"Don't like the style": "לא אוהב את הסגנון",
+	"Done": "",
 	"Download": "הורד",
 	"Download canceled": "ההורדה בוטלה",
 	"Download Database": "הורד מסד נתונים",
@@ -312,6 +318,7 @@
 	"Manage Models": "נהל מודלים",
 	"Manage Ollama Models": "נהל מודלים של Ollama",
 	"Manage Pipelines": "ניהול צינורות",
+	"Manage Valves": "",
 	"March": "מרץ",
 	"Max Tokens (num_predict)": "מקסימום אסימונים (num_predict)",
 	"Maximum of 3 models can be downloaded simultaneously. Please try again later.": "ניתן להוריד מקסימום 3 מודלים בו זמנית. אנא נסה שוב מאוחר יותר.",
@@ -460,10 +467,12 @@
 	"Seed": "זרע",
 	"Select a base model": "בחירת מודל בסיס",
 	"Select a engine": "",
+	"Select a function": "",
 	"Select a mode": "בחר מצב",
 	"Select a model": "בחר מודל",
 	"Select a pipeline": "בחר קו צינור",
 	"Select a pipeline url": "בחר כתובת URL של קו צינור",
+	"Select a tool": "",
 	"Select an Ollama instance": "בחר מופע של Ollama",
 	"Select Documents": "",
 	"Select model": "בחר מודל",
@@ -496,6 +505,7 @@
 	"Show Admin Details in Account Pending Overlay": "",
 	"Show Model": "",
 	"Show shortcuts": "הצג קיצורי דרך",
+	"Show your support!": "",
 	"Showcased creativity": "הצגת יצירתיות",
 	"sidebar": "סרגל צד",
 	"Sign in": "הירשם",
@@ -584,6 +594,7 @@
 	"Users": "משתמשים",
 	"Utilize": "שימוש",
 	"Valid time units:": "יחידות זמן תקינות:",
+	"Valves": "",
 	"variable": "משתנה",
 	"variable to have them replaced with clipboard content.": "משתנה להחליפו ב- clipboard תוכן.",
 	"Version": "גרסה",

+ 11 - 0
src/lib/i18n/locales/hi-IN/translation.json

@@ -43,6 +43,7 @@
 	"Allow Chat Deletion": "चैट हटाने की अनुमति दें",
 	"Allow non-local voices": "",
 	"Allow User Location": "",
+	"Allow Voice Interruption in Call": "",
 	"alphanumeric characters and hyphens": "अल्फ़ान्यूमेरिक वर्ण और हाइफ़न",
 	"Already have an account?": "क्या आपके पास पहले से एक खाता मौजूद है?",
 	"an assistant": "एक सहायक",
@@ -165,9 +166,13 @@
 	"Deleted {{name}}": "{{name}} हटा दिया गया",
 	"Description": "विवरण",
 	"Didn't fully follow instructions": "निर्देशों का पूरी तरह से पालन नहीं किया",
+	"Discover a function": "",
 	"Discover a model": "एक मॉडल की खोज करें",
 	"Discover a prompt": "प्रॉम्प्ट खोजें",
+	"Discover a tool": "",
+	"Discover, download, and explore custom functions": "",
 	"Discover, download, and explore custom prompts": "कस्टम प्रॉम्प्ट को खोजें, डाउनलोड करें और एक्सप्लोर करें",
+	"Discover, download, and explore custom tools": "",
 	"Discover, download, and explore model presets": "मॉडल प्रीसेट खोजें, डाउनलोड करें और एक्सप्लोर करें",
 	"Dismissible": "",
 	"Display Emoji in Call": "",
@@ -180,6 +185,7 @@
 	"Don't Allow": "अनुमति न दें",
 	"Don't have an account?": "कोई खाता नहीं है?",
 	"Don't like the style": "शैली पसंद नहीं है",
+	"Done": "",
 	"Download": "डाउनलोड",
 	"Download canceled": "डाउनलोड रद्द किया गया",
 	"Download Database": "डेटाबेस डाउनलोड करें",
@@ -312,6 +318,7 @@
 	"Manage Models": "मॉडल प्रबंधित करें",
 	"Manage Ollama Models": "Ollama मॉडल प्रबंधित करें",
 	"Manage Pipelines": "पाइपलाइनों का प्रबंधन करें",
+	"Manage Valves": "",
 	"March": "मार्च",
 	"Max Tokens (num_predict)": "अधिकतम टोकन (num_predict)",
 	"Maximum of 3 models can be downloaded simultaneously. Please try again later.": "अधिकतम 3 मॉडल एक साथ डाउनलोड किये जा सकते हैं। कृपया बाद में पुन: प्रयास करें।",
@@ -459,10 +466,12 @@
 	"Seed": "सीड्\u200c",
 	"Select a base model": "एक आधार मॉडल का चयन करें",
 	"Select a engine": "",
+	"Select a function": "",
 	"Select a mode": "एक मोड चुनें",
 	"Select a model": "एक मॉडल चुनें",
 	"Select a pipeline": "एक पाइपलाइन का चयन करें",
 	"Select a pipeline url": "एक पाइपलाइन url चुनें",
+	"Select a tool": "",
 	"Select an Ollama instance": "एक Ollama Instance चुनें",
 	"Select Documents": "",
 	"Select model": "मॉडल चुनें",
@@ -495,6 +504,7 @@
 	"Show Admin Details in Account Pending Overlay": "",
 	"Show Model": "",
 	"Show shortcuts": "शॉर्टकट दिखाएँ",
+	"Show your support!": "",
 	"Showcased creativity": "रचनात्मकता का प्रदर्शन किया",
 	"sidebar": "साइड बार",
 	"Sign in": "साइन इन",
@@ -583,6 +593,7 @@
 	"Users": "उपयोगकर्ताओं",
 	"Utilize": "उपयोग करें",
 	"Valid time units:": "मान्य समय इकाइयाँ:",
+	"Valves": "",
 	"variable": "वेरिएबल",
 	"variable to have them replaced with clipboard content.": "उन्हें क्लिपबोर्ड सामग्री से बदलने के लिए वेरिएबल।",
 	"Version": "संस्करण",

+ 11 - 0
src/lib/i18n/locales/hr-HR/translation.json

@@ -43,6 +43,7 @@
 	"Allow Chat Deletion": "Dopusti brisanje razgovora",
 	"Allow non-local voices": "Dopusti nelokalne glasove",
 	"Allow User Location": "",
+	"Allow Voice Interruption in Call": "",
 	"alphanumeric characters and hyphens": "alfanumerički znakovi i crtice",
 	"Already have an account?": "Već imate račun?",
 	"an assistant": "asistent",
@@ -165,9 +166,13 @@
 	"Deleted {{name}}": "Izbrisano {{name}}",
 	"Description": "Opis",
 	"Didn't fully follow instructions": "Nije u potpunosti slijedio upute",
+	"Discover a function": "",
 	"Discover a model": "Otkrijte model",
 	"Discover a prompt": "Otkrijte prompt",
+	"Discover a tool": "",
+	"Discover, download, and explore custom functions": "",
 	"Discover, download, and explore custom prompts": "Otkrijte, preuzmite i istražite prilagođene prompte",
+	"Discover, download, and explore custom tools": "",
 	"Discover, download, and explore model presets": "Otkrijte, preuzmite i istražite unaprijed postavljene modele",
 	"Dismissible": "Odbaciti",
 	"Display Emoji in Call": "",
@@ -180,6 +185,7 @@
 	"Don't Allow": "Ne dopuštaj",
 	"Don't have an account?": "Nemate račun?",
 	"Don't like the style": "Ne sviđa mi se stil",
+	"Done": "",
 	"Download": "Preuzimanje",
 	"Download canceled": "Preuzimanje otkazano",
 	"Download Database": "Preuzmi bazu podataka",
@@ -312,6 +318,7 @@
 	"Manage Models": "Upravljanje modelima",
 	"Manage Ollama Models": "Upravljanje Ollama modelima",
 	"Manage Pipelines": "Upravljanje cjevovodima",
+	"Manage Valves": "",
 	"March": "Ožujak",
 	"Max Tokens (num_predict)": "Maksimalan broj tokena (num_predict)",
 	"Maximum of 3 models can be downloaded simultaneously. Please try again later.": "Maksimalno 3 modela se mogu preuzeti istovremeno. Pokušajte ponovo kasnije.",
@@ -460,10 +467,12 @@
 	"Seed": "Sjeme",
 	"Select a base model": "Odabir osnovnog modela",
 	"Select a engine": "Odaberite pogon",
+	"Select a function": "",
 	"Select a mode": "Odaberite način",
 	"Select a model": "Odaberite model",
 	"Select a pipeline": "Odabir kanala",
 	"Select a pipeline url": "Odabir URL-a kanala",
+	"Select a tool": "",
 	"Select an Ollama instance": "Odaberite Ollama instancu",
 	"Select Documents": "Odaberite dokumente",
 	"Select model": "Odaberite model",
@@ -496,6 +505,7 @@
 	"Show Admin Details in Account Pending Overlay": "",
 	"Show Model": "",
 	"Show shortcuts": "Pokaži prečace",
+	"Show your support!": "",
 	"Showcased creativity": "Prikazana kreativnost",
 	"sidebar": "bočna traka",
 	"Sign in": "Prijava",
@@ -584,6 +594,7 @@
 	"Users": "Korisnici",
 	"Utilize": "Iskoristi",
 	"Valid time units:": "Važeće vremenske jedinice:",
+	"Valves": "",
 	"variable": "varijabla",
 	"variable to have them replaced with clipboard content.": "varijabla za zamjenu sadržajem međuspremnika.",
 	"Version": "Verzija",

+ 11 - 0
src/lib/i18n/locales/it-IT/translation.json

@@ -43,6 +43,7 @@
 	"Allow Chat Deletion": "Consenti l'eliminazione della chat",
 	"Allow non-local voices": "",
 	"Allow User Location": "",
+	"Allow Voice Interruption in Call": "",
 	"alphanumeric characters and hyphens": "caratteri alfanumerici e trattini",
 	"Already have an account?": "Hai già un account?",
 	"an assistant": "un assistente",
@@ -165,9 +166,13 @@
 	"Deleted {{name}}": "Eliminato {{name}}",
 	"Description": "Descrizione",
 	"Didn't fully follow instructions": "Non ha seguito completamente le istruzioni",
+	"Discover a function": "",
 	"Discover a model": "Scopri un modello",
 	"Discover a prompt": "Scopri un prompt",
+	"Discover a tool": "",
+	"Discover, download, and explore custom functions": "",
 	"Discover, download, and explore custom prompts": "Scopri, scarica ed esplora prompt personalizzati",
+	"Discover, download, and explore custom tools": "",
 	"Discover, download, and explore model presets": "Scopri, scarica ed esplora i preset del modello",
 	"Dismissible": "",
 	"Display Emoji in Call": "",
@@ -180,6 +185,7 @@
 	"Don't Allow": "Non consentire",
 	"Don't have an account?": "Non hai un account?",
 	"Don't like the style": "Non ti piace lo stile",
+	"Done": "",
 	"Download": "Scarica",
 	"Download canceled": "Scaricamento annullato",
 	"Download Database": "Scarica database",
@@ -312,6 +318,7 @@
 	"Manage Models": "Gestisci modelli",
 	"Manage Ollama Models": "Gestisci modelli Ollama",
 	"Manage Pipelines": "Gestire le pipeline",
+	"Manage Valves": "",
 	"March": "Marzo",
 	"Max Tokens (num_predict)": "Numero massimo di gettoni (num_predict)",
 	"Maximum of 3 models can be downloaded simultaneously. Please try again later.": "È possibile scaricare un massimo di 3 modelli contemporaneamente. Riprova più tardi.",
@@ -460,10 +467,12 @@
 	"Seed": "Seme",
 	"Select a base model": "Selezionare un modello di base",
 	"Select a engine": "",
+	"Select a function": "",
 	"Select a mode": "Seleziona una modalità",
 	"Select a model": "Seleziona un modello",
 	"Select a pipeline": "Selezionare una tubazione",
 	"Select a pipeline url": "Selezionare l'URL di una pipeline",
+	"Select a tool": "",
 	"Select an Ollama instance": "Seleziona un'istanza Ollama",
 	"Select Documents": "",
 	"Select model": "Seleziona modello",
@@ -496,6 +505,7 @@
 	"Show Admin Details in Account Pending Overlay": "",
 	"Show Model": "",
 	"Show shortcuts": "Mostra",
+	"Show your support!": "",
 	"Showcased creativity": "Creatività messa in mostra",
 	"sidebar": "barra laterale",
 	"Sign in": "Accedi",
@@ -584,6 +594,7 @@
 	"Users": "Utenti",
 	"Utilize": "Utilizza",
 	"Valid time units:": "Unità di tempo valide:",
+	"Valves": "",
 	"variable": "variabile",
 	"variable to have them replaced with clipboard content.": "variabile per farli sostituire con il contenuto degli appunti.",
 	"Version": "Versione",

+ 11 - 0
src/lib/i18n/locales/ja-JP/translation.json

@@ -43,6 +43,7 @@
 	"Allow Chat Deletion": "チャットの削除を許可",
 	"Allow non-local voices": "",
 	"Allow User Location": "",
+	"Allow Voice Interruption in Call": "",
 	"alphanumeric characters and hyphens": "英数字とハイフン",
 	"Already have an account?": "すでにアカウントをお持ちですか?",
 	"an assistant": "アシスタント",
@@ -165,9 +166,13 @@
 	"Deleted {{name}}": "{{name}}を削除しました",
 	"Description": "説明",
 	"Didn't fully follow instructions": "説明に沿って操作していませんでした",
+	"Discover a function": "",
 	"Discover a model": "モデルを検出する",
 	"Discover a prompt": "プロンプトを見つける",
+	"Discover a tool": "",
+	"Discover, download, and explore custom functions": "",
 	"Discover, download, and explore custom prompts": "カスタムプロンプトを見つけて、ダウンロードして、探索",
+	"Discover, download, and explore custom tools": "",
 	"Discover, download, and explore model presets": "モデルプリセットを見つけて、ダウンロードして、探索",
 	"Dismissible": "",
 	"Display Emoji in Call": "",
@@ -180,6 +185,7 @@
 	"Don't Allow": "許可しない",
 	"Don't have an account?": "アカウントをお持ちではありませんか?",
 	"Don't like the style": "デザインが好きでない",
+	"Done": "",
 	"Download": "ダウンロードをキャンセルしました",
 	"Download canceled": "ダウンロードをキャンセルしました",
 	"Download Database": "データベースをダウンロード",
@@ -312,6 +318,7 @@
 	"Manage Models": "モデルを管理",
 	"Manage Ollama Models": "Ollama モデルを管理",
 	"Manage Pipelines": "パイプラインの管理",
+	"Manage Valves": "",
 	"March": "3月",
 	"Max Tokens (num_predict)": "最大トークン数 (num_predict)",
 	"Maximum of 3 models can be downloaded simultaneously. Please try again later.": "同時にダウンロードできるモデルは最大 3 つです。後でもう一度お試しください。",
@@ -458,10 +465,12 @@
 	"Seed": "シード",
 	"Select a base model": "基本モデルの選択",
 	"Select a engine": "",
+	"Select a function": "",
 	"Select a mode": "モードを選択",
 	"Select a model": "モデルを選択",
 	"Select a pipeline": "パイプラインの選択",
 	"Select a pipeline url": "パイプラインの URL を選択する",
+	"Select a tool": "",
 	"Select an Ollama instance": "Ollama インスタンスを選択",
 	"Select Documents": "",
 	"Select model": "モデルを選択",
@@ -494,6 +503,7 @@
 	"Show Admin Details in Account Pending Overlay": "",
 	"Show Model": "",
 	"Show shortcuts": "表示",
+	"Show your support!": "",
 	"Showcased creativity": "創造性を披露",
 	"sidebar": "サイドバー",
 	"Sign in": "サインイン",
@@ -582,6 +592,7 @@
 	"Users": "ユーザー",
 	"Utilize": "活用",
 	"Valid time units:": "有効な時間単位:",
+	"Valves": "",
 	"variable": "変数",
 	"variable to have them replaced with clipboard content.": "クリップボードの内容に置き換える変数。",
 	"Version": "バージョン",

+ 11 - 0
src/lib/i18n/locales/ka-GE/translation.json

@@ -43,6 +43,7 @@
 	"Allow Chat Deletion": "მიმოწერის წაშლის დაშვება",
 	"Allow non-local voices": "",
 	"Allow User Location": "",
+	"Allow Voice Interruption in Call": "",
 	"alphanumeric characters and hyphens": "ალფანუმერული სიმბოლოები და დეფისები",
 	"Already have an account?": "უკვე გაქვს ანგარიში?",
 	"an assistant": "ასისტენტი",
@@ -165,9 +166,13 @@
 	"Deleted {{name}}": "Deleted {{name}}",
 	"Description": "აღწერა",
 	"Didn't fully follow instructions": "ვერ ყველა ინფორმაციისთვის ვერ ხელახლა ჩაწერე",
+	"Discover a function": "",
 	"Discover a model": "გაიგეთ მოდელი",
 	"Discover a prompt": "აღმოაჩინეთ მოთხოვნა",
+	"Discover a tool": "",
+	"Discover, download, and explore custom functions": "",
 	"Discover, download, and explore custom prompts": "აღმოაჩინეთ, ჩამოტვირთეთ და შეისწავლეთ მორგებული მოთხოვნები",
+	"Discover, download, and explore custom tools": "",
 	"Discover, download, and explore model presets": "აღმოაჩინეთ, ჩამოტვირთეთ და შეისწავლეთ მოდელის წინასწარ პარამეტრები",
 	"Dismissible": "",
 	"Display Emoji in Call": "",
@@ -180,6 +185,7 @@
 	"Don't Allow": "არ დაუშვა",
 	"Don't have an account?": "არ გაქვს ანგარიში?",
 	"Don't like the style": "არ ეთიკურია ფართოდ",
+	"Done": "",
 	"Download": "ჩამოტვირთვა გაუქმებულია",
 	"Download canceled": "ჩამოტვირთვა გაუქმებულია",
 	"Download Database": "გადმოწერე მონაცემთა ბაზა",
@@ -312,6 +318,7 @@
 	"Manage Models": "მოდელების მართვა",
 	"Manage Ollama Models": "Ollama მოდელების მართვა",
 	"Manage Pipelines": "მილსადენების მართვა",
+	"Manage Valves": "",
 	"March": "მარტივი",
 	"Max Tokens (num_predict)": "მაქს ტოკენსი (num_predict)",
 	"Maximum of 3 models can be downloaded simultaneously. Please try again later.": "მაქსიმუმ 3 მოდელის ჩამოტვირთვა შესაძლებელია ერთდროულად. Გთხოვთ სცადოთ მოგვიანებით.",
@@ -459,10 +466,12 @@
 	"Seed": "სიდი",
 	"Select a base model": "აირჩიეთ ბაზის მოდელი",
 	"Select a engine": "",
+	"Select a function": "",
 	"Select a mode": "რეჟიმის არჩევა",
 	"Select a model": "მოდელის არჩევა",
 	"Select a pipeline": "აირჩიეთ მილსადენი",
 	"Select a pipeline url": "აირჩიეთ მილსადენის url",
+	"Select a tool": "",
 	"Select an Ollama instance": "Ollama ინსტანსის არჩევა",
 	"Select Documents": "",
 	"Select model": "მოდელის არჩევა",
@@ -495,6 +504,7 @@
 	"Show Admin Details in Account Pending Overlay": "",
 	"Show Model": "",
 	"Show shortcuts": "მალსახმობების ჩვენება",
+	"Show your support!": "",
 	"Showcased creativity": "ჩვენებული ქონება",
 	"sidebar": "საიდბარი",
 	"Sign in": "ავტორიზაცია",
@@ -583,6 +593,7 @@
 	"Users": "მომხმარებლები",
 	"Utilize": "გამოყენება",
 	"Valid time units:": "მოქმედი დროის ერთეულები",
+	"Valves": "",
 	"variable": "ცვლადი",
 	"variable to have them replaced with clipboard content.": "ცვლადი, რომ შეცვალოს ისინი ბუფერში შიგთავსით.",
 	"Version": "ვერსია",

+ 11 - 0
src/lib/i18n/locales/ko-KR/translation.json

@@ -43,6 +43,7 @@
 	"Allow Chat Deletion": "채팅 삭제 허용",
 	"Allow non-local voices": "외부 음성 허용",
 	"Allow User Location": "사용자 위치 활용 허용",
+	"Allow Voice Interruption in Call": "",
 	"alphanumeric characters and hyphens": "영문자, 숫자, 하이픈",
 	"Already have an account?": "이미 계정이 있으신가요?",
 	"an assistant": "어시스턴트",
@@ -165,9 +166,13 @@
 	"Deleted {{name}}": "{{name}}을(를) 삭제했습니다.",
 	"Description": "설명",
 	"Didn't fully follow instructions": "완전히 지침을 따르지 않음",
+	"Discover a function": "",
 	"Discover a model": "모델 검색",
 	"Discover a prompt": "프롬프트 검색",
+	"Discover a tool": "",
+	"Discover, download, and explore custom functions": "",
 	"Discover, download, and explore custom prompts": "사용자 정의 프롬프트 검색, 다운로드 및 탐색",
+	"Discover, download, and explore custom tools": "",
 	"Discover, download, and explore model presets": "모델 사전 설정 검색, 다운로드 및 탐색",
 	"Dismissible": "제외가능",
 	"Display Emoji in Call": "콜(call)에서 이모지 표시",
@@ -180,6 +185,7 @@
 	"Don't Allow": "허용 안 함",
 	"Don't have an account?": "계정이 없으신가요?",
 	"Don't like the style": "스타일을 좋아하지 않으세요?",
+	"Done": "",
 	"Download": "다운로드",
 	"Download canceled": "다운로드 취소",
 	"Download Database": "데이터베이스 다운로드",
@@ -312,6 +318,7 @@
 	"Manage Models": "모델 관리",
 	"Manage Ollama Models": "Ollama 모델 관리",
 	"Manage Pipelines": "파이프라인 관리",
+	"Manage Valves": "",
 	"March": "3월",
 	"Max Tokens (num_predict)": "최대 토큰(num_predict)",
 	"Maximum of 3 models can be downloaded simultaneously. Please try again later.": "최대 3개의 모델을 동시에 다운로드할 수 있습니다. 나중에 다시 시도하세요.",
@@ -459,10 +466,12 @@
 	"Seed": "시드",
 	"Select a base model": "기본 모델 선택",
 	"Select a engine": "엔진 선택",
+	"Select a function": "",
 	"Select a mode": "모드 선택",
 	"Select a model": "모델 선택",
 	"Select a pipeline": "파이프라인 선택",
 	"Select a pipeline url": "파이프라인 URL 선택",
+	"Select a tool": "",
 	"Select an Ollama instance": "Ollama 인스턴스 선택",
 	"Select Documents": "문서 선택",
 	"Select model": "모델 선택",
@@ -495,6 +504,7 @@
 	"Show Admin Details in Account Pending Overlay": "사용자용 계정 보류 설명창에, 관리자 상세 정보 노출",
 	"Show Model": "",
 	"Show shortcuts": "단축키 보기",
+	"Show your support!": "",
 	"Showcased creativity": "창의성 발휘",
 	"sidebar": "사이드바",
 	"Sign in": "로그인",
@@ -583,6 +593,7 @@
 	"Users": "사용자",
 	"Utilize": "활용",
 	"Valid time units:": "유효 시간 단위:",
+	"Valves": "",
 	"variable": "변수",
 	"variable to have them replaced with clipboard content.": "변수를 사용하여 클립보드 내용으로 바꾸세요.",
 	"Version": "버전",

+ 11 - 0
src/lib/i18n/locales/lt-LT/translation.json

@@ -43,6 +43,7 @@
 	"Allow Chat Deletion": "Leisti pokalbių ištrynimą",
 	"Allow non-local voices": "",
 	"Allow User Location": "",
+	"Allow Voice Interruption in Call": "",
 	"alphanumeric characters and hyphens": "skaičiai, raidės ir brūkšneliai",
 	"Already have an account?": "Ar jau turite paskyrą?",
 	"an assistant": "assistentas",
@@ -165,9 +166,13 @@
 	"Deleted {{name}}": "",
 	"Description": "Aprašymas",
 	"Didn't fully follow instructions": "Pilnai nesekė instrukcijų",
+	"Discover a function": "",
 	"Discover a model": "",
 	"Discover a prompt": "Atrasti užklausas",
+	"Discover a tool": "",
+	"Discover, download, and explore custom functions": "",
 	"Discover, download, and explore custom prompts": "Atrasti ir parsisiųsti užklausas",
+	"Discover, download, and explore custom tools": "",
 	"Discover, download, and explore model presets": "Atrasti ir parsisiųsti modelių konfigūracija",
 	"Dismissible": "",
 	"Display Emoji in Call": "",
@@ -180,6 +185,7 @@
 	"Don't Allow": "Neleisti",
 	"Don't have an account?": "Neturite paskyros?",
 	"Don't like the style": "Nepatinka stilius",
+	"Done": "",
 	"Download": "Parsisiųsti",
 	"Download canceled": "Parsisiuntimas atšauktas",
 	"Download Database": "Parsisiųsti duomenų bazę",
@@ -312,6 +318,7 @@
 	"Manage Models": "Tvarkyti modelius",
 	"Manage Ollama Models": "Tvarkyti Ollama modelius",
 	"Manage Pipelines": "",
+	"Manage Valves": "",
 	"March": "Kovas",
 	"Max Tokens (num_predict)": "",
 	"Maximum of 3 models can be downloaded simultaneously. Please try again later.": "Daugiausiai trys modeliai gali būti parsisiunčiami vienu metu.",
@@ -461,10 +468,12 @@
 	"Seed": "Sėkla",
 	"Select a base model": "",
 	"Select a engine": "",
+	"Select a function": "",
 	"Select a mode": "Pasirinkti režimą",
 	"Select a model": "Pasirinkti modelį",
 	"Select a pipeline": "",
 	"Select a pipeline url": "",
+	"Select a tool": "",
 	"Select an Ollama instance": "Pasirinkti Ollama instanciją",
 	"Select Documents": "",
 	"Select model": "Pasirinkti modelį",
@@ -497,6 +506,7 @@
 	"Show Admin Details in Account Pending Overlay": "",
 	"Show Model": "",
 	"Show shortcuts": "Rodyti trumpinius",
+	"Show your support!": "",
 	"Showcased creativity": "Kūrybingų užklausų paroda",
 	"sidebar": "šoninis meniu",
 	"Sign in": "Prisijungti",
@@ -585,6 +595,7 @@
 	"Users": "Naudotojai",
 	"Utilize": "Naudoti",
 	"Valid time units:": "Teisingūs laiko vienetai :",
+	"Valves": "",
 	"variable": "kintamasis",
 	"variable to have them replaced with clipboard content.": "kintamoji pakeičiama kopijuoklės turiniu.",
 	"Version": "Versija",

+ 11 - 0
src/lib/i18n/locales/nb-NO/translation.json

@@ -43,6 +43,7 @@
 	"Allow Chat Deletion": "Tillat sletting av chatter",
 	"Allow non-local voices": "Tillat ikke-lokale stemmer",
 	"Allow User Location": "",
+	"Allow Voice Interruption in Call": "",
 	"alphanumeric characters and hyphens": "alfanumeriske tegn og bindestreker",
 	"Already have an account?": "Har du allerede en konto?",
 	"an assistant": "en assistent",
@@ -165,9 +166,13 @@
 	"Deleted {{name}}": "Slettet {{name}}",
 	"Description": "Beskrivelse",
 	"Didn't fully follow instructions": "Fulgte ikke instruksjonene fullt ut",
+	"Discover a function": "",
 	"Discover a model": "Oppdag en modell",
 	"Discover a prompt": "Oppdag en prompt",
+	"Discover a tool": "",
+	"Discover, download, and explore custom functions": "",
 	"Discover, download, and explore custom prompts": "Oppdag, last ned og utforsk egendefinerte prompts",
+	"Discover, download, and explore custom tools": "",
 	"Discover, download, and explore model presets": "Oppdag, last ned og utforsk modellforhåndsinnstillinger",
 	"Dismissible": "",
 	"Display Emoji in Call": "",
@@ -180,6 +185,7 @@
 	"Don't Allow": "Ikke tillat",
 	"Don't have an account?": "Har du ikke en konto?",
 	"Don't like the style": "Liker ikke stilen",
+	"Done": "",
 	"Download": "Last ned",
 	"Download canceled": "Nedlasting avbrutt",
 	"Download Database": "Last ned database",
@@ -312,6 +318,7 @@
 	"Manage Models": "Administrer modeller",
 	"Manage Ollama Models": "Administrer Ollama-modeller",
 	"Manage Pipelines": "Administrer pipelines",
+	"Manage Valves": "",
 	"March": "Mars",
 	"Max Tokens (num_predict)": "Maks antall tokens (num_predict)",
 	"Maximum of 3 models can be downloaded simultaneously. Please try again later.": "Maksimalt 3 modeller kan lastes ned samtidig. Vennligst prøv igjen senere.",
@@ -459,10 +466,12 @@
 	"Seed": "Seed",
 	"Select a base model": "Velg en grunnmodell",
 	"Select a engine": "",
+	"Select a function": "",
 	"Select a mode": "Velg en modus",
 	"Select a model": "Velg en modell",
 	"Select a pipeline": "Velg en pipeline",
 	"Select a pipeline url": "Velg en pipeline-URL",
+	"Select a tool": "",
 	"Select an Ollama instance": "Velg en Ollama-instans",
 	"Select Documents": "",
 	"Select model": "Velg modell",
@@ -495,6 +504,7 @@
 	"Show Admin Details in Account Pending Overlay": "Vis administratordetaljer i ventende kontooverlay",
 	"Show Model": "",
 	"Show shortcuts": "Vis snarveier",
+	"Show your support!": "",
 	"Showcased creativity": "Vist frem kreativitet",
 	"sidebar": "sidefelt",
 	"Sign in": "Logg inn",
@@ -583,6 +593,7 @@
 	"Users": "Brukere",
 	"Utilize": "Utnytt",
 	"Valid time units:": "Gyldige tidsenheter:",
+	"Valves": "",
 	"variable": "variabel",
 	"variable to have them replaced with clipboard content.": "variabel for å få dem erstattet med utklippstavleinnhold.",
 	"Version": "Versjon",

+ 11 - 0
src/lib/i18n/locales/nl-NL/translation.json

@@ -43,6 +43,7 @@
 	"Allow Chat Deletion": "Sta Chat Verwijdering toe",
 	"Allow non-local voices": "",
 	"Allow User Location": "",
+	"Allow Voice Interruption in Call": "",
 	"alphanumeric characters and hyphens": "alfanumerieke karakters en streepjes",
 	"Already have an account?": "Heb je al een account?",
 	"an assistant": "een assistent",
@@ -165,9 +166,13 @@
 	"Deleted {{name}}": "{{name}} verwijderd",
 	"Description": "Beschrijving",
 	"Didn't fully follow instructions": "Ik heb niet alle instructies volgt",
+	"Discover a function": "",
 	"Discover a model": "Ontdek een model",
 	"Discover a prompt": "Ontdek een prompt",
+	"Discover a tool": "",
+	"Discover, download, and explore custom functions": "",
 	"Discover, download, and explore custom prompts": "Ontdek, download en verken aangepaste prompts",
+	"Discover, download, and explore custom tools": "",
 	"Discover, download, and explore model presets": "Ontdek, download en verken model presets",
 	"Dismissible": "",
 	"Display Emoji in Call": "",
@@ -180,6 +185,7 @@
 	"Don't Allow": "Niet Toestaan",
 	"Don't have an account?": "Heb je geen account?",
 	"Don't like the style": "Je vindt het stijl niet?",
+	"Done": "",
 	"Download": "Download",
 	"Download canceled": "Download geannuleerd",
 	"Download Database": "Download Database",
@@ -312,6 +318,7 @@
 	"Manage Models": "Beheer Modellen",
 	"Manage Ollama Models": "Beheer Ollama Modellen",
 	"Manage Pipelines": "Pijplijnen beheren",
+	"Manage Valves": "",
 	"March": "Maart",
 	"Max Tokens (num_predict)": "Max Tokens (num_predict)",
 	"Maximum of 3 models can be downloaded simultaneously. Please try again later.": "Maximaal 3 modellen kunnen tegelijkertijd worden gedownload. Probeer het later opnieuw.",
@@ -459,10 +466,12 @@
 	"Seed": "Seed",
 	"Select a base model": "Selecteer een basismodel",
 	"Select a engine": "",
+	"Select a function": "",
 	"Select a mode": "Selecteer een modus",
 	"Select a model": "Selecteer een model",
 	"Select a pipeline": "Selecteer een pijplijn",
 	"Select a pipeline url": "Selecteer een pijplijn-URL",
+	"Select a tool": "",
 	"Select an Ollama instance": "Selecteer een Ollama instantie",
 	"Select Documents": "",
 	"Select model": "Selecteer een model",
@@ -495,6 +504,7 @@
 	"Show Admin Details in Account Pending Overlay": "",
 	"Show Model": "",
 	"Show shortcuts": "Toon snelkoppelingen",
+	"Show your support!": "",
 	"Showcased creativity": "Tooncase creativiteit",
 	"sidebar": "sidebar",
 	"Sign in": "Inloggen",
@@ -583,6 +593,7 @@
 	"Users": "Gebruikers",
 	"Utilize": "Utilize",
 	"Valid time units:": "Geldige tijdseenheden:",
+	"Valves": "",
 	"variable": "variabele",
 	"variable to have them replaced with clipboard content.": "variabele om ze te laten vervangen door klembord inhoud.",
 	"Version": "Versie",

+ 11 - 0
src/lib/i18n/locales/pa-IN/translation.json

@@ -43,6 +43,7 @@
 	"Allow Chat Deletion": "ਗੱਲਬਾਤ ਮਿਟਾਉਣ ਦੀ ਆਗਿਆ ਦਿਓ",
 	"Allow non-local voices": "",
 	"Allow User Location": "",
+	"Allow Voice Interruption in Call": "",
 	"alphanumeric characters and hyphens": "ਅਲਫ਼ਾਨਯੂਮੈਰਿਕ ਅੱਖਰ ਅਤੇ ਹਾਈਫਨ",
 	"Already have an account?": "ਪਹਿਲਾਂ ਹੀ ਖਾਤਾ ਹੈ?",
 	"an assistant": "ਇੱਕ ਸਹਾਇਕ",
@@ -165,9 +166,13 @@
 	"Deleted {{name}}": "ਮਿਟਾ ਦਿੱਤਾ ਗਿਆ {{name}}",
 	"Description": "ਵਰਣਨਾ",
 	"Didn't fully follow instructions": "ਹਦਾਇਤਾਂ ਨੂੰ ਪੂਰੀ ਤਰ੍ਹਾਂ ਫਾਲੋ ਨਹੀਂ ਕੀਤਾ",
+	"Discover a function": "",
 	"Discover a model": "ਇੱਕ ਮਾਡਲ ਲੱਭੋ",
 	"Discover a prompt": "ਇੱਕ ਪ੍ਰੰਪਟ ਖੋਜੋ",
+	"Discover a tool": "",
+	"Discover, download, and explore custom functions": "",
 	"Discover, download, and explore custom prompts": "ਕਸਟਮ ਪ੍ਰੰਪਟਾਂ ਨੂੰ ਖੋਜੋ, ਡਾਊਨਲੋਡ ਕਰੋ ਅਤੇ ਪੜਚੋਲ ਕਰੋ",
+	"Discover, download, and explore custom tools": "",
 	"Discover, download, and explore model presets": "ਮਾਡਲ ਪ੍ਰੀਸੈਟਾਂ ਨੂੰ ਖੋਜੋ, ਡਾਊਨਲੋਡ ਕਰੋ ਅਤੇ ਪੜਚੋਲ ਕਰੋ",
 	"Dismissible": "",
 	"Display Emoji in Call": "",
@@ -180,6 +185,7 @@
 	"Don't Allow": "ਆਗਿਆ ਨਾ ਦਿਓ",
 	"Don't have an account?": "ਖਾਤਾ ਨਹੀਂ ਹੈ?",
 	"Don't like the style": "ਸਟਾਈਲ ਪਸੰਦ ਨਹੀਂ ਹੈ",
+	"Done": "",
 	"Download": "ਡਾਊਨਲੋਡ",
 	"Download canceled": "ਡਾਊਨਲੋਡ ਰੱਦ ਕੀਤਾ ਗਿਆ",
 	"Download Database": "ਡਾਟਾਬੇਸ ਡਾਊਨਲੋਡ ਕਰੋ",
@@ -312,6 +318,7 @@
 	"Manage Models": "ਮਾਡਲਾਂ ਦਾ ਪ੍ਰਬੰਧਨ ਕਰੋ",
 	"Manage Ollama Models": "ਓਲਾਮਾ ਮਾਡਲਾਂ ਦਾ ਪ੍ਰਬੰਧਨ ਕਰੋ",
 	"Manage Pipelines": "ਪਾਈਪਲਾਈਨਾਂ ਦਾ ਪ੍ਰਬੰਧਨ ਕਰੋ",
+	"Manage Valves": "",
 	"March": "ਮਾਰਚ",
 	"Max Tokens (num_predict)": "ਮੈਕਸ ਟੋਕਨ (num_predict)",
 	"Maximum of 3 models can be downloaded simultaneously. Please try again later.": "ਇੱਕ ਸਮੇਂ ਵਿੱਚ ਵੱਧ ਤੋਂ ਵੱਧ 3 ਮਾਡਲ ਡਾਊਨਲੋਡ ਕੀਤੇ ਜਾ ਸਕਦੇ ਹਨ। ਕਿਰਪਾ ਕਰਕੇ ਬਾਅਦ ਵਿੱਚ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ।",
@@ -459,10 +466,12 @@
 	"Seed": "ਬੀਜ",
 	"Select a base model": "ਆਧਾਰ ਮਾਡਲ ਚੁਣੋ",
 	"Select a engine": "",
+	"Select a function": "",
 	"Select a mode": "ਇੱਕ ਮੋਡ ਚੁਣੋ",
 	"Select a model": "ਇੱਕ ਮਾਡਲ ਚੁਣੋ",
 	"Select a pipeline": "ਪਾਈਪਲਾਈਨ ਚੁਣੋ",
 	"Select a pipeline url": "ਪਾਈਪਲਾਈਨ URL ਚੁਣੋ",
+	"Select a tool": "",
 	"Select an Ollama instance": "ਇੱਕ ਓਲਾਮਾ ਇੰਸਟੈਂਸ ਚੁਣੋ",
 	"Select Documents": "",
 	"Select model": "ਮਾਡਲ ਚੁਣੋ",
@@ -495,6 +504,7 @@
 	"Show Admin Details in Account Pending Overlay": "",
 	"Show Model": "",
 	"Show shortcuts": "ਸ਼ਾਰਟਕਟ ਦਿਖਾਓ",
+	"Show your support!": "",
 	"Showcased creativity": "ਸਿਰਜਣਾਤਮਕਤਾ ਦਿਖਾਈ",
 	"sidebar": "ਸਾਈਡਬਾਰ",
 	"Sign in": "ਸਾਈਨ ਇਨ ਕਰੋ",
@@ -583,6 +593,7 @@
 	"Users": "ਉਪਭੋਗਤਾ",
 	"Utilize": "ਵਰਤੋਂ",
 	"Valid time units:": "ਵੈਧ ਸਮਾਂ ਇਕਾਈਆਂ:",
+	"Valves": "",
 	"variable": "ਵੈਰੀਏਬਲ",
 	"variable to have them replaced with clipboard content.": "ਕਲਿੱਪਬੋਰਡ ਸਮੱਗਰੀ ਨਾਲ ਬਦਲਣ ਲਈ ਵੈਰੀਏਬਲ।",
 	"Version": "ਵਰਜਨ",

+ 11 - 0
src/lib/i18n/locales/pl-PL/translation.json

@@ -43,6 +43,7 @@
 	"Allow Chat Deletion": "Pozwól na usuwanie czatu",
 	"Allow non-local voices": "",
 	"Allow User Location": "",
+	"Allow Voice Interruption in Call": "",
 	"alphanumeric characters and hyphens": "znaki alfanumeryczne i myślniki",
 	"Already have an account?": "Masz już konto?",
 	"an assistant": "asystent",
@@ -165,9 +166,13 @@
 	"Deleted {{name}}": "Usunięto {{name}}",
 	"Description": "Opis",
 	"Didn't fully follow instructions": "Nie postępował zgodnie z instrukcjami",
+	"Discover a function": "",
 	"Discover a model": "Odkryj model",
 	"Discover a prompt": "Odkryj prompt",
+	"Discover a tool": "",
+	"Discover, download, and explore custom functions": "",
 	"Discover, download, and explore custom prompts": "Odkryj, pobierz i eksploruj niestandardowe prompty",
+	"Discover, download, and explore custom tools": "",
 	"Discover, download, and explore model presets": "Odkryj, pobierz i eksploruj ustawienia modeli",
 	"Dismissible": "",
 	"Display Emoji in Call": "",
@@ -180,6 +185,7 @@
 	"Don't Allow": "Nie zezwalaj",
 	"Don't have an account?": "Nie masz konta?",
 	"Don't like the style": "Nie podobał mi się styl",
+	"Done": "",
 	"Download": "Pobieranie",
 	"Download canceled": "Pobieranie przerwane",
 	"Download Database": "Pobierz bazę danych",
@@ -312,6 +318,7 @@
 	"Manage Models": "Zarządzaj modelami",
 	"Manage Ollama Models": "Zarządzaj modelami Ollama",
 	"Manage Pipelines": "Zarządzanie potokami",
+	"Manage Valves": "",
 	"March": "Marzec",
 	"Max Tokens (num_predict)": "Maksymalna liczba żetonów (num_predict)",
 	"Maximum of 3 models can be downloaded simultaneously. Please try again later.": "Maksymalnie 3 modele można pobierać jednocześnie. Spróbuj ponownie później.",
@@ -461,10 +468,12 @@
 	"Seed": "Seed",
 	"Select a base model": "Wybieranie modelu bazowego",
 	"Select a engine": "",
+	"Select a function": "",
 	"Select a mode": "Wybierz tryb",
 	"Select a model": "Wybierz model",
 	"Select a pipeline": "Wybieranie potoku",
 	"Select a pipeline url": "Wybieranie adresu URL potoku",
+	"Select a tool": "",
 	"Select an Ollama instance": "Wybierz instancję Ollama",
 	"Select Documents": "",
 	"Select model": "Wybierz model",
@@ -497,6 +506,7 @@
 	"Show Admin Details in Account Pending Overlay": "",
 	"Show Model": "",
 	"Show shortcuts": "Pokaż skróty",
+	"Show your support!": "",
 	"Showcased creativity": "Pokaz kreatywności",
 	"sidebar": "Panel boczny",
 	"Sign in": "Zaloguj się",
@@ -585,6 +595,7 @@
 	"Users": "Użytkownicy",
 	"Utilize": "Wykorzystaj",
 	"Valid time units:": "Poprawne jednostki czasu:",
+	"Valves": "",
 	"variable": "zmienna",
 	"variable to have them replaced with clipboard content.": "zmienna która zostanie zastąpiona zawartością schowka.",
 	"Version": "Wersja",

+ 11 - 0
src/lib/i18n/locales/pt-BR/translation.json

@@ -43,6 +43,7 @@
 	"Allow Chat Deletion": "Permitir Exclusão de Bate-papo",
 	"Allow non-local voices": "",
 	"Allow User Location": "",
+	"Allow Voice Interruption in Call": "",
 	"alphanumeric characters and hyphens": "caracteres alfanuméricos e hífens",
 	"Already have an account?": "Já tem uma conta?",
 	"an assistant": "um assistente",
@@ -165,9 +166,13 @@
 	"Deleted {{name}}": "Excluído {{nome}}",
 	"Description": "Descrição",
 	"Didn't fully follow instructions": "Não seguiu instruções com precisão",
+	"Discover a function": "",
 	"Discover a model": "Descubra um modelo",
 	"Discover a prompt": "Descobrir um prompt",
+	"Discover a tool": "",
+	"Discover, download, and explore custom functions": "",
 	"Discover, download, and explore custom prompts": "Descubra, baixe e explore prompts personalizados",
+	"Discover, download, and explore custom tools": "",
 	"Discover, download, and explore model presets": "Descubra, baixe e explore predefinições de modelo",
 	"Dismissible": "",
 	"Display Emoji in Call": "",
@@ -180,6 +185,7 @@
 	"Don't Allow": "Não Permitir",
 	"Don't have an account?": "Não tem uma conta?",
 	"Don't like the style": "Não gosta do estilo",
+	"Done": "",
 	"Download": "Baixar",
 	"Download canceled": "Download cancelado",
 	"Download Database": "Baixar Banco de Dados",
@@ -312,6 +318,7 @@
 	"Manage Models": "Gerenciar Modelos",
 	"Manage Ollama Models": "Gerenciar Modelos Ollama",
 	"Manage Pipelines": "Gerenciar pipelines",
+	"Manage Valves": "",
 	"March": "Março",
 	"Max Tokens (num_predict)": "Fichas máximas (num_predict)",
 	"Maximum of 3 models can be downloaded simultaneously. Please try again later.": "Máximo de 3 modelos podem ser baixados simultaneamente. Tente novamente mais tarde.",
@@ -460,10 +467,12 @@
 	"Seed": "Semente",
 	"Select a base model": "Selecione um modelo base",
 	"Select a engine": "",
+	"Select a function": "",
 	"Select a mode": "Selecione um modo",
 	"Select a model": "Selecione um modelo",
 	"Select a pipeline": "Selecione um pipeline",
 	"Select a pipeline url": "Selecione uma URL de pipeline",
+	"Select a tool": "",
 	"Select an Ollama instance": "Selecione uma instância Ollama",
 	"Select Documents": "",
 	"Select model": "Selecione um modelo",
@@ -496,6 +505,7 @@
 	"Show Admin Details in Account Pending Overlay": "",
 	"Show Model": "",
 	"Show shortcuts": "Mostrar",
+	"Show your support!": "",
 	"Showcased creativity": "Criatividade Exibida",
 	"sidebar": "barra lateral",
 	"Sign in": "Entrar",
@@ -584,6 +594,7 @@
 	"Users": "Usuários",
 	"Utilize": "Utilizar",
 	"Valid time units:": "Unidades de tempo válidas:",
+	"Valves": "",
 	"variable": "variável",
 	"variable to have them replaced with clipboard content.": "variável para que sejam substituídos pelo conteúdo da área de transferência.",
 	"Version": "Versão",

+ 11 - 0
src/lib/i18n/locales/pt-PT/translation.json

@@ -43,6 +43,7 @@
 	"Allow Chat Deletion": "Permitir Exclusão de Conversa",
 	"Allow non-local voices": "Permitir vozes não locais",
 	"Allow User Location": "",
+	"Allow Voice Interruption in Call": "",
 	"alphanumeric characters and hyphens": "caracteres alfanuméricos e hífens",
 	"Already have an account?": "Já tem uma conta?",
 	"an assistant": "um assistente",
@@ -165,9 +166,13 @@
 	"Deleted {{name}}": "Apagado {{name}}",
 	"Description": "Descrição",
 	"Didn't fully follow instructions": "Não seguiu instruções com precisão",
+	"Discover a function": "",
 	"Discover a model": "Descubra um modelo",
 	"Discover a prompt": "Descobrir um prompt",
+	"Discover a tool": "",
+	"Discover, download, and explore custom functions": "",
 	"Discover, download, and explore custom prompts": "Descubra, descarregue e explore prompts personalizados",
+	"Discover, download, and explore custom tools": "",
 	"Discover, download, and explore model presets": "Descubra, descarregue e explore predefinições de modelo",
 	"Dismissible": "Dispensável",
 	"Display Emoji in Call": "",
@@ -180,6 +185,7 @@
 	"Don't Allow": "Não Permitir",
 	"Don't have an account?": "Não tem uma conta?",
 	"Don't like the style": "Não gosta do estilo",
+	"Done": "",
 	"Download": "Descarregar",
 	"Download canceled": "Download cancelado",
 	"Download Database": "Descarregar Base de Dados",
@@ -312,6 +318,7 @@
 	"Manage Models": "Gerir Modelos",
 	"Manage Ollama Models": "Gerir Modelos Ollama",
 	"Manage Pipelines": "Gerir pipelines",
+	"Manage Valves": "",
 	"March": "Março",
 	"Max Tokens (num_predict)": "Máx Tokens (num_predict)",
 	"Maximum of 3 models can be downloaded simultaneously. Please try again later.": "O máximo de 3 modelos podem ser descarregados simultaneamente. Tente novamente mais tarde.",
@@ -460,10 +467,12 @@
 	"Seed": "Semente",
 	"Select a base model": "Selecione um modelo base",
 	"Select a engine": "Selecione um motor",
+	"Select a function": "",
 	"Select a mode": "Selecione um modo",
 	"Select a model": "Selecione um modelo",
 	"Select a pipeline": "Selecione um pipeline",
 	"Select a pipeline url": "Selecione um URL de pipeline",
+	"Select a tool": "",
 	"Select an Ollama instance": "Selecione uma instância Ollama",
 	"Select Documents": "",
 	"Select model": "Selecione o modelo",
@@ -496,6 +505,7 @@
 	"Show Admin Details in Account Pending Overlay": "Mostrar Detalhes do Administrador na sobreposição de Conta Pendente",
 	"Show Model": "",
 	"Show shortcuts": "Mostrar atalhos",
+	"Show your support!": "",
 	"Showcased creativity": "Criatividade Exibida",
 	"sidebar": "barra lateral",
 	"Sign in": "Entrar",
@@ -584,6 +594,7 @@
 	"Users": "Utilizadores",
 	"Utilize": "Utilizar",
 	"Valid time units:": "Unidades de tempo válidas:",
+	"Valves": "",
 	"variable": "variável",
 	"variable to have them replaced with clipboard content.": "variável para que sejam substituídos pelo conteúdo da área de transferência.",
 	"Version": "Versão",

+ 11 - 0
src/lib/i18n/locales/ru-RU/translation.json

@@ -43,6 +43,7 @@
 	"Allow Chat Deletion": "Дозволять удаление чат",
 	"Allow non-local voices": "",
 	"Allow User Location": "",
+	"Allow Voice Interruption in Call": "",
 	"alphanumeric characters and hyphens": "буквенно цифровые символы и дефисы",
 	"Already have an account?": "у вас уже есть аккаунт?",
 	"an assistant": "ассистент",
@@ -165,9 +166,13 @@
 	"Deleted {{name}}": "Удалено {{name}}",
 	"Description": "Описание",
 	"Didn't fully follow instructions": "Не полностью следул инструкциям",
+	"Discover a function": "",
 	"Discover a model": "Откройте для себя модель",
 	"Discover a prompt": "Найти промт",
+	"Discover a tool": "",
+	"Discover, download, and explore custom functions": "",
 	"Discover, download, and explore custom prompts": "Находите, загружайте и исследуйте настраиваемые промты",
+	"Discover, download, and explore custom tools": "",
 	"Discover, download, and explore model presets": "Находите, загружайте и исследуйте предустановки модели",
 	"Dismissible": "",
 	"Display Emoji in Call": "",
@@ -180,6 +185,7 @@
 	"Don't Allow": "Не разрешать",
 	"Don't have an account?": "у вас не есть аккаунт?",
 	"Don't like the style": "Не нравится стиль",
+	"Done": "",
 	"Download": "Загрузить",
 	"Download canceled": "Загрузка отменена",
 	"Download Database": "Загрузить базу данных",
@@ -312,6 +318,7 @@
 	"Manage Models": "Управление моделями",
 	"Manage Ollama Models": "Управление моделями Ollama",
 	"Manage Pipelines": "Управление конвейерами",
+	"Manage Valves": "",
 	"March": "Март",
 	"Max Tokens (num_predict)": "Максимальное количество жетонов (num_predict)",
 	"Maximum of 3 models can be downloaded simultaneously. Please try again later.": "Максимальное количество моделей для загрузки одновременно - 3. Пожалуйста, попробуйте позже.",
@@ -461,10 +468,12 @@
 	"Seed": "Сид",
 	"Select a base model": "Выбор базовой модели",
 	"Select a engine": "",
+	"Select a function": "",
 	"Select a mode": "Выберите режим",
 	"Select a model": "Выберите модель",
 	"Select a pipeline": "Выбор конвейера",
 	"Select a pipeline url": "Выберите URL-адрес конвейера",
+	"Select a tool": "",
 	"Select an Ollama instance": "Выберите экземпляр Ollama",
 	"Select Documents": "",
 	"Select model": "Выберите модель",
@@ -497,6 +506,7 @@
 	"Show Admin Details in Account Pending Overlay": "",
 	"Show Model": "",
 	"Show shortcuts": "Показать клавиатурные сокращения",
+	"Show your support!": "",
 	"Showcased creativity": "Показать творчество",
 	"sidebar": "боковая панель",
 	"Sign in": "Войти",
@@ -585,6 +595,7 @@
 	"Users": "Пользователи",
 	"Utilize": "Использовать",
 	"Valid time units:": "Допустимые единицы времени:",
+	"Valves": "",
 	"variable": "переменная",
 	"variable to have them replaced with clipboard content.": "переменная, чтобы их заменить содержимым буфера обмена.",
 	"Version": "Версия",

+ 11 - 0
src/lib/i18n/locales/sr-RS/translation.json

@@ -43,6 +43,7 @@
 	"Allow Chat Deletion": "Дозволи брисање ћаскања",
 	"Allow non-local voices": "",
 	"Allow User Location": "",
+	"Allow Voice Interruption in Call": "",
 	"alphanumeric characters and hyphens": "алфанумерички знакови и цртице",
 	"Already have an account?": "Већ имате налог?",
 	"an assistant": "помоћник",
@@ -165,9 +166,13 @@
 	"Deleted {{name}}": "Избрисано {{наме}}",
 	"Description": "Опис",
 	"Didn't fully follow instructions": "Упутства нису праћена у потпуности",
+	"Discover a function": "",
 	"Discover a model": "Откријте модел",
 	"Discover a prompt": "Откриј упит",
+	"Discover a tool": "",
+	"Discover, download, and explore custom functions": "",
 	"Discover, download, and explore custom prompts": "Откријте, преузмите и истражите прилагођене упите",
+	"Discover, download, and explore custom tools": "",
 	"Discover, download, and explore model presets": "Откријте, преузмите и истражите образце модела",
 	"Dismissible": "",
 	"Display Emoji in Call": "",
@@ -180,6 +185,7 @@
 	"Don't Allow": "Не дозволи",
 	"Don't have an account?": "Немате налог?",
 	"Don't like the style": "Не свиђа ми се стил",
+	"Done": "",
 	"Download": "Преузми",
 	"Download canceled": "Преузимање отказано",
 	"Download Database": "Преузми базу података",
@@ -312,6 +318,7 @@
 	"Manage Models": "Управљај моделима",
 	"Manage Ollama Models": "Управљај Ollama моделима",
 	"Manage Pipelines": "Управљање цевоводима",
+	"Manage Valves": "",
 	"March": "Март",
 	"Max Tokens (num_predict)": "Маx Токенс (нум_предицт)",
 	"Maximum of 3 models can be downloaded simultaneously. Please try again later.": "Највише 3 модела могу бити преузета истовремено. Покушајте поново касније.",
@@ -460,10 +467,12 @@
 	"Seed": "Семе",
 	"Select a base model": "Избор основног модела",
 	"Select a engine": "",
+	"Select a function": "",
 	"Select a mode": "Изабери режим",
 	"Select a model": "Изабери модел",
 	"Select a pipeline": "Избор цевовода",
 	"Select a pipeline url": "Избор урл адресе цевовода",
+	"Select a tool": "",
 	"Select an Ollama instance": "Изабери Ollama инстанцу",
 	"Select Documents": "",
 	"Select model": "Изабери модел",
@@ -496,6 +505,7 @@
 	"Show Admin Details in Account Pending Overlay": "",
 	"Show Model": "",
 	"Show shortcuts": "Прикажи пречице",
+	"Show your support!": "",
 	"Showcased creativity": "Приказана креативност",
 	"sidebar": "бочна трака",
 	"Sign in": "Пријави се",
@@ -584,6 +594,7 @@
 	"Users": "Корисници",
 	"Utilize": "Искористи",
 	"Valid time units:": "Важеће временске јединице:",
+	"Valves": "",
 	"variable": "променљива",
 	"variable to have them replaced with clipboard content.": "променљива за замену са садржајем оставе.",
 	"Version": "Издање",

+ 11 - 0
src/lib/i18n/locales/sv-SE/translation.json

@@ -43,6 +43,7 @@
 	"Allow Chat Deletion": "Tillåt chattborttagning",
 	"Allow non-local voices": "Tillåt icke-lokala röster",
 	"Allow User Location": "",
+	"Allow Voice Interruption in Call": "",
 	"alphanumeric characters and hyphens": "alfanumeriska tecken och bindestreck",
 	"Already have an account?": "Har du redan ett konto?",
 	"an assistant": "en assistent",
@@ -165,9 +166,13 @@
 	"Deleted {{name}}": "Borttagen {{name}}",
 	"Description": "Beskrivning",
 	"Didn't fully follow instructions": "Följde inte instruktionerna",
+	"Discover a function": "",
 	"Discover a model": "Upptäck en modell",
 	"Discover a prompt": "Upptäck en instruktion",
+	"Discover a tool": "",
+	"Discover, download, and explore custom functions": "",
 	"Discover, download, and explore custom prompts": "Upptäck, ladda ner och utforska anpassade instruktioner",
+	"Discover, download, and explore custom tools": "",
 	"Discover, download, and explore model presets": "Upptäck, ladda ner och utforska modellförinställningar",
 	"Dismissible": "Kan stängas",
 	"Display Emoji in Call": "Visa Emoji under samtal",
@@ -180,6 +185,7 @@
 	"Don't Allow": "Tillåt inte",
 	"Don't have an account?": "Har du inget konto?",
 	"Don't like the style": "Tycker inte om utseendet",
+	"Done": "",
 	"Download": "Ladda ner",
 	"Download canceled": "Nedladdning avbruten",
 	"Download Database": "Ladda ner databas",
@@ -312,6 +318,7 @@
 	"Manage Models": "Hantera modeller",
 	"Manage Ollama Models": "Hantera Ollama-modeller",
 	"Manage Pipelines": "Hantera rörledningar",
+	"Manage Valves": "",
 	"March": "mars",
 	"Max Tokens (num_predict)": "Maximalt antal tokens (num_predict)",
 	"Maximum of 3 models can be downloaded simultaneously. Please try again later.": "Högst 3 modeller kan laddas ner samtidigt. Vänligen försök igen senare.",
@@ -459,10 +466,12 @@
 	"Seed": "Seed",
 	"Select a base model": "Välj en basmodell",
 	"Select a engine": "Välj en motor",
+	"Select a function": "",
 	"Select a mode": "Välj ett läge",
 	"Select a model": "Välj en modell",
 	"Select a pipeline": "Välj en rörledning",
 	"Select a pipeline url": "Välj en URL för rörledningen",
+	"Select a tool": "",
 	"Select an Ollama instance": "Välj en Ollama-instans",
 	"Select Documents": "Välj dokument",
 	"Select model": "Välj en modell",
@@ -495,6 +504,7 @@
 	"Show Admin Details in Account Pending Overlay": "Visa administratörsinformation till väntande konton",
 	"Show Model": "",
 	"Show shortcuts": "Visa genvägar",
+	"Show your support!": "",
 	"Showcased creativity": "Visade kreativitet",
 	"sidebar": "sidofält",
 	"Sign in": "Logga in",
@@ -583,6 +593,7 @@
 	"Users": "Användare",
 	"Utilize": "Använd",
 	"Valid time units:": "Giltiga tidsenheter:",
+	"Valves": "",
 	"variable": "variabel",
 	"variable to have them replaced with clipboard content.": "variabel för att få dem ersatta med urklippsinnehåll.",
 	"Version": "Version",

+ 11 - 0
src/lib/i18n/locales/tk-TW/translation.json

@@ -43,6 +43,7 @@
 	"Allow Chat Deletion": "",
 	"Allow non-local voices": "",
 	"Allow User Location": "",
+	"Allow Voice Interruption in Call": "",
 	"alphanumeric characters and hyphens": "",
 	"Already have an account?": "",
 	"an assistant": "",
@@ -165,9 +166,13 @@
 	"Deleted {{name}}": "",
 	"Description": "",
 	"Didn't fully follow instructions": "",
+	"Discover a function": "",
 	"Discover a model": "",
 	"Discover a prompt": "",
+	"Discover a tool": "",
+	"Discover, download, and explore custom functions": "",
 	"Discover, download, and explore custom prompts": "",
+	"Discover, download, and explore custom tools": "",
 	"Discover, download, and explore model presets": "",
 	"Dismissible": "",
 	"Display Emoji in Call": "",
@@ -180,6 +185,7 @@
 	"Don't Allow": "",
 	"Don't have an account?": "",
 	"Don't like the style": "",
+	"Done": "",
 	"Download": "",
 	"Download canceled": "",
 	"Download Database": "",
@@ -312,6 +318,7 @@
 	"Manage Models": "",
 	"Manage Ollama Models": "",
 	"Manage Pipelines": "",
+	"Manage Valves": "",
 	"March": "",
 	"Max Tokens (num_predict)": "",
 	"Maximum of 3 models can be downloaded simultaneously. Please try again later.": "",
@@ -459,10 +466,12 @@
 	"Seed": "",
 	"Select a base model": "",
 	"Select a engine": "",
+	"Select a function": "",
 	"Select a mode": "",
 	"Select a model": "",
 	"Select a pipeline": "",
 	"Select a pipeline url": "",
+	"Select a tool": "",
 	"Select an Ollama instance": "",
 	"Select Documents": "",
 	"Select model": "",
@@ -495,6 +504,7 @@
 	"Show Admin Details in Account Pending Overlay": "",
 	"Show Model": "",
 	"Show shortcuts": "",
+	"Show your support!": "",
 	"Showcased creativity": "",
 	"sidebar": "",
 	"Sign in": "",
@@ -583,6 +593,7 @@
 	"Users": "",
 	"Utilize": "",
 	"Valid time units:": "",
+	"Valves": "",
 	"variable": "",
 	"variable to have them replaced with clipboard content.": "",
 	"Version": "",

+ 11 - 0
src/lib/i18n/locales/tr-TR/translation.json

@@ -43,6 +43,7 @@
 	"Allow Chat Deletion": "Sohbet Silmeye İzin Ver",
 	"Allow non-local voices": "Yerel olmayan seslere izin verin",
 	"Allow User Location": "",
+	"Allow Voice Interruption in Call": "",
 	"alphanumeric characters and hyphens": "alfanumerik karakterler ve tireler",
 	"Already have an account?": "Zaten bir hesabınız mı var?",
 	"an assistant": "bir asistan",
@@ -165,9 +166,13 @@
 	"Deleted {{name}}": "{{name}} silindi",
 	"Description": "Açıklama",
 	"Didn't fully follow instructions": "Talimatları tam olarak takip etmedi",
+	"Discover a function": "",
 	"Discover a model": "Bir model keşfedin",
 	"Discover a prompt": "Bir prompt keşfedin",
+	"Discover a tool": "",
+	"Discover, download, and explore custom functions": "",
 	"Discover, download, and explore custom prompts": "Özel promptları keşfedin, indirin ve inceleyin",
+	"Discover, download, and explore custom tools": "",
 	"Discover, download, and explore model presets": "Model ön ayarlarını keşfedin, indirin ve inceleyin",
 	"Dismissible": "",
 	"Display Emoji in Call": "",
@@ -180,6 +185,7 @@
 	"Don't Allow": "İzin Verme",
 	"Don't have an account?": "Hesabınız yok mu?",
 	"Don't like the style": "Tarzını beğenmedim",
+	"Done": "",
 	"Download": "İndir",
 	"Download canceled": "İndirme iptal edildi",
 	"Download Database": "Veritabanını İndir",
@@ -312,6 +318,7 @@
 	"Manage Models": "Modelleri Yönet",
 	"Manage Ollama Models": "Ollama Modellerini Yönet",
 	"Manage Pipelines": "Pipeline'ları Yönet",
+	"Manage Valves": "",
 	"March": "Mart",
 	"Max Tokens (num_predict)": "Maksimum Token (num_predict)",
 	"Maximum of 3 models can be downloaded simultaneously. Please try again later.": "Aynı anda en fazla 3 model indirilebilir. Lütfen daha sonra tekrar deneyin.",
@@ -459,10 +466,12 @@
 	"Seed": "Seed",
 	"Select a base model": "Bir temel model seç",
 	"Select a engine": "",
+	"Select a function": "",
 	"Select a mode": "Bir mod seç",
 	"Select a model": "Bir model seç",
 	"Select a pipeline": "Bir pipeline seç",
 	"Select a pipeline url": "Bir pipeline URL'si seç",
+	"Select a tool": "",
 	"Select an Ollama instance": "Bir Ollama örneği seçin",
 	"Select Documents": "",
 	"Select model": "Model seç",
@@ -495,6 +504,7 @@
 	"Show Admin Details in Account Pending Overlay": "",
 	"Show Model": "",
 	"Show shortcuts": "Kısayolları göster",
+	"Show your support!": "",
 	"Showcased creativity": "Sergilenen yaratıcılık",
 	"sidebar": "kenar çubuğu",
 	"Sign in": "Oturum aç",
@@ -583,6 +593,7 @@
 	"Users": "Kullanıcılar",
 	"Utilize": "Kullan",
 	"Valid time units:": "Geçerli zaman birimleri:",
+	"Valves": "",
 	"variable": "değişken",
 	"variable to have them replaced with clipboard content.": "panodaki içerikle değiştirilmesi için değişken.",
 	"Version": "Sürüm",

+ 11 - 0
src/lib/i18n/locales/uk-UA/translation.json

@@ -43,6 +43,7 @@
 	"Allow Chat Deletion": "Дозволити видалення чату",
 	"Allow non-local voices": "Дозволити не локальні голоси",
 	"Allow User Location": "Доступ до місцезнаходження",
+	"Allow Voice Interruption in Call": "",
 	"alphanumeric characters and hyphens": "алфавітно-цифрові символи та дефіси",
 	"Already have an account?": "Вже є обліковий запис?",
 	"an assistant": "асистента",
@@ -165,9 +166,13 @@
 	"Deleted {{name}}": "Видалено {{name}}",
 	"Description": "Опис",
 	"Didn't fully follow instructions": "Не повністю дотримувалися інструкцій",
+	"Discover a function": "",
 	"Discover a model": "Знайдіть модель",
 	"Discover a prompt": "Знайти промт",
+	"Discover a tool": "",
+	"Discover, download, and explore custom functions": "",
 	"Discover, download, and explore custom prompts": "Знайдіть, завантажте та досліджуйте налаштовані промти",
+	"Discover, download, and explore custom tools": "",
 	"Discover, download, and explore model presets": "Знайдіть, завантажте та досліджуйте налаштовані налаштування моделі",
 	"Dismissible": "Неприйнятно",
 	"Display Emoji in Call": "Відображати емодзі у викликах",
@@ -180,6 +185,7 @@
 	"Don't Allow": "Не дозволяти",
 	"Don't have an account?": "Немає облікового запису?",
 	"Don't like the style": "Не подобається стиль",
+	"Done": "",
 	"Download": "Завантажити",
 	"Download canceled": "Завантаження скасовано",
 	"Download Database": "Завантажити базу даних",
@@ -312,6 +318,7 @@
 	"Manage Models": "Керування моделями",
 	"Manage Ollama Models": "Керування моделями Ollama",
 	"Manage Pipelines": "Управління Pipelines",
+	"Manage Valves": "",
 	"March": "Березень",
 	"Max Tokens (num_predict)": "Макс токенів (num_predict)",
 	"Maximum of 3 models can be downloaded simultaneously. Please try again later.": "Максимум 3 моделі можна завантажити одночасно. Будь ласка, спробуйте пізніше.",
@@ -461,10 +468,12 @@
 	"Seed": "Сід",
 	"Select a base model": "Вибрати базову модель",
 	"Select a engine": "Виберіть рушій",
+	"Select a function": "",
 	"Select a mode": "Оберіть режим",
 	"Select a model": "Виберіть модель",
 	"Select a pipeline": "Виберіть pipeline",
 	"Select a pipeline url": "Виберіть адресу pipeline",
+	"Select a tool": "",
 	"Select an Ollama instance": "Виберіть екземпляр Ollama",
 	"Select Documents": "Виберіть документи",
 	"Select model": "Вибрати модель",
@@ -497,6 +506,7 @@
 	"Show Admin Details in Account Pending Overlay": "Відобразити дані адміна у вікні очікування облікового запису",
 	"Show Model": "Показати модель",
 	"Show shortcuts": "Показати клавіатурні скорочення",
+	"Show your support!": "",
 	"Showcased creativity": "Продемонстрований креатив",
 	"sidebar": "бокова панель",
 	"Sign in": "Увійти",
@@ -585,6 +595,7 @@
 	"Users": "Користувачі",
 	"Utilize": "Використовувати",
 	"Valid time units:": "Дійсні одиниці часу:",
+	"Valves": "",
 	"variable": "змінна",
 	"variable to have them replaced with clipboard content.": "змінна, щоб замінити їх вмістом буфера обміну.",
 	"Version": "Версія",

+ 11 - 0
src/lib/i18n/locales/vi-VN/translation.json

@@ -43,6 +43,7 @@
 	"Allow Chat Deletion": "Cho phép Xóa nội dung chat",
 	"Allow non-local voices": "Cho phép giọng nói không bản xứ",
 	"Allow User Location": "Cho phép sử dụng vị trí người dùng",
+	"Allow Voice Interruption in Call": "",
 	"alphanumeric characters and hyphens": "ký tự số và gạch nối",
 	"Already have an account?": "Bạn đã có tài khoản?",
 	"an assistant": "trợ lý",
@@ -165,9 +166,13 @@
 	"Deleted {{name}}": "Đã xóa {{name}}",
 	"Description": "Mô tả",
 	"Didn't fully follow instructions": "Không tuân theo chỉ dẫn một cách đầy đủ",
+	"Discover a function": "",
 	"Discover a model": "Khám phá model",
 	"Discover a prompt": "Khám phá thêm prompt mới",
+	"Discover a tool": "",
+	"Discover, download, and explore custom functions": "",
 	"Discover, download, and explore custom prompts": "Tìm kiếm, tải về và khám phá thêm các prompt tùy chỉnh",
+	"Discover, download, and explore custom tools": "",
 	"Discover, download, and explore model presets": "Tìm kiếm, tải về và khám phá thêm các thiết lập mô hình sẵn",
 	"Dismissible": "Có thể loại bỏ",
 	"Display Emoji in Call": "Hiển thị Emoji trong cuộc gọi",
@@ -180,6 +185,7 @@
 	"Don't Allow": "Không Cho phép",
 	"Don't have an account?": "Không có tài khoản?",
 	"Don't like the style": "Không thích phong cách trả lời",
+	"Done": "",
 	"Download": "Tải về",
 	"Download canceled": "Đã hủy download",
 	"Download Database": "Tải xuống Cơ sở dữ liệu",
@@ -312,6 +318,7 @@
 	"Manage Models": "Quản lý mô hình",
 	"Manage Ollama Models": "Quản lý mô hình với Ollama",
 	"Manage Pipelines": "Quản lý Pipelines",
+	"Manage Valves": "",
 	"March": "Tháng 3",
 	"Max Tokens (num_predict)": "Tokens tối đa (num_predict)",
 	"Maximum of 3 models can be downloaded simultaneously. Please try again later.": "Tối đa 3 mô hình có thể được tải xuống cùng lúc. Vui lòng thử lại sau.",
@@ -458,10 +465,12 @@
 	"Seed": "Seed",
 	"Select a base model": "Chọn một base model",
 	"Select a engine": "Chọn dịch vụ",
+	"Select a function": "",
 	"Select a mode": "Chọn một chế độ",
 	"Select a model": "Chọn mô hình",
 	"Select a pipeline": "Chọn một quy trình",
 	"Select a pipeline url": "Chọn url quy trình",
+	"Select a tool": "",
 	"Select an Ollama instance": "Chọn một thực thể Ollama",
 	"Select Documents": "Chọn tài liệu",
 	"Select model": "Chọn model",
@@ -494,6 +503,7 @@
 	"Show Admin Details in Account Pending Overlay": "Hiển thị thông tin của Quản trị viên trên màn hình hiển thị Tài khoản đang chờ xử lý",
 	"Show Model": "Hiện mô hình",
 	"Show shortcuts": "Hiển thị phím tắt",
+	"Show your support!": "",
 	"Showcased creativity": "Thể hiện sự sáng tạo",
 	"sidebar": "thanh bên",
 	"Sign in": "Đăng nhập",
@@ -582,6 +592,7 @@
 	"Users": "Người sử dụng",
 	"Utilize": "Sử dụng",
 	"Valid time units:": "Đơn vị thời gian hợp lệ:",
+	"Valves": "",
 	"variable": "biến",
 	"variable to have them replaced with clipboard content.": "biến để có chúng được thay thế bằng nội dung clipboard.",
 	"Version": "Version",

+ 17 - 6
src/lib/i18n/locales/zh-CN/translation.json

@@ -43,6 +43,7 @@
 	"Allow Chat Deletion": "允许删除聊天记录",
 	"Allow non-local voices": "允许调用非本地音色",
 	"Allow User Location": "允许获取您的位置",
+	"Allow Voice Interruption in Call": "",
 	"alphanumeric characters and hyphens": "字母数字字符和连字符",
 	"Already have an account?": "已经拥有账号了?",
 	"an assistant": "助手",
@@ -99,7 +100,7 @@
 	"Clear memory": "清除记忆",
 	"Click here for help.": "点击这里获取帮助。",
 	"Click here to": "单击",
-	"Click here to download user import template file.": "",
+	"Click here to download user import template file.": "单击此处下载用户导入所需的模板文件。",
 	"Click here to select": "点击这里选择",
 	"Click here to select a csv file.": "单击此处选择 csv 文件。",
 	"Click here to select a py file.": "单击此处选择 py 文件。",
@@ -135,8 +136,8 @@
 	"Create new secret key": "创建新安全密钥",
 	"Created at": "创建于",
 	"Created At": "创建于",
-	"Created by": "",
-	"CSV Import": "",
+	"Created by": "作者",
+	"CSV Import": "通过 CSV 文件导入",
 	"Current Model": "当前模型",
 	"Current Password": "当前密码",
 	"Custom": "自定义",
@@ -165,9 +166,13 @@
 	"Deleted {{name}}": "已删除 {{name}}",
 	"Description": "描述",
 	"Didn't fully follow instructions": "没有完全遵照指示",
+	"Discover a function": "",
 	"Discover a model": "发现更多模型",
 	"Discover a prompt": "发现更多提示词",
+	"Discover a tool": "",
+	"Discover, download, and explore custom functions": "",
 	"Discover, download, and explore custom prompts": "发现、下载并探索更多自定义提示词",
+	"Discover, download, and explore custom tools": "",
 	"Discover, download, and explore model presets": "发现、下载并探索更多模型预设",
 	"Dismissible": "是否可关闭",
 	"Display Emoji in Call": "在通话中显示 Emoji 表情符号",
@@ -180,6 +185,7 @@
 	"Don't Allow": "不允许",
 	"Don't have an account?": "没有账号?",
 	"Don't like the style": "不喜欢这个文风",
+	"Done": "",
 	"Download": "下载",
 	"Download canceled": "下载已取消",
 	"Download Database": "下载数据库",
@@ -250,7 +256,7 @@
 	"Fluidly stream large external response chunks": "流畅地传输外部大型响应块数据",
 	"Focus chat input": "聚焦对话输入",
 	"Followed instructions perfectly": "完全按照指示执行",
-	"Form": "",
+	"Form": "手动创建",
 	"Format your variables using square brackets like this:": "使用这样的方括号格式化你的变量:",
 	"Frequency Penalty": "频率惩罚",
 	"Functions": "功能",
@@ -267,7 +273,7 @@
 	"Hello, {{name}}": "您好,{{name}}",
 	"Help": "帮助",
 	"Hide": "隐藏",
-	"Hide Model": "隐藏模型",
+	"Hide Model": "隐藏",
 	"How can I help you today?": "有什么我能帮您的吗?",
 	"Hybrid Search": "混合搜索",
 	"Image Generation (Experimental)": "图像生成(实验性)",
@@ -312,6 +318,7 @@
 	"Manage Models": "管理模型",
 	"Manage Ollama Models": "管理 Ollama 模型",
 	"Manage Pipelines": "管理 Pipeline",
+	"Manage Valves": "",
 	"March": "三月",
 	"Max Tokens (num_predict)": "最多 Token (num_predict)",
 	"Maximum of 3 models can be downloaded simultaneously. Please try again later.": "最多可以同时下载 3 个模型,请稍后重试。",
@@ -458,10 +465,12 @@
 	"Seed": "种子 (Seed)",
 	"Select a base model": "选择一个基础模型",
 	"Select a engine": "选择一个搜索引擎",
+	"Select a function": "",
 	"Select a mode": "选择一个模式",
 	"Select a model": "选择一个模型",
 	"Select a pipeline": "选择一个管道",
 	"Select a pipeline url": "选择一个管道 URL",
+	"Select a tool": "",
 	"Select an Ollama instance": "选择一个 Ollama 实例",
 	"Select Documents": "选择文档",
 	"Select model": "选择模型",
@@ -492,8 +501,9 @@
 	"short-summary": "简短总结",
 	"Show": "显示",
 	"Show Admin Details in Account Pending Overlay": "在用户待激活界面中显示管理员邮箱等详细信息",
-	"Show Model": "显示模型",
+	"Show Model": "显示",
 	"Show shortcuts": "显示快捷方式",
+	"Show your support!": "",
 	"Showcased creativity": "很有创意",
 	"sidebar": "侧边栏",
 	"Sign in": "登录",
@@ -582,6 +592,7 @@
 	"Users": "用户",
 	"Utilize": "利用",
 	"Valid time units:": "有效时间单位:",
+	"Valves": "",
 	"variable": "变量",
 	"variable to have them replaced with clipboard content.": "变量将被剪贴板内容替换。",
 	"Version": "版本",

+ 251 - 240
src/lib/i18n/locales/zh-TW/translation.json

@@ -4,36 +4,36 @@
 	"(e.g. `sh webui.sh --api`)": "(例如 `sh webui.sh --api`)",
 	"(latest)": "(最新版)",
 	"{{ models }}": "{{ models }}",
-	"{{ owner }}: You cannot delete a base model": "{{ owner }}:你無法刪除基本模型",
+	"{{ owner }}: You cannot delete a base model": "{{ owner }}:您無法刪除基礎模型",
 	"{{modelName}} is thinking...": "{{modelName}} 正在思考...",
 	"{{user}}'s Chats": "{{user}} 的聊天",
-	"{{webUIName}} Backend Required": "需要 {{webUIName}} 後",
-	"A task model is used when performing tasks such as generating titles for chats and web search queries": "在執行任務時使用任務模型,例如為聊天和網絡搜索查詢生成標題",
+	"{{webUIName}} Backend Required": "需要 {{webUIName}} 後",
+	"A task model is used when performing tasks such as generating titles for chats and web search queries": "在執行任務時使用任務模型,例如為聊天和網頁搜尋查詢生成標題",
 	"a user": "使用者",
 	"About": "關於",
 	"Account": "帳號",
-	"Account Activation Pending": "",
-	"Accurate information": "準確信息",
-	"Active Users": "",
+	"Account Activation Pending": "帳號啟用中",
+	"Accurate information": "準確資訊",
+	"Active Users": "活躍使用者",
 	"Add": "新增",
 	"Add a model id": "新增模型 ID",
-	"Add a short description about what this model does": "為這個模型添加一個簡短描述",
-	"Add a short title for this prompt": "為這個提示詞添加一個簡短的標題",
+	"Add a short description about what this model does": "為這個模型新增一個簡短描述",
+	"Add a short title for this prompt": "為這個提示詞新增一個簡短的標題",
 	"Add a tag": "新增標籤",
-	"Add custom prompt": "新增自定義提示詞",
+	"Add custom prompt": "新增自提示詞",
 	"Add Docs": "新增文件",
 	"Add Files": "新增檔案",
 	"Add Memory": "新增記憶",
 	"Add message": "新增訊息",
 	"Add Model": "新增模型",
 	"Add Tags": "新增標籤",
-	"Add User": "新增用户",
+	"Add User": "新增使用者",
 	"Adjusting these settings will apply changes universally to all users.": "調整這些設定將對所有使用者進行更改。",
 	"admin": "管理員",
-	"Admin": "",
+	"Admin": "管理員",
 	"Admin Panel": "管理員控制台",
 	"Admin Settings": "管理設定",
-	"Admins have access to all tools at all times; users need tools assigned per model in the workspace.": "",
+	"Admins have access to all tools at all times; users need tools assigned per model in the workspace.": "管理員隨時可以使用所有工具;使用者需要在工作區中為每個模型分配工具。",
 	"Advanced Parameters": "進階參數",
 	"Advanced Params": "進階參數",
 	"all": "所有",
@@ -41,43 +41,44 @@
 	"All Users": "所有使用者",
 	"Allow": "允許",
 	"Allow Chat Deletion": "允許刪除聊天紀錄",
-	"Allow non-local voices": "",
-	"Allow User Location": "",
-	"alphanumeric characters and hyphens": "英文字母、數字(0~9)和連字符(-)",
+	"Allow non-local voices": "允許非本機語音",
+	"Allow User Location": "允許使用者位置",
+	"Allow Voice Interruption in Call": "",
+	"alphanumeric characters and hyphens": "英文字母、數字(0~9)和連字元(-)",
 	"Already have an account?": "已經有帳號了嗎?",
 	"an assistant": "助手",
 	"and": "和",
-	"and create a new shared link.": "創建一個新的共享連結。",
+	"and create a new shared link.": "並建立一個新的共享連結。",
 	"API Base URL": "API 基本 URL",
-	"API Key": "API Key",
-	"API Key created.": "API Key",
-	"API keys": "API Keys",
-	"April": "4月",
-	"Archive": "存",
-	"Archive All Chats": "存所有聊天紀錄",
-	"Archived Chats": "聊天記錄存檔",
+	"API Key": "API 金鑰",
+	"API Key created.": "API 金鑰已建立。",
+	"API keys": "API 金鑰",
+	"April": "4 月",
+	"Archive": "存",
+	"Archive All Chats": "存所有聊天紀錄",
+	"Archived Chats": "已封存的聊天紀錄",
 	"are allowed - Activate this command by typing": "是允許的 - 透過輸入",
-	"Are you sure?": "確定嗎?",
+	"Are you sure?": "確定嗎?",
 	"Attach file": "附加檔案",
 	"Attention to detail": "細節精確",
 	"Audio": "音訊",
-	"August": "8月",
+	"August": "8 月",
 	"Auto-playback response": "自動播放回答",
 	"AUTOMATIC1111 Base URL": "AUTOMATIC1111 基本 URL",
 	"AUTOMATIC1111 Base URL is required.": "需要 AUTOMATIC1111 基本 URL",
-	"available!": "可以使用!",
+	"available!": "可用!",
 	"Back": "返回",
 	"Bad Response": "錯誤回應",
 	"Banners": "橫幅",
-	"Base Model (From)": "基模型(來自)",
-	"Batch Size (num_batch)": "",
+	"Base Model (From)": "基模型(來自)",
+	"Batch Size (num_batch)": "批次大小(num_batch)",
 	"before": "前",
 	"Being lazy": "懶人模式",
-	"Brave Search API Key": "搜尋 API Key",
-	"Bypass SSL verification for Websites": "跳過 SSL 驗證",
-	"Call": "",
-	"Call feature is not supported when using Web STT engine": "",
-	"Camera": "",
+	"Brave Search API Key": "Brave 搜尋 API 金鑰",
+	"Bypass SSL verification for Websites": "跳過網站的 SSL 驗證",
+	"Call": "呼叫",
+	"Call feature is not supported when using Web STT engine": "使用 Web STT 引擎時不支援呼叫功能",
+	"Camera": "相機",
 	"Cancel": "取消",
 	"Capabilities": "功能",
 	"Change Password": "修改密碼",
@@ -85,27 +86,27 @@
 	"Chat Background Image": "",
 	"Chat Bubble UI": "聊天氣泡介面",
 	"Chat direction": "聊天方向",
-	"Chat History": "聊天紀錄功能",
-	"Chat History is off for this browser.": "此瀏覽器已關閉聊天紀錄功能。",
+	"Chat History": "聊天紀錄",
+	"Chat History is off for this browser.": "此瀏覽器已關閉聊天紀錄。",
 	"Chats": "聊天",
 	"Check Again": "重新檢查",
 	"Check for updates": "檢查更新",
 	"Checking for updates...": "正在檢查更新...",
 	"Choose a model before saving...": "儲存前選擇一個模型...",
-	"Chunk Overlap": "Chunk Overlap",
-	"Chunk Params": "Chunk 參數",
-	"Chunk Size": "Chunk 大小",
+	"Chunk Overlap": "區塊重疊",
+	"Chunk Params": "區塊參數",
+	"Chunk Size": "區塊大小",
 	"Citation": "引文",
-	"Clear memory": "",
-	"Click here for help.": "點擊這裡尋找幫助。",
-	"Click here to": "點這裡",
-	"Click here to download user import template file.": "",
-	"Click here to select": "點這裡選擇",
-	"Click here to select a csv file.": "點這裡選擇 csv 檔案。",
-	"Click here to select a py file.": "",
-	"Click here to select documents.": "點這裡選擇文件。",
-	"click here.": "點這裡。",
-	"Click on the user role button to change a user's role.": "點擊使用者 Role 按鈕以更改使用者的 Role。",
+	"Clear memory": "清除記憶",
+	"Click here for help.": "點選這裡尋求幫助。",
+	"Click here to": "點這裡",
+	"Click here to download user import template file.": "點選這裡下載使用者匯入的範本",
+	"Click here to select": "點這裡選擇",
+	"Click here to select a csv file.": "點這裡選擇 csv 檔案。",
+	"Click here to select a py file.": "點選這裡選擇 py 檔案。",
+	"Click here to select documents.": "點這裡選擇文件。",
+	"click here.": "點這裡。",
+	"Click on the user role button to change a user's role.": "點選使用者角色按鈕以更改使用者的角色。",
 	"Clone": "複製",
 	"Close": "關閉",
 	"Collection": "收藏",
@@ -118,7 +119,7 @@
 	"Confirm Password": "確認密碼",
 	"Confirm your action": "",
 	"Connections": "連線",
-	"Contact Admin for WebUI Access": "",
+	"Contact Admin for WebUI Access": "聯絡管理員以取得 WebUI 存取權",
 	"Content": "內容",
 	"Context Length": "上下文長度",
 	"Continue Response": "繼續回答",
@@ -131,8 +132,8 @@
 	"Copying to clipboard was successful!": "成功複製到剪貼簿!",
 	"Create a model": "建立模型",
 	"Create Account": "建立帳號",
-	"Create new key": "建立新鑰",
-	"Create new secret key": "建立新鑰",
+	"Create new key": "建立新鑰",
+	"Create new secret key": "建立新鑰",
 	"Created at": "建立於",
 	"Created At": "建立於",
 	"Created by": "",
@@ -140,18 +141,18 @@
 	"Current Model": "目前模型",
 	"Current Password": "目前密碼",
 	"Custom": "自訂",
-	"Customize models for a specific purpose": "為特定目的自定義模型",
+	"Customize models for a specific purpose": "為特定目的自模型",
 	"Dark": "暗色",
-	"Dashboard": "",
+	"Dashboard": "儀表板",
 	"Database": "資料庫",
-	"December": "12月",
+	"December": "12 月",
 	"Default": "預設",
 	"Default (Automatic1111)": "預設(Automatic1111)",
 	"Default (SentenceTransformers)": "預設(SentenceTransformers)",
 	"Default Model": "預設模型",
 	"Default model updated": "預設模型已更新",
 	"Default Prompt Suggestions": "預設提示詞建議",
-	"Default User Role": "預設用戶 Role",
+	"Default User Role": "預設使用者角色",
 	"delete": "刪除",
 	"Delete": "刪除",
 	"Delete a model": "刪除一個模型",
@@ -160,178 +161,184 @@
 	"Delete Chat": "刪除聊天紀錄",
 	"Delete chat?": "",
 	"delete this link": "刪除此連結",
-	"Delete User": "刪除用戶",
+	"Delete User": "刪除使用者",
 	"Deleted {{deleteModelTag}}": "已刪除 {{deleteModelTag}}",
 	"Deleted {{name}}": "已刪除 {{name}}",
 	"Description": "描述",
-	"Didn't fully follow instructions": "無法完全遵循指示",
+	"Didn't fully follow instructions": "未完全遵循指示",
+	"Discover a function": "",
 	"Discover a model": "發現新模型",
 	"Discover a prompt": "發現新提示詞",
-	"Discover, download, and explore custom prompts": "發現、下載並探索他人設置的提示詞",
-	"Discover, download, and explore model presets": "發現、下載並探索他人設置的模型",
-	"Dismissible": "",
-	"Display Emoji in Call": "",
-	"Display the username instead of You in the Chat": "在聊天中顯示使用者名稱而不是「你」",
+	"Discover a tool": "",
+	"Discover, download, and explore custom functions": "",
+	"Discover, download, and explore custom prompts": "發現、下載並探索自訂提示詞",
+	"Discover, download, and explore custom tools": "",
+	"Discover, download, and explore model presets": "發現、下載並探索模型預設值",
+	"Dismissible": "可忽略",
+	"Display Emoji in Call": "在呼叫中顯示表情符號",
+	"Display the username instead of You in the Chat": "在聊天中顯示使用者名稱而不是「您」",
 	"Document": "文件",
 	"Document Settings": "文件設定",
-	"Documentation": "",
+	"Documentation": "文件",
 	"Documents": "文件",
-	"does not make any external connections, and your data stays securely on your locally hosted server.": "不會與外部溝通,你的數據會安全地留在你的本機伺服器上。",
+	"does not make any external connections, and your data stays securely on your locally hosted server.": "不會與外部連線,您的資料會安全地留在您的本機伺服器上。",
 	"Don't Allow": "不允許",
 	"Don't have an account?": "還沒有註冊帳號?",
 	"Don't like the style": "不喜歡這個樣式?",
+	"Done": "",
 	"Download": "下載",
 	"Download canceled": "下載已取消",
 	"Download Database": "下載資料庫",
-	"Drop any files here to add to the conversation": "拖拽文件到此處以新增至對話",
+	"Drop any files here to add to the conversation": "拖拽任意檔案到此處以新增至對話",
 	"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "例如 '30s', '10m'。有效的時間單位為 's', 'm', 'h'。",
 	"Edit": "編輯",
 	"Edit Doc": "編輯文件",
-	"Edit Memory": "",
+	"Edit Memory": "編輯記憶",
 	"Edit User": "編輯使用者",
 	"Email": "電子郵件",
-	"Embedding Batch Size": "",
+	"Embedding Batch Size": "嵌入批次大小",
 	"Embedding Model": "嵌入模型",
 	"Embedding Model Engine": "嵌入模型引擎",
 	"Embedding model set to \"{{embedding_model}}\"": "嵌入模型已設定為 \"{{embedding_model}}\"",
-	"Enable Chat History": "啟用聊天歷史",
-	"Enable Community Sharing": "啟用社分享",
+	"Enable Chat History": "啟用聊天紀錄",
+	"Enable Community Sharing": "啟用社分享",
 	"Enable New Sign Ups": "允許註冊新帳號",
-	"Enable Web Search": "啟用網絡搜索",
-	"Ensure your CSV file includes 4 columns in this order: Name, Email, Password, Role.": "請確保的 CSV 檔案包含這四個欄位,並按照此順序:名稱、電子郵件、密碼、角色。",
+	"Enable Web Search": "啟用網頁搜尋",
+	"Ensure your CSV file includes 4 columns in this order: Name, Email, Password, Role.": "請確保的 CSV 檔案包含這四個欄位,並按照此順序:名稱、電子郵件、密碼、角色。",
 	"Enter {{role}} message here": "在這裡輸入 {{role}} 訊息",
 	"Enter a detail about yourself for your LLMs to recall": "輸入 LLM 記憶的詳細內容",
-	"Enter Brave Search API Key": "輸入 Brave Search API Key",
-	"Enter Chunk Overlap": "輸入 Chunk Overlap",
-	"Enter Chunk Size": "輸入 Chunk 大小",
+	"Enter Brave Search API Key": "輸入 Brave 搜尋 API 金鑰",
+	"Enter Chunk Overlap": "輸入區塊重疊",
+	"Enter Chunk Size": "輸入區塊大小",
 	"Enter Github Raw URL": "輸入 Github Raw URL",
-	"Enter Google PSE API Key": "輸入 Google PSE API Key",
-	"Enter Google PSE Engine Id": "輸入 Google PSE Engine Id",
+	"Enter Google PSE API Key": "輸入 Google PSE API 金鑰",
+	"Enter Google PSE Engine Id": "輸入 Google PSE 引擎 ID",
 	"Enter Image Size (e.g. 512x512)": "輸入圖片大小(例如 512x512)",
 	"Enter language codes": "輸入語言代碼",
 	"Enter model tag (e.g. {{modelTag}})": "輸入模型標籤(例如 {{modelTag}})",
 	"Enter Number of Steps (e.g. 50)": "輸入步數(例如 50)",
 	"Enter Score": "輸入分數",
 	"Enter Searxng Query URL": "輸入 Searxng 查詢 URL",
-	"Enter Serper API Key": "輸入 Serper API Key",
-	"Enter Serply API Key": "",
-	"Enter Serpstack API Key": "輸入 Serpstack API Key",
+	"Enter Serper API Key": "輸入 Serper API 金鑰",
+	"Enter Serply API Key": "輸入 Serply API 金鑰",
+	"Enter Serpstack API Key": "輸入 Serpstack API 金鑰",
 	"Enter stop sequence": "輸入停止序列",
-	"Enter Tavily API Key": "",
+	"Enter Tavily API Key": "輸入 Tavily API 金鑰",
 	"Enter Top K": "輸入 Top K",
 	"Enter URL (e.g. http://127.0.0.1:7860/)": "輸入 URL(例如 http://127.0.0.1:7860/)",
 	"Enter URL (e.g. http://localhost:11434)": "輸入 URL(例如 http://localhost:11434)",
-	"Enter Your Email": "輸入的電子郵件",
-	"Enter Your Full Name": "輸入的全名",
-	"Enter Your Password": "輸入的密碼",
-	"Enter Your Role": "輸入的角色",
+	"Enter Your Email": "輸入的電子郵件",
+	"Enter Your Full Name": "輸入的全名",
+	"Enter Your Password": "輸入的密碼",
+	"Enter Your Role": "輸入的角色",
 	"Error": "錯誤",
-	"Experimental": "實驗功能",
-	"Export": "出",
+	"Experimental": "實驗功能",
+	"Export": "出",
 	"Export All Chats (All Users)": "匯出所有聊天紀錄(所有使用者)",
-	"Export chat (.json)": "",
+	"Export chat (.json)": "匯出聊天紀錄(.json)",
 	"Export Chats": "匯出聊天紀錄",
-	"Export Documents Mapping": "匯出文件映",
-	"Export Functions": "",
+	"Export Documents Mapping": "匯出文件映",
+	"Export Functions": "匯出功能",
 	"Export Models": "匯出模型",
 	"Export Prompts": "匯出提示詞",
-	"Export Tools": "",
-	"External Models": "",
-	"Failed to create API Key.": "無法建 API 金鑰。",
+	"Export Tools": "匯出工具",
+	"External Models": "外部模型",
+	"Failed to create API Key.": "無法建 API 金鑰。",
 	"Failed to read clipboard contents": "無法讀取剪貼簿內容",
-	"Failed to update settings": "",
-	"February": "2月",
-	"Feel free to add specific details": "請自由添加詳細內容。",
-	"File": "",
+	"Failed to update settings": "無法更新設定",
+	"February": "2 月",
+	"Feel free to add specific details": "請隨意新增詳細內容。",
+	"File": "檔案",
 	"File Mode": "檔案模式",
 	"File not found.": "找不到檔案。",
-	"Filters": "",
-	"Fingerprint spoofing detected: Unable to use initials as avatar. Defaulting to default profile image.": "偽裝偽裝檢測:無法使用頭像作為頭像。預設為預設頭像。",
-	"Fluidly stream large external response chunks": "流暢地傳輸大型外部應區塊",
+	"Filters": "篩選器",
+	"Fingerprint spoofing detected: Unable to use initials as avatar. Defaulting to default profile image.": "偽造偵測:無法使用初始頭像。預設為預設個人影象。",
+	"Fluidly stream large external response chunks": "流暢地傳輸大型外部應區塊",
 	"Focus chat input": "聚焦聊天輸入框",
 	"Followed instructions perfectly": "完全遵循指示",
-	"Form": "",
-	"Format your variables using square brackets like this:": "像這樣使用方括號來格式化的變數:",
+	"Form": "表單",
+	"Format your variables using square brackets like this:": "像這樣使用方括號來格式化的變數:",
 	"Frequency Penalty": "頻率懲罰",
-	"Functions": "",
+	"Functions": "功能",
 	"General": "常用",
 	"General Settings": "常用設定",
-	"Generate Image": "",
-	"Generating search query": "生成搜查詢",
-	"Generation Info": "生成信息",
+	"Generate Image": "生成圖片",
+	"Generating search query": "生成搜查詢",
+	"Generation Info": "生成資訊",
 	"Good Response": "優秀的回應",
-	"Google PSE API Key": "Google PSE API Key",
-	"Google PSE Engine Id": "Google PSE Engine Id",
+	"Google PSE API Key": "Google PSE API 金鑰",
+	"Google PSE Engine Id": "Google PSE 引擎 ID",
 	"h:mm a": "h:mm a",
 	"has no conversations.": "沒有對話",
-	"Hello, {{name}}": "好,{{name}}",
+	"Hello, {{name}}": "好,{{name}}",
 	"Help": "幫助",
 	"Hide": "隱藏",
-	"Hide Model": "",
-	"How can I help you today?": "今天能為你做什麼?",
-	"Hybrid Search": "混合搜",
-	"Image Generation (Experimental)": "圖像生成(實驗功能)",
-	"Image Generation Engine": "像生成引擎",
+	"Hide Model": "隱藏模型",
+	"How can I help you today?": "今天能為您做些什麼?",
+	"Hybrid Search": "混合搜",
+	"Image Generation (Experimental)": "影像生成(實驗性功能)",
+	"Image Generation Engine": "像生成引擎",
 	"Image Settings": "圖片設定",
 	"Images": "圖片",
 	"Import Chats": "匯入聊天紀錄",
-	"Import Documents Mapping": "匯入文件映",
-	"Import Functions": "",
+	"Import Documents Mapping": "匯入文件映",
+	"Import Functions": "匯入功能",
 	"Import Models": "匯入模型",
 	"Import Prompts": "匯入提示詞",
-	"Import Tools": "",
-	"Include `--api` flag when running stable-diffusion-webui": "在行 stable-diffusion-webui 時加上 `--api` 標誌",
+	"Import Tools": "匯入工具",
+	"Include `--api` flag when running stable-diffusion-webui": "在行 stable-diffusion-webui 時加上 `--api` 標誌",
 	"Info": "資訊",
 	"Input commands": "輸入命令",
 	"Install from Github URL": "從 Github URL 安裝",
-	"Instant Auto-Send After Voice Transcription": "",
+	"Instant Auto-Send After Voice Transcription": "語音轉錄後立即自動傳送",
 	"Interface": "介面",
 	"Invalid Tag": "無效標籤",
-	"January": "1月",
-	"join our Discord for help.": "加入我們的 Discord 尋幫助。",
+	"January": "1 月",
+	"join our Discord for help.": "加入我們的 Discord 尋幫助。",
 	"JSON": "JSON",
 	"JSON Preview": "JSON 預覽",
-	"July": "7月",
-	"June": "6月",
+	"July": "7 月",
+	"June": "6 月",
 	"JWT Expiration": "JWT 過期時間",
 	"JWT Token": "JWT Token",
 	"Keep Alive": "保持活躍",
 	"Keyboard shortcuts": "鍵盤快速鍵",
-	"Knowledge": "",
+	"Knowledge": "知識",
 	"Language": "語言",
 	"Last Active": "最後活動",
-	"Last Modified": "",
+	"Last Modified": "最後修改",
 	"Light": "亮色",
-	"Listening...": "",
+	"Listening...": "正在聆聽...",
 	"LLMs can make mistakes. Verify important information.": "LLM 可能會產生錯誤。請驗證重要資訊。",
-	"Local Models": "",
+	"Local Models": "本機模型",
 	"LTR": "LTR",
-	"Made by OpenWebUI Community": "由 OpenWebUI 社製作",
+	"Made by OpenWebUI Community": "由 OpenWebUI 社製作",
 	"Make sure to enclose them with": "請確保變數有被以下符號框住:",
-	"Manage": "",
-	"Manage Models": "管理模",
+	"Manage": "管理",
+	"Manage Models": "管理模",
 	"Manage Ollama Models": "管理 Ollama 模型",
-	"Manage Pipelines": "管理管道",
-	"March": "3月",
+	"Manage Pipelines": "管理管線",
+	"Manage Valves": "",
+	"March": "3 月",
 	"Max Tokens (num_predict)": "最大 Token(num_predict)",
 	"Maximum of 3 models can be downloaded simultaneously. Please try again later.": "最多可以同時下載 3 個模型。請稍後再試。",
-	"May": "5月",
+	"May": "5 月",
 	"Memories accessible by LLMs will be shown here.": "LLM 記憶將會顯示在此處。",
 	"Memory": "記憶",
-	"Messages you send after creating your link won't be shared. Users with the URL will be able to view the shared chat.": "創建連結後發送的訊息將不會被共享。具有 URL 的用戶將會能夠檢視共享的聊天。",
-	"Minimum Score": "最分數",
+	"Messages you send after creating your link won't be shared. Users with the URL will be able to view the shared chat.": "建立連結後傳送的訊息將不會被共享。具有 URL 的使用者將會能夠檢視共享的聊天。",
+	"Minimum Score": "最分數",
 	"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": "",
+	"MMMM DD, YYYY hh:mm:ss A": "MMMM DD, YYYY hh:mm:ss A",
 	"Model '{{modelName}}' has been successfully downloaded.": "'{{modelName}}' 模型已成功下載。",
 	"Model '{{modelTag}}' is already in queue for downloading.": "'{{modelTag}}' 模型已經在下載佇列中。",
 	"Model {{modelId}} not found": "找不到 {{modelId}} 模型",
 	"Model {{modelName}} is not vision capable": "{{modelName}} 模型不適用於視覺",
 	"Model {{name}} is now {{status}}": "{{name}} 模型現在是 {{status}}",
-	"Model filesystem path detected. Model shortname is required for update, cannot continue.": "模型文件系統路徑已檢測。需要更新模型短名,無法繼續。",
+	"Model filesystem path detected. Model shortname is required for update, cannot continue.": "已偵測到模型檔案系統路徑。需要更新模型簡稱,無法繼續。",
 	"Model ID": "模型 ID",
 	"Model not selected": "未選擇模型",
 	"Model Params": "模型參數",
@@ -345,33 +352,33 @@
 	"Name your model": "請輸入模型名稱",
 	"New Chat": "新增聊天",
 	"New Password": "新密碼",
-	"No documents found": "",
+	"No documents found": "找不到文件",
 	"No results found": "沒有找到結果",
-	"No search query generated": "沒有生成搜查詢",
+	"No search query generated": "沒有生成搜查詢",
 	"No source available": "沒有可用的來源",
 	"None": "無",
-	"Not factually correct": "與真實資訊不符",
-	"Note: If you set a minimum score, the search will only return documents with a score greater than or equal to the minimum score.": "註:如果設置最低分數,則搜索將只返回分數大於或等於最低分數的文檔。",
-	"Notifications": "桌面通知",
-	"November": "11月",
-	"num_thread (Ollama)": "num_thread(奧拉馬)",
+	"Not factually correct": "與真實資訊不符",
+	"Note: If you set a minimum score, the search will only return documents with a score greater than or equal to the minimum score.": "註:如果設定最低分數,則搜尋將只返回分數大於或等於最低分數的文件。",
+	"Notifications": "通知",
+	"November": "11 月",
+	"num_thread (Ollama)": "num_thread(Ollama)",
 	"OAuth ID": "",
 	"October": "10 月",
 	"Off": "關閉",
 	"Okay, Let's Go!": "好的,啟動吧!",
-	"OLED Dark": "`",
+	"OLED Dark": "OLED 深色",
 	"Ollama": "Ollama",
 	"Ollama API": "Ollama API",
-	"Ollama API disabled": "Ollama API 已用",
-	"Ollama API is disabled": "",
+	"Ollama API disabled": "Ollama API 已用",
+	"Ollama API is disabled": "Ollama API 已停用",
 	"Ollama Version": "Ollama 版本",
 	"On": "開啟",
 	"Only": "僅有",
-	"Only alphanumeric characters and hyphens are allowed in the command string.": "命令字串中只能包含英文字母、數字(0~9)和連字(-)。",
-	"Oops! Hold tight! Your files are still in the processing oven. We're cooking them up to perfection. Please be patient and we'll let you know once they're ready.": "哎呀!請稍等!的文件還在處理中。我們正最佳化文件,請耐心等待,一旦準備好,我們會通知。",
+	"Only alphanumeric characters and hyphens are allowed in the command string.": "命令字串中只能包含英文字母、數字(0~9)和連字(-)。",
+	"Oops! Hold tight! Your files are still in the processing oven. We're cooking them up to perfection. Please be patient and we'll let you know once they're ready.": "哎呀!請稍等!的文件還在處理中。我們正最佳化文件,請耐心等待,一旦準備好,我們會通知。",
 	"Oops! Looks like the URL is invalid. Please double-check and try again.": "哎呀!看起來 URL 無效。請仔細檢查後再試一次。",
-	"Oops! There was an error in the previous response. Please try again or contact admin.": "",
-	"Oops! You're using an unsupported method (frontend only). Please serve the WebUI from the backend.": "哎呀!你正在使用不支援的方法(僅有前台)。請從後台提供 WebUI。",
+	"Oops! There was an error in the previous response. Please try again or contact admin.": "哎呀!先前的回應發生錯誤。請重試或聯絡管理員",
+	"Oops! You're using an unsupported method (frontend only). Please serve the WebUI from the backend.": "哎呀!您正在使用不支援的方法(僅有前端)。請從後端提供 WebUI。",
 	"Open": "開啟",
 	"Open AI": "Open AI",
 	"Open AI (Dall-E)": "Open AI (Dall-E)",
@@ -385,22 +392,22 @@
 	"Other": "其他",
 	"Password": "密碼",
 	"PDF document (.pdf)": "PDF 文件 (.pdf)",
-	"PDF Extract Images (OCR)": "PDF 像擷取(OCR 光學文字辨識)",
+	"PDF Extract Images (OCR)": "PDF 像擷取(OCR 光學文字辨識)",
 	"pending": "待審查",
-	"Permission denied when accessing media devices": "",
-	"Permission denied when accessing microphone": "",
+	"Permission denied when accessing media devices": "存取媒體裝置時被拒絕權限",
+	"Permission denied when accessing microphone": "存取麥克風時被拒絕權限",
 	"Permission denied when accessing microphone: {{error}}": "存取麥克風時被拒絕權限:{{error}}",
 	"Personalization": "個人化",
 	"Pipelines": "管線",
-	"Pipelines Valves": "管線阀门",
+	"Pipelines Valves": "管線閥門",
 	"Plain text (.txt)": "純文字 (.txt)",
 	"Playground": "AI 對話遊樂場",
 	"Positive attitude": "積極態度",
 	"Previous 30 days": "前 30 天",
 	"Previous 7 days": "前 7 天",
-	"Profile Image": "個人像",
+	"Profile Image": "個人像",
 	"Prompt": "提示詞",
-	"Prompt (e.g. Tell me a fun fact about the Roman Empire)": "提示詞(例如:告訴我關於羅馬帝國的趣事)",
+	"Prompt (e.g. Tell me a fun fact about the Roman Empire)": "提示詞(例如:告訴我關於羅馬帝國的一些趣事)",
 	"Prompt Content": "提示詞內容",
 	"Prompt suggestions": "提示詞建議",
 	"Prompts": "提示詞",
@@ -410,32 +417,32 @@
 	"RAG Template": "RAG 範例",
 	"Read Aloud": "讀出",
 	"Record voice": "錄音",
-	"Redirecting you to OpenWebUI Community": "將重新導向到 OpenWebUI 社群",
-	"Refer to yourself as \"User\" (e.g., \"User is learning Spanish\")": "",
-	"Refused when it shouldn't have": "拒絕時不該拒絕",
+	"Redirecting you to OpenWebUI Community": "將重新導向到 OpenWebUI 社群",
+	"Refer to yourself as \"User\" (e.g., \"User is learning Spanish\")": "將自己稱為「使用者」(例如,「使用者正在學習西班牙語」)",
+	"Refused when it shouldn't have": "不該拒絕時拒絕了",
 	"Regenerate": "重新生成",
 	"Release Notes": "發布說明",
 	"Remove": "移除",
 	"Remove Model": "移除模型",
-	"Rename": "重命名",
+	"Rename": "重命名",
 	"Repeat Last N": "重複最後 N 次",
 	"Request Mode": "請求模式",
 	"Reranking Model": "重新排序模型",
-	"Reranking model disabled": "重新排序模型已用",
+	"Reranking model disabled": "重新排序模型已用",
 	"Reranking model set to \"{{reranking_model}}\"": "重新排序模型設定為 \"{{reranking_model}}\"",
-	"Reset": "",
-	"Reset Upload Directory": "",
-	"Reset Vector Storage": "重向量儲存空間",
+	"Reset": "重設",
+	"Reset Upload Directory": "重設上傳目錄",
+	"Reset Vector Storage": "重向量儲存空間",
 	"Response AutoCopy to Clipboard": "自動複製回答到剪貼簿",
-	"Role": "Role",
+	"Role": "角色",
 	"Rosé Pine": "玫瑰松",
 	"Rosé Pine Dawn": "黎明玫瑰松",
 	"RTL": "RTL",
-	"Running": "",
+	"Running": "運作中",
 	"Save": "儲存",
 	"Save & Create": "儲存並建立",
 	"Save & Update": "儲存並更新",
-	"Saving chat logs directly to your browser's storage is no longer supported. Please take a moment to download and delete your chat logs by clicking the button below. Don't worry, you can easily re-import your chat logs to the backend through": "現已不支援將聊天紀錄儲存到瀏覽器儲存空間中。請點擊下面的按鈕下載並刪除你的聊天記錄。別擔心,你可以通過以下方式輕鬆地重新匯入你的聊天記錄到後台",
+	"Saving chat logs directly to your browser's storage is no longer supported. Please take a moment to download and delete your chat logs by clicking the button below. Don't worry, you can easily re-import your chat logs to the backend through": "現已不支援將聊天紀錄儲存到瀏覽器儲存空間中。請點選下面的按鈕下載並刪除您的聊天記錄。別擔心,您可以透過以下方式輕鬆地重新匯入您的聊天記錄到後端",
 	"Scan": "掃描",
 	"Scan complete!": "掃描完成!",
 	"Scan for documents from {{path}}": "從 {{path}} 掃描文件",
@@ -443,37 +450,39 @@
 	"Search a model": "搜尋模型",
 	"Search Chats": "搜尋聊天",
 	"Search Documents": "搜尋文件",
-	"Search Functions": "",
+	"Search Functions": "搜尋功能",
 	"Search Models": "搜尋模型",
 	"Search Prompts": "搜尋提示詞",
-	"Search Query Generation Prompt": "",
-	"Search Query Generation Prompt Length Threshold": "",
+	"Search Query Generation Prompt": "搜尋查詢生成提示詞",
+	"Search Query Generation Prompt Length Threshold": "搜尋查詢生成提示詞長度閾值",
 	"Search Result Count": "搜尋結果數量",
-	"Search Tools": "",
-	"Searched {{count}} sites_other": "掃描 {{count}} 個網站_其他",
-	"Searching \"{{searchQuery}}\"": "",
+	"Search Tools": "搜尋工具",
+	"Searched {{count}} sites_other": "搜尋了 {{count}} 個網站",
+	"Searching \"{{searchQuery}}\"": "正在搜尋 \"{{searchQuery}}\"",
 	"Searxng Query URL": "Searxng 查詢 URL",
-	"See readme.md for instructions": "查看 readme.md 獲取指南",
-	"See what's new": "查看最新內容",
+	"See readme.md for instructions": "檢視 readme.md 取得指南",
+	"See what's new": "檢視最新內容",
 	"Seed": "種子",
 	"Select a base model": "選擇基礎模型",
-	"Select a engine": "",
+	"Select a engine": "選擇引擎",
+	"Select a function": "",
 	"Select a mode": "選擇模式",
 	"Select a model": "選擇一個模型",
-	"Select a pipeline": "選擇管道",
-	"Select a pipeline url": "選擇管道 URL",
-	"Select an Ollama instance": "選擇 Ollama 實例",
-	"Select Documents": "",
+	"Select a pipeline": "選擇管線",
+	"Select a pipeline url": "選擇管線 URL",
+	"Select a tool": "",
+	"Select an Ollama instance": "選擇 Ollama 執行個體",
+	"Select Documents": "選擇文件",
 	"Select model": "選擇模型",
-	"Select only one model to call": "",
-	"Selected model(s) do not support image inputs": "已選擇模型不支持圖像輸入",
+	"Select only one model to call": "僅選擇一個模型來呼叫",
+	"Selected model(s) do not support image inputs": "已選擇模型不支援影像輸入",
 	"Send": "傳送",
 	"Send a Message": "傳送訊息",
 	"Send message": "傳送訊息",
-	"September": "月",
-	"Serper API Key": "Serper API Key",
-	"Serply API Key": "",
-	"Serpstack API Key": "Serpstack API Key",
+	"September": "9 月",
+	"Serper API Key": "Serper API 金鑰",
+	"Serply API Key": "Serply API 金鑰",
+	"Serpstack API Key": "Serpstack API 金鑰",
 	"Server connection verified": "已驗證伺服器連線",
 	"Set as default": "設為預設",
 	"Set Default Model": "設定預設模型",
@@ -485,15 +494,16 @@
 	"Set Voice": "設定語音",
 	"Settings": "設定",
 	"Settings saved successfully!": "成功儲存設定",
-	"Settings updated successfully": "",
+	"Settings updated successfully": "設定更新成功",
 	"Share": "分享",
 	"Share Chat": "分享聊天",
 	"Share to OpenWebUI Community": "分享到 OpenWebUI 社群",
 	"short-summary": "簡短摘要",
 	"Show": "顯示",
-	"Show Admin Details in Account Pending Overlay": "",
-	"Show Model": "",
+	"Show Admin Details in Account Pending Overlay": "在帳號待審覆蓋層中顯示管理員詳細資訊",
+	"Show Model": "顯示模型",
 	"Show shortcuts": "顯示快速鍵",
+	"Show your support!": "",
 	"Showcased creativity": "展示創造性",
 	"sidebar": "側邊欄",
 	"Sign in": "登入",
@@ -504,115 +514,116 @@
 	"Speech recognition error: {{error}}": "語音識別錯誤:{{error}}",
 	"Speech-to-Text Engine": "語音轉文字引擎",
 	"Stop Sequence": "停止序列",
-	"STT Model": "",
+	"STT Model": "STT 模型",
 	"STT Settings": "語音轉文字設定",
 	"Submit": "提交",
-	"Subtitle (e.g. about the Roman Empire)": "標題(例如:關於羅馬帝國)",
+	"Subtitle (e.g. about the Roman Empire)": "標題(例如:關於羅馬帝國)",
 	"Success": "成功",
 	"Successfully updated.": "更新成功。",
 	"Suggested": "建議",
 	"System": "系統",
 	"System Prompt": "系統提示詞",
 	"Tags": "標籤",
-	"Tap to interrupt": "",
-	"Tavily API Key": "",
+	"Tap to interrupt": "點選以中斷",
+	"Tavily API Key": "Tavily API 金鑰",
 	"Tell us more:": "告訴我們更多:",
 	"Temperature": "溫度",
-	"Template": "模板",
-	"Text Completion": "文本補全(Text Completion)",
+	"Template": "範本",
+	"Text Completion": "文字補全",
 	"Text-to-Speech Engine": "文字轉語音引擎",
 	"Tfs Z": "Tfs Z",
-	"Thanks for your feedback!": "感謝的回饋!",
+	"Thanks for your feedback!": "感謝的回饋!",
 	"The score should be a value between 0.0 (0%) and 1.0 (100%).": "分數應該介於 0.0(0%)和 1.0(100%)之間。",
 	"Theme": "主題",
-	"Thinking...": "",
-	"This action cannot be undone. Do you wish to continue?": "",
-	"This ensures that your valuable conversations are securely saved to your backend database. Thank you!": "這確保你寶貴的對話安全地儲存到你的後台資料庫。謝謝!",
-	"This is an experimental feature, it may not function as expected and is subject to change at any time.": "",
+	"Thinking...": "正在思考...",
+	"This action cannot be undone. Do you wish to continue?": "此動作無法被復原。您想要繼續進行嗎?",
+	"This ensures that your valuable conversations are securely saved to your backend database. Thank you!": "這確保您寶貴的對話安全地儲存到您的後端資料庫。謝謝!",
+	"This is an experimental feature, it may not function as expected and is subject to change at any time.": "這是一個實驗性功能,可能無法如預期運作,並且隨時可能更改。",
 	"This setting does not sync across browsers or devices.": "此設定不會在瀏覽器或裝置間同步。",
 	"This will delete": "",
 	"Thorough explanation": "詳細說明",
 	"Tip: Update multiple variable slots consecutively by pressing the tab key in the chat input after each replacement.": "提示:透過在每次替換後在聊天輸入框中按 Tab 鍵連續更新多個變數。",
 	"Title": "標題",
 	"Title (e.g. Tell me a fun fact)": "標題(例如:告訴我一個有趣的事)",
-	"Title Auto-Generation": "自動生標題",
+	"Title Auto-Generation": "自動生標題",
 	"Title cannot be an empty string.": "標題不能為空字串",
-	"Title Generation Prompt": "自動生標題的提示詞",
+	"Title Generation Prompt": "自動生標題的提示詞",
 	"to": "到",
-	"To access the available model names for downloading,": "若想查看可供下載的模型名稱,",
-	"To access the GGUF models available for downloading,": "若想查看可供下載的 GGUF 模型名稱,",
-	"To access the WebUI, please reach out to the administrator. Admins can manage user statuses from the Admin Panel.": "",
-	"To add documents here, upload them to the \"Documents\" workspace first.": "",
+	"To access the available model names for downloading,": "若想檢視可供下載的模型名稱,",
+	"To access the GGUF models available for downloading,": "若想檢視可供下載的 GGUF 模型名稱,",
+	"To access the WebUI, please reach out to the administrator. Admins can manage user statuses from the Admin Panel.": "若要存取 WebUI,請聯絡管理員。管理員可以從管理面板管理使用者狀態。",
+	"To add documents here, upload them to the \"Documents\" workspace first.": "若要在此新增文件,請先將它們上傳到「文件」工作區。",
 	"to chat input.": "到聊天輸入框來啟動此命令。",
-	"To select filters here, add them to the \"Functions\" workspace first.": "",
-	"To select toolkits here, add them to the \"Tools\" workspace first.": "",
+	"To select filters here, add them to the \"Functions\" workspace first.": "若要在此選擇篩選器,請先將它們新增到「功能」工作區。",
+	"To select toolkits here, add them to the \"Tools\" workspace first.": "若要在此選擇工具包,請先將它們新增到「工具」工作區。",
 	"Today": "今天",
 	"Toggle settings": "切換設定",
 	"Toggle sidebar": "切換側邊欄",
-	"Tokens To Keep On Context Refresh (num_keep)": "",
-	"Tools": "",
+	"Tokens To Keep On Context Refresh (num_keep)": "上下文重新整理時保留的 Token 數量(num_keep)",
+	"Tools": "工具",
 	"Top K": "Top K",
 	"Top P": "Top P",
 	"Trouble accessing Ollama?": "存取 Ollama 時遇到問題?",
-	"TTS Model": "",
-	"TTS Settings": "文字轉語音設定",
-	"TTS Voice": "",
+	"TTS Model": "文字轉語音(TTS)模型",
+	"TTS Settings": "文字轉語音(TTS)設定",
+	"TTS Voice": "文字轉語音(TTS)聲調",
 	"Type": "類型",
 	"Type Hugging Face Resolve (Download) URL": "輸入 Hugging Face 解析後的(下載)URL",
 	"Uh-oh! There was an issue connecting to {{provider}}.": "哎呀!連線到 {{provider}} 時出現問題。",
-	"UI": "",
-	"Unknown file type '{{file_type}}'. Proceeding with the file upload anyway.": "",
-	"Update": "",
+	"UI": "使用者界面",
+	"Unknown file type '{{file_type}}'. Proceeding with the file upload anyway.": "未知的檔案類型 '{{file_type}}'。但仍會繼續上傳。",
+	"Update": "更新",
 	"Update and Copy Link": "更新並複製連結",
 	"Update password": "更新密碼",
-	"Updated at": "",
-	"Upload": "",
+	"Updated at": "更新於",
+	"Upload": "上傳",
 	"Upload a GGUF model": "上傳一個 GGUF 模型",
-	"Upload Files": "上傳文件",
-	"Upload Pipeline": "",
+	"Upload Files": "上傳檔案",
+	"Upload Pipeline": "上傳管線",
 	"Upload Progress": "上傳進度",
 	"URL Mode": "URL 模式",
-	"Use '#' in the prompt input to load and select your documents.": "在輸入框中輸入 '#' 以載入並選擇的文件。",
+	"Use '#' in the prompt input to load and select your documents.": "在輸入框中輸入 '#' 以載入並選擇的文件。",
 	"Use Gravatar": "使用 Gravatar",
-	"Use Initials": "使用初始像",
-	"use_mlock (Ollama)": "use_mlock(奧拉馬)",
-	"use_mmap (Ollama)": "use_mmap (Ollama)",
+	"Use Initials": "使用初始像",
+	"use_mlock (Ollama)": "use_mlock(Ollama)",
+	"use_mmap (Ollama)": "use_mmap(Ollama)",
 	"user": "使用者",
 	"User Permissions": "使用者權限",
 	"Users": "使用者",
 	"Utilize": "使用",
 	"Valid time units:": "有效時間單位:",
+	"Valves": "",
 	"variable": "變數",
 	"variable to have them replaced with clipboard content.": "變數將替換為剪貼簿內容",
 	"Version": "版本",
 	"Voice": "",
 	"Warning": "警告",
-	"Warning: If you update or change your embedding model, you will need to re-import all documents.": "警告:如果更新或更改你的嵌入模型,則需要重新導入所有文件",
+	"Warning: If you update or change your embedding model, you will need to re-import all documents.": "警告:如果更新或更改您的嵌入模型,則需要重新匯入所有文件",
 	"Web": "網頁",
-	"Web API": "",
-	"Web Loader Settings": "Web 載入器設定",
-	"Web Params": "Web 參數",
-	"Web Search": "Web 搜尋",
-	"Web Search Engine": "Web 搜尋引擎",
+	"Web API": "網頁 API",
+	"Web Loader Settings": "網頁載入器設定",
+	"Web Params": "網頁參數",
+	"Web Search": "網頁搜尋",
+	"Web Search Engine": "網頁搜尋引擎",
 	"Webhook URL": "Webhook URL",
 	"WebUI Settings": "WebUI 設定",
 	"WebUI will make requests to": "WebUI 將會存取",
 	"What’s New in": "全新內容",
 	"When history is turned off, new chats on this browser won't appear in your history on any of your devices.": "當歷史被關閉時,這個瀏覽器上的新聊天將不會出現在任何裝置的歷史記錄中",
-	"Whisper (Local)": "",
-	"Widescreen Mode": "",
+	"Whisper (Local)": "Whisper(本地)",
+	"Widescreen Mode": "寬螢幕模式",
 	"Workspace": "工作區",
-	"Write a prompt suggestion (e.g. Who are you?)": "寫一個提示詞建議(例如:是誰?)",
+	"Write a prompt suggestion (e.g. Who are you?)": "寫一個提示詞建議(例如:是誰?)",
 	"Write a summary in 50 words that summarizes [topic or keyword].": "寫一個 50 字的摘要來概括 [主題或關鍵詞]。",
 	"Yesterday": "昨天",
-	"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 clone a base model": "不能複製基礎模型",
-	"You have no archived conversations.": "沒有任何已封存的對話",
-	"You have shared this chat": "已分享此聊天",
-	"You're a helpful assistant.": "是一位善於協助他人的助手。",
+	"You": "",
+	"You can personalize your interactions with LLMs by adding memories through the 'Manage' button below, making them more helpful and tailored to you.": "您可以透過下方的「管理」按鈕新增記憶,個人化您的 LLM 互動,使其更有幫助並更符合您的需求。",
+	"You cannot clone a base model": "不能複製基礎模型",
+	"You have no archived conversations.": "沒有任何已封存的對話",
+	"You have shared this chat": "已分享此聊天",
+	"You're a helpful assistant.": "是一位善於協助他人的助手。",
 	"You're now logged in.": "已登入。",
-	"Your account status is currently pending activation.": "",
+	"Your account status is currently pending activation.": "您的帳號狀態目前待啟用。",
 	"Youtube": "Youtube",
 	"Youtube Loader Settings": "Youtube 載入器設定"
 }

+ 27 - 4
src/lib/utils/index.ts

@@ -506,13 +506,36 @@ export const removeEmojis = (str) => {
 	return str.replace(emojiRegex, '');
 };
 
+export const removeFormattings = (str) => {
+	return str.replace(/(\*)(.*?)\1/g, '').replace(/(```)(.*?)\1/gs, '');
+};
+
 export const extractSentences = (text) => {
-	// Split the paragraph into sentences based on common punctuation marks
-	const sentences = text.split(/(?<=[.!?])\s+/);
+	// This regular expression matches code blocks marked by triple backticks
+	const codeBlockRegex = /```[\s\S]*?```/g;
+
+	let codeBlocks = [];
+	let index = 0;
+
+	// Temporarily replace code blocks with placeholders and store the blocks separately
+	text = text.replace(codeBlockRegex, (match) => {
+		let placeholder = `\u0000${index}\u0000`; // Use a unique placeholder
+		codeBlocks[index++] = match;
+		return placeholder;
+	});
+
+	// Split the modified text into sentences based on common punctuation marks, avoiding these blocks
+	let sentences = text.split(/(?<=[.!?])\s+/);
+
+	// Restore code blocks and process sentences
+	sentences = sentences.map((sentence) => {
+		// Check if the sentence includes a placeholder for a code block
+		return sentence.replace(/\u0000(\d+)\u0000/g, (_, idx) => codeBlocks[idx]);
+	});
 
 	return sentences
-		.map((sentence) => removeEmojis(sentence.trim()))
-		.filter((sentence) => sentence !== '');
+		.map((sentence) => removeFormattings(removeEmojis(sentence.trim())))
+		.filter((sentence) => sentence);
 };
 
 export const extractSentencesForAudio = (text) => {

+ 6 - 1
src/routes/(app)/+layout.svelte

@@ -29,13 +29,15 @@
 		showChangelog,
 		config,
 		showCallOverlay,
-		tools
+		tools,
+		functions
 	} from '$lib/stores';
 
 	import SettingsModal from '$lib/components/chat/SettingsModal.svelte';
 	import Sidebar from '$lib/components/layout/Sidebar.svelte';
 	import ChangelogModal from '$lib/components/ChangelogModal.svelte';
 	import AccountPending from '$lib/components/layout/Overlay/AccountPending.svelte';
+	import { getFunctions } from '$lib/apis/functions';
 
 	const i18n = getContext('i18n');
 
@@ -93,6 +95,9 @@
 				(async () => {
 					tools.set(await getTools(localStorage.token));
 				})(),
+				(async () => {
+					functions.set(await getFunctions(localStorage.token));
+				})(),
 				(async () => {
 					banners.set(await getBanners(localStorage.token));
 				})(),

+ 1 - 1
src/routes/(app)/playground/+page.svelte

@@ -1,7 +1,7 @@
 <script>
 	import { showSidebar } from '$lib/stores';
 
-	import Playground from '$lib/components/workspace/Playground.svelte';
+	import Playground from '$lib/components/playground/Playground.svelte';
 </script>
 
 <div

+ 1 - 1
src/routes/(app)/workspace/+layout.svelte

@@ -9,7 +9,7 @@
 	const i18n = getContext('i18n');
 
 	onMount(async () => {
-		functions.set(await getFunctions(localStorage.token));
+		// functions.set(await getFunctions(localStorage.token));
 	});
 </script>
 

+ 27 - 0
src/routes/+layout.svelte

@@ -37,6 +37,8 @@
 	let loaded = false;
 	const BREAKPOINT = 768;
 
+	let wakeLock = null;
+
 	onMount(async () => {
 		theme.set(localStorage.theme);
 
@@ -51,6 +53,31 @@
 
 		window.addEventListener('resize', onResize);
 
+		const setWakeLock = async () => {
+			try {
+				wakeLock = await navigator.wakeLock.request('screen');
+			} catch (err) {
+				// The Wake Lock request has failed - usually system related, such as battery.
+				console.log(err);
+			}
+
+			wakeLock.addEventListener('release', () => {
+				// the wake lock has been released
+				console.log('Wake Lock released');
+			});
+		};
+
+		if ('wakeLock' in navigator) {
+			await setWakeLock();
+
+			document.addEventListener('visibilitychange', async () => {
+				// Re-request the wake lock if the document becomes visible
+				if (wakeLock !== null && document.visibilityState === 'visible') {
+					await setWakeLock();
+				}
+			});
+		}
+
 		let backendConfig = null;
 		try {
 			backendConfig = await getBackendConfig();