فهرست منبع

Merge branch 'dev' of github.com:taylorwilsdon/open-webui into dev

Taylor Wilsdon 3 ماه پیش
والد
کامیت
a67e5f725f
31فایلهای تغییر یافته به همراه618 افزوده شده و 371 حذف شده
  1. 1 1
      backend/open_webui/__init__.py
  2. 9 0
      backend/open_webui/config.py
  3. 10 0
      backend/open_webui/env.py
  4. 7 2
      backend/open_webui/main.py
  5. 30 6
      backend/open_webui/retrieval/vector/dbs/qdrant.py
  6. 8 8
      backend/open_webui/routers/knowledge.py
  7. 7 1
      backend/open_webui/routers/ollama.py
  8. 6 0
      backend/open_webui/routers/openai.py
  9. 5 2
      backend/open_webui/routers/retrieval.py
  10. 27 0
      backend/open_webui/routers/users.py
  11. 10 6
      backend/open_webui/utils/middleware.py
  12. 28 7
      backend/open_webui/utils/tools.py
  13. 1 1
      src/lib/components/channel/Channel.svelte
  14. 1 1
      src/lib/components/chat/ChatControls.svelte
  15. 2 2
      src/lib/components/chat/MessageInput.svelte
  16. 59 87
      src/lib/components/chat/MessageInput/InputMenu.svelte
  17. 1 0
      src/lib/components/chat/Messages/Citations.svelte
  18. 4 4
      src/lib/components/chat/Messages/ContentRenderer.svelte
  19. 4 16
      src/lib/components/chat/Messages/Markdown.svelte
  20. 6 7
      src/lib/components/chat/Messages/Markdown/MarkdownTokens.svelte
  21. 5 3
      src/lib/components/chat/Messages/UserMessage.svelte
  22. 3 4
      src/lib/components/common/Drawer.svelte
  23. 4 1
      src/lib/components/common/Textarea.svelte
  24. 36 7
      src/lib/components/workspace/Knowledge/KnowledgeBase.svelte
  25. 5 1
      src/lib/components/workspace/Models/ModelEditor.svelte
  26. 96 96
      src/lib/i18n/locales/es-ES/translation.json
  27. 1 1
      src/lib/i18n/locales/ko-KR/translation.json
  28. 32 32
      src/lib/i18n/locales/pl-PL/translation.json
  29. 5 1
      src/lib/utils/index.ts
  30. 204 74
      src/lib/utils/onedrive-file-picker.ts
  31. 1 0
      src/routes/+layout.svelte

+ 1 - 1
backend/open_webui/__init__.py

