Merge pull request #13707 from open-webui/dev

0.6.8
This commit is contained in:
Tim Jaeryang Baek 2025-05-10 19:31:52 +04:00 committed by GitHub
commit ef301aa16b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
96 changed files with 3002 additions and 2264 deletions

View file

@ -5,6 +5,40 @@ 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/), 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). and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [0.6.8] - 2025-05-10
### Added
- 🏆 **External Reranker Support for Knowledge Base Search**: Supercharge your Retrieval-Augmented Generation (RAG) workflows with the new External Reranker integration; easily plug in advanced reranking services via the UI to deliver sharper and more relevant search results, accelerating research and insight discovery.
- 📤 **Unstylized PDF Export Option (Reduced File Size)**: When exporting chat transcripts or documents, you can now choose an unstylized PDF export for snappier downloads, minimal file size, and clean data archiving—perfect for large-scale storage or sharing.
- 📝 **Vazirmatn Font for Persian & Arabic**: Arabic and Persian users will now see their text beautifully rendered with the specialized Vazirmatn font for an improved localized reading experience.
- 🏷️ **SharePoint Tenant ID Support for OneDrive**: You can now specify a SharePoint tenant ID in OneDrive settings for seamless authentication and granular enterprise integration.
- 👤 **Refresh OAuth Profile Picture**: Your OAuth profile picture now updates in real-time, ensuring your presence and avatar always match your latest identity across integrated platforms.
- 🔧 **Milvus Configuration Improvements**: Configure index and metric types for Milvus directly within settings; take full control of your vector database for more accurate and robust AI search experiences.
- 🛡️ **S3 Tagging Toggle for Compatibility**: Optional S3 tagging via an environment toggle grants full compatibility with all storage backends—including those that dont support tagging like Cloudflare R2—ensuring error-free attachment and document management.
- 👨‍🦯 **Icon Button Accessibility Improvements**: Key interactive icon-buttons now include aria-labels and ARIA descriptions, so screen readers provide precise guidance about what action each button performs for improved accessibility.
- ♿ **Enhanced Accessibility with Modal Focus Trap**: Modal dialogs and pop-ups now feature a focus trap and improved ARIA roles, ensuring seamless navigation and screen reader support—making the interface friendlier for everyone, including keyboard and assistive tech users.
- 🏃 **Improved Admin User List Loading Indicator**: The user list loading experience is now clearer and more responsive in the admin panel.
- 🧑‍🤝‍🧑 **Larger Admin User List Page Size**: Admins can now manage up to 30 users per page in the admin interface, drastically reducing pagination and making large user teams easier and faster to manage.
- 🌠 **Default Code Interpreter Prompt Clarified**: The built-in code interpreter prompt is now more explicit, preventing AI from wrapping code in Markdown blocks when not needed—ensuring properly formatted code runs as intended every time.
- 🧾 **Improved Default Title Generation Prompt Template**: Title generation now uses a robust template for reliable JSON output, improving chat organization and searchability.
- 🔗 **Support Jupyter Notebooks with Non-Root Base URLs**: Notebook-based code execution now supports non-root deployed Jupyter servers, granting full flexibility for hybrid or multi-user setups.
- 📰 **UI Scrollbar Always Visible for Overflow Tools**: When available tools overflow the display, the scrollbar is now always visible and theres a handy "show all" toggle, making navigation of large toolsets snappier and more intuitive.
- 🛠️ **General Backend Refactoring for Stability**: Multiple under-the-hood improvements have been made across backend components, ensuring smoother performance, fewer errors, and a more reliable overall experience for all users.
- 🚀 **Optimized Web Search for Faster Results**: Web search speed and performance have been significantly enhanced, delivering answers and sources in record time to accelerate your research-heavy workflows.
- 💡 **More Supported Languages**: Expanded language support ensures an even wider range of users can enjoy an intuitive and natural interface in their native tongue.
### Fixed
- 🏃‍♂️ **Exhausting Workers in Nginx Reverse Proxy Due to Websocket Fix**: Websocket sessions are now fully compatible behind Nginx, eliminating worker exhaustion and restoring 24/7 reliability for real-time chats even in complex deployments.
- 🎤 **Audio Transcription Issue with OpenAI Resolved**: OpenAI-based audio transcription now handles WebM and newer formats without error, ensuring seamless voice-to-text workflows every time.
- 👉 **Message Input RTL Issue Fixed**: The chat message input now displays correctly for right-to-left languages, creating a flawless typing and reading experience for Arabic, Hebrew, and more.
- 🀄 **Katex: Proper Rendering of Chinese Characters Next to Math**: Math formulas now render perfectly even when directly adjacent to Chinese (CJK) characters, improving visual clarity for multilingual teams and cross-language documents.
- 🔂 **Duplicate Web Search URLs Eliminated**: Search results now reliably filter out URL duplicates, so your knowledge and search citations are always clean, trimmed, and easy to review.
- 📄 **Markdown Rendering Fixed in Knowledge Bases**: Markdown is now displayed correctly within knowledge bases, enabling better formatting and clarity of information-rich files.
- 🗂️ **LDAP Import/Loading Issue Resolved**: LDAP user imports process correctly, ensuring smooth onboarding and access without interruption.
- 🌎 **Pinecone Batch Operations and Async Safety**: All Pinecone operations (batch insert, upsert, delete) now run efficiently and safely in an async environment, boosting performance and preventing slowdowns in large-scale RAG jobs.
## [0.6.7] - 2025-05-07 ## [0.6.7] - 2025-05-07
### Added ### Added

View file

