Merge pull request #12373 from open-webui/dev

0.6.1
This commit is contained in:
Timothy Jaeryang Baek 2025-04-05 10:15:32 -07:00 committed by GitHub
commit da94835165
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
134 changed files with 6230 additions and 1527 deletions

View file

@ -5,6 +5,29 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [0.6.1] - 2025-04-05
### Added
- 🛠️ **Global Tool Servers Configuration**: Admins can now centrally configure global external tool servers from Admin Settings > Tools, allowing seamless sharing of tool integrations across all users without manual setup per user.
- 🔐 **Direct Tool Usage Permission for Users**: Introduced a new user-level permission toggle that grants non-admin users access to direct external tools, empowering broader team collaboration while maintaining control.
- 🧠 **Mistral OCR Content Extraction Support**: Added native support for Mistral OCR as a high-accuracy document loader, drastically improving text extraction from scanned documents in RAG workflows.
- 🖼️ **Tools Indicator UI Redesign**: Enhanced message input now smartly displays both built-in and external tools via a unified dropdown, making it simpler and more intuitive to activate tools during conversations.
- 📄 **RAG Prompt Improved and More Coherent**: Default RAG system prompt has been revised to be more clear and citation-focused—admins can leave the template field empty to use this new gold-standard prompt.
- 🧰 **Performance & Developer Improvements**: Major internal restructuring of several tool-related components, simplifying styling and merging external/internal handling logic, resulting in better maintainability and performance.
- 🌍 **Improved Translations**: Updated translations for Tibetan, Polish, Chinese (Simplified & Traditional), Arabic, Russian, Ukrainian, Dutch, Finnish, and French to improve clarity and consistency across the interface.
### Fixed
- 🔑 **External Tool Server API Key Bug Resolved**: Fixed a critical issue where authentication headers were not being sent when calling tools from external OpenAPI tool servers, ensuring full security and smooth tool operations.
- 🚫 **Conditional Export Button Visibility**: UI now gracefully hides export buttons when there's nothing to export in models, prompts, tools, or functions, improving visual clarity and reducing confusion.
- 🧪 **Hybrid Search Failure Recovery**: Resolved edge case in parallel hybrid search where empty or unindexed collections caused backend crashes—these are now cleanly skipped to ensure system stability.
- 📂 **Admin Folder Deletion Fix**: Addressed an issue where folders created in the admin workspace couldn't be deleted, restoring full organizational flexibility for admins.
- 🔐 **Improved Generic Error Feedback on Login**: Authentication errors now show simplified, non-revealing messages for privacy and improved UX, especially with federated logins.
- 📝 **Tool Message with Images Improved**: Enhanced how tool-generated messages with image outputs are shown in chat, making them more readable and consistent with the overall UI design.
- ⚙️ **Auto-Exclusion for Broken RAG Collections**: Auto-skips document collections that fail to fetch data or return "None", preventing silent errors and streamlining retrieval workflows.
- 📝 **Docling Text File Handling Fix**: Fixed file parsing inconsistency that broke docling-based RAG functionality for certain plain text files, ensuring wider file compatibility.
## [0.6.0] - 2025-03-31
### Added

View file

@ -331,12 +331,14 @@ JWT_EXPIRES_IN = PersistentConfig(
# OAuth config
####################################
ENABLE_OAUTH_SIGNUP = PersistentConfig(
"ENABLE_OAUTH_SIGNUP",
"oauth.enable_signup",
os.environ.get("ENABLE_OAUTH_SIGNUP", "False").lower() == "true",
)
OAUTH_MERGE_ACCOUNTS_BY_EMAIL = PersistentConfig(
"OAUTH_MERGE_ACCOUNTS_BY_EMAIL",
"oauth.merge_accounts_by_email",
@ -466,6 +468,7 @@ OAUTH_USERNAME_CLAIM = PersistentConfig(
os.environ.get("OAUTH_USERNAME_CLAIM", "name"),
)
OAUTH_PICTURE_CLAIM = PersistentConfig(
"OAUTH_PICTURE_CLAIM",
"oauth.oidc.avatar_claim",
@ -878,6 +881,17 @@ except Exception:
pass
OPENAI_API_BASE_URL = "https://api.openai.com/v1"
####################################
# TOOL_SERVERS
####################################
TOOL_SERVER_CONNECTIONS = PersistentConfig(
"TOOL_SERVER_CONNECTIONS",
"tool_server.connections",
[],
)
####################################
# WEBUI
####################################
@ -1034,6 +1048,11 @@ USER_PERMISSIONS_CHAT_TEMPORARY_ENFORCED = (
== "true"
)
USER_PERMISSIONS_FEATURES_DIRECT_TOOL_SERVERS = (
os.environ.get("USER_PERMISSIONS_FEATURES_DIRECT_TOOL_SERVERS", "False").lower()
== "true"
)
USER_PERMISSIONS_FEATURES_WEB_SEARCH = (
os.environ.get("USER_PERMISSIONS_FEATURES_WEB_SEARCH", "True").lower() == "true"
)
@ -1071,6 +1090,7 @@ DEFAULT_USER_PERMISSIONS = {
"temporary_enforced": USER_PERMISSIONS_CHAT_TEMPORARY_ENFORCED,
},
"features": {
"direct_tool_servers": USER_PERMISSIONS_FEATURES_DIRECT_TOOL_SERVERS,
"web_search": USER_PERMISSIONS_FEATURES_WEB_SEARCH,
"image_generation": USER_PERMISSIONS_FEATURES_IMAGE_GENERATION,
"code_interpreter": USER_PERMISSIONS_FEATURES_CODE_INTERPRETER,
@ -1727,6 +1747,11 @@ DOCUMENT_INTELLIGENCE_KEY = PersistentConfig(
os.getenv("DOCUMENT_INTELLIGENCE_KEY", ""),
)
MISTRAL_OCR_API_KEY = PersistentConfig(
"MISTRAL_OCR_API_KEY",
"rag.mistral_ocr_api_key",
os.getenv("MISTRAL_OCR_API_KEY", ""),
)
BYPASS_EMBEDDING_AND_RETRIEVAL = PersistentConfig(
"BYPASS_EMBEDDING_AND_RETRIEVAL",
@ -1875,7 +1900,7 @@ CHUNK_OVERLAP = PersistentConfig(
)
DEFAULT_RAG_TEMPLATE = """### Task:
Respond to the user query using the provided context, incorporating inline citations in the format [source_id] **only when the <source_id> tag is explicitly provided** in the context.
Respond to the user query using the provided context, incorporating inline citations in the format [id] **only when the <source> tag includes an explicit id attribute** (e.g., <source id="1">).
### Guidelines:
- If you don't know the answer, clearly state that.
@ -1883,18 +1908,17 @@ Respond to the user query using the provided context, incorporating inline citat
- Respond in the same language as the user's query.
- If the context is unreadable or of poor quality, inform the user and provide the best possible answer.
- If the answer isn't present in the context but you possess the knowledge, explain this to the user and provide the answer using your own understanding.
- **Only include inline citations using [source_id] (e.g., [1], [2]) when a `<source_id>` tag is explicitly provided in the context.**
- Do not cite if the <source_id> tag is not provided in the context.
- **Only include inline citations using [id] (e.g., [1], [2]) when the <source> tag includes an id attribute.**
- Do not cite if the <source> tag does not contain an id attribute.
- Do not use XML tags in your response.
- Ensure citations are concise and directly related to the information provided.
### Example of Citation:
If the user asks about a specific topic and the information is found in "whitepaper.pdf" with a provided <source_id>, the response should include the citation like so:
* "According to the study, the proposed method increases efficiency by 20% [whitepaper.pdf]."
If no <source_id> is present, the response should omit the citation.
If the user asks about a specific topic and the information is found in a source with a provided id attribute, the response should include the citation like in the following example:
* "According to the study, the proposed method increases efficiency by 20% [1]."
### Output:
Provide a clear and direct response to the user's query, including inline citations in the format [source_id] only when the <source_id> tag is present in the context.
Provide a clear and direct response to the user's query, including inline citations in the format [id] only when the <source> tag with id attribute is present in the context.
<context>
{{CONTEXT}}

View file

@ -105,6 +105,8 @@ from open_webui.config import (
OPENAI_API_CONFIGS,
# Direct Connections
ENABLE_DIRECT_CONNECTIONS,
# Tool Server Configs
TOOL_SERVER_CONNECTIONS,
# Code Execution
ENABLE_CODE_EXECUTION,
CODE_EXECUTION_ENGINE,
@ -191,6 +193,7 @@ from open_webui.config import (
DOCLING_SERVER_URL,
DOCUMENT_INTELLIGENCE_ENDPOINT,
DOCUMENT_INTELLIGENCE_KEY,
MISTRAL_OCR_API_KEY,
RAG_TOP_K,
RAG_TOP_K_RERANKER,
RAG_TEXT_SPLITTER,
@ -355,6 +358,7 @@ from open_webui.utils.access_control import has_access
from open_webui.utils.auth import (
get_license_data,
get_http_authorization_cred,
decode_token,
get_admin_user,
get_verified_user,
@ -477,6 +481,15 @@ app.state.config.OPENAI_API_CONFIGS = OPENAI_API_CONFIGS
app.state.OPENAI_MODELS = {}
########################################
#
# TOOL SERVERS
#
########################################
app.state.config.TOOL_SERVER_CONNECTIONS = TOOL_SERVER_CONNECTIONS
app.state.TOOL_SERVERS = []
########################################
#
# DIRECT CONNECTIONS
@ -582,6 +595,7 @@ app.state.config.TIKA_SERVER_URL = TIKA_SERVER_URL
app.state.config.DOCLING_SERVER_URL = DOCLING_SERVER_URL
app.state.config.DOCUMENT_INTELLIGENCE_ENDPOINT = DOCUMENT_INTELLIGENCE_ENDPOINT
app.state.config.DOCUMENT_INTELLIGENCE_KEY = DOCUMENT_INTELLIGENCE_KEY
app.state.config.MISTRAL_OCR_API_KEY = MISTRAL_OCR_API_KEY
app.state.config.TEXT_SPLITTER = RAG_TEXT_SPLITTER
app.state.config.TIKTOKEN_ENCODING_NAME = TIKTOKEN_ENCODING_NAME
@ -862,6 +876,10 @@ async def commit_session_after_request(request: Request, call_next):
@app.middleware("http")
async def check_url(request: Request, call_next):
start_time = int(time.time())
request.state.token = get_http_authorization_cred(
request.headers.get("Authorization")
)
request.state.enable_api_key = app.state.config.ENABLE_API_KEY
response = await call_next(request)
process_time = int(time.time()) - start_time

View file

@ -20,6 +20,9 @@ from langchain_community.document_loaders import (
YoutubeLoader,
)
from langchain_core.documents import Document
from open_webui.retrieval.loaders.mistral import MistralLoader
from open_webui.env import SRC_LOG_LEVELS, GLOBAL_LOG_LEVEL
logging.basicConfig(stream=sys.stdout, level=GLOBAL_LOG_LEVEL)
@ -181,13 +184,16 @@ class Loader:
for doc in docs
]
def _is_text_file(self, file_ext: str, file_content_type: str) -> bool:
return file_ext in known_source_ext or (
file_content_type and file_content_type.find("text/") >= 0
)
def _get_loader(self, filename: str, file_content_type: str, file_path: str):
file_ext = filename.split(".")[-1].lower()
if self.engine == "tika" and self.kwargs.get("TIKA_SERVER_URL"):
if file_ext in known_source_ext or (
file_content_type and file_content_type.find("text/") >= 0
):
if self._is_text_file(file_ext, file_content_type):
loader = TextLoader(file_path, autodetect_encoding=True)
else:
loader = TikaLoader(
@ -196,11 +202,14 @@ class Loader:
mime_type=file_content_type,
)
elif self.engine == "docling" and self.kwargs.get("DOCLING_SERVER_URL"):
loader = DoclingLoader(
url=self.kwargs.get("DOCLING_SERVER_URL"),
file_path=file_path,
mime_type=file_content_type,
)
if self._is_text_file(file_ext, file_content_type):
loader = TextLoader(file_path, autodetect_encoding=True)
else:
loader = DoclingLoader(
url=self.kwargs.get("DOCLING_SERVER_URL"),
file_path=file_path,
mime_type=file_content_type,
)
elif (
self.engine == "document_intelligence"
and self.kwargs.get("DOCUMENT_INTELLIGENCE_ENDPOINT") != ""
@ -222,6 +231,15 @@ class Loader:
api_endpoint=self.kwargs.get("DOCUMENT_INTELLIGENCE_ENDPOINT"),
api_key=self.kwargs.get("DOCUMENT_INTELLIGENCE_KEY"),
)
elif (
self.engine == "mistral_ocr"
and self.kwargs.get("MISTRAL_OCR_API_KEY") != ""
and file_ext
in ["pdf"] # Mistral OCR currently only supports PDF and images
):
loader = MistralLoader(
api_key=self.kwargs.get("MISTRAL_OCR_API_KEY"), file_path=file_path
)
else:
if file_ext == "pdf":
loader = PyPDFLoader(
@ -257,9 +275,7 @@ class Loader:
loader = UnstructuredPowerPointLoader(file_path)
elif file_ext == "msg":
loader = OutlookMessageLoader(file_path)
elif file_ext in known_source_ext or (
file_content_type and file_content_type.find("text/") >= 0
):
elif self._is_text_file(file_ext, file_content_type):
loader = TextLoader(file_path, autodetect_encoding=True)
else:
loader = TextLoader(file_path, autodetect_encoding=True)

View file

@ -0,0 +1,225 @@
import requests
import logging
import os
import sys
from typing import List, Dict, Any
from langchain_core.documents import Document
from open_webui.env import SRC_LOG_LEVELS, GLOBAL_LOG_LEVEL
logging.basicConfig(stream=sys.stdout, level=GLOBAL_LOG_LEVEL)
log = logging.getLogger(__name__)
log.setLevel(SRC_LOG_LEVELS["RAG"])
class MistralLoader:
"""
Loads documents by processing them through the Mistral OCR API.
"""
BASE_API_URL = "https://api.mistral.ai/v1"
def __init__(self, api_key: str, file_path: str):
"""
Initializes the loader.
Args:
api_key: Your Mistral API key.
file_path: The local path to the PDF file to process.
"""
if not api_key:
raise ValueError("API key cannot be empty.")
if not os.path.exists(file_path):
raise FileNotFoundError(f"File not found at {file_path}")
self.api_key = api_key
self.file_path = file_path
self.headers = {"Authorization": f"Bearer {self.api_key}"}
def _handle_response(self, response: requests.Response) -> Dict[str, Any]:
"""Checks response status and returns JSON content."""
try:
response.raise_for_status() # Raises HTTPError for bad responses (4xx or 5xx)
# Handle potential empty responses for certain successful requests (e.g., DELETE)
if response.status_code == 204 or not response.content:
return {} # Return empty dict if no content
return response.json()
except requests.exceptions.HTTPError as http_err:
log.error(f"HTTP error occurred: {http_err} - Response: {response.text}")
raise
except requests.exceptions.RequestException as req_err:
log.error(f"Request exception occurred: {req_err}")
raise
except ValueError as json_err: # Includes JSONDecodeError
log.error(f"JSON decode error: {json_err} - Response: {response.text}")
raise # Re-raise after logging
def _upload_file(self) -> str:
"""Uploads the file to Mistral for OCR processing."""
log.info("Uploading file to Mistral API")
url = f"{self.BASE_API_URL}/files"
file_name = os.path.basename(self.file_path)
try:
with open(self.file_path, "rb") as f:
files = {"file": (file_name, f, "application/pdf")}
data = {"purpose": "ocr"}
upload_headers = self.headers.copy() # Avoid modifying self.headers
response = requests.post(
url, headers=upload_headers, files=files, data=data
)
response_data = self._handle_response(response)
file_id = response_data.get("id")
if not file_id:
raise ValueError("File ID not found in upload response.")
log.info(f"File uploaded successfully. File ID: {file_id}")
return file_id
except Exception as e:
log.error(f"Failed to upload file: {e}")
raise
def _get_signed_url(self, file_id: str) -> str:
"""Retrieves a temporary signed URL for the uploaded file."""
log.info(f"Getting signed URL for file ID: {file_id}")
url = f"{self.BASE_API_URL}/files/{file_id}/url"
params = {"expiry": 1}
signed_url_headers = {**self.headers, "Accept": "application/json"}
try:
response = requests.get(url, headers=signed_url_headers, params=params)
response_data = self._handle_response(response)
signed_url = response_data.get("url")
if not signed_url:
raise ValueError("Signed URL not found in response.")
log.info("Signed URL received.")
return signed_url
except Exception as e:
log.error(f"Failed to get signed URL: {e}")
raise
def _process_ocr(self, signed_url: str) -> Dict[str, Any]:
"""Sends the signed URL to the OCR endpoint for processing."""
log.info("Processing OCR via Mistral API")
url = f"{self.BASE_API_URL}/ocr"
ocr_headers = {
**self.headers,
"Content-Type": "application/json",
"Accept": "application/json",
}
payload = {
"model": "mistral-ocr-latest",
"document": {
"type": "document_url",
"document_url": signed_url,
},
"include_image_base64": False,
}
try:
response = requests.post(url, headers=ocr_headers, json=payload)
ocr_response = self._handle_response(response)
log.info("OCR processing done.")
log.debug("OCR response: %s", ocr_response)
return ocr_response
except Exception as e:
log.error(f"Failed during OCR processing: {e}")
raise
def _delete_file(self, file_id: str) -> None:
"""Deletes the file from Mistral storage."""
log.info(f"Deleting uploaded file ID: {file_id}")
url = f"{self.BASE_API_URL}/files/{file_id}"
# No specific Accept header needed, default or Authorization is usually sufficient
try:
response = requests.delete(url, headers=self.headers)
delete_response = self._handle_response(
response
) # Check status, ignore response body unless needed
log.info(
f"File deleted successfully: {delete_response}"
) # Log the response if available
except Exception as e:
# Log error but don't necessarily halt execution if deletion fails
log.error(f"Failed to delete file ID {file_id}: {e}")
# Depending on requirements, you might choose to raise the error here
def load(self) -> List[Document]:
"""
Executes the full OCR workflow: upload, get URL, process OCR, delete file.
Returns:
A list of Document objects, one for each page processed.
"""
file_id = None
try:
# 1. Upload file
file_id = self._upload_file()
# 2. Get Signed URL
signed_url = self._get_signed_url(file_id)
# 3. Process OCR
ocr_response = self._process_ocr(signed_url)
# 4. Process results
pages_data = ocr_response.get("pages")
if not pages_data:
log.warning("No pages found in OCR response.")
return [Document(page_content="No text content found", metadata={})]
documents = []
total_pages = len(pages_data)
for page_data in pages_data:
page_content = page_data.get("markdown")
page_index = page_data.get("index") # API uses 0-based index
if page_content is not None and page_index is not None:
documents.append(
Document(
page_content=page_content,
metadata={
"page": page_index, # 0-based index from API
"page_label": page_index
+ 1, # 1-based label for convenience
"total_pages": total_pages,
# Add other relevant metadata from page_data if available/needed
# e.g., page_data.get('width'), page_data.get('height')
},
)
)
else:
log.warning(
f"Skipping page due to missing 'markdown' or 'index'. Data: {page_data}"
)
if not documents:
# Case where pages existed but none had valid markdown/index
log.warning(
"OCR response contained pages, but none had valid content/index."
)
return [
Document(
page_content="No text content found in valid pages", metadata={}
)
]
return documents
except Exception as e:
log.error(f"An error occurred during the loading process: {e}")
# Return an empty list or a specific error document on failure
return [Document(page_content=f"Error during processing: {e}", metadata={})]
finally:
# 5. Delete file (attempt even if prior steps failed after upload)
if file_id:
try:
self._delete_file(file_id)
except Exception as del_e:
# Log deletion error, but don't overwrite original error if one occurred
log.error(
f"Cleanup error: Could not delete file ID {file_id}. Reason: {del_e}"
)

View file

@ -320,10 +320,13 @@ def query_collection_with_hybrid_search(
log.exception(f"Error when querying the collection with hybrid_search: {e}")
return None, e
# Prepare tasks for all collections and queries
# Avoid running any tasks for collections that failed to fetch data (have assigned None)
tasks = [
(collection_name, query)
for collection_name in collection_names
for query in queries
(cn, q)
for cn in collection_names
if collection_results[cn] is not None
for q in queries
]
with ThreadPoolExecutor() as executor:

View file

@ -194,8 +194,8 @@ async def ldap_auth(request: Request, response: Response, form_data: LdapForm):
ciphers=LDAP_CIPHERS,
)
except Exception as e:
log.error(f"An error occurred on TLS: {str(e)}")
raise HTTPException(400, detail=str(e))
log.error(f"TLS configuration error: {str(e)}")
raise HTTPException(400, detail="Failed to configure TLS for LDAP connection.")
try:
server = Server(
@ -232,7 +232,7 @@ async def ldap_auth(request: Request, response: Response, form_data: LdapForm):
username = str(entry[f"{LDAP_ATTRIBUTE_FOR_USERNAME}"]).lower()
email = str(entry[f"{LDAP_ATTRIBUTE_FOR_MAIL}"])
if not email or email == "" or email == "[]":
raise HTTPException(400, f"User {form_data.user} does not have email.")
raise HTTPException(400, "User does not have a valid email address.")
else:
email = email.lower()
@ -248,7 +248,7 @@ async def ldap_auth(request: Request, response: Response, form_data: LdapForm):
authentication="SIMPLE",
)
if not connection_user.bind():
raise HTTPException(400, f"Authentication failed for {form_data.user}")
raise HTTPException(400, "Authentication failed.")
user = Users.get_user_by_email(email)
if not user:
@ -276,7 +276,10 @@ async def ldap_auth(request: Request, response: Response, form_data: LdapForm):
except HTTPException:
raise
except Exception as err:
raise HTTPException(500, detail=ERROR_MESSAGES.DEFAULT(err))
log.error(f"LDAP user creation error: {str(err)}")
raise HTTPException(
500, detail="Internal error occurred during LDAP user creation."
)
user = Auths.authenticate_user_by_trusted_header(email)
@ -312,12 +315,10 @@ async def ldap_auth(request: Request, response: Response, form_data: LdapForm):
else:
raise HTTPException(400, detail=ERROR_MESSAGES.INVALID_CRED)
else:
raise HTTPException(
400,
f"User {form_data.user} does not match the record. Search result: {str(entry[f'{LDAP_ATTRIBUTE_FOR_USERNAME}'])}",
)
raise HTTPException(400, "User record mismatch.")
except Exception as e:
raise HTTPException(400, detail=str(e))
log.error(f"LDAP authentication error: {str(e)}")
raise HTTPException(400, detail="LDAP authentication failed.")
############################
@ -519,7 +520,8 @@ async def signup(request: Request, response: Response, form_data: SignupForm):
else:
raise HTTPException(500, detail=ERROR_MESSAGES.CREATE_USER_ERROR)
except Exception as err:
raise HTTPException(500, detail=ERROR_MESSAGES.DEFAULT(err))
log.error(f"Signup error: {str(err)}")
raise HTTPException(500, detail="An internal error occurred during signup.")
@router.get("/signout")
@ -547,7 +549,11 @@ async def signout(request: Request, response: Response):
detail="Failed to fetch OpenID configuration",
)
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
log.error(f"OpenID signout error: {str(e)}")
raise HTTPException(
status_code=500,
detail="Failed to sign out from the OpenID provider.",
)
return {"status": True}
@ -591,7 +597,10 @@ async def add_user(form_data: AddUserForm, user=Depends(get_admin_user)):
else:
raise HTTPException(500, detail=ERROR_MESSAGES.CREATE_USER_ERROR)
except Exception as err:
raise HTTPException(500, detail=ERROR_MESSAGES.DEFAULT(err))
log.error(f"Add user error: {str(err)}")
raise HTTPException(
500, detail="An internal error occurred while adding the user."
)
############################
@ -764,11 +773,6 @@ async def update_ldap_server(
if not value:
raise HTTPException(400, detail=f"Required field {key} is empty")
if form_data.use_tls and not form_data.certificate_path:
raise HTTPException(
400, detail="TLS is enabled but certificate file path is missing"
)
request.app.state.config.LDAP_SERVER_LABEL = form_data.label
request.app.state.config.LDAP_SERVER_HOST = form_data.host
request.app.state.config.LDAP_SERVER_PORT = form_data.port

View file

@ -1,5 +1,5 @@
from fastapi import APIRouter, Depends, Request
from pydantic import BaseModel
from fastapi import APIRouter, Depends, Request, HTTPException
from pydantic import BaseModel, ConfigDict
from typing import Optional
@ -7,6 +7,8 @@ from open_webui.utils.auth import get_admin_user, get_verified_user
from open_webui.config import get_config, save_config
from open_webui.config import BannerModel
from open_webui.utils.tools import get_tool_server_data, get_tool_servers_data
router = APIRouter()
@ -66,6 +68,75 @@ async def set_direct_connections_config(
}
############################
# ToolServers Config
############################
class ToolServerConnection(BaseModel):
url: str
path: str
auth_type: Optional[str]
key: Optional[str]
config: Optional[dict]
model_config = ConfigDict(extra="allow")
class ToolServersConfigForm(BaseModel):
TOOL_SERVER_CONNECTIONS: list[ToolServerConnection]
@router.get("/tool_servers", response_model=ToolServersConfigForm)
async def get_tool_servers_config(request: Request, user=Depends(get_admin_user)):
return {
"TOOL_SERVER_CONNECTIONS": request.app.state.config.TOOL_SERVER_CONNECTIONS,
}
@router.post("/tool_servers", response_model=ToolServersConfigForm)
async def set_tool_servers_config(
request: Request,
form_data: ToolServersConfigForm,
user=Depends(get_admin_user),
):
request.app.state.config.TOOL_SERVER_CONNECTIONS = [
connection.model_dump() for connection in form_data.TOOL_SERVER_CONNECTIONS
]
request.app.state.TOOL_SERVERS = await get_tool_servers_data(
request.app.state.config.TOOL_SERVER_CONNECTIONS
)
return {
"TOOL_SERVER_CONNECTIONS": request.app.state.config.TOOL_SERVER_CONNECTIONS,
}
@router.post("/tool_servers/verify")
async def verify_tool_servers_config(
request: Request, form_data: ToolServerConnection, user=Depends(get_admin_user)
):
"""
Verify the connection to the tool server.
"""
try:
token = None
if form_data.auth_type == "bearer":
token = form_data.key
elif form_data.auth_type == "session":
token = request.state.token.credentials
url = f"{form_data.url}/{form_data.path}"
return await get_tool_server_data(token, url)
except Exception as e:
raise HTTPException(
status_code=400,
detail=f"Failed to connect to the tool server: {str(e)}",
)
############################
# CodeInterpreterConfig
############################

View file

@ -236,7 +236,8 @@ async def delete_folder_by_id(
chat_delete_permission = has_permission(
user.id, "chat.delete", request.app.state.config.USER_PERMISSIONS
)
if not chat_delete_permission:
if user.role != "admin" and not chat_delete_permission:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail=ERROR_MESSAGES.ACCESS_PROHIBITED,

View file

@ -1197,7 +1197,7 @@ class OpenAIChatMessageContent(BaseModel):
class OpenAIChatMessage(BaseModel):
role: str
content: Union[str, list[OpenAIChatMessageContent]]
content: Union[Optional[str], list[OpenAIChatMessageContent]]
model_config = ConfigDict(extra="allow")

View file

@ -124,7 +124,7 @@ def get_ef(
def get_rf(
reranking_model: str,
reranking_model: Optional[str] = None,
auto_update: bool = False,
):
rf = None
@ -364,6 +364,9 @@ async def get_rag_config(request: Request, user=Depends(get_admin_user)):
"endpoint": request.app.state.config.DOCUMENT_INTELLIGENCE_ENDPOINT,
"key": request.app.state.config.DOCUMENT_INTELLIGENCE_KEY,
},
"mistral_ocr_config": {
"api_key": request.app.state.config.MISTRAL_OCR_API_KEY,
},
},
"chunk": {
"text_splitter": request.app.state.config.TEXT_SPLITTER,
@ -427,11 +430,16 @@ class DocumentIntelligenceConfigForm(BaseModel):
key: str
class MistralOCRConfigForm(BaseModel):
api_key: str
class ContentExtractionConfig(BaseModel):
engine: str = ""
tika_server_url: Optional[str] = None
docling_server_url: Optional[str] = None
document_intelligence_config: Optional[DocumentIntelligenceConfigForm] = None
mistral_ocr_config: Optional[MistralOCRConfigForm] = None
class ChunkParamUpdateForm(BaseModel):
@ -553,6 +561,10 @@ async def update_rag_config(
request.app.state.config.DOCUMENT_INTELLIGENCE_KEY = (
form_data.content_extraction.document_intelligence_config.key
)
if form_data.content_extraction.mistral_ocr_config is not None:
request.app.state.config.MISTRAL_OCR_API_KEY = (
form_data.content_extraction.mistral_ocr_config.api_key
)
if form_data.chunk is not None:
request.app.state.config.TEXT_SPLITTER = form_data.chunk.text_splitter
@ -659,6 +671,9 @@ async def update_rag_config(
"endpoint": request.app.state.config.DOCUMENT_INTELLIGENCE_ENDPOINT,
"key": request.app.state.config.DOCUMENT_INTELLIGENCE_KEY,
},
"mistral_ocr_config": {
"api_key": request.app.state.config.MISTRAL_OCR_API_KEY,
},
},
"chunk": {
"text_splitter": request.app.state.config.TEXT_SPLITTER,
@ -747,6 +762,9 @@ async def update_query_settings(
form_data.hybrid if form_data.hybrid else False
)
if not request.app.state.config.ENABLE_RAG_HYBRID_SEARCH:
request.app.state.rf = None
return {
"status": True,
"template": request.app.state.config.RAG_TEMPLATE,
@ -1007,6 +1025,7 @@ def process_file(
PDF_EXTRACT_IMAGES=request.app.state.config.PDF_EXTRACT_IMAGES,
DOCUMENT_INTELLIGENCE_ENDPOINT=request.app.state.config.DOCUMENT_INTELLIGENCE_ENDPOINT,
DOCUMENT_INTELLIGENCE_KEY=request.app.state.config.DOCUMENT_INTELLIGENCE_KEY,
MISTRAL_OCR_API_KEY=request.app.state.config.MISTRAL_OCR_API_KEY,
)
docs = loader.load(
file.filename, file.meta.get("content_type"), file_path
@ -1515,8 +1534,13 @@ def query_doc_handler(
):
try:
if request.app.state.config.ENABLE_RAG_HYBRID_SEARCH:
collection_results = {}
collection_results[form_data.collection_name] = VECTOR_DB_CLIENT.get(
collection_name=form_data.collection_name
)
return query_doc_with_hybrid_search(
collection_name=form_data.collection_name,
collection_result=collection_results[form_data.collection_name],
query=form_data.query,
embedding_function=lambda query, prefix: request.app.state.EMBEDDING_FUNCTION(
query, prefix=prefix, user=user

View file

@ -653,17 +653,6 @@ async def generate_moa_response(
detail="Model not found",
)
# Check if the user has a custom task model
# If the user has a custom task model, use that model
task_model_id = get_task_model_id(
model_id,
request.app.state.config.TASK_MODEL,
request.app.state.config.TASK_MODEL_EXTERNAL,
models,
)
log.debug(f"generating MOA model {task_model_id} for user {user.email} ")
template = DEFAULT_MOA_GENERATION_PROMPT_TEMPLATE
content = moa_response_generation_template(
@ -673,7 +662,7 @@ async def generate_moa_response(
)
payload = {
"model": task_model_id,
"model": model_id,
"messages": [{"role": "user", "content": content}],
"stream": form_data.get("stream", False),
"metadata": {

View file

@ -1,6 +1,7 @@
import logging
from pathlib import Path
from typing import Optional
import time
from open_webui.models.tools import (
ToolForm,
@ -18,6 +19,8 @@ from open_webui.utils.auth import get_admin_user, get_verified_user
from open_webui.utils.access_control import has_access, has_permission
from open_webui.env import SRC_LOG_LEVELS
from open_webui.utils.tools import get_tool_servers_data
log = logging.getLogger(__name__)
log.setLevel(SRC_LOG_LEVELS["MAIN"])
@ -30,11 +33,51 @@ router = APIRouter()
@router.get("/", response_model=list[ToolUserResponse])
async def get_tools(user=Depends(get_verified_user)):
if user.role == "admin":
tools = Tools.get_tools()
else:
tools = Tools.get_tools_by_user_id(user.id, "read")
async def get_tools(request: Request, user=Depends(get_verified_user)):
if not request.app.state.TOOL_SERVERS:
# If the tool servers are not set, we need to set them
# This is done only once when the server starts
# This is done to avoid loading the tool servers every time
request.app.state.TOOL_SERVERS = await get_tool_servers_data(
request.app.state.config.TOOL_SERVER_CONNECTIONS
)
tools = Tools.get_tools()
for idx, server in enumerate(request.app.state.TOOL_SERVERS):
tools.append(
ToolUserResponse(
**{
"id": f"server:{server['idx']}",
"user_id": f"server:{server['idx']}",
"name": server["openapi"]
.get("info", {})
.get("title", "Tool Server"),
"meta": {
"description": server["openapi"]
.get("info", {})
.get("description", ""),
},
"access_control": request.app.state.config.TOOL_SERVER_CONNECTIONS[
idx
]
.get("config", {})
.get("access_control", None),
"updated_at": int(time.time()),
"created_at": int(time.time()),
}
)
)
if user.role != "admin":
tools = [
tool
for tool in tools
if tool.user_id == user.id
or has_access(user.id, "read", tool.access_control)
]
return tools

View file

@ -93,6 +93,7 @@ class ChatPermissions(BaseModel):
class FeaturesPermissions(BaseModel):
direct_tool_servers: bool = False
web_search: bool = True
image_generation: bool = True
code_interpreter: bool = True

View file

@ -8,7 +8,9 @@ import requests
import os
from datetime import UTC, datetime, timedelta
from datetime import datetime, timedelta
import pytz
from pytz import UTC
from typing import Optional, Union, List, Dict
from open_webui.models.users import Users
@ -141,12 +143,14 @@ def create_api_key():
return f"sk-{key}"
def get_http_authorization_cred(auth_header: str):
def get_http_authorization_cred(auth_header: Optional[str]):
if not auth_header:
return None
try:
scheme, credentials = auth_header.split(" ")
return HTTPAuthorizationCredentials(scheme=scheme, credentials=credentials)
except Exception:
raise ValueError(ERROR_MESSAGES.INVALID_TOKEN)
return None
def get_current_user(
@ -180,7 +184,12 @@ def get_current_user(
).split(",")
]
if request.url.path not in allowed_paths:
# Check if the request path matches any allowed endpoint.
if not any(
request.url.path == allowed
or request.url.path.startswith(allowed + "/")
for allowed in allowed_paths
):
raise HTTPException(
status.HTTP_403_FORBIDDEN, detail=ERROR_MESSAGES.API_KEY_NOT_ALLOWED
)

View file

@ -12,9 +12,9 @@ log.setLevel(SRC_LOG_LEVELS["MAIN"])
def get_sorted_filter_ids(model: dict):
def get_priority(function_id):
function = Functions.get_function_by_id(function_id)
if function is not None and hasattr(function, "valves"):
# TODO: Fix FunctionModel to include vavles
return (function.valves if function.valves else {}).get("priority", 0)
if function is not None:
valves = Functions.get_function_valves_by_id(function_id)
return valves.get("priority", 0) if valves else 0
return 0
filter_ids = [function.id for function in Functions.get_global_filter_functions()]

View file

@ -221,13 +221,23 @@ async def chat_completion_tools_handler(
except Exception as e:
tool_result = str(e)
tool_result_files = []
if isinstance(tool_result, list):
for item in tool_result:
# check if string
if isinstance(item, str) and item.startswith("data:"):
tool_result_files.append(item)
tool_result.remove(item)
if isinstance(tool_result, dict) or isinstance(tool_result, list):
tool_result = json.dumps(tool_result, indent=2)
if isinstance(tool_result, str):
tool = tools[tool_function_name]
tool_id = tool.get("toolkit_id", "")
if tool.get("citation", False) or tool.get("direct", False):
tool_id = tool.get("tool_id", "")
if tool.get("metadata", {}).get("citation", False) or tool.get(
"direct", False
):
sources.append(
{
@ -238,7 +248,7 @@ async def chat_completion_tools_handler(
else f"{tool_function_name}"
),
},
"document": [tool_result],
"document": [tool_result, *tool_result_files],
"metadata": [
{
"source": (
@ -254,7 +264,7 @@ async def chat_completion_tools_handler(
sources.append(
{
"source": {},
"document": [tool_result],
"document": [tool_result, *tool_result_files],
"metadata": [
{
"source": (
@ -267,7 +277,11 @@ async def chat_completion_tools_handler(
}
)
if tools[tool_function_name].get("file_handler", False):
if (
tools[tool_function_name]
.get("metadata", {})
.get("file_handler", False)
):
skip_files = True
# check if "tool_calls" in result
@ -625,27 +639,28 @@ def apply_params_to_form_data(form_data, model):
if "keep_alive" in params:
form_data["keep_alive"] = params["keep_alive"]
else:
if "seed" in params:
if "seed" in params and params["seed"] is not None:
form_data["seed"] = params["seed"]
if "stop" in params:
if "stop" in params and params["stop"] is not None:
form_data["stop"] = params["stop"]
if "temperature" in params:
if "temperature" in params and params["temperature"] is not None:
form_data["temperature"] = params["temperature"]
if "max_tokens" in params:
if "max_tokens" in params and params["max_tokens"] is not None:
form_data["max_tokens"] = params["max_tokens"]
if "top_p" in params:
if "top_p" in params and params["top_p"] is not None:
form_data["top_p"] = params["top_p"]
if "frequency_penalty" in params:
if "frequency_penalty" in params and params["frequency_penalty"] is not None:
form_data["frequency_penalty"] = params["frequency_penalty"]
if "reasoning_effort" in params:
if "reasoning_effort" in params and params["reasoning_effort"] is not None:
form_data["reasoning_effort"] = params["reasoning_effort"]
if "logit_bias" in params:
if "logit_bias" in params and params["logit_bias"] is not None:
try:
form_data["logit_bias"] = json.loads(
convert_logit_bias_input_to_json(params["logit_bias"])
@ -865,7 +880,9 @@ async def process_chat_payload(request, form_data, user, metadata, model):
for source_idx, source in enumerate(sources):
if "document" in source:
for doc_idx, doc_context in enumerate(source["document"]):
context_string += f"<source><source_id>{source_idx + 1}</source_id><source_context>{doc_context}</source_context></source>\n"
context_string += (
f'<source id="{source_idx + 1}">{doc_context}</source>\n'
)
context_string = context_string.strip()
prompt = get_last_user_message(form_data["messages"])
@ -1198,13 +1215,15 @@ async def process_chat_response(
)
tool_result = None
tool_result_files = None
for result in results:
if tool_call_id == result.get("tool_call_id", ""):
tool_result = result.get("content", None)
tool_result_files = result.get("files", None)
break
if tool_result:
tool_calls_display_content = f'{tool_calls_display_content}\n<details type="tool_calls" done="true" id="{tool_call_id}" name="{tool_name}" arguments="{html.escape(json.dumps(tool_arguments))}" result="{html.escape(json.dumps(tool_result))}">\n<summary>Tool Executed</summary>\n</details>'
tool_calls_display_content = f'{tool_calls_display_content}\n<details type="tool_calls" done="true" id="{tool_call_id}" name="{tool_name}" arguments="{html.escape(json.dumps(tool_arguments))}" result="{html.escape(json.dumps(tool_result))}" files="{html.escape(json.dumps(tool_result_files)) if tool_result_files else ""}">\n<summary>Tool Executed</summary>\n</details>\n'
else:
tool_calls_display_content = f'{tool_calls_display_content}\n<details type="tool_calls" done="false" id="{tool_call_id}" name="{tool_name}" arguments="{html.escape(json.dumps(tool_arguments))}">\n<summary>Executing...</summary>\n</details>'
@ -1805,7 +1824,7 @@ async def process_chat_response(
await stream_body_handler(response)
MAX_TOOL_CALL_RETRIES = 5
MAX_TOOL_CALL_RETRIES = 10
tool_call_retries = 0
while len(tool_calls) > 0 and tool_call_retries < MAX_TOOL_CALL_RETRIES:
@ -1898,6 +1917,14 @@ async def process_chat_response(
except Exception as e:
tool_result = str(e)
tool_result_files = []
if isinstance(tool_result, list):
for item in tool_result:
# check if string
if isinstance(item, str) and item.startswith("data:"):
tool_result_files.append(item)
tool_result.remove(item)
if isinstance(tool_result, dict) or isinstance(
tool_result, list
):
@ -1907,6 +1934,11 @@ async def process_chat_response(
{
"tool_call_id": tool_call_id,
"content": tool_result,
**(
{"files": tool_result_files}
if tool_result_files
else {}
),
}
)

View file

@ -326,40 +326,45 @@ class OAuthManager:
raise HTTPException(400, detail=ERROR_MESSAGES.EMAIL_TAKEN)
picture_claim = auth_manager_config.OAUTH_PICTURE_CLAIM
picture_url = user_data.get(
picture_claim, OAUTH_PROVIDERS[provider].get("picture_url", "")
)
if picture_url:
# Download the profile image into a base64 string
try:
access_token = token.get("access_token")
get_kwargs = {}
if access_token:
get_kwargs["headers"] = {
"Authorization": f"Bearer {access_token}",
}
async with aiohttp.ClientSession() as session:
async with session.get(picture_url, **get_kwargs) as resp:
if resp.ok:
picture = await resp.read()
base64_encoded_picture = base64.b64encode(
picture
).decode("utf-8")
guessed_mime_type = mimetypes.guess_type(
picture_url
)[0]
if guessed_mime_type is None:
# assume JPG, browsers are tolerant enough of image formats
guessed_mime_type = "image/jpeg"
picture_url = f"data:{guessed_mime_type};base64,{base64_encoded_picture}"
else:
picture_url = "/user.png"
except Exception as e:
log.error(
f"Error downloading profile image '{picture_url}': {e}"
)
if picture_claim:
picture_url = user_data.get(
picture_claim, OAUTH_PROVIDERS[provider].get("picture_url", "")
)
if picture_url:
# Download the profile image into a base64 string
try:
access_token = token.get("access_token")
get_kwargs = {}
if access_token:
get_kwargs["headers"] = {
"Authorization": f"Bearer {access_token}",
}
async with aiohttp.ClientSession() as session:
async with session.get(
picture_url, **get_kwargs
) as resp:
if resp.ok:
picture = await resp.read()
base64_encoded_picture = base64.b64encode(
picture
).decode("utf-8")
guessed_mime_type = mimetypes.guess_type(
picture_url
)[0]
if guessed_mime_type is None:
# assume JPG, browsers are tolerant enough of image formats
guessed_mime_type = "image/jpeg"
picture_url = f"data:{guessed_mime_type};base64,{base64_encoded_picture}"
else:
picture_url = "/user.png"
except Exception as e:
log.error(
f"Error downloading profile image '{picture_url}': {e}"
)
picture_url = "/user.png"
if not picture_url:
picture_url = "/user.png"
if not picture_url:
else:
picture_url = "/user.png"
username_claim = auth_manager_config.OAUTH_USERNAME_CLAIM

View file

@ -68,23 +68,23 @@ def replace_imports(content):
return content
def load_tools_module_by_id(toolkit_id, content=None):
def load_tools_module_by_id(tool_id, content=None):
if content is None:
tool = Tools.get_tool_by_id(toolkit_id)
tool = Tools.get_tool_by_id(tool_id)
if not tool:
raise Exception(f"Toolkit not found: {toolkit_id}")
raise Exception(f"Toolkit not found: {tool_id}")
content = tool.content
content = replace_imports(content)
Tools.update_tool_by_id(toolkit_id, {"content": content})
Tools.update_tool_by_id(tool_id, {"content": content})
else:
frontmatter = extract_frontmatter(content)
# Install required packages found within the frontmatter
install_frontmatter_requirements(frontmatter.get("requirements", ""))
module_name = f"tool_{toolkit_id}"
module_name = f"tool_{tool_id}"
module = types.ModuleType(module_name)
sys.modules[module_name] = module
@ -108,7 +108,7 @@ def load_tools_module_by_id(toolkit_id, content=None):
else:
raise Exception("No Tools class found in the module")
except Exception as e:
log.error(f"Error loading module: {toolkit_id}: {e}")
log.error(f"Error loading module: {tool_id}: {e}")
del sys.modules[module_name] # Clean up
raise e
finally:

View file

@ -2,9 +2,10 @@ import inspect
import logging
import re
import inspect
import uuid
import aiohttp
import asyncio
from typing import Any, Awaitable, Callable, get_type_hints
from typing import Any, Awaitable, Callable, get_type_hints, Dict, List, Union, Optional
from functools import update_wrapper, partial
@ -17,96 +18,162 @@ from open_webui.models.tools import Tools
from open_webui.models.users import UserModel
from open_webui.utils.plugin import load_tools_module_by_id
import copy
log = logging.getLogger(__name__)
def apply_extra_params_to_tool_function(
def get_async_tool_function_and_apply_extra_params(
function: Callable, extra_params: dict
) -> Callable[..., Awaitable]:
sig = inspect.signature(function)
extra_params = {k: v for k, v in extra_params.items() if k in sig.parameters}
partial_func = partial(function, **extra_params)
if inspect.iscoroutinefunction(function):
update_wrapper(partial_func, function)
return partial_func
else:
# Make it a coroutine function
async def new_function(*args, **kwargs):
return partial_func(*args, **kwargs)
async def new_function(*args, **kwargs):
return partial_func(*args, **kwargs)
update_wrapper(new_function, function)
return new_function
update_wrapper(new_function, function)
return new_function
# Mutation on extra_params
def get_tools(
request: Request, tool_ids: list[str], user: UserModel, extra_params: dict
) -> dict[str, dict]:
tools_dict = {}
for tool_id in tool_ids:
tools = Tools.get_tool_by_id(tool_id)
if tools is None:
continue
tool = Tools.get_tool_by_id(tool_id)
if tool is None:
if tool_id.startswith("server:"):
server_idx = int(tool_id.split(":")[1])
tool_server_connection = (
request.app.state.config.TOOL_SERVER_CONNECTIONS[server_idx]
)
tool_server_data = request.app.state.TOOL_SERVERS[server_idx]
specs = tool_server_data.get("specs", [])
module = request.app.state.TOOLS.get(tool_id, None)
if module is None:
module, _ = load_tools_module_by_id(tool_id)
request.app.state.TOOLS[tool_id] = module
for spec in specs:
function_name = spec["name"]
extra_params["__id__"] = tool_id
if hasattr(module, "valves") and hasattr(module, "Valves"):
valves = Tools.get_tool_valves_by_id(tool_id) or {}
module.valves = module.Valves(**valves)
auth_type = tool_server_connection.get("auth_type", "bearer")
token = None
if hasattr(module, "UserValves"):
extra_params["__user__"]["valves"] = module.UserValves( # type: ignore
**Tools.get_user_valves_by_id_and_user_id(tool_id, user.id)
)
if auth_type == "bearer":
token = tool_server_connection.get("key", "")
elif auth_type == "session":
token = request.state.token.credentials
for spec in tools.specs:
# TODO: Fix hack for OpenAI API
# Some times breaks OpenAI but others don't. Leaving the comment
for val in spec.get("parameters", {}).get("properties", {}).values():
if val["type"] == "str":
val["type"] = "string"
def make_tool_function(function_name, token, tool_server_data):
async def tool_function(**kwargs):
print(
f"Executing tool function {function_name} with params: {kwargs}"
)
return await execute_tool_server(
token=token,
url=tool_server_data["url"],
name=function_name,
params=kwargs,
server_data=tool_server_data,
)
# Remove internal parameters
spec["parameters"]["properties"] = {
key: val
for key, val in spec["parameters"]["properties"].items()
if not key.startswith("__")
}
return tool_function
function_name = spec["name"]
tool_function = make_tool_function(
function_name, token, tool_server_data
)
# convert to function that takes only model params and inserts custom params
original_func = getattr(module, function_name)
callable = apply_extra_params_to_tool_function(original_func, extra_params)
callable = get_async_tool_function_and_apply_extra_params(
tool_function,
{},
)
if callable.__doc__ and callable.__doc__.strip() != "":
s = re.split(":(param|return)", callable.__doc__, 1)
spec["description"] = s[0]
tool_dict = {
"tool_id": tool_id,
"callable": callable,
"spec": spec,
}
# TODO: if collision, prepend toolkit name
if function_name in tools_dict:
log.warning(
f"Tool {function_name} already exists in another tools!"
)
log.warning(f"Discarding {tool_id}.{function_name}")
else:
tools_dict[function_name] = tool_dict
else:
spec["description"] = function_name
continue
else:
module = request.app.state.TOOLS.get(tool_id, None)
if module is None:
module, _ = load_tools_module_by_id(tool_id)
request.app.state.TOOLS[tool_id] = module
# TODO: This needs to be a pydantic model
tool_dict = {
"spec": spec,
"callable": callable,
"toolkit_id": tool_id,
"pydantic_model": function_to_pydantic_model(callable),
# Misc info
"file_handler": hasattr(module, "file_handler") and module.file_handler,
"citation": hasattr(module, "citation") and module.citation,
}
extra_params["__id__"] = tool_id
# TODO: if collision, prepend toolkit name
if function_name in tools_dict:
log.warning(f"Tool {function_name} already exists in another tools!")
log.warning(f"Collision between {tools} and {tool_id}.")
log.warning(f"Discarding {tools}.{function_name}")
else:
tools_dict[function_name] = tool_dict
# Set valves for the tool
if hasattr(module, "valves") and hasattr(module, "Valves"):
valves = Tools.get_tool_valves_by_id(tool_id) or {}
module.valves = module.Valves(**valves)
if hasattr(module, "UserValves"):
extra_params["__user__"]["valves"] = module.UserValves( # type: ignore
**Tools.get_user_valves_by_id_and_user_id(tool_id, user.id)
)
for spec in tool.specs:
# TODO: Fix hack for OpenAI API
# Some times breaks OpenAI but others don't. Leaving the comment
for val in spec.get("parameters", {}).get("properties", {}).values():
if val["type"] == "str":
val["type"] = "string"
# Remove internal reserved parameters (e.g. __id__, __user__)
spec["parameters"]["properties"] = {
key: val
for key, val in spec["parameters"]["properties"].items()
if not key.startswith("__")
}
# convert to function that takes only model params and inserts custom params
function_name = spec["name"]
tool_function = getattr(module, function_name)
callable = get_async_tool_function_and_apply_extra_params(
tool_function, extra_params
)
# TODO: Support Pydantic models as parameters
if callable.__doc__ and callable.__doc__.strip() != "":
s = re.split(":(param|return)", callable.__doc__, 1)
spec["description"] = s[0]
else:
spec["description"] = function_name
tool_dict = {
"tool_id": tool_id,
"callable": callable,
"spec": spec,
# Misc info
"metadata": {
"file_handler": hasattr(module, "file_handler")
and module.file_handler,
"citation": hasattr(module, "citation") and module.citation,
},
}
# TODO: if collision, prepend toolkit name
if function_name in tools_dict:
log.warning(
f"Tool {function_name} already exists in another tools!"
)
log.warning(f"Discarding {tool_id}.{function_name}")
else:
tools_dict[function_name] = tool_dict
return tools_dict
@ -214,6 +281,271 @@ def get_callable_attributes(tool: object) -> list[Callable]:
def get_tools_specs(tool_class: object) -> list[dict]:
function_list = get_callable_attributes(tool_class)
models = map(function_to_pydantic_model, function_list)
return [convert_to_openai_function(tool) for tool in models]
function_model_list = map(
function_to_pydantic_model, get_callable_attributes(tool_class)
)
return [
convert_to_openai_function(function_model)
for function_model in function_model_list
]
def resolve_schema(schema, components):
"""
Recursively resolves a JSON schema using OpenAPI components.
"""
if not schema:
return {}
if "$ref" in schema:
ref_path = schema["$ref"]
ref_parts = ref_path.strip("#/").split("/")
resolved = components
for part in ref_parts[1:]: # Skip the initial 'components'
resolved = resolved.get(part, {})
return resolve_schema(resolved, components)
resolved_schema = copy.deepcopy(schema)
# Recursively resolve inner schemas
if "properties" in resolved_schema:
for prop, prop_schema in resolved_schema["properties"].items():
resolved_schema["properties"][prop] = resolve_schema(
prop_schema, components
)
if "items" in resolved_schema:
resolved_schema["items"] = resolve_schema(resolved_schema["items"], components)
return resolved_schema
def convert_openapi_to_tool_payload(openapi_spec):
"""
Converts an OpenAPI specification into a custom tool payload structure.
Args:
openapi_spec (dict): The OpenAPI specification as a Python dict.
Returns:
list: A list of tool payloads.
"""
tool_payload = []
for path, methods in openapi_spec.get("paths", {}).items():
for method, operation in methods.items():
tool = {
"type": "function",
"name": operation.get("operationId"),
"description": operation.get("summary", "No description available."),
"parameters": {"type": "object", "properties": {}, "required": []},
}
# Extract path and query parameters
for param in operation.get("parameters", []):
param_name = param["name"]
param_schema = param.get("schema", {})
tool["parameters"]["properties"][param_name] = {
"type": param_schema.get("type"),
"description": param_schema.get("description", ""),
}
if param.get("required"):
tool["parameters"]["required"].append(param_name)
# Extract and resolve requestBody if available
request_body = operation.get("requestBody")
if request_body:
content = request_body.get("content", {})
json_schema = content.get("application/json", {}).get("schema")
if json_schema:
resolved_schema = resolve_schema(
json_schema, openapi_spec.get("components", {})
)
if resolved_schema.get("properties"):
tool["parameters"]["properties"].update(
resolved_schema["properties"]
)
if "required" in resolved_schema:
tool["parameters"]["required"] = list(
set(
tool["parameters"]["required"]
+ resolved_schema["required"]
)
)
elif resolved_schema.get("type") == "array":
tool["parameters"] = resolved_schema # special case for array
tool_payload.append(tool)
return tool_payload
async def get_tool_server_data(token: str, url: str) -> Dict[str, Any]:
headers = {
"Accept": "application/json",
"Content-Type": "application/json",
}
if token:
headers["Authorization"] = f"Bearer {token}"
error = None
try:
async with aiohttp.ClientSession() as session:
async with session.get(url, headers=headers) as response:
if response.status != 200:
error_body = await response.json()
raise Exception(error_body)
res = await response.json()
except Exception as err:
print("Error:", err)
if isinstance(err, dict) and "detail" in err:
error = err["detail"]
else:
error = str(err)
raise Exception(error)
data = {
"openapi": res,
"info": res.get("info", {}),
"specs": convert_openapi_to_tool_payload(res),
}
print("Fetched data:", data)
return data
async def get_tool_servers_data(
servers: List[Dict[str, Any]], session_token: Optional[str] = None
) -> List[Dict[str, Any]]:
# Prepare list of enabled servers along with their original index
server_entries = []
for idx, server in enumerate(servers):
if server.get("config", {}).get("enable"):
url_path = server.get("path", "openapi.json")
full_url = f"{server.get('url')}/{url_path}"
auth_type = server.get("auth_type", "bearer")
token = None
if auth_type == "bearer":
token = server.get("key", "")
elif auth_type == "session":
token = session_token
server_entries.append((idx, server, full_url, token))
# Create async tasks to fetch data
tasks = [get_tool_server_data(token, url) for (_, _, url, token) in server_entries]
# Execute tasks concurrently
responses = await asyncio.gather(*tasks, return_exceptions=True)
# Build final results with index and server metadata
results = []
for (idx, server, url, _), response in zip(server_entries, responses):
if isinstance(response, Exception):
print(f"Failed to connect to {url} OpenAPI tool server")
continue
results.append(
{
"idx": idx,
"url": server.get("url"),
"openapi": response.get("openapi"),
"info": response.get("info"),
"specs": response.get("specs"),
}
)
return results
async def execute_tool_server(
token: str, url: str, name: str, params: Dict[str, Any], server_data: Dict[str, Any]
) -> Any:
error = None
try:
openapi = server_data.get("openapi", {})
paths = openapi.get("paths", {})
matching_route = None
for route_path, methods in paths.items():
for http_method, operation in methods.items():
if isinstance(operation, dict) and operation.get("operationId") == name:
matching_route = (route_path, methods)
break
if matching_route:
break
if not matching_route:
raise Exception(f"No matching route found for operationId: {name}")
route_path, methods = matching_route
method_entry = None
for http_method, operation in methods.items():
if operation.get("operationId") == name:
method_entry = (http_method.lower(), operation)
break
if not method_entry:
raise Exception(f"No matching method found for operationId: {name}")
http_method, operation = method_entry
path_params = {}
query_params = {}
body_params = {}
for param in operation.get("parameters", []):
param_name = param["name"]
param_in = param["in"]
if param_name in params:
if param_in == "path":
path_params[param_name] = params[param_name]
elif param_in == "query":
query_params[param_name] = params[param_name]
final_url = f"{url}{route_path}"
for key, value in path_params.items():
final_url = final_url.replace(f"{{{key}}}", str(value))
if query_params:
query_string = "&".join(f"{k}={v}" for k, v in query_params.items())
final_url = f"{final_url}?{query_string}"
if operation.get("requestBody", {}).get("content"):
if params:
body_params = params
else:
raise Exception(
f"Request body expected for operation '{name}' but none found."
)
headers = {"Content-Type": "application/json"}
if token:
headers["Authorization"] = f"Bearer {token}"
async with aiohttp.ClientSession() 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
) 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:
if response.status >= 400:
text = await response.text()
raise Exception(f"HTTP error {response.status}: {text}")
return await response.json()
except Exception as err:
error = str(err)
print("API Request Error:", error)
return {"error": error}

View file

@ -1,7 +1,7 @@
fastapi==0.115.7
uvicorn[standard]==0.34.0
pydantic==2.10.6
python-multipart==0.0.18
python-multipart==0.0.20
python-socketio==5.11.3
python-jose==3.4.0
@ -55,7 +55,7 @@ elasticsearch==8.17.1
transformers
sentence-transformers==3.3.1
colbert-ai==0.2.21
einops==0.8.0
einops==0.8.1
ftfy==6.2.3
@ -67,7 +67,7 @@ python-pptx==1.0.0
unstructured==0.16.17
nltk==3.9.1
Markdown==3.7
pypandoc==1.13
pypandoc==1.15
pandas==2.2.3
openpyxl==3.1.5
pyxlsb==1.0.10
@ -83,6 +83,8 @@ opencv-python-headless==4.11.0.86
rapidocr-onnxruntime==1.3.24
rank-bm25==0.2.2
onnxruntime==1.20.1
faster-whisper==1.1.1
PyJWT[crypto]==2.10.1

4
package-lock.json generated
View file

@ -1,12 +1,12 @@
{
"name": "open-webui",
"version": "0.6.0",
"version": "0.6.1",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "open-webui",
"version": "0.6.0",
"version": "0.6.1",
"dependencies": {
"@azure/msal-browser": "^4.5.0",
"@codemirror/lang-javascript": "^6.2.2",

View file

@ -1,6 +1,6 @@
{
"name": "open-webui",
"version": "0.6.0",
"version": "0.6.1",
"private": true,
"scripts": {
"dev": "npm run pyodide:fetch && vite dev --host",

View file

@ -45,7 +45,7 @@ dependencies = [
"openai",
"anthropic",
"google-generativeai==0.7.2",
"google-generativeai==0.8.4",
"tiktoken",
"langchain==0.3.19",
@ -62,7 +62,7 @@ dependencies = [
"transformers",
"sentence-transformers==3.3.1",
"colbert-ai==0.2.21",
"einops==0.8.0",
"einops==0.8.1",
"ftfy==6.2.3",
"pypdf==4.3.1",
@ -73,7 +73,7 @@ dependencies = [
"unstructured==0.16.17",
"nltk==3.9.1",
"Markdown==3.7",
"pypandoc==1.13",
"pypandoc==1.15",
"pandas==2.2.3",
"openpyxl==3.1.5",
"pyxlsb==1.0.10",
@ -89,6 +89,8 @@ dependencies = [
"rapidocr-onnxruntime==1.3.24",
"rank-bm25==0.2.2",
"onnxruntime==1.20.1",
"faster-whisper==1.1.1",
"PyJWT[crypto]==2.10.1",

View file

@ -115,6 +115,93 @@ export const setDirectConnectionsConfig = async (token: string, config: object)
return res;
};
export const getToolServerConnections = async (token: string) => {
let error = null;
const res = await fetch(`${WEBUI_API_BASE_URL}/configs/tool_servers`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${token}`
}
})
.then(async (res) => {
if (!res.ok) throw await res.json();
return res.json();
})
.catch((err) => {
console.log(err);
error = err.detail;
return null;
});
if (error) {
throw error;
}
return res;
};
export const setToolServerConnections = async (token: string, connections: object) => {
let error = null;
const res = await fetch(`${WEBUI_API_BASE_URL}/configs/tool_servers`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${token}`
},
body: JSON.stringify({
...connections
})
})
.then(async (res) => {
if (!res.ok) throw await res.json();
return res.json();
})
.catch((err) => {
console.log(err);
error = err.detail;
return null;
});
if (error) {
throw error;
}
return res;
};
export const verifyToolServerConnection = async (token: string, connection: object) => {
let error = null;
const res = await fetch(`${WEBUI_API_BASE_URL}/configs/tool_servers/verify`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${token}`
},
body: JSON.stringify({
...connection
})
})
.then(async (res) => {
if (!res.ok) throw await res.json();
return res.json();
})
.catch((err) => {
console.log(err);
error = err.detail;
return null;
});
if (error) {
throw error;
}
return res;
};
export const getCodeExecutionConfig = async (token: string) => {
let error = null;

View file

@ -262,7 +262,7 @@ export const stopTask = async (token: string, id: string) => {
export const getToolServerData = async (token: string, url: string) => {
let error = null;
const res = await fetch(`${url}/openapi.json`, {
const res = await fetch(`${url}`, {
method: 'GET',
headers: {
Accept: 'application/json',
@ -304,10 +304,13 @@ export const getToolServersData = async (i18n, servers: object[]) => {
servers
.filter((server) => server?.config?.enable)
.map(async (server) => {
const data = await getToolServerData(server?.key, server?.url).catch((err) => {
const data = await getToolServerData(
server?.key,
server?.url + '/' + (server?.path ?? 'openapi.json')
).catch((err) => {
toast.error(
i18n.t(`Failed to connect to {{URL}} OpenAPI tool server`, {
URL: server?.url
URL: server?.url + '/' + (server?.path ?? 'openapi.json')
})
);
return null;

View file

@ -15,6 +15,9 @@
import Tooltip from '$lib/components/common/Tooltip.svelte';
import Switch from '$lib/components/common/Switch.svelte';
import Tags from './common/Tags.svelte';
import { getToolServerData } from '$lib/apis';
import { verifyToolServerConnection } from '$lib/apis/configs';
import AccessControl from './workspace/common/AccessControl.svelte';
export let onSubmit: Function = () => {};
export let onDelete: Function = () => {};
@ -22,14 +25,66 @@
export let show = false;
export let edit = false;
export let direct = false;
export let connection = null;
let url = '';
let path = 'openapi.json';
let auth_type = 'bearer';
let key = '';
let accessControl = null;
let enable = true;
let loading = false;
const verifyHandler = async () => {
if (url === '') {
toast.error($i18n.t('Please enter a valid URL'));
return;
}
if (path === '') {
toast.error($i18n.t('Please enter a valid path'));
return;
}
if (direct) {
const res = await getToolServerData(
auth_type === 'bearer' ? key : localStorage.token,
`${url}/${path}`
).catch((err) => {
toast.error($i18n.t('Connection failed'));
});
if (res) {
toast.success($i18n.t('Connection successful'));
console.debug('Connection successful', res);
}
} else {
const res = await verifyToolServerConnection(localStorage.token, {
url,
path,
auth_type,
key,
config: {
enable: enable,
access_control: accessControl
}
}).catch((err) => {
toast.error($i18n.t('Connection failed'));
});
if (res) {
toast.success($i18n.t('Connection successful'));
console.debug('Connection successful', res);
}
}
};
const submitHandler = async () => {
loading = true;
@ -38,9 +93,12 @@
const connection = {
url,
path,
auth_type,
key,
config: {
enable: enable
enable: enable,
access_control: accessControl
}
};
@ -50,16 +108,24 @@
show = false;
url = '';
path = 'openapi.json';
key = '';
auth_type = 'bearer';
enable = true;
accessControl = null;
};
const init = () => {
if (connection) {
url = connection.url;
key = connection.key;
path = connection?.path ?? 'openapi.json';
auth_type = connection?.auth_type ?? 'bearer';
key = connection?.key ?? '';
enable = connection.config?.enable ?? true;
accessControl = connection.config?.access_control ?? null;
}
};
@ -113,47 +179,113 @@
<div class="px-1">
<div class="flex gap-2">
<div class="flex flex-col w-full">
<div class=" mb-0.5 text-xs text-gray-500">{$i18n.t('URL')}</div>
<div class="flex justify-between mb-0.5">
<div class=" text-xs text-gray-500">{$i18n.t('URL')}</div>
</div>
<div class="flex-1">
<div class="flex flex-1 items-center">
<input
class="w-full text-sm bg-transparent placeholder:text-gray-300 dark:placeholder:text-gray-700 outline-hidden"
class="w-full flex-1 text-sm bg-transparent placeholder:text-gray-300 dark:placeholder:text-gray-700 outline-hidden"
type="text"
bind:value={url}
placeholder={$i18n.t('API Base URL')}
autocomplete="off"
required
/>
</div>
</div>
<div class="flex flex-col shrink-0 self-end">
<Tooltip content={enable ? $i18n.t('Enabled') : $i18n.t('Disabled')}>
<Switch bind:state={enable} />
</Tooltip>
<Tooltip
content={$i18n.t('Verify Connection')}
className="shrink-0 flex items-center mr-1"
>
<button
class="self-center p-1 bg-transparent hover:bg-gray-100 dark:bg-gray-900 dark:hover:bg-gray-850 rounded-lg transition"
on:click={() => {
verifyHandler();
}}
type="button"
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
fill="currentColor"
class="w-4 h-4"
>
<path
fill-rule="evenodd"
d="M15.312 11.424a5.5 5.5 0 01-9.201 2.466l-.312-.311h2.433a.75.75 0 000-1.5H3.989a.75.75 0 00-.75.75v4.242a.75.75 0 001.5 0v-2.43l.31.31a7 7 0 0011.712-3.138.75.75 0 00-1.449-.39zm1.23-3.723a.75.75 0 00.219-.53V2.929a.75.75 0 00-1.5 0V5.36l-.31-.31A7 7 0 003.239 8.188a.75.75 0 101.448.389A5.5 5.5 0 0113.89 6.11l.311.31h-2.432a.75.75 0 000 1.5h4.243a.75.75 0 00.53-.219z"
clip-rule="evenodd"
/>
</svg>
</button>
</Tooltip>
<Tooltip content={enable ? $i18n.t('Enabled') : $i18n.t('Disabled')}>
<Switch bind:state={enable} />
</Tooltip>
</div>
<div class="flex-1 flex items-center">
<div class="text-sm">/</div>
<input
class="w-full text-sm bg-transparent placeholder:text-gray-300 dark:placeholder:text-gray-700 outline-hidden"
type="text"
bind:value={path}
placeholder={$i18n.t('openapi.json Path')}
autocomplete="off"
required
/>
</div>
</div>
</div>
<div class="text-xs text-gray-500 mt-1">
{$i18n.t(`WebUI will make requests to "{{url}}/openapi.json"`, {
url: url
{$i18n.t(`WebUI will make requests to "{{url}}"`, {
url: `${url}/${path}`
})}
</div>
<div class="flex gap-2 mt-2">
<div class="flex flex-col w-full">
<div class=" mb-0.5 text-xs text-gray-500">{$i18n.t('Key')}</div>
<div class=" text-xs text-gray-500">{$i18n.t('Auth')}</div>
<div class="flex-1">
<SensitiveInput
className="w-full text-sm bg-transparent placeholder:text-gray-300 dark:placeholder:text-gray-700 outline-hidden"
bind:value={key}
placeholder={$i18n.t('API Key')}
required={false}
/>
<div class="flex gap-2">
<div class="flex-shrink-0 self-start">
<select
class="w-full text-sm bg-transparent dark:bg-gray-900 placeholder:text-gray-300 dark:placeholder:text-gray-700 outline-hidden pr-5"
bind:value={auth_type}
>
<option value="bearer">Bearer</option>
<option value="session">Session</option>
</select>
</div>
<div class="flex flex-1 items-center">
{#if auth_type === 'bearer'}
<SensitiveInput
className="w-full text-sm bg-transparent placeholder:text-gray-300 dark:placeholder:text-gray-700 outline-hidden"
bind:value={key}
placeholder={$i18n.t('API Key')}
required={false}
/>
{:else if auth_type === 'session'}
<div class="text-xs text-gray-500 self-center translate-y-[1px]">
{$i18n.t('Forwards system user session credentials to authenticate')}
</div>
{/if}
</div>
</div>
</div>
</div>
{#if !direct}
<hr class=" border-gray-100 dark:border-gray-700/10 my-2.5 w-full" />
<div class="my-2 -mx-2">
<div class="px-3 py-2 bg-gray-50 dark:bg-gray-950 rounded-lg">
<AccessControl bind:accessControl />
</div>
</div>
{/if}
</div>
<div class="flex justify-end pt-3 text-sm font-medium gap-1.5">

View file

@ -115,7 +115,7 @@
<span class="text-lg font-medium text-gray-500 dark:text-gray-300">{feedbacks.length}</span>
</div>
<div>
{#if feedbacks.length > 0}
<div>
<Tooltip content={$i18n.t('Export')}>
<button
@ -128,7 +128,7 @@
</button>
</Tooltip>
</div>
</div>
{/if}
</div>
<div

View file

@ -430,39 +430,41 @@
</div>
</button>
<button
class="flex text-xs items-center space-x-1 px-3 py-1.5 rounded-xl bg-gray-50 hover:bg-gray-100 dark:bg-gray-800 dark:hover:bg-gray-700 dark:text-gray-200 transition"
on:click={async () => {
const _functions = await exportFunctions(localStorage.token).catch((error) => {
toast.error(`${error}`);
return null;
});
if (_functions) {
let blob = new Blob([JSON.stringify(_functions)], {
type: 'application/json'
{#if $functions.length}
<button
class="flex text-xs items-center space-x-1 px-3 py-1.5 rounded-xl bg-gray-50 hover:bg-gray-100 dark:bg-gray-800 dark:hover:bg-gray-700 dark:text-gray-200 transition"
on:click={async () => {
const _functions = await exportFunctions(localStorage.token).catch((error) => {
toast.error(`${error}`);
return null;
});
saveAs(blob, `functions-export-${Date.now()}.json`);
}
}}
>
<div class=" self-center mr-2 font-medium line-clamp-1">{$i18n.t('Export Functions')}</div>
<div class=" self-center">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 16 16"
fill="currentColor"
class="w-4 h-4"
>
<path
fill-rule="evenodd"
d="M4 2a1.5 1.5 0 0 0-1.5 1.5v9A1.5 1.5 0 0 0 4 14h8a1.5 1.5 0 0 0 1.5-1.5V6.621a1.5 1.5 0 0 0-.44-1.06L9.94 2.439A1.5 1.5 0 0 0 8.878 2H4Zm4 3.5a.75.75 0 0 1 .75.75v2.69l.72-.72a.75.75 0 1 1 1.06 1.06l-2 2a.75.75 0 0 1-1.06 0l-2-2a.75.75 0 0 1 1.06-1.06l.72.72V6.25A.75.75 0 0 1 8 5.5Z"
clip-rule="evenodd"
/>
</svg>
</div>
</button>
if (_functions) {
let blob = new Blob([JSON.stringify(_functions)], {
type: 'application/json'
});
saveAs(blob, `functions-export-${Date.now()}.json`);
}
}}
>
<div class=" self-center mr-2 font-medium line-clamp-1">{$i18n.t('Export Functions')}</div>
<div class=" self-center">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 16 16"
fill="currentColor"
class="w-4 h-4"
>
<path
fill-rule="evenodd"
d="M4 2a1.5 1.5 0 0 0-1.5 1.5v9A1.5 1.5 0 0 0 4 14h8a1.5 1.5 0 0 0 1.5-1.5V6.621a1.5 1.5 0 0 0-.44-1.06L9.94 2.439A1.5 1.5 0 0 0 8.878 2H4Zm4 3.5a.75.75 0 0 1 .75.75v2.69l.72-.72a.75.75 0 1 1 1.06 1.06l-2 2a.75.75 0 0 1-1.06 0l-2-2a.75.75 0 0 1 1.06-1.06l.72.72V6.25A.75.75 0 0 1 8 5.5Z"
clip-rule="evenodd"
/>
</svg>
</div>
</button>
{/if}
</div>
</div>

View file

@ -20,6 +20,7 @@
import DocumentChartBar from '../icons/DocumentChartBar.svelte';
import Evaluations from './Settings/Evaluations.svelte';
import CodeExecution from './Settings/CodeExecution.svelte';
import Tools from './Settings/Tools.svelte';
const i18n = getContext('i18n');
@ -135,6 +136,32 @@
<div class=" self-center">{$i18n.t('Evaluations')}</div>
</button>
<button
class="px-0.5 py-1 min-w-fit rounded-lg flex-1 md:flex-none flex text-left transition {selectedTab ===
'tools'
? ''
: ' text-gray-300 dark:text-gray-600 hover:text-gray-700 dark:hover:text-white'}"
on:click={() => {
selectedTab = 'tools';
}}
>
<div class=" self-center mr-2">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="currentColor"
class="size-4"
>
<path
fill-rule="evenodd"
d="M12 6.75a5.25 5.25 0 0 1 6.775-5.025.75.75 0 0 1 .313 1.248l-3.32 3.319c.063.475.276.934.641 1.299.365.365.824.578 1.3.64l3.318-3.319a.75.75 0 0 1 1.248.313 5.25 5.25 0 0 1-5.472 6.756c-1.018-.086-1.87.1-2.309.634L7.344 21.3A3.298 3.298 0 1 1 2.7 16.657l8.684-7.151c.533-.44.72-1.291.634-2.309A5.342 5.342 0 0 1 12 6.75ZM4.117 19.125a.75.75 0 0 1 .75-.75h.008a.75.75 0 0 1 .75.75v.008a.75.75 0 0 1-.75.75h-.008a.75.75 0 0 1-.75-.75v-.008Z"
clip-rule="evenodd"
/>
</svg>
</div>
<div class=" self-center">{$i18n.t('Tools')}</div>
</button>
<button
class="px-0.5 py-1 min-w-fit rounded-lg flex-1 md:flex-none flex text-left transition {selectedTab ===
'documents'
@ -373,6 +400,8 @@
<Models />
{:else if selectedTab === 'evaluations'}
<Evaluations />
{:else if selectedTab === 'tools'}
<Tools />
{:else if selectedTab === 'documents'}
<Documents
on:save={async () => {

View file

@ -136,7 +136,7 @@
};
onMount(async () => {
if ($user.role === 'admin') {
if ($user?.role === 'admin') {
let ollamaConfig = {};
let openaiConfig = {};

View file

@ -54,6 +54,8 @@
let documentIntelligenceEndpoint = '';
let documentIntelligenceKey = '';
let showDocumentIntelligenceConfig = false;
let mistralApiKey = '';
let showMistralOcrConfig = false;
let textSplitter = '';
let chunkSize = 0;
@ -189,6 +191,10 @@
toast.error($i18n.t('Document Intelligence endpoint and key required.'));
return;
}
if (contentExtractionEngine === 'mistral_ocr' && mistralApiKey === '') {
toast.error($i18n.t('Mistral OCR API Key required.'));
return;
}
if (!BYPASS_EMBEDDING_AND_RETRIEVAL) {
await embeddingModelUpdateHandler();
@ -220,6 +226,9 @@
document_intelligence_config: {
key: documentIntelligenceKey,
endpoint: documentIntelligenceEndpoint
},
mistral_ocr_config: {
api_key: mistralApiKey
}
}
});
@ -284,6 +293,8 @@
documentIntelligenceEndpoint = res.content_extraction.document_intelligence_config.endpoint;
documentIntelligenceKey = res.content_extraction.document_intelligence_config.key;
showDocumentIntelligenceConfig = contentExtractionEngine === 'document_intelligence';
mistralApiKey = res.content_extraction.mistral_ocr_config.api_key;
showMistralOcrConfig = contentExtractionEngine === 'mistral_ocr';
fileMaxSize = res?.file.max_size ?? '';
fileMaxCount = res?.file.max_count ?? '';
@ -335,21 +346,21 @@
<hr class=" border-gray-100 dark:border-gray-850 my-2" />
<div class=" mb-2.5 flex flex-col w-full justify-between">
<div class="mb-2.5 flex flex-col w-full justify-between">
<div class="flex w-full justify-between">
<div class=" self-center text-xs font-medium">
<div class="self-center text-xs font-medium">
{$i18n.t('Content Extraction Engine')}
</div>
<div class="">
<select
class="dark:bg-gray-900 w-fit pr-8 rounded-sm px-2 text-xs bg-transparent outline-hidden text-right"
bind:value={contentExtractionEngine}
>
<option value="">{$i18n.t('Default')} </option>
<option value="">{$i18n.t('Default')}</option>
<option value="tika">{$i18n.t('Tika')}</option>
<option value="docling">{$i18n.t('Docling')}</option>
<option value="document_intelligence">{$i18n.t('Document Intelligence')}</option>
<option value="mistral_ocr">{$i18n.t('Mistral OCR')}</option>
</select>
</div>
</div>
@ -378,12 +389,18 @@
placeholder={$i18n.t('Enter Document Intelligence Endpoint')}
bind:value={documentIntelligenceEndpoint}
/>
<SensitiveInput
placeholder={$i18n.t('Enter Document Intelligence Key')}
bind:value={documentIntelligenceKey}
/>
</div>
{:else if contentExtractionEngine === 'mistral_ocr'}
<div class="my-0.5 flex gap-2 pr-2">
<SensitiveInput
placeholder={$i18n.t('Enter Mistral API Key')}
bind:value={mistralApiKey}
/>
</div>
{/if}
</div>

View file

@ -77,7 +77,7 @@
};
onMount(async () => {
if ($user.role === 'admin') {
if ($user?.role === 'admin') {
evaluationConfig = await getConfig(localStorage.token).catch((err) => {
toast.error(err);
return null;

View file

@ -176,7 +176,7 @@
};
onMount(async () => {
if ($user.role === 'admin') {
if ($user?.role === 'admin') {
const res = await getConfig(localStorage.token).catch((error) => {
toast.error(`${error}`);
return null;

View file

@ -380,7 +380,7 @@
</div>
</div>
{#if $user.role === 'admin'}
{#if $user?.role === 'admin'}
<div class=" space-y-3">
<div class="flex w-full justify-between mb-2">
<div class=" self-center text-sm font-semibold">

View file

@ -19,7 +19,7 @@
let ollamaConfig = null;
onMount(async () => {
if ($user.role === 'admin') {
if ($user?.role === 'admin') {
await Promise.all([
(async () => {
ollamaConfig = await getOllamaConfig(localStorage.token);

View file

@ -0,0 +1,134 @@
<script lang="ts">
import { toast } from 'svelte-sonner';
import { createEventDispatcher, onMount, getContext, tick } from 'svelte';
import { getModels as _getModels } from '$lib/apis';
const dispatch = createEventDispatcher();
const i18n = getContext('i18n');
import { models, settings, user } from '$lib/stores';
import Switch from '$lib/components/common/Switch.svelte';
import Spinner from '$lib/components/common/Spinner.svelte';
import Tooltip from '$lib/components/common/Tooltip.svelte';
import Plus from '$lib/components/icons/Plus.svelte';
import Connection from '$lib/components/chat/Settings/Tools/Connection.svelte';
import AddServerModal from '$lib/components/AddServerModal.svelte';
import { getToolServerConnections, setToolServerConnections } from '$lib/apis/configs';
export let saveSettings: Function;
let servers = null;
let showConnectionModal = false;
const addConnectionHandler = async (server) => {
servers = [...servers, server];
await updateHandler();
};
const updateHandler = async () => {
const res = await setToolServerConnections(localStorage.token, {
TOOL_SERVER_CONNECTIONS: servers
}).catch((err) => {
toast.error($i18n.t('Failed to save connections'));
return null;
});
if (res) {
toast.success($i18n.t('Connections saved successfully'));
}
};
onMount(async () => {
const res = await getToolServerConnections(localStorage.token);
servers = res.TOOL_SERVER_CONNECTIONS;
});
</script>
<AddServerModal bind:show={showConnectionModal} onSubmit={addConnectionHandler} />
<form
class="flex flex-col h-full justify-between text-sm"
on:submit|preventDefault={() => {
updateHandler();
}}
>
<div class=" overflow-y-scroll scrollbar-hidden h-full">
{#if servers !== null}
<div class="">
<div class="mb-3">
<div class=" mb-2.5 text-base font-medium">{$i18n.t('General')}</div>
<hr class=" border-gray-100 dark:border-gray-850 my-2" />
<div class="mb-2.5 flex flex-col w-full justify-between">
<!-- {$i18n.t(`Failed to connect to {{URL}} OpenAPI tool server`, {
URL: 'server?.url'
})} -->
<div class="flex justify-between items-center mb-0.5">
<div class="font-medium">{$i18n.t('Manage Tool Servers')}</div>
<Tooltip content={$i18n.t(`Add Connection`)}>
<button
class="px-1"
on:click={() => {
showConnectionModal = true;
}}
type="button"
>
<Plus />
</button>
</Tooltip>
</div>
<div class="flex flex-col gap-1.5">
{#each servers as server, idx}
<Connection
bind:connection={server}
onSubmit={() => {
updateHandler();
}}
onDelete={() => {
servers = servers.filter((_, i) => i !== idx);
updateHandler();
}}
/>
{/each}
</div>
<div class="my-1.5">
<div class="text-xs text-gray-500">
{$i18n.t('Connect to your own OpenAPI compatible external tool servers.')}
</div>
</div>
</div>
<!-- <div class="mb-2.5 flex w-full justify-between">
<div class=" text-xs font-medium">{$i18n.t('Arena Models')}</div>
<Tooltip content={$i18n.t(`Message rating should be enabled to use this feature`)}>
<Switch bind:state={evaluationConfig.ENABLE_EVALUATION_ARENA_MODELS} />
</Tooltip>
</div> -->
</div>
</div>
{:else}
<div class="flex h-full justify-center">
<div class="my-auto">
<Spinner className="size-6" />
</div>
</div>
{/if}
</div>
<div class="flex justify-end pt-3 text-sm font-medium">
<button
class="px-3.5 py-1.5 text-sm font-medium bg-black hover:bg-gray-900 text-white dark:bg-white dark:text-black dark:hover:bg-gray-100 transition rounded-full"
type="submit"
>
{$i18n.t('Save')}
</button>
</div>
</form>

View file

@ -64,9 +64,10 @@
delete: true,
edit: true,
temporary: true,
temporary_enforced: true
temporary_enforced: false
},
features: {
direct_tool_servers: false,
web_search: true,
image_generation: true,
code_interpreter: true

View file

@ -38,6 +38,12 @@
prompts: false,
tools: false
},
sharing: {
public_models: false,
public_knowledge: false,
public_prompts: false,
public_tools: false
},
chat: {
controls: true,
file_upload: true,
@ -46,6 +52,7 @@
temporary: true
},
features: {
direct_tool_servers: false,
web_search: true,
image_generation: true,
code_interpreter: true

View file

@ -25,9 +25,10 @@
edit: true,
file_upload: true,
temporary: true,
temporary_enforced: true
temporary_enforced: false
},
features: {
direct_tool_servers: false,
web_search: true,
image_generation: true,
code_interpreter: true
@ -295,6 +296,14 @@
<div>
<div class=" mb-2 text-sm font-medium">{$i18n.t('Features Permissions')}</div>
<div class=" flex w-full justify-between my-2 pr-2">
<div class=" self-center text-xs font-medium">
{$i18n.t('Direct Tool Servers')}
</div>
<Switch bind:state={permissions.features.direct_tool_servers} />
</div>
<div class=" flex w-full justify-between my-2 pr-2">
<div class=" self-center text-xs font-medium">
{$i18n.t('Web Search')}

View file

@ -106,7 +106,7 @@
messages[idx] = data;
}
} else if (type === 'typing' && event.message_id === null) {
if (event.user.id === $user.id) {
if (event.user.id === $user?.id) {
return;
}

View file

@ -132,7 +132,7 @@
if (
(message?.reactions ?? [])
.find((reaction) => reaction.name === name)
?.user_ids?.includes($user.id) ??
?.user_ids?.includes($user?.id) ??
false
) {
messages = messages.map((m) => {
@ -140,7 +140,7 @@
const reaction = m.reactions.find((reaction) => reaction.name === name);
if (reaction) {
reaction.user_ids = reaction.user_ids.filter((id) => id !== $user.id);
reaction.user_ids = reaction.user_ids.filter((id) => id !== $user?.id);
reaction.count = reaction.user_ids.length;
if (reaction.count === 0) {
@ -167,12 +167,12 @@
const reaction = m.reactions.find((reaction) => reaction.name === name);
if (reaction) {
reaction.user_ids.push($user.id);
reaction.user_ids.push($user?.id);
reaction.count = reaction.user_ids.length;
} else {
m.reactions.push({
name: name,
user_ids: [$user.id],
user_ids: [$user?.id],
count: 1
});
}

View file

@ -106,7 +106,7 @@
</Tooltip>
{/if}
{#if message.user_id === $user.id || $user.role === 'admin'}
{#if message.user_id === $user?.id || $user?.role === 'admin'}
<Tooltip content={$i18n.t('Edit')}>
<button
class="hover:bg-gray-100 dark:hover:bg-gray-800 transition rounded-lg p-1"
@ -265,7 +265,7 @@
<Tooltip content={`:${reaction.name}:`}>
<button
class="flex items-center gap-1.5 transition rounded-xl px-2 py-1 cursor-pointer {reaction.user_ids.includes(
$user.id
$user?.id
)
? ' bg-blue-300/10 outline outline-blue-500/50 outline-1'
: 'bg-gray-300/10 dark:bg-gray-500/10 hover:outline hover:outline-gray-700/30 dark:hover:outline-gray-300/30 hover:outline-1'}"

View file

@ -58,7 +58,7 @@
{#if $user !== undefined}
<UserMenu
className="max-w-[200px]"
role={$user.role}
role={$user?.role}
on:show={(e) => {
if (e.detail === 'archived-chat') {
showArchivedChats.set(true);
@ -71,7 +71,7 @@
>
<div class=" self-center">
<img
src={$user.profile_image_url}
src={$user?.profile_image_url}
class="size-6 object-cover rounded-full"
alt="User profile"
draggable="false"

View file

@ -89,7 +89,7 @@
}
}
} else if (type === 'typing' && event.message_id === threadId) {
if (event.user.id === $user.id) {
if (event.user.id === $user?.id) {
return;
}

View file

@ -48,7 +48,8 @@
splitStream,
sleep,
removeDetails,
getPromptVariables
getPromptVariables,
processDetails
} from '$lib/utils';
import { generateChatCompletion } from '$lib/apis/ollama';
@ -1321,6 +1322,10 @@
history.messages[messages.at(-1).id].childrenIds.push(userMessageId);
}
if (autoScroll) {
scrollToBottom();
}
// focus on chat input
const chatInput = document.getElementById('chat-input');
chatInput?.focus();
@ -1498,7 +1503,7 @@
role: 'system',
content: `${promptTemplate(
params?.system ?? $settings?.system ?? '',
$user.name,
$user?.name,
$settings?.userLocation
? await getAndUpdateUserLocation(localStorage.token).catch((err) => {
console.error(err);
@ -1514,7 +1519,7 @@
: undefined,
...createMessagesList(_history, responseMessageId).map((message) => ({
...message,
content: removeDetails(message.content, ['reasoning', 'code_interpreter'])
content: processDetails(message.content)
}))
].filter((message) => message);
@ -1572,23 +1577,23 @@
features: {
image_generation:
$config?.features?.enable_image_generation &&
($user.role === 'admin' || $user?.permissions?.features?.image_generation)
($user?.role === 'admin' || $user?.permissions?.features?.image_generation)
? imageGenerationEnabled
: false,
code_interpreter:
$config?.features?.enable_code_interpreter &&
($user.role === 'admin' || $user?.permissions?.features?.code_interpreter)
($user?.role === 'admin' || $user?.permissions?.features?.code_interpreter)
? codeInterpreterEnabled
: false,
web_search:
$config?.features?.enable_web_search &&
($user.role === 'admin' || $user?.permissions?.features?.web_search)
($user?.role === 'admin' || $user?.permissions?.features?.web_search)
? webSearchEnabled || ($settings?.webSearch ?? false) === 'always'
: false
},
variables: {
...getPromptVariables(
$user.name,
$user?.name,
$settings?.userLocation
? await getAndUpdateUserLocation(localStorage.token).catch((err) => {
console.error(err);

View file

@ -68,12 +68,12 @@
{#if $temporaryChatEnabled}
<Tooltip
content="This chat won't appear in history and your messages will not be saved."
className="w-fit"
placement="top-start"
content={$i18n.t('This chat wont appear in history and your messages will not be saved.')}
className="w-full flex justify-center mb-0.5"
placement="top"
>
<div class="flex items-center gap-2 text-gray-500 font-medium text-lg my-2 w-fit">
<EyeSlash strokeWidth="2.5" className="size-5" /> Temporary Chat
<EyeSlash strokeWidth="2.5" className="size-5" />{$i18n.t('Temporary Chat')}
</div>
</Tooltip>
{/if}
@ -86,7 +86,7 @@
{#if models[selectedModelIdx]?.name}
{models[selectedModelIdx]?.name}
{:else}
{$i18n.t('Hello, {{name}}', { name: $user.name })}
{$i18n.t('Hello, {{name}}', { name: $user?.name })}
{/if}
</div>

View file

@ -67,7 +67,7 @@
</div>
</Collapsible>
{#if $user.role === 'admin' || $user?.permissions.chat?.controls}
{#if $user?.role === 'admin' || $user?.permissions.chat?.controls}
<hr class="my-2 border-gray-50 dark:border-gray-700/10" />
<Collapsible title={$i18n.t('System Prompt')} open={true} buttonClassName="w-full">

View file

@ -21,7 +21,12 @@
TTSWorker
} from '$lib/stores';
import { blobToFile, compressImage, createMessagesList, findWordIndices } from '$lib/utils';
import {
blobToFile,
compressImage,
createMessagesList,
extractCurlyBraceWords
} from '$lib/utils';
import { transcribeAudio } from '$lib/apis/audio';
import { uploadFile } from '$lib/apis/files';
import { generateAutoCompletion } from '$lib/apis';
@ -47,6 +52,7 @@
import CommandLine from '../icons/CommandLine.svelte';
import { KokoroWorker } from '$lib/workers/KokoroWorker';
import ToolServersModal from './ToolServersModal.svelte';
import Wrench from '../icons/Wrench.svelte';
const i18n = getContext('i18n');
@ -85,7 +91,7 @@
webSearchEnabled
});
let showToolServers = false;
let showTools = false;
let loaded = false;
let recording = false;
@ -348,7 +354,7 @@
<FilesOverlay show={dragged} />
<ToolServersModal bind:show={showToolServers} />
<ToolServersModal bind:show={showTools} {selectedToolIds} />
{#if loaded}
<div class="w-full font-primary">
@ -392,38 +398,6 @@
<div
class="px-3 pb-0.5 pt-1.5 text-left w-full flex flex-col absolute bottom-0 left-0 right-0 bg-linear-to-t from-white dark:from-gray-900 z-10"
>
{#if selectedToolIds.length > 0}
<div class="flex items-center justify-between w-full">
<div class="flex items-center gap-2.5 text-sm dark:text-gray-500">
<div class="pl-1">
<span class="relative flex size-2">
<span
class="animate-ping absolute inline-flex h-full w-full rounded-full bg-yellow-400 opacity-75"
/>
<span class="relative inline-flex rounded-full size-2 bg-yellow-500" />
</span>
</div>
<div class=" text-ellipsis line-clamp-1 flex">
{#each selectedToolIds.map((id) => {
return $tools ? $tools.find((t) => t.id === id) : { id: id, name: id };
}) as tool, toolIdx (toolIdx)}
<Tooltip
content={tool?.meta?.description ?? ''}
className=" {toolIdx !== 0 ? 'pl-0.5' : ''} shrink-0"
placement="top"
>
{tool.name}
</Tooltip>
{#if toolIdx !== selectedToolIds.length - 1}
<span>, </span>
{/if}
{/each}
</div>
</div>
</div>
{/if}
{#if atSelectedModel !== undefined}
<div class="flex items-center justify-between w-full">
<div class="pl-[1px] flex items-center gap-2 text-sm dark:text-gray-500">
@ -631,6 +605,7 @@
{#if $settings?.richTextInput ?? true}
<div
class="scrollbar-hidden text-left bg-transparent dark:text-gray-100 outline-hidden w-full pt-3 px-1 resize-none h-fit max-h-80 overflow-auto"
id="chat-input-container"
>
<RichTextInput
bind:this={chatInputElement}
@ -977,7 +952,7 @@
}
if (e.key === 'Tab') {
const words = findWordIndices(prompt);
const words = extractCurlyBraceWords(prompt);
if (words.length > 0) {
const word = words.at(0);
@ -1058,7 +1033,7 @@
</div>
<div class=" flex justify-between mt-1.5 mb-2.5 mx-0.5 max-w-full">
<div class="ml-1 self-end gap-0.5 flex items-center flex-1 max-w-[80%]">
<div class="ml-1 self-end flex items-center flex-1 max-w-[80%] gap-0.5">
<InputMenu
bind:selectedToolIds
{screenCaptureHandler}
@ -1126,7 +1101,30 @@
</button>
</InputMenu>
<div class="flex gap-0.5 items-center overflow-x-auto scrollbar-none flex-1">
<div class="flex gap-[2px] items-center overflow-x-auto scrollbar-none flex-1">
{#if toolServers.length + selectedToolIds.length > 0}
<Tooltip
content={$i18n.t('{{COUNT}} Available Tools', {
COUNT: toolServers.length + selectedToolIds.length
})}
>
<button
class="translate-y-[0.5px] flex gap-1 items-center text-gray-600 dark:text-gray-300 hover:text-gray-700 dark:hover:text-gray-200 rounded-lg p-1 self-center transition"
aria-label="Available Tools"
type="button"
on:click={() => {
showTools = !showTools;
}}
>
<Wrench className="size-4" strokeWidth="1.75" />
<span class="text-sm font-medium text-gray-600 dark:text-gray-300">
{toolServers.length + selectedToolIds.length}
</span>
</button>
</Tooltip>
{/if}
{#if $_user}
{#if $config?.features?.enable_web_search && ($_user.role === 'admin' || $_user?.permissions?.features?.web_search)}
<Tooltip content={$i18n.t('Search the internet')} placement="top">
@ -1140,7 +1138,7 @@
>
<GlobeAlt className="size-5" strokeWidth="1.75" />
<span
class="hidden @xl:block whitespace-nowrap overflow-hidden text-ellipsis translate-y-[0.5px] mr-0.5"
class="hidden @xl:block whitespace-nowrap overflow-hidden text-ellipsis translate-y-[0.5px]"
>{$i18n.t('Web Search')}</span
>
</button>
@ -1159,7 +1157,7 @@
>
<Photo className="size-5" strokeWidth="1.75" />
<span
class="hidden @xl:block whitespace-nowrap overflow-hidden text-ellipsis translate-y-[0.5px] mr-0.5"
class="hidden @xl:block whitespace-nowrap overflow-hidden text-ellipsis translate-y-[0.5px]"
>{$i18n.t('Image')}</span
>
</button>
@ -1178,7 +1176,7 @@
>
<CommandLine className="size-5" strokeWidth="1.75" />
<span
class="hidden @xl:block whitespace-nowrap overflow-hidden text-ellipsis translate-y-[0.5px] mr-0.5"
class="hidden @xl:block whitespace-nowrap overflow-hidden text-ellipsis translate-y-[0.5px]"
>{$i18n.t('Code Interpreter')}</span
>
</button>
@ -1189,47 +1187,6 @@
</div>
<div class="self-end flex space-x-1 mr-1 shrink-0">
{#if toolServers.length > 0}
<Tooltip
content={$i18n.t('{{COUNT}} Available Tool Servers', {
COUNT: toolServers.length
})}
>
<button
class="translate-y-[1.5px] flex gap-1 items-center text-gray-600 dark:text-gray-300 hover:text-gray-700 dark:hover:text-gray-200 rounded-lg px-1.5 py-0.5 mr-0.5 self-center border border-gray-100 dark:border-gray-800 transition"
aria-label="Available Tool Servers"
type="button"
on:click={() => {
showToolServers = !showToolServers;
}}
>
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
class="size-3"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M21.75 6.75a4.5 4.5 0 0 1-4.884 4.484c-1.076-.091-2.264.071-2.95.904l-7.152 8.684a2.548 2.548 0 1 1-3.586-3.586l8.684-7.152c.833-.686.995-1.874.904-2.95a4.5 4.5 0 0 1 6.336-4.486l-3.276 3.276a3.004 3.004 0 0 0 2.25 2.25l3.276-3.276c.256.565.398 1.192.398 1.852Z"
/>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M4.867 19.125h.008v.008h-.008v-.008Z"
/>
</svg>
<span class="text-xs">
{toolServers.length}
</span>
</button>
</Tooltip>
{/if}
{#if !history?.currentId || history.messages[history.currentId]?.done == true}
<Tooltip content={$i18n.t('Record voice')}>
<button

View file

@ -1,7 +1,7 @@
<script lang="ts">
import { prompts, settings, user } from '$lib/stores';
import {
findWordIndices,
extractCurlyBraceWords,
getUserPosition,
getFormattedDate,
getFormattedTime,
@ -86,7 +86,7 @@
if (command.content.includes('{{USER_NAME}}')) {
console.log($user);
const name = $user.name || 'User';
const name = $user?.name || 'User';
text = text.replaceAll('{{USER_NAME}}', name);
}
@ -127,29 +127,49 @@
const lastWord = lastLineWords.pop();
if ($settings?.richTextInput ?? true) {
lastLineWords.push(`${text.replace(/</g, '&lt;').replace(/>/g, '&gt;')}`);
lastLineWords.push(
`${text.replace(/</g, '&lt;').replace(/>/g, '&gt;').replaceAll('\n', '<br/>')}`
);
lines.push(lastLineWords.join(' '));
prompt = lines.join('<br/>');
} else {
lastLineWords.push(text);
lines.push(lastLineWords.join(' '));
prompt = lines.join('\n');
}
prompt = lines.join('\n');
const chatInputContainerElement = document.getElementById('chat-input-container');
const chatInputElement = document.getElementById('chat-input');
await tick();
if (chatInputContainerElement) {
chatInputContainerElement.style.height = '';
chatInputContainerElement.style.height =
Math.min(chatInputContainerElement.scrollHeight, 200) + 'px';
chatInputContainerElement.scrollTop = chatInputContainerElement.scrollHeight;
}
await tick();
if (chatInputElement) {
chatInputElement.focus();
chatInputElement.dispatchEvent(new Event('input'));
const words = extractCurlyBraceWords(prompt);
if (words.length > 0) {
const word = words.at(0);
const fullPrompt = prompt;
prompt = prompt.substring(0, word?.endIndex + 1);
await tick();
chatInputElement.scrollTop = chatInputElement.scrollHeight;
prompt = fullPrompt;
await tick();
chatInputElement.setSelectionRange(word?.startIndex, word.endIndex + 1);
} else {
chatInputElement.scrollTop = chatInputElement.scrollHeight;
}
}
};
</script>

View file

@ -39,7 +39,7 @@
}
let fileUploadEnabled = true;
$: fileUploadEnabled = $user.role === 'admin' || $user?.permissions?.chat?.file_upload;
$: fileUploadEnabled = $user?.role === 'admin' || $user?.permissions?.chat?.file_upload;
const init = async () => {
if ($_tools === null) {

View file

@ -2,7 +2,7 @@
import { toast } from 'svelte-sonner';
import { createEventDispatcher, tick, getContext, onMount, onDestroy } from 'svelte';
import { config, settings } from '$lib/stores';
import { blobToFile, calculateSHA256, findWordIndices } from '$lib/utils';
import { blobToFile, calculateSHA256, extractCurlyBraceWords } from '$lib/utils';
import { transcribeAudio } from '$lib/apis/audio';

View file

@ -14,7 +14,7 @@
import { toast } from 'svelte-sonner';
import { getChatList, updateChatById } from '$lib/apis/chats';
import { copyToClipboard, findWordIndices } from '$lib/utils';
import { copyToClipboard, extractCurlyBraceWords } from '$lib/utils';
import Message from './Messages/Message.svelte';
import Loader from '../common/Loader.svelte';
@ -406,19 +406,6 @@
}
prompt = text;
await tick();
const chatInputContainerElement = document.getElementById('chat-input-container');
if (chatInputContainerElement) {
prompt = p;
chatInputContainerElement.style.height = '';
chatInputContainerElement.style.height =
Math.min(chatInputContainerElement.scrollHeight, 200) + 'px';
chatInputContainerElement.focus();
}
await tick();
}}
/>

View file

@ -332,9 +332,37 @@
}
};
let preprocessedDetailsCache = [];
function preprocessForEditing(content: string): string {
// Replace <details>...</details> with unique ID placeholder
const detailsBlocks = [];
let i = 0;
content = content.replace(/<details[\s\S]*?<\/details>/gi, (match) => {
detailsBlocks.push(match);
return `<details id="__DETAIL_${i++}__"/>`;
});
// Store original blocks in the editedContent or globally (see merging later)
preprocessedDetailsCache = detailsBlocks;
return content;
}
function postprocessAfterEditing(content: string): string {
const restoredContent = content.replace(
/<details id="__DETAIL_(\d+)__"\/>/g,
(_, index) => preprocessedDetailsCache[parseInt(index)] || ''
);
return restoredContent;
}
const editMessageHandler = async () => {
edit = true;
editedContent = message.content;
editedContent = preprocessForEditing(message.content);
await tick();
@ -343,7 +371,8 @@
};
const editMessageConfirmHandler = async () => {
editMessage(message.id, editedContent ? editedContent : '', false);
const messageContent = postprocessAfterEditing(editedContent ? editedContent : '');
editMessage(message.id, messageContent, false);
edit = false;
editedContent = '';
@ -352,7 +381,9 @@
};
const saveAsCopyHandler = async () => {
editMessage(message.id, editedContent ? editedContent : '');
const messageContent = postprocessAfterEditing(editedContent ? editedContent : '');
editMessage(message.id, messageContent);
edit = false;
editedContent = '';
@ -698,7 +729,7 @@
<div>
<button
id="save-new-message-button"
class=" px-4 py-2 bg-gray-50 hover:bg-gray-100 dark:bg-gray-800 dark:hover:bg-gray-700 border dark:border-gray-700 text-gray-700 dark:text-gray-200 transition rounded-3xl"
class=" px-4 py-2 bg-gray-50 hover:bg-gray-100 dark:bg-gray-800 dark:hover:bg-gray-700 border border-gray-100 dark:border-gray-700 text-gray-700 dark:text-gray-200 transition rounded-3xl"
on:click={() => {
saveAsCopyHandler();
}}
@ -920,7 +951,7 @@
{#if message.done}
{#if !readOnly}
{#if $user.role === 'user' ? ($user?.permissions?.chat?.edit ?? true) : true}
{#if $user?.role === 'user' ? ($user?.permissions?.chat?.edit ?? true) : true}
<Tooltip content={$i18n.t('Edit')} placement="bottom">
<button
class="{isLastMessage
@ -1053,7 +1084,7 @@
</button>
</Tooltip>
{#if $config?.features.enable_image_generation && ($user.role === 'admin' || $user?.permissions?.features?.image_generation) && !readOnly}
{#if $config?.features.enable_image_generation && ($user?.role === 'admin' || $user?.permissions?.features?.image_generation) && !readOnly}
<Tooltip content={$i18n.t('Generate Image')} placement="bottom">
<button
class="{isLastMessage

View file

@ -45,7 +45,7 @@
label: model.name,
model: model
}))}
showTemporaryChatControl={$user.role === 'user'
showTemporaryChatControl={$user?.role === 'user'
? ($user?.permissions?.chat?.temporary ?? true) &&
!($user?.permissions?.chat?.temporary_enforced ?? false)
: true}

View file

@ -634,7 +634,7 @@
</div>
{/each}
{#if !(searchValue.trim() in $MODEL_DOWNLOAD_POOL) && searchValue && ollamaVersion && $user.role === 'admin'}
{#if !(searchValue.trim() in $MODEL_DOWNLOAD_POOL) && searchValue && ollamaVersion && $user?.role === 'admin'}
<Tooltip
content={$i18n.t(`Pull "{{searchValue}}" from Ollama.com`, {
searchValue: searchValue

View file

@ -147,10 +147,10 @@
</button>
</Tooltip>
{#if $user !== undefined}
{#if $user !== undefined && $user !== null}
<UserMenu
className="max-w-[200px]"
role={$user.role}
role={$user?.role}
on:show={(e) => {
if (e.detail === 'archived-chat') {
showArchivedChats.set(true);
@ -163,7 +163,7 @@
>
<div class=" self-center">
<img
src={$user.profile_image_url}
src={$user?.profile_image_url}
class="size-6 object-cover rounded-full"
alt="User profile"
draggable="false"

View file

@ -8,7 +8,7 @@
const dispatch = createEventDispatcher();
import { config, user, models as _models, temporaryChatEnabled } from '$lib/stores';
import { sanitizeResponseContent, findWordIndices } from '$lib/utils';
import { sanitizeResponseContent, extractCurlyBraceWords } from '$lib/utils';
import { WEBUI_BASE_URL } from '$lib/constants';
import Suggestions from './Suggestions.svelte';
@ -65,9 +65,7 @@
const chatInputElement = document.getElementById('chat-input');
if (chatInputContainerElement) {
chatInputContainerElement.style.height = '';
chatInputContainerElement.style.height =
Math.min(chatInputContainerElement.scrollHeight, 200) + 'px';
chatInputContainerElement.scrollTop = chatInputContainerElement.scrollHeight;
}
await tick();
@ -93,12 +91,12 @@
<div class="m-auto w-full max-w-6xl px-2 @2xl:px-20 translate-y-6 py-24 text-center">
{#if $temporaryChatEnabled}
<Tooltip
content="This chat won't appear in history and your messages will not be saved."
content={$i18n.t('This chat wont appear in history and your messages will not be saved.')}
className="w-full flex justify-center mb-0.5"
placement="top"
>
<div class="flex items-center gap-2 text-gray-500 font-medium text-lg my-2 w-fit">
<EyeSlash strokeWidth="2.5" className="size-5" /> Temporary Chat
<EyeSlash strokeWidth="2.5" className="size-5" />{$i18n.t('Temporary Chat')}
</div>
</Tooltip>
{/if}
@ -142,7 +140,7 @@
{#if models[selectedModelIdx]?.name}
{models[selectedModelIdx]?.name}
{:else}
{$i18n.t('Hello, {{name}}', { name: $user.name })}
{$i18n.t('Hello, {{name}}', { name: $user?.name })}
{/if}
</div>
</div>

View file

@ -31,8 +31,8 @@
let profileImageInputElement: HTMLInputElement;
const submitHandler = async () => {
if (name !== $user.name) {
if (profileImageUrl === generateInitialsImage($user.name) || profileImageUrl === '') {
if (name !== $user?.name) {
if (profileImageUrl === generateInitialsImage($user?.name) || profileImageUrl === '') {
profileImageUrl = generateInitialsImage(name);
}
}
@ -75,8 +75,8 @@
};
onMount(async () => {
name = $user.name;
profileImageUrl = $user.profile_image_url;
name = $user?.name;
profileImageUrl = $user?.profile_image_url;
webhookUrl = $settings?.notifications?.webhook_url ?? '';
APIKey = await getAPIKey(localStorage.token).catch((error) => {
@ -214,7 +214,7 @@
<button
class=" text-xs text-center text-gray-800 dark:text-gray-400 rounded-full px-4 py-0.5 bg-gray-100 dark:bg-gray-850"
on:click={async () => {
const url = await getGravatarUrl(localStorage.token, $user.email);
const url = await getGravatarUrl(localStorage.token, $user?.email);
profileImageUrl = url;
}}>{$i18n.t('Use Gravatar')}</button
@ -236,10 +236,11 @@
<div class="flex-1">
<input
class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-hidden"
class="w-full text-sm dark:text-gray-300 dark:bg-gray-850 outline-hidden"
type="text"
bind:value={name}
required
placeholder={$i18n.t('Enter your name')}
/>
</div>
</div>
@ -268,7 +269,7 @@
<UpdatePassword />
</div>
<hr class="border-gray-100 dark:border-gray-850 my-4" />
<hr class="border-gray-50 dark:border-gray-850 my-2" />
<div class="flex justify-between items-center text-sm">
<div class=" font-medium">{$i18n.t('API keys')}</div>

View file

@ -786,62 +786,6 @@
{/if}
</div>
<div class=" py-0.5 w-full justify-between">
<Tooltip
content={$i18n.t(
'Control the repetition of token sequences in the generated text. A higher value (e.g., 1.5) will penalize repetitions more strongly, while a lower value (e.g., 1.1) will be more lenient. At 1, it is disabled.'
)}
placement="top-start"
className="inline-tooltip"
>
<div class="flex w-full justify-between">
<div class=" self-center text-xs font-medium">
{$i18n.t('Repeat Penalty (Ollama)')}
</div>
<button
class="p-1 px-3 text-xs flex rounded transition flex-shrink-0 outline-none"
type="button"
on:click={() => {
params.repeat_penalty = (params?.repeat_penalty ?? null) === null ? 1.1 : null;
}}
>
{#if (params?.repeat_penalty ?? null) === null}
<span class="ml-2 self-center">{$i18n.t('Default')}</span>
{:else}
<span class="ml-2 self-center">{$i18n.t('Custom')}</span>
{/if}
</button>
</div>
</Tooltip>
{#if (params?.repeat_penalty ?? null) !== null}
<div class="flex mt-0.5 space-x-2">
<div class=" flex-1">
<input
id="steps-range"
type="range"
min="-2"
max="2"
step="0.05"
bind:value={params.repeat_penalty}
class="w-full h-2 rounded-lg appearance-none cursor-pointer dark:bg-gray-700"
/>
</div>
<div>
<input
bind:value={params.repeat_penalty}
type="number"
class=" bg-transparent text-center w-14"
min="-2"
max="2"
step="any"
/>
</div>
</div>
{/if}
</div>
<div class=" py-0.5 w-full justify-between">
<Tooltip
content={$i18n.t('Sets how far back for the model to look back to prevent repetition.')}
@ -952,6 +896,172 @@
{/if}
</div>
<div class=" py-0.5 w-full justify-between">
<Tooltip
content={$i18n.t(
'This option controls how many tokens are preserved when refreshing the context. For example, if set to 2, the last 2 tokens of the conversation context will be retained. Preserving context can help maintain the continuity of a conversation, but it may reduce the ability to respond to new topics.'
)}
placement="top-start"
className="inline-tooltip"
>
<div class="flex w-full justify-between">
<div class=" self-center text-xs font-medium">
{$i18n.t('Tokens To Keep On Context Refresh (num_keep)')}
</div>
<button
class="p-1 px-3 text-xs flex rounded-sm transition shrink-0 outline-hidden"
type="button"
on:click={() => {
params.num_keep = (params?.num_keep ?? null) === null ? 24 : null;
}}
>
{#if (params?.num_keep ?? null) === null}
<span class="ml-2 self-center">{$i18n.t('Default')}</span>
{:else}
<span class="ml-2 self-center">{$i18n.t('Custom')}</span>
{/if}
</button>
</div>
</Tooltip>
{#if (params?.num_keep ?? null) !== null}
<div class="flex mt-0.5 space-x-2">
<div class=" flex-1">
<input
id="steps-range"
type="range"
min="-1"
max="10240000"
step="1"
bind:value={params.num_keep}
class="w-full h-2 rounded-lg appearance-none cursor-pointer dark:bg-gray-700"
/>
</div>
<div class="">
<input
bind:value={params.num_keep}
type="number"
class=" bg-transparent text-center w-14"
min="-1"
step="1"
/>
</div>
</div>
{/if}
</div>
<div class=" py-0.5 w-full justify-between">
<Tooltip
content={$i18n.t(
'This option sets the maximum number of tokens the model can generate in its response. Increasing this limit allows the model to provide longer answers, but it may also increase the likelihood of unhelpful or irrelevant content being generated.'
)}
placement="top-start"
className="inline-tooltip"
>
<div class="flex w-full justify-between">
<div class=" self-center text-xs font-medium">
{$i18n.t('Max Tokens (num_predict)')}
</div>
<button
class="p-1 px-3 text-xs flex rounded-sm transition shrink-0 outline-hidden"
type="button"
on:click={() => {
params.max_tokens = (params?.max_tokens ?? null) === null ? 128 : null;
}}
>
{#if (params?.max_tokens ?? null) === null}
<span class="ml-2 self-center">{$i18n.t('Default')}</span>
{:else}
<span class="ml-2 self-center">{$i18n.t('Custom')}</span>
{/if}
</button>
</div>
</Tooltip>
{#if (params?.max_tokens ?? null) !== null}
<div class="flex mt-0.5 space-x-2">
<div class=" flex-1">
<input
id="steps-range"
type="range"
min="-2"
max="131072"
step="1"
bind:value={params.max_tokens}
class="w-full h-2 rounded-lg appearance-none cursor-pointer dark:bg-gray-700"
/>
</div>
<div>
<input
bind:value={params.max_tokens}
type="number"
class=" bg-transparent text-center w-14"
min="-2"
step="1"
/>
</div>
</div>
{/if}
</div>
<div class=" py-0.5 w-full justify-between">
<Tooltip
content={$i18n.t(
'Control the repetition of token sequences in the generated text. A higher value (e.g., 1.5) will penalize repetitions more strongly, while a lower value (e.g., 1.1) will be more lenient. At 1, it is disabled.'
)}
placement="top-start"
className="inline-tooltip"
>
<div class="flex w-full justify-between">
<div class=" self-center text-xs font-medium">
{$i18n.t('Repeat Penalty (Ollama)')}
</div>
<button
class="p-1 px-3 text-xs flex rounded transition flex-shrink-0 outline-none"
type="button"
on:click={() => {
params.repeat_penalty = (params?.repeat_penalty ?? null) === null ? 1.1 : null;
}}
>
{#if (params?.repeat_penalty ?? null) === null}
<span class="ml-2 self-center">{$i18n.t('Default')}</span>
{:else}
<span class="ml-2 self-center">{$i18n.t('Custom')}</span>
{/if}
</button>
</div>
</Tooltip>
{#if (params?.repeat_penalty ?? null) !== null}
<div class="flex mt-0.5 space-x-2">
<div class=" flex-1">
<input
id="steps-range"
type="range"
min="-2"
max="2"
step="0.05"
bind:value={params.repeat_penalty}
class="w-full h-2 rounded-lg appearance-none cursor-pointer dark:bg-gray-700"
/>
</div>
<div>
<input
bind:value={params.repeat_penalty}
type="number"
class=" bg-transparent text-center w-14"
min="-2"
max="2"
step="any"
/>
</div>
</div>
{/if}
</div>
<div class=" py-0.5 w-full justify-between">
<Tooltip
content={$i18n.t('Sets the size of the context window used to generate the next token.')}
@ -1061,116 +1171,6 @@
{/if}
</div>
<div class=" py-0.5 w-full justify-between">
<Tooltip
content={$i18n.t(
'This option controls how many tokens are preserved when refreshing the context. For example, if set to 2, the last 2 tokens of the conversation context will be retained. Preserving context can help maintain the continuity of a conversation, but it may reduce the ability to respond to new topics.'
)}
placement="top-start"
className="inline-tooltip"
>
<div class="flex w-full justify-between">
<div class=" self-center text-xs font-medium">
{$i18n.t('Tokens To Keep On Context Refresh (num_keep)')}
</div>
<button
class="p-1 px-3 text-xs flex rounded-sm transition shrink-0 outline-hidden"
type="button"
on:click={() => {
params.num_keep = (params?.num_keep ?? null) === null ? 24 : null;
}}
>
{#if (params?.num_keep ?? null) === null}
<span class="ml-2 self-center">{$i18n.t('Default')}</span>
{:else}
<span class="ml-2 self-center">{$i18n.t('Custom')}</span>
{/if}
</button>
</div>
</Tooltip>
{#if (params?.num_keep ?? null) !== null}
<div class="flex mt-0.5 space-x-2">
<div class=" flex-1">
<input
id="steps-range"
type="range"
min="-1"
max="10240000"
step="1"
bind:value={params.num_keep}
class="w-full h-2 rounded-lg appearance-none cursor-pointer dark:bg-gray-700"
/>
</div>
<div class="">
<input
bind:value={params.num_keep}
type="number"
class=" bg-transparent text-center w-14"
min="-1"
step="1"
/>
</div>
</div>
{/if}
</div>
<div class=" py-0.5 w-full justify-between">
<Tooltip
content={$i18n.t(
'This option sets the maximum number of tokens the model can generate in its response. Increasing this limit allows the model to provide longer answers, but it may also increase the likelihood of unhelpful or irrelevant content being generated.'
)}
placement="top-start"
className="inline-tooltip"
>
<div class="flex w-full justify-between">
<div class=" self-center text-xs font-medium">
{$i18n.t('Max Tokens (num_predict)')}
</div>
<button
class="p-1 px-3 text-xs flex rounded-sm transition shrink-0 outline-hidden"
type="button"
on:click={() => {
params.max_tokens = (params?.max_tokens ?? null) === null ? 128 : null;
}}
>
{#if (params?.max_tokens ?? null) === null}
<span class="ml-2 self-center">{$i18n.t('Default')}</span>
{:else}
<span class="ml-2 self-center">{$i18n.t('Custom')}</span>
{/if}
</button>
</div>
</Tooltip>
{#if (params?.max_tokens ?? null) !== null}
<div class="flex mt-0.5 space-x-2">
<div class=" flex-1">
<input
id="steps-range"
type="range"
min="-2"
max="131072"
step="1"
bind:value={params.max_tokens}
class="w-full h-2 rounded-lg appearance-none cursor-pointer dark:bg-gray-700"
/>
</div>
<div>
<input
bind:value={params.max_tokens}
type="number"
class=" bg-transparent text-center w-14"
min="-2"
step="1"
/>
</div>
</div>
{/if}
</div>
{#if admin}
<div class=" py-0.5 w-full justify-between">
<Tooltip

View file

@ -293,7 +293,7 @@
<div class="flex-1">
<input
list="voice-list"
class="w-full rounded-lg py-2 px-4 text-sm bg-white dark:text-gray-300 dark:bg-gray-850 outline-hidden"
class="w-full text-sm bg-white dark:text-gray-300 dark:bg-gray-850 outline-hidden"
bind:value={voice}
placeholder="Select a voice"
/>
@ -330,7 +330,7 @@
<div class="flex w-full">
<div class="flex-1">
<select
class="w-full rounded-lg py-2 px-4 text-sm bg-white dark:text-gray-300 dark:bg-gray-850 outline-hidden"
class="w-full text-sm bg-white dark:text-gray-300 dark:bg-gray-850 outline-hidden"
bind:value={voice}
>
<option value="" selected={voice !== ''}>{$i18n.t('Default')}</option>
@ -361,7 +361,7 @@
<div class="flex-1">
<input
list="voice-list"
class="w-full rounded-lg py-2 px-4 text-sm bg-white dark:text-gray-300 dark:bg-gray-850 outline-hidden"
class="w-full text-sm bg-white dark:text-gray-300 dark:bg-gray-850 outline-hidden"
bind:value={voice}
placeholder="Select a voice"
/>

View file

@ -16,6 +16,7 @@
import { onMount, getContext } from 'svelte';
import { goto } from '$app/navigation';
import { toast } from 'svelte-sonner';
import ArchivedChatsModal from '$lib/components/layout/Sidebar/ArchivedChatsModal.svelte';
const i18n = getContext('i18n');
@ -26,6 +27,7 @@
let showArchiveConfirm = false;
let showDeleteConfirm = false;
let showArchivedChatsModal = false;
let chatImportInputElement: HTMLInputElement;
@ -95,8 +97,16 @@
await chats.set(await getChatList(localStorage.token, $currentChatPage));
scrollPaginationEnabled.set(true);
};
const handleArchivedChatsChange = async () => {
currentChatPage.set(1);
await chats.set(await getChatList(localStorage.token, $currentChatPage));
scrollPaginationEnabled.set(true);
};
</script>
<ArchivedChatsModal bind:show={showArchivedChatsModal} on:change={handleArchivedChatsChange} />
<div class="flex flex-col h-full justify-between space-y-3 text-sm">
<div class=" space-y-2 overflow-y-scroll max-h-[28rem] lg:max-h-full">
<div class="flex flex-col">
@ -157,6 +167,32 @@
<hr class=" border-gray-100 dark:border-gray-850" />
<div class="flex flex-col">
<button
class=" flex rounded-md py-2 px-3.5 w-full hover:bg-gray-200 dark:hover:bg-gray-800 transition"
on:click={() => {
showArchivedChatsModal = true;
}}
>
<div class=" self-center mr-3">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="currentColor"
class="size-4"
>
<path
d="M3.375 3C2.339 3 1.5 3.84 1.5 4.875v.75c0 1.036.84 1.875 1.875 1.875h17.25c1.035 0 1.875-.84 1.875-1.875v-.75C22.5 3.839 21.66 3 20.625 3H3.375Z"
/>
<path
fill-rule="evenodd"
d="m3.087 9 .54 9.176A3 3 0 0 0 6.62 21h10.757a3 3 0 0 0 2.995-2.824L20.913 9H3.087ZM12 10.5a.75.75 0 0 1 .75.75v4.94l1.72-1.72a.75.75 0 1 1 1.06 1.06l-3 3a.75.75 0 0 1-1.06 0l-3-3a.75.75 0 1 1 1.06-1.06l1.72 1.72v-4.94a.75.75 0 0 1 .75-.75Z"
clip-rule="evenodd"
/>
</svg>
</div>
<div class=" self-center text-sm font-medium">{$i18n.t('Archived Chats')}</div>
</button>
{#if showArchiveConfirm}
<div class="flex justify-between rounded-md items-center py-2 px-3.5 w-full transition">
<div class="flex items-center space-x-3">

View file

@ -308,15 +308,16 @@
</div>
</div>
{#if $user.role === 'admin' || $user?.permissions.chat?.controls}
<hr class="border-gray-100 dark:border-gray-850 my-3" />
{#if $user?.role === 'admin' || $user?.permissions.chat?.controls}
<hr class="border-gray-50 dark:border-gray-850 my-3" />
<div>
<div class=" my-2.5 text-sm font-medium">{$i18n.t('System Prompt')}</div>
<textarea
<Textarea
bind:value={system}
class="w-full rounded-lg p-4 text-sm bg-white dark:text-gray-300 dark:bg-gray-850 outline-hidden resize-none"
className="w-full text-sm bg-white dark:text-gray-300 dark:bg-gray-900 outline-hidden resize-none"
rows="4"
placeholder={$i18n.t('Enter system prompt here')}
/>
</div>
@ -358,7 +359,7 @@
{#if keepAlive !== null}
<div class="flex mt-1 space-x-2">
<input
class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-hidden"
class="w-full text-sm dark:text-gray-300 dark:bg-gray-850 outline-hidden"
type="text"
placeholder={$i18n.t("e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.")}
bind:value={keepAlive}
@ -398,7 +399,7 @@
{#if requestFormat !== null}
<div class="flex mt-1 space-x-2">
<Textarea
className="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-hidden"
className="w-full text-sm dark:text-gray-300 dark:bg-gray-900 outline-hidden"
placeholder={$i18n.t('e.g. "json" or a JSON schema')}
bind:value={requestFormat}
/>

View file

@ -441,7 +441,7 @@
</div>
</div>
{#if $user.role === 'admin'}
{#if $user?.role === 'admin'}
<div>
<div class=" py-0.5 flex w-full justify-between">
<div class=" self-center text-xs">

View file

@ -82,7 +82,7 @@
<div class="relative overflow-x-auto">
<table class="w-full text-sm text-left text-gray-600 dark:text-gray-400 table-auto">
<thead
class="text-xs text-gray-700 uppercase bg-transparent dark:text-gray-200 border-b-2 dark:border-gray-850"
class="text-xs text-gray-700 uppercase bg-transparent dark:text-gray-200 border-b-2 border-gray-50 dark:border-gray-850"
>
<tr>
<th scope="col" class="px-3 py-2"> {$i18n.t('Name')} </th>
@ -94,7 +94,7 @@
</thead>
<tbody>
{#each memories as memory}
<tr class="border-b dark:border-gray-850 items-center">
<tr class="border-b border-gray-50 dark:border-gray-850 items-center">
<td class="px-3 py-1">
<div class="line-clamp-1">
{memory.content}

View file

@ -39,7 +39,7 @@
});
</script>
<AddServerModal bind:show={showConnectionModal} onSubmit={addConnectionHandler} />
<AddServerModal bind:show={showConnectionModal} onSubmit={addConnectionHandler} direct />
<form
class="flex flex-col h-full justify-between text-sm"
@ -74,9 +74,8 @@
<div class="flex flex-col gap-1.5">
{#each servers as server, idx}
<Connection
bind:url={server.url}
bind:key={server.key}
bind:config={server.config}
bind:connection={server}
direct
onSubmit={() => {
updateHandler();
}}

View file

@ -11,11 +11,8 @@
export let onDelete = () => {};
export let onSubmit = () => {};
export let pipeline = false;
export let url = '';
export let key = '';
export let config = {};
export let connection = null;
export let direct = false;
let showConfigModal = false;
let showDeleteConfirmDialog = false;
@ -23,21 +20,15 @@
<AddServerModal
edit
direct
{direct}
bind:show={showConfigModal}
connection={{
url,
key,
config
}}
{connection}
onDelete={() => {
showDeleteConfirmDialog = true;
}}
onSubmit={(connection) => {
url = connection.url;
key = connection.key;
config = connection.config;
onSubmit(connection);
onSubmit={(c) => {
connection = c;
onSubmit(c);
}}
/>
@ -52,12 +43,12 @@
<div class="flex w-full gap-2 items-center">
<Tooltip
className="w-full relative"
content={$i18n.t(`WebUI will make requests to "{{url}}/openapi.json"`, {
url: url
content={$i18n.t(`WebUI will make requests to "{{url}}"`, {
url: `${connection?.url}/${connection?.path ?? 'openapi.json'}`
})}
placement="top-start"
>
{#if !(config?.enable ?? true)}
{#if !(connection?.config?.enable ?? true)}
<div
class="absolute top-0 bottom-0 left-0 right-0 opacity-60 bg-white dark:bg-gray-900 z-10"
></div>
@ -65,19 +56,21 @@
<div class="flex w-full">
<div class="flex-1 relative">
<input
class=" outline-hidden w-full bg-transparent {pipeline ? 'pr-8' : ''}"
class=" outline-hidden w-full bg-transparent"
placeholder={$i18n.t('API Base URL')}
bind:value={url}
bind:value={connection.url}
autocomplete="off"
/>
</div>
<SensitiveInput
inputClassName=" outline-hidden bg-transparent w-full"
placeholder={$i18n.t('API Key')}
bind:value={key}
required={false}
/>
{#if (connection?.auth_type ?? 'bearer') === 'bearer'}
<SensitiveInput
inputClassName=" outline-hidden bg-transparent w-full"
placeholder={$i18n.t('API Key')}
bind:value={connection.key}
required={false}
/>
{/if}
</div>
</Tooltip>

View file

@ -462,7 +462,7 @@
<div class=" self-center">{$i18n.t('Interface')}</div>
</button>
{:else if tabId === 'connections'}
{#if $user.role === 'admin' || ($user.role === 'user' && $config?.features?.enable_direct_connections)}
{#if $user?.role === 'admin' || ($user?.role === 'user' && $config?.features?.enable_direct_connections)}
<button
class="px-0.5 py-1 min-w-fit rounded-lg flex-1 md:flex-none flex text-left transition {selectedTab ===
'connections'
@ -488,7 +488,7 @@
</button>
{/if}
{:else if tabId === 'tools'}
{#if $user.role === 'admin' || ($user.role === 'user' && $config?.features?.enable_direct_tools)}
{#if $user?.role === 'admin' || ($user?.role === 'user' && $user?.permissions?.features?.direct_tool_servers)}
<button
class="px-0.5 py-1 min-w-fit rounded-lg flex-1 md:flex-none flex text-left transition {selectedTab ===
'tools'
@ -636,7 +636,7 @@
<div class=" self-center">{$i18n.t('About')}</div>
</button>
{:else if tabId === 'admin'}
{#if $user.role === 'admin'}
{#if $user?.role === 'admin'}
<button
class="px-0.5 py-1 min-w-fit rounded-lg flex-1 md:flex-none flex text-left transition {selectedTab ===
'admin'

View file

@ -1,6 +1,6 @@
<script lang="ts">
import { getContext, onMount } from 'svelte';
import { models, config, toolServers } from '$lib/stores';
import { models, config, toolServers, tools } from '$lib/stores';
import { toast } from 'svelte-sonner';
import { deleteSharedChatById, getChatById, shareChatById } from '$lib/apis/chats';
@ -11,6 +11,11 @@
import Collapsible from '../common/Collapsible.svelte';
export let show = false;
export let selectedToolIds = [];
let selectedTools = [];
$: selectedTools = $tools.filter((tool) => selectedToolIds.includes(tool.id));
const i18n = getContext('i18n');
</script>
@ -18,7 +23,7 @@
<Modal bind:show size="md">
<div>
<div class=" flex justify-between dark:text-gray-300 px-5 pt-4 pb-0.5">
<div class=" text-lg font-medium self-center">{$i18n.t('Available Tool Servers')}</div>
<div class=" text-lg font-medium self-center">{$i18n.t('Available Tools')}</div>
<button
class="self-center"
on:click={() => {
@ -38,47 +43,85 @@
</button>
</div>
<div class="px-5 pb-5 w-full flex flex-col justify-center">
<div class=" text-sm dark:text-gray-300 mb-2">
Open WebUI can use tools provided by any OpenAPI server. <br /><a
class="underline"
href="https://github.com/open-webui/openapi-servers"
target="_blank">Learn more about OpenAPI tool servers.</a
>
</div>
<div class=" text-sm dark:text-gray-300 mb-1">
{#each $toolServers as toolServer}
<Collapsible buttonClassName="w-full" chevron>
<div>
<div class="text-base font-medium dark:text-gray-100 text-gray-800">
{toolServer?.openapi?.info?.title} - v{toolServer?.openapi?.info?.version}
</div>
{#if selectedTools.length > 0}
{#if $toolServers.length > 0}
<div class=" flex justify-between dark:text-gray-300 px-5 pb-1">
<div class=" text-base font-medium self-center">{$i18n.t('Tools')}</div>
</div>
{/if}
<div class="text-sm text-gray-500">
{toolServer?.openapi?.info?.description}
</div>
<div class="text-sm text-gray-500">
{toolServer?.url}
</div>
</div>
<div slot="content">
{#each toolServer?.specs ?? [] as tool_spec}
<div class="my-1">
<div class="font-medium text-gray-800 dark:text-gray-100">
{tool_spec?.name}
</div>
<div>
{tool_spec?.description}
</div>
<div class="px-5 pb-3 w-full flex flex-col justify-center">
<div class=" text-sm dark:text-gray-300 mb-1">
{#each selectedTools as tool}
<Collapsible buttonClassName="w-full mb-0.5">
<div>
<div class="text-sm font-medium dark:text-gray-100 text-gray-800">
{tool?.name}
</div>
{/each}
</div>
</Collapsible>
{/each}
{#if tool?.meta?.description}
<div class="text-xs text-gray-500">
{tool?.meta?.description}
</div>
{/if}
</div>
<!-- <div slot="content">
{JSON.stringify(tool, null, 2)}
</div> -->
</Collapsible>
{/each}
</div>
</div>
</div>
{/if}
{#if $toolServers.length > 0}
<div class=" flex justify-between dark:text-gray-300 px-5 pb-0.5">
<div class=" text-base font-medium self-center">{$i18n.t('Tool Servers')}</div>
</div>
<div class="px-5 pb-5 w-full flex flex-col justify-center">
<div class=" text-xs text-gray-600 dark:text-gray-300 mb-2">
Open WebUI can use tools provided by any OpenAPI server. <br /><a
class="underline"
href="https://github.com/open-webui/openapi-servers"
target="_blank">Learn more about OpenAPI tool servers.</a
>
</div>
<div class=" text-sm dark:text-gray-300 mb-1">
{#each $toolServers as toolServer}
<Collapsible buttonClassName="w-full" chevron>
<div>
<div class="text-sm font-medium dark:text-gray-100 text-gray-800">
{toolServer?.openapi?.info?.title} - v{toolServer?.openapi?.info?.version}
</div>
<div class="text-xs text-gray-500">
{toolServer?.openapi?.info?.description}
</div>
<div class="text-xs text-gray-500">
{toolServer?.url}
</div>
</div>
<div slot="content">
{#each toolServer?.specs ?? [] as tool_spec}
<div class="my-1">
<div class="font-medium text-gray-800 dark:text-gray-100">
{tool_spec?.name}
</div>
<div>
{tool_spec?.description}
</div>
</div>
{/each}
</div>
</Collapsible>
{/each}
</div>
</div>
{/if}
</div>
</Modal>

View file

@ -1,5 +1,6 @@
<script lang="ts">
import { decode } from 'html-entities';
import { v4 as uuidv4 } from 'uuid';
import { getContext, createEventDispatcher } from 'svelte';
const i18n = getContext('i18n');
@ -36,6 +37,7 @@
import Spinner from './Spinner.svelte';
import CodeBlock from '../chat/Messages/CodeBlock.svelte';
import Markdown from '../chat/Messages/Markdown.svelte';
import Image from './Image.svelte';
export let open = false;
@ -53,9 +55,19 @@
export let disabled = false;
export let hide = false;
function formatJSONString(obj) {
const collapsibleId = uuidv4();
function parseJSONString(str) {
try {
const parsed = JSON.parse(JSON.parse(obj));
return parseJSONString(JSON.parse(str));
} catch (e) {
return str;
}
}
function formatJSONString(str) {
try {
const parsed = parseJSONString(str);
// If parsed is an object/array, then it's valid JSON
if (typeof parsed === 'object') {
return JSON.stringify(parsed, null, 2);
@ -65,7 +77,7 @@
}
} catch (e) {
// Not valid JSON, return as-is
return obj;
return str;
}
}
</script>
@ -119,15 +131,15 @@
{:else if attributes?.type === 'tool_calls'}
{#if attributes?.done === 'true'}
<Markdown
id={`tool-calls-${attributes?.id}`}
content={$i18n.t('View Result from `{{NAME}}`', {
id={`${collapsibleId}-tool-calls-${attributes?.id}`}
content={$i18n.t('View Result from **{{NAME}}**', {
NAME: attributes.name
})}
/>
{:else}
<Markdown
id={`tool-calls-${attributes?.id}`}
content={$i18n.t('Executing `{{NAME}}`...', {
id={`${collapsibleId}-tool-calls-${attributes?.id}-executing`}
content={$i18n.t('Executing **{{NAME}}**...', {
NAME: attributes.name
})}
/>
@ -188,32 +200,55 @@
</div>
{/if}
{#if !grow}
{#if open && !hide}
<div transition:slide={{ duration: 300, easing: quintOut, axis: 'y' }}>
{#if attributes?.type === 'tool_calls'}
{@const args = decode(attributes?.arguments)}
{@const result = decode(attributes?.result ?? '')}
{#if attributes?.type === 'tool_calls'}
{@const args = decode(attributes?.arguments)}
{@const result = decode(attributes?.result ?? '')}
{@const files = parseJSONString(decode(attributes?.files ?? ''))}
{#if attributes?.done === 'true'}
<Markdown
id={`tool-calls-${attributes?.id}-result`}
content={`> \`\`\`json
{#if !grow}
{#if open && !hide}
<div transition:slide={{ duration: 300, easing: quintOut, axis: 'y' }}>
{#if attributes?.type === 'tool_calls'}
{#if attributes?.done === 'true'}
<Markdown
id={`${collapsibleId}-tool-calls-${attributes?.id}-result`}
content={`> \`\`\`json
> ${formatJSONString(args)}
> ${formatJSONString(result)}
> \`\`\``}
/>
{:else}
<Markdown
id={`tool-calls-${attributes?.id}-result`}
content={`> \`\`\`json
/>
{:else}
<Markdown
id={`${collapsibleId}-tool-calls-${attributes?.id}-result`}
content={`> \`\`\`json
> ${formatJSONString(args)}
> \`\`\``}
/>
/>
{/if}
{:else}
<slot name="content" />
{/if}
{:else}
<slot name="content" />
</div>
{/if}
{#if attributes?.done === 'true'}
{#if typeof files === 'object'}
{#each files ?? [] as file, idx}
{#if file.startsWith('data:image/')}
<Image
id={`${collapsibleId}-tool-calls-${attributes?.id}-result-${idx}`}
src={file}
alt="Image"
/>
{/if}
{/each}
{/if}
{/if}
{/if}
{:else if !grow}
{#if open && !hide}
<div transition:slide={{ duration: 300, easing: quintOut, axis: 'y' }}>
<slot name="content" />
</div>
{/if}
{/if}

View file

@ -7,7 +7,7 @@
export let show = true;
export let size = 'md';
export let containerClassName = 'p-3';
export let className = 'bg-gray-50 dark:bg-gray-900 rounded-2xl';
export let className = 'bg-white dark:bg-gray-900 rounded-2xl';
let modalElement = null;
let mounted = false;

View file

@ -167,7 +167,7 @@
{#if $user !== undefined}
<UserMenu
className="max-w-[200px]"
role={$user.role}
role={$user?.role}
on:show={(e) => {
if (e.detail === 'archived-chat') {
showArchivedChats.set(true);
@ -180,7 +180,7 @@
>
<div class=" self-center">
<img
src={$user.profile_image_url}
src={$user?.profile_image_url}
class="size-6 object-cover rounded-full"
alt="User profile"
draggable="false"

View file

@ -446,7 +446,7 @@
});
if (res) {
$socket.emit('join-channels', { auth: { token: $user.token } });
$socket.emit('join-channels', { auth: { token: $user?.token } });
await initChannels();
showCreateChannel = false;
}
@ -627,13 +627,13 @@
? 'opacity-20'
: ''}"
>
{#if $config?.features?.enable_channels && ($user.role === 'admin' || $channels.length > 0) && !search}
{#if $config?.features?.enable_channels && ($user?.role === 'admin' || $channels.length > 0) && !search}
<Folder
className="px-2 mt-0.5"
name={$i18n.t('Channels')}
dragAndDrop={false}
onAdd={async () => {
if ($user.role === 'admin') {
if ($user?.role === 'admin') {
await tick();
setTimeout(() => {
@ -891,9 +891,9 @@
<div class="px-2">
<div class="flex flex-col font-primary">
{#if $user !== undefined}
{#if $user !== undefined && $user !== null}
<UserMenu
role={$user.role}
role={$user?.role}
on:show={(e) => {
if (e.detail === 'archived-chat') {
showArchivedChats.set(true);
@ -908,12 +908,12 @@
>
<div class=" self-center mr-3">
<img
src={$user.profile_image_url}
src={$user?.profile_image_url}
class=" max-w-[30px] object-cover rounded-full"
alt="User profile"
/>
</div>
<div class=" self-center font-medium">{$user.name}</div>
<div class=" self-center font-medium">{$user?.name}</div>
</button>
</UserMenu>
{/if}

View file

@ -134,7 +134,7 @@
<div class="relative overflow-x-auto">
<table class="w-full text-sm text-left text-gray-600 dark:text-gray-400 table-auto">
<thead
class="text-xs text-gray-700 uppercase bg-transparent dark:text-gray-200 border-b-2 dark:border-gray-850"
class="text-xs text-gray-700 uppercase bg-transparent dark:text-gray-200 border-b-2 border-gray-50 dark:border-gray-850"
>
<tr>
<th scope="col" class="px-3 py-2"> {$i18n.t('Name')} </th>
@ -150,7 +150,7 @@
.includes(searchValue.toLowerCase())) as chat, idx}
<tr
class="bg-transparent {idx !== chats.length - 1 &&
'border-b'} dark:bg-gray-900 dark:border-gray-850 text-xs"
'border-b'} dark:bg-gray-900 border-gray-50 dark:border-gray-850 text-xs"
>
<td class="px-3 py-1 w-2/3">
<a href="/c/{chat.id}" target="_blank">

View file

@ -163,7 +163,7 @@
{#if focused && (filteredOptions.length > 0 || filteredTags.length > 0)}
<!-- svelte-ignore a11y-no-static-element-interactions -->
<div
class="absolute top-0 mt-8 left-0 right-1 border dark:border-gray-900 bg-gray-50 dark:bg-gray-950 rounded-lg z-10 shadow-lg"
class="absolute top-0 mt-8 left-0 right-1 border border-gray-100 dark:border-gray-900 bg-gray-50 dark:bg-gray-950 rounded-lg z-10 shadow-lg"
in:fade={{ duration: 50 }}
on:mouseenter={() => {
selectedIdx = null;

View file

@ -93,13 +93,10 @@
const tab = await window.open(`${url}/models/create`, '_blank');
// Define the event handler function
const messageHandler = (event) => {
if (event.origin !== url) return;
if (event.data === 'loaded') {
tab.postMessage(JSON.stringify(model), '*');
// Remove the event listener after handling the message
window.removeEventListener('message', messageHandler);
}
};
@ -477,29 +474,33 @@
</div>
</button>
<button
class="flex text-xs items-center space-x-1 px-3 py-1.5 rounded-xl bg-gray-50 hover:bg-gray-100 dark:bg-gray-800 dark:hover:bg-gray-700 dark:text-gray-200 transition"
on:click={async () => {
downloadModels(models);
}}
>
<div class=" self-center mr-2 font-medium line-clamp-1">{$i18n.t('Export Models')}</div>
{#if models.length}
<button
class="flex text-xs items-center space-x-1 px-3 py-1.5 rounded-xl bg-gray-50 hover:bg-gray-100 dark:bg-gray-800 dark:hover:bg-gray-700 dark:text-gray-200 transition"
on:click={async () => {
downloadModels(models);
}}
>
<div class=" self-center mr-2 font-medium line-clamp-1">
{$i18n.t('Export Models')}
</div>
<div class=" self-center">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 16 16"
fill="currentColor"
class="w-3.5 h-3.5"
>
<path
fill-rule="evenodd"
d="M4 2a1.5 1.5 0 0 0-1.5 1.5v9A1.5 1.5 0 0 0 4 14h8a1.5 1.5 0 0 0 1.5-1.5V6.621a1.5 1.5 0 0 0-.44-1.06L9.94 2.439A1.5 1.5 0 0 0 8.878 2H4Zm4 3.5a.75.75 0 0 1 .75.75v2.69l.72-.72a.75.75 0 1 1 1.06 1.06l-2 2a.75.75 0 0 1-1.06 0l-2-2a.75.75 0 0 1 1.06-1.06l.72.72V6.25A.75.75 0 0 1 8 5.5Z"
clip-rule="evenodd"
/>
</svg>
</div>
</button>
<div class=" self-center">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 16 16"
fill="currentColor"
class="w-3.5 h-3.5"
>
<path
fill-rule="evenodd"
d="M4 2a1.5 1.5 0 0 0-1.5 1.5v9A1.5 1.5 0 0 0 4 14h8a1.5 1.5 0 0 0 1.5-1.5V6.621a1.5 1.5 0 0 0-.44-1.06L9.94 2.439A1.5 1.5 0 0 0 8.878 2H4Zm4 3.5a.75.75 0 0 1 .75.75v2.69l.72-.72a.75.75 0 1 1 1.06 1.06l-2 2a.75.75 0 0 1-1.06 0l-2-2a.75.75 0 0 1 1.06-1.06l.72.72V6.25A.75.75 0 0 1 8 5.5Z"
clip-rule="evenodd"
/>
</svg>
</div>
</button>
{/if}
</div>
</div>
{/if}

View file

@ -285,33 +285,36 @@
</div>
</button>
<button
class="flex text-xs items-center space-x-1 px-3 py-1.5 rounded-xl bg-gray-50 hover:bg-gray-100 dark:bg-gray-800 dark:hover:bg-gray-700 dark:text-gray-200 transition"
on:click={async () => {
// promptsImportInputElement.click();
let blob = new Blob([JSON.stringify(prompts)], {
type: 'application/json'
});
saveAs(blob, `prompts-export-${Date.now()}.json`);
}}
>
<div class=" self-center mr-2 font-medium line-clamp-1">{$i18n.t('Export Prompts')}</div>
{#if prompts.length}
<button
class="flex text-xs items-center space-x-1 px-3 py-1.5 rounded-xl bg-gray-50 hover:bg-gray-100 dark:bg-gray-800 dark:hover:bg-gray-700 dark:text-gray-200 transition"
on:click={async () => {
let blob = new Blob([JSON.stringify(prompts)], {
type: 'application/json'
});
saveAs(blob, `prompts-export-${Date.now()}.json`);
}}
>
<div class=" self-center mr-2 font-medium line-clamp-1">
{$i18n.t('Export Prompts')}
</div>
<div class=" self-center">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 16 16"
fill="currentColor"
class="w-4 h-4"
>
<path
fill-rule="evenodd"
d="M4 2a1.5 1.5 0 0 0-1.5 1.5v9A1.5 1.5 0 0 0 4 14h8a1.5 1.5 0 0 0 1.5-1.5V6.621a1.5 1.5 0 0 0-.44-1.06L9.94 2.439A1.5 1.5 0 0 0 8.878 2H4Zm4 3.5a.75.75 0 0 1 .75.75v2.69l.72-.72a.75.75 0 1 1 1.06 1.06l-2 2a.75.75 0 0 1-1.06 0l-2-2a.75.75 0 0 1 1.06-1.06l.72.72V6.25A.75.75 0 0 1 8 5.5Z"
clip-rule="evenodd"
/>
</svg>
</div>
</button>
<div class=" self-center">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 16 16"
fill="currentColor"
class="w-4 h-4"
>
<path
fill-rule="evenodd"
d="M4 2a1.5 1.5 0 0 0-1.5 1.5v9A1.5 1.5 0 0 0 4 14h8a1.5 1.5 0 0 0 1.5-1.5V6.621a1.5 1.5 0 0 0-.44-1.06L9.94 2.439A1.5 1.5 0 0 0 8.878 2H4Zm4 3.5a.75.75 0 0 1 .75.75v2.69l.72-.72a.75.75 0 1 1 1.06 1.06l-2 2a.75.75 0 0 1-1.06 0l-2-2a.75.75 0 0 1 1.06-1.06l.72.72V6.25A.75.75 0 0 1 8 5.5Z"
clip-rule="evenodd"
/>
</svg>
</div>
</button>
{/if}
</div>
</div>
{/if}

View file

@ -71,13 +71,10 @@
const tab = await window.open(`${url}/tools/create`, '_blank');
// Define the event handler function
const messageHandler = (event) => {
if (event.origin !== url) return;
if (event.data === 'loaded') {
tab.postMessage(JSON.stringify(item), '*');
// Remove the event listener after handling the message
window.removeEventListener('message', messageHandler);
}
};
@ -124,8 +121,7 @@
if (res) {
toast.success($i18n.t('Tool deleted successfully'));
init();
await init();
}
};
@ -398,39 +394,41 @@
</div>
</button>
<button
class="flex text-xs items-center space-x-1 px-3 py-1.5 rounded-xl bg-gray-50 hover:bg-gray-100 dark:bg-gray-800 dark:hover:bg-gray-700 dark:text-gray-200 transition"
on:click={async () => {
const _tools = await exportTools(localStorage.token).catch((error) => {
toast.error(`${error}`);
return null;
});
if (_tools) {
let blob = new Blob([JSON.stringify(_tools)], {
type: 'application/json'
{#if tools.length}
<button
class="flex text-xs items-center space-x-1 px-3 py-1.5 rounded-xl bg-gray-50 hover:bg-gray-100 dark:bg-gray-800 dark:hover:bg-gray-700 dark:text-gray-200 transition"
on:click={async () => {
const _tools = await exportTools(localStorage.token).catch((error) => {
toast.error(`${error}`);
return null;
});
saveAs(blob, `tools-export-${Date.now()}.json`);
}
}}
>
<div class=" self-center mr-2 font-medium line-clamp-1">{$i18n.t('Export Tools')}</div>
<div class=" self-center">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 16 16"
fill="currentColor"
class="w-4 h-4"
>
<path
fill-rule="evenodd"
d="M4 2a1.5 1.5 0 0 0-1.5 1.5v9A1.5 1.5 0 0 0 4 14h8a1.5 1.5 0 0 0 1.5-1.5V6.621a1.5 1.5 0 0 0-.44-1.06L9.94 2.439A1.5 1.5 0 0 0 8.878 2H4Zm4 3.5a.75.75 0 0 1 .75.75v2.69l.72-.72a.75.75 0 1 1 1.06 1.06l-2 2a.75.75 0 0 1-1.06 0l-2-2a.75.75 0 0 1 1.06-1.06l.72.72V6.25A.75.75 0 0 1 8 5.5Z"
clip-rule="evenodd"
/>
</svg>
</div>
</button>
if (_tools) {
let blob = new Blob([JSON.stringify(_tools)], {
type: 'application/json'
});
saveAs(blob, `tools-export-${Date.now()}.json`);
}
}}
>
<div class=" self-center mr-2 font-medium line-clamp-1">{$i18n.t('Export Tools')}</div>
<div class=" self-center">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 16 16"
fill="currentColor"
class="w-4 h-4"
>
<path
fill-rule="evenodd"
d="M4 2a1.5 1.5 0 0 0-1.5 1.5v9A1.5 1.5 0 0 0 4 14h8a1.5 1.5 0 0 0 1.5-1.5V6.621a1.5 1.5 0 0 0-.44-1.06L9.94 2.439A1.5 1.5 0 0 0 8.878 2H4Zm4 3.5a.75.75 0 0 1 .75.75v2.69l.72-.72a.75.75 0 1 1 1.06 1.06l-2 2a.75.75 0 0 1-1.06 0l-2-2a.75.75 0 0 1 1.06-1.06l.72.72V6.25A.75.75 0 0 1 8 5.5Z"
clip-rule="evenodd"
/>
</svg>
</div>
</button>
{/if}
</div>
</div>
{/if}

View file

@ -6,7 +6,7 @@
"(latest)": "(الأخير)",
"(Ollama)": "",
"{{ models }}": "{{ نماذج }}",
"{{COUNT}} Available Tool Servers": "",
"{{COUNT}} Available Tools": "",
"{{COUNT}} hidden lines": "",
"{{COUNT}} Replies": "",
"{{user}}'s Chats": "دردشات {{user}}",
@ -108,6 +108,7 @@
"Attribute for Username": "",
"Audio": "صوتي",
"August": "أغسطس",
"Auth": "",
"Authenticate": "",
"Authentication": "",
"Auto-Copy Response to Clipboard": "النسخ التلقائي للاستجابة إلى الحافظة",
@ -119,7 +120,7 @@
"AUTOMATIC1111 Base URL": "AUTOMATIC1111 الرابط الرئيسي",
"AUTOMATIC1111 Base URL is required.": "AUTOMATIC1111 الرابط مطلوب",
"Available list": "",
"Available Tool Servers": "",
"Available Tools": "",
"available!": "متاح",
"Awful": "",
"Azure AI Speech": "",
@ -218,7 +219,10 @@
"Confirm your new password": "",
"Connect to your own OpenAI compatible API endpoints.": "",
"Connect to your own OpenAPI compatible external tool servers.": "",
"Connection failed": "",
"Connection successful": "",
"Connections": "اتصالات",
"Connections saved successfully": "",
"Constrains effort on reasoning for reasoning models. Only applicable to reasoning models from specific providers that support reasoning effort.": "",
"Contact Admin for WebUI Access": "",
"Content": "الاتصال",
@ -303,6 +307,7 @@
"Direct Connections": "",
"Direct Connections allow users to connect to their own OpenAI compatible API endpoints.": "",
"Direct Connections settings updated": "",
"Direct Tool Servers": "",
"Disabled": "",
"Discover a function": "",
"Discover a model": "اكتشف نموذجا",
@ -412,6 +417,7 @@
"Enter Kagi Search API Key": "",
"Enter Key Behavior": "",
"Enter language codes": "أدخل كود اللغة",
"Enter Mistral API Key": "",
"Enter Model ID": "",
"Enter model tag (e.g. {{modelTag}})": "(e.g. {{modelTag}}) أدخل الموديل تاق",
"Enter Mojeek Search API Key": "",
@ -436,6 +442,7 @@
"Enter server port": "",
"Enter stop sequence": "أدخل تسلسل التوقف",
"Enter system prompt": "",
"Enter system prompt here": "",
"Enter Tavily API Key": "",
"Enter the public URL of your WebUI. This URL will be used to generate links in the notifications.": "",
"Enter Tika Server URL": "",
@ -449,6 +456,7 @@
"Enter Your Email": "أدخل البريد الاكتروني",
"Enter Your Full Name": "أدخل الاسم كامل",
"Enter your message": "",
"Enter your name": "",
"Enter your new password": "",
"Enter Your Password": "ادخل كلمة المرور",
"Enter Your Role": "أدخل الصلاحيات",
@ -468,7 +476,7 @@
"Exceeded the number of seats in your license. Please contact support to increase the number of seats.": "",
"Exclude": "",
"Execute code for analysis": "",
"Executing `{{NAME}}`...": "",
"Executing **{{NAME}}**...": "",
"Expand": "",
"Experimental": "تجريبي",
"Explain": "",
@ -493,6 +501,7 @@
"Failed to create API Key.": "فشل في إنشاء مفتاح API.",
"Failed to fetch models": "",
"Failed to read clipboard contents": "فشل في قراءة محتويات الحافظة",
"Failed to save connections": "",
"Failed to save models configuration": "",
"Failed to update settings": "",
"Failed to upload file.": "",
@ -525,6 +534,7 @@
"Forge new paths": "",
"Form": "",
"Format your variables using brackets like this:": "",
"Forwards system user session credentials to authenticate": "",
"Frequency Penalty": "عقوبة التردد",
"Full Context Mode": "",
"Function": "",
@ -694,6 +704,8 @@
"Mirostat": "Mirostat",
"Mirostat Eta": "Mirostat Eta",
"Mirostat Tau": "Mirostat Tau",
"Mistral OCR": "",
"Mistral OCR API Key required.": "",
"Model": "",
"Model '{{modelName}}' has been successfully downloaded.": "تم تحميل النموذج '{{modelName}}' بنجاح",
"Model '{{modelTag}}' is already in queue for downloading.": "النموذج '{{modelTag}}' موجود بالفعل في قائمة الانتظار للتحميل",
@ -789,6 +801,7 @@
"OpenAI API Key is required.": "OpenAI API.مطلوب مفتاح ",
"OpenAI API settings updated": "",
"OpenAI URL/Key required.": "URL/مفتاح OpenAI.مطلوب عنوان ",
"openapi.json Path": "",
"or": "أو",
"Organize your users": "",
"Other": "آخر",
@ -820,6 +833,8 @@
"Please carefully review the following warnings:": "",
"Please do not close the settings page while loading the model.": "",
"Please enter a prompt": "",
"Please enter a valid path": "",
"Please enter a valid URL": "",
"Please fill in all fields.": "",
"Please select a model first.": "",
"Please select a model.": "",
@ -1042,6 +1057,7 @@
"Thinking...": "",
"This action cannot be undone. Do you wish to continue?": "",
"This channel was created on {{createdAt}}. This is the very beginning of the {{channelName}} channel.": "",
"This chat wont appear in history and your messages will not be saved.": "",
"This ensures that your valuable conversations are securely saved to your backend database. Thank you!": "وهذا يضمن حفظ محادثاتك القيمة بشكل آمن في قاعدة بياناتك الخلفية. شكرًا لك!",
"This is an experimental feature, it may not function as expected and is subject to change at any time.": "",
"This option controls how many tokens are preserved when refreshing the context. For example, if set to 2, the last 2 tokens of the conversation context will be retained. Preserving context can help maintain the continuity of a conversation, but it may reduce the ability to respond to new topics.": "",
@ -1089,6 +1105,7 @@
"Tool ID": "",
"Tool imported successfully": "",
"Tool Name": "",
"Tool Servers": "",
"Tool updated successfully": "",
"Tools": "",
"Tools Access": "",
@ -1158,7 +1175,7 @@
"Version": "إصدار",
"Version {{selectedVersion}} of {{totalVersions}}": "",
"View Replies": "",
"View Result from `{{NAME}}`": "",
"View Result from **{{NAME}}**": "",
"Visibility": "",
"Voice": "",
"Voice Input": "",
@ -1176,9 +1193,9 @@
"Webhook URL": "Webhook الرابط",
"WebUI Settings": "WebUI اعدادات",
"WebUI URL": "",
"WebUI will make requests to \"{{url}}\"": "",
"WebUI will make requests to \"{{url}}/api/chat\"": "",
"WebUI will make requests to \"{{url}}/chat/completions\"": "",
"WebUI will make requests to \"{{url}}/openapi.json\"": "",
"What are you trying to achieve?": "",
"What are you working on?": "",
"Whats New in": "ما هو الجديد",

File diff suppressed because it is too large Load diff

View file

@ -6,7 +6,7 @@
"(latest)": "(последна)",
"(Ollama)": "",
"{{ models }}": "{{ models }}",
"{{COUNT}} Available Tool Servers": "",
"{{COUNT}} Available Tools": "",
"{{COUNT}} hidden lines": "",
"{{COUNT}} Replies": "{{COUNT}} Отговори",
"{{user}}'s Chats": "{{user}}'s чатове",
@ -108,6 +108,7 @@
"Attribute for Username": "Атрибут за потребителско име",
"Audio": "Аудио",
"August": "Август",
"Auth": "",
"Authenticate": "Удостоверяване",
"Authentication": "Автентикация",
"Auto-Copy Response to Clipboard": "Автоматично копиране на отговор в клипборда",
@ -119,7 +120,7 @@
"AUTOMATIC1111 Base URL": "AUTOMATIC1111 Базов URL",
"AUTOMATIC1111 Base URL is required.": "AUTOMATIC1111 Базов URL е задължителен.",
"Available list": "Наличен списък",
"Available Tool Servers": "",
"Available Tools": "",
"available!": "наличен!",
"Awful": "Ужасно",
"Azure AI Speech": "Azure AI Реч",
@ -218,7 +219,10 @@
"Confirm your new password": "Потвърдете новата си парола",
"Connect to your own OpenAI compatible API endpoints.": "Свържете се със собствени крайни точки на API, съвместими с OpenAI.",
"Connect to your own OpenAPI compatible external tool servers.": "",
"Connection failed": "",
"Connection successful": "",
"Connections": "Връзки",
"Connections saved successfully": "",
"Constrains effort on reasoning for reasoning models. Only applicable to reasoning models from specific providers that support reasoning effort.": "",
"Contact Admin for WebUI Access": "Свържете се с администратор за достъп до WebUI",
"Content": "Съдържание",
@ -303,6 +307,7 @@
"Direct Connections": "Директни връзки",
"Direct Connections allow users to connect to their own OpenAI compatible API endpoints.": "Директните връзки позволяват на потребителите да се свързват със собствени OpenAI съвместими API крайни точки.",
"Direct Connections settings updated": "Настройките за директни връзки са актуализирани",
"Direct Tool Servers": "",
"Disabled": "Деактивирано",
"Discover a function": "Открийте функция",
"Discover a model": "Открийте модел",
@ -412,6 +417,7 @@
"Enter Kagi Search API Key": "Въведете API ключ за Kagi Search",
"Enter Key Behavior": "",
"Enter language codes": "Въведете кодове на езика",
"Enter Mistral API Key": "",
"Enter Model ID": "Въведете ID на модела",
"Enter model tag (e.g. {{modelTag}})": "Въведете таг на модел (напр. {{modelTag}})",
"Enter Mojeek Search API Key": "Въведете API ключ за Mojeek Search",
@ -436,6 +442,7 @@
"Enter server port": "Въведете порт на сървъра",
"Enter stop sequence": "Въведете стоп последователност",
"Enter system prompt": "Въведете системен промпт",
"Enter system prompt here": "",
"Enter Tavily API Key": "Въведете API ключ за Tavily",
"Enter the public URL of your WebUI. This URL will be used to generate links in the notifications.": "Въведете публичния URL адрес на вашия WebUI. Този URL адрес ще бъде използван за генериране на връзки в известията.",
"Enter Tika Server URL": "Въведете URL адрес на Tika сървър",
@ -449,6 +456,7 @@
"Enter Your Email": "Въведете имейл",
"Enter Your Full Name": "Въведете вашето пълно име",
"Enter your message": "Въведете съобщението си",
"Enter your name": "",
"Enter your new password": "Въведете новата си парола",
"Enter Your Password": "Въведете вашата парола",
"Enter Your Role": "Въведете вашата роля",
@ -468,7 +476,7 @@
"Exceeded the number of seats in your license. Please contact support to increase the number of seats.": "",
"Exclude": "Изключи",
"Execute code for analysis": "Изпълнете код за анализ",
"Executing `{{NAME}}`...": "",
"Executing **{{NAME}}**...": "",
"Expand": "",
"Experimental": "Експериментално",
"Explain": "",
@ -493,6 +501,7 @@
"Failed to create API Key.": "Неуспешно създаване на API ключ.",
"Failed to fetch models": "Неуспешно извличане на модели",
"Failed to read clipboard contents": "Грешка при четене на съдържанието от клипборда",
"Failed to save connections": "",
"Failed to save models configuration": "Неуспешно запазване на конфигурацията на моделите",
"Failed to update settings": "Неуспешно актуализиране на настройките",
"Failed to upload file.": "Неуспешно качване на файл.",
@ -525,6 +534,7 @@
"Forge new paths": "Изковете нови пътища",
"Form": "Форма",
"Format your variables using brackets like this:": "Форматирайте вашите променливи, използвайки скоби като това:",
"Forwards system user session credentials to authenticate": "",
"Frequency Penalty": "Наказание за честота",
"Full Context Mode": "Режим на пълен контекст",
"Function": "Функция",
@ -694,6 +704,8 @@
"Mirostat": "Mirostat",
"Mirostat Eta": "Mirostat Eta",
"Mirostat Tau": "Mirostat Tau",
"Mistral OCR": "",
"Mistral OCR API Key required.": "",
"Model": "Модел",
"Model '{{modelName}}' has been successfully downloaded.": "Моделът '{{modelName}}' беше успешно свален.",
"Model '{{modelTag}}' is already in queue for downloading.": "Моделът '{{modelTag}}' е вече в очакване за сваляне.",
@ -789,6 +801,7 @@
"OpenAI API Key is required.": "OpenAI API ключ е задължителен.",
"OpenAI API settings updated": "Настройките на OpenAI API са актуализирани",
"OpenAI URL/Key required.": "OpenAI URL/Key е задължителен.",
"openapi.json Path": "",
"or": "или",
"Organize your users": "Организирайте вашите потребители",
"Other": "Друго",
@ -820,6 +833,8 @@
"Please carefully review the following warnings:": "Моля, внимателно прегледайте следните предупреждения:",
"Please do not close the settings page while loading the model.": "Моля, не затваряйте страницата с настройки, докато моделът се зарежда.",
"Please enter a prompt": "Моля, въведете промпт",
"Please enter a valid path": "",
"Please enter a valid URL": "",
"Please fill in all fields.": "Моля, попълнете всички полета.",
"Please select a model first.": "Моля, първо изберете модел.",
"Please select a model.": "Моля, изберете модел.",
@ -1042,6 +1057,7 @@
"Thinking...": "Мисля...",
"This action cannot be undone. Do you wish to continue?": "Това действие не може да бъде отменено. Желаете ли да продължите?",
"This channel was created on {{createdAt}}. This is the very beginning of the {{channelName}} channel.": "",
"This chat wont appear in history and your messages will not be saved.": "",
"This ensures that your valuable conversations are securely saved to your backend database. Thank you!": "Това гарантира, че ценните ви разговори се запазват сигурно във вашата бекенд база данни. Благодарим ви!",
"This is an experimental feature, it may not function as expected and is subject to change at any time.": "Това е експериментална функция, може да не работи според очакванията и подлежи на промяна по всяко време.",
"This option controls how many tokens are preserved when refreshing the context. For example, if set to 2, the last 2 tokens of the conversation context will be retained. Preserving context can help maintain the continuity of a conversation, but it may reduce the ability to respond to new topics.": "",
@ -1089,6 +1105,7 @@
"Tool ID": "ID на инструмента",
"Tool imported successfully": "Инструментът е импортиран успешно",
"Tool Name": "Име на инструмента",
"Tool Servers": "",
"Tool updated successfully": "Инструментът е актуализиран успешно",
"Tools": "Инструменти",
"Tools Access": "Достъп до инструменти",
@ -1158,7 +1175,7 @@
"Version": "Версия",
"Version {{selectedVersion}} of {{totalVersions}}": "Версия {{selectedVersion}} от {{totalVersions}}",
"View Replies": "Преглед на отговорите",
"View Result from `{{NAME}}`": "",
"View Result from **{{NAME}}**": "",
"Visibility": "Видимост",
"Voice": "Глас",
"Voice Input": "Гласов вход",
@ -1176,9 +1193,9 @@
"Webhook URL": "Уебхук URL",
"WebUI Settings": "WebUI Настройки",
"WebUI URL": "WebUI URL",
"WebUI will make requests to \"{{url}}\"": "",
"WebUI will make requests to \"{{url}}/api/chat\"": "WebUI ще прави заявки към \"{{url}}/api/chat\"",
"WebUI will make requests to \"{{url}}/chat/completions\"": "WebUI ще прави заявки към \"{{url}}/chat/completions\"",
"WebUI will make requests to \"{{url}}/openapi.json\"": "",
"What are you trying to achieve?": "Какво се опитвате да постигнете?",
"What are you working on?": "Върху какво работите?",
"Whats New in": "",

View file

@ -6,7 +6,7 @@
"(latest)": "(সর্বশেষ)",
"(Ollama)": "",
"{{ models }}": "{{ মডেল}}",
"{{COUNT}} Available Tool Servers": "",
"{{COUNT}} Available Tools": "",
"{{COUNT}} hidden lines": "",
"{{COUNT}} Replies": "",
"{{user}}'s Chats": "{{user}}র চ্যাটস",
@ -108,6 +108,7 @@
"Attribute for Username": "",
"Audio": "অডিও",
"August": "আগস্ট",
"Auth": "",
"Authenticate": "",
"Authentication": "",
"Auto-Copy Response to Clipboard": "রেসপন্সগুলো স্বয়ংক্রিভাবে ক্লিপবোর্ডে কপি হবে",
@ -119,7 +120,7 @@
"AUTOMATIC1111 Base URL": "AUTOMATIC1111 বেজ ইউআরএল",
"AUTOMATIC1111 Base URL is required.": "AUTOMATIC1111 বেজ ইউআরএল আবশ্যক",
"Available list": "",
"Available Tool Servers": "",
"Available Tools": "",
"available!": "উপলব্ধ!",
"Awful": "",
"Azure AI Speech": "",
@ -218,7 +219,10 @@
"Confirm your new password": "",
"Connect to your own OpenAI compatible API endpoints.": "",
"Connect to your own OpenAPI compatible external tool servers.": "",
"Connection failed": "",
"Connection successful": "",
"Connections": "কানেকশনগুলো",
"Connections saved successfully": "",
"Constrains effort on reasoning for reasoning models. Only applicable to reasoning models from specific providers that support reasoning effort.": "",
"Contact Admin for WebUI Access": "",
"Content": "বিষয়বস্তু",
@ -303,6 +307,7 @@
"Direct Connections": "",
"Direct Connections allow users to connect to their own OpenAI compatible API endpoints.": "",
"Direct Connections settings updated": "",
"Direct Tool Servers": "",
"Disabled": "",
"Discover a function": "",
"Discover a model": "একটি মডেল আবিষ্কার করুন",
@ -412,6 +417,7 @@
"Enter Kagi Search API Key": "",
"Enter Key Behavior": "",
"Enter language codes": "ল্যাঙ্গুয়েজ কোড লিখুন",
"Enter Mistral API Key": "",
"Enter Model ID": "",
"Enter model tag (e.g. {{modelTag}})": "মডেল ট্যাগ লিখুন (e.g. {{modelTag}})",
"Enter Mojeek Search API Key": "",
@ -436,6 +442,7 @@
"Enter server port": "",
"Enter stop sequence": "স্টপ সিকোয়েন্স লিখুন",
"Enter system prompt": "",
"Enter system prompt here": "",
"Enter Tavily API Key": "",
"Enter the public URL of your WebUI. This URL will be used to generate links in the notifications.": "",
"Enter Tika Server URL": "",
@ -449,6 +456,7 @@
"Enter Your Email": "আপনার ইমেইল লিখুন",
"Enter Your Full Name": "আপনার পূর্ণ নাম লিখুন",
"Enter your message": "",
"Enter your name": "",
"Enter your new password": "",
"Enter Your Password": "আপনার পাসওয়ার্ড লিখুন",
"Enter Your Role": "আপনার রোল লিখুন",
@ -468,7 +476,7 @@
"Exceeded the number of seats in your license. Please contact support to increase the number of seats.": "",
"Exclude": "",
"Execute code for analysis": "",
"Executing `{{NAME}}`...": "",
"Executing **{{NAME}}**...": "",
"Expand": "",
"Experimental": "পরিক্ষামূলক",
"Explain": "",
@ -493,6 +501,7 @@
"Failed to create API Key.": "API Key তৈরি করা যায়নি।",
"Failed to fetch models": "",
"Failed to read clipboard contents": "ক্লিপবোর্ডের বিষয়বস্তু পড়া সম্ভব হয়নি",
"Failed to save connections": "",
"Failed to save models configuration": "",
"Failed to update settings": "",
"Failed to upload file.": "",
@ -525,6 +534,7 @@
"Forge new paths": "",
"Form": "",
"Format your variables using brackets like this:": "",
"Forwards system user session credentials to authenticate": "",
"Frequency Penalty": "ফ্রিকোয়েন্সি পেনাল্টি",
"Full Context Mode": "",
"Function": "",
@ -694,6 +704,8 @@
"Mirostat": "Mirostat",
"Mirostat Eta": "Mirostat Eta",
"Mirostat Tau": "Mirostat Tau",
"Mistral OCR": "",
"Mistral OCR API Key required.": "",
"Model": "",
"Model '{{modelName}}' has been successfully downloaded.": "'{{modelName}}' মডেল সফলভাবে ডাউনলোড হয়েছে।",
"Model '{{modelTag}}' is already in queue for downloading.": "{{modelTag}} ডাউনলোডের জন্য আগে থেকেই অপেক্ষমান আছে।",
@ -789,6 +801,7 @@
"OpenAI API Key is required.": "OpenAI API কোড আবশ্যক",
"OpenAI API settings updated": "",
"OpenAI URL/Key required.": "OpenAI URL/Key আবশ্যক",
"openapi.json Path": "",
"or": "অথবা",
"Organize your users": "",
"Other": "অন্যান্য",
@ -820,6 +833,8 @@
"Please carefully review the following warnings:": "",
"Please do not close the settings page while loading the model.": "",
"Please enter a prompt": "",
"Please enter a valid path": "",
"Please enter a valid URL": "",
"Please fill in all fields.": "",
"Please select a model first.": "",
"Please select a model.": "",
@ -1042,6 +1057,7 @@
"Thinking...": "",
"This action cannot be undone. Do you wish to continue?": "",
"This channel was created on {{createdAt}}. This is the very beginning of the {{channelName}} channel.": "",
"This chat wont appear in history and your messages will not be saved.": "",
"This ensures that your valuable conversations are securely saved to your backend database. Thank you!": "এটা নিশ্চিত করে যে, আপনার গুরুত্বপূর্ণ আলোচনা নিরাপদে আপনার ব্যাকএন্ড ডেটাবেজে সংরক্ষিত আছে। ধন্যবাদ!",
"This is an experimental feature, it may not function as expected and is subject to change at any time.": "",
"This option controls how many tokens are preserved when refreshing the context. For example, if set to 2, the last 2 tokens of the conversation context will be retained. Preserving context can help maintain the continuity of a conversation, but it may reduce the ability to respond to new topics.": "",
@ -1089,6 +1105,7 @@
"Tool ID": "",
"Tool imported successfully": "",
"Tool Name": "",
"Tool Servers": "",
"Tool updated successfully": "",
"Tools": "",
"Tools Access": "",
@ -1158,7 +1175,7 @@
"Version": "ভার্সন",
"Version {{selectedVersion}} of {{totalVersions}}": "",
"View Replies": "",
"View Result from `{{NAME}}`": "",
"View Result from **{{NAME}}**": "",
"Visibility": "",
"Voice": "",
"Voice Input": "",
@ -1176,9 +1193,9 @@
"Webhook URL": "ওয়েবহুক URL",
"WebUI Settings": "WebUI সেটিংসমূহ",
"WebUI URL": "",
"WebUI will make requests to \"{{url}}\"": "",
"WebUI will make requests to \"{{url}}/api/chat\"": "",
"WebUI will make requests to \"{{url}}/chat/completions\"": "",
"WebUI will make requests to \"{{url}}/openapi.json\"": "",
"What are you trying to achieve?": "",
"What are you working on?": "",
"Whats New in": "এতে নতুন কী",

File diff suppressed because it is too large Load diff

View file

@ -6,7 +6,7 @@
"(latest)": "(últim)",
"(Ollama)": "(Ollama)",
"{{ models }}": "{{ models }}",
"{{COUNT}} Available Tool Servers": "{{COUNT}} Servidors d'eines disponibles",
"{{COUNT}} Available Tools": "",
"{{COUNT}} hidden lines": "{{COUNT}} línies ocultes",
"{{COUNT}} Replies": "{{COUNT}} respostes",
"{{user}}'s Chats": "Els xats de {{user}}",
@ -108,6 +108,7 @@
"Attribute for Username": "Atribut per al Nom d'usuari",
"Audio": "Àudio",
"August": "Agost",
"Auth": "",
"Authenticate": "Autenticar",
"Authentication": "Autenticació",
"Auto-Copy Response to Clipboard": "Copiar la resposta automàticament al porta-retalls",
@ -119,7 +120,7 @@
"AUTOMATIC1111 Base URL": "URL Base d'AUTOMATIC1111",
"AUTOMATIC1111 Base URL is required.": "Es requereix l'URL Base d'AUTOMATIC1111.",
"Available list": "Llista de disponibles",
"Available Tool Servers": "Servidors d'eines disponibles",
"Available Tools": "",
"available!": "disponible!",
"Awful": "Terrible",
"Azure AI Speech": "Azure AI Speech",
@ -218,7 +219,10 @@
"Confirm your new password": "Confirma la teva nova contrasenya",
"Connect to your own OpenAI compatible API endpoints.": "Connecta als teus propis punts de connexió de l'API compatible amb OpenAI",
"Connect to your own OpenAPI compatible external tool servers.": "Connecta als teus propis servidors d'eines externs compatibles amb OpenAPI",
"Connection failed": "",
"Connection successful": "",
"Connections": "Connexions",
"Connections saved successfully": "",
"Constrains effort on reasoning for reasoning models. Only applicable to reasoning models from specific providers that support reasoning effort.": "Restringeix l'esforç de raonament dels models de raonament. Només aplicable a models de raonament de proveïdors específics que donen suport a l'esforç de raonament.",
"Contact Admin for WebUI Access": "Posat en contacte amb l'administrador per accedir a WebUI",
"Content": "Contingut",
@ -303,6 +307,7 @@
"Direct Connections": "Connexions directes",
"Direct Connections allow users to connect to their own OpenAI compatible API endpoints.": "Les connexions directes permeten als usuaris connectar-se als seus propis endpoints d'API compatibles amb OpenAI.",
"Direct Connections settings updated": "Configuració de les connexions directes actualitzada",
"Direct Tool Servers": "",
"Disabled": "Deshabilitat",
"Discover a function": "Descobrir una funció",
"Discover a model": "Descobrir un model",
@ -412,6 +417,7 @@
"Enter Kagi Search API Key": "Introdueix la clau API de Kagi Search",
"Enter Key Behavior": "Introdueix el comportament de clau",
"Enter language codes": "Introdueix els codis de llenguatge",
"Enter Mistral API Key": "",
"Enter Model ID": "Introdueix l'identificador del model",
"Enter model tag (e.g. {{modelTag}})": "Introdueix l'etiqueta del model (p. ex. {{modelTag}})",
"Enter Mojeek Search API Key": "Introdueix la clau API de Mojeek Search",
@ -436,6 +442,7 @@
"Enter server port": "Introdueix el port del servidor",
"Enter stop sequence": "Introdueix la seqüència de parada",
"Enter system prompt": "Introdueix la indicació de sistema",
"Enter system prompt here": "",
"Enter Tavily API Key": "Introdueix la clau API de Tavily",
"Enter the public URL of your WebUI. This URL will be used to generate links in the notifications.": "Entra la URL pública de WebUI. Aquesta URL s'utilitzarà per generar els enllaços en les notificacions.",
"Enter Tika Server URL": "Introdueix l'URL del servidor Tika",
@ -449,6 +456,7 @@
"Enter Your Email": "Introdueix el teu correu electrònic",
"Enter Your Full Name": "Introdueix el teu nom complet",
"Enter your message": "Introdueix el teu missatge",
"Enter your name": "",
"Enter your new password": "Introdueix la teva nova contrasenya",
"Enter Your Password": "Introdueix la teva contrasenya",
"Enter Your Role": "Introdueix el teu rol",
@ -468,7 +476,7 @@
"Exceeded the number of seats in your license. Please contact support to increase the number of seats.": "S'ha superat el nombre de places a la vostra llicència. Poseu-vos en contacte amb el servei d'assistència per augmentar el nombre de places.",
"Exclude": "Excloure",
"Execute code for analysis": "Executar el codi per analitzar-lo",
"Executing `{{NAME}}`...": "Executant `{{NAME}}`...",
"Executing **{{NAME}}**...": "",
"Expand": "Expandir",
"Experimental": "Experimental",
"Explain": "Explicar",
@ -493,6 +501,7 @@
"Failed to create API Key.": "No s'ha pogut crear la clau API.",
"Failed to fetch models": "No s'han pogut obtenir els models",
"Failed to read clipboard contents": "No s'ha pogut llegir el contingut del porta-retalls",
"Failed to save connections": "",
"Failed to save models configuration": "No s'ha pogut desar la configuració dels models",
"Failed to update settings": "No s'han pogut actualitzar les preferències",
"Failed to upload file.": "No s'ha pogut pujar l'arxiu.",
@ -525,6 +534,7 @@
"Forge new paths": "Crea nous camins",
"Form": "Formulari",
"Format your variables using brackets like this:": "Formata les teves variables utilitzant claudàtors així:",
"Forwards system user session credentials to authenticate": "",
"Frequency Penalty": "Penalització per freqüència",
"Full Context Mode": "Mode de context complert",
"Function": "Funció",
@ -694,6 +704,8 @@
"Mirostat": "Mirostat",
"Mirostat Eta": "Eta de Mirostat",
"Mirostat Tau": "Tau de Mirostat",
"Mistral OCR": "",
"Mistral OCR API Key required.": "",
"Model": "Model",
"Model '{{modelName}}' has been successfully downloaded.": "El model '{{modelName}}' s'ha descarregat correctament.",
"Model '{{modelTag}}' is already in queue for downloading.": "El model '{{modelTag}}' ja està en cua per ser descarregat.",
@ -789,6 +801,7 @@
"OpenAI API Key is required.": "Es requereix la clau API d'OpenAI.",
"OpenAI API settings updated": "Configuració de l'API d'OpenAI actualitzada",
"OpenAI URL/Key required.": "URL/Clau d'OpenAI requerides.",
"openapi.json Path": "",
"or": "o",
"Organize your users": "Organitza els teus usuaris",
"Other": "Altres",
@ -820,6 +833,8 @@
"Please carefully review the following warnings:": "Si us plau, revisa els següents avisos amb cura:",
"Please do not close the settings page while loading the model.": "No tanquis la pàgina de configuració mentre carregues el model.",
"Please enter a prompt": "Si us plau, entra una indicació",
"Please enter a valid path": "",
"Please enter a valid URL": "",
"Please fill in all fields.": "Emplena tots els camps, si us plau.",
"Please select a model first.": "Si us plau, selecciona un model primer",
"Please select a model.": "Si us plau, selecciona un model.",
@ -1042,6 +1057,7 @@
"Thinking...": "Pensant...",
"This action cannot be undone. Do you wish to continue?": "Aquesta acció no es pot desfer. Vols continuar?",
"This channel was created on {{createdAt}}. This is the very beginning of the {{channelName}} channel.": "Aquest canal es va crear el dia {{createdAt}}. Aquest és el començament del canal {{channelName}}.",
"This chat wont appear in history and your messages will not be saved.": "",
"This ensures that your valuable conversations are securely saved to your backend database. Thank you!": "Això assegura que les teves converses valuoses queden desades de manera segura a la teva base de dades. Gràcies!",
"This is an experimental feature, it may not function as expected and is subject to change at any time.": "Aquesta és una funció experimental, és possible que no funcioni com s'espera i està subjecta a canvis en qualsevol moment.",
"This option controls how many tokens are preserved when refreshing the context. For example, if set to 2, the last 2 tokens of the conversation context will be retained. Preserving context can help maintain the continuity of a conversation, but it may reduce the ability to respond to new topics.": "Aquesta opció controla quants tokens es conserven en actualitzar el context. Per exemple, si s'estableix en 2, es conservaran els darrers 2 tokens del context de conversa. Preservar el context pot ajudar a mantenir la continuïtat d'una conversa, però pot reduir la capacitat de respondre a nous temes.",
@ -1089,6 +1105,7 @@
"Tool ID": "ID de l'eina",
"Tool imported successfully": "Eina importada correctament",
"Tool Name": "Nom de l'eina",
"Tool Servers": "",
"Tool updated successfully": "Eina actualitzada correctament",
"Tools": "Eines",
"Tools Access": "Accés a les eines",
@ -1158,7 +1175,7 @@
"Version": "Versió",
"Version {{selectedVersion}} of {{totalVersions}}": "Versió {{selectedVersion}} de {{totalVersions}}",
"View Replies": "Veure les respostes",
"View Result from `{{NAME}}`": "Veure el resultat de `{{NAME}}`",
"View Result from **{{NAME}}**": "",
"Visibility": "Visibilitat",
"Voice": "Veu",
"Voice Input": "Entrada de veu",
@ -1176,9 +1193,9 @@
"Webhook URL": "URL del webhook",
"WebUI Settings": "Preferències de WebUI",
"WebUI URL": "URL de WebUI",
"WebUI will make requests to \"{{url}}\"": "",
"WebUI will make requests to \"{{url}}/api/chat\"": "WebUI farà peticions a \"{{url}}/api/chat\"",
"WebUI will make requests to \"{{url}}/chat/completions\"": "WebUI farà peticions a \"{{url}}/chat/completions\"",
"WebUI will make requests to \"{{url}}/openapi.json\"": "WebUI farà peticions a \"{{url}}/openapi.json\"",
"What are you trying to achieve?": "Què intentes aconseguir?",
"What are you working on?": "En què estàs treballant?",
"Whats New in": "Què hi ha de nou a",

View file

@ -6,7 +6,7 @@
"(latest)": "",
"(Ollama)": "",
"{{ models }}": "",
"{{COUNT}} Available Tool Servers": "",
"{{COUNT}} Available Tools": "",
"{{COUNT}} hidden lines": "",
"{{COUNT}} Replies": "",
"{{user}}'s Chats": "",
@ -108,6 +108,7 @@
"Attribute for Username": "",
"Audio": "Audio",
"August": "",
"Auth": "",
"Authenticate": "",
"Authentication": "",
"Auto-Copy Response to Clipboard": "Awtomatikong kopya sa tubag sa clipboard",
@ -119,7 +120,7 @@
"AUTOMATIC1111 Base URL": "Base URL AUTOMATIC1111",
"AUTOMATIC1111 Base URL is required.": "Ang AUTOMATIC1111 base URL gikinahanglan.",
"Available list": "",
"Available Tool Servers": "",
"Available Tools": "",
"available!": "magamit!",
"Awful": "",
"Azure AI Speech": "",
@ -218,7 +219,10 @@
"Confirm your new password": "",
"Connect to your own OpenAI compatible API endpoints.": "",
"Connect to your own OpenAPI compatible external tool servers.": "",
"Connection failed": "",
"Connection successful": "",
"Connections": "Mga koneksyon",
"Connections saved successfully": "",
"Constrains effort on reasoning for reasoning models. Only applicable to reasoning models from specific providers that support reasoning effort.": "",
"Contact Admin for WebUI Access": "",
"Content": "Kontento",
@ -303,6 +307,7 @@
"Direct Connections": "",
"Direct Connections allow users to connect to their own OpenAI compatible API endpoints.": "",
"Direct Connections settings updated": "",
"Direct Tool Servers": "",
"Disabled": "",
"Discover a function": "",
"Discover a model": "",
@ -412,6 +417,7 @@
"Enter Kagi Search API Key": "",
"Enter Key Behavior": "",
"Enter language codes": "",
"Enter Mistral API Key": "",
"Enter Model ID": "",
"Enter model tag (e.g. {{modelTag}})": "Pagsulod sa template tag (e.g. {{modelTag}})",
"Enter Mojeek Search API Key": "",
@ -436,6 +442,7 @@
"Enter server port": "",
"Enter stop sequence": "Pagsulod sa katapusan nga han-ay",
"Enter system prompt": "",
"Enter system prompt here": "",
"Enter Tavily API Key": "",
"Enter the public URL of your WebUI. This URL will be used to generate links in the notifications.": "",
"Enter Tika Server URL": "",
@ -449,6 +456,7 @@
"Enter Your Email": "Pagsulod sa imong e-mail address",
"Enter Your Full Name": "Ibutang ang imong tibuok nga ngalan",
"Enter your message": "",
"Enter your name": "",
"Enter your new password": "",
"Enter Your Password": "Ibutang ang imong password",
"Enter Your Role": "",
@ -468,7 +476,7 @@
"Exceeded the number of seats in your license. Please contact support to increase the number of seats.": "",
"Exclude": "",
"Execute code for analysis": "",
"Executing `{{NAME}}`...": "",
"Executing **{{NAME}}**...": "",
"Expand": "",
"Experimental": "Eksperimento",
"Explain": "",
@ -493,6 +501,7 @@
"Failed to create API Key.": "",
"Failed to fetch models": "",
"Failed to read clipboard contents": "Napakyas sa pagbasa sa sulod sa clipboard",
"Failed to save connections": "",
"Failed to save models configuration": "",
"Failed to update settings": "",
"Failed to upload file.": "",
@ -525,6 +534,7 @@
"Forge new paths": "",
"Form": "",
"Format your variables using brackets like this:": "",
"Forwards system user session credentials to authenticate": "",
"Frequency Penalty": "",
"Full Context Mode": "",
"Function": "",
@ -694,6 +704,8 @@
"Mirostat": "Mirostat",
"Mirostat Eta": "Mirostat Eta",
"Mirostat Tau": "Mirostat Tau",
"Mistral OCR": "",
"Mistral OCR API Key required.": "",
"Model": "",
"Model '{{modelName}}' has been successfully downloaded.": "Ang modelo'{{modelName}}' malampuson nga na-download.",
"Model '{{modelTag}}' is already in queue for downloading.": "Ang modelo'{{modelTag}}' naa na sa pila para ma-download.",
@ -789,6 +801,7 @@
"OpenAI API Key is required.": "Ang yawe sa OpenAI API gikinahanglan.",
"OpenAI API settings updated": "",
"OpenAI URL/Key required.": "",
"openapi.json Path": "",
"or": "O",
"Organize your users": "",
"Other": "",
@ -820,6 +833,8 @@
"Please carefully review the following warnings:": "",
"Please do not close the settings page while loading the model.": "",
"Please enter a prompt": "",
"Please enter a valid path": "",
"Please enter a valid URL": "",
"Please fill in all fields.": "",
"Please select a model first.": "",
"Please select a model.": "",
@ -1042,6 +1057,7 @@
"Thinking...": "",
"This action cannot be undone. Do you wish to continue?": "",
"This channel was created on {{createdAt}}. This is the very beginning of the {{channelName}} channel.": "",
"This chat wont appear in history and your messages will not be saved.": "",
"This ensures that your valuable conversations are securely saved to your backend database. Thank you!": "Kini nagsiguro nga ang imong bililhon nga mga panag-istoryahanay luwas nga natipig sa imong backend database. ",
"This is an experimental feature, it may not function as expected and is subject to change at any time.": "",
"This option controls how many tokens are preserved when refreshing the context. For example, if set to 2, the last 2 tokens of the conversation context will be retained. Preserving context can help maintain the continuity of a conversation, but it may reduce the ability to respond to new topics.": "",
@ -1089,6 +1105,7 @@
"Tool ID": "",
"Tool imported successfully": "",
"Tool Name": "",
"Tool Servers": "",
"Tool updated successfully": "",
"Tools": "",
"Tools Access": "",
@ -1158,7 +1175,7 @@
"Version": "Bersyon",
"Version {{selectedVersion}} of {{totalVersions}}": "",
"View Replies": "",
"View Result from `{{NAME}}`": "",
"View Result from **{{NAME}}**": "",
"Visibility": "",
"Voice": "",
"Voice Input": "",
@ -1176,9 +1193,9 @@
"Webhook URL": "",
"WebUI Settings": "Mga Setting sa WebUI",
"WebUI URL": "",
"WebUI will make requests to \"{{url}}\"": "",
"WebUI will make requests to \"{{url}}/api/chat\"": "",
"WebUI will make requests to \"{{url}}/chat/completions\"": "",
"WebUI will make requests to \"{{url}}/openapi.json\"": "",
"What are you trying to achieve?": "",
"What are you working on?": "",
"Whats New in": "Unsay bag-o sa",

View file

@ -6,7 +6,7 @@
"(latest)": "Nejnovější",
"(Ollama)": "",
"{{ models }}": "{{ models }}",
"{{COUNT}} Available Tool Servers": "",
"{{COUNT}} Available Tools": "",
"{{COUNT}} hidden lines": "",
"{{COUNT}} Replies": "",
"{{user}}'s Chats": "{{user}}'s konverzace",
@ -108,6 +108,7 @@
"Attribute for Username": "",
"Audio": "Zvuk",
"August": "Srpen",
"Auth": "",
"Authenticate": "Autentikace",
"Authentication": "",
"Auto-Copy Response to Clipboard": "Automatické kopírování odpovědi do schránky",
@ -119,7 +120,7 @@
"AUTOMATIC1111 Base URL": "Výchozí URL pro AUTOMATIC1111",
"AUTOMATIC1111 Base URL is required.": "Vyžaduje se základní URL pro AUTOMATIC1111.",
"Available list": "Dostupný seznam",
"Available Tool Servers": "",
"Available Tools": "",
"available!": "k dispozici!",
"Awful": "",
"Azure AI Speech": "Azure AI syntéza řeči",
@ -218,7 +219,10 @@
"Confirm your new password": "",
"Connect to your own OpenAI compatible API endpoints.": "",
"Connect to your own OpenAPI compatible external tool servers.": "",
"Connection failed": "",
"Connection successful": "",
"Connections": "Připojení",
"Connections saved successfully": "",
"Constrains effort on reasoning for reasoning models. Only applicable to reasoning models from specific providers that support reasoning effort.": "",
"Contact Admin for WebUI Access": "Kontaktujte administrátora pro přístup k webovému rozhraní.",
"Content": "Obsah",
@ -303,6 +307,7 @@
"Direct Connections": "",
"Direct Connections allow users to connect to their own OpenAI compatible API endpoints.": "",
"Direct Connections settings updated": "",
"Direct Tool Servers": "",
"Disabled": "Zakázáno",
"Discover a function": "Objevit funkci",
"Discover a model": "Objevte model",
@ -412,6 +417,7 @@
"Enter Kagi Search API Key": "",
"Enter Key Behavior": "",
"Enter language codes": "Zadejte kódy jazyků",
"Enter Mistral API Key": "",
"Enter Model ID": "Zadejte ID modelu",
"Enter model tag (e.g. {{modelTag}})": "Zadejte označení modelu (např. {{modelTag}})",
"Enter Mojeek Search API Key": "",
@ -436,6 +442,7 @@
"Enter server port": "",
"Enter stop sequence": "Zadejte ukončovací sekvenci",
"Enter system prompt": "Vložte systémový prompt",
"Enter system prompt here": "",
"Enter Tavily API Key": "Zadejte API klíč Tavily",
"Enter the public URL of your WebUI. This URL will be used to generate links in the notifications.": "",
"Enter Tika Server URL": "Zadejte URL serveru Tika",
@ -449,6 +456,7 @@
"Enter Your Email": "Zadejte svůj email",
"Enter Your Full Name": "Zadejte své plné jméno",
"Enter your message": "Zadejte svou zprávu",
"Enter your name": "",
"Enter your new password": "",
"Enter Your Password": "Zadejte své heslo",
"Enter Your Role": "Zadejte svou roli",
@ -468,7 +476,7 @@
"Exceeded the number of seats in your license. Please contact support to increase the number of seats.": "",
"Exclude": "Vyloučit",
"Execute code for analysis": "",
"Executing `{{NAME}}`...": "",
"Executing **{{NAME}}**...": "",
"Expand": "",
"Experimental": "Experimentální",
"Explain": "",
@ -493,6 +501,7 @@
"Failed to create API Key.": "Nepodařilo se vytvořit API klíč.",
"Failed to fetch models": "",
"Failed to read clipboard contents": "Nepodařilo se přečíst obsah schránky",
"Failed to save connections": "",
"Failed to save models configuration": "",
"Failed to update settings": "Nepodařilo se aktualizovat nastavení",
"Failed to upload file.": "Nepodařilo se nahrát soubor.",
@ -525,6 +534,7 @@
"Forge new paths": "",
"Form": "Formulář",
"Format your variables using brackets like this:": "Formátujte své proměnné pomocí závorek takto:",
"Forwards system user session credentials to authenticate": "",
"Frequency Penalty": "Penalizace frekvence",
"Full Context Mode": "",
"Function": "Funkce",
@ -694,6 +704,8 @@
"Mirostat": "Mirostat",
"Mirostat Eta": "Mirostat Eta",
"Mirostat Tau": "Mirostat Tau",
"Mistral OCR": "",
"Mistral OCR API Key required.": "",
"Model": "Model",
"Model '{{modelName}}' has been successfully downloaded.": "Model „{{modelName}}“ byl úspěšně stažen.",
"Model '{{modelTag}}' is already in queue for downloading.": "Model '{{modelTag}}' je již zařazen do fronty pro stahování.",
@ -789,6 +801,7 @@
"OpenAI API Key is required.": "Je vyžadován klíč OpenAI API.",
"OpenAI API settings updated": "",
"OpenAI URL/Key required.": "Je vyžadován odkaz/adresa URL nebo klíč OpenAI.",
"openapi.json Path": "",
"or": "nebo",
"Organize your users": "",
"Other": "Jiné",
@ -820,6 +833,8 @@
"Please carefully review the following warnings:": "Prosím, pečlivě si přečtěte následující upozornění:",
"Please do not close the settings page while loading the model.": "",
"Please enter a prompt": "Prosím, zadejte zadání.",
"Please enter a valid path": "",
"Please enter a valid URL": "",
"Please fill in all fields.": "Prosím, vyplňte všechna pole.",
"Please select a model first.": "",
"Please select a model.": "",
@ -1042,6 +1057,7 @@
"Thinking...": "Přemýšlím...",
"This action cannot be undone. Do you wish to continue?": "Tuto akci nelze vrátit zpět. Přejete si pokračovat?",
"This channel was created on {{createdAt}}. This is the very beginning of the {{channelName}} channel.": "",
"This chat wont appear in history and your messages will not be saved.": "",
"This ensures that your valuable conversations are securely saved to your backend database. Thank you!": "To zajišťuje, že vaše cenné konverzace jsou bezpečně uloženy ve vaší backendové databázi. Děkujeme!",
"This is an experimental feature, it may not function as expected and is subject to change at any time.": "Jedná se o experimentální funkci, nemusí fungovat podle očekávání a může být kdykoliv změněna.",
"This option controls how many tokens are preserved when refreshing the context. For example, if set to 2, the last 2 tokens of the conversation context will be retained. Preserving context can help maintain the continuity of a conversation, but it may reduce the ability to respond to new topics.": "",
@ -1089,6 +1105,7 @@
"Tool ID": "ID nástroje",
"Tool imported successfully": "Nástroj byl úspěšně importován",
"Tool Name": "",
"Tool Servers": "",
"Tool updated successfully": "Nástroj byl úspěšně aktualizován.",
"Tools": "Nástroje",
"Tools Access": "",
@ -1158,7 +1175,7 @@
"Version": "Verze",
"Version {{selectedVersion}} of {{totalVersions}}": "Verze {{selectedVersion}} z {{totalVersions}}",
"View Replies": "",
"View Result from `{{NAME}}`": "",
"View Result from **{{NAME}}**": "",
"Visibility": "Viditelnost",
"Voice": "Hlas",
"Voice Input": "Hlasový vstup",
@ -1176,9 +1193,9 @@
"Webhook URL": "Webhook URL",
"WebUI Settings": "Nastavení WebUI",
"WebUI URL": "",
"WebUI will make requests to \"{{url}}\"": "",
"WebUI will make requests to \"{{url}}/api/chat\"": "",
"WebUI will make requests to \"{{url}}/chat/completions\"": "",
"WebUI will make requests to \"{{url}}/openapi.json\"": "",
"What are you trying to achieve?": "",
"What are you working on?": "",
"Whats New in": "Co je nového v",

View file

@ -6,7 +6,7 @@
"(latest)": "(seneste)",
"(Ollama)": "",
"{{ models }}": "{{ modeller }}",
"{{COUNT}} Available Tool Servers": "",
"{{COUNT}} Available Tools": "",
"{{COUNT}} hidden lines": "",
"{{COUNT}} Replies": "",
"{{user}}'s Chats": "{{user}}s chats",
@ -108,6 +108,7 @@
"Attribute for Username": "",
"Audio": "Lyd",
"August": "august",
"Auth": "",
"Authenticate": "",
"Authentication": "",
"Auto-Copy Response to Clipboard": "Automatisk kopiering af svar til udklipsholder",
@ -119,7 +120,7 @@
"AUTOMATIC1111 Base URL": "AUTOMATIC1111 Base URL",
"AUTOMATIC1111 Base URL is required.": "AUTOMATIC1111 Base URL er påkrævet.",
"Available list": "Tilgængelige lister",
"Available Tool Servers": "",
"Available Tools": "",
"available!": "tilgængelig!",
"Awful": "",
"Azure AI Speech": "Azure AI Speech",
@ -218,7 +219,10 @@
"Confirm your new password": "",
"Connect to your own OpenAI compatible API endpoints.": "",
"Connect to your own OpenAPI compatible external tool servers.": "",
"Connection failed": "",
"Connection successful": "",
"Connections": "Forbindelser",
"Connections saved successfully": "",
"Constrains effort on reasoning for reasoning models. Only applicable to reasoning models from specific providers that support reasoning effort.": "",
"Contact Admin for WebUI Access": "Kontakt din administrator for adgang til WebUI",
"Content": "Indhold",
@ -303,6 +307,7 @@
"Direct Connections": "",
"Direct Connections allow users to connect to their own OpenAI compatible API endpoints.": "",
"Direct Connections settings updated": "",
"Direct Tool Servers": "",
"Disabled": "Inaktiv",
"Discover a function": "Find en funktion",
"Discover a model": "Find en model",
@ -412,6 +417,7 @@
"Enter Kagi Search API Key": "",
"Enter Key Behavior": "",
"Enter language codes": "Indtast sprogkoder",
"Enter Mistral API Key": "",
"Enter Model ID": "Indtast model-ID",
"Enter model tag (e.g. {{modelTag}})": "Indtast modelmærke (f.eks. {{modelTag}})",
"Enter Mojeek Search API Key": "",
@ -436,6 +442,7 @@
"Enter server port": "",
"Enter stop sequence": "Indtast stopsekvens",
"Enter system prompt": "Indtast systemprompt",
"Enter system prompt here": "",
"Enter Tavily API Key": "Indtast Tavily API-nøgle",
"Enter the public URL of your WebUI. This URL will be used to generate links in the notifications.": "",
"Enter Tika Server URL": "Indtast Tika Server URL",
@ -449,6 +456,7 @@
"Enter Your Email": "Indtast din e-mail",
"Enter Your Full Name": "Indtast dit fulde navn",
"Enter your message": "Indtast din besked",
"Enter your name": "",
"Enter your new password": "",
"Enter Your Password": "Indtast din adgangskode",
"Enter Your Role": "Indtast din rolle",
@ -468,7 +476,7 @@
"Exceeded the number of seats in your license. Please contact support to increase the number of seats.": "",
"Exclude": "",
"Execute code for analysis": "",
"Executing `{{NAME}}`...": "",
"Executing **{{NAME}}**...": "",
"Expand": "",
"Experimental": "Eksperimentel",
"Explain": "",
@ -493,6 +501,7 @@
"Failed to create API Key.": "Kunne ikke oprette API-nøgle.",
"Failed to fetch models": "",
"Failed to read clipboard contents": "Kunne ikke læse indholdet af udklipsholderen",
"Failed to save connections": "",
"Failed to save models configuration": "",
"Failed to update settings": "Kunne ikke opdatere indstillinger",
"Failed to upload file.": "Kunne ikke uploade fil.",
@ -525,6 +534,7 @@
"Forge new paths": "",
"Form": "Formular",
"Format your variables using brackets like this:": "",
"Forwards system user session credentials to authenticate": "",
"Frequency Penalty": "Hyppighedsstraf",
"Full Context Mode": "",
"Function": "",
@ -694,6 +704,8 @@
"Mirostat": "Mirostat",
"Mirostat Eta": "Mirostat Eta",
"Mirostat Tau": "Mirostat Tau",
"Mistral OCR": "",
"Mistral OCR API Key required.": "",
"Model": "",
"Model '{{modelName}}' has been successfully downloaded.": "Model '{{modelName}}' er blevet downloadet.",
"Model '{{modelTag}}' is already in queue for downloading.": "Model '{{modelTag}}' er allerede i kø til download.",
@ -789,6 +801,7 @@
"OpenAI API Key is required.": "OpenAI API-nøgle er påkrævet.",
"OpenAI API settings updated": "",
"OpenAI URL/Key required.": "OpenAI URL/nøgle påkrævet.",
"openapi.json Path": "",
"or": "eller",
"Organize your users": "",
"Other": "Andet",
@ -820,6 +833,8 @@
"Please carefully review the following warnings:": "Gennemgå omhyggeligt følgende advarsler:",
"Please do not close the settings page while loading the model.": "",
"Please enter a prompt": "",
"Please enter a valid path": "",
"Please enter a valid URL": "",
"Please fill in all fields.": "Udfyld alle felter.",
"Please select a model first.": "",
"Please select a model.": "",
@ -1042,6 +1057,7 @@
"Thinking...": "Tænker...",
"This action cannot be undone. Do you wish to continue?": "Denne handling kan ikke fortrydes. Vil du fortsætte?",
"This channel was created on {{createdAt}}. This is the very beginning of the {{channelName}} channel.": "",
"This chat wont appear in history and your messages will not be saved.": "",
"This ensures that your valuable conversations are securely saved to your backend database. Thank you!": "Dette sikrer, at dine værdifulde samtaler gemmes sikkert i din backend-database. Tak!",
"This is an experimental feature, it may not function as expected and is subject to change at any time.": "Dette er en eksperimentel funktion, den fungerer muligvis ikke som forventet og kan ændres når som helst.",
"This option controls how many tokens are preserved when refreshing the context. For example, if set to 2, the last 2 tokens of the conversation context will be retained. Preserving context can help maintain the continuity of a conversation, but it may reduce the ability to respond to new topics.": "",
@ -1089,6 +1105,7 @@
"Tool ID": "",
"Tool imported successfully": "Værktøj importeret.",
"Tool Name": "",
"Tool Servers": "",
"Tool updated successfully": "Værktøj opdateret.",
"Tools": "Værktøjer",
"Tools Access": "",
@ -1158,7 +1175,7 @@
"Version": "Version",
"Version {{selectedVersion}} of {{totalVersions}}": "Version {{selectedVersion}} af {{totalVersions}}",
"View Replies": "",
"View Result from `{{NAME}}`": "",
"View Result from **{{NAME}}**": "",
"Visibility": "",
"Voice": "Stemme",
"Voice Input": "",
@ -1176,9 +1193,9 @@
"Webhook URL": "Webhook-URL",
"WebUI Settings": "WebUI-indstillinger",
"WebUI URL": "",
"WebUI will make requests to \"{{url}}\"": "",
"WebUI will make requests to \"{{url}}/api/chat\"": "",
"WebUI will make requests to \"{{url}}/chat/completions\"": "",
"WebUI will make requests to \"{{url}}/openapi.json\"": "",
"What are you trying to achieve?": "",
"What are you working on?": "",
"Whats New in": "Nyheder i",

View file

@ -6,7 +6,7 @@
"(latest)": "(neueste)",
"(Ollama)": "",
"{{ models }}": "{{ Modelle }}",
"{{COUNT}} Available Tool Servers": "",
"{{COUNT}} Available Tools": "",
"{{COUNT}} hidden lines": "{{COUNT}} versteckte Zeilen",
"{{COUNT}} Replies": "{{COUNT}} Antworten",
"{{user}}'s Chats": "{{user}}s Chats",
@ -108,6 +108,7 @@
"Attribute for Username": "Attribut für Benutzername",
"Audio": "Audio",
"August": "August",
"Auth": "",
"Authenticate": "Authentifizieren",
"Authentication": "Authentifizierung",
"Auto-Copy Response to Clipboard": "Antwort automatisch in die Zwischenablage kopieren",
@ -119,7 +120,7 @@
"AUTOMATIC1111 Base URL": "AUTOMATIC1111-Basis-URL",
"AUTOMATIC1111 Base URL is required.": "AUTOMATIC1111-Basis-URL ist erforderlich.",
"Available list": "Verfügbare Liste",
"Available Tool Servers": "",
"Available Tools": "",
"available!": "Verfügbar!",
"Awful": "Schrecklich",
"Azure AI Speech": "Azure AI Speech",
@ -218,7 +219,10 @@
"Confirm your new password": "Neues Passwort bestätigen",
"Connect to your own OpenAI compatible API endpoints.": "Verbinden Sie sich zu Ihren OpenAI-kompatiblen Endpunkten.",
"Connect to your own OpenAPI compatible external tool servers.": "",
"Connection failed": "",
"Connection successful": "",
"Connections": "Verbindungen",
"Connections saved successfully": "",
"Constrains effort on reasoning for reasoning models. Only applicable to reasoning models from specific providers that support reasoning effort.": "",
"Contact Admin for WebUI Access": "Kontaktieren Sie den Administrator für den Zugriff auf die Weboberfläche",
"Content": "Info",
@ -303,6 +307,7 @@
"Direct Connections": "Direktverbindungen",
"Direct Connections allow users to connect to their own OpenAI compatible API endpoints.": "Direktverbindungen ermöglichen es Benutzern, sich mit ihren eigenen OpenAI-kompatiblen API-Endpunkten zu verbinden.",
"Direct Connections settings updated": "Direktverbindungs-Einstellungen aktualisiert",
"Direct Tool Servers": "",
"Disabled": "Deaktiviert",
"Discover a function": "Entdecken Sie weitere Funktionen",
"Discover a model": "Entdecken Sie weitere Modelle",
@ -379,7 +384,7 @@
"Enable Mirostat sampling for controlling perplexity.": "",
"Enable New Sign Ups": "Registrierung erlauben",
"Enabled": "Aktiviert",
"Enforce Temporary Chat": "",
"Enforce Temporary Chat": "Temporären Chat erzwingen",
"Ensure your CSV file includes 4 columns in this order: Name, Email, Password, Role.": "Stellen Sie sicher, dass Ihre CSV-Datei 4 Spalten in dieser Reihenfolge enthält: Name, E-Mail, Passwort, Rolle.",
"Enter {{role}} message here": "Geben Sie die {{role}}-Nachricht hier ein",
"Enter a detail about yourself for your LLMs to recall": "Geben Sie ein Detail über sich selbst ein, das Ihre Sprachmodelle (LLMs) sich merken sollen",
@ -412,6 +417,7 @@
"Enter Kagi Search API Key": "Geben Sie den Kagi Search API-Schlüssel ein",
"Enter Key Behavior": "Verhalten von 'Enter'",
"Enter language codes": "Geben Sie die Sprachcodes ein",
"Enter Mistral API Key": "",
"Enter Model ID": "Geben Sie die Modell-ID ein",
"Enter model tag (e.g. {{modelTag}})": "Geben Sie den Model-Tag ein",
"Enter Mojeek Search API Key": "Geben Sie den Mojeek Search API-Schlüssel ein",
@ -436,6 +442,7 @@
"Enter server port": "Geben Sie den Server-Port ein",
"Enter stop sequence": "Stop-Sequenz eingeben",
"Enter system prompt": "Systemprompt eingeben",
"Enter system prompt here": "",
"Enter Tavily API Key": "Geben Sie den Tavily-API-Schlüssel ein",
"Enter the public URL of your WebUI. This URL will be used to generate links in the notifications.": "Geben sie die öffentliche URL Ihrer WebUI ein. Diese URL wird verwendet, um Links in den Benachrichtigungen zu generieren.",
"Enter Tika Server URL": "Geben Sie die Tika-Server-URL ein",
@ -449,6 +456,7 @@
"Enter Your Email": "Geben Sie Ihre E-Mail-Adresse ein",
"Enter Your Full Name": "Geben Sie Ihren vollständigen Namen ein",
"Enter your message": "Geben Sie Ihre Nachricht ein",
"Enter your name": "",
"Enter your new password": "Geben Sie Ihr neues Passwort ein",
"Enter Your Password": "Geben Sie Ihr Passwort ein",
"Enter Your Role": "Geben Sie Ihre Rolle ein",
@ -468,7 +476,7 @@
"Exceeded the number of seats in your license. Please contact support to increase the number of seats.": "",
"Exclude": "Ausschließen",
"Execute code for analysis": "Code für Analyse ausführen",
"Executing `{{NAME}}`...": "",
"Executing **{{NAME}}**...": "",
"Expand": "Aufklappen",
"Experimental": "Experimentell",
"Explain": "Erklären",
@ -493,6 +501,7 @@
"Failed to create API Key.": "Fehler beim Erstellen des API-Schlüssels.",
"Failed to fetch models": "Fehler beim Abrufen der Modelle",
"Failed to read clipboard contents": "Fehler beim Abruf der Zwischenablage",
"Failed to save connections": "",
"Failed to save models configuration": "Fehler beim Speichern der Modellkonfiguration",
"Failed to update settings": "Fehler beim Aktualisieren der Einstellungen",
"Failed to upload file.": "Fehler beim Hochladen der Datei.",
@ -525,6 +534,7 @@
"Forge new paths": "Neue Wege beschreiten",
"Form": "Formular",
"Format your variables using brackets like this:": "Formatieren Sie Ihre Variablen mit Klammern, wie hier:",
"Forwards system user session credentials to authenticate": "",
"Frequency Penalty": "Frequenzstrafe",
"Full Context Mode": "Voll-Kontext Modus",
"Function": "Funktion",
@ -694,6 +704,8 @@
"Mirostat": "Mirostat",
"Mirostat Eta": "Mirostat Eta",
"Mirostat Tau": "Mirostat Tau",
"Mistral OCR": "",
"Mistral OCR API Key required.": "",
"Model": "Modell",
"Model '{{modelName}}' has been successfully downloaded.": "Modell '{{modelName}}' wurde erfolgreich heruntergeladen.",
"Model '{{modelTag}}' is already in queue for downloading.": "Modell '{{modelTag}}' befindet sich bereits in der Warteschlange zum Herunterladen.",
@ -789,6 +801,7 @@
"OpenAI API Key is required.": "OpenAI-API-Schlüssel erforderlich.",
"OpenAI API settings updated": "OpenAI-API-Einstellungen aktualisiert",
"OpenAI URL/Key required.": "OpenAI-URL/Schlüssel erforderlich.",
"openapi.json Path": "",
"or": "oder",
"Organize your users": "Organisieren Sie Ihre Benutzer",
"Other": "Andere",
@ -820,6 +833,8 @@
"Please carefully review the following warnings:": "Bitte überprüfen Sie die folgenden Warnungen sorgfältig:",
"Please do not close the settings page while loading the model.": "Bitte schließen die Einstellungen-Seite nicht, während das Modell lädt.",
"Please enter a prompt": "Bitte geben Sie einen Prompt ein",
"Please enter a valid path": "",
"Please enter a valid URL": "",
"Please fill in all fields.": "Bitte füllen Sie alle Felder aus.",
"Please select a model first.": "Bitte wählen Sie zuerst ein Modell aus.",
"Please select a model.": "Bitte wählen Sie ein Modell aus.",
@ -1021,7 +1036,7 @@
"Tell us more:": "Erzähl uns mehr",
"Temperature": "Temperatur",
"Template": "Vorlage",
"Temporary Chat": "Temporärer Chat",
"Temporary Chat": "Temporäre Unterhaltung",
"Text Splitter": "Text-Splitter",
"Text-to-Speech Engine": "Text-zu-Sprache-Engine",
"Tfs Z": "Tfs Z",
@ -1042,6 +1057,7 @@
"Thinking...": "Denke nach...",
"This action cannot be undone. Do you wish to continue?": "Diese Aktion kann nicht rückgängig gemacht werden. Möchten Sie fortfahren?",
"This channel was created on {{createdAt}}. This is the very beginning of the {{channelName}} channel.": "Dieser Kanal wurde am {{createdAt}} erstellt. Dies ist der Beginn des {{channelName}} Kanals.",
"This chat wont appear in history and your messages will not be saved.": "Diese Unterhaltung erscheint nicht in deinem Chat-Verlauf. Alle Nachrichten sind privat und werden nicht gespeichert.",
"This ensures that your valuable conversations are securely saved to your backend database. Thank you!": "Dies stellt sicher, dass Ihre wertvollen Chats sicher in Ihrer Backend-Datenbank gespeichert werden. Vielen Dank!",
"This is an experimental feature, it may not function as expected and is subject to change at any time.": "Dies ist eine experimentelle Funktion, sie funktioniert möglicherweise nicht wie erwartet und kann jederzeit geändert werden.",
"This option controls how many tokens are preserved when refreshing the context. For example, if set to 2, the last 2 tokens of the conversation context will be retained. Preserving context can help maintain the continuity of a conversation, but it may reduce the ability to respond to new topics.": "",
@ -1089,6 +1105,7 @@
"Tool ID": "Werkzeug-ID",
"Tool imported successfully": "Werkzeug erfolgreich importiert",
"Tool Name": "Werkzeugname",
"Tool Servers": "",
"Tool updated successfully": "Werkzeug erfolgreich aktualisiert",
"Tools": "Werkzeuge",
"Tools Access": "Werkzeugzugriff",
@ -1158,7 +1175,7 @@
"Version": "Version",
"Version {{selectedVersion}} of {{totalVersions}}": "Version {{selectedVersion}} von {{totalVersions}}",
"View Replies": "Antworten anzeigen",
"View Result from `{{NAME}}`": "",
"View Result from **{{NAME}}**": "",
"Visibility": "Sichtbarkeit",
"Voice": "Stimme",
"Voice Input": "Spracheingabe",
@ -1176,9 +1193,9 @@
"Webhook URL": "Webhook URL",
"WebUI Settings": "WebUI-Einstellungen",
"WebUI URL": "WebUI-URL",
"WebUI will make requests to \"{{url}}\"": "",
"WebUI will make requests to \"{{url}}/api/chat\"": "WebUI wird Anfragen an \"{{url}}/api/chat\" senden",
"WebUI will make requests to \"{{url}}/chat/completions\"": "WebUI wird Anfragen an \"{{url}}/chat/completions\" senden",
"WebUI will make requests to \"{{url}}/openapi.json\"": "",
"What are you trying to achieve?": "Was versuchen Sie zu erreichen?",
"What are you working on?": "Woran arbeiten Sie?",
"Whats New in": "Neuigkeiten von",

View file

@ -6,7 +6,7 @@
"(latest)": "(much latest)",
"(Ollama)": "",
"{{ models }}": "",
"{{COUNT}} Available Tool Servers": "",
"{{COUNT}} Available Tools": "",
"{{COUNT}} hidden lines": "",
"{{COUNT}} Replies": "",
"{{user}}'s Chats": "",
@ -108,6 +108,7 @@
"Attribute for Username": "",
"Audio": "Audio",
"August": "",
"Auth": "",
"Authenticate": "",
"Authentication": "",
"Auto-Copy Response to Clipboard": "Copy Bark Auto Bark",
@ -119,7 +120,7 @@
"AUTOMATIC1111 Base URL": "AUTOMATIC1111 Base URL",
"AUTOMATIC1111 Base URL is required.": "AUTOMATIC1111 Base URL is required.",
"Available list": "",
"Available Tool Servers": "",
"Available Tools": "",
"available!": "available! So excite!",
"Awful": "",
"Azure AI Speech": "",
@ -218,7 +219,10 @@
"Confirm your new password": "",
"Connect to your own OpenAI compatible API endpoints.": "",
"Connect to your own OpenAPI compatible external tool servers.": "",
"Connection failed": "",
"Connection successful": "",
"Connections": "Connections",
"Connections saved successfully": "",
"Constrains effort on reasoning for reasoning models. Only applicable to reasoning models from specific providers that support reasoning effort.": "",
"Contact Admin for WebUI Access": "",
"Content": "Content",
@ -303,6 +307,7 @@
"Direct Connections": "",
"Direct Connections allow users to connect to their own OpenAI compatible API endpoints.": "",
"Direct Connections settings updated": "",
"Direct Tool Servers": "",
"Disabled": "",
"Discover a function": "",
"Discover a model": "",
@ -412,6 +417,7 @@
"Enter Kagi Search API Key": "",
"Enter Key Behavior": "",
"Enter language codes": "",
"Enter Mistral API Key": "",
"Enter Model ID": "",
"Enter model tag (e.g. {{modelTag}})": "Enter model doge tag (e.g. {{modelTag}})",
"Enter Mojeek Search API Key": "",
@ -436,6 +442,7 @@
"Enter server port": "",
"Enter stop sequence": "Enter stop bark",
"Enter system prompt": "",
"Enter system prompt here": "",
"Enter Tavily API Key": "",
"Enter the public URL of your WebUI. This URL will be used to generate links in the notifications.": "",
"Enter Tika Server URL": "",
@ -449,6 +456,7 @@
"Enter Your Email": "Enter Your Dogemail",
"Enter Your Full Name": "Enter Your Full Wow",
"Enter your message": "",
"Enter your name": "",
"Enter your new password": "",
"Enter Your Password": "Enter Your Barkword",
"Enter Your Role": "",
@ -468,7 +476,7 @@
"Exceeded the number of seats in your license. Please contact support to increase the number of seats.": "",
"Exclude": "",
"Execute code for analysis": "",
"Executing `{{NAME}}`...": "",
"Executing **{{NAME}}**...": "",
"Expand": "",
"Experimental": "Much Experiment",
"Explain": "",
@ -493,6 +501,7 @@
"Failed to create API Key.": "",
"Failed to fetch models": "",
"Failed to read clipboard contents": "Failed to read clipboard borks",
"Failed to save connections": "",
"Failed to save models configuration": "",
"Failed to update settings": "",
"Failed to upload file.": "",
@ -525,6 +534,7 @@
"Forge new paths": "",
"Form": "",
"Format your variables using brackets like this:": "",
"Forwards system user session credentials to authenticate": "",
"Frequency Penalty": "",
"Full Context Mode": "",
"Function": "",
@ -694,6 +704,8 @@
"Mirostat": "Mirostat",
"Mirostat Eta": "Mirostat Eta",
"Mirostat Tau": "Mirostat Tau",
"Mistral OCR": "",
"Mistral OCR API Key required.": "",
"Model": "",
"Model '{{modelName}}' has been successfully downloaded.": "Model '{{modelName}}' has been successfully downloaded.",
"Model '{{modelTag}}' is already in queue for downloading.": "Model '{{modelTag}}' is already in queue for downloading.",
@ -789,6 +801,7 @@
"OpenAI API Key is required.": "OpenAI Bark Key is required.",
"OpenAI API settings updated": "",
"OpenAI URL/Key required.": "",
"openapi.json Path": "",
"or": "or",
"Organize your users": "",
"Other": "",
@ -820,6 +833,8 @@
"Please carefully review the following warnings:": "",
"Please do not close the settings page while loading the model.": "",
"Please enter a prompt": "",
"Please enter a valid path": "",
"Please enter a valid URL": "",
"Please fill in all fields.": "",
"Please select a model first.": "",
"Please select a model.": "",
@ -1042,6 +1057,7 @@
"Thinking...": "",
"This action cannot be undone. Do you wish to continue?": "",
"This channel was created on {{createdAt}}. This is the very beginning of the {{channelName}} channel.": "",
"This chat wont appear in history and your messages will not be saved.": "",
"This ensures that your valuable conversations are securely saved to your backend database. Thank you!": "This ensures that your valuable conversations are securely saved to your backend database. Thank you! Much secure!",
"This is an experimental feature, it may not function as expected and is subject to change at any time.": "",
"This option controls how many tokens are preserved when refreshing the context. For example, if set to 2, the last 2 tokens of the conversation context will be retained. Preserving context can help maintain the continuity of a conversation, but it may reduce the ability to respond to new topics.": "",
@ -1089,6 +1105,7 @@
"Tool ID": "",
"Tool imported successfully": "",
"Tool Name": "",
"Tool Servers": "",
"Tool updated successfully": "",
"Tools": "",
"Tools Access": "",
@ -1158,7 +1175,7 @@
"Version": "Version much version",
"Version {{selectedVersion}} of {{totalVersions}}": "",
"View Replies": "",
"View Result from `{{NAME}}`": "",
"View Result from **{{NAME}}**": "",
"Visibility": "",
"Voice": "",
"Voice Input": "",
@ -1176,9 +1193,9 @@
"Webhook URL": "",
"WebUI Settings": "WebUI Settings much settings",
"WebUI URL": "",
"WebUI will make requests to \"{{url}}\"": "",
"WebUI will make requests to \"{{url}}/api/chat\"": "",
"WebUI will make requests to \"{{url}}/chat/completions\"": "",
"WebUI will make requests to \"{{url}}/openapi.json\"": "",
"What are you trying to achieve?": "",
"What are you working on?": "",
"Whats New in": "Whats New in much new",

View file

@ -6,7 +6,7 @@
"(latest)": "(τελευταίο)",
"(Ollama)": "",
"{{ models }}": "{{ models }}",
"{{COUNT}} Available Tool Servers": "",
"{{COUNT}} Available Tools": "",
"{{COUNT}} hidden lines": "",
"{{COUNT}} Replies": "",
"{{user}}'s Chats": "Συνομιλίες του {{user}}",
@ -108,6 +108,7 @@
"Attribute for Username": "Ιδιότητα για Όνομα Χρήστη",
"Audio": "Ήχος",
"August": "Αύγουστος",
"Auth": "",
"Authenticate": "Επαλήθευση",
"Authentication": "",
"Auto-Copy Response to Clipboard": "Αυτόματη Αντιγραφή Απάντησης στο Πρόχειρο",
@ -119,7 +120,7 @@
"AUTOMATIC1111 Base URL": "Βασικό URL AUTOMATIC1111",
"AUTOMATIC1111 Base URL is required.": "Απαιτείται το Βασικό URL AUTOMATIC1111.",
"Available list": "Διαθέσιμη λίστα",
"Available Tool Servers": "",
"Available Tools": "",
"available!": "διαθέσιμο!",
"Awful": "Ασχημο",
"Azure AI Speech": "Ομιλία Azure AI",
@ -218,7 +219,10 @@
"Confirm your new password": "",
"Connect to your own OpenAI compatible API endpoints.": "",
"Connect to your own OpenAPI compatible external tool servers.": "",
"Connection failed": "",
"Connection successful": "",
"Connections": "Συνδέσεις",
"Connections saved successfully": "",
"Constrains effort on reasoning for reasoning models. Only applicable to reasoning models from specific providers that support reasoning effort.": "",
"Contact Admin for WebUI Access": "Επικοινωνήστε με τον Διαχειριστή για Πρόσβαση στο WebUI",
"Content": "Περιεχόμενο",
@ -303,6 +307,7 @@
"Direct Connections": "",
"Direct Connections allow users to connect to their own OpenAI compatible API endpoints.": "",
"Direct Connections settings updated": "",
"Direct Tool Servers": "",
"Disabled": "Απενεργοποιημένο",
"Discover a function": "Ανακάλυψη λειτουργίας",
"Discover a model": "Ανακάλυψη μοντέλου",
@ -412,6 +417,7 @@
"Enter Kagi Search API Key": "",
"Enter Key Behavior": "",
"Enter language codes": "Εισάγετε κωδικούς γλώσσας",
"Enter Mistral API Key": "",
"Enter Model ID": "Εισάγετε το ID Μοντέλου",
"Enter model tag (e.g. {{modelTag}})": "Εισάγετε την ετικέτα μοντέλου (π.χ. {{modelTag}})",
"Enter Mojeek Search API Key": "Εισάγετε το Κλειδί API Mojeek Search",
@ -436,6 +442,7 @@
"Enter server port": "Εισάγετε την θύρα διακομιστή",
"Enter stop sequence": "Εισάγετε τη σειρά παύσης",
"Enter system prompt": "Εισάγετε την προτροπή συστήματος",
"Enter system prompt here": "",
"Enter Tavily API Key": "Εισάγετε το Κλειδί API Tavily",
"Enter the public URL of your WebUI. This URL will be used to generate links in the notifications.": "",
"Enter Tika Server URL": "Εισάγετε το URL διακομιστή Tika",
@ -449,6 +456,7 @@
"Enter Your Email": "Εισάγετε το Email σας",
"Enter Your Full Name": "Εισάγετε το Πλήρες Όνομά σας",
"Enter your message": "Εισάγετε το μήνυμά σας",
"Enter your name": "",
"Enter your new password": "",
"Enter Your Password": "Εισάγετε τον Κωδικό σας",
"Enter Your Role": "Εισάγετε τον Ρόλο σας",
@ -468,7 +476,7 @@
"Exceeded the number of seats in your license. Please contact support to increase the number of seats.": "",
"Exclude": "Εξαίρεση",
"Execute code for analysis": "",
"Executing `{{NAME}}`...": "",
"Executing **{{NAME}}**...": "",
"Expand": "",
"Experimental": "Πειραματικό",
"Explain": "",
@ -493,6 +501,7 @@
"Failed to create API Key.": "Αποτυχία δημιουργίας Κλειδιού API.",
"Failed to fetch models": "",
"Failed to read clipboard contents": "Αποτυχία ανάγνωσης περιεχομένων πρόχειρου",
"Failed to save connections": "",
"Failed to save models configuration": "Αποτυχία αποθήκευσης ρυθμίσεων μοντέλων",
"Failed to update settings": "Αποτυχία ενημέρωσης ρυθμίσεων",
"Failed to upload file.": "Αποτυχία ανεβάσματος αρχείου.",
@ -525,6 +534,7 @@
"Forge new paths": "Δημιουργήστε νέες διαδρομές",
"Form": "Φόρμα",
"Format your variables using brackets like this:": "Μορφοποιήστε τις μεταβλητές σας χρησιμοποιώντας αγκύλες όπως αυτό:",
"Forwards system user session credentials to authenticate": "",
"Frequency Penalty": "Ποινή Συχνότητας",
"Full Context Mode": "",
"Function": "Λειτουργία",
@ -694,6 +704,8 @@
"Mirostat": "Mirostat",
"Mirostat Eta": "Mirostat Eta",
"Mirostat Tau": "Mirostat Tau",
"Mistral OCR": "",
"Mistral OCR API Key required.": "",
"Model": "Μοντέλο",
"Model '{{modelName}}' has been successfully downloaded.": "Το μοντέλο '{{modelName}}' κατεβάστηκε με επιτυχία.",
"Model '{{modelTag}}' is already in queue for downloading.": "Το μοντέλο '{{modelTag}}' βρίσκεται ήδη στην ουρά για λήψη.",
@ -789,6 +801,7 @@
"OpenAI API Key is required.": "Απαιτείται το Κλειδί API OpenAI.",
"OpenAI API settings updated": "Οι ρυθμίσεις API OpenAI ενημερώθηκαν",
"OpenAI URL/Key required.": "Απαιτείται URL/Kλειδί OpenAI.",
"openapi.json Path": "",
"or": "ή",
"Organize your users": "Οργανώστε τους χρήστες σας",
"Other": "Άλλο",
@ -820,6 +833,8 @@
"Please carefully review the following warnings:": "Παρακαλώ αναθεωρήστε προσεκτικά τις ακόλουθες προειδοποιήσεις:",
"Please do not close the settings page while loading the model.": "",
"Please enter a prompt": "Παρακαλώ εισάγετε μια προτροπή",
"Please enter a valid path": "",
"Please enter a valid URL": "",
"Please fill in all fields.": "Παρακαλώ συμπληρώστε όλα τα πεδία.",
"Please select a model first.": "",
"Please select a model.": "",
@ -1042,6 +1057,7 @@
"Thinking...": "Σκέφτομαι...",
"This action cannot be undone. Do you wish to continue?": "Αυτή η ενέργεια δεν μπορεί να αναιρεθεί. Θέλετε να συνεχίσετε;",
"This channel was created on {{createdAt}}. This is the very beginning of the {{channelName}} channel.": "",
"This chat wont appear in history and your messages will not be saved.": "",
"This ensures that your valuable conversations are securely saved to your backend database. Thank you!": "Αυτό διασφαλίζει ότι οι πολύτιμες συνομιλίες σας αποθηκεύονται με ασφάλεια στη βάση δεδομένων backend σας. Ευχαριστούμε!",
"This is an experimental feature, it may not function as expected and is subject to change at any time.": "Αυτή είναι μια πειραματική λειτουργία, μπορεί να μην λειτουργεί όπως αναμένεται και υπόκειται σε αλλαγές οποιαδήποτε στιγμή.",
"This option controls how many tokens are preserved when refreshing the context. For example, if set to 2, the last 2 tokens of the conversation context will be retained. Preserving context can help maintain the continuity of a conversation, but it may reduce the ability to respond to new topics.": "",
@ -1089,6 +1105,7 @@
"Tool ID": "ID Εργαλείου",
"Tool imported successfully": "Το εργαλείο εισήχθη με επιτυχία",
"Tool Name": "Όνομα Εργαλείου",
"Tool Servers": "",
"Tool updated successfully": "Το εργαλείο ενημερώθηκε με επιτυχία",
"Tools": "Εργαλεία",
"Tools Access": "Πρόσβαση Εργαλείων",
@ -1158,7 +1175,7 @@
"Version": "Έκδοση",
"Version {{selectedVersion}} of {{totalVersions}}": "Έκδοση {{selectedVersion}} από {{totalVersions}}",
"View Replies": "",
"View Result from `{{NAME}}`": "",
"View Result from **{{NAME}}**": "",
"Visibility": "Ορατότητα",
"Voice": "Φωνή",
"Voice Input": "Εισαγωγή Φωνής",
@ -1176,9 +1193,9 @@
"Webhook URL": "URL Webhook",
"WebUI Settings": "Ρυθμίσεις WebUI",
"WebUI URL": "",
"WebUI will make requests to \"{{url}}\"": "",
"WebUI will make requests to \"{{url}}/api/chat\"": "Το WebUI θα κάνει αιτήματα στο \"{{url}}/api/chat\"",
"WebUI will make requests to \"{{url}}/chat/completions\"": "Το WebUI θα κάνει αιτήματα στο \"{{url}}/chat/completions\"",
"WebUI will make requests to \"{{url}}/openapi.json\"": "",
"What are you trying to achieve?": "Τι προσπαθείτε να πετύχετε?",
"What are you working on?": "Τι εργάζεστε;",
"Whats New in": "Τι νέο υπάρχει στο",

View file

@ -6,7 +6,7 @@
"(latest)": "",
"(Ollama)": "",
"{{ models }}": "",
"{{COUNT}} Available Tool Servers": "",
"{{COUNT}} Available Tools": "",
"{{COUNT}} hidden lines": "",
"{{COUNT}} Replies": "",
"{{user}}'s Chats": "",
@ -108,6 +108,7 @@
"Attribute for Username": "",
"Audio": "",
"August": "",
"Auth": "",
"Authenticate": "",
"Authentication": "",
"Auto-Copy Response to Clipboard": "",
@ -119,7 +120,7 @@
"AUTOMATIC1111 Base URL": "",
"AUTOMATIC1111 Base URL is required.": "",
"Available list": "",
"Available Tool Servers": "",
"Available Tools": "",
"available!": "",
"Awful": "",
"Azure AI Speech": "",
@ -218,7 +219,10 @@
"Confirm your new password": "",
"Connect to your own OpenAI compatible API endpoints.": "",
"Connect to your own OpenAPI compatible external tool servers.": "",
"Connection failed": "",
"Connection successful": "",
"Connections": "",
"Connections saved successfully": "",
"Constrains effort on reasoning for reasoning models. Only applicable to reasoning models from specific providers that support reasoning effort.": "",
"Contact Admin for WebUI Access": "",
"Content": "",
@ -303,6 +307,7 @@
"Direct Connections": "",
"Direct Connections allow users to connect to their own OpenAI compatible API endpoints.": "",
"Direct Connections settings updated": "",
"Direct Tool Servers": "",
"Disabled": "",
"Discover a function": "",
"Discover a model": "",
@ -412,6 +417,7 @@
"Enter Kagi Search API Key": "",
"Enter Key Behavior": "",
"Enter language codes": "",
"Enter Mistral API Key": "",
"Enter Model ID": "",
"Enter model tag (e.g. {{modelTag}})": "",
"Enter Mojeek Search API Key": "",
@ -436,6 +442,7 @@
"Enter server port": "",
"Enter stop sequence": "",
"Enter system prompt": "",
"Enter system prompt here": "",
"Enter Tavily API Key": "",
"Enter the public URL of your WebUI. This URL will be used to generate links in the notifications.": "",
"Enter Tika Server URL": "",
@ -449,6 +456,7 @@
"Enter Your Email": "",
"Enter Your Full Name": "",
"Enter your message": "",
"Enter your name": "",
"Enter your new password": "",
"Enter Your Password": "",
"Enter Your Role": "",
@ -468,7 +476,7 @@
"Exceeded the number of seats in your license. Please contact support to increase the number of seats.": "",
"Exclude": "",
"Execute code for analysis": "",
"Executing `{{NAME}}`...": "",
"Executing **{{NAME}}**...": "",
"Expand": "",
"Experimental": "",
"Explain": "",
@ -493,6 +501,7 @@
"Failed to create API Key.": "",
"Failed to fetch models": "",
"Failed to read clipboard contents": "",
"Failed to save connections": "",
"Failed to save models configuration": "",
"Failed to update settings": "",
"Failed to upload file.": "",
@ -525,6 +534,7 @@
"Forge new paths": "",
"Form": "",
"Format your variables using brackets like this:": "",
"Forwards system user session credentials to authenticate": "",
"Frequency Penalty": "",
"Full Context Mode": "",
"Function": "",
@ -694,6 +704,8 @@
"Mirostat": "",
"Mirostat Eta": "",
"Mirostat Tau": "",
"Mistral OCR": "",
"Mistral OCR API Key required.": "",
"Model": "",
"Model '{{modelName}}' has been successfully downloaded.": "",
"Model '{{modelTag}}' is already in queue for downloading.": "",
@ -789,6 +801,7 @@
"OpenAI API Key is required.": "",
"OpenAI API settings updated": "",
"OpenAI URL/Key required.": "",
"openapi.json Path": "",
"or": "",
"Organize your users": "",
"Other": "",
@ -820,6 +833,8 @@
"Please carefully review the following warnings:": "",
"Please do not close the settings page while loading the model.": "",
"Please enter a prompt": "",
"Please enter a valid path": "",
"Please enter a valid URL": "",
"Please fill in all fields.": "",
"Please select a model first.": "",
"Please select a model.": "",
@ -1042,6 +1057,7 @@
"Thinking...": "",
"This action cannot be undone. Do you wish to continue?": "",
"This channel was created on {{createdAt}}. This is the very beginning of the {{channelName}} channel.": "",
"This chat wont appear in history and your messages will not be saved.": "",
"This ensures that your valuable conversations are securely saved to your backend database. Thank you!": "",
"This is an experimental feature, it may not function as expected and is subject to change at any time.": "",
"This option controls how many tokens are preserved when refreshing the context. For example, if set to 2, the last 2 tokens of the conversation context will be retained. Preserving context can help maintain the continuity of a conversation, but it may reduce the ability to respond to new topics.": "",
@ -1089,6 +1105,7 @@
"Tool ID": "",
"Tool imported successfully": "",
"Tool Name": "",
"Tool Servers": "",
"Tool updated successfully": "",
"Tools": "",
"Tools Access": "",
@ -1158,7 +1175,7 @@
"Version": "",
"Version {{selectedVersion}} of {{totalVersions}}": "",
"View Replies": "",
"View Result from `{{NAME}}`": "",
"View Result from **{{NAME}}**": "",
"Visibility": "",
"Voice": "",
"Voice Input": "",
@ -1176,9 +1193,9 @@
"Webhook URL": "",
"WebUI Settings": "",
"WebUI URL": "",
"WebUI will make requests to \"{{url}}\"": "",
"WebUI will make requests to \"{{url}}/api/chat\"": "",
"WebUI will make requests to \"{{url}}/chat/completions\"": "",
"WebUI will make requests to \"{{url}}/openapi.json\"": "",
"What are you trying to achieve?": "",
"What are you working on?": "",
"Whats New in": "",

View file

@ -6,7 +6,7 @@
"(latest)": "",
"(Ollama)": "",
"{{ models }}": "",
"{{COUNT}} Available Tool Servers": "",
"{{COUNT}} Available Tools": "",
"{{COUNT}} hidden lines": "",
"{{COUNT}} Replies": "",
"{{user}}'s Chats": "",
@ -108,6 +108,7 @@
"Attribute for Username": "",
"Audio": "",
"August": "",
"Auth": "",
"Authenticate": "",
"Authentication": "",
"Auto-Copy Response to Clipboard": "",
@ -119,7 +120,7 @@
"AUTOMATIC1111 Base URL": "",
"AUTOMATIC1111 Base URL is required.": "",
"Available list": "",
"Available Tool Servers": "",
"Available Tools": "",
"available!": "",
"Awful": "",
"Azure AI Speech": "",
@ -218,7 +219,10 @@
"Confirm your new password": "",
"Connect to your own OpenAI compatible API endpoints.": "",
"Connect to your own OpenAPI compatible external tool servers.": "",
"Connection failed": "",
"Connection successful": "",
"Connections": "",
"Connections saved successfully": "",
"Constrains effort on reasoning for reasoning models. Only applicable to reasoning models from specific providers that support reasoning effort.": "",
"Contact Admin for WebUI Access": "",
"Content": "",
@ -303,6 +307,7 @@
"Direct Connections": "",
"Direct Connections allow users to connect to their own OpenAI compatible API endpoints.": "",
"Direct Connections settings updated": "",
"Direct Tool Servers": "",
"Disabled": "",
"Discover a function": "",
"Discover a model": "",
@ -412,6 +417,7 @@
"Enter Kagi Search API Key": "",
"Enter Key Behavior": "",
"Enter language codes": "",
"Enter Mistral API Key": "",
"Enter Model ID": "",
"Enter model tag (e.g. {{modelTag}})": "",
"Enter Mojeek Search API Key": "",
@ -436,6 +442,7 @@
"Enter server port": "",
"Enter stop sequence": "",
"Enter system prompt": "",
"Enter system prompt here": "",
"Enter Tavily API Key": "",
"Enter the public URL of your WebUI. This URL will be used to generate links in the notifications.": "",
"Enter Tika Server URL": "",
@ -449,6 +456,7 @@
"Enter Your Email": "",
"Enter Your Full Name": "",
"Enter your message": "",
"Enter your name": "",
"Enter your new password": "",
"Enter Your Password": "",
"Enter Your Role": "",
@ -468,7 +476,7 @@
"Exceeded the number of seats in your license. Please contact support to increase the number of seats.": "",
"Exclude": "",
"Execute code for analysis": "",
"Executing `{{NAME}}`...": "",
"Executing **{{NAME}}**...": "",
"Expand": "",
"Experimental": "",
"Explain": "",
@ -493,6 +501,7 @@
"Failed to create API Key.": "",
"Failed to fetch models": "",
"Failed to read clipboard contents": "",
"Failed to save connections": "",
"Failed to save models configuration": "",
"Failed to update settings": "",
"Failed to upload file.": "",
@ -525,6 +534,7 @@
"Forge new paths": "",
"Form": "",
"Format your variables using brackets like this:": "",
"Forwards system user session credentials to authenticate": "",
"Frequency Penalty": "",
"Full Context Mode": "",
"Function": "",
@ -694,6 +704,8 @@
"Mirostat": "",
"Mirostat Eta": "",
"Mirostat Tau": "",
"Mistral OCR": "",
"Mistral OCR API Key required.": "",
"Model": "",
"Model '{{modelName}}' has been successfully downloaded.": "",
"Model '{{modelTag}}' is already in queue for downloading.": "",
@ -789,6 +801,7 @@
"OpenAI API Key is required.": "",
"OpenAI API settings updated": "",
"OpenAI URL/Key required.": "",
"openapi.json Path": "",
"or": "",
"Organize your users": "",
"Other": "",
@ -820,6 +833,8 @@
"Please carefully review the following warnings:": "",
"Please do not close the settings page while loading the model.": "",
"Please enter a prompt": "",
"Please enter a valid path": "",
"Please enter a valid URL": "",
"Please fill in all fields.": "",
"Please select a model first.": "",
"Please select a model.": "",
@ -1042,6 +1057,7 @@
"Thinking...": "",
"This action cannot be undone. Do you wish to continue?": "",
"This channel was created on {{createdAt}}. This is the very beginning of the {{channelName}} channel.": "",
"This chat wont appear in history and your messages will not be saved.": "",
"This ensures that your valuable conversations are securely saved to your backend database. Thank you!": "",
"This is an experimental feature, it may not function as expected and is subject to change at any time.": "",
"This option controls how many tokens are preserved when refreshing the context. For example, if set to 2, the last 2 tokens of the conversation context will be retained. Preserving context can help maintain the continuity of a conversation, but it may reduce the ability to respond to new topics.": "",
@ -1089,6 +1105,7 @@
"Tool ID": "",
"Tool imported successfully": "",
"Tool Name": "",
"Tool Servers": "",
"Tool updated successfully": "",
"Tools": "",
"Tools Access": "",
@ -1158,7 +1175,7 @@
"Version": "",
"Version {{selectedVersion}} of {{totalVersions}}": "",
"View Replies": "",
"View Result from `{{NAME}}`": "",
"View Result from **{{NAME}}**": "",
"Visibility": "",
"Voice": "",
"Voice Input": "",
@ -1176,9 +1193,9 @@
"Webhook URL": "",
"WebUI Settings": "",
"WebUI URL": "",
"WebUI will make requests to \"{{url}}\"": "",
"WebUI will make requests to \"{{url}}/api/chat\"": "",
"WebUI will make requests to \"{{url}}/chat/completions\"": "",
"WebUI will make requests to \"{{url}}/openapi.json\"": "",
"What are you trying to achieve?": "",
"What are you working on?": "",
"Whats New in": "",

View file

@ -6,7 +6,7 @@
"(latest)": "(último)",
"(Ollama)": "(Ollama)",
"{{ models }}": "{{ models }}",
"{{COUNT}} Available Tool Servers": "{{COUNT]] Servidores de Herramientas Disponibles",
"{{COUNT}} Available Tools": "",
"{{COUNT}} hidden lines": "{{COUNT}} líneas ocultas",
"{{COUNT}} Replies": "{{COUNT}} Respuestas",
"{{user}}'s Chats": "Chats de {{user}}",
@ -108,6 +108,7 @@
"Attribute for Username": "Atributo para Nombre de Usuario",
"Audio": "Audio",
"August": "Agosto",
"Auth": "",
"Authenticate": "Autentificar",
"Authentication": "Autentificación",
"Auto-Copy Response to Clipboard": "Auto-Copiar respuesta al Portapapeles",
@ -119,7 +120,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 Tool Servers": "Servidores de Herramientas Disponible",
"Available Tools": "",
"available!": "¡disponible!",
"Awful": "Horrible",
"Azure AI Speech": "Voz Azure AI",
@ -218,7 +219,10 @@
"Confirm your new password": "Confirma tu nueva contraseña",
"Connect to your own OpenAI compatible API endpoints.": "Conectar a tus propios endpoints compatibles API OpenAI.",
"Connect to your own OpenAPI compatible external tool servers.": "Conectar a tus propios endpoints externos de herramientas compatibles API OpenAI.",
"Connection failed": "",
"Connection successful": "",
"Connections": "Conexiones",
"Connections saved successfully": "",
"Constrains effort on reasoning for reasoning models. Only applicable to reasoning models from specific providers that support reasoning effort.": "Limita el esfuerzo de razonamiento para los modelos de razonamiento. Solo aplicable a modelos de razonamiento de proveedores específicos que soportan el esfuerzo de razonamiento.",
"Contact Admin for WebUI Access": "Contacta con Admin para obtener acceso a WebUI",
"Content": "Contenido",
@ -303,6 +307,7 @@
"Direct Connections": "Conexiones Directas",
"Direct Connections allow users to connect to their own OpenAI compatible API endpoints.": "Las Conexiones Directas permiten a los usuarios conectar a sus propios endpoints compatibles API OpenAI.",
"Direct Connections settings updated": "Se actualizaron las configuraciones de las Conexiones Directas",
"Direct Tool Servers": "",
"Disabled": "Deshabilitado",
"Discover a function": "Descubre una Función",
"Discover a model": "Descubre un Modelo",
@ -412,6 +417,7 @@
"Enter Kagi Search API Key": "Ingresar Clave API de Kagi Search",
"Enter Key Behavior": "Ingresar Clave de Comportamiento",
"Enter language codes": "Ingresar Códigos de Idioma",
"Enter Mistral API Key": "",
"Enter Model ID": "Ingresar ID del Modelo",
"Enter model tag (e.g. {{modelTag}})": "Ingresar la etiqueta del modelo (p.ej. {{modelTag}})",
"Enter Mojeek Search API Key": "Ingresar Clave API de Mojeek Search",
@ -436,6 +442,7 @@
"Enter server port": "Ingresar puerto del servidor",
"Enter stop sequence": "Ingresar secuencia de parada",
"Enter system prompt": "Ingresar Indicador(prompt) del sistema",
"Enter system prompt here": "",
"Enter Tavily API Key": "Ingresar Clave API de Tavily",
"Enter the public URL of your WebUI. This URL will be used to generate links in the notifications.": "Ingresar URL pública de su WebUI. Esta URL se usará para generar enlaces en las notificaciones.",
"Enter Tika Server URL": "Ingresar URL del servidor Tika",
@ -449,6 +456,7 @@
"Enter Your Email": "Ingresa tu correo electrónico",
"Enter Your Full Name": "Ingresa su nombre completo",
"Enter your message": "Ingresa tu mensaje",
"Enter your name": "",
"Enter your new password": "Ingresa tu contraseña nueva",
"Enter Your Password": "Ingresa tu contraseña",
"Enter Your Role": "Ingresa tu rol",
@ -468,7 +476,7 @@
"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.",
"Exclude": "Excluir",
"Execute code for analysis": "Ejecutar código para análisis",
"Executing `{{NAME}}`...": "Ejecutando `{{NAME}}`...",
"Executing **{{NAME}}**...": "",
"Expand": "Expandir",
"Experimental": "Experimental",
"Explain": "Explicar",
@ -493,6 +501,7 @@
"Failed to create API Key.": "Fallo al crear la Clave API.",
"Failed to fetch models": "Fallo al obtener los modelos",
"Failed to read clipboard contents": "Fallo al leer el contenido del portapapeles",
"Failed to save connections": "",
"Failed to save models configuration": "Fallo al guardar la configuración de los modelos",
"Failed to update settings": "Fallo al actualizar los ajustes",
"Failed to upload file.": "Fallo al subir el archivo.",
@ -525,6 +534,7 @@
"Forge new paths": "Forjar nuevos caminos",
"Form": "Formulario",
"Format your variables using brackets like this:": "Formatea tus variables usando corchetes así:",
"Forwards system user session credentials to authenticate": "",
"Frequency Penalty": "Penalización de Frecuencia",
"Full Context Mode": "Modo Contexto Completo",
"Function": "Función",
@ -694,6 +704,8 @@
"Mirostat": "Mirostat",
"Mirostat Eta": "Mirostat Eta",
"Mirostat Tau": "Mirostat Tau",
"Mistral OCR": "",
"Mistral OCR API Key required.": "",
"Model": "Modelo",
"Model '{{modelName}}' has been successfully downloaded.": "Modelo '{{modelName}}' se ha descargado correctamente.",
"Model '{{modelTag}}' is already in queue for downloading.": "Modelo '{{modelTag}}' ya está en cola para descargar.",
@ -789,6 +801,7 @@
"OpenAI API Key is required.": "Clave API de OpenAI requerida.",
"OpenAI API settings updated": "Ajustes de API OpenAI actualizados",
"OpenAI URL/Key required.": "URL/Clave de OpenAI requerida.",
"openapi.json Path": "",
"or": "o",
"Organize your users": "Organiza tus usuarios",
"Other": "Otro",
@ -820,6 +833,8 @@
"Please carefully review the following warnings:": "Por favor revisar cuidadosamente los siguientes avisos:",
"Please do not close the settings page while loading the model.": "Por favor no cerrar la página de ajustes mientras se está descargando el modelo.",
"Please enter a prompt": "Por favor ingresar un indicador(prompt)",
"Please enter a valid path": "",
"Please enter a valid URL": "",
"Please fill in all fields.": "Por favor rellenar todos los campos.",
"Please select a model first.": "Por favor primero seleccionar un modelo.",
"Please select a model.": "Por favor seleccionar un modelo.",
@ -1042,6 +1057,7 @@
"Thinking...": "Pensando...",
"This action cannot be undone. Do you wish to continue?": "Esta acción no se puede deshacer. ¿Desea continuar?",
"This channel was created on {{createdAt}}. This is the very beginning of the {{channelName}} channel.": "Este canal fue creado el {{createdAt}}. Este es el comienzo del canal {{channelName}}.",
"This chat wont appear in history and your messages will not be saved.": "",
"This ensures that your valuable conversations are securely saved to your backend database. Thank you!": "Esto garantiza que sus valiosas conversaciones se guardan de forma segura en tu base de datos del servidor trasero (backend). ¡Gracias!",
"This is an experimental feature, it may not function as expected and is subject to change at any time.": "Esta es una característica experimental, por lo que puede no funcionar como se esperaba y está sujeta a cambios en cualquier momento.",
"This option controls how many tokens are preserved when refreshing the context. For example, if set to 2, the last 2 tokens of the conversation context will be retained. Preserving context can help maintain the continuity of a conversation, but it may reduce the ability to respond to new topics.": "Esta opción controla cuántos tokens se conservan cuando se actualiza el contexto. Por ejemplo, si se establece en 2, se conservarán los últimos 2 tokens del contexto de la conversación. Conservar el contexto puede ayudar a mantener la continuidad de una conversación, pero puede reducir la habilidad para responder a nuevos temas.",
@ -1089,6 +1105,7 @@
"Tool ID": "ID de la Herramienta",
"Tool imported successfully": "Herramienta importada correctamente",
"Tool Name": "Nombre de la Herramienta",
"Tool Servers": "",
"Tool updated successfully": "Herramienta actualizada correctamente",
"Tools": "Herramientas",
"Tools Access": "Acceso a Herramientas",
@ -1158,7 +1175,7 @@
"Version": "Versión",
"Version {{selectedVersion}} of {{totalVersions}}": "Versión {{selectedVersion}} de {{totalVersions}}",
"View Replies": "Ver Respuestas",
"View Result from `{{NAME}}`": "Ver Resultado desde `{{NAME}}`",
"View Result from **{{NAME}}**": "",
"Visibility": "Visibilidad",
"Voice": "Voz",
"Voice Input": "Entrada de Voz",
@ -1176,9 +1193,9 @@
"Webhook URL": "URL EngancheWeb(Webhook)",
"WebUI Settings": "WebUI Ajustes",
"WebUI URL": "WebUI URL",
"WebUI will make requests to \"{{url}}\"": "",
"WebUI will make requests to \"{{url}}/api/chat\"": "WebUI hará solicitudes a \"{{url}}/api/chat\"",
"WebUI will make requests to \"{{url}}/chat/completions\"": "WebUI hará solicitudes a \"{{url}}/chat/completions\"",
"WebUI will make requests to \"{{url}}/openapi.json\"": "WebUI hará solicitudes a \"{{url}}/openapi.json\"",
"What are you trying to achieve?": "¿Qué estás tratando de conseguir?",
"What are you working on?": "¿En qué estás trabajando?",
"Whats New in": "Que hay de Nuevo en",

View file

@ -6,7 +6,7 @@
"(latest)": "(uusim)",
"(Ollama)": "",
"{{ models }}": "{{ mudelid }}",
"{{COUNT}} Available Tool Servers": "",
"{{COUNT}} Available Tools": "",
"{{COUNT}} hidden lines": "{{COUNT}} peidetud rida",
"{{COUNT}} Replies": "{{COUNT}} vastust",
"{{user}}'s Chats": "{{user}} vestlused",
@ -108,6 +108,7 @@
"Attribute for Username": "Kasutajanime atribuut",
"Audio": "Heli",
"August": "August",
"Auth": "",
"Authenticate": "Autendi",
"Authentication": "Autentimine",
"Auto-Copy Response to Clipboard": "Kopeeri vastus automaatselt lõikelauale",
@ -119,7 +120,7 @@
"AUTOMATIC1111 Base URL": "AUTOMATIC1111 baas-URL",
"AUTOMATIC1111 Base URL is required.": "AUTOMATIC1111 baas-URL on nõutav.",
"Available list": "Saadaolevate nimekiri",
"Available Tool Servers": "",
"Available Tools": "",
"available!": "saadaval!",
"Awful": "Kohutav",
"Azure AI Speech": "Azure AI Kõne",
@ -218,7 +219,10 @@
"Confirm your new password": "Kinnita oma uus parool",
"Connect to your own OpenAI compatible API endpoints.": "Ühendu oma OpenAI-ga ühilduvate API lõpp-punktidega.",
"Connect to your own OpenAPI compatible external tool servers.": "",
"Connection failed": "",
"Connection successful": "",
"Connections": "Ühendused",
"Connections saved successfully": "",
"Constrains effort on reasoning for reasoning models. Only applicable to reasoning models from specific providers that support reasoning effort.": "Piirab arutluse pingutust arutlusvõimelistele mudelitele. Kohaldatav ainult konkreetsete pakkujate arutlusmudelitele, mis toetavad arutluspingutust.",
"Contact Admin for WebUI Access": "Võtke WebUI juurdepääsu saamiseks ühendust administraatoriga",
"Content": "Sisu",
@ -303,6 +307,7 @@
"Direct Connections": "Otsesed ühendused",
"Direct Connections allow users to connect to their own OpenAI compatible API endpoints.": "Otsesed ühendused võimaldavad kasutajatel ühenduda oma OpenAI-ga ühilduvate API lõpp-punktidega.",
"Direct Connections settings updated": "Otseste ühenduste seaded uuendatud",
"Direct Tool Servers": "",
"Disabled": "Keelatud",
"Discover a function": "Avasta funktsioon",
"Discover a model": "Avasta mudel",
@ -412,6 +417,7 @@
"Enter Kagi Search API Key": "Sisestage Kagi Search API võti",
"Enter Key Behavior": "Sisestage võtme käitumine",
"Enter language codes": "Sisestage keelekoodid",
"Enter Mistral API Key": "",
"Enter Model ID": "Sisestage mudeli ID",
"Enter model tag (e.g. {{modelTag}})": "Sisestage mudeli silt (nt {{modelTag}})",
"Enter Mojeek Search API Key": "Sisestage Mojeek Search API võti",
@ -436,6 +442,7 @@
"Enter server port": "Sisestage serveri port",
"Enter stop sequence": "Sisestage lõpetamise järjestus",
"Enter system prompt": "Sisestage süsteemi vihjed",
"Enter system prompt here": "",
"Enter Tavily API Key": "Sisestage Tavily API võti",
"Enter the public URL of your WebUI. This URL will be used to generate links in the notifications.": "Sisestage oma WebUI avalik URL. Seda URL-i kasutatakse teadaannetes linkide genereerimiseks.",
"Enter Tika Server URL": "Sisestage Tika serveri URL",
@ -449,6 +456,7 @@
"Enter Your Email": "Sisestage oma e-post",
"Enter Your Full Name": "Sisestage oma täisnimi",
"Enter your message": "Sisestage oma sõnum",
"Enter your name": "",
"Enter your new password": "Sisestage oma uus parool",
"Enter Your Password": "Sisestage oma parool",
"Enter Your Role": "Sisestage oma roll",
@ -468,7 +476,7 @@
"Exceeded the number of seats in your license. Please contact support to increase the number of seats.": "Ületasite litsentsis määratud istekohtade arvu. Palun võtke ühendust toega, et suurendada istekohtade arvu.",
"Exclude": "Välista",
"Execute code for analysis": "Käivita kood analüüsimiseks",
"Executing `{{NAME}}`...": "",
"Executing **{{NAME}}**...": "",
"Expand": "Laienda",
"Experimental": "Katsetuslik",
"Explain": "Selgita",
@ -493,6 +501,7 @@
"Failed to create API Key.": "API võtme loomine ebaõnnestus.",
"Failed to fetch models": "Mudelite toomine ebaõnnestus",
"Failed to read clipboard contents": "Lõikelaua sisu lugemine ebaõnnestus",
"Failed to save connections": "",
"Failed to save models configuration": "Mudelite konfiguratsiooni salvestamine ebaõnnestus",
"Failed to update settings": "Seadete uuendamine ebaõnnestus",
"Failed to upload file.": "Faili üleslaadimine ebaõnnestus.",
@ -525,6 +534,7 @@
"Forge new paths": "Loo uusi radu",
"Form": "Vorm",
"Format your variables using brackets like this:": "Vormindage oma muutujad sulgudega nagu siin:",
"Forwards system user session credentials to authenticate": "",
"Frequency Penalty": "Sageduse karistus",
"Full Context Mode": "Täiskonteksti režiim",
"Function": "Funktsioon",
@ -694,6 +704,8 @@
"Mirostat": "Mirostat",
"Mirostat Eta": "Mirostat Eta",
"Mirostat Tau": "Mirostat Tau",
"Mistral OCR": "",
"Mistral OCR API Key required.": "",
"Model": "Mudel",
"Model '{{modelName}}' has been successfully downloaded.": "Mudel '{{modelName}}' on edukalt alla laaditud.",
"Model '{{modelTag}}' is already in queue for downloading.": "Mudel '{{modelTag}}' on juba allalaadimise järjekorras.",
@ -789,6 +801,7 @@
"OpenAI API Key is required.": "OpenAI API võti on nõutav.",
"OpenAI API settings updated": "OpenAI API seaded uuendatud",
"OpenAI URL/Key required.": "OpenAI URL/võti on nõutav.",
"openapi.json Path": "",
"or": "või",
"Organize your users": "Korraldage oma kasutajad",
"Other": "Muu",
@ -820,6 +833,8 @@
"Please carefully review the following warnings:": "Palun vaadake hoolikalt läbi järgmised hoiatused:",
"Please do not close the settings page while loading the model.": "Palun ärge sulgege seadete lehte mudeli laadimise ajal.",
"Please enter a prompt": "Palun sisestage vihje",
"Please enter a valid path": "",
"Please enter a valid URL": "",
"Please fill in all fields.": "Palun täitke kõik väljad.",
"Please select a model first.": "Palun valige esmalt mudel.",
"Please select a model.": "Palun valige mudel.",
@ -1042,6 +1057,7 @@
"Thinking...": "Mõtleb...",
"This action cannot be undone. Do you wish to continue?": "Seda toimingut ei saa tagasi võtta. Kas soovite jätkata?",
"This channel was created on {{createdAt}}. This is the very beginning of the {{channelName}} channel.": "",
"This chat wont appear in history and your messages will not be saved.": "",
"This ensures that your valuable conversations are securely saved to your backend database. Thank you!": "See tagab, et teie väärtuslikud vestlused salvestatakse turvaliselt teie tagarakenduse andmebaasi. Täname!",
"This is an experimental feature, it may not function as expected and is subject to change at any time.": "See on katsetuslik funktsioon, see ei pruugi toimida ootuspäraselt ja võib igal ajal muutuda.",
"This option controls how many tokens are preserved when refreshing the context. For example, if set to 2, the last 2 tokens of the conversation context will be retained. Preserving context can help maintain the continuity of a conversation, but it may reduce the ability to respond to new topics.": "See valik kontrollib, mitu tokenit säilitatakse konteksti värskendamisel. Näiteks kui see on määratud 2-le, säilitatakse vestluse konteksti viimased 2 tokenit. Konteksti säilitamine võib aidata säilitada vestluse järjepidevust, kuid võib vähendada võimet reageerida uutele teemadele.",
@ -1089,6 +1105,7 @@
"Tool ID": "Tööriista ID",
"Tool imported successfully": "Tööriist edukalt imporditud",
"Tool Name": "Tööriista nimi",
"Tool Servers": "",
"Tool updated successfully": "Tööriist edukalt uuendatud",
"Tools": "Tööriistad",
"Tools Access": "Tööriistade juurdepääs",
@ -1158,7 +1175,7 @@
"Version": "Versioon",
"Version {{selectedVersion}} of {{totalVersions}}": "Versioon {{selectedVersion}} / {{totalVersions}}",
"View Replies": "Vaata vastuseid",
"View Result from `{{NAME}}`": "",
"View Result from **{{NAME}}**": "",
"Visibility": "Nähtavus",
"Voice": "Hääl",
"Voice Input": "Hääle sisend",
@ -1176,9 +1193,9 @@
"Webhook URL": "Webhooki URL",
"WebUI Settings": "WebUI seaded",
"WebUI URL": "WebUI URL",
"WebUI will make requests to \"{{url}}\"": "",
"WebUI will make requests to \"{{url}}/api/chat\"": "WebUI teeb päringuid aadressile \"{{url}}/api/chat\"",
"WebUI will make requests to \"{{url}}/chat/completions\"": "WebUI teeb päringuid aadressile \"{{url}}/chat/completions\"",
"WebUI will make requests to \"{{url}}/openapi.json\"": "",
"What are you trying to achieve?": "Mida te püüate saavutada?",
"What are you working on?": "Millega te tegelete?",
"Whats New in": "Mis on uut",

View file

@ -6,7 +6,7 @@
"(latest)": "(azkena)",
"(Ollama)": "",
"{{ models }}": "{{ models }}",
"{{COUNT}} Available Tool Servers": "",
"{{COUNT}} Available Tools": "",
"{{COUNT}} hidden lines": "",
"{{COUNT}} Replies": "",
"{{user}}'s Chats": "{{user}}-ren Txatak",
@ -108,6 +108,7 @@
"Attribute for Username": "Erabiltzaile-izenerako atributua",
"Audio": "Audioa",
"August": "Abuztua",
"Auth": "",
"Authenticate": "Autentifikatu",
"Authentication": "",
"Auto-Copy Response to Clipboard": "Automatikoki Kopiatu Erantzuna Arbelera",
@ -119,7 +120,7 @@
"AUTOMATIC1111 Base URL": "AUTOMATIC1111 Oinarri URLa",
"AUTOMATIC1111 Base URL is required.": "AUTOMATIC1111 Oinarri URLa beharrezkoa da.",
"Available list": "Zerrenda erabilgarria",
"Available Tool Servers": "",
"Available Tools": "",
"available!": "eskuragarri!",
"Awful": "Penagarria",
"Azure AI Speech": "Azure AI Hizketa",
@ -218,7 +219,10 @@
"Confirm your new password": "",
"Connect to your own OpenAI compatible API endpoints.": "",
"Connect to your own OpenAPI compatible external tool servers.": "",
"Connection failed": "",
"Connection successful": "",
"Connections": "Konexioak",
"Connections saved successfully": "",
"Constrains effort on reasoning for reasoning models. Only applicable to reasoning models from specific providers that support reasoning effort.": "",
"Contact Admin for WebUI Access": "Jarri harremanetan Administratzailearekin WebUI Sarbiderako",
"Content": "Edukia",
@ -303,6 +307,7 @@
"Direct Connections": "",
"Direct Connections allow users to connect to their own OpenAI compatible API endpoints.": "",
"Direct Connections settings updated": "",
"Direct Tool Servers": "",
"Disabled": "Desgaituta",
"Discover a function": "Aurkitu funtzio bat",
"Discover a model": "Aurkitu eredu bat",
@ -412,6 +417,7 @@
"Enter Kagi Search API Key": "",
"Enter Key Behavior": "",
"Enter language codes": "Sartu hizkuntza kodeak",
"Enter Mistral API Key": "",
"Enter Model ID": "Sartu Eredu IDa",
"Enter model tag (e.g. {{modelTag}})": "Sartu eredu etiketa (adib. {{modelTag}})",
"Enter Mojeek Search API Key": "Sartu Mojeek Bilaketa API Gakoa",
@ -436,6 +442,7 @@
"Enter server port": "Sartu zerbitzariaren portua",
"Enter stop sequence": "Sartu gelditze sekuentzia",
"Enter system prompt": "Sartu sistema prompta",
"Enter system prompt here": "",
"Enter Tavily API Key": "Sartu Tavily API Gakoa",
"Enter the public URL of your WebUI. This URL will be used to generate links in the notifications.": "",
"Enter Tika Server URL": "Sartu Tika Zerbitzari URLa",
@ -449,6 +456,7 @@
"Enter Your Email": "Sartu Zure Posta Elektronikoa",
"Enter Your Full Name": "Sartu Zure Izen-abizenak",
"Enter your message": "Sartu zure mezua",
"Enter your name": "",
"Enter your new password": "",
"Enter Your Password": "Sartu Zure Pasahitza",
"Enter Your Role": "Sartu Zure Rola",
@ -468,7 +476,7 @@
"Exceeded the number of seats in your license. Please contact support to increase the number of seats.": "",
"Exclude": "Baztertu",
"Execute code for analysis": "",
"Executing `{{NAME}}`...": "",
"Executing **{{NAME}}**...": "",
"Expand": "",
"Experimental": "Esperimentala",
"Explain": "",
@ -493,6 +501,7 @@
"Failed to create API Key.": "Huts egin du API Gakoa sortzean.",
"Failed to fetch models": "",
"Failed to read clipboard contents": "Huts egin du arbelaren edukia irakurtzean",
"Failed to save connections": "",
"Failed to save models configuration": "Huts egin du ereduen konfigurazioa gordetzean",
"Failed to update settings": "Huts egin du ezarpenak eguneratzean",
"Failed to upload file.": "Huts egin du fitxategia igotzean.",
@ -525,6 +534,7 @@
"Forge new paths": "Sortu bide berriak",
"Form": "Inprimakia",
"Format your variables using brackets like this:": "Formateatu zure aldagaiak kortxeteak erabiliz honela:",
"Forwards system user session credentials to authenticate": "",
"Frequency Penalty": "Maiztasun Zigorra",
"Full Context Mode": "",
"Function": "Funtzioa",
@ -694,6 +704,8 @@
"Mirostat": "Mirostat",
"Mirostat Eta": "Mirostat Eta",
"Mirostat Tau": "Mirostat Tau",
"Mistral OCR": "",
"Mistral OCR API Key required.": "",
"Model": "Modeloa",
"Model '{{modelName}}' has been successfully downloaded.": "'{{modelName}}' modeloa ongi deskargatu da.",
"Model '{{modelTag}}' is already in queue for downloading.": "'{{modelTag}}' modeloa dagoeneko deskarga ilaran dago.",
@ -789,6 +801,7 @@
"OpenAI API Key is required.": "OpenAI API gakoa beharrezkoa da.",
"OpenAI API settings updated": "OpenAI API ezarpenak eguneratu dira",
"OpenAI URL/Key required.": "OpenAI URL/Gakoa beharrezkoa da.",
"openapi.json Path": "",
"or": "edo",
"Organize your users": "Antolatu zure erabiltzaileak",
"Other": "Bestelakoa",
@ -820,6 +833,8 @@
"Please carefully review the following warnings:": "Mesedez, berrikusi arretaz hurrengo oharrak:",
"Please do not close the settings page while loading the model.": "",
"Please enter a prompt": "Mesedez, sartu prompt bat",
"Please enter a valid path": "",
"Please enter a valid URL": "",
"Please fill in all fields.": "Mesedez, bete eremu guztiak.",
"Please select a model first.": "",
"Please select a model.": "",
@ -1042,6 +1057,7 @@
"Thinking...": "Pentsatzen...",
"This action cannot be undone. Do you wish to continue?": "Ekintza hau ezin da desegin. Jarraitu nahi duzu?",
"This channel was created on {{createdAt}}. This is the very beginning of the {{channelName}} channel.": "",
"This chat wont appear in history and your messages will not be saved.": "",
"This ensures that your valuable conversations are securely saved to your backend database. Thank you!": "Honek zure elkarrizketa baliotsuak modu seguruan zure backend datu-basean gordeko direla ziurtatzen du. Eskerrik asko!",
"This is an experimental feature, it may not function as expected and is subject to change at any time.": "Hau funtzionalitate esperimental bat da, baliteke espero bezala ez funtzionatzea eta edozein unetan aldaketak izatea.",
"This option controls how many tokens are preserved when refreshing the context. For example, if set to 2, the last 2 tokens of the conversation context will be retained. Preserving context can help maintain the continuity of a conversation, but it may reduce the ability to respond to new topics.": "",
@ -1089,6 +1105,7 @@
"Tool ID": "Tresna ID",
"Tool imported successfully": "Tresna ongi inportatu da",
"Tool Name": "Tresnaren izena",
"Tool Servers": "",
"Tool updated successfully": "Tresna ongi eguneratu da",
"Tools": "Tresnak",
"Tools Access": "Tresnen sarbidea",
@ -1158,7 +1175,7 @@
"Version": "Bertsioa",
"Version {{selectedVersion}} of {{totalVersions}}": "{{totalVersions}}-tik {{selectedVersion}}. bertsioa",
"View Replies": "",
"View Result from `{{NAME}}`": "",
"View Result from **{{NAME}}**": "",
"Visibility": "Ikusgarritasuna",
"Voice": "Ahotsa",
"Voice Input": "Ahots sarrera",
@ -1176,9 +1193,9 @@
"Webhook URL": "Webhook URLa",
"WebUI Settings": "WebUI ezarpenak",
"WebUI URL": "",
"WebUI will make requests to \"{{url}}\"": "",
"WebUI will make requests to \"{{url}}/api/chat\"": "WebUI-k eskaerak egingo ditu \"{{url}}/api/chat\"-era",
"WebUI will make requests to \"{{url}}/chat/completions\"": "WebUI-k eskaerak egingo ditu \"{{url}}/chat/completions\"-era",
"WebUI will make requests to \"{{url}}/openapi.json\"": "",
"What are you trying to achieve?": "Zer lortu nahi duzu?",
"What are you working on?": "Zertan ari zara lanean?",
"Whats New in": "Zer berri honetan:",

View file

@ -6,7 +6,7 @@
"(latest)": "(آخرین)",
"(Ollama)": "",
"{{ models }}": "{{ models }}",
"{{COUNT}} Available Tool Servers": "",
"{{COUNT}} Available Tools": "",
"{{COUNT}} hidden lines": "",
"{{COUNT}} Replies": "",
"{{user}}'s Chats": "{{user}} گفتگوهای",
@ -108,6 +108,7 @@
"Attribute for Username": "",
"Audio": "صدا",
"August": "آگوست",
"Auth": "",
"Authenticate": "",
"Authentication": "",
"Auto-Copy Response to Clipboard": "کپی خودکار پاسخ به کلیپ بورد",
@ -119,7 +120,7 @@
"AUTOMATIC1111 Base URL": "پایه URL AUTOMATIC1111 ",
"AUTOMATIC1111 Base URL is required.": "به URL پایه AUTOMATIC1111 مورد نیاز است.",
"Available list": "فهرست دردسترس",
"Available Tool Servers": "",
"Available Tools": "",
"available!": "در دسترس!",
"Awful": "",
"Azure AI Speech": "سخنگوی هوش\u200cمصنوعی Azure",
@ -218,7 +219,10 @@
"Confirm your new password": "",
"Connect to your own OpenAI compatible API endpoints.": "",
"Connect to your own OpenAPI compatible external tool servers.": "",
"Connection failed": "",
"Connection successful": "",
"Connections": "ارتباطات",
"Connections saved successfully": "",
"Constrains effort on reasoning for reasoning models. Only applicable to reasoning models from specific providers that support reasoning effort.": "",
"Contact Admin for WebUI Access": "برای دسترسی به WebUI با مدیر تماس بگیرید",
"Content": "محتوا",
@ -303,6 +307,7 @@
"Direct Connections": "",
"Direct Connections allow users to connect to their own OpenAI compatible API endpoints.": "",
"Direct Connections settings updated": "",
"Direct Tool Servers": "",
"Disabled": "",
"Discover a function": "",
"Discover a model": "کشف یک مدل",
@ -412,6 +417,7 @@
"Enter Kagi Search API Key": "",
"Enter Key Behavior": "",
"Enter language codes": "کد زبان را وارد کنید",
"Enter Mistral API Key": "",
"Enter Model ID": "",
"Enter model tag (e.g. {{modelTag}})": "تگ مدل را وارد کنید (مثلا {{modelTag}})",
"Enter Mojeek Search API Key": "",
@ -436,6 +442,7 @@
"Enter server port": "",
"Enter stop sequence": "توالی توقف را وارد کنید",
"Enter system prompt": "",
"Enter system prompt here": "",
"Enter Tavily API Key": "",
"Enter the public URL of your WebUI. This URL will be used to generate links in the notifications.": "",
"Enter Tika Server URL": "",
@ -449,6 +456,7 @@
"Enter Your Email": "ایمیل خود را وارد کنید",
"Enter Your Full Name": "نام کامل خود را وارد کنید",
"Enter your message": "",
"Enter your name": "",
"Enter your new password": "",
"Enter Your Password": "رمز عبور خود را وارد کنید",
"Enter Your Role": "نقش خود را وارد کنید",
@ -468,7 +476,7 @@
"Exceeded the number of seats in your license. Please contact support to increase the number of seats.": "",
"Exclude": "",
"Execute code for analysis": "",
"Executing `{{NAME}}`...": "",
"Executing **{{NAME}}**...": "",
"Expand": "",
"Experimental": "آزمایشی",
"Explain": "",
@ -493,6 +501,7 @@
"Failed to create API Key.": "ایجاد کلید API با خطا مواجه شد.",
"Failed to fetch models": "",
"Failed to read clipboard contents": "خواندن محتوای کلیپ بورد ناموفق بود",
"Failed to save connections": "",
"Failed to save models configuration": "",
"Failed to update settings": "خطا در به\u200cروزرسانی تنظیمات",
"Failed to upload file.": "خطا در بارگذاری پرونده",
@ -525,6 +534,7 @@
"Forge new paths": "",
"Form": "",
"Format your variables using brackets like this:": "",
"Forwards system user session credentials to authenticate": "",
"Frequency Penalty": "مجازات فرکانس",
"Full Context Mode": "",
"Function": "",
@ -694,6 +704,8 @@
"Mirostat": "Mirostat",
"Mirostat Eta": "Mirostat Eta",
"Mirostat Tau": "Mirostat Tau",
"Mistral OCR": "",
"Mistral OCR API Key required.": "",
"Model": "",
"Model '{{modelName}}' has been successfully downloaded.": "مدل '{{modelName}}' با موفقیت دانلود شد.",
"Model '{{modelTag}}' is already in queue for downloading.": "مدل '{{modelTag}}' در حال حاضر در صف برای دانلود است.",
@ -789,6 +801,7 @@
"OpenAI API Key is required.": "مقدار کلید OpenAI API مورد نیاز است.",
"OpenAI API settings updated": "",
"OpenAI URL/Key required.": "URL/Key OpenAI مورد نیاز است.",
"openapi.json Path": "",
"or": "یا",
"Organize your users": "",
"Other": "دیگر",
@ -820,6 +833,8 @@
"Please carefully review the following warnings:": "",
"Please do not close the settings page while loading the model.": "",
"Please enter a prompt": "",
"Please enter a valid path": "",
"Please enter a valid URL": "",
"Please fill in all fields.": "",
"Please select a model first.": "",
"Please select a model.": "",
@ -1042,6 +1057,7 @@
"Thinking...": "در حال فکر...",
"This action cannot be undone. Do you wish to continue?": "این اقدام قابل بازگردانی نیست. برای ادامه اطمینان دارید؟",
"This channel was created on {{createdAt}}. This is the very beginning of the {{channelName}} channel.": "",
"This chat wont appear in history and your messages will not be saved.": "",
"This ensures that your valuable conversations are securely saved to your backend database. Thank you!": "این تضمین می کند که مکالمات ارزشمند شما به طور ایمن در پایگاه داده بکند ذخیره می شود. تشکر!",
"This is an experimental feature, it may not function as expected and is subject to change at any time.": "",
"This option controls how many tokens are preserved when refreshing the context. For example, if set to 2, the last 2 tokens of the conversation context will be retained. Preserving context can help maintain the continuity of a conversation, but it may reduce the ability to respond to new topics.": "",
@ -1089,6 +1105,7 @@
"Tool ID": "",
"Tool imported successfully": "",
"Tool Name": "",
"Tool Servers": "",
"Tool updated successfully": "",
"Tools": "",
"Tools Access": "",
@ -1158,7 +1175,7 @@
"Version": "نسخه",
"Version {{selectedVersion}} of {{totalVersions}}": "نسخهٔ {{selectedVersion}} از {{totalVersions}}",
"View Replies": "",
"View Result from `{{NAME}}`": "",
"View Result from **{{NAME}}**": "",
"Visibility": "",
"Voice": "صوت",
"Voice Input": "ورودی صوتی",
@ -1176,9 +1193,9 @@
"Webhook URL": "نشانی وب\u200cهوک",
"WebUI Settings": "تنظیمات WebUI",
"WebUI URL": "",
"WebUI will make requests to \"{{url}}\"": "",
"WebUI will make requests to \"{{url}}/api/chat\"": "",
"WebUI will make requests to \"{{url}}/chat/completions\"": "",
"WebUI will make requests to \"{{url}}/openapi.json\"": "",
"What are you trying to achieve?": "",
"What are you working on?": "",
"Whats New in": "موارد جدید در",

View file

@ -4,9 +4,9 @@
"(e.g. `sh webui.sh --api --api-auth username_password`)": "(esim. `sh webui.sh --api --api-auth username_password`)",
"(e.g. `sh webui.sh --api`)": "(esim. `sh webui.sh --api`)",
"(latest)": "(uusin)",
"(Ollama)": "",
"(Ollama)": "(Ollama)",
"{{ models }}": "{{ mallit }}",
"{{COUNT}} Available Tool Servers": "",
"{{COUNT}} Available Tools": "{{COUNT}} työkalua saatavilla",
"{{COUNT}} hidden lines": "{{COUNT}} piilotettua riviä",
"{{COUNT}} Replies": "{{COUNT}} vastausta",
"{{user}}'s Chats": "{{user}}:n keskustelut",
@ -16,7 +16,7 @@
"A task model is used when performing tasks such as generating titles for chats and web search queries": "Tehtävämallia käytetään tehtävien suorittamiseen, kuten otsikoiden luomiseen keskusteluille ja verkkohakukyselyille",
"a user": "käyttäjä",
"About": "Tietoja",
"Accept autocomplete generation / Jump to prompt variable": "",
"Accept autocomplete generation / Jump to prompt variable": "Hyväksy automaattinen täyttö / Siirry kehotteen muuttujaan",
"Access": "Pääsy",
"Access Control": "Käyttöoikeuksien hallinta",
"Accessible to all users": "Käytettävissä kaikille käyttäjille",
@ -31,7 +31,7 @@
"Add a model ID": "Lisää mallitunnus",
"Add a short description about what this model does": "Lisää lyhyt kuvaus siitä, mitä tämä malli tekee",
"Add a tag": "Lisää tagi",
"Add Arena Model": "Lisää Arena-malli",
"Add Arena Model": "Lisää Areena-malli",
"Add Connection": "Lisää yhteys",
"Add Content": "Lisää sisältöä",
"Add content here": "Lisää sisältöä tähän",
@ -70,8 +70,8 @@
"Already have an account?": "Onko sinulla jo tili?",
"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": "Aina",
"Always Collapse Code Blocks": "",
"Always Expand Details": "",
"Always Collapse Code Blocks": "Pienennä aina koodilohkot",
"Always Expand Details": "Laajenna aina tiedot",
"Amazing": "Hämmästyttävä",
"an assistant": "avustaja",
"Analyzed": "Analysoitu",
@ -79,7 +79,7 @@
"and": "ja",
"and {{COUNT}} more": "ja {{COUNT}} muuta",
"and create a new shared link.": "ja luo uusi jaettu linkki.",
"API Base URL": "APIn perus-URL",
"API Base URL": "API:n verkko-osoite",
"API Key": "API-avain",
"API Key created.": "API-avain luotu.",
"API Key Endpoint Restrictions": "API-avaimen päätepiste rajoitukset",
@ -92,7 +92,7 @@
"Archive All Chats": "Arkistoi kaikki keskustelut",
"Archived Chats": "Arkistoidut keskustelut",
"archived-chat-export": "arkistoitu-keskustelu-vienti",
"Are you sure you want to clear all memories? This action cannot be undone.": "",
"Are you sure you want to clear all memories? This action cannot be undone.": "Haluatko varmasti tyhjentää kaikki muistot? Tätä toimintoa ei voi peruuttaa.",
"Are you sure you want to delete this channel?": "Haluatko varmasti poistaa tämän kanavan?",
"Are you sure you want to delete this message?": "Haluatko varmasti poistaa tämän viestin?",
"Are you sure you want to unarchive all archived chats?": "Haluatko varmasti purkaa kaikkien arkistoitujen keskustelujen arkistoinnin?",
@ -108,6 +108,7 @@
"Attribute for Username": "Käyttäjänimi-määritämä",
"Audio": "Ääni",
"August": "elokuu",
"Auth": "Todennus",
"Authenticate": "Todentaa",
"Authentication": "Todennus",
"Auto-Copy Response to Clipboard": "Kopioi vastaus automaattisesti leikepöydälle",
@ -116,10 +117,10 @@
"Autocomplete Generation Input Max Length": "Automaattisen täydennyksen syötteen enimmäispituus",
"Automatic1111": "Automatic1111",
"AUTOMATIC1111 Api Auth String": "AUTOMATIC1111 API:n todennusmerkkijono",
"AUTOMATIC1111 Base URL": "AUTOMATIC1111-perus-URL",
"AUTOMATIC1111 Base URL is required.": "AUTOMATIC1111-perus-URL vaaditaan.",
"AUTOMATIC1111 Base URL": "AUTOMATIC1111 verkko-osoite",
"AUTOMATIC1111 Base URL is required.": "AUTOMATIC1111 verkko-osoite vaaditaan.",
"Available list": "Käytettävissä oleva luettelo",
"Available Tool Servers": "",
"Available Tools": "Käytettävissä olevat työkalut",
"available!": "saatavilla!",
"Awful": "Kauhea",
"Azure AI Speech": "Azure AI Speech",
@ -204,8 +205,8 @@
"Color": "Väri",
"ComfyUI": "ComfyUI",
"ComfyUI API Key": "ComfyUI API -avain",
"ComfyUI Base URL": "ComfyUI-perus-URL",
"ComfyUI Base URL is required.": "ComfyUI-perus-URL vaaditaan.",
"ComfyUI Base URL": "ComfyUI verkko-osoite",
"ComfyUI Base URL is required.": "ComfyUI verkko-osoite vaaditaan.",
"ComfyUI Workflow": "ComfyUI-työnkulku",
"ComfyUI Workflow Nodes": "ComfyUI-työnkulun solmut",
"Command": "Komento",
@ -216,9 +217,12 @@
"Confirm Password": "Vahvista salasana",
"Confirm your action": "Vahvista toimintasi",
"Confirm your new password": "Vahvista uusi salasanasi",
"Connect to your own OpenAI compatible API endpoints.": "Yhdistä oma OpenAI yhteensopiva API päätepiste.",
"Connect to your own OpenAPI compatible external tool servers.": "",
"Connect to your own OpenAI compatible API endpoints.": "Yhdistä omat OpenAI yhteensopivat API päätepisteet.",
"Connect to your own OpenAPI compatible external tool servers.": "Yhdistä omat ulkopuoliset OpenAPI yhteensopivat työkalu palvelimet.",
"Connection failed": "",
"Connection successful": "",
"Connections": "Yhteydet",
"Connections saved successfully": "",
"Constrains effort on reasoning for reasoning models. Only applicable to reasoning models from specific providers that support reasoning effort.": "",
"Contact Admin for WebUI Access": "Ota yhteyttä ylläpitäjään WebUI-käyttöä varten",
"Content": "Sisältö",
@ -231,7 +235,7 @@
"Control how message text is split for TTS requests. 'Punctuation' splits into sentences, 'paragraphs' splits into paragraphs, and 'none' keeps the message as a single string.": "Säädä, miten viestin teksti jaetaan puhesynteesipyyntöjä varten. 'Välimerkit' jakaa lauseisiin, 'kappaleet' jakaa kappaleisiin ja 'ei mitään' pitää viestin yhtenä merkkijonona.",
"Control the repetition of token sequences in the generated text. A higher value (e.g., 1.5) will penalize repetitions more strongly, while a lower value (e.g., 1.1) will be more lenient. At 1, it is disabled.": "",
"Controls": "Ohjaimet",
"Controls the balance between coherence and diversity of the output. A lower value will result in more focused and coherent text.": "",
"Controls the balance between coherence and diversity of the output. A lower value will result in more focused and coherent text.": "Kontrolloi yhtenäisyyden ja monimuotoisuuden tasapainoa tuloksessa. Pienempi arvo tuottaa kohdennetumman ja johdonmukaisemman tekstin.",
"Copied": "Kopioitu",
"Copied shared chat URL to clipboard!": "Jaettu keskustelulinkki kopioitu leikepöydälle!",
"Copied to clipboard": "Kopioitu leikepöydälle",
@ -276,7 +280,7 @@
"Default Prompt Suggestions": "Oletuskehotteiden ehdotukset",
"Default to 389 or 636 if TLS is enabled": "Oletus 389 tai 636, jos TLS on käytössä",
"Default to ALL": "Oletus KAIKKI",
"Default to segmented retrieval for focused and relevant content extraction, this is recommended for most cases.": "",
"Default to segmented retrieval for focused and relevant content extraction, this is recommended for most cases.": "Segmentoitu haku on oletuksena kohdennettua ja relevanttia sisällön poimimista varten. Tätä suositellaan useimmissa tapauksissa.",
"Default User Role": "Oletuskäyttäjärooli",
"Delete": "Poista",
"Delete a model": "Poista malli",
@ -299,10 +303,11 @@
"Describe your knowledge base and objectives": "Kuvaa tietokantasi ja tavoitteesi",
"Description": "Kuvaus",
"Didn't fully follow instructions": "Ei noudattanut ohjeita täysin",
"Direct": "",
"Direct": "Suora",
"Direct Connections": "Suorat yhteydet",
"Direct Connections allow users to connect to their own OpenAI compatible API endpoints.": "Suorat yhteydet mahdollistavat käyttäjien yhdistää omia OpenAI-yhteensopivia API-päätepisteitä.",
"Direct Connections settings updated": "Suorien yhteyksien asetukset päivitetty",
"Direct Tool Servers": "Suorat työkalu palvelimet",
"Disabled": "Ei käytössä",
"Discover a function": "Löydä toiminto",
"Discover a model": "Tutustu malliin",
@ -322,11 +327,11 @@
"Dive into knowledge": "Uppoudu tietoon",
"Do not install functions from sources you do not fully trust.": "Älä asenna toimintoja lähteistä, joihin et luota täysin.",
"Do not install tools from sources you do not fully trust.": "Älä asenna työkaluja lähteistä, joihin et luota täysin.",
"Docling": "",
"Docling Server URL required.": "",
"Docling": "Docling",
"Docling Server URL required.": "Docling palvelimen verkko-osoite vaaditaan.",
"Document": "Asiakirja",
"Document Intelligence": "",
"Document Intelligence endpoint and key required.": "",
"Document Intelligence": "Asiakirja tiedustelu",
"Document Intelligence endpoint and key required.": "Asiakirja tiedustelun päätepiste ja avain vaaditaan.",
"Documentation": "Dokumentaatio",
"Documents": "Asiakirjat",
"does not make any external connections, and your data stays securely on your locally hosted server.": "ei tee ulkoisia yhteyksiä, ja tietosi pysyvät turvallisesti paikallisesti isännöidyllä palvelimellasi.",
@ -344,7 +349,7 @@
"Draw": "Piirros",
"Drop any files here to add to the conversation": "Pudota tiedostoja tähän lisätäksesi ne keskusteluun",
"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "esim. '30s', '10m'. Kelpoiset aikayksiköt ovat 's', 'm', 'h'.",
"e.g. \"json\" or a JSON schema": "",
"e.g. \"json\" or a JSON schema": "esim. \"json\" tai JSON kaava",
"e.g. 60": "esim. 60",
"e.g. A filter to remove profanity from text": "esim. suodatin, joka poistaa kirosanoja tekstistä",
"e.g. My Filter": "esim. Oma suodatin",
@ -379,7 +384,7 @@
"Enable Mirostat sampling for controlling perplexity.": "",
"Enable New Sign Ups": "Salli uudet rekisteröitymiset",
"Enabled": "Käytössä",
"Enforce Temporary Chat": "",
"Enforce Temporary Chat": "Pakota väliaikaiset keskustelut",
"Ensure your CSV file includes 4 columns in this order: Name, Email, Password, Role.": "Varmista, että CSV-tiedostossasi on 4 saraketta tässä järjestyksessä: Nimi, Sähköposti, Salasana, Rooli.",
"Enter {{role}} message here": "Kirjoita {{role}}-viesti tähän",
"Enter a detail about yourself for your LLMs to recall": "Kirjoita yksityiskohta itsestäsi, jonka LLM-ohjelmat voivat muistaa",
@ -396,9 +401,9 @@
"Enter Chunk Size": "Syötä osien koko",
"Enter comma-seperated \"token:bias_value\" pairs (example: 5432:100, 413:-100)": "",
"Enter description": "Kirjoita kuvaus",
"Enter Docling Server URL": "",
"Enter Document Intelligence Endpoint": "",
"Enter Document Intelligence Key": "",
"Enter Docling Server URL": "Kirjoita Docling palvelimen verkko-osoite",
"Enter Document Intelligence Endpoint": "Kirjoita asiakirja tiedustelun päätepiste",
"Enter Document Intelligence Key": "Kirjoiuta asiakirja tiedustelun avain",
"Enter domains separated by commas (e.g., example.com,site.org)": "Verkko-osoitteet erotetaan pilkulla (esim. esimerkki.com,sivu.org)",
"Enter Exa API Key": "Kirjoita Exa API -avain",
"Enter Github Raw URL": "Kirjoita Github Raw -verkko-osoite",
@ -412,6 +417,7 @@
"Enter Kagi Search API Key": "Kirjoita Kagi Search API -avain",
"Enter Key Behavior": "Enter näppäimen käyttäytyminen",
"Enter language codes": "Kirjoita kielikoodit",
"Enter Mistral API Key": "Kirjoita Mistral API-avain",
"Enter Model ID": "Kirjoita mallitunnus",
"Enter model tag (e.g. {{modelTag}})": "Kirjoita mallitagi (esim. {{modelTag}})",
"Enter Mojeek Search API Key": "Kirjoita Mojeek Search API -avain",
@ -436,19 +442,21 @@
"Enter server port": "Kirjoita palvelimen portti",
"Enter stop sequence": "Kirjoita lopetussekvenssi",
"Enter system prompt": "Kirjoita järjestelmäkehote",
"Enter system prompt here": "Kirjoita järjestelmäkehote tähän",
"Enter Tavily API Key": "Kirjoita Tavily API -avain",
"Enter the public URL of your WebUI. This URL will be used to generate links in the notifications.": "Kirjoita julkinen WebUI verkko-osoitteesi. Verkko-osoitetta käytetään osoitteiden luontiin ilmoituksissa.",
"Enter Tika Server URL": "Kirjoita Tika Server URL",
"Enter timeout in seconds": "Aseta aikakatkaisu sekunneissa",
"Enter to Send": "Enter lähettääksesi",
"Enter Top K": "Kirjoita Top K",
"Enter Top K Reranker": "",
"Enter Top K Reranker": "Kirjoita Top K uudelleen sijoittaja",
"Enter URL (e.g. http://127.0.0.1:7860/)": "Kirjoita verkko-osoite (esim. http://127.0.0.1:7860/)",
"Enter URL (e.g. http://localhost:11434)": "Kirjoita verkko-osoite (esim. http://localhost:11434)",
"Enter your current password": "Kirjoita nykyinen salasanasi",
"Enter Your Email": "Kirjoita sähköpostiosoitteesi",
"Enter Your Full Name": "Kirjoita koko nimesi",
"Enter your message": "Kirjoita viestisi",
"Enter your name": "Kirjoita nimesi tähän",
"Enter your new password": "Kirjoita uusi salasanasi",
"Enter Your Password": "Kirjoita salasanasi",
"Enter Your Role": "Kirjoita roolisi",
@ -468,7 +476,7 @@
"Exceeded the number of seats in your license. Please contact support to increase the number of seats.": "",
"Exclude": "Jätä pois",
"Execute code for analysis": "Suorita koodi analysointia varten",
"Executing `{{NAME}}`...": "",
"Executing **{{NAME}}**...": "Suoritetaan **{{NAME}}**...",
"Expand": "Laajenna",
"Experimental": "Kokeellinen",
"Explain": "Selitä",
@ -486,13 +494,14 @@
"Export Prompts": "Vie kehotteet",
"Export to CSV": "Vie CSV-tiedostoon",
"Export Tools": "Vie työkalut",
"External": "",
"External": "Ulkoiset",
"External Models": "Ulkoiset mallit",
"Failed to add file.": "Tiedoston lisääminen epäonnistui.",
"Failed to connect to {{URL}} OpenAPI tool server": "",
"Failed to connect to {{URL}} OpenAPI tool server": "Yhdistäminen {{URL}} OpenAPI työkalu palvelimeen epäonnistui",
"Failed to create API Key.": "API-avaimen luonti epäonnistui.",
"Failed to fetch models": "Mallien hakeminen epäonnistui",
"Failed to read clipboard contents": "Leikepöydän sisällön lukeminen epäonnistui",
"Failed to save connections": "",
"Failed to save models configuration": "Mallien määrityksen tallentaminen epäonnistui",
"Failed to update settings": "Asetusten päivittäminen epäonnistui",
"Failed to upload file.": "Tiedoston lataaminen epäonnistui.",
@ -525,6 +534,7 @@
"Forge new paths": "Luo uusia polkuja",
"Form": "Lomake",
"Format your variables using brackets like this:": "Muotoile muuttujasi hakasulkeilla tällä tavalla:",
"Forwards system user session credentials to authenticate": "Välittää järjestelmän käyttäjän istunnon tunnistetiedot todennusta varten",
"Frequency Penalty": "Taajuussakko",
"Full Context Mode": "Koko kontekstitila",
"Function": "Toiminto",
@ -570,7 +580,7 @@
"Hex Color": "Heksadesimaaliväri",
"Hex Color - Leave empty for default color": "Heksadesimaaliväri - Jätä tyhjäksi, jos haluat oletusvärin",
"Hide": "Piilota",
"Hide Model": "",
"Hide Model": "Piilota malli",
"Home": "Koti",
"Host": "Palvelin",
"How can I help you today?": "Miten voin auttaa sinua tänään?",
@ -601,14 +611,14 @@
"Include `--api` flag when running stable-diffusion-webui": "Sisällytä `--api`-lippu ajettaessa stable-diffusion-webui",
"Influences how quickly the algorithm responds to feedback from the generated text. A lower learning rate will result in slower adjustments, while a higher learning rate will make the algorithm more responsive.": "",
"Info": "Tiedot",
"Inject the entire content as context for comprehensive processing, this is recommended for complex queries.": "",
"Inject the entire content as context for comprehensive processing, this is recommended for complex queries.": "Upota koko sisältö kontekstiin kattavaa käsittelyä varten. Tätä suositellaan monimutkaisille kyselyille.",
"Input commands": "Syötekäskyt",
"Install from Github URL": "Asenna Github-URL:stä",
"Instant Auto-Send After Voice Transcription": "Heti automaattinen lähetys äänitunnistuksen jälkeen",
"Integration": "Integrointi",
"Interface": "Käyttöliittymä",
"Invalid file format.": "Virheellinen tiedostomuoto.",
"Invalid JSON schema": "",
"Invalid JSON schema": "Virheellinen JSON kaava",
"Invalid Tag": "Virheellinen tagi",
"is typing...": "Kirjoittaa...",
"January": "tammikuu",
@ -630,7 +640,7 @@
"Knowledge Access": "Tiedon käyttöoikeus",
"Knowledge created successfully.": "Tietokanta luotu onnistuneesti.",
"Knowledge deleted successfully.": "Tietokanta poistettu onnistuneesti.",
"Knowledge Public Sharing": "",
"Knowledge Public Sharing": "Tietokannan julkinen jakaminen",
"Knowledge reset successfully.": "Tietokanta nollattu onnistuneesti.",
"Knowledge updated successfully": "Tietokanta päivitetty onnistuneesti",
"Kokoro.js (Browser)": "Kokoro.js (selain)",
@ -644,9 +654,9 @@
"LDAP": "LDAP",
"LDAP server updated": "LDAP-palvelin päivitetty",
"Leaderboard": "Tulosluettelo",
"Leave empty for unlimited": "Jätä tyhjäksi rajattomaksi",
"Leave empty to include all models from \"{{url}}/api/tags\" endpoint": "",
"Leave empty to include all models from \"{{url}}/models\" endpoint": "",
"Leave empty for unlimited": "Rajaton tyhjäksi jättämällä",
"Leave empty to include all models from \"{{url}}/api/tags\" endpoint": "Jätä tyhjäksi sisällyttääksesi \"{{url}}/api/tags\" päätepisteen mallit",
"Leave empty to include all models from \"{{url}}/models\" endpoint": "Jätä tyhjäksi sisällyttääksesi \"{{url}}/models\" päätepisteen mallit",
"Leave empty to include all models or select specific models": "Jätä tyhjäksi, jos haluat sisällyttää kaikki mallit tai valitse tietyt mallit",
"Leave empty to use the default prompt, or enter a custom prompt": "Jätä tyhjäksi käyttääksesi oletuskehotetta tai kirjoita mukautettu kehote",
"Leave model field empty to use the default model.": "Jätä malli kenttä tyhjäksi käyttääksesi oletus mallia.",
@ -673,7 +683,7 @@
"Manage Ollama API Connections": "Hallitse Ollama API -yhteyksiä",
"Manage OpenAI API Connections": "Hallitse OpenAI API -yhteyksiä",
"Manage Pipelines": "Hallitse putkia",
"Manage Tool Servers": "",
"Manage Tool Servers": "Hallitse työkalu palvelimia",
"March": "maaliskuu",
"Max Tokens (num_predict)": "Tokenien enimmäismäärä (num_predict)",
"Max Upload Count": "Latausten enimmäismäärä",
@ -694,14 +704,16 @@
"Mirostat": "Mirostat",
"Mirostat Eta": "Mirostat Eta",
"Mirostat Tau": "Mirostat Tau",
"Mistral OCR": "Mistral OCR",
"Mistral OCR API Key required.": "Mistral OCR api-avain vaaditaan",
"Model": "Malli",
"Model '{{modelName}}' has been successfully downloaded.": "Malli '{{modelName}}' ladattiin onnistuneesti.",
"Model '{{modelTag}}' is already in queue for downloading.": "Malli '{{modelTag}}' on jo jonossa ladattavaksi.",
"Model {{modelId}} not found": "Mallia {{modelId}} ei löytynyt",
"Model {{modelName}} is not vision capable": "Malli {{modelName}} ei kykene näkökykyyn",
"Model {{name}} is now {{status}}": "Malli {{name}} on nyt {{status}}",
"Model {{name}} is now hidden": "",
"Model {{name}} is now visible": "",
"Model {{name}} is now hidden": "Malli {{name}} on nyt piilotettu",
"Model {{name}} is now visible": "Malli {{name}} on nyt näkyvissä",
"Model accepts image inputs": "Malli hyväksyy kuvasyötteitä",
"Model created successfully!": "Malli luotu onnistuneesti!",
"Model filesystem path detected. Model shortname is required for update, cannot continue.": "Mallin tiedostojärjestelmäpolku havaittu. Mallin lyhytnimi vaaditaan päivitykseen, ei voida jatkaa.",
@ -717,7 +729,7 @@
"Models": "Mallit",
"Models Access": "Mallien käyttöoikeudet",
"Models configuration saved successfully": "Mallien määritykset tallennettu onnistuneesti",
"Models Public Sharing": "",
"Models Public Sharing": "Mallin julkinen jakaminen",
"Mojeek Search API Key": "Mojeek Search API -avain",
"more": "lisää",
"More": "Lisää",
@ -789,6 +801,7 @@
"OpenAI API Key is required.": "OpenAI API -avain vaaditaan.",
"OpenAI API settings updated": "OpenAI API -asetukset päivitetty",
"OpenAI URL/Key required.": "OpenAI URL/avain vaaditaan.",
"openapi.json Path": "openapi.json polku",
"or": "tai",
"Organize your users": "Järjestä käyttäjäsi",
"Other": "Muu",
@ -820,6 +833,8 @@
"Please carefully review the following warnings:": "Tarkista huolellisesti seuraavat varoitukset:",
"Please do not close the settings page while loading the model.": "Älä sulje asetussivua mallin latautuessa.",
"Please enter a prompt": "Kirjoita kehote",
"Please enter a valid path": "",
"Please enter a valid URL": "",
"Please fill in all fields.": "Täytä kaikki kentät.",
"Please select a model first.": "Valitse ensin malli.",
"Please select a model.": "Valitse malli.",
@ -831,19 +846,19 @@
"Presence Penalty": "",
"Previous 30 days": "Edelliset 30 päivää",
"Previous 7 days": "Edelliset 7 päivää",
"Private": "",
"Private": "Yksityinen",
"Profile Image": "Profiilikuva",
"Prompt": "Kehote",
"Prompt (e.g. Tell me a fun fact about the Roman Empire)": "Kehote (esim. Kerro hauska fakta Rooman valtakunnasta)",
"Prompt Autocompletion": "",
"Prompt Autocompletion": "Kehotteen automaattinen täydennys",
"Prompt Content": "Kehotteen sisältö",
"Prompt created successfully": "Kehote luotu onnistuneesti",
"Prompt suggestions": "Kehotteen ehdotukset",
"Prompt updated successfully": "Kehote päivitetty onnistuneesti",
"Prompts": "Kehotteet",
"Prompts Access": "Kehoitteiden käyttöoikeudet",
"Prompts Public Sharing": "",
"Public": "",
"Prompts Public Sharing": "Kehoitteiden julkinen jakaminen",
"Public": "Julkinen",
"Pull \"{{searchValue}}\" from Ollama.com": "Lataa \"{{searchValue}}\" Ollama.comista",
"Pull a model from Ollama.com": "Lataa malli Ollama.comista",
"Query Generation Prompt": "Kyselytulosten luontikehote",
@ -867,7 +882,7 @@
"Rename": "Nimeä uudelleen",
"Reorder Models": "Uudelleenjärjestä malleja",
"Repeat Last N": "Toista viimeiset N",
"Repeat Penalty (Ollama)": "",
"Repeat Penalty (Ollama)": "Toisto rangaistus (Ollama)",
"Reply in Thread": "Vastauksia ",
"Request Mode": "Pyyntötila",
"Reranking Model": "Uudelleenpisteytymismalli",
@ -966,20 +981,20 @@
"Set whisper model": "Aseta whisper-malli",
"Sets a flat bias against tokens that have appeared at least once. A higher value (e.g., 1.5) will penalize repetitions more strongly, while a lower value (e.g., 0.9) will be more lenient. At 0, it is disabled.": "",
"Sets a scaling bias against tokens to penalize repetitions, based on how many times they have appeared. A higher value (e.g., 1.5) will penalize repetitions more strongly, while a lower value (e.g., 0.9) will be more lenient. At 0, it is disabled.": "",
"Sets how far back for the model to look back to prevent repetition.": "",
"Sets the random number seed to use for generation. Setting this to a specific number will make the model generate the same text for the same prompt.": "",
"Sets the size of the context window used to generate the next token.": "",
"Sets how far back for the model to look back to prevent repetition.": "Määrittää kuinka kauas taaksepäin malli katsoo toistumisen estämiseksi.",
"Sets the random number seed to use for generation. Setting this to a specific number will make the model generate the same text for the same prompt.": "Määrittä satunnainen siemenluku luomista varten. Jos asetat siemenluvun, malli tuottaa saman vastauksen samalle kehotteelle.",
"Sets the size of the context window used to generate the next token.": "Määrittää konteksti-ikkunan koon seuraavaksi luotavalle tokenille.",
"Sets the stop sequences to use. When this pattern is encountered, the LLM will stop generating text and return. Multiple stop patterns may be set by specifying multiple separate stop parameters in a modelfile.": "Määrittää käytettävät lopetussekvenssit. Kun tämä kuvio havaitaan, LLM lopettaa tekstin tuottamisen ja palauttaa. Useita lopetuskuvioita voidaan asettaa määrittämällä useita erillisiä lopetusparametreja mallitiedostoon.",
"Settings": "Asetukset",
"Settings saved successfully!": "Asetukset tallennettu onnistuneesti!",
"Share": "Jaa",
"Share Chat": "Jaa keskustelu",
"Share to Open WebUI Community": "Jaa OpenWebUI-yhteisöön",
"Sharing Permissions": "",
"Sharing Permissions": "Jako oikeudet",
"Show": "Näytä",
"Show \"What's New\" modal on login": "Näytä \"Mitä uutta\" -modaali kirjautumisen yhteydessä",
"Show Admin Details in Account Pending Overlay": "Näytä ylläpitäjän tiedot odottavan tilin päällä",
"Show Model": "",
"Show Model": "Näytä malli",
"Show shortcuts": "Näytä pikanäppäimet",
"Show your support!": "Osoita tukesi!",
"Showcased creativity": "Osoitti luovuutta",
@ -1010,7 +1025,7 @@
"System": "Järjestelmä",
"System Instructions": "Järjestelmäohjeet",
"System Prompt": "Järjestelmäkehote",
"Tags": "",
"Tags": "Tagit",
"Tags Generation": "Tagien luonti",
"Tags Generation Prompt": "Tagien luontikehote",
"Tail free sampling is used to reduce the impact of less probable tokens from the output. A higher value (e.g., 2.0) will reduce the impact more, while a value of 1.0 disables this setting.": "",
@ -1037,11 +1052,12 @@
"The maximum file size in MB. If the file size exceeds this limit, the file will not be uploaded.": "Enimmäistiedostokoko megatavuissa. Jos tiedoston koko ylittää tämän rajan, tiedostoa ei ladata.",
"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.": "Suurin sallittu tiedostojen määrä käytettäväksi kerralla chatissa. Jos tiedostojen määrä ylittää tämän rajan, niitä ei ladata.",
"The score should be a value between 0.0 (0%) and 1.0 (100%).": "Pisteytyksen tulee olla arvo välillä 0,0 (0 %) ja 1,0 (100 %).",
"The temperature of the model. Increasing the temperature will make the model answer more creatively.": "",
"The temperature of the model. Increasing the temperature will make the model answer more creatively.": "Mallin lämpötila. Lisäämällä lämpötilaa mallin vastaukset ovat luovempia.",
"Theme": "Teema",
"Thinking...": "Ajattelee...",
"This action cannot be undone. Do you wish to continue?": "Tätä toimintoa ei voi peruuttaa. Haluatko jatkaa?",
"This channel was created on {{createdAt}}. This is the very beginning of the {{channelName}} channel.": "",
"This chat wont appear in history and your messages will not be saved.": "Tämä keskustelu ei näy historiassa, eikä viestejäsi tallenneta.",
"This ensures that your valuable conversations are securely saved to your backend database. Thank you!": "Tämä varmistaa, että arvokkaat keskustelusi tallennetaan turvallisesti backend-tietokantaasi. Kiitos!",
"This is an experimental feature, it may not function as expected and is subject to change at any time.": "Tämä on kokeellinen ominaisuus, se ei välttämättä toimi odotetulla tavalla ja se voi muuttua milloin tahansa.",
"This option controls how many tokens are preserved when refreshing the context. For example, if set to 2, the last 2 tokens of the conversation context will be retained. Preserving context can help maintain the continuity of a conversation, but it may reduce the ability to respond to new topics.": "",
@ -1089,6 +1105,7 @@
"Tool ID": "Työkalun tunnus",
"Tool imported successfully": "Työkalu tuotu onnistuneesti",
"Tool Name": "Työkalun nimi",
"Tool Servers": "Työkalu palvelin",
"Tool updated successfully": "Työkalu päivitetty onnistuneesti",
"Tools": "Työkalut",
"Tools Access": "Työkalujen käyttöoikeudet",
@ -1096,7 +1113,7 @@
"Tools Function Calling Prompt": "Työkalujen kutsukehote",
"Tools have a function calling system that allows arbitrary code execution": "Työkaluilla on toimintokutsuihin perustuva järjestelmä, joka sallii mielivaltaisen koodin suorittamisen",
"Tools have a function calling system that allows arbitrary code execution.": "Työkalut sallivat mielivaltaisen koodin suorittamisen toimintokutsuilla.",
"Tools Public Sharing": "",
"Tools Public Sharing": "Työkalujen julkinen jakaminen",
"Top K": "Top K",
"Top K Reranker": "",
"Top P": "Top P",
@ -1143,7 +1160,7 @@
"user": "käyttäjä",
"User": "Käyttäjä",
"User location successfully retrieved.": "Käyttäjän sijainti haettu onnistuneesti.",
"User Webhooks": "",
"User Webhooks": "Käyttäjän Webhook:it",
"Username": "Käyttäjätunnus",
"Users": "Käyttäjät",
"Using the default arena model with all models. Click the plus button to add custom models.": "Käytetään oletusarena-mallia kaikkien mallien kanssa. Napsauta plus-painiketta lisätäksesi mukautettuja malleja.",
@ -1154,11 +1171,11 @@
"Valves updated successfully": "Venttiilit päivitetty onnistuneesti",
"variable": "muuttuja",
"variable to have them replaced with clipboard content.": "muuttuja korvataan leikepöydän sisällöllä.",
"Verify Connection": "",
"Verify Connection": "Tarkista yhteys",
"Version": "Versio",
"Version {{selectedVersion}} of {{totalVersions}}": "Versio {{selectedVersion}} / {{totalVersions}}",
"View Replies": "Näytä vastaukset",
"View Result from `{{NAME}}`": "",
"View Result from **{{NAME}}**": "Näytä **{{NAME}}** tulokset",
"Visibility": "Näkyvyys",
"Voice": "Ääni",
"Voice Input": "Äänitulolaitteen käyttö",
@ -1176,9 +1193,9 @@
"Webhook URL": "Webhook verkko-osoite",
"WebUI Settings": "WebUI-asetukset",
"WebUI URL": "WebUI-osoite",
"WebUI will make requests to \"{{url}}\"": "WebUI lähettää pyyntöjä osoitteeseen \"{{url}}\"",
"WebUI will make requests to \"{{url}}/api/chat\"": "WebUI lähettää pyyntöjä osoitteeseen \"{{url}}/api/chat\"",
"WebUI will make requests to \"{{url}}/chat/completions\"": "WebUI lähettää pyyntöjä osoitteeseen \"{{url}}/chat/completions\"",
"WebUI will make requests to \"{{url}}/openapi.json\"": "",
"What are you trying to achieve?": "Mitä yrität saavuttaa?",
"What are you working on?": "Mitä olet työskentelemässä?",
"Whats New in": "Mitä uutta",

View file

@ -6,7 +6,7 @@
"(latest)": "(dernier)",
"(Ollama)": "",
"{{ models }}": "{{ modèles }}",
"{{COUNT}} Available Tool Servers": "",
"{{COUNT}} Available Tools": "",
"{{COUNT}} hidden lines": "",
"{{COUNT}} Replies": "",
"{{user}}'s Chats": "Discussions de {{user}}",
@ -108,6 +108,7 @@
"Attribute for Username": "",
"Audio": "Audio",
"August": "Août",
"Auth": "",
"Authenticate": "",
"Authentication": "",
"Auto-Copy Response to Clipboard": "Copie automatique de la réponse vers le presse-papiers",
@ -119,7 +120,7 @@
"AUTOMATIC1111 Base URL": "URL de base AUTOMATIC1111",
"AUTOMATIC1111 Base URL is required.": "L'URL de base {AUTOMATIC1111} est requise.",
"Available list": "",
"Available Tool Servers": "",
"Available Tools": "",
"available!": "disponible !",
"Awful": "",
"Azure AI Speech": "",
@ -218,7 +219,10 @@
"Confirm your new password": "",
"Connect to your own OpenAI compatible API endpoints.": "",
"Connect to your own OpenAPI compatible external tool servers.": "",
"Connection failed": "",
"Connection successful": "",
"Connections": "Connexions",
"Connections saved successfully": "",
"Constrains effort on reasoning for reasoning models. Only applicable to reasoning models from specific providers that support reasoning effort.": "",
"Contact Admin for WebUI Access": "Contacter l'administrateur pour l'accès à l'interface Web",
"Content": "Contenu",
@ -303,6 +307,7 @@
"Direct Connections": "",
"Direct Connections allow users to connect to their own OpenAI compatible API endpoints.": "",
"Direct Connections settings updated": "",
"Direct Tool Servers": "",
"Disabled": "",
"Discover a function": "Découvrez une fonction",
"Discover a model": "Découvrir un modèle",
@ -412,6 +417,7 @@
"Enter Kagi Search API Key": "",
"Enter Key Behavior": "",
"Enter language codes": "Entrez les codes de langue",
"Enter Mistral API Key": "",
"Enter Model ID": "",
"Enter model tag (e.g. {{modelTag}})": "Entrez l'étiquette du modèle (par ex. {{modelTag}})",
"Enter Mojeek Search API Key": "",
@ -436,6 +442,7 @@
"Enter server port": "",
"Enter stop sequence": "Entrez la séquence d'arrêt",
"Enter system prompt": "",
"Enter system prompt here": "",
"Enter Tavily API Key": "Entrez la clé API Tavily",
"Enter the public URL of your WebUI. This URL will be used to generate links in the notifications.": "",
"Enter Tika Server URL": "",
@ -449,6 +456,7 @@
"Enter Your Email": "Entrez votre adresse e-mail",
"Enter Your Full Name": "Entrez votre nom complet",
"Enter your message": "",
"Enter your name": "",
"Enter your new password": "",
"Enter Your Password": "Entrez votre mot de passe",
"Enter Your Role": "Entrez votre rôle",
@ -468,7 +476,7 @@
"Exceeded the number of seats in your license. Please contact support to increase the number of seats.": "",
"Exclude": "",
"Execute code for analysis": "",
"Executing `{{NAME}}`...": "",
"Executing **{{NAME}}**...": "",
"Expand": "",
"Experimental": "Expérimental",
"Explain": "",
@ -493,6 +501,7 @@
"Failed to create API Key.": "Échec de la création de la clé API.",
"Failed to fetch models": "",
"Failed to read clipboard contents": "Échec de la lecture du contenu du presse-papiers",
"Failed to save connections": "",
"Failed to save models configuration": "",
"Failed to update settings": "Échec de la mise à jour des paramètres",
"Failed to upload file.": "",
@ -525,21 +534,22 @@
"Forge new paths": "",
"Form": "Formulaire",
"Format your variables using brackets like this:": "",
"Forwards system user session credentials to authenticate": "",
"Frequency Penalty": "Pénalité de fréquence",
"Full Context Mode": "",
"Function": "",
"Function Calling": "",
"Function created successfully": "La fonction a été créée avec succès",
"Function deleted successfully": "Fonction supprimée avec succès",
"Function Description": "",
"Function ID": "",
"Function is now globally disabled": "",
"Function is now globally enabled": "",
"Function Name": "",
"Function Description": "Description de la fonction",
"Function ID": "ID de la fonction",
"Function is now globally disabled": "La fonction est désormais globalement désactivée",
"Function is now globally enabled": "La fonction est désormais globalement activée",
"Function Name": "Nom de la fonction",
"Function updated successfully": "La fonction a été mise à jour avec succès",
"Functions": "Fonctions",
"Functions allow arbitrary code execution": "",
"Functions allow arbitrary code execution.": "",
"Functions allow arbitrary code execution": "Les fonctions permettent l'exécution de code arbitraire",
"Functions allow arbitrary code execution.": "Les fonctions permettent l'exécution de code arbitraire.",
"Functions imported successfully": "Fonctions importées avec succès",
"Gemini": "",
"Gemini API Config": "",
@ -549,9 +559,9 @@
"Generate Image": "Générer une image",
"Generate prompt pair": "",
"Generating search query": "Génération d'une requête de recherche",
"Get started": "",
"Get started with {{WEBUI_NAME}}": "",
"Global": "Mondial",
"Get started": "Démarrer",
"Get started with {{WEBUI_NAME}}": "Démarrez avec {{WEBUI_NAME}}",
"Global": "Global",
"Good Response": "Bonne réponse",
"Google Drive": "",
"Google PSE API Key": "Clé API Google PSE",
@ -694,6 +704,8 @@
"Mirostat": "Mirostat",
"Mirostat Eta": "Mirostat Eta",
"Mirostat Tau": "Mirostat Tau",
"Mistral OCR": "",
"Mistral OCR API Key required.": "",
"Model": "",
"Model '{{modelName}}' has been successfully downloaded.": "Le modèle '{{modelName}}' a été téléchargé avec succès.",
"Model '{{modelTag}}' is already in queue for downloading.": "Le modèle '{{modelTag}}' est déjà dans la file d'attente pour le téléchargement.",
@ -789,6 +801,7 @@
"OpenAI API Key is required.": "Une clé API OpenAI est requise.",
"OpenAI API settings updated": "",
"OpenAI URL/Key required.": "URL/Clé OpenAI requise.",
"openapi.json Path": "",
"or": "ou",
"Organize your users": "",
"Other": "Autre",
@ -820,6 +833,8 @@
"Please carefully review the following warnings:": "",
"Please do not close the settings page while loading the model.": "",
"Please enter a prompt": "",
"Please enter a valid path": "",
"Please enter a valid URL": "",
"Please fill in all fields.": "",
"Please select a model first.": "",
"Please select a model.": "",
@ -1042,6 +1057,7 @@
"Thinking...": "En train de réfléchir...",
"This action cannot be undone. Do you wish to continue?": "Cette action ne peut pas être annulée. Souhaitez-vous continuer ?",
"This channel was created on {{createdAt}}. This is the very beginning of the {{channelName}} channel.": "",
"This chat wont appear in history and your messages will not be saved.": "",
"This ensures that your valuable conversations are securely saved to your backend database. Thank you!": "Cela garantit que vos conversations précieuses soient sauvegardées en toute sécurité dans votre base de données backend. Merci !",
"This is an experimental feature, it may not function as expected and is subject to change at any time.": "Il s'agit d'une fonctionnalité expérimentale, elle peut ne pas fonctionner comme prévu et est sujette à modification à tout moment.",
"This option controls how many tokens are preserved when refreshing the context. For example, if set to 2, the last 2 tokens of the conversation context will be retained. Preserving context can help maintain the continuity of a conversation, but it may reduce the ability to respond to new topics.": "",
@ -1089,6 +1105,7 @@
"Tool ID": "",
"Tool imported successfully": "Outil importé avec succès",
"Tool Name": "",
"Tool Servers": "",
"Tool updated successfully": "L'outil a été mis à jour avec succès",
"Tools": "Outils",
"Tools Access": "",
@ -1158,7 +1175,7 @@
"Version": "Version améliorée",
"Version {{selectedVersion}} of {{totalVersions}}": "",
"View Replies": "",
"View Result from `{{NAME}}`": "",
"View Result from **{{NAME}}**": "",
"Visibility": "",
"Voice": "Voix",
"Voice Input": "",
@ -1176,9 +1193,9 @@
"Webhook URL": "URL du webhook",
"WebUI Settings": "Paramètres de WebUI",
"WebUI URL": "",
"WebUI will make requests to \"{{url}}\"": "",
"WebUI will make requests to \"{{url}}/api/chat\"": "",
"WebUI will make requests to \"{{url}}/chat/completions\"": "",
"WebUI will make requests to \"{{url}}/openapi.json\"": "",
"What are you trying to achieve?": "",
"What are you working on?": "",
"Whats New in": "Quoi de neuf",

View file

@ -6,7 +6,7 @@
"(latest)": "(dernière version)",
"(Ollama)": "",
"{{ models }}": "{{ models }}",
"{{COUNT}} Available Tool Servers": "",
"{{COUNT}} Available Tools": "",
"{{COUNT}} hidden lines": "",
"{{COUNT}} Replies": "{{COUNT}} réponses",
"{{user}}'s Chats": "Conversations de {{user}}",
@ -108,6 +108,7 @@
"Attribute for Username": "Attribut pour le nom d'utilisateur",
"Audio": "Audio",
"August": "Août",
"Auth": "",
"Authenticate": "Authentifier",
"Authentication": "",
"Auto-Copy Response to Clipboard": "Copie automatique de la réponse vers le presse-papiers",
@ -119,7 +120,7 @@
"AUTOMATIC1111 Base URL": "URL de base AUTOMATIC1111",
"AUTOMATIC1111 Base URL is required.": "L'URL de base {AUTOMATIC1111} est requise.",
"Available list": "Liste disponible",
"Available Tool Servers": "",
"Available Tools": "",
"available!": "disponible !",
"Awful": "Horrible",
"Azure AI Speech": "Azure AI Speech",
@ -218,7 +219,10 @@
"Confirm your new password": "Confirmer votre nouveau mot de passe",
"Connect to your own OpenAI compatible API endpoints.": "",
"Connect to your own OpenAPI compatible external tool servers.": "",
"Connection failed": "",
"Connection successful": "",
"Connections": "Connexions",
"Connections saved successfully": "",
"Constrains effort on reasoning for reasoning models. Only applicable to reasoning models from specific providers that support reasoning effort.": "",
"Contact Admin for WebUI Access": "Contacter l'administrateur pour obtenir l'accès à WebUI",
"Content": "Contenu",
@ -303,6 +307,7 @@
"Direct Connections": "",
"Direct Connections allow users to connect to their own OpenAI compatible API endpoints.": "",
"Direct Connections settings updated": "",
"Direct Tool Servers": "",
"Disabled": "Désactivé",
"Discover a function": "Trouvez une fonction",
"Discover a model": "Trouvez un modèle",
@ -412,6 +417,7 @@
"Enter Kagi Search API Key": "Entrez la clé API Kagi Search",
"Enter Key Behavior": "",
"Enter language codes": "Entrez les codes de langue",
"Enter Mistral API Key": "",
"Enter Model ID": "Entrez l'ID du modèle",
"Enter model tag (e.g. {{modelTag}})": "Entrez le tag du modèle (par ex. {{modelTag}})",
"Enter Mojeek Search API Key": "Entrez la clé API Mojeek",
@ -436,6 +442,7 @@
"Enter server port": "Entrez le port du serveur",
"Enter stop sequence": "Entrez la séquence d'arrêt",
"Enter system prompt": "Entrez le prompt système",
"Enter system prompt here": "",
"Enter Tavily API Key": "Entrez la clé API Tavily",
"Enter the public URL of your WebUI. This URL will be used to generate links in the notifications.": "Entrez l'URL publique de votre WebUI. Cette URL sera utilisée pour générer des liens dans les notifications.",
"Enter Tika Server URL": "Entrez l'URL du serveur Tika",
@ -449,6 +456,7 @@
"Enter Your Email": "Entrez votre adresse e-mail",
"Enter Your Full Name": "Entrez votre nom complet",
"Enter your message": "Entrez votre message",
"Enter your name": "",
"Enter your new password": "Entrez votre nouveau mot de passe",
"Enter Your Password": "Entrez votre mot de passe",
"Enter Your Role": "Entrez votre rôle",
@ -468,7 +476,7 @@
"Exceeded the number of seats in your license. Please contact support to increase the number of seats.": "",
"Exclude": "Exclure",
"Execute code for analysis": "",
"Executing `{{NAME}}`...": "",
"Executing **{{NAME}}**...": "",
"Expand": "",
"Experimental": "Expérimental",
"Explain": "",
@ -493,6 +501,7 @@
"Failed to create API Key.": "Échec de la création de la clé API.",
"Failed to fetch models": "Échec de la récupération des modèles",
"Failed to read clipboard contents": "Échec de la lecture du contenu du presse-papiers",
"Failed to save connections": "",
"Failed to save models configuration": "Échec de la sauvegarde de la configuration des modèles",
"Failed to update settings": "Échec de la mise à jour des paramètres",
"Failed to upload file.": "Échec du téléchargement du fichier.",
@ -525,6 +534,7 @@
"Forge new paths": "Créer de nouveaux chemins",
"Form": "Formulaire",
"Format your variables using brackets like this:": "Formatez vos variables en utilisant des parenthèses comme ceci :",
"Forwards system user session credentials to authenticate": "",
"Frequency Penalty": "Pénalité de fréquence",
"Full Context Mode": "",
"Function": "Fonction",
@ -533,8 +543,8 @@
"Function deleted successfully": "Fonction supprimée avec succès",
"Function Description": "Description de la fonction",
"Function ID": "ID de la fonction",
"Function is now globally disabled": "La fonction est désormais désactivée globalement",
"Function is now globally enabled": "La fonction est désormais activée globalement",
"Function is now globally disabled": "La fonction est désormais globalement désactivée",
"Function is now globally enabled": "La fonction est désormais globalement activée",
"Function Name": "Nom de la fonction",
"Function updated successfully": "La fonction a été mise à jour avec succès",
"Functions": "Fonctions",
@ -549,9 +559,9 @@
"Generate Image": "Générer une image",
"Generate prompt pair": "",
"Generating search query": "Génération d'une requête de recherche",
"Get started": "Commencer",
"Get started with {{WEBUI_NAME}}": "Commencez avec {{WEBUI_NAME}}",
"Global": "Mondial",
"Get started": "Démarrer",
"Get started with {{WEBUI_NAME}}": "Démarrez avec {{WEBUI_NAME}}",
"Global": "Globale",
"Good Response": "Bonne réponse",
"Google Drive": "Google Drive",
"Google PSE API Key": "Clé API Google PSE",
@ -694,6 +704,8 @@
"Mirostat": "Mirostat",
"Mirostat Eta": "Mirostat Eta",
"Mirostat Tau": "Mirostat Tau",
"Mistral OCR": "",
"Mistral OCR API Key required.": "",
"Model": "Modèle",
"Model '{{modelName}}' has been successfully downloaded.": "Le modèle '{{modelName}}' a été téléchargé avec succès.",
"Model '{{modelTag}}' is already in queue for downloading.": "Le modèle '{{modelTag}}' est déjà dans la file d'attente pour le téléchargement.",
@ -789,6 +801,7 @@
"OpenAI API Key is required.": "Une clé API OpenAI est requise.",
"OpenAI API settings updated": "Paramètres de l'API OpenAI mis à jour",
"OpenAI URL/Key required.": "URL/Clé OpenAI requise.",
"openapi.json Path": "",
"or": "ou",
"Organize your users": "Organisez vos utilisateurs",
"Other": "Autre",
@ -820,6 +833,8 @@
"Please carefully review the following warnings:": "Veuillez lire attentivement les avertissements suivants :",
"Please do not close the settings page while loading the model.": "Veuillez ne pas fermer les paramètres pendant le chargement du modèle.",
"Please enter a prompt": "Veuillez saisir un prompt",
"Please enter a valid path": "",
"Please enter a valid URL": "",
"Please fill in all fields.": "Veuillez remplir tous les champs.",
"Please select a model first.": "Veuillez d'abord sélectionner un modèle.",
"Please select a model.": "Veuillez sélectionner un modèle.",
@ -1042,6 +1057,7 @@
"Thinking...": "En train de réfléchir...",
"This action cannot be undone. Do you wish to continue?": "Cette action ne peut pas être annulée. Souhaitez-vous continuer ?",
"This channel was created on {{createdAt}}. This is the very beginning of the {{channelName}} channel.": "",
"This chat wont appear in history and your messages will not be saved.": "",
"This ensures that your valuable conversations are securely saved to your backend database. Thank you!": "Cela garantit que vos conversations précieuses soient sauvegardées en toute sécurité dans votre base de données backend. Merci !",
"This is an experimental feature, it may not function as expected and is subject to change at any time.": "Il s'agit d'une fonctionnalité expérimentale, elle peut ne pas fonctionner comme prévu et est sujette à modification à tout moment.",
"This option controls how many tokens are preserved when refreshing the context. For example, if set to 2, the last 2 tokens of the conversation context will be retained. Preserving context can help maintain the continuity of a conversation, but it may reduce the ability to respond to new topics.": "",
@ -1089,6 +1105,7 @@
"Tool ID": "ID de l'outil",
"Tool imported successfully": "Outil importé avec succès",
"Tool Name": "Nom de l'outil",
"Tool Servers": "",
"Tool updated successfully": "L'outil a été mis à jour avec succès",
"Tools": "Outils",
"Tools Access": "Accès aux outils",
@ -1158,7 +1175,7 @@
"Version": "Version:",
"Version {{selectedVersion}} of {{totalVersions}}": "Version {{selectedVersion}} de {{totalVersions}}",
"View Replies": "Voir les réponses",
"View Result from `{{NAME}}`": "",
"View Result from **{{NAME}}**": "",
"Visibility": "Visibilité",
"Voice": "Voix",
"Voice Input": "Saisie vocale",
@ -1176,9 +1193,9 @@
"Webhook URL": "URL du webhook",
"WebUI Settings": "Paramètres de WebUI",
"WebUI URL": "URL de WebUI",
"WebUI will make requests to \"{{url}}\"": "",
"WebUI will make requests to \"{{url}}/api/chat\"": "WebUI fera des requêtes à \"{{url}}/api/chat\"",
"WebUI will make requests to \"{{url}}/chat/completions\"": "WebUI fera des requêtes à \"{{url}}/chat/completions\"",
"WebUI will make requests to \"{{url}}/openapi.json\"": "",
"What are you trying to achieve?": "Que cherchez-vous à accomplir ?",
"What are you working on?": "Sur quoi travaillez-vous ?",
"Whats New in": "Quoi de neuf dans",

View file

@ -6,7 +6,7 @@
"(latest)": "(האחרון)",
"(Ollama)": "",
"{{ models }}": "{{ דגמים }}",
"{{COUNT}} Available Tool Servers": "",
"{{COUNT}} Available Tools": "",
"{{COUNT}} hidden lines": "",
"{{COUNT}} Replies": "",
"{{user}}'s Chats": "צ'אטים של {{user}}",
@ -108,6 +108,7 @@
"Attribute for Username": "",
"Audio": "אודיו",
"August": "אוגוסט",
"Auth": "",
"Authenticate": "",
"Authentication": "",
"Auto-Copy Response to Clipboard": "העתקה אוטומטית של תגובה ללוח",
@ -119,7 +120,7 @@
"AUTOMATIC1111 Base URL": "כתובת URL בסיסית של AUTOMATIC1111",
"AUTOMATIC1111 Base URL is required.": "נדרשת כתובת URL בסיסית של AUTOMATIC1111",
"Available list": "",
"Available Tool Servers": "",
"Available Tools": "",
"available!": "זמין!",
"Awful": "",
"Azure AI Speech": "",
@ -218,7 +219,10 @@
"Confirm your new password": "",
"Connect to your own OpenAI compatible API endpoints.": "",
"Connect to your own OpenAPI compatible external tool servers.": "",
"Connection failed": "",
"Connection successful": "",
"Connections": "חיבורים",
"Connections saved successfully": "",
"Constrains effort on reasoning for reasoning models. Only applicable to reasoning models from specific providers that support reasoning effort.": "",
"Contact Admin for WebUI Access": "",
"Content": "תוכן",
@ -303,6 +307,7 @@
"Direct Connections": "",
"Direct Connections allow users to connect to their own OpenAI compatible API endpoints.": "",
"Direct Connections settings updated": "",
"Direct Tool Servers": "",
"Disabled": "",
"Discover a function": "",
"Discover a model": "גלה מודל",
@ -412,6 +417,7 @@
"Enter Kagi Search API Key": "",
"Enter Key Behavior": "",
"Enter language codes": "הזן קודי שפה",
"Enter Mistral API Key": "",
"Enter Model ID": "",
"Enter model tag (e.g. {{modelTag}})": "הזן תג מודל (למשל {{modelTag}})",
"Enter Mojeek Search API Key": "",
@ -436,6 +442,7 @@
"Enter server port": "",
"Enter stop sequence": "הזן רצף עצירה",
"Enter system prompt": "",
"Enter system prompt here": "",
"Enter Tavily API Key": "",
"Enter the public URL of your WebUI. This URL will be used to generate links in the notifications.": "",
"Enter Tika Server URL": "",
@ -449,6 +456,7 @@
"Enter Your Email": "הזן את דוא\"ל שלך",
"Enter Your Full Name": "הזן את שמך המלא",
"Enter your message": "",
"Enter your name": "",
"Enter your new password": "",
"Enter Your Password": "הזן את הסיסמה שלך",
"Enter Your Role": "הזן את התפקיד שלך",
@ -468,7 +476,7 @@
"Exceeded the number of seats in your license. Please contact support to increase the number of seats.": "",
"Exclude": "",
"Execute code for analysis": "",
"Executing `{{NAME}}`...": "",
"Executing **{{NAME}}**...": "",
"Expand": "",
"Experimental": "ניסיוני",
"Explain": "",
@ -493,6 +501,7 @@
"Failed to create API Key.": "יצירת מפתח API נכשלה.",
"Failed to fetch models": "",
"Failed to read clipboard contents": "קריאת תוכן הלוח נכשלה",
"Failed to save connections": "",
"Failed to save models configuration": "",
"Failed to update settings": "",
"Failed to upload file.": "",
@ -525,6 +534,7 @@
"Forge new paths": "",
"Form": "",
"Format your variables using brackets like this:": "",
"Forwards system user session credentials to authenticate": "",
"Frequency Penalty": "עונש תדירות",
"Full Context Mode": "",
"Function": "",
@ -694,6 +704,8 @@
"Mirostat": "Mirostat",
"Mirostat Eta": "Mirostat Eta",
"Mirostat Tau": "Mirostat Tau",
"Mistral OCR": "",
"Mistral OCR API Key required.": "",
"Model": "",
"Model '{{modelName}}' has been successfully downloaded.": "המודל '{{modelName}}' הורד בהצלחה.",
"Model '{{modelTag}}' is already in queue for downloading.": "המודל '{{modelTag}}' כבר בתור להורדה.",
@ -789,6 +801,7 @@
"OpenAI API Key is required.": "נדרש מפתח API של OpenAI.",
"OpenAI API settings updated": "",
"OpenAI URL/Key required.": "נדרשת כתובת URL/מפתח של OpenAI.",
"openapi.json Path": "",
"or": "או",
"Organize your users": "",
"Other": "אחר",
@ -820,6 +833,8 @@
"Please carefully review the following warnings:": "",
"Please do not close the settings page while loading the model.": "",
"Please enter a prompt": "",
"Please enter a valid path": "",
"Please enter a valid URL": "",
"Please fill in all fields.": "",
"Please select a model first.": "",
"Please select a model.": "",
@ -1042,6 +1057,7 @@
"Thinking...": "",
"This action cannot be undone. Do you wish to continue?": "",
"This channel was created on {{createdAt}}. This is the very beginning of the {{channelName}} channel.": "",
"This chat wont appear in history and your messages will not be saved.": "",
"This ensures that your valuable conversations are securely saved to your backend database. Thank you!": "פעולה זו מבטיחה שהשיחות בעלות הערך שלך יישמרו באופן מאובטח במסד הנתונים העורפי שלך. תודה!",
"This is an experimental feature, it may not function as expected and is subject to change at any time.": "",
"This option controls how many tokens are preserved when refreshing the context. For example, if set to 2, the last 2 tokens of the conversation context will be retained. Preserving context can help maintain the continuity of a conversation, but it may reduce the ability to respond to new topics.": "",
@ -1089,6 +1105,7 @@
"Tool ID": "",
"Tool imported successfully": "",
"Tool Name": "",
"Tool Servers": "",
"Tool updated successfully": "",
"Tools": "",
"Tools Access": "",
@ -1158,7 +1175,7 @@
"Version": "גרסה",
"Version {{selectedVersion}} of {{totalVersions}}": "",
"View Replies": "",
"View Result from `{{NAME}}`": "",
"View Result from **{{NAME}}**": "",
"Visibility": "",
"Voice": "",
"Voice Input": "",
@ -1176,9 +1193,9 @@
"Webhook URL": "URL Webhook",
"WebUI Settings": "הגדרות WebUI",
"WebUI URL": "",
"WebUI will make requests to \"{{url}}\"": "",
"WebUI will make requests to \"{{url}}/api/chat\"": "",
"WebUI will make requests to \"{{url}}/chat/completions\"": "",
"WebUI will make requests to \"{{url}}/openapi.json\"": "",
"What are you trying to achieve?": "",
"What are you working on?": "",
"Whats New in": "מה חדש ב",

Some files were not shown because too many files have changed in this diff Show more