mirror of
https://github.com/open-webui/open-webui.git
synced 2025-03-29 19:22:29 +01:00
Merge remote-tracking branch 'upstream/main'
This commit is contained in:
commit
f865a23a18
2
.github/workflows/release-pypi.yml
vendored
2
.github/workflows/release-pypi.yml
vendored
@ -19,7 +19,7 @@ jobs:
|
||||
uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18
|
||||
node-version: 22
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: 3.11
|
||||
|
19
CHANGELOG.md
19
CHANGELOG.md
@ -5,6 +5,25 @@ All notable changes to this project will be documented in this file.
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [0.5.15] - 2025-02-20
|
||||
|
||||
### Added
|
||||
|
||||
- **📄 Full Context Mode for Local Document Search (RAG)**: Toggle full context mode from Admin Settings > Documents to inject entire document content into context, improving accuracy for models with large context windows—ideal for deep context understanding.
|
||||
- **🌍 Smarter Web Search with Agentic Workflows**: Web searches now intelligently gather and refine multiple relevant terms, similar to RAG handling, delivering significantly better search results for more accurate information retrieval.
|
||||
- **🔎 Experimental Playwright Support for Web Loader**: Web content retrieval is taken to the next level with Playwright-powered scraping for enhanced accuracy in extracted web data.
|
||||
- **☁️ Experimental Azure Storage Provider**: Early-stage support for Azure Storage allows more cloud storage flexibility directly within Open WebUI.
|
||||
- **📊 Improved Jupyter Code Execution with Plots**: Interactive coding now properly displays inline plots, making data visualization more seamless inside chat interactions.
|
||||
- **⏳ Adjustable Execution Timeout for Jupyter Interpreter**: Customize execution timeout (default: 60s) for Jupyter-based code execution, allowing longer or more constrained execution based on your needs.
|
||||
- **▶️ "Running..." Indicator for Jupyter Code Execution**: A visual indicator now appears while code execution is in progress, providing real-time status updates on ongoing computations.
|
||||
- **⚙️ General Backend & Frontend Stability Enhancements**: Extensive refactoring improves reliability, performance, and overall user experience for a more seamless Open WebUI.
|
||||
- **🌍 Translation Updates**: Various international translation refinements ensure better localization and a more natural user interface experience.
|
||||
|
||||
### Fixed
|
||||
|
||||
- **📱 Mobile Hover Issue Resolved**: Users can now edit responses smoothly on mobile without interference, fixing a longstanding hover issue.
|
||||
- **🔄 Temporary Chat Message Duplication Fixed**: Eliminated buggy behavior where messages were being unnecessarily repeated in temporary chat mode, ensuring a smooth and consistent conversation flow.
|
||||
|
||||
## [0.5.14] - 2025-02-17
|
||||
|
||||
### Fixed
|
||||
|
@ -684,6 +684,10 @@ GOOGLE_APPLICATION_CREDENTIALS_JSON = os.environ.get(
|
||||
"GOOGLE_APPLICATION_CREDENTIALS_JSON", None
|
||||
)
|
||||
|
||||
AZURE_STORAGE_ENDPOINT = os.environ.get("AZURE_STORAGE_ENDPOINT", None)
|
||||
AZURE_STORAGE_CONTAINER_NAME = os.environ.get("AZURE_STORAGE_CONTAINER_NAME", None)
|
||||
AZURE_STORAGE_KEY = os.environ.get("AZURE_STORAGE_KEY", None)
|
||||
|
||||
####################################
|
||||
# File Upload DIR
|
||||
####################################
|
||||
@ -783,6 +787,9 @@ ENABLE_OPENAI_API = PersistentConfig(
|
||||
OPENAI_API_KEY = os.environ.get("OPENAI_API_KEY", "")
|
||||
OPENAI_API_BASE_URL = os.environ.get("OPENAI_API_BASE_URL", "")
|
||||
|
||||
GEMINI_API_KEY = os.environ.get("GEMINI_API_KEY", "")
|
||||
GEMINI_API_BASE_URL = os.environ.get("GEMINI_API_BASE_URL", "")
|
||||
|
||||
|
||||
if OPENAI_API_BASE_URL == "":
|
||||
OPENAI_API_BASE_URL = "https://api.openai.com/v1"
|
||||
@ -1395,6 +1402,11 @@ CODE_EXECUTION_JUPYTER_AUTH_PASSWORD = PersistentConfig(
|
||||
os.environ.get("CODE_EXECUTION_JUPYTER_AUTH_PASSWORD", ""),
|
||||
)
|
||||
|
||||
CODE_EXECUTION_JUPYTER_TIMEOUT = PersistentConfig(
|
||||
"CODE_EXECUTION_JUPYTER_TIMEOUT",
|
||||
"code_execution.jupyter.timeout",
|
||||
int(os.environ.get("CODE_EXECUTION_JUPYTER_TIMEOUT", "60")),
|
||||
)
|
||||
|
||||
ENABLE_CODE_INTERPRETER = PersistentConfig(
|
||||
"ENABLE_CODE_INTERPRETER",
|
||||
@ -1450,6 +1462,17 @@ CODE_INTERPRETER_JUPYTER_AUTH_PASSWORD = PersistentConfig(
|
||||
),
|
||||
)
|
||||
|
||||
CODE_INTERPRETER_JUPYTER_TIMEOUT = PersistentConfig(
|
||||
"CODE_INTERPRETER_JUPYTER_TIMEOUT",
|
||||
"code_interpreter.jupyter.timeout",
|
||||
int(
|
||||
os.environ.get(
|
||||
"CODE_INTERPRETER_JUPYTER_TIMEOUT",
|
||||
os.environ.get("CODE_EXECUTION_JUPYTER_TIMEOUT", "60"),
|
||||
)
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
DEFAULT_CODE_INTERPRETER_PROMPT = """
|
||||
#### Tools Available
|
||||
@ -1571,6 +1594,12 @@ ENABLE_RAG_HYBRID_SEARCH = PersistentConfig(
|
||||
os.environ.get("ENABLE_RAG_HYBRID_SEARCH", "").lower() == "true",
|
||||
)
|
||||
|
||||
RAG_FULL_CONTEXT = PersistentConfig(
|
||||
"RAG_FULL_CONTEXT",
|
||||
"rag.full_context",
|
||||
os.getenv("RAG_FULL_CONTEXT", "False").lower() == "true",
|
||||
)
|
||||
|
||||
RAG_FILE_MAX_COUNT = PersistentConfig(
|
||||
"RAG_FILE_MAX_COUNT",
|
||||
"rag.file.max_count",
|
||||
@ -1919,12 +1948,36 @@ RAG_WEB_SEARCH_CONCURRENT_REQUESTS = PersistentConfig(
|
||||
int(os.getenv("RAG_WEB_SEARCH_CONCURRENT_REQUESTS", "10")),
|
||||
)
|
||||
|
||||
RAG_WEB_LOADER_ENGINE = PersistentConfig(
|
||||
"RAG_WEB_LOADER_ENGINE",
|
||||
"rag.web.loader.engine",
|
||||
os.environ.get("RAG_WEB_LOADER_ENGINE", "safe_web"),
|
||||
)
|
||||
|
||||
RAG_WEB_SEARCH_TRUST_ENV = PersistentConfig(
|
||||
"RAG_WEB_SEARCH_TRUST_ENV",
|
||||
"rag.web.search.trust_env",
|
||||
os.getenv("RAG_WEB_SEARCH_TRUST_ENV", False),
|
||||
)
|
||||
|
||||
PLAYWRIGHT_WS_URI = PersistentConfig(
|
||||
"PLAYWRIGHT_WS_URI",
|
||||
"rag.web.loader.engine.playwright.ws.uri",
|
||||
os.environ.get("PLAYWRIGHT_WS_URI", None),
|
||||
)
|
||||
|
||||
FIRECRAWL_API_KEY = PersistentConfig(
|
||||
"FIRECRAWL_API_KEY",
|
||||
"firecrawl.api_key",
|
||||
os.environ.get("FIRECRAWL_API_KEY", ""),
|
||||
)
|
||||
|
||||
FIRECRAWL_API_BASE_URL = PersistentConfig(
|
||||
"FIRECRAWL_API_BASE_URL",
|
||||
"firecrawl.api_url",
|
||||
os.environ.get("FIRECRAWL_API_BASE_URL", "https://api.firecrawl.dev"),
|
||||
)
|
||||
|
||||
####################################
|
||||
# Images
|
||||
####################################
|
||||
@ -2135,6 +2188,17 @@ IMAGES_OPENAI_API_KEY = PersistentConfig(
|
||||
os.getenv("IMAGES_OPENAI_API_KEY", OPENAI_API_KEY),
|
||||
)
|
||||
|
||||
IMAGES_GEMINI_API_BASE_URL = PersistentConfig(
|
||||
"IMAGES_GEMINI_API_BASE_URL",
|
||||
"image_generation.gemini.api_base_url",
|
||||
os.getenv("IMAGES_GEMINI_API_BASE_URL", GEMINI_API_BASE_URL),
|
||||
)
|
||||
IMAGES_GEMINI_API_KEY = PersistentConfig(
|
||||
"IMAGES_GEMINI_API_KEY",
|
||||
"image_generation.gemini.api_key",
|
||||
os.getenv("IMAGES_GEMINI_API_KEY", GEMINI_API_KEY),
|
||||
)
|
||||
|
||||
IMAGE_SIZE = PersistentConfig(
|
||||
"IMAGE_SIZE", "image_generation.size", os.getenv("IMAGE_SIZE", "512x512")
|
||||
)
|
||||
|
@ -106,6 +106,7 @@ from open_webui.config import (
|
||||
CODE_EXECUTION_JUPYTER_AUTH,
|
||||
CODE_EXECUTION_JUPYTER_AUTH_TOKEN,
|
||||
CODE_EXECUTION_JUPYTER_AUTH_PASSWORD,
|
||||
CODE_EXECUTION_JUPYTER_TIMEOUT,
|
||||
ENABLE_CODE_INTERPRETER,
|
||||
CODE_INTERPRETER_ENGINE,
|
||||
CODE_INTERPRETER_PROMPT_TEMPLATE,
|
||||
@ -113,6 +114,7 @@ from open_webui.config import (
|
||||
CODE_INTERPRETER_JUPYTER_AUTH,
|
||||
CODE_INTERPRETER_JUPYTER_AUTH_TOKEN,
|
||||
CODE_INTERPRETER_JUPYTER_AUTH_PASSWORD,
|
||||
CODE_INTERPRETER_JUPYTER_TIMEOUT,
|
||||
# Image
|
||||
AUTOMATIC1111_API_AUTH,
|
||||
AUTOMATIC1111_BASE_URL,
|
||||
@ -131,6 +133,8 @@ from open_webui.config import (
|
||||
IMAGE_STEPS,
|
||||
IMAGES_OPENAI_API_BASE_URL,
|
||||
IMAGES_OPENAI_API_KEY,
|
||||
IMAGES_GEMINI_API_BASE_URL,
|
||||
IMAGES_GEMINI_API_KEY,
|
||||
# Audio
|
||||
AUDIO_STT_ENGINE,
|
||||
AUDIO_STT_MODEL,
|
||||
@ -145,6 +149,10 @@ from open_webui.config import (
|
||||
AUDIO_TTS_VOICE,
|
||||
AUDIO_TTS_AZURE_SPEECH_REGION,
|
||||
AUDIO_TTS_AZURE_SPEECH_OUTPUT_FORMAT,
|
||||
PLAYWRIGHT_WS_URI,
|
||||
FIRECRAWL_API_BASE_URL,
|
||||
FIRECRAWL_API_KEY,
|
||||
RAG_WEB_LOADER_ENGINE,
|
||||
WHISPER_MODEL,
|
||||
DEEPGRAM_API_KEY,
|
||||
WHISPER_MODEL_AUTO_UPDATE,
|
||||
@ -152,6 +160,7 @@ from open_webui.config import (
|
||||
# Retrieval
|
||||
RAG_TEMPLATE,
|
||||
DEFAULT_RAG_TEMPLATE,
|
||||
RAG_FULL_CONTEXT,
|
||||
RAG_EMBEDDING_MODEL,
|
||||
RAG_EMBEDDING_MODEL_AUTO_UPDATE,
|
||||
RAG_EMBEDDING_MODEL_TRUST_REMOTE_CODE,
|
||||
@ -515,6 +524,8 @@ app.state.config.RELEVANCE_THRESHOLD = RAG_RELEVANCE_THRESHOLD
|
||||
app.state.config.FILE_MAX_SIZE = RAG_FILE_MAX_SIZE
|
||||
app.state.config.FILE_MAX_COUNT = RAG_FILE_MAX_COUNT
|
||||
|
||||
|
||||
app.state.config.RAG_FULL_CONTEXT = RAG_FULL_CONTEXT
|
||||
app.state.config.ENABLE_RAG_HYBRID_SEARCH = ENABLE_RAG_HYBRID_SEARCH
|
||||
app.state.config.ENABLE_RAG_WEB_LOADER_SSL_VERIFICATION = (
|
||||
ENABLE_RAG_WEB_LOADER_SSL_VERIFICATION
|
||||
@ -576,7 +587,11 @@ app.state.config.EXA_API_KEY = EXA_API_KEY
|
||||
|
||||
app.state.config.RAG_WEB_SEARCH_RESULT_COUNT = RAG_WEB_SEARCH_RESULT_COUNT
|
||||
app.state.config.RAG_WEB_SEARCH_CONCURRENT_REQUESTS = RAG_WEB_SEARCH_CONCURRENT_REQUESTS
|
||||
app.state.config.RAG_WEB_LOADER_ENGINE = RAG_WEB_LOADER_ENGINE
|
||||
app.state.config.RAG_WEB_SEARCH_TRUST_ENV = RAG_WEB_SEARCH_TRUST_ENV
|
||||
app.state.config.PLAYWRIGHT_WS_URI = PLAYWRIGHT_WS_URI
|
||||
app.state.config.FIRECRAWL_API_BASE_URL = FIRECRAWL_API_BASE_URL
|
||||
app.state.config.FIRECRAWL_API_KEY = FIRECRAWL_API_KEY
|
||||
|
||||
app.state.EMBEDDING_FUNCTION = None
|
||||
app.state.ef = None
|
||||
@ -631,6 +646,7 @@ app.state.config.CODE_EXECUTION_JUPYTER_AUTH_TOKEN = CODE_EXECUTION_JUPYTER_AUTH
|
||||
app.state.config.CODE_EXECUTION_JUPYTER_AUTH_PASSWORD = (
|
||||
CODE_EXECUTION_JUPYTER_AUTH_PASSWORD
|
||||
)
|
||||
app.state.config.CODE_EXECUTION_JUPYTER_TIMEOUT = CODE_EXECUTION_JUPYTER_TIMEOUT
|
||||
|
||||
app.state.config.ENABLE_CODE_INTERPRETER = ENABLE_CODE_INTERPRETER
|
||||
app.state.config.CODE_INTERPRETER_ENGINE = CODE_INTERPRETER_ENGINE
|
||||
@ -644,6 +660,7 @@ app.state.config.CODE_INTERPRETER_JUPYTER_AUTH_TOKEN = (
|
||||
app.state.config.CODE_INTERPRETER_JUPYTER_AUTH_PASSWORD = (
|
||||
CODE_INTERPRETER_JUPYTER_AUTH_PASSWORD
|
||||
)
|
||||
app.state.config.CODE_INTERPRETER_JUPYTER_TIMEOUT = CODE_INTERPRETER_JUPYTER_TIMEOUT
|
||||
|
||||
########################################
|
||||
#
|
||||
@ -658,6 +675,9 @@ app.state.config.ENABLE_IMAGE_PROMPT_GENERATION = ENABLE_IMAGE_PROMPT_GENERATION
|
||||
app.state.config.IMAGES_OPENAI_API_BASE_URL = IMAGES_OPENAI_API_BASE_URL
|
||||
app.state.config.IMAGES_OPENAI_API_KEY = IMAGES_OPENAI_API_KEY
|
||||
|
||||
app.state.config.IMAGES_GEMINI_API_BASE_URL = IMAGES_GEMINI_API_BASE_URL
|
||||
app.state.config.IMAGES_GEMINI_API_KEY = IMAGES_GEMINI_API_KEY
|
||||
|
||||
app.state.config.IMAGE_GENERATION_MODEL = IMAGE_GENERATION_MODEL
|
||||
|
||||
app.state.config.AUTOMATIC1111_BASE_URL = AUTOMATIC1111_BASE_URL
|
||||
@ -967,7 +987,7 @@ async def chat_completion(
|
||||
"files": form_data.get("files", None),
|
||||
"features": form_data.get("features", None),
|
||||
"variables": form_data.get("variables", None),
|
||||
"model": model_info,
|
||||
"model": model_info.model_dump() if model_info else model,
|
||||
"direct": model_item.get("direct", False),
|
||||
**(
|
||||
{"function_calling": "native"}
|
||||
|
@ -84,6 +84,19 @@ def query_doc(
|
||||
raise e
|
||||
|
||||
|
||||
def get_doc(collection_name: str, user: UserModel = None):
|
||||
try:
|
||||
result = VECTOR_DB_CLIENT.get(collection_name=collection_name)
|
||||
|
||||
if result:
|
||||
log.info(f"query_doc:result {result.ids} {result.metadatas}")
|
||||
|
||||
return result
|
||||
except Exception as e:
|
||||
print(e)
|
||||
raise e
|
||||
|
||||
|
||||
def query_doc_with_hybrid_search(
|
||||
collection_name: str,
|
||||
query: str,
|
||||
@ -137,6 +150,27 @@ def query_doc_with_hybrid_search(
|
||||
raise e
|
||||
|
||||
|
||||
def merge_get_results(get_results: list[dict]) -> dict:
|
||||
# Initialize lists to store combined data
|
||||
combined_documents = []
|
||||
combined_metadatas = []
|
||||
combined_ids = []
|
||||
|
||||
for data in get_results:
|
||||
combined_documents.extend(data["documents"][0])
|
||||
combined_metadatas.extend(data["metadatas"][0])
|
||||
combined_ids.extend(data["ids"][0])
|
||||
|
||||
# Create the output dictionary
|
||||
result = {
|
||||
"documents": [combined_documents],
|
||||
"metadatas": [combined_metadatas],
|
||||
"ids": [combined_ids],
|
||||
}
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def merge_and_sort_query_results(
|
||||
query_results: list[dict], k: int, reverse: bool = False
|
||||
) -> list[dict]:
|
||||
@ -144,31 +178,45 @@ def merge_and_sort_query_results(
|
||||
combined_distances = []
|
||||
combined_documents = []
|
||||
combined_metadatas = []
|
||||
combined_ids = []
|
||||
|
||||
for data in query_results:
|
||||
combined_distances.extend(data["distances"][0])
|
||||
combined_documents.extend(data["documents"][0])
|
||||
combined_metadatas.extend(data["metadatas"][0])
|
||||
# DISTINCT(chunk_id,file_id) - in case if id (chunk_ids) become ordinals
|
||||
combined_ids.extend(
|
||||
[
|
||||
f"{id}-{meta['file_id']}"
|
||||
for id, meta in zip(data["ids"][0], data["metadatas"][0])
|
||||
]
|
||||
)
|
||||
|
||||
# Create a list of tuples (distance, document, metadata)
|
||||
combined = list(zip(combined_distances, combined_documents, combined_metadatas))
|
||||
# Create a list of tuples (distance, document, metadata, ids)
|
||||
combined = list(
|
||||
zip(combined_distances, combined_documents, combined_metadatas, combined_ids)
|
||||
)
|
||||
|
||||
# Sort the list based on distances
|
||||
combined.sort(key=lambda x: x[0], reverse=reverse)
|
||||
|
||||
# We don't have anything :-(
|
||||
if not combined:
|
||||
sorted_distances = []
|
||||
sorted_documents = []
|
||||
sorted_metadatas = []
|
||||
else:
|
||||
sorted_distances = []
|
||||
sorted_documents = []
|
||||
sorted_metadatas = []
|
||||
# Otherwise we don't have anything :-(
|
||||
if combined:
|
||||
# Unzip the sorted list
|
||||
sorted_distances, sorted_documents, sorted_metadatas = zip(*combined)
|
||||
|
||||
all_distances, all_documents, all_metadatas, all_ids = zip(*combined)
|
||||
seen_ids = set()
|
||||
# Slicing the lists to include only k elements
|
||||
sorted_distances = list(sorted_distances)[:k]
|
||||
sorted_documents = list(sorted_documents)[:k]
|
||||
sorted_metadatas = list(sorted_metadatas)[:k]
|
||||
for index, id in enumerate(all_ids):
|
||||
if id not in seen_ids:
|
||||
sorted_distances.append(all_distances[index])
|
||||
sorted_documents.append(all_documents[index])
|
||||
sorted_metadatas.append(all_metadatas[index])
|
||||
seen_ids.add(id)
|
||||
if len(sorted_distances) >= k:
|
||||
break
|
||||
|
||||
# Create the output dictionary
|
||||
result = {
|
||||
@ -180,6 +228,23 @@ def merge_and_sort_query_results(
|
||||
return result
|
||||
|
||||
|
||||
def get_all_items_from_collections(collection_names: list[str]) -> dict:
|
||||
results = []
|
||||
|
||||
for collection_name in collection_names:
|
||||
if collection_name:
|
||||
try:
|
||||
result = get_doc(collection_name=collection_name)
|
||||
if result is not None:
|
||||
results.append(result.model_dump())
|
||||
except Exception as e:
|
||||
log.exception(f"Error when querying the collection: {e}")
|
||||
else:
|
||||
pass
|
||||
|
||||
return merge_get_results(results)
|
||||
|
||||
|
||||
def query_collection(
|
||||
collection_names: list[str],
|
||||
queries: list[str],
|
||||
@ -297,8 +362,11 @@ def get_sources_from_files(
|
||||
reranking_function,
|
||||
r,
|
||||
hybrid_search,
|
||||
full_context=False,
|
||||
):
|
||||
log.debug(f"files: {files} {queries} {embedding_function} {reranking_function}")
|
||||
log.debug(
|
||||
f"files: {files} {queries} {embedding_function} {reranking_function} {full_context}"
|
||||
)
|
||||
|
||||
extracted_collections = []
|
||||
relevant_contexts = []
|
||||
@ -336,36 +404,43 @@ def get_sources_from_files(
|
||||
log.debug(f"skipping {file} as it has already been extracted")
|
||||
continue
|
||||
|
||||
try:
|
||||
context = None
|
||||
if file.get("type") == "text":
|
||||
context = file["content"]
|
||||
else:
|
||||
if hybrid_search:
|
||||
try:
|
||||
context = query_collection_with_hybrid_search(
|
||||
if full_context:
|
||||
try:
|
||||
context = get_all_items_from_collections(collection_names)
|
||||
except Exception as e:
|
||||
log.exception(e)
|
||||
|
||||
else:
|
||||
try:
|
||||
context = None
|
||||
if file.get("type") == "text":
|
||||
context = file["content"]
|
||||
else:
|
||||
if hybrid_search:
|
||||
try:
|
||||
context = query_collection_with_hybrid_search(
|
||||
collection_names=collection_names,
|
||||
queries=queries,
|
||||
embedding_function=embedding_function,
|
||||
k=k,
|
||||
reranking_function=reranking_function,
|
||||
r=r,
|
||||
)
|
||||
except Exception as e:
|
||||
log.debug(
|
||||
"Error when using hybrid search, using"
|
||||
" non hybrid search as fallback."
|
||||
)
|
||||
|
||||
if (not hybrid_search) or (context is None):
|
||||
context = query_collection(
|
||||
collection_names=collection_names,
|
||||
queries=queries,
|
||||
embedding_function=embedding_function,
|
||||
k=k,
|
||||
reranking_function=reranking_function,
|
||||
r=r,
|
||||
)
|
||||
except Exception as e:
|
||||
log.debug(
|
||||
"Error when using hybrid search, using"
|
||||
" non hybrid search as fallback."
|
||||
)
|
||||
|
||||
if (not hybrid_search) or (context is None):
|
||||
context = query_collection(
|
||||
collection_names=collection_names,
|
||||
queries=queries,
|
||||
embedding_function=embedding_function,
|
||||
k=k,
|
||||
)
|
||||
except Exception as e:
|
||||
log.exception(e)
|
||||
except Exception as e:
|
||||
log.exception(e)
|
||||
|
||||
extracted_collections.extend(collection_names)
|
||||
|
||||
|
@ -1,22 +1,38 @@
|
||||
import socket
|
||||
import aiohttp
|
||||
import asyncio
|
||||
import urllib.parse
|
||||
import validators
|
||||
from typing import Any, AsyncIterator, Dict, Iterator, List, Sequence, Union
|
||||
|
||||
|
||||
from langchain_community.document_loaders import (
|
||||
WebBaseLoader,
|
||||
)
|
||||
from langchain_core.documents import Document
|
||||
|
||||
|
||||
from open_webui.constants import ERROR_MESSAGES
|
||||
from open_webui.config import ENABLE_RAG_LOCAL_WEB_FETCH
|
||||
from open_webui.env import SRC_LOG_LEVELS
|
||||
|
||||
import logging
|
||||
import socket
|
||||
import ssl
|
||||
import urllib.parse
|
||||
import urllib.request
|
||||
from collections import defaultdict
|
||||
from datetime import datetime, time, timedelta
|
||||
from typing import (
|
||||
Any,
|
||||
AsyncIterator,
|
||||
Dict,
|
||||
Iterator,
|
||||
List,
|
||||
Optional,
|
||||
Sequence,
|
||||
Union,
|
||||
Literal,
|
||||
)
|
||||
import aiohttp
|
||||
import certifi
|
||||
import validators
|
||||
from langchain_community.document_loaders import PlaywrightURLLoader, WebBaseLoader
|
||||
from langchain_community.document_loaders.firecrawl import FireCrawlLoader
|
||||
from langchain_community.document_loaders.base import BaseLoader
|
||||
from langchain_core.documents import Document
|
||||
from open_webui.constants import ERROR_MESSAGES
|
||||
from open_webui.config import (
|
||||
ENABLE_RAG_LOCAL_WEB_FETCH,
|
||||
PLAYWRIGHT_WS_URI,
|
||||
RAG_WEB_LOADER_ENGINE,
|
||||
FIRECRAWL_API_BASE_URL,
|
||||
FIRECRAWL_API_KEY,
|
||||
)
|
||||
from open_webui.env import SRC_LOG_LEVELS
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
log.setLevel(SRC_LOG_LEVELS["RAG"])
|
||||
@ -68,6 +84,314 @@ def resolve_hostname(hostname):
|
||||
return ipv4_addresses, ipv6_addresses
|
||||
|
||||
|
||||
def extract_metadata(soup, url):
|
||||
metadata = {"source": url}
|
||||
if title := soup.find("title"):
|
||||
metadata["title"] = title.get_text()
|
||||
if description := soup.find("meta", attrs={"name": "description"}):
|
||||
metadata["description"] = description.get("content", "No description found.")
|
||||
if html := soup.find("html"):
|
||||
metadata["language"] = html.get("lang", "No language found.")
|
||||
return metadata
|
||||
|
||||
|
||||
def verify_ssl_cert(url: str) -> bool:
|
||||
"""Verify SSL certificate for the given URL."""
|
||||
if not url.startswith("https://"):
|
||||
return True
|
||||
|
||||
try:
|
||||
hostname = url.split("://")[-1].split("/")[0]
|
||||
context = ssl.create_default_context(cafile=certifi.where())
|
||||
with context.wrap_socket(ssl.socket(), server_hostname=hostname) as s:
|
||||
s.connect((hostname, 443))
|
||||
return True
|
||||
except ssl.SSLError:
|
||||
return False
|
||||
except Exception as e:
|
||||
log.warning(f"SSL verification failed for {url}: {str(e)}")
|
||||
return False
|
||||
|
||||
|
||||
class SafeFireCrawlLoader(BaseLoader):
|
||||
def __init__(
|
||||
self,
|
||||
web_paths,
|
||||
verify_ssl: bool = True,
|
||||
trust_env: bool = False,
|
||||
requests_per_second: Optional[float] = None,
|
||||
continue_on_failure: bool = True,
|
||||
api_key: Optional[str] = None,
|
||||
api_url: Optional[str] = None,
|
||||
mode: Literal["crawl", "scrape", "map"] = "crawl",
|
||||
proxy: Optional[Dict[str, str]] = None,
|
||||
params: Optional[Dict] = None,
|
||||
):
|
||||
"""Concurrent document loader for FireCrawl operations.
|
||||
|
||||
Executes multiple FireCrawlLoader instances concurrently using thread pooling
|
||||
to improve bulk processing efficiency.
|
||||
Args:
|
||||
web_paths: List of URLs/paths to process.
|
||||
verify_ssl: If True, verify SSL certificates.
|
||||
trust_env: If True, use proxy settings from environment variables.
|
||||
requests_per_second: Number of requests per second to limit to.
|
||||
continue_on_failure (bool): If True, continue loading other URLs on failure.
|
||||
api_key: API key for FireCrawl service. Defaults to None
|
||||
(uses FIRE_CRAWL_API_KEY environment variable if not provided).
|
||||
api_url: Base URL for FireCrawl API. Defaults to official API endpoint.
|
||||
mode: Operation mode selection:
|
||||
- 'crawl': Website crawling mode (default)
|
||||
- 'scrape': Direct page scraping
|
||||
- 'map': Site map generation
|
||||
proxy: Proxy override settings for the FireCrawl API.
|
||||
params: The parameters to pass to the Firecrawl API.
|
||||
Examples include crawlerOptions.
|
||||
For more details, visit: https://github.com/mendableai/firecrawl-py
|
||||
"""
|
||||
proxy_server = proxy.get("server") if proxy else None
|
||||
if trust_env and not proxy_server:
|
||||
env_proxies = urllib.request.getproxies()
|
||||
env_proxy_server = env_proxies.get("https") or env_proxies.get("http")
|
||||
if env_proxy_server:
|
||||
if proxy:
|
||||
proxy["server"] = env_proxy_server
|
||||
else:
|
||||
proxy = {"server": env_proxy_server}
|
||||
self.web_paths = web_paths
|
||||
self.verify_ssl = verify_ssl
|
||||
self.requests_per_second = requests_per_second
|
||||
self.last_request_time = None
|
||||
self.trust_env = trust_env
|
||||
self.continue_on_failure = continue_on_failure
|
||||
self.api_key = api_key
|
||||
self.api_url = api_url
|
||||
self.mode = mode
|
||||
self.params = params
|
||||
|
||||
def lazy_load(self) -> Iterator[Document]:
|
||||
"""Load documents concurrently using FireCrawl."""
|
||||
for url in self.web_paths:
|
||||
try:
|
||||
self._safe_process_url_sync(url)
|
||||
loader = FireCrawlLoader(
|
||||
url=url,
|
||||
api_key=self.api_key,
|
||||
api_url=self.api_url,
|
||||
mode=self.mode,
|
||||
params=self.params,
|
||||
)
|
||||
yield from loader.lazy_load()
|
||||
except Exception as e:
|
||||
if self.continue_on_failure:
|
||||
log.exception(e, "Error loading %s", url)
|
||||
continue
|
||||
raise e
|
||||
|
||||
async def alazy_load(self):
|
||||
"""Async version of lazy_load."""
|
||||
for url in self.web_paths:
|
||||
try:
|
||||
await self._safe_process_url(url)
|
||||
loader = FireCrawlLoader(
|
||||
url=url,
|
||||
api_key=self.api_key,
|
||||
api_url=self.api_url,
|
||||
mode=self.mode,
|
||||
params=self.params,
|
||||
)
|
||||
async for document in loader.alazy_load():
|
||||
yield document
|
||||
except Exception as e:
|
||||
if self.continue_on_failure:
|
||||
log.exception(e, "Error loading %s", url)
|
||||
continue
|
||||
raise e
|
||||
|
||||
def _verify_ssl_cert(self, url: str) -> bool:
|
||||
return verify_ssl_cert(url)
|
||||
|
||||
async def _wait_for_rate_limit(self):
|
||||
"""Wait to respect the rate limit if specified."""
|
||||
if self.requests_per_second and self.last_request_time:
|
||||
min_interval = timedelta(seconds=1.0 / self.requests_per_second)
|
||||
time_since_last = datetime.now() - self.last_request_time
|
||||
if time_since_last < min_interval:
|
||||
await asyncio.sleep((min_interval - time_since_last).total_seconds())
|
||||
self.last_request_time = datetime.now()
|
||||
|
||||
def _sync_wait_for_rate_limit(self):
|
||||
"""Synchronous version of rate limit wait."""
|
||||
if self.requests_per_second and self.last_request_time:
|
||||
min_interval = timedelta(seconds=1.0 / self.requests_per_second)
|
||||
time_since_last = datetime.now() - self.last_request_time
|
||||
if time_since_last < min_interval:
|
||||
time.sleep((min_interval - time_since_last).total_seconds())
|
||||
self.last_request_time = datetime.now()
|
||||
|
||||
async def _safe_process_url(self, url: str) -> bool:
|
||||
"""Perform safety checks before processing a URL."""
|
||||
if self.verify_ssl and not self._verify_ssl_cert(url):
|
||||
raise ValueError(f"SSL certificate verification failed for {url}")
|
||||
await self._wait_for_rate_limit()
|
||||
return True
|
||||
|
||||
def _safe_process_url_sync(self, url: str) -> bool:
|
||||
"""Synchronous version of safety checks."""
|
||||
if self.verify_ssl and not self._verify_ssl_cert(url):
|
||||
raise ValueError(f"SSL certificate verification failed for {url}")
|
||||
self._sync_wait_for_rate_limit()
|
||||
return True
|
||||
|
||||
|
||||
class SafePlaywrightURLLoader(PlaywrightURLLoader):
|
||||
"""Load HTML pages safely with Playwright, supporting SSL verification, rate limiting, and remote browser connection.
|
||||
|
||||
Attributes:
|
||||
web_paths (List[str]): List of URLs to load.
|
||||
verify_ssl (bool): If True, verify SSL certificates.
|
||||
trust_env (bool): If True, use proxy settings from environment variables.
|
||||
requests_per_second (Optional[float]): Number of requests per second to limit to.
|
||||
continue_on_failure (bool): If True, continue loading other URLs on failure.
|
||||
headless (bool): If True, the browser will run in headless mode.
|
||||
proxy (dict): Proxy override settings for the Playwright session.
|
||||
playwright_ws_url (Optional[str]): WebSocket endpoint URI for remote browser connection.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
web_paths: List[str],
|
||||
verify_ssl: bool = True,
|
||||
trust_env: bool = False,
|
||||
requests_per_second: Optional[float] = None,
|
||||
continue_on_failure: bool = True,
|
||||
headless: bool = True,
|
||||
remove_selectors: Optional[List[str]] = None,
|
||||
proxy: Optional[Dict[str, str]] = None,
|
||||
playwright_ws_url: Optional[str] = None,
|
||||
):
|
||||
"""Initialize with additional safety parameters and remote browser support."""
|
||||
|
||||
proxy_server = proxy.get("server") if proxy else None
|
||||
if trust_env and not proxy_server:
|
||||
env_proxies = urllib.request.getproxies()
|
||||
env_proxy_server = env_proxies.get("https") or env_proxies.get("http")
|
||||
if env_proxy_server:
|
||||
if proxy:
|
||||
proxy["server"] = env_proxy_server
|
||||
else:
|
||||
proxy = {"server": env_proxy_server}
|
||||
|
||||
# We'll set headless to False if using playwright_ws_url since it's handled by the remote browser
|
||||
super().__init__(
|
||||
urls=web_paths,
|
||||
continue_on_failure=continue_on_failure,
|
||||
headless=headless if playwright_ws_url is None else False,
|
||||
remove_selectors=remove_selectors,
|
||||
proxy=proxy,
|
||||
)
|
||||
self.verify_ssl = verify_ssl
|
||||
self.requests_per_second = requests_per_second
|
||||
self.last_request_time = None
|
||||
self.playwright_ws_url = playwright_ws_url
|
||||
self.trust_env = trust_env
|
||||
|
||||
def lazy_load(self) -> Iterator[Document]:
|
||||
"""Safely load URLs synchronously with support for remote browser."""
|
||||
from playwright.sync_api import sync_playwright
|
||||
|
||||
with sync_playwright() as p:
|
||||
# Use remote browser if ws_endpoint is provided, otherwise use local browser
|
||||
if self.playwright_ws_url:
|
||||
browser = p.chromium.connect(self.playwright_ws_url)
|
||||
else:
|
||||
browser = p.chromium.launch(headless=self.headless, proxy=self.proxy)
|
||||
|
||||
for url in self.urls:
|
||||
try:
|
||||
self._safe_process_url_sync(url)
|
||||
page = browser.new_page()
|
||||
response = page.goto(url)
|
||||
if response is None:
|
||||
raise ValueError(f"page.goto() returned None for url {url}")
|
||||
|
||||
text = self.evaluator.evaluate(page, browser, response)
|
||||
metadata = {"source": url}
|
||||
yield Document(page_content=text, metadata=metadata)
|
||||
except Exception as e:
|
||||
if self.continue_on_failure:
|
||||
log.exception(e, "Error loading %s", url)
|
||||
continue
|
||||
raise e
|
||||
browser.close()
|
||||
|
||||
async def alazy_load(self) -> AsyncIterator[Document]:
|
||||
"""Safely load URLs asynchronously with support for remote browser."""
|
||||
from playwright.async_api import async_playwright
|
||||
|
||||
async with async_playwright() as p:
|
||||
# Use remote browser if ws_endpoint is provided, otherwise use local browser
|
||||
if self.playwright_ws_url:
|
||||
browser = await p.chromium.connect(self.playwright_ws_url)
|
||||
else:
|
||||
browser = await p.chromium.launch(
|
||||
headless=self.headless, proxy=self.proxy
|
||||
)
|
||||
|
||||
for url in self.urls:
|
||||
try:
|
||||
await self._safe_process_url(url)
|
||||
page = await browser.new_page()
|
||||
response = await page.goto(url)
|
||||
if response is None:
|
||||
raise ValueError(f"page.goto() returned None for url {url}")
|
||||
|
||||
text = await self.evaluator.evaluate_async(page, browser, response)
|
||||
metadata = {"source": url}
|
||||
yield Document(page_content=text, metadata=metadata)
|
||||
except Exception as e:
|
||||
if self.continue_on_failure:
|
||||
log.exception(e, "Error loading %s", url)
|
||||
continue
|
||||
raise e
|
||||
await browser.close()
|
||||
|
||||
def _verify_ssl_cert(self, url: str) -> bool:
|
||||
return verify_ssl_cert(url)
|
||||
|
||||
async def _wait_for_rate_limit(self):
|
||||
"""Wait to respect the rate limit if specified."""
|
||||
if self.requests_per_second and self.last_request_time:
|
||||
min_interval = timedelta(seconds=1.0 / self.requests_per_second)
|
||||
time_since_last = datetime.now() - self.last_request_time
|
||||
if time_since_last < min_interval:
|
||||
await asyncio.sleep((min_interval - time_since_last).total_seconds())
|
||||
self.last_request_time = datetime.now()
|
||||
|
||||
def _sync_wait_for_rate_limit(self):
|
||||
"""Synchronous version of rate limit wait."""
|
||||
if self.requests_per_second and self.last_request_time:
|
||||
min_interval = timedelta(seconds=1.0 / self.requests_per_second)
|
||||
time_since_last = datetime.now() - self.last_request_time
|
||||
if time_since_last < min_interval:
|
||||
time.sleep((min_interval - time_since_last).total_seconds())
|
||||
self.last_request_time = datetime.now()
|
||||
|
||||
async def _safe_process_url(self, url: str) -> bool:
|
||||
"""Perform safety checks before processing a URL."""
|
||||
if self.verify_ssl and not self._verify_ssl_cert(url):
|
||||
raise ValueError(f"SSL certificate verification failed for {url}")
|
||||
await self._wait_for_rate_limit()
|
||||
return True
|
||||
|
||||
def _safe_process_url_sync(self, url: str) -> bool:
|
||||
"""Synchronous version of safety checks."""
|
||||
if self.verify_ssl and not self._verify_ssl_cert(url):
|
||||
raise ValueError(f"SSL certificate verification failed for {url}")
|
||||
self._sync_wait_for_rate_limit()
|
||||
return True
|
||||
|
||||
|
||||
class SafeWebBaseLoader(WebBaseLoader):
|
||||
"""WebBaseLoader with enhanced error handling for URLs."""
|
||||
|
||||
@ -143,20 +467,12 @@ class SafeWebBaseLoader(WebBaseLoader):
|
||||
text = soup.get_text(**self.bs_get_text_kwargs)
|
||||
|
||||
# Build metadata
|
||||
metadata = {"source": path}
|
||||
if title := soup.find("title"):
|
||||
metadata["title"] = title.get_text()
|
||||
if description := soup.find("meta", attrs={"name": "description"}):
|
||||
metadata["description"] = description.get(
|
||||
"content", "No description found."
|
||||
)
|
||||
if html := soup.find("html"):
|
||||
metadata["language"] = html.get("lang", "No language found.")
|
||||
metadata = extract_metadata(soup, path)
|
||||
|
||||
yield Document(page_content=text, metadata=metadata)
|
||||
except Exception as e:
|
||||
# Log the error and continue with the next URL
|
||||
log.error(f"Error loading {path}: {e}")
|
||||
log.exception(e, "Error loading %s", path)
|
||||
|
||||
async def alazy_load(self) -> AsyncIterator[Document]:
|
||||
"""Async lazy load text from the url(s) in web_path."""
|
||||
@ -179,6 +495,12 @@ class SafeWebBaseLoader(WebBaseLoader):
|
||||
return [document async for document in self.alazy_load()]
|
||||
|
||||
|
||||
RAG_WEB_LOADER_ENGINES = defaultdict(lambda: SafeWebBaseLoader)
|
||||
RAG_WEB_LOADER_ENGINES["playwright"] = SafePlaywrightURLLoader
|
||||
RAG_WEB_LOADER_ENGINES["safe_web"] = SafeWebBaseLoader
|
||||
RAG_WEB_LOADER_ENGINES["firecrawl"] = SafeFireCrawlLoader
|
||||
|
||||
|
||||
def get_web_loader(
|
||||
urls: Union[str, Sequence[str]],
|
||||
verify_ssl: bool = True,
|
||||
@ -188,10 +510,29 @@ def get_web_loader(
|
||||
# Check if the URLs are valid
|
||||
safe_urls = safe_validate_urls([urls] if isinstance(urls, str) else urls)
|
||||
|
||||
return SafeWebBaseLoader(
|
||||
web_path=safe_urls,
|
||||
verify_ssl=verify_ssl,
|
||||
requests_per_second=requests_per_second,
|
||||
continue_on_failure=True,
|
||||
trust_env=trust_env,
|
||||
web_loader_args = {
|
||||
"web_paths": safe_urls,
|
||||
"verify_ssl": verify_ssl,
|
||||
"requests_per_second": requests_per_second,
|
||||
"continue_on_failure": True,
|
||||
"trust_env": trust_env,
|
||||
}
|
||||
|
||||
if PLAYWRIGHT_WS_URI.value:
|
||||
web_loader_args["playwright_ws_url"] = PLAYWRIGHT_WS_URI.value
|
||||
|
||||
if RAG_WEB_LOADER_ENGINE.value == "firecrawl":
|
||||
web_loader_args["api_key"] = FIRECRAWL_API_KEY.value
|
||||
web_loader_args["api_url"] = FIRECRAWL_API_BASE_URL.value
|
||||
|
||||
# Create the appropriate WebLoader based on the configuration
|
||||
WebLoaderClass = RAG_WEB_LOADER_ENGINES[RAG_WEB_LOADER_ENGINE.value]
|
||||
web_loader = WebLoaderClass(**web_loader_args)
|
||||
|
||||
log.debug(
|
||||
"Using RAG_WEB_LOADER_ENGINE %s for %s URLs",
|
||||
web_loader.__class__.__name__,
|
||||
len(safe_urls),
|
||||
)
|
||||
|
||||
return web_loader
|
||||
|
@ -37,6 +37,7 @@ from open_webui.config import (
|
||||
|
||||
from open_webui.constants import ERROR_MESSAGES
|
||||
from open_webui.env import (
|
||||
AIOHTTP_CLIENT_TIMEOUT,
|
||||
ENV,
|
||||
SRC_LOG_LEVELS,
|
||||
DEVICE_TYPE,
|
||||
@ -266,7 +267,10 @@ async def speech(request: Request, user=Depends(get_verified_user)):
|
||||
|
||||
try:
|
||||
# print(payload)
|
||||
async with aiohttp.ClientSession() as session:
|
||||
timeout = aiohttp.ClientTimeout(total=AIOHTTP_CLIENT_TIMEOUT)
|
||||
async with aiohttp.ClientSession(
|
||||
timeout=timeout, trust_env=True
|
||||
) as session:
|
||||
async with session.post(
|
||||
url=f"{request.app.state.config.TTS_OPENAI_API_BASE_URL}/audio/speech",
|
||||
json=payload,
|
||||
@ -323,7 +327,10 @@ async def speech(request: Request, user=Depends(get_verified_user)):
|
||||
)
|
||||
|
||||
try:
|
||||
async with aiohttp.ClientSession() as session:
|
||||
timeout = aiohttp.ClientTimeout(total=AIOHTTP_CLIENT_TIMEOUT)
|
||||
async with aiohttp.ClientSession(
|
||||
timeout=timeout, trust_env=True
|
||||
) as session:
|
||||
async with session.post(
|
||||
f"https://api.elevenlabs.io/v1/text-to-speech/{voice_id}",
|
||||
json={
|
||||
@ -380,7 +387,10 @@ async def speech(request: Request, user=Depends(get_verified_user)):
|
||||
data = f"""<speak version="1.0" xmlns="http://www.w3.org/2001/10/synthesis" xml:lang="{locale}">
|
||||
<voice name="{language}">{payload["input"]}</voice>
|
||||
</speak>"""
|
||||
async with aiohttp.ClientSession() as session:
|
||||
timeout = aiohttp.ClientTimeout(total=AIOHTTP_CLIENT_TIMEOUT)
|
||||
async with aiohttp.ClientSession(
|
||||
timeout=timeout, trust_env=True
|
||||
) as session:
|
||||
async with session.post(
|
||||
f"https://{region}.tts.speech.microsoft.com/cognitiveservices/v1",
|
||||
headers={
|
||||
|
@ -546,7 +546,8 @@ async def signout(request: Request, response: Response):
|
||||
if logout_url:
|
||||
response.delete_cookie("oauth_id_token")
|
||||
return RedirectResponse(
|
||||
url=f"{logout_url}?id_token_hint={oauth_id_token}"
|
||||
headers=response.headers,
|
||||
url=f"{logout_url}?id_token_hint={oauth_id_token}",
|
||||
)
|
||||
else:
|
||||
raise HTTPException(
|
||||
|
@ -75,6 +75,7 @@ class CodeInterpreterConfigForm(BaseModel):
|
||||
CODE_EXECUTION_JUPYTER_AUTH: Optional[str]
|
||||
CODE_EXECUTION_JUPYTER_AUTH_TOKEN: Optional[str]
|
||||
CODE_EXECUTION_JUPYTER_AUTH_PASSWORD: Optional[str]
|
||||
CODE_EXECUTION_JUPYTER_TIMEOUT: Optional[int]
|
||||
ENABLE_CODE_INTERPRETER: bool
|
||||
CODE_INTERPRETER_ENGINE: str
|
||||
CODE_INTERPRETER_PROMPT_TEMPLATE: Optional[str]
|
||||
@ -82,6 +83,7 @@ class CodeInterpreterConfigForm(BaseModel):
|
||||
CODE_INTERPRETER_JUPYTER_AUTH: Optional[str]
|
||||
CODE_INTERPRETER_JUPYTER_AUTH_TOKEN: Optional[str]
|
||||
CODE_INTERPRETER_JUPYTER_AUTH_PASSWORD: Optional[str]
|
||||
CODE_INTERPRETER_JUPYTER_TIMEOUT: Optional[int]
|
||||
|
||||
|
||||
@router.get("/code_execution", response_model=CodeInterpreterConfigForm)
|
||||
@ -92,6 +94,7 @@ async def get_code_execution_config(request: Request, user=Depends(get_admin_use
|
||||
"CODE_EXECUTION_JUPYTER_AUTH": request.app.state.config.CODE_EXECUTION_JUPYTER_AUTH,
|
||||
"CODE_EXECUTION_JUPYTER_AUTH_TOKEN": request.app.state.config.CODE_EXECUTION_JUPYTER_AUTH_TOKEN,
|
||||
"CODE_EXECUTION_JUPYTER_AUTH_PASSWORD": request.app.state.config.CODE_EXECUTION_JUPYTER_AUTH_PASSWORD,
|
||||
"CODE_EXECUTION_JUPYTER_TIMEOUT": request.app.state.config.CODE_EXECUTION_JUPYTER_TIMEOUT,
|
||||
"ENABLE_CODE_INTERPRETER": request.app.state.config.ENABLE_CODE_INTERPRETER,
|
||||
"CODE_INTERPRETER_ENGINE": request.app.state.config.CODE_INTERPRETER_ENGINE,
|
||||
"CODE_INTERPRETER_PROMPT_TEMPLATE": request.app.state.config.CODE_INTERPRETER_PROMPT_TEMPLATE,
|
||||
@ -99,6 +102,7 @@ async def get_code_execution_config(request: Request, user=Depends(get_admin_use
|
||||
"CODE_INTERPRETER_JUPYTER_AUTH": request.app.state.config.CODE_INTERPRETER_JUPYTER_AUTH,
|
||||
"CODE_INTERPRETER_JUPYTER_AUTH_TOKEN": request.app.state.config.CODE_INTERPRETER_JUPYTER_AUTH_TOKEN,
|
||||
"CODE_INTERPRETER_JUPYTER_AUTH_PASSWORD": request.app.state.config.CODE_INTERPRETER_JUPYTER_AUTH_PASSWORD,
|
||||
"CODE_INTERPRETER_JUPYTER_TIMEOUT": request.app.state.config.CODE_INTERPRETER_JUPYTER_TIMEOUT,
|
||||
}
|
||||
|
||||
|
||||
@ -120,6 +124,9 @@ async def set_code_execution_config(
|
||||
request.app.state.config.CODE_EXECUTION_JUPYTER_AUTH_PASSWORD = (
|
||||
form_data.CODE_EXECUTION_JUPYTER_AUTH_PASSWORD
|
||||
)
|
||||
request.app.state.config.CODE_EXECUTION_JUPYTER_TIMEOUT = (
|
||||
form_data.CODE_EXECUTION_JUPYTER_TIMEOUT
|
||||
)
|
||||
|
||||
request.app.state.config.ENABLE_CODE_INTERPRETER = form_data.ENABLE_CODE_INTERPRETER
|
||||
request.app.state.config.CODE_INTERPRETER_ENGINE = form_data.CODE_INTERPRETER_ENGINE
|
||||
@ -141,6 +148,9 @@ async def set_code_execution_config(
|
||||
request.app.state.config.CODE_INTERPRETER_JUPYTER_AUTH_PASSWORD = (
|
||||
form_data.CODE_INTERPRETER_JUPYTER_AUTH_PASSWORD
|
||||
)
|
||||
request.app.state.config.CODE_INTERPRETER_JUPYTER_TIMEOUT = (
|
||||
form_data.CODE_INTERPRETER_JUPYTER_TIMEOUT
|
||||
)
|
||||
|
||||
return {
|
||||
"CODE_EXECUTION_ENGINE": request.app.state.config.CODE_EXECUTION_ENGINE,
|
||||
@ -148,6 +158,7 @@ async def set_code_execution_config(
|
||||
"CODE_EXECUTION_JUPYTER_AUTH": request.app.state.config.CODE_EXECUTION_JUPYTER_AUTH,
|
||||
"CODE_EXECUTION_JUPYTER_AUTH_TOKEN": request.app.state.config.CODE_EXECUTION_JUPYTER_AUTH_TOKEN,
|
||||
"CODE_EXECUTION_JUPYTER_AUTH_PASSWORD": request.app.state.config.CODE_EXECUTION_JUPYTER_AUTH_PASSWORD,
|
||||
"CODE_EXECUTION_JUPYTER_TIMEOUT": request.app.state.config.CODE_EXECUTION_JUPYTER_TIMEOUT,
|
||||
"ENABLE_CODE_INTERPRETER": request.app.state.config.ENABLE_CODE_INTERPRETER,
|
||||
"CODE_INTERPRETER_ENGINE": request.app.state.config.CODE_INTERPRETER_ENGINE,
|
||||
"CODE_INTERPRETER_PROMPT_TEMPLATE": request.app.state.config.CODE_INTERPRETER_PROMPT_TEMPLATE,
|
||||
@ -155,6 +166,7 @@ async def set_code_execution_config(
|
||||
"CODE_INTERPRETER_JUPYTER_AUTH": request.app.state.config.CODE_INTERPRETER_JUPYTER_AUTH,
|
||||
"CODE_INTERPRETER_JUPYTER_AUTH_TOKEN": request.app.state.config.CODE_INTERPRETER_JUPYTER_AUTH_TOKEN,
|
||||
"CODE_INTERPRETER_JUPYTER_AUTH_PASSWORD": request.app.state.config.CODE_INTERPRETER_JUPYTER_AUTH_PASSWORD,
|
||||
"CODE_INTERPRETER_JUPYTER_TIMEOUT": request.app.state.config.CODE_INTERPRETER_JUPYTER_TIMEOUT,
|
||||
}
|
||||
|
||||
|
||||
|
@ -225,17 +225,24 @@ async def get_file_content_by_id(id: str, user=Depends(get_verified_user)):
|
||||
filename = file.meta.get("name", file.filename)
|
||||
encoded_filename = quote(filename) # RFC5987 encoding
|
||||
|
||||
content_type = file.meta.get("content_type")
|
||||
filename = file.meta.get("name", file.filename)
|
||||
encoded_filename = quote(filename)
|
||||
headers = {}
|
||||
if file.meta.get("content_type") not in [
|
||||
"application/pdf",
|
||||
"text/plain",
|
||||
]:
|
||||
headers = {
|
||||
**headers,
|
||||
"Content-Disposition": f"attachment; filename*=UTF-8''{encoded_filename}",
|
||||
}
|
||||
|
||||
return FileResponse(file_path, headers=headers)
|
||||
if content_type == "application/pdf" or filename.lower().endswith(
|
||||
".pdf"
|
||||
):
|
||||
headers["Content-Disposition"] = (
|
||||
f"inline; filename*=UTF-8''{encoded_filename}"
|
||||
)
|
||||
content_type = "application/pdf"
|
||||
elif content_type != "text/plain":
|
||||
headers["Content-Disposition"] = (
|
||||
f"attachment; filename*=UTF-8''{encoded_filename}"
|
||||
)
|
||||
|
||||
return FileResponse(file_path, headers=headers, media_type=content_type)
|
||||
|
||||
else:
|
||||
raise HTTPException(
|
||||
|
@ -55,6 +55,10 @@ async def get_config(request: Request, user=Depends(get_admin_user)):
|
||||
"COMFYUI_WORKFLOW": request.app.state.config.COMFYUI_WORKFLOW,
|
||||
"COMFYUI_WORKFLOW_NODES": request.app.state.config.COMFYUI_WORKFLOW_NODES,
|
||||
},
|
||||
"gemini": {
|
||||
"GEMINI_API_BASE_URL": request.app.state.config.IMAGES_GEMINI_API_BASE_URL,
|
||||
"GEMINI_API_KEY": request.app.state.config.IMAGES_GEMINI_API_KEY,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@ -78,6 +82,11 @@ class ComfyUIConfigForm(BaseModel):
|
||||
COMFYUI_WORKFLOW_NODES: list[dict]
|
||||
|
||||
|
||||
class GeminiConfigForm(BaseModel):
|
||||
GEMINI_API_BASE_URL: str
|
||||
GEMINI_API_KEY: str
|
||||
|
||||
|
||||
class ConfigForm(BaseModel):
|
||||
enabled: bool
|
||||
engine: str
|
||||
@ -85,6 +94,7 @@ class ConfigForm(BaseModel):
|
||||
openai: OpenAIConfigForm
|
||||
automatic1111: Automatic1111ConfigForm
|
||||
comfyui: ComfyUIConfigForm
|
||||
gemini: GeminiConfigForm
|
||||
|
||||
|
||||
@router.post("/config/update")
|
||||
@ -103,6 +113,11 @@ async def update_config(
|
||||
)
|
||||
request.app.state.config.IMAGES_OPENAI_API_KEY = form_data.openai.OPENAI_API_KEY
|
||||
|
||||
request.app.state.config.IMAGES_GEMINI_API_BASE_URL = (
|
||||
form_data.gemini.GEMINI_API_BASE_URL
|
||||
)
|
||||
request.app.state.config.IMAGES_GEMINI_API_KEY = form_data.gemini.GEMINI_API_KEY
|
||||
|
||||
request.app.state.config.AUTOMATIC1111_BASE_URL = (
|
||||
form_data.automatic1111.AUTOMATIC1111_BASE_URL
|
||||
)
|
||||
@ -155,6 +170,10 @@ async def update_config(
|
||||
"COMFYUI_WORKFLOW": request.app.state.config.COMFYUI_WORKFLOW,
|
||||
"COMFYUI_WORKFLOW_NODES": request.app.state.config.COMFYUI_WORKFLOW_NODES,
|
||||
},
|
||||
"gemini": {
|
||||
"GEMINI_API_BASE_URL": request.app.state.config.IMAGES_GEMINI_API_BASE_URL,
|
||||
"GEMINI_API_KEY": request.app.state.config.IMAGES_GEMINI_API_KEY,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@ -224,6 +243,12 @@ def get_image_model(request):
|
||||
if request.app.state.config.IMAGE_GENERATION_MODEL
|
||||
else "dall-e-2"
|
||||
)
|
||||
elif request.app.state.config.IMAGE_GENERATION_ENGINE == "gemini":
|
||||
return (
|
||||
request.app.state.config.IMAGE_GENERATION_MODEL
|
||||
if request.app.state.config.IMAGE_GENERATION_MODEL
|
||||
else "imagen-3.0-generate-002"
|
||||
)
|
||||
elif request.app.state.config.IMAGE_GENERATION_ENGINE == "comfyui":
|
||||
return (
|
||||
request.app.state.config.IMAGE_GENERATION_MODEL
|
||||
@ -299,6 +324,10 @@ def get_models(request: Request, user=Depends(get_verified_user)):
|
||||
{"id": "dall-e-2", "name": "DALL·E 2"},
|
||||
{"id": "dall-e-3", "name": "DALL·E 3"},
|
||||
]
|
||||
elif request.app.state.config.IMAGE_GENERATION_ENGINE == "gemini":
|
||||
return [
|
||||
{"id": "imagen-3-0-generate-002", "name": "imagen-3.0 generate-002"},
|
||||
]
|
||||
elif request.app.state.config.IMAGE_GENERATION_ENGINE == "comfyui":
|
||||
# TODO - get models from comfyui
|
||||
headers = {
|
||||
@ -483,6 +512,41 @@ async def image_generations(
|
||||
images.append({"url": url})
|
||||
return images
|
||||
|
||||
elif request.app.state.config.IMAGE_GENERATION_ENGINE == "gemini":
|
||||
headers = {}
|
||||
headers["Content-Type"] = "application/json"
|
||||
headers["x-goog-api-key"] = request.app.state.config.IMAGES_GEMINI_API_KEY
|
||||
|
||||
model = get_image_model(request)
|
||||
data = {
|
||||
"instances": {"prompt": form_data.prompt},
|
||||
"parameters": {
|
||||
"sampleCount": form_data.n,
|
||||
"outputOptions": {"mimeType": "image/png"},
|
||||
},
|
||||
}
|
||||
|
||||
# Use asyncio.to_thread for the requests.post call
|
||||
r = await asyncio.to_thread(
|
||||
requests.post,
|
||||
url=f"{request.app.state.config.IMAGES_GEMINI_API_BASE_URL}/models/{model}:predict",
|
||||
json=data,
|
||||
headers=headers,
|
||||
)
|
||||
|
||||
r.raise_for_status()
|
||||
res = r.json()
|
||||
|
||||
images = []
|
||||
for image in res["predictions"]:
|
||||
image_data, content_type = load_b64_image_data(
|
||||
image["bytesBase64Encoded"]
|
||||
)
|
||||
url = upload_image(request, data, image_data, content_type, user)
|
||||
images.append({"url": url})
|
||||
|
||||
return images
|
||||
|
||||
elif request.app.state.config.IMAGE_GENERATION_ENGINE == "comfyui":
|
||||
data = {
|
||||
"prompt": form_data.prompt,
|
||||
|
@ -26,7 +26,7 @@ from fastapi import (
|
||||
)
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
from fastapi.responses import StreamingResponse
|
||||
from pydantic import BaseModel, ConfigDict
|
||||
from pydantic import BaseModel, ConfigDict, validator
|
||||
from starlette.background import BackgroundTask
|
||||
|
||||
|
||||
@ -936,10 +936,23 @@ async def generate_completion(
|
||||
|
||||
class ChatMessage(BaseModel):
|
||||
role: str
|
||||
content: str
|
||||
content: Optional[str] = None
|
||||
tool_calls: Optional[list[dict]] = None
|
||||
images: Optional[list[str]] = None
|
||||
|
||||
@validator("content", pre=True)
|
||||
@classmethod
|
||||
def check_at_least_one_field(cls, field_value, values, **kwargs):
|
||||
# Raise an error if both 'content' and 'tool_calls' are None
|
||||
if field_value is None and (
|
||||
"tool_calls" not in values or values["tool_calls"] is None
|
||||
):
|
||||
raise ValueError(
|
||||
"At least one of 'content' or 'tool_calls' must be provided"
|
||||
)
|
||||
|
||||
return field_value
|
||||
|
||||
|
||||
class GenerateChatCompletionForm(BaseModel):
|
||||
model: str
|
||||
|
@ -351,6 +351,7 @@ async def get_rag_config(request: Request, user=Depends(get_admin_user)):
|
||||
return {
|
||||
"status": True,
|
||||
"pdf_extract_images": request.app.state.config.PDF_EXTRACT_IMAGES,
|
||||
"RAG_FULL_CONTEXT": request.app.state.config.RAG_FULL_CONTEXT,
|
||||
"enable_google_drive_integration": request.app.state.config.ENABLE_GOOGLE_DRIVE_INTEGRATION,
|
||||
"content_extraction": {
|
||||
"engine": request.app.state.config.CONTENT_EXTRACTION_ENGINE,
|
||||
@ -463,6 +464,7 @@ class WebConfig(BaseModel):
|
||||
|
||||
|
||||
class ConfigUpdateForm(BaseModel):
|
||||
RAG_FULL_CONTEXT: Optional[bool] = None
|
||||
pdf_extract_images: Optional[bool] = None
|
||||
enable_google_drive_integration: Optional[bool] = None
|
||||
file: Optional[FileConfig] = None
|
||||
@ -482,6 +484,12 @@ async def update_rag_config(
|
||||
else request.app.state.config.PDF_EXTRACT_IMAGES
|
||||
)
|
||||
|
||||
request.app.state.config.RAG_FULL_CONTEXT = (
|
||||
form_data.RAG_FULL_CONTEXT
|
||||
if form_data.RAG_FULL_CONTEXT is not None
|
||||
else request.app.state.config.RAG_FULL_CONTEXT
|
||||
)
|
||||
|
||||
request.app.state.config.ENABLE_GOOGLE_DRIVE_INTEGRATION = (
|
||||
form_data.enable_google_drive_integration
|
||||
if form_data.enable_google_drive_integration is not None
|
||||
@ -588,6 +596,7 @@ async def update_rag_config(
|
||||
return {
|
||||
"status": True,
|
||||
"pdf_extract_images": request.app.state.config.PDF_EXTRACT_IMAGES,
|
||||
"RAG_FULL_CONTEXT": request.app.state.config.RAG_FULL_CONTEXT,
|
||||
"file": {
|
||||
"max_size": request.app.state.config.FILE_MAX_SIZE,
|
||||
"max_count": request.app.state.config.FILE_MAX_COUNT,
|
||||
|
@ -56,6 +56,7 @@ async def execute_code(
|
||||
if request.app.state.config.CODE_EXECUTION_JUPYTER_AUTH == "password"
|
||||
else None
|
||||
),
|
||||
request.app.state.config.CODE_EXECUTION_JUPYTER_TIMEOUT,
|
||||
)
|
||||
|
||||
return output
|
||||
|
@ -15,12 +15,18 @@ from open_webui.config import (
|
||||
S3_SECRET_ACCESS_KEY,
|
||||
GCS_BUCKET_NAME,
|
||||
GOOGLE_APPLICATION_CREDENTIALS_JSON,
|
||||
AZURE_STORAGE_ENDPOINT,
|
||||
AZURE_STORAGE_CONTAINER_NAME,
|
||||
AZURE_STORAGE_KEY,
|
||||
STORAGE_PROVIDER,
|
||||
UPLOAD_DIR,
|
||||
)
|
||||
from google.cloud import storage
|
||||
from google.cloud.exceptions import GoogleCloudError, NotFound
|
||||
from open_webui.constants import ERROR_MESSAGES
|
||||
from azure.identity import DefaultAzureCredential
|
||||
from azure.storage.blob import BlobServiceClient
|
||||
from azure.core.exceptions import ResourceNotFoundError
|
||||
|
||||
|
||||
class StorageProvider(ABC):
|
||||
@ -221,6 +227,74 @@ class GCSStorageProvider(StorageProvider):
|
||||
LocalStorageProvider.delete_all_files()
|
||||
|
||||
|
||||
class AzureStorageProvider(StorageProvider):
|
||||
def __init__(self):
|
||||
self.endpoint = AZURE_STORAGE_ENDPOINT
|
||||
self.container_name = AZURE_STORAGE_CONTAINER_NAME
|
||||
storage_key = AZURE_STORAGE_KEY
|
||||
|
||||
if storage_key:
|
||||
# Configure using the Azure Storage Account Endpoint and Key
|
||||
self.blob_service_client = BlobServiceClient(
|
||||
account_url=self.endpoint, credential=storage_key
|
||||
)
|
||||
else:
|
||||
# Configure using the Azure Storage Account Endpoint and DefaultAzureCredential
|
||||
# If the key is not configured, then the DefaultAzureCredential will be used to support Managed Identity authentication
|
||||
self.blob_service_client = BlobServiceClient(
|
||||
account_url=self.endpoint, credential=DefaultAzureCredential()
|
||||
)
|
||||
self.container_client = self.blob_service_client.get_container_client(
|
||||
self.container_name
|
||||
)
|
||||
|
||||
def upload_file(self, file: BinaryIO, filename: str) -> Tuple[bytes, str]:
|
||||
"""Handles uploading of the file to Azure Blob Storage."""
|
||||
contents, file_path = LocalStorageProvider.upload_file(file, filename)
|
||||
try:
|
||||
blob_client = self.container_client.get_blob_client(filename)
|
||||
blob_client.upload_blob(contents, overwrite=True)
|
||||
return contents, f"{self.endpoint}/{self.container_name}/{filename}"
|
||||
except Exception as e:
|
||||
raise RuntimeError(f"Error uploading file to Azure Blob Storage: {e}")
|
||||
|
||||
def get_file(self, file_path: str) -> str:
|
||||
"""Handles downloading of the file from Azure Blob Storage."""
|
||||
try:
|
||||
filename = file_path.split("/")[-1]
|
||||
local_file_path = f"{UPLOAD_DIR}/{filename}"
|
||||
blob_client = self.container_client.get_blob_client(filename)
|
||||
with open(local_file_path, "wb") as download_file:
|
||||
download_file.write(blob_client.download_blob().readall())
|
||||
return local_file_path
|
||||
except ResourceNotFoundError as e:
|
||||
raise RuntimeError(f"Error downloading file from Azure Blob Storage: {e}")
|
||||
|
||||
def delete_file(self, file_path: str) -> None:
|
||||
"""Handles deletion of the file from Azure Blob Storage."""
|
||||
try:
|
||||
filename = file_path.split("/")[-1]
|
||||
blob_client = self.container_client.get_blob_client(filename)
|
||||
blob_client.delete_blob()
|
||||
except ResourceNotFoundError as e:
|
||||
raise RuntimeError(f"Error deleting file from Azure Blob Storage: {e}")
|
||||
|
||||
# Always delete from local storage
|
||||
LocalStorageProvider.delete_file(file_path)
|
||||
|
||||
def delete_all_files(self) -> None:
|
||||
"""Handles deletion of all files from Azure Blob Storage."""
|
||||
try:
|
||||
blobs = self.container_client.list_blobs()
|
||||
for blob in blobs:
|
||||
self.container_client.delete_blob(blob.name)
|
||||
except Exception as e:
|
||||
raise RuntimeError(f"Error deleting all files from Azure Blob Storage: {e}")
|
||||
|
||||
# Always delete from local storage
|
||||
LocalStorageProvider.delete_all_files()
|
||||
|
||||
|
||||
def get_storage_provider(storage_provider: str):
|
||||
if storage_provider == "local":
|
||||
Storage = LocalStorageProvider()
|
||||
@ -228,6 +302,8 @@ def get_storage_provider(storage_provider: str):
|
||||
Storage = S3StorageProvider()
|
||||
elif storage_provider == "gcs":
|
||||
Storage = GCSStorageProvider()
|
||||
elif storage_provider == "azure":
|
||||
Storage = AzureStorageProvider()
|
||||
else:
|
||||
raise RuntimeError(f"Unsupported storage provider: {storage_provider}")
|
||||
return Storage
|
||||
|
@ -7,6 +7,8 @@ from moto import mock_aws
|
||||
from open_webui.storage import provider
|
||||
from gcp_storage_emulator.server import create_server
|
||||
from google.cloud import storage
|
||||
from azure.storage.blob import BlobServiceClient, ContainerClient, BlobClient
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
|
||||
def mock_upload_dir(monkeypatch, tmp_path):
|
||||
@ -22,6 +24,7 @@ def test_imports():
|
||||
provider.LocalStorageProvider
|
||||
provider.S3StorageProvider
|
||||
provider.GCSStorageProvider
|
||||
provider.AzureStorageProvider
|
||||
provider.Storage
|
||||
|
||||
|
||||
@ -32,6 +35,8 @@ def test_get_storage_provider():
|
||||
assert isinstance(Storage, provider.S3StorageProvider)
|
||||
Storage = provider.get_storage_provider("gcs")
|
||||
assert isinstance(Storage, provider.GCSStorageProvider)
|
||||
Storage = provider.get_storage_provider("azure")
|
||||
assert isinstance(Storage, provider.AzureStorageProvider)
|
||||
with pytest.raises(RuntimeError):
|
||||
provider.get_storage_provider("invalid")
|
||||
|
||||
@ -48,6 +53,7 @@ def test_class_instantiation():
|
||||
provider.LocalStorageProvider()
|
||||
provider.S3StorageProvider()
|
||||
provider.GCSStorageProvider()
|
||||
provider.AzureStorageProvider()
|
||||
|
||||
|
||||
class TestLocalStorageProvider:
|
||||
@ -272,3 +278,147 @@ class TestGCSStorageProvider:
|
||||
assert not (upload_dir / self.filename_extra).exists()
|
||||
assert self.Storage.bucket.get_blob(self.filename) == None
|
||||
assert self.Storage.bucket.get_blob(self.filename_extra) == None
|
||||
|
||||
|
||||
class TestAzureStorageProvider:
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
@pytest.fixture(scope="class")
|
||||
def setup_storage(self, monkeypatch):
|
||||
# Create mock Blob Service Client and related clients
|
||||
mock_blob_service_client = MagicMock()
|
||||
mock_container_client = MagicMock()
|
||||
mock_blob_client = MagicMock()
|
||||
|
||||
# Set up return values for the mock
|
||||
mock_blob_service_client.get_container_client.return_value = (
|
||||
mock_container_client
|
||||
)
|
||||
mock_container_client.get_blob_client.return_value = mock_blob_client
|
||||
|
||||
# Monkeypatch the Azure classes to return our mocks
|
||||
monkeypatch.setattr(
|
||||
azure.storage.blob,
|
||||
"BlobServiceClient",
|
||||
lambda *args, **kwargs: mock_blob_service_client,
|
||||
)
|
||||
monkeypatch.setattr(
|
||||
azure.storage.blob,
|
||||
"ContainerClient",
|
||||
lambda *args, **kwargs: mock_container_client,
|
||||
)
|
||||
monkeypatch.setattr(
|
||||
azure.storage.blob, "BlobClient", lambda *args, **kwargs: mock_blob_client
|
||||
)
|
||||
|
||||
self.Storage = provider.AzureStorageProvider()
|
||||
self.Storage.endpoint = "https://myaccount.blob.core.windows.net"
|
||||
self.Storage.container_name = "my-container"
|
||||
self.file_content = b"test content"
|
||||
self.filename = "test.txt"
|
||||
self.filename_extra = "test_extra.txt"
|
||||
self.file_bytesio_empty = io.BytesIO()
|
||||
|
||||
# Apply mocks to the Storage instance
|
||||
self.Storage.blob_service_client = mock_blob_service_client
|
||||
self.Storage.container_client = mock_container_client
|
||||
|
||||
def test_upload_file(self, monkeypatch, tmp_path):
|
||||
upload_dir = mock_upload_dir(monkeypatch, tmp_path)
|
||||
|
||||
# Simulate an error when container does not exist
|
||||
self.Storage.container_client.get_blob_client.side_effect = Exception(
|
||||
"Container does not exist"
|
||||
)
|
||||
with pytest.raises(Exception):
|
||||
self.Storage.upload_file(io.BytesIO(self.file_content), self.filename)
|
||||
|
||||
# Reset side effect and create container
|
||||
self.Storage.container_client.get_blob_client.side_effect = None
|
||||
self.Storage.create_container()
|
||||
contents, azure_file_path = self.Storage.upload_file(
|
||||
io.BytesIO(self.file_content), self.filename
|
||||
)
|
||||
|
||||
# Assertions
|
||||
self.Storage.container_client.get_blob_client.assert_called_with(self.filename)
|
||||
self.Storage.container_client.get_blob_client().upload_blob.assert_called_once_with(
|
||||
self.file_content, overwrite=True
|
||||
)
|
||||
assert contents == self.file_content
|
||||
assert (
|
||||
azure_file_path
|
||||
== f"https://myaccount.blob.core.windows.net/{self.Storage.container_name}/{self.filename}"
|
||||
)
|
||||
assert (upload_dir / self.filename).exists()
|
||||
assert (upload_dir / self.filename).read_bytes() == self.file_content
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
self.Storage.upload_file(self.file_bytesio_empty, self.filename)
|
||||
|
||||
def test_get_file(self, monkeypatch, tmp_path):
|
||||
upload_dir = mock_upload_dir(monkeypatch, tmp_path)
|
||||
self.Storage.create_container()
|
||||
|
||||
# Mock upload behavior
|
||||
self.Storage.upload_file(io.BytesIO(self.file_content), self.filename)
|
||||
# Mock blob download behavior
|
||||
self.Storage.container_client.get_blob_client().download_blob().readall.return_value = (
|
||||
self.file_content
|
||||
)
|
||||
|
||||
file_url = f"https://myaccount.blob.core.windows.net/{self.Storage.container_name}/{self.filename}"
|
||||
file_path = self.Storage.get_file(file_url)
|
||||
|
||||
assert file_path == str(upload_dir / self.filename)
|
||||
assert (upload_dir / self.filename).exists()
|
||||
assert (upload_dir / self.filename).read_bytes() == self.file_content
|
||||
|
||||
def test_delete_file(self, monkeypatch, tmp_path):
|
||||
upload_dir = mock_upload_dir(monkeypatch, tmp_path)
|
||||
self.Storage.create_container()
|
||||
|
||||
# Mock file upload
|
||||
self.Storage.upload_file(io.BytesIO(self.file_content), self.filename)
|
||||
# Mock deletion
|
||||
self.Storage.container_client.get_blob_client().delete_blob.return_value = None
|
||||
|
||||
file_url = f"https://myaccount.blob.core.windows.net/{self.Storage.container_name}/{self.filename}"
|
||||
self.Storage.delete_file(file_url)
|
||||
|
||||
self.Storage.container_client.get_blob_client().delete_blob.assert_called_once()
|
||||
assert not (upload_dir / self.filename).exists()
|
||||
|
||||
def test_delete_all_files(self, monkeypatch, tmp_path):
|
||||
upload_dir = mock_upload_dir(monkeypatch, tmp_path)
|
||||
self.Storage.create_container()
|
||||
|
||||
# Mock file uploads
|
||||
self.Storage.upload_file(io.BytesIO(self.file_content), self.filename)
|
||||
self.Storage.upload_file(io.BytesIO(self.file_content), self.filename_extra)
|
||||
|
||||
# Mock listing and deletion behavior
|
||||
self.Storage.container_client.list_blobs.return_value = [
|
||||
{"name": self.filename},
|
||||
{"name": self.filename_extra},
|
||||
]
|
||||
self.Storage.container_client.get_blob_client().delete_blob.return_value = None
|
||||
|
||||
self.Storage.delete_all_files()
|
||||
|
||||
self.Storage.container_client.list_blobs.assert_called_once()
|
||||
self.Storage.container_client.get_blob_client().delete_blob.assert_any_call()
|
||||
assert not (upload_dir / self.filename).exists()
|
||||
assert not (upload_dir / self.filename_extra).exists()
|
||||
|
||||
def test_get_file_not_found(self, monkeypatch):
|
||||
self.Storage.create_container()
|
||||
|
||||
file_url = f"https://myaccount.blob.core.windows.net/{self.Storage.container_name}/{self.filename}"
|
||||
# Mock behavior to raise an error for missing blobs
|
||||
self.Storage.container_client.get_blob_client().download_blob.side_effect = (
|
||||
Exception("Blob not found")
|
||||
)
|
||||
with pytest.raises(Exception, match="Blob not found"):
|
||||
self.Storage.get_file(file_url)
|
||||
|
@ -321,89 +321,94 @@ async def chat_web_search_handler(
|
||||
)
|
||||
return form_data
|
||||
|
||||
searchQuery = queries[0]
|
||||
all_results = []
|
||||
|
||||
await event_emitter(
|
||||
{
|
||||
"type": "status",
|
||||
"data": {
|
||||
"action": "web_search",
|
||||
"description": 'Searching "{{searchQuery}}"',
|
||||
"query": searchQuery,
|
||||
"done": False,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
try:
|
||||
|
||||
results = await process_web_search(
|
||||
request,
|
||||
SearchForm(
|
||||
**{
|
||||
for searchQuery in queries:
|
||||
await event_emitter(
|
||||
{
|
||||
"type": "status",
|
||||
"data": {
|
||||
"action": "web_search",
|
||||
"description": 'Searching "{{searchQuery}}"',
|
||||
"query": searchQuery,
|
||||
}
|
||||
),
|
||||
user,
|
||||
"done": False,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
if results:
|
||||
await event_emitter(
|
||||
{
|
||||
"type": "status",
|
||||
"data": {
|
||||
"action": "web_search",
|
||||
"description": "Searched {{count}} sites",
|
||||
try:
|
||||
results = await process_web_search(
|
||||
request,
|
||||
SearchForm(
|
||||
**{
|
||||
"query": searchQuery,
|
||||
"urls": results["filenames"],
|
||||
"done": True,
|
||||
},
|
||||
}
|
||||
}
|
||||
),
|
||||
user=user,
|
||||
)
|
||||
|
||||
files = form_data.get("files", [])
|
||||
if results:
|
||||
all_results.append(results)
|
||||
files = form_data.get("files", [])
|
||||
|
||||
if request.app.state.config.RAG_WEB_SEARCH_FULL_CONTEXT:
|
||||
files.append(
|
||||
{
|
||||
"docs": results.get("docs", []),
|
||||
"name": searchQuery,
|
||||
"type": "web_search_docs",
|
||||
"urls": results["filenames"],
|
||||
}
|
||||
)
|
||||
else:
|
||||
files.append(
|
||||
{
|
||||
"collection_name": results["collection_name"],
|
||||
"name": searchQuery,
|
||||
"type": "web_search_results",
|
||||
"urls": results["filenames"],
|
||||
}
|
||||
)
|
||||
form_data["files"] = files
|
||||
else:
|
||||
if request.app.state.config.RAG_WEB_SEARCH_FULL_CONTEXT:
|
||||
files.append(
|
||||
{
|
||||
"docs": results.get("docs", []),
|
||||
"name": searchQuery,
|
||||
"type": "web_search_docs",
|
||||
"urls": results["filenames"],
|
||||
}
|
||||
)
|
||||
else:
|
||||
files.append(
|
||||
{
|
||||
"collection_name": results["collection_name"],
|
||||
"name": searchQuery,
|
||||
"type": "web_search_results",
|
||||
"urls": results["filenames"],
|
||||
}
|
||||
)
|
||||
form_data["files"] = files
|
||||
except Exception as e:
|
||||
log.exception(e)
|
||||
await event_emitter(
|
||||
{
|
||||
"type": "status",
|
||||
"data": {
|
||||
"action": "web_search",
|
||||
"description": "No search results found",
|
||||
"description": 'Error searching "{{searchQuery}}"',
|
||||
"query": searchQuery,
|
||||
"done": True,
|
||||
"error": True,
|
||||
},
|
||||
}
|
||||
)
|
||||
except Exception as e:
|
||||
log.exception(e)
|
||||
|
||||
if all_results:
|
||||
urls = []
|
||||
for results in all_results:
|
||||
if "filenames" in results:
|
||||
urls.extend(results["filenames"])
|
||||
|
||||
await event_emitter(
|
||||
{
|
||||
"type": "status",
|
||||
"data": {
|
||||
"action": "web_search",
|
||||
"description": 'Error searching "{{searchQuery}}"',
|
||||
"query": searchQuery,
|
||||
"description": "Searched {{count}} sites",
|
||||
"urls": urls,
|
||||
"done": True,
|
||||
},
|
||||
}
|
||||
)
|
||||
else:
|
||||
await event_emitter(
|
||||
{
|
||||
"type": "status",
|
||||
"data": {
|
||||
"action": "web_search",
|
||||
"description": "No search results found",
|
||||
"done": True,
|
||||
"error": True,
|
||||
},
|
||||
@ -560,9 +565,9 @@ async def chat_completion_files_handler(
|
||||
reranking_function=request.app.state.rf,
|
||||
r=request.app.state.config.RELEVANCE_THRESHOLD,
|
||||
hybrid_search=request.app.state.config.ENABLE_RAG_HYBRID_SEARCH,
|
||||
full_context=request.app.state.config.RAG_FULL_CONTEXT,
|
||||
),
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
log.exception(e)
|
||||
|
||||
@ -1359,7 +1364,15 @@ async def process_chat_response(
|
||||
|
||||
tool_calls = []
|
||||
|
||||
last_assistant_message = get_last_assistant_message(form_data["messages"])
|
||||
last_assistant_message = None
|
||||
try:
|
||||
if form_data["messages"][-1]["role"] == "assistant":
|
||||
last_assistant_message = get_last_assistant_message(
|
||||
form_data["messages"]
|
||||
)
|
||||
except Exception as e:
|
||||
pass
|
||||
|
||||
content = (
|
||||
message.get("content", "")
|
||||
if message
|
||||
@ -1748,6 +1761,7 @@ async def process_chat_response(
|
||||
== "password"
|
||||
else None
|
||||
),
|
||||
request.app.state.config.CODE_INTERPRETER_JUPYTER_TIMEOUT,
|
||||
)
|
||||
else:
|
||||
output = {
|
||||
|
@ -140,7 +140,14 @@ class OAuthManager:
|
||||
log.debug("Running OAUTH Group management")
|
||||
oauth_claim = auth_manager_config.OAUTH_GROUPS_CLAIM
|
||||
|
||||
user_oauth_groups: list[str] = user_data.get(oauth_claim, list())
|
||||
# Nested claim search for groups claim
|
||||
if oauth_claim:
|
||||
claim_data = user_data
|
||||
nested_claims = oauth_claim.split(".")
|
||||
for nested_claim in nested_claims:
|
||||
claim_data = claim_data.get(nested_claim, {})
|
||||
user_oauth_groups = claim_data if isinstance(claim_data, list) else None
|
||||
|
||||
user_current_groups: list[GroupModel] = Groups.get_groups_by_member_id(user.id)
|
||||
all_available_groups: list[GroupModel] = Groups.get_groups()
|
||||
|
||||
@ -239,11 +246,46 @@ class OAuthManager:
|
||||
raise HTTPException(400, detail=ERROR_MESSAGES.INVALID_CRED)
|
||||
provider_sub = f"{provider}@{sub}"
|
||||
email_claim = auth_manager_config.OAUTH_EMAIL_CLAIM
|
||||
email = user_data.get(email_claim, "").lower()
|
||||
email = user_data.get(email_claim, "")
|
||||
# We currently mandate that email addresses are provided
|
||||
if not email:
|
||||
log.warning(f"OAuth callback failed, email is missing: {user_data}")
|
||||
raise HTTPException(400, detail=ERROR_MESSAGES.INVALID_CRED)
|
||||
# If the provider is GitHub,and public email is not provided, we can use the access token to fetch the user's email
|
||||
if provider == "github":
|
||||
try:
|
||||
access_token = token.get("access_token")
|
||||
headers = {"Authorization": f"Bearer {access_token}"}
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.get(
|
||||
"https://api.github.com/user/emails", headers=headers
|
||||
) as resp:
|
||||
if resp.ok:
|
||||
emails = await resp.json()
|
||||
# use the primary email as the user's email
|
||||
primary_email = next(
|
||||
(e["email"] for e in emails if e.get("primary")),
|
||||
None,
|
||||
)
|
||||
if primary_email:
|
||||
email = primary_email
|
||||
else:
|
||||
log.warning(
|
||||
"No primary email found in GitHub response"
|
||||
)
|
||||
raise HTTPException(
|
||||
400, detail=ERROR_MESSAGES.INVALID_CRED
|
||||
)
|
||||
else:
|
||||
log.warning("Failed to fetch GitHub email")
|
||||
raise HTTPException(
|
||||
400, detail=ERROR_MESSAGES.INVALID_CRED
|
||||
)
|
||||
except Exception as e:
|
||||
log.warning(f"Error fetching GitHub email: {e}")
|
||||
raise HTTPException(400, detail=ERROR_MESSAGES.INVALID_CRED)
|
||||
else:
|
||||
log.warning(f"OAuth callback failed, email is missing: {user_data}")
|
||||
raise HTTPException(400, detail=ERROR_MESSAGES.INVALID_CRED)
|
||||
email = email.lower()
|
||||
if (
|
||||
"*" not in auth_manager_config.OAUTH_ALLOWED_DOMAINS
|
||||
and email.split("@")[-1] not in auth_manager_config.OAUTH_ALLOWED_DOMAINS
|
||||
@ -285,9 +327,7 @@ class OAuthManager:
|
||||
# If the user does not exist, check if signups are enabled
|
||||
if auth_manager_config.ENABLE_OAUTH_SIGNUP:
|
||||
# Check if an existing user with the same email already exists
|
||||
existing_user = Users.get_user_by_email(
|
||||
user_data.get("email", "").lower()
|
||||
)
|
||||
existing_user = Users.get_user_by_email(email)
|
||||
if existing_user:
|
||||
raise HTTPException(400, detail=ERROR_MESSAGES.EMAIL_TAKEN)
|
||||
|
||||
|
@ -4,6 +4,7 @@ from open_webui.utils.misc import (
|
||||
)
|
||||
|
||||
from typing import Callable, Optional
|
||||
import json
|
||||
|
||||
|
||||
# inplace function: form_data is modified
|
||||
@ -66,38 +67,49 @@ def apply_model_params_to_body_openai(params: dict, form_data: dict) -> dict:
|
||||
|
||||
|
||||
def apply_model_params_to_body_ollama(params: dict, form_data: dict) -> dict:
|
||||
opts = [
|
||||
"temperature",
|
||||
"top_p",
|
||||
"seed",
|
||||
"mirostat",
|
||||
"mirostat_eta",
|
||||
"mirostat_tau",
|
||||
"num_ctx",
|
||||
"num_batch",
|
||||
"num_keep",
|
||||
"repeat_last_n",
|
||||
"tfs_z",
|
||||
"top_k",
|
||||
"min_p",
|
||||
"use_mmap",
|
||||
"use_mlock",
|
||||
"num_thread",
|
||||
"num_gpu",
|
||||
]
|
||||
mappings = {i: lambda x: x for i in opts}
|
||||
form_data = apply_model_params_to_body(params, form_data, mappings)
|
||||
|
||||
# Convert OpenAI parameter names to Ollama parameter names if needed.
|
||||
name_differences = {
|
||||
"max_tokens": "num_predict",
|
||||
"frequency_penalty": "repeat_penalty",
|
||||
}
|
||||
|
||||
for key, value in name_differences.items():
|
||||
if (param := params.get(key, None)) is not None:
|
||||
form_data[value] = param
|
||||
# Copy the parameter to new name then delete it, to prevent Ollama warning of invalid option provided
|
||||
params[value] = params[key]
|
||||
del params[key]
|
||||
|
||||
return form_data
|
||||
# See https://github.com/ollama/ollama/blob/main/docs/api.md#request-8
|
||||
mappings = {
|
||||
"temperature": float,
|
||||
"top_p": float,
|
||||
"seed": lambda x: x,
|
||||
"mirostat": int,
|
||||
"mirostat_eta": float,
|
||||
"mirostat_tau": float,
|
||||
"num_ctx": int,
|
||||
"num_batch": int,
|
||||
"num_keep": int,
|
||||
"num_predict": int,
|
||||
"repeat_last_n": int,
|
||||
"top_k": int,
|
||||
"min_p": float,
|
||||
"typical_p": float,
|
||||
"repeat_penalty": float,
|
||||
"presence_penalty": float,
|
||||
"frequency_penalty": float,
|
||||
"penalize_newline": bool,
|
||||
"stop": lambda x: [bytes(s, "utf-8").decode("unicode_escape") for s in x],
|
||||
"numa": bool,
|
||||
"num_gpu": int,
|
||||
"main_gpu": int,
|
||||
"low_vram": bool,
|
||||
"vocab_only": bool,
|
||||
"use_mmap": bool,
|
||||
"use_mlock": bool,
|
||||
"num_thread": int,
|
||||
}
|
||||
|
||||
return apply_model_params_to_body(params, form_data, mappings)
|
||||
|
||||
|
||||
def convert_messages_openai_to_ollama(messages: list[dict]) -> list[dict]:
|
||||
@ -108,11 +120,38 @@ def convert_messages_openai_to_ollama(messages: list[dict]) -> list[dict]:
|
||||
new_message = {"role": message["role"]}
|
||||
|
||||
content = message.get("content", [])
|
||||
tool_calls = message.get("tool_calls", None)
|
||||
tool_call_id = message.get("tool_call_id", None)
|
||||
|
||||
# Check if the content is a string (just a simple message)
|
||||
if isinstance(content, str):
|
||||
# If the content is a string, it's pure text
|
||||
new_message["content"] = content
|
||||
|
||||
# If message is a tool call, add the tool call id to the message
|
||||
if tool_call_id:
|
||||
new_message["tool_call_id"] = tool_call_id
|
||||
|
||||
elif tool_calls:
|
||||
# If tool calls are present, add them to the message
|
||||
ollama_tool_calls = []
|
||||
for tool_call in tool_calls:
|
||||
ollama_tool_call = {
|
||||
"index": tool_call.get("index", 0),
|
||||
"id": tool_call.get("id", None),
|
||||
"function": {
|
||||
"name": tool_call.get("function", {}).get("name", ""),
|
||||
"arguments": json.loads(
|
||||
tool_call.get("function", {}).get("arguments", {})
|
||||
),
|
||||
},
|
||||
}
|
||||
ollama_tool_calls.append(ollama_tool_call)
|
||||
new_message["tool_calls"] = ollama_tool_calls
|
||||
|
||||
# Put the content to empty string (Ollama requires an empty string for tool calls)
|
||||
new_message["content"] = ""
|
||||
|
||||
else:
|
||||
# Otherwise, assume the content is a list of dicts, e.g., text followed by an image URL
|
||||
content_text = ""
|
||||
@ -173,34 +212,23 @@ def convert_payload_openai_to_ollama(openai_payload: dict) -> dict:
|
||||
ollama_payload["format"] = openai_payload["format"]
|
||||
|
||||
# If there are advanced parameters in the payload, format them in Ollama's options field
|
||||
ollama_options = {}
|
||||
|
||||
if openai_payload.get("options"):
|
||||
ollama_payload["options"] = openai_payload["options"]
|
||||
ollama_options = openai_payload["options"]
|
||||
|
||||
# Handle parameters which map directly
|
||||
for param in ["temperature", "top_p", "seed"]:
|
||||
if param in openai_payload:
|
||||
ollama_options[param] = openai_payload[param]
|
||||
# Re-Mapping OpenAI's `max_tokens` -> Ollama's `num_predict`
|
||||
if "max_tokens" in ollama_options:
|
||||
ollama_options["num_predict"] = ollama_options["max_tokens"]
|
||||
del ollama_options[
|
||||
"max_tokens"
|
||||
] # To prevent Ollama warning of invalid option provided
|
||||
|
||||
# Mapping OpenAI's `max_tokens` -> Ollama's `num_predict`
|
||||
if "max_completion_tokens" in openai_payload:
|
||||
ollama_options["num_predict"] = openai_payload["max_completion_tokens"]
|
||||
elif "max_tokens" in openai_payload:
|
||||
ollama_options["num_predict"] = openai_payload["max_tokens"]
|
||||
|
||||
# Handle frequency / presence_penalty, which needs renaming and checking
|
||||
if "frequency_penalty" in openai_payload:
|
||||
ollama_options["repeat_penalty"] = openai_payload["frequency_penalty"]
|
||||
|
||||
if "presence_penalty" in openai_payload and "penalty" not in ollama_options:
|
||||
# We are assuming presence penalty uses a similar concept in Ollama, which needs custom handling if exists.
|
||||
ollama_options["new_topic_penalty"] = openai_payload["presence_penalty"]
|
||||
|
||||
# Add options to payload if any have been set
|
||||
if ollama_options:
|
||||
ollama_payload["options"] = ollama_options
|
||||
# Ollama lacks a "system" prompt option. It has to be provided as a direct parameter, so we copy it down.
|
||||
if "system" in ollama_options:
|
||||
ollama_payload["system"] = ollama_options["system"]
|
||||
del ollama_options[
|
||||
"system"
|
||||
] # To prevent Ollama warning of invalid option provided
|
||||
|
||||
if "metadata" in openai_payload:
|
||||
ollama_payload["metadata"] = openai_payload["metadata"]
|
||||
|
@ -24,17 +24,8 @@ def convert_ollama_tool_call_to_openai(tool_calls: dict) -> dict:
|
||||
return openai_tool_calls
|
||||
|
||||
|
||||
def convert_response_ollama_to_openai(ollama_response: dict) -> dict:
|
||||
model = ollama_response.get("model", "ollama")
|
||||
message_content = ollama_response.get("message", {}).get("content", "")
|
||||
tool_calls = ollama_response.get("message", {}).get("tool_calls", None)
|
||||
openai_tool_calls = None
|
||||
|
||||
if tool_calls:
|
||||
openai_tool_calls = convert_ollama_tool_call_to_openai(tool_calls)
|
||||
|
||||
data = ollama_response
|
||||
usage = {
|
||||
def convert_ollama_usage_to_openai(data: dict) -> dict:
|
||||
return {
|
||||
"response_token/s": (
|
||||
round(
|
||||
(
|
||||
@ -66,14 +57,42 @@ def convert_response_ollama_to_openai(ollama_response: dict) -> dict:
|
||||
"total_duration": data.get("total_duration", 0),
|
||||
"load_duration": data.get("load_duration", 0),
|
||||
"prompt_eval_count": data.get("prompt_eval_count", 0),
|
||||
"prompt_tokens": int(
|
||||
data.get("prompt_eval_count", 0)
|
||||
), # This is the OpenAI compatible key
|
||||
"prompt_eval_duration": data.get("prompt_eval_duration", 0),
|
||||
"eval_count": data.get("eval_count", 0),
|
||||
"completion_tokens": int(
|
||||
data.get("eval_count", 0)
|
||||
), # This is the OpenAI compatible key
|
||||
"eval_duration": data.get("eval_duration", 0),
|
||||
"approximate_total": (lambda s: f"{s // 3600}h{(s % 3600) // 60}m{s % 60}s")(
|
||||
(data.get("total_duration", 0) or 0) // 1_000_000_000
|
||||
),
|
||||
"total_tokens": int( # This is the OpenAI compatible key
|
||||
data.get("prompt_eval_count", 0) + data.get("eval_count", 0)
|
||||
),
|
||||
"completion_tokens_details": { # This is the OpenAI compatible key
|
||||
"reasoning_tokens": 0,
|
||||
"accepted_prediction_tokens": 0,
|
||||
"rejected_prediction_tokens": 0,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def convert_response_ollama_to_openai(ollama_response: dict) -> dict:
|
||||
model = ollama_response.get("model", "ollama")
|
||||
message_content = ollama_response.get("message", {}).get("content", "")
|
||||
tool_calls = ollama_response.get("message", {}).get("tool_calls", None)
|
||||
openai_tool_calls = None
|
||||
|
||||
if tool_calls:
|
||||
openai_tool_calls = convert_ollama_tool_call_to_openai(tool_calls)
|
||||
|
||||
data = ollama_response
|
||||
|
||||
usage = convert_ollama_usage_to_openai(data)
|
||||
|
||||
response = openai_chat_completion_message_template(
|
||||
model, message_content, openai_tool_calls, usage
|
||||
)
|
||||
@ -96,45 +115,7 @@ async def convert_streaming_response_ollama_to_openai(ollama_streaming_response)
|
||||
|
||||
usage = None
|
||||
if done:
|
||||
usage = {
|
||||
"response_token/s": (
|
||||
round(
|
||||
(
|
||||
(
|
||||
data.get("eval_count", 0)
|
||||
/ ((data.get("eval_duration", 0) / 10_000_000))
|
||||
)
|
||||
* 100
|
||||
),
|
||||
2,
|
||||
)
|
||||
if data.get("eval_duration", 0) > 0
|
||||
else "N/A"
|
||||
),
|
||||
"prompt_token/s": (
|
||||
round(
|
||||
(
|
||||
(
|
||||
data.get("prompt_eval_count", 0)
|
||||
/ ((data.get("prompt_eval_duration", 0) / 10_000_000))
|
||||
)
|
||||
* 100
|
||||
),
|
||||
2,
|
||||
)
|
||||
if data.get("prompt_eval_duration", 0) > 0
|
||||
else "N/A"
|
||||
),
|
||||
"total_duration": data.get("total_duration", 0),
|
||||
"load_duration": data.get("load_duration", 0),
|
||||
"prompt_eval_count": data.get("prompt_eval_count", 0),
|
||||
"prompt_eval_duration": data.get("prompt_eval_duration", 0),
|
||||
"eval_count": data.get("eval_count", 0),
|
||||
"eval_duration": data.get("eval_duration", 0),
|
||||
"approximate_total": (
|
||||
lambda s: f"{s // 3600}h{(s % 3600) // 60}m{s % 60}s"
|
||||
)((data.get("total_duration", 0) or 0) // 1_000_000_000),
|
||||
}
|
||||
usage = convert_ollama_usage_to_openai(data)
|
||||
|
||||
data = openai_chat_chunk_message_template(
|
||||
model, message_content if not done else None, openai_tool_calls, usage
|
||||
|
@ -1,10 +1,10 @@
|
||||
fastapi==0.115.7
|
||||
uvicorn[standard]==0.30.6
|
||||
pydantic==2.9.2
|
||||
pydantic==2.10.6
|
||||
python-multipart==0.0.18
|
||||
|
||||
python-socketio==5.11.3
|
||||
python-jose==3.3.0
|
||||
python-jose==3.4.0
|
||||
passlib[bcrypt]==1.7.4
|
||||
|
||||
requests==2.32.3
|
||||
@ -45,7 +45,7 @@ chromadb==0.6.2
|
||||
pymilvus==2.5.0
|
||||
qdrant-client~=1.12.0
|
||||
opensearch-py==2.8.0
|
||||
|
||||
playwright==1.49.1 # Caution: version must match docker-compose.playwright.yaml
|
||||
|
||||
transformers
|
||||
sentence-transformers==3.3.1
|
||||
@ -59,7 +59,7 @@ fpdf2==2.8.2
|
||||
pymdown-extensions==10.14.2
|
||||
docx2txt==0.8
|
||||
python-pptx==1.0.0
|
||||
unstructured==0.16.11
|
||||
unstructured==0.16.17
|
||||
nltk==3.9.1
|
||||
Markdown==3.7
|
||||
pypandoc==1.13
|
||||
@ -103,5 +103,12 @@ pytest-docker~=3.1.1
|
||||
googleapis-common-protos==1.63.2
|
||||
google-cloud-storage==2.19.0
|
||||
|
||||
azure-identity==1.20.0
|
||||
azure-storage-blob==12.24.1
|
||||
|
||||
|
||||
## LDAP
|
||||
ldap3==2.9.1
|
||||
|
||||
## Firecrawl
|
||||
firecrawl-py==1.12.0
|
||||
|
@ -3,6 +3,17 @@
|
||||
SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
|
||||
cd "$SCRIPT_DIR" || exit
|
||||
|
||||
# Add conditional Playwright browser installation
|
||||
if [[ "${RAG_WEB_LOADER_ENGINE,,}" == "playwright" ]]; then
|
||||
if [[ -z "${PLAYWRIGHT_WS_URI}" ]]; then
|
||||
echo "Installing Playwright browsers..."
|
||||
playwright install chromium
|
||||
playwright install-deps chromium
|
||||
fi
|
||||
|
||||
python -c "import nltk; nltk.download('punkt_tab')"
|
||||
fi
|
||||
|
||||
KEY_FILE=.webui_secret_key
|
||||
|
||||
PORT="${PORT:-8080}"
|
||||
|
@ -6,6 +6,17 @@ SETLOCAL ENABLEDELAYEDEXPANSION
|
||||
SET "SCRIPT_DIR=%~dp0"
|
||||
cd /d "%SCRIPT_DIR%" || exit /b
|
||||
|
||||
:: Add conditional Playwright browser installation
|
||||
IF /I "%RAG_WEB_LOADER_ENGINE%" == "playwright" (
|
||||
IF "%PLAYWRIGHT_WS_URI%" == "" (
|
||||
echo Installing Playwright browsers...
|
||||
playwright install chromium
|
||||
playwright install-deps chromium
|
||||
)
|
||||
|
||||
python -c "import nltk; nltk.download('punkt_tab')"
|
||||
)
|
||||
|
||||
SET "KEY_FILE=.webui_secret_key"
|
||||
IF "%PORT%"=="" SET PORT=8080
|
||||
IF "%HOST%"=="" SET HOST=0.0.0.0
|
||||
|
10
docker-compose.playwright.yaml
Normal file
10
docker-compose.playwright.yaml
Normal file
@ -0,0 +1,10 @@
|
||||
services:
|
||||
playwright:
|
||||
image: mcr.microsoft.com/playwright:v1.49.1-noble # Version must match requirements.txt
|
||||
container_name: playwright
|
||||
command: npx -y playwright@1.49.1 run-server --port 3000 --host 0.0.0.0
|
||||
|
||||
open-webui:
|
||||
environment:
|
||||
- 'RAG_WEB_LOADER_ENGINE=playwright'
|
||||
- 'PLAYWRIGHT_WS_URI=ws://playwright:3000'
|
14
package-lock.json
generated
14
package-lock.json
generated
@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "open-webui",
|
||||
"version": "0.5.14",
|
||||
"version": "0.5.15",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "open-webui",
|
||||
"version": "0.5.14",
|
||||
"version": "0.5.15",
|
||||
"dependencies": {
|
||||
"@codemirror/lang-javascript": "^6.2.2",
|
||||
"@codemirror/lang-python": "^6.1.6",
|
||||
@ -63,6 +63,7 @@
|
||||
"svelte-sonner": "^0.3.19",
|
||||
"tippy.js": "^6.3.7",
|
||||
"turndown": "^7.2.0",
|
||||
"undici": "^7.3.0",
|
||||
"uuid": "^9.0.1",
|
||||
"vite-plugin-static-copy": "^2.2.0"
|
||||
},
|
||||
@ -11528,6 +11529,15 @@
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/undici": {
|
||||
"version": "7.3.0",
|
||||
"resolved": "https://registry.npmjs.org/undici/-/undici-7.3.0.tgz",
|
||||
"integrity": "sha512-Qy96NND4Dou5jKoSJ2gm8ax8AJM/Ey9o9mz7KN1bb9GP+G0l20Zw8afxTnY2f4b7hmhn/z8aC2kfArVQlAhFBw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=20.18.1"
|
||||
}
|
||||
},
|
||||
"node_modules/undici-types": {
|
||||
"version": "5.26.5",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "open-webui",
|
||||
"version": "0.5.14",
|
||||
"version": "0.5.15",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "npm run pyodide:fetch && vite dev --host",
|
||||
@ -106,6 +106,7 @@
|
||||
"svelte-sonner": "^0.3.19",
|
||||
"tippy.js": "^6.3.7",
|
||||
"turndown": "^7.2.0",
|
||||
"undici": "^7.3.0",
|
||||
"uuid": "^9.0.1",
|
||||
"vite-plugin-static-copy": "^2.2.0"
|
||||
},
|
||||
|
@ -8,11 +8,11 @@ license = { file = "LICENSE" }
|
||||
dependencies = [
|
||||
"fastapi==0.115.7",
|
||||
"uvicorn[standard]==0.30.6",
|
||||
"pydantic==2.9.2",
|
||||
"pydantic==2.10.6",
|
||||
"python-multipart==0.0.18",
|
||||
|
||||
"python-socketio==5.11.3",
|
||||
"python-jose==3.3.0",
|
||||
"python-jose==3.4.0",
|
||||
"passlib[bcrypt]==1.7.4",
|
||||
|
||||
"requests==2.32.3",
|
||||
@ -53,6 +53,7 @@ dependencies = [
|
||||
"pymilvus==2.5.0",
|
||||
"qdrant-client~=1.12.0",
|
||||
"opensearch-py==2.8.0",
|
||||
"playwright==1.49.1",
|
||||
|
||||
"transformers",
|
||||
"sentence-transformers==3.3.1",
|
||||
@ -65,7 +66,7 @@ dependencies = [
|
||||
"pymdown-extensions==10.14.2",
|
||||
"docx2txt==0.8",
|
||||
"python-pptx==1.0.0",
|
||||
"unstructured==0.16.11",
|
||||
"unstructured==0.16.17",
|
||||
"nltk==3.9.1",
|
||||
"Markdown==3.7",
|
||||
"pypandoc==1.13",
|
||||
@ -108,7 +109,13 @@ dependencies = [
|
||||
"googleapis-common-protos==1.63.2",
|
||||
"google-cloud-storage==2.19.0",
|
||||
|
||||
"azure-identity==1.20.0",
|
||||
"azure-storage-blob==12.24.1",
|
||||
|
||||
"ldap3==2.9.1",
|
||||
|
||||
"firecrawl-py==1.12.0",
|
||||
|
||||
"gcp-storage-emulator>=2024.8.3",
|
||||
]
|
||||
readme = "README.md"
|
||||
|
@ -74,6 +74,7 @@ usage() {
|
||||
echo " --enable-api[port=PORT] Enable API and expose it on the specified port."
|
||||
echo " --webui[port=PORT] Set the port for the web user interface."
|
||||
echo " --data[folder=PATH] Bind mount for ollama data folder (by default will create the 'ollama' volume)."
|
||||
echo " --playwright Enable Playwright support for web scraping."
|
||||
echo " --build Build the docker image before running the compose project."
|
||||
echo " --drop Drop the compose project."
|
||||
echo " -q, --quiet Run script in headless mode."
|
||||
@ -100,6 +101,7 @@ webui_port=3000
|
||||
headless=false
|
||||
build_image=false
|
||||
kill_compose=false
|
||||
enable_playwright=false
|
||||
|
||||
# Function to extract value from the parameter
|
||||
extract_value() {
|
||||
@ -129,6 +131,9 @@ while [[ $# -gt 0 ]]; do
|
||||
value=$(extract_value "$key")
|
||||
data_dir=${value:-"./ollama-data"}
|
||||
;;
|
||||
--playwright)
|
||||
enable_playwright=true
|
||||
;;
|
||||
--drop)
|
||||
kill_compose=true
|
||||
;;
|
||||
@ -182,6 +187,9 @@ else
|
||||
DEFAULT_COMPOSE_COMMAND+=" -f docker-compose.data.yaml"
|
||||
export OLLAMA_DATA_DIR=$data_dir # Set OLLAMA_DATA_DIR environment variable
|
||||
fi
|
||||
if [[ $enable_playwright == true ]]; then
|
||||
DEFAULT_COMPOSE_COMMAND+=" -f docker-compose.playwright.yaml"
|
||||
fi
|
||||
if [[ -n $webui_port ]]; then
|
||||
export OPEN_WEBUI_PORT=$webui_port # Set OPEN_WEBUI_PORT environment variable
|
||||
fi
|
||||
@ -201,6 +209,7 @@ echo -e " ${GREEN}${BOLD}GPU Count:${NC} ${OLLAMA_GPU_COUNT:-Not Enabled}"
|
||||
echo -e " ${GREEN}${BOLD}WebAPI Port:${NC} ${OLLAMA_WEBAPI_PORT:-Not Enabled}"
|
||||
echo -e " ${GREEN}${BOLD}Data Folder:${NC} ${data_dir:-Using ollama volume}"
|
||||
echo -e " ${GREEN}${BOLD}WebUI Port:${NC} $webui_port"
|
||||
echo -e " ${GREEN}${BOLD}Playwright:${NC} ${enable_playwright:-false}"
|
||||
echo
|
||||
|
||||
if [[ $headless == true ]]; then
|
||||
|
@ -16,8 +16,39 @@ const packages = [
|
||||
];
|
||||
|
||||
import { loadPyodide } from 'pyodide';
|
||||
import { setGlobalDispatcher, ProxyAgent } from 'undici';
|
||||
import { writeFile, readFile, copyFile, readdir, rmdir } from 'fs/promises';
|
||||
|
||||
/**
|
||||
* Loading network proxy configurations from the environment variables.
|
||||
* And the proxy config with lowercase name has the highest priority to use.
|
||||
*/
|
||||
function initNetworkProxyFromEnv() {
|
||||
// we assume all subsequent requests in this script are HTTPS:
|
||||
// https://cdn.jsdelivr.net
|
||||
// https://pypi.org
|
||||
// https://files.pythonhosted.org
|
||||
const allProxy = process.env.all_proxy || process.env.ALL_PROXY;
|
||||
const httpsProxy = process.env.https_proxy || process.env.HTTPS_PROXY;
|
||||
const httpProxy = process.env.http_proxy || process.env.HTTP_PROXY;
|
||||
const preferedProxy = httpsProxy || allProxy || httpProxy;
|
||||
/**
|
||||
* use only http(s) proxy because socks5 proxy is not supported currently:
|
||||
* @see https://github.com/nodejs/undici/issues/2224
|
||||
*/
|
||||
if (!preferedProxy || !preferedProxy.startsWith('http')) return;
|
||||
let preferedProxyURL;
|
||||
try {
|
||||
preferedProxyURL = new URL(preferedProxy).toString();
|
||||
} catch {
|
||||
console.warn(`Invalid network proxy URL: "${preferedProxy}"`);
|
||||
return;
|
||||
}
|
||||
const dispatcher = new ProxyAgent({ uri: preferedProxyURL });
|
||||
setGlobalDispatcher(dispatcher);
|
||||
console.log(`Initialized network proxy "${preferedProxy}" from env`);
|
||||
}
|
||||
|
||||
async function downloadPackages() {
|
||||
console.log('Setting up pyodide + micropip');
|
||||
|
||||
@ -84,5 +115,6 @@ async function copyPyodide() {
|
||||
}
|
||||
}
|
||||
|
||||
initNetworkProxyFromEnv();
|
||||
await downloadPackages();
|
||||
await copyPyodide();
|
||||
|
@ -101,7 +101,7 @@ li p {
|
||||
|
||||
/* Dark theme scrollbar styles */
|
||||
.dark ::-webkit-scrollbar-thumb {
|
||||
background-color: rgba(33, 33, 33, 0.8); /* Darker color for dark theme */
|
||||
background-color: rgba(42, 42, 42, 0.8); /* Darker color for dark theme */
|
||||
border-color: rgba(0, 0, 0, var(--tw-border-opacity));
|
||||
}
|
||||
|
||||
|
@ -91,45 +91,65 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class=" flex gap-2 w-full items-center justify-between">
|
||||
<div class="text-xs font-medium">
|
||||
{$i18n.t('Jupyter Auth')}
|
||||
</div>
|
||||
<div class="mb-2.5 flex flex-col gap-1.5 w-full">
|
||||
<div class=" flex gap-2 w-full items-center justify-between">
|
||||
<div class="text-xs font-medium">
|
||||
{$i18n.t('Jupyter Auth')}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<select
|
||||
class="dark:bg-gray-900 w-fit pr-8 rounded-sm px-2 p-1 text-xs bg-transparent outline-hidden text-left"
|
||||
bind:value={config.CODE_EXECUTION_JUPYTER_AUTH}
|
||||
placeholder={$i18n.t('Select an auth method')}
|
||||
>
|
||||
<option selected value="">{$i18n.t('None')}</option>
|
||||
<option value="token">{$i18n.t('Token')}</option>
|
||||
<option value="password">{$i18n.t('Password')}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{#if config.CODE_EXECUTION_JUPYTER_AUTH}
|
||||
<div class="flex w-full gap-2">
|
||||
<div class="flex-1">
|
||||
{#if config.CODE_EXECUTION_JUPYTER_AUTH === 'password'}
|
||||
<SensitiveInput
|
||||
type="text"
|
||||
placeholder={$i18n.t('Enter Jupyter Password')}
|
||||
bind:value={config.CODE_EXECUTION_JUPYTER_AUTH_PASSWORD}
|
||||
autocomplete="off"
|
||||
/>
|
||||
{:else}
|
||||
<SensitiveInput
|
||||
type="text"
|
||||
placeholder={$i18n.t('Enter Jupyter Token')}
|
||||
bind:value={config.CODE_EXECUTION_JUPYTER_AUTH_TOKEN}
|
||||
autocomplete="off"
|
||||
/>
|
||||
{/if}
|
||||
<div>
|
||||
<select
|
||||
class="dark:bg-gray-900 w-fit pr-8 rounded-sm px-2 p-1 text-xs bg-transparent outline-hidden text-left"
|
||||
bind:value={config.CODE_EXECUTION_JUPYTER_AUTH}
|
||||
placeholder={$i18n.t('Select an auth method')}
|
||||
>
|
||||
<option selected value="">{$i18n.t('None')}</option>
|
||||
<option value="token">{$i18n.t('Token')}</option>
|
||||
<option value="password">{$i18n.t('Password')}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if config.CODE_EXECUTION_JUPYTER_AUTH}
|
||||
<div class="flex w-full gap-2">
|
||||
<div class="flex-1">
|
||||
{#if config.CODE_EXECUTION_JUPYTER_AUTH === 'password'}
|
||||
<SensitiveInput
|
||||
type="text"
|
||||
placeholder={$i18n.t('Enter Jupyter Password')}
|
||||
bind:value={config.CODE_EXECUTION_JUPYTER_AUTH_PASSWORD}
|
||||
autocomplete="off"
|
||||
/>
|
||||
{:else}
|
||||
<SensitiveInput
|
||||
type="text"
|
||||
placeholder={$i18n.t('Enter Jupyter Token')}
|
||||
bind:value={config.CODE_EXECUTION_JUPYTER_AUTH_TOKEN}
|
||||
autocomplete="off"
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<div class="flex gap-2 w-full items-center justify-between">
|
||||
<div class="text-xs font-medium">
|
||||
{$i18n.t('Code Execution Timeout')}
|
||||
</div>
|
||||
|
||||
<div class="">
|
||||
<Tooltip content={$i18n.t('Enter timeout in seconds')}>
|
||||
<input
|
||||
class="dark:bg-gray-900 w-fit rounded-sm px-2 p-1 text-xs bg-transparent outline-hidden text-right"
|
||||
type="number"
|
||||
bind:value={config.CODE_EXECUTION_JUPYTER_TIMEOUT}
|
||||
placeholder={$i18n.t('e.g. 60')}
|
||||
autocomplete="off"
|
||||
/>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
@ -197,45 +217,65 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex gap-2 w-full items-center justify-between">
|
||||
<div class="text-xs font-medium">
|
||||
{$i18n.t('Jupyter Auth')}
|
||||
</div>
|
||||
<div class="mb-2.5 flex flex-col gap-1.5 w-full">
|
||||
<div class="flex gap-2 w-full items-center justify-between">
|
||||
<div class="text-xs font-medium">
|
||||
{$i18n.t('Jupyter Auth')}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<select
|
||||
class="dark:bg-gray-900 w-fit pr-8 rounded-sm px-2 p-1 text-xs bg-transparent outline-hidden text-left"
|
||||
bind:value={config.CODE_INTERPRETER_JUPYTER_AUTH}
|
||||
placeholder={$i18n.t('Select an auth method')}
|
||||
>
|
||||
<option selected value="">{$i18n.t('None')}</option>
|
||||
<option value="token">{$i18n.t('Token')}</option>
|
||||
<option value="password">{$i18n.t('Password')}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{#if config.CODE_INTERPRETER_JUPYTER_AUTH}
|
||||
<div class="flex w-full gap-2">
|
||||
<div class="flex-1">
|
||||
{#if config.CODE_INTERPRETER_JUPYTER_AUTH === 'password'}
|
||||
<SensitiveInput
|
||||
type="text"
|
||||
placeholder={$i18n.t('Enter Jupyter Password')}
|
||||
bind:value={config.CODE_INTERPRETER_JUPYTER_AUTH_PASSWORD}
|
||||
autocomplete="off"
|
||||
/>
|
||||
{:else}
|
||||
<SensitiveInput
|
||||
type="text"
|
||||
placeholder={$i18n.t('Enter Jupyter Token')}
|
||||
bind:value={config.CODE_INTERPRETER_JUPYTER_AUTH_TOKEN}
|
||||
autocomplete="off"
|
||||
/>
|
||||
{/if}
|
||||
<div>
|
||||
<select
|
||||
class="dark:bg-gray-900 w-fit pr-8 rounded-sm px-2 p-1 text-xs bg-transparent outline-hidden text-left"
|
||||
bind:value={config.CODE_INTERPRETER_JUPYTER_AUTH}
|
||||
placeholder={$i18n.t('Select an auth method')}
|
||||
>
|
||||
<option selected value="">{$i18n.t('None')}</option>
|
||||
<option value="token">{$i18n.t('Token')}</option>
|
||||
<option value="password">{$i18n.t('Password')}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if config.CODE_INTERPRETER_JUPYTER_AUTH}
|
||||
<div class="flex w-full gap-2">
|
||||
<div class="flex-1">
|
||||
{#if config.CODE_INTERPRETER_JUPYTER_AUTH === 'password'}
|
||||
<SensitiveInput
|
||||
type="text"
|
||||
placeholder={$i18n.t('Enter Jupyter Password')}
|
||||
bind:value={config.CODE_INTERPRETER_JUPYTER_AUTH_PASSWORD}
|
||||
autocomplete="off"
|
||||
/>
|
||||
{:else}
|
||||
<SensitiveInput
|
||||
type="text"
|
||||
placeholder={$i18n.t('Enter Jupyter Token')}
|
||||
bind:value={config.CODE_INTERPRETER_JUPYTER_AUTH_TOKEN}
|
||||
autocomplete="off"
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<div class="flex gap-2 w-full items-center justify-between">
|
||||
<div class="text-xs font-medium">
|
||||
{$i18n.t('Code Execution Timeout')}
|
||||
</div>
|
||||
|
||||
<div class="">
|
||||
<Tooltip content={$i18n.t('Enter timeout in seconds')}>
|
||||
<input
|
||||
class="dark:bg-gray-900 w-fit rounded-sm px-2 p-1 text-xs bg-transparent outline-hidden text-right"
|
||||
type="number"
|
||||
bind:value={config.CODE_INTERPRETER_JUPYTER_TIMEOUT}
|
||||
placeholder={$i18n.t('e.g. 60')}
|
||||
autocomplete="off"
|
||||
/>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<hr class="border-gray-100 dark:border-gray-850 my-2" />
|
||||
|
@ -27,7 +27,6 @@
|
||||
import SensitiveInput from '$lib/components/common/SensitiveInput.svelte';
|
||||
import Tooltip from '$lib/components/common/Tooltip.svelte';
|
||||
import Switch from '$lib/components/common/Switch.svelte';
|
||||
import { text } from '@sveltejs/kit';
|
||||
import Textarea from '$lib/components/common/Textarea.svelte';
|
||||
|
||||
const i18n = getContext('i18n');
|
||||
@ -56,6 +55,8 @@
|
||||
let chunkOverlap = 0;
|
||||
let pdfExtractImages = true;
|
||||
|
||||
let RAG_FULL_CONTEXT = false;
|
||||
|
||||
let enableGoogleDriveIntegration = false;
|
||||
|
||||
let OpenAIUrl = '';
|
||||
@ -182,6 +183,7 @@
|
||||
max_size: fileMaxSize === '' ? null : fileMaxSize,
|
||||
max_count: fileMaxCount === '' ? null : fileMaxCount
|
||||
},
|
||||
RAG_FULL_CONTEXT: RAG_FULL_CONTEXT,
|
||||
chunk: {
|
||||
text_splitter: textSplitter,
|
||||
chunk_overlap: chunkOverlap,
|
||||
@ -242,6 +244,8 @@
|
||||
chunkSize = res.chunk.chunk_size;
|
||||
chunkOverlap = res.chunk.chunk_overlap;
|
||||
|
||||
RAG_FULL_CONTEXT = res.RAG_FULL_CONTEXT;
|
||||
|
||||
contentExtractionEngine = res.content_extraction.engine;
|
||||
tikaServerUrl = res.content_extraction.tika_server_url;
|
||||
showTikaServerUrl = contentExtractionEngine === 'tika';
|
||||
@ -388,6 +392,19 @@
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class=" py-0.5 flex w-full justify-between">
|
||||
<div class=" self-center text-xs font-medium">{$i18n.t('Full Context Mode')}</div>
|
||||
<div class="flex items-center relative">
|
||||
<Tooltip
|
||||
content={RAG_FULL_CONTEXT
|
||||
? 'Inject entire contents as context for comprehensive processing, this is recommended for complex queries.'
|
||||
: 'Default to segmented retrieval for focused and relevant content extraction, this is recommended for most cases.'}
|
||||
>
|
||||
<Switch bind:state={RAG_FULL_CONTEXT} />
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr class="border-gray-100 dark:border-gray-850" />
|
||||
|
@ -261,6 +261,9 @@
|
||||
} else if (config.engine === 'openai' && config.openai.OPENAI_API_KEY === '') {
|
||||
toast.error($i18n.t('OpenAI API Key is required.'));
|
||||
config.enabled = false;
|
||||
} else if (config.engine === 'gemini' && config.gemini.GEMINI_API_KEY === '') {
|
||||
toast.error($i18n.t('Gemini API Key is required.'));
|
||||
config.enabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -294,6 +297,7 @@
|
||||
<option value="openai">{$i18n.t('Default (Open AI)')}</option>
|
||||
<option value="comfyui">{$i18n.t('ComfyUI')}</option>
|
||||
<option value="automatic1111">{$i18n.t('Automatic1111')}</option>
|
||||
<option value="gemini">{$i18n.t('Gemini')}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
@ -605,6 +609,24 @@
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{:else if config?.engine === 'gemini'}
|
||||
<div>
|
||||
<div class=" mb-1.5 text-sm font-medium">{$i18n.t('Gemini API Config')}</div>
|
||||
|
||||
<div class="flex gap-2 mb-1">
|
||||
<input
|
||||
class="flex-1 w-full text-sm bg-transparent outline-none"
|
||||
placeholder={$i18n.t('API Base URL')}
|
||||
bind:value={config.gemini.GEMINI_API_BASE_URL}
|
||||
required
|
||||
/>
|
||||
|
||||
<SensitiveInput
|
||||
placeholder={$i18n.t('API Key')}
|
||||
bind:value={config.gemini.GEMINI_API_KEY}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
|
@ -51,7 +51,7 @@
|
||||
onMount(async () => {
|
||||
taskConfig = await getTaskConfig(localStorage.token);
|
||||
|
||||
promptSuggestions = $config?.default_prompt_suggestions;
|
||||
promptSuggestions = $config?.default_prompt_suggestions ?? [];
|
||||
banners = await getBanners(localStorage.token);
|
||||
});
|
||||
|
||||
|
@ -85,8 +85,9 @@
|
||||
return true;
|
||||
} else {
|
||||
let name = user.name.toLowerCase();
|
||||
let email = user.email.toLowerCase();
|
||||
const query = search.toLowerCase();
|
||||
return name.includes(query);
|
||||
return name.includes(query) || email.includes(query);
|
||||
}
|
||||
})
|
||||
.sort((a, b) => {
|
||||
|
@ -430,7 +430,7 @@
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if webSearchEnabled || ($settings?.webSearch ?? false) === 'always'}
|
||||
{#if webSearchEnabled || ($config?.features?.enable_web_search && ($settings?.webSearch ?? false)) === 'always'}
|
||||
<div class="flex items-center justify-between w-full">
|
||||
<div class="flex items-center gap-2.5 text-sm dark:text-gray-500">
|
||||
<div class="pl-1">
|
||||
|
@ -7,6 +7,7 @@
|
||||
|
||||
const i18n = getContext('i18n');
|
||||
|
||||
export let id = '';
|
||||
export let sources = [];
|
||||
|
||||
let citations = [];
|
||||
@ -100,7 +101,7 @@
|
||||
<div class="flex text-xs font-medium flex-wrap">
|
||||
{#each citations as citation, idx}
|
||||
<button
|
||||
id={`source-${idx}`}
|
||||
id={`source-${id}-${idx}`}
|
||||
class="no-toggle outline-hidden flex dark:text-gray-300 p-1 bg-white dark:bg-gray-900 rounded-xl max-w-96"
|
||||
on:click={() => {
|
||||
showCitationModal = true;
|
||||
@ -179,7 +180,7 @@
|
||||
<div class="flex text-xs font-medium flex-wrap">
|
||||
{#each citations as citation, idx}
|
||||
<button
|
||||
id={`source-${idx}`}
|
||||
id={`source-${id}-${idx}`}
|
||||
class="no-toggle outline-hidden flex dark:text-gray-300 p-1 bg-gray-50 hover:bg-gray-100 dark:bg-gray-900 dark:hover:bg-gray-850 transition rounded-xl max-w-96"
|
||||
on:click={() => {
|
||||
showCitationModal = true;
|
||||
|
@ -123,6 +123,12 @@
|
||||
};
|
||||
|
||||
const executePython = async (code) => {
|
||||
result = null;
|
||||
stdout = null;
|
||||
stderr = null;
|
||||
|
||||
executing = true;
|
||||
|
||||
if ($config?.code?.engine === 'jupyter') {
|
||||
const output = await executeCode(localStorage.token, code).catch((error) => {
|
||||
toast.error(`${error}`);
|
||||
@ -130,22 +136,74 @@
|
||||
});
|
||||
|
||||
if (output) {
|
||||
stdout = output.stdout;
|
||||
stderr = output.stderr;
|
||||
result = output.result;
|
||||
if (output['stdout']) {
|
||||
stdout = output['stdout'];
|
||||
const stdoutLines = stdout.split('\n');
|
||||
|
||||
for (const [idx, line] of stdoutLines.entries()) {
|
||||
if (line.startsWith('data:image/png;base64')) {
|
||||
if (files) {
|
||||
files.push({
|
||||
type: 'image/png',
|
||||
data: line
|
||||
});
|
||||
} else {
|
||||
files = [
|
||||
{
|
||||
type: 'image/png',
|
||||
data: line
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
if (stdout.startsWith(`${line}\n`)) {
|
||||
stdout = stdout.replace(`${line}\n`, ``);
|
||||
} else if (stdout.startsWith(`${line}`)) {
|
||||
stdout = stdout.replace(`${line}`, ``);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (output['result']) {
|
||||
result = output['result'];
|
||||
const resultLines = result.split('\n');
|
||||
|
||||
for (const [idx, line] of resultLines.entries()) {
|
||||
if (line.startsWith('data:image/png;base64')) {
|
||||
if (files) {
|
||||
files.push({
|
||||
type: 'image/png',
|
||||
data: line
|
||||
});
|
||||
} else {
|
||||
files = [
|
||||
{
|
||||
type: 'image/png',
|
||||
data: line
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
if (result.startsWith(`${line}\n`)) {
|
||||
result = result.replace(`${line}\n`, ``);
|
||||
} else if (result.startsWith(`${line}`)) {
|
||||
result = result.replace(`${line}`, ``);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
output['stderr'] && (stderr = output['stderr']);
|
||||
}
|
||||
|
||||
executing = false;
|
||||
} else {
|
||||
executePythonAsWorker(code);
|
||||
}
|
||||
};
|
||||
|
||||
const executePythonAsWorker = async (code) => {
|
||||
result = null;
|
||||
stdout = null;
|
||||
stderr = null;
|
||||
|
||||
executing = true;
|
||||
|
||||
let packages = [
|
||||
code.includes('requests') ? 'requests' : null,
|
||||
code.includes('bs4') ? 'beautifulsoup4' : null,
|
||||
@ -205,7 +263,40 @@
|
||||
];
|
||||
}
|
||||
|
||||
stdout = stdout.replace(`${line}\n`, ``);
|
||||
if (stdout.startsWith(`${line}\n`)) {
|
||||
stdout = stdout.replace(`${line}\n`, ``);
|
||||
} else if (stdout.startsWith(`${line}`)) {
|
||||
stdout = stdout.replace(`${line}`, ``);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (data['result']) {
|
||||
result = data['result'];
|
||||
const resultLines = result.split('\n');
|
||||
|
||||
for (const [idx, line] of resultLines.entries()) {
|
||||
if (line.startsWith('data:image/png;base64')) {
|
||||
if (files) {
|
||||
files.push({
|
||||
type: 'image/png',
|
||||
data: line
|
||||
});
|
||||
} else {
|
||||
files = [
|
||||
{
|
||||
type: 'image/png',
|
||||
data: line
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
if (result.startsWith(`${line}\n`)) {
|
||||
result = result.replace(`${line}\n`, ``);
|
||||
} else if (result.startsWith(`${line}`)) {
|
||||
result = result.replace(`${line}`, ``);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -391,7 +482,7 @@
|
||||
class="bg-gray-50 dark:bg-[#202123] dark:text-white max-w-full overflow-x-auto scrollbar-hidden"
|
||||
/>
|
||||
|
||||
{#if executing || stdout || stderr || result}
|
||||
{#if executing || stdout || stderr || result || files}
|
||||
<div
|
||||
class="bg-gray-50 dark:bg-[#202123] dark:text-white rounded-b-lg! py-4 px-4 flex flex-col gap-2"
|
||||
>
|
||||
@ -404,7 +495,13 @@
|
||||
{#if stdout || stderr}
|
||||
<div class=" ">
|
||||
<div class=" text-gray-500 text-xs mb-1">STDOUT/STDERR</div>
|
||||
<div class="text-sm">{stdout || stderr}</div>
|
||||
<div
|
||||
class="text-sm {stdout?.split('\n')?.length > 100
|
||||
? `max-h-96`
|
||||
: ''} overflow-y-auto"
|
||||
>
|
||||
{stdout || stderr}
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
{#if result || files}
|
||||
|
@ -120,6 +120,11 @@
|
||||
sourceIds={(sources ?? []).reduce((acc, s) => {
|
||||
let ids = [];
|
||||
s.document.forEach((document, index) => {
|
||||
if (model?.info?.meta?.capabilities?.citations == false) {
|
||||
ids.push('N/A');
|
||||
return ids;
|
||||
}
|
||||
|
||||
const metadata = s.metadata?.[index];
|
||||
const id = metadata?.source ?? 'N/A';
|
||||
|
||||
|
@ -29,7 +29,7 @@
|
||||
{:else if token.text.includes(`<iframe src="${WEBUI_BASE_URL}/api/v1/files/`)}
|
||||
{@html `${token.text}`}
|
||||
{:else if token.text.includes(`<source_id`)}
|
||||
<Source {token} onClick={onSourceClick} />
|
||||
<Source {id} {token} onClick={onSourceClick} />
|
||||
{:else}
|
||||
{token.text}
|
||||
{/if}
|
||||
|
@ -76,7 +76,7 @@
|
||||
{#if token.type === 'hr'}
|
||||
<hr class=" border-gray-100 dark:border-gray-850" />
|
||||
{:else if token.type === 'heading'}
|
||||
<svelte:element this={headerComponent(token.depth)}>
|
||||
<svelte:element this={headerComponent(token.depth)} dir="auto">
|
||||
<MarkdownInlineTokens id={`${id}-${tokenIdx}-h`} tokens={token.tokens} {onSourceClick} />
|
||||
</svelte:element>
|
||||
{:else if token.type === 'code'}
|
||||
@ -176,7 +176,7 @@
|
||||
{#if token.ordered}
|
||||
<ol start={token.start || 1}>
|
||||
{#each token.items as item, itemIdx}
|
||||
<li dir="auto">
|
||||
<li dir="auto" class="text-start">
|
||||
{#if item?.task}
|
||||
<input
|
||||
class=" translate-y-[1px] -translate-x-1"
|
||||
@ -208,7 +208,7 @@
|
||||
{:else}
|
||||
<ul>
|
||||
{#each token.items as item, itemIdx}
|
||||
<li dir="auto">
|
||||
<li dir="auto" class="text-start">
|
||||
{#if item?.task}
|
||||
<input
|
||||
class=" translate-y-[1px] -translate-x-1"
|
||||
@ -239,7 +239,12 @@
|
||||
</ul>
|
||||
{/if}
|
||||
{:else if token.type === 'details'}
|
||||
<Collapsible title={token.summary} attributes={token?.attributes} className="w-full space-y-1">
|
||||
<Collapsible
|
||||
title={token.summary}
|
||||
attributes={token?.attributes}
|
||||
className="w-full space-y-1"
|
||||
dir="auto"
|
||||
>
|
||||
<div class=" mb-1.5" slot="content">
|
||||
<svelte:self
|
||||
id={`${id}-${tokenIdx}-d`}
|
||||
|
@ -1,8 +1,9 @@
|
||||
<script lang="ts">
|
||||
export let id;
|
||||
export let token;
|
||||
export let onClick: Function = () => {};
|
||||
|
||||
let attributes: Record<string, string> = {};
|
||||
let attributes: Record<string, string | undefined> = {};
|
||||
|
||||
function extractAttributes(input: string): Record<string, string> {
|
||||
const regex = /(\w+)="([^"]*)"/g;
|
||||
@ -35,13 +36,15 @@
|
||||
$: attributes = extractAttributes(token.text);
|
||||
</script>
|
||||
|
||||
<button
|
||||
class="text-xs font-medium w-fit translate-y-[2px] px-2 py-0.5 dark:bg-white/5 dark:text-white/60 dark:hover:text-white bg-gray-50 text-black/60 hover:text-black transition rounded-lg"
|
||||
on:click={() => {
|
||||
onClick(attributes.data);
|
||||
}}
|
||||
>
|
||||
<span class="line-clamp-1">
|
||||
{formattedTitle(attributes.title)}
|
||||
</span>
|
||||
</button>
|
||||
{#if attributes.title !== 'N/A'}
|
||||
<button
|
||||
class="text-xs font-medium w-fit translate-y-[2px] px-2 py-0.5 dark:bg-white/5 dark:text-white/60 dark:hover:text-white bg-gray-50 text-black/60 hover:text-black transition rounded-lg"
|
||||
on:click={() => {
|
||||
onClick(id, attributes.data);
|
||||
}}
|
||||
>
|
||||
<span class="line-clamp-1">
|
||||
{attributes.title ? formattedTitle(attributes.title) : ''}
|
||||
</span>
|
||||
</button>
|
||||
{/if}
|
||||
|
@ -732,9 +732,9 @@
|
||||
onTaskClick={async (e) => {
|
||||
console.log(e);
|
||||
}}
|
||||
onSourceClick={async (e) => {
|
||||
console.log(e);
|
||||
let sourceButton = document.getElementById(`source-${e}`);
|
||||
onSourceClick={async (id, idx) => {
|
||||
console.log(id, idx);
|
||||
let sourceButton = document.getElementById(`source-${message.id}-${idx}`);
|
||||
const sourcesCollapsible = document.getElementById(`collapsible-sources`);
|
||||
|
||||
if (sourceButton) {
|
||||
@ -753,7 +753,7 @@
|
||||
});
|
||||
|
||||
// Try clicking the source button again
|
||||
sourceButton = document.getElementById(`source-${e}`);
|
||||
sourceButton = document.getElementById(`source-${message.id}-${idx}`);
|
||||
sourceButton && sourceButton.click();
|
||||
}
|
||||
}}
|
||||
@ -790,7 +790,7 @@
|
||||
{/if}
|
||||
|
||||
{#if (message?.sources || message?.citations) && (model?.info?.meta?.capabilities?.citations ?? true)}
|
||||
<Citations sources={message?.sources ?? message?.citations} />
|
||||
<Citations id={message?.id} sources={message?.sources ?? message?.citations} />
|
||||
{/if}
|
||||
|
||||
{#if message.code_executions}
|
||||
|
@ -1,6 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { getContext, onMount } from 'svelte';
|
||||
import { formatFileSize, getLineCount } from '$lib/utils';
|
||||
import { WEBUI_API_BASE_URL } from '$lib/constants';
|
||||
|
||||
const i18n = getContext('i18n');
|
||||
|
||||
@ -12,14 +13,15 @@
|
||||
|
||||
export let item;
|
||||
export let show = false;
|
||||
|
||||
export let edit = false;
|
||||
|
||||
let enableFullContent = false;
|
||||
$: isPDF =
|
||||
item?.meta?.content_type === 'application/pdf' ||
|
||||
(item?.name && item?.name.toLowerCase().endsWith('.pdf'));
|
||||
|
||||
onMount(() => {
|
||||
console.log(item);
|
||||
|
||||
if (item?.context === 'full') {
|
||||
enableFullContent = true;
|
||||
}
|
||||
@ -33,9 +35,16 @@
|
||||
<div>
|
||||
<div class=" font-medium text-lg dark:text-gray-100">
|
||||
<a
|
||||
href={item.url ? (item.type === 'file' ? `${item.url}/content` : `${item.url}`) : '#'}
|
||||
target="_blank"
|
||||
href="#"
|
||||
class="hover:underline line-clamp-1"
|
||||
on:click|preventDefault={() => {
|
||||
if (!isPDF && item.url) {
|
||||
window.open(
|
||||
item.type === 'file' ? `${item.url}/content` : `${item.url}`,
|
||||
'_blank'
|
||||
);
|
||||
}
|
||||
}}
|
||||
>
|
||||
{item?.name ?? 'File'}
|
||||
</a>
|
||||
@ -101,8 +110,18 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="max-h-96 overflow-scroll scrollbar-hidden text-xs whitespace-pre-wrap">
|
||||
{item?.file?.data?.content ?? 'No content'}
|
||||
<div class="max-h-[75vh] overflow-auto">
|
||||
{#if isPDF}
|
||||
<iframe
|
||||
title={item?.name}
|
||||
src={`${WEBUI_API_BASE_URL}/files/${item.id}/content`}
|
||||
class="w-full h-[70vh] border-0 rounded-lg mt-4"
|
||||
/>
|
||||
{:else}
|
||||
<div class="max-h-96 overflow-scroll scrollbar-hidden text-xs whitespace-pre-wrap">
|
||||
{item?.file?.data?.content ?? 'No content'}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
|
@ -6,7 +6,6 @@
|
||||
|
||||
export let show = true;
|
||||
export let size = 'md';
|
||||
|
||||
export let containerClassName = 'p-3';
|
||||
export let className = 'bg-gray-50 dark:bg-gray-900 rounded-2xl';
|
||||
|
||||
@ -74,7 +73,7 @@
|
||||
}}
|
||||
>
|
||||
<div
|
||||
class=" m-auto max-w-full {sizeToWidth(size)} {size !== 'full'
|
||||
class="m-auto max-w-full {sizeToWidth(size)} {size !== 'full'
|
||||
? 'mx-2'
|
||||
: ''} shadow-3xl min-h-fit scrollbar-hidden {className}"
|
||||
in:flyAndScale
|
||||
|
@ -182,6 +182,7 @@
|
||||
"Code execution": "",
|
||||
"Code Execution": "",
|
||||
"Code Execution Engine": "",
|
||||
"Code Execution Timeout": "",
|
||||
"Code formatted successfully": "",
|
||||
"Code Interpreter": "",
|
||||
"Code Interpreter Engine": "",
|
||||
@ -321,6 +322,7 @@
|
||||
"Draw": "",
|
||||
"Drop any files here to add to the conversation": "أسقط أية ملفات هنا لإضافتها إلى المحادثة",
|
||||
"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "e.g. '30s','10m'. الوحدات الزمنية الصالحة هي 's', 'm', 'h'.",
|
||||
"e.g. 60": "",
|
||||
"e.g. A filter to remove profanity from text": "",
|
||||
"e.g. My Filter": "",
|
||||
"e.g. My Tools": "",
|
||||
@ -408,6 +410,7 @@
|
||||
"Enter Tavily API Key": "",
|
||||
"Enter the public URL of your WebUI. This URL will be used to generate links in the notifications.": "",
|
||||
"Enter Tika Server URL": "",
|
||||
"Enter timeout in seconds": "",
|
||||
"Enter Top K": "أدخل Top K",
|
||||
"Enter URL (e.g. http://127.0.0.1:7860/)": "الرابط (e.g. http://127.0.0.1:7860/)",
|
||||
"Enter URL (e.g. http://localhost:11434)": "URL (e.g. http://localhost:11434)",
|
||||
@ -500,6 +503,9 @@
|
||||
"Functions allow arbitrary code execution": "",
|
||||
"Functions allow arbitrary code execution.": "",
|
||||
"Functions imported successfully": "",
|
||||
"Gemini": "",
|
||||
"Gemini API Config": "",
|
||||
"Gemini API Key is required.": "",
|
||||
"General": "عام",
|
||||
"General Settings": "الاعدادات العامة",
|
||||
"Generate an image": "",
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -182,6 +182,7 @@
|
||||
"Code execution": "",
|
||||
"Code Execution": "",
|
||||
"Code Execution Engine": "",
|
||||
"Code Execution Timeout": "",
|
||||
"Code formatted successfully": "",
|
||||
"Code Interpreter": "",
|
||||
"Code Interpreter Engine": "",
|
||||
@ -321,6 +322,7 @@
|
||||
"Draw": "",
|
||||
"Drop any files here to add to the conversation": "আলোচনায় যুক্ত করার জন্য যে কোন ফাইল এখানে ড্রপ করুন",
|
||||
"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "যেমন '30s','10m'. সময়ের অনুমোদিত অনুমোদিত এককগুলি হচ্ছে 's', 'm', 'h'.",
|
||||
"e.g. 60": "",
|
||||
"e.g. A filter to remove profanity from text": "",
|
||||
"e.g. My Filter": "",
|
||||
"e.g. My Tools": "",
|
||||
@ -408,6 +410,7 @@
|
||||
"Enter Tavily API Key": "",
|
||||
"Enter the public URL of your WebUI. This URL will be used to generate links in the notifications.": "",
|
||||
"Enter Tika Server URL": "",
|
||||
"Enter timeout in seconds": "",
|
||||
"Enter Top K": "Top K লিখুন",
|
||||
"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)",
|
||||
@ -500,6 +503,9 @@
|
||||
"Functions allow arbitrary code execution": "",
|
||||
"Functions allow arbitrary code execution.": "",
|
||||
"Functions imported successfully": "",
|
||||
"Gemini": "",
|
||||
"Gemini API Config": "",
|
||||
"Gemini API Key is required.": "",
|
||||
"General": "সাধারণ",
|
||||
"General Settings": "সাধারণ সেটিংসমূহ",
|
||||
"Generate an image": "",
|
||||
|
@ -182,6 +182,7 @@
|
||||
"Code execution": "Execució de codi",
|
||||
"Code Execution": "",
|
||||
"Code Execution Engine": "",
|
||||
"Code Execution Timeout": "",
|
||||
"Code formatted successfully": "Codi formatat correctament",
|
||||
"Code Interpreter": "Intèrpret de codi",
|
||||
"Code Interpreter Engine": "",
|
||||
@ -321,6 +322,7 @@
|
||||
"Draw": "Dibuixar",
|
||||
"Drop any files here to add to the conversation": "Deixa qualsevol arxiu aquí per afegir-lo a la conversa",
|
||||
"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "p. ex. '30s','10m'. Les unitats de temps vàlides són 's', 'm', 'h'.",
|
||||
"e.g. 60": "",
|
||||
"e.g. A filter to remove profanity from text": "p. ex. Un filtre per eliminar paraules malsonants del text",
|
||||
"e.g. My Filter": "p. ex. El meu filtre",
|
||||
"e.g. My Tools": "p. ex. Les meves eines",
|
||||
@ -408,6 +410,7 @@
|
||||
"Enter Tavily API Key": "Introdueix la clau API de Tavily",
|
||||
"Enter the public URL of your WebUI. This URL will be used to generate links in the notifications.": "Entra la URL pública de WebUI. Aquesta URL s'utilitzarà per generar els enllaços en les notificacions.",
|
||||
"Enter Tika Server URL": "Introdueix l'URL del servidor Tika",
|
||||
"Enter timeout in seconds": "",
|
||||
"Enter Top K": "Introdueix Top K",
|
||||
"Enter URL (e.g. http://127.0.0.1:7860/)": "Introdueix l'URL (p. ex. http://127.0.0.1:7860/)",
|
||||
"Enter URL (e.g. http://localhost:11434)": "Introdueix l'URL (p. ex. http://localhost:11434)",
|
||||
@ -500,6 +503,9 @@
|
||||
"Functions allow arbitrary code execution": "Les funcions permeten l'execució de codi arbitrari",
|
||||
"Functions allow arbitrary code execution.": "Les funcions permeten l'execució de codi arbitrari.",
|
||||
"Functions imported successfully": "Les funcions s'han importat correctament",
|
||||
"Gemini": "",
|
||||
"Gemini API Config": "",
|
||||
"Gemini API Key is required.": "",
|
||||
"General": "General",
|
||||
"General Settings": "Preferències generals",
|
||||
"Generate an image": "Generar una imatge",
|
||||
|
@ -182,6 +182,7 @@
|
||||
"Code execution": "",
|
||||
"Code Execution": "",
|
||||
"Code Execution Engine": "",
|
||||
"Code Execution Timeout": "",
|
||||
"Code formatted successfully": "",
|
||||
"Code Interpreter": "",
|
||||
"Code Interpreter Engine": "",
|
||||
@ -321,6 +322,7 @@
|
||||
"Draw": "",
|
||||
"Drop any files here to add to the conversation": "Ihulog ang bisan unsang file dinhi aron idugang kini sa panag-istoryahanay",
|
||||
"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "p. ",
|
||||
"e.g. 60": "",
|
||||
"e.g. A filter to remove profanity from text": "",
|
||||
"e.g. My Filter": "",
|
||||
"e.g. My Tools": "",
|
||||
@ -408,6 +410,7 @@
|
||||
"Enter Tavily API Key": "",
|
||||
"Enter the public URL of your WebUI. This URL will be used to generate links in the notifications.": "",
|
||||
"Enter Tika Server URL": "",
|
||||
"Enter timeout in seconds": "",
|
||||
"Enter Top K": "Pagsulod sa Top K",
|
||||
"Enter URL (e.g. http://127.0.0.1:7860/)": "Pagsulod sa URL (e.g. http://127.0.0.1:7860/)",
|
||||
"Enter URL (e.g. http://localhost:11434)": "",
|
||||
@ -500,6 +503,9 @@
|
||||
"Functions allow arbitrary code execution": "",
|
||||
"Functions allow arbitrary code execution.": "",
|
||||
"Functions imported successfully": "",
|
||||
"Gemini": "",
|
||||
"Gemini API Config": "",
|
||||
"Gemini API Key is required.": "",
|
||||
"General": "Heneral",
|
||||
"General Settings": "kinatibuk-ang mga setting",
|
||||
"Generate an image": "",
|
||||
|
@ -182,6 +182,7 @@
|
||||
"Code execution": "Provádění kódu",
|
||||
"Code Execution": "",
|
||||
"Code Execution Engine": "",
|
||||
"Code Execution Timeout": "",
|
||||
"Code formatted successfully": "Kód byl úspěšně naformátován.",
|
||||
"Code Interpreter": "",
|
||||
"Code Interpreter Engine": "",
|
||||
@ -321,6 +322,7 @@
|
||||
"Draw": "Namalovat",
|
||||
"Drop any files here to add to the conversation": "Sem přetáhněte libovolné soubory, které chcete přidat do konverzace",
|
||||
"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "např. '30s','10m'. Platné časové jednotky jsou 's', 'm', 'h'.",
|
||||
"e.g. 60": "",
|
||||
"e.g. A filter to remove profanity from text": "",
|
||||
"e.g. My Filter": "",
|
||||
"e.g. My Tools": "",
|
||||
@ -408,6 +410,7 @@
|
||||
"Enter Tavily API Key": "Zadejte API klíč Tavily",
|
||||
"Enter the public URL of your WebUI. This URL will be used to generate links in the notifications.": "",
|
||||
"Enter Tika Server URL": "Zadejte URL serveru Tika",
|
||||
"Enter timeout in seconds": "",
|
||||
"Enter Top K": "Zadejte horní K",
|
||||
"Enter URL (e.g. http://127.0.0.1:7860/)": "Zadejte URL (např. http://127.0.0.1:7860/)",
|
||||
"Enter URL (e.g. http://localhost:11434)": "Zadejte URL (např. http://localhost:11434)",
|
||||
@ -500,6 +503,9 @@
|
||||
"Functions allow arbitrary code execution": "Funkce umožňují vykonávat libovolný kód.",
|
||||
"Functions allow arbitrary code execution.": "Funkce umožňují provádění libovolného kódu.",
|
||||
"Functions imported successfully": "Funkce byly úspěšně importovány",
|
||||
"Gemini": "",
|
||||
"Gemini API Config": "",
|
||||
"Gemini API Key is required.": "",
|
||||
"General": "Obecný",
|
||||
"General Settings": "Obecná nastavení",
|
||||
"Generate an image": "",
|
||||
|
@ -182,6 +182,7 @@
|
||||
"Code execution": "",
|
||||
"Code Execution": "",
|
||||
"Code Execution Engine": "",
|
||||
"Code Execution Timeout": "",
|
||||
"Code formatted successfully": "Kode formateret korrekt",
|
||||
"Code Interpreter": "",
|
||||
"Code Interpreter Engine": "",
|
||||
@ -321,6 +322,7 @@
|
||||
"Draw": "",
|
||||
"Drop any files here to add to the conversation": "Upload filer her for at tilføje til samtalen",
|
||||
"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "f.eks. '30s', '10m'. Tilladte værdier er 's', 'm', 'h'.",
|
||||
"e.g. 60": "",
|
||||
"e.g. A filter to remove profanity from text": "",
|
||||
"e.g. My Filter": "",
|
||||
"e.g. My Tools": "",
|
||||
@ -408,6 +410,7 @@
|
||||
"Enter Tavily API Key": "Indtast Tavily API-nøgle",
|
||||
"Enter the public URL of your WebUI. This URL will be used to generate links in the notifications.": "",
|
||||
"Enter Tika Server URL": "Indtast Tika Server URL",
|
||||
"Enter timeout in seconds": "",
|
||||
"Enter Top K": "Indtast Top K",
|
||||
"Enter URL (e.g. http://127.0.0.1:7860/)": "Indtast URL (f.eks. http://127.0.0.1:7860/)",
|
||||
"Enter URL (e.g. http://localhost:11434)": "Indtast URL (f.eks. http://localhost:11434)",
|
||||
@ -500,6 +503,9 @@
|
||||
"Functions allow arbitrary code execution": "Funktioner tillader kørsel af vilkårlig kode",
|
||||
"Functions allow arbitrary code execution.": "Funktioner tillader kørsel af vilkårlig kode.",
|
||||
"Functions imported successfully": "Funktioner importeret.",
|
||||
"Gemini": "",
|
||||
"Gemini API Config": "",
|
||||
"Gemini API Key is required.": "",
|
||||
"General": "Generelt",
|
||||
"General Settings": "Generelle indstillinger",
|
||||
"Generate an image": "",
|
||||
|
@ -182,6 +182,7 @@
|
||||
"Code execution": "Codeausführung",
|
||||
"Code Execution": "",
|
||||
"Code Execution Engine": "",
|
||||
"Code Execution Timeout": "",
|
||||
"Code formatted successfully": "Code erfolgreich formatiert",
|
||||
"Code Interpreter": "Code-Interpreter",
|
||||
"Code Interpreter Engine": "",
|
||||
@ -321,6 +322,7 @@
|
||||
"Draw": "Zeichnen",
|
||||
"Drop any files here to add to the conversation": "Ziehen Sie beliebige Dateien hierher, um sie der Unterhaltung hinzuzufügen",
|
||||
"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "z. B. '30s','10m'. Gültige Zeiteinheiten sind 's', 'm', 'h'.",
|
||||
"e.g. 60": "",
|
||||
"e.g. A filter to remove profanity from text": "z. B. Ein Filter, um Schimpfwörter aus Text zu entfernen",
|
||||
"e.g. My Filter": "z. B. Mein Filter",
|
||||
"e.g. My Tools": "z. B. Meine Werkzeuge",
|
||||
@ -408,6 +410,7 @@
|
||||
"Enter Tavily API Key": "Geben Sie den Tavily-API-Schlüssel ein",
|
||||
"Enter the public URL of your WebUI. This URL will be used to generate links in the notifications.": "Geben sie die öffentliche URL Ihrer WebUI ein. Diese URL wird verwendet, um Links in den Benachrichtigungen zu generieren.",
|
||||
"Enter Tika Server URL": "Geben Sie die Tika-Server-URL ein",
|
||||
"Enter timeout in seconds": "",
|
||||
"Enter Top K": "Geben Sie Top K ein",
|
||||
"Enter URL (e.g. http://127.0.0.1:7860/)": "Geben Sie die URL ein (z. B. http://127.0.0.1:7860/)",
|
||||
"Enter URL (e.g. http://localhost:11434)": "Geben Sie die URL ein (z. B. http://localhost:11434)",
|
||||
@ -500,6 +503,9 @@
|
||||
"Functions allow arbitrary code execution": "Funktionen ermöglichen die Ausführung beliebigen Codes",
|
||||
"Functions allow arbitrary code execution.": "Funktionen ermöglichen die Ausführung beliebigen Codes.",
|
||||
"Functions imported successfully": "Funktionen erfolgreich importiert",
|
||||
"Gemini": "",
|
||||
"Gemini API Config": "",
|
||||
"Gemini API Key is required.": "",
|
||||
"General": "Allgemein",
|
||||
"General Settings": "Allgemeine Einstellungen",
|
||||
"Generate an image": "Bild erzeugen",
|
||||
|
@ -182,6 +182,7 @@
|
||||
"Code execution": "",
|
||||
"Code Execution": "",
|
||||
"Code Execution Engine": "",
|
||||
"Code Execution Timeout": "",
|
||||
"Code formatted successfully": "",
|
||||
"Code Interpreter": "",
|
||||
"Code Interpreter Engine": "",
|
||||
@ -321,6 +322,7 @@
|
||||
"Draw": "",
|
||||
"Drop any files here to add to the conversation": "Drop files here to add to conversation",
|
||||
"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "e.g. '30s','10m'. Much time units are 's', 'm', 'h'.",
|
||||
"e.g. 60": "",
|
||||
"e.g. A filter to remove profanity from text": "",
|
||||
"e.g. My Filter": "",
|
||||
"e.g. My Tools": "",
|
||||
@ -408,6 +410,7 @@
|
||||
"Enter Tavily API Key": "",
|
||||
"Enter the public URL of your WebUI. This URL will be used to generate links in the notifications.": "",
|
||||
"Enter Tika Server URL": "",
|
||||
"Enter timeout in seconds": "",
|
||||
"Enter Top K": "Enter Top Wow",
|
||||
"Enter URL (e.g. http://127.0.0.1:7860/)": "Enter URL (e.g. http://127.0.0.1:7860/)",
|
||||
"Enter URL (e.g. http://localhost:11434)": "",
|
||||
@ -500,6 +503,9 @@
|
||||
"Functions allow arbitrary code execution": "",
|
||||
"Functions allow arbitrary code execution.": "",
|
||||
"Functions imported successfully": "",
|
||||
"Gemini": "",
|
||||
"Gemini API Config": "",
|
||||
"Gemini API Key is required.": "",
|
||||
"General": "Woweral",
|
||||
"General Settings": "General Doge Settings",
|
||||
"Generate an image": "",
|
||||
|
@ -182,6 +182,7 @@
|
||||
"Code execution": "Εκτέλεση κώδικα",
|
||||
"Code Execution": "",
|
||||
"Code Execution Engine": "",
|
||||
"Code Execution Timeout": "",
|
||||
"Code formatted successfully": "Ο κώδικας μορφοποιήθηκε επιτυχώς",
|
||||
"Code Interpreter": "",
|
||||
"Code Interpreter Engine": "",
|
||||
@ -321,6 +322,7 @@
|
||||
"Draw": "Σχεδίαση",
|
||||
"Drop any files here to add to the conversation": "Αφήστε οποιαδήποτε αρχεία εδώ για να προστεθούν στη συνομιλία",
|
||||
"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "π.χ. '30s','10m'. Οι έγκυρες μονάδες χρόνου είναι 's', 'm', 'h'.",
|
||||
"e.g. 60": "",
|
||||
"e.g. A filter to remove profanity from text": "π.χ. Ένα φίλτρο για να αφαιρέσετε βρισιές από το κείμενο",
|
||||
"e.g. My Filter": "π.χ. Το Φίλτρου Μου",
|
||||
"e.g. My Tools": "π.χ. Τα Εργαλεία Μου",
|
||||
@ -408,6 +410,7 @@
|
||||
"Enter Tavily API Key": "Εισάγετε το Κλειδί API Tavily",
|
||||
"Enter the public URL of your WebUI. This URL will be used to generate links in the notifications.": "",
|
||||
"Enter Tika Server URL": "Εισάγετε το URL διακομιστή Tika",
|
||||
"Enter timeout in seconds": "",
|
||||
"Enter Top K": "Εισάγετε το Top K",
|
||||
"Enter URL (e.g. http://127.0.0.1:7860/)": "Εισάγετε το URL (π.χ. http://127.0.0.1:7860/)",
|
||||
"Enter URL (e.g. http://localhost:11434)": "Εισάγετε το URL (π.χ. http://localhost:11434)",
|
||||
@ -500,6 +503,9 @@
|
||||
"Functions allow arbitrary code execution": "Οι λειτουργίες επιτρέπουν την εκτέλεση αυθαίρετου κώδικα",
|
||||
"Functions allow arbitrary code execution.": "Οι λειτουργίες επιτρέπουν την εκτέλεση αυθαίρετου κώδικα.",
|
||||
"Functions imported successfully": "Οι λειτουργίες εισήχθησαν με επιτυχία",
|
||||
"Gemini": "",
|
||||
"Gemini API Config": "",
|
||||
"Gemini API Key is required.": "",
|
||||
"General": "Γενικά",
|
||||
"General Settings": "Γενικές Ρυθμίσεις",
|
||||
"Generate an image": "",
|
||||
|
@ -182,6 +182,7 @@
|
||||
"Code execution": "",
|
||||
"Code Execution": "",
|
||||
"Code Execution Engine": "",
|
||||
"Code Execution Timeout": "",
|
||||
"Code formatted successfully": "",
|
||||
"Code Interpreter": "",
|
||||
"Code Interpreter Engine": "",
|
||||
@ -321,6 +322,7 @@
|
||||
"Draw": "",
|
||||
"Drop any files here to add to the conversation": "",
|
||||
"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "",
|
||||
"e.g. 60": "",
|
||||
"e.g. A filter to remove profanity from text": "",
|
||||
"e.g. My Filter": "",
|
||||
"e.g. My Tools": "",
|
||||
@ -408,6 +410,7 @@
|
||||
"Enter Tavily API Key": "",
|
||||
"Enter the public URL of your WebUI. This URL will be used to generate links in the notifications.": "",
|
||||
"Enter Tika Server URL": "",
|
||||
"Enter timeout in seconds": "",
|
||||
"Enter Top K": "",
|
||||
"Enter URL (e.g. http://127.0.0.1:7860/)": "",
|
||||
"Enter URL (e.g. http://localhost:11434)": "",
|
||||
@ -500,6 +503,9 @@
|
||||
"Functions allow arbitrary code execution": "",
|
||||
"Functions allow arbitrary code execution.": "",
|
||||
"Functions imported successfully": "",
|
||||
"Gemini": "",
|
||||
"Gemini API Config": "",
|
||||
"Gemini API Key is required.": "",
|
||||
"General": "",
|
||||
"General Settings": "",
|
||||
"Generate an image": "",
|
||||
|
@ -182,6 +182,7 @@
|
||||
"Code execution": "",
|
||||
"Code Execution": "",
|
||||
"Code Execution Engine": "",
|
||||
"Code Execution Timeout": "",
|
||||
"Code formatted successfully": "",
|
||||
"Code Interpreter": "",
|
||||
"Code Interpreter Engine": "",
|
||||
@ -321,6 +322,7 @@
|
||||
"Draw": "",
|
||||
"Drop any files here to add to the conversation": "",
|
||||
"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "",
|
||||
"e.g. 60": "",
|
||||
"e.g. A filter to remove profanity from text": "",
|
||||
"e.g. My Filter": "",
|
||||
"e.g. My Tools": "",
|
||||
@ -408,6 +410,7 @@
|
||||
"Enter Tavily API Key": "",
|
||||
"Enter the public URL of your WebUI. This URL will be used to generate links in the notifications.": "",
|
||||
"Enter Tika Server URL": "",
|
||||
"Enter timeout in seconds": "",
|
||||
"Enter Top K": "",
|
||||
"Enter URL (e.g. http://127.0.0.1:7860/)": "",
|
||||
"Enter URL (e.g. http://localhost:11434)": "",
|
||||
@ -500,6 +503,9 @@
|
||||
"Functions allow arbitrary code execution": "",
|
||||
"Functions allow arbitrary code execution.": "",
|
||||
"Functions imported successfully": "",
|
||||
"Gemini": "",
|
||||
"Gemini API Config": "",
|
||||
"Gemini API Key is required.": "",
|
||||
"General": "",
|
||||
"General Settings": "",
|
||||
"Generate an image": "",
|
||||
|
@ -182,6 +182,7 @@
|
||||
"Code execution": "Ejecución de código",
|
||||
"Code Execution": "",
|
||||
"Code Execution Engine": "",
|
||||
"Code Execution Timeout": "",
|
||||
"Code formatted successfully": "Se ha formateado correctamente el código.",
|
||||
"Code Interpreter": "Interprete de Código",
|
||||
"Code Interpreter Engine": "",
|
||||
@ -321,6 +322,7 @@
|
||||
"Draw": "Dibujar",
|
||||
"Drop any files here to add to the conversation": "Suelta cualquier archivo aquí para agregarlo a la conversación",
|
||||
"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "p.ej. '30s','10m'. Unidades válidas de tiempo son 's', 'm', 'h'.",
|
||||
"e.g. 60": "",
|
||||
"e.g. A filter to remove profanity from text": "p.ej. Un filtro para eliminar la profanidad del texto",
|
||||
"e.g. My Filter": "p.ej. Mi Filtro",
|
||||
"e.g. My Tools": "p.ej. Mis Herramientas",
|
||||
@ -408,6 +410,7 @@
|
||||
"Enter Tavily API Key": "Ingrese la clave API de Tavily",
|
||||
"Enter the public URL of your WebUI. This URL will be used to generate links in the notifications.": "Ingrese la URL pública de su WebUI. Esta URL se utilizará para generar enlaces en las notificaciones.",
|
||||
"Enter Tika Server URL": "Ingrese la URL del servidor Tika",
|
||||
"Enter timeout in seconds": "",
|
||||
"Enter Top K": "Ingrese el Top K",
|
||||
"Enter URL (e.g. http://127.0.0.1:7860/)": "Ingrese la URL (p.ej., http://127.0.0.1:7860/)",
|
||||
"Enter URL (e.g. http://localhost:11434)": "Ingrese la URL (p.ej., http://localhost:11434)",
|
||||
@ -500,6 +503,9 @@
|
||||
"Functions allow arbitrary code execution": "Funciones habilitan la ejecución de código arbitrario",
|
||||
"Functions allow arbitrary code execution.": "Funciones habilitan la ejecución de código arbitrario.",
|
||||
"Functions imported successfully": "Funciones importadas exitosamente",
|
||||
"Gemini": "",
|
||||
"Gemini API Config": "",
|
||||
"Gemini API Key is required.": "",
|
||||
"General": "General",
|
||||
"General Settings": "Opciones Generales",
|
||||
"Generate an image": "Generar una imagen",
|
||||
|
@ -182,6 +182,7 @@
|
||||
"Code execution": "Kodearen exekuzioa",
|
||||
"Code Execution": "",
|
||||
"Code Execution Engine": "",
|
||||
"Code Execution Timeout": "",
|
||||
"Code formatted successfully": "Kodea ongi formateatu da",
|
||||
"Code Interpreter": "",
|
||||
"Code Interpreter Engine": "",
|
||||
@ -321,6 +322,7 @@
|
||||
"Draw": "Marraztu",
|
||||
"Drop any files here to add to the conversation": "Jaregin edozein fitxategi hemen elkarrizketara gehitzeko",
|
||||
"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "adib. '30s','10m'. Denbora unitate baliodunak dira 's', 'm', 'h'.",
|
||||
"e.g. 60": "",
|
||||
"e.g. A filter to remove profanity from text": "adib. Testutik lizunkeriak kentzeko iragazki bat",
|
||||
"e.g. My Filter": "adib. Nire Iragazkia",
|
||||
"e.g. My Tools": "adib. Nire Tresnak",
|
||||
@ -408,6 +410,7 @@
|
||||
"Enter Tavily API Key": "Sartu Tavily API Gakoa",
|
||||
"Enter the public URL of your WebUI. This URL will be used to generate links in the notifications.": "",
|
||||
"Enter Tika Server URL": "Sartu Tika Zerbitzari URLa",
|
||||
"Enter timeout in seconds": "",
|
||||
"Enter Top K": "Sartu Top K",
|
||||
"Enter URL (e.g. http://127.0.0.1:7860/)": "Sartu URLa (adib. http://127.0.0.1:7860/)",
|
||||
"Enter URL (e.g. http://localhost:11434)": "Sartu URLa (adib. http://localhost:11434)",
|
||||
@ -500,6 +503,9 @@
|
||||
"Functions allow arbitrary code execution": "Funtzioek kode arbitrarioa exekutatzea ahalbidetzen dute",
|
||||
"Functions allow arbitrary code execution.": "Funtzioek kode arbitrarioa exekutatzea ahalbidetzen dute.",
|
||||
"Functions imported successfully": "Funtzioak ongi inportatu dira",
|
||||
"Gemini": "",
|
||||
"Gemini API Config": "",
|
||||
"Gemini API Key is required.": "",
|
||||
"General": "Orokorra",
|
||||
"General Settings": "Ezarpen Orokorrak",
|
||||
"Generate an image": "",
|
||||
|
@ -182,6 +182,7 @@
|
||||
"Code execution": "",
|
||||
"Code Execution": "",
|
||||
"Code Execution Engine": "",
|
||||
"Code Execution Timeout": "",
|
||||
"Code formatted successfully": "",
|
||||
"Code Interpreter": "",
|
||||
"Code Interpreter Engine": "",
|
||||
@ -321,6 +322,7 @@
|
||||
"Draw": "",
|
||||
"Drop any files here to add to the conversation": "هر فایلی را اینجا رها کنید تا به مکالمه اضافه شود",
|
||||
"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "به طور مثال '30s','10m'. واحد\u200cهای زمانی معتبر 's', 'm', 'h' هستند.",
|
||||
"e.g. 60": "",
|
||||
"e.g. A filter to remove profanity from text": "",
|
||||
"e.g. My Filter": "",
|
||||
"e.g. My Tools": "",
|
||||
@ -408,6 +410,7 @@
|
||||
"Enter Tavily API Key": "",
|
||||
"Enter the public URL of your WebUI. This URL will be used to generate links in the notifications.": "",
|
||||
"Enter Tika Server URL": "",
|
||||
"Enter timeout in seconds": "",
|
||||
"Enter Top K": "مقدار Top K را وارد کنید",
|
||||
"Enter URL (e.g. http://127.0.0.1:7860/)": "مقدار URL را وارد کنید (مثال http://127.0.0.1:7860/)",
|
||||
"Enter URL (e.g. http://localhost:11434)": "مقدار URL را وارد کنید (مثال http://localhost:11434)",
|
||||
@ -500,6 +503,9 @@
|
||||
"Functions allow arbitrary code execution": "",
|
||||
"Functions allow arbitrary code execution.": "",
|
||||
"Functions imported successfully": "درون\u200cریزی توابع با موفقیت انجام شد",
|
||||
"Gemini": "",
|
||||
"Gemini API Config": "",
|
||||
"Gemini API Key is required.": "",
|
||||
"General": "عمومی",
|
||||
"General Settings": "تنظیمات عمومی",
|
||||
"Generate an image": "",
|
||||
|
@ -182,6 +182,7 @@
|
||||
"Code execution": "Koodin suorittaminen",
|
||||
"Code Execution": "",
|
||||
"Code Execution Engine": "",
|
||||
"Code Execution Timeout": "",
|
||||
"Code formatted successfully": "Koodin muotoilu onnistui",
|
||||
"Code Interpreter": "Ohjelmatulkki",
|
||||
"Code Interpreter Engine": "Ohjelmatulkin moottori",
|
||||
@ -321,6 +322,7 @@
|
||||
"Draw": "Piirros",
|
||||
"Drop any files here to add to the conversation": "Pudota tiedostoja tähän lisätäksesi ne keskusteluun",
|
||||
"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "esim. '30s', '10m'. Kelpoiset aikayksiköt ovat 's', 'm', 'h'.",
|
||||
"e.g. 60": "",
|
||||
"e.g. A filter to remove profanity from text": "esim. suodatin, joka poistaa kirosanoja tekstistä",
|
||||
"e.g. My Filter": "esim. Oma suodatin",
|
||||
"e.g. My Tools": "esim. Omat työkalut",
|
||||
@ -408,6 +410,7 @@
|
||||
"Enter Tavily API Key": "Kirjoita Tavily API -avain",
|
||||
"Enter the public URL of your WebUI. This URL will be used to generate links in the notifications.": "Kirjoita julkinen WebUI verkko-osoitteesi. Verkko-osoitetta käytetään osoitteiden luontiin ilmoituksissa.",
|
||||
"Enter Tika Server URL": "Kirjoita Tika Server URL",
|
||||
"Enter timeout in seconds": "",
|
||||
"Enter Top K": "Kirjoita Top K",
|
||||
"Enter URL (e.g. http://127.0.0.1:7860/)": "Kirjoita URL-osoite (esim. http://127.0.0.1:7860/)",
|
||||
"Enter URL (e.g. http://localhost:11434)": "Kirjoita URL-osoite (esim. http://localhost:11434)",
|
||||
@ -500,6 +503,9 @@
|
||||
"Functions allow arbitrary code execution": "Toiminnot sallivat mielivaltaisen koodin suorittamisen",
|
||||
"Functions allow arbitrary code execution.": "Toiminnot sallivat mielivaltaisen koodin suorittamisen.",
|
||||
"Functions imported successfully": "Toiminnot tuotu onnistuneesti",
|
||||
"Gemini": "",
|
||||
"Gemini API Config": "",
|
||||
"Gemini API Key is required.": "",
|
||||
"General": "Yleinen",
|
||||
"General Settings": "Yleiset asetukset",
|
||||
"Generate an image": "Luo kuva",
|
||||
|
@ -182,6 +182,7 @@
|
||||
"Code execution": "",
|
||||
"Code Execution": "",
|
||||
"Code Execution Engine": "",
|
||||
"Code Execution Timeout": "",
|
||||
"Code formatted successfully": "Le code a été formaté avec succès",
|
||||
"Code Interpreter": "",
|
||||
"Code Interpreter Engine": "",
|
||||
@ -321,6 +322,7 @@
|
||||
"Draw": "",
|
||||
"Drop any files here to add to the conversation": "Déposez des fichiers ici pour les ajouter à la conversation",
|
||||
"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "par ex. '30s', '10 min'. Les unités de temps valides sont 's', 'm', 'h'.",
|
||||
"e.g. 60": "",
|
||||
"e.g. A filter to remove profanity from text": "",
|
||||
"e.g. My Filter": "",
|
||||
"e.g. My Tools": "",
|
||||
@ -408,6 +410,7 @@
|
||||
"Enter Tavily API Key": "Entrez la clé API Tavily",
|
||||
"Enter the public URL of your WebUI. This URL will be used to generate links in the notifications.": "",
|
||||
"Enter Tika Server URL": "",
|
||||
"Enter timeout in seconds": "",
|
||||
"Enter Top K": "Entrez les Top K",
|
||||
"Enter URL (e.g. http://127.0.0.1:7860/)": "Entrez l'URL (par ex. {http://127.0.0.1:7860/})",
|
||||
"Enter URL (e.g. http://localhost:11434)": "Entrez l'URL (par ex. http://localhost:11434)",
|
||||
@ -500,6 +503,9 @@
|
||||
"Functions allow arbitrary code execution": "",
|
||||
"Functions allow arbitrary code execution.": "",
|
||||
"Functions imported successfully": "Fonctions importées avec succès",
|
||||
"Gemini": "",
|
||||
"Gemini API Config": "",
|
||||
"Gemini API Key is required.": "",
|
||||
"General": "Général",
|
||||
"General Settings": "Paramètres Généraux",
|
||||
"Generate an image": "",
|
||||
|
@ -182,6 +182,7 @@
|
||||
"Code execution": "Exécution de code",
|
||||
"Code Execution": "",
|
||||
"Code Execution Engine": "",
|
||||
"Code Execution Timeout": "",
|
||||
"Code formatted successfully": "Le code a été formaté avec succès",
|
||||
"Code Interpreter": "",
|
||||
"Code Interpreter Engine": "",
|
||||
@ -321,6 +322,7 @@
|
||||
"Draw": "Match nul",
|
||||
"Drop any files here to add to the conversation": "Déposez des fichiers ici pour les ajouter à la conversation",
|
||||
"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "par ex. '30s', '10 min'. Les unités de temps valides sont 's', 'm', 'h'.",
|
||||
"e.g. 60": "",
|
||||
"e.g. A filter to remove profanity from text": "par ex. un filtre pour retirer les vulgarités du texte",
|
||||
"e.g. My Filter": "par ex. Mon Filtre",
|
||||
"e.g. My Tools": "par ex. Mes Outils",
|
||||
@ -408,6 +410,7 @@
|
||||
"Enter Tavily API Key": "Entrez la clé API Tavily",
|
||||
"Enter the public URL of your WebUI. This URL will be used to generate links in the notifications.": "Entrez l'URL publique de votre WebUI. Cette URL sera utilisée pour générer des liens dans les notifications.",
|
||||
"Enter Tika Server URL": "Entrez l'URL du serveur Tika",
|
||||
"Enter timeout in seconds": "",
|
||||
"Enter Top K": "Entrez les Top K",
|
||||
"Enter URL (e.g. http://127.0.0.1:7860/)": "Entrez l'URL (par ex. {http://127.0.0.1:7860/})",
|
||||
"Enter URL (e.g. http://localhost:11434)": "Entrez l'URL (par ex. http://localhost:11434)",
|
||||
@ -500,6 +503,9 @@
|
||||
"Functions allow arbitrary code execution": "Les fonctions permettent l'exécution de code arbitraire",
|
||||
"Functions allow arbitrary code execution.": "Les fonctions permettent l'exécution de code arbitraire.",
|
||||
"Functions imported successfully": "Fonctions importées avec succès",
|
||||
"Gemini": "",
|
||||
"Gemini API Config": "",
|
||||
"Gemini API Key is required.": "",
|
||||
"General": "Général",
|
||||
"General Settings": "Paramètres généraux",
|
||||
"Generate an image": "",
|
||||
|
@ -182,6 +182,7 @@
|
||||
"Code execution": "",
|
||||
"Code Execution": "",
|
||||
"Code Execution Engine": "",
|
||||
"Code Execution Timeout": "",
|
||||
"Code formatted successfully": "",
|
||||
"Code Interpreter": "",
|
||||
"Code Interpreter Engine": "",
|
||||
@ -321,6 +322,7 @@
|
||||
"Draw": "",
|
||||
"Drop any files here to add to the conversation": "גרור כל קובץ לכאן כדי להוסיף לשיחה",
|
||||
"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "למשל '30s', '10m'. יחידות זמן חוקיות הן 's', 'm', 'h'.",
|
||||
"e.g. 60": "",
|
||||
"e.g. A filter to remove profanity from text": "",
|
||||
"e.g. My Filter": "",
|
||||
"e.g. My Tools": "",
|
||||
@ -408,6 +410,7 @@
|
||||
"Enter Tavily API Key": "",
|
||||
"Enter the public URL of your WebUI. This URL will be used to generate links in the notifications.": "",
|
||||
"Enter Tika Server URL": "",
|
||||
"Enter timeout in seconds": "",
|
||||
"Enter Top K": "הזן Top K",
|
||||
"Enter URL (e.g. http://127.0.0.1:7860/)": "הזן כתובת URL (למשל http://127.0.0.1:7860/)",
|
||||
"Enter URL (e.g. http://localhost:11434)": "הזן כתובת URL (למשל http://localhost:11434)",
|
||||
@ -500,6 +503,9 @@
|
||||
"Functions allow arbitrary code execution": "",
|
||||
"Functions allow arbitrary code execution.": "",
|
||||
"Functions imported successfully": "",
|
||||
"Gemini": "",
|
||||
"Gemini API Config": "",
|
||||
"Gemini API Key is required.": "",
|
||||
"General": "כללי",
|
||||
"General Settings": "הגדרות כלליות",
|
||||
"Generate an image": "",
|
||||
|
@ -182,6 +182,7 @@
|
||||
"Code execution": "",
|
||||
"Code Execution": "",
|
||||
"Code Execution Engine": "",
|
||||
"Code Execution Timeout": "",
|
||||
"Code formatted successfully": "",
|
||||
"Code Interpreter": "",
|
||||
"Code Interpreter Engine": "",
|
||||
@ -321,6 +322,7 @@
|
||||
"Draw": "",
|
||||
"Drop any files here to add to the conversation": "बातचीत में जोड़ने के लिए कोई भी फ़ाइल यहां छोड़ें",
|
||||
"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "जैसे '30s', '10m', मान्य समय इकाइयाँ 's', 'm', 'h' हैं।",
|
||||
"e.g. 60": "",
|
||||
"e.g. A filter to remove profanity from text": "",
|
||||
"e.g. My Filter": "",
|
||||
"e.g. My Tools": "",
|
||||
@ -408,6 +410,7 @@
|
||||
"Enter Tavily API Key": "",
|
||||
"Enter the public URL of your WebUI. This URL will be used to generate links in the notifications.": "",
|
||||
"Enter Tika Server URL": "",
|
||||
"Enter timeout in seconds": "",
|
||||
"Enter Top K": "शीर्ष K दर्ज करें",
|
||||
"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)",
|
||||
@ -500,6 +503,9 @@
|
||||
"Functions allow arbitrary code execution": "",
|
||||
"Functions allow arbitrary code execution.": "",
|
||||
"Functions imported successfully": "",
|
||||
"Gemini": "",
|
||||
"Gemini API Config": "",
|
||||
"Gemini API Key is required.": "",
|
||||
"General": "सामान्य",
|
||||
"General Settings": "सामान्य सेटिंग्स",
|
||||
"Generate an image": "",
|
||||
|
@ -182,6 +182,7 @@
|
||||
"Code execution": "",
|
||||
"Code Execution": "",
|
||||
"Code Execution Engine": "",
|
||||
"Code Execution Timeout": "",
|
||||
"Code formatted successfully": "",
|
||||
"Code Interpreter": "",
|
||||
"Code Interpreter Engine": "",
|
||||
@ -321,6 +322,7 @@
|
||||
"Draw": "",
|
||||
"Drop any files here to add to the conversation": "Spustite bilo koje datoteke ovdje za dodavanje u razgovor",
|
||||
"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "npr. '30s','10m'. Važeće vremenske jedinice su 's', 'm', 'h'.",
|
||||
"e.g. 60": "",
|
||||
"e.g. A filter to remove profanity from text": "",
|
||||
"e.g. My Filter": "",
|
||||
"e.g. My Tools": "",
|
||||
@ -408,6 +410,7 @@
|
||||
"Enter Tavily API Key": "",
|
||||
"Enter the public URL of your WebUI. This URL will be used to generate links in the notifications.": "",
|
||||
"Enter Tika Server URL": "",
|
||||
"Enter timeout in seconds": "",
|
||||
"Enter Top K": "Unesite Top K",
|
||||
"Enter URL (e.g. http://127.0.0.1:7860/)": "Unesite URL (npr. http://127.0.0.1:7860/)",
|
||||
"Enter URL (e.g. http://localhost:11434)": "Unesite URL (npr. http://localhost:11434)",
|
||||
@ -500,6 +503,9 @@
|
||||
"Functions allow arbitrary code execution": "",
|
||||
"Functions allow arbitrary code execution.": "",
|
||||
"Functions imported successfully": "",
|
||||
"Gemini": "",
|
||||
"Gemini API Config": "",
|
||||
"Gemini API Key is required.": "",
|
||||
"General": "Općenito",
|
||||
"General Settings": "Opće postavke",
|
||||
"Generate an image": "",
|
||||
|
@ -182,6 +182,7 @@
|
||||
"Code execution": "Kód végrehajtás",
|
||||
"Code Execution": "",
|
||||
"Code Execution Engine": "",
|
||||
"Code Execution Timeout": "",
|
||||
"Code formatted successfully": "Kód sikeresen formázva",
|
||||
"Code Interpreter": "",
|
||||
"Code Interpreter Engine": "",
|
||||
@ -321,6 +322,7 @@
|
||||
"Draw": "Rajzolás",
|
||||
"Drop any files here to add to the conversation": "Húzz ide fájlokat a beszélgetéshez való hozzáadáshoz",
|
||||
"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "pl. '30s','10m'. Érvényes időegységek: 's', 'm', 'h'.",
|
||||
"e.g. 60": "",
|
||||
"e.g. A filter to remove profanity from text": "",
|
||||
"e.g. My Filter": "",
|
||||
"e.g. My Tools": "",
|
||||
@ -408,6 +410,7 @@
|
||||
"Enter Tavily API Key": "Add meg a Tavily API kulcsot",
|
||||
"Enter the public URL of your WebUI. This URL will be used to generate links in the notifications.": "",
|
||||
"Enter Tika Server URL": "Add meg a Tika szerver URL-t",
|
||||
"Enter timeout in seconds": "",
|
||||
"Enter Top K": "Add meg a Top K értéket",
|
||||
"Enter URL (e.g. http://127.0.0.1:7860/)": "Add meg az URL-t (pl. http://127.0.0.1:7860/)",
|
||||
"Enter URL (e.g. http://localhost:11434)": "Add meg az URL-t (pl. http://localhost:11434)",
|
||||
@ -500,6 +503,9 @@
|
||||
"Functions allow arbitrary code execution": "A funkciók tetszőleges kód végrehajtását teszik lehetővé",
|
||||
"Functions allow arbitrary code execution.": "A funkciók tetszőleges kód végrehajtását teszik lehetővé.",
|
||||
"Functions imported successfully": "Funkciók sikeresen importálva",
|
||||
"Gemini": "",
|
||||
"Gemini API Config": "",
|
||||
"Gemini API Key is required.": "",
|
||||
"General": "Általános",
|
||||
"General Settings": "Általános beállítások",
|
||||
"Generate an image": "",
|
||||
|
@ -182,6 +182,7 @@
|
||||
"Code execution": "",
|
||||
"Code Execution": "",
|
||||
"Code Execution Engine": "",
|
||||
"Code Execution Timeout": "",
|
||||
"Code formatted successfully": "Kode berhasil diformat",
|
||||
"Code Interpreter": "",
|
||||
"Code Interpreter Engine": "",
|
||||
@ -321,6 +322,7 @@
|
||||
"Draw": "",
|
||||
"Drop any files here to add to the conversation": "Letakkan file apa pun di sini untuk ditambahkan ke percakapan",
|
||||
"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "misalnya '30-an', '10m'. Satuan waktu yang valid adalah 's', 'm', 'h'.",
|
||||
"e.g. 60": "",
|
||||
"e.g. A filter to remove profanity from text": "",
|
||||
"e.g. My Filter": "",
|
||||
"e.g. My Tools": "",
|
||||
@ -408,6 +410,7 @@
|
||||
"Enter Tavily API Key": "Masukkan Kunci API Tavily",
|
||||
"Enter the public URL of your WebUI. This URL will be used to generate links in the notifications.": "",
|
||||
"Enter Tika Server URL": "",
|
||||
"Enter timeout in seconds": "",
|
||||
"Enter Top K": "Masukkan Top K",
|
||||
"Enter URL (e.g. http://127.0.0.1:7860/)": "Masukkan URL (mis. http://127.0.0.1:7860/)",
|
||||
"Enter URL (e.g. http://localhost:11434)": "Masukkan URL (mis. http://localhost:11434)",
|
||||
@ -500,6 +503,9 @@
|
||||
"Functions allow arbitrary code execution": "",
|
||||
"Functions allow arbitrary code execution.": "",
|
||||
"Functions imported successfully": "Fungsi berhasil diimpor",
|
||||
"Gemini": "",
|
||||
"Gemini API Config": "",
|
||||
"Gemini API Key is required.": "",
|
||||
"General": "Umum",
|
||||
"General Settings": "Pengaturan Umum",
|
||||
"Generate an image": "",
|
||||
|
@ -182,6 +182,7 @@
|
||||
"Code execution": "Cód a fhorghníomhú",
|
||||
"Code Execution": "",
|
||||
"Code Execution Engine": "",
|
||||
"Code Execution Timeout": "",
|
||||
"Code formatted successfully": "Cód formáidithe go rathúil",
|
||||
"Code Interpreter": "Ateangaire Cód",
|
||||
"Code Interpreter Engine": "",
|
||||
@ -321,6 +322,7 @@
|
||||
"Draw": "Tarraing",
|
||||
"Drop any files here to add to the conversation": "Scaoil aon chomhaid anseo le cur leis an gcomhrá",
|
||||
"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "m.sh. '30s', '10m'. Is iad aonaid ama bailí ná 's', 'm', 'h'.",
|
||||
"e.g. 60": "",
|
||||
"e.g. A filter to remove profanity from text": "m.h. Scagaire chun profanity a bhaint as téacs",
|
||||
"e.g. My Filter": "m.sh. Mo Scagaire",
|
||||
"e.g. My Tools": "e.g. Mo Uirlisí",
|
||||
@ -408,6 +410,7 @@
|
||||
"Enter Tavily API Key": "Cuir isteach eochair API Tavily",
|
||||
"Enter the public URL of your WebUI. This URL will be used to generate links in the notifications.": "Cuir isteach URL poiblí do WebUI. Bainfear úsáid as an URL seo chun naisc a ghiniúint sna fógraí.",
|
||||
"Enter Tika Server URL": "Cuir isteach URL freastalaí Tika",
|
||||
"Enter timeout in seconds": "",
|
||||
"Enter Top K": "Cuir isteach Barr K",
|
||||
"Enter URL (e.g. http://127.0.0.1:7860/)": "Iontráil URL (m.sh. http://127.0.0.1:7860/)",
|
||||
"Enter URL (e.g. http://localhost:11434)": "Iontráil URL (m.sh. http://localhost:11434)",
|
||||
@ -500,6 +503,9 @@
|
||||
"Functions allow arbitrary code execution": "Ligeann feidhmeanna forghníomhú cód",
|
||||
"Functions allow arbitrary code execution.": "Ceadaíonn feidhmeanna forghníomhú cód treallach.",
|
||||
"Functions imported successfully": "Feidhmeanna allmhairi",
|
||||
"Gemini": "",
|
||||
"Gemini API Config": "",
|
||||
"Gemini API Key is required.": "",
|
||||
"General": "Ginearálta",
|
||||
"General Settings": "Socruithe Ginearálta",
|
||||
"Generate an image": "Gin íomhá",
|
||||
|
@ -182,6 +182,7 @@
|
||||
"Code execution": "",
|
||||
"Code Execution": "",
|
||||
"Code Execution Engine": "",
|
||||
"Code Execution Timeout": "",
|
||||
"Code formatted successfully": "",
|
||||
"Code Interpreter": "",
|
||||
"Code Interpreter Engine": "",
|
||||
@ -321,6 +322,7 @@
|
||||
"Draw": "",
|
||||
"Drop any files here to add to the conversation": "Trascina qui i file da aggiungere alla conversazione",
|
||||
"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "ad esempio '30s','10m'. Le unità di tempo valide sono 's', 'm', 'h'.",
|
||||
"e.g. 60": "",
|
||||
"e.g. A filter to remove profanity from text": "",
|
||||
"e.g. My Filter": "",
|
||||
"e.g. My Tools": "",
|
||||
@ -408,6 +410,7 @@
|
||||
"Enter Tavily API Key": "",
|
||||
"Enter the public URL of your WebUI. This URL will be used to generate links in the notifications.": "",
|
||||
"Enter Tika Server URL": "",
|
||||
"Enter timeout in seconds": "",
|
||||
"Enter Top K": "Inserisci Top K",
|
||||
"Enter URL (e.g. http://127.0.0.1:7860/)": "Inserisci URL (ad esempio http://127.0.0.1:7860/)",
|
||||
"Enter URL (e.g. http://localhost:11434)": "Inserisci URL (ad esempio http://localhost:11434)",
|
||||
@ -500,6 +503,9 @@
|
||||
"Functions allow arbitrary code execution": "",
|
||||
"Functions allow arbitrary code execution.": "",
|
||||
"Functions imported successfully": "",
|
||||
"Gemini": "",
|
||||
"Gemini API Config": "",
|
||||
"Gemini API Key is required.": "",
|
||||
"General": "Generale",
|
||||
"General Settings": "Impostazioni generali",
|
||||
"Generate an image": "",
|
||||
|
@ -182,6 +182,7 @@
|
||||
"Code execution": "",
|
||||
"Code Execution": "",
|
||||
"Code Execution Engine": "",
|
||||
"Code Execution Timeout": "",
|
||||
"Code formatted successfully": "コードフォーマットに成功しました",
|
||||
"Code Interpreter": "",
|
||||
"Code Interpreter Engine": "",
|
||||
@ -321,6 +322,7 @@
|
||||
"Draw": "",
|
||||
"Drop any files here to add to the conversation": "会話を追加するには、ここにファイルをドロップしてください",
|
||||
"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "例: '30秒'、'10分'。有効な時間単位は '秒'、'分'、'時間' です。",
|
||||
"e.g. 60": "",
|
||||
"e.g. A filter to remove profanity from text": "",
|
||||
"e.g. My Filter": "",
|
||||
"e.g. My Tools": "",
|
||||
@ -408,6 +410,7 @@
|
||||
"Enter Tavily API Key": "Tavily API Keyを入力してください。",
|
||||
"Enter the public URL of your WebUI. This URL will be used to generate links in the notifications.": "",
|
||||
"Enter Tika Server URL": "Tika Server URLを入力してください。",
|
||||
"Enter timeout in seconds": "",
|
||||
"Enter Top K": "トップ K を入力してください",
|
||||
"Enter URL (e.g. http://127.0.0.1:7860/)": "URL を入力してください (例: http://127.0.0.1:7860/)",
|
||||
"Enter URL (e.g. http://localhost:11434)": "URL を入力してください (例: http://localhost:11434)",
|
||||
@ -500,6 +503,9 @@
|
||||
"Functions allow arbitrary code execution": "",
|
||||
"Functions allow arbitrary code execution.": "",
|
||||
"Functions imported successfully": "Functionsのインポートが成功しました",
|
||||
"Gemini": "",
|
||||
"Gemini API Config": "",
|
||||
"Gemini API Key is required.": "",
|
||||
"General": "一般",
|
||||
"General Settings": "一般設定",
|
||||
"Generate an image": "",
|
||||
|
@ -182,6 +182,7 @@
|
||||
"Code execution": "",
|
||||
"Code Execution": "",
|
||||
"Code Execution Engine": "",
|
||||
"Code Execution Timeout": "",
|
||||
"Code formatted successfully": "",
|
||||
"Code Interpreter": "",
|
||||
"Code Interpreter Engine": "",
|
||||
@ -321,6 +322,7 @@
|
||||
"Draw": "",
|
||||
"Drop any files here to add to the conversation": "გადაიტანეთ ფაილები აქ, რათა დაამატოთ ისინი მიმოწერაში",
|
||||
"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "მაგალითად, '30წ', '10მ'. მოქმედი დროის ერთეულები: 'წ', 'წთ', 'სთ'.",
|
||||
"e.g. 60": "",
|
||||
"e.g. A filter to remove profanity from text": "",
|
||||
"e.g. My Filter": "",
|
||||
"e.g. My Tools": "",
|
||||
@ -408,6 +410,7 @@
|
||||
"Enter Tavily API Key": "",
|
||||
"Enter the public URL of your WebUI. This URL will be used to generate links in the notifications.": "",
|
||||
"Enter Tika Server URL": "",
|
||||
"Enter timeout in seconds": "",
|
||||
"Enter Top K": "შეიყვანეთ Top K",
|
||||
"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)",
|
||||
@ -500,6 +503,9 @@
|
||||
"Functions allow arbitrary code execution": "",
|
||||
"Functions allow arbitrary code execution.": "",
|
||||
"Functions imported successfully": "",
|
||||
"Gemini": "",
|
||||
"Gemini API Config": "",
|
||||
"Gemini API Key is required.": "",
|
||||
"General": "ზოგადი",
|
||||
"General Settings": "ზოგადი პარამეტრები",
|
||||
"Generate an image": "",
|
||||
|
@ -182,6 +182,7 @@
|
||||
"Code execution": "코드 실행",
|
||||
"Code Execution": "",
|
||||
"Code Execution Engine": "",
|
||||
"Code Execution Timeout": "",
|
||||
"Code formatted successfully": "성공적으로 코드가 생성되었습니다",
|
||||
"Code Interpreter": "",
|
||||
"Code Interpreter Engine": "",
|
||||
@ -321,6 +322,7 @@
|
||||
"Draw": "그리기",
|
||||
"Drop any files here to add to the conversation": "대화에 추가할 파일을 여기에 드롭하세요.",
|
||||
"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "예: '30초','10분'. 유효한 시간 단위는 '초', '분', '시'입니다.",
|
||||
"e.g. 60": "",
|
||||
"e.g. A filter to remove profanity from text": "",
|
||||
"e.g. My Filter": "",
|
||||
"e.g. My Tools": "",
|
||||
@ -408,6 +410,7 @@
|
||||
"Enter Tavily API Key": "Tavily API 키 입력",
|
||||
"Enter the public URL of your WebUI. This URL will be used to generate links in the notifications.": "WebUI의 공개 URL을 입력해 주세요. 이 URL은 알림에서 링크를 생성하는 데 사용합니다.",
|
||||
"Enter Tika Server URL": "Tika 서버 URL 입력",
|
||||
"Enter timeout in seconds": "",
|
||||
"Enter Top K": "Top K 입력",
|
||||
"Enter URL (e.g. http://127.0.0.1:7860/)": "URL 입력(예: http://127.0.0.1:7860/)",
|
||||
"Enter URL (e.g. http://localhost:11434)": "URL 입력(예: http://localhost:11434)",
|
||||
@ -500,6 +503,9 @@
|
||||
"Functions allow arbitrary code execution": "함수로 임이의 코드 실행 허용하기",
|
||||
"Functions allow arbitrary code execution.": "함수가 임이의 코드를 실행하도록 허용하였습니다",
|
||||
"Functions imported successfully": "성공적으로 함수가 가져왔습니다",
|
||||
"Gemini": "",
|
||||
"Gemini API Config": "",
|
||||
"Gemini API Key is required.": "",
|
||||
"General": "일반",
|
||||
"General Settings": "일반 설정",
|
||||
"Generate an image": "",
|
||||
|
@ -182,6 +182,7 @@
|
||||
"Code execution": "",
|
||||
"Code Execution": "",
|
||||
"Code Execution Engine": "",
|
||||
"Code Execution Timeout": "",
|
||||
"Code formatted successfully": "Kodas suformatuotas sėkmingai",
|
||||
"Code Interpreter": "",
|
||||
"Code Interpreter Engine": "",
|
||||
@ -321,6 +322,7 @@
|
||||
"Draw": "",
|
||||
"Drop any files here to add to the conversation": "Įkelkite dokumentus čia, kad juos pridėti į pokalbį",
|
||||
"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "pvz. '30s', '10m'. Laiko vienetai yra 's', 'm', 'h'.",
|
||||
"e.g. 60": "",
|
||||
"e.g. A filter to remove profanity from text": "",
|
||||
"e.g. My Filter": "",
|
||||
"e.g. My Tools": "",
|
||||
@ -408,6 +410,7 @@
|
||||
"Enter Tavily API Key": "Įveskite Tavily API raktą",
|
||||
"Enter the public URL of your WebUI. This URL will be used to generate links in the notifications.": "",
|
||||
"Enter Tika Server URL": "Įveskite Tika serverio nuorodą",
|
||||
"Enter timeout in seconds": "",
|
||||
"Enter Top K": "Įveskite Top K",
|
||||
"Enter URL (e.g. http://127.0.0.1:7860/)": "Įveskite nuorodą (pvz. http://127.0.0.1:7860/)",
|
||||
"Enter URL (e.g. http://localhost:11434)": "Įveskite nuorododą (pvz. http://localhost:11434",
|
||||
@ -500,6 +503,9 @@
|
||||
"Functions allow arbitrary code execution": "Funkcijos leidžia nekontroliuojamo kodo vykdymą",
|
||||
"Functions allow arbitrary code execution.": "Funkcijos leidžia nekontroliuojamo kodo vykdymą",
|
||||
"Functions imported successfully": "Funkcijos importuotos sėkmingai",
|
||||
"Gemini": "",
|
||||
"Gemini API Config": "",
|
||||
"Gemini API Key is required.": "",
|
||||
"General": "Bendri",
|
||||
"General Settings": "Bendri nustatymai",
|
||||
"Generate an image": "",
|
||||
|
@ -182,6 +182,7 @@
|
||||
"Code execution": "",
|
||||
"Code Execution": "",
|
||||
"Code Execution Engine": "",
|
||||
"Code Execution Timeout": "",
|
||||
"Code formatted successfully": "Kod berjaya diformatkan",
|
||||
"Code Interpreter": "",
|
||||
"Code Interpreter Engine": "",
|
||||
@ -321,6 +322,7 @@
|
||||
"Draw": "",
|
||||
"Drop any files here to add to the conversation": "Letakkan mana-mana fail di sini untuk ditambahkan pada perbualan",
|
||||
"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "cth '30s','10m'. Unit masa yang sah ialah 's', 'm', 'h'.",
|
||||
"e.g. 60": "",
|
||||
"e.g. A filter to remove profanity from text": "",
|
||||
"e.g. My Filter": "",
|
||||
"e.g. My Tools": "",
|
||||
@ -408,6 +410,7 @@
|
||||
"Enter Tavily API Key": "Masukkan Kunci API Tavily",
|
||||
"Enter the public URL of your WebUI. This URL will be used to generate links in the notifications.": "",
|
||||
"Enter Tika Server URL": "Masukkan URL Pelayan Tika",
|
||||
"Enter timeout in seconds": "",
|
||||
"Enter Top K": "Masukkan 'Top K'",
|
||||
"Enter URL (e.g. http://127.0.0.1:7860/)": "Masukkan URL (cth http://127.0.0.1:7860/)",
|
||||
"Enter URL (e.g. http://localhost:11434)": "Masukkan URL (cth http://localhost:11434)",
|
||||
@ -500,6 +503,9 @@
|
||||
"Functions allow arbitrary code execution": "Fungsi membenarkan pelaksanaan kod sewenang-wenangnya",
|
||||
"Functions allow arbitrary code execution.": "Fungsi membenarkan pelaksanaan kod sewenang-wenangnya.",
|
||||
"Functions imported successfully": "Fungsi berjaya diimport",
|
||||
"Gemini": "",
|
||||
"Gemini API Config": "",
|
||||
"Gemini API Key is required.": "",
|
||||
"General": "Umum",
|
||||
"General Settings": "Tetapan Umum",
|
||||
"Generate an image": "",
|
||||
|
@ -182,6 +182,7 @@
|
||||
"Code execution": "Kodekjøring",
|
||||
"Code Execution": "",
|
||||
"Code Execution Engine": "",
|
||||
"Code Execution Timeout": "",
|
||||
"Code formatted successfully": "Koden er formatert",
|
||||
"Code Interpreter": "",
|
||||
"Code Interpreter Engine": "",
|
||||
@ -321,6 +322,7 @@
|
||||
"Draw": "Tegne",
|
||||
"Drop any files here to add to the conversation": "Slipp filer her for å legge dem til i samtalen",
|
||||
"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "f.eks. '30s','10m'. Gyldige tidsenheter er 's', 'm', 't'.",
|
||||
"e.g. 60": "",
|
||||
"e.g. A filter to remove profanity from text": "f.eks. et filter for å fjerne banning fra tekst",
|
||||
"e.g. My Filter": "f.eks. Mitt filter",
|
||||
"e.g. My Tools": "f.eks. Mine verktøy",
|
||||
@ -408,6 +410,7 @@
|
||||
"Enter Tavily API Key": "Angi API-nøkkel for Tavily",
|
||||
"Enter the public URL of your WebUI. This URL will be used to generate links in the notifications.": "",
|
||||
"Enter Tika Server URL": "Angi server-URL for Tika",
|
||||
"Enter timeout in seconds": "",
|
||||
"Enter Top K": "Angi Top K",
|
||||
"Enter URL (e.g. http://127.0.0.1:7860/)": "Angi URL (f.eks. http://127.0.0.1:7860/)",
|
||||
"Enter URL (e.g. http://localhost:11434)": "Angi URL (f.eks. http://localhost:11434)",
|
||||
@ -500,6 +503,9 @@
|
||||
"Functions allow arbitrary code execution": "Funksjoner tillater vilkårlig kodekjøring",
|
||||
"Functions allow arbitrary code execution.": "Funksjoner tillater vilkårlig kodekjøring.",
|
||||
"Functions imported successfully": "Funksjoner er importert",
|
||||
"Gemini": "",
|
||||
"Gemini API Config": "",
|
||||
"Gemini API Key is required.": "",
|
||||
"General": "Generelt",
|
||||
"General Settings": "Generelle innstillinger",
|
||||
"Generate an image": "",
|
||||
|
@ -182,6 +182,7 @@
|
||||
"Code execution": "Code uitvoeren",
|
||||
"Code Execution": "",
|
||||
"Code Execution Engine": "",
|
||||
"Code Execution Timeout": "",
|
||||
"Code formatted successfully": "Code succesvol geformateerd",
|
||||
"Code Interpreter": "",
|
||||
"Code Interpreter Engine": "",
|
||||
@ -321,6 +322,7 @@
|
||||
"Draw": "Teken",
|
||||
"Drop any files here to add to the conversation": "Sleep hier bestanden om toe te voegen aan het gesprek",
|
||||
"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "bijv. '30s', '10m'. Geldige tijdseenheden zijn 's', 'm', 'h'.",
|
||||
"e.g. 60": "",
|
||||
"e.g. A filter to remove profanity from text": "bijv. Een filter om gevloek uit tekst te verwijderen",
|
||||
"e.g. My Filter": "bijv. Mijn filter",
|
||||
"e.g. My Tools": "bijv. Mijn gereedschappen",
|
||||
@ -408,6 +410,7 @@
|
||||
"Enter Tavily API Key": "Voer Tavily API-sleutel in",
|
||||
"Enter the public URL of your WebUI. This URL will be used to generate links in the notifications.": "",
|
||||
"Enter Tika Server URL": "Voer Tika Server URL in",
|
||||
"Enter timeout in seconds": "",
|
||||
"Enter Top K": "Voeg Top K toe",
|
||||
"Enter URL (e.g. http://127.0.0.1:7860/)": "Voer URL in (Bijv. http://127.0.0.1:7860/)",
|
||||
"Enter URL (e.g. http://localhost:11434)": "Voer URL in (Bijv. http://localhost:11434)",
|
||||
@ -500,6 +503,9 @@
|
||||
"Functions allow arbitrary code execution": "Functies staan willekeurige code-uitvoering toe",
|
||||
"Functions allow arbitrary code execution.": "Functies staan willekeurige code-uitvoering toe",
|
||||
"Functions imported successfully": "Functies succesvol geïmporteerd",
|
||||
"Gemini": "",
|
||||
"Gemini API Config": "",
|
||||
"Gemini API Key is required.": "",
|
||||
"General": "Algemeen",
|
||||
"General Settings": "Algemene instellingen",
|
||||
"Generate an image": "",
|
||||
|
@ -182,6 +182,7 @@
|
||||
"Code execution": "",
|
||||
"Code Execution": "",
|
||||
"Code Execution Engine": "",
|
||||
"Code Execution Timeout": "",
|
||||
"Code formatted successfully": "",
|
||||
"Code Interpreter": "",
|
||||
"Code Interpreter Engine": "",
|
||||
@ -321,6 +322,7 @@
|
||||
"Draw": "",
|
||||
"Drop any files here to add to the conversation": "ਗੱਲਬਾਤ ਵਿੱਚ ਸ਼ਾਮਲ ਕਰਨ ਲਈ ਕੋਈ ਵੀ ਫਾਈਲ ਇੱਥੇ ਛੱਡੋ",
|
||||
"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "ਉਦਾਹਰਣ ਲਈ '30ਸ','10ਮਿ'. ਸਹੀ ਸਮਾਂ ਇਕਾਈਆਂ ਹਨ 'ਸ', 'ਮ', 'ਘੰ'.",
|
||||
"e.g. 60": "",
|
||||
"e.g. A filter to remove profanity from text": "",
|
||||
"e.g. My Filter": "",
|
||||
"e.g. My Tools": "",
|
||||
@ -408,6 +410,7 @@
|
||||
"Enter Tavily API Key": "",
|
||||
"Enter the public URL of your WebUI. This URL will be used to generate links in the notifications.": "",
|
||||
"Enter Tika Server URL": "",
|
||||
"Enter timeout in seconds": "",
|
||||
"Enter Top K": "ਸਿਖਰ K ਦਰਜ ਕਰੋ",
|
||||
"Enter URL (e.g. http://127.0.0.1:7860/)": "URL ਦਰਜ ਕਰੋ (ਉਦਾਹਰਣ ਲਈ http://127.0.0.1:7860/)",
|
||||
"Enter URL (e.g. http://localhost:11434)": "URL ਦਰਜ ਕਰੋ (ਉਦਾਹਰਣ ਲਈ http://localhost:11434)",
|
||||
@ -500,6 +503,9 @@
|
||||
"Functions allow arbitrary code execution": "",
|
||||
"Functions allow arbitrary code execution.": "",
|
||||
"Functions imported successfully": "",
|
||||
"Gemini": "",
|
||||
"Gemini API Config": "",
|
||||
"Gemini API Key is required.": "",
|
||||
"General": "ਆਮ",
|
||||
"General Settings": "ਆਮ ਸੈਟਿੰਗਾਂ",
|
||||
"Generate an image": "",
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -182,6 +182,7 @@
|
||||
"Code execution": "Execução de código",
|
||||
"Code Execution": "",
|
||||
"Code Execution Engine": "",
|
||||
"Code Execution Timeout": "",
|
||||
"Code formatted successfully": "Código formatado com sucesso",
|
||||
"Code Interpreter": "",
|
||||
"Code Interpreter Engine": "",
|
||||
@ -321,6 +322,7 @@
|
||||
"Draw": "Empate",
|
||||
"Drop any files here to add to the conversation": "Solte qualquer arquivo aqui para adicionar à conversa",
|
||||
"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "por exemplo, '30s', '10m'. Unidades de tempo válidas são 's', 'm', 'h'.",
|
||||
"e.g. 60": "",
|
||||
"e.g. A filter to remove profanity from text": "Exemplo: Um filtro para remover palavrões do texto",
|
||||
"e.g. My Filter": "Exemplo: Meu Filtro",
|
||||
"e.g. My Tools": "Exemplo: Minhas Ferramentas",
|
||||
@ -408,6 +410,7 @@
|
||||
"Enter Tavily API Key": "Digite a Chave API do Tavily",
|
||||
"Enter the public URL of your WebUI. This URL will be used to generate links in the notifications.": "",
|
||||
"Enter Tika Server URL": "Digite a URL do Servidor Tika",
|
||||
"Enter timeout in seconds": "",
|
||||
"Enter Top K": "Digite o Top K",
|
||||
"Enter URL (e.g. http://127.0.0.1:7860/)": "Digite a URL (por exemplo, http://127.0.0.1:7860/)",
|
||||
"Enter URL (e.g. http://localhost:11434)": "Digite a URL (por exemplo, http://localhost:11434)",
|
||||
@ -500,6 +503,9 @@
|
||||
"Functions allow arbitrary code execution": "Funções permitem a execução arbitrária de código",
|
||||
"Functions allow arbitrary code execution.": "Funções permitem a execução arbitrária de código.",
|
||||
"Functions imported successfully": "Funções importadas com sucesso",
|
||||
"Gemini": "",
|
||||
"Gemini API Config": "",
|
||||
"Gemini API Key is required.": "",
|
||||
"General": "Geral",
|
||||
"General Settings": "Configurações Gerais",
|
||||
"Generate an image": "",
|
||||
|
@ -182,6 +182,7 @@
|
||||
"Code execution": "",
|
||||
"Code Execution": "",
|
||||
"Code Execution Engine": "",
|
||||
"Code Execution Timeout": "",
|
||||
"Code formatted successfully": "",
|
||||
"Code Interpreter": "",
|
||||
"Code Interpreter Engine": "",
|
||||
@ -321,6 +322,7 @@
|
||||
"Draw": "",
|
||||
"Drop any files here to add to the conversation": "Largue os ficheiros aqui para adicionar à conversa",
|
||||
"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "por exemplo, '30s', '10m'. Unidades de tempo válidas são 's', 'm', 'h'.",
|
||||
"e.g. 60": "",
|
||||
"e.g. A filter to remove profanity from text": "",
|
||||
"e.g. My Filter": "",
|
||||
"e.g. My Tools": "",
|
||||
@ -408,6 +410,7 @@
|
||||
"Enter Tavily API Key": "",
|
||||
"Enter the public URL of your WebUI. This URL will be used to generate links in the notifications.": "",
|
||||
"Enter Tika Server URL": "",
|
||||
"Enter timeout in seconds": "",
|
||||
"Enter Top K": "Escreva o Top K",
|
||||
"Enter URL (e.g. http://127.0.0.1:7860/)": "Escreva o URL (por exemplo, http://127.0.0.1:7860/)",
|
||||
"Enter URL (e.g. http://localhost:11434)": "Escreva o URL (por exemplo, http://localhost:11434)",
|
||||
@ -500,6 +503,9 @@
|
||||
"Functions allow arbitrary code execution": "",
|
||||
"Functions allow arbitrary code execution.": "",
|
||||
"Functions imported successfully": "",
|
||||
"Gemini": "",
|
||||
"Gemini API Config": "",
|
||||
"Gemini API Key is required.": "",
|
||||
"General": "Geral",
|
||||
"General Settings": "Configurações Gerais",
|
||||
"Generate an image": "",
|
||||
|
@ -182,6 +182,7 @@
|
||||
"Code execution": "Executarea codului",
|
||||
"Code Execution": "",
|
||||
"Code Execution Engine": "",
|
||||
"Code Execution Timeout": "",
|
||||
"Code formatted successfully": "Cod formatat cu succes",
|
||||
"Code Interpreter": "",
|
||||
"Code Interpreter Engine": "",
|
||||
@ -321,6 +322,7 @@
|
||||
"Draw": "Desenează",
|
||||
"Drop any files here to add to the conversation": "Plasează orice fișiere aici pentru a le adăuga la conversație",
|
||||
"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "de ex. '30s', '10m'. Unitățile de timp valide sunt 's', 'm', 'h'.",
|
||||
"e.g. 60": "",
|
||||
"e.g. A filter to remove profanity from text": "",
|
||||
"e.g. My Filter": "",
|
||||
"e.g. My Tools": "",
|
||||
@ -408,6 +410,7 @@
|
||||
"Enter Tavily API Key": "Introduceți Cheia API Tavily",
|
||||
"Enter the public URL of your WebUI. This URL will be used to generate links in the notifications.": "",
|
||||
"Enter Tika Server URL": "Introduceți URL-ul Serverului Tika",
|
||||
"Enter timeout in seconds": "",
|
||||
"Enter Top K": "Introduceți Top K",
|
||||
"Enter URL (e.g. http://127.0.0.1:7860/)": "Introduceți URL-ul (de ex. http://127.0.0.1:7860/)",
|
||||
"Enter URL (e.g. http://localhost:11434)": "Introduceți URL-ul (de ex. http://localhost:11434)",
|
||||
@ -500,6 +503,9 @@
|
||||
"Functions allow arbitrary code execution": "Funcțiile permit executarea arbitrară a codului",
|
||||
"Functions allow arbitrary code execution.": "Funcțiile permit executarea arbitrară a codului.",
|
||||
"Functions imported successfully": "Funcțiile au fost importate cu succes",
|
||||
"Gemini": "",
|
||||
"Gemini API Config": "",
|
||||
"Gemini API Key is required.": "",
|
||||
"General": "General",
|
||||
"General Settings": "Setări Generale",
|
||||
"Generate an image": "",
|
||||
|
@ -182,6 +182,7 @@
|
||||
"Code execution": "Выполнение кода",
|
||||
"Code Execution": "",
|
||||
"Code Execution Engine": "",
|
||||
"Code Execution Timeout": "",
|
||||
"Code formatted successfully": "Код успешно отформатирован",
|
||||
"Code Interpreter": "",
|
||||
"Code Interpreter Engine": "",
|
||||
@ -321,6 +322,7 @@
|
||||
"Draw": "Рисовать",
|
||||
"Drop any files here to add to the conversation": "Перетащите сюда файлы, чтобы добавить их в разговор",
|
||||
"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "например, '30s','10m'. Допустимые единицы времени: 's', 'm', 'h'.",
|
||||
"e.g. 60": "",
|
||||
"e.g. A filter to remove profanity from text": "",
|
||||
"e.g. My Filter": "",
|
||||
"e.g. My Tools": "",
|
||||
@ -408,6 +410,7 @@
|
||||
"Enter Tavily API Key": "Введите ключ API Tavily",
|
||||
"Enter the public URL of your WebUI. This URL will be used to generate links in the notifications.": "",
|
||||
"Enter Tika Server URL": "Введите URL-адрес сервера Tika",
|
||||
"Enter timeout in seconds": "",
|
||||
"Enter Top K": "Введите Top K",
|
||||
"Enter URL (e.g. http://127.0.0.1:7860/)": "Введите URL-адрес (например, http://127.0.0.1:7860/)",
|
||||
"Enter URL (e.g. http://localhost:11434)": "Введите URL-адрес (например, http://localhost:11434)",
|
||||
@ -500,6 +503,9 @@
|
||||
"Functions allow arbitrary code execution": "Функции позволяют выполнять произвольный код",
|
||||
"Functions allow arbitrary code execution.": "Функции позволяют выполнять произвольный код.",
|
||||
"Functions imported successfully": "Функции успешно импортированы",
|
||||
"Gemini": "",
|
||||
"Gemini API Config": "",
|
||||
"Gemini API Key is required.": "",
|
||||
"General": "Общее",
|
||||
"General Settings": "Общие настройки",
|
||||
"Generate an image": "",
|
||||
|
@ -182,6 +182,7 @@
|
||||
"Code execution": "Vykonávanie kódu",
|
||||
"Code Execution": "",
|
||||
"Code Execution Engine": "",
|
||||
"Code Execution Timeout": "",
|
||||
"Code formatted successfully": "Kód bol úspešne naformátovaný.",
|
||||
"Code Interpreter": "",
|
||||
"Code Interpreter Engine": "",
|
||||
@ -321,6 +322,7 @@
|
||||
"Draw": "Nakresliť",
|
||||
"Drop any files here to add to the conversation": "Sem presuňte akékoľvek súbory, ktoré chcete pridať do konverzácie",
|
||||
"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "napr. '30s','10m'. Platné časové jednotky sú 's', 'm', 'h'.",
|
||||
"e.g. 60": "",
|
||||
"e.g. A filter to remove profanity from text": "",
|
||||
"e.g. My Filter": "",
|
||||
"e.g. My Tools": "",
|
||||
@ -408,6 +410,7 @@
|
||||
"Enter Tavily API Key": "Zadajte API kľúč Tavily",
|
||||
"Enter the public URL of your WebUI. This URL will be used to generate links in the notifications.": "",
|
||||
"Enter Tika Server URL": "Zadajte URL servera Tika",
|
||||
"Enter timeout in seconds": "",
|
||||
"Enter Top K": "Zadajte horné K",
|
||||
"Enter URL (e.g. http://127.0.0.1:7860/)": "Zadajte URL (napr. http://127.0.0.1:7860/)",
|
||||
"Enter URL (e.g. http://localhost:11434)": "Zadajte URL (napr. http://localhost:11434)",
|
||||
@ -500,6 +503,9 @@
|
||||
"Functions allow arbitrary code execution": "Funkcie umožňujú vykonávať ľubovoľný kód.",
|
||||
"Functions allow arbitrary code execution.": "Funkcie umožňujú vykonávanie ľubovoľného kódu.",
|
||||
"Functions imported successfully": "Funkcie boli úspešne importované",
|
||||
"Gemini": "",
|
||||
"Gemini API Config": "",
|
||||
"Gemini API Key is required.": "",
|
||||
"General": "Všeobecné",
|
||||
"General Settings": "Všeobecné nastavenia",
|
||||
"Generate an image": "",
|
||||
|
@ -182,6 +182,7 @@
|
||||
"Code execution": "Извршавање кода",
|
||||
"Code Execution": "",
|
||||
"Code Execution Engine": "",
|
||||
"Code Execution Timeout": "",
|
||||
"Code formatted successfully": "Код форматиран успешно",
|
||||
"Code Interpreter": "",
|
||||
"Code Interpreter Engine": "",
|
||||
@ -321,6 +322,7 @@
|
||||
"Draw": "Нацртај",
|
||||
"Drop any files here to add to the conversation": "Убаците било које датотеке овде да их додате у разговор",
|
||||
"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "нпр. '30s', '10m'. Важеће временске јединице су 's', 'm', 'h'.",
|
||||
"e.g. 60": "",
|
||||
"e.g. A filter to remove profanity from text": "",
|
||||
"e.g. My Filter": "",
|
||||
"e.g. My Tools": "",
|
||||
@ -408,6 +410,7 @@
|
||||
"Enter Tavily API Key": "",
|
||||
"Enter the public URL of your WebUI. This URL will be used to generate links in the notifications.": "",
|
||||
"Enter Tika Server URL": "",
|
||||
"Enter timeout in seconds": "",
|
||||
"Enter Top K": "Унесите Топ К",
|
||||
"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)",
|
||||
@ -500,6 +503,9 @@
|
||||
"Functions allow arbitrary code execution": "",
|
||||
"Functions allow arbitrary code execution.": "",
|
||||
"Functions imported successfully": "",
|
||||
"Gemini": "",
|
||||
"Gemini API Config": "",
|
||||
"Gemini API Key is required.": "",
|
||||
"General": "Опште",
|
||||
"General Settings": "Општа подешавања",
|
||||
"Generate an image": "",
|
||||
|
@ -182,6 +182,7 @@
|
||||
"Code execution": "",
|
||||
"Code Execution": "",
|
||||
"Code Execution Engine": "",
|
||||
"Code Execution Timeout": "",
|
||||
"Code formatted successfully": "",
|
||||
"Code Interpreter": "",
|
||||
"Code Interpreter Engine": "",
|
||||
@ -321,6 +322,7 @@
|
||||
"Draw": "",
|
||||
"Drop any files here to add to the conversation": "Släpp filer här för att lägga till i samtalet",
|
||||
"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "t.ex. '30s', '10m'. Giltiga tidsenheter är 's', 'm', 'h'.",
|
||||
"e.g. 60": "",
|
||||
"e.g. A filter to remove profanity from text": "",
|
||||
"e.g. My Filter": "",
|
||||
"e.g. My Tools": "",
|
||||
@ -408,6 +410,7 @@
|
||||
"Enter Tavily API Key": "",
|
||||
"Enter the public URL of your WebUI. This URL will be used to generate links in the notifications.": "",
|
||||
"Enter Tika Server URL": "",
|
||||
"Enter timeout in seconds": "",
|
||||
"Enter Top K": "Ange Top K",
|
||||
"Enter URL (e.g. http://127.0.0.1:7860/)": "Ange URL (t.ex. http://127.0.0.1:7860/)",
|
||||
"Enter URL (e.g. http://localhost:11434)": "Ange URL (t.ex. http://localhost:11434)",
|
||||
@ -500,6 +503,9 @@
|
||||
"Functions allow arbitrary code execution": "",
|
||||
"Functions allow arbitrary code execution.": "",
|
||||
"Functions imported successfully": "",
|
||||
"Gemini": "",
|
||||
"Gemini API Config": "",
|
||||
"Gemini API Key is required.": "",
|
||||
"General": "Allmän",
|
||||
"General Settings": "Allmänna inställningar",
|
||||
"Generate an image": "",
|
||||
|
@ -182,6 +182,7 @@
|
||||
"Code execution": "",
|
||||
"Code Execution": "",
|
||||
"Code Execution Engine": "",
|
||||
"Code Execution Timeout": "",
|
||||
"Code formatted successfully": "จัดรูปแบบโค้ดสำเร็จแล้ว",
|
||||
"Code Interpreter": "",
|
||||
"Code Interpreter Engine": "",
|
||||
@ -321,6 +322,7 @@
|
||||
"Draw": "",
|
||||
"Drop any files here to add to the conversation": "วางไฟล์ใดๆ ที่นี่เพื่อเพิ่มในการสนทนา",
|
||||
"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "เช่น '30s', '10m' หน่วยเวลาที่ถูกต้องคือ 's', 'm', 'h'",
|
||||
"e.g. 60": "",
|
||||
"e.g. A filter to remove profanity from text": "",
|
||||
"e.g. My Filter": "",
|
||||
"e.g. My Tools": "",
|
||||
@ -408,6 +410,7 @@
|
||||
"Enter Tavily API Key": "ใส่คีย์ API ของ Tavily",
|
||||
"Enter the public URL of your WebUI. This URL will be used to generate links in the notifications.": "",
|
||||
"Enter Tika Server URL": "ใส่ URL เซิร์ฟเวอร์ของ Tika",
|
||||
"Enter timeout in seconds": "",
|
||||
"Enter Top K": "ใส่ Top K",
|
||||
"Enter URL (e.g. http://127.0.0.1:7860/)": "ใส่ URL (เช่น http://127.0.0.1:7860/)",
|
||||
"Enter URL (e.g. http://localhost:11434)": "ใส่ URL (เช่น http://localhost:11434)",
|
||||
@ -500,6 +503,9 @@
|
||||
"Functions allow arbitrary code execution": "ฟังก์ชันอนุญาตการเรียกใช้โค้ดโดยพลการ",
|
||||
"Functions allow arbitrary code execution.": "ฟังก์ชันอนุญาตการเรียกใช้โค้ดโดยพลการ",
|
||||
"Functions imported successfully": "นำเข้าฟังก์ชันสำเร็จ",
|
||||
"Gemini": "",
|
||||
"Gemini API Config": "",
|
||||
"Gemini API Key is required.": "",
|
||||
"General": "ทั่วไป",
|
||||
"General Settings": "การตั้งค่าทั่วไป",
|
||||
"Generate an image": "",
|
||||
|
@ -182,6 +182,7 @@
|
||||
"Code execution": "",
|
||||
"Code Execution": "",
|
||||
"Code Execution Engine": "",
|
||||
"Code Execution Timeout": "",
|
||||
"Code formatted successfully": "",
|
||||
"Code Interpreter": "",
|
||||
"Code Interpreter Engine": "",
|
||||
@ -321,6 +322,7 @@
|
||||
"Draw": "",
|
||||
"Drop any files here to add to the conversation": "",
|
||||
"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "",
|
||||
"e.g. 60": "",
|
||||
"e.g. A filter to remove profanity from text": "",
|
||||
"e.g. My Filter": "",
|
||||
"e.g. My Tools": "",
|
||||
@ -408,6 +410,7 @@
|
||||
"Enter Tavily API Key": "",
|
||||
"Enter the public URL of your WebUI. This URL will be used to generate links in the notifications.": "",
|
||||
"Enter Tika Server URL": "",
|
||||
"Enter timeout in seconds": "",
|
||||
"Enter Top K": "",
|
||||
"Enter URL (e.g. http://127.0.0.1:7860/)": "",
|
||||
"Enter URL (e.g. http://localhost:11434)": "",
|
||||
@ -500,6 +503,9 @@
|
||||
"Functions allow arbitrary code execution": "",
|
||||
"Functions allow arbitrary code execution.": "",
|
||||
"Functions imported successfully": "",
|
||||
"Gemini": "",
|
||||
"Gemini API Config": "",
|
||||
"Gemini API Key is required.": "",
|
||||
"General": "",
|
||||
"General Settings": "",
|
||||
"Generate an image": "",
|
||||
|
@ -182,6 +182,7 @@
|
||||
"Code execution": "Kod yürütme",
|
||||
"Code Execution": "",
|
||||
"Code Execution Engine": "",
|
||||
"Code Execution Timeout": "",
|
||||
"Code formatted successfully": "Kod başarıyla biçimlendirildi",
|
||||
"Code Interpreter": "",
|
||||
"Code Interpreter Engine": "",
|
||||
@ -321,6 +322,7 @@
|
||||
"Draw": "Çiz",
|
||||
"Drop any files here to add to the conversation": "Sohbete eklemek istediğiniz dosyaları buraya bırakın",
|
||||
"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "örn. '30s', '10m'. Geçerli zaman birimleri 's', 'm', 'h'.",
|
||||
"e.g. 60": "",
|
||||
"e.g. A filter to remove profanity from text": "örn. Metinden küfürleri kaldırmak için bir filtre",
|
||||
"e.g. My Filter": "örn. Benim Filtrem",
|
||||
"e.g. My Tools": "örn. Benim Araçlarım",
|
||||
@ -408,6 +410,7 @@
|
||||
"Enter Tavily API Key": "Tavily API Anahtarını Girin",
|
||||
"Enter the public URL of your WebUI. This URL will be used to generate links in the notifications.": "",
|
||||
"Enter Tika Server URL": "Tika Sunucu URL'sini Girin",
|
||||
"Enter timeout in seconds": "",
|
||||
"Enter Top K": "Top K'yı girin",
|
||||
"Enter URL (e.g. http://127.0.0.1:7860/)": "URL'yi Girin (örn. http://127.0.0.1:7860/)",
|
||||
"Enter URL (e.g. http://localhost:11434)": "URL'yi Girin (e.g. http://localhost:11434)",
|
||||
@ -500,6 +503,9 @@
|
||||
"Functions allow arbitrary code execution": "Fonksiyonlar keyfi kod yürütülmesine izin verir",
|
||||
"Functions allow arbitrary code execution.": "Fonksiyonlar keyfi kod yürütülmesine izin verir.",
|
||||
"Functions imported successfully": "Fonksiyonlar başarıyla içe aktarıldı",
|
||||
"Gemini": "",
|
||||
"Gemini API Config": "",
|
||||
"Gemini API Key is required.": "",
|
||||
"General": "Genel",
|
||||
"General Settings": "Genel Ayarlar",
|
||||
"Generate an image": "",
|
||||
|
@ -182,6 +182,7 @@
|
||||
"Code execution": "Виконання коду",
|
||||
"Code Execution": "",
|
||||
"Code Execution Engine": "",
|
||||
"Code Execution Timeout": "",
|
||||
"Code formatted successfully": "Код успішно відформатовано",
|
||||
"Code Interpreter": "Інтерпретатор коду",
|
||||
"Code Interpreter Engine": "Двигун інтерпретатора коду",
|
||||
@ -321,6 +322,7 @@
|
||||
"Draw": "Малювати",
|
||||
"Drop any files here to add to the conversation": "Перетягніть сюди файли, щоб додати до розмови",
|
||||
"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "напр., '30s','10m'. Дійсні одиниці часу: 'с', 'хв', 'г'.",
|
||||
"e.g. 60": "",
|
||||
"e.g. A filter to remove profanity from text": "напр., фільтр для видалення нецензурної лексики з тексту",
|
||||
"e.g. My Filter": "напр., Мій фільтр",
|
||||
"e.g. My Tools": "напр., Мої інструменти",
|
||||
@ -408,6 +410,7 @@
|
||||
"Enter Tavily API Key": "Введіть ключ API Tavily",
|
||||
"Enter the public URL of your WebUI. This URL will be used to generate links in the notifications.": "Введіть публічний URL вашого WebUI. Цей URL буде використовуватися для генерування посилань у сповіщеннях.",
|
||||
"Enter Tika Server URL": "Введіть URL-адресу сервера Tika ",
|
||||
"Enter timeout in seconds": "",
|
||||
"Enter Top K": "Введіть Top K",
|
||||
"Enter URL (e.g. http://127.0.0.1:7860/)": "Введіть URL-адресу (напр., http://127.0.0.1:7860/)",
|
||||
"Enter URL (e.g. http://localhost:11434)": "Введіть URL-адресу (напр., http://localhost:11434)",
|
||||
@ -500,6 +503,9 @@
|
||||
"Functions allow arbitrary code execution": "Функції дозволяють виконання довільного коду",
|
||||
"Functions allow arbitrary code execution.": "Функції дозволяють виконання довільного коду.",
|
||||
"Functions imported successfully": "Функції успішно імпортовано",
|
||||
"Gemini": "",
|
||||
"Gemini API Config": "",
|
||||
"Gemini API Key is required.": "",
|
||||
"General": "Загальні",
|
||||
"General Settings": "Загальні налаштування",
|
||||
"Generate an image": "Згенерувати зображення",
|
||||
|
@ -182,6 +182,7 @@
|
||||
"Code execution": "کوڈ کا نفاذ",
|
||||
"Code Execution": "",
|
||||
"Code Execution Engine": "",
|
||||
"Code Execution Timeout": "",
|
||||
"Code formatted successfully": "کوڈ کامیابی سے فارمیٹ ہو گیا",
|
||||
"Code Interpreter": "",
|
||||
"Code Interpreter Engine": "",
|
||||
@ -321,6 +322,7 @@
|
||||
"Draw": "ڈرائنگ کریں",
|
||||
"Drop any files here to add to the conversation": "گفتگو میں شامل کرنے کے لیے کوئی بھی فائل یہاں چھوڑیں",
|
||||
"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "مثلاً '30s'، '10m' درست وقت کی اکائیاں ہیں 's'، 'm'، 'h'",
|
||||
"e.g. 60": "",
|
||||
"e.g. A filter to remove profanity from text": "",
|
||||
"e.g. My Filter": "",
|
||||
"e.g. My Tools": "",
|
||||
@ -408,6 +410,7 @@
|
||||
"Enter Tavily API Key": "Tavily API کلید درج کریں",
|
||||
"Enter the public URL of your WebUI. This URL will be used to generate links in the notifications.": "",
|
||||
"Enter Tika Server URL": "ٹیکا سرور یو آر ایل درج کریں",
|
||||
"Enter timeout in seconds": "",
|
||||
"Enter Top K": "اوپر کے K درج کریں",
|
||||
"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)",
|
||||
@ -500,6 +503,9 @@
|
||||
"Functions allow arbitrary code execution": "فنکشنز کوڈ کے بلاواسطہ نفاذ کی اجازت دیتے ہیں",
|
||||
"Functions allow arbitrary code execution.": "افعال صوابدیدی کوڈ کے اجرا کی اجازت دیتے ہیں",
|
||||
"Functions imported successfully": "فنکشنز کامیابی سے درآمد ہو گئے ہیں",
|
||||
"Gemini": "",
|
||||
"Gemini API Config": "",
|
||||
"Gemini API Key is required.": "",
|
||||
"General": "عمومی",
|
||||
"General Settings": "عمومی ترتیبات",
|
||||
"Generate an image": "",
|
||||
|
@ -182,6 +182,7 @@
|
||||
"Code execution": "",
|
||||
"Code Execution": "",
|
||||
"Code Execution Engine": "",
|
||||
"Code Execution Timeout": "",
|
||||
"Code formatted successfully": "Mã được định dạng thành công",
|
||||
"Code Interpreter": "",
|
||||
"Code Interpreter Engine": "",
|
||||
@ -321,6 +322,7 @@
|
||||
"Draw": "",
|
||||
"Drop any files here to add to the conversation": "Thả bất kỳ tệp nào ở đây để thêm vào nội dung chat",
|
||||
"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "vd: '30s','10m'. Đơn vị thời gian hợp lệ là 's', 'm', 'h'.",
|
||||
"e.g. 60": "",
|
||||
"e.g. A filter to remove profanity from text": "",
|
||||
"e.g. My Filter": "",
|
||||
"e.g. My Tools": "",
|
||||
@ -408,6 +410,7 @@
|
||||
"Enter Tavily API Key": "Nhập Tavily API Key",
|
||||
"Enter the public URL of your WebUI. This URL will be used to generate links in the notifications.": "",
|
||||
"Enter Tika Server URL": "Nhập URL cho Tika Server",
|
||||
"Enter timeout in seconds": "",
|
||||
"Enter Top K": "Nhập Top K",
|
||||
"Enter URL (e.g. http://127.0.0.1:7860/)": "Nhập URL (vd: http://127.0.0.1:7860/)",
|
||||
"Enter URL (e.g. http://localhost:11434)": "Nhập URL (vd: http://localhost:11434)",
|
||||
@ -500,6 +503,9 @@
|
||||
"Functions allow arbitrary code execution": "Các Function cho phép thực thi mã tùy ý",
|
||||
"Functions allow arbitrary code execution.": "Các Function cho phép thực thi mã tùy ý.",
|
||||
"Functions imported successfully": "Các function đã được nạp thành công",
|
||||
"Gemini": "",
|
||||
"Gemini API Config": "",
|
||||
"Gemini API Key is required.": "",
|
||||
"General": "Cài đặt chung",
|
||||
"General Settings": "Cấu hình chung",
|
||||
"Generate an image": "",
|
||||
|
@ -20,7 +20,7 @@
|
||||
"Account Activation Pending": "账号待激活",
|
||||
"Accurate information": "提供的信息很准确",
|
||||
"Actions": "自动化",
|
||||
"Activate": "",
|
||||
"Activate": "激活",
|
||||
"Activate this command by typing \"/{{COMMAND}}\" to chat input.": "通过输入 \"/{{COMMAND}}\" 激活此命令",
|
||||
"Active Users": "当前在线用户",
|
||||
"Add": "添加",
|
||||
@ -100,7 +100,7 @@
|
||||
"Audio": "语音",
|
||||
"August": "八月",
|
||||
"Authenticate": "认证",
|
||||
"Authentication": "",
|
||||
"Authentication": "身份验证",
|
||||
"Auto-Copy Response to Clipboard": "自动复制回复到剪贴板",
|
||||
"Auto-playback response": "自动念出回复内容",
|
||||
"Autocomplete Generation": "输入框内容猜测补全",
|
||||
@ -167,7 +167,7 @@
|
||||
"Click here to": "点击",
|
||||
"Click here to download user import template file.": "点击此处下载用户导入所需的模板文件。",
|
||||
"Click here to learn more about faster-whisper and see the available models.": "点击此处了解更多关于faster-whisper的信息,并查看可用的模型。",
|
||||
"Click here to see available models.": "单击此处查看可用型号。",
|
||||
"Click here to see available models.": "单击此处查看可用模型。",
|
||||
"Click here to select": "点击这里选择",
|
||||
"Click here to select a csv file.": "点击此处选择 csv 文件。",
|
||||
"Click here to select a py file.": "点击此处选择 py 文件。",
|
||||
@ -180,12 +180,13 @@
|
||||
"Clone of {{TITLE}}": "{{TITLE}} 的副本",
|
||||
"Close": "关闭",
|
||||
"Code execution": "代码执行",
|
||||
"Code Execution": "",
|
||||
"Code Execution Engine": "",
|
||||
"Code Execution": "代码执行",
|
||||
"Code Execution Engine": "代码执行引擎",
|
||||
"Code Execution Timeout": "",
|
||||
"Code formatted successfully": "代码格式化成功",
|
||||
"Code Interpreter": "代码解释器",
|
||||
"Code Interpreter Engine": "代码解释引擎",
|
||||
"Code Interpreter Prompt Template": "代码解释器提示模板",
|
||||
"Code Interpreter Prompt Template": "代码解释器提示词模板",
|
||||
"Collection": "文件集",
|
||||
"Color": "颜色",
|
||||
"ComfyUI": "ComfyUI",
|
||||
@ -202,7 +203,7 @@
|
||||
"Confirm Password": "确认密码",
|
||||
"Confirm your action": "确定吗?",
|
||||
"Confirm your new password": "确认新密码",
|
||||
"Connect to your own OpenAI compatible API endpoints.": "连接到您自己的 OpenAI 兼容 API 端点。",
|
||||
"Connect to your own OpenAI compatible API endpoints.": "连接到你自己的与 OpenAI 兼容的 API 接口端点。",
|
||||
"Connections": "外部连接",
|
||||
"Constrains effort on reasoning for reasoning models. Only applicable to reasoning models from specific providers that support reasoning effort. (Default: medium)": "限制推理模型的推理努力。仅适用于支持推理努力的特定提供商的推理模型。(默认值:中等)",
|
||||
"Contact Admin for WebUI Access": "请联系管理员以获取访问权限",
|
||||
@ -214,7 +215,7 @@
|
||||
"Continue with Email": "使用邮箱登录",
|
||||
"Continue with LDAP": "使用 LDAP 登录",
|
||||
"Control how message text is split for TTS requests. 'Punctuation' splits into sentences, 'paragraphs' splits into paragraphs, and 'none' keeps the message as a single string.": "控制消息文本如何拆分以用于 TTS 请求。“Punctuation”拆分为句子,“paragraphs”拆分为段落,“none”将消息保留为单个字符串。",
|
||||
"Control the repetition of token sequences in the generated text. A higher value (e.g., 1.5) will penalize repetitions more strongly, while a lower value (e.g., 1.1) will be more lenient. At 1, it is disabled. (Default: 1.1)": "",
|
||||
"Control the repetition of token sequences in the generated text. A higher value (e.g., 1.5) will penalize repetitions more strongly, while a lower value (e.g., 1.1) will be more lenient. At 1, it is disabled. (Default: 1.1)": "控制生成文本中 Token 的重复。较高的值(例如 1.5)会更强烈地惩罚重复,而较低的值(例如 1.1)则更宽松。设置为 1 时,此功能被禁用。(默认值:1.1)",
|
||||
"Controls": "对话高级设置",
|
||||
"Controls the balance between coherence and diversity of the output. A lower value will result in more focused and coherent text. (Default: 5.0)": "控制输出的连贯性和多样性之间的平衡。较低的值将导致更集中和连贯的文本。(默认值:5.0)",
|
||||
"Copied": "已复制",
|
||||
@ -270,7 +271,7 @@
|
||||
"Delete folder?": "删除分组?",
|
||||
"Delete function?": "删除函数?",
|
||||
"Delete Message": "删除消息",
|
||||
"Delete message?": "",
|
||||
"Delete message?": "删除消息?",
|
||||
"Delete prompt?": "删除提示词?",
|
||||
"delete this link": "此处删除这个链接",
|
||||
"Delete tool?": "删除工具?",
|
||||
@ -282,14 +283,14 @@
|
||||
"Description": "描述",
|
||||
"Didn't fully follow instructions": "没有完全遵照指示",
|
||||
"Direct Connections": "直接连接",
|
||||
"Direct Connections allow users to connect to their own OpenAI compatible API endpoints.": "直接连接允许用户连接到他们自己的与 OpenAI 兼容的 API 端点。",
|
||||
"Direct Connections allow users to connect to their own OpenAI compatible API endpoints.": "直接连接功能允许用户连接至其自有的、兼容 OpenAI 的 API 端点。",
|
||||
"Direct Connections settings updated": "直接连接设置已更新",
|
||||
"Disabled": "禁用",
|
||||
"Discover a function": "发现更多函数",
|
||||
"Discover a model": "发现更多模型",
|
||||
"Discover a prompt": "发现更多提示词",
|
||||
"Discover a tool": "发现更多工具",
|
||||
"Discover how to use Open WebUI and seek support from the community.": "",
|
||||
"Discover how to use Open WebUI and seek support from the community.": "了解如何使用 Open WebUI 并寻求社区支持。",
|
||||
"Discover wonders": "发现奇迹",
|
||||
"Discover, download, and explore custom functions": "发现、下载并探索更多函数",
|
||||
"Discover, download, and explore custom prompts": "发现、下载并探索更多自定义提示词",
|
||||
@ -314,13 +315,14 @@
|
||||
"Don't like the style": "不喜欢这个文风",
|
||||
"Done": "完成",
|
||||
"Download": "下载",
|
||||
"Download as SVG": "",
|
||||
"Download as SVG": "下载为 SVG",
|
||||
"Download canceled": "下载已取消",
|
||||
"Download Database": "下载数据库",
|
||||
"Drag and drop a file to upload or select a file to view": "拖动文件上传或选择文件查看",
|
||||
"Draw": "平局",
|
||||
"Drop any files here to add to the conversation": "拖动文件到此处以添加到对话中",
|
||||
"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "例如 '30s','10m'。有效的时间单位是秒:'s',分:'m',时:'h'。",
|
||||
"e.g. 60": "",
|
||||
"e.g. A filter to remove profanity from text": "例如:一个用于过滤文本中不当内容的过滤器",
|
||||
"e.g. My Filter": "例如:我的过滤器",
|
||||
"e.g. My Tools": "例如:我的工具",
|
||||
@ -370,7 +372,7 @@
|
||||
"Enter Chunk Overlap": "输入块重叠 (Chunk Overlap)",
|
||||
"Enter Chunk Size": "输入块大小 (Chunk Size)",
|
||||
"Enter description": "输入简介描述",
|
||||
"Enter domains separated by commas (e.g., example.com,site.org)": "输入以逗号分隔的域名(例如:example.com,site.org)",
|
||||
"Enter domains separated by commas (e.g., example.com,site.org)": "输入以逗号分隔的域名(例如:example.com、site.org)",
|
||||
"Enter Exa API Key": "输入 Exa API 密钥",
|
||||
"Enter Github Raw URL": "输入 Github Raw 地址",
|
||||
"Enter Google PSE API Key": "输入 Google PSE API 密钥",
|
||||
@ -408,6 +410,7 @@
|
||||
"Enter Tavily API Key": "输入 Tavily API 密钥",
|
||||
"Enter the public URL of your WebUI. This URL will be used to generate links in the notifications.": "输入 WebUI 的公共 URL。此 URL 将用于在通知中生成链接。",
|
||||
"Enter Tika Server URL": "输入 Tika 服务器地址",
|
||||
"Enter timeout in seconds": "",
|
||||
"Enter Top K": "输入 Top K",
|
||||
"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)",
|
||||
@ -455,7 +458,7 @@
|
||||
"Failed to save models configuration": "无法保存模型配置",
|
||||
"Failed to update settings": "无法更新设置",
|
||||
"Failed to upload file.": "上传文件失败",
|
||||
"Features": "",
|
||||
"Features": "功能",
|
||||
"Features Permissions": "功能权限",
|
||||
"February": "二月",
|
||||
"Feedback History": "反馈历史",
|
||||
@ -485,7 +488,7 @@
|
||||
"Form": "手动创建",
|
||||
"Format your variables using brackets like this:": "使用括号格式化你的变量,如下所示:",
|
||||
"Frequency Penalty": "频率惩罚",
|
||||
"Full Context Mode": "",
|
||||
"Full Context Mode": "完整上下文模式",
|
||||
"Function": "函数",
|
||||
"Function Calling": "函数调用 (Function Calling)",
|
||||
"Function created successfully": "函数创建成功",
|
||||
@ -500,6 +503,9 @@
|
||||
"Functions allow arbitrary code execution": "注意:函数有权执行任意代码",
|
||||
"Functions allow arbitrary code execution.": "注意:函数有权执行任意代码。",
|
||||
"Functions imported successfully": "函数导入成功",
|
||||
"Gemini": "",
|
||||
"Gemini API Config": "",
|
||||
"Gemini API Key is required.": "",
|
||||
"General": "通用",
|
||||
"General Settings": "通用设置",
|
||||
"Generate an image": "生成图像",
|
||||
@ -601,7 +607,7 @@
|
||||
"Leave empty to include all models or select specific models": "留空表示包含所有模型或请选择模型",
|
||||
"Leave empty to use the default prompt, or enter a custom prompt": "留空以使用默认提示词,或输入自定义提示词。",
|
||||
"Leave model field empty to use the default model.": "将模型字段留空以使用默认模型。",
|
||||
"License": "",
|
||||
"License": "授权",
|
||||
"Light": "浅色",
|
||||
"Listening...": "正在倾听...",
|
||||
"Llama.cpp": "Llama.cpp",
|
||||
@ -761,7 +767,7 @@
|
||||
"Playground": "AI 对话游乐场",
|
||||
"Please carefully review the following warnings:": "请仔细阅读以下警告信息:",
|
||||
"Please do not close the settings page while loading the model.": "加载模型时请不要关闭设置页面。",
|
||||
"Please enter a prompt": "请输出一个 Prompt",
|
||||
"Please enter a prompt": "请输入一个 Prompt",
|
||||
"Please fill in all fields.": "请填写所有字段。",
|
||||
"Please select a model first.": "请先选择一个模型。",
|
||||
"Please select a model.": "请选择一个模型。",
|
||||
@ -770,7 +776,7 @@
|
||||
"Positive attitude": "积极的态度",
|
||||
"Prefix ID": "Prefix ID",
|
||||
"Prefix ID is used to avoid conflicts with other connections by adding a prefix to the model IDs - leave empty to disable": "Prefix ID 用于通过为模型 ID 添加前缀来避免与其他连接发生冲突 - 留空则禁用此功能",
|
||||
"Presence Penalty": "",
|
||||
"Presence Penalty": "重复惩罚(Presence Penalty)",
|
||||
"Previous 30 days": "过去 30 天",
|
||||
"Previous 7 days": "过去 7 天",
|
||||
"Profile Image": "用户头像",
|
||||
@ -807,7 +813,7 @@
|
||||
"Rename": "重命名",
|
||||
"Reorder Models": "重新排序模型",
|
||||
"Repeat Last N": "重复最后 N 次",
|
||||
"Repeat Penalty (Ollama)": "",
|
||||
"Repeat Penalty (Ollama)": "重复惩罚(Ollama)",
|
||||
"Reply in Thread": "在主题中回复",
|
||||
"Request Mode": "请求模式",
|
||||
"Reranking Model": "重排模型",
|
||||
@ -870,7 +876,7 @@
|
||||
"Select a pipeline": "选择一个管道",
|
||||
"Select a pipeline url": "选择一个管道 URL",
|
||||
"Select a tool": "选择一个工具",
|
||||
"Select an auth method": "选择身份验证方法",
|
||||
"Select an auth method": "选择身份验证方式",
|
||||
"Select an Ollama instance": "选择一个 Ollama 实例。",
|
||||
"Select Engine": "选择引擎",
|
||||
"Select Knowledge": "选择知识",
|
||||
@ -904,10 +910,10 @@
|
||||
"Set the number of worker threads used for computation. This option controls how many threads are used to process incoming requests concurrently. Increasing this value can improve performance under high concurrency workloads but may also consume more CPU resources.": "设置用于计算的工作线程数量。该选项可控制并发处理传入请求的线程数量。增加该值可以提高高并发工作负载下的性能,但也可能消耗更多的 CPU 资源。",
|
||||
"Set Voice": "设置音色",
|
||||
"Set whisper model": "设置 whisper 模型",
|
||||
"Sets a flat bias against tokens that have appeared at least once. A higher value (e.g., 1.5) will penalize repetitions more strongly, while a lower value (e.g., 0.9) will be more lenient. At 0, it is disabled. (Default: 0)": "",
|
||||
"Sets a scaling bias against tokens to penalize repetitions, based on how many times they have appeared. A higher value (e.g., 1.5) will penalize repetitions more strongly, while a lower value (e.g., 0.9) will be more lenient. At 0, it is disabled. (Default: 1.1)": "",
|
||||
"Sets a flat bias against tokens that have appeared at least once. A higher value (e.g., 1.5) will penalize repetitions more strongly, while a lower value (e.g., 0.9) will be more lenient. At 0, it is disabled. (Default: 0)": "这个设置项用于调整对重复 tokens 的抑制强度。当某个 token 至少出现过一次后,系统会通过 flat bias 参数施加惩罚力度:数值越大(如 1.5),抑制重复的效果越强烈;数值较小(如 0.9)则相对宽容。当设为 0 时,系统会完全关闭这个重复抑制功能(默认值为 0)。",
|
||||
"Sets a scaling bias against tokens to penalize repetitions, based on how many times they have appeared. A higher value (e.g., 1.5) will penalize repetitions more strongly, while a lower value (e.g., 0.9) will be more lenient. At 0, it is disabled. (Default: 1.1)": "这个参数用于通过 scaling bias 机制抑制重复内容:当某些 tokens 重复出现时,系统会根据它们已出现的次数自动施加惩罚。数值越大(如 1.5)惩罚力度越强,能更有效减少重复;数值较小(如 0.9)则允许更多重复。当设为 0 时完全关闭该功能,默认值设置为 1.1 保持适度抑制。",
|
||||
"Sets how far back for the model to look back to prevent repetition. (Default: 64, 0 = disabled, -1 = num_ctx)": "设置模型回溯多远以防止重复。(默认值:64,0 = 禁用,-1 = num_ctx)",
|
||||
"Sets the random number seed to use for generation. Setting this to a specific number will make the model generate the same text for the same prompt. (Default: random)": "设置生成文本时使用的随机数种子。将其设置为一个特定的数字将使模型在同一提示下生成相同的文本。 默认值:随机",
|
||||
"Sets the random number seed to use for generation. Setting this to a specific number will make the model generate the same text for the same prompt. (Default: random)": "设置 random number seed 可以控制模型生成文本的随机起点。如果指定一个具体数字,当输入相同的提示语时,模型每次都会生成完全相同的文本内容(默认是随机选取 seed)。",
|
||||
"Sets the size of the context window used to generate the next token. (Default: 2048)": "设置用于生成下一个 Token 的上下文大小。(默认值:2048)",
|
||||
"Sets the stop sequences to use. When this pattern is encountered, the LLM will stop generating text and return. Multiple stop patterns may be set by specifying multiple separate stop parameters in a modelfile.": "设置要使用的停止序列。遇到这种模式时,大语言模型将停止生成文本并返回。可以通过在模型文件中指定多个单独的停止参数来设置多个停止模式。",
|
||||
"Settings": "设置",
|
||||
@ -952,7 +958,7 @@
|
||||
"Tags Generation Prompt": "标签生成提示词",
|
||||
"Tail free sampling is used to reduce the impact of less probable tokens from the output. A higher value (e.g., 2.0) will reduce the impact more, while a value of 1.0 disables this setting. (default: 1)": "Tail free sampling 用于减少输出中可能性较低的标记的影响。数值越大(如 2.0),影响就越小,而数值为 1.0 则会禁用此设置。(默认值:1)",
|
||||
"Tap to interrupt": "点击以中断",
|
||||
"Tasks": "",
|
||||
"Tasks": "任务",
|
||||
"Tavily API Key": "Tavily API 密钥",
|
||||
"Tell us more:": "请告诉我们更多细节",
|
||||
"Temperature": "温度 (Temperature)",
|
||||
@ -975,7 +981,7 @@
|
||||
"The score should be a value between 0.0 (0%) and 1.0 (100%).": "分值应介于 0.0(0%)和 1.0(100%)之间。",
|
||||
"The temperature of the model. Increasing the temperature will make the model answer more creatively. (Default: 0.8)": "模型的温度。提高温度将使模型更具创造性地回答。(默认值:0.8)",
|
||||
"Theme": "主题",
|
||||
"Thinking...": "正在思考...",
|
||||
"Thinking...": "正在深度思考...",
|
||||
"This action cannot be undone. Do you wish to continue?": "此操作无法撤销。是否确认继续?",
|
||||
"This ensures that your valuable conversations are securely saved to your backend database. Thank you!": "这将确保您的宝贵对话被安全地保存到后台数据库中。感谢!",
|
||||
"This is an experimental feature, it may not function as expected and is subject to change at any time.": "这是一个实验功能,可能不会如预期那样工作,而且可能随时发生变化。",
|
||||
@ -989,8 +995,8 @@
|
||||
"This will delete all models including custom models and cannot be undone.": "这将删除所有模型,包括自定义模型,且无法撤销。",
|
||||
"This will reset the knowledge base and sync all files. Do you wish to continue?": "这将重置知识库并替换所有文件为目录下文件。确认继续?",
|
||||
"Thorough explanation": "解释较为详细",
|
||||
"Thought for {{DURATION}}": "已推理 持续 {{DURATION}}",
|
||||
"Thought for {{DURATION}} seconds": "已推理 持续 {{DURATION}} 秒",
|
||||
"Thought for {{DURATION}}": "已深度思考 用时 {{DURATION}}",
|
||||
"Thought for {{DURATION}} seconds": "已深度思考 用时 {{DURATION}} 秒",
|
||||
"Tika": "Tika",
|
||||
"Tika Server URL required.": "请输入 Tika 服务器地址。",
|
||||
"Tiktoken": "Tiktoken",
|
||||
@ -1011,7 +1017,7 @@
|
||||
"To select actions here, add them to the \"Functions\" workspace first.": "要在这里选择自动化,请先将其添加到工作空间中的“函数”。",
|
||||
"To select filters here, add them to the \"Functions\" workspace first.": "要在这里选择过滤器,请先将其添加到工作空间中的“函数”。",
|
||||
"To select toolkits here, add them to the \"Tools\" workspace first.": "要在这里选择工具包,请先将其添加到工作空间中的“工具”。",
|
||||
"Toast notifications for new updates": "新更新的弹窗提示",
|
||||
"Toast notifications for new updates": "更新后弹窗提示更新内容",
|
||||
"Today": "今天",
|
||||
"Toggle settings": "切换设置",
|
||||
"Toggle sidebar": "切换侧边栏",
|
||||
@ -1056,7 +1062,7 @@
|
||||
"Updated": "已更新",
|
||||
"Updated at": "更新于",
|
||||
"Updated At": "更新于",
|
||||
"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 a GGUF model": "上传一个 GGUF 模型",
|
||||
"Upload directory": "上传目录",
|
||||
@ -1095,7 +1101,7 @@
|
||||
"Warning:": "警告:",
|
||||
"Warning: Enabling this will allow users to upload arbitrary code on the server.": "警告:启用此功能将允许用户在服务器上上传任意代码。",
|
||||
"Warning: If you update or change your embedding model, you will need to re-import all documents.": "警告:如果您修改了语义向量模型,则需要重新导入所有文档。",
|
||||
"Warning: Jupyter execution enables arbitrary code execution, posing severe security risks—proceed with extreme caution.": "",
|
||||
"Warning: Jupyter execution enables arbitrary code execution, posing severe security risks—proceed with extreme caution.": "警告:Jupyter 执行允许任意代码执行,存在严重的安全风险——请极其谨慎地操作。",
|
||||
"Web": "网页",
|
||||
"Web API": "网页 API",
|
||||
"Web Loader Settings": "网页爬取设置",
|
||||
|
@ -20,7 +20,7 @@
|
||||
"Account Activation Pending": "帳號待啟用",
|
||||
"Accurate information": "準確資訊",
|
||||
"Actions": "動作",
|
||||
"Activate": "",
|
||||
"Activate": "啟用",
|
||||
"Activate this command by typing \"/{{COMMAND}}\" to chat input.": "在對話輸入框中輸入 \"/{{COMMAND}}\" 來啟用此命令。",
|
||||
"Active Users": "活躍使用者",
|
||||
"Add": "新增",
|
||||
@ -100,7 +100,7 @@
|
||||
"Audio": "音訊",
|
||||
"August": "8 月",
|
||||
"Authenticate": "驗證",
|
||||
"Authentication": "",
|
||||
"Authentication": "驗證",
|
||||
"Auto-Copy Response to Clipboard": "自動將回應複製到剪貼簿",
|
||||
"Auto-playback response": "自動播放回應",
|
||||
"Autocomplete Generation": "自動完成生成",
|
||||
@ -180,8 +180,9 @@
|
||||
"Clone of {{TITLE}}": "{{TITLE}} 的副本",
|
||||
"Close": "關閉",
|
||||
"Code execution": "程式碼執行",
|
||||
"Code Execution": "",
|
||||
"Code Execution Engine": "",
|
||||
"Code Execution": "程式碼執行",
|
||||
"Code Execution Engine": "程式碼執行引擎",
|
||||
"Code Execution Timeout": "",
|
||||
"Code formatted successfully": "程式碼格式化成功",
|
||||
"Code Interpreter": "程式碼解釋器",
|
||||
"Code Interpreter Engine": "程式碼解釋器引擎",
|
||||
@ -214,7 +215,7 @@
|
||||
"Continue with Email": "使用 Email 繼續",
|
||||
"Continue with LDAP": "使用 LDAP 繼續",
|
||||
"Control how message text is split for TTS requests. 'Punctuation' splits into sentences, 'paragraphs' splits into paragraphs, and 'none' keeps the message as a single string.": "控制文字轉語音(TTS)請求中如何分割訊息文字。「標點符號」分割為句子,「段落」分割為段落,「無」則保持訊息為單一字串。",
|
||||
"Control the repetition of token sequences in the generated text. A higher value (e.g., 1.5) will penalize repetitions more strongly, while a lower value (e.g., 1.1) will be more lenient. At 1, it is disabled. (Default: 1.1)": "",
|
||||
"Control the repetition of token sequences in the generated text. A higher value (e.g., 1.5) will penalize repetitions more strongly, while a lower value (e.g., 1.1) will be more lenient. At 1, it is disabled. (Default: 1.1)": "控制在生成文本中 token 序列的重複程度。 數值越高(例如 1.5)將會更強烈地懲罰重複,而數值越低(例如 1.1)則會較為寬鬆。 若數值為 1,則停用此功能。(預設值:1.1)",
|
||||
"Controls": "控制項",
|
||||
"Controls the balance between coherence and diversity of the output. A lower value will result in more focused and coherent text. (Default: 5.0)": "控制輸出的連貫性和多樣性之間的平衡。較低的值會產生更專注和連貫的文字。(預設:5.0)",
|
||||
"Copied": "已複製",
|
||||
@ -269,8 +270,8 @@
|
||||
"Delete chat?": "刪除對話紀錄?",
|
||||
"Delete folder?": "刪除資料夾?",
|
||||
"Delete function?": "刪除函式?",
|
||||
"Delete Message": "刪除訊息?",
|
||||
"Delete message?": "",
|
||||
"Delete Message": "刪除訊息",
|
||||
"Delete message?": "刪除訊息?",
|
||||
"Delete prompt?": "刪除提示詞?",
|
||||
"delete this link": "刪除此連結",
|
||||
"Delete tool?": "刪除工具?",
|
||||
@ -289,7 +290,7 @@
|
||||
"Discover a model": "發掘模型",
|
||||
"Discover a prompt": "發掘提示詞",
|
||||
"Discover a tool": "發掘工具",
|
||||
"Discover how to use Open WebUI and seek support from the community.": "",
|
||||
"Discover how to use Open WebUI and seek support from the community.": "探索如何使用 Open WebUI 並從社群尋求支援。",
|
||||
"Discover wonders": "發掘奇蹟",
|
||||
"Discover, download, and explore custom functions": "發掘、下載及探索自訂函式",
|
||||
"Discover, download, and explore custom prompts": "發掘、下載及探索自訂提示詞",
|
||||
@ -314,13 +315,14 @@
|
||||
"Don't like the style": "不喜歡這個樣式",
|
||||
"Done": "完成",
|
||||
"Download": "下載",
|
||||
"Download as SVG": "",
|
||||
"Download as SVG": "以 SVG 格式下載",
|
||||
"Download canceled": "已取消下載",
|
||||
"Download Database": "下載資料庫",
|
||||
"Drag and drop a file to upload or select a file to view": "拖放檔案以上傳或選擇檔案以檢視",
|
||||
"Draw": "繪製",
|
||||
"Drop any files here to add to the conversation": "拖拽任意檔案到此處以新增至對話",
|
||||
"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "例如:'30s'、'10m'。有效的時間單位為 's'、'm'、'h'。",
|
||||
"e.g. 60": "",
|
||||
"e.g. A filter to remove profanity from text": "例如:從文字中移除髒話的篩選器",
|
||||
"e.g. My Filter": "例如:我的篩選器",
|
||||
"e.g. My Tools": "例如:我的工具",
|
||||
@ -408,6 +410,7 @@
|
||||
"Enter Tavily API Key": "輸入 Tavily API 金鑰",
|
||||
"Enter the public URL of your WebUI. This URL will be used to generate links in the notifications.": "請輸入您 WebUI 的公開 URL。此 URL 將用於在通知中產生連結。",
|
||||
"Enter Tika Server URL": "輸入 Tika 伺服器 URL",
|
||||
"Enter timeout in seconds": "",
|
||||
"Enter Top K": "輸入 Top K 值",
|
||||
"Enter URL (e.g. http://127.0.0.1:7860/)": "輸入 URL(例如:http://127.0.0.1:7860/)",
|
||||
"Enter URL (e.g. http://localhost:11434)": "輸入 URL(例如:http://localhost:11434)",
|
||||
@ -455,7 +458,7 @@
|
||||
"Failed to save models configuration": "儲存模型設定失敗",
|
||||
"Failed to update settings": "更新設定失敗",
|
||||
"Failed to upload file.": "上傳檔案失敗。",
|
||||
"Features": "",
|
||||
"Features": "功能",
|
||||
"Features Permissions": "功能權限",
|
||||
"February": "2 月",
|
||||
"Feedback History": "回饋歷史",
|
||||
@ -485,7 +488,7 @@
|
||||
"Form": "表單",
|
||||
"Format your variables using brackets like this:": "使用方括號格式化您的變數,如下所示:",
|
||||
"Frequency Penalty": "頻率懲罰",
|
||||
"Full Context Mode": "",
|
||||
"Full Context Mode": "完整上下文模式",
|
||||
"Function": "函式",
|
||||
"Function Calling": "函式呼叫",
|
||||
"Function created successfully": "成功建立函式",
|
||||
@ -500,6 +503,9 @@
|
||||
"Functions allow arbitrary code execution": "函式允許執行任意程式碼",
|
||||
"Functions allow arbitrary code execution.": "函式允許執行任意程式碼。",
|
||||
"Functions imported successfully": "成功匯入函式",
|
||||
"Gemini": "",
|
||||
"Gemini API Config": "",
|
||||
"Gemini API Key is required.": "",
|
||||
"General": "一般",
|
||||
"General Settings": "一般設定",
|
||||
"Generate an image": "產生圖片",
|
||||
@ -601,7 +607,7 @@
|
||||
"Leave empty to include all models or select specific models": "留空以包含所有模型或選擇特定模型",
|
||||
"Leave empty to use the default prompt, or enter a custom prompt": "留空以使用預設提示詞,或輸入自訂提示詞",
|
||||
"Leave model field empty to use the default model.": "留空模型欄位以使用預設模型。",
|
||||
"License": "",
|
||||
"License": "授權",
|
||||
"Light": "淺色",
|
||||
"Listening...": "正在聆聽...",
|
||||
"Llama.cpp": "Llama.cpp",
|
||||
@ -770,7 +776,7 @@
|
||||
"Positive attitude": "積極的態度",
|
||||
"Prefix ID": "前綴 ID",
|
||||
"Prefix ID is used to avoid conflicts with other connections by adding a prefix to the model IDs - leave empty to disable": "前綴 ID 用於透過為模型 ID 新增前綴以避免與其他連線衝突 - 留空以停用",
|
||||
"Presence Penalty": "",
|
||||
"Presence Penalty": "在場懲罰",
|
||||
"Previous 30 days": "過去 30 天",
|
||||
"Previous 7 days": "過去 7 天",
|
||||
"Profile Image": "個人檔案圖片",
|
||||
@ -807,7 +813,7 @@
|
||||
"Rename": "重新命名",
|
||||
"Reorder Models": "重新排序模型",
|
||||
"Repeat Last N": "重複最後 N 個",
|
||||
"Repeat Penalty (Ollama)": "",
|
||||
"Repeat Penalty (Ollama)": "重複懲罰 (Ollama)",
|
||||
"Reply in Thread": "在討論串中回覆",
|
||||
"Request Mode": "請求模式",
|
||||
"Reranking Model": "重新排序模型",
|
||||
@ -904,8 +910,8 @@
|
||||
"Set the number of worker threads used for computation. This option controls how many threads are used to process incoming requests concurrently. Increasing this value can improve performance under high concurrency workloads but may also consume more CPU resources.": "設定用於計算的工作執行緒數量。此選項控制使用多少執行緒來同時處理傳入的請求。增加此值可以在高併發工作負載下提升效能,但也可能消耗更多 CPU 資源。",
|
||||
"Set Voice": "設定語音",
|
||||
"Set whisper model": "設定 whisper 模型",
|
||||
"Sets a flat bias against tokens that have appeared at least once. A higher value (e.g., 1.5) will penalize repetitions more strongly, while a lower value (e.g., 0.9) will be more lenient. At 0, it is disabled. (Default: 0)": "",
|
||||
"Sets a scaling bias against tokens to penalize repetitions, based on how many times they have appeared. A higher value (e.g., 1.5) will penalize repetitions more strongly, while a lower value (e.g., 0.9) will be more lenient. At 0, it is disabled. (Default: 1.1)": "",
|
||||
"Sets a flat bias against tokens that have appeared at least once. A higher value (e.g., 1.5) will penalize repetitions more strongly, while a lower value (e.g., 0.9) will be more lenient. At 0, it is disabled. (Default: 0)": "針對至少出現一次的 token,設定固定的負面偏見。 數值越高(例如 1.5)將會更強烈地懲罰重複,而數值越低(例如 0.9)則會較為寬鬆。 若數值為 0,則停用此功能。(預設值:0)",
|
||||
"Sets a scaling bias against tokens to penalize repetitions, based on how many times they have appeared. A higher value (e.g., 1.5) will penalize repetitions more strongly, while a lower value (e.g., 0.9) will be more lenient. At 0, it is disabled. (Default: 1.1)": "針對 token 設定比例偏差以懲罰重複,其基於 token 出現的次數。 數值越高(例如 1.5)將會更強烈地懲罰重複,而數值越低(例如 0.9)則會較為寬鬆。 若數值為 0,則停用此功能。(預設值:1.1)",
|
||||
"Sets how far back for the model to look back to prevent repetition. (Default: 64, 0 = disabled, -1 = num_ctx)": "設定模型向後查看以防止重複的距離。(預設:64,0 = 停用,-1 = num_ctx)",
|
||||
"Sets the random number seed to use for generation. Setting this to a specific number will make the model generate the same text for the same prompt. (Default: random)": "設定用於生成的隨機數種子。將其設定為特定數字會使模型對相同的提示詞產生相同的文字。(預設:隨機)",
|
||||
"Sets the size of the context window used to generate the next token. (Default: 2048)": "設定用於生成下一個 token 的上下文視窗大小。(預設:2048)",
|
||||
@ -952,7 +958,7 @@
|
||||
"Tags Generation Prompt": "標籤生成提示詞",
|
||||
"Tail free sampling is used to reduce the impact of less probable tokens from the output. A higher value (e.g., 2.0) will reduce the impact more, while a value of 1.0 disables this setting. (default: 1)": "使用無尾採樣來減少較不可能的 token 對輸出的影響。較高的值(例如 2.0)會減少更多影響,而值為 1.0 則停用此設定。(預設:1)",
|
||||
"Tap to interrupt": "點選以中斷",
|
||||
"Tasks": "",
|
||||
"Tasks": "任務",
|
||||
"Tavily API Key": "Tavily API 金鑰",
|
||||
"Tell us more:": "告訴我們更多:",
|
||||
"Temperature": "溫度",
|
||||
@ -1056,7 +1062,7 @@
|
||||
"Updated": "已更新",
|
||||
"Updated at": "更新於",
|
||||
"Updated At": "更新於",
|
||||
"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 a GGUF model": "上傳 GGUF 模型",
|
||||
"Upload directory": "上傳目錄",
|
||||
@ -1095,7 +1101,7 @@
|
||||
"Warning:": "警告:",
|
||||
"Warning: Enabling this will allow users to upload arbitrary code on the server.": "警告:啟用此功能將允許使用者在伺服器上上傳任意程式碼。",
|
||||
"Warning: If you update or change your embedding model, you will need to re-import all documents.": "警告:如果您更新或更改嵌入模型,您將需要重新匯入所有文件。",
|
||||
"Warning: Jupyter execution enables arbitrary code execution, posing severe security risks—proceed with extreme caution.": "",
|
||||
"Warning: Jupyter execution enables arbitrary code execution, posing severe security risks—proceed with extreme caution.": "警告:Jupyter 執行允許任意程式碼執行,構成嚴重安全風險——請務必極度謹慎。",
|
||||
"Web": "網頁",
|
||||
"Web API": "網頁 API",
|
||||
"Web Loader Settings": "網頁載入器設定",
|
||||
@ -1109,7 +1115,7 @@
|
||||
"WebUI will make requests to \"{{url}}/api/chat\"": "WebUI 將向 \"{{url}}/api/chat\" 發送請求",
|
||||
"WebUI will make requests to \"{{url}}/chat/completions\"": "WebUI 將向 \"{{url}}/chat/completions\" 發送請求",
|
||||
"What are you trying to achieve?": "您正在試圖完成什麽?",
|
||||
"What are you working on?": "您正在工作什麽?",
|
||||
"What are you working on?": "您現在的工作是什麽?",
|
||||
"What’s New in": "新功能",
|
||||
"When enabled, the model will respond to each chat message in real-time, generating a response as soon as the user sends a message. This mode is useful for live chat applications, but may impact performance on slower hardware.": "啟用時,模型將即時回應每個對話訊息,在使用者傳送訊息後立即生成回應。此模式適用於即時對話應用程式,但在較慢的硬體上可能會影響效能。",
|
||||
"wherever you are": "無論您在何處",
|
||||
|
@ -134,6 +134,9 @@ function inlineKatex(options) {
|
||||
},
|
||||
tokenizer(src, tokens) {
|
||||
return katexTokenizer(src, tokens, false);
|
||||
},
|
||||
renderer(token) {
|
||||
return `${token?.text ?? ''}`;
|
||||
}
|
||||
};
|
||||
}
|
||||
@ -147,6 +150,9 @@ function blockKatex(options) {
|
||||
},
|
||||
tokenizer(src, tokens) {
|
||||
return katexTokenizer(src, tokens, true);
|
||||
},
|
||||
renderer(token) {
|
||||
return `${token?.text ?? ''}`;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -28,6 +28,12 @@
|
||||
|
||||
let ldapUsername = '';
|
||||
|
||||
const querystringValue = (key) => {
|
||||
const querystring = window.location.search;
|
||||
const urlParams = new URLSearchParams(querystring);
|
||||
return urlParams.get(key);
|
||||
};
|
||||
|
||||
const setSessionUser = async (sessionUser) => {
|
||||
if (sessionUser) {
|
||||
console.log(sessionUser);
|
||||
@ -39,7 +45,9 @@
|
||||
$socket.emit('user-join', { auth: { token: sessionUser.token } });
|
||||
await user.set(sessionUser);
|
||||
await config.set(await getBackendConfig());
|
||||
goto('/');
|
||||
|
||||
const redirectPath = querystringValue('redirect') || '/';
|
||||
goto(redirectPath);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -36,3 +36,5 @@
|
||||
@apply cursor-pointer;
|
||||
}
|
||||
}
|
||||
|
||||
@custom-variant hover (&:hover);
|
||||
|
Loading…
x
Reference in New Issue
Block a user