@@ -76,7 +76,7 @@ def serve(
     from open_webui.env import UVICORN_WORKERS  # Import the workers setting
 
     uvicorn.run(
-        open_webui.main.app,
+        "open_webui.main:app",
         host=host,
         port=port,
         forwarded_allow_ips="*",

+ 9 - 0
backend/open_webui/config.py

@@ -1694,6 +1694,8 @@ MILVUS_TOKEN = os.environ.get("MILVUS_TOKEN", None)
 QDRANT_URI = os.environ.get("QDRANT_URI", None)
 QDRANT_API_KEY = os.environ.get("QDRANT_API_KEY", None)
 QDRANT_ON_DISK = os.environ.get("QDRANT_ON_DISK", "false").lower() == "true"
+QDRANT_PREFER_GRPC = os.environ.get("QDRANT_PREFER_GRPC", "False").lower() == "true"
+QDRANT_GRPC_PORT = int(os.environ.get("QDRANT_GRPC_PORT", "6334"))
 
 # OpenSearch
 OPENSEARCH_URI = os.environ.get("OPENSEARCH_URI", "https://localhost:9200")
@@ -1761,6 +1763,13 @@ ONEDRIVE_CLIENT_ID = PersistentConfig(
     os.environ.get("ONEDRIVE_CLIENT_ID", ""),
 )
 
+ONEDRIVE_SHAREPOINT_URL = PersistentConfig(
+    "ONEDRIVE_SHAREPOINT_URL",
+    "onedrive.sharepoint_url",
+    os.environ.get("ONEDRIVE_SHAREPOINT_URL", ""),
+)
+
+
 # RAG Content Extraction
 CONTENT_EXTRACTION_ENGINE = PersistentConfig(
     "CONTENT_EXTRACTION_ENGINE",

+ 10 - 0
backend/open_webui/env.py

@@ -409,6 +409,11 @@ else:
     except Exception:
         AIOHTTP_CLIENT_TIMEOUT = 300
 
+
+AIOHTTP_CLIENT_SESSION_SSL = (
+    os.environ.get("AIOHTTP_CLIENT_SESSION_SSL", "True").lower() == "true"
+)
+
 AIOHTTP_CLIENT_TIMEOUT_MODEL_LIST = os.environ.get(
     "AIOHTTP_CLIENT_TIMEOUT_MODEL_LIST",
     os.environ.get("AIOHTTP_CLIENT_TIMEOUT_OPENAI_MODEL_LIST", "10"),
@@ -437,6 +442,11 @@ else:
     except Exception:
         AIOHTTP_CLIENT_TIMEOUT_TOOL_SERVER_DATA = 10
 
+
+AIOHTTP_CLIENT_SESSION_TOOL_SERVER_SSL = (
+    os.environ.get("AIOHTTP_CLIENT_SESSION_TOOL_SERVER_SSL", "True").lower() == "true"
+)
+
 ####################################
 # OFFLINE_MODE
 ####################################

+ 7 - 2
backend/open_webui/main.py

@@ -100,6 +100,7 @@ from open_webui.config import (
     # OpenAI
     ENABLE_OPENAI_API,
     ONEDRIVE_CLIENT_ID,
+    ONEDRIVE_SHAREPOINT_URL,
     OPENAI_API_BASE_URLS,
     OPENAI_API_KEYS,
     OPENAI_API_CONFIGS,
@@ -240,6 +241,7 @@ from open_webui.config import (
     GOOGLE_DRIVE_CLIENT_ID,
     GOOGLE_DRIVE_API_KEY,
     ONEDRIVE_CLIENT_ID,
+    ONEDRIVE_SHAREPOINT_URL,
     ENABLE_RAG_HYBRID_SEARCH,
     ENABLE_RAG_LOCAL_WEB_FETCH,
     ENABLE_WEB_LOADER_SSL_VERIFICATION,
@@ -1327,7 +1329,10 @@ async def get_app_config(request: Request):
                     "client_id": GOOGLE_DRIVE_CLIENT_ID.value,
                     "api_key": GOOGLE_DRIVE_API_KEY.value,
                 },
-                "onedrive": {"client_id": ONEDRIVE_CLIENT_ID.value},
+                "onedrive": {
+                    "client_id": ONEDRIVE_CLIENT_ID.value,
+                    "sharepoint_url": ONEDRIVE_SHAREPOINT_URL.value,
+                },
                 "license_metadata": app.state.LICENSE_METADATA,
                 **(
                     {
@@ -1439,7 +1444,7 @@ async def get_manifest_json():
             "start_url": "/",
             "display": "standalone",
             "background_color": "#343541",
-            "orientation": "natural",
+            "orientation": "any",
             "icons": [
                 {
                     "src": "/static/logo.png",

+ 30 - 6
backend/open_webui/retrieval/vector/dbs/qdrant.py

@@ -1,12 +1,19 @@
 from typing import Optional
 import logging
+from urllib.parse import urlparse
 
 from qdrant_client import QdrantClient as Qclient
 from qdrant_client.http.models import PointStruct
 from qdrant_client.models import models
 
 from open_webui.retrieval.vector.main import VectorItem, SearchResult, GetResult
-from open_webui.config import QDRANT_URI, QDRANT_API_KEY, QDRANT_ON_DISK
+from open_webui.config import (
+    QDRANT_URI,
+    QDRANT_API_KEY,
+    QDRANT_ON_DISK,
+    QDRANT_GRPC_PORT,
+    QDRANT_PREFER_GRPC,
+)
 from open_webui.env import SRC_LOG_LEVELS
 
 NO_LIMIT = 999999999
@@ -21,11 +28,28 @@ class QdrantClient:
         self.QDRANT_URI = QDRANT_URI
         self.QDRANT_API_KEY = QDRANT_API_KEY
         self.QDRANT_ON_DISK = QDRANT_ON_DISK
-        self.client = (
-            Qclient(url=self.QDRANT_URI, api_key=self.QDRANT_API_KEY)
-            if self.QDRANT_URI
-            else None
-        )
+        self.PREFER_GRPC = QDRANT_PREFER_GRPC
+        self.GRPC_PORT = QDRANT_GRPC_PORT
+
+        if not self.QDRANT_URI:
+            self.client = None
+            return
+
+        # Unified handling for either scheme
+        parsed = urlparse(self.QDRANT_URI)
+        host = parsed.hostname or self.QDRANT_URI
+        http_port = parsed.port or 6333  # default REST port
+
+        if self.PREFER_GRPC:
+            self.client = Qclient(
+                host=host,
+                port=http_port,
+                grpc_port=self.GRPC_PORT,
+                prefer_grpc=self.PREFER_GRPC,
+                api_key=self.QDRANT_API_KEY,
+            )
+        else:
+            self.client = Qclient(url=self.QDRANT_URI, api_key=self.QDRANT_API_KEY)
 
     def _result_to_get_result(self, points) -> GetResult:
         ids = []

+ 8 - 8
backend/open_webui/routers/knowledge.py

@@ -9,7 +9,7 @@ from open_webui.models.knowledge import (
     KnowledgeResponse,
     KnowledgeUserResponse,
 )
-from open_webui.models.files import Files, FileModel
+from open_webui.models.files import Files, FileModel, FileMetadataResponse
 from open_webui.retrieval.vector.connector import VECTOR_DB_CLIENT
 from open_webui.routers.retrieval import (
     process_file,
@@ -235,7 +235,7 @@ async def reindex_knowledge_files(request: Request, user=Depends(get_verified_us
 
 
 class KnowledgeFilesResponse(KnowledgeResponse):
-    files: list[FileModel]
+    files: list[FileMetadataResponse]
 
 
 @router.get("/{id}", response_model=Optional[KnowledgeFilesResponse])
@@ -251,7 +251,7 @@ async def get_knowledge_by_id(id: str, user=Depends(get_verified_user)):
         ):
 
             file_ids = knowledge.data.get("file_ids", []) if knowledge.data else []
-            files = Files.get_files_by_ids(file_ids)
+            files = Files.get_file_metadatas_by_ids(file_ids)
 
             return KnowledgeFilesResponse(
                 **knowledge.model_dump(),
@@ -379,7 +379,7 @@ def add_file_to_knowledge_by_id(
             knowledge = Knowledges.update_knowledge_data_by_id(id=id, data=data)
 
             if knowledge:
-                files = Files.get_files_by_ids(file_ids)
+                files = Files.get_file_metadatas_by_ids(file_ids)
 
                 return KnowledgeFilesResponse(
                     **knowledge.model_dump(),
@@ -456,7 +456,7 @@ def update_file_from_knowledge_by_id(
         data = knowledge.data or {}
         file_ids = data.get("file_ids", [])
 
-        files = Files.get_files_by_ids(file_ids)
+        files = Files.get_file_metadatas_by_ids(file_ids)
 
         return KnowledgeFilesResponse(
             **knowledge.model_dump(),
@@ -538,7 +538,7 @@ def remove_file_from_knowledge_by_id(
             knowledge = Knowledges.update_knowledge_data_by_id(id=id, data=data)
 
             if knowledge:
-                files = Files.get_files_by_ids(file_ids)
+                files = Files.get_file_metadatas_by_ids(file_ids)
 
                 return KnowledgeFilesResponse(
                     **knowledge.model_dump(),
@@ -734,7 +734,7 @@ def add_files_to_knowledge_batch(
         error_details = [f"{err.file_id}: {err.error}" for err in result.errors]
         return KnowledgeFilesResponse(
             **knowledge.model_dump(),
-            files=Files.get_files_by_ids(existing_file_ids),
+            files=Files.get_file_metadatas_by_ids(existing_file_ids),
             warnings={
                 "message": "Some files failed to process",
                 "errors": error_details,
@@ -742,5 +742,5 @@ def add_files_to_knowledge_batch(
         )
 
     return KnowledgeFilesResponse(
-        **knowledge.model_dump(), files=Files.get_files_by_ids(existing_file_ids)
+        **knowledge.model_dump(), files=Files.get_file_metadatas_by_ids(existing_file_ids)
     )

+ 7 - 1
backend/open_webui/routers/ollama.py

@@ -54,6 +54,7 @@ from open_webui.config import (
 from open_webui.env import (
     ENV,
     SRC_LOG_LEVELS,
+    AIOHTTP_CLIENT_SESSION_SSL,
     AIOHTTP_CLIENT_TIMEOUT,
     AIOHTTP_CLIENT_TIMEOUT_MODEL_LIST,
     BYPASS_MODEL_ACCESS_CONTROL,
@@ -91,6 +92,7 @@ async def send_get_request(url, key=None, user: UserModel = None):
                         else {}
                     ),
                 },
+                ssl=AIOHTTP_CLIENT_SESSION_SSL,
             ) as response:
                 return await response.json()
     except Exception as e:
@@ -141,6 +143,7 @@ async def send_post_request(
                     else {}
                 ),
             },
+            ssl=AIOHTTP_CLIENT_SESSION_SSL,
         )
         r.raise_for_status()
 
@@ -234,6 +237,7 @@ async def verify_connection(
                         else {}
                     ),
                 },
+                ssl=AIOHTTP_CLIENT_SESSION_SSL,
             ) as r:
                 if r.status != 200:
                     detail = f"HTTP Error: {r.status}"
@@ -1482,7 +1486,9 @@ async def download_file_stream(
     timeout = aiohttp.ClientTimeout(total=600)  # Set the timeout
 
     async with aiohttp.ClientSession(timeout=timeout, trust_env=True) as session:
-        async with session.get(file_url, headers=headers) as response:
+        async with session.get(
+            file_url, headers=headers, ssl=AIOHTTP_CLIENT_SESSION_SSL
+        ) as response:
             total_size = int(response.headers.get("content-length", 0)) + current_size
 
             with open(file_path, "ab+") as file:

+ 6 - 0
backend/open_webui/routers/openai.py

@@ -21,6 +21,7 @@ from open_webui.config import (
     CACHE_DIR,
 )
 from open_webui.env import (
+    AIOHTTP_CLIENT_SESSION_SSL,
     AIOHTTP_CLIENT_TIMEOUT,
     AIOHTTP_CLIENT_TIMEOUT_MODEL_LIST,
     ENABLE_FORWARD_USER_INFO_HEADERS,
@@ -74,6 +75,7 @@ async def send_get_request(url, key=None, user: UserModel = None):
                         else {}
                     ),
                 },
+                ssl=AIOHTTP_CLIENT_SESSION_SSL,
             ) as response:
                 return await response.json()
     except Exception as e:
@@ -481,6 +483,7 @@ async def get_models(
                             else {}
                         ),
                     },
+                    ssl=AIOHTTP_CLIENT_SESSION_SSL,
                 ) as r:
                     if r.status != 200:
                         # Extract response error details if available
@@ -561,6 +564,7 @@ async def verify_connection(
                         else {}
                     ),
                 },
+                ssl=AIOHTTP_CLIENT_SESSION_SSL,
             ) as r:
                 if r.status != 200:
                     # Extract response error details if available
@@ -723,6 +727,7 @@ async def generate_chat_completion(
                     else {}
                 ),
             },
+            ssl=AIOHTTP_CLIENT_SESSION_SSL,
         )
 
         # Check if response is SSE
@@ -802,6 +807,7 @@ async def proxy(path: str, request: Request, user=Depends(get_verified_user)):
                     else {}
                 ),
             },
+            ssl=AIOHTTP_CLIENT_SESSION_SSL,
         )
         r.raise_for_status()
 

+ 5 - 2
backend/open_webui/routers/retrieval.py

@@ -1477,8 +1477,11 @@ async def process_web_search(
         logging.info(
             f"trying to web search with {request.app.state.config.WEB_SEARCH_ENGINE, form_data.query}"
         )
-        web_results = search_web(
-            request, request.app.state.config.WEB_SEARCH_ENGINE, form_data.query
+        web_results = await run_in_threadpool(
+            search_web,
+            request,
+            request.app.state.config.WEB_SEARCH_ENGINE,
+            form_data.query,
         )
     except Exception as e:
         log.exception(e)

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

@@ -288,6 +288,19 @@ async def update_user_by_id(
     form_data: UserUpdateForm,
     session_user=Depends(get_admin_user),
 ):
+    # Prevent modification of the primary admin user by other admins
+    try:
+        first_user = Users.get_first_user()
+        if first_user and user_id == first_user.id and session_user.id != user_id:
+            raise HTTPException(
+                status_code=status.HTTP_403_FORBIDDEN,
+                detail=ERROR_MESSAGES.ACTION_PROHIBITED,
+            )
+    except Exception as e:
+        log.error(f"Error checking primary admin status: {e}")
+        raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="Could not verify primary admin status.")
+
+
     user = Users.get_user_by_id(user_id)
 
     if user:
@@ -328,6 +341,7 @@ async def update_user_by_id(
     )
 
 
+
 ############################
 # DeleteUserById
 ############################
@@ -335,6 +349,18 @@ async def update_user_by_id(
 
 @router.delete("/{user_id}", response_model=bool)
 async def delete_user_by_id(user_id: str, user=Depends(get_admin_user)):
+    # Prevent deletion of the primary admin user
+    try:
+        first_user = Users.get_first_user()
+        if first_user and user_id == first_user.id:
+            raise HTTPException(
+                status_code=status.HTTP_403_FORBIDDEN,
+                detail=ERROR_MESSAGES.ACTION_PROHIBITED,
+            )
+    except Exception as e:
+        log.error(f"Error checking primary admin status: {e}")
+        raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="Could not verify primary admin status.")
+
     if user.id != user_id:
         result = Auths.delete_auth_by_id(user_id)
 
@@ -346,6 +372,7 @@ async def delete_user_by_id(user_id: str, user=Depends(get_admin_user)):
             detail=ERROR_MESSAGES.DELETE_USER_ERROR,
         )
 
+    # Prevent self-deletion
     raise HTTPException(
         status_code=status.HTTP_403_FORBIDDEN,
         detail=ERROR_MESSAGES.ACTION_PROHIBITED,

+ 10 - 6
backend/open_webui/utils/middleware.py

@@ -888,16 +888,20 @@ async def process_chat_payload(request, form_data, user, metadata, model):
     # If context is not empty, insert it into the messages
     if len(sources) > 0:
         context_string = ""
-        citated_file_idx = {}
-        for _, source in enumerate(sources, 1):
+        citation_idx = {}
+        for source in sources:
             if "document" in source:
                 for doc_context, doc_meta in zip(
                     source["document"], source["metadata"]
                 ):
-                    file_id = doc_meta.get("file_id")
-                    if file_id not in citated_file_idx:
-                        citated_file_idx[file_id] = len(citated_file_idx) + 1
-                    context_string += f'<source id="{citated_file_idx[file_id]}">{doc_context}</source>\n'
+                    citation_id = (
+                        doc_meta.get("source", None)
+                        or source.get("source", {}).get("id", None)
+                        or "N/A"
+                    )
+                    if citation_id not in citation_idx:
+                        citation_idx[citation_id] = len(citation_idx) + 1
+                    context_string += f'<source id="{citation_idx[citation_id]}">{doc_context}</source>\n'
 
         context_string = context_string.strip()
         prompt = get_last_user_message(form_data["messages"])

+ 28 - 7
backend/open_webui/utils/tools.py

@@ -36,7 +36,10 @@ from langchain_core.utils.function_calling import (
 from open_webui.models.tools import Tools
 from open_webui.models.users import UserModel
 from open_webui.utils.plugin import load_tool_module_by_id
-from open_webui.env import AIOHTTP_CLIENT_TIMEOUT_TOOL_SERVER_DATA
+from open_webui.env import (
+    AIOHTTP_CLIENT_TIMEOUT_TOOL_SERVER_DATA,
+    AIOHTTP_CLIENT_SESSION_TOOL_SERVER_SSL,
+)
 
 import copy
 
@@ -384,9 +387,18 @@ def convert_openapi_to_tool_payload(openapi_spec):
             for param in operation.get("parameters", []):
                 param_name = param["name"]
                 param_schema = param.get("schema", {})
+                description = param_schema.get("description", "")
+                if not description:
+                    description = param.get("description") or ""
+                if param_schema.get("enum") and isinstance(
+                    param_schema.get("enum"), list
+                ):
+                    description += (
+                        f". Possible values: {', '.join(param_schema.get('enum'))}"
+                    )
                 tool["parameters"]["properties"][param_name] = {
                     "type": param_schema.get("type"),
-                    "description": param_schema.get("description", ""),
+                    "description": description,
                 }
                 if param.get("required"):
                     tool["parameters"]["required"].append(param_name)
@@ -431,8 +443,10 @@ async def get_tool_server_data(token: str, url: str) -> Dict[str, Any]:
     error = None
     try:
         timeout = aiohttp.ClientTimeout(total=AIOHTTP_CLIENT_TIMEOUT_TOOL_SERVER_DATA)
-        async with aiohttp.ClientSession(timeout=timeout) as session:
-            async with session.get(url, headers=headers) as response:
+        async with aiohttp.ClientSession(timeout=timeout, trust_env=True) as session:
+            async with session.get(
+                url, headers=headers, ssl=AIOHTTP_CLIENT_SESSION_TOOL_SERVER_SSL
+            ) as response:
                 if response.status != 200:
                     error_body = await response.json()
                     raise Exception(error_body)
@@ -573,19 +587,26 @@ async def execute_tool_server(
         if token:
             headers["Authorization"] = f"Bearer {token}"
 
-        async with aiohttp.ClientSession() as session:
+        async with aiohttp.ClientSession(trust_env=True) as session:
             request_method = getattr(session, http_method.lower())
 
             if http_method in ["post", "put", "patch"]:
                 async with request_method(
-                    final_url, json=body_params, headers=headers
+                    final_url,
+                    json=body_params,
+                    headers=headers,
+                    ssl=AIOHTTP_CLIENT_SESSION_TOOL_SERVER_SSL,
                 ) as response:
                     if response.status >= 400:
                         text = await response.text()
                         raise Exception(f"HTTP error {response.status}: {text}")
                     return await response.json()
             else:
-                async with request_method(final_url, headers=headers) as response:
+                async with request_method(
+                    final_url,
+                    headers=headers,
+                    ssl=AIOHTTP_CLIENT_SESSION_TOOL_SERVER_SSL,
+                ) as response:
                     if response.status >= 400:
                         text = await response.text()
                         raise Exception(f"HTTP error {response.status}: {text}")

+ 1 - 1
src/lib/components/channel/Channel.svelte

@@ -262,7 +262,7 @@
 			{#if threadId !== null}
 				<Drawer
 					show={threadId !== null}
-					on:close={() => {
+					onClose={() => {
 						threadId = null;
 					}}
 				>

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

@@ -140,7 +140,7 @@
 		{#if $showControls}
 			<Drawer
 				show={$showControls}
-				on:close={() => {
+				onClose={() => {
 					showControls.set(false);
 				}}
 			>

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

@@ -1063,9 +1063,9 @@
 													);
 												}
 											}}
-											uploadOneDriveHandler={async () => {
+											uploadOneDriveHandler={async (authorityType) => {
 												try {
-													const fileData = await pickAndDownloadFile();
+													const fileData = await pickAndDownloadFile(authorityType);
 													if (fileData) {
 														const file = new File([fileData.blob], fileData.name, {
 															type: fileData.blob.type || 'application/octet-stream'

+ 59 - 87
src/lib/components/chat/MessageInput/InputMenu.svelte

@@ -229,94 +229,66 @@
 			{/if}
 
 			{#if $config?.features?.enable_onedrive_integration}
-				<DropdownMenu.Item
-					class="flex gap-2 items-center px-3 py-2 text-sm font-medium cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800 rounded-xl"
-					on:click={() => {
-						uploadOneDriveHandler();
-					}}
-				>
-					<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" class="w-5 h-5" fill="none">
-						<mask
-							id="mask0_87_7796"
-							style="mask-type:alpha"
-							maskUnits="userSpaceOnUse"
-							x="0"
-							y="6"
-							width="32"
-							height="20"
+				<DropdownMenu.Sub>
+					<DropdownMenu.SubTrigger class="flex gap-2 items-center px-3 py-2 text-sm font-medium cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800 rounded-xl w-full">
+						<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" class="w-5 h-5" fill="none">
+							<mask id="mask0_87_7796" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="0" y="6" width="32" height="20">
+								<path d="M7.82979 26C3.50549 26 0 22.5675 0 18.3333C0 14.1921 3.35322 10.8179 7.54613 10.6716C9.27535 7.87166 12.4144 6 16 6C20.6308 6 24.5169 9.12183 25.5829 13.3335C29.1316 13.3603 32 16.1855 32 19.6667C32 23.0527 29 26 25.8723 25.9914L7.82979 26Z" fill="#C4C4C4"/>
+							</mask>
+							<g mask="url(#mask0_87_7796)">
+								<path d="M7.83017 26.0001C5.37824 26.0001 3.18957 24.8966 1.75391 23.1691L18.0429 16.3335L30.7089 23.4647C29.5926 24.9211 27.9066 26.0001 26.0004 25.9915C23.1254 26.0001 12.0629 26.0001 7.83017 26.0001Z" fill="url(#paint0_linear_87_7796)"/>
+								<path d="M25.5785 13.3149L18.043 16.3334L30.709 23.4647C31.5199 22.4065 32.0004 21.0916 32.0004 19.6669C32.0004 16.1857 29.1321 13.3605 25.5833 13.3337C25.5817 13.3274 25.5801 13.3212 25.5785 13.3149Z" fill="url(#paint1_linear_87_7796)"/>
+								<path d="M7.06445 10.7028L18.0423 16.3333L25.5779 13.3148C24.5051 9.11261 20.6237 6 15.9997 6C12.4141 6 9.27508 7.87166 7.54586 10.6716C7.3841 10.6773 7.22358 10.6877 7.06445 10.7028Z" fill="url(#paint2_linear_87_7796)"/>
+								<path d="M1.7535 23.1687L18.0425 16.3331L7.06471 10.7026C3.09947 11.0792 0 14.3517 0 18.3331C0 20.1665 0.657197 21.8495 1.7535 23.1687Z" fill="url(#paint3_linear_87_7796)"/>
+							</g>
+							<defs>
+								<linearGradient id="paint0_linear_87_7796" x1="4.42591" y1="24.6668" x2="27.2309" y2="23.2764" gradientUnits="userSpaceOnUse">
+									<stop stop-color="#2086B8"/>
+									<stop offset="1" stop-color="#46D3F6"/>
+								</linearGradient>
+								<linearGradient id="paint1_linear_87_7796" x1="23.8302" y1="19.6668" x2="30.2108" y2="15.2082" gradientUnits="userSpaceOnUse">
+									<stop stop-color="#1694DB"/>
+									<stop offset="1" stop-color="#62C3FE"/>
+								</linearGradient>
+								<linearGradient id="paint2_linear_87_7796" x1="8.51037" y1="7.33333" x2="23.3335" y2="15.9348" gradientUnits="userSpaceOnUse">
+									<stop stop-color="#0D3D78"/>
+									<stop offset="1" stop-color="#063B83"/>
+								</linearGradient>
+								<linearGradient id="paint3_linear_87_7796" x1="-0.340429" y1="19.9998" x2="14.5634" y2="14.4649" gradientUnits="userSpaceOnUse">
+									<stop stop-color="#16589B"/>
+									<stop offset="1" stop-color="#1464B7"/>
+								</linearGradient>
+							</defs>
+						</svg>
+						<div class="line-clamp-1">{$i18n.t('Microsoft OneDrive')}</div>
+					</DropdownMenu.SubTrigger>
+					<DropdownMenu.SubContent 
+						class="w-[calc(100vw-2rem)] max-w-[280px] rounded-xl px-1 py-1 border border-gray-300/30 dark:border-gray-700/50 z-50 bg-white dark:bg-gray-850 dark:text-white shadow-sm"
+						side={$mobile ? "bottom" : "right"}
+						sideOffset={$mobile ? 5 : 0}
+						alignOffset={$mobile ? 0 : -8}
+					>
+						<DropdownMenu.Item
+							class="flex gap-2 items-center px-3 py-2 text-sm font-medium cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800 rounded-xl"
+							on:click={() => {
+								uploadOneDriveHandler('personal');
+							}}
 						>
-							<path
-								d="M7.82979 26C3.50549 26 0 22.5675 0 18.3333C0 14.1921 3.35322 10.8179 7.54613 10.6716C9.27535 7.87166 12.4144 6 16 6C20.6308 6 24.5169 9.12183 25.5829 13.3335C29.1316 13.3603 32 16.1855 32 19.6667C32 23.0527 29 26 25.8723 25.9914L7.82979 26Z"
-								fill="#C4C4C4"
-							/>
-						</mask>
-						<g mask="url(#mask0_87_7796)">
-							<path
-								d="M7.83017 26.0001C5.37824 26.0001 3.18957 24.8966 1.75391 23.1691L18.0429 16.3335L30.7089 23.4647C29.5926 24.9211 27.9066 26.0001 26.0004 25.9915C23.1254 26.0001 12.0629 26.0001 7.83017 26.0001Z"
-								fill="url(#paint0_linear_87_7796)"
-							/>
-							<path
-								d="M25.5785 13.3149L18.043 16.3334L30.709 23.4647C31.5199 22.4065 32.0004 21.0916 32.0004 19.6669C32.0004 16.1857 29.1321 13.3605 25.5833 13.3337C25.5817 13.3274 25.5801 13.3212 25.5785 13.3149Z"
-								fill="url(#paint1_linear_87_7796)"
-							/>
-							<path
-								d="M7.06445 10.7028L18.0423 16.3333L25.5779 13.3148C24.5051 9.11261 20.6237 6 15.9997 6C12.4141 6 9.27508 7.87166 7.54586 10.6716C7.3841 10.6773 7.22358 10.6877 7.06445 10.7028Z"
-								fill="url(#paint2_linear_87_7796)"
-							/>
-							<path
-								d="M1.7535 23.1687L18.0425 16.3331L7.06471 10.7026C3.09947 11.0792 0 14.3517 0 18.3331C0 20.1665 0.657197 21.8495 1.7535 23.1687Z"
-								fill="url(#paint3_linear_87_7796)"
-							/>
-						</g>
-						<defs>
-							<linearGradient
-								id="paint0_linear_87_7796"
-								x1="4.42591"
-								y1="24.6668"
-								x2="27.2309"
-								y2="23.2764"
-								gradientUnits="userSpaceOnUse"
-							>
-								<stop stop-color="#2086B8" />
-								<stop offset="1" stop-color="#46D3F6" />
-							</linearGradient>
-							<linearGradient
-								id="paint1_linear_87_7796"
-								x1="23.8302"
-								y1="19.6668"
-								x2="30.2108"
-								y2="15.2082"
-								gradientUnits="userSpaceOnUse"
-							>
-								<stop stop-color="#1694DB" />
-								<stop offset="1" stop-color="#62C3FE" />
-							</linearGradient>
-							<linearGradient
-								id="paint2_linear_87_7796"
-								x1="8.51037"
-								y1="7.33333"
-								x2="23.3335"
-								y2="15.9348"
-								gradientUnits="userSpaceOnUse"
-							>
-								<stop stop-color="#0D3D78" />
-								<stop offset="1" stop-color="#063B83" />
-							</linearGradient>
-							<linearGradient
-								id="paint3_linear_87_7796"
-								x1="-0.340429"
-								y1="19.9998"
-								x2="14.5634"
-								y2="14.4649"
-								gradientUnits="userSpaceOnUse"
-							>
-								<stop stop-color="#16589B" />
-								<stop offset="1" stop-color="#1464B7" />
-							</linearGradient>
-						</defs>
-					</svg>
-					<div class="line-clamp-1">{$i18n.t('OneDrive')}</div>
-				</DropdownMenu.Item>
+							<div class="line-clamp-1">{$i18n.t('Microsoft OneDrive (personal)')}</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-xl"
+							on:click={() => {
+								uploadOneDriveHandler('organizations');
+							}}
+						>
+							<div class="flex flex-col">
+								<div class="line-clamp-1">{$i18n.t('Microsoft OneDrive (work/school)')}</div>
+								<div class="text-xs text-gray-500">Includes SharePoint</div>
+							</div>
+						</DropdownMenu.Item>
+					</DropdownMenu.SubContent>
+				</DropdownMenu.Sub>
 			{/if}
 		</DropdownMenu.Content>
 	</div>

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

@@ -83,6 +83,7 @@
 			});
 			return acc;
 		}, []);
+		console.log('citations', citations);
 
 		showRelevance = calculateShowRelevance(citations);
 		showPercentage = shouldShowPercentage(citations);

+ 4 - 4
src/lib/components/chat/Messages/ContentRenderer.svelte

@@ -154,11 +154,11 @@
 		}, [])}
 		{onSourceClick}
 		{onTaskClick}
-		on:update={(e) => {
-			dispatch('update', e.detail);
+		onUpdate={(value) => {
+			dispatch('update', value);
 		}}
-		on:code={(e) => {
-			const { lang, code } = e.detail;
+		onCode={(value) => {
+			const { lang, code } = value;
 
 			if (
 				($settings?.detectArtifacts ?? true) &&

+ 4 - 16
src/lib/components/chat/Messages/Markdown.svelte

@@ -7,9 +7,6 @@
 	import markedKatexExtension from '$lib/utils/marked/katex-extension';
 
 	import MarkdownTokens from './Markdown/MarkdownTokens.svelte';
-	import { createEventDispatcher } from 'svelte';
-
-	const dispatch = createEventDispatcher();
 
 	export let id = '';
 	export let content;
@@ -18,6 +15,9 @@
 
 	export let sourceIds = [];
 
+	export let onUpdate = () => {};
+	export let onCode = () => {};
+
 	export let onSourceClick = () => {};
 	export let onTaskClick = () => {};
 
@@ -40,17 +40,5 @@
 </script>
 
 {#key id}
-	<MarkdownTokens
-		{tokens}
-		{id}
-		{save}
-		{onTaskClick}
-		{onSourceClick}
-		on:update={(e) => {
-			dispatch('update', e.detail);
-		}}
-		on:code={(e) => {
-			dispatch('code', e.detail);
-		}}
-	/>
+	<MarkdownTokens {tokens} {id} {save} {onTaskClick} {onSourceClick} {onUpdate} {onCode} />
 {/key}

+ 6 - 7
src/lib/components/chat/Messages/Markdown/MarkdownTokens.svelte

@@ -1,6 +1,6 @@
 <script lang="ts">
 	import DOMPurify from 'dompurify';
-	import { createEventDispatcher, onMount, getContext } from 'svelte';
+	import { onMount, getContext } from 'svelte';
 	const i18n = getContext('i18n');
 
 	import fileSaver from 'file-saver';
@@ -22,8 +22,6 @@
 	import Source from './Source.svelte';
 	import { settings } from '$lib/stores';
 
-	const dispatch = createEventDispatcher();
-
 	export let id: string;
 	export let tokens: Token[];
 	export let top = true;
@@ -31,6 +29,9 @@
 
 	export let save = false;
 
+	export let onUpdate: Function = () => {};
+	export let onCode: Function = () => {};
+
 	export let onTaskClick: Function = () => {};
 	export let onSourceClick: Function = () => {};
 
@@ -93,11 +94,9 @@
 				code={token?.text ?? ''}
 				{attributes}
 				{save}
-				onCode={(value) => {
-					dispatch('code', value);
-				}}
+				{onCode}
 				onSave={(value) => {
-					dispatch('update', {
+					onUpdate({
 						raw: token.raw,
 						oldContent: token.text,
 						newContent: value

+ 5 - 3
src/lib/components/chat/Messages/UserMessage.svelte

@@ -65,10 +65,12 @@
 
 		await tick();
 
-		messageEditTextAreaElement.style.height = '';
-		messageEditTextAreaElement.style.height = `${messageEditTextAreaElement.scrollHeight}px`;
+		if (messageEditTextAreaElement) {
+			messageEditTextAreaElement.style.height = '';
+			messageEditTextAreaElement.style.height = `${messageEditTextAreaElement.scrollHeight}px`;
 
-		messageEditTextAreaElement?.focus();
+			messageEditTextAreaElement?.focus();
+		}
 	};
 
 	const editMessageConfirmHandler = async (submit = true) => {

+ 3 - 4
src/lib/components/common/Drawer.svelte

@@ -1,13 +1,12 @@
 <script lang="ts">
-	import { onDestroy, onMount, createEventDispatcher } from 'svelte';
+	import { onDestroy, onMount } from 'svelte';
 	import { flyAndScale } from '$lib/utils/transitions';
 	import { fade, fly, slide } from 'svelte/transition';
 	import { isApp } from '$lib/stores';
 
-	const dispatch = createEventDispatcher();
-
 	export let show = false;
 	export let className = '';
+	export let onClose = () => {};
 
 	let modalElement = null;
 	let mounted = false;
@@ -33,7 +32,7 @@
 		window.addEventListener('keydown', handleKeyDown);
 		document.body.style.overflow = 'hidden';
 	} else if (modalElement) {
-		dispatch('close');
+		onClose();
 		window.removeEventListener('keydown', handleKeyDown);
 
 		if (document.body.contains(modalElement)) {

+ 4 - 1
src/lib/components/common/Textarea.svelte

@@ -4,6 +4,7 @@
 	export let value = '';
 	export let placeholder = '';
 	export let rows = 1;
+	export let minSize = null;
 	export let required = false;
 	export let className =
 		'w-full rounded-lg px-3 py-2 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-hidden  h-full';
@@ -29,7 +30,9 @@
 	const resize = () => {
 		if (textareaElement) {
 			textareaElement.style.height = '';
-			textareaElement.style.height = `${textareaElement.scrollHeight}px`;
+			textareaElement.style.height = minSize
+				? `${Math.max(textareaElement.scrollHeight, minSize)}px`
+				: `${textareaElement.scrollHeight}px`;
 		}
 	};
 </script>

+ 36 - 7
src/lib/components/workspace/Knowledge/KnowledgeBase.svelte

@@ -11,7 +11,7 @@
 	import { page } from '$app/stores';
 	import { mobile, showSidebar, knowledge as _knowledge, config, user } from '$lib/stores';
 
-	import { updateFileDataContentById, uploadFile, deleteFileById } from '$lib/apis/files';
+	import { updateFileDataContentById, uploadFile, deleteFileById, getFileById } from '$lib/apis/files';
 	import {
 		addFileToKnowledgeById,
 		getKnowledgeById,
@@ -84,12 +84,15 @@
 
 	let selectedFile = null;
 	let selectedFileId = null;
+	let selectedFileContent = '';
+
+	// Add cache object
+	let fileContentCache = new Map();
 
 	$: if (selectedFileId) {
 		const file = (knowledge?.files ?? []).find((file) => file.id === selectedFileId);
 		if (file) {
-			file.data = file.data ?? { content: '' };
-			selectedFile = file;
+			fileSelectHandler(file);
 		} else {
 			selectedFile = null;
 		}
@@ -394,7 +397,10 @@
 
 	const updateFileContentHandler = async () => {
 		const fileId = selectedFile.id;
-		const content = selectedFile.data.content;
+		const content = selectedFileContent;
+
+		// Clear the cache for this file since we're updating it
+		fileContentCache.delete(fileId);
 
 		const res = updateFileDataContentById(localStorage.token, fileId, content).catch((e) => {
 			toast.error(`${e}`);
@@ -450,6 +456,29 @@
 		}
 	};
 
+	const fileSelectHandler = async (file) => {
+		try {
+			selectedFile = file;
+			
+			// Check cache first
+			if (fileContentCache.has(file.id)) {
+				selectedFileContent = fileContentCache.get(file.id);
+				return;
+			}
+
+			const response = await getFileById(localStorage.token, file.id);
+			if (response) {
+				selectedFileContent = response.data.content;
+				// Cache the content
+				fileContentCache.set(file.id, response.data.content);
+			} else {
+				toast.error($i18n.t('No content found in file.'));
+			}
+		} catch (e) {
+			toast.error($i18n.t('Failed to load file content.'));
+		}
+	};  
+
 	const onDragOver = (e) => {
 		e.preventDefault();
 
@@ -728,7 +757,7 @@
 								{#key selectedFile.id}
 									<RichTextInput
 										className="input-prose-sm"
-										bind:value={selectedFile.data.content}
+										bind:value={selectedFileContent}
 										placeholder={$i18n.t('Add content here')}
 										preserveBreaks={true}
 									/>
@@ -747,7 +776,7 @@
 				<Drawer
 					className="h-full"
 					show={selectedFileId !== null}
-					on:close={() => {
+					onClose={() => {
 						selectedFileId = null;
 					}}
 				>
@@ -786,7 +815,7 @@
 								{#key selectedFile.id}
 									<RichTextInput
 										className="input-prose-sm"
-										bind:value={selectedFile.data.content}
+										bind:value={selectedFileContent}
 										placeholder={$i18n.t('Add content here')}
 										preserveBreaks={true}
 									/>

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

@@ -56,6 +56,7 @@
 		}
 	}
 
+	let system = '';
 	let info = {
 		id: '',
 		base_model_id: null,
@@ -155,6 +156,7 @@
 			}
 		}
 
+		info.params.system = system.trim() === '' ? null : system;
 		info.params.stop = params.stop ? params.stop.split(',').filter((s) => s.trim()) : null;
 		Object.keys(info.params).forEach((key) => {
 			if (info.params[key] === '' || info.params[key] === null) {
@@ -201,6 +203,8 @@
 				}
 			}
 
+			system = model?.params?.system ?? '';
+
 			params = { ...params, ...model?.params };
 			params.stop = params?.stop
 				? (typeof params.stop === 'string' ? params.stop.split(',') : (params?.stop ?? [])).join(
@@ -553,7 +557,7 @@
 										className=" text-sm w-full bg-transparent outline-hidden resize-none overflow-y-hidden "
 										placeholder={`Write your model system prompt content here\ne.g.) You are Mario from Super Mario Bros, acting as an assistant.`}
 										rows={4}
-										bind:value={info.params.system}
+										bind:value={system}
 									/>
 								</div>
 							</div>

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

@@ -14,29 +14,29 @@
 	"*Prompt node ID(s) are required for image generation": "Los ID de nodo son requeridos para la generación de imágenes",
 	"A new version (v{{LATEST_VERSION}}) is now available.": "Nueva versión (v{{LATEST_VERSION}}) disponible.",
 	"A task model is used when performing tasks such as generating titles for chats and web search queries": "El modelo de tareas realiza tareas como la generación de títulos para chats y consultas de búsqueda web",
-	"a user": "un/a usuari@",
+	"a user": "un usuario",
 	"About": "Acerca de",
 	"Accept autocomplete generation / Jump to prompt variable": "Aceptar generación de autocompletado / Saltar a indicador variable",
 	"Access": "Acceso",
 	"Access Control": "Control de Acceso",
-	"Accessible to all users": "Accesible para todos l@s usuari@s",
+	"Accessible to all users": "Accesible para todos los usuarios",
 	"Account": "Cuenta",
 	"Account Activation Pending": "Activación de cuenta Pendiente",
 	"Accurate information": "Información precisa",
 	"Actions": "Acciones",
 	"Activate": "Activar",
 	"Activate this command by typing \"/{{COMMAND}}\" to chat input.": "Activar este comando escribiendo \"/{{COMMAND}}\" en el chat",
-	"Active Users": "Usuari@s activos",
+	"Active Users": "Usuarios activos",
 	"Add": "Añadir",
 	"Add a model ID": "Añadir un ID de modelo",
 	"Add a short description about what this model does": "Añadir una breve descripción sobre lo que hace este modelo",
 	"Add a tag": "Añadir una etiqueta",
-	"Add Arena Model": "Añadir un modelo a la Arena",
+	"Add Arena Model": "Añadir modelo a la Arena",
 	"Add Connection": "Añadir Conexión",
 	"Add Content": "Añadir Contenido",
 	"Add content here": "Añadir contenido aquí",
 	"Add custom prompt": "Añadir un indicador personalizado",
-	"Add Files": "Añadir Ficheros",
+	"Add Files": "Añadir Archivos",
 	"Add Group": "Añadir Grupo",
 	"Add Memory": "Añadir Memoria",
 	"Add Model": "Añadir Modelo",
@@ -44,31 +44,31 @@
 	"Add Tag": "Añadir etiqueta",
 	"Add Tags": "Añadir etiquetas",
 	"Add text content": "Añade contenido de texto",
-	"Add User": "Añadir Usuari@",
-	"Add User Group": "Añadir Grupo de Usuari@",
-	"Adjusting these settings will apply changes universally to all users.": "El ajuste de estas opciones se aplicará globalmente a todos l@s usuari@s.",
+	"Add User": "Añadir Usuario",
+	"Add User Group": "Añadir grupo de usuarios",
+	"Adjusting these settings will apply changes universally to all users.": "El ajuste de estas opciones se aplicará globalmente a todos los usuarios.",
 	"admin": "admin",
 	"Admin": "Admin",
 	"Admin Panel": "Administración",
 	"Admin Settings": "Ajustes de Admin",
-	"Admins have access to all tools at all times; users need tools assigned per model in the workspace.": "Los Admins tienen acceso a todas las herramientas en todo momento; l@s usuari@s necesitan, en el área de trabajo, que los modelos tengan asignadas las herramentas.",
+	"Admins have access to all tools at all times; users need tools assigned per model in the workspace.": "Los administradores tienen acceso a todas las herramientas en todo momento; los usuarios necesitan que los modelos tengan asignadas las herramientas en el area de trabajo.",
 	"Advanced Parameters": "Parámetros Avanzados",
-	"Advanced Params": "Param. Avanz.",
+	"Advanced Params": "Parámetros Avanzados",
 	"All": "Todos",
 	"All Documents": "Todos los Documentos",
 	"All models deleted successfully": "Todos los modelos borrados correctamnete",
-	"Allow Call": "",
+	"Allow Call": "Permitir Llamada",
 	"Allow Chat Controls": "Permitir Controles del Chat",
 	"Allow Chat Delete": "Permitir Borrar Chat",
 	"Allow Chat Deletion": "Permitir Borrado de Chat",
 	"Allow Chat Edit": "Pemritir Editar Chat",
-	"Allow File Upload": "Permitir Subida de Ficheros",
+	"Allow File Upload": "Permitir Subida de Archivos",
 	"Allow Multiple Models in Chat": "",
 	"Allow non-local voices": "Permitir voces no locales",
 	"Allow Speech to Text": "",
 	"Allow Temporary Chat": "Permitir Chat Temporal",
 	"Allow Text to Speech": "",
-	"Allow User Location": "Permitir Ubicación de Usuari@",
+	"Allow User Location": "Permitir Ubicación de Usuario",
 	"Allow Voice Interruption in Call": "Permitir Interrupción de Voz en Llamada",
 	"Allowed Endpoints": "Endpoints Permitidos",
 	"Already have an account?": "¿Ya tienes una cuenta?",
@@ -91,34 +91,34 @@
 	"API keys": "Claves API",
 	"Application DN": "Aplicacion DN",
 	"Application DN Password": "Contraseña Aplicacion DN",
-	"applies to all users with the \"user\" role": "se aplica a todos l@s usuari@s con el rol \"user\" ",
+	"applies to all users with the \"user\" role": "se aplica a todos los usuarios con el rol \"user\" ",
 	"April": "Abril",
 	"Archive": "Archivar",
 	"Archive All Chats": "Archivar Todos los Chats",
 	"Archived Chats": "Chats archivados",
 	"archived-chat-export": "exportar chats archivados",
-	"Are you sure you want to clear all memories? This action cannot be undone.": "¿estas segur@ que quieres borrar todas las memorias? (¡esta acción NO se puede deshacer!)",
-	"Are you sure you want to delete this channel?": "¿Estás segur@ de que quieres eliminar este canal?",
-	"Are you sure you want to delete this message?": "¿Estás segur@ de que quieres eliminar este mensaje? ",
-	"Are you sure you want to unarchive all archived chats?": "¿Estás segur@ de que quieres desarchivar todos los chats archivados?",
-	"Are you sure?": "¿Está segur@?",
+	"Are you sure you want to clear all memories? This action cannot be undone.": "¿Seguro que quieres borrar todas las memorias? (¡esta acción NO se puede deshacer!)",
+	"Are you sure you want to delete this channel?": "¿Seguro de que quieres eliminar este canal?",
+	"Are you sure you want to delete this message?": "¿Seguro de que quieres eliminar este mensaje? ",
+	"Are you sure you want to unarchive all archived chats?": "¿Seguro de que quieres desarchivar todos los chats archivados?",
+	"Are you sure?": "¿Estás seguro?",
 	"Arena Models": "Arena de Modelos",
 	"Artifacts": "Artefactos",
 	"Ask": "Preguntar",
 	"Ask a question": "Haz una pregunta",
 	"Assistant": "Asistente",
-	"Attach file from knowledge": "Adjuntar fichero desde el conocimiento",
+	"Attach file from knowledge": "Adjuntar archivo desde conocimiento",
 	"Attention to detail": "Atención al detalle",
 	"Attribute for Mail": "Atributo para Correo",
-	"Attribute for Username": "Atributo para Nombre de Usuari@",
+	"Attribute for Username": "Atributo para Nombre de Usuario",
 	"Audio": "Audio",
 	"August": "Agosto",
 	"Auth": "Autorización",
 	"Authenticate": "Autentificar",
-	"Authentication": "Autentificación",
+	"Authentication": "Autenticación",
 	"Auto": "Auto",
 	"Auto-Copy Response to Clipboard": "AutoCopiado de respuesta al Portapapeles",
-	"Auto-playback response": "AutoReproducir Respuesta",
+	"Auto-playback response": "Reproducir Respuesta automáticamente",
 	"Autocomplete Generation": "Generación de Autocompletado",
 	"Autocomplete Generation Input Max Length": "Max. Longitud de Entrada en Generación de Autocompletado",
 	"Automatic1111": "AUTOMATIC1111",
@@ -126,7 +126,7 @@
 	"AUTOMATIC1111 Base URL": "URL Base de AUTOMATIC1111",
 	"AUTOMATIC1111 Base URL is required.": "la URL Base de AUTOMATIC1111 es necesaria.",
 	"Available list": "Lista disponible",
-	"Available Tools": "",
+	"Available Tools": "Herramientas Disponibles",
 	"available!": "¡disponible!",
 	"Awful": "Horrible",
 	"Azure AI Speech": "Voz Azure AI",
@@ -148,23 +148,23 @@
 	"Bypass Embedding and Retrieval": "Evitar Incrustración y Recuperación",
 	"Calendar": "Calendario",
 	"Call": "Llamada",
-	"Call feature is not supported when using Web STT engine": "La característica Llamada no está soportada cuando se usa el motor Web STT",
+	"Call feature is not supported when using Web STT engine": "La funcionalidad de Llamada no está soportada cuando se usa el motor Web STT",
 	"Camera": "Cámara",
 	"Cancel": "Cancelar",
 	"Capabilities": "Capacidades",
 	"Capture": "Captura",
 	"Certificate Path": "Ruta a Certificado",
-	"Change Password": "Cambia la Contraseña",
-	"Channel Name": "Nombre de Canal",
+	"Change Password": "Cambiar Contraseña",
+	"Channel Name": "Nombre del Canal",
 	"Channels": "Canal",
 	"Character": "Carácter",
 	"Character limit for autocomplete generation input": "Límite de caracteres de entrada de la generación de autocompletado",
 	"Chart new frontiers": "Trazar nuevas fronteras",
 	"Chat": "Chat",
 	"Chat Background Image": "Imágen de Fondo del Chat",
-	"Chat Bubble UI": "Interface del Chat tipo Burbuja",
-	"Chat Controls": "Controles del chat",
-	"Chat direction": "Dirección del Chat",
+	"Chat Bubble UI": "Interface de Chat tipo Burbuja",
+	"Chat Controls": "Controles de chat",
+	"Chat direction": "Dirección de Chat",
 	"Chat Overview": "Vista General del Chat",
 	"Chat Permissions": "Permisos del Chat",
 	"Chat Tags Auto-Generation": "AutoGeneración de Etiquetas de Chat",
@@ -183,15 +183,15 @@
 	"Click here for filter guides.": "Pulsar aquí para guías de filtros",
 	"Click here for help.": "Pulsar aquí para Ayuda.",
 	"Click here to": "Pulsa aquí para",
-	"Click here to download user import template file.": "Pulsa aquí para descargar la plantilla de importación de usuari@.",
-	"Click here to learn more about faster-whisper and see the available models.": "Pulsa aquí para saber más sobre faster-whisper y ver modelos disponibles.",
+	"Click here to download user import template file.": "Pulsa aquí para descargar la plantilla para importar usuarios.",
+	"Click here to learn more about faster-whisper and see the available models.": "Pulsa aquí para saber más sobre faster-whisper y ver los modelos disponibles.",
 	"Click here to see available models.": "Pulsa aquí para ver modelos disponibles.",
 	"Click here to select": "Pulsa aquí para seleccionar",
 	"Click here to select a csv file.": "Pulsa aquí para seleccionar un fichero de Valores Separados por Comas (.csv)",
 	"Click here to select a py file.": "Pulsa aquí para seleccionar un fichero Python (.py)",
 	"Click here to upload a workflow.json file.": "Pulsa aquí para subir un fichero workflow.json",
 	"click here.": "Pulsa aquí.",
-	"Click on the user role button to change a user's role.": "Pulsa en el botón rol de usuari@ para cambiar su rol.",
+	"Click on the user role button to change a user's role.": "Pulsa en el botón rol de usuario para cambiar su rol.",
 	"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "Permisos de escritura del portapapeles denegado. Por favor, comprueba la configuración de tu navegador para otorgar el permiso necesario.",
 	"Clone": "Clonar",
 	"Clone Chat": "Clonar Chat",
@@ -200,8 +200,8 @@
 	"Code execution": "Ejecución de Código",
 	"Code Execution": "Ejecución de Código",
 	"Code Execution Engine": "Motor de Ejecución de Código",
-	"Code Execution Timeout": "Tiempo",
-	"Code formatted successfully": "Se ha formateado correctamente el código.",
+	"Code Execution Timeout": "Tiempo límite de espera para Ejecución de Código",
+	"Code formatted successfully": "El codigo se ha formateado correctamente.",
 	"Code Interpreter": "Interprete de Código",
 	"Code Interpreter Engine": "Motor del Interprete de Código",
 	"Code Interpreter Prompt Template": "Plantilla del Indicador del Interprete de Código",
@@ -287,7 +287,7 @@
 	"Default to 389 or 636 if TLS is enabled": "Predeterminado a 389, o 636 si TLS está habilitado",
 	"Default to ALL": "Predeterminado a TODOS",
 	"Default to segmented retrieval for focused and relevant content extraction, this is recommended for most cases.": "Por defecto está predeterminada una segmentación de la recuperación para una extracción de contenido centrado y relevante, recomendado para la mayoría de los casos.",
-	"Default User Role": "Rol Predeterminado de l@s Usuari@s Nuev@s",
+	"Default User Role": "Rol predeterminado de los nuevos usuarios",
 	"Delete": "Borrar",
 	"Delete a model": "Borrar un modelo",
 	"Delete All Chats": "Borrar todos los chats",
@@ -302,17 +302,17 @@
 	"Delete prompt?": "¿Borrar el indicador?",
 	"delete this link": "Borrar este enlace",
 	"Delete tool?": "¿Borrar la herramienta?",
-	"Delete User": "Borrar Usuari@",
+	"Delete User": "Borrar Usuario",
 	"Deleted {{deleteModelTag}}": "{{deleteModelTag}} Borrado",
 	"Deleted {{name}}": "{{nombre}} Borrado",
-	"Deleted User": "Usuari@ Borrado",
+	"Deleted User": "Usuario Borrado",
 	"Describe your knowledge base and objectives": "Describe tu Base de Conocimientos y sus objetivos",
 	"Description": "Descripción",
-	"Detect Artifacts Automatically": "",
+	"Detect Artifacts Automatically": "Detectar Artefactos Automáticamente",
 	"Didn't fully follow instructions": "No seguiste completamente las instrucciones",
 	"Direct": "Directo",
 	"Direct Connections": "Conexiones Directas",
-	"Direct Connections allow users to connect to their own OpenAI compatible API endpoints.": "Las Conexiones Directas permiten a l@s usuari@s conectar a sus propios endpoints compatibles API OpenAI.",
+	"Direct Connections allow users to connect to their own OpenAI compatible API endpoints.": "Las Conexiones Directas permiten a los usuarios conectar a sus propios endpoints compatibles API OpenAI.",
 	"Direct Connections settings updated": "Se actualizaron las configuraciones de las Conexiones Directas",
 	"Direct Tool Servers": "Servidores de Herramientas Directos",
 	"Disabled": "Deshabilitado",
@@ -328,8 +328,8 @@
 	"Discover, download, and explore model presets": "Descubre, descarga y explora modelos con preajustados",
 	"Dismissible": "Desestimable",
 	"Display": "Mostrar",
-	"Display Emoji in Call": "Muestra chirimbolitos(Emojis) en Llamada",
-	"Display the username instead of You in the Chat": "Mostrar en el chat el nombre de usuari@ en lugar del genérico Tu/Usted",
+	"Display Emoji in Call": "Muestra Emojis en Llamada",
+	"Display the username instead of You in the Chat": "Mostrar en el chat el nombre de usuario en lugar del genérico Tu",
 	"Displays citations in the response": "Mostrar citas en la respuesta",
 	"Dive into knowledge": "Sumérgete en el conocimiento",
 	"Do not install functions from sources you do not fully trust.": "¡No instalar funciones de fuentes en las que que no se confíe totalmente!",
@@ -344,8 +344,8 @@
 	"does not make any external connections, and your data stays securely on your locally hosted server.": "no se realiza ninguna conexión externa y tus datos permanecen seguros alojados localmente en tu servidor.",
 	"Domain Filter List": "Lista de Filtrado de Dominio",
 	"Don't have an account?": "¿No tienes una cuenta?",
-	"don't install random functions from sources you don't trust.": "¡no instalar funciones aleatorias de fuentes en las que no se confíe!",
-	"don't install random tools from sources you don't trust.": "¡no instalar herramientas aleatorias de fuentes en las que no se confíe!",
+	"don't install random functions from sources you don't trust.": "¡no instalar funciones desconocidas de fuentes en las que no se confíe!",
+	"don't install random tools from sources you don't trust.": "¡no instalar herramientas desconocidas de fuentes en las que no se confíe!",
 	"Don't like the style": "¿No te gusta el estilo?",
 	"Done": "Hecho",
 	"Download": "Descargar",
@@ -358,21 +358,21 @@
 	"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "p.ej. '30s','10m'. Unidades de tiempo válidas son 's', 'm', 'h'.",
 	"e.g. \"json\" or a JSON schema": "p.ej. \"json\" o un esquema JSON",
 	"e.g. 60": "p.ej. 60",
-	"e.g. A filter to remove profanity from text": "p.ej. Un filtro para eliminar 'blasfemia' del texto",
+	"e.g. A filter to remove profanity from text": "p.ej. Un filtro para eliminar malas palabras del texto",
 	"e.g. My Filter": "p.ej. Mi Filtro",
 	"e.g. My Tools": "p.ej. Mis Herramientas",
 	"e.g. my_filter": "p.ej. mi_filtro",
 	"e.g. my_tools": "p.ej. mis_herramientas",
 	"e.g. Tools for performing various operations": "p.ej. Herramientas para realizar varias operaciones",
-	"e.g., en-US,ja-JP (leave blank for auto-detect)": "",
+	"e.g., en-US,ja-JP (leave blank for auto-detect)": "p. ej., en-US,ja-JP (dejar en blanco para detectar automáticamente)",
 	"Edit": "Editar",
 	"Edit Arena Model": "Editar Modelo en Arena",
 	"Edit Channel": "Editar Canal",
 	"Edit Connection": "Editar Conexión",
 	"Edit Default Permissions": "Editar Permisos Predeterminados",
 	"Edit Memory": "Editar Memoria",
-	"Edit User": "Editar Usuari@",
-	"Edit User Group": "Editar Grupo de Usuari@",
+	"Edit User": "Editar Usuario",
+	"Edit User Group": "Editar Grupo de Usuarios",
 	"ElevenLabs": "ElevenLabs",
 	"Email": "Email",
 	"Embark on adventures": "Embarcate en aventuras",
@@ -390,13 +390,13 @@
 	"Enable Memory Mapping (mmap) to load model data. This option allows the system to use disk storage as an extension of RAM by treating disk files as if they were in RAM. This can improve model performance by allowing for faster data access. However, it may not work correctly with all systems and can consume a significant amount of disk space.": "Habilitar Mapeado de Memoria (mmap) para cargar datos del modelo. Esta opción permite al sistema usar el almacenamiento del disco como una extensión de la RAM al tratar los archivos en disco como si estuvieran en la RAM. Esto puede mejorar el rendimiento del modelo al permitir un acceso más rápido a los datos. Sin embargo, puede no funcionar correctamente con todos los sistemas y puede consumir una cantidad significativa de espacio en disco.",
 	"Enable Message Rating": "Habilitar Calificación de los Mensajes",
 	"Enable Mirostat sampling for controlling perplexity.": "Algoritmo de decodificación de texto neuronal que controla activamente el proceso generativo para mantener la perplejidad del texto generado en un valor deseado. Previene las trampas de aburrimiento (por excesivas repeticiones) y de incoherencia (por generación de excesivo texto).",
-	"Enable New Sign Ups": "Habilitar Registros de Nuev@s Usuari@s",
+	"Enable New Sign Ups": "Habilitar Registros de Nuevos Usuarios",
 	"Enabled": "Habilitado",
-	"Enforce Temporary Chat": "",
+	"Enforce Temporary Chat": "Forzar el uso de Chat Temporal",
 	"Ensure your CSV file includes 4 columns in this order: Name, Email, Password, Role.": "Asegúrese de que su archivo CSV incluya 4 columnas en este orden: Nombre, Correo Electrónico, Contraseña, Rol.",
 	"Enter {{role}} message here": "Ingresar mensaje {{role}} aquí",
 	"Enter a detail about yourself for your LLMs to recall": "Ingresar detalles sobre ti para que los recuerden sus LLMs",
-	"Enter api auth string (e.g. username:password)": "Ingresar cadena de autorización de la api (p.ej. nombre:contraseña)",
+	"Enter api auth string (e.g. username:password)": "Ingresar campo de autorización de la api (p.ej. nombre:contraseña)",
 	"Enter Application DN": "Ingresar el DN de la Aplicación",
 	"Enter Application DN Password": "Ingresar la Contraseña del DN de la Aplicación",
 	"Enter Bing Search V7 Endpoint": "Ingresar el Endpoint de Bing Search V7",
@@ -414,8 +414,8 @@
 	"Enter Document Intelligence Key": "Ingresar Clave de Azure Document Intelligence",
 	"Enter domains separated by commas (e.g., example.com,site.org)": "Ingresar dominios separados por comas (p.ej., ejemplo.com,sitio.org)",
 	"Enter Exa API Key": "Ingresar Clave API de Exa",
-	"Enter Firecrawl API Base URL": "",
-	"Enter Firecrawl API Key": "",
+	"Enter Firecrawl API Base URL": "Ingresar URL Base del API de Firecrawl",
+	"Enter Firecrawl API Key": "Ingresar Clave del API de Firecrawl",
 	"Enter Github Raw URL": "Ingresar URL Github en Bruto(raw)",
 	"Enter Google PSE API Key": "Ingresar Clave API de Google PSE",
 	"Enter Google PSE Engine Id": "Ingresa ID del Motor PSE de Google",
@@ -433,8 +433,8 @@
 	"Enter Mojeek Search API Key": "Ingresar Clave API de Mojeek Search",
 	"Enter Number of Steps (e.g. 50)": "Ingresar Número de Pasos (p.ej., 50)",
 	"Enter Perplexity API Key": "Ingresar Clave API de Perplexity",
-	"Enter Playwright Timeout": "",
-	"Enter Playwright WebSocket URL": "",
+	"Enter Playwright Timeout": "Ingresar límite de tiempo de espera de Playwright",
+	"Enter Playwright WebSocket URL": "Ingresar URL de WebSocket de Playwright",
 	"Enter proxy URL (e.g. https://user:password@host:port)": "Ingresar URL del proxy (p.ej. https://user:password@host:port)",
 	"Enter reasoning effort": "Ingresar esfuerzo de razonamiento",
 	"Enter Sampler (e.g. Euler a)": "Ingresar Muestreador (p.ej., Euler a)",
@@ -452,16 +452,16 @@
 	"Enter server host": "Ingresar host del servidor",
 	"Enter server label": "Ingresar etiqueta del servidor",
 	"Enter server port": "Ingresar puerto del servidor",
-	"Enter Sougou Search API sID": "",
-	"Enter Sougou Search API SK": "",
+	"Enter Sougou Search API sID": "Ingresar Sougou Search API sID",
+	"Enter Sougou Search API SK": "Ingresar Sougou Search API SK",
 	"Enter stop sequence": "Ingresar secuencia de parada",
 	"Enter system prompt": "Ingresar Indicador del sistema",
 	"Enter system prompt here": "Ingresa aquí el indicador del sistema",
 	"Enter Tavily API Key": "Ingresar Clave API de Tavily",
-	"Enter Tavily Extract Depth": "",
-	"Enter the public URL of your WebUI. This URL will be used to generate links in the notifications.": "Ingresar URL pública de su WebUI. Esta URL se usará para generar enlaces en las notificaciones.",
+	"Enter Tavily Extract Depth": "Ingresar parámetro de Extract Depth de Taviliy",
+	"Enter the public URL of your WebUI. This URL will be used to generate links in the notifications.": "Ingresar URL pública de WebUI. Esta URL se usará para generar enlaces en las notificaciones.",
 	"Enter Tika Server URL": "Ingresar URL del servidor Tika",
-	"Enter timeout in seconds": "Ingresar timeout en segundos",
+	"Enter timeout in seconds": "Ingresar tiempo límite de espera en segundos",
 	"Enter to Send": "'Enter' para Enviar",
 	"Enter Top K": "Ingresar Top K",
 	"Enter Top K Reranker": "Ingresar Top K Reclasificador",
@@ -475,8 +475,8 @@
 	"Enter your new password": "Ingresa tu contraseña nueva",
 	"Enter Your Password": "Ingresa tu contraseña",
 	"Enter Your Role": "Ingresa tu rol",
-	"Enter Your Username": "Ingresa tu nombre de usuari@",
-	"Enter your webhook URL": "Ingresa tu URL de enganchesWeb(webhook)",
+	"Enter Your Username": "Ingresa tu nombre de usuario",
+	"Enter your webhook URL": "Ingresa tu URL de webhook",
 	"Error": "Error",
 	"ERROR": "ERROR",
 	"Error accessing Google Drive: {{error}}": "Error accediendo a Google Drive: {{error}}",
@@ -488,18 +488,18 @@
 	"Example: mail": "Ejemplo: correo",
 	"Example: ou=users,dc=foo,dc=example": "Ejemplo: ou=usuarios,dc=foo,dc=ejemplo",
 	"Example: sAMAccountName or uid or userPrincipalName": "Ejemplo: sAMNombreCuenta o uid o userNombrePrincipal",
-	"Exceeded the number of seats in your license. Please contact support to increase the number of seats.": "Excedido el número de accesos en su licencia. Por favor, contacte con soporte para aumentar el número de accesos.",
+	"Exceeded the number of seats in your license. Please contact support to increase the number of seats.": "Excedido el número de accesos de usuarios en tu licencia. Por favor, contacta con soporte para aumentar el número de accesos.",
 	"Exclude": "Excluir",
 	"Execute code for analysis": "Ejecutar código para análisis",
-	"Executing **{{NAME}}**...": "",
+	"Executing **{{NAME}}**...": "Ejecutando **{{NAME}}**...",
 	"Expand": "Expandir",
 	"Experimental": "Experimental",
 	"Explain": "Explicar",
-	"Explain this section to me in more detail": "Explicame esta sección con más detalle",
+	"Explain this section to me in more detail": "Explícame esta sección con más detalle",
 	"Explore the cosmos": "Explora el cosmos",
 	"Export": "Exportar",
 	"Export All Archived Chats": "Exportar Todos los Chats Archivados",
-	"Export All Chats (All Users)": "Exportar Todos los Chats (Todos l@s Usuari@s)",
+	"Export All Chats (All Users)": "Exportar Todos los Chats (Todos los Usuarios)",
 	"Export chat (.json)": "Exportar chat (.json)",
 	"Export Chats": "Exportar Chats",
 	"Export Config to JSON File": "Exportar Configuración a archivo JSON",
@@ -523,8 +523,8 @@
 	"Features": "Características",
 	"Features Permissions": "Permisos de las Características",
 	"February": "Febrero",
-	"Feedback History": "Historial de realimentación",
-	"Feedbacks": "Realimentaciones",
+	"Feedback History": "Historial de Opiniones",
+	"Feedbacks": "Opiniones",
 	"Feel free to add specific details": "Añade libremente detalles específicos",
 	"File": "Archivo",
 	"File added successfully.": "Archivo añadido correctamente.",
@@ -539,11 +539,11 @@
 	"Filter is now globally enabled": "El filtro ahora está habilitado globalmente",
 	"Filters": "Filtros",
 	"Fingerprint spoofing detected: Unable to use initials as avatar. Defaulting to default profile image.": "Se detectó suplantación de huellas: No se pueden usar las iniciales como avatar. Se establece la imagen de perfil predeterminada.",
-	"Firecrawl API Base URL": "",
-	"Firecrawl API Key": "",
+	"Firecrawl API Base URL": "URL Base de API de Firecrawl",
+	"Firecrawl API Key": "Clave de API de Firecrawl",
 	"Fluidly stream large external response chunks": "Transmisión fluida de fragmentos de grandes respuestas externas",
-	"Focus chat input": "Enfoque entrada del chat",
-	"Folder deleted successfully": "Carpeta bollada correctamente",
+	"Focus chat input": "Enfocar campo de chat",
+	"Folder deleted successfully": "Carpeta eliminada correctamente",
 	"Folder name cannot be empty": "El nombre de la carpeta no puede estar vacío",
 	"Folder name cannot be empty.": "El nombre de la carpeta no puede estar vacío",
 	"Folder name updated successfully": "Nombre de la carpeta actualizado correctamente",
@@ -719,7 +719,7 @@
 	"Memory updated successfully": "Memoria actualizada correctamente",
 	"Merge Responses": "Fusionar Respuestas",
 	"Message rating should be enabled to use this feature": "Para usar esta función debe estar habilitada la calificación de mensajes",
-	"Messages you send after creating your link won't be shared. Users with the URL will be able to view the shared chat.": "Los mensajes que envíe después de la creación del enlace no se compartirán. L@s usuari@s con la URL del enlace podrán ver el chat compartido.",
+	"Messages you send after creating your link won't be shared. Users with the URL will be able to view the shared chat.": "Los mensajes que envíe después de la creación del enlace no se compartirán. Los usuarios con la URL del enlace podrán ver el chat compartido.",
 	"Min P": "Min P",
 	"Mirostat": "Mirostat",
 	"Mirostat Eta": "Mirostat Eta",
@@ -777,7 +777,7 @@
 	"No results found": "No se encontraron resultados",
 	"No search query generated": "No se generó ninguna consulta de búsqueda",
 	"No source available": "No hay fuente disponible",
-	"No users were found.": "No se encontraron usuari@s.",
+	"No users were found.": "No se encontraron usuarios.",
 	"No valves to update": "No hay válvulas para actualizar",
 	"None": "Ninguno",
 	"Not factually correct": "No es correcto en todos los aspectos",
@@ -804,7 +804,7 @@
 	"Only alphanumeric characters and hyphens are allowed": "Sólo están permitidos caracteres alfanuméricos y guiones",
 	"Only alphanumeric characters and hyphens are allowed in the command string.": "Sólo están permitidos en la cadena de comandos caracteres alfanuméricos y guiones.",
 	"Only collections can be edited, create a new knowledge base to edit/add documents.": "Solo se pueden editar las colecciones, para añadir/editar documentos hay que crear una nueva base de conocimientos",
-	"Only select users and groups with permission can access": "Solo pueden acceder l@s usuari@s y grupos con permiso",
+	"Only select users and groups with permission can access": "Solo pueden acceder los usuarios y grupos con permiso",
 	"Oops! Looks like the URL is invalid. Please double-check and try again.": "¡vaya! Parece que la URL es inválida. Por favor, revisala y reintenta de nuevo.",
 	"Oops! There are files still uploading. Please wait for the upload to complete.": "¡vaya! Todavía hay archivos subiendose. Por favor, espera a que se complete la subida.",
 	"Oops! There was an error in the previous response.": "¡vaya! Hubo un error en la respuesta previa.",
@@ -824,7 +824,7 @@
 	"OpenAI URL/Key required.": "URL/Clave de OpenAI requerida.",
 	"openapi.json Path": "Ruta a openapi.json",
 	"or": "o",
-	"Organize your users": "Organiza tus usuari@s",
+	"Organize your users": "Organiza tus usuarios",
 	"Other": "Otro",
 	"OUTPUT": "SALIDA",
 	"Output format": "Formato de salida",
@@ -851,8 +851,8 @@
 	"Pipelines Valves": "Válvulas de Tuberías",
 	"Plain text (.txt)": "Texto plano (.txt)",
 	"Playground": "Zona de Pruebas",
-	"Playwright Timeout (ms)": "",
-	"Playwright WebSocket URL": "",
+	"Playwright Timeout (ms)": "Tiempo Límite de Espera (ms) de Playwright",
+	"Playwright WebSocket URL": "URL de WebSocket de Playwright",
 	"Please carefully review the following warnings:": "Por favor revisar cuidadosamente los siguientes avisos:",
 	"Please do not close the settings page while loading the model.": "Por favor no cerrar la página de ajustes mientras se está descargando el modelo.",
 	"Please enter a prompt": "Por favor ingresar un indicador",
@@ -894,7 +894,7 @@
 	"Record voice": "Grabar voz",
 	"Redirecting you to Open WebUI Community": "Redireccionando a la Comunidad Open-WebUI",
 	"Reduces the probability of generating nonsense. A higher value (e.g. 100) will give more diverse answers, while a lower value (e.g. 10) will be more conservative.": "Reduce la probabilidad de generación sin sentido. Un valor más alto (p.ej. 100) dará respuestas más diversas, mientras que un valor más bajo (p.ej. 10) será más conservador.",
-	"Refer to yourself as \"User\" (e.g., \"User is learning Spanish\")": "Referir a ti mismo como \"Usuari@\" (p.ej. \"Usuari@ está aprendiendo Español\")",
+	"Refer to yourself as \"User\" (e.g., \"User is learning Spanish\")": "Referir a ti mismo como \"Usuario\" (p.ej. \"Usuario está aprendiendo Español\")",
 	"References from": "Referencias desde",
 	"Refused when it shouldn't have": "Rechazado cuando no debería haberlo hecho",
 	"Regenerate": "Regenerar",
@@ -902,7 +902,7 @@
 	"Reindex Knowledge Base Vectors": "Reindexar Base Vectorial de Conocimiento",
 	"Release Notes": "Notas de la Versión",
 	"Relevance": "Relevancia",
-	"Relevance Threshold": "",
+	"Relevance Threshold": "Umbral de Relevancia",
 	"Remove": "Eliminar",
 	"Remove Model": "Eliminar Modelo",
 	"Rename": "Renombrar",
@@ -1020,7 +1020,7 @@
 	"Show": "Mostrar",
 	"Show \"What's New\" modal on login": "Mostrar modal \"Qué hay de Nuevo\" al iniciar sesión",
 	"Show Admin Details in Account Pending Overlay": "Mostrar Detalles Admin en la sobrecapa de 'Cuenta Pendiente'",
-	"Show Model": "",
+	"Show Model": "Mostrar Modelo",
 	"Show shortcuts": "Mostrar Atajos",
 	"Show your support!": "¡Muestra tu apoyo!",
 	"Showcased creativity": "Creatividad exhibida",
@@ -1061,7 +1061,7 @@
 	"Tap to interrupt": "Toca para interrumpir",
 	"Tasks": "Tareas",
 	"Tavily API Key": "Clave API de Tavily",
-	"Tavily Extract Depth": "",
+	"Tavily Extract Depth": "Parámetro Extract Depth de Taviliy",
 	"Tell us more:": "Dinos algo más:",
 	"Temperature": "Temperatura",
 	"Template": "Plantilla",
@@ -1069,14 +1069,14 @@
 	"Text Splitter": "Divisor de Texto",
 	"Text-to-Speech Engine": "Motor Texto a Voz(TTS)",
 	"Tfs Z": "TFS Z",
-	"Thanks for your feedback!": "¡Gracias por tu realimentación!",
+	"Thanks for your feedback!": "¡Gracias por tu comentario!",
 	"The Application Account DN you bind with for search": "Cuenta DN de la aplicación vinculada para búsqueda",
-	"The base to search for users": "La base para buscar usuari@s",
+	"The base to search for users": "Base para buscar usuarios",
 	"The batch size determines how many text requests are processed together at once. A higher batch size can increase the performance and speed of the model, but it also requires more memory.": "El tamaño de lote determina cuántas solicitudes de texto se procesan juntas de una vez. Un tamaño de lote más alto puede aumentar el rendimiento y la velocidad del modelo, pero también requiere más memoria.",
-	"The developers behind this plugin are passionate volunteers from the community. If you find this plugin helpful, please consider contributing to its development.": "L@s desarrolladores de este complemento son apasionad@s voluntari@s de la comunidad. Si este complemento te es útil, por favor considera contribuir a su desarrollo.",
+	"The developers behind this plugin are passionate volunteers from the community. If you find this plugin helpful, please consider contributing to its development.": "Quienes desarollaron este complemento son apasionados voluntarios/as de la comunidad. Si este complemento te es útil, por favor considera contribuir a su desarrollo.",
 	"The evaluation leaderboard is based on the Elo rating system and is updated in real-time.": "La tabla clasificatoria de evaluación se basa en el sistema de clasificación Elo y se actualiza en tiempo real.",
-	"The LDAP attribute that maps to the mail that users use to sign in.": "El atributo LDAP que mapea el correo que l@s usuari@s utilizan para iniciar sesión.",
-	"The LDAP attribute that maps to the username that users use to sign in.": "El atributo LDAP que mapea el nombre de usuari@ que l@s usuari@s utilizan para iniciar sesión.",
+	"The LDAP attribute that maps to the mail that users use to sign in.": "El atributo LDAP que mapea el correo que los usuarios utilizan para iniciar sesión.",
+	"The LDAP attribute that maps to the username that users use to sign in.": "El atributo LDAP que mapea el nombre de usuario que los usuarios utilizan para iniciar sesión.",
 	"The leaderboard is currently in beta, and we may adjust the rating calculations as we refine the algorithm.": "La tabla clasificatoria está actualmente en beta, por lo que los cálculos de clasificación pueden reajustarse a medida que se refina el algoritmo.",
 	"The maximum file size in MB. If the file size exceeds this limit, the file will not be uploaded.": "El tamaño máximo del archivo en MB. Si el tamaño del archivo supera este límite, el archivo no se subirá.",
 	"The maximum number of files that can be used at once in chat. If the number of files exceeds this limit, the files will not be uploaded.": "El número máximo de archivos que se pueden utilizar a la vez en el chat. Si se supera este límite, los archivos no se subirán.",
@@ -1114,7 +1114,7 @@
 	"TLS": "TLS",
 	"To access the available model names for downloading,": "Para acceder a los nombres de modelos disponibles para descargar,",
 	"To access the GGUF models available for downloading,": "Para acceder a los modelos GGUF disponibles para descargar,",
-	"To access the WebUI, please reach out to the administrator. Admins can manage user statuses from the Admin Panel.": "Para acceder a WebUI, por favor contacte con Admins. L@s administradores pueden gestionar los estados de l@s usuari@s esde el panel de administración.",
+	"To access the WebUI, please reach out to the administrator. Admins can manage user statuses from the Admin Panel.": "Para acceder a WebUI, por favor contacte con Admins. Los administradores pueden gestionar los estados de los usuarios esde el panel de administración.",
 	"To attach knowledge base here, add them to the \"Knowledge\" workspace first.": "Para adjuntar la base de conocimientos aquí, primero añadirla a \"Conocimiento\" en el área de trabajo.",
 	"To learn more about available endpoints, visit our documentation.": "Para aprender más sobre los endpoints disponibles, visite nuestra documentación.",
 	"To protect your privacy, only ratings, model IDs, tags, and metadata are shared from your feedback—your chat logs remain private and are not included.": "Para proteger tu privacidad, de tu realimentación solo se comparten las calificaciones, IDs de modelo, etiquetas y metadatos; tus chat registrados permanecen privados y no se incluyen.",
@@ -1182,18 +1182,18 @@
 	"URL Mode": "Modo URL",
 	"Use '#' in the prompt input to load and include your knowledge.": "Utilizar '#' en el indicador para cargar e incluir tu conocimiento.",
 	"Use Gravatar": "Usar Gravatar",
-	"Use groups to group your users and assign permissions.": "Usar grupos para agrupar a usuari@s y asignar permisos.",
+	"Use groups to group your users and assign permissions.": "Usar grupos para agrupar a usuarios y asignar permisos.",
 	"Use Initials": "Usar Iniciales",
 	"Use no proxy to fetch page contents.": "No usar proxy para extraer contenidos",
 	"Use proxy designated by http_proxy and https_proxy environment variables to fetch page contents.": "Usar el proxy asignado en las variables del entorno http_proxy y/o https_proxy para extraer contenido",
 	"use_mlock (Ollama)": "use_mlock (Ollama)",
 	"use_mmap (Ollama)": "use_mmap (Ollama)",
-	"user": "usuari@",
-	"User": "Usuari@",
-	"User location successfully retrieved.": "Ubicación de usuari@ obtenida correctamente.",
-	"User Webhooks": "Usuari@ EnganchesWeb(webhooks)",
-	"Username": "Nombre de Usuari@",
-	"Users": "Usuari@s",
+	"user": "usuario",
+	"User": "Usuario",
+	"User location successfully retrieved.": "Ubicación de usuario obtenida correctamente.",
+	"User Webhooks": "Usuario Webhooks",
+	"Username": "Nombre de Usuario",
+	"Users": "Usuarios",
 	"Using the default arena model with all models. Click the plus button to add custom models.": "Usando el modelo de arena predeterminado con todos los modelos. Pulsar en el botón + para agregar modelos personalizados.",
 	"Utilize": "Utilizar",
 	"Valid time units:": "Unidades de tiempo válidas:",
@@ -1213,7 +1213,7 @@
 	"Voice Input": "Entrada de Voz",
 	"Warning": "Aviso",
 	"Warning:": "Aviso:",
-	"Warning: Enabling this will allow users to upload arbitrary code on the server.": "Aviso: Habilitar esto permitirá a l@s usuari@s subir código arbitrario al servidor.",
+	"Warning: Enabling this will allow users to upload arbitrary code on the server.": "Aviso: Habilitar esto permitirá a los usuarios subir código arbitrario al servidor.",
 	"Warning: If you update or change your embedding model, you will need to re-import all documents.": "Aviso: Si actualizas o cambias el modelo de incrustacción, necesitarás re-importar todos los documentos.",
 	"Warning: Jupyter execution enables arbitrary code execution, posing severe security risks—proceed with extreme caution.": "Aviso: La ejecución Jupyter habilita la ejecución de código arbitrario, planteando graves riesgos de seguridad; Proceder con extrema precaución.",
 	"Web": "Web",

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

@@ -128,7 +128,7 @@
 	"Available list": "가능한 목록",
 	"Available Tools": "",
 	"available!": "사용 가능!",
-	"Awful": "끔찍함",
+	"Awful": "형편없음",
 	"Azure AI Speech": "Azure AI 음성",
 	"Azure Region": "Azure 지역",
 	"Back": "뒤로가기",

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

@@ -43,7 +43,7 @@
 	"Add Reaction": "Dodaj reakcję",
 	"Add Tag": "Dodaj tag",
 	"Add Tags": "Dodaj tagi",
-	"Add text content": "Dodaj tekstową zawartość",
+	"Add text content": "Dodaj zawartość tekstową",
 	"Add User": "Dodaj użytkownika",
 	"Add User Group": "Dodaj grupę użytkowników",
 	"Adjusting these settings will apply changes universally to all users.": "Dostosowanie tych ustawień spowoduje wprowadzenie zmian dla wszystkich użytkowników.",
@@ -54,7 +54,7 @@
 	"Admins have access to all tools at all times; users need tools assigned per model in the workspace.": "Administratorzy mają dostęp do wszystkich narzędzi przez cały czas; użytkownicy muszą mieć przydzielone narzędzia dla każdego modelu w przestrzeni roboczej.",
 	"Advanced Parameters": "Zaawansowane ustawienia",
 	"Advanced Params": "Zaawansowane ustawienia",
-	"All": "",
+	"All": "Wszystkie",
 	"All Documents": "Wszystkie dokumenty",
 	"All models deleted successfully": "Wszystkie modele zostały usunięte pomyślnie.",
 	"Allow Call": "",
@@ -74,8 +74,8 @@
 	"Already have an account?": "Czy masz już konto?",
 	"Alternative to the top_p, and aims to ensure a balance of quality and variety. The parameter p represents the minimum probability for a token to be considered, relative to the probability of the most likely token. For example, with p=0.05 and the most likely token having a probability of 0.9, logits with a value less than 0.045 are filtered out.": "",
 	"Always": "Zawsze",
-	"Always Collapse Code Blocks": "",
-	"Always Expand Details": "",
+	"Always Collapse Code Blocks": "Zawsze zwijaj bloki kodu",
+	"Always Expand Details": "Zawsze rozwiń szczegóły",
 	"Amazing": "Niesamowite",
 	"an assistant": "asystent",
 	"Analyzed": "Przeanalizowane",
@@ -147,8 +147,8 @@
 	"By {{name}}": "Przez {{name}}",
 	"Bypass Embedding and Retrieval": "",
 	"Calendar": "Kalendarz",
-	"Call": "Wywołanie",
-	"Call feature is not supported when using Web STT engine": "Funkcja wywołania nie jest obsługiwana podczas korzystania z silnika Web STT",
+	"Call": "Rozmowa",
+	"Call feature is not supported when using Web STT engine": "Funkcja rozmowy nie jest obsługiwana podczas korzystania z silnika Web STT",
 	"Camera": "Kamera",
 	"Cancel": "Anuluj",
 	"Capabilities": "Możliwości",
@@ -193,14 +193,14 @@
 	"click here.": "kliknij tutaj.",
 	"Click on the user role button to change a user's role.": "Kliknij przycisk roli użytkownika, aby zmienić jego uprawnienia.",
 	"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "Nie można było skopiować do schowka. Sprawdź ustawienia przeglądarki, aby przyznać wymagany dostęp.",
-	"Clone": "Sklonuj",
+	"Clone": "Duplikuj",
 	"Clone Chat": "Sklonuj czat",
 	"Clone of {{TITLE}}": "Klon {{TITLE}}",
 	"Close": "Zamknij",
 	"Code execution": "Wykonanie kodu",
 	"Code Execution": "Wykonanie kodu",
 	"Code Execution Engine": "Silnik wykonawczy kodu",
-	"Code Execution Timeout": "Limit czasu wykonania kodu",
+	"Code Execution Timeout": "Limit czasu wykonywania kodu",
 	"Code formatted successfully": "Kod został sformatowany pomyślnie.",
 	"Code Interpreter": "Interpreter kodu",
 	"Code Interpreter Engine": "Silnik interpretatora kodu",
@@ -245,7 +245,7 @@
 	"Copied shared chat URL to clipboard!": "Skopiowano udostępniony URL czatu do schowka!",
 	"Copied to clipboard": "Skopiowane do schowka",
 	"Copy": "Skopiuj",
-	"Copy Formatted Text": "",
+	"Copy Formatted Text": "Skopiuj sformatowany tekst",
 	"Copy last code block": "Skopiuj ostatni fragment kodu",
 	"Copy last response": "Skopiuj ostatnią wypowiedź",
 	"Copy Link": "Skopiuj link",
@@ -283,7 +283,7 @@
 	"Default Models": "Domyślne modele",
 	"Default permissions": "Domyślne uprawnienia",
 	"Default permissions updated successfully": "Domyślne uprawnienia zaktualizowane pomyślnie",
-	"Default Prompt Suggestions": "Domyślne propozycje wpisów",
+	"Default Prompt Suggestions": "Domyślne propozycje promptów",
 	"Default to 389 or 636 if TLS is enabled": "Domyślnie użyj 389 lub 636, jeśli TLS jest włączony",
 	"Default to ALL": "Domyślne dla wszystkich",
 	"Default to segmented retrieval for focused and relevant content extraction, this is recommended for most cases.": "",
@@ -316,12 +316,12 @@
 	"Direct Connections settings updated": "Ustawienia połączeń bezpośrednich zaktualizowane",
 	"Direct Tool Servers": "",
 	"Disabled": "Wyłączony",
-	"Discover a function": "Odkryj funkcję",
-	"Discover a model": "Odkryj model",
-	"Discover a prompt": "Odkryj prompty",
-	"Discover a tool": "Odkryj narzędzie",
+	"Discover a function": "Odkrywaj funkcję",
+	"Discover a model": "Odkrywaj model",
+	"Discover a prompt": "Odkrywaj prompty",
+	"Discover a tool": "Odkrywaj narzędzia",
 	"Discover how to use Open WebUI and seek support from the community.": "Odkryj, jak korzystać z Open WebUI i szukaj wsparcia w społeczności.",
-	"Discover wonders": "Odkryj cuda",
+	"Discover wonders": "Odkrywaj cuda",
 	"Discover, download, and explore custom functions": "Odkryj, pobierz i eksploruj niestandardowe funkcje",
 	"Discover, download, and explore custom prompts": "Odkryj, pobierz i eksploruj niestandardowe prompty",
 	"Discover, download, and explore custom tools": "Odkryj, pobierz i eksploruj niestandardowe narzędzia",
@@ -425,7 +425,7 @@
 	"Enter Jupyter Token": "Wprowadź token Jupyter",
 	"Enter Jupyter URL": "Podaj adres URL Jupytera",
 	"Enter Kagi Search API Key": "Wprowadź klucz wyszukiwania Kagi",
-	"Enter Key Behavior": "",
+	"Enter Key Behavior": "Zachowanie klawisza Enter",
 	"Enter language codes": "Wprowadź kody języków",
 	"Enter Mistral API Key": "",
 	"Enter Model ID": "Wprowadź ID modelu",
@@ -455,7 +455,7 @@
 	"Enter Sougou Search API sID": "",
 	"Enter Sougou Search API SK": "",
 	"Enter stop sequence": "Wprowadź sekwencję stop",
-	"Enter system prompt": "Wprowadź polecenie systemowe",
+	"Enter system prompt": "Wprowadź prompt systemowy",
 	"Enter system prompt here": "",
 	"Enter Tavily API Key": "Wprowadź klucz API Tavily",
 	"Enter Tavily Extract Depth": "",
@@ -624,7 +624,7 @@
 	"Import Models": "Importowanie modeli",
 	"Import Presets": "Importuj ustawienia",
 	"Import Prompts": "Importuj prompty",
-	"Import Tools": "Import narzędzi",
+	"Import Tools": "Importuj narzędzia",
 	"Include": "Włączyć",
 	"Include `--api-auth` flag when running stable-diffusion-webui": "Użyj flagi `--api-auth` podczas uruchamiania stable-diffusion-webui",
 	"Include `--api` flag when running stable-diffusion-webui": "Użyj flagi `--api` podczas uruchamiania stable-diffusion-webui.",
@@ -792,14 +792,14 @@
 	"num_thread (Ollama)": "num_thread (Ollama)",
 	"OAuth ID": "Identyfikator OAuth",
 	"October": "Październik",
-	"Off": "Wyłączony",
+	"Off": "Wyłączone",
 	"Okay, Let's Go!": "Okej, do dzieła!",
 	"OLED Dark": "Ciemny OLED",
 	"Ollama": "Ollama",
 	"Ollama API": "Interfejs API Ollama",
 	"Ollama API settings updated": "Ustawienia API Ollama zostały zaktualizowane",
 	"Ollama Version": "Wersja Ollama",
-	"On": "Włączony",
+	"On": "Włączone",
 	"OneDrive": "",
 	"Only alphanumeric characters and hyphens are allowed": "Dozwolone są tylko znaki alfanumeryczne i myślniki",
 	"Only alphanumeric characters and hyphens are allowed in the command string.": "W komendzie dozwolone są wyłącznie znaki alfanumeryczne i myślniki.",
@@ -855,7 +855,7 @@
 	"Playwright WebSocket URL": "",
 	"Please carefully review the following warnings:": "Proszę uważnie przejrzeć poniższe ostrzeżenia:",
 	"Please do not close the settings page while loading the model.": "Proszę nie zamykać strony ustawień podczas ładowania modelu.",
-	"Please enter a prompt": "Proszę podać promp",
+	"Please enter a prompt": "Proszę podać prompt",
 	"Please enter a valid path": "",
 	"Please enter a valid URL": "",
 	"Please fill in all fields.": "Proszę wypełnić wszystkie pola.",
@@ -874,7 +874,7 @@
 	"Prompt": "Wprowadź prompt: ",
 	"Prompt (e.g. Tell me a fun fact about the Roman Empire)": "Prompt (np. podaj ciekawostkę o Imperium Rzymskim)",
 	"Prompt Autocompletion": "",
-	"Prompt Content": "Treść podpowiedzi",
+	"Prompt Content": "Treść promptu",
 	"Prompt created successfully": "Prompt został utworzony pomyślnie",
 	"Prompt suggestions": "Sugestie promptów",
 	"Prompt updated successfully": "Prompt został zaktualizowany pomyślnie.",
@@ -894,7 +894,7 @@
 	"Record voice": "Nagraj swój głos",
 	"Redirecting you to Open WebUI Community": "Przekierowujemy Cię do społeczności Open WebUI",
 	"Reduces the probability of generating nonsense. A higher value (e.g. 100) will give more diverse answers, while a lower value (e.g. 10) will be more conservative.": "",
-	"Refer to yourself as \"User\" (e.g., \"User is learning Spanish\")": "Odnosić się do siebie jako \"Użytkownik\" (np. \"Użytkownik uczy się hiszpańskiego\")",
+	"Refer to yourself as \"User\" (e.g., \"User is learning Spanish\")": "Odnoś się do mnie jako \"Użytkownik\" (np. \"Użytkownik uczy się hiszpańskiego\")",
 	"References from": "Odniesienia do",
 	"Refused when it shouldn't have": "Odmówił, gdy nie powinien",
 	"Regenerate": "Wygeneruj ponownie",
@@ -948,18 +948,18 @@
 	"Search Filters": "Filtry wyszukiwania",
 	"search for tags": "wyszukiwanie tagów",
 	"Search Functions": "Funkcje wyszukiwania",
-	"Search Knowledge": "Wyszukaj wiedzę",
+	"Search Knowledge": "Przeszukaj wiedzę",
 	"Search Models": "Wyszukiwanie modeli",
 	"Search options": "Opcje wyszukiwania",
-	"Search Prompts": "Prompty wyszukiwania",
+	"Search Prompts": "Szukaj promptów",
 	"Search Result Count": "Liczba wyników wyszukiwania",
 	"Search the internet": "Przeszukaj internet",
-	"Search Tools": "Narzędzia wyszukiwania",
+	"Search Tools": "Szukaj narzędzi",
 	"SearchApi API Key": "Klucz API SearchApi",
 	"SearchApi Engine": "Search API Engine",
 	"Searched {{count}} sites": "Przeszukano {{count}} stron",
 	"Searching \"{{searchQuery}}\"": "Wyszukiwanie \"{{searchQuery}}\"",
-	"Searching Knowledge for \"{{searchQuery}}\"": "Wyszukiwanie wiedzy dla \"{{searchQuery}}\"",
+	"Searching Knowledge for \"{{searchQuery}}\"": "Przeszukiwanie wiedzy dla \"{{searchQuery}}\"",
 	"Searxng Query URL": "Adres URL zapytania Searxng",
 	"See readme.md for instructions": "Sprawdź readme.md dla instrukcji",
 	"See what's new": "Sprawdź nowości",
@@ -998,7 +998,7 @@
 	"Set Image Size": "Ustaw rozmiar obrazu",
 	"Set reranking model (e.g. {{model}})": "Skonfiguruj model ponownego rankingu (np. {{model}})",
 	"Set Sampler": "Próbnik samplera",
-	"Set Scheduler": "Ustawiacz harmonogramu",
+	"Set Scheduler": "Ustaw harmonogram",
 	"Set Steps": "Ustaw kroki",
 	"Set Task Model": "Konfiguracja modelu zadań",
 	"Set the number of layers, which will be off-loaded to GPU. Increasing this value can significantly improve performance for models that are optimized for GPU acceleration but may also consume more power and GPU resources.": "Ustaw liczbę warstw, które zostaną przeniesione na GPU. Zwiększenie tej wartości może znacząco poprawić wydajność dla modeli optymalizowanych pod kątem akceleracji GPU, ale także może zużywać więcej energii i zasobów GPU.",
@@ -1038,7 +1038,7 @@
 	"Speech Playback Speed": "Prędkość odtwarzania mowy",
 	"Speech recognition error: {{error}}": "Błąd rozpoznawania mowy: {{error}}",
 	"Speech-to-Text Engine": "Silnik konwersji mowy na tekst",
-	"Stop": "Zatrzymaj się",
+	"Stop": "Zatrzymaj",
 	"Stop Sequence": "Zatrzymaj sekwencję",
 	"Stream Chat Response": "Strumieniowanie odpowiedzi z czatu",
 	"STT Model": "Model STT",
@@ -1108,7 +1108,7 @@
 	"Title": "Tytuł",
 	"Title (e.g. Tell me a fun fact)": "Tytuł (na przykład {e.g.} Powiedz mi jakiś zabawny fakt)",
 	"Title Auto-Generation": "Automatyczne tworzenie tytułu",
-	"Title cannot be an empty string.": "Tytuł nie może być pustą stringiem.",
+	"Title cannot be an empty string.": "Tytuł nie może być pustym stringiem.",
 	"Title Generation": "Generowanie tytułów",
 	"Title Generation Prompt": "Prompt do generowania tytułu",
 	"TLS": "TLS",
@@ -1202,8 +1202,8 @@
 	"Valves updated successfully": "Zawory zaktualizowane pomyślnie",
 	"variable": "zmienna",
 	"variable to have them replaced with clipboard content.": "Zmienna, która ma zostać zastąpiona zawartością schowka.",
-	"Verify Connection": "",
-	"Verify SSL Certificate": "",
+	"Verify Connection": "Sprawdź połączenie",
+	"Verify SSL Certificate": "Sprawdź certyfikat SSL",
 	"Version": "Wersja",
 	"Version {{selectedVersion}} of {{totalVersions}}": "Wersja {{selectedVersion}} z {{totalVersions}}",
 	"View Replies": "Wyświetl odpowiedzi",

+ 5 - 1
src/lib/utils/index.ts

@@ -1257,9 +1257,13 @@ export const convertOpenApiToToolPayload = (openApiSpec) => {
 			// Extract path and query parameters
 			if (operation.parameters) {
 				operation.parameters.forEach((param) => {
+					let description = param.schema.description || param.description || '';
+					if (param.schema.enum && Array.isArray(param.schema.enum)) {
+						description += `. Possible values: ${param.schema.enum.join(', ')}`;
+					}
 					tool.parameters.properties[param.name] = {
 						type: param.schema.type,
-						description: param.schema.description || ''
+						description: description
 					};
 
 					if (param.required) {

+ 204 - 74
src/lib/utils/onedrive-file-picker.ts

@@ -2,70 +2,128 @@ import { PublicClientApplication } from '@azure/msal-browser';
 import type { PopupRequest } from '@azure/msal-browser';
 import { v4 as uuidv4 } from 'uuid';
 
-let CLIENT_ID = '';
+class OneDriveConfig {
+	private static instance: OneDriveConfig;
+	private clientId: string = '';
+	private sharepointUrl: string = '';
+	private msalInstance: PublicClientApplication | null = null;
+	private currentAuthorityType: 'personal' | 'organizations' = 'personal';
 
-async function getCredentials() {
-	if (CLIENT_ID) return;
+	private constructor() {}
 
-	const response = await fetch('/api/config');
-	if (!response.ok) {
-		throw new Error('Failed to fetch OneDrive credentials');
+	public static getInstance(): OneDriveConfig {
+		if (!OneDriveConfig.instance) {
+			OneDriveConfig.instance = new OneDriveConfig();
+		}
+		return OneDriveConfig.instance;
 	}
-	const config = await response.json();
-	CLIENT_ID = config.onedrive?.client_id;
-	if (!CLIENT_ID) {
-		throw new Error('OneDrive client ID not configured');
+
+	public async initialize(authorityType?: 'personal' | 'organizations'): Promise<void> {
+		if (authorityType && this.currentAuthorityType !== authorityType) {
+			this.currentAuthorityType = authorityType;
+			this.msalInstance = null;
+		}
+		await this.getCredentials();
 	}
-}
 
-let msalInstance: PublicClientApplication | null = null;
+	public async ensureInitialized(authorityType?: 'personal' | 'organizations'): Promise<void> {
+		await this.initialize(authorityType);
+	}
 
-// Initialize MSAL authentication
-async function initializeMsal() {
-	try {
-		if (!CLIENT_ID) {
-			await getCredentials();
+	private async getCredentials(): Promise<void> {
+		
+		const headers: HeadersInit = {
+			'Content-Type': 'application/json'
+		};
+
+		const response = await fetch('/api/config', { 
+			headers,
+			credentials: 'include'
+		});
+		
+		if (!response.ok) {
+			throw new Error('Failed to fetch OneDrive credentials');
+		}
+		
+		const config = await response.json();
+		
+		const newClientId = config.onedrive?.client_id;
+		const newSharepointUrl = config.onedrive?.sharepoint_url;
+		
+		if (!newClientId) {
+			throw new Error('OneDrive configuration is incomplete');
 		}
 
-		const msalParams = {
-			auth: {
-				authority: 'https://login.microsoftonline.com/consumers',
-				clientId: CLIENT_ID
-			}
-		};
+		this.clientId = newClientId;
+		this.sharepointUrl = newSharepointUrl;
+	}
+
+	public async getMsalInstance(authorityType?: 'personal' | 'organizations'): Promise<PublicClientApplication> {
+		await this.ensureInitialized(authorityType);
+		
+		if (!this.msalInstance) {
+			const authorityEndpoint = this.currentAuthorityType === 'organizations' ? 'common' : 'consumers';
+			const msalParams = {
+				auth: {
+					authority: `https://login.microsoftonline.com/${authorityEndpoint}`,
+					clientId: this.clientId
+				}
+			};
 
-		if (!msalInstance) {
-			msalInstance = new PublicClientApplication(msalParams);
-			if (msalInstance.initialize) {
-				await msalInstance.initialize();
+			this.msalInstance = new PublicClientApplication(msalParams);
+			if (this.msalInstance.initialize) {
+				await this.msalInstance.initialize();
 			}
 		}
 
-		return msalInstance;
-	} catch (error) {
-		throw new Error(
-			'MSAL initialization failed: ' + (error instanceof Error ? error.message : String(error))
-		);
+		return this.msalInstance;
+	}
+
+	public getAuthorityType(): 'personal' | 'organizations' {
+		return this.currentAuthorityType;
+	}
+
+	public getSharepointUrl(): string {
+		return this.sharepointUrl;
+	}
+
+	public getBaseUrl(): string {
+		if (this.currentAuthorityType === 'organizations') {
+			if (!this.sharepointUrl || this.sharepointUrl === '') {
+				throw new Error('Sharepoint URL not configured');
+			}
+
+			let sharePointBaseUrl = this.sharepointUrl.replace(/^https?:\/\//, '');
+			sharePointBaseUrl = sharePointBaseUrl.replace(/\/$/, '');
+
+			return `https://${sharePointBaseUrl}`;
+		} else {
+			return 'https://onedrive.live.com/picker';
+		}
 	}
 }
 
+
 // Retrieve OneDrive access token
-async function getToken(): Promise<string> {
-	const authParams: PopupRequest = { scopes: ['OneDrive.ReadWrite'] };
+async function getToken(resource?: string, authorityType?: 'personal' | 'organizations'): Promise<string> {
+	const config = OneDriveConfig.getInstance();
+	await config.ensureInitialized(authorityType);
+	
+	const currentAuthorityType = config.getAuthorityType();
+
+	const scopes = currentAuthorityType === 'organizations'
+		? [`${resource || config.getBaseUrl()}/.default`]
+		: ['OneDrive.ReadWrite'];
+	
+	const authParams: PopupRequest = { scopes };
 	let accessToken = '';
-	try {
-		msalInstance = await initializeMsal();
-		if (!msalInstance) {
-			throw new Error('MSAL not initialized');
-		}
 
+	try {
+		const msalInstance = await config.getMsalInstance(authorityType);
 		const resp = await msalInstance.acquireTokenSilent(authParams);
 		accessToken = resp.accessToken;
 	} catch (err) {
-		if (!msalInstance) {
-			throw new Error('MSAL not initialized');
-		}
-
+		const msalInstance = await config.getMsalInstance(authorityType);
 		try {
 			const resp = await msalInstance.loginPopup(authParams);
 			msalInstance.setActiveAccount(resp.account);
@@ -88,60 +146,121 @@ async function getToken(): Promise<string> {
 	return accessToken;
 }
 
-const baseUrl = 'https://onedrive.live.com/picker';
-const params = {
-	sdk: '8.0',
+interface PickerParams {
+	sdk: string;
 	entry: {
-		oneDrive: {
-			files: {}
-		}
-	},
-	authentication: {},
+		oneDrive: Record<string, unknown>;
+	};
+	authentication: Record<string, unknown>;
 	messaging: {
-		origin: window?.location?.origin,
-		channelId: uuidv4()
-	},
+		origin: string;
+		channelId: string;
+	};
 	typesAndSources: {
-		mode: 'files',
-		pivots: {
-			oneDrive: true,
-			recent: true
+		mode: string;
+		pivots: Record<string, boolean>;
+	};
+}
+
+interface PickerResult {
+	command?: string;
+	items?: OneDriveFileInfo[];
+	[key: string]: any;
+}
+
+// Get picker parameters based on account type
+function getPickerParams(): PickerParams {
+	const channelId = uuidv4();
+	const config = OneDriveConfig.getInstance();
+	
+	const params: PickerParams = {
+		sdk: '8.0',
+		entry: {
+			oneDrive: {}
+		},
+		authentication: {},
+		messaging: {
+			origin: window?.location?.origin || '',
+			channelId
+		},
+		typesAndSources: {
+			mode: 'files',
+			pivots: {
+				oneDrive: true,
+				recent: true
+			}
 		}
+	};
+	
+	// For personal accounts, set files object in oneDrive
+	if (config.getAuthorityType() !== 'organizations') {
+		params.entry.oneDrive = { files: {} };
 	}
-};
+	
+	return params;
+}
+
+interface OneDriveFileInfo {
+	id: string;
+	name: string;
+	parentReference: {
+		driveId: string;
+	};
+	'@sharePoint.endpoint': string;
+	[key: string]: any;
+}
 
 // Download file from OneDrive
-async function downloadOneDriveFile(fileInfo: any): Promise<Blob> {
-	const accessToken = await getToken();
+async function downloadOneDriveFile(fileInfo: OneDriveFileInfo, authorityType?: 'personal' | 'organizations'): Promise<Blob> {
+	const accessToken = await getToken(undefined, authorityType);
 	if (!accessToken) {
 		throw new Error('Unable to retrieve OneDrive access token.');
 	}
+	
+	// The endpoint URL is provided in the file info
 	const fileInfoUrl = `${fileInfo['@sharePoint.endpoint']}/drives/${fileInfo.parentReference.driveId}/items/${fileInfo.id}`;
+	
 	const response = await fetch(fileInfoUrl, {
 		headers: {
 			Authorization: `Bearer ${accessToken}`
 		}
 	});
+	
 	if (!response.ok) {
-		throw new Error('Failed to fetch file information.');
+		throw new Error(`Failed to fetch file information: ${response.status} ${response.statusText}`);
 	}
+	
 	const fileData = await response.json();
 	const downloadUrl = fileData['@content.downloadUrl'];
+	
+	if (!downloadUrl) {
+		throw new Error('Download URL not found in file data');
+	}
+	
 	const downloadResponse = await fetch(downloadUrl);
+	
 	if (!downloadResponse.ok) {
-		throw new Error('Failed to download file.');
+		throw new Error(`Failed to download file: ${downloadResponse.status} ${downloadResponse.statusText}`);
 	}
+	
 	return await downloadResponse.blob();
 }
 
 // Open OneDrive file picker and return selected file metadata
-export async function openOneDrivePicker(): Promise<any | null> {
+export async function openOneDrivePicker(authorityType?: 'personal' | 'organizations'): Promise<PickerResult | null> {
 	if (typeof window === 'undefined') {
 		throw new Error('Not in browser environment');
 	}
+
+	// Initialize OneDrive config with the specified authority type
+	const config = OneDriveConfig.getInstance();
+	await config.initialize(authorityType);
+	
 	return new Promise((resolve, reject) => {
 		let pickerWindow: Window | null = null;
 		let channelPort: MessagePort | null = null;
+		const params = getPickerParams();
+		const baseUrl = config.getBaseUrl();
 
 		const handleWindowMessage = (event: MessageEvent) => {
 			if (event.source !== pickerWindow) return;
@@ -166,7 +285,9 @@ export async function openOneDrivePicker(): Promise<any | null> {
 					switch (command.command) {
 						case 'authenticate': {
 							try {
-								const newToken = await getToken();
+								// Pass the resource from the command for org accounts
+								const resource = config.getAuthorityType() === 'organizations' ? command.resource : undefined;
+								const newToken = await getToken(resource, authorityType);
 								if (newToken) {
 									channelPort?.postMessage({
 										type: 'result',
@@ -178,9 +299,12 @@ export async function openOneDrivePicker(): Promise<any | null> {
 								}
 							} catch (err) {
 								channelPort?.postMessage({
-									result: 'error',
-									error: { code: 'tokenError', message: 'Failed to get token' },
-									isExpected: true
+									type: 'result',
+									id: portData.id,
+									data: {
+										result: 'error',
+										error: { code: 'tokenError', message: 'Failed to get token' }
+									}
 								});
 							}
 							break;
@@ -227,7 +351,7 @@ export async function openOneDrivePicker(): Promise<any | null> {
 
 		const initializePicker = async () => {
 			try {
-				const authToken = await getToken();
+				const authToken = await getToken(undefined, authorityType);
 				if (!authToken) {
 					return reject(new Error('Failed to acquire access token'));
 				}
@@ -240,8 +364,14 @@ export async function openOneDrivePicker(): Promise<any | null> {
 				const queryString = new URLSearchParams({
 					filePicker: JSON.stringify(params)
 				});
-				const url = `${baseUrl}?${queryString.toString()}`;
 
+				let url = '';
+				if(config.getAuthorityType() === 'organizations') {
+					url = baseUrl + `/_layouts/15/FilePicker.aspx?${queryString}`;
+				} else {
+					url = baseUrl + `?${queryString}`;
+				}
+			
 				const form = pickerWindow.document.createElement('form');
 				form.setAttribute('action', url);
 				form.setAttribute('method', 'POST');
@@ -268,17 +398,17 @@ export async function openOneDrivePicker(): Promise<any | null> {
 }
 
 // Pick and download file from OneDrive
-export async function pickAndDownloadFile(): Promise<{ blob: Blob; name: string } | null> {
-	const pickerResult = await openOneDrivePicker();
+export async function pickAndDownloadFile(authorityType?: 'personal' | 'organizations'): Promise<{ blob: Blob; name: string } | null> {
+	const pickerResult = await openOneDrivePicker(authorityType);
 
 	if (!pickerResult || !pickerResult.items || pickerResult.items.length === 0) {
 		return null;
 	}
 
 	const selectedFile = pickerResult.items[0];
-	const blob = await downloadOneDriveFile(selectedFile);
+	const blob = await downloadOneDriveFile(selectedFile, authorityType);
 
 	return { blob, name: selectedFile.name };
 }
 
-export { downloadOneDriveFile };
+export { downloadOneDriveFile };

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

@@ -639,4 +639,5 @@
 			: 'light'}
 	richColors
 	position="top-right"
+	closeButton
 />