@ -1,64 +0,0 @@
# Run with
# caddy run --envfile ./example.env --config ./Caddyfile.localhost
#
# This is configured for
# - Automatic HTTPS (even for localhost)
# - Reverse Proxying to Ollama API Base URL (http://localhost:11434/api)
# - CORS
# - HTTP Basic Auth API Tokens (uncomment basicauth section)
# CORS Preflight (OPTIONS) + Request (GET, POST, PATCH, PUT, DELETE)
(cors-api) {
@match-cors-api-preflight method OPTIONS
handle @match-cors-api-preflight {
header {
Access-Control-Allow-Origin "{http.request.header.origin}"
Access-Control-Allow-Methods "GET, POST, PUT, PATCH, DELETE, OPTIONS"
Access-Control-Allow-Headers "Origin, Accept, Authorization, Content-Type, X-Requested-With"
Access-Control-Allow-Credentials "true"
Access-Control-Max-Age "3600"
defer
}
respond "" 204
}
@match-cors-api-request {
not {
header Origin "{http.request.scheme}://{http.request.host}"
}
header Origin "{http.request.header.origin}"
}
handle @match-cors-api-request {
header {
Access-Control-Allow-Origin "{http.request.header.origin}"
Access-Control-Allow-Methods "GET, POST, PUT, PATCH, DELETE, OPTIONS"
Access-Control-Allow-Headers "Origin, Accept, Authorization, Content-Type, X-Requested-With"
Access-Control-Allow-Credentials "true"
Access-Control-Max-Age "3600"
defer
}
}
}
# replace localhost with example.com or whatever
localhost {
## HTTP Basic Auth
## (uncomment to enable)
# basicauth {
# # see .example.env for how to generate tokens
# {env.OLLAMA_API_ID} {env.OLLAMA_API_TOKEN_DIGEST}
# }
handle /api/* {
# Comment to disable CORS
import cors-api
reverse_proxy localhost:11434
}
# Same-Origin Static Web Server
file_server {
root ./build/
}
}

View file

@ -109,54 +109,7 @@ if os.path.exists(f"{DATA_DIR}/config.json"):
DEFAULT_CONFIG = { DEFAULT_CONFIG = {
"version": 0, "version": 0,
"ui": { "ui": {},
"default_locale": "",
"prompt_suggestions": [
{
"title": [
"Help me study",
"vocabulary for a college entrance exam",
],
"content": "Help me study vocabulary: write a sentence for me to fill in the blank, and I'll try to pick the correct option.",
},
{
"title": [
"Give me ideas",
"for what to do with my kids' art",
],
"content": "What are 5 creative things I could do with my kids' art? I don't want to throw them away, but it's also so much clutter.",
},
{
"title": ["Tell me a fun fact", "about the Roman Empire"],
"content": "Tell me a random fun fact about the Roman Empire",
},
{
"title": [
"Show me a code snippet",
"of a website's sticky header",
],
"content": "Show me a code snippet of a website's sticky header in CSS and JavaScript.",
},
{
"title": [
"Explain options trading",
"if I'm familiar with buying and selling stocks",
],
"content": "Explain options trading in simple terms if I'm familiar with buying and selling stocks.",
},
{
"title": ["Overcome procrastination", "give me tips"],
"content": "Could you start by asking me about instances when I procrastinate the most and then give me some suggestions to overcome it?",
},
{
"title": [
"Grammar check",
"rewrite it for better readability ",
],
"content": 'Check the following sentence for grammar and clarity: "[sentence]". Rewrite it for better readability while maintaining its original meaning.',
},
],
},
} }
@ -552,6 +505,12 @@ OAUTH_ALLOWED_DOMAINS = PersistentConfig(
], ],
) )
OAUTH_UPDATE_PICTURE_ON_LOGIN = PersistentConfig(
"OAUTH_UPDATE_PICTURE_ON_LOGIN",
"oauth.update_picture_on_login",
os.environ.get("OAUTH_UPDATE_PICTURE_ON_LOGIN", "False").lower() == "true",
)
def load_oauth_providers(): def load_oauth_providers():
OAUTH_PROVIDERS.clear() OAUTH_PROVIDERS.clear()
@ -761,9 +720,10 @@ S3_BUCKET_NAME = os.environ.get("S3_BUCKET_NAME", None)
S3_KEY_PREFIX = os.environ.get("S3_KEY_PREFIX", None) S3_KEY_PREFIX = os.environ.get("S3_KEY_PREFIX", None)
S3_ENDPOINT_URL = os.environ.get("S3_ENDPOINT_URL", None) S3_ENDPOINT_URL = os.environ.get("S3_ENDPOINT_URL", None)
S3_USE_ACCELERATE_ENDPOINT = ( S3_USE_ACCELERATE_ENDPOINT = (
os.environ.get("S3_USE_ACCELERATE_ENDPOINT", "False").lower() == "true" os.environ.get("S3_USE_ACCELERATE_ENDPOINT", "false").lower() == "true"
) )
S3_ADDRESSING_STYLE = os.environ.get("S3_ADDRESSING_STYLE", None) S3_ADDRESSING_STYLE = os.environ.get("S3_ADDRESSING_STYLE", None)
S3_ENABLE_TAGGING = os.getenv("S3_ENABLE_TAGGING", "false").lower() == "true"
GCS_BUCKET_NAME = os.environ.get("GCS_BUCKET_NAME", None) GCS_BUCKET_NAME = os.environ.get("GCS_BUCKET_NAME", None)
GOOGLE_APPLICATION_CREDENTIALS_JSON = os.environ.get( GOOGLE_APPLICATION_CREDENTIALS_JSON = os.environ.get(
@ -1255,7 +1215,16 @@ ENABLE_USER_WEBHOOKS = PersistentConfig(
) )
# FastAPI / AnyIO settings # FastAPI / AnyIO settings
THREAD_POOL_SIZE = int(os.getenv("THREAD_POOL_SIZE", "0")) THREAD_POOL_SIZE = os.getenv("THREAD_POOL_SIZE", None)
if THREAD_POOL_SIZE is not None and isinstance(THREAD_POOL_SIZE, str):
try:
THREAD_POOL_SIZE = int(THREAD_POOL_SIZE)
except ValueError:
log.warning(
f"THREAD_POOL_SIZE is not a valid integer: {THREAD_POOL_SIZE}. Defaulting to None."
)
THREAD_POOL_SIZE = None
def validate_cors_origins(origins): def validate_cors_origins(origins):
@ -1357,6 +1326,9 @@ Generate a concise, 3-5 word title with an emoji summarizing the chat history.
- Use emojis that enhance understanding of the topic, but avoid quotation marks or special formatting. - Use emojis that enhance understanding of the topic, but avoid quotation marks or special formatting.
- Write the title in the chat's primary language; default to English if multilingual. - Write the title in the chat's primary language; default to English if multilingual.
- Prioritize accuracy over excessive creativity; keep it clear and simple. - Prioritize accuracy over excessive creativity; keep it clear and simple.
- Your entire response must consist solely of the JSON object, without any introductory or concluding text.
- The output must be a single, raw JSON object, without any markdown code fences or other encapsulating text.
- Ensure no conversational text, affirmations, or explanations precede or follow the raw JSON output, as this will cause direct parsing failure.
### Output: ### Output:
JSON format: { "title": "your concise title here" } JSON format: { "title": "your concise title here" }
### Examples: ### Examples:
@ -1699,7 +1671,8 @@ DEFAULT_CODE_INTERPRETER_PROMPT = """
1. **Code Interpreter**: `<code_interpreter type="code" lang="python"></code_interpreter>` 1. **Code Interpreter**: `<code_interpreter type="code" lang="python"></code_interpreter>`
- You have access to a Python shell that runs directly in the user's browser, enabling fast execution of code for analysis, calculations, or problem-solving. Use it in this response. - You have access to a Python shell that runs directly in the user's browser, enabling fast execution of code for analysis, calculations, or problem-solving. Use it in this response.
- The Python code you write can incorporate a wide array of libraries, handle data manipulation or visualization, perform API calls for web-related tasks, or tackle virtually any computational challenge. Use this flexibility to **think outside the box, craft elegant solutions, and harness Python's full potential**. - The Python code you write can incorporate a wide array of libraries, handle data manipulation or visualization, perform API calls for web-related tasks, or tackle virtually any computational challenge. Use this flexibility to **think outside the box, craft elegant solutions, and harness Python's full potential**.
- To use it, **you must enclose your code within `<code_interpreter type="code" lang="python">` XML tags** and stop right away. If you don't, the code won't execute. Do NOT use triple backticks. - To use it, **you must enclose your code within `<code_interpreter type="code" lang="python">` XML tags** and stop right away. If you don't, the code won't execute.
- When writing code in the code_interpreter XML tag, Do NOT use the triple backticks code block for markdown formatting, example: ```py # python code ``` will cause an error because it is markdown formatting, it is not python code.
- When coding, **always aim to print meaningful outputs** (e.g., results, tables, summaries, or visuals) to better interpret and verify the findings. Avoid relying on implicit outputs; prioritize explicit and clear print statements so the results are effectively communicated to the user. - When coding, **always aim to print meaningful outputs** (e.g., results, tables, summaries, or visuals) to better interpret and verify the findings. Avoid relying on implicit outputs; prioritize explicit and clear print statements so the results are effectively communicated to the user.
- After obtaining the printed output, **always provide a concise analysis, interpretation, or next steps to help the user understand the findings or refine the outcome further.** - After obtaining the printed output, **always provide a concise analysis, interpretation, or next steps to help the user understand the findings or refine the outcome further.**
- If the results are unclear, unexpected, or require validation, refine the code and execute it again as needed. Always aim to deliver meaningful insights from the results, iterating if necessary. - If the results are unclear, unexpected, or require validation, refine the code and execute it again as needed. Always aim to deliver meaningful insights from the results, iterating if necessary.
@ -1746,6 +1719,12 @@ MILVUS_URI = os.environ.get("MILVUS_URI", f"{DATA_DIR}/vector_db/milvus.db")
MILVUS_DB = os.environ.get("MILVUS_DB", "default") MILVUS_DB = os.environ.get("MILVUS_DB", "default")
MILVUS_TOKEN = os.environ.get("MILVUS_TOKEN", None) MILVUS_TOKEN = os.environ.get("MILVUS_TOKEN", None)
MILVUS_INDEX_TYPE = os.environ.get("MILVUS_INDEX_TYPE", "HNSW")
MILVUS_METRIC_TYPE = os.environ.get("MILVUS_METRIC_TYPE", "COSINE")
MILVUS_HNSW_M = int(os.environ.get("MILVUS_HNSW_M", "16"))
MILVUS_HNSW_EFCONSTRUCTION = int(os.environ.get("MILVUS_HNSW_EFCONSTRUCTION", "100"))
MILVUS_IVF_FLAT_NLIST = int(os.environ.get("MILVUS_IVF_FLAT_NLIST", "128"))
# Qdrant # Qdrant
QDRANT_URI = os.environ.get("QDRANT_URI", None) QDRANT_URI = os.environ.get("QDRANT_URI", None)
QDRANT_API_KEY = os.environ.get("QDRANT_API_KEY", None) QDRANT_API_KEY = os.environ.get("QDRANT_API_KEY", None)
@ -1833,6 +1812,11 @@ ONEDRIVE_SHAREPOINT_URL = PersistentConfig(
os.environ.get("ONEDRIVE_SHAREPOINT_URL", ""), os.environ.get("ONEDRIVE_SHAREPOINT_URL", ""),
) )
ONEDRIVE_SHAREPOINT_TENANT_ID = PersistentConfig(
"ONEDRIVE_SHAREPOINT_TENANT_ID",
"onedrive.sharepoint_tenant_id",
os.environ.get("ONEDRIVE_SHAREPOINT_TENANT_ID", ""),
)
# RAG Content Extraction # RAG Content Extraction
CONTENT_EXTRACTION_ENGINE = PersistentConfig( CONTENT_EXTRACTION_ENGINE = PersistentConfig(
@ -1981,6 +1965,12 @@ RAG_EMBEDDING_PREFIX_FIELD_NAME = os.environ.get(
"RAG_EMBEDDING_PREFIX_FIELD_NAME", None "RAG_EMBEDDING_PREFIX_FIELD_NAME", None
) )
RAG_RERANKING_ENGINE = PersistentConfig(
"RAG_RERANKING_ENGINE",
"rag.reranking_engine",
os.environ.get("RAG_RERANKING_ENGINE", ""),
)
RAG_RERANKING_MODEL = PersistentConfig( RAG_RERANKING_MODEL = PersistentConfig(
"RAG_RERANKING_MODEL", "RAG_RERANKING_MODEL",
"rag.reranking_model", "rag.reranking_model",
@ -1989,6 +1979,7 @@ RAG_RERANKING_MODEL = PersistentConfig(
if RAG_RERANKING_MODEL.value != "": if RAG_RERANKING_MODEL.value != "":
log.info(f"Reranking model set: {RAG_RERANKING_MODEL.value}") log.info(f"Reranking model set: {RAG_RERANKING_MODEL.value}")
RAG_RERANKING_MODEL_AUTO_UPDATE = ( RAG_RERANKING_MODEL_AUTO_UPDATE = (
not OFFLINE_MODE not OFFLINE_MODE
and os.environ.get("RAG_RERANKING_MODEL_AUTO_UPDATE", "True").lower() == "true" and os.environ.get("RAG_RERANKING_MODEL_AUTO_UPDATE", "True").lower() == "true"
@ -1998,6 +1989,18 @@ RAG_RERANKING_MODEL_TRUST_REMOTE_CODE = (
os.environ.get("RAG_RERANKING_MODEL_TRUST_REMOTE_CODE", "True").lower() == "true" os.environ.get("RAG_RERANKING_MODEL_TRUST_REMOTE_CODE", "True").lower() == "true"
) )
RAG_EXTERNAL_RERANKER_URL = PersistentConfig(
"RAG_EXTERNAL_RERANKER_URL",
"rag.external_reranker_url",
os.environ.get("RAG_EXTERNAL_RERANKER_URL", ""),
)
RAG_EXTERNAL_RERANKER_API_KEY = PersistentConfig(
"RAG_EXTERNAL_RERANKER_API_KEY",
"rag.external_reranker_api_key",
os.environ.get("RAG_EXTERNAL_RERANKER_API_KEY", ""),
)
RAG_TEXT_SPLITTER = PersistentConfig( RAG_TEXT_SPLITTER = PersistentConfig(
"RAG_TEXT_SPLITTER", "RAG_TEXT_SPLITTER",

View file

@ -103,6 +103,7 @@ from open_webui.config import (
ENABLE_OPENAI_API, ENABLE_OPENAI_API,
ONEDRIVE_CLIENT_ID, ONEDRIVE_CLIENT_ID,
ONEDRIVE_SHAREPOINT_URL, ONEDRIVE_SHAREPOINT_URL,
ONEDRIVE_SHAREPOINT_TENANT_ID,
OPENAI_API_BASE_URLS, OPENAI_API_BASE_URLS,
OPENAI_API_KEYS, OPENAI_API_KEYS,
OPENAI_API_CONFIGS, OPENAI_API_CONFIGS,
@ -187,7 +188,10 @@ from open_webui.config import (
RAG_EMBEDDING_MODEL, RAG_EMBEDDING_MODEL,
RAG_EMBEDDING_MODEL_AUTO_UPDATE, RAG_EMBEDDING_MODEL_AUTO_UPDATE,
RAG_EMBEDDING_MODEL_TRUST_REMOTE_CODE, RAG_EMBEDDING_MODEL_TRUST_REMOTE_CODE,
RAG_RERANKING_ENGINE,
RAG_RERANKING_MODEL, RAG_RERANKING_MODEL,
RAG_EXTERNAL_RERANKER_URL,
RAG_EXTERNAL_RERANKER_API_KEY,
RAG_RERANKING_MODEL_AUTO_UPDATE, RAG_RERANKING_MODEL_AUTO_UPDATE,
RAG_RERANKING_MODEL_TRUST_REMOTE_CODE, RAG_RERANKING_MODEL_TRUST_REMOTE_CODE,
RAG_EMBEDDING_ENGINE, RAG_EMBEDDING_ENGINE,
@ -255,6 +259,7 @@ from open_webui.config import (
GOOGLE_DRIVE_API_KEY, GOOGLE_DRIVE_API_KEY,
ONEDRIVE_CLIENT_ID, ONEDRIVE_CLIENT_ID,
ONEDRIVE_SHAREPOINT_URL, ONEDRIVE_SHAREPOINT_URL,
ONEDRIVE_SHAREPOINT_TENANT_ID,
ENABLE_RAG_HYBRID_SEARCH, ENABLE_RAG_HYBRID_SEARCH,
ENABLE_RAG_LOCAL_WEB_FETCH, ENABLE_RAG_LOCAL_WEB_FETCH,
ENABLE_WEB_LOADER_SSL_VERIFICATION, ENABLE_WEB_LOADER_SSL_VERIFICATION,
@ -459,10 +464,9 @@ async def lifespan(app: FastAPI):
log.info("Installing external dependencies of functions and tools...") log.info("Installing external dependencies of functions and tools...")
install_tool_and_function_dependencies() install_tool_and_function_dependencies()
pool_size = THREAD_POOL_SIZE if THREAD_POOL_SIZE and THREAD_POOL_SIZE > 0:
if pool_size and pool_size > 0:
limiter = anyio.to_thread.current_default_thread_limiter() limiter = anyio.to_thread.current_default_thread_limiter()
limiter.total_tokens = pool_size limiter.total_tokens = THREAD_POOL_SIZE
asyncio.create_task(periodic_usage_pool_cleanup()) asyncio.create_task(periodic_usage_pool_cleanup())
@ -654,7 +658,12 @@ app.state.config.CHUNK_OVERLAP = CHUNK_OVERLAP
app.state.config.RAG_EMBEDDING_ENGINE = RAG_EMBEDDING_ENGINE app.state.config.RAG_EMBEDDING_ENGINE = RAG_EMBEDDING_ENGINE
app.state.config.RAG_EMBEDDING_MODEL = RAG_EMBEDDING_MODEL app.state.config.RAG_EMBEDDING_MODEL = RAG_EMBEDDING_MODEL
app.state.config.RAG_EMBEDDING_BATCH_SIZE = RAG_EMBEDDING_BATCH_SIZE app.state.config.RAG_EMBEDDING_BATCH_SIZE = RAG_EMBEDDING_BATCH_SIZE
app.state.config.RAG_RERANKING_ENGINE = RAG_RERANKING_ENGINE
app.state.config.RAG_RERANKING_MODEL = RAG_RERANKING_MODEL app.state.config.RAG_RERANKING_MODEL = RAG_RERANKING_MODEL
app.state.config.RAG_EXTERNAL_RERANKER_URL = RAG_EXTERNAL_RERANKER_URL
app.state.config.RAG_EXTERNAL_RERANKER_API_KEY = RAG_EXTERNAL_RERANKER_API_KEY
app.state.config.RAG_TEMPLATE = RAG_TEMPLATE app.state.config.RAG_TEMPLATE = RAG_TEMPLATE
app.state.config.RAG_OPENAI_API_BASE_URL = RAG_OPENAI_API_BASE_URL app.state.config.RAG_OPENAI_API_BASE_URL = RAG_OPENAI_API_BASE_URL
@ -735,7 +744,10 @@ try:
) )
app.state.rf = get_rf( app.state.rf = get_rf(
app.state.config.RAG_RERANKING_ENGINE,
app.state.config.RAG_RERANKING_MODEL, app.state.config.RAG_RERANKING_MODEL,
app.state.config.RAG_EXTERNAL_RERANKER_URL,
app.state.config.RAG_EXTERNAL_RERANKER_API_KEY,
RAG_RERANKING_MODEL_AUTO_UPDATE, RAG_RERANKING_MODEL_AUTO_UPDATE,
) )
except Exception as e: except Exception as e:
@ -1381,6 +1393,7 @@ async def get_app_config(request: Request):
"onedrive": { "onedrive": {
"client_id": ONEDRIVE_CLIENT_ID.value, "client_id": ONEDRIVE_CLIENT_ID.value,
"sharepoint_url": ONEDRIVE_SHAREPOINT_URL.value, "sharepoint_url": ONEDRIVE_SHAREPOINT_URL.value,
"sharepoint_tenant_id": ONEDRIVE_SHAREPOINT_TENANT_ID.value,
}, },
"license_metadata": app.state.LICENSE_METADATA, "license_metadata": app.state.LICENSE_METADATA,
**( **(

View file

@ -0,0 +1,58 @@
import logging
import requests
from typing import Optional, List, Tuple
from open_webui.env import SRC_LOG_LEVELS
log = logging.getLogger(__name__)
log.setLevel(SRC_LOG_LEVELS["RAG"])
class ExternalReranker:
def __init__(
self,
api_key: str,
url: str = "http://localhost:8080/v1/rerank",
model: str = "reranker",
):
self.api_key = api_key
self.url = url
self.model = model
def predict(self, sentences: List[Tuple[str, str]]) -> Optional[List[float]]:
query = sentences[0][0]
docs = [i[1] for i in sentences]
payload = {
"model": self.model,
"query": query,
"documents": docs,
"top_n": len(docs),
}
try:
log.info(f"ExternalReranker:predict:model {self.model}")
log.info(f"ExternalReranker:predict:query {query}")
r = requests.post(
f"{self.url}",
headers={
"Content-Type": "application/json",
"Authorization": f"Bearer {self.api_key}",
},
json=payload,
)
r.raise_for_status()
data = r.json()
if "results" in data:
sorted_results = sorted(data["results"], key=lambda x: x["index"])
return [result["relevance_score"] for result in sorted_results]
else:
log.error("No results found in external reranking response")
return None
except Exception as e:
log.exception(f"Error in external reranking: {e}")
return None

View file

@ -3,7 +3,6 @@ from pymilvus import FieldSchema, DataType
import json import json
import logging import logging
from typing import Optional from typing import Optional
from open_webui.retrieval.vector.main import ( from open_webui.retrieval.vector.main import (
VectorDBBase, VectorDBBase,
VectorItem, VectorItem,
@ -14,6 +13,11 @@ from open_webui.config import (
MILVUS_URI, MILVUS_URI,
MILVUS_DB, MILVUS_DB,
MILVUS_TOKEN, MILVUS_TOKEN,
MILVUS_INDEX_TYPE,
MILVUS_METRIC_TYPE,
MILVUS_HNSW_M,
MILVUS_HNSW_EFCONSTRUCTION,
MILVUS_IVF_FLAT_NLIST,
) )
from open_webui.env import SRC_LOG_LEVELS from open_webui.env import SRC_LOG_LEVELS
@ -33,7 +37,6 @@ class MilvusClient(VectorDBBase):
ids = [] ids = []
documents = [] documents = []
metadatas = [] metadatas = []
for match in result: for match in result:
_ids = [] _ids = []
_documents = [] _documents = []
@ -42,11 +45,9 @@ class MilvusClient(VectorDBBase):
_ids.append(item.get("id")) _ids.append(item.get("id"))
_documents.append(item.get("data", {}).get("text")) _documents.append(item.get("data", {}).get("text"))
_metadatas.append(item.get("metadata")) _metadatas.append(item.get("metadata"))
ids.append(_ids) ids.append(_ids)
documents.append(_documents) documents.append(_documents)
metadatas.append(_metadatas) metadatas.append(_metadatas)
return GetResult( return GetResult(
**{ **{
"ids": ids, "ids": ids,
@ -60,13 +61,11 @@ class MilvusClient(VectorDBBase):
distances = [] distances = []
documents = [] documents = []
metadatas = [] metadatas = []
for match in result: for match in result:
_ids = [] _ids = []
_distances = [] _distances = []
_documents = [] _documents = []
_metadatas = [] _metadatas = []
for item in match: for item in match:
_ids.append(item.get("id")) _ids.append(item.get("id"))
# normalize milvus score from [-1, 1] to [0, 1] range # normalize milvus score from [-1, 1] to [0, 1] range
@ -75,12 +74,10 @@ class MilvusClient(VectorDBBase):
_distances.append(_dist) _distances.append(_dist)
_documents.append(item.get("entity", {}).get("data", {}).get("text")) _documents.append(item.get("entity", {}).get("data", {}).get("text"))
_metadatas.append(item.get("entity", {}).get("metadata")) _metadatas.append(item.get("entity", {}).get("metadata"))
ids.append(_ids) ids.append(_ids)
distances.append(_distances) distances.append(_distances)
documents.append(_documents) documents.append(_documents)
metadatas.append(_metadatas) metadatas.append(_metadatas)
return SearchResult( return SearchResult(
**{ **{
"ids": ids, "ids": ids,
@ -113,11 +110,39 @@ class MilvusClient(VectorDBBase):
) )
index_params = self.client.prepare_index_params() index_params = self.client.prepare_index_params()
# Use configurations from config.py
index_type = MILVUS_INDEX_TYPE.upper()
metric_type = MILVUS_METRIC_TYPE.upper()
log.info(f"Using Milvus index type: {index_type}, metric type: {metric_type}")
index_creation_params = {}
if index_type == "HNSW":
index_creation_params = {
"M": MILVUS_HNSW_M,
"efConstruction": MILVUS_HNSW_EFCONSTRUCTION,
}
log.info(f"HNSW params: {index_creation_params}")
elif index_type == "IVF_FLAT":
index_creation_params = {"nlist": MILVUS_IVF_FLAT_NLIST}
log.info(f"IVF_FLAT params: {index_creation_params}")
elif index_type in ["FLAT", "AUTOINDEX"]:
log.info(f"Using {index_type} index with no specific build-time params.")
else:
log.warning(
f"Unsupported MILVUS_INDEX_TYPE: '{index_type}'. "
f"Supported types: HNSW, IVF_FLAT, FLAT, AUTOINDEX. "
f"Milvus will use its default for the collection if this type is not directly supported for index creation."
)
# For unsupported types, pass the type directly to Milvus; it might handle it or use a default.
# If Milvus errors out, the user needs to correct the MILVUS_INDEX_TYPE env var.
index_params.add_index( index_params.add_index(
field_name="vector", field_name="vector",
index_type="HNSW", index_type=index_type,
metric_type="COSINE", metric_type=metric_type,
params={"M": 16, "efConstruction": 100}, params=index_creation_params,
) )
self.client.create_collection( self.client.create_collection(
@ -125,6 +150,9 @@ class MilvusClient(VectorDBBase):
schema=schema, schema=schema,
index_params=index_params, index_params=index_params,
) )
log.info(
f"Successfully created collection '{self.collection_prefix}_{collection_name}' with index type '{index_type}' and metric '{metric_type}'."
)
def has_collection(self, collection_name: str) -> bool: def has_collection(self, collection_name: str) -> bool:
# Check if the collection exists based on the collection name. # Check if the collection exists based on the collection name.
@ -145,84 +173,113 @@ class MilvusClient(VectorDBBase):
) -> Optional[SearchResult]: ) -> Optional[SearchResult]:
# Search for the nearest neighbor items based on the vectors and return 'limit' number of results. # Search for the nearest neighbor items based on the vectors and return 'limit' number of results.
collection_name = collection_name.replace("-", "_") collection_name = collection_name.replace("-", "_")
# For some index types like IVF_FLAT, search params like nprobe can be set.
# Example: search_params = {"nprobe": 10} if using IVF_FLAT
# For simplicity, not adding configurable search_params here, but could be extended.
result = self.client.search( result = self.client.search(
collection_name=f"{self.collection_prefix}_{collection_name}", collection_name=f"{self.collection_prefix}_{collection_name}",
data=vectors, data=vectors,
limit=limit, limit=limit,
output_fields=["data", "metadata"], output_fields=["data", "metadata"],
# search_params=search_params # Potentially add later if needed
) )
return self._result_to_search_result(result) return self._result_to_search_result(result)
def query(self, collection_name: str, filter: dict, limit: Optional[int] = None): def query(self, collection_name: str, filter: dict, limit: Optional[int] = None):
# Construct the filter string for querying # Construct the filter string for querying
collection_name = collection_name.replace("-", "_") collection_name = collection_name.replace("-", "_")
if not self.has_collection(collection_name): if not self.has_collection(collection_name):
log.warning(
f"Query attempted on non-existent collection: {self.collection_prefix}_{collection_name}"
)
return None return None
filter_string = " && ".join( filter_string = " && ".join(
[ [
f'metadata["{key}"] == {json.dumps(value)}' f'metadata["{key}"] == {json.dumps(value)}'
for key, value in filter.items() for key, value in filter.items()
] ]
) )
max_limit = 16383 # The maximum number of records per request max_limit = 16383 # The maximum number of records per request
all_results = [] all_results = []
if limit is None: if limit is None:
limit = float("inf") # Use infinity as a placeholder for no limit # Milvus default limit for query if not specified is 16384, but docs mention iteration.
# Let's set a practical high number if "all" is intended, or handle true pagination.
# For now, if limit is None, we'll fetch in batches up to a very large number.
# This part could be refined based on expected use cases for "get all".
# For this function signature, None implies "as many as possible" up to Milvus limits.
limit = (
16384 * 10
) # A large number to signify fetching many, will be capped by actual data or max_limit per call.
log.info(
f"Limit not specified for query, fetching up to {limit} results in batches."
)
# Initialize offset and remaining to handle pagination # Initialize offset and remaining to handle pagination
offset = 0 offset = 0
remaining = limit remaining = limit
try: try:
log.info(
f"Querying collection {self.collection_prefix}_{collection_name} with filter: '{filter_string}', limit: {limit}"
)
# Loop until there are no more items to fetch or the desired limit is reached # Loop until there are no more items to fetch or the desired limit is reached
while remaining > 0: while remaining > 0:
log.info(f"remaining: {remaining}")
current_fetch = min( current_fetch = min(
max_limit, remaining max_limit, remaining if isinstance(remaining, int) else max_limit
) # Determine how many items to fetch in this iteration )
log.debug(
f"Querying with offset: {offset}, current_fetch: {current_fetch}"
)
results = self.client.query( results = self.client.query(
collection_name=f"{self.collection_prefix}_{collection_name}", collection_name=f"{self.collection_prefix}_{collection_name}",
filter=filter_string, filter=filter_string,
output_fields=["*"], output_fields=[
"id",
"data",
"metadata",
], # Explicitly list needed fields. Vector not usually needed in query.
limit=current_fetch, limit=current_fetch,
offset=offset, offset=offset,
) )
if not results: if not results:
log.debug("No more results from query.")
break break
all_results.extend(results) all_results.extend(results)
results_count = len(results) results_count = len(results)
remaining -= ( log.debug(f"Fetched {results_count} results in this batch.")
results_count # Decrease remaining by the number of items fetched
) if isinstance(remaining, int):
remaining -= results_count
offset += results_count offset += results_count
# Break the loop if the results returned are less than the requested fetch count # Break the loop if the results returned are less than the requested fetch count (means end of data)
if results_count < current_fetch: if results_count < current_fetch:
log.debug(
"Fetched less than requested, assuming end of results for this query."
)
break break
log.debug(all_results) log.info(f"Total results from query: {len(all_results)}")
return self._result_to_get_result([all_results]) return self._result_to_get_result([all_results])
except Exception as e: except Exception as e:
log.exception( log.exception(
f"Error querying collection {collection_name} with limit {limit}: {e}" f"Error querying collection {self.collection_prefix}_{collection_name} with filter '{filter_string}' and limit {limit}: {e}"
) )
return None return None
def get(self, collection_name: str) -> Optional[GetResult]: def get(self, collection_name: str) -> Optional[GetResult]:
# Get all the items in the collection. # Get all the items in the collection. This can be very resource-intensive for large collections.
collection_name = collection_name.replace("-", "_") collection_name = collection_name.replace("-", "_")
result = self.client.query( log.warning(
collection_name=f"{self.collection_prefix}_{collection_name}", f"Fetching ALL items from collection '{self.collection_prefix}_{collection_name}'. This might be slow for large collections."
filter='id != ""',
) )
return self._result_to_get_result([result]) # Using query with a trivial filter to get all items.
# This will use the paginated query logic.
return self.query(collection_name=collection_name, filter={}, limit=None)
def insert(self, collection_name: str, items: list[VectorItem]): def insert(self, collection_name: str, items: list[VectorItem]):
# Insert the items into the collection, if the collection does not exist, it will be created. # Insert the items into the collection, if the collection does not exist, it will be created.
@ -230,10 +287,23 @@ class MilvusClient(VectorDBBase):
if not self.client.has_collection( if not self.client.has_collection(
collection_name=f"{self.collection_prefix}_{collection_name}" collection_name=f"{self.collection_prefix}_{collection_name}"
): ):
log.info(
f"Collection {self.collection_prefix}_{collection_name} does not exist. Creating now."
)
if not items:
log.error(
f"Cannot create collection {self.collection_prefix}_{collection_name} without items to determine dimension."
)
raise ValueError(
"Cannot create Milvus collection without items to determine vector dimension."
)
self._create_collection( self._create_collection(
collection_name=collection_name, dimension=len(items[0]["vector"]) collection_name=collection_name, dimension=len(items[0]["vector"])
) )
log.info(
f"Inserting {len(items)} items into collection {self.collection_prefix}_{collection_name}."
)
return self.client.insert( return self.client.insert(
collection_name=f"{self.collection_prefix}_{collection_name}", collection_name=f"{self.collection_prefix}_{collection_name}",
data=[ data=[
@ -253,10 +323,23 @@ class MilvusClient(VectorDBBase):
if not self.client.has_collection( if not self.client.has_collection(
collection_name=f"{self.collection_prefix}_{collection_name}" collection_name=f"{self.collection_prefix}_{collection_name}"
): ):
log.info(
f"Collection {self.collection_prefix}_{collection_name} does not exist for upsert. Creating now."
)
if not items:
log.error(
f"Cannot create collection {self.collection_prefix}_{collection_name} for upsert without items to determine dimension."
)
raise ValueError(
"Cannot create Milvus collection for upsert without items to determine vector dimension."
)
self._create_collection( self._create_collection(
collection_name=collection_name, dimension=len(items[0]["vector"]) collection_name=collection_name, dimension=len(items[0]["vector"])
) )
log.info(
f"Upserting {len(items)} items into collection {self.collection_prefix}_{collection_name}."
)
return self.client.upsert( return self.client.upsert(
collection_name=f"{self.collection_prefix}_{collection_name}", collection_name=f"{self.collection_prefix}_{collection_name}",
data=[ data=[
@ -276,30 +359,55 @@ class MilvusClient(VectorDBBase):
ids: Optional[list[str]] = None, ids: Optional[list[str]] = None,
filter: Optional[dict] = None, filter: Optional[dict] = None,
): ):
# Delete the items from the collection based on the ids. # Delete the items from the collection based on the ids or filter.
collection_name = collection_name.replace("-", "_") collection_name = collection_name.replace("-", "_")
if not self.has_collection(collection_name):
log.warning(
f"Delete attempted on non-existent collection: {self.collection_prefix}_{collection_name}"
)
return None
if ids: if ids:
log.info(
f"Deleting items by IDs from {self.collection_prefix}_{collection_name}. IDs: {ids}"
)
return self.client.delete( return self.client.delete(
collection_name=f"{self.collection_prefix}_{collection_name}", collection_name=f"{self.collection_prefix}_{collection_name}",
ids=ids, ids=ids,
) )
elif filter: elif filter:
# Convert the filter dictionary to a string using JSON_CONTAINS.
filter_string = " && ".join( filter_string = " && ".join(
[ [
f'metadata["{key}"] == {json.dumps(value)}' f'metadata["{key}"] == {json.dumps(value)}'
for key, value in filter.items() for key, value in filter.items()
] ]
) )
log.info(
f"Deleting items by filter from {self.collection_prefix}_{collection_name}. Filter: {filter_string}"
)
return self.client.delete( return self.client.delete(
collection_name=f"{self.collection_prefix}_{collection_name}", collection_name=f"{self.collection_prefix}_{collection_name}",
filter=filter_string, filter=filter_string,
) )
else:
log.warning(
f"Delete operation on {self.collection_prefix}_{collection_name} called without IDs or filter. No action taken."
)
return None
def reset(self): def reset(self):
# Resets the database. This will delete all collections and item entries. # Resets the database. This will delete all collections and item entries that match the prefix.
log.warning(
f"Resetting Milvus: Deleting all collections with prefix '{self.collection_prefix}'."
)
collection_names = self.client.list_collections() collection_names = self.client.list_collections()
for collection_name in collection_names: deleted_collections = []
if collection_name.startswith(self.collection_prefix): for collection_name_full in collection_names:
self.client.drop_collection(collection_name=collection_name) if collection_name_full.startswith(self.collection_prefix):
try:
self.client.drop_collection(collection_name=collection_name_full)
deleted_collections.append(collection_name_full)
log.info(f"Deleted collection: {collection_name_full}")
except Exception as e:
log.error(f"Error deleting collection {collection_name_full}: {e}")
log.info(f"Milvus reset complete. Deleted collections: {deleted_collections}")

View file

@ -1,6 +1,13 @@
from typing import Optional, List, Dict, Any, Union from typing import Optional, List, Dict, Any, Union
import logging import logging
from pinecone import Pinecone, ServerlessSpec import time # for measuring elapsed time
from pinecone import ServerlessSpec
import asyncio # for async upserts
import functools # for partial binding in async tasks
import concurrent.futures # for parallel batch upserts
from pinecone.grpc import PineconeGRPC # use gRPC client for faster upserts
from open_webui.retrieval.vector.main import ( from open_webui.retrieval.vector.main import (
VectorDBBase, VectorDBBase,
@ -40,8 +47,13 @@ class PineconeClient(VectorDBBase):
self.metric = PINECONE_METRIC self.metric = PINECONE_METRIC
self.cloud = PINECONE_CLOUD self.cloud = PINECONE_CLOUD
# Initialize Pinecone client # Initialize Pinecone gRPC client for improved performance
self.client = Pinecone(api_key=self.api_key) self.client = PineconeGRPC(
api_key=self.api_key, environment=self.environment, cloud=self.cloud
)
# Persistent executor for batch operations
self._executor = concurrent.futures.ThreadPoolExecutor(max_workers=5)
# Create index if it doesn't exist # Create index if it doesn't exist
self._initialize_index() self._initialize_index()
@ -191,27 +203,29 @@ class PineconeClient(VectorDBBase):
log.warning("No items to insert") log.warning("No items to insert")
return return
start_time = time.time()
collection_name_with_prefix = self._get_collection_name_with_prefix( collection_name_with_prefix = self._get_collection_name_with_prefix(
collection_name collection_name
) )
points = self._create_points(items, collection_name_with_prefix) points = self._create_points(items, collection_name_with_prefix)
# Insert in batches for better performance and reliability # Parallelize batch inserts for performance
executor = self._executor
futures = []
for i in range(0, len(points), BATCH_SIZE): for i in range(0, len(points), BATCH_SIZE):
batch = points[i : i + BATCH_SIZE] batch = points[i : i + BATCH_SIZE]
futures.append(executor.submit(self.index.upsert, vectors=batch))
for future in concurrent.futures.as_completed(futures):
try: try:
self.index.upsert(vectors=batch) future.result()
log.debug(
f"Inserted batch of {len(batch)} vectors into '{collection_name_with_prefix}'"
)
except Exception as e: except Exception as e:
log.error( log.error(f"Error inserting batch: {e}")
f"Error inserting batch into '{collection_name_with_prefix}': {e}"
)
raise raise
elapsed = time.time() - start_time
log.debug(f"Insert of {len(points)} vectors took {elapsed:.2f} seconds")
log.info( log.info(
f"Successfully inserted {len(items)} vectors into '{collection_name_with_prefix}'" f"Successfully inserted {len(points)} vectors in parallel batches into '{collection_name_with_prefix}'"
) )
def upsert(self, collection_name: str, items: List[VectorItem]) -> None: def upsert(self, collection_name: str, items: List[VectorItem]) -> None:
@ -220,29 +234,119 @@ class PineconeClient(VectorDBBase):
log.warning("No items to upsert") log.warning("No items to upsert")
return return
start_time = time.time()
collection_name_with_prefix = self._get_collection_name_with_prefix(
collection_name
)
points = self._create_points(items, collection_name_with_prefix)
# Parallelize batch upserts for performance
executor = self._executor
futures = []
for i in range(0, len(points), BATCH_SIZE):
batch = points[i : i + BATCH_SIZE]
futures.append(executor.submit(self.index.upsert, vectors=batch))
for future in concurrent.futures.as_completed(futures):
try:
future.result()
except Exception as e:
log.error(f"Error upserting batch: {e}")
raise
elapsed = time.time() - start_time
log.debug(f"Upsert of {len(points)} vectors took {elapsed:.2f} seconds")
log.info(
f"Successfully upserted {len(points)} vectors in parallel batches into '{collection_name_with_prefix}'"
)
async def insert_async(self, collection_name: str, items: List[VectorItem]) -> None:
"""Async version of insert using asyncio and run_in_executor for improved performance."""
if not items:
log.warning("No items to insert")
return
collection_name_with_prefix = self._get_collection_name_with_prefix(
collection_name
)
points = self._create_points(items, collection_name_with_prefix)
# Create batches
batches = [
points[i : i + BATCH_SIZE] for i in range(0, len(points), BATCH_SIZE)
]
loop = asyncio.get_event_loop()
tasks = [
loop.run_in_executor(
None, functools.partial(self.index.upsert, vectors=batch)
)
for batch in batches
]
results = await asyncio.gather(*tasks, return_exceptions=True)
for result in results:
if isinstance(result, Exception):
log.error(f"Error in async insert batch: {result}")
raise result
log.info(
f"Successfully async inserted {len(points)} vectors in batches into '{collection_name_with_prefix}'"
)
async def upsert_async(self, collection_name: str, items: List[VectorItem]) -> None:
"""Async version of upsert using asyncio and run_in_executor for improved performance."""
if not items:
log.warning("No items to upsert")
return
collection_name_with_prefix = self._get_collection_name_with_prefix( collection_name_with_prefix = self._get_collection_name_with_prefix(
collection_name collection_name
) )
points = self._create_points(items, collection_name_with_prefix) points = self._create_points(items, collection_name_with_prefix)
# Upsert in batches # Create batches
for i in range(0, len(points), BATCH_SIZE): batches = [
batch = points[i : i + BATCH_SIZE] points[i : i + BATCH_SIZE] for i in range(0, len(points), BATCH_SIZE)
try: ]
self.index.upsert(vectors=batch) loop = asyncio.get_event_loop()
log.debug( tasks = [
f"Upserted batch of {len(batch)} vectors into '{collection_name_with_prefix}'" loop.run_in_executor(
) None, functools.partial(self.index.upsert, vectors=batch)
except Exception as e: )
log.error( for batch in batches
f"Error upserting batch into '{collection_name_with_prefix}': {e}" ]
) results = await asyncio.gather(*tasks, return_exceptions=True)
raise for result in results:
if isinstance(result, Exception):
log.error(f"Error in async upsert batch: {result}")
raise result
log.info( log.info(
f"Successfully upserted {len(items)} vectors into '{collection_name_with_prefix}'" f"Successfully async upserted {len(points)} vectors in batches into '{collection_name_with_prefix}'"
) )
def streaming_upsert(self, collection_name: str, items: List[VectorItem]) -> None:
"""Perform a streaming upsert over gRPC for performance testing."""
if not items:
log.warning("No items to upsert via streaming")
return
collection_name_with_prefix = self._get_collection_name_with_prefix(
collection_name
)
points = self._create_points(items, collection_name_with_prefix)
# Open a streaming upsert channel
stream = self.index.streaming_upsert()
try:
for point in points:
# send each point over the stream
stream.send(point)
# close the stream to finalize
stream.close()
log.info(
f"Successfully streamed upsert of {len(points)} vectors into '{collection_name_with_prefix}'"
)
except Exception as e:
log.error(f"Error during streaming upsert: {e}")
raise
def search( def search(
self, collection_name: str, vectors: List[List[Union[float, int]]], limit: int self, collection_name: str, vectors: List[List[Union[float, int]]], limit: int
) -> Optional[SearchResult]: ) -> Optional[SearchResult]:
@ -410,3 +514,20 @@ class PineconeClient(VectorDBBase):
except Exception as e: except Exception as e:
log.error(f"Failed to reset Pinecone index: {e}") log.error(f"Failed to reset Pinecone index: {e}")
raise raise
def close(self):
"""Shut down the gRPC channel and thread pool."""
try:
self.client.close()
log.info("Pinecone gRPC channel closed.")
except Exception as e:
log.warning(f"Failed to close Pinecone gRPC channel: {e}")
self._executor.shutdown(wait=True)
def __enter__(self):
"""Enter context manager."""
return self
def __exit__(self, exc_type, exc_val, exc_tb):
"""Exit context manager, ensuring resources are cleaned up."""
self.close()

View file

@ -71,23 +71,27 @@ from pydub import AudioSegment
from pydub.utils import mediainfo from pydub.utils import mediainfo
def get_audio_format(file_path): def get_audio_convert_format(file_path):
"""Check if the given file needs to be converted to a different format.""" """Check if the given file needs to be converted to a different format."""
if not os.path.isfile(file_path): if not os.path.isfile(file_path):
log.error(f"File not found: {file_path}") log.error(f"File not found: {file_path}")
return False return False
info = mediainfo(file_path) try:
if ( info = mediainfo(file_path)
info.get("codec_name") == "aac"
and info.get("codec_type") == "audio" if (
and info.get("codec_tag_string") == "mp4a" info.get("codec_name") == "aac"
): and info.get("codec_type") == "audio"
return "mp4" and info.get("codec_tag_string") == "mp4a"
elif info.get("format_name") == "ogg": ):
return "ogg" return "mp4"
elif info.get("format_name") == "matroska,webm": elif info.get("format_name") == "ogg":
return "webm" return "ogg"
except Exception as e:
log.error(f"Error getting audio format: {e}")
return False
return None return None
@ -538,14 +542,17 @@ def transcribe(request: Request, file_path):
log.debug(data) log.debug(data)
return data return data
elif request.app.state.config.STT_ENGINE == "openai": elif request.app.state.config.STT_ENGINE == "openai":
audio_format = get_audio_format(file_path) convert_format = get_audio_convert_format(file_path)
if audio_format:
os.rename(file_path, file_path.replace(".wav", f".{audio_format}")) if convert_format:
ext = convert_format.split(".")[-1]
os.rename(file_path, file_path.replace(".{ext}", f".{convert_format}"))
# Convert unsupported audio file to WAV format # Convert unsupported audio file to WAV format
convert_audio_to_wav( convert_audio_to_wav(
file_path.replace(".wav", f".{audio_format}"), file_path.replace(".{ext}", f".{convert_format}"),
file_path, file_path,
audio_format, convert_format,
) )
r = None r = None

View file

@ -234,7 +234,7 @@ async def ldap_auth(request: Request, response: Response, form_data: LdapForm):
], ],
) )
if not search_success: if not search_success or not connection_app.entries:
raise HTTPException(400, detail="User not found in the LDAP server") raise HTTPException(400, detail="User not found in the LDAP server")
entry = connection_app.entries[0] entry = connection_app.entries[0]

View file

@ -133,6 +133,7 @@ def upload_file(
"audio/ogg", "audio/ogg",
"audio/x-m4a", "audio/x-m4a",
"audio/webm", "audio/webm",
"video/webm",
) )
): ):
file_path = Storage.get_file(file_path) file_path = Storage.get_file(file_path)
@ -150,7 +151,6 @@ def upload_file(
"video/mp4", "video/mp4",
"video/ogg", "video/ogg",
"video/quicktime", "video/quicktime",
"video/webm",
]: ]:
process_file(request, ProcessFileForm(file_id=id), user=user) process_file(request, ProcessFileForm(file_id=id), user=user)

View file

@ -3,6 +3,8 @@ import logging
import mimetypes import mimetypes
import os import os
import shutil import shutil
import asyncio
import uuid import uuid
from datetime import datetime from datetime import datetime
@ -135,7 +137,10 @@ def get_ef(
def get_rf( def get_rf(
engine: str = "",
reranking_model: Optional[str] = None, reranking_model: Optional[str] = None,
external_reranker_url: str = "",
external_reranker_api_key: str = "",
auto_update: bool = False, auto_update: bool = False,
): ):
rf = None rf = None
@ -153,19 +158,33 @@ def get_rf(
log.error(f"ColBERT: {e}") log.error(f"ColBERT: {e}")
raise Exception(ERROR_MESSAGES.DEFAULT(e)) raise Exception(ERROR_MESSAGES.DEFAULT(e))
else: else:
import sentence_transformers if engine == "external":
try:
from open_webui.retrieval.models.external import ExternalReranker
rf = ExternalReranker(
url=external_reranker_url,
api_key=external_reranker_api_key,
model=reranking_model,
)
except Exception as e:
log.error(f"ExternalReranking: {e}")
raise Exception(ERROR_MESSAGES.DEFAULT(e))
else:
import sentence_transformers
try:
rf = sentence_transformers.CrossEncoder(
get_model_path(reranking_model, auto_update),
device=DEVICE_TYPE,
trust_remote_code=RAG_RERANKING_MODEL_TRUST_REMOTE_CODE,
backend=SENTENCE_TRANSFORMERS_CROSS_ENCODER_BACKEND,
model_kwargs=SENTENCE_TRANSFORMERS_CROSS_ENCODER_MODEL_KWARGS,
)
except Exception as e:
log.error(f"CrossEncoder: {e}")
raise Exception(ERROR_MESSAGES.DEFAULT("CrossEncoder error"))
try:
rf = sentence_transformers.CrossEncoder(
get_model_path(reranking_model, auto_update),
device=DEVICE_TYPE,
trust_remote_code=RAG_RERANKING_MODEL_TRUST_REMOTE_CODE,
backend=SENTENCE_TRANSFORMERS_CROSS_ENCODER_BACKEND,
model_kwargs=SENTENCE_TRANSFORMERS_CROSS_ENCODER_MODEL_KWARGS,
)
except Exception as e:
log.error(f"CrossEncoder: {e}")
raise Exception(ERROR_MESSAGES.DEFAULT("CrossEncoder error"))
return rf return rf
@ -188,7 +207,7 @@ class ProcessUrlForm(CollectionNameForm):
class SearchForm(BaseModel): class SearchForm(BaseModel):
query: str queries: List[str]
@router.get("/") @router.get("/")
@ -223,14 +242,6 @@ async def get_embedding_config(request: Request, user=Depends(get_admin_user)):
} }
@router.get("/reranking")
async def get_reraanking_config(request: Request, user=Depends(get_admin_user)):
return {
"status": True,
"reranking_model": request.app.state.config.RAG_RERANKING_MODEL,
}
class OpenAIConfigForm(BaseModel): class OpenAIConfigForm(BaseModel):
url: str url: str
key: str key: str
@ -325,41 +336,6 @@ async def update_embedding_config(
) )
class RerankingModelUpdateForm(BaseModel):
reranking_model: str
@router.post("/reranking/update")
async def update_reranking_config(
request: Request, form_data: RerankingModelUpdateForm, user=Depends(get_admin_user)
):
log.info(
f"Updating reranking model: {request.app.state.config.RAG_RERANKING_MODEL} to {form_data.reranking_model}"
)
try:
request.app.state.config.RAG_RERANKING_MODEL = form_data.reranking_model
try:
request.app.state.rf = get_rf(
request.app.state.config.RAG_RERANKING_MODEL,
True,
)
except Exception as e:
log.error(f"Error loading reranking model: {e}")
request.app.state.config.ENABLE_RAG_HYBRID_SEARCH = False
return {
"status": True,
"reranking_model": request.app.state.config.RAG_RERANKING_MODEL,
}
except Exception as e:
log.exception(f"Problem updating reranking model: {e}")
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=ERROR_MESSAGES.DEFAULT(e),
)
@router.get("/config") @router.get("/config")
async def get_rag_config(request: Request, user=Depends(get_admin_user)): async def get_rag_config(request: Request, user=Depends(get_admin_user)):
return { return {
@ -383,6 +359,11 @@ async def get_rag_config(request: Request, user=Depends(get_admin_user)):
"DOCUMENT_INTELLIGENCE_ENDPOINT": request.app.state.config.DOCUMENT_INTELLIGENCE_ENDPOINT, "DOCUMENT_INTELLIGENCE_ENDPOINT": request.app.state.config.DOCUMENT_INTELLIGENCE_ENDPOINT,
"DOCUMENT_INTELLIGENCE_KEY": request.app.state.config.DOCUMENT_INTELLIGENCE_KEY, "DOCUMENT_INTELLIGENCE_KEY": request.app.state.config.DOCUMENT_INTELLIGENCE_KEY,
"MISTRAL_OCR_API_KEY": request.app.state.config.MISTRAL_OCR_API_KEY, "MISTRAL_OCR_API_KEY": request.app.state.config.MISTRAL_OCR_API_KEY,
# Reranking settings
"RAG_RERANKING_MODEL": request.app.state.config.RAG_RERANKING_MODEL,
"RAG_RERANKING_ENGINE": request.app.state.config.RAG_RERANKING_ENGINE,
"RAG_EXTERNAL_RERANKER_URL": request.app.state.config.RAG_EXTERNAL_RERANKER_URL,
"RAG_EXTERNAL_RERANKER_API_KEY": request.app.state.config.RAG_EXTERNAL_RERANKER_API_KEY,
# Chunking settings # Chunking settings
"TEXT_SPLITTER": request.app.state.config.TEXT_SPLITTER, "TEXT_SPLITTER": request.app.state.config.TEXT_SPLITTER,
"CHUNK_SIZE": request.app.state.config.CHUNK_SIZE, "CHUNK_SIZE": request.app.state.config.CHUNK_SIZE,
@ -519,6 +500,12 @@ class ConfigForm(BaseModel):
DOCUMENT_INTELLIGENCE_KEY: Optional[str] = None DOCUMENT_INTELLIGENCE_KEY: Optional[str] = None
MISTRAL_OCR_API_KEY: Optional[str] = None MISTRAL_OCR_API_KEY: Optional[str] = None
# Reranking settings
RAG_RERANKING_MODEL: Optional[str] = None
RAG_RERANKING_ENGINE: Optional[str] = None
RAG_EXTERNAL_RERANKER_URL: Optional[str] = None
RAG_EXTERNAL_RERANKER_API_KEY: Optional[str] = None
# Chunking settings # Chunking settings
TEXT_SPLITTER: Optional[str] = None TEXT_SPLITTER: Optional[str] = None
CHUNK_SIZE: Optional[int] = None CHUNK_SIZE: Optional[int] = None
@ -630,6 +617,49 @@ async def update_rag_config(
else request.app.state.config.MISTRAL_OCR_API_KEY else request.app.state.config.MISTRAL_OCR_API_KEY
) )
# Reranking settings
request.app.state.config.RAG_RERANKING_ENGINE = (
form_data.RAG_RERANKING_ENGINE
if form_data.RAG_RERANKING_ENGINE is not None
else request.app.state.config.RAG_RERANKING_ENGINE
)
request.app.state.config.RAG_EXTERNAL_RERANKER_URL = (
form_data.RAG_EXTERNAL_RERANKER_URL
if form_data.RAG_EXTERNAL_RERANKER_URL is not None
else request.app.state.config.RAG_EXTERNAL_RERANKER_URL
)
request.app.state.config.RAG_EXTERNAL_RERANKER_API_KEY = (
form_data.RAG_EXTERNAL_RERANKER_API_KEY
if form_data.RAG_EXTERNAL_RERANKER_API_KEY is not None
else request.app.state.config.RAG_EXTERNAL_RERANKER_API_KEY
)
log.info(
f"Updating reranking model: {request.app.state.config.RAG_RERANKING_MODEL} to {form_data.RAG_RERANKING_MODEL}"
)
try:
request.app.state.config.RAG_RERANKING_MODEL = form_data.RAG_RERANKING_MODEL
try:
request.app.state.rf = get_rf(
request.app.state.config.RAG_RERANKING_ENGINE,
request.app.state.config.RAG_RERANKING_MODEL,
request.app.state.config.RAG_EXTERNAL_RERANKER_URL,
request.app.state.config.RAG_EXTERNAL_RERANKER_API_KEY,
True,
)
except Exception as e:
log.error(f"Error loading reranking model: {e}")
request.app.state.config.ENABLE_RAG_HYBRID_SEARCH = False
except Exception as e:
log.exception(f"Problem updating reranking model: {e}")
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=ERROR_MESSAGES.DEFAULT(e),
)
# Chunking settings # Chunking settings
request.app.state.config.TEXT_SPLITTER = ( request.app.state.config.TEXT_SPLITTER = (
form_data.TEXT_SPLITTER form_data.TEXT_SPLITTER
@ -786,6 +816,11 @@ async def update_rag_config(
"DOCUMENT_INTELLIGENCE_ENDPOINT": request.app.state.config.DOCUMENT_INTELLIGENCE_ENDPOINT, "DOCUMENT_INTELLIGENCE_ENDPOINT": request.app.state.config.DOCUMENT_INTELLIGENCE_ENDPOINT,
"DOCUMENT_INTELLIGENCE_KEY": request.app.state.config.DOCUMENT_INTELLIGENCE_KEY, "DOCUMENT_INTELLIGENCE_KEY": request.app.state.config.DOCUMENT_INTELLIGENCE_KEY,
"MISTRAL_OCR_API_KEY": request.app.state.config.MISTRAL_OCR_API_KEY, "MISTRAL_OCR_API_KEY": request.app.state.config.MISTRAL_OCR_API_KEY,
# Reranking settings
"RAG_RERANKING_MODEL": request.app.state.config.RAG_RERANKING_MODEL,
"RAG_RERANKING_ENGINE": request.app.state.config.RAG_RERANKING_ENGINE,
"RAG_EXTERNAL_RERANKER_URL": request.app.state.config.RAG_EXTERNAL_RERANKER_URL,
"RAG_EXTERNAL_RERANKER_API_KEY": request.app.state.config.RAG_EXTERNAL_RERANKER_API_KEY,
# Chunking settings # Chunking settings
"TEXT_SPLITTER": request.app.state.config.TEXT_SPLITTER, "TEXT_SPLITTER": request.app.state.config.TEXT_SPLITTER,
"CHUNK_SIZE": request.app.state.config.CHUNK_SIZE, "CHUNK_SIZE": request.app.state.config.CHUNK_SIZE,
@ -1568,16 +1603,34 @@ def search_web(request: Request, engine: str, query: str) -> list[SearchResult]:
async def process_web_search( async def process_web_search(
request: Request, form_data: SearchForm, user=Depends(get_verified_user) request: Request, form_data: SearchForm, user=Depends(get_verified_user)
): ):
urls = []
try: try:
logging.info( logging.info(
f"trying to web search with {request.app.state.config.WEB_SEARCH_ENGINE, form_data.query}" f"trying to web search with {request.app.state.config.WEB_SEARCH_ENGINE, form_data.queries}"
)
web_results = await run_in_threadpool(
search_web,
request,
request.app.state.config.WEB_SEARCH_ENGINE,
form_data.query,
) )
search_tasks = [
run_in_threadpool(
search_web,
request,
request.app.state.config.WEB_SEARCH_ENGINE,
query,
)
for query in form_data.queries
]
search_results = await asyncio.gather(*search_tasks)
for result in search_results:
if result:
for item in result:
if item and item.link:
urls.append(item.link)
urls = list(dict.fromkeys(urls))
log.debug(f"urls: {urls}")
except Exception as e: except Exception as e:
log.exception(e) log.exception(e)
@ -1586,10 +1639,7 @@ async def process_web_search(
detail=ERROR_MESSAGES.WEB_SEARCH_ERROR(e), detail=ERROR_MESSAGES.WEB_SEARCH_ERROR(e),
) )
log.debug(f"web_results: {web_results}")
try: try:
urls = [result.link for result in web_results]
loader = get_web_loader( loader = get_web_loader(
urls, urls,
verify_ssl=request.app.state.config.ENABLE_WEB_LOADER_SSL_VERIFICATION, verify_ssl=request.app.state.config.ENABLE_WEB_LOADER_SSL_VERIFICATION,
@ -1599,7 +1649,7 @@ async def process_web_search(
docs = await loader.aload() docs = await loader.aload()
urls = [ urls = [
doc.metadata.get("source") for doc in docs if doc.metadata.get("source") doc.metadata.get("source") for doc in docs if doc.metadata.get("source")
] # only keep URLs ] # only keep the urls returned by the loader
if request.app.state.config.BYPASS_WEB_SEARCH_EMBEDDING_AND_RETRIEVAL: if request.app.state.config.BYPASS_WEB_SEARCH_EMBEDDING_AND_RETRIEVAL:
return { return {
@ -1616,29 +1666,28 @@ async def process_web_search(
"loaded_count": len(docs), "loaded_count": len(docs),
} }
else: else:
collection_names = [] # Create a single collection for all documents
for doc_idx, doc in enumerate(docs): collection_name = (
if doc and doc.page_content: f"web-search-{calculate_sha256_string('-'.join(form_data.queries))}"[
try: :63
collection_name = f"web-search-{calculate_sha256_string(form_data.query + '-' + urls[doc_idx])}"[ ]
:63 )
]
collection_names.append(collection_name) try:
await run_in_threadpool( await run_in_threadpool(
save_docs_to_vector_db, save_docs_to_vector_db,
request, request,
[doc], docs,
collection_name, collection_name,
overwrite=True, overwrite=True,
user=user, user=user,
) )
except Exception as e: except Exception as e:
log.debug(f"error saving doc {doc_idx}: {e}") log.debug(f"error saving docs: {e}")
return { return {
"status": True, "status": True,
"collection_names": collection_names, "collection_names": [collection_name],
"filenames": urls, "filenames": urls,
"loaded_count": len(docs), "loaded_count": len(docs),
} }

View file

@ -186,20 +186,9 @@ async def generate_title(
else: else:
template = DEFAULT_TITLE_GENERATION_PROMPT_TEMPLATE template = DEFAULT_TITLE_GENERATION_PROMPT_TEMPLATE
messages = form_data["messages"]
# Remove reasoning details from the messages
for message in messages:
message["content"] = re.sub(
r"<details\s+type=\"reasoning\"[^>]*>.*?<\/details>",
"",
message["content"],
flags=re.S,
).strip()
content = title_generation_template( content = title_generation_template(
template, template,
messages, form_data["messages"],
{ {
"name": user.name, "name": user.name,
"location": user.info.get("location") if user.info else None, "location": user.info.get("location") if user.info else None,

View file

@ -34,7 +34,7 @@ router = APIRouter()
############################ ############################
PAGE_ITEM_COUNT = 10 PAGE_ITEM_COUNT = 30
@router.get("/", response_model=UserListResponse) @router.get("/", response_model=UserListResponse)

View file

@ -159,18 +159,19 @@ def get_models_in_use():
@sio.on("usage") @sio.on("usage")
async def usage(sid, data): async def usage(sid, data):
model_id = data["model"] if sid in SESSION_POOL:
# Record the timestamp for the last update model_id = data["model"]
current_time = int(time.time()) # Record the timestamp for the last update
current_time = int(time.time())
# Store the new usage data and task # Store the new usage data and task
USAGE_POOL[model_id] = { USAGE_POOL[model_id] = {
**(USAGE_POOL[model_id] if model_id in USAGE_POOL else {}), **(USAGE_POOL[model_id] if model_id in USAGE_POOL else {}),
sid: {"updated_at": current_time}, sid: {"updated_at": current_time},
} }
# Broadcast the usage data to all clients # Broadcast the usage data to all clients
await sio.emit("usage", {"models": get_models_in_use()}) await sio.emit("usage", {"models": get_models_in_use()})
@sio.event @sio.event
@ -192,9 +193,6 @@ async def connect(sid, environ, auth):
# print(f"user {user.name}({user.id}) connected with session ID {sid}") # print(f"user {user.name}({user.id}) connected with session ID {sid}")
await sio.emit("user-list", {"user_ids": list(USER_POOL.keys())}) await sio.emit("user-list", {"user_ids": list(USER_POOL.keys())})
await sio.emit("usage", {"models": get_models_in_use()}) await sio.emit("usage", {"models": get_models_in_use()})
return True
return False
@sio.on("user-join") @sio.on("user-join")
@ -281,7 +279,8 @@ async def channel_events(sid, data):
@sio.on("user-list") @sio.on("user-list")
async def user_list(sid): async def user_list(sid):
await sio.emit("user-list", {"user_ids": list(USER_POOL.keys())}) if sid in SESSION_POOL:
await sio.emit("user-list", {"user_ids": list(USER_POOL.keys())})
@sio.event @sio.event

View file

@ -17,6 +17,7 @@ from open_webui.config import (
S3_SECRET_ACCESS_KEY, S3_SECRET_ACCESS_KEY,
S3_USE_ACCELERATE_ENDPOINT, S3_USE_ACCELERATE_ENDPOINT,
S3_ADDRESSING_STYLE, S3_ADDRESSING_STYLE,
S3_ENABLE_TAGGING,
GCS_BUCKET_NAME, GCS_BUCKET_NAME,
GOOGLE_APPLICATION_CREDENTIALS_JSON, GOOGLE_APPLICATION_CREDENTIALS_JSON,
AZURE_STORAGE_ENDPOINT, AZURE_STORAGE_ENDPOINT,
@ -140,18 +141,19 @@ class S3StorageProvider(StorageProvider):
) -> Tuple[bytes, str]: ) -> Tuple[bytes, str]:
"""Handles uploading of the file to S3 storage.""" """Handles uploading of the file to S3 storage."""
_, file_path = LocalStorageProvider.upload_file(file, filename, tags) _, file_path = LocalStorageProvider.upload_file(file, filename, tags)
tagging = {"TagSet": [{"Key": k, "Value": v} for k, v in tags.items()]} s3_key = os.path.join(self.key_prefix, filename)
try: try:
s3_key = os.path.join(self.key_prefix, filename)
self.s3_client.upload_file(file_path, self.bucket_name, s3_key) self.s3_client.upload_file(file_path, self.bucket_name, s3_key)
self.s3_client.put_object_tagging( if S3_ENABLE_TAGGING and tags:
Bucket=self.bucket_name, tagging = {"TagSet": [{"Key": k, "Value": v} for k, v in tags.items()]}
Key=s3_key, self.s3_client.put_object_tagging(
Tagging=tagging, Bucket=self.bucket_name,
) Key=s3_key,
Tagging=tagging,
)
return ( return (
open(file_path, "rb").read(), open(file_path, "rb").read(),
"s3://" + self.bucket_name + "/" + s3_key, f"s3://{self.bucket_name}/{s3_key}",
) )
except ClientError as e: except ClientError as e:
raise RuntimeError(f"Error uploading file to S3: {e}") raise RuntimeError(f"Error uploading file to S3: {e}")

View file

@ -44,12 +44,14 @@ class JupyterCodeExecuter:
:param password: Jupyter password (optional) :param password: Jupyter password (optional)
:param timeout: WebSocket timeout in seconds (default: 60s) :param timeout: WebSocket timeout in seconds (default: 60s)
""" """
self.base_url = base_url.rstrip("/") self.base_url = base_url
self.code = code self.code = code
self.token = token self.token = token
self.password = password self.password = password
self.timeout = timeout self.timeout = timeout
self.kernel_id = "" self.kernel_id = ""
if self.base_url[-1] != "/":
self.base_url += "/"
self.session = aiohttp.ClientSession(trust_env=True, base_url=self.base_url) self.session = aiohttp.ClientSession(trust_env=True, base_url=self.base_url)
self.params = {} self.params = {}
self.result = ResultModel() self.result = ResultModel()
@ -61,7 +63,7 @@ class JupyterCodeExecuter:
if self.kernel_id: if self.kernel_id:
try: try:
async with self.session.delete( async with self.session.delete(
f"/api/kernels/{self.kernel_id}", params=self.params f"api/kernels/{self.kernel_id}", params=self.params
) as response: ) as response:
response.raise_for_status() response.raise_for_status()
except Exception as err: except Exception as err:
@ -81,7 +83,7 @@ class JupyterCodeExecuter:
async def sign_in(self) -> None: async def sign_in(self) -> None:
# password authentication # password authentication
if self.password and not self.token: if self.password and not self.token:
async with self.session.get("/login") as response: async with self.session.get("login") as response:
response.raise_for_status() response.raise_for_status()
xsrf_token = response.cookies["_xsrf"].value xsrf_token = response.cookies["_xsrf"].value
if not xsrf_token: if not xsrf_token:
@ -89,7 +91,7 @@ class JupyterCodeExecuter:
self.session.cookie_jar.update_cookies(response.cookies) self.session.cookie_jar.update_cookies(response.cookies)
self.session.headers.update({"X-XSRFToken": xsrf_token}) self.session.headers.update({"X-XSRFToken": xsrf_token})
async with self.session.post( async with self.session.post(
"/login", "login",
data={"_xsrf": xsrf_token, "password": self.password}, data={"_xsrf": xsrf_token, "password": self.password},
allow_redirects=False, allow_redirects=False,
) as response: ) as response:
@ -101,17 +103,15 @@ class JupyterCodeExecuter:
self.params.update({"token": self.token}) self.params.update({"token": self.token})
async def init_kernel(self) -> None: async def init_kernel(self) -> None:
async with self.session.post( async with self.session.post(url="api/kernels", params=self.params) as response:
url="/api/kernels", params=self.params
) as response:
response.raise_for_status() response.raise_for_status()
kernel_data = await response.json() kernel_data = await response.json()
self.kernel_id = kernel_data["id"] self.kernel_id = kernel_data["id"]
def init_ws(self) -> (str, dict): def init_ws(self) -> (str, dict):
ws_base = self.base_url.replace("http", "ws") ws_base = self.base_url.replace("http", "ws", 1)
ws_params = "?" + "&".join([f"{key}={val}" for key, val in self.params.items()]) ws_params = "?" + "&".join([f"{key}={val}" for key, val in self.params.items()])
websocket_url = f"{ws_base}/api/kernels/{self.kernel_id}/channels{ws_params if len(ws_params) > 1 else ''}" websocket_url = f"{ws_base}api/kernels/{self.kernel_id}/channels{ws_params if len(ws_params) > 1 else ''}"
ws_headers = {} ws_headers = {}
if self.password and not self.token: if self.password and not self.token:
ws_headers = { ws_headers = {

View file

@ -353,8 +353,6 @@ async def chat_web_search_handler(
) )
return form_data return form_data
all_results = []
await event_emitter( await event_emitter(
{ {
"type": "status", "type": "status",
@ -366,106 +364,75 @@ async def chat_web_search_handler(
} }
) )
gathered_results = await asyncio.gather( try:
*( results = await process_web_search(
process_web_search( request,
request, SearchForm(queries=queries),
SearchForm(**{"query": searchQuery}), user=user,
user=user, )
)
for searchQuery in queries
),
return_exceptions=True,
)
for searchQuery, results in zip(queries, gathered_results): if results:
try: files = form_data.get("files", [])
if isinstance(results, Exception):
raise Exception(f"Error searching {searchQuery}: {str(results)}")
if results: if results.get("collection_names"):
all_results.append(results) for col_idx, collection_name in enumerate(
files = form_data.get("files", []) results.get("collection_names")
):
files.append(
{
"collection_name": collection_name,
"name": ", ".join(queries),
"type": "web_search",
"urls": results["filenames"],
}
)
elif results.get("docs"):
# Invoked when bypass embedding and retrieval is set to True
docs = results["docs"]
files.append(
{
"docs": docs,
"name": ", ".join(queries),
"type": "web_search",
"urls": results["filenames"],
}
)
if results.get("collection_names"): form_data["files"] = files
for col_idx, collection_name in enumerate(
results.get("collection_names")
):
files.append(
{
"collection_name": collection_name,
"name": searchQuery,
"type": "web_search",
"urls": [results["filenames"][col_idx]],
}
)
elif results.get("docs"):
# Invoked when bypass embedding and retrieval is set to True
docs = results["docs"]
if len(docs) == len(results["filenames"]):
# the number of docs and filenames (urls) should be the same
for doc_idx, doc in enumerate(docs):
files.append(
{
"docs": [doc],
"name": searchQuery,
"type": "web_search",
"urls": [results["filenames"][doc_idx]],
}
)
else:
# edge case when the number of docs and filenames (urls) are not the same
# this should not happen, but if it does, we will just append the docs
files.append(
{
"docs": results.get("docs", []),
"name": searchQuery,
"type": "web_search",
"urls": results["filenames"],
}
)
form_data["files"] = files
except Exception as e:
log.exception(e)
await event_emitter( await event_emitter(
{ {
"type": "status", "type": "status",
"data": { "data": {
"action": "web_search", "action": "web_search",
"description": 'Error searching "{{searchQuery}}"', "description": "Searched {{count}} sites",
"query": searchQuery, "urls": results["filenames"],
"done": True,
},
}
)
else:
await event_emitter(
{
"type": "status",
"data": {
"action": "web_search",
"description": "No search results found",
"done": True, "done": True,
"error": True, "error": True,
}, },
} }
) )
if all_results: except Exception as e:
urls = [] log.exception(e)
for results in all_results:
if "filenames" in results:
urls.extend(results["filenames"])
await event_emitter( await event_emitter(
{ {
"type": "status", "type": "status",
"data": { "data": {
"action": "web_search", "action": "web_search",
"description": "Searched {{count}} sites", "description": "An error occurred while searching the web",
"urls": urls, "queries": queries,
"done": True,
},
}
)
else:
await event_emitter(
{
"type": "status",
"data": {
"action": "web_search",
"description": "No search results found",
"done": True, "done": True,
"error": True, "error": True,
}, },
@ -672,6 +639,9 @@ def apply_params_to_form_data(form_data, model):
if "frequency_penalty" in params and params["frequency_penalty"] is not None: if "frequency_penalty" in params and params["frequency_penalty"] is not None:
form_data["frequency_penalty"] = params["frequency_penalty"] form_data["frequency_penalty"] = params["frequency_penalty"]
if "presence_penalty" in params and params["presence_penalty"] is not None:
form_data["presence_penalty"] = params["presence_penalty"]
if "reasoning_effort" in params and params["reasoning_effort"] is not None: if "reasoning_effort" in params and params["reasoning_effort"] is not None:
form_data["reasoning_effort"] = params["reasoning_effort"] form_data["reasoning_effort"] = params["reasoning_effort"]
@ -974,6 +944,20 @@ async def process_chat_response(
if message: if message:
messages = get_message_list(message_map, message.get("id")) messages = get_message_list(message_map, message.get("id"))
# Remove reasoning details and files from the messages.
# as get_message_list creates a new list, it does not affect
# the original messages outside of this handler
for message in messages:
message["content"] = re.sub(
r"<details\s+type=\"reasoning\"[^>]*>.*?<\/details>",
"",
message["content"],
flags=re.S,
).strip()
if message.get("files"):
message["files"] = []
if tasks and messages: if tasks and messages:
if TASKS.TITLE_GENERATION in tasks: if TASKS.TITLE_GENERATION in tasks:
if tasks[TASKS.TITLE_GENERATION]: if tasks[TASKS.TITLE_GENERATION]:

View file

@ -34,6 +34,7 @@ from open_webui.config import (
OAUTH_ALLOWED_ROLES, OAUTH_ALLOWED_ROLES,
OAUTH_ADMIN_ROLES, OAUTH_ADMIN_ROLES,
OAUTH_ALLOWED_DOMAINS, OAUTH_ALLOWED_DOMAINS,
OAUTH_UPDATE_PICTURE_ON_LOGIN,
WEBHOOK_URL, WEBHOOK_URL,
JWT_EXPIRES_IN, JWT_EXPIRES_IN,
AppConfig, AppConfig,
@ -72,6 +73,7 @@ auth_manager_config.OAUTH_ADMIN_ROLES = OAUTH_ADMIN_ROLES
auth_manager_config.OAUTH_ALLOWED_DOMAINS = OAUTH_ALLOWED_DOMAINS auth_manager_config.OAUTH_ALLOWED_DOMAINS = OAUTH_ALLOWED_DOMAINS
auth_manager_config.WEBHOOK_URL = WEBHOOK_URL auth_manager_config.WEBHOOK_URL = WEBHOOK_URL
auth_manager_config.JWT_EXPIRES_IN = JWT_EXPIRES_IN auth_manager_config.JWT_EXPIRES_IN = JWT_EXPIRES_IN
auth_manager_config.OAUTH_UPDATE_PICTURE_ON_LOGIN = OAUTH_UPDATE_PICTURE_ON_LOGIN
class OAuthManager: class OAuthManager:
@ -282,6 +284,49 @@ class OAuthManager:
id=group_model.id, form_data=update_form, overwrite=False id=group_model.id, form_data=update_form, overwrite=False
) )
async def _process_picture_url(
self, picture_url: str, access_token: str = None
) -> str:
"""Process a picture URL and return a base64 encoded data URL.
Args:
picture_url: The URL of the picture to process
access_token: Optional OAuth access token for authenticated requests
Returns:
A data URL containing the base64 encoded picture, or "/user.png" if processing fails
"""
if not picture_url:
return "/user.png"
try:
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:
guessed_mime_type = "image/jpeg"
return (
f"data:{guessed_mime_type};base64,{base64_encoded_picture}"
)
else:
log.warning(
f"Failed to fetch profile picture from {picture_url}"
)
return "/user.png"
except Exception as e:
log.error(f"Error processing profile picture '{picture_url}': {e}")
return "/user.png"
async def handle_login(self, request, provider): async def handle_login(self, request, provider):
if provider not in OAUTH_PROVIDERS: if provider not in OAUTH_PROVIDERS:
raise HTTPException(404) raise HTTPException(404)
@ -382,6 +427,22 @@ class OAuthManager:
if user.role != determined_role: if user.role != determined_role:
Users.update_user_role_by_id(user.id, determined_role) Users.update_user_role_by_id(user.id, determined_role)
# Update profile picture if enabled and different from current
if auth_manager_config.OAUTH_UPDATE_PICTURE_ON_LOGIN:
picture_claim = auth_manager_config.OAUTH_PICTURE_CLAIM
if picture_claim:
new_picture_url = user_data.get(
picture_claim, OAUTH_PROVIDERS[provider].get("picture_url", "")
)
processed_picture_url = await self._process_picture_url(
new_picture_url, token.get("access_token")
)
if processed_picture_url != user.profile_image_url:
Users.update_user_profile_image_url_by_id(
user.id, processed_picture_url
)
log.debug(f"Updated profile picture for user {user.email}")
if not user: if not user:
user_count = Users.get_num_users() user_count = Users.get_num_users()
@ -397,40 +458,9 @@ class OAuthManager:
picture_url = user_data.get( picture_url = user_data.get(
picture_claim, OAUTH_PROVIDERS[provider].get("picture_url", "") picture_claim, OAUTH_PROVIDERS[provider].get("picture_url", "")
) )
if picture_url: picture_url = await self._process_picture_url(
# Download the profile image into a base64 string picture_url, token.get("access_token")
try: )
access_token = token.get("access_token")
get_kwargs = {}
if access_token:
get_kwargs["headers"] = {
"Authorization": f"Bearer {access_token}",
}
async with aiohttp.ClientSession(trust_env=True) 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"
else: else:
picture_url = "/user.png" picture_url = "/user.png"

View file

@ -59,6 +59,7 @@ def apply_model_params_to_body_openai(params: dict, form_data: dict) -> dict:
"top_p": float, "top_p": float,
"max_tokens": int, "max_tokens": int,
"frequency_penalty": float, "frequency_penalty": float,
"presence_penalty": float,
"reasoning_effort": str, "reasoning_effort": str,
"seed": lambda x: x, "seed": lambda x: x,
"stop": lambda x: [bytes(s, "utf-8").decode("unicode_escape") for s in x], "stop": lambda x: [bytes(s, "utf-8").decode("unicode_escape") for s in x],

View file

@ -65,4 +65,6 @@ if [ -n "$SPACE_ID" ]; then
export WEBUI_URL=${SPACE_HOST} export WEBUI_URL=${SPACE_HOST}
fi fi
WEBUI_SECRET_KEY="$WEBUI_SECRET_KEY" exec uvicorn open_webui.main:app --host "$HOST" --port "$PORT" --forwarded-allow-ips '*' --workers "${UVICORN_WORKERS:-1}" PYTHON_CMD=$(command -v python3 || command -v python)
WEBUI_SECRET_KEY="$WEBUI_SECRET_KEY" exec "$PYTHON_CMD" -m uvicorn open_webui.main:app --host "$HOST" --port "$PORT" --forwarded-allow-ips '*' --workers "${UVICORN_WORKERS:-1}"

74
contribution_stats.py Normal file
View file

@ -0,0 +1,74 @@
import os
import subprocess
from collections import Counter
CONFIG_FILE_EXTENSIONS = (".json", ".yml", ".yaml", ".ini", ".conf", ".toml")
def is_text_file(filepath):
# Check for binary file by scanning for null bytes.
try:
with open(filepath, "rb") as f:
chunk = f.read(4096)
if b"\0" in chunk:
return False
return True
except Exception:
return False
def should_skip_file(path):
base = os.path.basename(path)
# Skip dotfiles and dotdirs
if base.startswith("."):
return True
# Skip config files by extension
if base.lower().endswith(CONFIG_FILE_EXTENSIONS):
return True
return False
def get_tracked_files():
try:
output = subprocess.check_output(["git", "ls-files"], text=True)
files = output.strip().split("\n")
files = [f for f in files if f and os.path.isfile(f)]
return files
except subprocess.CalledProcessError:
print("Error: Are you in a git repository?")
return []
def main():
files = get_tracked_files()
email_counter = Counter()
total_lines = 0
for file in files:
if should_skip_file(file):
continue
if not is_text_file(file):
continue
try:
blame = subprocess.check_output(
["git", "blame", "-e", file], text=True, errors="replace"
)
for line in blame.splitlines():
# The email always inside <>
if "<" in line and ">" in line:
try:
email = line.split("<")[1].split(">")[0].strip()
except Exception:
continue
email_counter[email] += 1
total_lines += 1
except subprocess.CalledProcessError:
continue
for email, lines in email_counter.most_common():
percent = (lines / total_lines * 100) if total_lines else 0
print(f"{email}: {lines}/{total_lines} {percent:.2f}%")
if __name__ == "__main__":
main()

12
package-lock.json generated
View file

@ -1,12 +1,12 @@
{ {
"name": "open-webui", "name": "open-webui",
"version": "0.6.7", "version": "0.6.8",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "open-webui", "name": "open-webui",
"version": "0.6.7", "version": "0.6.8",
"dependencies": { "dependencies": {
"@azure/msal-browser": "^4.5.0", "@azure/msal-browser": "^4.5.0",
"@codemirror/lang-javascript": "^6.2.2", "@codemirror/lang-javascript": "^6.2.2",
@ -36,6 +36,7 @@
"dompurify": "^3.2.5", "dompurify": "^3.2.5",
"eventsource-parser": "^1.1.2", "eventsource-parser": "^1.1.2",
"file-saver": "^2.0.5", "file-saver": "^2.0.5",
"focus-trap": "^7.6.4",
"fuse.js": "^7.0.0", "fuse.js": "^7.0.0",
"highlight.js": "^11.9.0", "highlight.js": "^11.9.0",
"html-entities": "^2.5.3", "html-entities": "^2.5.3",
@ -6801,9 +6802,10 @@
"dev": true "dev": true
}, },
"node_modules/focus-trap": { "node_modules/focus-trap": {
"version": "7.5.4", "version": "7.6.4",
"resolved": "https://registry.npmjs.org/focus-trap/-/focus-trap-7.5.4.tgz", "resolved": "https://registry.npmjs.org/focus-trap/-/focus-trap-7.6.4.tgz",
"integrity": "sha512-N7kHdlgsO/v+iD/dMoJKtsSqs5Dz/dXZVebRgJw23LDk+jMi/974zyiOYDziY2JPp8xivq9BmUGwIJMiuSBi7w==", "integrity": "sha512-xx560wGBk7seZ6y933idtjJQc1l+ck+pI3sKvhKozdBV1dRZoKhkW5xoCaFv9tQiX5RH1xfSxjuNu6g+lmN/gw==",
"license": "MIT",
"dependencies": { "dependencies": {
"tabbable": "^6.2.0" "tabbable": "^6.2.0"
} }

View file

@ -1,6 +1,6 @@
{ {
"name": "open-webui", "name": "open-webui",
"version": "0.6.7", "version": "0.6.8",
"private": true, "private": true,
"scripts": { "scripts": {
"dev": "npm run pyodide:fetch && vite dev --host", "dev": "npm run pyodide:fetch && vite dev --host",
@ -79,6 +79,7 @@
"dompurify": "^3.2.5", "dompurify": "^3.2.5",
"eventsource-parser": "^1.1.2", "eventsource-parser": "^1.1.2",
"file-saver": "^2.0.5", "file-saver": "^2.0.5",
"focus-trap": "^7.6.4",
"fuse.js": "^7.0.0", "fuse.js": "^7.0.0",
"highlight.js": "^11.9.0", "highlight.js": "^11.9.0",
"html-entities": "^2.5.3", "html-entities": "^2.5.3",

View file

@ -24,6 +24,12 @@
font-display: swap; font-display: swap;
} }
@font-face {
font-family: 'Vazirmatn';
src: url('/assets/fonts/Vazirmatn-Variable.ttf');
font-display: swap;
}
html { html {
word-break: break-word; word-break: break-word;
} }

View file

@ -43,6 +43,7 @@
fill="currentColor" fill="currentColor"
class="w-5 h-5" class="w-5 h-5"
> >
<p class="sr-only">{$i18n.t('Close')}</p>
<path <path
d="M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z" d="M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z"
/> />

View file

@ -87,6 +87,7 @@
<div class="flex justify-center mt-8"> <div class="flex justify-center mt-8">
<div class="flex flex-col justify-center items-center"> <div class="flex flex-col justify-center items-center">
<button <button
aria-labelledby="get-started"
class="relative z-20 flex p-1 rounded-full bg-white/5 hover:bg-white/10 transition font-medium text-sm" class="relative z-20 flex p-1 rounded-full bg-white/5 hover:bg-white/10 transition font-medium text-sm"
on:click={() => { on:click={() => {
getStartedHandler(); getStartedHandler();
@ -94,12 +95,12 @@
> >
<ArrowRightCircle className="size-6" /> <ArrowRightCircle className="size-6" />
</button> </button>
<div class="mt-1.5 font-primary text-base font-medium">{$i18n.t(`Get started`)}</div> <div id="get-started" class="mt-1.5 font-primary text-base font-medium">
{$i18n.t(`Get started`)}
</div>
</div> </div>
</div> </div>
</div> </div>
<!-- <div class="absolute bottom-12 left-0 right-0 w-full"></div> -->
</div> </div>
</div> </div>
{/if} {/if}

View file

@ -123,35 +123,6 @@
} }
}; };
const rerankingModelUpdateHandler = async () => {
console.log('Update reranking model attempt:', rerankingModel);
updateRerankingModelLoading = true;
const res = await updateRerankingConfig(localStorage.token, {
reranking_model: rerankingModel
}).catch(async (error) => {
toast.error(`${error}`);
await setRerankingConfig();
return null;
});
updateRerankingModelLoading = false;
if (res) {
console.log('rerankingModelUpdateHandler:', res);
if (res.status === true) {
if (rerankingModel === '') {
toast.success($i18n.t('Reranking model disabled', res), {
duration: 1000 * 10
});
} else {
toast.success($i18n.t('Reranking model set to "{{reranking_model}}"', res), {
duration: 1000 * 10
});
}
}
}
};
const submitHandler = async () => { const submitHandler = async () => {
if (RAGConfig.CONTENT_EXTRACTION_ENGINE === 'tika' && RAGConfig.TIKA_SERVER_URL === '') { if (RAGConfig.CONTENT_EXTRACTION_ENGINE === 'tika' && RAGConfig.TIKA_SERVER_URL === '') {
toast.error($i18n.t('Tika Server URL required.')); toast.error($i18n.t('Tika Server URL required.'));
@ -190,10 +161,6 @@
if (!RAGConfig.BYPASS_EMBEDDING_AND_RETRIEVAL) { if (!RAGConfig.BYPASS_EMBEDDING_AND_RETRIEVAL) {
await embeddingModelUpdateHandler(); await embeddingModelUpdateHandler();
if (RAGConfig.ENABLE_RAG_HYBRID_SEARCH) {
await rerankingModelUpdateHandler();
}
} }
const res = await updateRAGConfig(localStorage.token, RAGConfig); const res = await updateRAGConfig(localStorage.token, RAGConfig);
@ -215,18 +182,8 @@
OllamaUrl = embeddingConfig.ollama_config.url; OllamaUrl = embeddingConfig.ollama_config.url;
} }
}; };
const setRerankingConfig = async () => {
const rerankingConfig = await getRerankingConfig(localStorage.token);
if (rerankingConfig) {
rerankingModel = rerankingConfig.reranking_model;
}
};
onMount(async () => { onMount(async () => {
await setEmbeddingConfig(); await setEmbeddingConfig();
await setRerankingConfig();
RAGConfig = await getRAGConfig(localStorage.token); RAGConfig = await getRAGConfig(localStorage.token);
}); });
@ -655,6 +612,48 @@
</div> </div>
{#if RAGConfig.ENABLE_RAG_HYBRID_SEARCH === true} {#if RAGConfig.ENABLE_RAG_HYBRID_SEARCH === true}
<div class=" mb-2.5 flex flex-col w-full justify-between">
<div class="flex w-full justify-between">
<div class=" self-center text-xs font-medium">
{$i18n.t('Reranking Engine')}
</div>
<div class="flex items-center relative">
<select
class="dark:bg-gray-900 w-fit pr-8 rounded-sm px-2 p-1 text-xs bg-transparent outline-hidden text-right"
bind:value={RAGConfig.RAG_RERANKING_ENGINE}
placeholder="Select a reranking model engine"
on:change={(e) => {
if (e.target.value === 'external') {
RAGConfig.RAG_RERANKING_MODEL = '';
} else if (e.target.value === '') {
RAGConfig.RAG_RERANKING_MODEL = 'BAAI/bge-reranker-v2-m3';
}
}}
>
<option value="">{$i18n.t('Default (SentenceTransformers)')}</option>
<option value="external">{$i18n.t('External')}</option>
</select>
</div>
</div>
{#if RAGConfig.RAG_RERANKING_ENGINE === 'external'}
<div class="my-0.5 flex gap-2 pr-2">
<input
class="flex-1 w-full rounded-lg text-sm bg-transparent outline-hidden"
placeholder={$i18n.t('API Base URL')}
bind:value={RAGConfig.RAG_EXTERNAL_RERANKER_URL}
required
/>
<SensitiveInput
placeholder={$i18n.t('API Key')}
bind:value={RAGConfig.RAG_EXTERNAL_RERANKER_API_KEY}
required={false}
/>
</div>
{/if}
</div>
<div class=" mb-2.5 flex flex-col w-full"> <div class=" mb-2.5 flex flex-col w-full">
<div class=" mb-1 text-xs font-medium">{$i18n.t('Reranking Model')}</div> <div class=" mb-1 text-xs font-medium">{$i18n.t('Reranking Model')}</div>
@ -666,62 +665,9 @@
placeholder={$i18n.t('Set reranking model (e.g. {{model}})', { placeholder={$i18n.t('Set reranking model (e.g. {{model}})', {
model: 'BAAI/bge-reranker-v2-m3' model: 'BAAI/bge-reranker-v2-m3'
})} })}
bind:value={rerankingModel} bind:value={RAGConfig.RAG_RERANKING_MODEL}
/> />
</div> </div>
<button
class="px-2.5 bg-transparent text-gray-800 dark:bg-transparent dark:text-gray-100 rounded-lg transition"
on:click={() => {
rerankingModelUpdateHandler();
}}
disabled={updateRerankingModelLoading}
>
{#if updateRerankingModelLoading}
<div class="self-center">
<svg
class=" w-4 h-4"
viewBox="0 0 24 24"
fill="currentColor"
xmlns="http://www.w3.org/2000/svg"
>
<style>
.spinner_ajPY {
transform-origin: center;
animation: spinner_AtaB 0.75s infinite linear;
}
@keyframes spinner_AtaB {
100% {
transform: rotate(360deg);
}
}
</style>
<path
d="M12,1A11,11,0,1,0,23,12,11,11,0,0,0,12,1Zm0,19a8,8,0,1,1,8-8A8,8,0,0,1,12,20Z"
opacity=".25"
/>
<path
d="M10.14,1.16a11,11,0,0,0-9,8.92A1.59,1.59,0,0,0,2.46,12,1.52,1.52,0,0,0,4.11,10.7a8,8,0,0,1,6.66-6.61A1.42,1.42,0,0,0,12,2.69h0A1.57,1.57,0,0,0,10.14,1.16Z"
class="spinner_ajPY"
/>
</svg>
</div>
{:else}
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 16 16"
fill="currentColor"
class="w-4 h-4"
>
<path
d="M8.75 2.75a.75.75 0 0 0-1.5 0v5.69L5.03 6.22a.75.75 0 0 0-1.06 1.06l3.5 3.5a.75.75 0 0 0 1.06 0l3.5-3.5a.75.75 0 0 0-1.06-1.06L8.75 8.44V2.75Z"
/>
<path
d="M3.5 9.75a.75.75 0 0 0-1.5 0v1.5A2.75 2.75 0 0 0 4.75 14h6.5A2.75 2.75 0 0 0 14 11.25v-1.5a.75.75 0 0 0-1.5 0v1.5c0 .69-.56 1.25-1.25 1.25h-6.5c-.69 0-1.25-.56-1.25-1.25v-1.5Z"
/>
</svg>
{/if}
</button>
</div> </div>
</div> </div>
</div> </div>

View file

@ -32,13 +32,14 @@
import About from '$lib/components/chat/Settings/About.svelte'; import About from '$lib/components/chat/Settings/About.svelte';
import Banner from '$lib/components/common/Banner.svelte'; import Banner from '$lib/components/common/Banner.svelte';
import Markdown from '$lib/components/chat/Messages/Markdown.svelte'; import Markdown from '$lib/components/chat/Messages/Markdown.svelte';
import Spinner from '$lib/components/common/Spinner.svelte';
const i18n = getContext('i18n'); const i18n = getContext('i18n');
let page = 1; let page = 1;
let users = []; let users = null;
let total = 0; let total = null;
let query = ''; let query = '';
let orderBy = 'created_at'; // default sort key let orderBy = 'created_at'; // default sort key
@ -181,314 +182,293 @@
</div> </div>
{/if} {/if}
<div class="mt-0.5 mb-2 gap-1 flex flex-col md:flex-row justify-between"> {#if users === null || total === null}
<div class="flex md:self-center text-lg font-medium px-0.5"> <div class="my-10">
<div class="flex-shrink-0"> <Spinner />
{$i18n.t('Users')} </div>
</div> {:else}
<div class="flex self-center w-[1px] h-6 mx-2.5 bg-gray-50 dark:bg-gray-850" /> <div class="mt-0.5 mb-2 gap-1 flex flex-col md:flex-row justify-between">
<div class="flex md:self-center text-lg font-medium px-0.5">
<div class="flex-shrink-0">
{$i18n.t('Users')}
</div>
<div class="flex self-center w-[1px] h-6 mx-2.5 bg-gray-50 dark:bg-gray-850" />
{#if ($config?.license_metadata?.seats ?? null) !== null} {#if ($config?.license_metadata?.seats ?? null) !== null}
{#if total > $config?.license_metadata?.seats} {#if total > $config?.license_metadata?.seats}
<span class="text-lg font-medium text-red-500" <span class="text-lg font-medium text-red-500"
>{total} of {$config?.license_metadata?.seats} >{total} of {$config?.license_metadata?.seats}
<span class="text-sm font-normal">available users</span></span <span class="text-sm font-normal">available users</span></span
> >
{:else}
<span class="text-lg font-medium text-gray-500 dark:text-gray-300"
>{total} of {$config?.license_metadata?.seats}
<span class="text-sm font-normal">available users</span></span
>
{/if}
{:else} {:else}
<span class="text-lg font-medium text-gray-500 dark:text-gray-300" <span class="text-lg font-medium text-gray-500 dark:text-gray-300">{total}</span>
>{total} of {$config?.license_metadata?.seats}
<span class="text-sm font-normal">available users</span></span
>
{/if} {/if}
{:else}
<span class="text-lg font-medium text-gray-500 dark:text-gray-300">{total}</span>
{/if}
</div>
<div class="flex gap-1">
<div class=" flex w-full space-x-2">
<div class="flex flex-1">
<div class=" self-center ml-1 mr-3">
<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="M9 3.5a5.5 5.5 0 100 11 5.5 5.5 0 000-11zM2 9a7 7 0 1112.452 4.391l3.328 3.329a.75.75 0 11-1.06 1.06l-3.329-3.328A7 7 0 012 9z"
clip-rule="evenodd"
/>
</svg>
</div>
<input
class=" w-full text-sm pr-4 py-1 rounded-r-xl outline-hidden bg-transparent"
bind:value={query}
placeholder={$i18n.t('Search')}
/>
</div>
<div>
<Tooltip content={$i18n.t('Add User')}>
<button
class=" p-2 rounded-xl hover:bg-gray-100 dark:bg-gray-900 dark:hover:bg-gray-850 transition font-medium text-sm flex items-center space-x-1"
on:click={() => {
showAddUserModal = !showAddUserModal;
}}
>
<Plus className="size-3.5" />
</button>
</Tooltip>
</div>
</div> </div>
</div>
</div>
<div <div class="flex gap-1">
class="scrollbar-hidden relative whitespace-nowrap overflow-x-auto max-w-full rounded-sm pt-0.5" <div class=" flex w-full space-x-2">
> <div class="flex flex-1">
<table <div class=" self-center ml-1 mr-3">
class="w-full text-sm text-left text-gray-500 dark:text-gray-400 table-auto max-w-full rounded-sm" <svg
> xmlns="http://www.w3.org/2000/svg"
<thead viewBox="0 0 20 20"
class="text-xs text-gray-700 uppercase bg-gray-50 dark:bg-gray-850 dark:text-gray-400 -translate-y-0.5" fill="currentColor"
> class="w-4 h-4"
<tr class=""> >
<th <path
scope="col" fill-rule="evenodd"
class="px-3 py-1.5 cursor-pointer select-none" d="M9 3.5a5.5 5.5 0 100 11 5.5 5.5 0 000-11zM2 9a7 7 0 1112.452 4.391l3.328 3.329a.75.75 0 11-1.06 1.06l-3.329-3.328A7 7 0 012 9z"
on:click={() => setSortKey('role')} clip-rule="evenodd"
> />
<div class="flex gap-1.5 items-center"> </svg>
{$i18n.t('Role')}
{#if orderBy === 'role'}
<span class="font-normal"
>{#if direction === 'asc'}
<ChevronUp className="size-2" />
{:else}
<ChevronDown className="size-2" />
{/if}
</span>
{:else}
<span class="invisible">
<ChevronUp className="size-2" />
</span>
{/if}
</div> </div>
</th> <input
<th class=" w-full text-sm pr-4 py-1 rounded-r-xl outline-hidden bg-transparent"
scope="col" bind:value={query}
class="px-3 py-1.5 cursor-pointer select-none" placeholder={$i18n.t('Search')}
on:click={() => setSortKey('name')} />
> </div>
<div class="flex gap-1.5 items-center">
{$i18n.t('Name')}
{#if orderBy === 'name'} <div>
<span class="font-normal" <Tooltip content={$i18n.t('Add User')}>
>{#if direction === 'asc'}
<ChevronUp className="size-2" />
{:else}
<ChevronDown className="size-2" />
{/if}
</span>
{:else}
<span class="invisible">
<ChevronUp className="size-2" />
</span>
{/if}
</div>
</th>
<th
scope="col"
class="px-3 py-1.5 cursor-pointer select-none"
on:click={() => setSortKey('email')}
>
<div class="flex gap-1.5 items-center">
{$i18n.t('Email')}
{#if orderBy === 'email'}
<span class="font-normal"
>{#if direction === 'asc'}
<ChevronUp className="size-2" />
{:else}
<ChevronDown className="size-2" />
{/if}
</span>
{:else}
<span class="invisible">
<ChevronUp className="size-2" />
</span>
{/if}
</div>
</th>
<th
scope="col"
class="px-3 py-1.5 cursor-pointer select-none"
on:click={() => setSortKey('last_active_at')}
>
<div class="flex gap-1.5 items-center">
{$i18n.t('Last Active')}
{#if orderBy === 'last_active_at'}
<span class="font-normal"
>{#if direction === 'asc'}
<ChevronUp className="size-2" />
{:else}
<ChevronDown className="size-2" />
{/if}
</span>
{:else}
<span class="invisible">
<ChevronUp className="size-2" />
</span>
{/if}
</div>
</th>
<th
scope="col"
class="px-3 py-1.5 cursor-pointer select-none"
on:click={() => setSortKey('created_at')}
>
<div class="flex gap-1.5 items-center">
{$i18n.t('Created at')}
{#if orderBy === 'created_at'}
<span class="font-normal"
>{#if direction === 'asc'}
<ChevronUp className="size-2" />
{:else}
<ChevronDown className="size-2" />
{/if}
</span>
{:else}
<span class="invisible">
<ChevronUp className="size-2" />
</span>
{/if}
</div>
</th>
<th
scope="col"
class="px-3 py-1.5 cursor-pointer select-none"
on:click={() => setSortKey('oauth_sub')}
>
<div class="flex gap-1.5 items-center">
{$i18n.t('OAuth ID')}
{#if orderBy === 'oauth_sub'}
<span class="font-normal"
>{#if direction === 'asc'}
<ChevronUp className="size-2" />
{:else}
<ChevronDown className="size-2" />
{/if}
</span>
{:else}
<span class="invisible">
<ChevronUp className="size-2" />
</span>
{/if}
</div>
</th>
<th scope="col" class="px-3 py-2 text-right" />
</tr>
</thead>
<tbody class="">
{#each users as user, userIdx}
<tr class="bg-white dark:bg-gray-900 dark:border-gray-850 text-xs">
<td class="px-3 py-1 min-w-[7rem] w-28">
<button <button
class=" translate-y-0.5" class=" p-2 rounded-xl hover:bg-gray-100 dark:bg-gray-900 dark:hover:bg-gray-850 transition font-medium text-sm flex items-center space-x-1"
on:click={() => { on:click={() => {
selectedUser = user; showAddUserModal = !showAddUserModal;
showUpdateRoleModal = true;
}} }}
> >
<Badge <Plus className="size-3.5" />
type={user.role === 'admin' ? 'info' : user.role === 'user' ? 'success' : 'muted'}
content={$i18n.t(user.role)}
/>
</button> </button>
</td> </Tooltip>
<td class="px-3 py-1 font-medium text-gray-900 dark:text-white w-max"> </div>
<div class="flex flex-row w-max"> </div>
<img </div>
class=" rounded-full w-6 h-6 object-cover mr-2.5" </div>
src={user.profile_image_url.startsWith(WEBUI_BASE_URL) ||
user.profile_image_url.startsWith('https://www.gravatar.com/avatar/') ||
user.profile_image_url.startsWith('data:')
? user.profile_image_url
: `/user.png`}
alt="user"
/>
<div class=" font-medium self-center">{user.name}</div> <div
</div> class="scrollbar-hidden relative whitespace-nowrap overflow-x-auto max-w-full rounded-sm pt-0.5"
</td> >
<td class=" px-3 py-1"> {user.email} </td> <table
class="w-full text-sm text-left text-gray-500 dark:text-gray-400 table-auto max-w-full rounded-sm"
>
<thead
class="text-xs text-gray-700 uppercase bg-gray-50 dark:bg-gray-850 dark:text-gray-400 -translate-y-0.5"
>
<tr class="">
<th
scope="col"
class="px-3 py-1.5 cursor-pointer select-none"
on:click={() => setSortKey('role')}
>
<div class="flex gap-1.5 items-center">
{$i18n.t('Role')}
<td class=" px-3 py-1"> {#if orderBy === 'role'}
{dayjs(user.last_active_at * 1000).fromNow()} <span class="font-normal"
</td> >{#if direction === 'asc'}
<ChevronUp className="size-2" />
<td class=" px-3 py-1"> {:else}
{dayjs(user.created_at * 1000).format('LL')} <ChevronDown className="size-2" />
</td> {/if}
</span>
<td class=" px-3 py-1"> {user.oauth_sub ?? ''} </td> {:else}
<span class="invisible">
<td class="px-3 py-1 text-right"> <ChevronUp className="size-2" />
<div class="flex justify-end w-full"> </span>
{#if $config.features.enable_admin_chat_access && user.role !== 'admin'}
<Tooltip content={$i18n.t('Chats')}>
<button
class="self-center w-fit text-sm px-2 py-2 hover:bg-black/5 dark:hover:bg-white/5 rounded-xl"
on:click={async () => {
showUserChatsModal = !showUserChatsModal;
selectedUser = user;
}}
>
<ChatBubbles />
</button>
</Tooltip>
{/if} {/if}
</div>
</th>
<th
scope="col"
class="px-3 py-1.5 cursor-pointer select-none"
on:click={() => setSortKey('name')}
>
<div class="flex gap-1.5 items-center">
{$i18n.t('Name')}
<Tooltip content={$i18n.t('Edit User')}> {#if orderBy === 'name'}
<button <span class="font-normal"
class="self-center w-fit text-sm px-2 py-2 hover:bg-black/5 dark:hover:bg-white/5 rounded-xl" >{#if direction === 'asc'}
on:click={async () => { <ChevronUp className="size-2" />
showEditUserModal = !showEditUserModal; {:else}
selectedUser = user; <ChevronDown className="size-2" />
}} {/if}
> </span>
<svg {:else}
xmlns="http://www.w3.org/2000/svg" <span class="invisible">
fill="none" <ChevronUp className="size-2" />
viewBox="0 0 24 24" </span>
stroke-width="1.5" {/if}
stroke="currentColor" </div>
class="w-4 h-4" </th>
> <th
<path scope="col"
stroke-linecap="round" class="px-3 py-1.5 cursor-pointer select-none"
stroke-linejoin="round" on:click={() => setSortKey('email')}
d="m16.862 4.487 1.687-1.688a1.875 1.875 0 1 1 2.652 2.652L6.832 19.82a4.5 4.5 0 0 1-1.897 1.13l-2.685.8.8-2.685a4.5 4.5 0 0 1 1.13-1.897L16.863 4.487Zm0 0L19.5 7.125" >
/> <div class="flex gap-1.5 items-center">
</svg> {$i18n.t('Email')}
</button>
</Tooltip>
{#if user.role !== 'admin'} {#if orderBy === 'email'}
<Tooltip content={$i18n.t('Delete User')}> <span class="font-normal"
>{#if direction === 'asc'}
<ChevronUp className="size-2" />
{:else}
<ChevronDown className="size-2" />
{/if}
</span>
{:else}
<span class="invisible">
<ChevronUp className="size-2" />
</span>
{/if}
</div>
</th>
<th
scope="col"
class="px-3 py-1.5 cursor-pointer select-none"
on:click={() => setSortKey('last_active_at')}
>
<div class="flex gap-1.5 items-center">
{$i18n.t('Last Active')}
{#if orderBy === 'last_active_at'}
<span class="font-normal"
>{#if direction === 'asc'}
<ChevronUp className="size-2" />
{:else}
<ChevronDown className="size-2" />
{/if}
</span>
{:else}
<span class="invisible">
<ChevronUp className="size-2" />
</span>
{/if}
</div>
</th>
<th
scope="col"
class="px-3 py-1.5 cursor-pointer select-none"
on:click={() => setSortKey('created_at')}
>
<div class="flex gap-1.5 items-center">
{$i18n.t('Created at')}
{#if orderBy === 'created_at'}
<span class="font-normal"
>{#if direction === 'asc'}
<ChevronUp className="size-2" />
{:else}
<ChevronDown className="size-2" />
{/if}
</span>
{:else}
<span class="invisible">
<ChevronUp className="size-2" />
</span>
{/if}
</div>
</th>
<th
scope="col"
class="px-3 py-1.5 cursor-pointer select-none"
on:click={() => setSortKey('oauth_sub')}
>
<div class="flex gap-1.5 items-center">
{$i18n.t('OAuth ID')}
{#if orderBy === 'oauth_sub'}
<span class="font-normal"
>{#if direction === 'asc'}
<ChevronUp className="size-2" />
{:else}
<ChevronDown className="size-2" />
{/if}
</span>
{:else}
<span class="invisible">
<ChevronUp className="size-2" />
</span>
{/if}
</div>
</th>
<th scope="col" class="px-3 py-2 text-right" />
</tr>
</thead>
<tbody class="">
{#each users as user, userIdx}
<tr class="bg-white dark:bg-gray-900 dark:border-gray-850 text-xs">
<td class="px-3 py-1 min-w-[7rem] w-28">
<button
class=" translate-y-0.5"
on:click={() => {
selectedUser = user;
showUpdateRoleModal = true;
}}
>
<Badge
type={user.role === 'admin' ? 'info' : user.role === 'user' ? 'success' : 'muted'}
content={$i18n.t(user.role)}
/>
</button>
</td>
<td class="px-3 py-1 font-medium text-gray-900 dark:text-white w-max">
<div class="flex flex-row w-max">
<img
class=" rounded-full w-6 h-6 object-cover mr-2.5"
src={user.profile_image_url.startsWith(WEBUI_BASE_URL) ||
user.profile_image_url.startsWith('https://www.gravatar.com/avatar/') ||
user.profile_image_url.startsWith('data:')
? user.profile_image_url
: `/user.png`}
alt="user"
/>
<div class=" font-medium self-center">{user.name}</div>
</div>
</td>
<td class=" px-3 py-1"> {user.email} </td>
<td class=" px-3 py-1">
{dayjs(user.last_active_at * 1000).fromNow()}
</td>
<td class=" px-3 py-1">
{dayjs(user.created_at * 1000).format('LL')}
</td>
<td class=" px-3 py-1"> {user.oauth_sub ?? ''} </td>
<td class="px-3 py-1 text-right">
<div class="flex justify-end w-full">
{#if $config.features.enable_admin_chat_access && user.role !== 'admin'}
<Tooltip content={$i18n.t('Chats')}>
<button
class="self-center w-fit text-sm px-2 py-2 hover:bg-black/5 dark:hover:bg-white/5 rounded-xl"
on:click={async () => {
showUserChatsModal = !showUserChatsModal;
selectedUser = user;
}}
>
<ChatBubbles />
</button>
</Tooltip>
{/if}
<Tooltip content={$i18n.t('Edit User')}>
<button <button
class="self-center w-fit text-sm px-2 py-2 hover:bg-black/5 dark:hover:bg-white/5 rounded-xl" class="self-center w-fit text-sm px-2 py-2 hover:bg-black/5 dark:hover:bg-white/5 rounded-xl"
on:click={async () => { on:click={async () => {
showDeleteConfirmDialog = true; showEditUserModal = !showEditUserModal;
selectedUser = user; selectedUser = user;
}} }}
> >
@ -503,25 +483,52 @@
<path <path
stroke-linecap="round" stroke-linecap="round"
stroke-linejoin="round" stroke-linejoin="round"
d="m14.74 9-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 0 1-2.244 2.077H8.084a2.25 2.25 0 0 1-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 0 0-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 0 1 3.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 0 0-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 0 0-7.5 0" d="m16.862 4.487 1.687-1.688a1.875 1.875 0 1 1 2.652 2.652L6.832 19.82a4.5 4.5 0 0 1-1.897 1.13l-2.685.8.8-2.685a4.5 4.5 0 0 1 1.13-1.897L16.863 4.487Zm0 0L19.5 7.125"
/> />
</svg> </svg>
</button> </button>
</Tooltip> </Tooltip>
{/if}
</div>
</td>
</tr>
{/each}
</tbody>
</table>
</div>
<div class=" text-gray-500 text-xs mt-1.5 text-right"> {#if user.role !== 'admin'}
{$i18n.t("Click on the user role button to change a user's role.")} <Tooltip content={$i18n.t('Delete User')}>
</div> <button
class="self-center w-fit text-sm px-2 py-2 hover:bg-black/5 dark:hover:bg-white/5 rounded-xl"
on:click={async () => {
showDeleteConfirmDialog = true;
selectedUser = user;
}}
>
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
class="w-4 h-4"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="m14.74 9-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 0 1-2.244 2.077H8.084a2.25 2.25 0 0 1-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 0 0-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 0 1 3.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 0 0-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 0 0-7.5 0"
/>
</svg>
</button>
</Tooltip>
{/if}
</div>
</td>
</tr>
{/each}
</tbody>
</table>
</div>
<Pagination bind:page count={total} perPage={10} /> <div class=" text-gray-500 text-xs mt-1.5 text-right">
{$i18n.t("Click on the user role button to change a user's role.")}
</div>
<Pagination bind:page count={total} perPage={30} />
{/if}
{#if !$config?.license_metadata} {#if !$config?.license_metadata}
{#if total > 50} {#if total > 50}

View file

@ -440,8 +440,10 @@
} }
} }
loading = false; if (!chatIdProp) {
await tick(); loading = false;
await tick();
}
showControls.subscribe(async (value) => { showControls.subscribe(async (value) => {
if (controlPane && !$mobile) { if (controlPane && !$mobile) {

View file

@ -86,7 +86,7 @@
$: onChange({ $: onChange({
prompt, prompt,
files, files: files.filter((file) => file.type !== 'image'),
selectedToolIds, selectedToolIds,
imageGenerationEnabled, imageGenerationEnabled,
webSearchEnabled, webSearchEnabled,
@ -604,7 +604,7 @@
<div class="px-2.5"> <div class="px-2.5">
{#if $settings?.richTextInput ?? true} {#if $settings?.richTextInput ?? true}
<div <div
class="scrollbar-hidden text-left bg-transparent dark:text-gray-100 outline-hidden w-full pt-3 px-1 resize-none h-fit max-h-80 overflow-auto" class="scrollbar-hidden rtl:text-right ltr: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" id="chat-input-container"
> >
<RichTextInput <RichTextInput

View file

@ -33,6 +33,7 @@
let tools = {}; let tools = {};
let show = false; let show = false;
let showAllTools = false;
$: if (show) { $: if (show) {
init(); init();
@ -102,7 +103,7 @@
transition={flyAndScale} transition={flyAndScale}
> >
{#if Object.keys(tools).length > 0} {#if Object.keys(tools).length > 0}
<div class=" max-h-28 overflow-y-auto scrollbar-hidden"> <div class="{showAllTools ? '' : 'max-h-28'} overflow-y-auto scrollbar-thin">
{#each Object.keys(tools) as toolId} {#each Object.keys(tools) as toolId}
<button <button
class="flex w-full justify-between gap-2 items-center px-3 py-2 text-sm font-medium cursor-pointer rounded-xl" class="flex w-full justify-between gap-2 items-center px-3 py-2 text-sm font-medium cursor-pointer rounded-xl"
@ -141,7 +142,29 @@
</button> </button>
{/each} {/each}
</div> </div>
{#if Object.keys(tools).length > 3}
<button
class="flex w-full justify-center items-center text-sm font-medium cursor-pointer rounded-lg hover:bg-gray-50 dark:hover:bg-gray-800"
on:click={() => {
showAllTools = !showAllTools;
}}
title={showAllTools ? $i18n.t('Show Less') : $i18n.t('Show All')}
>
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="2.5"
stroke="currentColor"
class="size-3 transition-transform duration-200 {showAllTools
? 'rotate-180'
: ''} text-gray-300 dark:text-gray-600"
>
<path stroke-linecap="round" stroke-linejoin="round" d="m19.5 8.25-7.5 7.5-7.5-7.5"
></path>
</svg>
</button>
{/if}
<hr class="border-black/5 dark:border-white/5 my-1" /> <hr class="border-black/5 dark:border-white/5 my-1" />
{/if} {/if}

View file

@ -32,6 +32,7 @@
title="Video player" title="Video player"
frameborder="0" frameborder="0"
referrerpolicy="strict-origin-when-cross-origin" referrerpolicy="strict-origin-when-cross-origin"
controls
allowfullscreen allowfullscreen
></video> ></video>
{:else} {:else}

View file

@ -54,6 +54,9 @@
height: '' height: ''
}; };
// chat export
let stylizedPdfExport = true;
// Admin - Show Update Available Toast // Admin - Show Update Available Toast
let showUpdateToast = true; let showUpdateToast = true;
let showChangelog = true; let showChangelog = true;
@ -152,6 +155,11 @@
saveSettings({ hapticFeedback: hapticFeedback }); saveSettings({ hapticFeedback: hapticFeedback });
}; };
const toggleStylizedPdfExport = async () => {
stylizedPdfExport = !stylizedPdfExport;
saveSettings({ stylizedPdfExport: stylizedPdfExport });
};
const toggleUserLocation = async () => { const toggleUserLocation = async () => {
userLocation = !userLocation; userLocation = !userLocation;
@ -302,6 +310,11 @@
notificationSound = $settings?.notificationSound ?? true; notificationSound = $settings?.notificationSound ?? true;
notificationSoundAlways = $settings?.notificationSoundAlways ?? false; notificationSoundAlways = $settings?.notificationSoundAlways ?? false;
iframeSandboxAllowSameOrigin = $settings?.iframeSandboxAllowSameOrigin ?? false;
iframeSandboxAllowForms = $settings?.iframeSandboxAllowForms ?? false;
stylizedPdfExport = $settings?.stylizedPdfExport ?? true;
hapticFeedback = $settings.hapticFeedback ?? false; hapticFeedback = $settings.hapticFeedback ?? false;
ctrlEnterToSend = $settings.ctrlEnterToSend ?? false; ctrlEnterToSend = $settings.ctrlEnterToSend ?? false;
@ -964,6 +977,28 @@
</div> </div>
</div> </div>
<div>
<div class=" py-0.5 flex w-full justify-between">
<div class=" self-center text-xs">
{$i18n.t('Stylized PDF Export')}
</div>
<button
class="p-1 px-3 text-xs flex rounded-sm transition"
on:click={() => {
toggleStylizedPdfExport();
}}
type="button"
>
{#if stylizedPdfExport === true}
<span class="ml-2 self-center">{$i18n.t('On')}</span>
{:else}
<span class="ml-2 self-center">{$i18n.t('Off')}</span>
{/if}
</button>
</div>
</div>
<div class=" my-1.5 text-sm font-medium">{$i18n.t('Voice')}</div> <div class=" my-1.5 text-sm font-medium">{$i18n.t('Voice')}</div>
<div> <div>

View file

@ -3,7 +3,7 @@
import { fade } from 'svelte/transition'; import { fade } from 'svelte/transition';
import { flyAndScale } from '$lib/utils/transitions'; import { flyAndScale } from '$lib/utils/transitions';
import * as FocusTrap from 'focus-trap';
export let show = true; export let show = true;
export let size = 'md'; export let size = 'md';
export let containerClassName = 'p-3'; export let containerClassName = 'p-3';
@ -11,6 +11,10 @@
let modalElement = null; let modalElement = null;
let mounted = false; let mounted = false;
// Create focus trap to trap user tabs inside modal
// https://www.w3.org/WAI/WCAG21/Understanding/focus-order.html
// https://www.w3.org/WAI/WCAG21/Understanding/keyboard.html
let focusTrap: FocusTrap.FocusTrap | null = null;
const sizeToWidth = (size) => { const sizeToWidth = (size) => {
if (size === 'full') { if (size === 'full') {
@ -45,9 +49,12 @@
$: if (show && modalElement) { $: if (show && modalElement) {
document.body.appendChild(modalElement); document.body.appendChild(modalElement);
focusTrap = FocusTrap.createFocusTrap(modalElement);
focusTrap.activate();
window.addEventListener('keydown', handleKeyDown); window.addEventListener('keydown', handleKeyDown);
document.body.style.overflow = 'hidden'; document.body.style.overflow = 'hidden';
} else if (modalElement) { } else if (modalElement) {
focusTrap.deactivate();
window.removeEventListener('keydown', handleKeyDown); window.removeEventListener('keydown', handleKeyDown);
document.body.removeChild(modalElement); document.body.removeChild(modalElement);
document.body.style.overflow = 'unset'; document.body.style.overflow = 'unset';
@ -55,6 +62,9 @@
onDestroy(() => { onDestroy(() => {
show = false; show = false;
if (focusTrap) {
focusTrap.deactivate();
}
if (modalElement) { if (modalElement) {
document.body.removeChild(modalElement); document.body.removeChild(modalElement);
} }
@ -66,6 +76,8 @@
<!-- svelte-ignore a11y-no-static-element-interactions --> <!-- svelte-ignore a11y-no-static-element-interactions -->
<div <div
bind:this={modalElement} bind:this={modalElement}
aria-modal="true"
role="dialog"
class="modal fixed top-0 right-0 left-0 bottom-0 bg-black/60 w-full h-screen max-h-[100dvh] {containerClassName} flex justify-center z-9999 overflow-y-auto overscroll-contain" class="modal fixed top-0 right-0 left-0 bottom-0 bg-black/60 w-full h-screen max-h-[100dvh] {containerClassName} flex justify-center z-9999 overflow-y-auto overscroll-contain"
in:fade={{ duration: 10 }} in:fade={{ duration: 10 }}
on:mousedown={() => { on:mousedown={() => {

View file

@ -19,7 +19,8 @@
mobile, mobile,
temporaryChatEnabled, temporaryChatEnabled,
theme, theme,
user user,
settings
} from '$lib/stores'; } from '$lib/stores';
import { flyAndScale } from '$lib/utils/transitions'; import { flyAndScale } from '$lib/utils/transitions';
@ -63,75 +64,124 @@
}; };
const downloadPdf = async () => { const downloadPdf = async () => {
const containerElement = document.getElementById('messages-container'); if ($settings?.stylizedPdfExport ?? true) {
const containerElement = document.getElementById('messages-container');
if (containerElement) { if (containerElement) {
try { try {
const isDarkMode = document.documentElement.classList.contains('dark'); const isDarkMode = document.documentElement.classList.contains('dark');
const virtualWidth = 800; // Fixed width in px
const pagePixelHeight = 1200; // Each slice height (adjust to avoid canvas bugs; generally 24k is safe)
console.log('isDarkMode', isDarkMode); // Clone & style once
const clonedElement = containerElement.cloneNode(true);
clonedElement.classList.add('text-black');
clonedElement.classList.add('dark:text-white');
clonedElement.style.width = `${virtualWidth}px`;
clonedElement.style.position = 'absolute';
clonedElement.style.left = '-9999px'; // Offscreen
clonedElement.style.height = 'auto';
document.body.appendChild(clonedElement);
// Define a fixed virtual screen size // Get total height after attached to DOM
const virtualWidth = 800; // Fixed width (adjust as needed) const totalHeight = clonedElement.scrollHeight;
// Clone the container to avoid layout shifts let offsetY = 0;
const clonedElement = containerElement.cloneNode(true); let page = 0;
clonedElement.classList.add('text-black');
clonedElement.classList.add('dark:text-white');
clonedElement.style.width = `${virtualWidth}px`; // Apply fixed width
clonedElement.style.height = 'auto'; // Allow content to expand
document.body.appendChild(clonedElement); // Temporarily add to DOM // Prepare PDF
const pdf = new jsPDF('p', 'mm', 'a4');
const imgWidth = 210; // A4 mm
const pageHeight = 297; // A4 mm
// Render to canvas with predefined width while (offsetY < totalHeight) {
const canvas = await html2canvas(clonedElement, { // For each slice, adjust scrollTop to show desired part
backgroundColor: isDarkMode ? '#000' : '#fff', clonedElement.scrollTop = offsetY;
useCORS: true,
scale: 2, // Keep at 1x to avoid unexpected enlargements
width: virtualWidth, // Set fixed virtual screen width
windowWidth: virtualWidth // Ensure consistent rendering
});
document.body.removeChild(clonedElement); // Clean up temp element // Optionally: mask/hide overflowing content via CSS if needed
clonedElement.style.maxHeight = `${pagePixelHeight}px`;
// Only render the visible part
const canvas = await html2canvas(clonedElement, {
backgroundColor: isDarkMode ? '#000' : '#fff',
useCORS: true,
scale: 2,
width: virtualWidth,
height: Math.min(pagePixelHeight, totalHeight - offsetY),
// Optionally: y offset for correct region?
windowWidth: virtualWidth
//windowHeight: pagePixelHeight,
});
const imgData = canvas.toDataURL('image/png');
// Maintain aspect ratio
const imgHeight = (canvas.height * imgWidth) / canvas.width;
const position = 0; // Always first line, since we've clipped vertically
const imgData = canvas.toDataURL('image/png'); if (page > 0) pdf.addPage();
// A4 page settings // Set page background for dark mode
const pdf = new jsPDF('p', 'mm', 'a4'); if (isDarkMode) {
const imgWidth = 210; // A4 width in mm pdf.setFillColor(0, 0, 0);
const pageHeight = 297; // A4 height in mm pdf.rect(0, 0, imgWidth, pageHeight, 'F'); // black bg
}
// Maintain aspect ratio pdf.addImage(imgData, 'PNG', 0, position, imgWidth, imgHeight);
const imgHeight = (canvas.height * imgWidth) / canvas.width;
let heightLeft = imgHeight;
let position = 0;
// Set page background for dark mode offsetY += pagePixelHeight;
if (isDarkMode) { page++;
pdf.setFillColor(0, 0, 0);
pdf.rect(0, 0, imgWidth, pageHeight, 'F'); // Apply black bg
}
pdf.addImage(imgData, 'PNG', 0, position, imgWidth, imgHeight);
heightLeft -= pageHeight;
// Handle additional pages
while (heightLeft > 0) {
position -= pageHeight;
pdf.addPage();
if (isDarkMode) {
pdf.setFillColor(0, 0, 0);
pdf.rect(0, 0, imgWidth, pageHeight, 'F');
} }
pdf.addImage(imgData, 'PNG', 0, position, imgWidth, imgHeight); document.body.removeChild(clonedElement);
heightLeft -= pageHeight;
}
pdf.save(`chat-${chat.chat.title}.pdf`); pdf.save(`chat-${chat.chat.title}.pdf`);
} catch (error) { } catch (error) {
console.error('Error generating PDF', error); console.error('Error generating PDF', error);
}
} }
} else {
console.log('Downloading PDF');
const chatText = await getChatAsText();
const doc = new jsPDF();
// Margins
const left = 15;
const top = 20;
const right = 15;
const bottom = 20;
const pageWidth = doc.internal.pageSize.getWidth();
const pageHeight = doc.internal.pageSize.getHeight();
const usableWidth = pageWidth - left - right;
const usableHeight = pageHeight - top - bottom;
// Font size and line height
const fontSize = 8;
doc.setFontSize(fontSize);
const lineHeight = fontSize * 1; // adjust if needed
// Split the markdown into lines (handles \n)
const paragraphs = chatText.split('\n');
let y = top;
for (let paragraph of paragraphs) {
// Wrap each paragraph to fit the width
const lines = doc.splitTextToSize(paragraph, usableWidth);
for (let line of lines) {
// If the line would overflow the bottom, add a new page
if (y + lineHeight > pageHeight - bottom) {
doc.addPage();
y = top;
}
doc.text(line, left, y);
y += lineHeight * 0.5;
}
// Add empty line at paragraph breaks
y += lineHeight * 0.1;
}
doc.save(`chat-${chat.chat.title}.pdf`);
} }
}; };

View file

@ -26,7 +26,7 @@
getChatPinnedStatusById, getChatPinnedStatusById,
toggleChatPinnedStatusById toggleChatPinnedStatusById
} from '$lib/apis/chats'; } from '$lib/apis/chats';
import { chats, theme, user } from '$lib/stores'; import { chats, settings, theme, user } from '$lib/stores';
import { createMessagesList } from '$lib/utils'; import { createMessagesList } from '$lib/utils';
import { downloadChatAsPDF } from '$lib/apis/utils'; import { downloadChatAsPDF } from '$lib/apis/utils';
import Download from '$lib/components/icons/Download.svelte'; import Download from '$lib/components/icons/Download.svelte';
@ -81,74 +81,124 @@
const downloadPdf = async () => { const downloadPdf = async () => {
const chat = await getChatById(localStorage.token, chatId); const chat = await getChatById(localStorage.token, chatId);
const containerElement = document.getElementById('messages-container'); if ($settings?.stylizedPdfExport ?? true) {
const containerElement = document.getElementById('messages-container');
if (containerElement) { if (containerElement) {
try { try {
const isDarkMode = $theme.includes('dark'); // Check theme mode const isDarkMode = document.documentElement.classList.contains('dark');
const virtualWidth = 800; // Fixed width in px
const pagePixelHeight = 1200; // Each slice height (adjust to avoid canvas bugs; generally 24k is safe)
// Define a fixed virtual screen size // Clone & style once
const virtualWidth = 1024; // Fixed width (adjust as needed) const clonedElement = containerElement.cloneNode(true);
const virtualHeight = 1400; // Fixed height (adjust as needed) clonedElement.classList.add('text-black');
clonedElement.classList.add('dark:text-white');
clonedElement.style.width = `${virtualWidth}px`;
clonedElement.style.position = 'absolute';
clonedElement.style.left = '-9999px'; // Offscreen
clonedElement.style.height = 'auto';
document.body.appendChild(clonedElement);
// Clone the container to avoid layout shifts // Get total height after attached to DOM
const clonedElement = containerElement.cloneNode(true); const totalHeight = clonedElement.scrollHeight;
clonedElement.style.width = `${virtualWidth}px`; // Apply fixed width let offsetY = 0;
clonedElement.style.height = 'auto'; // Allow content to expand let page = 0;
document.body.appendChild(clonedElement); // Temporarily add to DOM // Prepare PDF
const pdf = new jsPDF('p', 'mm', 'a4');
const imgWidth = 210; // A4 mm
const pageHeight = 297; // A4 mm
// Render to canvas with predefined width while (offsetY < totalHeight) {
const canvas = await html2canvas(clonedElement, { // For each slice, adjust scrollTop to show desired part
backgroundColor: isDarkMode ? '#000' : '#fff', clonedElement.scrollTop = offsetY;
useCORS: true,
scale: 2, // Keep at 1x to avoid unexpected enlargements
width: virtualWidth, // Set fixed virtual screen width
windowWidth: virtualWidth, // Ensure consistent rendering
windowHeight: virtualHeight
});
document.body.removeChild(clonedElement); // Clean up temp element // Optionally: mask/hide overflowing content via CSS if needed
clonedElement.style.maxHeight = `${pagePixelHeight}px`;
// Only render the visible part
const canvas = await html2canvas(clonedElement, {
backgroundColor: isDarkMode ? '#000' : '#fff',
useCORS: true,
scale: 2,
width: virtualWidth,
height: Math.min(pagePixelHeight, totalHeight - offsetY),
// Optionally: y offset for correct region?
windowWidth: virtualWidth
//windowHeight: pagePixelHeight,
});
const imgData = canvas.toDataURL('image/png');
// Maintain aspect ratio
const imgHeight = (canvas.height * imgWidth) / canvas.width;
const position = 0; // Always first line, since we've clipped vertically
const imgData = canvas.toDataURL('image/png'); if (page > 0) pdf.addPage();
// A4 page settings // Set page background for dark mode
const pdf = new jsPDF('p', 'mm', 'a4'); if (isDarkMode) {
const imgWidth = 210; // A4 width in mm pdf.setFillColor(0, 0, 0);
const pageHeight = 297; // A4 height in mm pdf.rect(0, 0, imgWidth, pageHeight, 'F'); // black bg
}
// Maintain aspect ratio pdf.addImage(imgData, 'PNG', 0, position, imgWidth, imgHeight);
const imgHeight = (canvas.height * imgWidth) / canvas.width;
let heightLeft = imgHeight;
let position = 0;
// Set page background for dark mode offsetY += pagePixelHeight;
if (isDarkMode) { page++;
pdf.setFillColor(0, 0, 0);
pdf.rect(0, 0, imgWidth, pageHeight, 'F'); // Apply black bg
}
pdf.addImage(imgData, 'PNG', 0, position, imgWidth, imgHeight);
heightLeft -= pageHeight;
// Handle additional pages
while (heightLeft > 0) {
position -= pageHeight;
pdf.addPage();
if (isDarkMode) {
pdf.setFillColor(0, 0, 0);
pdf.rect(0, 0, imgWidth, pageHeight, 'F');
} }
pdf.addImage(imgData, 'PNG', 0, position, imgWidth, imgHeight); document.body.removeChild(clonedElement);
heightLeft -= pageHeight;
}
pdf.save(`chat-${chat.chat.title}.pdf`); pdf.save(`chat-${chat.chat.title}.pdf`);
} catch (error) { } catch (error) {
console.error('Error generating PDF', error); console.error('Error generating PDF', error);
}
} }
} else {
console.log('Downloading PDF');
const chatText = await getChatAsText(chat);
const doc = new jsPDF();
// Margins
const left = 15;
const top = 20;
const right = 15;
const bottom = 20;
const pageWidth = doc.internal.pageSize.getWidth();
const pageHeight = doc.internal.pageSize.getHeight();
const usableWidth = pageWidth - left - right;
const usableHeight = pageHeight - top - bottom;
// Font size and line height
const fontSize = 8;
doc.setFontSize(fontSize);
const lineHeight = fontSize * 1; // adjust if needed
// Split the markdown into lines (handles \n)
const paragraphs = chatText.split('\n');
let y = top;
for (let paragraph of paragraphs) {
// Wrap each paragraph to fit the width
const lines = doc.splitTextToSize(paragraph, usableWidth);
for (let line of lines) {
// If the line would overflow the bottom, add a new page
if (y + lineHeight > pageHeight - bottom) {
doc.addPage();
y = top;
}
doc.text(line, left, y);
y += lineHeight;
}
// Add empty line at paragraph breaks
y += lineHeight * 0.5;
}
doc.save(`chat-${chat.chat.title}.pdf`);
} }
}; };

View file

@ -180,7 +180,10 @@
return; return;
} }
const model = $models.find((model) => model.id === selectedModelId); const model = $models
.filter((model) => model.id === selectedModelId && !(model?.info?.meta?.hidden ?? false))
.find((model) => model.id === selectedModelId);
if (!model) { if (!model) {
selectedModelId = ''; selectedModelId = '';
return; return;
@ -599,6 +602,16 @@ Provide the enhanced notes in markdown format. Use markdown syntax for headings,
selectedModelId = ''; selectedModelId = '';
} }
if (selectedModelId) {
const model = $models
.filter((model) => model.id === selectedModelId && !(model?.info?.meta?.hidden ?? false))
.find((model) => model.id === selectedModelId);
if (!model) {
selectedModelId = '';
}
}
const dropzoneElement = document.getElementById('note-editor'); const dropzoneElement = document.getElementById('note-editor');
dropzoneElement?.addEventListener('dragover', onDragOver); dropzoneElement?.addEventListener('dragover', onDragOver);
@ -660,7 +673,10 @@ Provide the enhanced notes in markdown format. Use markdown syntax for headings,
class="w-full bg-transparent text-sm outline-hidden" class="w-full bg-transparent text-sm outline-hidden"
bind:value={selectedModelId} bind:value={selectedModelId}
> >
{#each $models as model} <option value="" class="bg-gray-50 dark:bg-gray-700" disabled>
{$i18n.t('Select a model')}
</option>
{#each $models.filter((model) => !(model?.info?.meta?.hidden ?? false)) as model}
<option value={model.id} class="bg-gray-50 dark:bg-gray-700">{model.name}</option> <option value={model.id} class="bg-gray-50 dark:bg-gray-700">{model.name}</option>
{/each} {/each}
</select> </select>

View file

@ -764,7 +764,7 @@
className="input-prose-sm" className="input-prose-sm"
bind:value={selectedFileContent} bind:value={selectedFileContent}
placeholder={$i18n.t('Add content here')} placeholder={$i18n.t('Add content here')}
preserveBreaks={true} preserveBreaks={false}
/> />
{/key} {/key}
</div> </div>
@ -822,7 +822,7 @@
className="input-prose-sm" className="input-prose-sm"
bind:value={selectedFileContent} bind:value={selectedFileContent}
placeholder={$i18n.t('Add content here')} placeholder={$i18n.t('Add content here')}
preserveBreaks={true} preserveBreaks={false}
/> />
{/key} {/key}
</div> </div>

View file

@ -959,9 +959,8 @@
"Repeat Penalty (Ollama)": "", "Repeat Penalty (Ollama)": "",
"Reply in Thread": "", "Reply in Thread": "",
"Request Mode": "وضع الطلب", "Request Mode": "وضع الطلب",
"Reranking Engine": "",
"Reranking Model": "إعادة تقييم النموذج", "Reranking Model": "إعادة تقييم النموذج",
"Reranking model disabled": "تم تعطيل نموذج إعادة الترتيب",
"Reranking model set to \"{{reranking_model}}\"": "تم ضبط نموذج إعادة الترتيب على \"{{reranking_model}}\"",
"Reset": "", "Reset": "",
"Reset All Models": "", "Reset All Models": "",
"Reset Upload Directory": "", "Reset Upload Directory": "",
@ -1069,6 +1068,8 @@
"Show": "عرض", "Show": "عرض",
"Show \"What's New\" modal on login": "", "Show \"What's New\" modal on login": "",
"Show Admin Details in Account Pending Overlay": "", "Show Admin Details in Account Pending Overlay": "",
"Show All": "",
"Show Less": "",
"Show Model": "", "Show Model": "",
"Show shortcuts": "إظهار الاختصارات", "Show shortcuts": "إظهار الاختصارات",
"Show your support!": "", "Show your support!": "",
@ -1092,6 +1093,7 @@
"Stream Chat Response": "", "Stream Chat Response": "",
"STT Model": "", "STT Model": "",
"STT Settings": "STT اعدادات", "STT Settings": "STT اعدادات",
"Stylized PDF Export": "",
"Subtitle (e.g. about the Roman Empire)": "(e.g. about the Roman Empire) الترجمة", "Subtitle (e.g. about the Roman Empire)": "(e.g. about the Roman Empire) الترجمة",
"Success": "نجاح", "Success": "نجاح",
"Successfully updated.": "تم التحديث بنجاح", "Successfully updated.": "تم التحديث بنجاح",

View file

@ -959,9 +959,8 @@
"Repeat Penalty (Ollama)": "عقوبة التكرار (Ollama)", "Repeat Penalty (Ollama)": "عقوبة التكرار (Ollama)",
"Reply in Thread": "الرد داخل سلسلة الرسائل", "Reply in Thread": "الرد داخل سلسلة الرسائل",
"Request Mode": "وضع الطلب", "Request Mode": "وضع الطلب",
"Reranking Engine": "",
"Reranking Model": "إعادة تقييم النموذج", "Reranking Model": "إعادة تقييم النموذج",
"Reranking model disabled": "تم تعطيل نموذج إعادة الترتيب",
"Reranking model set to \"{{reranking_model}}\"": "تم ضبط نموذج إعادة الترتيب على \"{{reranking_model}}\"",
"Reset": "إعادة تعيين", "Reset": "إعادة تعيين",
"Reset All Models": "إعادة تعيين جميع النماذج", "Reset All Models": "إعادة تعيين جميع النماذج",
"Reset Upload Directory": "إعادة تعيين مجلد التحميل", "Reset Upload Directory": "إعادة تعيين مجلد التحميل",
@ -1069,6 +1068,8 @@
"Show": "عرض", "Show": "عرض",
"Show \"What's New\" modal on login": "عرض نافذة \"ما الجديد\" عند تسجيل الدخول", "Show \"What's New\" modal on login": "عرض نافذة \"ما الجديد\" عند تسجيل الدخول",
"Show Admin Details in Account Pending Overlay": "عرض تفاصيل المشرف في نافذة \"الحساب قيد الانتظار\"", "Show Admin Details in Account Pending Overlay": "عرض تفاصيل المشرف في نافذة \"الحساب قيد الانتظار\"",
"Show All": "",
"Show Less": "",
"Show Model": "", "Show Model": "",
"Show shortcuts": "إظهار الاختصارات", "Show shortcuts": "إظهار الاختصارات",
"Show your support!": "أظهر دعمك!", "Show your support!": "أظهر دعمك!",
@ -1092,6 +1093,7 @@
"Stream Chat Response": "بث استجابة الدردشة", "Stream Chat Response": "بث استجابة الدردشة",
"STT Model": "نموذج تحويل الصوت إلى نص (STT)", "STT Model": "نموذج تحويل الصوت إلى نص (STT)",
"STT Settings": "STT اعدادات", "STT Settings": "STT اعدادات",
"Stylized PDF Export": "",
"Subtitle (e.g. about the Roman Empire)": "(e.g. about the Roman Empire) الترجمة", "Subtitle (e.g. about the Roman Empire)": "(e.g. about the Roman Empire) الترجمة",
"Success": "نجاح", "Success": "نجاح",
"Successfully updated.": "تم التحديث بنجاح", "Successfully updated.": "تم التحديث بنجاح",

View file

@ -959,9 +959,8 @@
"Repeat Penalty (Ollama)": "Наказание за повторение (Ollama)", "Repeat Penalty (Ollama)": "Наказание за повторение (Ollama)",
"Reply in Thread": "Отговори в тред", "Reply in Thread": "Отговори в тред",
"Request Mode": "Режим на заявка", "Request Mode": "Режим на заявка",
"Reranking Engine": "",
"Reranking Model": "Модел за преподреждане", "Reranking Model": "Модел за преподреждане",
"Reranking model disabled": "Моделът за преподреждане е деактивиран",
"Reranking model set to \"{{reranking_model}}\"": "Моделът за преподреждане е зададен на \"{{reranking_model}}\"",
"Reset": "Нулиране", "Reset": "Нулиране",
"Reset All Models": "Нулиране на всички модели", "Reset All Models": "Нулиране на всички модели",
"Reset Upload Directory": "Нулиране на директорията за качване", "Reset Upload Directory": "Нулиране на директорията за качване",
@ -1069,6 +1068,8 @@
"Show": "Покажи", "Show": "Покажи",
"Show \"What's New\" modal on login": "Покажи модалния прозорец \"Какво е ново\" при вписване", "Show \"What's New\" modal on login": "Покажи модалния прозорец \"Какво е ново\" при вписване",
"Show Admin Details in Account Pending Overlay": "Покажи детайлите на администратора в наслагването на изчакващ акаунт", "Show Admin Details in Account Pending Overlay": "Покажи детайлите на администратора в наслагването на изчакващ акаунт",
"Show All": "",
"Show Less": "",
"Show Model": "", "Show Model": "",
"Show shortcuts": "Покажи преки пътища", "Show shortcuts": "Покажи преки пътища",
"Show your support!": "Покажете вашата подкрепа!", "Show your support!": "Покажете вашата подкрепа!",
@ -1092,6 +1093,7 @@
"Stream Chat Response": "Поточен чат отговор", "Stream Chat Response": "Поточен чат отговор",
"STT Model": "STT Модел", "STT Model": "STT Модел",
"STT Settings": "STT Настройки", "STT Settings": "STT Настройки",
"Stylized PDF Export": "",
"Subtitle (e.g. about the Roman Empire)": "Подтитул (напр. за Римска империя)", "Subtitle (e.g. about the Roman Empire)": "Подтитул (напр. за Римска империя)",
"Success": "Успех", "Success": "Успех",
"Successfully updated.": "Успешно обновено.", "Successfully updated.": "Успешно обновено.",

View file

@ -959,9 +959,8 @@
"Repeat Penalty (Ollama)": "", "Repeat Penalty (Ollama)": "",
"Reply in Thread": "", "Reply in Thread": "",
"Request Mode": "রিকোয়েস্ট মোড", "Request Mode": "রিকোয়েস্ট মোড",
"Reranking Engine": "",
"Reranking Model": "রির্যাক্টিং মডেল", "Reranking Model": "রির্যাক্টিং মডেল",
"Reranking model disabled": "রির্যাক্টিং মডেল নিষ্ক্রিয় করা",
"Reranking model set to \"{{reranking_model}}\"": "রির ্যাঙ্কিং মডেল \"{{reranking_model}}\" -এ সেট করা আছে",
"Reset": "", "Reset": "",
"Reset All Models": "", "Reset All Models": "",
"Reset Upload Directory": "", "Reset Upload Directory": "",
@ -1069,6 +1068,8 @@
"Show": "দেখান", "Show": "দেখান",
"Show \"What's New\" modal on login": "", "Show \"What's New\" modal on login": "",
"Show Admin Details in Account Pending Overlay": "", "Show Admin Details in Account Pending Overlay": "",
"Show All": "",
"Show Less": "",
"Show Model": "", "Show Model": "",
"Show shortcuts": "শর্টকাটগুলো দেখান", "Show shortcuts": "শর্টকাটগুলো দেখান",
"Show your support!": "", "Show your support!": "",
@ -1092,6 +1093,7 @@
"Stream Chat Response": "", "Stream Chat Response": "",
"STT Model": "", "STT Model": "",
"STT Settings": "STT সেটিংস", "STT Settings": "STT সেটিংস",
"Stylized PDF Export": "",
"Subtitle (e.g. about the Roman Empire)": "সাবটাইটল (রোমান ইম্পার্টের সম্পর্কে)", "Subtitle (e.g. about the Roman Empire)": "সাবটাইটল (রোমান ইম্পার্টের সম্পর্কে)",
"Success": "সফল", "Success": "সফল",
"Successfully updated.": "সফলভাবে আপডেট হয়েছে", "Successfully updated.": "সফলভাবে আপডেট হয়েছে",

View file

@ -959,9 +959,8 @@
"Repeat Penalty (Ollama)": "བསྐྱར་ཟློས་ཀྱི་ཆད་པ། (Ollama)", "Repeat Penalty (Ollama)": "བསྐྱར་ཟློས་ཀྱི་ཆད་པ། (Ollama)",
"Reply in Thread": "བརྗོད་གཞིའི་ནང་ལན་འདེབས།", "Reply in Thread": "བརྗོད་གཞིའི་ནང་ལན་འདེབས།",
"Request Mode": "རེ་ཞུའི་མ་དཔེ།", "Request Mode": "རེ་ཞུའི་མ་དཔེ།",
"Reranking Engine": "",
"Reranking Model": "བསྐྱར་སྒྲིག་དཔེ་དབྱིབས།", "Reranking Model": "བསྐྱར་སྒྲིག་དཔེ་དབྱིབས།",
"Reranking model disabled": "བསྐྱར་སྒྲིག་དཔེ་དབྱིབས་ནུས་མེད་བཏང་།",
"Reranking model set to \"{{reranking_model}}\"": "བསྐྱར་སྒྲིག་དཔེ་དབྱིབས་ \"{{reranking_model}}\" ལ་བཀོད་སྒྲིག་བྱས།",
"Reset": "སླར་སྒྲིག", "Reset": "སླར་སྒྲིག",
"Reset All Models": "དཔེ་དབྱིབས་ཡོངས་རྫོགས་སླར་སྒྲིག", "Reset All Models": "དཔེ་དབྱིབས་ཡོངས་རྫོགས་སླར་སྒྲིག",
"Reset Upload Directory": "སྤར་བའི་ཐོ་འཚོལ་སླར་སྒྲིག", "Reset Upload Directory": "སྤར་བའི་ཐོ་འཚོལ་སླར་སྒྲིག",
@ -1069,6 +1068,8 @@
"Show": "སྟོན་པ།", "Show": "སྟོན་པ།",
"Show \"What's New\" modal on login": "ནང་འཛུལ་སྐབས་ \"གསར་པ་ཅི་ཡོད\" modal སྟོན་པ།", "Show \"What's New\" modal on login": "ནང་འཛུལ་སྐབས་ \"གསར་པ་ཅི་ཡོད\" modal སྟོན་པ།",
"Show Admin Details in Account Pending Overlay": "རྩིས་ཁྲ་སྒུག་བཞིན་པའི་གཏོགས་ངོས་སུ་དོ་དམ་པའི་ཞིབ་ཕྲ་སྟོན་པ།", "Show Admin Details in Account Pending Overlay": "རྩིས་ཁྲ་སྒུག་བཞིན་པའི་གཏོགས་ངོས་སུ་དོ་དམ་པའི་ཞིབ་ཕྲ་སྟོན་པ།",
"Show All": "",
"Show Less": "",
"Show Model": "དཔེ་དབྱིབས་སྟོན་པ།", "Show Model": "དཔེ་དབྱིབས་སྟོན་པ།",
"Show shortcuts": "མྱུར་ལམ་སྟོན་པ།", "Show shortcuts": "མྱུར་ལམ་སྟོན་པ།",
"Show your support!": "ཁྱེད་ཀྱི་རྒྱབ་སྐྱོར་སྟོན་པ།", "Show your support!": "ཁྱེད་ཀྱི་རྒྱབ་སྐྱོར་སྟོན་པ།",
@ -1092,6 +1093,7 @@
"Stream Chat Response": "ཁ་བརྡའི་ལན་རྒྱུག་པ།", "Stream Chat Response": "ཁ་བརྡའི་ལན་རྒྱུག་པ།",
"STT Model": "STT དཔེ་དབྱིབས།", "STT Model": "STT དཔེ་དབྱིབས།",
"STT Settings": "STT སྒྲིག་འགོད།", "STT Settings": "STT སྒྲིག་འགོད།",
"Stylized PDF Export": "",
"Subtitle (e.g. about the Roman Empire)": "ཁ་བྱང་ཕལ་པ། (དཔེར་ན། རོམ་མའི་གོང་མའི་རྒྱལ་ཁབ་སྐོར།)", "Subtitle (e.g. about the Roman Empire)": "ཁ་བྱང་ཕལ་པ། (དཔེར་ན། རོམ་མའི་གོང་མའི་རྒྱལ་ཁབ་སྐོར།)",
"Success": "ལེགས་འགྲུབ།", "Success": "ལེགས་འགྲུབ།",
"Successfully updated.": "ལེགས་པར་གསར་སྒྱུར་བྱས།", "Successfully updated.": "ལེགས་པར་གསར་སྒྱུར་བྱས།",

View file

@ -959,9 +959,8 @@
"Repeat Penalty (Ollama)": "Penalització per repetició (Ollama)", "Repeat Penalty (Ollama)": "Penalització per repetició (Ollama)",
"Reply in Thread": "Respondre al fil", "Reply in Thread": "Respondre al fil",
"Request Mode": "Mode de sol·licitud", "Request Mode": "Mode de sol·licitud",
"Reranking Engine": "",
"Reranking Model": "Model de reavaluació", "Reranking Model": "Model de reavaluació",
"Reranking model disabled": "Model de reavaluació desactivat",
"Reranking model set to \"{{reranking_model}}\"": "Model de reavaluació establert a \"{{reranking_model}}\"",
"Reset": "Restableix", "Reset": "Restableix",
"Reset All Models": "Restablir tots els models", "Reset All Models": "Restablir tots els models",
"Reset Upload Directory": "Restableix el directori de pujades", "Reset Upload Directory": "Restableix el directori de pujades",
@ -1069,6 +1068,8 @@
"Show": "Mostrar", "Show": "Mostrar",
"Show \"What's New\" modal on login": "Veure 'Què hi ha de nou' a l'entrada", "Show \"What's New\" modal on login": "Veure 'Què hi ha de nou' a l'entrada",
"Show Admin Details in Account Pending Overlay": "Mostrar els detalls de l'administrador a la superposició del compte pendent", "Show Admin Details in Account Pending Overlay": "Mostrar els detalls de l'administrador a la superposició del compte pendent",
"Show All": "",
"Show Less": "",
"Show Model": "Mostrar el model", "Show Model": "Mostrar el model",
"Show shortcuts": "Mostrar dreceres", "Show shortcuts": "Mostrar dreceres",
"Show your support!": "Mostra el teu suport!", "Show your support!": "Mostra el teu suport!",
@ -1092,6 +1093,7 @@
"Stream Chat Response": "Fer streaming de la resposta del xat", "Stream Chat Response": "Fer streaming de la resposta del xat",
"STT Model": "Model SST", "STT Model": "Model SST",
"STT Settings": "Preferències de STT", "STT Settings": "Preferències de STT",
"Stylized PDF Export": "",
"Subtitle (e.g. about the Roman Empire)": "Subtítol (per exemple, sobre l'Imperi Romà)", "Subtitle (e.g. about the Roman Empire)": "Subtítol (per exemple, sobre l'Imperi Romà)",
"Success": "Èxit", "Success": "Èxit",
"Successfully updated.": "Actualitzat correctament.", "Successfully updated.": "Actualitzat correctament.",

View file

@ -959,9 +959,8 @@
"Repeat Penalty (Ollama)": "", "Repeat Penalty (Ollama)": "",
"Reply in Thread": "", "Reply in Thread": "",
"Request Mode": "Query mode", "Request Mode": "Query mode",
"Reranking Engine": "",
"Reranking Model": "", "Reranking Model": "",
"Reranking model disabled": "",
"Reranking model set to \"{{reranking_model}}\"": "",
"Reset": "", "Reset": "",
"Reset All Models": "", "Reset All Models": "",
"Reset Upload Directory": "", "Reset Upload Directory": "",
@ -1069,6 +1068,8 @@
"Show": "Pagpakita", "Show": "Pagpakita",
"Show \"What's New\" modal on login": "", "Show \"What's New\" modal on login": "",
"Show Admin Details in Account Pending Overlay": "", "Show Admin Details in Account Pending Overlay": "",
"Show All": "",
"Show Less": "",
"Show Model": "", "Show Model": "",
"Show shortcuts": "Ipakita ang mga shortcut", "Show shortcuts": "Ipakita ang mga shortcut",
"Show your support!": "", "Show your support!": "",
@ -1092,6 +1093,7 @@
"Stream Chat Response": "", "Stream Chat Response": "",
"STT Model": "", "STT Model": "",
"STT Settings": "Mga setting sa STT", "STT Settings": "Mga setting sa STT",
"Stylized PDF Export": "",
"Subtitle (e.g. about the Roman Empire)": "", "Subtitle (e.g. about the Roman Empire)": "",
"Success": "Kalampusan", "Success": "Kalampusan",
"Successfully updated.": "Malampuson nga na-update.", "Successfully updated.": "Malampuson nga na-update.",

View file

@ -959,9 +959,8 @@
"Repeat Penalty (Ollama)": "", "Repeat Penalty (Ollama)": "",
"Reply in Thread": "", "Reply in Thread": "",
"Request Mode": "Režim žádosti", "Request Mode": "Režim žádosti",
"Reranking Engine": "",
"Reranking Model": "Model pro přehodnocení pořadí", "Reranking Model": "Model pro přehodnocení pořadí",
"Reranking model disabled": "Přeřazovací model je deaktivován",
"Reranking model set to \"{{reranking_model}}\"": "Model pro přeřazení nastaven na \"{{reranking_model}}\"",
"Reset": "režim Reset", "Reset": "režim Reset",
"Reset All Models": "", "Reset All Models": "",
"Reset Upload Directory": "Resetovat adresář nahrávání", "Reset Upload Directory": "Resetovat adresář nahrávání",
@ -1069,6 +1068,8 @@
"Show": "Zobrazit", "Show": "Zobrazit",
"Show \"What's New\" modal on login": "", "Show \"What's New\" modal on login": "",
"Show Admin Details in Account Pending Overlay": "Zobrazit podrobnosti administrátora v překryvném okně s čekajícím účtem", "Show Admin Details in Account Pending Overlay": "Zobrazit podrobnosti administrátora v překryvném okně s čekajícím účtem",
"Show All": "",
"Show Less": "",
"Show Model": "", "Show Model": "",
"Show shortcuts": "Zobrazit klávesové zkratky", "Show shortcuts": "Zobrazit klávesové zkratky",
"Show your support!": "Vyjadřete svou podporu!", "Show your support!": "Vyjadřete svou podporu!",
@ -1092,6 +1093,7 @@
"Stream Chat Response": "Odezva chatu Stream", "Stream Chat Response": "Odezva chatu Stream",
"STT Model": "Model rozpoznávání řeči na text (STT)", "STT Model": "Model rozpoznávání řeči na text (STT)",
"STT Settings": "Nastavení STT (Rozpoznávání řeči)", "STT Settings": "Nastavení STT (Rozpoznávání řeči)",
"Stylized PDF Export": "",
"Subtitle (e.g. about the Roman Empire)": "Titulky (např. o Římské říši)", "Subtitle (e.g. about the Roman Empire)": "Titulky (např. o Římské říši)",
"Success": "Úspěch", "Success": "Úspěch",
"Successfully updated.": "Úspěšně aktualizováno.", "Successfully updated.": "Úspěšně aktualizováno.",

File diff suppressed because it is too large Load diff

View file

@ -959,9 +959,8 @@
"Repeat Penalty (Ollama)": "Wiederholungsstrafe (Ollama)", "Repeat Penalty (Ollama)": "Wiederholungsstrafe (Ollama)",
"Reply in Thread": "Im Thread antworten", "Reply in Thread": "Im Thread antworten",
"Request Mode": "Anforderungsmodus", "Request Mode": "Anforderungsmodus",
"Reranking Engine": "",
"Reranking Model": "Reranking-Modell", "Reranking Model": "Reranking-Modell",
"Reranking model disabled": "Reranking-Modell deaktiviert",
"Reranking model set to \"{{reranking_model}}\"": "Reranking-Modell \"{{reranking_model}}\" fesgelegt",
"Reset": "Zurücksetzen", "Reset": "Zurücksetzen",
"Reset All Models": "Alle Modelle zurücksetzen", "Reset All Models": "Alle Modelle zurücksetzen",
"Reset Upload Directory": "Upload-Verzeichnis zurücksetzen", "Reset Upload Directory": "Upload-Verzeichnis zurücksetzen",
@ -1069,6 +1068,8 @@
"Show": "Anzeigen", "Show": "Anzeigen",
"Show \"What's New\" modal on login": "\"Was gibt's Neues\"-Modal beim Anmelden anzeigen", "Show \"What's New\" modal on login": "\"Was gibt's Neues\"-Modal beim Anmelden anzeigen",
"Show Admin Details in Account Pending Overlay": "Admin-Details im Account-Pending-Overlay anzeigen", "Show Admin Details in Account Pending Overlay": "Admin-Details im Account-Pending-Overlay anzeigen",
"Show All": "",
"Show Less": "",
"Show Model": "Modell anzeigen", "Show Model": "Modell anzeigen",
"Show shortcuts": "Verknüpfungen anzeigen", "Show shortcuts": "Verknüpfungen anzeigen",
"Show your support!": "Zeigen Sie Ihre Unterstützung!", "Show your support!": "Zeigen Sie Ihre Unterstützung!",
@ -1092,6 +1093,7 @@
"Stream Chat Response": "Chat-Antwort streamen", "Stream Chat Response": "Chat-Antwort streamen",
"STT Model": "STT-Modell", "STT Model": "STT-Modell",
"STT Settings": "STT-Einstellungen", "STT Settings": "STT-Einstellungen",
"Stylized PDF Export": "",
"Subtitle (e.g. about the Roman Empire)": "Untertitel (z. B. über das Römische Reich)", "Subtitle (e.g. about the Roman Empire)": "Untertitel (z. B. über das Römische Reich)",
"Success": "Erfolg", "Success": "Erfolg",
"Successfully updated.": "Erfolgreich aktualisiert.", "Successfully updated.": "Erfolgreich aktualisiert.",

View file

@ -959,9 +959,8 @@
"Repeat Penalty (Ollama)": "", "Repeat Penalty (Ollama)": "",
"Reply in Thread": "", "Reply in Thread": "",
"Request Mode": "Request Bark", "Request Mode": "Request Bark",
"Reranking Engine": "",
"Reranking Model": "", "Reranking Model": "",
"Reranking model disabled": "",
"Reranking model set to \"{{reranking_model}}\"": "",
"Reset": "", "Reset": "",
"Reset All Models": "", "Reset All Models": "",
"Reset Upload Directory": "", "Reset Upload Directory": "",
@ -1069,6 +1068,8 @@
"Show": "Show much show", "Show": "Show much show",
"Show \"What's New\" modal on login": "", "Show \"What's New\" modal on login": "",
"Show Admin Details in Account Pending Overlay": "", "Show Admin Details in Account Pending Overlay": "",
"Show All": "",
"Show Less": "",
"Show Model": "", "Show Model": "",
"Show shortcuts": "Show shortcuts much shortcut", "Show shortcuts": "Show shortcuts much shortcut",
"Show your support!": "", "Show your support!": "",
@ -1092,6 +1093,7 @@
"Stream Chat Response": "", "Stream Chat Response": "",
"STT Model": "", "STT Model": "",
"STT Settings": "STT Settings very settings", "STT Settings": "STT Settings very settings",
"Stylized PDF Export": "",
"Subtitle (e.g. about the Roman Empire)": "", "Subtitle (e.g. about the Roman Empire)": "",
"Success": "Success very success", "Success": "Success very success",
"Successfully updated.": "Successfully updated. Very updated.", "Successfully updated.": "Successfully updated. Very updated.",

View file

@ -959,9 +959,8 @@
"Repeat Penalty (Ollama)": "", "Repeat Penalty (Ollama)": "",
"Reply in Thread": "", "Reply in Thread": "",
"Request Mode": "Λειτουργία Αιτήματος", "Request Mode": "Λειτουργία Αιτήματος",
"Reranking Engine": "",
"Reranking Model": "Μοντέλο Επαναταξινόμησης", "Reranking Model": "Μοντέλο Επαναταξινόμησης",
"Reranking model disabled": "Το μοντέλο επαναταξινόμησης απενεργοποιήθηκε",
"Reranking model set to \"{{reranking_model}}\"": "Το μοντέλο επαναταξινόμησης ορίστηκε σε \"{{reranking_model}}\"",
"Reset": "Επαναφορά", "Reset": "Επαναφορά",
"Reset All Models": "Επαναφορά Όλων των Μοντέλων", "Reset All Models": "Επαναφορά Όλων των Μοντέλων",
"Reset Upload Directory": "Επαναφορά Καταλόγου Ανεβάσματος", "Reset Upload Directory": "Επαναφορά Καταλόγου Ανεβάσματος",
@ -1069,6 +1068,8 @@
"Show": "Εμφάνιση", "Show": "Εμφάνιση",
"Show \"What's New\" modal on login": "Εμφάνιση του παράθυρου \"Τι νέο υπάρχει\" κατά την είσοδο", "Show \"What's New\" modal on login": "Εμφάνιση του παράθυρου \"Τι νέο υπάρχει\" κατά την είσοδο",
"Show Admin Details in Account Pending Overlay": "Εμφάνιση Λεπτομερειών Διαχειριστή στο Υπέρθεση Εκκρεμής Λογαριασμού", "Show Admin Details in Account Pending Overlay": "Εμφάνιση Λεπτομερειών Διαχειριστή στο Υπέρθεση Εκκρεμής Λογαριασμού",
"Show All": "",
"Show Less": "",
"Show Model": "", "Show Model": "",
"Show shortcuts": "Εμφάνιση συντομεύσεων", "Show shortcuts": "Εμφάνιση συντομεύσεων",
"Show your support!": "Δείξτε την υποστήριξή σας!", "Show your support!": "Δείξτε την υποστήριξή σας!",
@ -1092,6 +1093,7 @@
"Stream Chat Response": "Συνομιλία Ροής Απάντησης", "Stream Chat Response": "Συνομιλία Ροής Απάντησης",
"STT Model": "Μοντέλο STT", "STT Model": "Μοντέλο STT",
"STT Settings": "Ρυθμίσεις STT", "STT Settings": "Ρυθμίσεις STT",
"Stylized PDF Export": "",
"Subtitle (e.g. about the Roman Empire)": "Υπότιτλος (π.χ. για την Ρωμαϊκή Αυτοκρατορία)", "Subtitle (e.g. about the Roman Empire)": "Υπότιτλος (π.χ. για την Ρωμαϊκή Αυτοκρατορία)",
"Success": "Επιτυχία", "Success": "Επιτυχία",
"Successfully updated.": "Επιτυχώς ενημερώθηκε.", "Successfully updated.": "Επιτυχώς ενημερώθηκε.",

View file

@ -959,9 +959,8 @@
"Repeat Penalty (Ollama)": "", "Repeat Penalty (Ollama)": "",
"Reply in Thread": "", "Reply in Thread": "",
"Request Mode": "", "Request Mode": "",
"Reranking Engine": "",
"Reranking Model": "", "Reranking Model": "",
"Reranking model disabled": "",
"Reranking model set to \"{{reranking_model}}\"": "",
"Reset": "", "Reset": "",
"Reset All Models": "", "Reset All Models": "",
"Reset Upload Directory": "", "Reset Upload Directory": "",
@ -1069,6 +1068,8 @@
"Show": "", "Show": "",
"Show \"What's New\" modal on login": "", "Show \"What's New\" modal on login": "",
"Show Admin Details in Account Pending Overlay": "", "Show Admin Details in Account Pending Overlay": "",
"Show All": "",
"Show Less": "",
"Show Model": "", "Show Model": "",
"Show shortcuts": "", "Show shortcuts": "",
"Show your support!": "", "Show your support!": "",
@ -1092,6 +1093,7 @@
"Stream Chat Response": "", "Stream Chat Response": "",
"STT Model": "", "STT Model": "",
"STT Settings": "", "STT Settings": "",
"Stylized PDF Export": "",
"Subtitle (e.g. about the Roman Empire)": "", "Subtitle (e.g. about the Roman Empire)": "",
"Success": "", "Success": "",
"Successfully updated.": "", "Successfully updated.": "",

View file

@ -959,9 +959,8 @@
"Repeat Penalty (Ollama)": "", "Repeat Penalty (Ollama)": "",
"Reply in Thread": "", "Reply in Thread": "",
"Request Mode": "", "Request Mode": "",
"Reranking Engine": "",
"Reranking Model": "", "Reranking Model": "",
"Reranking model disabled": "",
"Reranking model set to \"{{reranking_model}}\"": "",
"Reset": "", "Reset": "",
"Reset All Models": "", "Reset All Models": "",
"Reset Upload Directory": "", "Reset Upload Directory": "",
@ -1069,6 +1068,8 @@
"Show": "", "Show": "",
"Show \"What's New\" modal on login": "", "Show \"What's New\" modal on login": "",
"Show Admin Details in Account Pending Overlay": "", "Show Admin Details in Account Pending Overlay": "",
"Show All": "",
"Show Less": "",
"Show Model": "", "Show Model": "",
"Show shortcuts": "", "Show shortcuts": "",
"Show your support!": "", "Show your support!": "",
@ -1092,6 +1093,7 @@
"Stream Chat Response": "", "Stream Chat Response": "",
"STT Model": "", "STT Model": "",
"STT Settings": "", "STT Settings": "",
"Stylized PDF Export": "",
"Subtitle (e.g. about the Roman Empire)": "", "Subtitle (e.g. about the Roman Empire)": "",
"Success": "", "Success": "",
"Successfully updated.": "", "Successfully updated.": "",

View file

@ -959,9 +959,8 @@
"Repeat Penalty (Ollama)": "Penalización Repetición (Ollama)", "Repeat Penalty (Ollama)": "Penalización Repetición (Ollama)",
"Reply in Thread": "Responder en Hilo", "Reply in Thread": "Responder en Hilo",
"Request Mode": "Modo de Petición", "Request Mode": "Modo de Petición",
"Reranking Engine": "",
"Reranking Model": "Modelo de Reclasificación", "Reranking Model": "Modelo de Reclasificación",
"Reranking model disabled": "Modelo de reclasificacioń deshabilitado",
"Reranking model set to \"{{reranking_model}}\"": "Modelo de reclasificación establecido a \"{{reranking_model}}\"",
"Reset": "Reiniciar", "Reset": "Reiniciar",
"Reset All Models": "Reiniciar Todos los Modelos", "Reset All Models": "Reiniciar Todos los Modelos",
"Reset Upload Directory": "Reiniciar Directorio de Subidas", "Reset Upload Directory": "Reiniciar Directorio de Subidas",
@ -1069,6 +1068,8 @@
"Show": "Mostrar", "Show": "Mostrar",
"Show \"What's New\" modal on login": "Mostrar modal \"Qué hay de Nuevo\" al iniciar sesión", "Show \"What's New\" modal on login": "Mostrar modal \"Qué hay de Nuevo\" al iniciar sesión",
"Show Admin Details in Account Pending Overlay": "Mostrar Detalles Admin en la sobrecapa de 'Cuenta Pendiente'", "Show Admin Details in Account Pending Overlay": "Mostrar Detalles Admin en la sobrecapa de 'Cuenta Pendiente'",
"Show All": "",
"Show Less": "",
"Show Model": "Mostrar Modelo", "Show Model": "Mostrar Modelo",
"Show shortcuts": "Mostrar Atajos", "Show shortcuts": "Mostrar Atajos",
"Show your support!": "¡Muestra tu apoyo!", "Show your support!": "¡Muestra tu apoyo!",
@ -1092,6 +1093,7 @@
"Stream Chat Response": "Transmisión Directa de la Respuesta del Chat", "Stream Chat Response": "Transmisión Directa de la Respuesta del Chat",
"STT Model": "Modelo STT", "STT Model": "Modelo STT",
"STT Settings": "Ajustes Voz a Texto (STT)", "STT Settings": "Ajustes Voz a Texto (STT)",
"Stylized PDF Export": "",
"Subtitle (e.g. about the Roman Empire)": "Subtítulo (p.ej. sobre el Imperio Romano)", "Subtitle (e.g. about the Roman Empire)": "Subtítulo (p.ej. sobre el Imperio Romano)",
"Success": "Correcto", "Success": "Correcto",
"Successfully updated.": "Actualizado correctamente.", "Successfully updated.": "Actualizado correctamente.",

View file

@ -959,9 +959,8 @@
"Repeat Penalty (Ollama)": "Korduse karistus (Ollama)", "Repeat Penalty (Ollama)": "Korduse karistus (Ollama)",
"Reply in Thread": "Vasta lõimes", "Reply in Thread": "Vasta lõimes",
"Request Mode": "Päringu režiim", "Request Mode": "Päringu režiim",
"Reranking Engine": "",
"Reranking Model": "Ümberjärjestamise mudel", "Reranking Model": "Ümberjärjestamise mudel",
"Reranking model disabled": "Ümberjärjestamise mudel keelatud",
"Reranking model set to \"{{reranking_model}}\"": "Ümberjärjestamise mudel määratud kui \"{{reranking_model}}\"",
"Reset": "Lähtesta", "Reset": "Lähtesta",
"Reset All Models": "Lähtesta kõik mudelid", "Reset All Models": "Lähtesta kõik mudelid",
"Reset Upload Directory": "Lähtesta üleslaadimiste kataloog", "Reset Upload Directory": "Lähtesta üleslaadimiste kataloog",
@ -1069,6 +1068,8 @@
"Show": "Näita", "Show": "Näita",
"Show \"What's New\" modal on login": "Näita \"Mis on uut\" modaalakent sisselogimisel", "Show \"What's New\" modal on login": "Näita \"Mis on uut\" modaalakent sisselogimisel",
"Show Admin Details in Account Pending Overlay": "Näita administraatori üksikasju konto ootel kattekihil", "Show Admin Details in Account Pending Overlay": "Näita administraatori üksikasju konto ootel kattekihil",
"Show All": "",
"Show Less": "",
"Show Model": "", "Show Model": "",
"Show shortcuts": "Näita otseteid", "Show shortcuts": "Näita otseteid",
"Show your support!": "Näita oma toetust!", "Show your support!": "Näita oma toetust!",
@ -1092,6 +1093,7 @@
"Stream Chat Response": "Voogedasta vestluse vastust", "Stream Chat Response": "Voogedasta vestluse vastust",
"STT Model": "STT mudel", "STT Model": "STT mudel",
"STT Settings": "STT seaded", "STT Settings": "STT seaded",
"Stylized PDF Export": "",
"Subtitle (e.g. about the Roman Empire)": "Alampealkiri (nt Rooma impeeriumi kohta)", "Subtitle (e.g. about the Roman Empire)": "Alampealkiri (nt Rooma impeeriumi kohta)",
"Success": "Õnnestus", "Success": "Õnnestus",
"Successfully updated.": "Edukalt uuendatud.", "Successfully updated.": "Edukalt uuendatud.",

View file

@ -959,9 +959,8 @@
"Repeat Penalty (Ollama)": "", "Repeat Penalty (Ollama)": "",
"Reply in Thread": "", "Reply in Thread": "",
"Request Mode": "Eskaera modua", "Request Mode": "Eskaera modua",
"Reranking Engine": "",
"Reranking Model": "Berrantolatze modeloa", "Reranking Model": "Berrantolatze modeloa",
"Reranking model disabled": "Berrantolatze modeloa desgaituta",
"Reranking model set to \"{{reranking_model}}\"": "Berrantolatze modeloa \"{{reranking_model}}\"-era ezarrita",
"Reset": "Berrezarri", "Reset": "Berrezarri",
"Reset All Models": "", "Reset All Models": "",
"Reset Upload Directory": "Berrezarri karga direktorioa", "Reset Upload Directory": "Berrezarri karga direktorioa",
@ -1069,6 +1068,8 @@
"Show": "Erakutsi", "Show": "Erakutsi",
"Show \"What's New\" modal on login": "Erakutsi \"Berritasunak\" modala saioa hastean", "Show \"What's New\" modal on login": "Erakutsi \"Berritasunak\" modala saioa hastean",
"Show Admin Details in Account Pending Overlay": "Erakutsi administratzaile xehetasunak kontu zain geruzan", "Show Admin Details in Account Pending Overlay": "Erakutsi administratzaile xehetasunak kontu zain geruzan",
"Show All": "",
"Show Less": "",
"Show Model": "", "Show Model": "",
"Show shortcuts": "Erakutsi lasterbideak", "Show shortcuts": "Erakutsi lasterbideak",
"Show your support!": "Erakutsi zure babesa!", "Show your support!": "Erakutsi zure babesa!",
@ -1092,6 +1093,7 @@
"Stream Chat Response": "Transmititu txat erantzuna", "Stream Chat Response": "Transmititu txat erantzuna",
"STT Model": "STT modeloa", "STT Model": "STT modeloa",
"STT Settings": "STT ezarpenak", "STT Settings": "STT ezarpenak",
"Stylized PDF Export": "",
"Subtitle (e.g. about the Roman Empire)": "Azpititulua (adib. Erromatar Inperioari buruz)", "Subtitle (e.g. about the Roman Empire)": "Azpititulua (adib. Erromatar Inperioari buruz)",
"Success": "Arrakasta", "Success": "Arrakasta",
"Successfully updated.": "Ongi eguneratu da.", "Successfully updated.": "Ongi eguneratu da.",

View file

@ -959,9 +959,8 @@
"Repeat Penalty (Ollama)": "جریمه تکرار (ollama)", "Repeat Penalty (Ollama)": "جریمه تکرار (ollama)",
"Reply in Thread": "پاسخ در رشته", "Reply in Thread": "پاسخ در رشته",
"Request Mode": "حالت درخواست", "Request Mode": "حالت درخواست",
"Reranking Engine": "",
"Reranking Model": "مدل ری\u200cشناسی مجدد غیرفعال است", "Reranking Model": "مدل ری\u200cشناسی مجدد غیرفعال است",
"Reranking model disabled": "مدل ری\u200cشناسی مجدد غیرفعال است",
"Reranking model set to \"{{reranking_model}}\"": "مدل ری\u200cشناسی مجدد به \"{{reranking_model}}\" تنظیم شده است",
"Reset": "بازنشانی", "Reset": "بازنشانی",
"Reset All Models": "بازنشانی همه مدل\u200cها", "Reset All Models": "بازنشانی همه مدل\u200cها",
"Reset Upload Directory": "بازنشانی پوشه آپلود", "Reset Upload Directory": "بازنشانی پوشه آپلود",
@ -1069,6 +1068,8 @@
"Show": "نمایش", "Show": "نمایش",
"Show \"What's New\" modal on login": "نمایش مودال \"موارد جدید\" هنگام ورود", "Show \"What's New\" modal on login": "نمایش مودال \"موارد جدید\" هنگام ورود",
"Show Admin Details in Account Pending Overlay": "نمایش جزئیات مدیر در پوشش حساب در انتظار", "Show Admin Details in Account Pending Overlay": "نمایش جزئیات مدیر در پوشش حساب در انتظار",
"Show All": "",
"Show Less": "",
"Show Model": "نمایش مدل", "Show Model": "نمایش مدل",
"Show shortcuts": "نمایش میانبرها", "Show shortcuts": "نمایش میانبرها",
"Show your support!": "حمایت خود را نشان دهید!", "Show your support!": "حمایت خود را نشان دهید!",
@ -1092,6 +1093,7 @@
"Stream Chat Response": "پاسخ چت جریانی", "Stream Chat Response": "پاسخ چت جریانی",
"STT Model": "مدل تبدیل صدا به متن", "STT Model": "مدل تبدیل صدا به متن",
"STT Settings": "تنظیمات تبدیل صدا به متن", "STT Settings": "تنظیمات تبدیل صدا به متن",
"Stylized PDF Export": "",
"Subtitle (e.g. about the Roman Empire)": "زیرنویس (برای مثال: درباره رمانی)", "Subtitle (e.g. about the Roman Empire)": "زیرنویس (برای مثال: درباره رمانی)",
"Success": "موفقیت", "Success": "موفقیت",
"Successfully updated.": "با موفقیت به\u200cروز شد", "Successfully updated.": "با موفقیت به\u200cروز شد",

View file

@ -959,9 +959,8 @@
"Repeat Penalty (Ollama)": "Toisto rangaistus (Ollama)", "Repeat Penalty (Ollama)": "Toisto rangaistus (Ollama)",
"Reply in Thread": "Vastauksia ", "Reply in Thread": "Vastauksia ",
"Request Mode": "Pyyntötila", "Request Mode": "Pyyntötila",
"Reranking Engine": "",
"Reranking Model": "Uudelleenpisteytymismalli", "Reranking Model": "Uudelleenpisteytymismalli",
"Reranking model disabled": "Uudelleenpisteytymismalli poistettu käytöstä",
"Reranking model set to \"{{reranking_model}}\"": "\"{{reranking_model}}\" valittu uudelleenpisteytysmalliksi",
"Reset": "Palauta", "Reset": "Palauta",
"Reset All Models": "Palauta kaikki mallit", "Reset All Models": "Palauta kaikki mallit",
"Reset Upload Directory": "Palauta latauspolku", "Reset Upload Directory": "Palauta latauspolku",
@ -1069,6 +1068,8 @@
"Show": "Näytä", "Show": "Näytä",
"Show \"What's New\" modal on login": "Näytä \"Mitä uutta\" -modaali kirjautumisen yhteydessä", "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 Admin Details in Account Pending Overlay": "Näytä ylläpitäjän tiedot odottavan tilin päällä",
"Show All": "",
"Show Less": "",
"Show Model": "Näytä malli", "Show Model": "Näytä malli",
"Show shortcuts": "Näytä pikanäppäimet", "Show shortcuts": "Näytä pikanäppäimet",
"Show your support!": "Osoita tukesi!", "Show your support!": "Osoita tukesi!",
@ -1092,6 +1093,7 @@
"Stream Chat Response": "Streamaa keskusteluvastaus", "Stream Chat Response": "Streamaa keskusteluvastaus",
"STT Model": "Puheentunnistusmalli", "STT Model": "Puheentunnistusmalli",
"STT Settings": "Puheentunnistuksen asetukset", "STT Settings": "Puheentunnistuksen asetukset",
"Stylized PDF Export": "",
"Subtitle (e.g. about the Roman Empire)": "Alaotsikko (esim. Rooman valtakunta)", "Subtitle (e.g. about the Roman Empire)": "Alaotsikko (esim. Rooman valtakunta)",
"Success": "Onnistui", "Success": "Onnistui",
"Successfully updated.": "Päivitetty onnistuneesti.", "Successfully updated.": "Päivitetty onnistuneesti.",

View file

@ -959,9 +959,8 @@
"Repeat Penalty (Ollama)": "", "Repeat Penalty (Ollama)": "",
"Reply in Thread": "", "Reply in Thread": "",
"Request Mode": "Mode de Requête", "Request Mode": "Mode de Requête",
"Reranking Engine": "",
"Reranking Model": "Modèle de ré-ranking", "Reranking Model": "Modèle de ré-ranking",
"Reranking model disabled": "Modèle de ré-ranking désactivé",
"Reranking model set to \"{{reranking_model}}\"": "Modèle de ré-ranking défini sur « {{reranking_model}} »",
"Reset": "Réinitialiser", "Reset": "Réinitialiser",
"Reset All Models": "", "Reset All Models": "",
"Reset Upload Directory": "Répertoire de téléchargement réinitialisé", "Reset Upload Directory": "Répertoire de téléchargement réinitialisé",
@ -1069,6 +1068,8 @@
"Show": "Montrer", "Show": "Montrer",
"Show \"What's New\" modal on login": "", "Show \"What's New\" modal on login": "",
"Show Admin Details in Account Pending Overlay": "Afficher les détails de l'administrateur dans la superposition en attente du compte", "Show Admin Details in Account Pending Overlay": "Afficher les détails de l'administrateur dans la superposition en attente du compte",
"Show All": "",
"Show Less": "",
"Show Model": "", "Show Model": "",
"Show shortcuts": "Afficher les raccourcis", "Show shortcuts": "Afficher les raccourcis",
"Show your support!": "Montre ton soutien !", "Show your support!": "Montre ton soutien !",
@ -1092,6 +1093,7 @@
"Stream Chat Response": "", "Stream Chat Response": "",
"STT Model": "Modèle de STT", "STT Model": "Modèle de STT",
"STT Settings": "Paramètres de STT", "STT Settings": "Paramètres de STT",
"Stylized PDF Export": "",
"Subtitle (e.g. about the Roman Empire)": "Sous-titres (par ex. sur l'Empire romain)", "Subtitle (e.g. about the Roman Empire)": "Sous-titres (par ex. sur l'Empire romain)",
"Success": "Réussite", "Success": "Réussite",
"Successfully updated.": "Mise à jour réussie.", "Successfully updated.": "Mise à jour réussie.",

View file

@ -959,9 +959,8 @@
"Repeat Penalty (Ollama)": "Pénalité de répétition (Ollama)", "Repeat Penalty (Ollama)": "Pénalité de répétition (Ollama)",
"Reply in Thread": "Répondre dans le fil de discussion", "Reply in Thread": "Répondre dans le fil de discussion",
"Request Mode": "Mode de requête", "Request Mode": "Mode de requête",
"Reranking Engine": "",
"Reranking Model": "Modèle de ré-ranking", "Reranking Model": "Modèle de ré-ranking",
"Reranking model disabled": "Modèle de ré-ranking désactivé",
"Reranking model set to \"{{reranking_model}}\"": "Modèle de ré-ranking défini sur « {{reranking_model}} »",
"Reset": "Réinitialiser", "Reset": "Réinitialiser",
"Reset All Models": "Réinitialiser tous les modèles", "Reset All Models": "Réinitialiser tous les modèles",
"Reset Upload Directory": "Réinitialiser le répertoire de téléchargement", "Reset Upload Directory": "Réinitialiser le répertoire de téléchargement",
@ -1069,6 +1068,8 @@
"Show": "Afficher", "Show": "Afficher",
"Show \"What's New\" modal on login": "Afficher la fenêtre modale \"Quoi de neuf\" lors de la connexion", "Show \"What's New\" modal on login": "Afficher la fenêtre modale \"Quoi de neuf\" lors de la connexion",
"Show Admin Details in Account Pending Overlay": "Afficher les coordonnées de l'administrateur aux comptes en attente", "Show Admin Details in Account Pending Overlay": "Afficher les coordonnées de l'administrateur aux comptes en attente",
"Show All": "",
"Show Less": "",
"Show Model": "Afficher le model", "Show Model": "Afficher le model",
"Show shortcuts": "Afficher les raccourcis", "Show shortcuts": "Afficher les raccourcis",
"Show your support!": "Montrez votre soutien !", "Show your support!": "Montrez votre soutien !",
@ -1092,6 +1093,7 @@
"Stream Chat Response": "Streamer la réponse de la conversation", "Stream Chat Response": "Streamer la réponse de la conversation",
"STT Model": "Modèle de Speech-to-Text", "STT Model": "Modèle de Speech-to-Text",
"STT Settings": "Paramètres de Speech-to-Text", "STT Settings": "Paramètres de Speech-to-Text",
"Stylized PDF Export": "",
"Subtitle (e.g. about the Roman Empire)": "Sous-titres (par ex. sur l'Empire romain)", "Subtitle (e.g. about the Roman Empire)": "Sous-titres (par ex. sur l'Empire romain)",
"Success": "Réussite", "Success": "Réussite",
"Successfully updated.": "Mise à jour réussie.", "Successfully updated.": "Mise à jour réussie.",

View file

@ -959,9 +959,8 @@
"Repeat Penalty (Ollama)": "", "Repeat Penalty (Ollama)": "",
"Reply in Thread": "", "Reply in Thread": "",
"Request Mode": "מצב בקשה", "Request Mode": "מצב בקשה",
"Reranking Engine": "",
"Reranking Model": "מודל דירוג מחדש", "Reranking Model": "מודל דירוג מחדש",
"Reranking model disabled": "מודל דירוג מחדש מושבת",
"Reranking model set to \"{{reranking_model}}\"": "מודל דירוג מחדש הוגדר ל-\"{{reranking_model}}\"",
"Reset": "", "Reset": "",
"Reset All Models": "", "Reset All Models": "",
"Reset Upload Directory": "", "Reset Upload Directory": "",
@ -1069,6 +1068,8 @@
"Show": "הצג", "Show": "הצג",
"Show \"What's New\" modal on login": "", "Show \"What's New\" modal on login": "",
"Show Admin Details in Account Pending Overlay": "", "Show Admin Details in Account Pending Overlay": "",
"Show All": "",
"Show Less": "",
"Show Model": "", "Show Model": "",
"Show shortcuts": "הצג קיצורי דרך", "Show shortcuts": "הצג קיצורי דרך",
"Show your support!": "", "Show your support!": "",
@ -1092,6 +1093,7 @@
"Stream Chat Response": "", "Stream Chat Response": "",
"STT Model": "", "STT Model": "",
"STT Settings": "הגדרות חקירה של TTS", "STT Settings": "הגדרות חקירה של TTS",
"Stylized PDF Export": "",
"Subtitle (e.g. about the Roman Empire)": "תחקור (לדוגמה: על מעמד הרומי)", "Subtitle (e.g. about the Roman Empire)": "תחקור (לדוגמה: על מעמד הרומי)",
"Success": "הצלחה", "Success": "הצלחה",
"Successfully updated.": "עדכון הצלחה.", "Successfully updated.": "עדכון הצלחה.",

View file

@ -959,9 +959,8 @@
"Repeat Penalty (Ollama)": "", "Repeat Penalty (Ollama)": "",
"Reply in Thread": "", "Reply in Thread": "",
"Request Mode": "अनुरोध मोड", "Request Mode": "अनुरोध मोड",
"Reranking Engine": "",
"Reranking Model": "रीरैकिंग मोड", "Reranking Model": "रीरैकिंग मोड",
"Reranking model disabled": "पुनर्रैंकिंग मॉडल अक्षम किया गया",
"Reranking model set to \"{{reranking_model}}\"": "रीरैंकिंग मॉडल को \"{{reranking_model}}\" पर \u200b\u200bसेट किया गया",
"Reset": "", "Reset": "",
"Reset All Models": "", "Reset All Models": "",
"Reset Upload Directory": "", "Reset Upload Directory": "",
@ -1069,6 +1068,8 @@
"Show": "दिखाओ", "Show": "दिखाओ",
"Show \"What's New\" modal on login": "", "Show \"What's New\" modal on login": "",
"Show Admin Details in Account Pending Overlay": "", "Show Admin Details in Account Pending Overlay": "",
"Show All": "",
"Show Less": "",
"Show Model": "", "Show Model": "",
"Show shortcuts": "शॉर्टकट दिखाएँ", "Show shortcuts": "शॉर्टकट दिखाएँ",
"Show your support!": "", "Show your support!": "",
@ -1092,6 +1093,7 @@
"Stream Chat Response": "", "Stream Chat Response": "",
"STT Model": "", "STT Model": "",
"STT Settings": "STT सेटिंग्स ", "STT Settings": "STT सेटिंग्स ",
"Stylized PDF Export": "",
"Subtitle (e.g. about the Roman Empire)": "उपशीर्षक (जैसे रोमन साम्राज्य के बारे में)", "Subtitle (e.g. about the Roman Empire)": "उपशीर्षक (जैसे रोमन साम्राज्य के बारे में)",
"Success": "संपन्न", "Success": "संपन्न",
"Successfully updated.": "सफलतापूर्वक उत्परिवर्तित।", "Successfully updated.": "सफलतापूर्वक उत्परिवर्तित।",

View file

@ -959,9 +959,8 @@
"Repeat Penalty (Ollama)": "", "Repeat Penalty (Ollama)": "",
"Reply in Thread": "", "Reply in Thread": "",
"Request Mode": "Način zahtjeva", "Request Mode": "Način zahtjeva",
"Reranking Engine": "",
"Reranking Model": "Model za ponovno rangiranje", "Reranking Model": "Model za ponovno rangiranje",
"Reranking model disabled": "Model za ponovno rangiranje onemogućen",
"Reranking model set to \"{{reranking_model}}\"": "Model za ponovno rangiranje postavljen na \"{{reranking_model}}\"",
"Reset": "", "Reset": "",
"Reset All Models": "", "Reset All Models": "",
"Reset Upload Directory": "Poništi upload direktorij", "Reset Upload Directory": "Poništi upload direktorij",
@ -1069,6 +1068,8 @@
"Show": "Pokaži", "Show": "Pokaži",
"Show \"What's New\" modal on login": "", "Show \"What's New\" modal on login": "",
"Show Admin Details in Account Pending Overlay": "", "Show Admin Details in Account Pending Overlay": "",
"Show All": "",
"Show Less": "",
"Show Model": "", "Show Model": "",
"Show shortcuts": "Pokaži prečace", "Show shortcuts": "Pokaži prečace",
"Show your support!": "", "Show your support!": "",
@ -1092,6 +1093,7 @@
"Stream Chat Response": "", "Stream Chat Response": "",
"STT Model": "STT model", "STT Model": "STT model",
"STT Settings": "STT postavke", "STT Settings": "STT postavke",
"Stylized PDF Export": "",
"Subtitle (e.g. about the Roman Empire)": "Podnaslov (npr. o Rimskom carstvu)", "Subtitle (e.g. about the Roman Empire)": "Podnaslov (npr. o Rimskom carstvu)",
"Success": "Uspjeh", "Success": "Uspjeh",
"Successfully updated.": "Uspješno ažurirano.", "Successfully updated.": "Uspješno ažurirano.",

View file

@ -959,9 +959,8 @@
"Repeat Penalty (Ollama)": "Ismétlési büntetés (Ollama)", "Repeat Penalty (Ollama)": "Ismétlési büntetés (Ollama)",
"Reply in Thread": "Válasz szálban", "Reply in Thread": "Válasz szálban",
"Request Mode": "Kérési mód", "Request Mode": "Kérési mód",
"Reranking Engine": "",
"Reranking Model": "Újrarangsoroló modell", "Reranking Model": "Újrarangsoroló modell",
"Reranking model disabled": "Újrarangsoroló modell letiltva",
"Reranking model set to \"{{reranking_model}}\"": "Újrarangsoroló modell beállítva erre: \"{{reranking_model}}\"",
"Reset": "Visszaállítás", "Reset": "Visszaállítás",
"Reset All Models": "Minden modell visszaállítása", "Reset All Models": "Minden modell visszaállítása",
"Reset Upload Directory": "Feltöltési könyvtár visszaállítása", "Reset Upload Directory": "Feltöltési könyvtár visszaállítása",
@ -1069,6 +1068,8 @@
"Show": "Mutat", "Show": "Mutat",
"Show \"What's New\" modal on login": "\"Mi újság\" modal megjelenítése bejelentkezéskor", "Show \"What's New\" modal on login": "\"Mi újság\" modal megjelenítése bejelentkezéskor",
"Show Admin Details in Account Pending Overlay": "Admin részletek megjelenítése a függő fiók átfedésben", "Show Admin Details in Account Pending Overlay": "Admin részletek megjelenítése a függő fiók átfedésben",
"Show All": "",
"Show Less": "",
"Show Model": "Modell megjelenítése", "Show Model": "Modell megjelenítése",
"Show shortcuts": "Gyorsbillentyűk megjelenítése", "Show shortcuts": "Gyorsbillentyűk megjelenítése",
"Show your support!": "Mutassa meg támogatását!", "Show your support!": "Mutassa meg támogatását!",
@ -1092,6 +1093,7 @@
"Stream Chat Response": "Chat válasz streamelése", "Stream Chat Response": "Chat válasz streamelése",
"STT Model": "STT modell", "STT Model": "STT modell",
"STT Settings": "STT beállítások", "STT Settings": "STT beállítások",
"Stylized PDF Export": "",
"Subtitle (e.g. about the Roman Empire)": "Alcím (pl. a Római Birodalomról)", "Subtitle (e.g. about the Roman Empire)": "Alcím (pl. a Római Birodalomról)",
"Success": "Siker", "Success": "Siker",
"Successfully updated.": "Sikeresen frissítve.", "Successfully updated.": "Sikeresen frissítve.",

View file

@ -959,9 +959,8 @@
"Repeat Penalty (Ollama)": "", "Repeat Penalty (Ollama)": "",
"Reply in Thread": "", "Reply in Thread": "",
"Request Mode": "Mode Permintaan", "Request Mode": "Mode Permintaan",
"Reranking Engine": "",
"Reranking Model": "Model Pemeringkatan Ulang", "Reranking Model": "Model Pemeringkatan Ulang",
"Reranking model disabled": "Model pemeringkatan ulang dinonaktifkan",
"Reranking model set to \"{{reranking_model}}\"": "Model pemeringkatan diatur ke \"{{reranking_model}}\"",
"Reset": "Atur Ulang", "Reset": "Atur Ulang",
"Reset All Models": "", "Reset All Models": "",
"Reset Upload Directory": "Setel Ulang Direktori Unggahan", "Reset Upload Directory": "Setel Ulang Direktori Unggahan",
@ -1069,6 +1068,8 @@
"Show": "Tampilkan", "Show": "Tampilkan",
"Show \"What's New\" modal on login": "", "Show \"What's New\" modal on login": "",
"Show Admin Details in Account Pending Overlay": "Tampilkan Detail Admin di Hamparan Akun Tertunda", "Show Admin Details in Account Pending Overlay": "Tampilkan Detail Admin di Hamparan Akun Tertunda",
"Show All": "",
"Show Less": "",
"Show Model": "", "Show Model": "",
"Show shortcuts": "Tampilkan pintasan", "Show shortcuts": "Tampilkan pintasan",
"Show your support!": "Tunjukkan dukungan Anda!", "Show your support!": "Tunjukkan dukungan Anda!",
@ -1092,6 +1093,7 @@
"Stream Chat Response": "", "Stream Chat Response": "",
"STT Model": "Model STT", "STT Model": "Model STT",
"STT Settings": "Pengaturan STT", "STT Settings": "Pengaturan STT",
"Stylized PDF Export": "",
"Subtitle (e.g. about the Roman Empire)": "Subtitle (misalnya tentang Kekaisaran Romawi)", "Subtitle (e.g. about the Roman Empire)": "Subtitle (misalnya tentang Kekaisaran Romawi)",
"Success": "Berhasil", "Success": "Berhasil",
"Successfully updated.": "Berhasil diperbarui.", "Successfully updated.": "Berhasil diperbarui.",

View file

@ -959,9 +959,8 @@
"Repeat Penalty (Ollama)": "Pionós Athrá (Ollama)", "Repeat Penalty (Ollama)": "Pionós Athrá (Ollama)",
"Reply in Thread": "Freagra i Snáithe", "Reply in Thread": "Freagra i Snáithe",
"Request Mode": "Mód Iarratais", "Request Mode": "Mód Iarratais",
"Reranking Engine": "",
"Reranking Model": "Múnla Athrangú", "Reranking Model": "Múnla Athrangú",
"Reranking model disabled": "Samhail athrangú faoi mhíchumas",
"Reranking model set to \"{{reranking_model}}\"": "Samhail athrangú socraithe go \"{{reranking_model}}\"",
"Reset": "Athshocraigh", "Reset": "Athshocraigh",
"Reset All Models": "Athshocraigh Gach Múnla", "Reset All Models": "Athshocraigh Gach Múnla",
"Reset Upload Directory": "Athshocraigh Eolaire Uas", "Reset Upload Directory": "Athshocraigh Eolaire Uas",
@ -1069,6 +1068,8 @@
"Show": "Taispeáin", "Show": "Taispeáin",
"Show \"What's New\" modal on login": "Taispeáin módúil \"Cad atá Nua\" ar logáil isteach", "Show \"What's New\" modal on login": "Taispeáin módúil \"Cad atá Nua\" ar logáil isteach",
"Show Admin Details in Account Pending Overlay": "Taispeáin Sonraí Riaracháin sa Chuntas ar Feitheamh Forleagan", "Show Admin Details in Account Pending Overlay": "Taispeáin Sonraí Riaracháin sa Chuntas ar Feitheamh Forleagan",
"Show All": "",
"Show Less": "",
"Show Model": "Taispeáin Múnla", "Show Model": "Taispeáin Múnla",
"Show shortcuts": "Taispeáin aicearraí", "Show shortcuts": "Taispeáin aicearraí",
"Show your support!": "Taispeáin do thacaíocht!", "Show your support!": "Taispeáin do thacaíocht!",
@ -1092,6 +1093,7 @@
"Stream Chat Response": "Freagra Comhrá Sruth", "Stream Chat Response": "Freagra Comhrá Sruth",
"STT Model": "Múnla STT", "STT Model": "Múnla STT",
"STT Settings": "Socruithe STT", "STT Settings": "Socruithe STT",
"Stylized PDF Export": "",
"Subtitle (e.g. about the Roman Empire)": "Fotheideal (m.sh. faoin Impireacht Rómhánach)", "Subtitle (e.g. about the Roman Empire)": "Fotheideal (m.sh. faoin Impireacht Rómhánach)",
"Success": "Rath", "Success": "Rath",
"Successfully updated.": "Nuashonraithe go rathúil.", "Successfully updated.": "Nuashonraithe go rathúil.",

File diff suppressed because it is too large Load diff

View file

@ -959,9 +959,8 @@
"Repeat Penalty (Ollama)": "", "Repeat Penalty (Ollama)": "",
"Reply in Thread": "", "Reply in Thread": "",
"Request Mode": "リクエストモード", "Request Mode": "リクエストモード",
"Reranking Engine": "",
"Reranking Model": "モデルの再ランキング", "Reranking Model": "モデルの再ランキング",
"Reranking model disabled": "再ランキングモデルが無効です",
"Reranking model set to \"{{reranking_model}}\"": "再ランキングモデルを \"{{reranking_model}}\" に設定しました",
"Reset": "", "Reset": "",
"Reset All Models": "", "Reset All Models": "",
"Reset Upload Directory": "アップロードディレクトリをリセット", "Reset Upload Directory": "アップロードディレクトリをリセット",
@ -1069,6 +1068,8 @@
"Show": "表示", "Show": "表示",
"Show \"What's New\" modal on login": "", "Show \"What's New\" modal on login": "",
"Show Admin Details in Account Pending Overlay": "", "Show Admin Details in Account Pending Overlay": "",
"Show All": "",
"Show Less": "",
"Show Model": "", "Show Model": "",
"Show shortcuts": "表示", "Show shortcuts": "表示",
"Show your support!": "", "Show your support!": "",
@ -1092,6 +1093,7 @@
"Stream Chat Response": "", "Stream Chat Response": "",
"STT Model": "STTモデル", "STT Model": "STTモデル",
"STT Settings": "STT設定", "STT Settings": "STT設定",
"Stylized PDF Export": "",
"Subtitle (e.g. about the Roman Empire)": "タイトル (例: ローマ帝国)", "Subtitle (e.g. about the Roman Empire)": "タイトル (例: ローマ帝国)",
"Success": "成功", "Success": "成功",
"Successfully updated.": "正常に更新されました。", "Successfully updated.": "正常に更新されました。",

View file

@ -959,9 +959,8 @@
"Repeat Penalty (Ollama)": "", "Repeat Penalty (Ollama)": "",
"Reply in Thread": "ნაკადში პასუხი", "Reply in Thread": "ნაკადში პასუხი",
"Request Mode": "მოთხოვნის რეჟიმი", "Request Mode": "მოთხოვნის რეჟიმი",
"Reranking Engine": "",
"Reranking Model": "Reranking მოდელი", "Reranking Model": "Reranking მოდელი",
"Reranking model disabled": "Reranking მოდელი გათიშულია",
"Reranking model set to \"{{reranking_model}}\"": "Reranking model set to \"{{reranking_model}}\"",
"Reset": "ჩამოყრა", "Reset": "ჩამოყრა",
"Reset All Models": "", "Reset All Models": "",
"Reset Upload Directory": "", "Reset Upload Directory": "",
@ -1069,6 +1068,8 @@
"Show": "ჩვენება", "Show": "ჩვენება",
"Show \"What's New\" modal on login": "", "Show \"What's New\" modal on login": "",
"Show Admin Details in Account Pending Overlay": "", "Show Admin Details in Account Pending Overlay": "",
"Show All": "",
"Show Less": "",
"Show Model": "", "Show Model": "",
"Show shortcuts": "მალსახმობების ჩვენება", "Show shortcuts": "მალსახმობების ჩვენება",
"Show your support!": "", "Show your support!": "",
@ -1092,6 +1093,7 @@
"Stream Chat Response": "", "Stream Chat Response": "",
"STT Model": "", "STT Model": "",
"STT Settings": "STT-ის მორგება", "STT Settings": "STT-ის მორგება",
"Stylized PDF Export": "",
"Subtitle (e.g. about the Roman Empire)": "სუბტიტრები (მაგ. რომის იმპერიის შესახებ)", "Subtitle (e.g. about the Roman Empire)": "სუბტიტრები (მაგ. რომის იმპერიის შესახებ)",
"Success": "წარმატება", "Success": "წარმატება",
"Successfully updated.": "წარმატებით განახლდა.", "Successfully updated.": "წარმატებით განახლდა.",

View file

@ -959,9 +959,8 @@
"Repeat Penalty (Ollama)": "", "Repeat Penalty (Ollama)": "",
"Reply in Thread": "스레드에 답글 달기", "Reply in Thread": "스레드에 답글 달기",
"Request Mode": "요청 모드", "Request Mode": "요청 모드",
"Reranking Engine": "",
"Reranking Model": "Reranking 모델", "Reranking Model": "Reranking 모델",
"Reranking model disabled": "Reranking 모델 비활성화",
"Reranking model set to \"{{reranking_model}}\"": "Reranking 모델을 \"{{reranking_model}}\"로 설정",
"Reset": "초기화", "Reset": "초기화",
"Reset All Models": "모든 모델 초기화", "Reset All Models": "모든 모델 초기화",
"Reset Upload Directory": "업로드 디렉토리 초기화", "Reset Upload Directory": "업로드 디렉토리 초기화",
@ -1069,6 +1068,8 @@
"Show": "보기", "Show": "보기",
"Show \"What's New\" modal on login": "로그인시 \"새로운 기능\" 모달 보기", "Show \"What's New\" modal on login": "로그인시 \"새로운 기능\" 모달 보기",
"Show Admin Details in Account Pending Overlay": "사용자용 계정 보류 설명창에, 관리자 상세 정보 노출", "Show Admin Details in Account Pending Overlay": "사용자용 계정 보류 설명창에, 관리자 상세 정보 노출",
"Show All": "",
"Show Less": "",
"Show Model": "", "Show Model": "",
"Show shortcuts": "단축키 보기", "Show shortcuts": "단축키 보기",
"Show your support!": "당신의 응원을 보내주세요!", "Show your support!": "당신의 응원을 보내주세요!",
@ -1092,6 +1093,7 @@
"Stream Chat Response": "스트림 채팅 응답", "Stream Chat Response": "스트림 채팅 응답",
"STT Model": "STT 모델", "STT Model": "STT 모델",
"STT Settings": "STT 설정", "STT Settings": "STT 설정",
"Stylized PDF Export": "",
"Subtitle (e.g. about the Roman Empire)": "자막 (예: 로마 황제)", "Subtitle (e.g. about the Roman Empire)": "자막 (예: 로마 황제)",
"Success": "성공", "Success": "성공",
"Successfully updated.": "성공적으로 업데이트되었습니다.", "Successfully updated.": "성공적으로 업데이트되었습니다.",

View file

@ -959,9 +959,8 @@
"Repeat Penalty (Ollama)": "", "Repeat Penalty (Ollama)": "",
"Reply in Thread": "", "Reply in Thread": "",
"Request Mode": "Užklausos rėžimas", "Request Mode": "Užklausos rėžimas",
"Reranking Engine": "",
"Reranking Model": "Reranking modelis", "Reranking Model": "Reranking modelis",
"Reranking model disabled": "Reranking modelis neleidžiamas",
"Reranking model set to \"{{reranking_model}}\"": "Nustatytas rereanking modelis: \"{{reranking_model}}\"",
"Reset": "Atkurti", "Reset": "Atkurti",
"Reset All Models": "", "Reset All Models": "",
"Reset Upload Directory": "Atkurti įkėlimų direktoiją", "Reset Upload Directory": "Atkurti įkėlimų direktoiją",
@ -1069,6 +1068,8 @@
"Show": "Rodyti", "Show": "Rodyti",
"Show \"What's New\" modal on login": "", "Show \"What's New\" modal on login": "",
"Show Admin Details in Account Pending Overlay": "Rodyti administratoriaus duomenis laukiant paskyros patvirtinimo", "Show Admin Details in Account Pending Overlay": "Rodyti administratoriaus duomenis laukiant paskyros patvirtinimo",
"Show All": "",
"Show Less": "",
"Show Model": "", "Show Model": "",
"Show shortcuts": "Rodyti trumpinius", "Show shortcuts": "Rodyti trumpinius",
"Show your support!": "Palaikykite", "Show your support!": "Palaikykite",
@ -1092,6 +1093,7 @@
"Stream Chat Response": "", "Stream Chat Response": "",
"STT Model": "STT modelis", "STT Model": "STT modelis",
"STT Settings": "STT nustatymai", "STT Settings": "STT nustatymai",
"Stylized PDF Export": "",
"Subtitle (e.g. about the Roman Empire)": "Subtitras", "Subtitle (e.g. about the Roman Empire)": "Subtitras",
"Success": "Sėkmingai", "Success": "Sėkmingai",
"Successfully updated.": "Sėkmingai atnaujinta.", "Successfully updated.": "Sėkmingai atnaujinta.",

View file

@ -959,9 +959,8 @@
"Repeat Penalty (Ollama)": "", "Repeat Penalty (Ollama)": "",
"Reply in Thread": "", "Reply in Thread": "",
"Request Mode": "Mod Permintaan", "Request Mode": "Mod Permintaan",
"Reranking Engine": "",
"Reranking Model": "Model 'Reranking'", "Reranking Model": "Model 'Reranking'",
"Reranking model disabled": "Model 'Reranking' dilumpuhkan",
"Reranking model set to \"{{reranking_model}}\"": "Model 'Reranking' ditetapkan kepada \"{{reranking_model}}\"",
"Reset": "Tetapkan Semula", "Reset": "Tetapkan Semula",
"Reset All Models": "", "Reset All Models": "",
"Reset Upload Directory": "Tetapkan Semula Direktori Muat Naik", "Reset Upload Directory": "Tetapkan Semula Direktori Muat Naik",
@ -1069,6 +1068,8 @@
"Show": "Tunjukkan", "Show": "Tunjukkan",
"Show \"What's New\" modal on login": "", "Show \"What's New\" modal on login": "",
"Show Admin Details in Account Pending Overlay": "Tunjukkan Butiran Pentadbir dalam Akaun Menunggu Tindanan", "Show Admin Details in Account Pending Overlay": "Tunjukkan Butiran Pentadbir dalam Akaun Menunggu Tindanan",
"Show All": "",
"Show Less": "",
"Show Model": "", "Show Model": "",
"Show shortcuts": "Tunjukkan pintasan", "Show shortcuts": "Tunjukkan pintasan",
"Show your support!": "Tunjukkan sokongan anda!", "Show your support!": "Tunjukkan sokongan anda!",
@ -1092,6 +1093,7 @@
"Stream Chat Response": "", "Stream Chat Response": "",
"STT Model": "Model STT", "STT Model": "Model STT",
"STT Settings": "Tetapan STT", "STT Settings": "Tetapan STT",
"Stylized PDF Export": "",
"Subtitle (e.g. about the Roman Empire)": "Sari kata (cth tentang Kesultanan Melaka)", "Subtitle (e.g. about the Roman Empire)": "Sari kata (cth tentang Kesultanan Melaka)",
"Success": "Berjaya", "Success": "Berjaya",
"Successfully updated.": "Berjaya Dikemaskini", "Successfully updated.": "Berjaya Dikemaskini",

View file

@ -959,9 +959,8 @@
"Repeat Penalty (Ollama)": "Gjenta straff (Ollama)", "Repeat Penalty (Ollama)": "Gjenta straff (Ollama)",
"Reply in Thread": "Svar i tråd", "Reply in Thread": "Svar i tråd",
"Request Mode": "Forespørselsmodus", "Request Mode": "Forespørselsmodus",
"Reranking Engine": "",
"Reranking Model": "Omrangeringsmodell", "Reranking Model": "Omrangeringsmodell",
"Reranking model disabled": "Omrangeringsmodell deaktivert",
"Reranking model set to \"{{reranking_model}}\"": "Omrangeringsmodell er angitt til \"{{reranking_model}}\"",
"Reset": "Tilbakestill", "Reset": "Tilbakestill",
"Reset All Models": "Tilbakestill alle modeller", "Reset All Models": "Tilbakestill alle modeller",
"Reset Upload Directory": "Tilbakestill opplastingskatalog", "Reset Upload Directory": "Tilbakestill opplastingskatalog",
@ -1069,6 +1068,8 @@
"Show": "Vis", "Show": "Vis",
"Show \"What's New\" modal on login": "Vis \"Hva er nytt\"-modal ved innlogging", "Show \"What's New\" modal on login": "Vis \"Hva er nytt\"-modal ved innlogging",
"Show Admin Details in Account Pending Overlay": "Vis administratordetaljer i ventende kontovisning", "Show Admin Details in Account Pending Overlay": "Vis administratordetaljer i ventende kontovisning",
"Show All": "",
"Show Less": "",
"Show Model": "", "Show Model": "",
"Show shortcuts": "Vis snarveier", "Show shortcuts": "Vis snarveier",
"Show your support!": "Vis din støtte!", "Show your support!": "Vis din støtte!",
@ -1092,6 +1093,7 @@
"Stream Chat Response": "Strømme chat-svar", "Stream Chat Response": "Strømme chat-svar",
"STT Model": "STT-modell", "STT Model": "STT-modell",
"STT Settings": "STT-innstillinger", "STT Settings": "STT-innstillinger",
"Stylized PDF Export": "",
"Subtitle (e.g. about the Roman Empire)": "Undertittel (f.eks. om romerriket)", "Subtitle (e.g. about the Roman Empire)": "Undertittel (f.eks. om romerriket)",
"Success": "Suksess", "Success": "Suksess",
"Successfully updated.": "Oppdatert.", "Successfully updated.": "Oppdatert.",

View file

@ -959,9 +959,8 @@
"Repeat Penalty (Ollama)": "Herhalingsstraf (Ollama)", "Repeat Penalty (Ollama)": "Herhalingsstraf (Ollama)",
"Reply in Thread": "Antwoord in draad", "Reply in Thread": "Antwoord in draad",
"Request Mode": "Request Modus", "Request Mode": "Request Modus",
"Reranking Engine": "",
"Reranking Model": "Reranking Model", "Reranking Model": "Reranking Model",
"Reranking model disabled": "Reranking model uitgeschakeld",
"Reranking model set to \"{{reranking_model}}\"": "Reranking model ingesteld op \"{{reranking_model}}\"",
"Reset": "Herstellen", "Reset": "Herstellen",
"Reset All Models": "Herstel alle modellen", "Reset All Models": "Herstel alle modellen",
"Reset Upload Directory": "Herstel Uploadmap", "Reset Upload Directory": "Herstel Uploadmap",
@ -1069,6 +1068,8 @@
"Show": "Toon", "Show": "Toon",
"Show \"What's New\" modal on login": "Toon \"Wat is nieuw\" bij inloggen", "Show \"What's New\" modal on login": "Toon \"Wat is nieuw\" bij inloggen",
"Show Admin Details in Account Pending Overlay": "Admin-details weergeven in overlay in afwachting van account", "Show Admin Details in Account Pending Overlay": "Admin-details weergeven in overlay in afwachting van account",
"Show All": "",
"Show Less": "",
"Show Model": "Toon model", "Show Model": "Toon model",
"Show shortcuts": "Toon snelkoppelingen", "Show shortcuts": "Toon snelkoppelingen",
"Show your support!": "Toon je steun", "Show your support!": "Toon je steun",
@ -1092,6 +1093,7 @@
"Stream Chat Response": "Stream chat-antwoord", "Stream Chat Response": "Stream chat-antwoord",
"STT Model": "STT Model", "STT Model": "STT Model",
"STT Settings": "STT Instellingen", "STT Settings": "STT Instellingen",
"Stylized PDF Export": "",
"Subtitle (e.g. about the Roman Empire)": "Ondertitel (bijv. over de Romeinse Empire)", "Subtitle (e.g. about the Roman Empire)": "Ondertitel (bijv. over de Romeinse Empire)",
"Success": "Succes", "Success": "Succes",
"Successfully updated.": "Succesvol bijgewerkt.", "Successfully updated.": "Succesvol bijgewerkt.",

View file

@ -959,9 +959,8 @@
"Repeat Penalty (Ollama)": "", "Repeat Penalty (Ollama)": "",
"Reply in Thread": "", "Reply in Thread": "",
"Request Mode": "ਬੇਨਤੀ ਮੋਡ", "Request Mode": "ਬੇਨਤੀ ਮੋਡ",
"Reranking Engine": "",
"Reranking Model": "ਮਾਡਲ ਮੁੜ ਰੈਂਕਿੰਗ", "Reranking Model": "ਮਾਡਲ ਮੁੜ ਰੈਂਕਿੰਗ",
"Reranking model disabled": "ਮਾਡਲ ਮੁੜ ਰੈਂਕਿੰਗ ਅਯੋਗ ਕੀਤਾ ਗਿਆ",
"Reranking model set to \"{{reranking_model}}\"": "ਮਾਡਲ ਮੁੜ ਰੈਂਕਿੰਗ ਨੂੰ \"{{reranking_model}}\" 'ਤੇ ਸੈੱਟ ਕੀਤਾ ਗਿਆ",
"Reset": "", "Reset": "",
"Reset All Models": "", "Reset All Models": "",
"Reset Upload Directory": "", "Reset Upload Directory": "",
@ -1069,6 +1068,8 @@
"Show": "ਦਿਖਾਓ", "Show": "ਦਿਖਾਓ",
"Show \"What's New\" modal on login": "", "Show \"What's New\" modal on login": "",
"Show Admin Details in Account Pending Overlay": "", "Show Admin Details in Account Pending Overlay": "",
"Show All": "",
"Show Less": "",
"Show Model": "", "Show Model": "",
"Show shortcuts": "ਸ਼ਾਰਟਕਟ ਦਿਖਾਓ", "Show shortcuts": "ਸ਼ਾਰਟਕਟ ਦਿਖਾਓ",
"Show your support!": "", "Show your support!": "",
@ -1092,6 +1093,7 @@
"Stream Chat Response": "", "Stream Chat Response": "",
"STT Model": "", "STT Model": "",
"STT Settings": "STT ਸੈਟਿੰਗਾਂ", "STT Settings": "STT ਸੈਟਿੰਗਾਂ",
"Stylized PDF Export": "",
"Subtitle (e.g. about the Roman Empire)": "ਉਪਸਿਰਲੇਖ (ਉਦਾਹਰਣ ਲਈ ਰੋਮਨ ਸਾਮਰਾਜ ਬਾਰੇ)", "Subtitle (e.g. about the Roman Empire)": "ਉਪਸਿਰਲੇਖ (ਉਦਾਹਰਣ ਲਈ ਰੋਮਨ ਸਾਮਰਾਜ ਬਾਰੇ)",
"Success": "ਸਫਲਤਾ", "Success": "ਸਫਲਤਾ",
"Successfully updated.": "ਸਫਲਤਾਪੂਰਵਕ ਅੱਪਡੇਟ ਕੀਤਾ ਗਿਆ।", "Successfully updated.": "ਸਫਲਤਾਪੂਰਵਕ ਅੱਪਡੇਟ ਕੀਤਾ ਗਿਆ।",

View file

@ -959,9 +959,8 @@
"Repeat Penalty (Ollama)": "Powtórzona kara (Ollama)", "Repeat Penalty (Ollama)": "Powtórzona kara (Ollama)",
"Reply in Thread": "Odpowiedz w wątku", "Reply in Thread": "Odpowiedz w wątku",
"Request Mode": "Tryb żądania", "Request Mode": "Tryb żądania",
"Reranking Engine": "",
"Reranking Model": "Poprawa rankingu modelu", "Reranking Model": "Poprawa rankingu modelu",
"Reranking model disabled": "Ponowny ranking modeli wyłączony",
"Reranking model set to \"{{reranking_model}}\"": "Ponowny ranking modeli ustawiony na \"{{reranking_model}}\".",
"Reset": "Resetuj", "Reset": "Resetuj",
"Reset All Models": "Resetuj wszystkie modele", "Reset All Models": "Resetuj wszystkie modele",
"Reset Upload Directory": "Resetuj katalog pobierania", "Reset Upload Directory": "Resetuj katalog pobierania",
@ -1069,6 +1068,8 @@
"Show": "Wyświetl", "Show": "Wyświetl",
"Show \"What's New\" modal on login": "Wyświetl okno dialogowe \"What's New\" podczas logowania", "Show \"What's New\" modal on login": "Wyświetl okno dialogowe \"What's New\" podczas logowania",
"Show Admin Details in Account Pending Overlay": "Wyświetl szczegóły administratora w okienu informacyjnym o potrzebie zatwierdzenia przez administratora konta użytkownika", "Show Admin Details in Account Pending Overlay": "Wyświetl szczegóły administratora w okienu informacyjnym o potrzebie zatwierdzenia przez administratora konta użytkownika",
"Show All": "",
"Show Less": "",
"Show Model": "", "Show Model": "",
"Show shortcuts": "Wyświetl skróty", "Show shortcuts": "Wyświetl skróty",
"Show your support!": "Wyraź swoje poparcie!", "Show your support!": "Wyraź swoje poparcie!",
@ -1092,6 +1093,7 @@
"Stream Chat Response": "Strumieniowanie odpowiedzi z czatu", "Stream Chat Response": "Strumieniowanie odpowiedzi z czatu",
"STT Model": "Model STT", "STT Model": "Model STT",
"STT Settings": "Ustawienia STT", "STT Settings": "Ustawienia STT",
"Stylized PDF Export": "",
"Subtitle (e.g. about the Roman Empire)": "Podtytuł (np. o Imperium Rzymskim)", "Subtitle (e.g. about the Roman Empire)": "Podtytuł (np. o Imperium Rzymskim)",
"Success": "Sukces", "Success": "Sukces",
"Successfully updated.": "Uaktualniono pomyślnie.", "Successfully updated.": "Uaktualniono pomyślnie.",

View file

@ -959,9 +959,8 @@
"Repeat Penalty (Ollama)": "", "Repeat Penalty (Ollama)": "",
"Reply in Thread": "", "Reply in Thread": "",
"Request Mode": "Modo de Solicitação", "Request Mode": "Modo de Solicitação",
"Reranking Engine": "",
"Reranking Model": "Modelo de Reclassificação", "Reranking Model": "Modelo de Reclassificação",
"Reranking model disabled": "Modelo de Reclassificação desativado",
"Reranking model set to \"{{reranking_model}}\"": "Modelo de Reclassificação definido como \"{{reranking_model}}\"",
"Reset": "Redefinir", "Reset": "Redefinir",
"Reset All Models": "", "Reset All Models": "",
"Reset Upload Directory": "Redefinir Diretório de Upload", "Reset Upload Directory": "Redefinir Diretório de Upload",
@ -1069,6 +1068,8 @@
"Show": "Mostrar", "Show": "Mostrar",
"Show \"What's New\" modal on login": "Mostrar \"O que há de Novo\" no login", "Show \"What's New\" modal on login": "Mostrar \"O que há de Novo\" no login",
"Show Admin Details in Account Pending Overlay": "Mostrar Detalhes do Administrador na Sobreposição de Conta Pendentes", "Show Admin Details in Account Pending Overlay": "Mostrar Detalhes do Administrador na Sobreposição de Conta Pendentes",
"Show All": "",
"Show Less": "",
"Show Model": "", "Show Model": "",
"Show shortcuts": "Mostrar atalhos", "Show shortcuts": "Mostrar atalhos",
"Show your support!": "Mostre seu apoio!", "Show your support!": "Mostre seu apoio!",
@ -1092,6 +1093,7 @@
"Stream Chat Response": "Stream Resposta do Chat", "Stream Chat Response": "Stream Resposta do Chat",
"STT Model": "Modelo STT", "STT Model": "Modelo STT",
"STT Settings": "Configurações STT", "STT Settings": "Configurações STT",
"Stylized PDF Export": "",
"Subtitle (e.g. about the Roman Empire)": "Subtítulo (por exemplo, sobre o Império Romano)", "Subtitle (e.g. about the Roman Empire)": "Subtítulo (por exemplo, sobre o Império Romano)",
"Success": "Sucesso", "Success": "Sucesso",
"Successfully updated.": "Atualizado com sucesso.", "Successfully updated.": "Atualizado com sucesso.",

View file

@ -959,9 +959,8 @@
"Repeat Penalty (Ollama)": "", "Repeat Penalty (Ollama)": "",
"Reply in Thread": "", "Reply in Thread": "",
"Request Mode": "Modo de Pedido", "Request Mode": "Modo de Pedido",
"Reranking Engine": "",
"Reranking Model": "Modelo de Reranking", "Reranking Model": "Modelo de Reranking",
"Reranking model disabled": "Modelo de Reranking desativado",
"Reranking model set to \"{{reranking_model}}\"": "Modelo de Reranking definido como \"{{reranking_model}}\"",
"Reset": "", "Reset": "",
"Reset All Models": "", "Reset All Models": "",
"Reset Upload Directory": "Limpar Pasta de Carregamento", "Reset Upload Directory": "Limpar Pasta de Carregamento",
@ -1069,6 +1068,8 @@
"Show": "Mostrar", "Show": "Mostrar",
"Show \"What's New\" modal on login": "", "Show \"What's New\" modal on login": "",
"Show Admin Details in Account Pending Overlay": "Mostrar Detalhes do Administrador na sobreposição de Conta Pendente", "Show Admin Details in Account Pending Overlay": "Mostrar Detalhes do Administrador na sobreposição de Conta Pendente",
"Show All": "",
"Show Less": "",
"Show Model": "", "Show Model": "",
"Show shortcuts": "Mostrar atalhos", "Show shortcuts": "Mostrar atalhos",
"Show your support!": "", "Show your support!": "",
@ -1092,6 +1093,7 @@
"Stream Chat Response": "", "Stream Chat Response": "",
"STT Model": "Modelo STT", "STT Model": "Modelo STT",
"STT Settings": "Configurações STT", "STT Settings": "Configurações STT",
"Stylized PDF Export": "",
"Subtitle (e.g. about the Roman Empire)": "Subtítulo (ex.: sobre o Império Romano)", "Subtitle (e.g. about the Roman Empire)": "Subtítulo (ex.: sobre o Império Romano)",
"Success": "Sucesso", "Success": "Sucesso",
"Successfully updated.": "Atualizado com sucesso.", "Successfully updated.": "Atualizado com sucesso.",

View file

@ -959,9 +959,8 @@
"Repeat Penalty (Ollama)": "", "Repeat Penalty (Ollama)": "",
"Reply in Thread": "", "Reply in Thread": "",
"Request Mode": "Mod de Cerere", "Request Mode": "Mod de Cerere",
"Reranking Engine": "",
"Reranking Model": "Model de Rearanjare", "Reranking Model": "Model de Rearanjare",
"Reranking model disabled": "Modelul de Rearanjare este dezactivat",
"Reranking model set to \"{{reranking_model}}\"": "Modelul de Rearanjare setat la \"{{reranking_model}}\"",
"Reset": "Resetează", "Reset": "Resetează",
"Reset All Models": "", "Reset All Models": "",
"Reset Upload Directory": "Resetează Directorul de Încărcare", "Reset Upload Directory": "Resetează Directorul de Încărcare",
@ -1069,6 +1068,8 @@
"Show": "Afișează", "Show": "Afișează",
"Show \"What's New\" modal on login": "", "Show \"What's New\" modal on login": "",
"Show Admin Details in Account Pending Overlay": "Afișează Detaliile Administratorului în Suprapunerea Contului În Așteptare", "Show Admin Details in Account Pending Overlay": "Afișează Detaliile Administratorului în Suprapunerea Contului În Așteptare",
"Show All": "",
"Show Less": "",
"Show Model": "", "Show Model": "",
"Show shortcuts": "Afișează scurtături", "Show shortcuts": "Afișează scurtături",
"Show your support!": "Arată-ți susținerea!", "Show your support!": "Arată-ți susținerea!",
@ -1092,6 +1093,7 @@
"Stream Chat Response": "Răspuns Stream Chat", "Stream Chat Response": "Răspuns Stream Chat",
"STT Model": "Model STT", "STT Model": "Model STT",
"STT Settings": "Setări STT", "STT Settings": "Setări STT",
"Stylized PDF Export": "",
"Subtitle (e.g. about the Roman Empire)": "Subtitlu (de ex. despre Imperiul Roman)", "Subtitle (e.g. about the Roman Empire)": "Subtitlu (de ex. despre Imperiul Roman)",
"Success": "Succes", "Success": "Succes",
"Successfully updated.": "Actualizat cu succes.", "Successfully updated.": "Actualizat cu succes.",

View file

@ -959,9 +959,8 @@
"Repeat Penalty (Ollama)": "Повторить наказание (Ollama)", "Repeat Penalty (Ollama)": "Повторить наказание (Ollama)",
"Reply in Thread": "Ответить в обсуждении", "Reply in Thread": "Ответить в обсуждении",
"Request Mode": "Режим запроса", "Request Mode": "Режим запроса",
"Reranking Engine": "",
"Reranking Model": "Модель реранжирования", "Reranking Model": "Модель реранжирования",
"Reranking model disabled": "Модель реранжирования отключена",
"Reranking model set to \"{{reranking_model}}\"": "Модель реранжирования установлена на \"{{reranking_model}}\"",
"Reset": "Сбросить", "Reset": "Сбросить",
"Reset All Models": "Сбросить все модели", "Reset All Models": "Сбросить все модели",
"Reset Upload Directory": "Сбросить каталог загрузок", "Reset Upload Directory": "Сбросить каталог загрузок",
@ -1069,6 +1068,8 @@
"Show": "Показать", "Show": "Показать",
"Show \"What's New\" modal on login": "Показывать окно «Что нового» при входе в систему", "Show \"What's New\" modal on login": "Показывать окно «Что нового» при входе в систему",
"Show Admin Details in Account Pending Overlay": "Показывать данные администратора в оверлее ожидающей учетной записи", "Show Admin Details in Account Pending Overlay": "Показывать данные администратора в оверлее ожидающей учетной записи",
"Show All": "",
"Show Less": "",
"Show Model": "Показать модель", "Show Model": "Показать модель",
"Show shortcuts": "Показать горячие клавиши", "Show shortcuts": "Показать горячие клавиши",
"Show your support!": "Поддержите нас!", "Show your support!": "Поддержите нас!",
@ -1092,6 +1093,7 @@
"Stream Chat Response": "Потоковый вывод ответа", "Stream Chat Response": "Потоковый вывод ответа",
"STT Model": "Модель распознавания речи", "STT Model": "Модель распознавания речи",
"STT Settings": "Настройки распознавания речи", "STT Settings": "Настройки распознавания речи",
"Stylized PDF Export": "",
"Subtitle (e.g. about the Roman Empire)": "Подзаголовок (напр., о Римской империи)", "Subtitle (e.g. about the Roman Empire)": "Подзаголовок (напр., о Римской империи)",
"Success": "Успех", "Success": "Успех",
"Successfully updated.": "Успешно обновлено.", "Successfully updated.": "Успешно обновлено.",

View file

@ -959,9 +959,8 @@
"Repeat Penalty (Ollama)": "", "Repeat Penalty (Ollama)": "",
"Reply in Thread": "", "Reply in Thread": "",
"Request Mode": "Režim žiadosti", "Request Mode": "Režim žiadosti",
"Reranking Engine": "",
"Reranking Model": "Model na prehodnotenie poradia", "Reranking Model": "Model na prehodnotenie poradia",
"Reranking model disabled": "Model na prehodnotenie poradia je deaktivovaný",
"Reranking model set to \"{{reranking_model}}\"": "Model na prehodnotenie poradia nastavený na \"{{reranking_model}}\"",
"Reset": "režim Reset", "Reset": "režim Reset",
"Reset All Models": "", "Reset All Models": "",
"Reset Upload Directory": "Resetovať adresár nahrávania", "Reset Upload Directory": "Resetovať adresár nahrávania",
@ -1069,6 +1068,8 @@
"Show": "Zobraziť", "Show": "Zobraziť",
"Show \"What's New\" modal on login": "", "Show \"What's New\" modal on login": "",
"Show Admin Details in Account Pending Overlay": "Zobraziť podrobnosti administrátora v prekryvnom okne s čakajúcim účtom", "Show Admin Details in Account Pending Overlay": "Zobraziť podrobnosti administrátora v prekryvnom okne s čakajúcim účtom",
"Show All": "",
"Show Less": "",
"Show Model": "", "Show Model": "",
"Show shortcuts": "Zobraziť klávesové skratky", "Show shortcuts": "Zobraziť klávesové skratky",
"Show your support!": "Vyjadrite svoju podporu!", "Show your support!": "Vyjadrite svoju podporu!",
@ -1092,6 +1093,7 @@
"Stream Chat Response": "Odozva chatu Stream", "Stream Chat Response": "Odozva chatu Stream",
"STT Model": "Model rozpoznávania reči na text (STT)", "STT Model": "Model rozpoznávania reči na text (STT)",
"STT Settings": "Nastavenia STT (Rozpoznávanie reči)", "STT Settings": "Nastavenia STT (Rozpoznávanie reči)",
"Stylized PDF Export": "",
"Subtitle (e.g. about the Roman Empire)": "Titulky (napr. o Rímskej ríši)", "Subtitle (e.g. about the Roman Empire)": "Titulky (napr. o Rímskej ríši)",
"Success": "Úspech", "Success": "Úspech",
"Successfully updated.": "Úspešne aktualizované.", "Successfully updated.": "Úspešne aktualizované.",

View file

@ -959,9 +959,8 @@
"Repeat Penalty (Ollama)": "", "Repeat Penalty (Ollama)": "",
"Reply in Thread": "", "Reply in Thread": "",
"Request Mode": "Режим захтева", "Request Mode": "Режим захтева",
"Reranking Engine": "",
"Reranking Model": "Модел поновног рангирања", "Reranking Model": "Модел поновног рангирања",
"Reranking model disabled": "Модел поновног рангирања онемогућен",
"Reranking model set to \"{{reranking_model}}\"": "Модел поновног рангирања подешен на \"{{reranking_model}}\"",
"Reset": "Поврати", "Reset": "Поврати",
"Reset All Models": "Поврати све моделе", "Reset All Models": "Поврати све моделе",
"Reset Upload Directory": "", "Reset Upload Directory": "",
@ -1069,6 +1068,8 @@
"Show": "Прикажи", "Show": "Прикажи",
"Show \"What's New\" modal on login": "Прикажи \"Погледај шта је ново\" прозорче при пријави", "Show \"What's New\" modal on login": "Прикажи \"Погледај шта је ново\" прозорче при пријави",
"Show Admin Details in Account Pending Overlay": "", "Show Admin Details in Account Pending Overlay": "",
"Show All": "",
"Show Less": "",
"Show Model": "", "Show Model": "",
"Show shortcuts": "Прикажи пречице", "Show shortcuts": "Прикажи пречице",
"Show your support!": "", "Show your support!": "",
@ -1092,6 +1093,7 @@
"Stream Chat Response": "", "Stream Chat Response": "",
"STT Model": "STT модел", "STT Model": "STT модел",
"STT Settings": "STT подешавања", "STT Settings": "STT подешавања",
"Stylized PDF Export": "",
"Subtitle (e.g. about the Roman Empire)": "Поднаслов (нпр. о Римском царству)", "Subtitle (e.g. about the Roman Empire)": "Поднаслов (нпр. о Римском царству)",
"Success": "Успех", "Success": "Успех",
"Successfully updated.": "Успешно ажурирано.", "Successfully updated.": "Успешно ажурирано.",

View file

@ -959,9 +959,8 @@
"Repeat Penalty (Ollama)": "", "Repeat Penalty (Ollama)": "",
"Reply in Thread": "", "Reply in Thread": "",
"Request Mode": "Frågeläge", "Request Mode": "Frågeläge",
"Reranking Engine": "",
"Reranking Model": "Reranking modell", "Reranking Model": "Reranking modell",
"Reranking model disabled": "Reranking modell inaktiverad",
"Reranking model set to \"{{reranking_model}}\"": "Reranking modell inställd på \"{{reranking_model}}\"",
"Reset": "", "Reset": "",
"Reset All Models": "", "Reset All Models": "",
"Reset Upload Directory": "Återställ uppladdningskatalog", "Reset Upload Directory": "Återställ uppladdningskatalog",
@ -1069,6 +1068,8 @@
"Show": "Visa", "Show": "Visa",
"Show \"What's New\" modal on login": "", "Show \"What's New\" modal on login": "",
"Show Admin Details in Account Pending Overlay": "Visa administratörsinformation till väntande konton", "Show Admin Details in Account Pending Overlay": "Visa administratörsinformation till väntande konton",
"Show All": "",
"Show Less": "",
"Show Model": "", "Show Model": "",
"Show shortcuts": "Visa genvägar", "Show shortcuts": "Visa genvägar",
"Show your support!": "Visa ditt stöd!", "Show your support!": "Visa ditt stöd!",
@ -1092,6 +1093,7 @@
"Stream Chat Response": "", "Stream Chat Response": "",
"STT Model": "Tal-till-text-modell", "STT Model": "Tal-till-text-modell",
"STT Settings": "Tal-till-text-inställningar", "STT Settings": "Tal-till-text-inställningar",
"Stylized PDF Export": "",
"Subtitle (e.g. about the Roman Empire)": "Undertext (t.ex. om Romerska Imperiet)", "Subtitle (e.g. about the Roman Empire)": "Undertext (t.ex. om Romerska Imperiet)",
"Success": "Framgång", "Success": "Framgång",
"Successfully updated.": "Uppdaterades framgångsrikt.", "Successfully updated.": "Uppdaterades framgångsrikt.",

View file

@ -959,9 +959,8 @@
"Repeat Penalty (Ollama)": "", "Repeat Penalty (Ollama)": "",
"Reply in Thread": "", "Reply in Thread": "",
"Request Mode": "โหมดคำขอ", "Request Mode": "โหมดคำขอ",
"Reranking Engine": "",
"Reranking Model": "จัดอันดับใหม่โมเดล", "Reranking Model": "จัดอันดับใหม่โมเดล",
"Reranking model disabled": "ปิดการใช้งานโมเดลการจัดอันดับใหม่",
"Reranking model set to \"{{reranking_model}}\"": "ตั้งค่าโมเดลการจัดอันดับใหม่เป็น \"{{reranking_model}}\"",
"Reset": "รีเซ็ต", "Reset": "รีเซ็ต",
"Reset All Models": "", "Reset All Models": "",
"Reset Upload Directory": "รีเซ็ตไดเร็กทอรีการอัปโหลด", "Reset Upload Directory": "รีเซ็ตไดเร็กทอรีการอัปโหลด",
@ -1069,6 +1068,8 @@
"Show": "แสดง", "Show": "แสดง",
"Show \"What's New\" modal on login": "", "Show \"What's New\" modal on login": "",
"Show Admin Details in Account Pending Overlay": "แสดงรายละเอียดผู้ดูแลระบบในหน้าจอรอการอนุมัติบัญชี", "Show Admin Details in Account Pending Overlay": "แสดงรายละเอียดผู้ดูแลระบบในหน้าจอรอการอนุมัติบัญชี",
"Show All": "",
"Show Less": "",
"Show Model": "", "Show Model": "",
"Show shortcuts": "แสดงทางลัด", "Show shortcuts": "แสดงทางลัด",
"Show your support!": "แสดงการสนับสนุนของคุณ!", "Show your support!": "แสดงการสนับสนุนของคุณ!",
@ -1092,6 +1093,7 @@
"Stream Chat Response": "", "Stream Chat Response": "",
"STT Model": "โมเดลแปลงเสียงเป็นข้อความ", "STT Model": "โมเดลแปลงเสียงเป็นข้อความ",
"STT Settings": "การตั้งค่าแปลงเสียงเป็นข้อความ", "STT Settings": "การตั้งค่าแปลงเสียงเป็นข้อความ",
"Stylized PDF Export": "",
"Subtitle (e.g. about the Roman Empire)": "คำบรรยาย (เช่น เกี่ยวกับจักรวรรดิโรมัน)", "Subtitle (e.g. about the Roman Empire)": "คำบรรยาย (เช่น เกี่ยวกับจักรวรรดิโรมัน)",
"Success": "สำเร็จ", "Success": "สำเร็จ",
"Successfully updated.": "อัปเดตเรียบร้อยแล้ว", "Successfully updated.": "อัปเดตเรียบร้อยแล้ว",

View file

@ -959,9 +959,8 @@
"Repeat Penalty (Ollama)": "", "Repeat Penalty (Ollama)": "",
"Reply in Thread": "", "Reply in Thread": "",
"Request Mode": "", "Request Mode": "",
"Reranking Engine": "",
"Reranking Model": "", "Reranking Model": "",
"Reranking model disabled": "",
"Reranking model set to \"{{reranking_model}}\"": "",
"Reset": "", "Reset": "",
"Reset All Models": "", "Reset All Models": "",
"Reset Upload Directory": "", "Reset Upload Directory": "",
@ -1069,6 +1068,8 @@
"Show": "", "Show": "",
"Show \"What's New\" modal on login": "", "Show \"What's New\" modal on login": "",
"Show Admin Details in Account Pending Overlay": "", "Show Admin Details in Account Pending Overlay": "",
"Show All": "",
"Show Less": "",
"Show Model": "", "Show Model": "",
"Show shortcuts": "", "Show shortcuts": "",
"Show your support!": "", "Show your support!": "",
@ -1092,6 +1093,7 @@
"Stream Chat Response": "", "Stream Chat Response": "",
"STT Model": "", "STT Model": "",
"STT Settings": "", "STT Settings": "",
"Stylized PDF Export": "",
"Subtitle (e.g. about the Roman Empire)": "", "Subtitle (e.g. about the Roman Empire)": "",
"Success": "", "Success": "",
"Successfully updated.": "", "Successfully updated.": "",

View file

@ -959,9 +959,8 @@
"Repeat Penalty (Ollama)": "", "Repeat Penalty (Ollama)": "",
"Reply in Thread": "Konuya Yanıtla", "Reply in Thread": "Konuya Yanıtla",
"Request Mode": "İstek Modu", "Request Mode": "İstek Modu",
"Reranking Engine": "",
"Reranking Model": "Yeniden Sıralama Modeli", "Reranking Model": "Yeniden Sıralama Modeli",
"Reranking model disabled": "Yeniden sıralama modeli devre dışı bırakıldı",
"Reranking model set to \"{{reranking_model}}\"": "Yeniden sıralama modeli \"{{reranking_model}}\" olarak ayarlandı",
"Reset": "Sıfırla", "Reset": "Sıfırla",
"Reset All Models": "Tüm Modelleri Sıfırla", "Reset All Models": "Tüm Modelleri Sıfırla",
"Reset Upload Directory": "Yükleme Dizinini Sıfırla", "Reset Upload Directory": "Yükleme Dizinini Sıfırla",
@ -1069,6 +1068,8 @@
"Show": "Göster", "Show": "Göster",
"Show \"What's New\" modal on login": "Girişte \"Yenilikler\" modalını göster", "Show \"What's New\" modal on login": "Girişte \"Yenilikler\" modalını göster",
"Show Admin Details in Account Pending Overlay": "Yönetici Ayrıntılarını Hesap Bekliyor Ekranında Göster", "Show Admin Details in Account Pending Overlay": "Yönetici Ayrıntılarını Hesap Bekliyor Ekranında Göster",
"Show All": "",
"Show Less": "",
"Show Model": "", "Show Model": "",
"Show shortcuts": "Kısayolları göster", "Show shortcuts": "Kısayolları göster",
"Show your support!": "Desteğinizi gösterin!", "Show your support!": "Desteğinizi gösterin!",
@ -1092,6 +1093,7 @@
"Stream Chat Response": "SAkış Sohbet Yanıtı", "Stream Chat Response": "SAkış Sohbet Yanıtı",
"STT Model": "STT Modeli", "STT Model": "STT Modeli",
"STT Settings": "STT Ayarları", "STT Settings": "STT Ayarları",
"Stylized PDF Export": "",
"Subtitle (e.g. about the Roman Empire)": "Alt başlık (örn. Roma İmparatorluğu hakkında)", "Subtitle (e.g. about the Roman Empire)": "Alt başlık (örn. Roma İmparatorluğu hakkında)",
"Success": "Başarılı", "Success": "Başarılı",
"Successfully updated.": "Başarıyla güncellendi.", "Successfully updated.": "Başarıyla güncellendi.",

View file

@ -959,9 +959,8 @@
"Repeat Penalty (Ollama)": "Штраф за повторення (Ollama)", "Repeat Penalty (Ollama)": "Штраф за повторення (Ollama)",
"Reply in Thread": "Відповісти в потоці", "Reply in Thread": "Відповісти в потоці",
"Request Mode": "Режим запиту", "Request Mode": "Режим запиту",
"Reranking Engine": "",
"Reranking Model": "Модель переранжування", "Reranking Model": "Модель переранжування",
"Reranking model disabled": "Модель переранжування вимкнена",
"Reranking model set to \"{{reranking_model}}\"": "Модель переранжування встановлено на \"{{reranking_model}}\"",
"Reset": "Скидання", "Reset": "Скидання",
"Reset All Models": "Скинути усі моделі", "Reset All Models": "Скинути усі моделі",
"Reset Upload Directory": "Скинути каталог завантажень", "Reset Upload Directory": "Скинути каталог завантажень",
@ -1069,6 +1068,8 @@
"Show": "Показати", "Show": "Показати",
"Show \"What's New\" modal on login": "Показати модальне вікно \"Що нового\" під час входу", "Show \"What's New\" modal on login": "Показати модальне вікно \"Що нового\" під час входу",
"Show Admin Details in Account Pending Overlay": "Відобразити дані адміна у вікні очікування облікового запису", "Show Admin Details in Account Pending Overlay": "Відобразити дані адміна у вікні очікування облікового запису",
"Show All": "",
"Show Less": "",
"Show Model": "Показати модель", "Show Model": "Показати модель",
"Show shortcuts": "Показати клавіатурні скорочення", "Show shortcuts": "Показати клавіатурні скорочення",
"Show your support!": "Підтримайте нас!", "Show your support!": "Підтримайте нас!",
@ -1092,6 +1093,7 @@
"Stream Chat Response": "Відповідь стрім-чату", "Stream Chat Response": "Відповідь стрім-чату",
"STT Model": "Модель STT ", "STT Model": "Модель STT ",
"STT Settings": "Налаштування STT", "STT Settings": "Налаштування STT",
"Stylized PDF Export": "",
"Subtitle (e.g. about the Roman Empire)": "Підзаголовок (напр., про Римську імперію)", "Subtitle (e.g. about the Roman Empire)": "Підзаголовок (напр., про Римську імперію)",
"Success": "Успіх", "Success": "Успіх",
"Successfully updated.": "Успішно оновлено.", "Successfully updated.": "Успішно оновлено.",

View file

@ -959,9 +959,8 @@
"Repeat Penalty (Ollama)": "", "Repeat Penalty (Ollama)": "",
"Reply in Thread": "", "Reply in Thread": "",
"Request Mode": "درخواست کا موڈ", "Request Mode": "درخواست کا موڈ",
"Reranking Engine": "",
"Reranking Model": "دوبارہ درجہ بندی کا ماڈل", "Reranking Model": "دوبارہ درجہ بندی کا ماڈل",
"Reranking model disabled": "دوبارہ درجہ بندی کا ماڈل غیر فعال کر دیا گیا",
"Reranking model set to \"{{reranking_model}}\"": "دوبارہ درجہ بندی کا ماڈل \"{{reranking_model}}\" پر مقرر کر دیا گیا ہے",
"Reset": "ری سیٹ", "Reset": "ری سیٹ",
"Reset All Models": "", "Reset All Models": "",
"Reset Upload Directory": "اپلوڈ ڈائریکٹری کو ری سیٹ کریں", "Reset Upload Directory": "اپلوڈ ڈائریکٹری کو ری سیٹ کریں",
@ -1069,6 +1068,8 @@
"Show": "دکھائیں", "Show": "دکھائیں",
"Show \"What's New\" modal on login": "", "Show \"What's New\" modal on login": "",
"Show Admin Details in Account Pending Overlay": "اکاؤنٹ پینڈنگ اوورلے میں ایڈمن کی تفصیلات دکھائیں", "Show Admin Details in Account Pending Overlay": "اکاؤنٹ پینڈنگ اوورلے میں ایڈمن کی تفصیلات دکھائیں",
"Show All": "",
"Show Less": "",
"Show Model": "", "Show Model": "",
"Show shortcuts": "شارٹ کٹ دکھائیں", "Show shortcuts": "شارٹ کٹ دکھائیں",
"Show your support!": "اپنی حمایت دکھائیں!", "Show your support!": "اپنی حمایت دکھائیں!",
@ -1092,6 +1093,7 @@
"Stream Chat Response": "اسٹریم چیٹ جواب", "Stream Chat Response": "اسٹریم چیٹ جواب",
"STT Model": "ایس ٹی ٹی ماڈل", "STT Model": "ایس ٹی ٹی ماڈل",
"STT Settings": "ایس ٹی ٹی ترتیبات", "STT Settings": "ایس ٹی ٹی ترتیبات",
"Stylized PDF Export": "",
"Subtitle (e.g. about the Roman Empire)": "ذیلی عنوان (جیسے رومن سلطنت کے بارے میں)", "Subtitle (e.g. about the Roman Empire)": "ذیلی عنوان (جیسے رومن سلطنت کے بارے میں)",
"Success": "کامیابی", "Success": "کامیابی",
"Successfully updated.": "کامیابی سے تازہ کاری ہو گئی", "Successfully updated.": "کامیابی سے تازہ کاری ہو گئی",

View file

@ -959,9 +959,8 @@
"Repeat Penalty (Ollama)": "Hình phạt Lặp lại (Ollama)", "Repeat Penalty (Ollama)": "Hình phạt Lặp lại (Ollama)",
"Reply in Thread": "Trả lời trong Luồng", "Reply in Thread": "Trả lời trong Luồng",
"Request Mode": "Chế độ Yêu cầu", "Request Mode": "Chế độ Yêu cầu",
"Reranking Engine": "",
"Reranking Model": "Reranking Model", "Reranking Model": "Reranking Model",
"Reranking model disabled": "Đã tắt mô hình reranking",
"Reranking model set to \"{{reranking_model}}\"": "Reranking model được đặt thành \"{{reranking_model}}\"",
"Reset": "Xóa toàn bộ", "Reset": "Xóa toàn bộ",
"Reset All Models": "Đặt lại Tất cả Mô hình", "Reset All Models": "Đặt lại Tất cả Mô hình",
"Reset Upload Directory": "Xóa toàn bộ thư mục Upload", "Reset Upload Directory": "Xóa toàn bộ thư mục Upload",
@ -1069,6 +1068,8 @@
"Show": "Hiển thị", "Show": "Hiển thị",
"Show \"What's New\" modal on login": "Hiển thị cửa sổ \"Có gì mới\" khi đăng nhập", "Show \"What's New\" modal on login": "Hiển thị cửa sổ \"Có gì mới\" khi đăng nhập",
"Show Admin Details in Account Pending Overlay": "Hiển thị thông tin của Quản trị viên trên màn hình hiển thị Tài khoản đang chờ xử lý", "Show Admin Details in Account Pending Overlay": "Hiển thị thông tin của Quản trị viên trên màn hình hiển thị Tài khoản đang chờ xử lý",
"Show All": "",
"Show Less": "",
"Show Model": "Hiển thị Mô hình", "Show Model": "Hiển thị Mô hình",
"Show shortcuts": "Hiển thị phím tắt", "Show shortcuts": "Hiển thị phím tắt",
"Show your support!": "Thể hiện sự ủng hộ của bạn!", "Show your support!": "Thể hiện sự ủng hộ của bạn!",
@ -1092,6 +1093,7 @@
"Stream Chat Response": "Truyền trực tiếp Phản hồi Chat", "Stream Chat Response": "Truyền trực tiếp Phản hồi Chat",
"STT Model": "Mô hình STT", "STT Model": "Mô hình STT",
"STT Settings": "Cài đặt Nhận dạng Giọng nói", "STT Settings": "Cài đặt Nhận dạng Giọng nói",
"Stylized PDF Export": "",
"Subtitle (e.g. about the Roman Empire)": "Phụ đề (ví dụ: về Đế chế La Mã)", "Subtitle (e.g. about the Roman Empire)": "Phụ đề (ví dụ: về Đế chế La Mã)",
"Success": "Thành công", "Success": "Thành công",
"Successfully updated.": "Đã cập nhật thành công.", "Successfully updated.": "Đã cập nhật thành công.",

View file

@ -105,7 +105,7 @@
"Are you sure you want to delete this channel?": "是否确认删除此频道?", "Are you sure you want to delete this channel?": "是否确认删除此频道?",
"Are you sure you want to delete this message?": "是否确认删除此消息?", "Are you sure you want to delete this message?": "是否确认删除此消息?",
"Are you sure you want to unarchive all archived chats?": "是否确认取消所有已归档的对话?", "Are you sure you want to unarchive all archived chats?": "是否确认取消所有已归档的对话?",
"Are you sure you want to update this user's role to **{{ROLE}}**?": "", "Are you sure you want to update this user's role to **{{ROLE}}**?": "您确定要将此用户的角色更新为 **{{ROLE}}** 吗?",
"Are you sure?": "是否确定?", "Are you sure?": "是否确定?",
"Arena Models": "启用竞技场匿名评价模型", "Arena Models": "启用竞技场匿名评价模型",
"Artifacts": "Artifacts", "Artifacts": "Artifacts",
@ -148,7 +148,7 @@
"Bing Search V7 Subscription Key": "Bing 搜索 V7 订阅密钥", "Bing Search V7 Subscription Key": "Bing 搜索 V7 订阅密钥",
"Bocha Search API Key": "Bocha Search API 密钥", "Bocha Search API Key": "Bocha Search API 密钥",
"Boosting or penalizing specific tokens for constrained responses. Bias values will be clamped between -100 and 100 (inclusive). (Default: none)": "为受限响应提升或惩罚特定标记。偏置值将被限制在 -100 到 100包括两端之间。默认", "Boosting or penalizing specific tokens for constrained responses. Bias values will be clamped between -100 and 100 (inclusive). (Default: none)": "为受限响应提升或惩罚特定标记。偏置值将被限制在 -100 到 100包括两端之间。默认",
"Both Docling OCR Engine and Language(s) must be provided or both left empty.": "", "Both Docling OCR Engine and Language(s) must be provided or both left empty.": "必须提供 Docling OCR Engine 和语言,或者都留空。",
"Brave Search API Key": "Brave Search API 密钥", "Brave Search API Key": "Brave Search API 密钥",
"By {{name}}": "由 {{name}} 提供", "By {{name}}": "由 {{name}} 提供",
"Bypass Embedding and Retrieval": "绕过嵌入和检索", "Bypass Embedding and Retrieval": "绕过嵌入和检索",
@ -159,7 +159,7 @@
"Cancel": "取消", "Cancel": "取消",
"Capabilities": "能力", "Capabilities": "能力",
"Capture": "截图", "Capture": "截图",
"Capture Audio": "", "Capture Audio": "录制音频",
"Certificate Path": "证书路径", "Certificate Path": "证书路径",
"Change Password": "更改密码", "Change Password": "更改密码",
"Channel Name": "频道名称", "Channel Name": "频道名称",
@ -269,8 +269,8 @@
"Create Knowledge": "创建知识", "Create Knowledge": "创建知识",
"Create new key": "创建新密钥", "Create new key": "创建新密钥",
"Create new secret key": "创建新安全密钥", "Create new secret key": "创建新安全密钥",
"Create Note": "", "Create Note": "创建笔记",
"Create your first note by clicking on the plus button below.": "", "Create your first note by clicking on the plus button below.": "单击下面的加号按钮创建您的第一个笔记。",
"Created at": "创建于", "Created at": "创建于",
"Created At": "创建于", "Created At": "创建于",
"Created by": "作者", "Created by": "作者",
@ -308,7 +308,7 @@
"Delete function?": "删除函数?", "Delete function?": "删除函数?",
"Delete Message": "删除消息", "Delete Message": "删除消息",
"Delete message?": "删除消息?", "Delete message?": "删除消息?",
"Delete note?": "", "Delete note?": "删除笔记?",
"Delete prompt?": "删除提示词?", "Delete prompt?": "删除提示词?",
"delete this link": "此处删除这个链接", "delete this link": "此处删除这个链接",
"Delete tool?": "删除工具?", "Delete tool?": "删除工具?",
@ -364,7 +364,7 @@
"Download Database": "下载数据库", "Download Database": "下载数据库",
"Drag and drop a file to upload or select a file to view": "拖动文件上传或选择文件查看", "Drag and drop a file to upload or select a file to view": "拖动文件上传或选择文件查看",
"Draw": "平局", "Draw": "平局",
"Drop any files here to upload": "", "Drop any files here to upload": "将任何文件拖放到此处进行上传",
"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "例如 '30s''10m'。有效的时间单位是秒:'s',分:'m',时:'h'。", "e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "例如 '30s''10m'。有效的时间单位是秒:'s',分:'m',时:'h'。",
"e.g. \"json\" or a JSON schema": "例如 \"json\" 或一个 JSON schema", "e.g. \"json\" or a JSON schema": "例如 \"json\" 或一个 JSON schema",
"e.g. 60": "例如 '60'", "e.g. 60": "例如 '60'",
@ -374,7 +374,7 @@
"e.g. my_filter": "例如my_filter", "e.g. my_filter": "例如my_filter",
"e.g. my_tools": "例如my_tools", "e.g. my_tools": "例如my_tools",
"e.g. Tools for performing various operations": "例如:用于执行各种操作的工具", "e.g. Tools for performing various operations": "例如:用于执行各种操作的工具",
"e.g., 3, 4, 5 (leave blank for default)": "", "e.g., 3, 4, 5 (leave blank for default)": "例如3、4、5留空为默认值",
"e.g., en-US,ja-JP (leave blank for auto-detect)": "例如,'en-US,ja-JP'(留空以便自动检测)", "e.g., en-US,ja-JP (leave blank for auto-detect)": "例如,'en-US,ja-JP'(留空以便自动检测)",
"e.g., westus (leave blank for eastus)": "", "e.g., westus (leave blank for eastus)": "",
"Edit": "编辑", "Edit": "编辑",
@ -406,7 +406,7 @@
"Enabled": "启用", "Enabled": "启用",
"Endpoint URL": "", "Endpoint URL": "",
"Enforce Temporary Chat": "强制临时聊天", "Enforce Temporary Chat": "强制临时聊天",
"Enhance": "", "Enhance": "增强",
"Ensure your CSV file includes 4 columns in this order: Name, Email, Password, Role.": "确保您的 CSV 文件按以下顺序包含 4 列: 姓名、电子邮箱、密码、角色。", "Ensure your CSV file includes 4 columns in this order: Name, Email, Password, Role.": "确保您的 CSV 文件按以下顺序包含 4 列: 姓名、电子邮箱、密码、角色。",
"Enter {{role}} message here": "在此处输入 {{role}} 的对话内容", "Enter {{role}} message here": "在此处输入 {{role}} 的对话内容",
"Enter a detail about yourself for your LLMs to recall": "输入一个关于你自己的详细信息,方便你的大语言模型记住这些内容", "Enter a detail about yourself for your LLMs to recall": "输入一个关于你自己的详细信息,方便你的大语言模型记住这些内容",
@ -423,8 +423,8 @@
"Enter Chunk Size": "输入块大小 (Chunk Size)", "Enter Chunk Size": "输入块大小 (Chunk Size)",
"Enter comma-separated \"token:bias_value\" pairs (example: 5432:100, 413:-100)": "输入以逗号分隔的“token:bias_value”对例如5432:100, 413:-100", "Enter comma-separated \"token:bias_value\" pairs (example: 5432:100, 413:-100)": "输入以逗号分隔的“token:bias_value”对例如5432:100, 413:-100",
"Enter description": "输入简介描述", "Enter description": "输入简介描述",
"Enter Docling OCR Engine": "", "Enter Docling OCR Engine": "输入 Docling OCR Engine",
"Enter Docling OCR Language(s)": "", "Enter Docling OCR Language(s)": "输入 Docling OCR 语言",
"Enter Docling Server URL": "输入 Docling 服务器 URL", "Enter Docling Server URL": "输入 Docling 服务器 URL",
"Enter Document Intelligence Endpoint": "输入 Document Intelligence 端点", "Enter Document Intelligence Endpoint": "输入 Document Intelligence 端点",
"Enter Document Intelligence Key": "输入 Document Intelligence 密钥", "Enter Document Intelligence Key": "输入 Document Intelligence 密钥",
@ -451,7 +451,7 @@
"Enter Model ID": "输入模型 ID", "Enter Model ID": "输入模型 ID",
"Enter model tag (e.g. {{modelTag}})": "输入模型标签 (例如:{{modelTag}})", "Enter model tag (e.g. {{modelTag}})": "输入模型标签 (例如:{{modelTag}})",
"Enter Mojeek Search API Key": "输入 Mojeek Search API 密钥", "Enter Mojeek Search API Key": "输入 Mojeek Search API 密钥",
"Enter New Password": "", "Enter New Password": "输入新密码",
"Enter Number of Steps (e.g. 50)": "输入步骤数 (Steps) (例如50)", "Enter Number of Steps (e.g. 50)": "输入步骤数 (Steps) (例如50)",
"Enter Perplexity API Key": "输入 Perplexity API 密钥", "Enter Perplexity API Key": "输入 Perplexity API 密钥",
"Enter Playwright Timeout": "输入 Playwright 超时时间", "Enter Playwright Timeout": "输入 Playwright 超时时间",
@ -488,15 +488,15 @@
"Enter Top K Reranker": "输入 Top K Reranker", "Enter Top K Reranker": "输入 Top K Reranker",
"Enter URL (e.g. http://127.0.0.1:7860/)": "输入地址 (例如http://127.0.0.1:7860/)", "Enter URL (e.g. http://127.0.0.1:7860/)": "输入地址 (例如http://127.0.0.1:7860/)",
"Enter URL (e.g. http://localhost:11434)": "输入地址 (例如http://localhost:11434)", "Enter URL (e.g. http://localhost:11434)": "输入地址 (例如http://localhost:11434)",
"Enter Yacy Password": "", "Enter Yacy Password": "输入 Yacy 密码",
"Enter Yacy URL (e.g. http://yacy.example.com:8090)": "", "Enter Yacy URL (e.g. http://yacy.example.com:8090)": "输入 Yacy URL例如http://yacy.example.com:8090",
"Enter Yacy Username": "", "Enter Yacy Username": "输入 Yacy 用户名",
"Enter your current password": "输入当前密码", "Enter your current password": "输入当前密码",
"Enter Your Email": "输入您的电子邮箱", "Enter Your Email": "输入您的电子邮箱",
"Enter Your Full Name": "输入您的名称", "Enter Your Full Name": "输入您的名称",
"Enter your message": "输入您的消息", "Enter your message": "输入您的消息",
"Enter your name": "输入您的名称", "Enter your name": "输入您的名称",
"Enter Your Name": "", "Enter Your Name": "输入你的名称",
"Enter your new password": "输入新的密码", "Enter your new password": "输入新的密码",
"Enter Your Password": "输入您的密码", "Enter Your Password": "输入您的密码",
"Enter Your Role": "输入您的权限组", "Enter Your Role": "输入您的权限组",
@ -505,8 +505,8 @@
"Error": "错误", "Error": "错误",
"ERROR": "错误", "ERROR": "错误",
"Error accessing Google Drive: {{error}}": "访问 Google 云端硬盘 出错: {{error}}", "Error accessing Google Drive: {{error}}": "访问 Google 云端硬盘 出错: {{error}}",
"Error accessing media devices.": "", "Error accessing media devices.": "访问媒体设备时出错。",
"Error starting recording.": "", "Error starting recording.": "开始录制时出错。",
"Error uploading file: {{error}}": "上传文件时出错: {{error}}", "Error uploading file: {{error}}": "上传文件时出错: {{error}}",
"Evaluations": "竞技场评估", "Evaluations": "竞技场评估",
"Exa API Key": "Exa API 密钥", "Exa API Key": "Exa API 密钥",
@ -545,7 +545,7 @@
"Failed to add file.": "添加文件失败。", "Failed to add file.": "添加文件失败。",
"Failed to connect to {{URL}} OpenAPI tool server": "无法连接到 {{URL}} OpenAPI 工具服务器", "Failed to connect to {{URL}} OpenAPI tool server": "无法连接到 {{URL}} OpenAPI 工具服务器",
"Failed to create API Key.": "无法创建 API 密钥。", "Failed to create API Key.": "无法创建 API 密钥。",
"Failed to delete note": "", "Failed to delete note": "删除笔记失败",
"Failed to fetch models": "无法获取模型", "Failed to fetch models": "无法获取模型",
"Failed to load file content.": "无法加载文件内容。", "Failed to load file content.": "无法加载文件内容。",
"Failed to read clipboard contents": "无法读取剪贴板内容", "Failed to read clipboard contents": "无法读取剪贴板内容",
@ -603,12 +603,12 @@
"Gemini API Config": "Gemini API 配置", "Gemini API Config": "Gemini API 配置",
"Gemini API Key is required.": "需要 Gemini API 密钥。", "Gemini API Key is required.": "需要 Gemini API 密钥。",
"General": "通用", "General": "通用",
"Generate": "", "Generate": "生成",
"Generate an image": "生成图像", "Generate an image": "生成图像",
"Generate Image": "生成图像", "Generate Image": "生成图像",
"Generate prompt pair": "生成提示对", "Generate prompt pair": "生成提示对",
"Generating search query": "生成搜索查询", "Generating search query": "生成搜索查询",
"Generating...": "", "Generating...": "生成中...",
"Get started": "开始使用", "Get started": "开始使用",
"Get started with {{WEBUI_NAME}}": "开始使用 {{WEBUI_NAME}}", "Get started with {{WEBUI_NAME}}": "开始使用 {{WEBUI_NAME}}",
"Global": "全局", "Global": "全局",
@ -655,7 +655,7 @@
"Import Config from JSON File": "导入 JSON 文件中的配置信息", "Import Config from JSON File": "导入 JSON 文件中的配置信息",
"Import Functions": "导入函数", "Import Functions": "导入函数",
"Import Models": "导入模型", "Import Models": "导入模型",
"Import Notes": "", "Import Notes": "导入笔记",
"Import Presets": "导入预设", "Import Presets": "导入预设",
"Import Prompts": "导入提示词", "Import Prompts": "导入提示词",
"Import Tools": "导入工具", "Import Tools": "导入工具",
@ -670,7 +670,7 @@
"Instant Auto-Send After Voice Transcription": "语音转录文字后即时自动发送", "Instant Auto-Send After Voice Transcription": "语音转录文字后即时自动发送",
"Integration": "集成", "Integration": "集成",
"Interface": "界面", "Interface": "界面",
"Invalid file content": "", "Invalid file content": "无效的文件内容",
"Invalid file format.": "无效文件格式。", "Invalid file format.": "无效文件格式。",
"Invalid JSON schema": "无效的 JSON schema", "Invalid JSON schema": "无效的 JSON schema",
"Invalid Tag": "无效标签", "Invalid Tag": "无效标签",
@ -741,7 +741,7 @@
"Manage Pipelines": "管理 Pipeline", "Manage Pipelines": "管理 Pipeline",
"Manage Tool Servers": "管理工具服务器", "Manage Tool Servers": "管理工具服务器",
"March": "三月", "March": "三月",
"Max Speakers": "", "Max Speakers": "最大扬声器数量",
"Max Tokens (num_predict)": "最大 Token 数量 (num_predict)", "Max Tokens (num_predict)": "最大 Token 数量 (num_predict)",
"Max Upload Count": "最大上传数量", "Max Upload Count": "最大上传数量",
"Max Upload Size": "最大上传大小", "Max Upload Size": "最大上传大小",
@ -793,16 +793,16 @@
"Mojeek Search API Key": "Mojeek Search API 密钥", "Mojeek Search API Key": "Mojeek Search API 密钥",
"more": "更多", "more": "更多",
"More": "更多", "More": "更多",
"My Notes": "", "My Notes": "我的笔记",
"Name": "名称", "Name": "名称",
"Name your knowledge base": "为您的知识库命名", "Name your knowledge base": "为您的知识库命名",
"Native": "原生", "Native": "原生",
"New Chat": "新对话", "New Chat": "新对话",
"New Folder": "新文件夹", "New Folder": "新文件夹",
"New Note": "", "New Note": "新笔记",
"New Password": "新密码", "New Password": "新密码",
"new-channel": "新频道", "new-channel": "新频道",
"No content": "", "No content": "没有内容",
"No content found": "未发现内容", "No content found": "未发现内容",
"No content found in file.": "文件中未找到内容", "No content found in file.": "文件中未找到内容",
"No content to speak": "没有内容可朗读", "No content to speak": "没有内容可朗读",
@ -817,7 +817,7 @@
"No model IDs": "没有模型 ID", "No model IDs": "没有模型 ID",
"No models found": "未找到任何模型", "No models found": "未找到任何模型",
"No models selected": "未选择任何模型", "No models selected": "未选择任何模型",
"No Notes": "", "No Notes": "没有笔记",
"No results found": "未找到结果", "No results found": "未找到结果",
"No search query generated": "未生成搜索查询", "No search query generated": "未生成搜索查询",
"No source available": "没有可用来源", "No source available": "没有可用来源",
@ -826,7 +826,7 @@
"None": "无", "None": "无",
"Not factually correct": "事实并非如此", "Not factually correct": "事实并非如此",
"Not helpful": "无帮助", "Not helpful": "无帮助",
"Note deleted successfully": "", "Note deleted successfully": "笔记删除成功",
"Note: If you set a minimum score, the search will only return documents with a score greater than or equal to the minimum score.": "注意:如果设置了最低分数,搜索只会返回分数大于或等于最低分数的文档。", "Note: If you set a minimum score, the search will only return documents with a score greater than or equal to the minimum score.": "注意:如果设置了最低分数,搜索只会返回分数大于或等于最低分数的文档。",
"Notes": "笔记", "Notes": "笔记",
"Notification Sound": "通知提示音", "Notification Sound": "通知提示音",
@ -849,7 +849,7 @@
"Only alphanumeric characters and hyphens are allowed": "只允许使用英文字母,数字 (0-9) 以及连字符 (-)", "Only alphanumeric characters and hyphens are allowed": "只允许使用英文字母,数字 (0-9) 以及连字符 (-)",
"Only alphanumeric characters and hyphens are allowed in the command string.": "命令字符串中只允许使用英文字母,数字 (0-9) 以及连字符 (-)。", "Only alphanumeric characters and hyphens are allowed in the command string.": "命令字符串中只允许使用英文字母,数字 (0-9) 以及连字符 (-)。",
"Only collections can be edited, create a new knowledge base to edit/add documents.": "只能编辑文件集,创建一个新的知识库来编辑/添加文件。", "Only collections can be edited, create a new knowledge base to edit/add documents.": "只能编辑文件集,创建一个新的知识库来编辑/添加文件。",
"Only markdown files are allowed": "", "Only markdown files are allowed": "仅允许使用 markdown 文件",
"Only select users and groups with permission can access": "只有具有权限的用户和组才能访问", "Only select users and groups with permission can access": "只有具有权限的用户和组才能访问",
"Oops! Looks like the URL is invalid. Please double-check and try again.": "此链接似乎为无效链接。请检查后重试。", "Oops! Looks like the URL is invalid. Please double-check and try again.": "此链接似乎为无效链接。请检查后重试。",
"Oops! There are files still uploading. Please wait for the upload to complete.": "稍等!还有文件正在上传。请等待上传完成。", "Oops! There are files still uploading. Please wait for the upload to complete.": "稍等!还有文件正在上传。请等待上传完成。",
@ -895,8 +895,8 @@
"Pipelines": "Pipeline", "Pipelines": "Pipeline",
"Pipelines Not Detected": "未检测到 Pipeline", "Pipelines Not Detected": "未检测到 Pipeline",
"Pipelines Valves": "Pipeline 值", "Pipelines Valves": "Pipeline 值",
"Plain text (.md)": "", "Plain text (.md)": "纯文本文档(.md",
"Plain text (.txt)": "TXT 文档 (.txt)", "Plain text (.txt)": "纯文本文档 (.txt)",
"Playground": "AI 对话游乐场", "Playground": "AI 对话游乐场",
"Playwright Timeout (ms)": "Playwright 超时时间 (ms)", "Playwright Timeout (ms)": "Playwright 超时时间 (ms)",
"Playwright WebSocket URL": "Playwright WebSocket URL", "Playwright WebSocket URL": "Playwright WebSocket URL",
@ -938,7 +938,7 @@
"Read": "只读", "Read": "只读",
"Read Aloud": "朗读", "Read Aloud": "朗读",
"Reasoning Effort": "推理努力", "Reasoning Effort": "推理努力",
"Record": "", "Record": "录制",
"Record voice": "录音", "Record voice": "录音",
"Redirecting you to Open WebUI Community": "正在将您重定向到 OpenWebUI 社区", "Redirecting you to Open WebUI Community": "正在将您重定向到 OpenWebUI 社区",
"Reduces the probability of generating nonsense. A higher value (e.g. 100) will give more diverse answers, while a lower value (e.g. 10) will be more conservative.": "降低生成无意义内容的概率。较高的值如100将产生更多样化的回答而较低的值如10则更加保守。", "Reduces the probability of generating nonsense. A higher value (e.g. 100) will give more diverse answers, while a lower value (e.g. 10) will be more conservative.": "降低生成无意义内容的概率。较高的值如100将产生更多样化的回答而较低的值如10则更加保守。",
@ -959,9 +959,8 @@
"Repeat Penalty (Ollama)": "重复惩罚 (Ollama)", "Repeat Penalty (Ollama)": "重复惩罚 (Ollama)",
"Reply in Thread": "在主题中回复", "Reply in Thread": "在主题中回复",
"Request Mode": "请求模式", "Request Mode": "请求模式",
"Reranking Engine": "",
"Reranking Model": "重排模型", "Reranking Model": "重排模型",
"Reranking model disabled": "重排模型已禁用",
"Reranking model set to \"{{reranking_model}}\"": "重排模型设置为 \"{{reranking_model}}\"",
"Reset": "重置", "Reset": "重置",
"Reset All Models": "重置所有模型", "Reset All Models": "重置所有模型",
"Reset Upload Directory": "重置上传目录", "Reset Upload Directory": "重置上传目录",
@ -1008,7 +1007,7 @@
"Searched {{count}} sites": "已搜索 {{count}} 个网站", "Searched {{count}} sites": "已搜索 {{count}} 个网站",
"Searching \"{{searchQuery}}\"": "搜索 \"{{searchQuery}}\" 中", "Searching \"{{searchQuery}}\"": "搜索 \"{{searchQuery}}\" 中",
"Searching Knowledge for \"{{searchQuery}}\"": "检索有关 \"{{searchQuery}}\" 的知识中", "Searching Knowledge for \"{{searchQuery}}\"": "检索有关 \"{{searchQuery}}\" 的知识中",
"Searching the web...": "", "Searching the web...": "正在搜索网络...",
"Searxng Query URL": "Searxng 查询 URL", "Searxng Query URL": "Searxng 查询 URL",
"See readme.md for instructions": "查看 readme.md 以获取说明", "See readme.md for instructions": "查看 readme.md 以获取说明",
"See what's new": "查阅最新更新内容", "See what's new": "查阅最新更新内容",
@ -1069,6 +1068,8 @@
"Show": "显示", "Show": "显示",
"Show \"What's New\" modal on login": "在登录时显示“更新内容”弹窗", "Show \"What's New\" modal on login": "在登录时显示“更新内容”弹窗",
"Show Admin Details in Account Pending Overlay": "在用户待激活界面中显示管理员邮箱等详细信息", "Show Admin Details in Account Pending Overlay": "在用户待激活界面中显示管理员邮箱等详细信息",
"Show All": "",
"Show Less": "",
"Show Model": "显示模型", "Show Model": "显示模型",
"Show shortcuts": "显示快捷方式", "Show shortcuts": "显示快捷方式",
"Show your support!": "表达你的支持!", "Show your support!": "表达你的支持!",
@ -1092,6 +1093,7 @@
"Stream Chat Response": "以流式返回对话响应", "Stream Chat Response": "以流式返回对话响应",
"STT Model": "语音转文本模型", "STT Model": "语音转文本模型",
"STT Settings": "语音转文本设置", "STT Settings": "语音转文本设置",
"Stylized PDF Export": "",
"Subtitle (e.g. about the Roman Empire)": "副标题(例如:关于罗马帝国的副标题)", "Subtitle (e.g. about the Roman Empire)": "副标题(例如:关于罗马帝国的副标题)",
"Success": "成功", "Success": "成功",
"Successfully updated.": "成功更新。", "Successfully updated.": "成功更新。",
@ -1211,7 +1213,7 @@
"Unpin": "取消置顶", "Unpin": "取消置顶",
"Unravel secrets": "解开秘密", "Unravel secrets": "解开秘密",
"Untagged": "无标签", "Untagged": "无标签",
"Untitled": "", "Untitled": "无标题",
"Update": "更新", "Update": "更新",
"Update and Copy Link": "更新和复制链接", "Update and Copy Link": "更新和复制链接",
"Update for the latest features and improvements.": "更新来获得最新功能与改进。", "Update for the latest features and improvements.": "更新来获得最新功能与改进。",
@ -1222,7 +1224,7 @@
"Upgrade to a licensed plan for enhanced capabilities, including custom theming and branding, and dedicated support.": "升级到授权计划以获得增强功能,包括自定义主题与品牌以及专属支持。", "Upgrade to a licensed plan for enhanced capabilities, including custom theming and branding, and dedicated support.": "升级到授权计划以获得增强功能,包括自定义主题与品牌以及专属支持。",
"Upload": "上传", "Upload": "上传",
"Upload a GGUF model": "上传一个 GGUF 模型", "Upload a GGUF model": "上传一个 GGUF 模型",
"Upload Audio": "", "Upload Audio": "上传音频",
"Upload directory": "上传目录", "Upload directory": "上传目录",
"Upload files": "上传文件", "Upload files": "上传文件",
"Upload Files": "上传文件", "Upload Files": "上传文件",
@ -1296,9 +1298,9 @@
"Write a summary in 50 words that summarizes [topic or keyword].": "用 50 个字写一个总结 [主题或关键词]。", "Write a summary in 50 words that summarizes [topic or keyword].": "用 50 个字写一个总结 [主题或关键词]。",
"Write something...": "单击以键入内容...", "Write something...": "单击以键入内容...",
"Write your model template content here": "在此写入模型模板内容", "Write your model template content here": "在此写入模型模板内容",
"Yacy Instance URL": "", "Yacy Instance URL": "Yacy Instance URL",
"Yacy Password": "", "Yacy Password": "Yacy 密码",
"Yacy Username": "", "Yacy Username": "Yacy 用户名",
"Yesterday": "昨天", "Yesterday": "昨天",
"You": "你", "You": "你",
"You are currently using a trial license. Please contact support to upgrade your license.": "当前为试用许可证,请联系支持人员升级许可证。", "You are currently using a trial license. Please contact support to upgrade your license.": "当前为试用许可证,请联系支持人员升级许可证。",

View file

@ -959,9 +959,8 @@
"Repeat Penalty (Ollama)": "重複懲罰 (Ollama)", "Repeat Penalty (Ollama)": "重複懲罰 (Ollama)",
"Reply in Thread": "在討論串中回覆", "Reply in Thread": "在討論串中回覆",
"Request Mode": "請求模式", "Request Mode": "請求模式",
"Reranking Engine": "",
"Reranking Model": "重新排序模型", "Reranking Model": "重新排序模型",
"Reranking model disabled": "已停用重新排序模型",
"Reranking model set to \"{{reranking_model}}\"": "重新排序模型已設定為 \"{{reranking_model}}\"",
"Reset": "重設", "Reset": "重設",
"Reset All Models": "重設所有模型", "Reset All Models": "重設所有模型",
"Reset Upload Directory": "重設上傳目錄", "Reset Upload Directory": "重設上傳目錄",
@ -1069,6 +1068,8 @@
"Show": "顯示", "Show": "顯示",
"Show \"What's New\" modal on login": "登入時顯示「新功能」對話框", "Show \"What's New\" modal on login": "登入時顯示「新功能」對話框",
"Show Admin Details in Account Pending Overlay": "在帳號待審覆蓋層中顯示管理員詳細資訊", "Show Admin Details in Account Pending Overlay": "在帳號待審覆蓋層中顯示管理員詳細資訊",
"Show All": "",
"Show Less": "",
"Show Model": "顯示模型", "Show Model": "顯示模型",
"Show shortcuts": "顯示快捷鍵", "Show shortcuts": "顯示快捷鍵",
"Show your support!": "表達您的支持!", "Show your support!": "表達您的支持!",
@ -1092,6 +1093,7 @@
"Stream Chat Response": "串流式對話回應", "Stream Chat Response": "串流式對話回應",
"STT Model": "語音轉文字 (STT) 模型", "STT Model": "語音轉文字 (STT) 模型",
"STT Settings": "語音轉文字 (STT) 設定", "STT Settings": "語音轉文字 (STT) 設定",
"Stylized PDF Export": "",
"Subtitle (e.g. about the Roman Empire)": "副標題(例如:關於羅馬帝國)", "Subtitle (e.g. about the Roman Empire)": "副標題(例如:關於羅馬帝國)",
"Success": "成功", "Success": "成功",
"Successfully updated.": "更新成功。", "Successfully updated.": "更新成功。",

View file

@ -10,6 +10,10 @@ const DELIMITER_LIST = [
{ left: '\\begin{equation}', right: '\\end{equation}', display: true } { left: '\\begin{equation}', right: '\\end{equation}', display: true }
]; ];
// Defines characters that are allowed to immediately precede or follow a math delimiter.
const ALLOWED_SURROUNDING_CHARS =
'\\s?。,、;!-\\/:-@\\[-`{-~\\p{Script=Han}\\p{Script=Hiragana}\\p{Script=Katakana}\\p{Script=Hangul}';
// const DELIMITER_LIST = [ // const DELIMITER_LIST = [
// { left: '$$', right: '$$', display: false }, // { left: '$$', right: '$$', display: false },
// { left: '$', right: '$', display: false }, // { left: '$', right: '$', display: false },
@ -44,10 +48,13 @@ function generateRegexRules(delimiters) {
// Math formulas can end in special characters // Math formulas can end in special characters
const inlineRule = new RegExp( const inlineRule = new RegExp(
`^(${inlinePatterns.join('|')})(?=[\\s?。,!-\/:-@[-\`{-~]|$)`, `^(${inlinePatterns.join('|')})(?=[${ALLOWED_SURROUNDING_CHARS}]|$)`,
'u'
);
const blockRule = new RegExp(
`^(${blockPatterns.join('|')})(?=[${ALLOWED_SURROUNDING_CHARS}]|$)`,
'u' 'u'
); );
const blockRule = new RegExp(`^(${blockPatterns.join('|')})(?=[\\s?。,!-\/:-@[-\`{-~]|$)`, 'u');
return { inlineRule, blockRule }; return { inlineRule, blockRule };
} }
@ -91,7 +98,9 @@ function katexStart(src, displayMode: boolean) {
// Check if the delimiter is preceded by a special character. // Check if the delimiter is preceded by a special character.
// If it does, then it's potentially a math formula. // If it does, then it's potentially a math formula.
const f = index === 0 || indexSrc.charAt(index - 1).match(/[\s?。,!-\/:-@[-`{-~]/); const f =
index === 0 ||
indexSrc.charAt(index - 1).match(new RegExp(`[${ALLOWED_SURROUNDING_CHARS}]`, 'u'));
if (f) { if (f) {
const possibleKatex = indexSrc.substring(index); const possibleKatex = indexSrc.substring(index);

View file

@ -6,6 +6,7 @@ class OneDriveConfig {
private static instance: OneDriveConfig; private static instance: OneDriveConfig;
private clientId: string = ''; private clientId: string = '';
private sharepointUrl: string = ''; private sharepointUrl: string = '';
private sharepointTenantId: string = '';
private msalInstance: PublicClientApplication | null = null; private msalInstance: PublicClientApplication | null = null;
private currentAuthorityType: 'personal' | 'organizations' = 'personal'; private currentAuthorityType: 'personal' | 'organizations' = 'personal';
@ -48,6 +49,7 @@ class OneDriveConfig {
const newClientId = config.onedrive?.client_id; const newClientId = config.onedrive?.client_id;
const newSharepointUrl = config.onedrive?.sharepoint_url; const newSharepointUrl = config.onedrive?.sharepoint_url;
const newSharepointTenantId = config.onedrive?.sharepoint_tenant_id;
if (!newClientId) { if (!newClientId) {
throw new Error('OneDrive configuration is incomplete'); throw new Error('OneDrive configuration is incomplete');
@ -55,6 +57,7 @@ class OneDriveConfig {
this.clientId = newClientId; this.clientId = newClientId;
this.sharepointUrl = newSharepointUrl; this.sharepointUrl = newSharepointUrl;
this.sharepointTenantId = newSharepointTenantId;
} }
public async getMsalInstance( public async getMsalInstance(
@ -64,7 +67,9 @@ class OneDriveConfig {
if (!this.msalInstance) { if (!this.msalInstance) {
const authorityEndpoint = const authorityEndpoint =
this.currentAuthorityType === 'organizations' ? 'common' : 'consumers'; this.currentAuthorityType === 'organizations'
? this.sharepointTenantId || 'common'
: 'consumers';
const msalParams = { const msalParams = {
auth: { auth: {
authority: `https://login.microsoftonline.com/${authorityEndpoint}`, authority: `https://login.microsoftonline.com/${authorityEndpoint}`,
@ -89,6 +94,10 @@ class OneDriveConfig {
return this.sharepointUrl; return this.sharepointUrl;
} }
public getSharepointTenantId(): string {
return this.sharepointTenantId;
}
public getBaseUrl(): string { public getBaseUrl(): string {
if (this.currentAuthorityType === 'organizations') { if (this.currentAuthorityType === 'organizations') {
if (!this.sharepointUrl || this.sharepointUrl === '') { if (!this.sharepointUrl || this.sharepointUrl === '') {

View file

@ -48,7 +48,6 @@
import NotificationToast from '$lib/components/NotificationToast.svelte'; import NotificationToast from '$lib/components/NotificationToast.svelte';
import AppSidebar from '$lib/components/app/AppSidebar.svelte'; import AppSidebar from '$lib/components/app/AppSidebar.svelte';
import { chatCompletion } from '$lib/apis/openai'; import { chatCompletion } from '$lib/apis/openai';
import { setupSocket } from '$lib/utils/websocket';
setContext('i18n', i18n); setContext('i18n', i18n);
@ -59,6 +58,53 @@
const BREAKPOINT = 768; const BREAKPOINT = 768;
const setupSocket = async (enableWebsocket) => {
const _socket = io(`${WEBUI_BASE_URL}` || undefined, {
reconnection: true,
reconnectionDelay: 1000,
reconnectionDelayMax: 5000,
randomizationFactor: 0.5,
path: '/ws/socket.io',
transports: enableWebsocket ? ['websocket'] : ['polling', 'websocket'],
auth: { token: localStorage.token }
});
await socket.set(_socket);
_socket.on('connect_error', (err) => {
console.log('connect_error', err);
});
_socket.on('connect', () => {
console.log('connected', _socket.id);
});
_socket.on('reconnect_attempt', (attempt) => {
console.log('reconnect_attempt', attempt);
});
_socket.on('reconnect_failed', () => {
console.log('reconnect_failed');
});
_socket.on('disconnect', (reason, details) => {
console.log(`Socket ${_socket.id} disconnected due to ${reason}`);
if (details) {
console.log('Additional details:', details);
}
});
_socket.on('user-list', (data) => {
console.log('user-list', data);
activeUserIds.set(data.user_ids);
});
_socket.on('usage', (data) => {
console.log('usage', data);
USAGE_POOL.set(data['models']);
});
};
const executePythonAsWorker = async (id, code, cb) => { const executePythonAsWorker = async (id, code, cb) => {
let result = null; let result = null;
let stdout = null; let stdout = null;
@ -515,6 +561,8 @@
await WEBUI_NAME.set(backendConfig.name); await WEBUI_NAME.set(backendConfig.name);
if ($config) { if ($config) {
await setupSocket($config.features?.enable_websocket ?? true);
const currentUrl = `${window.location.pathname}${window.location.search}`; const currentUrl = `${window.location.pathname}${window.location.search}`;
const encodedUrl = encodeURIComponent(currentUrl); const encodedUrl = encodeURIComponent(currentUrl);
@ -526,7 +574,6 @@
}); });
if (sessionUser) { if (sessionUser) {
await setupSocket($config.features?.enable_websocket ?? true);
// Save Session User to Store // Save Session User to Store
$socket.emit('user-join', { auth: { token: sessionUser.token } }); $socket.emit('user-join', { auth: { token: sessionUser.token } });

View file

@ -12,7 +12,6 @@
import { WEBUI_NAME, config, user, socket } from '$lib/stores'; import { WEBUI_NAME, config, user, socket } from '$lib/stores';
import { generateInitialsImage, canvasPixelTest } from '$lib/utils'; import { generateInitialsImage, canvasPixelTest } from '$lib/utils';
import { setupSocket } from '$lib/utils/websocket';
import Spinner from '$lib/components/common/Spinner.svelte'; import Spinner from '$lib/components/common/Spinner.svelte';
import OnBoarding from '$lib/components/OnBoarding.svelte'; import OnBoarding from '$lib/components/OnBoarding.svelte';
@ -42,10 +41,6 @@
if (sessionUser.token) { if (sessionUser.token) {
localStorage.token = sessionUser.token; localStorage.token = sessionUser.token;
} }
if (!$socket) {
await setupSocket($config.features?.enable_websocket ?? true);
}
$socket.emit('user-join', { auth: { token: sessionUser.token } }); $socket.emit('user-join', { auth: { token: sessionUser.token } });
await user.set(sessionUser); await user.set(sessionUser);
await config.set(await getBackendConfig()); await config.set(await getBackendConfig());
@ -188,7 +183,7 @@
crossorigin="anonymous" crossorigin="anonymous"
src="{WEBUI_BASE_URL}/static/splash.png" src="{WEBUI_BASE_URL}/static/splash.png"
class=" w-6 rounded-full" class=" w-6 rounded-full"
alt="logo" alt=""
/> />
</div> </div>
</div> </div>
@ -235,7 +230,7 @@
</div> </div>
{#if $config?.onboarding ?? false} {#if $config?.onboarding ?? false}
<div class=" mt-1 text-xs font-medium text-gray-500"> <div class="mt-1 text-xs font-medium text-gray-600 dark:text-gray-500">
{$WEBUI_NAME} {$WEBUI_NAME}
{$i18n.t( {$i18n.t(
'does not make any external connections, and your data stays securely on your locally hosted server.' 'does not make any external connections, and your data stays securely on your locally hosted server.'
@ -248,10 +243,13 @@
<div class="flex flex-col mt-4"> <div class="flex flex-col mt-4">
{#if mode === 'signup'} {#if mode === 'signup'}
<div class="mb-2"> <div class="mb-2">
<div class=" text-sm font-medium text-left mb-1">{$i18n.t('Name')}</div> <label for="name" class="text-sm font-medium text-left mb-1 block"
>{$i18n.t('Name')}</label
>
<input <input
bind:value={name} bind:value={name}
type="text" type="text"
id="name"
class="my-0.5 w-full text-sm outline-hidden bg-transparent" class="my-0.5 w-full text-sm outline-hidden bg-transparent"
autocomplete="name" autocomplete="name"
placeholder={$i18n.t('Enter Your Full Name')} placeholder={$i18n.t('Enter Your Full Name')}
@ -262,23 +260,29 @@
{#if mode === 'ldap'} {#if mode === 'ldap'}
<div class="mb-2"> <div class="mb-2">
<div class=" text-sm font-medium text-left mb-1">{$i18n.t('Username')}</div> <label for="username" class="text-sm font-medium text-left mb-1 block"
>{$i18n.t('Username')}</label
>
<input <input
bind:value={ldapUsername} bind:value={ldapUsername}
type="text" type="text"
class="my-0.5 w-full text-sm outline-hidden bg-transparent" class="my-0.5 w-full text-sm outline-hidden bg-transparent"
autocomplete="username" autocomplete="username"
name="username" name="username"
id="username"
placeholder={$i18n.t('Enter Your Username')} placeholder={$i18n.t('Enter Your Username')}
required required
/> />
</div> </div>
{:else} {:else}
<div class="mb-2"> <div class="mb-2">
<div class=" text-sm font-medium text-left mb-1">{$i18n.t('Email')}</div> <label for="email" class="text-sm font-medium text-left mb-1 block"
>{$i18n.t('Email')}</label
>
<input <input
bind:value={email} bind:value={email}
type="email" type="email"
id="email"
class="my-0.5 w-full text-sm outline-hidden bg-transparent" class="my-0.5 w-full text-sm outline-hidden bg-transparent"
autocomplete="email" autocomplete="email"
name="email" name="email"
@ -289,11 +293,13 @@
{/if} {/if}
<div> <div>
<div class=" text-sm font-medium text-left mb-1">{$i18n.t('Password')}</div> <label for="password" class="text-sm font-medium text-left mb-1 block"
>{$i18n.t('Password')}</label
>
<input <input
bind:value={password} bind:value={password}
type="password" type="password"
id="password"
class="my-0.5 w-full text-sm outline-hidden bg-transparent" class="my-0.5 w-full text-sm outline-hidden bg-transparent"
placeholder={$i18n.t('Enter Your Password')} placeholder={$i18n.t('Enter Your Password')}
autocomplete="current-password" autocomplete="current-password"

View file

@ -24,7 +24,7 @@
html, html,
pre { pre {
font-family: -apple-system, BlinkMacSystemFont, 'Inter', ui-sans-serif, system-ui, 'Segoe UI', font-family: -apple-system, BlinkMacSystemFont, 'Inter', ui-sans-serif, system-ui, 'Segoe UI',
Roboto, Ubuntu, Cantarell, 'Noto Sans', sans-serif, 'Helvetica Neue', Arial, Roboto, Ubuntu, Cantarell, 'Vazirmatn', 'Noto Sans', sans-serif, 'Helvetica Neue', Arial,
'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji'; 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji';
} }

Binary file not shown.