Merge branch 'open-webui:main' into feature-external-db-reconnect

This commit is contained in:
perfectra1n 2024-06-18 12:11:28 -07:00 committed by GitHub
commit 7e061d19ca
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
92 changed files with 3224 additions and 1284 deletions

View File

@ -11,8 +11,6 @@ on:
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
FULL_IMAGE_NAME: ghcr.io/${{ github.repository }}
jobs:
build-main-image:
@ -28,6 +26,15 @@ jobs:
- linux/arm64
steps:
# GitHub Packages requires the entire repository name to be in lowercase
# although the repository owner has a lowercase username, this prevents some people from running actions after forking
- name: Set repository and image name to lowercase
run: |
echo "IMAGE_NAME=${IMAGE_NAME,,}" >>${GITHUB_ENV}
echo "FULL_IMAGE_NAME=ghcr.io/${IMAGE_NAME,,}" >>${GITHUB_ENV}
env:
IMAGE_NAME: '${{ github.repository }}'
- name: Prepare
run: |
platform=${{ matrix.platform }}
@ -116,6 +123,15 @@ jobs:
- linux/arm64
steps:
# GitHub Packages requires the entire repository name to be in lowercase
# although the repository owner has a lowercase username, this prevents some people from running actions after forking
- name: Set repository and image name to lowercase
run: |
echo "IMAGE_NAME=${IMAGE_NAME,,}" >>${GITHUB_ENV}
echo "FULL_IMAGE_NAME=ghcr.io/${IMAGE_NAME,,}" >>${GITHUB_ENV}
env:
IMAGE_NAME: '${{ github.repository }}'
- name: Prepare
run: |
platform=${{ matrix.platform }}
@ -207,6 +223,15 @@ jobs:
- linux/arm64
steps:
# GitHub Packages requires the entire repository name to be in lowercase
# although the repository owner has a lowercase username, this prevents some people from running actions after forking
- name: Set repository and image name to lowercase
run: |
echo "IMAGE_NAME=${IMAGE_NAME,,}" >>${GITHUB_ENV}
echo "FULL_IMAGE_NAME=ghcr.io/${IMAGE_NAME,,}" >>${GITHUB_ENV}
env:
IMAGE_NAME: '${{ github.repository }}'
- name: Prepare
run: |
platform=${{ matrix.platform }}
@ -289,6 +314,15 @@ jobs:
runs-on: ubuntu-latest
needs: [ build-main-image ]
steps:
# GitHub Packages requires the entire repository name to be in lowercase
# although the repository owner has a lowercase username, this prevents some people from running actions after forking
- name: Set repository and image name to lowercase
run: |
echo "IMAGE_NAME=${IMAGE_NAME,,}" >>${GITHUB_ENV}
echo "FULL_IMAGE_NAME=ghcr.io/${IMAGE_NAME,,}" >>${GITHUB_ENV}
env:
IMAGE_NAME: '${{ github.repository }}'
- name: Download digests
uses: actions/download-artifact@v4
with:
@ -335,6 +369,15 @@ jobs:
runs-on: ubuntu-latest
needs: [ build-cuda-image ]
steps:
# GitHub Packages requires the entire repository name to be in lowercase
# although the repository owner has a lowercase username, this prevents some people from running actions after forking
- name: Set repository and image name to lowercase
run: |
echo "IMAGE_NAME=${IMAGE_NAME,,}" >>${GITHUB_ENV}
echo "FULL_IMAGE_NAME=ghcr.io/${IMAGE_NAME,,}" >>${GITHUB_ENV}
env:
IMAGE_NAME: '${{ github.repository }}'
- name: Download digests
uses: actions/download-artifact@v4
with:
@ -382,6 +425,15 @@ jobs:
runs-on: ubuntu-latest
needs: [ build-ollama-image ]
steps:
# GitHub Packages requires the entire repository name to be in lowercase
# although the repository owner has a lowercase username, this prevents some people from running actions after forking
- name: Set repository and image name to lowercase
run: |
echo "IMAGE_NAME=${IMAGE_NAME,,}" >>${GITHUB_ENV}
echo "FULL_IMAGE_NAME=ghcr.io/${IMAGE_NAME,,}" >>${GITHUB_ENV}
env:
IMAGE_NAME: '${{ github.repository }}'
- name: Download digests
uses: actions/download-artifact@v4
with:

View File

@ -5,6 +5,30 @@ 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.3.5] - 2024-06-16
### Added
- **📞 Enhanced Voice Call**: Text-to-speech (TTS) callback now operates in real-time for each sentence, reducing latency by not waiting for full completion.
- **👆 Tap to Interrupt**: During a call, you can now stop the assistant from speaking by simply tapping, instead of using voice. This resolves the issue of the speaker's voice being mistakenly registered as input.
- **😊 Emoji Call**: Toggle this feature on from the Settings > Interface, allowing LLMs to express emotions using emojis during voice calls for a more dynamic interaction.
- **🖱️ Quick Archive/Delete**: Use the Shift key + mouseover on the chat list to swiftly archive or delete items.
- **📝 Markdown Support in Model Descriptions**: You can now format model descriptions with markdown, enabling bold text, links, etc.
- **🧠 Editable Memories**: Adds the capability to modify memories.
- **📋 Admin Panel Sorting**: Introduces the ability to sort users/chats within the admin panel.
- **🌑 Dark Mode for Quick Selectors**: Dark mode now available for chat quick selectors (prompts, models, documents).
- **🔧 Advanced Parameters**: Adds 'num_keep' and 'num_batch' to advanced parameters for customization.
- **📅 Dynamic System Prompts**: New variables '{{CURRENT_DATETIME}}', '{{CURRENT_TIME}}', '{{USER_LOCATION}}' added for system prompts. Ensure '{{USER_LOCATION}}' is toggled on from Settings > Interface.
- **🌐 Tavily Web Search**: Includes Tavily as a web search provider option.
- **🖊️ Federated Auth Usernames**: Ability to set user names for federated authentication.
- **🔗 Auto Clean URLs**: When adding connection URLs, trailing slashes are now automatically removed.
- **🌐 Enhanced Translations**: Improved Chinese and Swedish translations.
### Fixed
- **⏳ AIOHTTP_CLIENT_TIMEOUT**: Introduced a new environment variable 'AIOHTTP_CLIENT_TIMEOUT' for requests to Ollama lasting longer than 5 minutes. Default is 300 seconds; set to blank ('') for no timeout.
- **❌ Message Delete Freeze**: Resolved an issue where message deletion would sometimes cause the web UI to freeze.
## [0.3.4] - 2024-06-12
### Fixed

View File

@ -37,7 +37,7 @@ Open WebUI is an [extensible](https://github.com/open-webui/pipelines), feature-
- 📚 **Local RAG Integration**: Dive into the future of chat interactions with groundbreaking Retrieval Augmented Generation (RAG) support. This feature seamlessly integrates document interactions into your chat experience. You can load documents directly into the chat or add files to your document library, effortlessly accessing them using the `#` command before a query.
- 🔍 **Web Search for RAG**: Perform web searches using providers like `SearXNG`, `Google PSE`, `Brave Search`, `serpstack`, `serper`, and `Serply` and inject the results directly into your chat experience.
- 🔍 **Web Search for RAG**: Perform web searches using providers like `SearXNG`, `Google PSE`, `Brave Search`, `serpstack`, `serper`, `Serply`, `DuckDuckGo` and `TavilySearch` and inject the results directly into your chat experience.
- 🌐 **Web Browsing Capability**: Seamlessly integrate websites into your chat experience using the `#` command followed by a URL. This feature allows you to incorporate web content directly into your conversations, enhancing the richness and depth of your interactions.
@ -160,7 +160,7 @@ Check our Migration Guide available in our [Open WebUI Documentation](https://do
If you want to try out the latest bleeding-edge features and are okay with occasional instability, you can use the `:dev` tag like this:
```bash
docker run -d -p 3000:8080 -v open-webui:/app/backend/data --name open-webui --restart always ghcr.io/open-webui/open-webui:dev
docker run -d -p 3000:8080 -v open-webui:/app/backend/data --name open-webui --add-host=host.docker.internal:host-gateway --restart always ghcr.io/open-webui/open-webui:dev
```
## What's Next? 🌟

View File

@ -18,6 +18,10 @@ If you're experiencing connection issues, its often due to the WebUI docker c
docker run -d --network=host -v open-webui:/app/backend/data -e OLLAMA_BASE_URL=http://127.0.0.1:11434 --name open-webui --restart always ghcr.io/open-webui/open-webui:main
```
### Error on Slow Reponses for Ollama
Open WebUI has a default timeout of 5 minutes for Ollama to finish generating the response. If needed, this can be adjusted via the environment variable AIOHTTP_CLIENT_TIMEOUT, which sets the timeout in seconds.
### General Connection Errors
**Ensure Ollama Version is Up-to-Date**: Always start by checking that you have the latest version of Ollama. Visit [Ollama's official site](https://ollama.com/) for the latest updates.

View File

@ -37,6 +37,10 @@ from config import (
ENABLE_IMAGE_GENERATION,
AUTOMATIC1111_BASE_URL,
COMFYUI_BASE_URL,
COMFYUI_CFG_SCALE,
COMFYUI_SAMPLER,
COMFYUI_SCHEDULER,
COMFYUI_SD3,
IMAGES_OPENAI_API_BASE_URL,
IMAGES_OPENAI_API_KEY,
IMAGE_GENERATION_MODEL,
@ -78,6 +82,10 @@ app.state.config.COMFYUI_BASE_URL = COMFYUI_BASE_URL
app.state.config.IMAGE_SIZE = IMAGE_SIZE
app.state.config.IMAGE_STEPS = IMAGE_STEPS
app.state.config.COMFYUI_CFG_SCALE = COMFYUI_CFG_SCALE
app.state.config.COMFYUI_SAMPLER = COMFYUI_SAMPLER
app.state.config.COMFYUI_SCHEDULER = COMFYUI_SCHEDULER
app.state.config.COMFYUI_SD3 = COMFYUI_SD3
@app.get("/config")
@ -457,6 +465,18 @@ def generate_image(
if form_data.negative_prompt is not None:
data["negative_prompt"] = form_data.negative_prompt
if app.state.config.COMFYUI_CFG_SCALE:
data["cfg_scale"] = app.state.config.COMFYUI_CFG_SCALE
if app.state.config.COMFYUI_SAMPLER is not None:
data["sampler"] = app.state.config.COMFYUI_SAMPLER
if app.state.config.COMFYUI_SCHEDULER is not None:
data["scheduler"] = app.state.config.COMFYUI_SCHEDULER
if app.state.config.COMFYUI_SD3 is not None:
data["sd3"] = app.state.config.COMFYUI_SD3
data = ImageGenerationPayload(**data)
res = comfyui_generate_image(

View File

@ -190,6 +190,10 @@ class ImageGenerationPayload(BaseModel):
width: int
height: int
n: int = 1
cfg_scale: Optional[float] = None
sampler: Optional[str] = None
scheduler: Optional[str] = None
sd3: Optional[bool] = None
def comfyui_generate_image(
@ -199,6 +203,18 @@ def comfyui_generate_image(
comfyui_prompt = json.loads(COMFYUI_DEFAULT_PROMPT)
if payload.cfg_scale:
comfyui_prompt["3"]["inputs"]["cfg"] = payload.cfg_scale
if payload.sampler:
comfyui_prompt["3"]["inputs"]["sampler"] = payload.sampler
if payload.scheduler:
comfyui_prompt["3"]["inputs"]["scheduler"] = payload.scheduler
if payload.sd3:
comfyui_prompt["5"]["class_type"] = "EmptySD3LatentImage"
comfyui_prompt["4"]["inputs"]["ckpt_name"] = model
comfyui_prompt["5"]["inputs"]["batch_size"] = payload.n
comfyui_prompt["5"]["inputs"]["width"] = payload.width

View File

@ -46,6 +46,7 @@ from config import (
SRC_LOG_LEVELS,
OLLAMA_BASE_URLS,
ENABLE_OLLAMA_API,
AIOHTTP_CLIENT_TIMEOUT,
ENABLE_MODEL_FILTER,
MODEL_FILTER_LIST,
UPLOAD_DIR,
@ -154,7 +155,9 @@ async def cleanup_response(
async def post_streaming_url(url: str, payload: str):
r = None
try:
session = aiohttp.ClientSession(trust_env=True)
session = aiohttp.ClientSession(
trust_env=True, timeout=aiohttp.ClientTimeout(total=AIOHTTP_CLIENT_TIMEOUT)
)
r = await session.post(url, data=payload)
r.raise_for_status()
@ -751,6 +754,14 @@ async def generate_chat_completion(
if model_info.params.get("num_ctx", None):
payload["options"]["num_ctx"] = model_info.params.get("num_ctx", None)
if model_info.params.get("num_batch", None):
payload["options"]["num_batch"] = model_info.params.get(
"num_batch", None
)
if model_info.params.get("num_keep", None):
payload["options"]["num_keep"] = model_info.params.get("num_keep", None)
if model_info.params.get("repeat_last_n", None):
payload["options"]["repeat_last_n"] = model_info.params.get(
"repeat_last_n", None
@ -839,8 +850,7 @@ async def generate_chat_completion(
url = app.state.config.OLLAMA_BASE_URLS[url_idx]
log.info(f"url: {url}")
print(payload)
log.debug(payload)
return await post_streaming_url(f"{url}/api/chat", json.dumps(payload))

View File

@ -430,13 +430,11 @@ async def generate_chat_completion(
# Convert the modified body back to JSON
payload = json.dumps(payload)
print(payload)
log.debug(payload)
url = app.state.config.OPENAI_API_BASE_URLS[idx]
key = app.state.config.OPENAI_API_KEYS[idx]
print(payload)
headers = {}
headers["Authorization"] = f"Bearer {key}"
headers["Content-Type"] = "application/json"

View File

@ -73,6 +73,7 @@ from apps.rag.search.serper import search_serper
from apps.rag.search.serpstack import search_serpstack
from apps.rag.search.serply import search_serply
from apps.rag.search.duckduckgo import search_duckduckgo
from apps.rag.search.tavily import search_tavily
from utils.misc import (
calculate_sha256,
@ -119,6 +120,7 @@ from config import (
SERPSTACK_HTTPS,
SERPER_API_KEY,
SERPLY_API_KEY,
TAVILY_API_KEY,
RAG_WEB_SEARCH_RESULT_COUNT,
RAG_WEB_SEARCH_CONCURRENT_REQUESTS,
RAG_EMBEDDING_OPENAI_BATCH_SIZE,
@ -172,6 +174,7 @@ app.state.config.SERPSTACK_API_KEY = SERPSTACK_API_KEY
app.state.config.SERPSTACK_HTTPS = SERPSTACK_HTTPS
app.state.config.SERPER_API_KEY = SERPER_API_KEY
app.state.config.SERPLY_API_KEY = SERPLY_API_KEY
app.state.config.TAVILY_API_KEY = TAVILY_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
@ -400,6 +403,7 @@ async def get_rag_config(user=Depends(get_admin_user)):
"serpstack_https": app.state.config.SERPSTACK_HTTPS,
"serper_api_key": app.state.config.SERPER_API_KEY,
"serply_api_key": app.state.config.SERPLY_API_KEY,
"tavily_api_key": app.state.config.TAVILY_API_KEY,
"result_count": app.state.config.RAG_WEB_SEARCH_RESULT_COUNT,
"concurrent_requests": app.state.config.RAG_WEB_SEARCH_CONCURRENT_REQUESTS,
},
@ -428,6 +432,7 @@ class WebSearchConfig(BaseModel):
serpstack_https: Optional[bool] = None
serper_api_key: Optional[str] = None
serply_api_key: Optional[str] = None
tavily_api_key: Optional[str] = None
result_count: Optional[int] = None
concurrent_requests: Optional[int] = None
@ -479,6 +484,7 @@ async def update_rag_config(form_data: ConfigUpdateForm, user=Depends(get_admin_
app.state.config.SERPSTACK_HTTPS = form_data.web.search.serpstack_https
app.state.config.SERPER_API_KEY = form_data.web.search.serper_api_key
app.state.config.SERPLY_API_KEY = form_data.web.search.serply_api_key
app.state.config.TAVILY_API_KEY = form_data.web.search.tavily_api_key
app.state.config.RAG_WEB_SEARCH_RESULT_COUNT = form_data.web.search.result_count
app.state.config.RAG_WEB_SEARCH_CONCURRENT_REQUESTS = (
form_data.web.search.concurrent_requests
@ -508,6 +514,7 @@ async def update_rag_config(form_data: ConfigUpdateForm, user=Depends(get_admin_
"serpstack_https": app.state.config.SERPSTACK_HTTPS,
"serper_api_key": app.state.config.SERPER_API_KEY,
"serply_api_key": app.state.config.SERPLY_API_KEY,
"tavily_api_key": app.state.config.TAVILY_API_KEY,
"result_count": app.state.config.RAG_WEB_SEARCH_RESULT_COUNT,
"concurrent_requests": app.state.config.RAG_WEB_SEARCH_CONCURRENT_REQUESTS,
},
@ -756,7 +763,7 @@ def search_web(engine: str, query: str) -> list[SearchResult]:
- SERPSTACK_API_KEY
- SERPER_API_KEY
- SERPLY_API_KEY
- TAVILY_API_KEY
Args:
query (str): The query to search for
"""
@ -825,6 +832,15 @@ def search_web(engine: str, query: str) -> list[SearchResult]:
raise Exception("No SERPLY_API_KEY found in environment variables")
elif engine == "duckduckgo":
return search_duckduckgo(query, app.state.config.RAG_WEB_SEARCH_RESULT_COUNT)
elif engine == "tavily":
if app.state.config.TAVILY_API_KEY:
return search_tavily(
app.state.config.TAVILY_API_KEY,
query,
app.state.config.RAG_WEB_SEARCH_RESULT_COUNT,
)
else:
raise Exception("No TAVILY_API_KEY found in environment variables")
else:
raise Exception("No search engine API key found in environment variables")

View File

@ -0,0 +1,39 @@
import logging
import requests
from apps.rag.search.main import SearchResult
from config import SRC_LOG_LEVELS
log = logging.getLogger(__name__)
log.setLevel(SRC_LOG_LEVELS["RAG"])
def search_tavily(api_key: str, query: str, count: int) -> list[SearchResult]:
"""Search using Tavily's Search API and return the results as a list of SearchResult objects.
Args:
api_key (str): A Tavily Search API key
query (str): The query to search for
Returns:
List[SearchResult]: A list of search results
"""
url = "https://api.tavily.com/search"
data = {"query": query, "api_key": api_key}
response = requests.post(url, json=data)
response.raise_for_status()
json_response = response.json()
raw_search_results = json_response.get("results", [])
return [
SearchResult(
link=result["url"],
title=result.get("title", ""),
snippet=result.get("content"),
)
for result in raw_search_results[:count]
]

View File

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

View File

@ -25,6 +25,7 @@ from config import (
USER_PERMISSIONS,
WEBHOOK_URL,
WEBUI_AUTH_TRUSTED_EMAIL_HEADER,
WEBUI_AUTH_TRUSTED_NAME_HEADER,
JWT_EXPIRES_IN,
WEBUI_BANNERS,
ENABLE_COMMUNITY_SHARING,
@ -40,6 +41,7 @@ app.state.config = AppConfig()
app.state.config.ENABLE_SIGNUP = ENABLE_SIGNUP
app.state.config.JWT_EXPIRES_IN = JWT_EXPIRES_IN
app.state.AUTH_TRUSTED_EMAIL_HEADER = WEBUI_AUTH_TRUSTED_EMAIL_HEADER
app.state.AUTH_TRUSTED_NAME_HEADER = WEBUI_AUTH_TRUSTED_NAME_HEADER
app.state.config.SHOW_ADMIN_DETAILS = SHOW_ADMIN_DETAILS

View File

@ -65,6 +65,20 @@ class MemoriesTable:
else:
return None
def update_memory_by_id(
self,
id: str,
content: str,
) -> Optional[MemoryModel]:
try:
memory = Memory.get(Memory.id == id)
memory.content = content
memory.updated_at = int(time.time())
memory.save()
return MemoryModel(**model_to_dict(memory))
except:
return None
def get_memories(self) -> List[MemoryModel]:
try:
memories = Memory.select()

View File

@ -26,6 +26,7 @@ class User(Model):
api_key = CharField(null=True, unique=True)
settings = JSONField(null=True)
info = JSONField(null=True)
class Meta:
database = DB
@ -50,6 +51,7 @@ class UserModel(BaseModel):
api_key: Optional[str] = None
settings: Optional[UserSettings] = None
info: Optional[dict] = None
####################

View File

@ -33,7 +33,11 @@ from utils.utils import (
from utils.misc import parse_duration, validate_email_format
from utils.webhook import post_webhook
from constants import ERROR_MESSAGES, WEBHOOK_MESSAGES
from config import WEBUI_AUTH, WEBUI_AUTH_TRUSTED_EMAIL_HEADER
from config import (
WEBUI_AUTH,
WEBUI_AUTH_TRUSTED_EMAIL_HEADER,
WEBUI_AUTH_TRUSTED_NAME_HEADER,
)
router = APIRouter()
@ -110,11 +114,16 @@ async def signin(request: Request, form_data: SigninForm):
raise HTTPException(400, detail=ERROR_MESSAGES.INVALID_TRUSTED_HEADER)
trusted_email = request.headers[WEBUI_AUTH_TRUSTED_EMAIL_HEADER].lower()
trusted_name = trusted_email
if WEBUI_AUTH_TRUSTED_NAME_HEADER:
trusted_name = request.headers.get(
WEBUI_AUTH_TRUSTED_NAME_HEADER, trusted_email
)
if not Users.get_user_by_email(trusted_email.lower()):
await signup(
request,
SignupForm(
email=trusted_email, password=str(uuid.uuid4()), name=trusted_email
email=trusted_email, password=str(uuid.uuid4()), name=trusted_name
),
)
user = Auths.authenticate_user_by_trusted_header(trusted_email)

View File

@ -44,6 +44,10 @@ class AddMemoryForm(BaseModel):
content: str
class MemoryUpdateModel(BaseModel):
content: Optional[str] = None
@router.post("/add", response_model=Optional[MemoryModel])
async def add_memory(
request: Request, form_data: AddMemoryForm, user=Depends(get_verified_user)
@ -62,6 +66,34 @@ async def add_memory(
return memory
@router.post("/{memory_id}/update", response_model=Optional[MemoryModel])
async def update_memory_by_id(
memory_id: str,
request: Request,
form_data: MemoryUpdateModel,
user=Depends(get_verified_user),
):
memory = Memories.update_memory_by_id(memory_id, form_data.content)
if memory is None:
raise HTTPException(status_code=404, detail="Memory not found")
if form_data.content is not None:
memory_embedding = request.app.state.EMBEDDING_FUNCTION(form_data.content)
collection = CHROMA_CLIENT.get_or_create_collection(
name=f"user-memory-{user.id}"
)
collection.upsert(
documents=[form_data.content],
ids=[memory.id],
embeddings=[memory_embedding],
metadatas=[
{"created_at": memory.created_at, "updated_at": memory.updated_at}
],
)
return memory
############################
# QueryMemory
############################

View File

@ -115,6 +115,52 @@ async def update_user_settings_by_session_user(
)
############################
# GetUserInfoBySessionUser
############################
@router.get("/user/info", response_model=Optional[dict])
async def get_user_info_by_session_user(user=Depends(get_verified_user)):
user = Users.get_user_by_id(user.id)
if user:
return user.info
else:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=ERROR_MESSAGES.USER_NOT_FOUND,
)
############################
# UpdateUserInfoBySessionUser
############################
@router.post("/user/info/update", response_model=Optional[dict])
async def update_user_settings_by_session_user(
form_data: dict, user=Depends(get_verified_user)
):
user = Users.get_user_by_id(user.id)
if user:
if user.info is None:
user.info = {}
user = Users.update_user_by_id(user.id, {"info": {**user.info, **form_data}})
if user:
return user.info
else:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=ERROR_MESSAGES.USER_NOT_FOUND,
)
else:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=ERROR_MESSAGES.USER_NOT_FOUND,
)
############################
# GetUserById
############################

View File

@ -294,6 +294,7 @@ WEBUI_AUTH = os.environ.get("WEBUI_AUTH", "True").lower() == "true"
WEBUI_AUTH_TRUSTED_EMAIL_HEADER = os.environ.get(
"WEBUI_AUTH_TRUSTED_EMAIL_HEADER", None
)
WEBUI_AUTH_TRUSTED_NAME_HEADER = os.environ.get("WEBUI_AUTH_TRUSTED_NAME_HEADER", None)
JWT_EXPIRES_IN = PersistentConfig(
"JWT_EXPIRES_IN", "auth.jwt_expiry", os.environ.get("JWT_EXPIRES_IN", "-1")
)
@ -425,6 +426,14 @@ OLLAMA_API_BASE_URL = os.environ.get(
)
OLLAMA_BASE_URL = os.environ.get("OLLAMA_BASE_URL", "")
AIOHTTP_CLIENT_TIMEOUT = os.environ.get("AIOHTTP_CLIENT_TIMEOUT", "300")
if AIOHTTP_CLIENT_TIMEOUT == "":
AIOHTTP_CLIENT_TIMEOUT = None
else:
AIOHTTP_CLIENT_TIMEOUT = int(AIOHTTP_CLIENT_TIMEOUT)
K8S_FLAG = os.environ.get("K8S_FLAG", "")
USE_OLLAMA_DOCKER = os.environ.get("USE_OLLAMA_DOCKER", "false")
@ -942,6 +951,11 @@ SERPLY_API_KEY = PersistentConfig(
os.getenv("SERPLY_API_KEY", ""),
)
TAVILY_API_KEY = PersistentConfig(
"TAVILY_API_KEY",
"rag.web.search.tavily_api_key",
os.getenv("TAVILY_API_KEY", ""),
)
RAG_WEB_SEARCH_RESULT_COUNT = PersistentConfig(
"RAG_WEB_SEARCH_RESULT_COUNT",
@ -994,6 +1008,30 @@ COMFYUI_BASE_URL = PersistentConfig(
os.getenv("COMFYUI_BASE_URL", ""),
)
COMFYUI_CFG_SCALE = PersistentConfig(
"COMFYUI_CFG_SCALE",
"image_generation.comfyui.cfg_scale",
os.getenv("COMFYUI_CFG_SCALE", ""),
)
COMFYUI_SAMPLER = PersistentConfig(
"COMFYUI_SAMPLER",
"image_generation.comfyui.sampler",
os.getenv("COMFYUI_SAMPLER", ""),
)
COMFYUI_SCHEDULER = PersistentConfig(
"COMFYUI_SCHEDULER",
"image_generation.comfyui.scheduler",
os.getenv("COMFYUI_SCHEDULER", ""),
)
COMFYUI_SD3 = PersistentConfig(
"COMFYUI_SD3",
"image_generation.comfyui.sd3",
os.environ.get("COMFYUI_SD3", "").lower() == "true",
)
IMAGES_OPENAI_API_BASE_URL = PersistentConfig(
"IMAGES_OPENAI_API_BASE_URL",
"image_generation.openai.api_base_url",

View File

@ -494,6 +494,9 @@ def filter_pipeline(payload, user):
if "title" in payload:
del payload["title"]
if "task" in payload:
del payload["task"]
return payload
@ -761,7 +764,12 @@ async def generate_title(form_data: dict, user=Depends(get_verified_user)):
template = app.state.config.TITLE_GENERATION_PROMPT_TEMPLATE
content = title_generation_template(
template, form_data["prompt"], user.model_dump()
template,
form_data["prompt"],
{
"name": user.name,
"location": user.info.get("location") if user.info else None,
},
)
payload = {
@ -773,7 +781,7 @@ async def generate_title(form_data: dict, user=Depends(get_verified_user)):
"title": True,
}
print(payload)
log.debug(payload)
try:
payload = filter_pipeline(payload, user)
@ -827,7 +835,7 @@ async def generate_search_query(form_data: dict, user=Depends(get_verified_user)
template = app.state.config.SEARCH_QUERY_GENERATION_PROMPT_TEMPLATE
content = search_query_generation_template(
template, form_data["prompt"], user.model_dump()
template, form_data["prompt"], {"name": user.name}
)
payload = {
@ -835,6 +843,7 @@ async def generate_search_query(form_data: dict, user=Depends(get_verified_user)
"messages": [{"role": "user", "content": content}],
"stream": False,
"max_tokens": 30,
"task": True,
}
print(payload)
@ -855,6 +864,75 @@ async def generate_search_query(form_data: dict, user=Depends(get_verified_user)
return await generate_openai_chat_completion(payload, user=user)
@app.post("/api/task/emoji/completions")
async def generate_emoji(form_data: dict, user=Depends(get_verified_user)):
print("generate_emoji")
model_id = form_data["model"]
if model_id not in app.state.MODELS:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Model not found",
)
# Check if the user has a custom task model
# If the user has a custom task model, use that model
if app.state.MODELS[model_id]["owned_by"] == "ollama":
if app.state.config.TASK_MODEL:
task_model_id = app.state.config.TASK_MODEL
if task_model_id in app.state.MODELS:
model_id = task_model_id
else:
if app.state.config.TASK_MODEL_EXTERNAL:
task_model_id = app.state.config.TASK_MODEL_EXTERNAL
if task_model_id in app.state.MODELS:
model_id = task_model_id
print(model_id)
model = app.state.MODELS[model_id]
template = '''
Your task is to reflect the speaker's likely facial expression through a fitting emoji. Interpret emotions from the message and reflect their facial expression using fitting, diverse emojis (e.g., 😊, 😢, 😡, 😱).
Message: """{{prompt}}"""
'''
content = title_generation_template(
template,
form_data["prompt"],
{
"name": user.name,
"location": user.info.get("location") if user.info else None,
},
)
payload = {
"model": model_id,
"messages": [{"role": "user", "content": content}],
"stream": False,
"max_tokens": 4,
"chat_id": form_data.get("chat_id", None),
"task": True,
}
log.debug(payload)
try:
payload = filter_pipeline(payload, user)
except Exception as e:
return JSONResponse(
status_code=e.args[0],
content={"detail": e.args[1]},
)
if model["owned_by"] == "ollama":
return await generate_ollama_chat_completion(
OpenAIChatCompletionForm(**payload), user=user
)
else:
return await generate_openai_chat_completion(payload, user=user)
@app.post("/api/task/tools/completions")
async def get_tools_function_calling(form_data: dict, user=Depends(get_verified_user)):
print("get_tools_function_calling")

View File

@ -6,24 +6,28 @@ from typing import Optional
def prompt_template(
template: str, user_name: str = None, current_location: str = None
template: str, user_name: str = None, user_location: str = None
) -> str:
# Get the current date
current_date = datetime.now()
# Format the date to YYYY-MM-DD
formatted_date = current_date.strftime("%Y-%m-%d")
formatted_time = current_date.strftime("%I:%M:%S %p")
# Replace {{CURRENT_DATE}} in the template with the formatted date
template = template.replace("{{CURRENT_DATE}}", formatted_date)
template = template.replace("{{CURRENT_TIME}}", formatted_time)
template = template.replace(
"{{CURRENT_DATETIME}}", f"{formatted_date} {formatted_time}"
)
if user_name:
# Replace {{USER_NAME}} in the template with the user's name
template = template.replace("{{USER_NAME}}", user_name)
if current_location:
# Replace {{CURRENT_LOCATION}} in the template with the current location
template = template.replace("{{CURRENT_LOCATION}}", current_location)
if user_location:
# Replace {{USER_LOCATION}} in the template with the current location
template = template.replace("{{USER_LOCATION}}", user_location)
return template
@ -61,7 +65,7 @@ def title_generation_template(
template = prompt_template(
template,
**(
{"user_name": user.get("name"), "current_location": user.get("location")}
{"user_name": user.get("name"), "user_location": user.get("location")}
if user
else {}
),
@ -104,7 +108,7 @@ def search_query_generation_template(
template = prompt_template(
template,
**(
{"user_name": user.get("name"), "current_location": user.get("location")}
{"user_name": user.get("name"), "user_location": user.get("location")}
if user
else {}
),

4
package-lock.json generated
View File

@ -1,12 +1,12 @@
{
"name": "open-webui",
"version": "0.3.4",
"version": "0.3.5",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "open-webui",
"version": "0.3.4",
"version": "0.3.5",
"dependencies": {
"@codemirror/lang-javascript": "^6.2.2",
"@codemirror/lang-python": "^6.1.6",

View File

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

View File

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

View File

@ -205,6 +205,54 @@ export const generateTitle = async (
return res?.choices[0]?.message?.content.replace(/["']/g, '') ?? 'New Chat';
};
export const generateEmoji = async (
token: string = '',
model: string,
prompt: string,
chat_id?: string
) => {
let error = null;
const res = await fetch(`${WEBUI_BASE_URL}/api/task/emoji/completions`, {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
Authorization: `Bearer ${token}`
},
body: JSON.stringify({
model: model,
prompt: prompt,
...(chat_id && { chat_id: chat_id })
})
})
.then(async (res) => {
if (!res.ok) throw await res.json();
return res.json();
})
.catch((err) => {
console.log(err);
if ('detail' in err) {
error = err.detail;
}
return null;
});
if (error) {
throw error;
}
const response = res?.choices[0]?.message?.content.replace(/["']/g, '') ?? null;
if (response) {
if (/\p{Extended_Pictographic}/u.test(response)) {
return response.match(/\p{Extended_Pictographic}/gu)[0];
}
}
return null;
};
export const generateSearchQuery = async (
token: string = '',
model: string,

View File

@ -59,6 +59,37 @@ export const addNewMemory = async (token: string, content: string) => {
return res;
};
export const updateMemoryById = async (token: string, id: string, content: string) => {
let error = null;
const res = await fetch(`${WEBUI_API_BASE_URL}/memories/${id}/update`, {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
authorization: `Bearer ${token}`
},
body: JSON.stringify({
content: content
})
})
.then(async (res) => {
if (!res.ok) throw await res.json();
return res.json();
})
.catch((err) => {
error = err.detail;
console.log(err);
return null;
});
if (error) {
throw error;
}
return res;
};
export const queryMemory = async (token: string, content: string) => {
let error = null;

View File

@ -1,4 +1,5 @@
import { WEBUI_API_BASE_URL } from '$lib/constants';
import { getUserPosition } from '$lib/utils';
export const getUserPermissions = async (token: string) => {
let error = null;
@ -198,6 +199,75 @@ export const getUserById = async (token: string, userId: string) => {
return res;
};
export const getUserInfo = async (token: string) => {
let error = null;
const res = await fetch(`${WEBUI_API_BASE_URL}/users/user/info`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${token}`
}
})
.then(async (res) => {
if (!res.ok) throw await res.json();
return res.json();
})
.catch((err) => {
console.log(err);
error = err.detail;
return null;
});
if (error) {
throw error;
}
return res;
};
export const updateUserInfo = async (token: string, info: object) => {
let error = null;
const res = await fetch(`${WEBUI_API_BASE_URL}/users/user/info/update`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${token}`
},
body: JSON.stringify({
...info
})
})
.then(async (res) => {
if (!res.ok) throw await res.json();
return res.json();
})
.catch((err) => {
console.log(err);
error = err.detail;
return null;
});
if (error) {
throw error;
}
return res;
};
export const getAndUpdateUserLocation = async (token: string) => {
const location = await getUserPosition().catch((err) => {
throw err;
});
if (location) {
await updateUserInfo(token, { location: location });
return location;
} else {
throw new Error('Failed to get user location');
}
};
export const deleteUserById = async (token: string, userId: string) => {
let error = null;

View File

@ -44,6 +44,8 @@
let ENABLE_OLLAMA_API = null;
const verifyOpenAIHandler = async (idx) => {
OPENAI_API_BASE_URLS = OPENAI_API_BASE_URLS.map((url) => url.replace(/\/$/, ''));
OPENAI_API_BASE_URLS = await updateOpenAIUrls(localStorage.token, OPENAI_API_BASE_URLS);
OPENAI_API_KEYS = await updateOpenAIKeys(localStorage.token, OPENAI_API_KEYS);
@ -63,6 +65,10 @@
};
const verifyOllamaHandler = async (idx) => {
OLLAMA_BASE_URLS = OLLAMA_BASE_URLS.filter((url) => url !== '').map((url) =>
url.replace(/\/$/, '')
);
OLLAMA_BASE_URLS = await updateOllamaUrls(localStorage.token, OLLAMA_BASE_URLS);
const res = await getOllamaVersion(localStorage.token, idx).catch((error) => {
@ -78,6 +84,8 @@
};
const updateOpenAIHandler = async () => {
OPENAI_API_BASE_URLS = OPENAI_API_BASE_URLS.map((url) => url.replace(/\/$/, ''));
// Check if API KEYS length is same than API URLS length
if (OPENAI_API_KEYS.length !== OPENAI_API_BASE_URLS.length) {
// if there are more keys than urls, remove the extra keys
@ -100,7 +108,10 @@
};
const updateOllamaUrlsHandler = async () => {
OLLAMA_BASE_URLS = OLLAMA_BASE_URLS.filter((url) => url !== '');
OLLAMA_BASE_URLS = OLLAMA_BASE_URLS.filter((url) => url !== '').map((url) =>
url.replace(/\/$/, '')
);
console.log(OLLAMA_BASE_URLS);
if (OLLAMA_BASE_URLS.length === 0) {

View File

@ -13,6 +13,8 @@
getRAGConfig,
updateRAGConfig
} from '$lib/apis/rag';
import ResetUploadDirConfirmDialog from '$lib/components/common/ConfirmDialog.svelte';
import ResetVectorDBConfirmDialog from '$lib/components/common/ConfirmDialog.svelte';
import { documents, models } from '$lib/stores';
import { onMount, getContext } from 'svelte';
@ -213,6 +215,34 @@
});
</script>
<ResetUploadDirConfirmDialog
bind:show={showResetUploadDirConfirm}
on:confirm={() => {
const res = resetUploadDir(localStorage.token).catch((error) => {
toast.error(error);
return null;
});
if (res) {
toast.success($i18n.t('Success'));
}
}}
/>
<ResetVectorDBConfirmDialog
bind:show={showResetConfirm}
on:confirm={() => {
const res = resetVectorDB(localStorage.token).catch((error) => {
toast.error(error);
return null;
});
if (res) {
toast.success($i18n.t('Success'));
}
}}
/>
<form
class="flex flex-col h-full justify-between space-y-3 text-sm"
on:submit|preventDefault={() => {
@ -640,199 +670,56 @@
<hr class=" dark:border-gray-850" />
<div>
{#if showResetUploadDirConfirm}
<div class="flex justify-between rounded-md items-center py-2 px-3.5 w-full transition">
<div class="flex items-center space-x-3">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="currentColor"
class="size-4"
>
<path
fill-rule="evenodd"
d="M5.625 1.5H9a3.75 3.75 0 0 1 3.75 3.75v1.875c0 1.036.84 1.875 1.875 1.875H16.5a3.75 3.75 0 0 1 3.75 3.75v7.875c0 1.035-.84 1.875-1.875 1.875H5.625a1.875 1.875 0 0 1-1.875-1.875V3.375c0-1.036.84-1.875 1.875-1.875ZM9.75 14.25a.75.75 0 0 0 0 1.5H15a.75.75 0 0 0 0-1.5H9.75Z"
clip-rule="evenodd"
/>
<path
d="M14.25 5.25a5.23 5.23 0 0 0-1.279-3.434 9.768 9.768 0 0 1 6.963 6.963A5.23 5.23 0 0 0 16.5 7.5h-1.875a.375.375 0 0 1-.375-.375V5.25Z"
/>
</svg>
<span>{$i18n.t('Are you sure?')}</span>
</div>
<div class="flex space-x-1.5 items-center">
<button
class="hover:text-white transition"
on:click={() => {
const res = resetUploadDir(localStorage.token).catch((error) => {
toast.error(error);
return null;
});
if (res) {
toast.success($i18n.t('Success'));
}
showResetUploadDirConfirm = false;
}}
type="button"
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
fill="currentColor"
class="w-4 h-4"
>
<path
fill-rule="evenodd"
d="M16.704 4.153a.75.75 0 01.143 1.052l-8 10.5a.75.75 0 01-1.127.075l-4.5-4.5a.75.75 0 011.06-1.06l3.894 3.893 7.48-9.817a.75.75 0 011.05-.143z"
clip-rule="evenodd"
/>
</svg>
</button>
<button
class="hover:text-white transition"
type="button"
on:click={() => {
showResetUploadDirConfirm = false;
}}
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
fill="currentColor"
class="w-4 h-4"
>
<path
d="M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z"
/>
</svg>
</button>
</div>
<button
class=" flex rounded-xl py-2 px-3.5 w-full hover:bg-gray-200 dark:hover:bg-gray-800 transition"
on:click={() => {
showResetUploadDirConfirm = true;
}}
type="button"
>
<div class=" self-center mr-3">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="currentColor"
class="size-4"
>
<path
fill-rule="evenodd"
d="M5.625 1.5H9a3.75 3.75 0 0 1 3.75 3.75v1.875c0 1.036.84 1.875 1.875 1.875H16.5a3.75 3.75 0 0 1 3.75 3.75v7.875c0 1.035-.84 1.875-1.875 1.875H5.625a1.875 1.875 0 0 1-1.875-1.875V3.375c0-1.036.84-1.875 1.875-1.875ZM9.75 14.25a.75.75 0 0 0 0 1.5H15a.75.75 0 0 0 0-1.5H9.75Z"
clip-rule="evenodd"
/>
<path
d="M14.25 5.25a5.23 5.23 0 0 0-1.279-3.434 9.768 9.768 0 0 1 6.963 6.963A5.23 5.23 0 0 0 16.5 7.5h-1.875a.375.375 0 0 1-.375-.375V5.25Z"
/>
</svg>
</div>
{:else}
<button
class=" flex rounded-xl py-2 px-3.5 w-full hover:bg-gray-200 dark:hover:bg-gray-800 transition"
on:click={() => {
showResetUploadDirConfirm = true;
}}
type="button"
>
<div class=" self-center mr-3">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="currentColor"
class="size-4"
>
<path
fill-rule="evenodd"
d="M5.625 1.5H9a3.75 3.75 0 0 1 3.75 3.75v1.875c0 1.036.84 1.875 1.875 1.875H16.5a3.75 3.75 0 0 1 3.75 3.75v7.875c0 1.035-.84 1.875-1.875 1.875H5.625a1.875 1.875 0 0 1-1.875-1.875V3.375c0-1.036.84-1.875 1.875-1.875ZM9.75 14.25a.75.75 0 0 0 0 1.5H15a.75.75 0 0 0 0-1.5H9.75Z"
clip-rule="evenodd"
/>
<path
d="M14.25 5.25a5.23 5.23 0 0 0-1.279-3.434 9.768 9.768 0 0 1 6.963 6.963A5.23 5.23 0 0 0 16.5 7.5h-1.875a.375.375 0 0 1-.375-.375V5.25Z"
/>
</svg>
</div>
<div class=" self-center text-sm font-medium">{$i18n.t('Reset Upload Directory')}</div>
</button>
{/if}
<div class=" self-center text-sm font-medium">{$i18n.t('Reset Upload Directory')}</div>
</button>
{#if showResetConfirm}
<div class="flex justify-between rounded-md items-center py-2 px-3.5 w-full transition">
<div class="flex items-center space-x-3">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 16 16"
fill="currentColor"
class="w-4 h-4"
>
<path
fill-rule="evenodd"
d="M3.5 2A1.5 1.5 0 0 0 2 3.5v9A1.5 1.5 0 0 0 3.5 14h9a1.5 1.5 0 0 0 1.5-1.5v-7A1.5 1.5 0 0 0 12.5 4H9.621a1.5 1.5 0 0 1-1.06-.44L7.439 2.44A1.5 1.5 0 0 0 6.38 2H3.5Zm6.75 7.75a.75.75 0 0 0 0-1.5h-4.5a.75.75 0 0 0 0 1.5h4.5Z"
clip-rule="evenodd"
/>
</svg>
<span>{$i18n.t('Are you sure?')}</span>
</div>
<div class="flex space-x-1.5 items-center">
<button
class="hover:text-white transition"
on:click={() => {
const res = resetVectorDB(localStorage.token).catch((error) => {
toast.error(error);
return null;
});
if (res) {
toast.success($i18n.t('Success'));
}
showResetConfirm = false;
}}
type="button"
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
fill="currentColor"
class="w-4 h-4"
>
<path
fill-rule="evenodd"
d="M16.704 4.153a.75.75 0 01.143 1.052l-8 10.5a.75.75 0 01-1.127.075l-4.5-4.5a.75.75 0 011.06-1.06l3.894 3.893 7.48-9.817a.75.75 0 011.05-.143z"
clip-rule="evenodd"
/>
</svg>
</button>
<button
class="hover:text-white transition"
on:click={() => {
showResetConfirm = false;
}}
type="button"
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
fill="currentColor"
class="w-4 h-4"
>
<path
d="M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z"
/>
</svg>
</button>
</div>
<button
class=" flex rounded-xl py-2 px-3.5 w-full hover:bg-gray-200 dark:hover:bg-gray-800 transition"
on:click={() => {
showResetConfirm = true;
}}
type="button"
>
<div class=" self-center mr-3">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 16 16"
fill="currentColor"
class="w-4 h-4"
>
<path
fill-rule="evenodd"
d="M3.5 2A1.5 1.5 0 0 0 2 3.5v9A1.5 1.5 0 0 0 3.5 14h9a1.5 1.5 0 0 0 1.5-1.5v-7A1.5 1.5 0 0 0 12.5 4H9.621a1.5 1.5 0 0 1-1.06-.44L7.439 2.44A1.5 1.5 0 0 0 6.38 2H3.5Zm6.75 7.75a.75.75 0 0 0 0-1.5h-4.5a.75.75 0 0 0 0 1.5h4.5Z"
clip-rule="evenodd"
/>
</svg>
</div>
{:else}
<button
class=" flex rounded-xl py-2 px-3.5 w-full hover:bg-gray-200 dark:hover:bg-gray-800 transition"
on:click={() => {
showResetConfirm = true;
}}
type="button"
>
<div class=" self-center mr-3">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 16 16"
fill="currentColor"
class="w-4 h-4"
>
<path
fill-rule="evenodd"
d="M3.5 2A1.5 1.5 0 0 0 2 3.5v9A1.5 1.5 0 0 0 3.5 14h9a1.5 1.5 0 0 0 1.5-1.5v-7A1.5 1.5 0 0 0 12.5 4H9.621a1.5 1.5 0 0 1-1.06-.44L7.439 2.44A1.5 1.5 0 0 0 6.38 2H3.5Zm6.75 7.75a.75.75 0 0 0 0-1.5h-4.5a.75.75 0 0 0 0 1.5h4.5Z"
clip-rule="evenodd"
/>
</svg>
</div>
<div class=" self-center text-sm font-medium">{$i18n.t('Reset Vector Storage')}</div>
</button>
{/if}
<div class=" self-center text-sm font-medium">{$i18n.t('Reset Vector Storage')}</div>
</button>
</div>
</div>
<div class="flex justify-end pt-3 text-sm font-medium">

View File

@ -1,5 +1,10 @@
<script lang="ts">
import { toast } from 'svelte-sonner';
import { onMount, getContext } from 'svelte';
import { WEBUI_API_BASE_URL, WEBUI_BASE_URL } from '$lib/constants';
import { WEBUI_NAME, models, MODEL_DOWNLOAD_POOL, user, config } from '$lib/stores';
import { splitStream } from '$lib/utils';
import {
createModel,
@ -11,15 +16,11 @@
uploadModel,
getOllamaConfig
} from '$lib/apis/ollama';
import { WEBUI_API_BASE_URL, WEBUI_BASE_URL } from '$lib/constants';
import { WEBUI_NAME, models, MODEL_DOWNLOAD_POOL, user, config } from '$lib/stores';
import { splitStream } from '$lib/utils';
import { onMount, getContext } from 'svelte';
import { getModels as _getModels } from '$lib/apis';
import Tooltip from '$lib/components/common/Tooltip.svelte';
import Spinner from '$lib/components/common/Spinner.svelte';
import { getModels as _getModels } from '$lib/apis';
import ModelDeleteConfirmDialog from '$lib/components/common/ConfirmDialog.svelte';
const i18n = getContext('i18n');
@ -29,6 +30,8 @@
let modelUploadInputElement: HTMLInputElement;
let showModelDeleteConfirm = false;
// Models
let ollamaEnabled = null;
@ -549,6 +552,13 @@
});
</script>
<ModelDeleteConfirmDialog
bind:show={showModelDeleteConfirm}
on:confirm={() => {
deleteModelHandler();
}}
/>
<div class="flex flex-col h-full justify-between text-sm">
<div class=" space-y-3 overflow-y-scroll scrollbar-hidden h-full">
{#if ollamaEnabled}
@ -763,7 +773,7 @@
<button
class="px-2.5 bg-gray-100 hover:bg-gray-200 text-gray-800 dark:bg-gray-850 dark:hover:bg-gray-800 dark:text-gray-100 rounded-lg transition"
on:click={() => {
deleteModelHandler();
showModelDeleteConfirm = true;
}}
>
<svg

View File

@ -18,7 +18,8 @@
'serpstack',
'serper',
'serply',
'duckduckgo'
'duckduckgo',
'tavily'
];
let youtubeLanguage = 'en';
@ -214,6 +215,24 @@
</div>
</div>
</div>
{:else if webConfig.search.engine === 'tavily'}
<div>
<div class=" self-center text-xs font-medium mb-1">
{$i18n.t('Tavily API Key')}
</div>
<div class="flex w-full">
<div class="flex-1">
<input
class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
type="text"
placeholder={$i18n.t('Enter Tavily API Key')}
bind:value={webConfig.search.tavily_api_key}
autocomplete="off"
/>
</div>
</div>
</div>
{/if}
</div>
{/if}

View File

@ -31,6 +31,17 @@
}
})();
}
let sortKey = 'updated_at'; // default sort key
let sortOrder = 'desc'; // default sort order
function setSortKey(key) {
if (sortKey === key) {
sortOrder = sortOrder === 'asc' ? 'desc' : 'asc';
} else {
sortKey = key;
sortOrder = 'asc';
}
}
</script>
<Modal size="lg" bind:show>
@ -69,18 +80,56 @@
class="text-xs text-gray-700 uppercase bg-transparent dark:text-gray-200 border-b-2 dark:border-gray-800"
>
<tr>
<th scope="col" class="px-3 py-2"> {$i18n.t('Name')} </th>
<th scope="col" class="px-3 py-2 hidden md:flex"> {$i18n.t('Created at')} </th>
<th
scope="col"
class="px-3 py-2 cursor-pointer select-none"
on:click={() => setSortKey('title')}
>
{$i18n.t('Title')}
{#if sortKey === 'title'}
{sortOrder === 'asc' ? '▲' : '▼'}
{:else}
<span class="invisible"></span>
{/if}
</th>
<th
scope="col"
class="px-3 py-2 cursor-pointer select-none"
on:click={() => setSortKey('created_at')}
>
{$i18n.t('Created at')}
{#if sortKey === 'created_at'}
{sortOrder === 'asc' ? '▲' : '▼'}
{:else}
<span class="invisible"></span>
{/if}
</th>
<th
scope="col"
class="px-3 py-2 hidden md:flex cursor-pointer select-none"
on:click={() => setSortKey('updated_at')}
>
{$i18n.t('Updated at')}
{#if sortKey === 'updated_at'}
{sortOrder === 'asc' ? '▲' : '▼'}
{:else}
<span class="invisible"></span>
{/if}
</th>
<th scope="col" class="px-3 py-2 text-right" />
</tr>
</thead>
<tbody>
{#each chats as chat, idx}
{#each chats.sort((a, b) => {
if (a[sortKey] < b[sortKey]) return sortOrder === 'asc' ? -1 : 1;
if (a[sortKey] > b[sortKey]) return sortOrder === 'asc' ? 1 : -1;
return 0;
}) as chat, idx}
<tr
class="bg-transparent {idx !== chats.length - 1 &&
'border-b'} dark:bg-gray-900 dark:border-gray-850 text-xs"
>
<td class="px-3 py-1 w-2/3">
<td class="px-3 py-1">
<a href="/s/{chat.id}" target="_blank">
<div class=" underline line-clamp-1">
{chat.title}
@ -88,11 +137,16 @@
</a>
</td>
<td class=" px-3 py-1 hidden md:flex h-[2.5rem]">
<td class=" px-3 py-1 h-[2.5rem]">
<div class="my-auto">
{dayjs(chat.created_at * 1000).format($i18n.t('MMMM DD, YYYY HH:mm'))}
</div>
</td>
<td class=" px-3 py-1 hidden md:flex h-[2.5rem]">
<div class="my-auto">
{dayjs(chat.updated_at * 1000).format($i18n.t('MMMM DD, YYYY HH:mm'))}
</div>
</td>
<td class="px-3 py-1 text-right">
<div class="flex justify-end w-full">

View File

@ -30,6 +30,8 @@
import {
convertMessagesToHistory,
copyToClipboard,
extractSentencesForAudio,
getUserPosition,
promptTemplate,
splitStream
} from '$lib/utils';
@ -49,7 +51,7 @@
import { runWebSearch } from '$lib/apis/rag';
import { createOpenAITextStream } from '$lib/apis/streaming';
import { queryMemory } from '$lib/apis/memories';
import { getUserSettings } from '$lib/apis/users';
import { getAndUpdateUserLocation, getUserSettings } from '$lib/apis/users';
import { chatCompleted, generateTitle, generateSearchQuery } from '$lib/apis';
import Banner from '../common/Banner.svelte';
@ -64,6 +66,8 @@
export let chatIdProp = '';
let loaded = false;
const eventTarget = new EventTarget();
let stopResponseFlag = false;
let autoScroll = true;
let processing = '';
@ -108,7 +112,8 @@
$: if (chatIdProp) {
(async () => {
if (await loadChat()) {
console.log(chatIdProp);
if (chatIdProp && (await loadChat())) {
await tick();
loaded = true;
@ -123,7 +128,11 @@
onMount(async () => {
if (!$chatId) {
await initNewChat();
chatId.subscribe(async (value) => {
if (!value) {
await initNewChat();
}
});
} else {
if (!($settings.saveChatHistory ?? true)) {
await goto('/');
@ -300,7 +309,7 @@
// Chat functions
//////////////////////////
const submitPrompt = async (userPrompt, _user = null) => {
const submitPrompt = async (userPrompt, { _raw = false } = {}) => {
let _responses = [];
console.log('submitPrompt', $chatId);
@ -344,7 +353,6 @@
parentId: messages.length !== 0 ? messages.at(-1).id : null,
childrenIds: [],
role: 'user',
user: _user ?? undefined,
content: userPrompt,
files: _files.length > 0 ? _files : undefined,
timestamp: Math.floor(Date.now() / 1000), // Unix epoch
@ -362,15 +370,13 @@
// Wait until history/message have been updated
await tick();
// Send prompt
_responses = await sendPrompt(userPrompt, userMessageId);
_responses = await sendPrompt(userPrompt, userMessageId, { newChat: true });
}
return _responses;
};
const sendPrompt = async (prompt, parentId, modelId = null, newChat = true) => {
const sendPrompt = async (prompt, parentId, { modelId = null, newChat = false } = {}) => {
let _responses = [];
// If modelId is provided, use it, else use selected model
@ -490,7 +496,6 @@
responseMessage.userContext = userContext;
const chatEventEmitter = await getChatEventEmitter(model.id, _chatId);
if (webSearchEnabled) {
await getWebSearchResults(model.id, parentId, responseMessageId);
}
@ -503,8 +508,6 @@
}
_responses.push(_response);
console.log('chatEventEmitter', chatEventEmitter);
if (chatEventEmitter) clearInterval(chatEventEmitter);
} else {
toast.error($i18n.t(`Model {{modelId}} not found`, { modelId }));
@ -513,88 +516,9 @@
);
await chats.set(await getChatList(localStorage.token));
return _responses;
};
const getWebSearchResults = async (model: string, parentId: string, responseId: string) => {
const responseMessage = history.messages[responseId];
responseMessage.statusHistory = [
{
done: false,
action: 'web_search',
description: $i18n.t('Generating search query')
}
];
messages = messages;
const prompt = history.messages[parentId].content;
let searchQuery = await generateSearchQuery(localStorage.token, model, messages, prompt).catch(
(error) => {
console.log(error);
return prompt;
}
);
if (!searchQuery) {
toast.warning($i18n.t('No search query generated'));
responseMessage.statusHistory.push({
done: true,
error: true,
action: 'web_search',
description: 'No search query generated'
});
messages = messages;
}
responseMessage.statusHistory.push({
done: false,
action: 'web_search',
description: $i18n.t(`Searching "{{searchQuery}}"`, { searchQuery })
});
messages = messages;
const results = await runWebSearch(localStorage.token, searchQuery).catch((error) => {
console.log(error);
toast.error(error);
return null;
});
if (results) {
responseMessage.statusHistory.push({
done: true,
action: 'web_search',
description: $i18n.t('Searched {{count}} sites', { count: results.filenames.length }),
query: searchQuery,
urls: results.filenames
});
if (responseMessage?.files ?? undefined === undefined) {
responseMessage.files = [];
}
responseMessage.files.push({
collection_name: results.collection_name,
name: searchQuery,
type: 'web_search_results',
urls: results.filenames
});
messages = messages;
} else {
responseMessage.statusHistory.push({
done: true,
error: true,
action: 'web_search',
description: 'No search results found'
});
messages = messages;
}
};
const sendPromptOllama = async (model, userPrompt, responseMessageId, _chatId) => {
let _response = null;
@ -610,7 +534,13 @@
$settings.system || (responseMessage?.userContext ?? null)
? {
role: 'system',
content: `${promptTemplate($settings?.system ?? '', $user.name)}${
content: `${promptTemplate(
$settings?.system ?? '',
$user.name,
$settings?.userLocation
? await getAndUpdateUserLocation(localStorage.token)
: undefined
)}${
responseMessage?.userContext ?? null
? `\n\nUser Context:\n${(responseMessage?.userContext ?? []).join('\n')}`
: ''
@ -676,6 +606,16 @@
array.findIndex((i) => JSON.stringify(i) === JSON.stringify(item)) === index
);
eventTarget.dispatchEvent(
new CustomEvent('chat:start', {
detail: {
id: responseMessageId
}
})
);
await tick();
const [res, controller] = await generateChatCompletion(localStorage.token, {
model: model.id,
messages: messagesBody,
@ -745,6 +685,23 @@
continue;
} else {
responseMessage.content += data.message.content;
const sentences = extractSentencesForAudio(responseMessage.content);
sentences.pop();
// dispatch only last sentence and make sure it hasn't been dispatched before
if (
sentences.length > 0 &&
sentences[sentences.length - 1] !== responseMessage.lastSentence
) {
responseMessage.lastSentence = sentences[sentences.length - 1];
eventTarget.dispatchEvent(
new CustomEvent('chat', {
detail: { id: responseMessageId, content: sentences[sentences.length - 1] }
})
);
}
messages = messages;
}
} else {
@ -771,21 +728,13 @@
messages = messages;
if ($settings.notificationEnabled && !document.hasFocus()) {
const notification = new Notification(
selectedModelfile
? `${
selectedModelfile.title.charAt(0).toUpperCase() +
selectedModelfile.title.slice(1)
}`
: `${model.id}`,
{
body: responseMessage.content,
icon: selectedModelfile?.imageUrl ?? `${WEBUI_BASE_URL}/static/favicon.png`
}
);
const notification = new Notification(`${model.id}`, {
body: responseMessage.content,
icon: `${WEBUI_BASE_URL}/static/favicon.png`
});
}
if ($settings.responseAutoCopy) {
if ($settings?.responseAutoCopy ?? false) {
copyToClipboard(responseMessage.content);
}
@ -847,6 +796,23 @@
stopResponseFlag = false;
await tick();
let lastSentence = extractSentencesForAudio(responseMessage.content)?.at(-1) ?? '';
if (lastSentence) {
eventTarget.dispatchEvent(
new CustomEvent('chat', {
detail: { id: responseMessageId, content: lastSentence }
})
);
}
eventTarget.dispatchEvent(
new CustomEvent('chat:finish', {
detail: {
id: responseMessageId,
content: responseMessage.content
}
})
);
if (autoScroll) {
scrollToBottom();
}
@ -887,6 +853,15 @@
scrollToBottom();
eventTarget.dispatchEvent(
new CustomEvent('chat:start', {
detail: {
id: responseMessageId
}
})
);
await tick();
try {
const [res, controller] = await generateOpenAIChatCompletion(
localStorage.token,
@ -903,7 +878,13 @@
$settings.system || (responseMessage?.userContext ?? null)
? {
role: 'system',
content: `${promptTemplate($settings?.system ?? '', $user.name)}${
content: `${promptTemplate(
$settings?.system ?? '',
$user.name,
$settings?.userLocation
? await getAndUpdateUserLocation(localStorage.token)
: undefined
)}${
responseMessage?.userContext ?? null
? `\n\nUser Context:\n${(responseMessage?.userContext ?? []).join('\n')}`
: ''
@ -1007,6 +988,23 @@
continue;
} else {
responseMessage.content += value;
const sentences = extractSentencesForAudio(responseMessage.content);
sentences.pop();
// dispatch only last sentence and make sure it hasn't been dispatched before
if (
sentences.length > 0 &&
sentences[sentences.length - 1] !== responseMessage.lastSentence
) {
responseMessage.lastSentence = sentences[sentences.length - 1];
eventTarget.dispatchEvent(
new CustomEvent('chat', {
detail: { id: responseMessageId, content: sentences[sentences.length - 1] }
})
);
}
messages = messages;
}
@ -1057,6 +1055,24 @@
stopResponseFlag = false;
await tick();
let lastSentence = extractSentencesForAudio(responseMessage.content)?.at(-1) ?? '';
if (lastSentence) {
eventTarget.dispatchEvent(
new CustomEvent('chat', {
detail: { id: responseMessageId, content: lastSentence }
})
);
}
eventTarget.dispatchEvent(
new CustomEvent('chat:finish', {
detail: {
id: responseMessageId,
content: responseMessage.content
}
})
);
if (autoScroll) {
scrollToBottom();
}
@ -1123,9 +1139,12 @@
let userPrompt = userMessage.content;
if ((userMessage?.models ?? [...selectedModels]).length == 1) {
await sendPrompt(userPrompt, userMessage.id, undefined, false);
// If user message has only one model selected, sendPrompt automatically selects it for regeneration
await sendPrompt(userPrompt, userMessage.id);
} else {
await sendPrompt(userPrompt, userMessage.id, message.model, false);
// If there are multiple models selected, use the model of the response message for regeneration
// e.g. many model chat
await sendPrompt(userPrompt, userMessage.id, { modelId: message.model });
}
}
};
@ -1191,6 +1210,84 @@
}
};
const getWebSearchResults = async (model: string, parentId: string, responseId: string) => {
const responseMessage = history.messages[responseId];
responseMessage.statusHistory = [
{
done: false,
action: 'web_search',
description: $i18n.t('Generating search query')
}
];
messages = messages;
const prompt = history.messages[parentId].content;
let searchQuery = await generateSearchQuery(localStorage.token, model, messages, prompt).catch(
(error) => {
console.log(error);
return prompt;
}
);
if (!searchQuery) {
toast.warning($i18n.t('No search query generated'));
responseMessage.statusHistory.push({
done: true,
error: true,
action: 'web_search',
description: 'No search query generated'
});
messages = messages;
}
responseMessage.statusHistory.push({
done: false,
action: 'web_search',
description: $i18n.t(`Searching "{{searchQuery}}"`, { searchQuery })
});
messages = messages;
const results = await runWebSearch(localStorage.token, searchQuery).catch((error) => {
console.log(error);
toast.error(error);
return null;
});
if (results) {
responseMessage.statusHistory.push({
done: true,
action: 'web_search',
description: $i18n.t('Searched {{count}} sites', { count: results.filenames.length }),
query: searchQuery,
urls: results.filenames
});
if (responseMessage?.files ?? undefined === undefined) {
responseMessage.files = [];
}
responseMessage.files.push({
collection_name: results.collection_name,
name: searchQuery,
type: 'web_search_results',
urls: results.filenames
});
messages = messages;
} else {
responseMessage.statusHistory.push({
done: true,
error: true,
action: 'web_search',
description: 'No search results found'
});
messages = messages;
}
};
const getTags = async () => {
return await getTagsById(localStorage.token, $chatId).catch(async (error) => {
return [];
@ -1206,7 +1303,18 @@
</title>
</svelte:head>
<CallOverlay {submitPrompt} bind:files />
<audio id="audioElement" src="" style="display: none;" />
{#if $showCallOverlay}
<CallOverlay
{submitPrompt}
{stopResponse}
bind:files
modelId={selectedModelIds?.at(0) ?? null}
chatId={$chatId}
{eventTarget}
/>
{/if}
{#if !chatIdProp || (loaded && chatIdProp)}
<div

View File

@ -348,7 +348,6 @@
<Models
bind:this={modelsElement}
bind:prompt
bind:user
bind:chatInputPlaceholder
{messages}
on:select={(e) => {
@ -467,7 +466,7 @@
document.getElementById('chat-textarea')?.focus();
if ($settings?.speechAutoSend ?? false) {
submitPrompt(prompt, user);
submitPrompt(prompt);
}
}}
/>
@ -476,7 +475,7 @@
class="w-full flex gap-1.5"
on:submit|preventDefault={() => {
// check if selectedModels support image input
submitPrompt(prompt, user);
submitPrompt(prompt);
}}
>
<div
@ -718,7 +717,7 @@
// Submit the prompt when Enter key is pressed
if (prompt !== '' && e.key === 'Enter' && !e.shiftKey) {
submitPrompt(prompt, user);
submitPrompt(prompt);
}
}
}}

View File

@ -2,342 +2,49 @@
import { config, settings, showCallOverlay } from '$lib/stores';
import { onMount, tick, getContext } from 'svelte';
import { blobToFile, calculateSHA256, extractSentences, findWordIndices } from '$lib/utils';
import {
blobToFile,
calculateSHA256,
extractSentencesForAudio,
findWordIndices
} from '$lib/utils';
import { generateEmoji } from '$lib/apis';
import { synthesizeOpenAISpeech, transcribeAudio } from '$lib/apis/audio';
import { toast } from 'svelte-sonner';
import Tooltip from '$lib/components/common/Tooltip.svelte';
import VideoInputMenu from './CallOverlay/VideoInputMenu.svelte';
import { get } from 'svelte/store';
const i18n = getContext('i18n');
export let eventTarget: EventTarget;
export let submitPrompt: Function;
export let stopResponse: Function;
export let files;
export let chatId;
export let modelId;
let loading = false;
let confirmed = false;
let interrupted = false;
let assistantSpeaking = false;
let emoji = null;
let camera = false;
let cameraStream = null;
let assistantSpeaking = false;
let assistantAudio = {};
let assistantAudioIdx = null;
let chatStreaming = false;
let rmsLevel = 0;
let hasStartedSpeaking = false;
let currentUtterance = null;
let mediaRecorder;
let audioChunks = [];
const MIN_DECIBELS = -45;
const VISUALIZER_BUFFER_LENGTH = 300;
// Function to calculate the RMS level from time domain data
const calculateRMS = (data: Uint8Array) => {
let sumSquares = 0;
for (let i = 0; i < data.length; i++) {
const normalizedValue = (data[i] - 128) / 128; // Normalize the data
sumSquares += normalizedValue * normalizedValue;
}
return Math.sqrt(sumSquares / data.length);
};
const normalizeRMS = (rms) => {
rms = rms * 10;
const exp = 1.5; // Adjust exponent value; values greater than 1 expand larger numbers more and compress smaller numbers more
const scaledRMS = Math.pow(rms, exp);
// Scale between 0.01 (1%) and 1.0 (100%)
return Math.min(1.0, Math.max(0.01, scaledRMS));
};
const analyseAudio = (stream) => {
const audioContext = new AudioContext();
const audioStreamSource = audioContext.createMediaStreamSource(stream);
const analyser = audioContext.createAnalyser();
analyser.minDecibels = MIN_DECIBELS;
audioStreamSource.connect(analyser);
const bufferLength = analyser.frequencyBinCount;
const domainData = new Uint8Array(bufferLength);
const timeDomainData = new Uint8Array(analyser.fftSize);
let lastSoundTime = Date.now();
hasStartedSpeaking = false;
const detectSound = () => {
const processFrame = () => {
if (!mediaRecorder || !$showCallOverlay) {
if (mediaRecorder) {
mediaRecorder.stop();
}
return;
}
analyser.getByteTimeDomainData(timeDomainData);
analyser.getByteFrequencyData(domainData);
// Calculate RMS level from time domain data
rmsLevel = calculateRMS(timeDomainData);
// Check if initial speech/noise has started
const hasSound = domainData.some((value) => value > 0);
if (hasSound) {
stopAllAudio();
hasStartedSpeaking = true;
lastSoundTime = Date.now();
}
// Start silence detection only after initial speech/noise has been detected
if (hasStartedSpeaking) {
if (Date.now() - lastSoundTime > 2000) {
confirmed = true;
if (mediaRecorder) {
mediaRecorder.stop();
}
}
}
window.requestAnimationFrame(processFrame);
};
window.requestAnimationFrame(processFrame);
};
detectSound();
};
const stopAllAudio = () => {
if (currentUtterance) {
speechSynthesis.cancel();
currentUtterance = null;
}
if (assistantAudio[assistantAudioIdx]) {
assistantAudio[assistantAudioIdx].pause();
assistantAudio[assistantAudioIdx].currentTime = 0;
}
const audioElement = document.getElementById('audioElement');
audioElement.pause();
audioElement.currentTime = 0;
assistantSpeaking = false;
};
const playAudio = (idx) => {
if ($showCallOverlay) {
return new Promise((res) => {
assistantAudioIdx = idx;
const audioElement = document.getElementById('audioElement');
const audio = assistantAudio[idx];
audioElement.src = audio.src; // Assume `assistantAudio` has objects with a `src` property
audioElement.muted = true;
audioElement
.play()
.then(() => {
audioElement.muted = false;
})
.catch((error) => {
toast.error(error);
});
audioElement.onended = async (e) => {
await new Promise((r) => setTimeout(r, 300));
if (Object.keys(assistantAudio).length - 1 === idx) {
assistantSpeaking = false;
}
res(e);
};
});
} else {
return Promise.resolve();
}
};
const getOpenAISpeech = async (text) => {
const res = await synthesizeOpenAISpeech(
localStorage.token,
$settings?.audio?.tts?.voice ?? $config?.audio?.tts?.voice,
text
).catch((error) => {
toast.error(error);
assistantSpeaking = false;
return null;
});
if (res) {
const blob = await res.blob();
const blobUrl = URL.createObjectURL(blob);
const audio = new Audio(blobUrl);
assistantAudio = audio;
}
};
const transcribeHandler = async (audioBlob) => {
// Create a blob from the audio chunks
await tick();
const file = blobToFile(audioBlob, 'recording.wav');
const res = await transcribeAudio(localStorage.token, file).catch((error) => {
toast.error(error);
return null;
});
if (res) {
console.log(res.text);
if (res.text !== '') {
const _responses = await submitPrompt(res.text);
console.log(_responses);
if (_responses.at(0)) {
const content = _responses[0];
if ((content ?? '').trim() !== '') {
assistantSpeakingHandler(content);
}
}
}
}
};
const assistantSpeakingHandler = async (content) => {
assistantSpeaking = true;
if (($config.audio.tts.engine ?? '') == '') {
let voices = [];
const getVoicesLoop = setInterval(async () => {
voices = await speechSynthesis.getVoices();
if (voices.length > 0) {
clearInterval(getVoicesLoop);
const voice =
voices
?.filter(
(v) => v.voiceURI === ($settings?.audio?.tts?.voice ?? $config?.audio?.tts?.voice)
)
?.at(0) ?? undefined;
currentUtterance = new SpeechSynthesisUtterance(content);
if (voice) {
currentUtterance.voice = voice;
}
speechSynthesis.speak(currentUtterance);
}
}, 100);
} else if ($config.audio.tts.engine === 'openai') {
console.log('openai');
const sentences = extractSentences(content).reduce((mergedTexts, currentText) => {
const lastIndex = mergedTexts.length - 1;
if (lastIndex >= 0) {
const previousText = mergedTexts[lastIndex];
const wordCount = previousText.split(/\s+/).length;
if (wordCount < 2) {
mergedTexts[lastIndex] = previousText + ' ' + currentText;
} else {
mergedTexts.push(currentText);
}
} else {
mergedTexts.push(currentText);
}
return mergedTexts;
}, []);
console.log(sentences);
let lastPlayedAudioPromise = Promise.resolve(); // Initialize a promise that resolves immediately
for (const [idx, sentence] of sentences.entries()) {
const res = await synthesizeOpenAISpeech(
localStorage.token,
$settings?.audio?.tts?.voice ?? $config?.audio?.tts?.voice,
sentence
).catch((error) => {
toast.error(error);
assistantSpeaking = false;
return null;
});
if (res) {
const blob = await res.blob();
const blobUrl = URL.createObjectURL(blob);
const audio = new Audio(blobUrl);
assistantAudio[idx] = audio;
lastPlayedAudioPromise = lastPlayedAudioPromise.then(() => playAudio(idx));
}
}
}
};
const stopRecordingCallback = async () => {
if ($showCallOverlay) {
if (confirmed) {
loading = true;
if (cameraStream) {
const imageUrl = takeScreenshot();
files = [
{
type: 'image',
url: imageUrl
}
];
}
const audioBlob = new Blob(audioChunks, { type: 'audio/wav' });
await transcribeHandler(audioBlob);
confirmed = false;
loading = false;
}
audioChunks = [];
mediaRecorder = false;
startRecording();
} else {
audioChunks = [];
mediaRecorder = false;
}
};
const startRecording = async () => {
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
mediaRecorder = new MediaRecorder(stream);
mediaRecorder.onstart = () => {
console.log('Recording started');
audioChunks = [];
analyseAudio(stream);
};
mediaRecorder.ondataavailable = (event) => {
if (hasStartedSpeaking) {
audioChunks.push(event.data);
}
};
mediaRecorder.onstop = async () => {
console.log('Recording stopped');
await stopRecordingCallback();
};
mediaRecorder.start();
};
let videoInputDevices = [];
let selectedVideoInputDeviceId = null;
@ -439,23 +146,473 @@
camera = false;
};
$: if ($showCallOverlay) {
const MIN_DECIBELS = -55;
const VISUALIZER_BUFFER_LENGTH = 300;
const transcribeHandler = async (audioBlob) => {
// Create a blob from the audio chunks
await tick();
const file = blobToFile(audioBlob, 'recording.wav');
const res = await transcribeAudio(localStorage.token, file).catch((error) => {
toast.error(error);
return null;
});
if (res) {
console.log(res.text);
if (res.text !== '') {
const _responses = await submitPrompt(res.text, { _raw: true });
console.log(_responses);
}
}
};
const stopRecordingCallback = async (_continue = true) => {
if ($showCallOverlay) {
console.log('%c%s', 'color: red; font-size: 20px;', '🚨 stopRecordingCallback 🚨');
// deep copy the audioChunks array
const _audioChunks = audioChunks.slice(0);
audioChunks = [];
mediaRecorder = false;
if (_continue) {
startRecording();
}
if (confirmed) {
loading = true;
emoji = null;
if (cameraStream) {
const imageUrl = takeScreenshot();
files = [
{
type: 'image',
url: imageUrl
}
];
}
const audioBlob = new Blob(_audioChunks, { type: 'audio/wav' });
await transcribeHandler(audioBlob);
confirmed = false;
loading = false;
}
} else {
audioChunks = [];
mediaRecorder = false;
}
};
const startRecording = async () => {
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
mediaRecorder = new MediaRecorder(stream);
mediaRecorder.onstart = () => {
console.log('Recording started');
audioChunks = [];
analyseAudio(stream);
};
mediaRecorder.ondataavailable = (event) => {
if (hasStartedSpeaking) {
audioChunks.push(event.data);
}
};
mediaRecorder.onstop = (e) => {
console.log('Recording stopped', e);
stopRecordingCallback();
};
mediaRecorder.start();
};
// Function to calculate the RMS level from time domain data
const calculateRMS = (data: Uint8Array) => {
let sumSquares = 0;
for (let i = 0; i < data.length; i++) {
const normalizedValue = (data[i] - 128) / 128; // Normalize the data
sumSquares += normalizedValue * normalizedValue;
}
return Math.sqrt(sumSquares / data.length);
};
const analyseAudio = (stream) => {
const audioContext = new AudioContext();
const audioStreamSource = audioContext.createMediaStreamSource(stream);
const analyser = audioContext.createAnalyser();
analyser.minDecibels = MIN_DECIBELS;
audioStreamSource.connect(analyser);
const bufferLength = analyser.frequencyBinCount;
const domainData = new Uint8Array(bufferLength);
const timeDomainData = new Uint8Array(analyser.fftSize);
let lastSoundTime = Date.now();
hasStartedSpeaking = false;
console.log('🔊 Sound detection started', lastSoundTime, hasStartedSpeaking);
const detectSound = () => {
const processFrame = () => {
if (!mediaRecorder || !$showCallOverlay) {
return;
}
if (assistantSpeaking) {
// Mute the audio if the assistant is speaking
analyser.maxDecibels = 0;
analyser.minDecibels = -1;
} else {
analyser.minDecibels = MIN_DECIBELS;
analyser.maxDecibels = -30;
}
analyser.getByteTimeDomainData(timeDomainData);
analyser.getByteFrequencyData(domainData);
// Calculate RMS level from time domain data
rmsLevel = calculateRMS(timeDomainData);
// Check if initial speech/noise has started
const hasSound = domainData.some((value) => value > 0);
if (hasSound) {
// BIG RED TEXT
console.log('%c%s', 'color: red; font-size: 20px;', '🔊 Sound detected');
if (!hasStartedSpeaking) {
hasStartedSpeaking = true;
stopAllAudio();
}
lastSoundTime = Date.now();
}
// Start silence detection only after initial speech/noise has been detected
if (hasStartedSpeaking) {
if (Date.now() - lastSoundTime > 2000) {
confirmed = true;
if (mediaRecorder) {
console.log('%c%s', 'color: red; font-size: 20px;', '🔇 Silence detected');
mediaRecorder.stop();
return;
}
}
}
window.requestAnimationFrame(processFrame);
};
window.requestAnimationFrame(processFrame);
};
detectSound();
};
let finishedMessages = {};
let currentMessageId = null;
let currentUtterance = null;
const speakSpeechSynthesisHandler = (content) => {
if ($showCallOverlay) {
return new Promise((resolve) => {
let voices = [];
const getVoicesLoop = setInterval(async () => {
voices = await speechSynthesis.getVoices();
if (voices.length > 0) {
clearInterval(getVoicesLoop);
const voice =
voices
?.filter(
(v) => v.voiceURI === ($settings?.audio?.tts?.voice ?? $config?.audio?.tts?.voice)
)
?.at(0) ?? undefined;
currentUtterance = new SpeechSynthesisUtterance(content);
if (voice) {
currentUtterance.voice = voice;
}
speechSynthesis.speak(currentUtterance);
currentUtterance.onend = async (e) => {
await new Promise((r) => setTimeout(r, 200));
resolve(e);
};
}
}, 100);
});
} else {
return Promise.resolve();
}
};
const playAudio = (audio) => {
if ($showCallOverlay) {
return new Promise((resolve) => {
const audioElement = document.getElementById('audioElement');
if (audioElement) {
audioElement.src = audio.src;
audioElement.muted = true;
audioElement
.play()
.then(() => {
audioElement.muted = false;
})
.catch((error) => {
console.error(error);
});
audioElement.onended = async (e) => {
await new Promise((r) => setTimeout(r, 100));
resolve(e);
};
}
});
} else {
return Promise.resolve();
}
};
const stopAllAudio = async () => {
assistantSpeaking = false;
interrupted = true;
if (chatStreaming) {
stopResponse();
}
if (currentUtterance) {
speechSynthesis.cancel();
currentUtterance = null;
}
const audioElement = document.getElementById('audioElement');
if (audioElement) {
audioElement.muted = true;
audioElement.pause();
audioElement.currentTime = 0;
}
};
let audioAbortController = new AbortController();
// Audio cache map where key is the content and value is the Audio object.
const audioCache = new Map();
const emojiCache = new Map();
const fetchAudio = async (content) => {
if (!audioCache.has(content)) {
try {
// Set the emoji for the content if needed
if ($settings?.showEmojiInCall ?? false) {
const emoji = await generateEmoji(localStorage.token, modelId, content, chatId);
if (emoji) {
emojiCache.set(content, emoji);
}
}
if ($config.audio.tts.engine !== '') {
const res = await synthesizeOpenAISpeech(
localStorage.token,
$settings?.audio?.tts?.voice ?? $config?.audio?.tts?.voice,
content
).catch((error) => {
console.error(error);
return null;
});
if (res) {
const blob = await res.blob();
const blobUrl = URL.createObjectURL(blob);
audioCache.set(content, new Audio(blobUrl));
}
} else {
audioCache.set(content, true);
}
} catch (error) {
console.error('Error synthesizing speech:', error);
}
}
return audioCache.get(content);
};
let messages = {};
const monitorAndPlayAudio = async (id, signal) => {
while (!signal.aborted) {
if (messages[id] && messages[id].length > 0) {
// Retrieve the next content string from the queue
const content = messages[id].shift(); // Dequeues the content for playing
if (audioCache.has(content)) {
// If content is available in the cache, play it
// Set the emoji for the content if available
if (($settings?.showEmojiInCall ?? false) && emojiCache.has(content)) {
emoji = emojiCache.get(content);
} else {
emoji = null;
}
if ($config.audio.tts.engine !== '') {
try {
console.log(
'%c%s',
'color: red; font-size: 20px;',
`Playing audio for content: ${content}`
);
const audio = audioCache.get(content);
await playAudio(audio); // Here ensure that playAudio is indeed correct method to execute
console.log(`Played audio for content: ${content}`);
await new Promise((resolve) => setTimeout(resolve, 200)); // Wait before retrying to reduce tight loop
} catch (error) {
console.error('Error playing audio:', error);
}
} else {
await speakSpeechSynthesisHandler(content);
}
} else {
// If not available in the cache, push it back to the queue and delay
messages[id].unshift(content); // Re-queue the content at the start
console.log(`Audio for "${content}" not yet available in the cache, re-queued...`);
await new Promise((resolve) => setTimeout(resolve, 200)); // Wait before retrying to reduce tight loop
}
} else if (finishedMessages[id] && messages[id] && messages[id].length === 0) {
// If the message is finished and there are no more messages to process, break the loop
assistantSpeaking = false;
break;
} else {
// No messages to process, sleep for a bit
await new Promise((resolve) => setTimeout(resolve, 200));
}
}
console.log(`Audio monitoring and playing stopped for message ID ${id}`);
};
onMount(async () => {
startRecording();
} else {
stopCamera();
}
const chatStartHandler = async (e) => {
const { id } = e.detail;
chatStreaming = true;
if (currentMessageId !== id) {
console.log(`Received chat start event for message ID ${id}`);
currentMessageId = id;
if (audioAbortController) {
audioAbortController.abort();
}
audioAbortController = new AbortController();
assistantSpeaking = true;
// Start monitoring and playing audio for the message ID
monitorAndPlayAudio(id, audioAbortController.signal);
}
};
const chatEventHandler = async (e) => {
const { id, content } = e.detail;
// "id" here is message id
// if "id" is not the same as "currentMessageId" then do not process
// "content" here is a sentence from the assistant,
// there will be many sentences for the same "id"
if (currentMessageId === id) {
console.log(`Received chat event for message ID ${id}: ${content}`);
try {
if (messages[id] === undefined) {
messages[id] = [content];
} else {
messages[id].push(content);
}
console.log(content);
fetchAudio(content);
} catch (error) {
console.error('Failed to fetch or play audio:', error);
}
}
};
const chatFinishHandler = async (e) => {
const { id, content } = e.detail;
// "content" here is the entire message from the assistant
finishedMessages[id] = true;
chatStreaming = false;
};
eventTarget.addEventListener('chat:start', chatStartHandler);
eventTarget.addEventListener('chat', chatEventHandler);
eventTarget.addEventListener('chat:finish', chatFinishHandler);
return async () => {
eventTarget.removeEventListener('chat:start', chatStartHandler);
eventTarget.removeEventListener('chat', chatEventHandler);
eventTarget.removeEventListener('chat:finish', chatFinishHandler);
audioAbortController.abort();
await tick();
await stopAllAudio();
await stopRecordingCallback(false);
await stopCamera();
};
});
</script>
{#if $showCallOverlay}
<audio id="audioElement" src="" style="display: none;" />
<div class=" absolute w-full h-screen max-h-[100dvh] flex z-[999] overflow-hidden">
<div
class="absolute w-full h-screen max-h-[100dvh] bg-white text-gray-700 dark:bg-black dark:text-gray-300 flex justify-center"
>
<div class="max-w-lg w-full h-screen max-h-[100dvh] flex flex-col justify-between p-3 md:p-6">
{#if camera}
<div class="flex justify-center items-center w-full min-h-20">
{#if loading}
<button
type="button"
class="flex justify-center items-center w-full h-20 min-h-20"
on:click={() => {
if (assistantSpeaking) {
stopAllAudio();
}
}}
>
{#if emoji}
<div
class=" transition-all rounded-full"
style="font-size:{rmsLevel * 100 > 4
? '4.5'
: rmsLevel * 100 > 2
? '4.25'
: rmsLevel * 100 > 1
? '3.75'
: '3.5'}rem;width: 100%; text-align:center;"
>
{emoji}
</div>
{:else if loading || assistantSpeaking}
<svg
class="size-12 text-gray-900 dark:text-gray-400"
viewBox="0 0 24 24"
@ -504,59 +661,81 @@
/>
{/if}
<!-- navbar -->
</div>
</button>
{/if}
<div class="flex justify-center items-center flex-1 h-full w-full max-h-full">
{#if !camera}
{#if loading}
<svg
class="size-44 text-gray-900 dark:text-gray-400"
viewBox="0 0 24 24"
fill="currentColor"
xmlns="http://www.w3.org/2000/svg"
><style>
.spinner_qM83 {
animation: spinner_8HQG 1.05s infinite;
}
.spinner_oXPr {
animation-delay: 0.1s;
}
.spinner_ZTLf {
animation-delay: 0.2s;
}
@keyframes spinner_8HQG {
0%,
57.14% {
animation-timing-function: cubic-bezier(0.33, 0.66, 0.66, 1);
transform: translate(0);
<button
type="button"
on:click={() => {
if (assistantSpeaking) {
stopAllAudio();
}
}}
>
{#if emoji}
<div
class=" transition-all rounded-full"
style="font-size:{rmsLevel * 100 > 4
? '13'
: rmsLevel * 100 > 2
? '12'
: rmsLevel * 100 > 1
? '11.5'
: '11'}rem;width:100%;text-align:center;"
>
{emoji}
</div>
{:else if loading || assistantSpeaking}
<svg
class="size-44 text-gray-900 dark:text-gray-400"
viewBox="0 0 24 24"
fill="currentColor"
xmlns="http://www.w3.org/2000/svg"
><style>
.spinner_qM83 {
animation: spinner_8HQG 1.05s infinite;
}
28.57% {
animation-timing-function: cubic-bezier(0.33, 0, 0.66, 0.33);
transform: translateY(-6px);
.spinner_oXPr {
animation-delay: 0.1s;
}
100% {
transform: translate(0);
.spinner_ZTLf {
animation-delay: 0.2s;
}
}
</style><circle class="spinner_qM83" cx="4" cy="12" r="3" /><circle
class="spinner_qM83 spinner_oXPr"
cx="12"
cy="12"
r="3"
/><circle class="spinner_qM83 spinner_ZTLf" cx="20" cy="12" r="3" /></svg
>
{:else}
<div
class=" {rmsLevel * 100 > 4
? ' size-52'
: rmsLevel * 100 > 2
? 'size-48'
: rmsLevel * 100 > 1
? 'size-[11.5rem]'
: 'size-44'} transition-all bg-black dark:bg-white rounded-full"
/>
{/if}
@keyframes spinner_8HQG {
0%,
57.14% {
animation-timing-function: cubic-bezier(0.33, 0.66, 0.66, 1);
transform: translate(0);
}
28.57% {
animation-timing-function: cubic-bezier(0.33, 0, 0.66, 0.33);
transform: translateY(-6px);
}
100% {
transform: translate(0);
}
}
</style><circle class="spinner_qM83" cx="4" cy="12" r="3" /><circle
class="spinner_qM83 spinner_oXPr"
cx="12"
cy="12"
r="3"
/><circle class="spinner_qM83 spinner_ZTLf" cx="20" cy="12" r="3" /></svg
>
{:else}
<div
class=" {rmsLevel * 100 > 4
? ' size-52'
: rmsLevel * 100 > 2
? 'size-48'
: rmsLevel * 100 > 1
? 'size-[11.5rem]'
: 'size-44'} transition-all bg-black dark:bg-white rounded-full"
/>
{/if}
</button>
{:else}
<div
class="relative flex video-container w-full max-h-full pt-2 pb-4 md:py-6 px-2 h-full"
@ -656,10 +835,19 @@
</div>
<div>
<button type="button">
<button
type="button"
on:click={() => {
if (assistantSpeaking) {
stopAllAudio();
}
}}
>
<div class=" line-clamp-1 text-sm font-medium">
{#if loading}
{$i18n.t('Thinking...')}
{:else if assistantSpeaking}
{$i18n.t('Tap to interrupt')}
{:else}
{$i18n.t('Listening...')}
{/if}

View File

@ -102,17 +102,19 @@
{#if filteredItems.length > 0 || prompt.split(' ')?.at(0)?.substring(1).startsWith('http')}
<div class="pl-1 pr-12 mb-3 text-left w-full absolute bottom-0 left-0 right-0">
<div class="flex w-full px-2">
<div class=" bg-gray-100 dark:bg-gray-700 w-10 rounded-l-xl text-center">
<div class="flex w-full dark:border dark:border-gray-850 rounded-lg">
<div class=" bg-gray-50 dark:bg-gray-850 w-10 rounded-l-lg text-center">
<div class=" text-lg font-semibold mt-2">#</div>
</div>
<div class="max-h-60 flex flex-col w-full rounded-r-xl bg-white">
<div class="m-1 overflow-y-auto p-1 rounded-r-xl space-y-0.5">
<div
class="max-h-60 flex flex-col w-full rounded-r-xl bg-white dark:bg-gray-900 dark:text-gray-100"
>
<div class="m-1 overflow-y-auto p-1 rounded-r-xl space-y-0.5 scrollbar-hidden">
{#each filteredItems as doc, docIdx}
<button
class=" px-3 py-1.5 rounded-xl w-full text-left {docIdx === selectedIdx
? ' bg-gray-100 selected-command-option-button'
? ' bg-gray-50 dark:bg-gray-850 dark:text-gray-100 selected-command-option-button'
: ''}"
type="button"
on:click={() => {
@ -126,17 +128,19 @@
on:focus={() => {}}
>
{#if doc.type === 'collection'}
<div class=" font-medium text-black line-clamp-1">
<div class=" font-medium text-black dark:text-gray-100 line-clamp-1">
{doc?.title ?? `#${doc.name}`}
</div>
<div class=" text-xs text-gray-600 line-clamp-1">{$i18n.t('Collection')}</div>
<div class=" text-xs text-gray-600 dark:text-gray-100 line-clamp-1">
{$i18n.t('Collection')}
</div>
{:else}
<div class=" font-medium text-black line-clamp-1">
<div class=" font-medium text-black dark:text-gray-100 line-clamp-1">
#{doc.name} ({doc.filename})
</div>
<div class=" text-xs text-gray-600 line-clamp-1">
<div class=" text-xs text-gray-600 dark:text-gray-100 line-clamp-1">
{doc.title}
</div>
{/if}

View File

@ -134,17 +134,19 @@
{#if prompt.charAt(0) === '@'}
{#if filteredModels.length > 0}
<div class="pl-1 pr-12 mb-3 text-left w-full absolute bottom-0 left-0 right-0">
<div class="flex w-full px-2">
<div class=" bg-gray-100 dark:bg-gray-700 w-10 rounded-l-xl text-center">
<div class="flex w-full dark:border dark:border-gray-850 rounded-lg">
<div class=" bg-gray-50 dark:bg-gray-850 w-10 rounded-l-lg text-center">
<div class=" text-lg font-semibold mt-2">@</div>
</div>
<div class="max-h-60 flex flex-col w-full rounded-r-xl bg-white">
<div class="m-1 overflow-y-auto p-1 rounded-r-xl space-y-0.5">
<div
class="max-h-60 flex flex-col w-full rounded-r-lg bg-white dark:bg-gray-900 dark:text-gray-100"
>
<div class="m-1 overflow-y-auto p-1 rounded-r-lg space-y-0.5 scrollbar-hidden">
{#each filteredModels as model, modelIdx}
<button
class=" px-3 py-1.5 rounded-xl w-full text-left {modelIdx === selectedIdx
? ' bg-gray-100 selected-command-option-button'
? ' bg-gray-50 dark:bg-gray-850 selected-command-option-button'
: ''}"
type="button"
on:click={() => {
@ -155,7 +157,7 @@
}}
on:focus={() => {}}
>
<div class=" font-medium text-black line-clamp-1">
<div class=" font-medium text-black dark:text-gray-100 line-clamp-1">
{model.name}
</div>

View File

@ -89,17 +89,19 @@
{#if filteredPromptCommands.length > 0}
<div class="pl-1 pr-12 mb-3 text-left w-full absolute bottom-0 left-0 right-0">
<div class="flex w-full px-2">
<div class=" bg-gray-100 dark:bg-gray-700 w-10 rounded-l-xl text-center">
<div class="flex w-full dark:border dark:border-gray-850 rounded-lg">
<div class=" bg-gray-50 dark:bg-gray-850 w-10 rounded-l-lg text-center">
<div class=" text-lg font-semibold mt-2">/</div>
</div>
<div class="max-h-60 flex flex-col w-full rounded-r-xl bg-white">
<div class="m-1 overflow-y-auto p-1 rounded-r-xl space-y-0.5">
<div
class="max-h-60 flex flex-col w-full rounded-r-lg bg-white dark:bg-gray-900 dark:text-gray-100"
>
<div class="m-1 overflow-y-auto p-1 rounded-r-lg space-y-0.5 scrollbar-hidden">
{#each filteredPromptCommands as command, commandIdx}
<button
class=" px-3 py-1.5 rounded-xl w-full text-left {commandIdx === selectedCommandIdx
? ' bg-gray-100 selected-command-option-button'
? ' bg-gray-50 dark:bg-gray-850 selected-command-option-button'
: ''}"
type="button"
on:click={() => {
@ -110,11 +112,11 @@
}}
on:focus={() => {}}
>
<div class=" font-medium text-black">
<div class=" font-medium text-black dark:text-gray-100">
{command.command}
</div>
<div class=" text-xs text-gray-600">
<div class=" text-xs text-gray-600 dark:text-gray-100">
{command.title}
</div>
</button>
@ -122,7 +124,7 @@
</div>
<div
class=" px-2 pb-1 text-xs text-gray-600 bg-white rounded-br-xl flex items-center space-x-1"
class=" px-2 pb-1 text-xs text-gray-600 dark:text-gray-100 bg-white dark:bg-gray-900 rounded-br-xl flex items-center space-x-1"
>
<div>
<svg

View File

@ -79,7 +79,7 @@
history.currentId = userMessageId;
await tick();
await sendPrompt(userPrompt, userMessageId, undefined, false);
await sendPrompt(userPrompt, userMessageId);
};
const updateChatMessages = async () => {
@ -202,38 +202,51 @@
}, 100);
};
const messageDeleteHandler = async (messageId) => {
const deleteMessageHandler = async (messageId) => {
const messageToDelete = history.messages[messageId];
const messageParentId = messageToDelete.parentId;
const messageChildrenIds = messageToDelete.childrenIds ?? [];
const hasSibling = messageChildrenIds.some(
const parentMessageId = messageToDelete.parentId;
const childMessageIds = messageToDelete.childrenIds ?? [];
const hasDescendantMessages = childMessageIds.some(
(childId) => history.messages[childId]?.childrenIds?.length > 0
);
messageChildrenIds.forEach((childId) => {
const child = history.messages[childId];
if (child && child.childrenIds) {
if (child.childrenIds.length === 0 && !hasSibling) {
// if last prompt/response pair
history.messages[messageParentId].childrenIds = [];
history.currentId = messageParentId;
history.currentId = parentMessageId;
await tick();
// Remove the message itself from the parent message's children array
history.messages[parentMessageId].childrenIds = history.messages[
parentMessageId
].childrenIds.filter((id) => id !== messageId);
await tick();
childMessageIds.forEach((childId) => {
const childMessage = history.messages[childId];
if (childMessage && childMessage.childrenIds) {
if (childMessage.childrenIds.length === 0 && !hasDescendantMessages) {
// If there are no other responses/prompts
history.messages[parentMessageId].childrenIds = [];
} else {
child.childrenIds.forEach((grandChildId) => {
childMessage.childrenIds.forEach((grandChildId) => {
if (history.messages[grandChildId]) {
history.messages[grandChildId].parentId = messageParentId;
history.messages[messageParentId].childrenIds.push(grandChildId);
history.messages[grandChildId].parentId = parentMessageId;
history.messages[parentMessageId].childrenIds.push(grandChildId);
}
});
}
}
// remove response
history.messages[messageParentId].childrenIds = history.messages[
messageParentId
// Remove child message id from the parent message's children array
history.messages[parentMessageId].childrenIds = history.messages[
parentMessageId
].childrenIds.filter((id) => id !== childId);
});
// remove prompt
history.messages[messageParentId].childrenIds = history.messages[
messageParentId
].childrenIds.filter((id) => id !== messageId);
await tick();
await updateChatById(localStorage.token, chatId, {
messages: messages,
history: history
@ -292,7 +305,7 @@
>
{#if message.role === 'user'}
<UserMessage
on:delete={() => messageDeleteHandler(message.id)}
on:delete={() => deleteMessageHandler(message.id)}
{user}
{readOnly}
{message}
@ -308,7 +321,7 @@
copyToClipboard={copyToClipboardWithToast}
/>
{:else if $mobile || (history.messages[message.parentId]?.models?.length ?? 1) === 1}
{#key message.id}
{#key message.id && history.currentId}
<ResponseMessage
{message}
siblings={history.messages[message.parentId]?.childrenIds ?? []}

View File

@ -1,11 +1,14 @@
<script lang="ts">
import { WEBUI_BASE_URL } from '$lib/constants';
import { marked } from 'marked';
import { config, user, models as _models } from '$lib/stores';
import { onMount, getContext } from 'svelte';
import { blur, fade } from 'svelte/transition';
import Suggestions from '../MessageInput/Suggestions.svelte';
import { sanitizeResponseContent } from '$lib/utils';
const i18n = getContext('i18n');
@ -65,8 +68,12 @@
<div in:fade={{ duration: 200, delay: 200 }}>
{#if models[selectedModelIdx]?.info?.meta?.description ?? null}
<div class="mt-0.5 text-base font-normal text-gray-500 dark:text-gray-400 line-clamp-3">
{models[selectedModelIdx]?.info?.meta?.description}
<div
class="mt-0.5 text-base font-normal text-gray-500 dark:text-gray-400 line-clamp-3 markdown"
>
{@html marked.parse(
sanitizeResponseContent(models[selectedModelIdx]?.info?.meta?.description)
)}
</div>
{#if models[selectedModelIdx]?.info?.meta?.user}
<div class="mt-0.5 text-sm font-normal text-gray-400 dark:text-gray-500">

View File

@ -940,68 +940,68 @@
>
</button>
</Tooltip>
{/if}
{#if isLastMessage && !readOnly}
<Tooltip content={$i18n.t('Continue Response')} placement="bottom">
<button
type="button"
class="{isLastMessage
? 'visible'
: 'invisible group-hover:visible'} p-1.5 hover:bg-black/5 dark:hover:bg-white/5 rounded-lg dark:hover:text-white hover:text-black transition regenerate-response-button"
on:click={() => {
continueGeneration();
}}
>
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="2.3"
stroke="currentColor"
class="w-4 h-4"
{#if isLastMessage}
<Tooltip content={$i18n.t('Continue Response')} placement="bottom">
<button
type="button"
class="{isLastMessage
? 'visible'
: 'invisible group-hover:visible'} p-1.5 hover:bg-black/5 dark:hover:bg-white/5 rounded-lg dark:hover:text-white hover:text-black transition regenerate-response-button"
on:click={() => {
continueGeneration();
}}
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z"
/>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M15.91 11.672a.375.375 0 0 1 0 .656l-5.603 3.113a.375.375 0 0 1-.557-.328V8.887c0-.286.307-.466.557-.327l5.603 3.112Z"
/>
</svg>
</button>
</Tooltip>
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="2.3"
stroke="currentColor"
class="w-4 h-4"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z"
/>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M15.91 11.672a.375.375 0 0 1 0 .656l-5.603 3.113a.375.375 0 0 1-.557-.328V8.887c0-.286.307-.466.557-.327l5.603 3.112Z"
/>
</svg>
</button>
</Tooltip>
<Tooltip content={$i18n.t('Regenerate')} placement="bottom">
<button
type="button"
class="{isLastMessage
? 'visible'
: 'invisible group-hover:visible'} p-1.5 hover:bg-black/5 dark:hover:bg-white/5 rounded-lg dark:hover:text-white hover:text-black transition regenerate-response-button"
on:click={() => {
showRateComment = false;
regenerateResponse(message);
}}
>
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="2.3"
stroke="currentColor"
class="w-4 h-4"
<Tooltip content={$i18n.t('Regenerate')} placement="bottom">
<button
type="button"
class="{isLastMessage
? 'visible'
: 'invisible group-hover:visible'} p-1.5 hover:bg-black/5 dark:hover:bg-white/5 rounded-lg dark:hover:text-white hover:text-black transition regenerate-response-button"
on:click={() => {
showRateComment = false;
regenerateResponse(message);
}}
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M16.023 9.348h4.992v-.001M2.985 19.644v-4.992m0 0h4.992m-4.993 0l3.181 3.183a8.25 8.25 0 0013.803-3.7M4.031 9.865a8.25 8.25 0 0113.803-3.7l3.181 3.182m0-4.991v4.99"
/>
</svg>
</button>
</Tooltip>
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="2.3"
stroke="currentColor"
class="w-4 h-4"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M16.023 9.348h4.992v-.001M2.985 19.644v-4.992m0 0h4.992m-4.993 0l3.181 3.183a8.25 8.25 0 0013.803-3.7M4.031 9.865a8.25 8.25 0 0113.803-3.7l3.181 3.182m0-4.991v4.99"
/>
</svg>
</button>
</Tooltip>
{/if}
{/if}
{/if}
</div>

View File

@ -1,5 +1,6 @@
<script lang="ts">
import { DropdownMenu } from 'bits-ui';
import { marked } from 'marked';
import { flyAndScale } from '$lib/utils/transitions';
import { createEventDispatcher, onMount, getContext, tick } from 'svelte';
@ -333,9 +334,12 @@
{#if item.model?.info?.meta?.description}
<Tooltip
content={`${sanitizeResponseContent(
item.model?.info?.meta?.description
).replaceAll('\n', '<br>')}`}
content={`${marked.parse(
sanitizeResponseContent(item.model?.info?.meta?.description).replaceAll(
'\n',
'<br>'
)
)}`}
>
<div class="">
<svg

View File

@ -21,6 +21,8 @@
top_p: null,
tfs_z: null,
num_ctx: null,
num_batch: null,
num_keep: null,
max_tokens: null,
use_mmap: null,
use_mlock: null,
@ -565,6 +567,98 @@
{/if}
</div>
<div class=" py-0.5 w-full justify-between">
<div class="flex w-full justify-between">
<div class=" self-center text-xs font-medium">{$i18n.t('Batch Size (num_batch)')}</div>
<button
class="p-1 px-3 text-xs flex rounded transition"
type="button"
on:click={() => {
params.num_batch = (params?.num_batch ?? null) === null ? 512 : null;
}}
>
{#if (params?.num_batch ?? null) === null}
<span class="ml-2 self-center">{$i18n.t('Default')}</span>
{:else}
<span class="ml-2 self-center">{$i18n.t('Custom')}</span>
{/if}
</button>
</div>
{#if (params?.num_batch ?? null) !== null}
<div class="flex mt-0.5 space-x-2">
<div class=" flex-1">
<input
id="steps-range"
type="range"
min="256"
max="8192"
step="256"
bind:value={params.num_batch}
class="w-full h-2 rounded-lg appearance-none cursor-pointer dark:bg-gray-700"
/>
</div>
<div class="">
<input
bind:value={params.num_batch}
type="number"
class=" bg-transparent text-center w-14"
min="256"
step="256"
/>
</div>
</div>
{/if}
</div>
<div class=" py-0.5 w-full justify-between">
<div class="flex w-full justify-between">
<div class=" self-center text-xs font-medium">
{$i18n.t('Tokens To Keep On Context Refresh (num_keep)')}
</div>
<button
class="p-1 px-3 text-xs flex rounded transition"
type="button"
on:click={() => {
params.num_keep = (params?.num_keep ?? null) === null ? 24 : null;
}}
>
{#if (params?.num_keep ?? null) === null}
<span class="ml-2 self-center">{$i18n.t('Default')}</span>
{:else}
<span class="ml-2 self-center">{$i18n.t('Custom')}</span>
{/if}
</button>
</div>
{#if (params?.num_keep ?? null) !== null}
<div class="flex mt-0.5 space-x-2">
<div class=" flex-1">
<input
id="steps-range"
type="range"
min="-1"
max="10240000"
step="1"
bind:value={params.num_keep}
class="w-full h-2 rounded-lg appearance-none cursor-pointer dark:bg-gray-700"
/>
</div>
<div class="">
<input
bind:value={params.num_keep}
type="number"
class=" bg-transparent text-center w-14"
min="-1"
step="1"
/>
</div>
</div>
{/if}
</div>
<div class=" py-0.5 w-full justify-between">
<div class="flex w-full justify-between">
<div class=" self-center text-xs font-medium">{$i18n.t('Max Tokens (num_predict)')}</div>

View File

@ -55,6 +55,8 @@
stop: null,
tfs_z: null,
num_ctx: null,
num_batch: null,
num_keep: null,
max_tokens: null
};
@ -308,6 +310,8 @@
top_p: params.top_p !== null ? params.top_p : undefined,
tfs_z: params.tfs_z !== null ? params.tfs_z : undefined,
num_ctx: params.num_ctx !== null ? params.num_ctx : undefined,
num_batch: params.num_batch !== null ? params.num_batch : undefined,
num_keep: params.num_keep !== null ? params.num_keep : undefined,
max_tokens: params.max_tokens !== null ? params.max_tokens : undefined,
use_mmap: params.use_mmap !== null ? params.use_mmap : undefined,
use_mlock: params.use_mlock !== null ? params.use_mlock : undefined,

View File

@ -5,6 +5,8 @@
import { createEventDispatcher, onMount, getContext } from 'svelte';
import { toast } from 'svelte-sonner';
import Tooltip from '$lib/components/common/Tooltip.svelte';
import { updateUserInfo } from '$lib/apis/users';
import { getUserPosition } from '$lib/utils';
const dispatch = createEventDispatcher();
const i18n = getContext('i18n');
@ -16,13 +18,17 @@
let responseAutoCopy = false;
let widescreenMode = false;
let splitLargeChunks = false;
let userLocation = false;
// Interface
let defaultModelId = '';
let showUsername = false;
let chatBubble = true;
let chatDirection: 'LTR' | 'RTL' = 'LTR';
let showEmojiInCall = false;
const toggleSplitLargeChunks = async () => {
splitLargeChunks = !splitLargeChunks;
saveSettings({ splitLargeChunks: splitLargeChunks });
@ -43,6 +49,31 @@
saveSettings({ showUsername: showUsername });
};
const toggleEmojiInCall = async () => {
showEmojiInCall = !showEmojiInCall;
saveSettings({ showEmojiInCall: showEmojiInCall });
};
const toggleUserLocation = async () => {
userLocation = !userLocation;
if (userLocation) {
const position = await getUserPosition().catch((error) => {
toast.error(error.message);
return null;
});
if (position) {
await updateUserInfo(localStorage.token, { location: position });
toast.success('User location successfully retrieved.');
} else {
userLocation = false;
}
}
saveSettings({ userLocation });
};
const toggleTitleAutoGenerate = async () => {
titleAutoGenerate = !titleAutoGenerate;
saveSettings({
@ -88,12 +119,17 @@
onMount(async () => {
titleAutoGenerate = $settings?.title?.auto ?? true;
responseAutoCopy = $settings.responseAutoCopy ?? false;
showUsername = $settings.showUsername ?? false;
showEmojiInCall = $settings.showEmojiInCall ?? false;
chatBubble = $settings.chatBubble ?? true;
widescreenMode = $settings.widescreenMode ?? false;
splitLargeChunks = $settings.splitLargeChunks ?? false;
chatDirection = $settings.chatDirection ?? 'LTR';
userLocation = $settings.userLocation ?? false;
defaultModelId = ($settings?.models ?? ['']).at(0);
});
@ -130,6 +166,26 @@
</div>
</div>
<div>
<div class=" py-0.5 flex w-full justify-between">
<div class=" self-center text-xs font-medium">{$i18n.t('Widescreen Mode')}</div>
<button
class="p-1 px-3 text-xs flex rounded transition"
on:click={() => {
togglewidescreenMode();
}}
type="button"
>
{#if widescreenMode === true}
<span class="ml-2 self-center">{$i18n.t('On')}</span>
{:else}
<span class="ml-2 self-center">{$i18n.t('Off')}</span>
{/if}
</button>
</div>
</div>
<div>
<div class=" py-0.5 flex w-full justify-between">
<div class=" self-center text-xs font-medium">{$i18n.t('Title Auto-Generation')}</div>
@ -174,16 +230,36 @@
<div>
<div class=" py-0.5 flex w-full justify-between">
<div class=" self-center text-xs font-medium">{$i18n.t('Widescreen Mode')}</div>
<div class=" self-center text-xs font-medium">{$i18n.t('Allow User Location')}</div>
<button
class="p-1 px-3 text-xs flex rounded transition"
on:click={() => {
togglewidescreenMode();
toggleUserLocation();
}}
type="button"
>
{#if widescreenMode === true}
{#if userLocation === true}
<span class="ml-2 self-center">{$i18n.t('On')}</span>
{:else}
<span class="ml-2 self-center">{$i18n.t('Off')}</span>
{/if}
</button>
</div>
</div>
<div>
<div class=" py-0.5 flex w-full justify-between">
<div class=" self-center text-xs font-medium">{$i18n.t('Display Emoji in Call')}</div>
<button
class="p-1 px-3 text-xs flex rounded transition"
on:click={() => {
toggleEmojiInCall();
}}
type="button"
>
{#if showEmojiInCall === true}
<span class="ml-2 self-center">{$i18n.t('On')}</span>
{:else}
<span class="ml-2 self-center">{$i18n.t('Off')}</span>

View File

@ -2,13 +2,12 @@
import { createEventDispatcher, getContext } from 'svelte';
import Modal from '$lib/components/common/Modal.svelte';
import { addNewMemory } from '$lib/apis/memories';
import { addNewMemory, updateMemoryById } from '$lib/apis/memories';
import { toast } from 'svelte-sonner';
const dispatch = createEventDispatcher();
export let show;
const i18n = getContext('i18n');
let loading = false;
@ -38,7 +37,9 @@
<Modal bind:show size="sm">
<div>
<div class=" flex justify-between dark:text-gray-300 px-5 pt-4 pb-2">
<div class=" text-lg font-medium self-center">{$i18n.t('Add Memory')}</div>
<div class=" text-lg font-medium self-center">
{$i18n.t('Add Memory')}
</div>
<button
class="self-center"
on:click={() => {

View File

@ -0,0 +1,136 @@
<script>
import { createEventDispatcher, getContext } from 'svelte';
import { toast } from 'svelte-sonner';
import { updateMemoryById } from '$lib/apis/memories';
import Modal from '$lib/components/common/Modal.svelte';
const dispatch = createEventDispatcher();
export let show;
export let memory = {};
const i18n = getContext('i18n');
let loading = false;
let content = '';
$: if (show) {
setContent();
}
const setContent = () => {
content = memory.content;
};
const submitHandler = async () => {
loading = true;
const res = await updateMemoryById(localStorage.token, memory.id, content).catch((error) => {
toast.error(error);
return null;
});
if (res) {
console.log(res);
toast.success('Memory updated successfully');
dispatch('save');
show = false;
}
loading = false;
};
</script>
<Modal bind:show size="sm">
<div>
<div class=" flex justify-between dark:text-gray-300 px-5 pt-4 pb-2">
<div class=" text-lg font-medium self-center">
{$i18n.t('Edit Memory')}
</div>
<button
class="self-center"
on:click={() => {
show = false;
}}
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
fill="currentColor"
class="w-5 h-5"
>
<path
d="M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z"
/>
</svg>
</button>
</div>
<div class="flex flex-col md:flex-row w-full px-5 pb-4 md:space-x-4 dark:text-gray-200">
<div class=" flex flex-col w-full sm:flex-row sm:justify-center sm:space-x-6">
<form
class="flex flex-col w-full"
on:submit|preventDefault={() => {
submitHandler();
}}
>
<div class="">
<textarea
bind:value={content}
class=" bg-transparent w-full text-sm resize-none rounded-xl p-3 outline outline-1 outline-gray-100 dark:outline-gray-800"
rows="3"
placeholder={$i18n.t('Enter a detail about yourself for your LLMs to recall')}
/>
<div class="text-xs text-gray-500">
{$i18n.t('Refer to yourself as "User" (e.g., "User is learning Spanish")')}
</div>
</div>
<div class="flex justify-end pt-1 text-sm font-medium">
<button
class=" px-4 py-2 bg-emerald-700 hover:bg-emerald-800 text-gray-100 transition rounded-3xl flex flex-row space-x-1 items-center {loading
? ' cursor-not-allowed'
: ''}"
type="submit"
disabled={loading}
>
{$i18n.t('Update')}
{#if loading}
<div class="ml-2 self-center">
<svg
class=" w-4 h-4"
viewBox="0 0 24 24"
fill="currentColor"
xmlns="http://www.w3.org/2000/svg"
><style>
.spinner_ajPY {
transform-origin: center;
animation: spinner_AtaB 0.75s infinite linear;
}
@keyframes spinner_AtaB {
100% {
transform: rotate(360deg);
}
}
</style><path
d="M12,1A11,11,0,1,0,23,12,11,11,0,0,0,12,1Zm0,19a8,8,0,1,1,8-8A8,8,0,0,1,12,20Z"
opacity=".25"
/><path
d="M10.14,1.16a11,11,0,0,0-9,8.92A1.59,1.59,0,0,0,2.46,12,1.52,1.52,0,0,0,4.11,10.7a8,8,0,0,1,6.66-6.61A1.42,1.42,0,0,0,12,2.69h0A1.57,1.57,0,0,0,10.14,1.16Z"
class="spinner_ajPY"
/></svg
>
</div>
{/if}
</button>
</div>
</form>
</div>
</div>
</div>
</Modal>

View File

@ -10,18 +10,24 @@
import { deleteMemoriesByUserId, deleteMemoryById, getMemories } from '$lib/apis/memories';
import Tooltip from '$lib/components/common/Tooltip.svelte';
import { error } from '@sveltejs/kit';
import EditMemoryModal from './EditMemoryModal.svelte';
const i18n = getContext('i18n');
export let show = false;
let memories = [];
let loading = true;
let showAddMemoryModal = false;
let showEditMemoryModal = false;
$: if (show) {
let selectedMemory = null;
$: if (show && memories.length === 0 && loading) {
(async () => {
memories = await getMemories(localStorage.token);
loading = false;
})();
}
</script>
@ -62,7 +68,9 @@
>
<tr>
<th scope="col" class="px-3 py-2"> {$i18n.t('Name')} </th>
<th scope="col" class="px-3 py-2 hidden md:flex"> {$i18n.t('Created At')} </th>
<th scope="col" class="px-3 py-2 hidden md:flex">
{$i18n.t('Last Modified')}
</th>
<th scope="col" class="px-3 py-2 text-right" />
</tr>
</thead>
@ -76,11 +84,38 @@
</td>
<td class=" px-3 py-1 hidden md:flex h-[2.5rem]">
<div class="my-auto whitespace-nowrap">
{dayjs(memory.created_at * 1000).format($i18n.t('MMMM DD, YYYY'))}
{dayjs(memory.updated_at * 1000).format(
$i18n.t('MMMM DD, YYYY hh:mm:ss A')
)}
</div>
</td>
<td class="px-3 py-1">
<div class="flex justify-end w-full">
<Tooltip content="Edit">
<button
class="self-center w-fit text-sm px-2 py-2 hover:bg-black/5 dark:hover:bg-white/5 rounded-xl"
on:click={() => {
selectedMemory = memory;
showEditMemoryModal = true;
}}
>
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
class="w-4 h-4 s-FoVA_WMOgxUD"
><path
stroke-linecap="round"
stroke-linejoin="round"
d="m16.862 4.487 1.687-1.688a1.875 1.875 0 1 1 2.652 2.652L6.832 19.82a4.5 4.5 0 0 1-1.897 1.13l-2.685.8.8-2.685a4.5 4.5 0 0 1 1.13-1.897L16.863 4.487Zm0 0L19.5 7.125"
class="s-FoVA_WMOgxUD"
/></svg
>
</button>
</Tooltip>
<Tooltip content="Delete">
<button
class="self-center w-fit text-sm px-2 py-2 hover:bg-black/5 dark:hover:bg-white/5 rounded-xl"
@ -163,3 +198,11 @@
memories = await getMemories(localStorage.token);
}}
/>
<EditMemoryModal
bind:show={showEditMemoryModal}
memory={selectedMemory}
on:save={async () => {
memories = await getMemories(localStorage.token);
}}
/>

View File

@ -9,6 +9,9 @@
export let title = 'Confirm your action';
export let message = 'This action cannot be undone. Do you wish to continue?';
export let cancelLabel = 'Cancel';
export let confirmLabel = 'Confirm';
export let show = false;
let modalElement = null;
let mounted = false;
@ -70,7 +73,7 @@
}}
type="button"
>
Cancel
{cancelLabel}
</button>
<button
class="bg-gray-900 hover:bg-gray-850 text-gray-100 dark:bg-gray-100 dark:hover:bg-white dark:text-gray-800 font-medium w-full py-2.5 rounded-lg transition"
@ -80,7 +83,7 @@
}}
type="button"
>
Confirm
{confirmLabel}
</button>
</div>
</div>

View File

@ -1,5 +1,7 @@
<script lang="ts">
import { onDestroy } from 'svelte';
import { marked } from 'marked';
import tippy from 'tippy.js';
export let placement = 'top';

View File

@ -1,4 +1,5 @@
<script lang="ts">
import { toast } from 'svelte-sonner';
import { goto } from '$app/navigation';
import {
user,
@ -11,10 +12,11 @@
mobile,
showArchivedChats
} from '$lib/stores';
import { onMount, getContext } from 'svelte';
import { onMount, getContext, tick } from 'svelte';
const i18n = getContext('i18n');
import { updateUserSettings } from '$lib/apis/users';
import {
deleteChatById,
getChatList,
@ -25,37 +27,25 @@
archiveChatById,
cloneChatById
} from '$lib/apis/chats';
import { toast } from 'svelte-sonner';
import { fade, slide } from 'svelte/transition';
import { WEBUI_BASE_URL } from '$lib/constants';
import Tooltip from '../common/Tooltip.svelte';
import ChatMenu from './Sidebar/ChatMenu.svelte';
import ShareChatModal from '../chat/ShareChatModal.svelte';
import ArchiveBox from '../icons/ArchiveBox.svelte';
import ArchivedChatsModal from './Sidebar/ArchivedChatsModal.svelte';
import UserMenu from './Sidebar/UserMenu.svelte';
import { updateUserSettings } from '$lib/apis/users';
import ChatItem from './Sidebar/ChatItem.svelte';
import DeleteConfirmDialog from '$lib/components/common/ConfirmDialog.svelte';
const BREAKPOINT = 768;
let show = false;
let navElement;
let title: string = 'UI';
let search = '';
let shareChatId = null;
let shiftKey = false;
let selectedChatId = null;
let deleteChat = null;
let chatDeleteId = null;
let chatTitleEditId = null;
let chatTitle = '';
let showShareChatModal = false;
let showDeleteConfirm = false;
let showDropdown = false;
let isEditing = false;
let filteredChatList = [];
$: filteredChatList = $chats.filter((chat) => {
@ -78,13 +68,6 @@
}
});
mobile;
const onResize = () => {
if ($showSidebar && window.innerWidth < BREAKPOINT) {
showSidebar.set(false);
}
};
onMount(async () => {
mobile.subscribe((e) => {
if ($showSidebar && e) {
@ -125,12 +108,43 @@
checkDirection();
};
const onKeyDown = (e) => {
if (e.key === 'Shift') {
shiftKey = true;
}
};
const onKeyUp = (e) => {
if (e.key === 'Shift') {
shiftKey = false;
}
};
const onFocus = () => {};
const onBlur = () => {
shiftKey = false;
selectedChatId = null;
};
window.addEventListener('keydown', onKeyDown);
window.addEventListener('keyup', onKeyUp);
window.addEventListener('touchstart', onTouchStart);
window.addEventListener('touchend', onTouchEnd);
window.addEventListener('focus', onFocus);
window.addEventListener('blur', onBlur);
return () => {
window.removeEventListener('keydown', onKeyDown);
window.removeEventListener('keyup', onKeyUp);
window.removeEventListener('touchstart', onTouchStart);
window.removeEventListener('touchend', onTouchEnd);
window.removeEventListener('focus', onFocus);
window.removeEventListener('blur', onBlur);
};
});
@ -149,69 +163,29 @@
await chats.set(enrichedChats);
};
const loadChat = async (id) => {
goto(`/c/${id}`);
};
const editChatTitle = async (id, _title) => {
if (_title === '') {
toast.error($i18n.t('Title cannot be an empty string.'));
} else {
title = _title;
await updateChatById(localStorage.token, id, {
title: _title
});
await chats.set(await getChatList(localStorage.token));
}
};
const deleteChat = async (id) => {
const res = await deleteChatById(localStorage.token, id).catch((error) => {
toast.error(error);
chatDeleteId = null;
return null;
});
if (res) {
if ($chatId === id) {
goto('/');
}
await chats.set(await getChatList(localStorage.token));
}
};
const cloneChatHandler = async (id) => {
const res = await cloneChatById(localStorage.token, id).catch((error) => {
toast.error(error);
return null;
});
if (res) {
goto(`/c/${res.id}`);
await chats.set(await getChatList(localStorage.token));
}
};
const saveSettings = async (updated) => {
await settings.set({ ...$settings, ...updated });
await updateUserSettings(localStorage.token, { ui: $settings });
location.href = '/';
};
const archiveChatHandler = async (id) => {
await archiveChatById(localStorage.token, id);
await chats.set(await getChatList(localStorage.token));
};
const deleteChatHandler = async (id) => {
const res = await deleteChatById(localStorage.token, id).catch((error) => {
toast.error(error);
return null;
});
const focusEdit = async (node: HTMLInputElement) => {
node.focus();
if (res) {
if ($chatId === id) {
await chatId.set('');
await tick();
goto('/');
}
await chats.set(await getChatList(localStorage.token));
}
};
</script>
<ShareChatModal bind:show={showShareChatModal} chatId={shareChatId} />
<ArchivedChatsModal
bind:show={$showArchivedChats}
on:change={async () => {
@ -219,6 +193,18 @@
}}
/>
<DeleteConfirmDialog
bind:show={showDeleteConfirm}
title="Delete chat?"
on:confirm={() => {
deleteChatHandler(deleteChat.id);
}}
>
<div class=" text-sm text-gray-500">
This will delete <span class=" font-semibold">{deleteChat.title}</span>.
</div>
</DeleteConfirmDialog>
<!-- svelte-ignore a11y-no-static-element-interactions -->
{#if $showSidebar}
@ -252,12 +238,10 @@
draggable="false"
on:click={async () => {
selectedChatId = null;
await goto('/');
const newChatButton = document.getElementById('new-chat-button');
setTimeout(() => {
newChatButton?.click();
if ($mobile) {
showSidebar.set(false);
}
@ -486,215 +470,25 @@
</div>
{/if}
<div class=" w-full pr-2 relative group">
{#if chatTitleEditId === chat.id}
<div
class=" w-full flex justify-between rounded-xl px-3 py-2 {chat.id === $chatId ||
chat.id === chatTitleEditId ||
chat.id === chatDeleteId
? 'bg-gray-200 dark:bg-gray-900'
: chat.id === selectedChatId
? 'bg-gray-100 dark:bg-gray-950'
: 'group-hover:bg-gray-100 dark:group-hover:bg-gray-950'} whitespace-nowrap text-ellipsis"
>
<input
use:focusEdit
bind:value={chatTitle}
class=" bg-transparent w-full outline-none mr-10"
/>
</div>
{:else}
<a
class=" w-full flex justify-between rounded-xl px-3 py-2 {chat.id === $chatId ||
chat.id === chatTitleEditId ||
chat.id === chatDeleteId
? 'bg-gray-200 dark:bg-gray-900'
: chat.id === selectedChatId
? 'bg-gray-100 dark:bg-gray-950'
: ' group-hover:bg-gray-100 dark:group-hover:bg-gray-950'} whitespace-nowrap text-ellipsis"
href="/c/{chat.id}"
on:click={() => {
selectedChatId = chat.id;
if ($mobile) {
showSidebar.set(false);
}
}}
on:dblclick={() => {
chatTitle = chat.title;
chatTitleEditId = chat.id;
}}
draggable="false"
>
<div class=" flex self-center flex-1 w-full">
<div class=" text-left self-center overflow-hidden w-full h-[20px]">
{chat.title}
</div>
</div>
</a>
{/if}
<div
class="
{chat.id === $chatId || chat.id === chatTitleEditId || chat.id === chatDeleteId
? 'from-gray-200 dark:from-gray-900'
: chat.id === selectedChatId
? 'from-gray-100 dark:from-gray-950'
: 'invisible group-hover:visible from-gray-100 dark:from-gray-950'}
absolute right-[10px] top-[10px] pr-2 pl-5 bg-gradient-to-l from-80%
to-transparent"
>
{#if chatTitleEditId === chat.id}
<div class="flex self-center space-x-1.5 z-10">
<button
class=" self-center dark:hover:text-white transition"
on:click={() => {
editChatTitle(chat.id, chatTitle);
chatTitleEditId = null;
chatTitle = '';
}}
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
fill="currentColor"
class="w-4 h-4"
>
<path
fill-rule="evenodd"
d="M16.704 4.153a.75.75 0 01.143 1.052l-8 10.5a.75.75 0 01-1.127.075l-4.5-4.5a.75.75 0 011.06-1.06l3.894 3.893 7.48-9.817a.75.75 0 011.05-.143z"
clip-rule="evenodd"
/>
</svg>
</button>
<button
class=" self-center dark:hover:text-white transition"
on:click={() => {
chatTitleEditId = null;
chatTitle = '';
}}
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
fill="currentColor"
class="w-4 h-4"
>
<path
d="M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z"
/>
</svg>
</button>
</div>
{:else if chatDeleteId === chat.id}
<div class="flex self-center space-x-1.5 z-10">
<button
class=" self-center dark:hover:text-white transition"
on:click={() => {
deleteChat(chat.id);
}}
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
fill="currentColor"
class="w-4 h-4"
>
<path
fill-rule="evenodd"
d="M16.704 4.153a.75.75 0 01.143 1.052l-8 10.5a.75.75 0 01-1.127.075l-4.5-4.5a.75.75 0 011.06-1.06l3.894 3.893 7.48-9.817a.75.75 0 011.05-.143z"
clip-rule="evenodd"
/>
</svg>
</button>
<button
class=" self-center dark:hover:text-white transition"
on:click={() => {
chatDeleteId = null;
}}
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
fill="currentColor"
class="w-4 h-4"
>
<path
d="M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z"
/>
</svg>
</button>
</div>
{:else}
<div class="flex self-center space-x-1 z-10">
<ChatMenu
chatId={chat.id}
cloneChatHandler={() => {
cloneChatHandler(chat.id);
}}
shareHandler={() => {
shareChatId = selectedChatId;
showShareChatModal = true;
}}
archiveChatHandler={() => {
archiveChatHandler(chat.id);
}}
renameHandler={() => {
chatTitle = chat.title;
chatTitleEditId = chat.id;
}}
deleteHandler={() => {
chatDeleteId = chat.id;
}}
onClose={() => {
selectedChatId = null;
}}
>
<button
aria-label="Chat Menu"
class=" self-center dark:hover:text-white transition"
on:click={() => {
selectedChatId = chat.id;
}}
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 16 16"
fill="currentColor"
class="w-4 h-4"
>
<path
d="M2 8a1.5 1.5 0 1 1 3 0 1.5 1.5 0 0 1-3 0ZM6.5 8a1.5 1.5 0 1 1 3 0 1.5 1.5 0 0 1-3 0ZM12.5 6.5a1.5 1.5 0 1 0 0 3 1.5 1.5 0 0 0 0-3Z"
/>
</svg>
</button>
</ChatMenu>
{#if chat.id === $chatId}
<button
id="delete-chat-button"
class="hidden"
on:click={() => {
chatDeleteId = chat.id;
}}
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 16 16"
fill="currentColor"
class="w-4 h-4"
>
<path
d="M2 8a1.5 1.5 0 1 1 3 0 1.5 1.5 0 0 1-3 0ZM6.5 8a1.5 1.5 0 1 1 3 0 1.5 1.5 0 0 1-3 0ZM12.5 6.5a1.5 1.5 0 1 0 0 3 1.5 1.5 0 0 0 0-3Z"
/>
</svg>
</button>
{/if}
</div>
{/if}
</div>
</div>
<ChatItem
{chat}
{shiftKey}
selected={selectedChatId === chat.id}
on:select={() => {
selectedChatId = chat.id;
}}
on:unselect={() => {
selectedChatId = null;
}}
on:delete={(e) => {
if ((e?.detail ?? '') === 'shift') {
deleteChatHandler(chat.id);
} else {
deleteChat = chat;
showDeleteConfirm = true;
}
}}
/>
{/each}
</div>
</div>

View File

@ -0,0 +1,281 @@
<script lang="ts">
import { toast } from 'svelte-sonner';
import { goto, invalidate, invalidateAll } from '$app/navigation';
import { onMount, getContext, createEventDispatcher, tick } from 'svelte';
const i18n = getContext('i18n');
const dispatch = createEventDispatcher();
import {
archiveChatById,
cloneChatById,
deleteChatById,
getChatList,
updateChatById
} from '$lib/apis/chats';
import { chatId, chats, mobile, showSidebar } from '$lib/stores';
import ChatMenu from './ChatMenu.svelte';
import ShareChatModal from '$lib/components/chat/ShareChatModal.svelte';
import GarbageBin from '$lib/components/icons/GarbageBin.svelte';
import Tooltip from '$lib/components/common/Tooltip.svelte';
import ArchiveBox from '$lib/components/icons/ArchiveBox.svelte';
export let chat;
export let selected = false;
export let shiftKey = false;
let mouseOver = false;
let showShareChatModal = false;
let confirmEdit = false;
let chatTitle = chat.title;
const editChatTitle = async (id, _title) => {
if (_title === '') {
toast.error($i18n.t('Title cannot be an empty string.'));
} else {
await updateChatById(localStorage.token, id, {
title: _title
});
await chats.set(await getChatList(localStorage.token));
}
};
const cloneChatHandler = async (id) => {
const res = await cloneChatById(localStorage.token, id).catch((error) => {
toast.error(error);
return null;
});
if (res) {
goto(`/c/${res.id}`);
await chats.set(await getChatList(localStorage.token));
}
};
const archiveChatHandler = async (id) => {
await archiveChatById(localStorage.token, id);
await chats.set(await getChatList(localStorage.token));
};
const focusEdit = async (node: HTMLInputElement) => {
node.focus();
};
</script>
<ShareChatModal bind:show={showShareChatModal} chatId={chat.id} />
<div class=" w-full pr-2 relative group">
{#if confirmEdit}
<div
class=" w-full flex justify-between rounded-xl px-3 py-2 {chat.id === $chatId || confirmEdit
? 'bg-gray-200 dark:bg-gray-900'
: selected
? 'bg-gray-100 dark:bg-gray-950'
: 'group-hover:bg-gray-100 dark:group-hover:bg-gray-950'} whitespace-nowrap text-ellipsis"
>
<input
use:focusEdit
bind:value={chatTitle}
class=" bg-transparent w-full outline-none mr-10"
/>
</div>
{:else}
<a
class=" w-full flex justify-between rounded-xl px-3 py-2 {chat.id === $chatId || confirmEdit
? 'bg-gray-200 dark:bg-gray-900'
: selected
? 'bg-gray-100 dark:bg-gray-950'
: ' group-hover:bg-gray-100 dark:group-hover:bg-gray-950'} whitespace-nowrap text-ellipsis"
href="/c/{chat.id}"
on:click={() => {
dispatch('select');
if ($mobile) {
showSidebar.set(false);
}
}}
on:dblclick={() => {
chatTitle = chat.title;
confirmEdit = true;
}}
on:mouseenter={(e) => {
mouseOver = true;
}}
on:mouseleave={(e) => {
mouseOver = false;
}}
on:focus={(e) => {}}
draggable="false"
>
<div class=" flex self-center flex-1 w-full">
<div class=" text-left self-center overflow-hidden w-full h-[20px]">
{chat.title}
</div>
</div>
</a>
{/if}
<!-- svelte-ignore a11y-no-static-element-interactions -->
<div
class="
{chat.id === $chatId || confirmEdit
? 'from-gray-200 dark:from-gray-900'
: selected
? 'from-gray-100 dark:from-gray-950'
: 'invisible group-hover:visible from-gray-100 dark:from-gray-950'}
absolute right-[10px] top-[10px] pr-2 pl-5 bg-gradient-to-l from-80%
to-transparent"
on:mouseenter={(e) => {
mouseOver = true;
}}
on:mouseleave={(e) => {
mouseOver = false;
}}
>
{#if confirmEdit}
<div class="flex self-center space-x-1.5 z-10">
<Tooltip content="Confirm">
<button
class=" self-center dark:hover:text-white transition"
on:click={() => {
editChatTitle(chat.id, chatTitle);
confirmEdit = false;
chatTitle = '';
}}
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
fill="currentColor"
class="w-4 h-4"
>
<path
fill-rule="evenodd"
d="M16.704 4.153a.75.75 0 01.143 1.052l-8 10.5a.75.75 0 01-1.127.075l-4.5-4.5a.75.75 0 011.06-1.06l3.894 3.893 7.48-9.817a.75.75 0 011.05-.143z"
clip-rule="evenodd"
/>
</svg>
</button>
</Tooltip>
<Tooltip content="Cancel">
<button
class=" self-center dark:hover:text-white transition"
on:click={() => {
confirmEdit = false;
chatTitle = '';
}}
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
fill="currentColor"
class="w-4 h-4"
>
<path
d="M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z"
/>
</svg>
</button>
</Tooltip>
</div>
{:else if shiftKey && mouseOver}
<div class=" flex items-center self-center space-x-1.5">
<Tooltip content="Archive" className="flex items-center">
<button
class=" self-center dark:hover:text-white transition"
on:click={() => {
archiveChatHandler(chat.id);
}}
type="button"
>
<ArchiveBox className="size-4 translate-y-[0.5px]" strokeWidth="2" />
</button>
</Tooltip>
<Tooltip content="Delete">
<button
class=" self-center dark:hover:text-white transition"
on:click={() => {
dispatch('delete', 'shift');
}}
type="button"
>
<GarbageBin strokeWidth="2" />
</button>
</Tooltip>
</div>
{:else}
<div class="flex self-center space-x-1 z-10">
<ChatMenu
chatId={chat.id}
cloneChatHandler={() => {
cloneChatHandler(chat.id);
}}
shareHandler={() => {
showShareChatModal = true;
}}
archiveChatHandler={() => {
archiveChatHandler(chat.id);
}}
renameHandler={() => {
chatTitle = chat.title;
confirmEdit = true;
}}
deleteHandler={() => {
dispatch('delete');
}}
onClose={() => {
dispatch('unselect');
}}
>
<button
aria-label="Chat Menu"
class=" self-center dark:hover:text-white transition"
on:click={() => {
dispatch('select');
}}
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 16 16"
fill="currentColor"
class="w-4 h-4"
>
<path
d="M2 8a1.5 1.5 0 1 1 3 0 1.5 1.5 0 0 1-3 0ZM6.5 8a1.5 1.5 0 1 1 3 0 1.5 1.5 0 0 1-3 0ZM12.5 6.5a1.5 1.5 0 1 0 0 3 1.5 1.5 0 0 0 0-3Z"
/>
</svg>
</button>
</ChatMenu>
{#if chat.id === $chatId}
<!-- Shortcut support using "delete-chat-button" id -->
<button
id="delete-chat-button"
class="hidden"
on:click={() => {
dispatch('delete');
}}
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 16 16"
fill="currentColor"
class="w-4 h-4"
>
<path
d="M2 8a1.5 1.5 0 1 1 3 0 1.5 1.5 0 0 1-3 0ZM6.5 8a1.5 1.5 0 1 1 3 0 1.5 1.5 0 0 1-3 0ZM12.5 6.5a1.5 1.5 0 1 0 0 3 1.5 1.5 0 0 0 0-3Z"
/>
</svg>
</button>
{/if}
</div>
{/if}
</div>
</div>

View File

@ -17,15 +17,19 @@
import EllipsisHorizontal from '../icons/EllipsisHorizontal.svelte';
import ModelMenu from './Models/ModelMenu.svelte';
import ModelDeleteConfirmDialog from '../common/ConfirmDialog.svelte';
const i18n = getContext('i18n');
let showModelDeleteConfirm = false;
let localModelfiles = [];
let importFiles;
let modelsImportInputElement: HTMLInputElement;
let _models = [];
let selectedModel = null;
let sortable = null;
let searchValue = '';
@ -199,6 +203,13 @@
</title>
</svelte:head>
<ModelDeleteConfirmDialog
bind:show={showModelDeleteConfirm}
on:confirm={() => {
deleteModelHandler(selectedModel);
}}
/>
<div class=" text-lg font-semibold mb-3">{$i18n.t('Models')}</div>
<div class=" flex w-full space-x-2">
@ -339,7 +350,8 @@
hideModelHandler(model);
}}
deleteHandler={() => {
deleteModelHandler(model);
selectedModel = model;
showModelDeleteConfirm = true;
}}
onClose={() => {}}
>

View File

@ -42,6 +42,7 @@
"Allow": "يسمح",
"Allow Chat Deletion": "يستطيع حذف المحادثات",
"Allow non-local voices": "",
"Allow User Location": "",
"alphanumeric characters and hyphens": "الأحرف الأبجدية الرقمية والواصلات",
"Already have an account?": "هل تملك حساب ؟",
"an assistant": "مساعد",
@ -69,6 +70,7 @@
"Bad Response": "استجابة خطاء",
"Banners": "لافتات",
"Base Model (From)": "النموذج الأساسي (من)",
"Batch Size (num_batch)": "",
"before": "قبل",
"Being lazy": "كون كسول",
"Brave Search API Key": "مفتاح واجهة برمجة تطبيقات البحث الشجاع",
@ -160,6 +162,7 @@
"Discover, download, and explore custom prompts": "اكتشاف وتنزيل واستكشاف المطالبات المخصصة",
"Discover, download, and explore model presets": "اكتشاف وتنزيل واستكشاف الإعدادات المسبقة للنموذج",
"Dismissible": "",
"Display Emoji in Call": "",
"Display the username instead of You in the Chat": "اعرض اسم المستخدم بدلاً منك في الدردشة",
"Document": "المستند",
"Document Settings": "أعدادات المستند",
@ -176,6 +179,7 @@
"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "e.g. '30s','10m'. الوحدات الزمنية الصالحة هي 's', 'm', 'h'.",
"Edit": "تعديل",
"Edit Doc": "تعديل الملف",
"Edit Memory": "",
"Edit User": "تعديل المستخدم",
"Email": "البريد",
"Embedding Batch Size": "",
@ -205,6 +209,7 @@
"Enter Serply API Key": "",
"Enter Serpstack API Key": "أدخل مفتاح واجهة برمجة تطبيقات Serpstack",
"Enter stop sequence": "أدخل تسلسل التوقف",
"Enter Tavily API Key": "",
"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)",
@ -280,6 +285,7 @@
"Knowledge": "",
"Language": "اللغة",
"Last Active": "آخر نشاط",
"Last Modified": "",
"Light": "فاتح",
"Listening...": "",
"LLMs can make mistakes. Verify important information.": "يمكن أن تصدر بعض الأخطاء. لذلك يجب التحقق من المعلومات المهمة",
@ -304,6 +310,7 @@
"Mirostat Tau": "Mirostat Tau",
"MMMM DD, YYYY": "MMMM DD, YYYY",
"MMMM DD, YYYY HH:mm": "MMMM DD, YYYY HH:mm",
"MMMM DD, YYYY hh:mm:ss A": "",
"Model '{{modelName}}' has been successfully downloaded.": "تم تحميل النموذج '{{modelName}}' بنجاح",
"Model '{{modelTag}}' is already in queue for downloading.": "النموذج '{{modelTag}}' موجود بالفعل في قائمة الانتظار للتحميل",
"Model {{modelId}} not found": "لم يتم العثور على النموذج {{modelId}}.",
@ -492,6 +499,8 @@
"System": "النظام",
"System Prompt": "محادثة النظام",
"Tags": "الوسوم",
"Tap to interrupt": "",
"Tavily API Key": "",
"Tell us more:": "أخبرنا المزيد:",
"Temperature": "درجة حرارة",
"Template": "نموذج",
@ -522,6 +531,7 @@
"Today": "اليوم",
"Toggle settings": "فتح وأغلاق الاعدادات",
"Toggle sidebar": "فتح وأغلاق الشريط الجانبي",
"Tokens To Keep On Context Refresh (num_keep)": "",
"Tools": "",
"Top K": "Top K",
"Top P": "Top P",
@ -533,8 +543,10 @@
"Type Hugging Face Resolve (Download) URL": "اكتب عنوان URL لحل مشكلة الوجه (تنزيل).",
"Uh-oh! There was an issue connecting to {{provider}}.": "{{provider}}خطاء أوه! حدثت مشكلة في الاتصال بـ ",
"Unknown File Type '{{file_type}}', but accepting and treating as plain text": "نوع ملف غير معروف '{{file_type}}', ولكن القبول والتعامل كنص عادي ",
"Update": "",
"Update and Copy Link": "تحديث ونسخ الرابط",
"Update password": "تحديث كلمة المرور",
"Updated at": "",
"Upload a GGUF model": "GGUF رفع موديل نوع",
"Upload Files": "تحميل الملفات",
"Upload Pipeline": "",

View File

@ -42,6 +42,7 @@
"Allow": "Позволи",
"Allow Chat Deletion": "Позволи Изтриване на Чат",
"Allow non-local voices": "",
"Allow User Location": "",
"alphanumeric characters and hyphens": "алфанумерични знаци и тире",
"Already have an account?": "Вече имате акаунт? ",
"an assistant": "асистент",
@ -69,6 +70,7 @@
"Bad Response": "Невалиден отговор от API",
"Banners": "Банери",
"Base Model (From)": "Базов модел (от)",
"Batch Size (num_batch)": "",
"before": "преди",
"Being lazy": "Да бъдеш мързелив",
"Brave Search API Key": "Смел ключ за API за търсене",
@ -160,6 +162,7 @@
"Discover, download, and explore custom prompts": "Откриване, сваляне и преглед на персонализирани промптове",
"Discover, download, and explore model presets": "Откриване, сваляне и преглед на пресетове на модели",
"Dismissible": "",
"Display Emoji in Call": "",
"Display the username instead of You in the Chat": "Показване на потребителското име вместо Вие в чата",
"Document": "Документ",
"Document Settings": "Документ Настройки",
@ -176,6 +179,7 @@
"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "напр. '30с','10м'. Валидни единици са 'с', 'м', 'ч'.",
"Edit": "Редактиране",
"Edit Doc": "Редактиране на документ",
"Edit Memory": "",
"Edit User": "Редактиране на потребител",
"Email": "Имейл",
"Embedding Batch Size": "",
@ -205,6 +209,7 @@
"Enter Serply API Key": "",
"Enter Serpstack API Key": "Въведете Serpstack API ключ",
"Enter stop sequence": "Въведете стоп последователност",
"Enter Tavily API Key": "",
"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)",
@ -280,6 +285,7 @@
"Knowledge": "",
"Language": "Език",
"Last Active": "Последни активни",
"Last Modified": "",
"Light": "Светъл",
"Listening...": "",
"LLMs can make mistakes. Verify important information.": "LLMs могат да правят грешки. Проверете важните данни.",
@ -304,6 +310,7 @@
"Mirostat Tau": "Mirostat Tau",
"MMMM DD, YYYY": "MMMM DD, YYYY",
"MMMM DD, YYYY HH:mm": "MMMM DD, YYYY HH:mm",
"MMMM DD, YYYY hh:mm:ss A": "",
"Model '{{modelName}}' has been successfully downloaded.": "Моделът '{{modelName}}' беше успешно свален.",
"Model '{{modelTag}}' is already in queue for downloading.": "Моделът '{{modelTag}}' е вече в очакване за сваляне.",
"Model {{modelId}} not found": "Моделът {{modelId}} не е намерен",
@ -488,6 +495,8 @@
"System": "Система",
"System Prompt": "Системен Промпт",
"Tags": "Тагове",
"Tap to interrupt": "",
"Tavily API Key": "",
"Tell us more:": "Повече информация:",
"Temperature": "Температура",
"Template": "Шаблон",
@ -518,6 +527,7 @@
"Today": "днес",
"Toggle settings": "Toggle settings",
"Toggle sidebar": "Toggle sidebar",
"Tokens To Keep On Context Refresh (num_keep)": "",
"Tools": "",
"Top K": "Top K",
"Top P": "Top P",
@ -529,8 +539,10 @@
"Type Hugging Face Resolve (Download) URL": "Въведете Hugging Face Resolve (Download) URL",
"Uh-oh! There was an issue connecting to {{provider}}.": "О, не! Възникна проблем при свързването с {{provider}}.",
"Unknown File Type '{{file_type}}', but accepting and treating as plain text": "Непознат файлов тип '{{file_type}}', но се приема и обработва като текст",
"Update": "",
"Update and Copy Link": "Обнови и копирай връзка",
"Update password": "Обновяване на парола",
"Updated at": "",
"Upload a GGUF model": "Качване на GGUF модел",
"Upload Files": "Качване на файлове",
"Upload Pipeline": "",

View File

@ -42,6 +42,7 @@
"Allow": "অনুমোদন",
"Allow Chat Deletion": "চ্যাট ডিলিট করতে দিন",
"Allow non-local voices": "",
"Allow User Location": "",
"alphanumeric characters and hyphens": "ইংরেজি অক্ষর, সংখ্যা এবং হাইফেন",
"Already have an account?": "আগে থেকেই একাউন্ট আছে?",
"an assistant": "একটা এসিস্ট্যান্ট",
@ -69,6 +70,7 @@
"Bad Response": "খারাপ প্রতিক্রিয়া",
"Banners": "ব্যানার",
"Base Model (From)": "বেস মডেল (থেকে)",
"Batch Size (num_batch)": "",
"before": "পূর্ববর্তী",
"Being lazy": "অলস হওয়া",
"Brave Search API Key": "সাহসী অনুসন্ধান API কী",
@ -160,6 +162,7 @@
"Discover, download, and explore custom prompts": "কাস্টম প্রম্পটগুলো আবিস্কার, ডাউনলোড এবং এক্সপ্লোর করুন",
"Discover, download, and explore model presets": "মডেল প্রিসেটগুলো আবিস্কার, ডাউনলোড এবং এক্সপ্লোর করুন",
"Dismissible": "",
"Display Emoji in Call": "",
"Display the username instead of You in the Chat": "চ্যাটে 'আপনি'-র পরবর্তে ইউজারনেম দেখান",
"Document": "ডকুমেন্ট",
"Document Settings": "ডকুমেন্ট সেটিংসমূহ",
@ -176,6 +179,7 @@
"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "যেমন '30s','10m'. সময়ের অনুমোদিত অনুমোদিত এককগুলি হচ্ছে 's', 'm', 'h'.",
"Edit": "এডিট করুন",
"Edit Doc": "ডকুমেন্ট এডিট করুন",
"Edit Memory": "",
"Edit User": "ইউজার এডিট করুন",
"Email": "ইমেইল",
"Embedding Batch Size": "",
@ -205,6 +209,7 @@
"Enter Serply API Key": "",
"Enter Serpstack API Key": "Serpstack API কী লিখুন",
"Enter stop sequence": "স্টপ সিকোয়েন্স লিখুন",
"Enter Tavily API Key": "",
"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)",
@ -280,6 +285,7 @@
"Knowledge": "",
"Language": "ভাষা",
"Last Active": "সর্বশেষ সক্রিয়",
"Last Modified": "",
"Light": "লাইট",
"Listening...": "",
"LLMs can make mistakes. Verify important information.": "LLM ভুল করতে পারে। গুরুত্বপূর্ণ তথ্য যাচাই করে নিন।",
@ -304,6 +310,7 @@
"Mirostat Tau": "Mirostat Tau",
"MMMM DD, YYYY": "MMMM DD, YYYY",
"MMMM DD, YYYY HH:mm": "MMMM DD, YYYY HH:mm",
"MMMM DD, YYYY hh:mm:ss A": "",
"Model '{{modelName}}' has been successfully downloaded.": "'{{modelName}}' মডেল সফলভাবে ডাউনলোড হয়েছে।",
"Model '{{modelTag}}' is already in queue for downloading.": "{{modelTag}} ডাউনলোডের জন্য আগে থেকেই অপেক্ষমান আছে।",
"Model {{modelId}} not found": "{{modelId}} মডেল পাওয়া যায়নি",
@ -488,6 +495,8 @@
"System": "সিস্টেম",
"System Prompt": "সিস্টেম প্রম্পট",
"Tags": "ট্যাগসমূহ",
"Tap to interrupt": "",
"Tavily API Key": "",
"Tell us more:": "আরও বলুন:",
"Temperature": "তাপমাত্রা",
"Template": "টেম্পলেট",
@ -518,6 +527,7 @@
"Today": "আজ",
"Toggle settings": "সেটিংস টোগল",
"Toggle sidebar": "সাইডবার টোগল",
"Tokens To Keep On Context Refresh (num_keep)": "",
"Tools": "",
"Top K": "Top K",
"Top P": "Top P",
@ -529,8 +539,10 @@
"Type Hugging Face Resolve (Download) URL": "Hugging Face থেকে ডাউনলোড করার ইউআরএল টাইপ করুন",
"Uh-oh! There was an issue connecting to {{provider}}.": "ওহ-হো! {{provider}} এর সাথে কানেকশনে সমস্যা হয়েছে।",
"Unknown File Type '{{file_type}}', but accepting and treating as plain text": "অপরিচিত ফাইল ফরম্যাট '{{file_type}}', তবে প্লেইন টেক্সট হিসেবে গ্রহণ করা হলো",
"Update": "",
"Update and Copy Link": "আপডেট এবং লিংক কপি করুন",
"Update password": "পাসওয়ার্ড আপডেট করুন",
"Updated at": "",
"Upload a GGUF model": "একটি GGUF মডেল আপলোড করুন",
"Upload Files": "ফাইল আপলোড করুন",
"Upload Pipeline": "",

View File

@ -42,6 +42,7 @@
"Allow": "Permet",
"Allow Chat Deletion": "Permet la Supressió del Xat",
"Allow non-local voices": "",
"Allow User Location": "",
"alphanumeric characters and hyphens": "caràcters alfanumèrics i guions",
"Already have an account?": "Ja tens un compte?",
"an assistant": "un assistent",
@ -69,6 +70,7 @@
"Bad Response": "Resposta Erroni",
"Banners": "Banners",
"Base Model (From)": "Model base (des de)",
"Batch Size (num_batch)": "",
"before": "abans",
"Being lazy": "Ser l'estupidez",
"Brave Search API Key": "Clau API Brave Search",
@ -160,6 +162,7 @@
"Discover, download, and explore custom prompts": "Descobreix, descarrega i explora prompts personalitzats",
"Discover, download, and explore model presets": "Descobreix, descarrega i explora presets de models",
"Dismissible": "",
"Display Emoji in Call": "",
"Display the username instead of You in the Chat": "Mostra el nom d'usuari en lloc de 'Tu' al Xat",
"Document": "Document",
"Document Settings": "Configuració de Documents",
@ -176,6 +179,7 @@
"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "p. ex. '30s','10m'. Les unitats de temps vàlides són 's', 'm', 'h'.",
"Edit": "Editar",
"Edit Doc": "Edita Document",
"Edit Memory": "",
"Edit User": "Edita Usuari",
"Email": "Correu electrònic",
"Embedding Batch Size": "",
@ -205,6 +209,7 @@
"Enter Serply API Key": "",
"Enter Serpstack API Key": "Introduïu la clau de l'API Serpstack",
"Enter stop sequence": "Introdueix la seqüència de parada",
"Enter Tavily API Key": "",
"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)",
@ -280,6 +285,7 @@
"Knowledge": "",
"Language": "Idioma",
"Last Active": "Últim Actiu",
"Last Modified": "",
"Light": "Clar",
"Listening...": "",
"LLMs can make mistakes. Verify important information.": "Els LLMs poden cometre errors. Verifica la informació important.",
@ -304,6 +310,7 @@
"Mirostat Tau": "Tau de Mirostat",
"MMMM DD, YYYY": "DD de MMMM, YYYY",
"MMMM DD, YYYY HH:mm": "DD de MMMM, YYYY HH:mm",
"MMMM DD, YYYY hh:mm:ss A": "",
"Model '{{modelName}}' has been successfully downloaded.": "El model '{{modelName}}' s'ha descarregat amb èxit.",
"Model '{{modelTag}}' is already in queue for downloading.": "El model '{{modelTag}}' ja està en cua per ser descarregat.",
"Model {{modelId}} not found": "Model {{modelId}} no trobat",
@ -489,6 +496,8 @@
"System": "Sistema",
"System Prompt": "Prompt del Sistema",
"Tags": "Etiquetes",
"Tap to interrupt": "",
"Tavily API Key": "",
"Tell us more:": "Dóna'ns més informació:",
"Temperature": "Temperatura",
"Template": "Plantilla",
@ -519,6 +528,7 @@
"Today": "Avui",
"Toggle settings": "Commuta configuracions",
"Toggle sidebar": "Commuta barra lateral",
"Tokens To Keep On Context Refresh (num_keep)": "",
"Tools": "",
"Top K": "Top K",
"Top P": "Top P",
@ -530,8 +540,10 @@
"Type Hugging Face Resolve (Download) URL": "Escriu URL de Resolució (Descàrrega) de Hugging Face",
"Uh-oh! There was an issue connecting to {{provider}}.": "Uf! Hi va haver un problema connectant-se a {{provider}}.",
"Unknown File Type '{{file_type}}', but accepting and treating as plain text": "Tipus d'Arxiu Desconegut '{{file_type}}', però acceptant i tractant com a text pla",
"Update": "",
"Update and Copy Link": "Actualitza i Copia enllaç",
"Update password": "Actualitza contrasenya",
"Updated at": "",
"Upload a GGUF model": "Puja un model GGUF",
"Upload Files": "Pujar fitxers",
"Upload Pipeline": "",

View File

@ -42,6 +42,7 @@
"Allow": "Sa pagtugot",
"Allow Chat Deletion": "Tugoti nga mapapas ang mga chat",
"Allow non-local voices": "",
"Allow User Location": "",
"alphanumeric characters and hyphens": "alphanumeric nga mga karakter ug hyphen",
"Already have an account?": "Naa na kay account ?",
"an assistant": "usa ka katabang",
@ -69,6 +70,7 @@
"Bad Response": "",
"Banners": "",
"Base Model (From)": "",
"Batch Size (num_batch)": "",
"before": "",
"Being lazy": "",
"Brave Search API Key": "",
@ -160,6 +162,7 @@
"Discover, download, and explore custom prompts": "Pagdiskubre, pag-download ug pagsuhid sa mga naandan nga pag-aghat",
"Discover, download, and explore model presets": "Pagdiskobre, pag-download, ug pagsuhid sa mga preset sa template",
"Dismissible": "",
"Display Emoji in Call": "",
"Display the username instead of You in the Chat": "Ipakita ang username imbes nga 'Ikaw' sa Panaghisgutan",
"Document": "Dokumento",
"Document Settings": "Mga Setting sa Dokumento",
@ -176,6 +179,7 @@
"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "p. ",
"Edit": "",
"Edit Doc": "I-edit ang dokumento",
"Edit Memory": "",
"Edit User": "I-edit ang tiggamit",
"Email": "E-mail",
"Embedding Batch Size": "",
@ -205,6 +209,7 @@
"Enter Serply API Key": "",
"Enter Serpstack API Key": "",
"Enter stop sequence": "Pagsulod sa katapusan nga han-ay",
"Enter Tavily API Key": "",
"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)": "",
@ -280,6 +285,7 @@
"Knowledge": "",
"Language": "Pinulongan",
"Last Active": "",
"Last Modified": "",
"Light": "Kahayag",
"Listening...": "",
"LLMs can make mistakes. Verify important information.": "Ang mga LLM mahimong masayop. ",
@ -304,6 +310,7 @@
"Mirostat Tau": "Mirostat Tau",
"MMMM DD, YYYY": "MMMM DD, YYYY",
"MMMM DD, YYYY HH:mm": "",
"MMMM DD, YYYY hh:mm:ss A": "",
"Model '{{modelName}}' has been successfully downloaded.": "Ang modelo'{{modelName}}' malampuson nga na-download.",
"Model '{{modelTag}}' is already in queue for downloading.": "Ang modelo'{{modelTag}}' naa na sa pila para ma-download.",
"Model {{modelId}} not found": "Modelo {{modelId}} wala makit-an",
@ -488,6 +495,8 @@
"System": "Sistema",
"System Prompt": "Madasig nga Sistema",
"Tags": "Mga tag",
"Tap to interrupt": "",
"Tavily API Key": "",
"Tell us more:": "",
"Temperature": "Temperatura",
"Template": "Modelo",
@ -518,6 +527,7 @@
"Today": "",
"Toggle settings": "I-toggle ang mga setting",
"Toggle sidebar": "I-toggle ang sidebar",
"Tokens To Keep On Context Refresh (num_keep)": "",
"Tools": "",
"Top K": "Top K",
"Top P": "Ibabaw nga P",
@ -529,8 +539,10 @@
"Type Hugging Face Resolve (Download) URL": "Pagsulod sa resolusyon (pag-download) URL Hugging Face",
"Uh-oh! There was an issue connecting to {{provider}}.": "Uh-oh! {{provider}}.",
"Unknown File Type '{{file_type}}', but accepting and treating as plain text": "Wala mailhi nga tipo sa file '{{file_type}}', apan gidawat ug gitratar ingon yano nga teksto",
"Update": "",
"Update and Copy Link": "",
"Update password": "I-update ang password",
"Updated at": "",
"Upload a GGUF model": "Pag-upload ug modelo sa GGUF",
"Upload Files": "",
"Upload Pipeline": "",

View File

@ -42,6 +42,7 @@
"Allow": "Erlauben",
"Allow Chat Deletion": "Chat Löschung erlauben",
"Allow non-local voices": "Nicht-lokale Stimmen erlauben",
"Allow User Location": "",
"alphanumeric characters and hyphens": "alphanumerische Zeichen und Bindestriche",
"Already have an account?": "Hast du vielleicht schon ein Account?",
"an assistant": "ein Assistent",
@ -69,6 +70,7 @@
"Bad Response": "Schlechte Antwort",
"Banners": "Banner",
"Base Model (From)": "Basismodell (von)",
"Batch Size (num_batch)": "",
"before": "bereits geteilt",
"Being lazy": "Faul sein",
"Brave Search API Key": "API-Schlüssel für die Brave-Suche",
@ -160,6 +162,7 @@
"Discover, download, and explore custom prompts": "Benutzerdefinierte Prompts entdecken, herunterladen und erkunden",
"Discover, download, and explore model presets": "Modellvorgaben entdecken, herunterladen und erkunden",
"Dismissible": "ausblendbar",
"Display Emoji in Call": "",
"Display the username instead of You in the Chat": "Den Benutzernamen anstelle von 'du' im Chat anzeigen",
"Document": "Dokument",
"Document Settings": "Dokumenteinstellungen",
@ -176,6 +179,7 @@
"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "z.B. '30s','10m'. Gültige Zeiteinheiten sind 's', 'm', 'h'.",
"Edit": "Bearbeiten",
"Edit Doc": "Dokument bearbeiten",
"Edit Memory": "",
"Edit User": "Benutzer bearbeiten",
"Email": "E-Mail",
"Embedding Batch Size": "Embedding Batch Größe",
@ -205,6 +209,7 @@
"Enter Serply API Key": "",
"Enter Serpstack API Key": "Geben Sie den Serpstack-API-Schlüssel ein",
"Enter stop sequence": "Stop-Sequenz eingeben",
"Enter Tavily API Key": "",
"Enter Top K": "Gib Top K ein",
"Enter URL (e.g. http://127.0.0.1:7860/)": "Gib die URL ein (z.B. http://127.0.0.1:7860/)",
"Enter URL (e.g. http://localhost:11434)": "Gib die URL ein (z.B. http://localhost:11434)",
@ -280,6 +285,7 @@
"Knowledge": "",
"Language": "Sprache",
"Last Active": "Zuletzt aktiv",
"Last Modified": "",
"Light": "Hell",
"Listening...": "",
"LLMs can make mistakes. Verify important information.": "LLMs können Fehler machen. Überprüfe wichtige Informationen.",
@ -304,6 +310,7 @@
"Mirostat Tau": "Mirostat Tau",
"MMMM DD, YYYY": "DD MMMM YYYY",
"MMMM DD, YYYY HH:mm": "DD MMMM YYYY HH:mm",
"MMMM DD, YYYY hh:mm:ss A": "",
"Model '{{modelName}}' has been successfully downloaded.": "Modell '{{modelName}}' wurde erfolgreich heruntergeladen.",
"Model '{{modelTag}}' is already in queue for downloading.": "Modell '{{modelTag}}' befindet sich bereits in der Warteschlange zum Herunterladen.",
"Model {{modelId}} not found": "Modell {{modelId}} nicht gefunden",
@ -488,6 +495,8 @@
"System": "System",
"System Prompt": "System-Prompt",
"Tags": "Tags",
"Tap to interrupt": "",
"Tavily API Key": "",
"Tell us more:": "Erzähl uns mehr",
"Temperature": "Temperatur",
"Template": "Vorlage",
@ -518,6 +527,7 @@
"Today": "Heute",
"Toggle settings": "Einstellungen umschalten",
"Toggle sidebar": "Seitenleiste umschalten",
"Tokens To Keep On Context Refresh (num_keep)": "",
"Tools": "",
"Top K": "Top K",
"Top P": "Top P",
@ -529,8 +539,10 @@
"Type Hugging Face Resolve (Download) URL": "Gib die Hugging Face Resolve (Download) URL ein",
"Uh-oh! There was an issue connecting to {{provider}}.": "Ups! Es gab ein Problem bei der Verbindung mit {{provider}}.",
"Unknown File Type '{{file_type}}', but accepting and treating as plain text": "Unbekannter Dateityp '{{file_type}}', wird jedoch akzeptiert und als einfacher Text behandelt.",
"Update": "",
"Update and Copy Link": "Erneuern und kopieren",
"Update password": "Passwort aktualisieren",
"Updated at": "",
"Upload a GGUF model": "GGUF Model hochladen",
"Upload Files": "Dateien hochladen",
"Upload Pipeline": "",

View File

@ -42,6 +42,7 @@
"Allow": "Allow",
"Allow Chat Deletion": "Allow Delete Chats",
"Allow non-local voices": "",
"Allow User Location": "",
"alphanumeric characters and hyphens": "so alpha, many hyphen",
"Already have an account?": "Such account exists?",
"an assistant": "such assistant",
@ -69,6 +70,7 @@
"Bad Response": "",
"Banners": "",
"Base Model (From)": "",
"Batch Size (num_batch)": "",
"before": "",
"Being lazy": "",
"Brave Search API Key": "",
@ -160,6 +162,7 @@
"Discover, download, and explore custom prompts": "Discover, download, and explore custom prompts",
"Discover, download, and explore model presets": "Discover, download, and explore model presets",
"Dismissible": "",
"Display Emoji in Call": "",
"Display the username instead of You in the Chat": "Display username instead of You in Chat",
"Document": "Document",
"Document Settings": "Document Settings",
@ -176,6 +179,7 @@
"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "e.g. '30s','10m'. Much time units are 's', 'm', 'h'.",
"Edit": "",
"Edit Doc": "Edit Doge",
"Edit Memory": "",
"Edit User": "Edit Wowser",
"Email": "Email",
"Embedding Batch Size": "",
@ -205,6 +209,7 @@
"Enter Serply API Key": "",
"Enter Serpstack API Key": "",
"Enter stop sequence": "Enter stop bark",
"Enter Tavily API Key": "",
"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)": "",
@ -280,6 +285,7 @@
"Knowledge": "",
"Language": "Doge Speak",
"Last Active": "",
"Last Modified": "",
"Light": "Light",
"Listening...": "",
"LLMs can make mistakes. Verify important information.": "LLMs can make borks. Verify important info.",
@ -304,6 +310,7 @@
"Mirostat Tau": "Mirostat Tau",
"MMMM DD, YYYY": "MMMM DD, YYYY",
"MMMM DD, YYYY HH:mm": "",
"MMMM DD, YYYY hh:mm:ss A": "",
"Model '{{modelName}}' has been successfully downloaded.": "Model '{{modelName}}' has been successfully downloaded.",
"Model '{{modelTag}}' is already in queue for downloading.": "Model '{{modelTag}}' is already in queue for downloading.",
"Model {{modelId}} not found": "Model {{modelId}} not found",
@ -488,6 +495,8 @@
"System": "System very system",
"System Prompt": "System Prompt much prompt",
"Tags": "Tags very tags",
"Tap to interrupt": "",
"Tavily API Key": "",
"Tell us more:": "",
"Temperature": "Temperature very temp",
"Template": "Template much template",
@ -518,6 +527,7 @@
"Today": "",
"Toggle settings": "Toggle settings much toggle",
"Toggle sidebar": "Toggle sidebar much toggle",
"Tokens To Keep On Context Refresh (num_keep)": "",
"Tools": "",
"Top K": "Top K very top",
"Top P": "Top P very top",
@ -529,8 +539,10 @@
"Type Hugging Face Resolve (Download) URL": "Type Hugging Face Resolve (Download) URL much download",
"Uh-oh! There was an issue connecting to {{provider}}.": "Uh-oh! There was an issue connecting to {{provider}}. Much uh-oh!",
"Unknown File Type '{{file_type}}', but accepting and treating as plain text": "Unknown File Type '{{file_type}}', but accepting and treating as plain text very unknown",
"Update": "",
"Update and Copy Link": "",
"Update password": "Update password much change",
"Updated at": "",
"Upload a GGUF model": "Upload a GGUF model very upload",
"Upload Files": "",
"Upload Pipeline": "",

View File

@ -42,6 +42,7 @@
"Allow": "",
"Allow Chat Deletion": "",
"Allow non-local voices": "",
"Allow User Location": "",
"alphanumeric characters and hyphens": "",
"Already have an account?": "",
"an assistant": "",
@ -69,6 +70,7 @@
"Bad Response": "",
"Banners": "",
"Base Model (From)": "",
"Batch Size (num_batch)": "",
"before": "",
"Being lazy": "",
"Brave Search API Key": "",
@ -160,6 +162,7 @@
"Discover, download, and explore custom prompts": "",
"Discover, download, and explore model presets": "",
"Dismissible": "",
"Display Emoji in Call": "",
"Display the username instead of You in the Chat": "",
"Document": "",
"Document Settings": "",
@ -176,6 +179,7 @@
"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "",
"Edit": "",
"Edit Doc": "",
"Edit Memory": "",
"Edit User": "",
"Email": "",
"Embedding Batch Size": "",
@ -205,6 +209,7 @@
"Enter Serply API Key": "",
"Enter Serpstack API Key": "",
"Enter stop sequence": "",
"Enter Tavily API Key": "",
"Enter Top K": "",
"Enter URL (e.g. http://127.0.0.1:7860/)": "",
"Enter URL (e.g. http://localhost:11434)": "",
@ -280,6 +285,7 @@
"Knowledge": "",
"Language": "",
"Last Active": "",
"Last Modified": "",
"Light": "",
"Listening...": "",
"LLMs can make mistakes. Verify important information.": "",
@ -304,6 +310,7 @@
"Mirostat Tau": "",
"MMMM DD, YYYY": "",
"MMMM DD, YYYY HH:mm": "",
"MMMM DD, YYYY hh:mm:ss A": "",
"Model '{{modelName}}' has been successfully downloaded.": "",
"Model '{{modelTag}}' is already in queue for downloading.": "",
"Model {{modelId}} not found": "",
@ -488,6 +495,8 @@
"System": "",
"System Prompt": "",
"Tags": "",
"Tap to interrupt": "",
"Tavily API Key": "",
"Tell us more:": "",
"Temperature": "",
"Template": "",
@ -518,6 +527,7 @@
"Today": "",
"Toggle settings": "",
"Toggle sidebar": "",
"Tokens To Keep On Context Refresh (num_keep)": "",
"Tools": "",
"Top K": "",
"Top P": "",
@ -529,8 +539,10 @@
"Type Hugging Face Resolve (Download) URL": "",
"Uh-oh! There was an issue connecting to {{provider}}.": "",
"Unknown File Type '{{file_type}}', but accepting and treating as plain text": "",
"Update": "",
"Update and Copy Link": "",
"Update password": "",
"Updated at": "",
"Upload a GGUF model": "",
"Upload Files": "",
"Upload Pipeline": "",

View File

@ -42,6 +42,7 @@
"Allow": "",
"Allow Chat Deletion": "",
"Allow non-local voices": "",
"Allow User Location": "",
"alphanumeric characters and hyphens": "",
"Already have an account?": "",
"an assistant": "",
@ -69,6 +70,7 @@
"Bad Response": "",
"Banners": "",
"Base Model (From)": "",
"Batch Size (num_batch)": "",
"before": "",
"Being lazy": "",
"Brave Search API Key": "",
@ -160,6 +162,7 @@
"Discover, download, and explore custom prompts": "",
"Discover, download, and explore model presets": "",
"Dismissible": "",
"Display Emoji in Call": "",
"Display the username instead of You in the Chat": "",
"Document": "",
"Document Settings": "",
@ -176,6 +179,7 @@
"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "",
"Edit": "",
"Edit Doc": "",
"Edit Memory": "",
"Edit User": "",
"Email": "",
"Embedding Batch Size": "",
@ -205,6 +209,7 @@
"Enter Serply API Key": "",
"Enter Serpstack API Key": "",
"Enter stop sequence": "",
"Enter Tavily API Key": "",
"Enter Top K": "",
"Enter URL (e.g. http://127.0.0.1:7860/)": "",
"Enter URL (e.g. http://localhost:11434)": "",
@ -280,6 +285,7 @@
"Knowledge": "",
"Language": "",
"Last Active": "",
"Last Modified": "",
"Light": "",
"Listening...": "",
"LLMs can make mistakes. Verify important information.": "",
@ -304,6 +310,7 @@
"Mirostat Tau": "",
"MMMM DD, YYYY": "",
"MMMM DD, YYYY HH:mm": "",
"MMMM DD, YYYY hh:mm:ss A": "",
"Model '{{modelName}}' has been successfully downloaded.": "",
"Model '{{modelTag}}' is already in queue for downloading.": "",
"Model {{modelId}} not found": "",
@ -488,6 +495,8 @@
"System": "",
"System Prompt": "",
"Tags": "",
"Tap to interrupt": "",
"Tavily API Key": "",
"Tell us more:": "",
"Temperature": "",
"Template": "",
@ -518,6 +527,7 @@
"Today": "",
"Toggle settings": "",
"Toggle sidebar": "",
"Tokens To Keep On Context Refresh (num_keep)": "",
"Tools": "",
"Top K": "",
"Top P": "",
@ -529,8 +539,10 @@
"Type Hugging Face Resolve (Download) URL": "",
"Uh-oh! There was an issue connecting to {{provider}}.": "",
"Unknown File Type '{{file_type}}', but accepting and treating as plain text": "",
"Update": "",
"Update and Copy Link": "",
"Update password": "",
"Updated at": "",
"Upload a GGUF model": "",
"Upload Files": "",
"Upload Pipeline": "",

View File

@ -42,6 +42,7 @@
"Allow": "Permitir",
"Allow Chat Deletion": "Permitir Borrar Chats",
"Allow non-local voices": "",
"Allow User Location": "",
"alphanumeric characters and hyphens": "caracteres alfanuméricos y guiones",
"Already have an account?": "¿Ya tienes una cuenta?",
"an assistant": "un asistente",
@ -69,6 +70,7 @@
"Bad Response": "Respuesta incorrecta",
"Banners": "Banners",
"Base Model (From)": "Modelo base (desde)",
"Batch Size (num_batch)": "",
"before": "antes",
"Being lazy": "Ser perezoso",
"Brave Search API Key": "Clave de API de Brave Search",
@ -160,6 +162,7 @@
"Discover, download, and explore custom prompts": "Descubre, descarga, y explora Prompts personalizados",
"Discover, download, and explore model presets": "Descubre, descarga y explora ajustes preestablecidos de modelos",
"Dismissible": "",
"Display Emoji in Call": "",
"Display the username instead of You in the Chat": "Mostrar el nombre de usuario en lugar de Usted en el chat",
"Document": "Documento",
"Document Settings": "Configuración del Documento",
@ -176,6 +179,7 @@
"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "p.ej. '30s','10m'. Unidades válidas de tiempo son 's', 'm', 'h'.",
"Edit": "Editar",
"Edit Doc": "Editar Documento",
"Edit Memory": "",
"Edit User": "Editar Usuario",
"Email": "Email",
"Embedding Batch Size": "",
@ -205,6 +209,7 @@
"Enter Serply API Key": "",
"Enter Serpstack API Key": "Ingrese la clave API de Serpstack",
"Enter stop sequence": "Ingrese la secuencia de parada",
"Enter Tavily API Key": "",
"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)",
@ -280,6 +285,7 @@
"Knowledge": "",
"Language": "Lenguaje",
"Last Active": "Última Actividad",
"Last Modified": "",
"Light": "Claro",
"Listening...": "",
"LLMs can make mistakes. Verify important information.": "Los LLM pueden cometer errores. Verifica la información importante.",
@ -304,6 +310,7 @@
"Mirostat Tau": "Mirostat Tau",
"MMMM DD, YYYY": "MMMM DD, YYYY",
"MMMM DD, YYYY HH:mm": "MMMM DD, YYYY HH:mm",
"MMMM DD, YYYY hh:mm:ss A": "",
"Model '{{modelName}}' has been successfully downloaded.": "El modelo '{{modelName}}' se ha descargado correctamente.",
"Model '{{modelTag}}' is already in queue for downloading.": "El modelo '{{modelTag}}' ya está en cola para descargar.",
"Model {{modelId}} not found": "El modelo {{modelId}} no fue encontrado",
@ -489,6 +496,8 @@
"System": "Sistema",
"System Prompt": "Prompt del sistema",
"Tags": "Etiquetas",
"Tap to interrupt": "",
"Tavily API Key": "",
"Tell us more:": "Dinos más:",
"Temperature": "Temperatura",
"Template": "Plantilla",
@ -519,6 +528,7 @@
"Today": "Hoy",
"Toggle settings": "Alternar configuración",
"Toggle sidebar": "Alternar barra lateral",
"Tokens To Keep On Context Refresh (num_keep)": "",
"Tools": "",
"Top K": "Top K",
"Top P": "Top P",
@ -530,8 +540,10 @@
"Type Hugging Face Resolve (Download) URL": "Escriba la URL (Descarga) de Hugging Face Resolve",
"Uh-oh! There was an issue connecting to {{provider}}.": "¡Uh oh! Hubo un problema al conectarse a {{provider}}.",
"Unknown File Type '{{file_type}}', but accepting and treating as plain text": "Tipo de archivo desconocido '{{file_type}}', pero se acepta y se trata como texto sin formato",
"Update": "",
"Update and Copy Link": "Actualizar y copiar enlace",
"Update password": "Actualizar contraseña",
"Updated at": "",
"Upload a GGUF model": "Subir un modelo GGUF",
"Upload Files": "Subir archivos",
"Upload Pipeline": "",

View File

@ -42,6 +42,7 @@
"Allow": "اجازه دادن",
"Allow Chat Deletion": "اجازه حذف گپ",
"Allow non-local voices": "",
"Allow User Location": "",
"alphanumeric characters and hyphens": "حروف الفبایی و خط فاصله",
"Already have an account?": "از قبل حساب کاربری دارید؟",
"an assistant": "یک دستیار",
@ -69,6 +70,7 @@
"Bad Response": "پاسخ خوب نیست",
"Banners": "بنر",
"Base Model (From)": "مدل پایه (از)",
"Batch Size (num_batch)": "",
"before": "قبل",
"Being lazy": "حالت سازنده",
"Brave Search API Key": "کلید API جستجوی شجاع",
@ -160,6 +162,7 @@
"Discover, download, and explore custom prompts": "پرامپت\u200cهای سفارشی را کشف، دانلود و کاوش کنید",
"Discover, download, and explore model presets": "پیش تنظیمات مدل را کشف، دانلود و کاوش کنید",
"Dismissible": "",
"Display Emoji in Call": "",
"Display the username instead of You in the Chat": "نمایش نام کاربری به جای «شما» در چت",
"Document": "سند",
"Document Settings": "تنظیمات سند",
@ -176,6 +179,7 @@
"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "به طور مثال '30s','10m'. واحد\u200cهای زمانی معتبر 's', 'm', 'h' هستند.",
"Edit": "ویرایش",
"Edit Doc": "ویرایش سند",
"Edit Memory": "",
"Edit User": "ویرایش کاربر",
"Email": "ایمیل",
"Embedding Batch Size": "",
@ -205,6 +209,7 @@
"Enter Serply API Key": "",
"Enter Serpstack API Key": "کلید API Serpstack را وارد کنید",
"Enter stop sequence": "توالی توقف را وارد کنید",
"Enter Tavily API Key": "",
"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)",
@ -280,6 +285,7 @@
"Knowledge": "",
"Language": "زبان",
"Last Active": "آخرین فعال",
"Last Modified": "",
"Light": "روشن",
"Listening...": "",
"LLMs can make mistakes. Verify important information.": "مدل\u200cهای زبانی بزرگ می\u200cتوانند اشتباه کنند. اطلاعات مهم را راستی\u200cآزمایی کنید.",
@ -304,6 +310,7 @@
"Mirostat Tau": "Mirostat Tau",
"MMMM DD, YYYY": "MMMM DD, YYYY",
"MMMM DD, YYYY HH:mm": "MMMM DD, YYYY HH:mm",
"MMMM DD, YYYY hh:mm:ss A": "",
"Model '{{modelName}}' has been successfully downloaded.": "مدل '{{modelName}}' با موفقیت دانلود شد.",
"Model '{{modelTag}}' is already in queue for downloading.": "مدل '{{modelTag}}' در حال حاضر در صف برای دانلود است.",
"Model {{modelId}} not found": "مدل {{modelId}} یافت نشد",
@ -488,6 +495,8 @@
"System": "سیستم",
"System Prompt": "پرامپت سیستم",
"Tags": "تگ\u200cها",
"Tap to interrupt": "",
"Tavily API Key": "",
"Tell us more:": "بیشتر بگویید:",
"Temperature": "دما",
"Template": "الگو",
@ -518,6 +527,7 @@
"Today": "امروز",
"Toggle settings": "نمایش/عدم نمایش تنظیمات",
"Toggle sidebar": "نمایش/عدم نمایش نوار کناری",
"Tokens To Keep On Context Refresh (num_keep)": "",
"Tools": "",
"Top K": "Top K",
"Top P": "Top P",
@ -529,8 +539,10 @@
"Type Hugging Face Resolve (Download) URL": "مقدار URL دانلود (Resolve) Hugging Face را وارد کنید",
"Uh-oh! There was an issue connecting to {{provider}}.": "اوه اوه! مشکلی در اتصال به {{provider}} وجود داشت.",
"Unknown File Type '{{file_type}}', but accepting and treating as plain text": "نوع فایل '{{file_type}}' ناشناخته است، به عنوان یک فایل متنی ساده با آن برخورد می شود.",
"Update": "",
"Update and Copy Link": "به روزرسانی و کپی لینک",
"Update password": "به روزرسانی رمزعبور",
"Updated at": "",
"Upload a GGUF model": "آپلود یک مدل GGUF",
"Upload Files": "بارگذاری پروندهها",
"Upload Pipeline": "",

View File

@ -42,6 +42,7 @@
"Allow": "Salli",
"Allow Chat Deletion": "Salli keskustelujen poisto",
"Allow non-local voices": "",
"Allow User Location": "",
"alphanumeric characters and hyphens": "kirjaimia, numeroita ja väliviivoja",
"Already have an account?": "Onko sinulla jo tili?",
"an assistant": "avustaja",
@ -69,6 +70,7 @@
"Bad Response": "Epäkelpo vastaus",
"Banners": "Bannerit",
"Base Model (From)": "Perusmalli (alkaen)",
"Batch Size (num_batch)": "",
"before": "ennen",
"Being lazy": "Oli laiska",
"Brave Search API Key": "Brave Search API -avain",
@ -160,6 +162,7 @@
"Discover, download, and explore custom prompts": "Löydä ja lataa mukautettuja kehotteita",
"Discover, download, and explore model presets": "Löydä ja lataa mallien esiasetuksia",
"Dismissible": "",
"Display Emoji in Call": "",
"Display the username instead of You in the Chat": "Näytä käyttäjänimi keskustelussa",
"Document": "Asiakirja",
"Document Settings": "Asiakirja-asetukset",
@ -176,6 +179,7 @@
"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "esim. '30s', '10m'. Kelpoiset aikayksiköt ovat 's', 'm', 'h'.",
"Edit": "Muokkaa",
"Edit Doc": "Muokkaa asiakirjaa",
"Edit Memory": "",
"Edit User": "Muokkaa käyttäjää",
"Email": "Sähköposti",
"Embedding Batch Size": "",
@ -205,6 +209,7 @@
"Enter Serply API Key": "",
"Enter Serpstack API Key": "Anna Serpstack API -avain",
"Enter stop sequence": "Syötä lopetussekvenssi",
"Enter Tavily API Key": "",
"Enter Top K": "Syötä Top K",
"Enter URL (e.g. http://127.0.0.1:7860/)": "Syötä URL (esim. http://127.0.0.1:7860/)",
"Enter URL (e.g. http://localhost:11434)": "Syötä URL (esim. http://localhost:11434)",
@ -280,6 +285,7 @@
"Knowledge": "",
"Language": "Kieli",
"Last Active": "Viimeksi aktiivinen",
"Last Modified": "",
"Light": "Vaalea",
"Listening...": "",
"LLMs can make mistakes. Verify important information.": "Kielimallit voivat tehdä virheitä. Varmista tärkeät tiedot.",
@ -304,6 +310,7 @@
"Mirostat Tau": "Mirostat Tau",
"MMMM DD, YYYY": "DD MMMM YYYY",
"MMMM DD, YYYY HH:mm": "DD MMMM YYYY, HH:mm",
"MMMM DD, YYYY hh:mm:ss A": "",
"Model '{{modelName}}' has been successfully downloaded.": "Malli '{{modelName}}' ladattiin onnistuneesti.",
"Model '{{modelTag}}' is already in queue for downloading.": "Malli '{{modelTag}}' on jo jonossa ladattavaksi.",
"Model {{modelId}} not found": "Mallia {{modelId}} ei löytynyt",
@ -488,6 +495,8 @@
"System": "Järjestelmä",
"System Prompt": "Järjestelmäkehote",
"Tags": "Tagit",
"Tap to interrupt": "",
"Tavily API Key": "",
"Tell us more:": "Kerro lisää:",
"Temperature": "Lämpötila",
"Template": "Malline",
@ -518,6 +527,7 @@
"Today": "Tänään",
"Toggle settings": "Kytke asetukset",
"Toggle sidebar": "Kytke sivupalkki",
"Tokens To Keep On Context Refresh (num_keep)": "",
"Tools": "",
"Top K": "Top K",
"Top P": "Top P",
@ -529,8 +539,10 @@
"Type Hugging Face Resolve (Download) URL": "Kirjoita Hugging Face -resolve-osoite",
"Uh-oh! There was an issue connecting to {{provider}}.": "Voi ei! Yhteysongelma {{provider}}:n kanssa.",
"Unknown File Type '{{file_type}}', but accepting and treating as plain text": "Tuntematon tiedostotyyppi '{{file_type}}', mutta hyväksytään ja käsitellään pelkkänä tekstinä",
"Update": "",
"Update and Copy Link": "Päivitä ja kopioi linkki",
"Update password": "Päivitä salasana",
"Updated at": "",
"Upload a GGUF model": "Lataa GGUF-malli",
"Upload Files": "Lataa tiedostoja",
"Upload Pipeline": "",

View File

@ -42,6 +42,7 @@
"Allow": "Autoriser",
"Allow Chat Deletion": "Autoriser la suppression des discussions",
"Allow non-local voices": "",
"Allow User Location": "",
"alphanumeric characters and hyphens": "caractères alphanumériques et tirets",
"Already have an account?": "Vous avez déjà un compte ?",
"an assistant": "un assistant",
@ -69,6 +70,7 @@
"Bad Response": "Mauvaise réponse",
"Banners": "Bannières",
"Base Model (From)": "Modèle de base (à partir de)",
"Batch Size (num_batch)": "",
"before": "avant",
"Being lazy": "En manque de temps",
"Brave Search API Key": "Clé dAPI de recherche brave",
@ -160,6 +162,7 @@
"Discover, download, and explore custom prompts": "Découvrir, télécharger et explorer des prompts personnalisés",
"Discover, download, and explore model presets": "Découvrir, télécharger et explorer des préconfigurations de modèles",
"Dismissible": "",
"Display Emoji in Call": "",
"Display the username instead of You in the Chat": "Afficher le nom d'utilisateur au lieu de 'Vous' dans la Discussion",
"Document": "Document",
"Document Settings": "Paramètres du document",
@ -176,6 +179,7 @@
"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "p. ex. '30s', '10m'. Les unités de temps valides sont 's', 'm', 'h'.",
"Edit": "Éditer",
"Edit Doc": "Éditer le document",
"Edit Memory": "",
"Edit User": "Éditer l'utilisateur",
"Email": "Email",
"Embedding Batch Size": "",
@ -205,6 +209,7 @@
"Enter Serply API Key": "",
"Enter Serpstack API Key": "Entrez dans la clé API Serpstack",
"Enter stop sequence": "Entrez la séquence de fin",
"Enter Tavily API Key": "",
"Enter Top K": "Entrez Top K",
"Enter URL (e.g. http://127.0.0.1:7860/)": "Entrez l'URL (p. ex. http://127.0.0.1:7860/)",
"Enter URL (e.g. http://localhost:11434)": "Entrez l'URL (p. ex. http://localhost:11434)",
@ -280,6 +285,7 @@
"Knowledge": "",
"Language": "Langue",
"Last Active": "Dernière activité",
"Last Modified": "",
"Light": "Lumière",
"Listening...": "",
"LLMs can make mistakes. Verify important information.": "Les LLMs peuvent faire des erreurs. Vérifiez les informations importantes.",
@ -304,6 +310,7 @@
"Mirostat Tau": "Mirostat Tau",
"MMMM DD, YYYY": "MMMM DD, YYYY",
"MMMM DD, YYYY HH:mm": "MMMM DD, YYYY HH:mm",
"MMMM DD, YYYY hh:mm:ss A": "",
"Model '{{modelName}}' has been successfully downloaded.": "Le modèle '{{modelName}}' a été téléchargé avec succès.",
"Model '{{modelTag}}' is already in queue for downloading.": "Le modèle '{{modelTag}}' est déjà dans la file d'attente pour le téléchargement.",
"Model {{modelId}} not found": "Modèle {{modelId}} non trouvé",
@ -489,6 +496,8 @@
"System": "Système",
"System Prompt": "Prompt Système",
"Tags": "Tags",
"Tap to interrupt": "",
"Tavily API Key": "",
"Tell us more:": "Donnez-nous plus:",
"Temperature": "Température",
"Template": "Modèle",
@ -519,6 +528,7 @@
"Today": "Aujourd'hui",
"Toggle settings": "Basculer les paramètres",
"Toggle sidebar": "Basculer la barre latérale",
"Tokens To Keep On Context Refresh (num_keep)": "",
"Tools": "",
"Top K": "Top K",
"Top P": "Top P",
@ -530,8 +540,10 @@
"Type Hugging Face Resolve (Download) URL": "Entrez l'URL de résolution (téléchargement) Hugging Face",
"Uh-oh! There was an issue connecting to {{provider}}.": "Uh-oh ! Il y a eu un problème de connexion à {{provider}}.",
"Unknown File Type '{{file_type}}', but accepting and treating as plain text": "Type de fichier inconnu '{{file_type}}', mais accepté et traité comme du texte brut",
"Update": "",
"Update and Copy Link": "Mettre à jour et copier le lien",
"Update password": "Mettre à jour le mot de passe",
"Updated at": "",
"Upload a GGUF model": "Téléverser un modèle GGUF",
"Upload Files": "Téléverser des fichiers",
"Upload Pipeline": "",

View File

@ -42,6 +42,7 @@
"Allow": "Autoriser",
"Allow Chat Deletion": "Autoriser la suppression du chat",
"Allow non-local voices": "",
"Allow User Location": "",
"alphanumeric characters and hyphens": "caractères alphanumériques et tirets",
"Already have an account?": "Vous avez déjà un compte ?",
"an assistant": "un assistant",
@ -69,6 +70,7 @@
"Bad Response": "Mauvaise Réponse",
"Banners": "Bannières",
"Base Model (From)": "Modèle de Base (De)",
"Batch Size (num_batch)": "",
"before": "avant",
"Being lazy": "Est paresseux",
"Brave Search API Key": "Clé API Brave Search",
@ -160,6 +162,7 @@
"Discover, download, and explore custom prompts": "Découvrir, télécharger et explorer des prompts personnalisés",
"Discover, download, and explore model presets": "Découvrir, télécharger et explorer des préconfigurations de modèles",
"Dismissible": "",
"Display Emoji in Call": "",
"Display the username instead of You in the Chat": "Afficher le nom d'utilisateur au lieu de 'Vous' dans le Chat",
"Document": "Document",
"Document Settings": "Paramètres du document",
@ -176,6 +179,7 @@
"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "par ex. '30s', '10m'. Les unités de temps valides sont 's', 'm', 'h'.",
"Edit": "Éditer",
"Edit Doc": "Éditer le document",
"Edit Memory": "",
"Edit User": "Éditer l'utilisateur",
"Email": "Email",
"Embedding Batch Size": "",
@ -205,6 +209,7 @@
"Enter Serply API Key": "",
"Enter Serpstack API Key": "Entrez la clé API Serpstack",
"Enter stop sequence": "Entrez la séquence de fin",
"Enter Tavily API Key": "",
"Enter Top K": "Entrez Top K",
"Enter URL (e.g. http://127.0.0.1:7860/)": "Entrez l'URL (p. ex. http://127.0.0.1:7860/)",
"Enter URL (e.g. http://localhost:11434)": "Entrez l'URL (p. ex. http://localhost:11434)",
@ -280,6 +285,7 @@
"Knowledge": "",
"Language": "Langue",
"Last Active": "Dernier Activité",
"Last Modified": "",
"Light": "Clair",
"Listening...": "",
"LLMs can make mistakes. Verify important information.": "Les LLMs peuvent faire des erreurs. Vérifiez les informations importantes.",
@ -304,6 +310,7 @@
"Mirostat Tau": "Mirostat Tau",
"MMMM DD, YYYY": "MMMM DD, YYYY",
"MMMM DD, YYYY HH:mm": "MMMM DD, YYYY HH:mm",
"MMMM DD, YYYY hh:mm:ss A": "",
"Model '{{modelName}}' has been successfully downloaded.": "Le modèle '{{modelName}}' a été téléchargé avec succès.",
"Model '{{modelTag}}' is already in queue for downloading.": "Le modèle '{{modelTag}}' est déjà dans la file d'attente pour le téléchargement.",
"Model {{modelId}} not found": "Modèle {{modelId}} non trouvé",
@ -489,6 +496,8 @@
"System": "Système",
"System Prompt": "Prompt du Système",
"Tags": "Tags",
"Tap to interrupt": "",
"Tavily API Key": "",
"Tell us more:": "Dites-nous en plus :",
"Temperature": "Température",
"Template": "Modèle",
@ -519,6 +528,7 @@
"Today": "Aujourd'hui",
"Toggle settings": "Basculer les paramètres",
"Toggle sidebar": "Basculer la barre latérale",
"Tokens To Keep On Context Refresh (num_keep)": "",
"Tools": "",
"Top K": "Top K",
"Top P": "Top P",
@ -530,8 +540,10 @@
"Type Hugging Face Resolve (Download) URL": "Entrez l'URL de Résolution (Téléchargement) Hugging Face",
"Uh-oh! There was an issue connecting to {{provider}}.": "Uh-oh ! Il y a eu un problème de connexion à {{provider}}.",
"Unknown File Type '{{file_type}}', but accepting and treating as plain text": "Type de Fichier Inconnu '{{file_type}}', mais accepté et traité comme du texte brut",
"Update": "",
"Update and Copy Link": "Mettre à Jour et Copier le Lien",
"Update password": "Mettre à Jour le Mot de Passe",
"Updated at": "",
"Upload a GGUF model": "Téléverser un modèle GGUF",
"Upload Files": "Téléverser des fichiers",
"Upload Pipeline": "",

View File

@ -42,6 +42,7 @@
"Allow": "אפשר",
"Allow Chat Deletion": "אפשר מחיקת צ'אט",
"Allow non-local voices": "",
"Allow User Location": "",
"alphanumeric characters and hyphens": "תווים אלפאנומריים ומקפים",
"Already have an account?": "כבר יש לך חשבון?",
"an assistant": "עוזר",
@ -69,6 +70,7 @@
"Bad Response": "תגובה שגויה",
"Banners": "באנרים",
"Base Model (From)": "דגם בסיס (מ)",
"Batch Size (num_batch)": "",
"before": "לפני",
"Being lazy": "להיות עצלן",
"Brave Search API Key": "מפתח API של חיפוש אמיץ",
@ -160,6 +162,7 @@
"Discover, download, and explore custom prompts": "גלה, הורד, וחקור פקודות מותאמות אישית",
"Discover, download, and explore model presets": "גלה, הורד, וחקור הגדרות מודל מוגדרות מראש",
"Dismissible": "",
"Display Emoji in Call": "",
"Display the username instead of You in the Chat": "הצג את שם המשתמש במקום 'אתה' בצ'אט",
"Document": "מסמך",
"Document Settings": "הגדרות מסמך",
@ -176,6 +179,7 @@
"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "למשל '30s', '10m'. יחידות זמן חוקיות הן 's', 'm', 'h'.",
"Edit": "ערוך",
"Edit Doc": "ערוך מסמך",
"Edit Memory": "",
"Edit User": "ערוך משתמש",
"Email": "דוא\"ל",
"Embedding Batch Size": "",
@ -205,6 +209,7 @@
"Enter Serply API Key": "",
"Enter Serpstack API Key": "הזן מפתח API של Serpstack",
"Enter stop sequence": "הזן רצף עצירה",
"Enter Tavily API Key": "",
"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)",
@ -280,6 +285,7 @@
"Knowledge": "",
"Language": "שפה",
"Last Active": "פעיל לאחרונה",
"Last Modified": "",
"Light": "בהיר",
"Listening...": "",
"LLMs can make mistakes. Verify important information.": "מודלים בשפה טבעית יכולים לטעות. אמת מידע חשוב.",
@ -304,6 +310,7 @@
"Mirostat Tau": "Mirostat Tau",
"MMMM DD, YYYY": "DD בMMMM, YYYY",
"MMMM DD, YYYY HH:mm": "DD בMMMM, YYYY HH:mm",
"MMMM DD, YYYY hh:mm:ss A": "",
"Model '{{modelName}}' has been successfully downloaded.": "המודל '{{modelName}}' הורד בהצלחה.",
"Model '{{modelTag}}' is already in queue for downloading.": "המודל '{{modelTag}}' כבר בתור להורדה.",
"Model {{modelId}} not found": "המודל {{modelId}} לא נמצא",
@ -489,6 +496,8 @@
"System": "מערכת",
"System Prompt": "תגובת מערכת",
"Tags": "תגיות",
"Tap to interrupt": "",
"Tavily API Key": "",
"Tell us more:": "תרשמו יותר:",
"Temperature": "טמפרטורה",
"Template": "תבנית",
@ -519,6 +528,7 @@
"Today": "היום",
"Toggle settings": "החלפת מצב של הגדרות",
"Toggle sidebar": "החלפת מצב של סרגל הצד",
"Tokens To Keep On Context Refresh (num_keep)": "",
"Tools": "",
"Top K": "Top K",
"Top P": "Top P",
@ -530,8 +540,10 @@
"Type Hugging Face Resolve (Download) URL": "הקלד כתובת URL של פתרון פנים מחבק (הורד)",
"Uh-oh! There was an issue connecting to {{provider}}.": "או-הו! אירעה בעיה בהתחברות ל- {{provider}}.",
"Unknown File Type '{{file_type}}', but accepting and treating as plain text": "סוג קובץ לא ידוע '{{file_type}}', אך מקבל ומתייחס אליו כטקסט רגיל",
"Update": "",
"Update and Copy Link": "עדכן ושכפל קישור",
"Update password": "עדכן סיסמה",
"Updated at": "",
"Upload a GGUF model": "העלה מודל GGUF",
"Upload Files": "העלאת קבצים",
"Upload Pipeline": "",

View File

@ -42,6 +42,7 @@
"Allow": "अनुमति दें",
"Allow Chat Deletion": "चैट हटाने की अनुमति दें",
"Allow non-local voices": "",
"Allow User Location": "",
"alphanumeric characters and hyphens": "अल्फ़ान्यूमेरिक वर्ण और हाइफ़न",
"Already have an account?": "क्या आपके पास पहले से एक खाता मौजूद है?",
"an assistant": "एक सहायक",
@ -69,6 +70,7 @@
"Bad Response": "ख़राब प्रतिक्रिया",
"Banners": "बैनर",
"Base Model (From)": "बेस मॉडल (से)",
"Batch Size (num_batch)": "",
"before": "पहले",
"Being lazy": "आलसी होना",
"Brave Search API Key": "Brave सर्च एपीआई कुंजी",
@ -160,6 +162,7 @@
"Discover, download, and explore custom prompts": "कस्टम प्रॉम्प्ट को खोजें, डाउनलोड करें और एक्सप्लोर करें",
"Discover, download, and explore model presets": "मॉडल प्रीसेट खोजें, डाउनलोड करें और एक्सप्लोर करें",
"Dismissible": "",
"Display Emoji in Call": "",
"Display the username instead of You in the Chat": "चैट में 'आप' के स्थान पर उपयोगकर्ता नाम प्रदर्शित करें",
"Document": "दस्तावेज़",
"Document Settings": "दस्तावेज़ सेटिंग्स",
@ -176,6 +179,7 @@
"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "जैसे '30s', '10m', मान्य समय इकाइयाँ 's', 'm', 'h' हैं।",
"Edit": "संपादित करें",
"Edit Doc": "दस्तावेज़ संपादित करें",
"Edit Memory": "",
"Edit User": "यूजर को संपादित करो",
"Email": "ईमेल",
"Embedding Batch Size": "",
@ -205,6 +209,7 @@
"Enter Serply API Key": "",
"Enter Serpstack API Key": "सर्पस्टैक एपीआई कुंजी दर्ज करें",
"Enter stop sequence": "स्टॉप अनुक्रम दर्ज करें",
"Enter Tavily API Key": "",
"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)",
@ -280,6 +285,7 @@
"Knowledge": "",
"Language": "भाषा",
"Last Active": "पिछली बार सक्रिय",
"Last Modified": "",
"Light": "सुन",
"Listening...": "",
"LLMs can make mistakes. Verify important information.": "एलएलएम गलतियाँ कर सकते हैं। महत्वपूर्ण जानकारी सत्यापित करें.",
@ -304,6 +310,7 @@
"Mirostat Tau": "मिरोस्तात ताऊ",
"MMMM DD, YYYY": "MMMM DD, YYYY",
"MMMM DD, YYYY HH:mm": "MMMM DD, YYYY HH:mm",
"MMMM DD, YYYY hh:mm:ss A": "",
"Model '{{modelName}}' has been successfully downloaded.": "मॉडल '{{modelName}}' सफलतापूर्वक डाउनलोड हो गया है।",
"Model '{{modelTag}}' is already in queue for downloading.": "मॉडल '{{modelTag}}' पहले से ही डाउनलोड करने के लिए कतार में है।",
"Model {{modelId}} not found": "मॉडल {{modelId}} नहीं मिला",
@ -488,6 +495,8 @@
"System": "सिस्टम",
"System Prompt": "सिस्टम प्रॉम्प्ट",
"Tags": "टैग",
"Tap to interrupt": "",
"Tavily API Key": "",
"Tell us more:": "हमें और अधिक बताएँ:",
"Temperature": "टेंपेरेचर",
"Template": "टेम्पलेट",
@ -518,6 +527,7 @@
"Today": "आज",
"Toggle settings": "सेटिंग्स टॉगल करें",
"Toggle sidebar": "साइडबार टॉगल करें",
"Tokens To Keep On Context Refresh (num_keep)": "",
"Tools": "",
"Top K": "शीर्ष K",
"Top P": "शीर्ष P",
@ -529,8 +539,10 @@
"Type Hugging Face Resolve (Download) URL": "हगिंग फेस रिज़ॉल्व (डाउनलोड) यूआरएल टाइप करें",
"Uh-oh! There was an issue connecting to {{provider}}.": "उह ओह! {{provider}} से कनेक्ट करने में एक समस्या थी।",
"Unknown File Type '{{file_type}}', but accepting and treating as plain text": "अज्ञात फ़ाइल प्रकार '{{file_type}}', लेकिन स्वीकार करना और सादे पाठ के रूप में व्यवहार करना",
"Update": "",
"Update and Copy Link": "अपडेट करें और लिंक कॉपी करें",
"Update password": "पासवर्ड अपडेट करें",
"Updated at": "",
"Upload a GGUF model": "GGUF मॉडल अपलोड करें",
"Upload Files": "फ़ाइलें अपलोड करें",
"Upload Pipeline": "",

View File

@ -42,6 +42,7 @@
"Allow": "Dopusti",
"Allow Chat Deletion": "Dopusti brisanje razgovora",
"Allow non-local voices": "Dopusti nelokalne glasove",
"Allow User Location": "",
"alphanumeric characters and hyphens": "alfanumerički znakovi i crtice",
"Already have an account?": "Već imate račun?",
"an assistant": "asistent",
@ -69,6 +70,7 @@
"Bad Response": "Loš odgovor",
"Banners": "Baneri",
"Base Model (From)": "Osnovni model (Od)",
"Batch Size (num_batch)": "",
"before": "prije",
"Being lazy": "Biti lijen",
"Brave Search API Key": "Brave tražilica - API ključ",
@ -160,6 +162,7 @@
"Discover, download, and explore custom prompts": "Otkrijte, preuzmite i istražite prilagođene prompte",
"Discover, download, and explore model presets": "Otkrijte, preuzmite i istražite unaprijed postavljene modele",
"Dismissible": "Odbaciti",
"Display Emoji in Call": "",
"Display the username instead of You in the Chat": "Prikaži korisničko ime umjesto Vas u razgovoru",
"Document": "Dokument",
"Document Settings": "Postavke dokumenta",
@ -176,6 +179,7 @@
"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "npr. '30s','10m'. Važeće vremenske jedinice su 's', 'm', 'h'.",
"Edit": "Uredi",
"Edit Doc": "Uredi dokument",
"Edit Memory": "",
"Edit User": "Uredi korisnika",
"Email": "Email",
"Embedding Batch Size": "Embedding - Veličina batch-a",
@ -205,6 +209,7 @@
"Enter Serply API Key": "Unesite Serply API ključ",
"Enter Serpstack API Key": "Unesite Serpstack API ključ",
"Enter stop sequence": "Unesite sekvencu zaustavljanja",
"Enter Tavily API Key": "",
"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)",
@ -280,6 +285,7 @@
"Knowledge": "Znanje",
"Language": "Jezik",
"Last Active": "Zadnja aktivnost",
"Last Modified": "",
"Light": "Svijetlo",
"Listening...": "Slušam...",
"LLMs can make mistakes. Verify important information.": "LLM-ovi mogu pogriješiti. Provjerite važne informacije.",
@ -304,6 +310,7 @@
"Mirostat Tau": "Mirostat Tau",
"MMMM DD, YYYY": "MMMM DD, YYYY",
"MMMM DD, YYYY HH:mm": "MMMM DD, YYYY HH:mm",
"MMMM DD, YYYY hh:mm:ss A": "",
"Model '{{modelName}}' has been successfully downloaded.": "Model '{{modelName}}' je uspješno preuzet.",
"Model '{{modelTag}}' is already in queue for downloading.": "Model '{{modelTag}}' je već u redu za preuzimanje.",
"Model {{modelId}} not found": "Model {{modelId}} nije pronađen",
@ -489,6 +496,8 @@
"System": "Sustav",
"System Prompt": "Sistemski prompt",
"Tags": "Oznake",
"Tap to interrupt": "",
"Tavily API Key": "",
"Tell us more:": "Recite nam više:",
"Temperature": "Temperatura",
"Template": "Predložak",
@ -519,6 +528,7 @@
"Today": "Danas",
"Toggle settings": "Prebaci postavke",
"Toggle sidebar": "Prebaci bočnu traku",
"Tokens To Keep On Context Refresh (num_keep)": "",
"Tools": "Alati",
"Top K": "Top K",
"Top P": "Top P",
@ -530,8 +540,10 @@
"Type Hugging Face Resolve (Download) URL": "Upišite Hugging Face Resolve (Download) URL",
"Uh-oh! There was an issue connecting to {{provider}}.": "Uh-oh! Pojavio se problem s povezivanjem na {{provider}}.",
"Unknown File Type '{{file_type}}', but accepting and treating as plain text": "Nepoznata vrsta datoteke '{{file_type}}', ali prihvaćena i obrađuje se kao običan tekst",
"Update": "",
"Update and Copy Link": "Ažuriraj i kopiraj vezu",
"Update password": "Ažuriraj lozinku",
"Updated at": "",
"Upload a GGUF model": "Učitaj GGUF model",
"Upload Files": "Prijenos datoteka",
"Upload Pipeline": "Prijenos kanala",

View File

@ -42,6 +42,7 @@
"Allow": "Consenti",
"Allow Chat Deletion": "Consenti l'eliminazione della chat",
"Allow non-local voices": "",
"Allow User Location": "",
"alphanumeric characters and hyphens": "caratteri alfanumerici e trattini",
"Already have an account?": "Hai già un account?",
"an assistant": "un assistente",
@ -69,6 +70,7 @@
"Bad Response": "Risposta non valida",
"Banners": "Banner",
"Base Model (From)": "Modello base (da)",
"Batch Size (num_batch)": "",
"before": "prima",
"Being lazy": "Essere pigri",
"Brave Search API Key": "Chiave API di ricerca Brave",
@ -160,6 +162,7 @@
"Discover, download, and explore custom prompts": "Scopri, scarica ed esplora prompt personalizzati",
"Discover, download, and explore model presets": "Scopri, scarica ed esplora i preset del modello",
"Dismissible": "",
"Display Emoji in Call": "",
"Display the username instead of You in the Chat": "Visualizza il nome utente invece di Tu nella chat",
"Document": "Documento",
"Document Settings": "Impostazioni documento",
@ -176,6 +179,7 @@
"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "ad esempio '30s','10m'. Le unità di tempo valide sono 's', 'm', 'h'.",
"Edit": "Modifica",
"Edit Doc": "Modifica documento",
"Edit Memory": "",
"Edit User": "Modifica utente",
"Email": "Email",
"Embedding Batch Size": "",
@ -205,6 +209,7 @@
"Enter Serply API Key": "",
"Enter Serpstack API Key": "Inserisci la chiave API Serpstack",
"Enter stop sequence": "Inserisci la sequenza di arresto",
"Enter Tavily API Key": "",
"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)",
@ -280,6 +285,7 @@
"Knowledge": "",
"Language": "Lingua",
"Last Active": "Ultima attività",
"Last Modified": "",
"Light": "Chiaro",
"Listening...": "",
"LLMs can make mistakes. Verify important information.": "Gli LLM possono commettere errori. Verifica le informazioni importanti.",
@ -304,6 +310,7 @@
"Mirostat Tau": "Mirostat Tau",
"MMMM DD, YYYY": "MMMM DD, YYYY",
"MMMM DD, YYYY HH:mm": "MMMM DD, YYYY HH:mm",
"MMMM DD, YYYY hh:mm:ss A": "",
"Model '{{modelName}}' has been successfully downloaded.": "Il modello '{{modelName}}' è stato scaricato con successo.",
"Model '{{modelTag}}' is already in queue for downloading.": "Il modello '{{modelTag}}' è già in coda per il download.",
"Model {{modelId}} not found": "Modello {{modelId}} non trovato",
@ -489,6 +496,8 @@
"System": "Sistema",
"System Prompt": "Prompt di sistema",
"Tags": "Tag",
"Tap to interrupt": "",
"Tavily API Key": "",
"Tell us more:": "Raccontaci di più:",
"Temperature": "Temperatura",
"Template": "Modello",
@ -519,6 +528,7 @@
"Today": "Oggi",
"Toggle settings": "Attiva/disattiva impostazioni",
"Toggle sidebar": "Attiva/disattiva barra laterale",
"Tokens To Keep On Context Refresh (num_keep)": "",
"Tools": "",
"Top K": "Top K",
"Top P": "Top P",
@ -530,8 +540,10 @@
"Type Hugging Face Resolve (Download) URL": "Digita l'URL di Hugging Face Resolve (Download)",
"Uh-oh! There was an issue connecting to {{provider}}.": "Uh-oh! Si è verificato un problema durante la connessione a {{provider}}.",
"Unknown File Type '{{file_type}}', but accepting and treating as plain text": "Tipo di file sconosciuto '{{file_type}}', ma accettato e trattato come testo normale",
"Update": "",
"Update and Copy Link": "Aggiorna e copia link",
"Update password": "Aggiorna password",
"Updated at": "",
"Upload a GGUF model": "Carica un modello GGUF",
"Upload Files": "Carica file",
"Upload Pipeline": "",

View File

@ -42,6 +42,7 @@
"Allow": "許可",
"Allow Chat Deletion": "チャットの削除を許可",
"Allow non-local voices": "",
"Allow User Location": "",
"alphanumeric characters and hyphens": "英数字とハイフン",
"Already have an account?": "すでにアカウントをお持ちですか?",
"an assistant": "アシスタント",
@ -69,6 +70,7 @@
"Bad Response": "応答が悪い",
"Banners": "バナー",
"Base Model (From)": "ベースモデル(From)",
"Batch Size (num_batch)": "",
"before": "より前",
"Being lazy": "怠惰な",
"Brave Search API Key": "Brave Search APIキー",
@ -160,6 +162,7 @@
"Discover, download, and explore custom prompts": "カスタムプロンプトを見つけて、ダウンロードして、探索",
"Discover, download, and explore model presets": "モデルプリセットを見つけて、ダウンロードして、探索",
"Dismissible": "",
"Display Emoji in Call": "",
"Display the username instead of You in the Chat": "チャットで「あなた」の代わりにユーザー名を表示",
"Document": "ドキュメント",
"Document Settings": "ドキュメント設定",
@ -176,6 +179,7 @@
"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "例: '30秒'、'10分'。有効な時間単位は '秒'、'分'、'時間' です。",
"Edit": "編集",
"Edit Doc": "ドキュメントを編集",
"Edit Memory": "",
"Edit User": "ユーザーを編集",
"Email": "メールアドレス",
"Embedding Batch Size": "",
@ -205,6 +209,7 @@
"Enter Serply API Key": "",
"Enter Serpstack API Key": "Serpstack APIキーの入力",
"Enter stop sequence": "ストップシーケンスを入力してください",
"Enter Tavily API Key": "",
"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)",
@ -280,6 +285,7 @@
"Knowledge": "",
"Language": "言語",
"Last Active": "最終アクティブ",
"Last Modified": "",
"Light": "ライト",
"Listening...": "",
"LLMs can make mistakes. Verify important information.": "LLM は間違いを犯す可能性があります。重要な情報を検証してください。",
@ -304,6 +310,7 @@
"Mirostat Tau": "ミロスタット Tau",
"MMMM DD, YYYY": "MMMM DD, YYYY",
"MMMM DD, YYYY HH:mm": "MMMM DD, YYYY HH:mm",
"MMMM DD, YYYY hh:mm:ss A": "",
"Model '{{modelName}}' has been successfully downloaded.": "モデル '{{modelName}}' が正常にダウンロードされました。",
"Model '{{modelTag}}' is already in queue for downloading.": "モデル '{{modelTag}}' はすでにダウンロード待ち行列に入っています。",
"Model {{modelId}} not found": "モデル {{modelId}} が見つかりません",
@ -487,6 +494,8 @@
"System": "システム",
"System Prompt": "システムプロンプト",
"Tags": "タグ",
"Tap to interrupt": "",
"Tavily API Key": "",
"Tell us more:": "もっと話してください:",
"Temperature": "温度",
"Template": "テンプレート",
@ -517,6 +526,7 @@
"Today": "今日",
"Toggle settings": "設定を切り替え",
"Toggle sidebar": "サイドバーを切り替え",
"Tokens To Keep On Context Refresh (num_keep)": "",
"Tools": "",
"Top K": "トップ K",
"Top P": "トップ P",
@ -528,8 +538,10 @@
"Type Hugging Face Resolve (Download) URL": "Hugging Face Resolve (ダウンロード) URL を入力してください",
"Uh-oh! There was an issue connecting to {{provider}}.": "おっと! {{provider}} への接続に問題が発生しました。",
"Unknown File Type '{{file_type}}', but accepting and treating as plain text": "不明なファイルタイプ '{{file_type}}' ですが、プレーンテキストとして受け入れて処理します",
"Update": "",
"Update and Copy Link": "リンクの更新とコピー",
"Update password": "パスワードを更新",
"Updated at": "",
"Upload a GGUF model": "GGUF モデルをアップロード",
"Upload Files": "ファイルのアップロード",
"Upload Pipeline": "",

View File

@ -42,6 +42,7 @@
"Allow": "ნების დართვა",
"Allow Chat Deletion": "მიმოწერის წაშლის დაშვება",
"Allow non-local voices": "",
"Allow User Location": "",
"alphanumeric characters and hyphens": "ალფანუმერული სიმბოლოები და დეფისები",
"Already have an account?": "უკვე გაქვს ანგარიში?",
"an assistant": "ასისტენტი",
@ -69,6 +70,7 @@
"Bad Response": "ხარვეზი",
"Banners": "რეკლამა",
"Base Model (From)": "საბაზო მოდელი (-დან)",
"Batch Size (num_batch)": "",
"before": "ადგილზე",
"Being lazy": "ჩაიტყვევა",
"Brave Search API Key": "Brave Search API გასაღები",
@ -160,6 +162,7 @@
"Discover, download, and explore custom prompts": "აღმოაჩინეთ, ჩამოტვირთეთ და შეისწავლეთ მორგებული მოთხოვნები",
"Discover, download, and explore model presets": "აღმოაჩინეთ, ჩამოტვირთეთ და შეისწავლეთ მოდელის წინასწარ პარამეტრები",
"Dismissible": "",
"Display Emoji in Call": "",
"Display the username instead of You in the Chat": "ჩატში აჩვენე მომხმარებლის სახელი თქვენს ნაცვლად",
"Document": "დოკუმენტი",
"Document Settings": "დოკუმენტის პარამეტრები",
@ -176,6 +179,7 @@
"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "მაგალითად, '30წ', '10მ'. მოქმედი დროის ერთეულები: 'წ', 'წთ', 'სთ'.",
"Edit": "რედაქტირება",
"Edit Doc": "დოკუმენტის ედიტირება",
"Edit Memory": "",
"Edit User": "მომხმარებლის ედიტირება",
"Email": "ელ-ფოსტა",
"Embedding Batch Size": "",
@ -205,6 +209,7 @@
"Enter Serply API Key": "",
"Enter Serpstack API Key": "შეიყვანეთ Serpstack API Key",
"Enter stop sequence": "შეიყვანეთ ტოპ თანმიმდევრობა",
"Enter Tavily API Key": "",
"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)",
@ -280,6 +285,7 @@
"Knowledge": "",
"Language": "ენა",
"Last Active": "ბოლო აქტიური",
"Last Modified": "",
"Light": "მსუბუქი",
"Listening...": "",
"LLMs can make mistakes. Verify important information.": "შესაძლოა LLM-ებმა შეცდომები დაუშვან. გადაამოწმეთ მნიშვნელოვანი ინფორმაცია.",
@ -304,6 +310,7 @@
"Mirostat Tau": "მიროსტატი ტაუ",
"MMMM DD, YYYY": "თვე დღე, წელი",
"MMMM DD, YYYY HH:mm": "თვე დღე, წელი HH:mm",
"MMMM DD, YYYY hh:mm:ss A": "",
"Model '{{modelName}}' has been successfully downloaded.": "მოდელი „{{modelName}}“ წარმატებით ჩამოიტვირთა.",
"Model '{{modelTag}}' is already in queue for downloading.": "მოდელი „{{modelTag}}“ უკვე ჩამოტვირთვის რიგშია.",
"Model {{modelId}} not found": "მოდელი {{modelId}} ვერ მოიძებნა",
@ -488,6 +495,8 @@
"System": "სისტემა",
"System Prompt": "სისტემური მოთხოვნა",
"Tags": "ტეგები",
"Tap to interrupt": "",
"Tavily API Key": "",
"Tell us more:": "ჩვენთან დავუკავშირდით",
"Temperature": "ტემპერატურა",
"Template": "შაბლონი",
@ -518,6 +527,7 @@
"Today": "დღეს",
"Toggle settings": "პარამეტრების გადართვა",
"Toggle sidebar": "გვერდითი ზოლის გადართვა",
"Tokens To Keep On Context Refresh (num_keep)": "",
"Tools": "",
"Top K": "ტოპ K",
"Top P": "ტოპ P",
@ -529,8 +539,10 @@
"Type Hugging Face Resolve (Download) URL": "სცადე გადმოწერო Hugging Face Resolve URL",
"Uh-oh! There was an issue connecting to {{provider}}.": "{{provider}}-თან დაკავშირების პრობლემა წარმოიშვა.",
"Unknown File Type '{{file_type}}', but accepting and treating as plain text": "უცნობი ფაილის ტიპი „{{file_type}}“, მაგრამ მიიღება და განიხილება როგორც მარტივი ტექსტი",
"Update": "",
"Update and Copy Link": "განახლება და ბმულის კოპირება",
"Update password": "პაროლის განახლება",
"Updated at": "",
"Upload a GGUF model": "GGUF მოდელის ატვირთვა",
"Upload Files": "ატვირთეთ ფაილები",
"Upload Pipeline": "",

View File

@ -42,6 +42,7 @@
"Allow": "허용",
"Allow Chat Deletion": "채팅 삭제 허용",
"Allow non-local voices": "",
"Allow User Location": "",
"alphanumeric characters and hyphens": "영문자,숫자 및 하이픈",
"Already have an account?": "이미 계정이 있으신가요?",
"an assistant": "어시스턴트",
@ -69,6 +70,7 @@
"Bad Response": "응답이 좋지 않습니다.",
"Banners": "배너",
"Base Model (From)": "기본 모델(시작)",
"Batch Size (num_batch)": "",
"before": "이전",
"Being lazy": "게으름 피우기",
"Brave Search API Key": "Brave Search API 키",
@ -160,6 +162,7 @@
"Discover, download, and explore custom prompts": "사용자 정의 프롬프트 검색, 다운로드 및 탐색",
"Discover, download, and explore model presets": "모델 사전 설정 검색, 다운로드 및 탐색",
"Dismissible": "",
"Display Emoji in Call": "",
"Display the username instead of You in the Chat": "채팅에서 'You' 대신 사용자 이름 표시",
"Document": "문서",
"Document Settings": "문서 설정",
@ -176,6 +179,7 @@
"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "예: '30s','10m'. 유효한 시간 단위는 's', 'm', 'h'입니다.",
"Edit": "편집",
"Edit Doc": "문서 편집",
"Edit Memory": "",
"Edit User": "사용자 편집",
"Email": "이메일",
"Embedding Batch Size": "",
@ -205,6 +209,7 @@
"Enter Serply API Key": "",
"Enter Serpstack API Key": "Serpstack API Key 입력",
"Enter stop sequence": "중지 시퀀스 입력",
"Enter Tavily API Key": "",
"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)",
@ -280,6 +285,7 @@
"Knowledge": "",
"Language": "언어",
"Last Active": "최근 활동",
"Last Modified": "",
"Light": "밝음",
"Listening...": "",
"LLMs can make mistakes. Verify important information.": "LLM은 실수를 할 수 있습니다. 중요한 정보를 확인하세요.",
@ -304,6 +310,7 @@
"Mirostat Tau": "Mirostat Tau",
"MMMM DD, YYYY": "MMMM DD, YYYY",
"MMMM DD, YYYY HH:mm": "MMMM DD, YYYY HH:mm",
"MMMM DD, YYYY hh:mm:ss A": "",
"Model '{{modelName}}' has been successfully downloaded.": "모델 '{{modelName}}'이(가) 성공적으로 다운로드되었습니다.",
"Model '{{modelTag}}' is already in queue for downloading.": "모델 '{{modelTag}}'이(가) 이미 다운로드 대기열에 있습니다.",
"Model {{modelId}} not found": "모델 {{modelId}}를 찾을 수 없습니다.",
@ -487,6 +494,8 @@
"System": "시스템",
"System Prompt": "시스템 프롬프트",
"Tags": "Tags",
"Tap to interrupt": "",
"Tavily API Key": "",
"Tell us more:": "더 알려주세요:",
"Temperature": "Temperature",
"Template": "Template",
@ -517,6 +526,7 @@
"Today": "오늘",
"Toggle settings": "설정 전환",
"Toggle sidebar": "사이드바 전환",
"Tokens To Keep On Context Refresh (num_keep)": "",
"Tools": "",
"Top K": "Top K",
"Top P": "Top P",
@ -528,8 +538,10 @@
"Type Hugging Face Resolve (Download) URL": "Hugging Face Resolve (다운로드) URL 입력",
"Uh-oh! There was an issue connecting to {{provider}}.": "앗! {{provider}}에 연결하는 데 문제가 있었습니다.",
"Unknown File Type '{{file_type}}', but accepting and treating as plain text": "알 수 없는 파일 유형 '{{file_type}}', 하지만 일반 텍스트로 허용하고 처리합니다.",
"Update": "",
"Update and Copy Link": "링크 업데이트 및 복사",
"Update password": "비밀번호 업데이트",
"Updated at": "",
"Upload a GGUF model": "GGUF 모델 업로드",
"Upload Files": "파일 업로드",
"Upload Pipeline": "",

View File

@ -42,6 +42,7 @@
"Allow": "Leisti",
"Allow Chat Deletion": "Leisti pokalbių ištrynimą",
"Allow non-local voices": "",
"Allow User Location": "",
"alphanumeric characters and hyphens": "skaičiai, raidės ir brūkšneliai",
"Already have an account?": "Ar jau turite paskyrą?",
"an assistant": "assistentas",
@ -69,6 +70,7 @@
"Bad Response": "Neteisingas atsakymas",
"Banners": "",
"Base Model (From)": "",
"Batch Size (num_batch)": "",
"before": "prieš",
"Being lazy": "Būvimas tingiu",
"Brave Search API Key": "",
@ -160,6 +162,7 @@
"Discover, download, and explore custom prompts": "Atrasti ir parsisiųsti užklausas",
"Discover, download, and explore model presets": "Atrasti ir parsisiųsti modelių konfigūracija",
"Dismissible": "",
"Display Emoji in Call": "",
"Display the username instead of You in the Chat": "Rodyti naudotojo vardą vietoje žodžio Jūs pokalbyje",
"Document": "Dokumentas",
"Document Settings": "Dokumento nuostatos",
@ -176,6 +179,7 @@
"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "pvz. '30s', '10m'. Laiko vienetai yra 's', 'm', 'h'.",
"Edit": "Redaguoti",
"Edit Doc": "Redaguoti dokumentą",
"Edit Memory": "",
"Edit User": "Redaguoti naudotoją",
"Email": "El. paštas",
"Embedding Batch Size": "",
@ -205,6 +209,7 @@
"Enter Serply API Key": "",
"Enter Serpstack API Key": "",
"Enter stop sequence": "Įveskite pabaigos sekvenciją",
"Enter Tavily API Key": "",
"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",
@ -280,6 +285,7 @@
"Knowledge": "",
"Language": "Kalba",
"Last Active": "Paskutinį kartą aktyvus",
"Last Modified": "",
"Light": "Šviesus",
"Listening...": "",
"LLMs can make mistakes. Verify important information.": "Dideli kalbos modeliai gali klysti. Patikrinkite atsakymų teisingumą.",
@ -304,6 +310,7 @@
"Mirostat Tau": "Mirostat Tau",
"MMMM DD, YYYY": "MMMM DD, YYYY",
"MMMM DD, YYYY HH:mm": "MMMM DD, YYYY HH:mm",
"MMMM DD, YYYY hh:mm:ss A": "",
"Model '{{modelName}}' has been successfully downloaded.": "'{{modelName}}' modelis sėkmingai atsisiųstas.",
"Model '{{modelTag}}' is already in queue for downloading.": "Modelis '{{modelTag}}' jau atsisiuntimų eilėje.",
"Model {{modelId}} not found": "Modelis {{modelId}} nerastas",
@ -490,6 +497,8 @@
"System": "Sistema",
"System Prompt": "Sistemos užklausa",
"Tags": "Žymos",
"Tap to interrupt": "",
"Tavily API Key": "",
"Tell us more:": "Papasakokite daugiau",
"Temperature": "Temperatūra",
"Template": "Modelis",
@ -520,6 +529,7 @@
"Today": "Šiandien",
"Toggle settings": "Atverti/užverti parametrus",
"Toggle sidebar": "Atverti/užverti šoninį meniu",
"Tokens To Keep On Context Refresh (num_keep)": "",
"Tools": "",
"Top K": "Top K",
"Top P": "Top P",
@ -531,8 +541,10 @@
"Type Hugging Face Resolve (Download) URL": "Įveskite Hugging Face Resolve nuorodą",
"Uh-oh! There was an issue connecting to {{provider}}.": "O ne! Prisijungiant prie {{provider}} kilo problema.",
"Unknown File Type '{{file_type}}', but accepting and treating as plain text": "Nepažįstamas '{{file_type}}' failo formatas, tačiau jis priimtas ir bus apdorotas kaip grynas tekstas",
"Update": "",
"Update and Copy Link": "Atnaujinti ir kopijuoti nuorodą",
"Update password": "Atnaujinti slaptažodį",
"Updated at": "",
"Upload a GGUF model": "Parsisiųsti GGUF modelį",
"Upload Files": "",
"Upload Pipeline": "",

View File

@ -42,6 +42,7 @@
"Allow": "Tillat",
"Allow Chat Deletion": "Tillat sletting av chatter",
"Allow non-local voices": "Tillat ikke-lokale stemmer",
"Allow User Location": "",
"alphanumeric characters and hyphens": "alfanumeriske tegn og bindestreker",
"Already have an account?": "Har du allerede en konto?",
"an assistant": "en assistent",
@ -69,6 +70,7 @@
"Bad Response": "Dårlig svar",
"Banners": "Bannere",
"Base Model (From)": "Grunnmodell (Fra)",
"Batch Size (num_batch)": "",
"before": "før",
"Being lazy": "Er lat",
"Brave Search API Key": "Brave Search API-nøkkel",
@ -160,6 +162,7 @@
"Discover, download, and explore custom prompts": "Oppdag, last ned og utforsk egendefinerte prompts",
"Discover, download, and explore model presets": "Oppdag, last ned og utforsk modellforhåndsinnstillinger",
"Dismissible": "",
"Display Emoji in Call": "",
"Display the username instead of You in the Chat": "Vis brukernavnet i stedet for Du i chatten",
"Document": "Dokument",
"Document Settings": "Dokumentinnstillinger",
@ -176,6 +179,7 @@
"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "f.eks. '30s','10m'. Gyldige tidsenheter er 's', 'm', 't'.",
"Edit": "Rediger",
"Edit Doc": "Rediger dokument",
"Edit Memory": "",
"Edit User": "Rediger bruker",
"Email": "E-post",
"Embedding Batch Size": "Batch-størrelse for embedding",
@ -205,6 +209,7 @@
"Enter Serply API Key": "",
"Enter Serpstack API Key": "Skriv inn Serpstack API-nøkkel",
"Enter stop sequence": "Skriv inn stoppsekvens",
"Enter Tavily API Key": "",
"Enter Top K": "Skriv inn Top K",
"Enter URL (e.g. http://127.0.0.1:7860/)": "Skriv inn URL (f.eks. http://127.0.0.1:7860/)",
"Enter URL (e.g. http://localhost:11434)": "Skriv inn URL (f.eks. http://localhost:11434)",
@ -280,6 +285,7 @@
"Knowledge": "",
"Language": "Språk",
"Last Active": "Sist aktiv",
"Last Modified": "",
"Light": "Lys",
"Listening...": "",
"LLMs can make mistakes. Verify important information.": "LLM-er kan gjøre feil. Verifiser viktig informasjon.",
@ -304,6 +310,7 @@
"Mirostat Tau": "Mirostat Tau",
"MMMM DD, YYYY": "MMMM DD, YYYY",
"MMMM DD, YYYY HH:mm": "MMMM DD, YYYY HH:mm",
"MMMM DD, YYYY hh:mm:ss A": "",
"Model '{{modelName}}' has been successfully downloaded.": "Modellen '{{modelName}}' er lastet ned.",
"Model '{{modelTag}}' is already in queue for downloading.": "Modellen '{{modelTag}}' er allerede i nedlastingskøen.",
"Model {{modelId}} not found": "Modellen {{modelId}} ble ikke funnet",
@ -488,6 +495,8 @@
"System": "System",
"System Prompt": "Systemprompt",
"Tags": "Tagger",
"Tap to interrupt": "",
"Tavily API Key": "",
"Tell us more:": "Fortell oss mer:",
"Temperature": "Temperatur",
"Template": "Mal",
@ -518,6 +527,7 @@
"Today": "I dag",
"Toggle settings": "Veksle innstillinger",
"Toggle sidebar": "Veksle sidefelt",
"Tokens To Keep On Context Refresh (num_keep)": "",
"Tools": "",
"Top K": "Top K",
"Top P": "Top P",
@ -529,8 +539,10 @@
"Type Hugging Face Resolve (Download) URL": "Skriv inn Hugging Face Resolve (nedlasting) URL",
"Uh-oh! There was an issue connecting to {{provider}}.": "Oops! Det oppsto et problem med tilkoblingen til {{provider}}.",
"Unknown File Type '{{file_type}}', but accepting and treating as plain text": "Ukjent filtype '{{file_type}}', men aksepteres og behandles som ren tekst",
"Update": "",
"Update and Copy Link": "Oppdater og kopier lenke",
"Update password": "Oppdater passord",
"Updated at": "",
"Upload a GGUF model": "Last opp en GGUF-modell",
"Upload Files": "Last opp filer",
"Upload Pipeline": "",

View File

@ -42,6 +42,7 @@
"Allow": "Toestaan",
"Allow Chat Deletion": "Sta Chat Verwijdering toe",
"Allow non-local voices": "",
"Allow User Location": "",
"alphanumeric characters and hyphens": "alfanumerieke karakters en streepjes",
"Already have an account?": "Heb je al een account?",
"an assistant": "een assistent",
@ -69,6 +70,7 @@
"Bad Response": "Ongeldig antwoord",
"Banners": "Banners",
"Base Model (From)": "Basismodel (vanaf)",
"Batch Size (num_batch)": "",
"before": "voor",
"Being lazy": "Lustig zijn",
"Brave Search API Key": "Brave Search API-sleutel",
@ -160,6 +162,7 @@
"Discover, download, and explore custom prompts": "Ontdek, download en verken aangepaste prompts",
"Discover, download, and explore model presets": "Ontdek, download en verken model presets",
"Dismissible": "",
"Display Emoji in Call": "",
"Display the username instead of You in the Chat": "Toon de gebruikersnaam in plaats van Jij in de Chat",
"Document": "Document",
"Document Settings": "Document Instellingen",
@ -176,6 +179,7 @@
"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "bijv. '30s', '10m'. Geldige tijdseenheden zijn 's', 'm', 'h'.",
"Edit": "Wijzig",
"Edit Doc": "Wijzig Doc",
"Edit Memory": "",
"Edit User": "Wijzig Gebruiker",
"Email": "Email",
"Embedding Batch Size": "",
@ -205,6 +209,7 @@
"Enter Serply API Key": "",
"Enter Serpstack API Key": "Voer de Serpstack API-sleutel in",
"Enter stop sequence": "Zet stop sequentie",
"Enter Tavily API Key": "",
"Enter Top K": "Voeg Top K toe",
"Enter URL (e.g. http://127.0.0.1:7860/)": "Zet URL (Bijv. http://127.0.0.1:7860/)",
"Enter URL (e.g. http://localhost:11434)": "Zet URL (Bijv. http://localhost:11434)",
@ -280,6 +285,7 @@
"Knowledge": "",
"Language": "Taal",
"Last Active": "Laatst Actief",
"Last Modified": "",
"Light": "Licht",
"Listening...": "",
"LLMs can make mistakes. Verify important information.": "LLMs kunnen fouten maken. Verifieer belangrijke informatie.",
@ -304,6 +310,7 @@
"Mirostat Tau": "Mirostat Tau",
"MMMM DD, YYYY": "MMMM DD, YYYY",
"MMMM DD, YYYY HH:mm": "MMMM DD, YYYY HH:mm",
"MMMM DD, YYYY hh:mm:ss A": "",
"Model '{{modelName}}' has been successfully downloaded.": "Model '{{modelName}}' is succesvol gedownload.",
"Model '{{modelTag}}' is already in queue for downloading.": "Model '{{modelTag}}' staat al in de wachtrij voor downloaden.",
"Model {{modelId}} not found": "Model {{modelId}} niet gevonden",
@ -488,6 +495,8 @@
"System": "Systeem",
"System Prompt": "Systeem Prompt",
"Tags": "Tags",
"Tap to interrupt": "",
"Tavily API Key": "",
"Tell us more:": "Vertel ons meer:",
"Temperature": "Temperatuur",
"Template": "Template",
@ -518,6 +527,7 @@
"Today": "Vandaag",
"Toggle settings": "Wissel instellingen",
"Toggle sidebar": "Wissel sidebar",
"Tokens To Keep On Context Refresh (num_keep)": "",
"Tools": "",
"Top K": "Top K",
"Top P": "Top P",
@ -529,8 +539,10 @@
"Type Hugging Face Resolve (Download) URL": "Type Hugging Face Resolve (Download) URL",
"Uh-oh! There was an issue connecting to {{provider}}.": "Uh-oh! Er was een probleem met verbinden met {{provider}}.",
"Unknown File Type '{{file_type}}', but accepting and treating as plain text": "Onbekend Bestandstype '{{file_type}}', maar accepteren en behandelen als platte tekst",
"Update": "",
"Update and Copy Link": "Update en Kopieer Link",
"Update password": "Wijzig wachtwoord",
"Updated at": "",
"Upload a GGUF model": "Upload een GGUF model",
"Upload Files": "Bestanden uploaden",
"Upload Pipeline": "",

View File

@ -42,6 +42,7 @@
"Allow": "ਅਨੁਮਤੀ",
"Allow Chat Deletion": "ਗੱਲਬਾਤ ਮਿਟਾਉਣ ਦੀ ਆਗਿਆ ਦਿਓ",
"Allow non-local voices": "",
"Allow User Location": "",
"alphanumeric characters and hyphens": "ਅਲਫ਼ਾਨਯੂਮੈਰਿਕ ਅੱਖਰ ਅਤੇ ਹਾਈਫਨ",
"Already have an account?": "ਪਹਿਲਾਂ ਹੀ ਖਾਤਾ ਹੈ?",
"an assistant": "ਇੱਕ ਸਹਾਇਕ",
@ -69,6 +70,7 @@
"Bad Response": "ਖਰਾਬ ਜਵਾਬ",
"Banners": "ਬੈਨਰ",
"Base Model (From)": "ਬੇਸ ਮਾਡਲ (ਤੋਂ)",
"Batch Size (num_batch)": "",
"before": "ਪਹਿਲਾਂ",
"Being lazy": "ਆਲਸੀ ਹੋਣਾ",
"Brave Search API Key": "ਬਹਾਦਰ ਖੋਜ API ਕੁੰਜੀ",
@ -160,6 +162,7 @@
"Discover, download, and explore custom prompts": "ਕਸਟਮ ਪ੍ਰੰਪਟਾਂ ਨੂੰ ਖੋਜੋ, ਡਾਊਨਲੋਡ ਕਰੋ ਅਤੇ ਪੜਚੋਲ ਕਰੋ",
"Discover, download, and explore model presets": "ਮਾਡਲ ਪ੍ਰੀਸੈਟਾਂ ਨੂੰ ਖੋਜੋ, ਡਾਊਨਲੋਡ ਕਰੋ ਅਤੇ ਪੜਚੋਲ ਕਰੋ",
"Dismissible": "",
"Display Emoji in Call": "",
"Display the username instead of You in the Chat": "ਗੱਲਬਾਤ 'ਚ ਤੁਹਾਡੇ ਸਥਾਨ 'ਤੇ ਉਪਭੋਗਤਾ ਨਾਮ ਦਿਖਾਓ",
"Document": "ਡਾਕੂਮੈਂਟ",
"Document Settings": "ਡਾਕੂਮੈਂਟ ਸੈਟਿੰਗਾਂ",
@ -176,6 +179,7 @@
"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "ਉਦਾਹਰਣ ਲਈ '30ਸ','10ਮਿ'. ਸਹੀ ਸਮਾਂ ਇਕਾਈਆਂ ਹਨ 'ਸ', 'ਮ', 'ਘੰ'.",
"Edit": "ਸੰਪਾਦਨ ਕਰੋ",
"Edit Doc": "ਡਾਕੂਮੈਂਟ ਸੰਪਾਦਨ ਕਰੋ",
"Edit Memory": "",
"Edit User": "ਉਪਭੋਗਤਾ ਸੰਪਾਦਨ ਕਰੋ",
"Email": "ਈਮੇਲ",
"Embedding Batch Size": "",
@ -205,6 +209,7 @@
"Enter Serply API Key": "",
"Enter Serpstack API Key": "Serpstack API ਕੁੰਜੀ ਦਾਖਲ ਕਰੋ",
"Enter stop sequence": "ਰੋਕਣ ਦਾ ਕ੍ਰਮ ਦਰਜ ਕਰੋ",
"Enter Tavily API Key": "",
"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)",
@ -280,6 +285,7 @@
"Knowledge": "",
"Language": "ਭਾਸ਼ਾ",
"Last Active": "ਆਖਰੀ ਸਰਗਰਮ",
"Last Modified": "",
"Light": "ਹਲਕਾ",
"Listening...": "",
"LLMs can make mistakes. Verify important information.": "LLMs ਗਲਤੀਆਂ ਕਰ ਸਕਦੇ ਹਨ। ਮਹੱਤਵਪੂਰਨ ਜਾਣਕਾਰੀ ਦੀ ਪੁਸ਼ਟੀ ਕਰੋ।",
@ -304,6 +310,7 @@
"Mirostat Tau": "ਮਿਰੋਸਟੈਟ ਟਾਉ",
"MMMM DD, YYYY": "MMMM DD, YYYY",
"MMMM DD, YYYY HH:mm": "MMMM DD, YYYY HH:mm",
"MMMM DD, YYYY hh:mm:ss A": "",
"Model '{{modelName}}' has been successfully downloaded.": "ਮਾਡਲ '{{modelName}}' ਸਫਲਤਾਪੂਰਵਕ ਡਾਊਨਲੋਡ ਕੀਤਾ ਗਿਆ ਹੈ।",
"Model '{{modelTag}}' is already in queue for downloading.": "ਮਾਡਲ '{{modelTag}}' ਪਹਿਲਾਂ ਹੀ ਡਾਊਨਲੋਡ ਲਈ ਕਤਾਰ ਵਿੱਚ ਹੈ।",
"Model {{modelId}} not found": "ਮਾਡਲ {{modelId}} ਨਹੀਂ ਮਿਲਿਆ",
@ -488,6 +495,8 @@
"System": "ਸਿਸਟਮ",
"System Prompt": "ਸਿਸਟਮ ਪ੍ਰੰਪਟ",
"Tags": "ਟੈਗ",
"Tap to interrupt": "",
"Tavily API Key": "",
"Tell us more:": "ਸਾਨੂੰ ਹੋਰ ਦੱਸੋ:",
"Temperature": "ਤਾਪਮਾਨ",
"Template": "ਟੈਮਪਲੇਟ",
@ -518,6 +527,7 @@
"Today": "ਅੱਜ",
"Toggle settings": "ਸੈਟਿੰਗਾਂ ਟੌਗਲ ਕਰੋ",
"Toggle sidebar": "ਸਾਈਡਬਾਰ ਟੌਗਲ ਕਰੋ",
"Tokens To Keep On Context Refresh (num_keep)": "",
"Tools": "",
"Top K": "ਸਿਖਰ K",
"Top P": "ਸਿਖਰ P",
@ -529,8 +539,10 @@
"Type Hugging Face Resolve (Download) URL": "Hugging Face Resolve (ਡਾਊਨਲੋਡ) URL ਟਾਈਪ ਕਰੋ",
"Uh-oh! There was an issue connecting to {{provider}}.": "ਓਹੋ! {{provider}} ਨਾਲ ਕਨੈਕਟ ਕਰਨ ਵਿੱਚ ਸਮੱਸਿਆ ਆਈ।",
"Unknown File Type '{{file_type}}', but accepting and treating as plain text": "ਅਣਜਾਣ ਫਾਈਲ ਕਿਸਮ '{{file_type}}', ਪਰ ਸਧਾਰਨ ਪਾਠ ਵਜੋਂ ਸਵੀਕਾਰ ਕਰਦੇ ਹੋਏ",
"Update": "",
"Update and Copy Link": "ਅੱਪਡੇਟ ਕਰੋ ਅਤੇ ਲਿੰਕ ਕਾਪੀ ਕਰੋ",
"Update password": "ਪਾਸਵਰਡ ਅੱਪਡੇਟ ਕਰੋ",
"Updated at": "",
"Upload a GGUF model": "ਇੱਕ GGUF ਮਾਡਲ ਅਪਲੋਡ ਕਰੋ",
"Upload Files": "ਫਾਇਲਾਂ ਅੱਪਲੋਡ ਕਰੋ",
"Upload Pipeline": "",

View File

@ -42,6 +42,7 @@
"Allow": "Pozwól",
"Allow Chat Deletion": "Pozwól na usuwanie czatu",
"Allow non-local voices": "",
"Allow User Location": "",
"alphanumeric characters and hyphens": "znaki alfanumeryczne i myślniki",
"Already have an account?": "Masz już konto?",
"an assistant": "asystent",
@ -69,6 +70,7 @@
"Bad Response": "Zła odpowiedź",
"Banners": "Banery",
"Base Model (From)": "Model podstawowy (od)",
"Batch Size (num_batch)": "",
"before": "przed",
"Being lazy": "Jest leniwy",
"Brave Search API Key": "Klucz API wyszukiwania Brave",
@ -160,6 +162,7 @@
"Discover, download, and explore custom prompts": "Odkryj, pobierz i eksploruj niestandardowe prompty",
"Discover, download, and explore model presets": "Odkryj, pobierz i eksploruj ustawienia modeli",
"Dismissible": "",
"Display Emoji in Call": "",
"Display the username instead of You in the Chat": "Wyświetl nazwę użytkownika zamiast Ty w czacie",
"Document": "Dokument",
"Document Settings": "Ustawienia dokumentu",
@ -176,6 +179,7 @@
"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "np. '30s', '10m'. Poprawne jednostki czasu to 's', 'm', 'h'.",
"Edit": "Edytuj",
"Edit Doc": "Edytuj dokument",
"Edit Memory": "",
"Edit User": "Edytuj użytkownika",
"Email": "Email",
"Embedding Batch Size": "",
@ -205,6 +209,7 @@
"Enter Serply API Key": "",
"Enter Serpstack API Key": "Wprowadź klucz API Serpstack",
"Enter stop sequence": "Wprowadź sekwencję zatrzymania",
"Enter Tavily API Key": "",
"Enter Top K": "Wprowadź Top K",
"Enter URL (e.g. http://127.0.0.1:7860/)": "Wprowadź adres URL (np. http://127.0.0.1:7860/)",
"Enter URL (e.g. http://localhost:11434)": "Wprowadź adres URL (np. http://localhost:11434/)",
@ -280,6 +285,7 @@
"Knowledge": "",
"Language": "Język",
"Last Active": "Ostatnio aktywny",
"Last Modified": "",
"Light": "Jasny",
"Listening...": "",
"LLMs can make mistakes. Verify important information.": "LLMy mogą popełniać błędy. Zweryfikuj ważne informacje.",
@ -304,6 +310,7 @@
"Mirostat Tau": "Mirostat Tau",
"MMMM DD, YYYY": "MMMM DD, YYYY",
"MMMM DD, YYYY HH:mm": "MMMM DD, YYYY HH:mm",
"MMMM DD, YYYY hh:mm:ss A": "",
"Model '{{modelName}}' has been successfully downloaded.": "Model '{{modelName}}' został pomyślnie pobrany.",
"Model '{{modelTag}}' is already in queue for downloading.": "Model '{{modelTag}}' jest już w kolejce do pobrania.",
"Model {{modelId}} not found": "Model {{modelId}} nie został znaleziony",
@ -490,6 +497,8 @@
"System": "System",
"System Prompt": "Prompt systemowy",
"Tags": "Tagi",
"Tap to interrupt": "",
"Tavily API Key": "",
"Tell us more:": "Powiedz nam więcej",
"Temperature": "Temperatura",
"Template": "Szablon",
@ -520,6 +529,7 @@
"Today": "Dzisiaj",
"Toggle settings": "Przełącz ustawienia",
"Toggle sidebar": "Przełącz panel boczny",
"Tokens To Keep On Context Refresh (num_keep)": "",
"Tools": "",
"Top K": "Najlepsze K",
"Top P": "Najlepsze P",
@ -531,8 +541,10 @@
"Type Hugging Face Resolve (Download) URL": "Wprowadź adres URL do pobrania z Hugging Face",
"Uh-oh! There was an issue connecting to {{provider}}.": "O nie! Wystąpił problem z połączeniem z {{provider}}.",
"Unknown File Type '{{file_type}}', but accepting and treating as plain text": "Nieznany typ pliku '{{file_type}}', ale akceptowany i traktowany jako zwykły tekst",
"Update": "",
"Update and Copy Link": "Uaktualnij i skopiuj link",
"Update password": "Aktualizacja hasła",
"Updated at": "",
"Upload a GGUF model": "Prześlij model GGUF",
"Upload Files": "Prześlij pliki",
"Upload Pipeline": "",

View File

@ -42,6 +42,7 @@
"Allow": "Permitir",
"Allow Chat Deletion": "Permitir Exclusão de Bate-papo",
"Allow non-local voices": "",
"Allow User Location": "",
"alphanumeric characters and hyphens": "caracteres alfanuméricos e hífens",
"Already have an account?": "Já tem uma conta?",
"an assistant": "um assistente",
@ -69,6 +70,7 @@
"Bad Response": "Resposta ruim",
"Banners": "Banners",
"Base Model (From)": "Modelo Base (De)",
"Batch Size (num_batch)": "",
"before": "antes",
"Being lazy": "Ser preguiçoso",
"Brave Search API Key": "Chave da API de pesquisa do Brave",
@ -160,6 +162,7 @@
"Discover, download, and explore custom prompts": "Descubra, baixe e explore prompts personalizados",
"Discover, download, and explore model presets": "Descubra, baixe e explore predefinições de modelo",
"Dismissible": "",
"Display Emoji in Call": "",
"Display the username instead of You in the Chat": "Exibir o nome de usuário em vez de Você no Bate-papo",
"Document": "Documento",
"Document Settings": "Configurações de Documento",
@ -176,6 +179,7 @@
"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'.",
"Edit": "Editar",
"Edit Doc": "Editar Documento",
"Edit Memory": "",
"Edit User": "Editar Usuário",
"Email": "E-mail",
"Embedding Batch Size": "",
@ -205,6 +209,7 @@
"Enter Serply API Key": "",
"Enter Serpstack API Key": "Digite a chave da API Serpstack",
"Enter stop sequence": "Digite a sequência de parada",
"Enter Tavily API Key": "",
"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)",
@ -280,6 +285,7 @@
"Knowledge": "",
"Language": "Idioma",
"Last Active": "Último Ativo",
"Last Modified": "",
"Light": "Claro",
"Listening...": "",
"LLMs can make mistakes. Verify important information.": "LLMs podem cometer erros. Verifique informações importantes.",
@ -304,6 +310,7 @@
"Mirostat Tau": "Mirostat Tau",
"MMMM DD, YYYY": "DD/MM/YYYY",
"MMMM DD, YYYY HH:mm": "DD/MM/YYYY HH:mm",
"MMMM DD, YYYY hh:mm:ss A": "",
"Model '{{modelName}}' has been successfully downloaded.": "O modelo '{{modelName}}' foi baixado com sucesso.",
"Model '{{modelTag}}' is already in queue for downloading.": "O modelo '{{modelTag}}' já está na fila para download.",
"Model {{modelId}} not found": "Modelo {{modelId}} não encontrado",
@ -489,6 +496,8 @@
"System": "Sistema",
"System Prompt": "Prompt do Sistema",
"Tags": "Tags",
"Tap to interrupt": "",
"Tavily API Key": "",
"Tell us more:": "Dê-nos mais:",
"Temperature": "Temperatura",
"Template": "Modelo",
@ -519,6 +528,7 @@
"Today": "Hoje",
"Toggle settings": "Alternar configurações",
"Toggle sidebar": "Alternar barra lateral",
"Tokens To Keep On Context Refresh (num_keep)": "",
"Tools": "",
"Top K": "Top K",
"Top P": "Top P",
@ -530,8 +540,10 @@
"Type Hugging Face Resolve (Download) URL": "Digite a URL do Hugging Face Resolve (Download)",
"Uh-oh! There was an issue connecting to {{provider}}.": "Opa! Houve um problema ao conectar-se a {{provider}}.",
"Unknown File Type '{{file_type}}', but accepting and treating as plain text": "Tipo de arquivo desconhecido '{{file_type}}', mas aceitando e tratando como texto simples",
"Update": "",
"Update and Copy Link": "Atualizar e Copiar Link",
"Update password": "Atualizar senha",
"Updated at": "",
"Upload a GGUF model": "Carregar um modelo GGUF",
"Upload Files": "Carregar arquivos",
"Upload Pipeline": "",

View File

@ -42,6 +42,7 @@
"Allow": "Permitir",
"Allow Chat Deletion": "Permitir Exclusão de Conversa",
"Allow non-local voices": "Permitir vozes não locais",
"Allow User Location": "",
"alphanumeric characters and hyphens": "caracteres alfanuméricos e hífens",
"Already have an account?": "Já tem uma conta?",
"an assistant": "um assistente",
@ -69,6 +70,7 @@
"Bad Response": "Resposta má",
"Banners": "Estandartes",
"Base Model (From)": "Modelo Base (De)",
"Batch Size (num_batch)": "",
"before": "antes",
"Being lazy": "Ser preguiçoso",
"Brave Search API Key": "Chave da API de Pesquisa Brave",
@ -160,6 +162,7 @@
"Discover, download, and explore custom prompts": "Descubra, descarregue e explore prompts personalizados",
"Discover, download, and explore model presets": "Descubra, descarregue e explore predefinições de modelo",
"Dismissible": "Dispensável",
"Display Emoji in Call": "",
"Display the username instead of You in the Chat": "Exibir o nome de utilizador em vez de Você na Conversa",
"Document": "Documento",
"Document Settings": "Configurações de Documento",
@ -176,6 +179,7 @@
"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'.",
"Edit": "Editar",
"Edit Doc": "Editar Documento",
"Edit Memory": "",
"Edit User": "Editar Utilizador",
"Email": "E-mail",
"Embedding Batch Size": "Tamanho do Lote do Embedding",
@ -205,6 +209,7 @@
"Enter Serply API Key": "",
"Enter Serpstack API Key": "Escreva a chave da API Serpstack",
"Enter stop sequence": "Escreva a sequência de paragem",
"Enter Tavily API Key": "",
"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)",
@ -280,6 +285,7 @@
"Knowledge": "Conhecimento",
"Language": "Idioma",
"Last Active": "Último Ativo",
"Last Modified": "",
"Light": "Claro",
"Listening...": "A escutar...",
"LLMs can make mistakes. Verify important information.": "LLMs podem cometer erros. Verifique informações importantes.",
@ -304,6 +310,7 @@
"Mirostat Tau": "Mirostat Tau",
"MMMM DD, YYYY": "DD/MM/YYYY",
"MMMM DD, YYYY HH:mm": "DD/MM/YYYY HH:mm",
"MMMM DD, YYYY hh:mm:ss A": "",
"Model '{{modelName}}' has been successfully downloaded.": "O modelo '{{modelName}}' foi descarregado com sucesso.",
"Model '{{modelTag}}' is already in queue for downloading.": "O modelo '{{modelTag}}' já está na fila para descarregar.",
"Model {{modelId}} not found": "Modelo {{modelId}} não foi encontrado",
@ -489,6 +496,8 @@
"System": "Sistema",
"System Prompt": "Prompt do Sistema",
"Tags": "Etiquetas",
"Tap to interrupt": "",
"Tavily API Key": "",
"Tell us more:": "Diga-nos mais:",
"Temperature": "Temperatura",
"Template": "Modelo",
@ -519,6 +528,7 @@
"Today": "Hoje",
"Toggle settings": "Alternar configurações",
"Toggle sidebar": "Alternar barra lateral",
"Tokens To Keep On Context Refresh (num_keep)": "",
"Tools": "",
"Top K": "Top K",
"Top P": "Top P",
@ -530,8 +540,10 @@
"Type Hugging Face Resolve (Download) URL": "Escreva o URL do Hugging Face Resolve (Descarregar)",
"Uh-oh! There was an issue connecting to {{provider}}.": "Uh-oh! Houve um problema ao conectar a {{provider}}.",
"Unknown File Type '{{file_type}}', but accepting and treating as plain text": "Tipo de ficheiro desconhecido '{{file_type}}', mas aceitando e tratando como texto simples",
"Update": "",
"Update and Copy Link": "Atualizar e Copiar Link",
"Update password": "Atualizar senha",
"Updated at": "",
"Upload a GGUF model": "Carregar um modelo GGUF",
"Upload Files": "Carregar ficheiros",
"Upload Pipeline": "Carregar Pipeline",

View File

@ -42,6 +42,7 @@
"Allow": "Разрешить",
"Allow Chat Deletion": "Дозволять удаление чат",
"Allow non-local voices": "",
"Allow User Location": "",
"alphanumeric characters and hyphens": "буквенно цифровые символы и дефисы",
"Already have an account?": "у вас уже есть аккаунт?",
"an assistant": "ассистент",
@ -69,6 +70,7 @@
"Bad Response": "Недопустимый ответ",
"Banners": "Баннеры",
"Base Model (From)": "Базовая модель (от)",
"Batch Size (num_batch)": "",
"before": "до",
"Being lazy": "ленивый",
"Brave Search API Key": "Ключ API поиска Brave",
@ -160,6 +162,7 @@
"Discover, download, and explore custom prompts": "Находите, загружайте и исследуйте настраиваемые промты",
"Discover, download, and explore model presets": "Находите, загружайте и исследуйте предустановки модели",
"Dismissible": "",
"Display Emoji in Call": "",
"Display the username instead of You in the Chat": "Отображать имя пользователя вместо 'Вы' в чате",
"Document": "Документ",
"Document Settings": "Настройки документа",
@ -176,6 +179,7 @@
"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "например, '30с','10м'. Допустимые единицы времени: 'с', 'м', 'ч'.",
"Edit": "Редактировать",
"Edit Doc": "Редактировать документ",
"Edit Memory": "",
"Edit User": "Редактировать пользователя",
"Email": "Электронная почта",
"Embedding Batch Size": "",
@ -205,6 +209,7 @@
"Enter Serply API Key": "",
"Enter Serpstack API Key": "Введите ключ API Serpstack",
"Enter stop sequence": "Введите последовательность остановки",
"Enter Tavily API Key": "",
"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)",
@ -280,6 +285,7 @@
"Knowledge": "",
"Language": "Язык",
"Last Active": "Последний активный",
"Last Modified": "",
"Light": "Светлый",
"Listening...": "",
"LLMs can make mistakes. Verify important information.": "LLMs могут допускать ошибки. Проверяйте важную информацию.",
@ -304,6 +310,7 @@
"Mirostat Tau": "Mirostat Tau",
"MMMM DD, YYYY": "DD MMMM YYYY г.",
"MMMM DD, YYYY HH:mm": "DD MMMM YYYY HH:mm",
"MMMM DD, YYYY hh:mm:ss A": "",
"Model '{{modelName}}' has been successfully downloaded.": "Модель '{{modelName}}' успешно загружена.",
"Model '{{modelTag}}' is already in queue for downloading.": "Модель '{{modelTag}}' уже находится в очереди на загрузку.",
"Model {{modelId}} not found": "Модель {{modelId}} не найдена",
@ -490,6 +497,8 @@
"System": "Система",
"System Prompt": "Системный промпт",
"Tags": "Теги",
"Tap to interrupt": "",
"Tavily API Key": "",
"Tell us more:": "Пожалуйста, расскажите нам больше:",
"Temperature": "Температура",
"Template": "Шаблон",
@ -520,6 +529,7 @@
"Today": "Сегодня",
"Toggle settings": "Переключить настройки",
"Toggle sidebar": "Переключить боковую панель",
"Tokens To Keep On Context Refresh (num_keep)": "",
"Tools": "",
"Top K": "Top K",
"Top P": "Top P",
@ -531,8 +541,10 @@
"Type Hugging Face Resolve (Download) URL": "Введите URL-адрес Hugging Face Resolve (загрузки)",
"Uh-oh! There was an issue connecting to {{provider}}.": "Упс! Возникла проблема подключения к {{provider}}.",
"Unknown File Type '{{file_type}}', but accepting and treating as plain text": "Неизвестный тип файла '{{file_type}}', но принимается и обрабатывается как обычный текст",
"Update": "",
"Update and Copy Link": "Обновить и скопировать ссылку",
"Update password": "Обновить пароль",
"Updated at": "",
"Upload a GGUF model": "Загрузить модель GGUF",
"Upload Files": "Загрузка файлов",
"Upload Pipeline": "",

View File

@ -42,6 +42,7 @@
"Allow": "Дозволи",
"Allow Chat Deletion": "Дозволи брисање ћаскања",
"Allow non-local voices": "",
"Allow User Location": "",
"alphanumeric characters and hyphens": "алфанумерички знакови и цртице",
"Already have an account?": "Већ имате налог?",
"an assistant": "помоћник",
@ -69,6 +70,7 @@
"Bad Response": "Лош одговор",
"Banners": "Барјаке",
"Base Model (From)": "Основни модел (од)",
"Batch Size (num_batch)": "",
"before": "пре",
"Being lazy": "Бити лењ",
"Brave Search API Key": "Апи кључ за храбру претрагу",
@ -160,6 +162,7 @@
"Discover, download, and explore custom prompts": "Откријте, преузмите и истражите прилагођене упите",
"Discover, download, and explore model presets": "Откријте, преузмите и истражите образце модела",
"Dismissible": "",
"Display Emoji in Call": "",
"Display the username instead of You in the Chat": "Прикажи корисничко име уместо Ти у чату",
"Document": "Документ",
"Document Settings": "Подешавања документа",
@ -176,6 +179,7 @@
"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "нпр. '30s', '10m'. Важеће временске јединице су 's', 'm', 'h'.",
"Edit": "Уреди",
"Edit Doc": "Уреди документ",
"Edit Memory": "",
"Edit User": "Уреди корисника",
"Email": "Е-пошта",
"Embedding Batch Size": "",
@ -205,6 +209,7 @@
"Enter Serply API Key": "",
"Enter Serpstack API Key": "Унесите Серпстацк АПИ кључ",
"Enter stop sequence": "Унесите секвенцу заустављања",
"Enter Tavily API Key": "",
"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)",
@ -280,6 +285,7 @@
"Knowledge": "",
"Language": "Језик",
"Last Active": "Последња активност",
"Last Modified": "",
"Light": "Светла",
"Listening...": "",
"LLMs can make mistakes. Verify important information.": "ВЈМ-ови (LLM-ови) могу правити грешке. Проверите важне податке.",
@ -304,6 +310,7 @@
"Mirostat Tau": "Миростат Тау",
"MMMM DD, YYYY": "ММММ ДД, ГГГГ",
"MMMM DD, YYYY HH:mm": "ММММ ДД, ГГГГ ЧЧ:мм",
"MMMM DD, YYYY hh:mm:ss A": "",
"Model '{{modelName}}' has been successfully downloaded.": "Модел „{{modelName}}“ је успешно преузет.",
"Model '{{modelTag}}' is already in queue for downloading.": "Модел „{{modelTag}}“ је већ у реду за преузимање.",
"Model {{modelId}} not found": "Модел {{modelId}} није пронађен",
@ -489,6 +496,8 @@
"System": "Систем",
"System Prompt": "Системски упит",
"Tags": "Ознаке",
"Tap to interrupt": "",
"Tavily API Key": "",
"Tell us more:": "Реците нам више:",
"Temperature": "Температура",
"Template": "Шаблон",
@ -519,6 +528,7 @@
"Today": "Данас",
"Toggle settings": "Пребаци подешавања",
"Toggle sidebar": "Пребаци бочну траку",
"Tokens To Keep On Context Refresh (num_keep)": "",
"Tools": "",
"Top K": "Топ К",
"Top P": "Топ П",
@ -530,8 +540,10 @@
"Type Hugging Face Resolve (Download) URL": "Унесите Hugging Face Resolve (Download) адресу",
"Uh-oh! There was an issue connecting to {{provider}}.": "Упс! Дошло је до проблема при повезивању са {{provider}}.",
"Unknown File Type '{{file_type}}', but accepting and treating as plain text": "Непознат тип датотеке '{{file_type}}', али прихваћен и третиран као обичан текст",
"Update": "",
"Update and Copy Link": "Ажурирај и копирај везу",
"Update password": "Ажурирај лозинку",
"Updated at": "",
"Upload a GGUF model": "Отпреми GGUF модел",
"Upload Files": "Отпремање датотека",
"Upload Pipeline": "",

View File

@ -1,5 +1,5 @@
{
"'s', 'm', 'h', 'd', 'w' or '-1' for no expiration.": "'s', 'm', 'h', 'd', 'w' eller '-1' för ingen utgång.",
"'s', 'm', 'h', 'd', 'w' or '-1' for no expiration.": "'s', 'm', 'h', 'd', 'w' eller '-1' för inget utgångsdatum",
"(Beta)": "(Beta)",
"(e.g. `sh webui.sh --api`)": "(t.ex. `sh webui.sh --api`)",
"(latest)": "(senaste)",
@ -12,15 +12,15 @@
"a user": "en användare",
"About": "Om",
"Account": "Konto",
"Account Activation Pending": "",
"Account Activation Pending": "Kontoaktivering väntar",
"Accurate information": "Exakt information",
"Active Users": "",
"Active Users": "Aktiva användare",
"Add": "Lägg till",
"Add a model id": "Lägga till ett modell-ID",
"Add a short description about what this model does": "Lägg till en kort beskrivning av vad den här modellen gör",
"Add a short title for this prompt": "Lägg till en kort titel för denna prompt",
"Add a short title for this prompt": "Lägg till en kort titel för denna instruktion",
"Add a tag": "Lägg till en tagg",
"Add custom prompt": "Lägg till en anpassad prompt",
"Add custom prompt": "Lägg till en anpassad instruktion",
"Add Docs": "Lägg till dokument",
"Add Files": "Lägg till filer",
"Add Memory": "Lägg till minne",
@ -30,10 +30,10 @@
"Add User": "Lägg till användare",
"Adjusting these settings will apply changes universally to all users.": "Justering av dessa inställningar kommer att tillämpa ändringar universellt för alla användare.",
"admin": "administratör",
"Admin": "",
"Admin": "Admin",
"Admin Panel": "Administrationspanel",
"Admin Settings": "Administratörsinställningar",
"Admins have access to all tools at all times; users need tools assigned per model in the workspace.": "",
"Admins have access to all tools at all times; users need tools assigned per model in the workspace.": "Administratörer har tillgång till alla verktyg hela tiden, medan användare behöver verktyg som tilldelas per modell i arbetsytan.",
"Advanced Parameters": "Avancerade parametrar",
"Advanced Params": "Avancerade parametrar",
"all": "alla",
@ -41,7 +41,8 @@
"All Users": "Alla användare",
"Allow": "Tillåt",
"Allow Chat Deletion": "Tillåt chattborttagning",
"Allow non-local voices": "",
"Allow non-local voices": "Tillåt icke-lokala röster",
"Allow User Location": "",
"alphanumeric characters and hyphens": "alfanumeriska tecken och bindestreck",
"Already have an account?": "Har du redan ett konto?",
"an assistant": "en assistent",
@ -51,7 +52,7 @@
"API Key": "API-nyckel",
"API Key created.": "API-nyckel skapad.",
"API keys": "API-nycklar",
"April": "April",
"April": "april",
"Archive": "Arkiv",
"Archive All Chats": "Arkivera alla chattar",
"Archived Chats": "Arkiverade chattar",
@ -60,7 +61,7 @@
"Attach file": "Bifoga fil",
"Attention to detail": "Detaljerad uppmärksamhet",
"Audio": "Ljud",
"August": "Augusti",
"August": "augusti",
"Auto-playback response": "Automatisk uppspelning",
"AUTOMATIC1111 Base URL": "AUTOMATIC1111 bas-URL",
"AUTOMATIC1111 Base URL is required.": "AUTOMATIC1111 bas-URL krävs.",
@ -68,16 +69,17 @@
"Back": "Tillbaka",
"Bad Response": "Felaktig respons",
"Banners": "Banners",
"Base Model (From)": "Basmodell (från)",
"before": "før",
"Base Model (From)": "Basmodell (Från)",
"Batch Size (num_batch)": "Batchstorlek (num_batch)",
"before": "för",
"Being lazy": "Lägg till",
"Brave Search API Key": "API-nyckel för modig sökning",
"Brave Search API Key": "API-nyckel för Brave Search",
"Bypass SSL verification for Websites": "Kringgå SSL-verifiering för webbplatser",
"Call": "",
"Call feature is not supported when using Web STT engine": "",
"Camera": "",
"Call": "Samtal",
"Call feature is not supported when using Web STT engine": "Samtalsfunktionen är inte kompatibel med Web Tal-till-text motor",
"Camera": "Kamera",
"Cancel": "Avbryt",
"Capabilities": "Kapacitet",
"Capabilities": "Kapaciteter",
"Change Password": "Ändra lösenord",
"Chat": "Chatt",
"Chat Bubble UI": "Chatbubblar UI",
@ -93,12 +95,12 @@
"Chunk Params": "Chunk-parametrar",
"Chunk Size": "Chunk-storlek",
"Citation": "Citat",
"Clear memory": "",
"Clear memory": "Rensa minnet",
"Click here for help.": "Klicka här för hjälp.",
"Click here to": "Klicka här för att",
"Click here to select": "Klicka här för att välja",
"Click here to select a csv file.": "Klicka här för att välja en csv-fil.",
"Click here to select a py file.": "",
"Click here to select a py file.": "Klicka här för att välja en python-fil.",
"Click here to select documents.": "Klicka här för att välja dokument.",
"click here.": "klicka här.",
"Click on the user role button to change a user's role.": "Klicka på knappen för användarroll för att ändra en användares roll.",
@ -109,10 +111,10 @@
"ComfyUI Base URL": "ComfyUI Base URL",
"ComfyUI Base URL is required.": "ComfyUI Base URL krävs.",
"Command": "Kommando",
"Concurrent Requests": "Samtidiga begäranden",
"Concurrent Requests": "Parallella anrop",
"Confirm Password": "Bekräfta lösenord",
"Connections": "Anslutningar",
"Contact Admin for WebUI Access": "",
"Contact Admin for WebUI Access": "Kontakta administratören för att få åtkomst till WebUI",
"Content": "Innehåll",
"Context Length": "Kontextlängd",
"Continue Response": "Fortsätt svar",
@ -133,15 +135,15 @@
"Custom": "Anpassad",
"Customize models for a specific purpose": "Anpassa modeller för ett specifikt syfte",
"Dark": "Mörk",
"Dashboard": "",
"Dashboard": "Instrumentpanel",
"Database": "Databas",
"December": "December",
"December": "december",
"Default": "Standard",
"Default (Automatic1111)": "Standard (Automatic1111)",
"Default (SentenceTransformers)": "Standard (SentenceTransformers)",
"Default Model": "Standardmodell",
"Default model updated": "Standardmodell uppdaterad",
"Default Prompt Suggestions": "Standardpromptförslag",
"Default Prompt Suggestions": "Standardinstruktionsförslag",
"Default User Role": "Standardanvändarroll",
"delete": "radera",
"Delete": "Radera",
@ -156,42 +158,44 @@
"Description": "Beskrivning",
"Didn't fully follow instructions": "Följde inte instruktionerna",
"Discover a model": "Upptäck en modell",
"Discover a prompt": "Upptäck en prompt",
"Discover, download, and explore custom prompts": "Upptäck, ladda ner och utforska anpassade prompts",
"Discover a prompt": "Upptäck en instruktion",
"Discover, download, and explore custom prompts": "Upptäck, ladda ner och utforska anpassade instruktioner",
"Discover, download, and explore model presets": "Upptäck, ladda ner och utforska modellförinställningar",
"Dismissible": "",
"Dismissible": "Kan stängas",
"Display Emoji in Call": "Visa Emoji under samtal",
"Display the username instead of You in the Chat": "Visa användarnamnet istället för du i chatten",
"Document": "Dokument",
"Document Settings": "Dokumentinställningar",
"Documentation": "",
"Documentation": "Dokumentation",
"Documents": "Dokument",
"does not make any external connections, and your data stays securely on your locally hosted server.": "gör inga externa anslutningar, och dina data förblir säkra på din lokalt värdade server.",
"Don't Allow": "Tillåt inte",
"Don't have an account?": "Har du inte ett konto?",
"Don't like the style": "Du tycker inte om utseendet",
"Don't have an account?": "Har du inget konto?",
"Don't like the style": "Tycker inte om utseendet",
"Download": "Ladda ner",
"Download canceled": "Nedladdning avbruten",
"Download Database": "Ladda ner databas",
"Drop any files here to add to the conversation": "Släpp filer här för att lägga till i konversationen",
"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'.",
"Edit": "Redigera",
"Edit Doc": "Redigera dokument",
"Edit Memory": "",
"Edit User": "Redigera användare",
"Email": "E-post",
"Embedding Batch Size": "",
"Embedding Model": "Embeddingsmodell",
"Embedding Model Engine": "Embeddingsmodellmotor",
"Embedding model set to \"{{embedding_model}}\"": "Embeddingsmodell inställd på \"{{embedding_model}}\"",
"Embedding Batch Size": "Batchstorlek för inbäddning",
"Embedding Model": "Inbäddningsmodell",
"Embedding Model Engine": "Motor för inbäddningsmodell",
"Embedding model set to \"{{embedding_model}}\"": "Inbäddningsmodell inställd på \"{{embedding_model}}\"",
"Enable Chat History": "Aktivera chatthistorik",
"Enable Community Sharing": "Aktivera community-delning",
"Enable New Sign Ups": "Aktivera nya registreringar",
"Enable Web Search": "Aktivera webbsökning",
"Ensure your CSV file includes 4 columns in this order: Name, Email, Password, Role.": "Se till att din CSV-fil innehåller fyra kolumner i denna ordning: Namn, E-post, Lösenord, Roll.",
"Ensure your CSV file includes 4 columns in this order: Name, Email, Password, Role.": "Se till att din CSV-fil innehåller fyra kolumner i denna ordning: Name, Email, Password, Role.",
"Enter {{role}} message here": "Skriv {{role}} meddelande här",
"Enter a detail about yourself for your LLMs to recall": "Skriv en detalj om dig själv för att dina LLMs ska komma ihåg",
"Enter Brave Search API Key": "Ange API-nyckel för modig sökning",
"Enter Chunk Overlap": "Ange Chunk-överlappning",
"Enter Chunk Size": "Ange Chunk-storlek",
"Enter Brave Search API Key": "Ange API-nyckel för Brave Search",
"Enter Chunk Overlap": "Ange chunköverlappning",
"Enter Chunk Size": "Ange chunkstorlek",
"Enter Github Raw URL": "Ange Github Raw URL",
"Enter Google PSE API Key": "Ange Google PSE API-nyckel",
"Enter Google PSE Engine Id": "Ange Google PSE Engine Id",
@ -199,12 +203,13 @@
"Enter language codes": "Skriv språkkoder",
"Enter model tag (e.g. {{modelTag}})": "Ange modelltagg (t.ex. {{modelTag}})",
"Enter Number of Steps (e.g. 50)": "Ange antal steg (t.ex. 50)",
"Enter Score": "Ange poäng",
"Enter Score": "Ange betyg",
"Enter Searxng Query URL": "Ange Searxng Query URL",
"Enter Serper API Key": "Ange Serper API-nyckel",
"Enter Serply API Key": "",
"Enter Serply API Key": "Ange Serply API-nyckel",
"Enter Serpstack API Key": "Ange Serpstack API-nyckel",
"Enter stop sequence": "Ange stoppsekvens",
"Enter Tavily API Key": "",
"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)",
@ -216,38 +221,38 @@
"Experimental": "Experimentell",
"Export": "Export",
"Export All Chats (All Users)": "Exportera alla chattar (alla användare)",
"Export chat (.json)": "",
"Export chat (.json)": "Exportera chatt (.json)",
"Export Chats": "Exportera chattar",
"Export Documents Mapping": "Exportera dokumentmappning",
"Export Models": "Exportera modeller",
"Export Prompts": "Exportera prompts",
"Export Tools": "",
"External Models": "",
"Export Prompts": "Exportera instruktioner",
"Export Tools": "Exportera verktyg",
"External Models": "Externa modeller",
"Failed to create API Key.": "Misslyckades med att skapa API-nyckel.",
"Failed to read clipboard contents": "Misslyckades med att läsa urklippsinnehåll",
"Failed to update settings": "",
"February": "Februar",
"Feel free to add specific details": "Förfoga att lägga till specifika detaljer",
"Failed to update settings": "Misslyckades med att uppdatera inställningarna",
"February": "februari",
"Feel free to add specific details": "Tveka inte att lägga till specifika detaljer",
"File Mode": "Fil-läge",
"File not found.": "Fil hittades inte.",
"Fingerprint spoofing detected: Unable to use initials as avatar. Defaulting to default profile image.": "Fingeravtrycksmanipulering upptäckt: Kan inte använda initialer som avatar. Återställning till standardprofilbild.",
"Fluidly stream large external response chunks": "Flytande ström stora externa svarsblock",
"Focus chat input": "Fokusera chattindata",
"Fluidly stream large external response chunks": "Strömma flytande stora externa svarschunks",
"Focus chat input": "Fokusera på chattfältet",
"Followed instructions perfectly": "Följde instruktionerna perfekt",
"Format your variables using square brackets like this:": "Formatera dina variabler med hakparenteser så här:",
"Frequency Penalty": "Straff för frekvens",
"General": "Allmän",
"General Settings": "Allmänna inställningar",
"Generate Image": "",
"Generating search query": "Generera sökfråga",
"Generation Info": "Generasjon Info",
"Generate Image": "Generera bild",
"Generating search query": "Genererar sökfråga",
"Generation Info": "Info om generation",
"Good Response": "Bra svar",
"Google PSE API Key": "Google PSE API-nyckel",
"Google PSE Engine Id": "Google PSE Engine Id",
"h:mm a": "h:mm a",
"has no conversations.": "har ingen samtaler.",
"has no conversations.": "har inga samtal.",
"Hello, {{name}}": "Hej, {{name}}",
"Help": "Hjelp",
"Help": "Hjälp",
"Hide": "Dölj",
"How can I help you today?": "Hur kan jag hjälpa dig idag?",
"Hybrid Search": "Hybrid sökning",
@ -258,52 +263,54 @@
"Import Chats": "Importera chattar",
"Import Documents Mapping": "Importera dokumentmappning",
"Import Models": "Importera modeller",
"Import Prompts": "Importera prompts",
"Import Tools": "",
"Include `--api` flag when running stable-diffusion-webui": "Inkludera `--api`-flagga när du kör stabil-diffusion-webui",
"Import Prompts": "Importera instruktioner",
"Import Tools": "Importera verktyg",
"Include `--api` flag when running stable-diffusion-webui": "Inkludera flaggan `--api` när du kör stable-diffusion-webui",
"Info": "Information",
"Input commands": "Indatakommandon",
"Install from Github URL": "Installera från Github-URL",
"Instant Auto-Send After Voice Transcription": "",
"Instant Auto-Send After Voice Transcription": "Skicka automatiskt efter rösttranskribering",
"Interface": "Gränssnitt",
"Invalid Tag": "Ogiltig tagg",
"January": "januar",
"January": "januari",
"join our Discord for help.": "gå med i vår Discord för hjälp.",
"JSON": "JSON",
"JSON Preview": "Förhandsversion av JSON",
"July": "juli",
"June": "juni",
"JWT Expiration": "JWT-utgång",
"JWT Expiration": "JWT-utgångsdatum",
"JWT Token": "JWT-token",
"Keep Alive": "Håll vid liv",
"Keep Alive": "Keep Alive",
"Keyboard shortcuts": "Tangentbordsgenvägar",
"Knowledge": "",
"Knowledge": "Kunskap",
"Language": "Språk",
"Last Active": "Senast aktiv",
"Last Modified": "",
"Light": "Ljus",
"Listening...": "",
"LLMs can make mistakes. Verify important information.": "LLM:er kan göra misstag. Verifiera viktig information.",
"Local Models": "",
"Listening...": "Lyssnar...",
"LLMs can make mistakes. Verify important information.": "LLM:er kan göra misstag. Granska viktig information.",
"Local Models": "Lokala modeller",
"LTR": "LTR",
"Made by OpenWebUI Community": "Skapad av OpenWebUI Community",
"Make sure to enclose them with": "Se till att bifoga dem med",
"Manage": "",
"Manage": "Hantera",
"Manage Models": "Hantera modeller",
"Manage Ollama Models": "Hantera Ollama-modeller",
"Manage Pipelines": "Hantera pipelines",
"Manage Pipelines": "Hantera rörledningar",
"March": "mars",
"Max Tokens (num_predict)": "Maximalt antal polletter (num_predict)",
"Max Tokens (num_predict)": "Maximalt antal tokens (num_predict)",
"Maximum of 3 models can be downloaded simultaneously. Please try again later.": "Högst 3 modeller kan laddas ner samtidigt. Vänligen försök igen senare.",
"May": "mai",
"Memories accessible by LLMs will be shown here.": "Minnen som kan komma ihåg av LLM:er kommer att visas här.",
"May": "maj",
"Memories accessible by LLMs will be shown here.": "Minnen som LLM:er kan komma åt visas här.",
"Memory": "Minnen",
"Messages you send after creating your link won't be shared. Users with the URL will be able to view the shared chat.": "Meddelanden du skickar efter att ha skapat din länk kommer inte att delas. Användare med URL:en kommer att kunna se delad chatt.",
"Minimum Score": "Minimum poäng",
"Minimum Score": "Tröskel",
"Mirostat": "Mirostat",
"Mirostat Eta": "Mirostat Eta",
"Mirostat Tau": "Mirostat Tau",
"MMMM DD, YYYY": "MMMM DD, ÅÅÅÅ",
"MMMM DD, YYYY HH:mm": "MMMM DD, ÅÅÅÅ HH:mm",
"MMMM DD, YYYY": "MMMM DD, YYYY",
"MMMM DD, YYYY HH:mm": "MMMM DD, YYYY HH:mm",
"MMMM DD, YYYY hh:mm:ss A": "",
"Model '{{modelName}}' has been successfully downloaded.": "Modellen '{{modelName}}' har laddats ner framgångsrikt.",
"Model '{{modelTag}}' is already in queue for downloading.": "Modellen '{{modelTag}}' är redan i kö för nedladdning.",
"Model {{modelId}} not found": "Modell {{modelId}} hittades inte",
@ -314,7 +321,7 @@
"Model not selected": "Modell inte vald",
"Model Params": "Modell Params",
"Model Whitelisting": "Modellens vitlista",
"Model(s) Whitelisted": "Modell(er) vitlistade",
"Model(s) Whitelisted": "Vitlistade modeller",
"Modelfile Content": "Modelfilens innehåll",
"Models": "Modeller",
"More": "Mer",
@ -323,24 +330,24 @@
"Name your model": "Namnge din modell",
"New Chat": "Ny chatt",
"New Password": "Nytt lösenord",
"No documents found": "",
"No documents found": "Inga dokument hittades",
"No results found": "Inga resultat hittades",
"No search query generated": "Ingen sökfråga genererad",
"No source available": "Ingen tilgjengelig kilde",
"No source available": "Ingen tillgänglig källa",
"None": "Ingen",
"Not factually correct": "Inte faktiskt korrekt",
"Note: If you set a minimum score, the search will only return documents with a score greater than or equal to the minimum score.": "Merk: Hvis du angir en minimumspoengsum, returnerer søket bare dokumenter med en poengsum som er større enn eller lik minimumspoengsummen.",
"Note: If you set a minimum score, the search will only return documents with a score greater than or equal to the minimum score.": "Obs: Om du anger en tröskel kommer sökningen endast att returnera dokument med ett betyg som är större än eller lika med tröskeln.",
"Notifications": "Notifikationer",
"November": "november",
"num_thread (Ollama)": "num_thread (Ollama)",
"October": "oktober",
"Off": "Av",
"Okay, Let's Go!": "Okej, nu kör vi!",
"OLED Dark": "OLED mörkt",
"OLED Dark": "Mörk (OLED)",
"Ollama": "Ollama",
"Ollama API": "Ollama API",
"Ollama API disabled": "Ollama API inaktiverat",
"Ollama API is disabled": "",
"Ollama API is disabled": "Ollama API är inaktiverat",
"Ollama Version": "Ollama-version",
"On": "På",
"Only": "Endast",
@ -363,50 +370,50 @@
"PDF document (.pdf)": "PDF-dokument (.pdf)",
"PDF Extract Images (OCR)": "PDF Extrahera bilder (OCR)",
"pending": "väntande",
"Permission denied when accessing media devices": "",
"Permission denied when accessing microphone": "",
"Permission denied when accessing media devices": "Nekad behörighet vid åtkomst till mediaenheter",
"Permission denied when accessing microphone": "Nekad behörighet vid åtkomst till mikrofon",
"Permission denied when accessing microphone: {{error}}": "Tillstånd nekades vid åtkomst till mikrofon: {{error}}",
"Personalization": "Personalisering",
"Pipelines": "Rörledningar",
"Pipelines Valves": "Rörledningar Ventiler",
"Plain text (.txt)": "Rå text (.txt)",
"Pipelines Valves": "Ventiler för rörledningar",
"Plain text (.txt)": "Text (.txt)",
"Playground": "Lekplats",
"Positive attitude": "Positivt humör",
"Positive attitude": "Positivt inställning",
"Previous 30 days": "Föregående 30 dagar",
"Previous 7 days": "Föregående 7 dagar",
"Profile Image": "Profilbild",
"Prompt": "Prompt",
"Prompt (e.g. Tell me a fun fact about the Roman Empire)": "Prompt (t.ex. Berätta mig en rolig faktor om Romerska Imperiet)",
"Prompt Content": "Promptinnehåll",
"Prompt suggestions": "Förslag",
"Prompts": "Prompts",
"Pull \"{{searchValue}}\" from Ollama.com": "Dra \"{{searchValue}}\" från Ollama.com",
"Pull a model from Ollama.com": "Dra en modell från Ollama.com",
"Query Params": "Frågeparametrar",
"Prompt": "Instruktion",
"Prompt (e.g. Tell me a fun fact about the Roman Empire)": "Instruktion (t.ex. Berätta en kuriosa om Romerska Imperiet)",
"Prompt Content": "Instruktionens innehåll",
"Prompt suggestions": "Instruktionsförslag",
"Prompts": "Instruktioner",
"Pull \"{{searchValue}}\" from Ollama.com": "Ladda ner \"{{searchValue}}\" från Ollama.com",
"Pull a model from Ollama.com": "Ladda ner en modell från Ollama.com",
"Query Params": "Inställningar för sökfråga",
"RAG Template": "RAG-mall",
"Read Aloud": "Läs igenom",
"Record voice": "Spela in röst",
"Redirecting you to OpenWebUI Community": "Omdirigerar dig till OpenWebUI Community",
"Refer to yourself as \"User\" (e.g., \"User is learning Spanish\")": "",
"Refused when it shouldn't have": "Avvisades när det inte borde ha",
"Refer to yourself as \"User\" (e.g., \"User is learning Spanish\")": "Referera till dig själv som \"Användare\" (t.ex. \"Användaren lär sig spanska\")",
"Refused when it shouldn't have": "Avvisades när det inte borde ha gjort det",
"Regenerate": "Regenerera",
"Release Notes": "Versionsinformation",
"Remove": "Ta bort",
"Remove Model": "Ta bort modell",
"Rename": "Byt namn",
"Repeat Last N": "Upprepa senaste N",
"Request Mode": "Begär läge",
"Request Mode": "Frågeläge",
"Reranking Model": "Reranking modell",
"Reranking model disabled": "Reranking modell inaktiverad",
"Reranking model set to \"{{reranking_model}}\"": "Reranking modell inställd på \"{{reranking_model}}\"",
"Reset Upload Directory": "",
"Reset Upload Directory": "Återställ uppladdningskatalog",
"Reset Vector Storage": "Återställ vektorlager",
"Response AutoCopy to Clipboard": "Svara AutoCopy till urklipp",
"Role": "Roll",
"Rosé Pine": "Rosé Pine",
"Rosé Pine Dawn": "Rosé Pine Dawn",
"RTL": "RTL",
"Running": "",
"Running": "Kör",
"Save": "Spara",
"Save & Create": "Spara och skapa",
"Save & Update": "Spara och uppdatera",
@ -419,35 +426,35 @@
"Search Chats": "Sök i chattar",
"Search Documents": "Sök dokument",
"Search Models": "Sök modeller",
"Search Prompts": "Sök promptar",
"Search Query Generation Prompt": "",
"Search Query Generation Prompt Length Threshold": "",
"Search Prompts": "Sök instruktioner",
"Search Query Generation Prompt": "Instruktion för generering av sökfrågor",
"Search Query Generation Prompt Length Threshold": "Tröskelvärde för generering av sökfrågor",
"Search Result Count": "Antal sökresultat",
"Search Tools": "",
"Search Tools": "Sökverktyg",
"Searched {{count}} sites_one": "Sökte på {{count}} sites_one",
"Searched {{count}} sites_other": "Sökte på {{count}} sites_other",
"Searching \"{{searchQuery}}\"": "",
"Searching \"{{searchQuery}}\"": "Söker \"{{searchQuery}}\"",
"Searxng Query URL": "Searxng Query URL",
"See readme.md for instructions": "Se readme.md för instruktioner",
"See what's new": "Se vad som är nytt",
"Seed": "Seed",
"Select a base model": "Välj en basmodell",
"Select a engine": "",
"Select a engine": "Välj en motor",
"Select a mode": "Välj ett läge",
"Select a model": "Välj en modell",
"Select a pipeline": "Välj en pipeline",
"Select a pipeline url": "Välj en pipeline-URL",
"Select a pipeline": "Välj en rörledning",
"Select a pipeline url": "Välj en URL för rörledningen",
"Select an Ollama instance": "Välj en Ollama-instans",
"Select Documents": "",
"Select Documents": "Välj dokument",
"Select model": "Välj en modell",
"Select only one model to call": "",
"Select only one model to call": "Välj endast en modell att ringa",
"Selected model(s) do not support image inputs": "Valda modeller stöder inte bildinmatningar",
"Send": "Skicka",
"Send a Message": "Skicka ett meddelande",
"Send message": "Skicka meddelande",
"September": "september",
"Serper API Key": "Serper API-nyckel",
"Serply API Key": "",
"Serply API Key": "Serply API-nyckel",
"Serpstack API Key": "Serpstack API-nyckel",
"Server connection verified": "Serveranslutning verifierad",
"Set as default": "Ange som standard",
@ -460,15 +467,15 @@
"Set Voice": "Ange röst",
"Settings": "Inställningar",
"Settings saved successfully!": "Inställningar sparades framgångsrikt!",
"Settings updated successfully": "",
"Settings updated successfully": "Inställningar uppdaterades framgångsrikt",
"Share": "Dela",
"Share Chat": "Dela chatt",
"Share to OpenWebUI Community": "Dela till OpenWebUI Community",
"short-summary": "kort sammanfattning",
"Show": "Visa",
"Show Admin Details in Account Pending Overlay": "",
"Show Admin Details in Account Pending Overlay": "Visa administratörsinformation till väntande konton",
"Show shortcuts": "Visa genvägar",
"Showcased creativity": "Visuell kreativitet",
"Showcased creativity": "Visade kreativitet",
"sidebar": "sidofält",
"Sign in": "Logga in",
"Sign Out": "Logga ut",
@ -478,16 +485,18 @@
"Speech recognition error: {{error}}": "Fel vid taligenkänning: {{error}}",
"Speech-to-Text Engine": "Tal-till-text-motor",
"Stop Sequence": "Stoppsekvens",
"STT Model": "",
"STT Settings": "STT-inställningar",
"STT Model": "Tal-till-text-modell",
"STT Settings": "Tal-till-text-inställningar",
"Submit": "Skicka in",
"Subtitle (e.g. about the Roman Empire)": "Undertext (t.ex. om Romerska Imperiet)",
"Success": "Framgång",
"Successfully updated.": "Uppdaterades framgångsrikt.",
"Suggested": "Föreslagen",
"System": "System",
"System Prompt": "Systemprompt",
"System Prompt": "Systeminstruktion",
"Tags": "Taggar",
"Tap to interrupt": "",
"Tavily API Key": "",
"Tell us more:": "Berätta mer:",
"Temperature": "Temperatur",
"Template": "Mall",
@ -495,48 +504,51 @@
"Text-to-Speech Engine": "Text-till-tal-motor",
"Tfs Z": "Tfs Z",
"Thanks for your feedback!": "Tack för din feedback!",
"The score should be a value between 0.0 (0%) and 1.0 (100%).": "Poäng ska vara ett värde mellan 0,0 (0%) och 1,0 (100%).",
"The score should be a value between 0.0 (0%) and 1.0 (100%).": "Betyget ska vara ett värde mellan 0.0 (0%) och 1.0 (100%).",
"Theme": "Tema",
"Thinking...": "",
"This ensures that your valuable conversations are securely saved to your backend database. Thank you!": "Detta säkerställer att dina värdefulla konversationer sparas säkert till din backend-databas. Tack!",
"This is an experimental feature, it may not function as expected and is subject to change at any time.": "",
"This ensures that your valuable conversations are securely saved to your backend database. Thank you!": "Detta säkerställer att dina värdefulla samtal sparas säkert till din backend-databas. Tack!",
"This is an experimental feature, it may not function as expected and is subject to change at any time.": "Detta är en experimentell funktion som kanske inte fungerar som förväntat och som kan komma att ändras när som helst.",
"This setting does not sync across browsers or devices.": "Denna inställning synkroniseras inte mellan webbläsare eller enheter.",
"Thorough explanation": "Djupare förklaring",
"Tip: Update multiple variable slots consecutively by pressing the tab key in the chat input after each replacement.": "Tips: Uppdatera flera variabelplatser efter varandra genom att trycka på tabb-tangenten i chattinmatningen efter varje ersättning.",
"Tip: Update multiple variable slots consecutively by pressing the tab key in the chat input after each replacement.": "Tips: Uppdatera fler variabler genom att trycka på tabb-tangenten i chattinmatningen efter varje ersättning.",
"Title": "Titel",
"Title (e.g. Tell me a fun fact)": "Tittel (f.eks. Fortell meg en fun fact)",
"Title (e.g. Tell me a fun fact)": "Titel (t.ex. Berätta en kuriosa)",
"Title Auto-Generation": "Automatisk generering av titel",
"Title cannot be an empty string.": "Tittel kan ikke være en tom streng.",
"Title Generation Prompt": "Titelgenereringsprompt",
"Title cannot be an empty string.": "Titeln får inte vara en tom sträng.",
"Title Generation Prompt": "Instruktion för titelgenerering",
"to": "till",
"To access the available model names for downloading,": "För att komma åt de tillgängliga modellnamnen för nedladdning,",
"To access the GGUF models available for downloading,": "För att komma åt de GGUF-modeller som finns tillgängliga för nedladdning,",
"To access the WebUI, please reach out to the administrator. Admins can manage user statuses from the Admin Panel.": "",
"To add documents here, upload them to the \"Documents\" workspace first.": "",
"To access the GGUF models available for downloading,": "För att komma åt de GGUF-modellerna som finns tillgängliga för nedladdning,",
"To access the WebUI, please reach out to the administrator. Admins can manage user statuses from the Admin Panel.": "För att få tillgång till WebUI, kontakta administratören. Administratörer kan hantera behörigheter från administrationspanelen.",
"To add documents here, upload them to the \"Documents\" workspace first.": "Om du vill lägga till dokument här ska du först ladda upp dem till arbetsytan \"Dokument\".",
"to chat input.": "till chattinmatning.",
"To select toolkits here, add them to the \"Tools\" workspace first.": "",
"To select toolkits here, add them to the \"Tools\" workspace first.": "Om du vill välja verktygslådor här måste du först lägga till dem i arbetsytan \"Verktyg\".",
"Today": "Idag",
"Toggle settings": "Växla inställningar",
"Toggle sidebar": "Växla sidofält",
"Tools": "",
"Tokens To Keep On Context Refresh (num_keep)": "Tokens att behålla vid kontextuppdatering (num_keep)",
"Tools": "Verktyg",
"Top K": "Topp K",
"Top P": "Topp P",
"Trouble accessing Ollama?": "Problem med att komma åt Ollama?",
"TTS Model": "",
"TTS Settings": "TTS-inställningar",
"TTS Voice": "",
"TTS Model": "Text-till-tal-modell",
"TTS Settings": "Text-till-tal-inställningar",
"TTS Voice": "Text-till-tal-röst",
"Type": "Typ",
"Type Hugging Face Resolve (Download) URL": "Skriv Hugging Face Resolve (nedladdning) URL",
"Uh-oh! There was an issue connecting to {{provider}}.": "Oj då! Det uppstod ett problem med att ansluta till {{provider}}.",
"Uh-oh! There was an issue connecting to {{provider}}.": "Oj då! Det uppstod ett problem med anslutningen till {{provider}}.",
"Unknown File Type '{{file_type}}', but accepting and treating as plain text": "Okänd filtyp '{{file_type}}', men accepterar och behandlar som vanlig text",
"Update": "",
"Update and Copy Link": "Uppdatera och kopiera länk",
"Update password": "Uppdatera lösenord",
"Updated at": "",
"Upload a GGUF model": "Ladda upp en GGUF-modell",
"Upload Files": "Ladda upp filer",
"Upload Pipeline": "",
"Upload Progress": "Uppladdningsförlopp",
"Upload Pipeline": "Ladda upp rörledning",
"Upload Progress": "Uppladdningsframsteg",
"URL Mode": "URL-läge",
"Use '#' in the prompt input to load and select your documents.": "Använd '#' i promptinmatningen för att ladda och välja dina dokument.",
"Use '#' in the prompt input to load and select your documents.": "Använd '#' i instruktionsinmatningen för att ladda och välja dina dokument.",
"Use Gravatar": "Använd Gravatar",
"Use Initials": "Använd initialer",
"use_mlock (Ollama)": "use_mlock (Ollama)",
@ -552,31 +564,31 @@
"Warning": "Varning",
"Warning: If you update or change your embedding model, you will need to re-import all documents.": "Varning: Om du uppdaterar eller ändrar din embedding modell måste du importera alla dokument igen.",
"Web": "Webb",
"Web API": "",
"Web API": "Webb-API",
"Web Loader Settings": "Web Loader-inställningar",
"Web Params": "Web-parametrar",
"Web Search": "Webbsökning",
"Web Search Engine": "Sökmotor på webben",
"Web Search Engine": "Webbsökmotor",
"Webhook URL": "Webhook-URL",
"WebUI Add-ons": "WebUI-tillägg",
"WebUI Settings": "WebUI-inställningar",
"WebUI will make requests to": "WebUI kommer att skicka förfrågningar till",
"Whats New in": "Vad är nytt i",
"When history is turned off, new chats on this browser won't appear in your history on any of your devices.": "När historiken är avstängd visas inte nya chattar i denna webbläsare i din historik på någon av dina enheter.",
"Whisper (Local)": "",
"Widescreen Mode": "",
"Workspace": "arbetsyta",
"Write a prompt suggestion (e.g. Who are you?)": "Skriv ett förslag (t.ex. Vem är du?)",
"Whisper (Local)": "Whisper (lokal)",
"Widescreen Mode": "Bredbildsläge",
"Workspace": "Arbetsyta",
"Write a prompt suggestion (e.g. Who are you?)": "Skriv ett instruktionsförslag (t.ex. Vem är du?)",
"Write a summary in 50 words that summarizes [topic or keyword].": "Skriv en sammanfattning på 50 ord som sammanfattar [ämne eller nyckelord].",
"Yesterday": "Igenom",
"You": "du",
"You can personalize your interactions with LLMs by adding memories through the 'Manage' button below, making them more helpful and tailored to you.": "",
"Yesterday": "Igår",
"You": "Dig",
"You can personalize your interactions with LLMs by adding memories through the 'Manage' button below, making them more helpful and tailored to you.": "Du kan anpassa dina interaktioner med stora språkmodeller genom att lägga till minnen via knappen 'Hantera' nedan, så att de blir mer användbara och skräddarsydda för dig.",
"You cannot clone a base model": "Du kan inte klona en basmodell",
"You have no archived conversations.": "Du har inga arkiverade konversationer.",
"You have no archived conversations.": "Du har inga arkiverade samtal.",
"You have shared this chat": "Du har delat denna chatt",
"You're a helpful assistant.": "Du är en hjälpsam assistent.",
"You're now logged in.": "Du är nu inloggad.",
"Your account status is currently pending activation.": "",
"Your account status is currently pending activation.": "Ditt konto väntar på att bli aktiverat",
"Youtube": "Youtube",
"Youtube Loader Settings": "Youtube Loader-inställningar"
}

View File

@ -42,6 +42,7 @@
"Allow": "",
"Allow Chat Deletion": "",
"Allow non-local voices": "",
"Allow User Location": "",
"alphanumeric characters and hyphens": "",
"Already have an account?": "",
"an assistant": "",
@ -69,6 +70,7 @@
"Bad Response": "",
"Banners": "",
"Base Model (From)": "",
"Batch Size (num_batch)": "",
"before": "",
"Being lazy": "",
"Brave Search API Key": "",
@ -160,6 +162,7 @@
"Discover, download, and explore custom prompts": "",
"Discover, download, and explore model presets": "",
"Dismissible": "",
"Display Emoji in Call": "",
"Display the username instead of You in the Chat": "",
"Document": "",
"Document Settings": "",
@ -176,6 +179,7 @@
"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "",
"Edit": "",
"Edit Doc": "",
"Edit Memory": "",
"Edit User": "",
"Email": "",
"Embedding Batch Size": "",
@ -205,6 +209,7 @@
"Enter Serply API Key": "",
"Enter Serpstack API Key": "",
"Enter stop sequence": "",
"Enter Tavily API Key": "",
"Enter Top K": "",
"Enter URL (e.g. http://127.0.0.1:7860/)": "",
"Enter URL (e.g. http://localhost:11434)": "",
@ -280,6 +285,7 @@
"Knowledge": "",
"Language": "",
"Last Active": "",
"Last Modified": "",
"Light": "",
"Listening...": "",
"LLMs can make mistakes. Verify important information.": "",
@ -304,6 +310,7 @@
"Mirostat Tau": "",
"MMMM DD, YYYY": "",
"MMMM DD, YYYY HH:mm": "",
"MMMM DD, YYYY hh:mm:ss A": "",
"Model '{{modelName}}' has been successfully downloaded.": "",
"Model '{{modelTag}}' is already in queue for downloading.": "",
"Model {{modelId}} not found": "",
@ -488,6 +495,8 @@
"System": "",
"System Prompt": "",
"Tags": "",
"Tap to interrupt": "",
"Tavily API Key": "",
"Tell us more:": "",
"Temperature": "",
"Template": "",
@ -518,6 +527,7 @@
"Today": "",
"Toggle settings": "",
"Toggle sidebar": "",
"Tokens To Keep On Context Refresh (num_keep)": "",
"Tools": "",
"Top K": "",
"Top P": "",
@ -529,8 +539,10 @@
"Type Hugging Face Resolve (Download) URL": "",
"Uh-oh! There was an issue connecting to {{provider}}.": "",
"Unknown File Type '{{file_type}}', but accepting and treating as plain text": "",
"Update": "",
"Update and Copy Link": "",
"Update password": "",
"Updated at": "",
"Upload a GGUF model": "",
"Upload Files": "",
"Upload Pipeline": "",

View File

@ -42,6 +42,7 @@
"Allow": "İzin ver",
"Allow Chat Deletion": "Sohbet Silmeye İzin Ver",
"Allow non-local voices": "Yerel olmayan seslere izin verin",
"Allow User Location": "",
"alphanumeric characters and hyphens": "alfanumerik karakterler ve tireler",
"Already have an account?": "Zaten bir hesabınız mı var?",
"an assistant": "bir asistan",
@ -69,6 +70,7 @@
"Bad Response": "Kötü Yanıt",
"Banners": "Afişler",
"Base Model (From)": "Temel Model ('den)",
"Batch Size (num_batch)": "",
"before": "önce",
"Being lazy": "Tembelleşiyor",
"Brave Search API Key": "Brave Search API Anahtarı",
@ -160,6 +162,7 @@
"Discover, download, and explore custom prompts": "Özel promptları keşfedin, indirin ve inceleyin",
"Discover, download, and explore model presets": "Model ön ayarlarını keşfedin, indirin ve inceleyin",
"Dismissible": "",
"Display Emoji in Call": "",
"Display the username instead of You in the Chat": "Sohbet'te Siz yerine kullanıcı adını göster",
"Document": "Belge",
"Document Settings": "Belge Ayarları",
@ -176,6 +179,7 @@
"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "örn. '30s', '10m'. Geçerli zaman birimleri 's', 'm', 'h'.",
"Edit": "Düzenle",
"Edit Doc": "Belgeyi Düzenle",
"Edit Memory": "",
"Edit User": "Kullanıcıyı Düzenle",
"Email": "E-posta",
"Embedding Batch Size": "Gömme Yığın Boyutu",
@ -205,6 +209,7 @@
"Enter Serply API Key": "",
"Enter Serpstack API Key": "Serpstack API Anahtarını Girin",
"Enter stop sequence": "Durdurma dizisini girin",
"Enter Tavily API Key": "",
"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)",
@ -280,6 +285,7 @@
"Knowledge": "",
"Language": "Dil",
"Last Active": "Son Aktivite",
"Last Modified": "",
"Light": "Açık",
"Listening...": "",
"LLMs can make mistakes. Verify important information.": "LLM'ler hata yapabilir. Önemli bilgileri doğrulayın.",
@ -304,6 +310,7 @@
"Mirostat Tau": "Mirostat Tau",
"MMMM DD, YYYY": "DD MMMM YYYY",
"MMMM DD, YYYY HH:mm": "DD MMMM YYYY HH:mm",
"MMMM DD, YYYY hh:mm:ss A": "",
"Model '{{modelName}}' has been successfully downloaded.": "'{{modelName}}' başarıyla indirildi.",
"Model '{{modelTag}}' is already in queue for downloading.": "'{{modelTag}}' zaten indirme sırasında.",
"Model {{modelId}} not found": "{{modelId}} bulunamadı",
@ -488,6 +495,8 @@
"System": "Sistem",
"System Prompt": "Sistem Promptu",
"Tags": "Etiketler",
"Tap to interrupt": "",
"Tavily API Key": "",
"Tell us more:": "Bize daha fazlasını anlat:",
"Temperature": "Temperature",
"Template": "Şablon",
@ -518,6 +527,7 @@
"Today": "Bugün",
"Toggle settings": "Ayarları Aç/Kapat",
"Toggle sidebar": "Kenar Çubuğunu Aç/Kapat",
"Tokens To Keep On Context Refresh (num_keep)": "",
"Tools": "",
"Top K": "Top K",
"Top P": "Top P",
@ -529,8 +539,10 @@
"Type Hugging Face Resolve (Download) URL": "Hugging Face Resolve (Download) URL'sini Yazın",
"Uh-oh! There was an issue connecting to {{provider}}.": "Ah! {{provider}}'a bağlanırken bir sorun oluştu.",
"Unknown File Type '{{file_type}}', but accepting and treating as plain text": "Bilinmeyen Dosya Türü '{{file_type}}', ancak düz metin olarak kabul ediliyor ve işleniyor",
"Update": "",
"Update and Copy Link": "Güncelle ve Bağlantıyı Kopyala",
"Update password": "Parolayı Güncelle",
"Updated at": "",
"Upload a GGUF model": "Bir GGUF modeli yükle",
"Upload Files": "Dosyaları Yükle",
"Upload Pipeline": "",

View File

@ -42,6 +42,7 @@
"Allow": "Дозволити",
"Allow Chat Deletion": "Дозволити видалення чату",
"Allow non-local voices": "Дозволити не локальні голоси",
"Allow User Location": "",
"alphanumeric characters and hyphens": "алфавітно-цифрові символи та дефіси",
"Already have an account?": "Вже є обліковий запис?",
"an assistant": "асистента",
@ -69,6 +70,7 @@
"Bad Response": "Неправильна відповідь",
"Banners": "Прапори",
"Base Model (From)": "Базова модель (від)",
"Batch Size (num_batch)": "",
"before": "до того, як",
"Being lazy": "Не поспішати",
"Brave Search API Key": "Ключ API пошуку Brave",
@ -160,6 +162,7 @@
"Discover, download, and explore custom prompts": "Знайдіть, завантажте та досліджуйте налаштовані промти",
"Discover, download, and explore model presets": "Знайдіть, завантажте та досліджуйте налаштовані налаштування моделі",
"Dismissible": "Неприйнятно",
"Display Emoji in Call": "",
"Display the username instead of You in the Chat": "Показувати ім'я користувача замість 'Ви' в чаті",
"Document": "Документ",
"Document Settings": "Налаштування документа",
@ -176,6 +179,7 @@
"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "напр., '30s','10m'. Дійсні одиниці часу: 'с', 'хв', 'г'.",
"Edit": "Редагувати",
"Edit Doc": "Редагувати документ",
"Edit Memory": "",
"Edit User": "Редагувати користувача",
"Email": "Електронна пошта",
"Embedding Batch Size": "Розмір пакету під час вбудовування",
@ -205,6 +209,7 @@
"Enter Serply API Key": "",
"Enter Serpstack API Key": "Введіть ключ API Serpstack",
"Enter stop sequence": "Введіть символ зупинки",
"Enter Tavily API Key": "",
"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)",
@ -280,6 +285,7 @@
"Knowledge": "",
"Language": "Мова",
"Last Active": "Остання активність",
"Last Modified": "",
"Light": "Світла",
"Listening...": "",
"LLMs can make mistakes. Verify important information.": "LLMs можуть помилятися. Перевірте важливу інформацію.",
@ -304,6 +310,7 @@
"Mirostat Tau": "Mirostat Tau",
"MMMM DD, YYYY": "MMMM DD, YYYY",
"MMMM DD, YYYY HH:mm": "MMMM DD, YYYY HH:mm",
"MMMM DD, YYYY hh:mm:ss A": "",
"Model '{{modelName}}' has been successfully downloaded.": "Модель '{{modelName}}' успішно завантажено.",
"Model '{{modelTag}}' is already in queue for downloading.": "Модель '{{modelTag}}' вже знаходиться в черзі на завантаження.",
"Model {{modelId}} not found": "Модель {{modelId}} не знайдено",
@ -490,6 +497,8 @@
"System": "Система",
"System Prompt": "Системний промт",
"Tags": "Теги",
"Tap to interrupt": "",
"Tavily API Key": "",
"Tell us more:": "Розкажи нам більше:",
"Temperature": "Температура",
"Template": "Шаблон",
@ -520,6 +529,7 @@
"Today": "Сьогодні",
"Toggle settings": "Переключити налаштування",
"Toggle sidebar": "Переключити бокову панель",
"Tokens To Keep On Context Refresh (num_keep)": "",
"Tools": "",
"Top K": "Top K",
"Top P": "Top P",
@ -531,8 +541,10 @@
"Type Hugging Face Resolve (Download) URL": "Введіть URL ресурсу Hugging Face Resolve (завантаження)",
"Uh-oh! There was an issue connecting to {{provider}}.": "Ой! Виникла проблема при підключенні до {{provider}}.",
"Unknown File Type '{{file_type}}', but accepting and treating as plain text": "Невідомий тип файлу '{{file_type}}', але приймається та обробляється як звичайний текст",
"Update": "",
"Update and Copy Link": "Оновлення та копіювання посилання",
"Update password": "Оновити пароль",
"Updated at": "",
"Upload a GGUF model": "Завантажити GGUF модель",
"Upload Files": "Завантажити файли",
"Upload Pipeline": "Завантаження Pipeline",

View File

@ -42,6 +42,7 @@
"Allow": "Cho phép",
"Allow Chat Deletion": "Cho phép Xóa nội dung chat",
"Allow non-local voices": "",
"Allow User Location": "",
"alphanumeric characters and hyphens": "ký tự số và gạch nối",
"Already have an account?": "Bạn đã có tài khoản?",
"an assistant": "trợ lý",
@ -69,6 +70,7 @@
"Bad Response": "Trả lời KHÔNG tốt",
"Banners": "Biểu ngữ",
"Base Model (From)": "Mô hình cơ sở (từ)",
"Batch Size (num_batch)": "",
"before": "trước",
"Being lazy": "Lười biếng",
"Brave Search API Key": "Khóa API tìm kiếm dũng cảm",
@ -160,6 +162,7 @@
"Discover, download, and explore custom prompts": "Tìm kiếm, tải về và khám phá thêm các prompt tùy chỉnh",
"Discover, download, and explore model presets": "Tìm kiếm, tải về và khám phá thêm các thiết lập mô hình sẵn",
"Dismissible": "Có thể loại bỏ",
"Display Emoji in Call": "",
"Display the username instead of You in the Chat": "Hiển thị tên người sử dụng thay vì 'Bạn' trong nội dung chat",
"Document": "Tài liệu",
"Document Settings": "Cấu hình kho tài liệu",
@ -176,6 +179,7 @@
"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'.",
"Edit": "Chỉnh sửa",
"Edit Doc": "Thay đổi tài liệu",
"Edit Memory": "",
"Edit User": "Thay đổi thông tin người sử dụng",
"Email": "Email",
"Embedding Batch Size": "",
@ -205,6 +209,7 @@
"Enter Serply API Key": "Nhập Serply API Key",
"Enter Serpstack API Key": "Nhập Serpstack API Key",
"Enter stop sequence": "Nhập stop sequence",
"Enter Tavily API Key": "",
"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)",
@ -280,6 +285,7 @@
"Knowledge": "Kiến thức",
"Language": "Ngôn ngữ",
"Last Active": "Truy cập gần nhất",
"Last Modified": "",
"Light": "Sáng",
"Listening...": "Đang nghe...",
"LLMs can make mistakes. Verify important information.": "Hệ thống có thể tạo ra nội dung không chính xác hoặc sai. Hãy kiểm chứng kỹ lưỡng thông tin trước khi tiếp nhận và sử dụng.",
@ -304,6 +310,7 @@
"Mirostat Tau": "Mirostat Tau",
"MMMM DD, YYYY": "MMMM DD, YYYY",
"MMMM DD, YYYY HH:mm": "MMMM DD, YYYY HH:mm",
"MMMM DD, YYYY hh:mm:ss A": "",
"Model '{{modelName}}' has been successfully downloaded.": "Mô hình '{{modelName}}' đã được tải xuống thành công.",
"Model '{{modelTag}}' is already in queue for downloading.": "Mô hình '{{modelTag}}' đã có trong hàng đợi để tải xuống.",
"Model {{modelId}} not found": "Không tìm thấy Mô hình {{modelId}}",
@ -487,6 +494,8 @@
"System": "Hệ thống",
"System Prompt": "Prompt Hệ thống (System Prompt)",
"Tags": "Thẻ",
"Tap to interrupt": "",
"Tavily API Key": "",
"Tell us more:": "Hãy cho chúng tôi hiểu thêm về chất lượng của câu trả lời:",
"Temperature": "Mức độ sáng tạo",
"Template": "Mẫu",
@ -517,6 +526,7 @@
"Today": "Hôm nay",
"Toggle settings": "Bật/tắt cài đặt",
"Toggle sidebar": "Bật/tắt thanh bên",
"Tokens To Keep On Context Refresh (num_keep)": "",
"Tools": "",
"Top K": "Top K",
"Top P": "Top P",
@ -528,8 +538,10 @@
"Type Hugging Face Resolve (Download) URL": "Nhập URL Hugging Face Resolve (Tải xuống)",
"Uh-oh! There was an issue connecting to {{provider}}.": "Ồ! Đã xảy ra sự cố khi kết nối với {{provider}}.",
"Unknown File Type '{{file_type}}', but accepting and treating as plain text": "Loại Tệp Không xác định '{{file_type}}', nhưng đang chấp nhận và xử lý như văn bản thô",
"Update": "",
"Update and Copy Link": "Cập nhật và sao chép link",
"Update password": "Cập nhật mật khẩu",
"Updated at": "",
"Upload a GGUF model": "Tải lên mô hình GGUF",
"Upload Files": "Tải tệp lên máy chủ",
"Upload Pipeline": "",

View File

@ -33,7 +33,7 @@
"Admin": "管理员联系方式",
"Admin Panel": "管理员面板",
"Admin Settings": "管理员设置",
"Admins have access to all tools at all times; users need tools assigned per model in the workspace.": "",
"Admins have access to all tools at all times; users need tools assigned per model in the workspace.": "管理员拥有所有工具的访问权限;用户则需在工作空间中为每个模型单独分配工具。",
"Advanced Parameters": "高级参数",
"Advanced Params": "高级参数",
"all": "所有",
@ -42,6 +42,7 @@
"Allow": "允许",
"Allow Chat Deletion": "允许删除聊天记录",
"Allow non-local voices": "允许调用非本地音色",
"Allow User Location": "",
"alphanumeric characters and hyphens": "字母数字字符和连字符",
"Already have an account?": "已经拥有账号了?",
"an assistant": "助手",
@ -69,6 +70,7 @@
"Bad Response": "点踩回复",
"Banners": "公告横幅",
"Base Model (From)": "基础模型 (来自)",
"Batch Size (num_batch)": "批大小 (num_batch)",
"before": "对话",
"Being lazy": "懒惰",
"Brave Search API Key": "Brave Search API 密钥",
@ -160,6 +162,7 @@
"Discover, download, and explore custom prompts": "发现、下载并探索更多自定义提示词",
"Discover, download, and explore model presets": "发现、下载并探索更多模型预设",
"Dismissible": "是否可关闭",
"Display Emoji in Call": "在通话中显示 Emoji 表情符号",
"Display the username instead of You in the Chat": "在对话中显示用户名而不是“你”",
"Document": "文档",
"Document Settings": "文档设置",
@ -176,6 +179,7 @@
"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "例如 '30s','10m'。有效的时间单位是秒:'s',分:'m',时:'h'。",
"Edit": "编辑",
"Edit Doc": "编辑文档",
"Edit Memory": "",
"Edit User": "编辑用户",
"Email": "电子邮箱",
"Embedding Batch Size": "嵌入层批处理大小 (Embedding Batch Size)",
@ -202,9 +206,10 @@
"Enter Score": "输入评分",
"Enter Searxng Query URL": "输入 Searxng 查询地址",
"Enter Serper API Key": "输入 Serper API 密钥",
"Enter Serply API Key": "",
"Enter Serply API Key": "输入 Serply API 密钥",
"Enter Serpstack API Key": "输入 Serpstack API 密钥",
"Enter stop sequence": "输入停止序列 (Stop Sequence)",
"Enter Tavily API Key": "",
"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)",
@ -280,6 +285,7 @@
"Knowledge": "知识库",
"Language": "语言",
"Last Active": "最后在线时间",
"Last Modified": "",
"Light": "浅色",
"Listening...": "正在倾听...",
"LLMs can make mistakes. Verify important information.": "大语言模型可能会生成误导性错误信息,请对关键信息加以验证。",
@ -304,6 +310,7 @@
"Mirostat Tau": "Mirostat Tau",
"MMMM DD, YYYY": "YYYY年 MM月 DD日",
"MMMM DD, YYYY HH:mm": "YYYY年 MM月 DD日 HH:mm",
"MMMM DD, YYYY hh:mm:ss A": "",
"Model '{{modelName}}' has been successfully downloaded.": "模型'{{modelName}}'已成功下载。",
"Model '{{modelTag}}' is already in queue for downloading.": "模型'{{modelTag}}'已在下载队列中。",
"Model {{modelId}} not found": "未找到模型 {{modelId}}",
@ -424,7 +431,7 @@
"Search Query Generation Prompt Length Threshold": "搜索查询生成提示长度阈值",
"Search Result Count": "搜索结果数量",
"Search Tools": "搜索工具",
"Searched {{count}} sites_other": "检索到 {{count}} 个网站",
"Searched {{count}} sites_other": "搜索到 {{count}} 个结果",
"Searching \"{{searchQuery}}\"": "搜索 \"{{searchQuery}}\" 中",
"Searxng Query URL": "Searxng 查询 URL",
"See readme.md for instructions": "查看 readme.md 以获取说明",
@ -487,6 +494,8 @@
"System": "系统",
"System Prompt": "系统提示词",
"Tags": "标签",
"Tap to interrupt": "",
"Tavily API Key": "",
"Tell us more:": "请告诉我们更多细节",
"Temperature": "温度 (Temperature)",
"Template": "模板",
@ -513,10 +522,11 @@
"To access the WebUI, please reach out to the administrator. Admins can manage user statuses from the Admin Panel.": "请联系管理员以访问。管理员可以在后台管理面板中管理用户状态。",
"To add documents here, upload them to the \"Documents\" workspace first.": "要在此处添加文档,请先将它们上传到工作空间中的“文档”内。",
"to chat input.": "到对话输入。",
"To select toolkits here, add them to the \"Tools\" workspace first.": "",
"To select toolkits here, add them to the \"Tools\" workspace first.": "要在这里选择工具包,请先将它们添加到工作空间中的“工具”。",
"Today": "今天",
"Toggle settings": "切换设置",
"Toggle sidebar": "切换侧边栏",
"Tokens To Keep On Context Refresh (num_keep)": "在语境刷新时需保留的 Tokens",
"Tools": "工具",
"Top K": "Top K",
"Top P": "Top P",
@ -528,8 +538,10 @@
"Type Hugging Face Resolve (Download) URL": "输入 Hugging Face 解析下载URL",
"Uh-oh! There was an issue connecting to {{provider}}.": "糟糕!连接到 {{provider}} 时出现问题。",
"Unknown File Type '{{file_type}}', but accepting and treating as plain text": "未知文件类型'{{file_type}}',将视为纯文本进行处理",
"Update": "",
"Update and Copy Link": "更新和复制链接",
"Update password": "更新密码",
"Updated at": "",
"Upload a GGUF model": "上传一个 GGUF 模型",
"Upload Files": "上传文件",
"Upload Pipeline": "上传 Pipeline",
@ -555,7 +567,7 @@
"Web Loader Settings": "网页爬取设置",
"Web Params": "网络爬取设置",
"Web Search": "网络搜索",
"Web Search Engine": "Web 搜索引擎",
"Web Search Engine": "网络搜索引擎",
"Webhook URL": "Webhook URL",
"WebUI Add-ons": "WebUI 附加组件",
"WebUI Settings": "WebUI 设置",

View File

@ -42,6 +42,7 @@
"Allow": "允許",
"Allow Chat Deletion": "允許刪除聊天紀錄",
"Allow non-local voices": "",
"Allow User Location": "",
"alphanumeric characters and hyphens": "英文字母、數字0~9和連字符-",
"Already have an account?": "已經有帳號了嗎?",
"an assistant": "助手",
@ -69,6 +70,7 @@
"Bad Response": "錯誤回應",
"Banners": "橫幅",
"Base Model (From)": "基本模型(來自)",
"Batch Size (num_batch)": "",
"before": "前",
"Being lazy": "懶人模式",
"Brave Search API Key": "搜尋 API Key",
@ -160,6 +162,7 @@
"Discover, download, and explore custom prompts": "發現、下載並探索他人設置的提示詞",
"Discover, download, and explore model presets": "發現、下載並探索他人設置的模型",
"Dismissible": "",
"Display Emoji in Call": "",
"Display the username instead of You in the Chat": "在聊天中顯示使用者名稱而不是「你」",
"Document": "文件",
"Document Settings": "文件設定",
@ -176,6 +179,7 @@
"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "例如 '30s', '10m'。有效的時間單位為 's', 'm', 'h'。",
"Edit": "編輯",
"Edit Doc": "編輯文件",
"Edit Memory": "",
"Edit User": "編輯使用者",
"Email": "電子郵件",
"Embedding Batch Size": "",
@ -205,6 +209,7 @@
"Enter Serply API Key": "",
"Enter Serpstack API Key": "輸入 Serpstack API Key",
"Enter stop sequence": "輸入停止序列",
"Enter Tavily API Key": "",
"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",
@ -280,6 +285,7 @@
"Knowledge": "",
"Language": "語言",
"Last Active": "最後活動",
"Last Modified": "",
"Light": "亮色",
"Listening...": "",
"LLMs can make mistakes. Verify important information.": "LLM 可能會產生錯誤。請驗證重要資訊。",
@ -304,6 +310,7 @@
"Mirostat Tau": "Mirostat Tau",
"MMMM DD, YYYY": "MMMM DD, YYYY",
"MMMM DD, YYYY HH:mm": "MMMM DD, YYYY HH:mm",
"MMMM DD, YYYY hh:mm:ss A": "",
"Model '{{modelName}}' has been successfully downloaded.": "'{{modelName}}' 模型已成功下載。",
"Model '{{modelTag}}' is already in queue for downloading.": "'{{modelTag}}' 模型已經在下載佇列中。",
"Model {{modelId}} not found": "找不到 {{modelId}} 模型",
@ -487,6 +494,8 @@
"System": "系統",
"System Prompt": "系統提示詞",
"Tags": "標籤",
"Tap to interrupt": "",
"Tavily API Key": "",
"Tell us more:": "告訴我們更多:",
"Temperature": "溫度",
"Template": "模板",
@ -517,6 +526,7 @@
"Today": "今天",
"Toggle settings": "切換設定",
"Toggle sidebar": "切換側邊欄",
"Tokens To Keep On Context Refresh (num_keep)": "",
"Tools": "",
"Top K": "Top K",
"Top P": "Top P",
@ -528,8 +538,10 @@
"Type Hugging Face Resolve (Download) URL": "輸入 Hugging Face 解析後的下載URL",
"Uh-oh! There was an issue connecting to {{provider}}.": "哎呀!連線到 {{provider}} 時出現問題。",
"Unknown File Type '{{file_type}}', but accepting and treating as plain text": "未知的文件類型 '{{file_type}}',但接受並視為純文字",
"Update": "",
"Update and Copy Link": "更新並複製連結",
"Update password": "更新密碼",
"Updated at": "",
"Upload a GGUF model": "上傳一個 GGUF 模型",
"Upload Files": "上傳文件",
"Upload Pipeline": "",

View File

@ -92,6 +92,8 @@ type Settings = {
top_k?: string;
top_p?: string;
num_ctx?: string;
num_batch?: string;
num_keep?: string;
options?: ModelOptions;
};

View File

@ -302,6 +302,29 @@ export const getImportOrigin = (_chats) => {
return 'webui';
};
export const getUserPosition = async (raw = false) => {
// Get the user's location using the Geolocation API
const position = await new Promise((resolve, reject) => {
navigator.geolocation.getCurrentPosition(resolve, reject);
}).catch((error) => {
console.error('Error getting user location:', error);
throw error;
});
if (!position) {
return 'Location not available';
}
// Extract the latitude and longitude from the position
const { latitude, longitude } = position.coords;
if (raw) {
return { latitude, longitude };
} else {
return `${latitude.toFixed(3)}, ${longitude.toFixed(3)} (lat, long)`;
}
};
const convertOpenAIMessages = (convo) => {
// Parse OpenAI chat messages and create chat dictionary for creating new chats
const mapping = convo['mapping'];
@ -436,13 +459,31 @@ export const removeEmojis = (str) => {
export const extractSentences = (text) => {
// Split the paragraph into sentences based on common punctuation marks
const sentences = text.split(/(?<=[.!?])/);
const sentences = text.split(/(?<=[.!?])\s+/);
return sentences
.map((sentence) => removeEmojis(sentence.trim()))
.filter((sentence) => sentence !== '');
};
export const extractSentencesForAudio = (text) => {
return extractSentences(text).reduce((mergedTexts, currentText) => {
const lastIndex = mergedTexts.length - 1;
if (lastIndex >= 0) {
const previousText = mergedTexts[lastIndex];
const wordCount = previousText.split(/\s+/).length;
if (wordCount < 2) {
mergedTexts[lastIndex] = previousText + ' ' + currentText;
} else {
mergedTexts.push(currentText);
}
} else {
mergedTexts.push(currentText);
}
return mergedTexts;
}, []);
};
export const blobToFile = (blob, fileName) => {
// Create a new File object from the Blob
const file = new File([blob], fileName, { type: blob.type });
@ -456,7 +497,7 @@ export const blobToFile = (blob, fileName) => {
export const promptTemplate = (
template: string,
user_name?: string,
current_location?: string
user_location?: string
): string => {
// Get the current date
const currentDate = new Date();
@ -469,17 +510,31 @@ export const promptTemplate = (
'-' +
String(currentDate.getDate()).padStart(2, '0');
// Format the time to HH:MM:SS AM/PM
const currentTime = currentDate.toLocaleTimeString('en-US', {
hour: 'numeric',
minute: 'numeric',
second: 'numeric',
hour12: true
});
// Replace {{CURRENT_DATETIME}} in the template with the formatted datetime
template = template.replace('{{CURRENT_DATETIME}}', `${formattedDate} ${currentTime}`);
// Replace {{CURRENT_DATE}} in the template with the formatted date
template = template.replace('{{CURRENT_DATE}}', formattedDate);
// Replace {{CURRENT_TIME}} in the template with the formatted time
template = template.replace('{{CURRENT_TIME}}', currentTime);
if (user_name) {
// Replace {{USER_NAME}} in the template with the user's name
template = template.replace('{{USER_NAME}}', user_name);
}
if (current_location) {
// Replace {{CURRENT_LOCATION}} in the template with the current location
template = template.replace('{{CURRENT_LOCATION}}', current_location);
if (user_location) {
// Replace {{USER_LOCATION}} in the template with the current location
template = template.replace('{{USER_LOCATION}}', user_location);
}
return template;

View File

@ -18,6 +18,7 @@
import Tooltip from '$lib/components/common/Tooltip.svelte';
import UserChatsModal from '$lib/components/admin/UserChatsModal.svelte';
import AddUserModal from '$lib/components/admin/AddUserModal.svelte';
import ConfirmDialog from '$lib/components/common/ConfirmDialog.svelte';
const i18n = getContext('i18n');
@ -30,6 +31,7 @@
let page = 1;
let showDeleteConfirmDialog = false;
let showAddUserModal = false;
let showUserChatsModal = false;
@ -75,8 +77,26 @@
}
loaded = true;
});
let sortKey = 'created_at'; // default sort key
let sortOrder = 'asc'; // default sort order
function setSortKey(key) {
if (sortKey === key) {
sortOrder = sortOrder === 'asc' ? 'desc' : 'asc';
} else {
sortKey = key;
sortOrder = 'asc';
}
}
</script>
<ConfirmDialog
bind:show={showDeleteConfirmDialog}
on:confirm={() => {
deleteUserHandler(selectedUser.id);
}}
/>
{#key selectedUser}
<EditUserModal
bind:show={showEditUserModal}
@ -139,12 +159,66 @@
<table class="w-full text-sm text-left text-gray-500 dark:text-gray-400 table-auto max-w-full">
<thead class="text-xs text-gray-700 uppercase bg-gray-50 dark:bg-gray-850 dark:text-gray-400">
<tr>
<th scope="col" class="px-3 py-2"> {$i18n.t('Role')} </th>
<th scope="col" class="px-3 py-2"> {$i18n.t('Name')} </th>
<th scope="col" class="px-3 py-2"> {$i18n.t('Email')} </th>
<th scope="col" class="px-3 py-2"> {$i18n.t('Last Active')} </th>
<th scope="col" class="px-3 py-2"> {$i18n.t('Created at')} </th>
<th
scope="col"
class="px-3 py-2 cursor-pointer select-none"
on:click={() => setSortKey('role')}
>
{$i18n.t('Role')}
{#if sortKey === 'role'}
{sortOrder === 'asc' ? '▲' : '▼'}
{:else}
<span class="invisible"></span>
{/if}
</th>
<th
scope="col"
class="px-3 py-2 cursor-pointer select-none"
on:click={() => setSortKey('name')}
>
{$i18n.t('Name')}
{#if sortKey === 'name'}
{sortOrder === 'asc' ? '▲' : '▼'}
{:else}
<span class="invisible"></span>
{/if}
</th>
<th
scope="col"
class="px-3 py-2 cursor-pointer select-none"
on:click={() => setSortKey('email')}
>
{$i18n.t('Email')}
{#if sortKey === 'email'}
{sortOrder === 'asc' ? '▲' : '▼'}
{:else}
<span class="invisible"></span>
{/if}
</th>
<th
scope="col"
class="px-3 py-2 cursor-pointer select-none"
on:click={() => setSortKey('last_active_at')}
>
{$i18n.t('Last Active')}
{#if sortKey === 'last_active_at'}
{sortOrder === 'asc' ? '▲' : '▼'}
{:else}
<span class="invisible"></span>
{/if}
</th>
<th
scope="col"
class="px-3 py-2 cursor-pointer select-none"
on:click={() => setSortKey('created_at')}
>
{$i18n.t('Created at')}
{#if sortKey === 'created_at'}
{sortOrder === 'asc' ? '▲' : '▼'}
{:else}
<span class="invisible"></span>
{/if}
</th>
<th scope="col" class="px-3 py-2 text-right" />
</tr>
@ -160,6 +234,11 @@
return name.includes(query);
}
})
.sort((a, b) => {
if (a[sortKey] < b[sortKey]) return sortOrder === 'asc' ? -1 : 1;
if (a[sortKey] > b[sortKey]) return sortOrder === 'asc' ? 1 : -1;
return 0;
})
.slice((page - 1) * 20, page * 20) as user}
<tr class="bg-white border-b dark:bg-gray-900 dark:border-gray-850 text-xs">
<td class="px-3 py-2 min-w-[7rem] w-28">
@ -256,7 +335,8 @@
<button
class="self-center w-fit text-sm px-2 py-2 hover:bg-black/5 dark:hover:bg-white/5 rounded-xl"
on:click={async () => {
deleteUserHandler(user.id);
showDeleteConfirmDialog = true;
selectedUser = user;
}}
>
<svg