merged conflicts

This commit is contained in:
Jannik Streidl 2024-03-07 15:47:15 +01:00
commit 0d127ffef1
41 changed files with 1104 additions and 814 deletions

View file

@ -7,7 +7,6 @@ node_modules
/package /package
.env .env
.env.* .env.*
!.env.example
vite.config.js.timestamp-* vite.config.js.timestamp-*
vite.config.ts.timestamp-* vite.config.ts.timestamp-*
__pycache__ __pycache__

View file

@ -1,6 +1,6 @@
# Ollama URL for the backend to connect # Ollama URL for the backend to connect
# The path '/ollama/api' will be redirected to the specified backend URL # The path '/ollama' will be redirected to the specified backend URL
OLLAMA_API_BASE_URL='http://localhost:11434/api' OLLAMA_BASE_URL='http://localhost:11434'
OPENAI_API_BASE_URL='' OPENAI_API_BASE_URL=''
OPENAI_API_KEY='' OPENAI_API_KEY=''

View file

@ -26,17 +26,27 @@ jobs:
VERSION=$(jq -r '.version' package.json) VERSION=$(jq -r '.version' package.json)
echo "::set-output name=version::$VERSION" echo "::set-output name=version::$VERSION"
- name: Extract latest CHANGELOG entry
id: changelog
run: |
CHANGELOG_CONTENT=$(awk '/^## \[/{n++} n==1' CHANGELOG.md)
echo "CHANGELOG_CONTENT<<EOF"
echo "$CHANGELOG_CONTENT"
echo "EOF"
echo "::set-output name=content::${CHANGELOG_CONTENT}"
- name: Create GitHub release - name: Create GitHub release
uses: actions/github-script@v5 uses: actions/github-script@v5
with: with:
github-token: ${{ secrets.GITHUB_TOKEN }} github-token: ${{ secrets.GITHUB_TOKEN }}
script: | script: |
const changelog = `${{ steps.changelog.outputs.content }}`;
const release = await github.rest.repos.createRelease({ const release = await github.rest.repos.createRelease({
owner: context.repo.owner, owner: context.repo.owner,
repo: context.repo.repo, repo: context.repo.repo,
tag_name: `v${{ steps.get_version.outputs.version }}`, tag_name: `v${{ steps.get_version.outputs.version }}`,
name: `v${{ steps.get_version.outputs.version }}`, name: `v${{ steps.get_version.outputs.version }}`,
body: 'Automatically created new release', body: changelog,
}) })
console.log(`Created release ${release.data.html_url}`) console.log(`Created release ${release.data.html_url}`)

View file

@ -5,6 +5,38 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [0.1.110] - 2024-03-06
### Added
- **🌐 Multiple OpenAI Servers Support**: Enjoy seamless integration with multiple OpenAI-compatible APIs, now supported natively.
### Fixed
- **🔍 OCR Issue**: Resolved PDF parsing issue caused by OCR malfunction.
- **🚫 RAG Issue**: Fixed the RAG functionality, ensuring it operates smoothly.
- **📄 "Add Docs" Model Button**: Addressed the non-functional behavior of the "Add Docs" model button.
## [0.1.109] - 2024-03-06
### Added
- **🔄 Multiple Ollama Servers Support**: Enjoy enhanced scalability and performance with support for multiple Ollama servers in a single WebUI. Load balancing features are now available, providing improved efficiency (#788, #278).
- **🔧 Support for Claude 3 and Gemini**: Responding to user requests, we've expanded our toolset to include Claude 3 and Gemini, offering a wider range of functionalities within our platform (#1064).
- **🔍 OCR Functionality for PDF Loader**: We've augmented our PDF loader with Optical Character Recognition (OCR) capabilities. Now, extract text from scanned documents and images within PDFs, broadening the scope of content processing (#1050).
### Fixed
- **🛠️ RAG Collection**: Implemented a dynamic mechanism to recreate RAG collections, ensuring users have up-to-date and accurate data (#1031).
- **📝 User Agent Headers**: Fixed issue of RAG web requests being sent with empty user_agent headers, reducing rejections from certain websites. Realistic headers are now utilized for these requests (#1024).
- **⏹️ Playground Cancel Functionality**: Introducing a new "Cancel" option for stopping Ollama generation in the Playground, enhancing user control and usability (#1006).
- **🔤 Typographical Error in 'ASSISTANT' Field**: Corrected a typographical error in the 'ASSISTANT' field within the GGUF model upload template for accuracy and consistency (#1061).
### Changed
- **🔄 Refactored Message Deletion Logic**: Streamlined message deletion process for improved efficiency and user experience, simplifying interactions within the platform (#1004).
- **⚠️ Deprecation of `OLLAMA_API_BASE_URL`**: Deprecated `OLLAMA_API_BASE_URL` environment variable; recommend using `OLLAMA_BASE_URL` instead. Refer to our documentation for further details.
## [0.1.108] - 2024-03-02 ## [0.1.108] - 2024-03-02
### Added ### Added

View file

@ -20,7 +20,7 @@ FROM python:3.11-slim-bookworm as base
ENV ENV=prod ENV ENV=prod
ENV PORT "" ENV PORT ""
ENV OLLAMA_API_BASE_URL "/ollama/api" ENV OLLAMA_BASE_URL "/ollama"
ENV OPENAI_API_BASE_URL "" ENV OPENAI_API_BASE_URL ""
ENV OPENAI_API_KEY "" ENV OPENAI_API_KEY ""
@ -53,6 +53,8 @@ WORKDIR /app/backend
# install python dependencies # install python dependencies
COPY ./backend/requirements.txt ./requirements.txt COPY ./backend/requirements.txt ./requirements.txt
RUN apt-get update && apt-get install ffmpeg libsm6 libxext6 -y
RUN pip3 install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cpu --no-cache-dir RUN pip3 install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cpu --no-cache-dir
RUN pip3 install -r requirements.txt --no-cache-dir RUN pip3 install -r requirements.txt --no-cache-dir

View file

@ -95,10 +95,10 @@ Don't forget to explore our sibling project, [Open WebUI Community](https://open
- **If Ollama is on a Different Server**, use this command: - **If Ollama is on a Different Server**, use this command:
- To connect to Ollama on another server, change the `OLLAMA_API_BASE_URL` to the server's URL: - To connect to Ollama on another server, change the `OLLAMA_BASE_URL` to the server's URL:
```bash ```bash
docker run -d -p 3000:8080 -e OLLAMA_API_BASE_URL=https://example.com/api -v open-webui:/app/backend/data --name open-webui --restart always ghcr.io/open-webui/open-webui:main docker run -d -p 3000:8080 -e OLLAMA_BASE_URL=https://example.com -v open-webui:/app/backend/data --name open-webui --restart always ghcr.io/open-webui/open-webui:main
``` ```
- After installation, you can access Open WebUI at [http://localhost:3000](http://localhost:3000). Enjoy! 😄 - After installation, you can access Open WebUI at [http://localhost:3000](http://localhost:3000). Enjoy! 😄
@ -110,7 +110,7 @@ If you're experiencing connection issues, its often due to the WebUI docker c
**Example Docker Command**: **Example Docker Command**:
```bash ```bash
docker run -d --network=host -v open-webui:/app/backend/data -e OLLAMA_API_BASE_URL=http://127.0.0.1:11434/api --name open-webui --restart always ghcr.io/open-webui/open-webui:main 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
``` ```
### Other Installation Methods ### Other Installation Methods

View file

@ -4,7 +4,7 @@
The Open WebUI system is designed to streamline interactions between the client (your browser) and the Ollama API. At the heart of this design is a backend reverse proxy, enhancing security and resolving CORS issues. The Open WebUI system is designed to streamline interactions between the client (your browser) and the Ollama API. At the heart of this design is a backend reverse proxy, enhancing security and resolving CORS issues.
- **How it Works**: The Open WebUI is designed to interact with the Ollama API through a specific route. When a request is made from the WebUI to Ollama, it is not directly sent to the Ollama API. Initially, the request is sent to the Open WebUI backend via `/ollama/api` route. From there, the backend is responsible for forwarding the request to the Ollama API. This forwarding is accomplished by using the route specified in the `OLLAMA_API_BASE_URL` environment variable. Therefore, a request made to `/ollama/api` in the WebUI is effectively the same as making a request to `OLLAMA_API_BASE_URL` in the backend. For instance, a request to `/ollama/api/tags` in the WebUI is equivalent to `OLLAMA_API_BASE_URL/tags` in the backend. - **How it Works**: The Open WebUI is designed to interact with the Ollama API through a specific route. When a request is made from the WebUI to Ollama, it is not directly sent to the Ollama API. Initially, the request is sent to the Open WebUI backend via `/ollama` route. From there, the backend is responsible for forwarding the request to the Ollama API. This forwarding is accomplished by using the route specified in the `OLLAMA_BASE_URL` environment variable. Therefore, a request made to `/ollama` in the WebUI is effectively the same as making a request to `OLLAMA_BASE_URL` in the backend. For instance, a request to `/ollama/api/tags` in the WebUI is equivalent to `OLLAMA_BASE_URL/api/tags` in the backend.
- **Security Benefits**: This design prevents direct exposure of the Ollama API to the frontend, safeguarding against potential CORS (Cross-Origin Resource Sharing) issues and unauthorized access. Requiring authentication to access the Ollama API further enhances this security layer. - **Security Benefits**: This design prevents direct exposure of the Ollama API to the frontend, safeguarding against potential CORS (Cross-Origin Resource Sharing) issues and unauthorized access. Requiring authentication to access the Ollama API further enhances this security layer.
@ -15,7 +15,7 @@ If you're experiencing connection issues, its often due to the WebUI docker c
**Example Docker Command**: **Example Docker Command**:
```bash ```bash
docker run -d --network=host -v open-webui:/app/backend/data -e OLLAMA_API_BASE_URL=http://127.0.0.1:11434/api --name open-webui --restart always ghcr.io/open-webui/open-webui:main 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
``` ```
### General Connection Errors ### General Connection Errors
@ -25,8 +25,8 @@ docker run -d --network=host -v open-webui:/app/backend/data -e OLLAMA_API_BASE_
**Troubleshooting Steps**: **Troubleshooting Steps**:
1. **Verify Ollama URL Format**: 1. **Verify Ollama URL Format**:
- When running the Web UI container, ensure the `OLLAMA_API_BASE_URL` is correctly set, including the `/api` suffix. (e.g., `http://192.168.1.1:11434/api` for different host setups). - When running the Web UI container, ensure the `OLLAMA_BASE_URL` is correctly set. (e.g., `http://192.168.1.1:11434` for different host setups).
- In the Open WebUI, navigate to "Settings" > "General". - In the Open WebUI, navigate to "Settings" > "General".
- Confirm that the Ollama Server URL is correctly set to `[OLLAMA URL]/api` (e.g., `http://localhost:11434/api`), including the `/api` suffix. - Confirm that the Ollama Server URL is correctly set to `[OLLAMA URL]` (e.g., `http://localhost:11434`).
By following these enhanced troubleshooting steps, connection issues should be effectively resolved. For further assistance or queries, feel free to reach out to us on our community Discord. By following these enhanced troubleshooting steps, connection issues should be effectively resolved. For further assistance or queries, feel free to reach out to us on our community Discord.

View file

@ -15,7 +15,7 @@ import asyncio
from apps.web.models.users import Users from apps.web.models.users import Users
from constants import ERROR_MESSAGES from constants import ERROR_MESSAGES
from utils.utils import decode_token, get_current_user, get_admin_user from utils.utils import decode_token, get_current_user, get_admin_user
from config import OLLAMA_BASE_URL, WEBUI_AUTH from config import OLLAMA_BASE_URLS
from typing import Optional, List, Union from typing import Optional, List, Union
@ -29,8 +29,7 @@ app.add_middleware(
allow_headers=["*"], allow_headers=["*"],
) )
app.state.OLLAMA_BASE_URL = OLLAMA_BASE_URL app.state.OLLAMA_BASE_URLS = OLLAMA_BASE_URLS
app.state.OLLAMA_BASE_URLS = [OLLAMA_BASE_URL]
app.state.MODELS = {} app.state.MODELS = {}
@ -223,7 +222,7 @@ async def pull_model(
r = requests.request( r = requests.request(
method="POST", method="POST",
url=f"{url}/api/pull", url=f"{url}/api/pull",
data=form_data.model_dump_json(exclude_none=True), data=form_data.model_dump_json(exclude_none=True).encode(),
stream=True, stream=True,
) )
@ -295,7 +294,7 @@ async def push_model(
r = requests.request( r = requests.request(
method="POST", method="POST",
url=f"{url}/api/push", url=f"{url}/api/push",
data=form_data.model_dump_json(exclude_none=True), data=form_data.model_dump_json(exclude_none=True).encode(),
) )
r.raise_for_status() r.raise_for_status()
@ -357,7 +356,7 @@ async def create_model(
r = requests.request( r = requests.request(
method="POST", method="POST",
url=f"{url}/api/create", url=f"{url}/api/create",
data=form_data.model_dump_json(exclude_none=True), data=form_data.model_dump_json(exclude_none=True).encode(),
stream=True, stream=True,
) )
@ -420,7 +419,7 @@ async def copy_model(
r = requests.request( r = requests.request(
method="POST", method="POST",
url=f"{url}/api/copy", url=f"{url}/api/copy",
data=form_data.model_dump_json(exclude_none=True), data=form_data.model_dump_json(exclude_none=True).encode(),
) )
r.raise_for_status() r.raise_for_status()
@ -467,7 +466,7 @@ async def delete_model(
r = requests.request( r = requests.request(
method="DELETE", method="DELETE",
url=f"{url}/api/delete", url=f"{url}/api/delete",
data=form_data.model_dump_json(exclude_none=True), data=form_data.model_dump_json(exclude_none=True).encode(),
) )
r.raise_for_status() r.raise_for_status()
@ -507,7 +506,7 @@ async def show_model_info(form_data: ModelNameForm, user=Depends(get_current_use
r = requests.request( r = requests.request(
method="POST", method="POST",
url=f"{url}/api/show", url=f"{url}/api/show",
data=form_data.model_dump_json(exclude_none=True), data=form_data.model_dump_json(exclude_none=True).encode(),
) )
r.raise_for_status() r.raise_for_status()
@ -559,7 +558,7 @@ async def generate_embeddings(
r = requests.request( r = requests.request(
method="POST", method="POST",
url=f"{url}/api/embeddings", url=f"{url}/api/embeddings",
data=form_data.model_dump_json(exclude_none=True), data=form_data.model_dump_json(exclude_none=True).encode(),
) )
r.raise_for_status() r.raise_for_status()
@ -645,7 +644,7 @@ async def generate_completion(
r = requests.request( r = requests.request(
method="POST", method="POST",
url=f"{url}/api/generate", url=f"{url}/api/generate",
data=form_data.model_dump_json(exclude_none=True), data=form_data.model_dump_json(exclude_none=True).encode(),
stream=True, stream=True,
) )
@ -715,7 +714,7 @@ async def generate_chat_completion(
r = None r = None
print(form_data.model_dump_json(exclude_none=True)) print(form_data.model_dump_json(exclude_none=True).encode())
def get_request(): def get_request():
nonlocal form_data nonlocal form_data
@ -745,7 +744,7 @@ async def generate_chat_completion(
r = requests.request( r = requests.request(
method="POST", method="POST",
url=f"{url}/api/chat", url=f"{url}/api/chat",
data=form_data.model_dump_json(exclude_none=True), data=form_data.model_dump_json(exclude_none=True).encode(),
stream=True, stream=True,
) )
@ -757,6 +756,7 @@ async def generate_chat_completion(
headers=dict(r.headers), headers=dict(r.headers),
) )
except Exception as e: except Exception as e:
print(e)
raise e raise e
try: try:
@ -844,7 +844,7 @@ async def generate_openai_chat_completion(
r = requests.request( r = requests.request(
method="POST", method="POST",
url=f"{url}/v1/chat/completions", url=f"{url}/v1/chat/completions",
data=form_data.model_dump_json(exclude_none=True), data=form_data.model_dump_json(exclude_none=True).encode(),
stream=True, stream=True,
) )

View file

@ -3,7 +3,10 @@ from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import StreamingResponse, JSONResponse, FileResponse from fastapi.responses import StreamingResponse, JSONResponse, FileResponse
import requests import requests
import aiohttp
import asyncio
import json import json
from pydantic import BaseModel from pydantic import BaseModel
@ -15,7 +18,9 @@ from utils.utils import (
get_verified_user, get_verified_user,
get_admin_user, get_admin_user,
) )
from config import OPENAI_API_BASE_URL, OPENAI_API_KEY, CACHE_DIR from config import OPENAI_API_BASE_URLS, OPENAI_API_KEYS, CACHE_DIR
from typing import List, Optional
import hashlib import hashlib
from pathlib import Path from pathlib import Path
@ -29,116 +34,207 @@ app.add_middleware(
allow_headers=["*"], allow_headers=["*"],
) )
app.state.OPENAI_API_BASE_URL = OPENAI_API_BASE_URL app.state.OPENAI_API_BASE_URLS = OPENAI_API_BASE_URLS
app.state.OPENAI_API_KEY = OPENAI_API_KEY app.state.OPENAI_API_KEYS = OPENAI_API_KEYS
app.state.MODELS = {}
class UrlUpdateForm(BaseModel): @app.middleware("http")
url: str async def check_url(request: Request, call_next):
if len(app.state.MODELS) == 0:
await get_all_models()
else:
pass
response = await call_next(request)
return response
class KeyUpdateForm(BaseModel): class UrlsUpdateForm(BaseModel):
key: str urls: List[str]
@app.get("/url") class KeysUpdateForm(BaseModel):
async def get_openai_url(user=Depends(get_admin_user)): keys: List[str]
return {"OPENAI_API_BASE_URL": app.state.OPENAI_API_BASE_URL}
@app.post("/url/update") @app.get("/urls")
async def update_openai_url(form_data: UrlUpdateForm, user=Depends(get_admin_user)): async def get_openai_urls(user=Depends(get_admin_user)):
app.state.OPENAI_API_BASE_URL = form_data.url return {"OPENAI_API_BASE_URLS": app.state.OPENAI_API_BASE_URLS}
return {"OPENAI_API_BASE_URL": app.state.OPENAI_API_BASE_URL}
@app.get("/key") @app.post("/urls/update")
async def get_openai_key(user=Depends(get_admin_user)): async def update_openai_urls(form_data: UrlsUpdateForm, user=Depends(get_admin_user)):
return {"OPENAI_API_KEY": app.state.OPENAI_API_KEY} app.state.OPENAI_API_BASE_URLS = form_data.urls
return {"OPENAI_API_BASE_URLS": app.state.OPENAI_API_BASE_URLS}
@app.post("/key/update") @app.get("/keys")
async def update_openai_key(form_data: KeyUpdateForm, user=Depends(get_admin_user)): async def get_openai_keys(user=Depends(get_admin_user)):
app.state.OPENAI_API_KEY = form_data.key return {"OPENAI_API_KEYS": app.state.OPENAI_API_KEYS}
return {"OPENAI_API_KEY": app.state.OPENAI_API_KEY}
@app.post("/keys/update")
async def update_openai_key(form_data: KeysUpdateForm, user=Depends(get_admin_user)):
app.state.OPENAI_API_KEYS = form_data.keys
return {"OPENAI_API_KEYS": app.state.OPENAI_API_KEYS}
@app.post("/audio/speech") @app.post("/audio/speech")
async def speech(request: Request, user=Depends(get_verified_user)): async def speech(request: Request, user=Depends(get_verified_user)):
target_url = f"{app.state.OPENAI_API_BASE_URL}/audio/speech" idx = None
if app.state.OPENAI_API_KEY == "":
raise HTTPException(status_code=401, detail=ERROR_MESSAGES.API_KEY_NOT_FOUND)
body = await request.body()
name = hashlib.sha256(body).hexdigest()
SPEECH_CACHE_DIR = Path(CACHE_DIR).joinpath("./audio/speech/")
SPEECH_CACHE_DIR.mkdir(parents=True, exist_ok=True)
file_path = SPEECH_CACHE_DIR.joinpath(f"{name}.mp3")
file_body_path = SPEECH_CACHE_DIR.joinpath(f"{name}.json")
# Check if the file already exists in the cache
if file_path.is_file():
return FileResponse(file_path)
headers = {}
headers["Authorization"] = f"Bearer {app.state.OPENAI_API_KEY}"
headers["Content-Type"] = "application/json"
try: try:
print("openai") idx = app.state.OPENAI_API_BASE_URLS.index("https://api.openai.com/v1")
r = requests.post( body = await request.body()
url=target_url, name = hashlib.sha256(body).hexdigest()
data=body,
headers=headers, SPEECH_CACHE_DIR = Path(CACHE_DIR).joinpath("./audio/speech/")
stream=True, SPEECH_CACHE_DIR.mkdir(parents=True, exist_ok=True)
file_path = SPEECH_CACHE_DIR.joinpath(f"{name}.mp3")
file_body_path = SPEECH_CACHE_DIR.joinpath(f"{name}.json")
# Check if the file already exists in the cache
if file_path.is_file():
return FileResponse(file_path)
headers = {}
headers["Authorization"] = f"Bearer {app.state.OPENAI_API_KEYS[idx]}"
headers["Content-Type"] = "application/json"
try:
r = requests.post(
url=f"{app.state.OPENAI_API_BASE_URLS[idx]}/audio/speech",
data=body,
headers=headers,
stream=True,
)
r.raise_for_status()
# Save the streaming content to a file
with open(file_path, "wb") as f:
for chunk in r.iter_content(chunk_size=8192):
f.write(chunk)
with open(file_body_path, "w") as f:
json.dump(json.loads(body.decode("utf-8")), f)
# Return the saved file
return FileResponse(file_path)
except Exception as e:
print(e)
error_detail = "Open WebUI: Server Connection Error"
if r is not None:
try:
res = r.json()
if "error" in res:
error_detail = f"External: {res['error']}"
except:
error_detail = f"External: {e}"
raise HTTPException(status_code=r.status_code, detail=error_detail)
except ValueError:
raise HTTPException(status_code=401, detail=ERROR_MESSAGES.OPENAI_NOT_FOUND)
async def fetch_url(url, key):
try:
headers = {"Authorization": f"Bearer {key}"}
async with aiohttp.ClientSession() as session:
async with session.get(url, headers=headers) as response:
return await response.json()
except Exception as e:
# Handle connection error here
print(f"Connection error: {e}")
return None
def merge_models_lists(model_lists):
merged_list = []
for idx, models in enumerate(model_lists):
merged_list.extend(
[
{**model, "urlIdx": idx}
for model in models
if "api.openai.com" not in app.state.OPENAI_API_BASE_URLS[idx]
or "gpt" in model["id"]
]
) )
r.raise_for_status() return merged_list
# Save the streaming content to a file
with open(file_path, "wb") as f:
for chunk in r.iter_content(chunk_size=8192):
f.write(chunk)
with open(file_body_path, "w") as f: async def get_all_models():
json.dump(json.loads(body.decode("utf-8")), f) print("get_all_models")
tasks = [
fetch_url(f"{url}/models", app.state.OPENAI_API_KEYS[idx])
for idx, url in enumerate(app.state.OPENAI_API_BASE_URLS)
]
responses = await asyncio.gather(*tasks)
responses = list(filter(lambda x: x is not None and "error" not in x, responses))
models = {
"data": merge_models_lists(
list(map(lambda response: response["data"], responses))
)
}
app.state.MODELS = {model["id"]: model for model in models["data"]}
# Return the saved file return models
return FileResponse(file_path)
except Exception as e:
print(e)
error_detail = "Open WebUI: Server Connection Error"
if r is not None:
try:
res = r.json()
if "error" in res:
error_detail = f"External: {res['error']}"
except:
error_detail = f"External: {e}"
raise HTTPException(status_code=r.status_code, detail=error_detail) # , user=Depends(get_current_user)
@app.get("/models")
@app.get("/models/{url_idx}")
async def get_models(url_idx: Optional[int] = None):
if url_idx == None:
return await get_all_models()
else:
url = app.state.OPENAI_API_BASE_URLS[url_idx]
try:
r = requests.request(method="GET", url=f"{url}/models")
r.raise_for_status()
response_data = r.json()
if "api.openai.com" in url:
response_data["data"] = list(
filter(lambda model: "gpt" in model["id"], response_data["data"])
)
return response_data
except Exception as e:
print(e)
error_detail = "Open WebUI: Server Connection Error"
if r is not None:
try:
res = r.json()
if "error" in res:
error_detail = f"External: {res['error']}"
except:
error_detail = f"External: {e}"
raise HTTPException(
status_code=r.status_code if r else 500,
detail=error_detail,
)
@app.api_route("/{path:path}", methods=["GET", "POST", "PUT", "DELETE"]) @app.api_route("/{path:path}", methods=["GET", "POST", "PUT", "DELETE"])
async def proxy(path: str, request: Request, user=Depends(get_verified_user)): async def proxy(path: str, request: Request, user=Depends(get_verified_user)):
target_url = f"{app.state.OPENAI_API_BASE_URL}/{path}" idx = 0
print(target_url, app.state.OPENAI_API_KEY)
if app.state.OPENAI_API_KEY == "":
raise HTTPException(status_code=401, detail=ERROR_MESSAGES.API_KEY_NOT_FOUND)
body = await request.body() body = await request.body()
# TODO: Remove below after gpt-4-vision fix from Open AI # TODO: Remove below after gpt-4-vision fix from Open AI
# Try to decode the body of the request from bytes to a UTF-8 string (Require add max_token to fix gpt-4-vision) # Try to decode the body of the request from bytes to a UTF-8 string (Require add max_token to fix gpt-4-vision)
try: try:
body = body.decode("utf-8") body = body.decode("utf-8")
body = json.loads(body) body = json.loads(body)
idx = app.state.MODELS[body.get("model")]["urlIdx"]
# Check if the model is "gpt-4-vision-preview" and set "max_tokens" to 4000 # Check if the model is "gpt-4-vision-preview" and set "max_tokens" to 4000
# This is a workaround until OpenAI fixes the issue with this model # This is a workaround until OpenAI fixes the issue with this model
if body.get("model") == "gpt-4-vision-preview": if body.get("model") == "gpt-4-vision-preview":
@ -158,8 +254,16 @@ async def proxy(path: str, request: Request, user=Depends(get_verified_user)):
except json.JSONDecodeError as e: except json.JSONDecodeError as e:
print("Error loading request body into a dictionary:", e) print("Error loading request body into a dictionary:", e)
url = app.state.OPENAI_API_BASE_URLS[idx]
key = app.state.OPENAI_API_KEYS[idx]
target_url = f"{url}/{path}"
if key == "":
raise HTTPException(status_code=401, detail=ERROR_MESSAGES.API_KEY_NOT_FOUND)
headers = {} headers = {}
headers["Authorization"] = f"Bearer {app.state.OPENAI_API_KEY}" headers["Authorization"] = f"Bearer {key}"
headers["Content-Type"] = "application/json" headers["Content-Type"] = "application/json"
try: try:
@ -181,21 +285,7 @@ async def proxy(path: str, request: Request, user=Depends(get_verified_user)):
headers=dict(r.headers), headers=dict(r.headers),
) )
else: else:
# For non-SSE, read the response and return it
# response_data = (
# r.json()
# if r.headers.get("Content-Type", "")
# == "application/json"
# else r.text
# )
response_data = r.json() response_data = r.json()
if "api.openai.com" in app.state.OPENAI_API_BASE_URL and path == "models":
response_data["data"] = list(
filter(lambda model: "gpt" in model["id"], response_data["data"])
)
return response_data return response_data
except Exception as e: except Exception as e:
print(e) print(e)

View file

@ -425,7 +425,7 @@ def get_loader(filename: str, file_content_type: str, file_path: str):
] ]
if file_ext == "pdf": if file_ext == "pdf":
loader = PyPDFLoader(file_path) loader = PyPDFLoader(file_path, extract_images=True)
elif file_ext == "csv": elif file_ext == "csv":
loader = CSVLoader(file_path) loader = CSVLoader(file_path)
elif file_ext == "rst": elif file_ext == "rst":

View file

@ -14,7 +14,7 @@ import json
from utils.utils import get_admin_user from utils.utils import get_admin_user
from utils.misc import calculate_sha256, get_gravatar_url from utils.misc import calculate_sha256, get_gravatar_url
from config import OLLAMA_API_BASE_URL, DATA_DIR, UPLOAD_DIR from config import OLLAMA_BASE_URLS, DATA_DIR, UPLOAD_DIR
from constants import ERROR_MESSAGES from constants import ERROR_MESSAGES
@ -75,7 +75,7 @@ async def download_file_stream(url, file_path, file_name, chunk_size=1024 * 1024
hashed = calculate_sha256(file) hashed = calculate_sha256(file)
file.seek(0) file.seek(0)
url = f"{OLLAMA_API_BASE_URL}/blobs/sha256:{hashed}" url = f"{OLLAMA_BASE_URLS[0]}/blobs/sha256:{hashed}"
response = requests.post(url, data=file) response = requests.post(url, data=file)
if response.ok: if response.ok:
@ -147,7 +147,7 @@ def upload(file: UploadFile = File(...)):
hashed = calculate_sha256(f) hashed = calculate_sha256(f)
f.seek(0) f.seek(0)
url = f"{OLLAMA_API_BASE_URL}/blobs/sha256:{hashed}" url = f"{OLLAMA_BASE_URLS[0]}/blobs/sha256:{hashed}"
response = requests.post(url, data=f) response = requests.post(url, data=f)
if response.ok: if response.ok:

View file

@ -200,27 +200,32 @@ if not os.path.exists(LITELLM_CONFIG_PATH):
#################################### ####################################
# OLLAMA_API_BASE_URL # OLLAMA_BASE_URL
#################################### ####################################
OLLAMA_API_BASE_URL = os.environ.get( OLLAMA_API_BASE_URL = os.environ.get(
"OLLAMA_API_BASE_URL", "http://localhost:11434/api" "OLLAMA_API_BASE_URL", "http://localhost:11434/api"
) )
if ENV == "prod":
if OLLAMA_API_BASE_URL == "/ollama/api":
OLLAMA_API_BASE_URL = "http://host.docker.internal:11434/api"
OLLAMA_BASE_URL = os.environ.get("OLLAMA_BASE_URL", "") OLLAMA_BASE_URL = os.environ.get("OLLAMA_BASE_URL", "")
if OLLAMA_BASE_URL == "": if ENV == "prod":
if OLLAMA_BASE_URL == "/ollama":
OLLAMA_BASE_URL = "http://host.docker.internal:11434"
if OLLAMA_BASE_URL == "" and OLLAMA_API_BASE_URL != "":
OLLAMA_BASE_URL = ( OLLAMA_BASE_URL = (
OLLAMA_API_BASE_URL[:-4] OLLAMA_API_BASE_URL[:-4]
if OLLAMA_API_BASE_URL.endswith("/api") if OLLAMA_API_BASE_URL.endswith("/api")
else OLLAMA_API_BASE_URL else OLLAMA_API_BASE_URL
) )
OLLAMA_BASE_URLS = os.environ.get("OLLAMA_BASE_URLS", "")
OLLAMA_BASE_URLS = OLLAMA_BASE_URLS if OLLAMA_BASE_URLS != "" else OLLAMA_BASE_URL
OLLAMA_BASE_URLS = [url.strip() for url in OLLAMA_BASE_URLS.split(";")]
#################################### ####################################
# OPENAI_API # OPENAI_API
@ -229,9 +234,25 @@ if OLLAMA_BASE_URL == "":
OPENAI_API_KEY = os.environ.get("OPENAI_API_KEY", "") OPENAI_API_KEY = os.environ.get("OPENAI_API_KEY", "")
OPENAI_API_BASE_URL = os.environ.get("OPENAI_API_BASE_URL", "") OPENAI_API_BASE_URL = os.environ.get("OPENAI_API_BASE_URL", "")
if OPENAI_API_KEY == "":
OPENAI_API_KEY = "none"
if OPENAI_API_BASE_URL == "": if OPENAI_API_BASE_URL == "":
OPENAI_API_BASE_URL = "https://api.openai.com/v1" OPENAI_API_BASE_URL = "https://api.openai.com/v1"
OPENAI_API_KEYS = os.environ.get("OPENAI_API_KEYS", "")
OPENAI_API_KEYS = OPENAI_API_KEYS if OPENAI_API_KEYS != "" else OPENAI_API_KEY
OPENAI_API_KEYS = [url.strip() for url in OPENAI_API_KEYS.split(";")]
OPENAI_API_BASE_URLS = os.environ.get("OPENAI_API_BASE_URLS", "")
OPENAI_API_BASE_URLS = (
OPENAI_API_BASE_URLS if OPENAI_API_BASE_URLS != "" else OPENAI_API_BASE_URL
)
OPENAI_API_BASE_URLS = [url.strip() for url in OPENAI_API_BASE_URL.split(";")]
#################################### ####################################
# WEBUI # WEBUI

View file

@ -41,6 +41,7 @@ class ERROR_MESSAGES(str, Enum):
NOT_FOUND = "We could not find what you're looking for :/" NOT_FOUND = "We could not find what you're looking for :/"
USER_NOT_FOUND = "We could not find what you're looking for :/" USER_NOT_FOUND = "We could not find what you're looking for :/"
API_KEY_NOT_FOUND = "Oops! It looks like there's a hiccup. The API key is missing. Please make sure to provide a valid API key to access this feature." API_KEY_NOT_FOUND = "Oops! It looks like there's a hiccup. The API key is missing. Please make sure to provide a valid API key to access this feature."
MALICIOUS = "Unusual activities detected, please try again in a few minutes." MALICIOUS = "Unusual activities detected, please try again in a few minutes."
PANDOC_NOT_INSTALLED = "Pandoc is not installed on the server. Please contact your administrator for assistance." PANDOC_NOT_INSTALLED = "Pandoc is not installed on the server. Please contact your administrator for assistance."
@ -50,3 +51,4 @@ class ERROR_MESSAGES(str, Enum):
RATE_LIMIT_EXCEEDED = "API rate limit exceeded" RATE_LIMIT_EXCEEDED = "API rate limit exceeded"
MODEL_NOT_FOUND = lambda name="": f"Model '{name}' was not found" MODEL_NOT_FOUND = lambda name="": f"Model '{name}' was not found"
OPENAI_NOT_FOUND = lambda name="": f"OpenAI API was not found"

View file

@ -35,6 +35,9 @@ openpyxl
pyxlsb pyxlsb
xlrd xlrd
opencv-python-headless
rapidocr-onnxruntime
faster-whisper faster-whisper
PyJWT PyJWT

View file

@ -14,7 +14,7 @@ services:
build: build:
context: . context: .
args: args:
OLLAMA_API_BASE_URL: '/ollama/api' OLLAMA_BASE_URL: '/ollama'
dockerfile: Dockerfile dockerfile: Dockerfile
image: ghcr.io/open-webui/open-webui:main image: ghcr.io/open-webui/open-webui:main
container_name: open-webui container_name: open-webui
@ -25,7 +25,7 @@ services:
ports: ports:
- ${OPEN_WEBUI_PORT-3000}:8080 - ${OPEN_WEBUI_PORT-3000}:8080
environment: environment:
- 'OLLAMA_API_BASE_URL=http://ollama:11434/api' - 'OLLAMA_BASE_URL=http://ollama:11434'
- 'WEBUI_SECRET_KEY=' - 'WEBUI_SECRET_KEY='
extra_hosts: extra_hosts:
- host.docker.internal:host-gateway - host.docker.internal:host-gateway

38
i18next-parser.config.ts Normal file
View file

@ -0,0 +1,38 @@
// i18next-parser.config.ts
import { getLanguages } from './src/lib/i18n/index.ts';
const getLangCodes = async () => {
const languages = await getLanguages();
return languages.map((l) => l.code);
};
export default {
contextSeparator: '_',
createOldCatalogs: false,
defaultNamespace: 'translation',
defaultValue: '',
indentation: 2,
keepRemoved: false,
keySeparator: false,
lexers: {
svelte: ['JavascriptLexer'],
js: ['JavascriptLexer'], // if you're writing jsx inside .js files, change this to JsxLexer
ts: ['JavascriptLexer'],
default: ['JavascriptLexer']
},
lineEnding: 'auto',
locales: await getLangCodes(),
namespaceSeparator: false,
output: 'src/lib/i18n/locales/$LOCALE/$NAMESPACE.json',
pluralSeparator: '_',
input: 'src/**/*.{js,svelte}',
sort: true,
verbose: true,
failOnWarnings: false,
failOnUpdate: false,
customValueTemplate: null,
resetDefaultValueLocale: null,
i18nextOptions: null,
yamlOptions: null
};

View file

@ -40,7 +40,7 @@ spec:
- name: data - name: data
mountPath: /app/backend/data mountPath: /app/backend/data
env: env:
- name: OLLAMA_API_BASE_URL - name: OLLAMA_BASE_URL
value: {{ include "ollama.url" . | quote }} value: {{ include "ollama.url" . | quote }}
tty: true tty: true
{{- with .Values.webui.nodeSelector }} {{- with .Values.webui.nodeSelector }}

View file

@ -26,8 +26,8 @@ spec:
cpu: "1000m" cpu: "1000m"
memory: "1Gi" memory: "1Gi"
env: env:
- name: OLLAMA_API_BASE_URL - name: OLLAMA_BASE_URL
value: "http://ollama-service.open-webui.svc.cluster.local:11434/api" value: "http://ollama-service.open-webui.svc.cluster.local:11434"
tty: true tty: true
volumeMounts: volumeMounts:
- name: webui-volume - name: webui-volume

View file

@ -1,6 +1,6 @@
{ {
"name": "open-webui", "name": "open-webui",
"version": "0.1.108", "version": "0.1.110",
"private": true, "private": true,
"scripts": { "scripts": {
"dev": "vite dev --host", "dev": "vite dev --host",
@ -27,6 +27,7 @@
"eslint": "^8.56.0", "eslint": "^8.56.0",
"eslint-config-prettier": "^8.5.0", "eslint-config-prettier": "^8.5.0",
"eslint-plugin-svelte": "^2.30.0", "eslint-plugin-svelte": "^2.30.0",
"i18next-parser": "^8.13.0",
"postcss": "^8.4.31", "postcss": "^8.4.31",
"prettier": "^2.8.0", "prettier": "^2.8.0",
"prettier-plugin-svelte": "^2.10.1", "prettier-plugin-svelte": "^2.10.1",

View file

@ -43,6 +43,10 @@ ol > li {
font-weight: 400; font-weight: 400;
} }
li p {
display: inline;
}
::-webkit-scrollbar-thumb { ::-webkit-scrollbar-thumb {
--tw-border-opacity: 1; --tw-border-opacity: 1;
background-color: rgba(217, 217, 227, 0.8); background-color: rgba(217, 217, 227, 0.8);

View file

@ -1,9 +1,9 @@
import { OPENAI_API_BASE_URL } from '$lib/constants'; import { OPENAI_API_BASE_URL } from '$lib/constants';
export const getOpenAIUrl = async (token: string = '') => { export const getOpenAIUrls = async (token: string = '') => {
let error = null; let error = null;
const res = await fetch(`${OPENAI_API_BASE_URL}/url`, { const res = await fetch(`${OPENAI_API_BASE_URL}/urls`, {
method: 'GET', method: 'GET',
headers: { headers: {
Accept: 'application/json', Accept: 'application/json',
@ -29,13 +29,13 @@ export const getOpenAIUrl = async (token: string = '') => {
throw error; throw error;
} }
return res.OPENAI_API_BASE_URL; return res.OPENAI_API_BASE_URLS;
}; };
export const updateOpenAIUrl = async (token: string = '', url: string) => { export const updateOpenAIUrls = async (token: string = '', urls: string[]) => {
let error = null; let error = null;
const res = await fetch(`${OPENAI_API_BASE_URL}/url/update`, { const res = await fetch(`${OPENAI_API_BASE_URL}/urls/update`, {
method: 'POST', method: 'POST',
headers: { headers: {
Accept: 'application/json', Accept: 'application/json',
@ -43,7 +43,7 @@ export const updateOpenAIUrl = async (token: string = '', url: string) => {
...(token && { authorization: `Bearer ${token}` }) ...(token && { authorization: `Bearer ${token}` })
}, },
body: JSON.stringify({ body: JSON.stringify({
url: url urls: urls
}) })
}) })
.then(async (res) => { .then(async (res) => {
@ -64,13 +64,13 @@ export const updateOpenAIUrl = async (token: string = '', url: string) => {
throw error; throw error;
} }
return res.OPENAI_API_BASE_URL; return res.OPENAI_API_BASE_URLS;
}; };
export const getOpenAIKey = async (token: string = '') => { export const getOpenAIKeys = async (token: string = '') => {
let error = null; let error = null;
const res = await fetch(`${OPENAI_API_BASE_URL}/key`, { const res = await fetch(`${OPENAI_API_BASE_URL}/keys`, {
method: 'GET', method: 'GET',
headers: { headers: {
Accept: 'application/json', Accept: 'application/json',
@ -96,13 +96,13 @@ export const getOpenAIKey = async (token: string = '') => {
throw error; throw error;
} }
return res.OPENAI_API_KEY; return res.OPENAI_API_KEYS;
}; };
export const updateOpenAIKey = async (token: string = '', key: string) => { export const updateOpenAIKeys = async (token: string = '', keys: string[]) => {
let error = null; let error = null;
const res = await fetch(`${OPENAI_API_BASE_URL}/key/update`, { const res = await fetch(`${OPENAI_API_BASE_URL}/keys/update`, {
method: 'POST', method: 'POST',
headers: { headers: {
Accept: 'application/json', Accept: 'application/json',
@ -110,7 +110,7 @@ export const updateOpenAIKey = async (token: string = '', key: string) => {
...(token && { authorization: `Bearer ${token}` }) ...(token && { authorization: `Bearer ${token}` })
}, },
body: JSON.stringify({ body: JSON.stringify({
key: key keys: keys
}) })
}) })
.then(async (res) => { .then(async (res) => {
@ -131,7 +131,7 @@ export const updateOpenAIKey = async (token: string = '', key: string) => {
throw error; throw error;
} }
return res.OPENAI_API_KEY; return res.OPENAI_API_KEYS;
}; };
export const getOpenAIModels = async (token: string = '') => { export const getOpenAIModels = async (token: string = '') => {

View file

@ -225,33 +225,80 @@
}, 100); }, 100);
}; };
// TODO: change delete behaviour
// const deleteMessageAndDescendants = async (messageId: string) => {
// if (history.messages[messageId]) {
// history.messages[messageId].deleted = true;
// for (const childId of history.messages[messageId].childrenIds) {
// await deleteMessageAndDescendants(childId);
// }
// }
// };
// const triggerDeleteMessageRecursive = async (messageId: string) => {
// await deleteMessageAndDescendants(messageId);
// await updateChatById(localStorage.token, chatId, { history });
// await chats.set(await getChatList(localStorage.token));
// };
const messageDeleteHandler = async (messageId) => { const messageDeleteHandler = async (messageId) => {
if (history.messages[messageId]) { const messageToDelete = history.messages[messageId];
history.messages[messageId].deleted = true; const messageParentId = messageToDelete.parentId;
const messageChildrenIds = messageToDelete.childrenIds ?? [];
for (const childId of history.messages[messageId].childrenIds) { const hasSibling = messageChildrenIds.some(
history.messages[childId].deleted = true; (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;
} else {
child.childrenIds.forEach((grandChildId) => {
if (history.messages[grandChildId]) {
history.messages[grandChildId].parentId = messageParentId;
history.messages[messageParentId].childrenIds.push(grandChildId);
}
});
}
} }
} // remove response
await updateChatById(localStorage.token, chatId, { history }); history.messages[messageParentId].childrenIds = history.messages[
messageParentId
].childrenIds.filter((id) => id !== childId);
});
// remove prompt
history.messages[messageParentId].childrenIds = history.messages[
messageParentId
].childrenIds.filter((id) => id !== messageId);
await updateChatById(localStorage.token, chatId, {
messages: messages,
history: history
});
}; };
// const messageDeleteHandler = async (messageId) => {
// const message = history.messages[messageId];
// const parentId = message.parentId;
// const childrenIds = message.childrenIds ?? [];
// const grandchildrenIds = [];
// // Iterate through childrenIds to find grandchildrenIds
// for (const childId of childrenIds) {
// const childMessage = history.messages[childId];
// const grandChildrenIds = childMessage.childrenIds ?? [];
// for (const grandchildId of grandchildrenIds) {
// const childMessage = history.messages[grandchildId];
// childMessage.parentId = parentId;
// }
// grandchildrenIds.push(...grandChildrenIds);
// }
// history.messages[parentId].childrenIds.push(...grandchildrenIds);
// history.messages[parentId].childrenIds = history.messages[parentId].childrenIds.filter(
// (id) => id !== messageId
// );
// // Select latest message
// let currentMessageId = grandchildrenIds.at(-1);
// if (currentMessageId) {
// let messageChildrenIds = history.messages[currentMessageId].childrenIds;
// while (messageChildrenIds.length !== 0) {
// currentMessageId = messageChildrenIds.at(-1);
// messageChildrenIds = history.messages[currentMessageId].childrenIds;
// }
// history.currentId = currentMessageId;
// }
// await updateChatById(localStorage.token, chatId, { messages, history });
// };
</script> </script>
{#if messages.length == 0} {#if messages.length == 0}
@ -260,57 +307,55 @@
<div class=" pb-10"> <div class=" pb-10">
{#key chatId} {#key chatId}
{#each messages as message, messageIdx} {#each messages as message, messageIdx}
{#if !message.deleted} <div class=" w-full">
<div class=" w-full"> <div
<div class="flex flex-col justify-between px-5 mb-3 {$settings?.fullScreenMode ?? null
class="flex flex-col justify-between px-5 mb-3 {$settings?.fullScreenMode ?? null ? 'max-w-full'
? 'max-w-full' : 'max-w-3xl'} mx-auto rounded-lg group"
: 'max-w-3xl'} mx-auto rounded-lg group" >
> {#if message.role === 'user'}
{#if message.role === 'user'} <UserMessage
<UserMessage on:delete={() => messageDeleteHandler(message.id)}
on:delete={() => messageDeleteHandler(message.id)} user={$user}
user={$user} {message}
{message} isFirstMessage={messageIdx === 0}
isFirstMessage={messageIdx === 0} siblings={message.parentId !== null
siblings={message.parentId !== null ? history.messages[message.parentId]?.childrenIds ?? []
? history.messages[message.parentId]?.childrenIds ?? [] : Object.values(history.messages)
: Object.values(history.messages) .filter((message) => message.parentId === null)
.filter((message) => message.parentId === null) .map((message) => message.id) ?? []}
.map((message) => message.id) ?? []} {confirmEditMessage}
{confirmEditMessage} {showPreviousMessage}
{showPreviousMessage} {showNextMessage}
{showNextMessage} {copyToClipboard}
{copyToClipboard} />
/> {:else}
{:else} <ResponseMessage
<ResponseMessage {message}
{message} modelfiles={selectedModelfiles}
modelfiles={selectedModelfiles} siblings={history.messages[message.parentId]?.childrenIds ?? []}
siblings={history.messages[message.parentId]?.childrenIds ?? []} isLastMessage={messageIdx + 1 === messages.length}
isLastMessage={messageIdx + 1 === messages.length} {confirmEditResponseMessage}
{confirmEditResponseMessage} {showPreviousMessage}
{showPreviousMessage} {showNextMessage}
{showNextMessage} {rateMessage}
{rateMessage} {copyToClipboard}
{copyToClipboard} {continueGeneration}
{continueGeneration} {regenerateResponse}
{regenerateResponse} on:save={async (e) => {
on:save={async (e) => { console.log('save', e);
console.log('save', e);
const message = e.detail; const message = e.detail;
history.messages[message.id] = message; history.messages[message.id] = message;
await updateChatById(localStorage.token, chatId, { await updateChatById(localStorage.token, chatId, {
messages: messages, messages: messages,
history: history history: history
}); });
}} }}
/> />
{/if} {/if}
</div>
</div> </div>
{/if} </div>
{/each} {/each}
{#if bottomPadding} {#if bottomPadding}

View file

@ -24,6 +24,7 @@
import CodeBlock from './CodeBlock.svelte'; import CodeBlock from './CodeBlock.svelte';
import Image from '$lib/components/common/Image.svelte'; import Image from '$lib/components/common/Image.svelte';
import { WEBUI_BASE_URL } from '$lib/constants'; import { WEBUI_BASE_URL } from '$lib/constants';
import Tooltip from '$lib/components/common/Tooltip.svelte';
export let modelfiles = []; export let modelfiles = [];
export let message; export let message;
@ -346,6 +347,7 @@
class=" bg-transparent outline-none w-full resize-none" class=" bg-transparent outline-none w-full resize-none"
bind:value={editedContent} bind:value={editedContent}
on:input={(e) => { on:input={(e) => {
e.target.style.height = '';
e.target.style.height = `${e.target.scrollHeight}px`; e.target.style.height = `${e.target.scrollHeight}px`;
}} }}
/> />
@ -464,189 +466,125 @@
</div> </div>
{/if} {/if}
<button <Tooltip content="Edit" placement="bottom">
class="{isLastMessage
? 'visible'
: 'invisible group-hover:visible'} p-1 rounded dark:hover:text-white hover:text-black transition"
on:click={() => {
editMessageHandler();
}}
>
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
class="w-4 h-4"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M16.862 4.487l1.687-1.688a1.875 1.875 0 112.652 2.652L6.832 19.82a4.5 4.5 0 01-1.897 1.13l-2.685.8.8-2.685a4.5 4.5 0 011.13-1.897L16.863 4.487zm0 0L19.5 7.125"
/>
</svg>
</button>
<button
class="{isLastMessage
? 'visible'
: 'invisible group-hover:visible'} p-1 rounded dark:hover:text-white hover:text-black transition copy-response-button"
on:click={() => {
copyToClipboard(message.content);
}}
>
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
class="w-4 h-4"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M15.666 3.888A2.25 2.25 0 0013.5 2.25h-3c-1.03 0-1.9.693-2.166 1.638m7.332 0c.055.194.084.4.084.612v0a.75.75 0 01-.75.75H9a.75.75 0 01-.75-.75v0c0-.212.03-.418.084-.612m7.332 0c.646.049 1.288.11 1.927.184 1.1.128 1.907 1.077 1.907 2.185V19.5a2.25 2.25 0 01-2.25 2.25H6.75A2.25 2.25 0 014.5 19.5V6.257c0-1.108.806-2.057 1.907-2.185a48.208 48.208 0 011.927-.184"
/>
</svg>
</button>
<button
class="{isLastMessage
? 'visible'
: 'invisible group-hover:visible'} p-1 rounded {message.rating === 1
? 'bg-gray-100 dark:bg-gray-800'
: ''} dark:hover:text-white hover:text-black transition"
on:click={() => {
rateMessage(message.id, 1);
}}
>
<svg
stroke="currentColor"
fill="none"
stroke-width="2"
viewBox="0 0 24 24"
stroke-linecap="round"
stroke-linejoin="round"
class="w-4 h-4"
xmlns="http://www.w3.org/2000/svg"
><path
d="M14 9V5a3 3 0 0 0-3-3l-4 9v11h11.28a2 2 0 0 0 2-1.7l1.38-9a2 2 0 0 0-2-2.3zM7 22H4a2 2 0 0 1-2-2v-7a2 2 0 0 1 2-2h3"
/></svg
>
</button>
<button
class="{isLastMessage
? 'visible'
: 'invisible group-hover:visible'} p-1 rounded {message.rating === -1
? 'bg-gray-100 dark:bg-gray-800'
: ''} dark:hover:text-white hover:text-black transition"
on:click={() => {
rateMessage(message.id, -1);
}}
>
<svg
stroke="currentColor"
fill="none"
stroke-width="2"
viewBox="0 0 24 24"
stroke-linecap="round"
stroke-linejoin="round"
class="w-4 h-4"
xmlns="http://www.w3.org/2000/svg"
><path
d="M10 15v4a3 3 0 0 0 3 3l4-9V2H5.72a2 2 0 0 0-2 1.7l-1.38 9a2 2 0 0 0 2 2.3zm7-13h2.67A2.31 2.31 0 0 1 22 4v7a2.31 2.31 0 0 1-2.33 2H17"
/></svg
>
</button>
<button
id="speak-button-{message.id}"
class="{isLastMessage
? 'visible'
: 'invisible group-hover:visible'} p-1 rounded dark:hover:text-white hover:text-black transition"
on:click={() => {
if (!loadingSpeech) {
toggleSpeakMessage(message);
}
}}
>
{#if loadingSpeech}
<svg
class=" w-4 h-4"
fill="currentColor"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
><style>
.spinner_S1WN {
animation: spinner_MGfb 0.8s linear infinite;
animation-delay: -0.8s;
}
.spinner_Km9P {
animation-delay: -0.65s;
}
.spinner_JApP {
animation-delay: -0.5s;
}
@keyframes spinner_MGfb {
93.75%,
100% {
opacity: 0.2;
}
}
</style><circle class="spinner_S1WN" cx="4" cy="12" r="3" /><circle
class="spinner_S1WN spinner_Km9P"
cx="12"
cy="12"
r="3"
/><circle class="spinner_S1WN spinner_JApP" cx="20" cy="12" r="3" /></svg
>
{:else if speaking}
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
class="w-4 h-4"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M17.25 9.75 19.5 12m0 0 2.25 2.25M19.5 12l2.25-2.25M19.5 12l-2.25 2.25m-10.5-6 4.72-4.72a.75.75 0 0 1 1.28.53v15.88a.75.75 0 0 1-1.28.53l-4.72-4.72H4.51c-.88 0-1.704-.507-1.938-1.354A9.009 9.009 0 0 1 2.25 12c0-.83.112-1.633.322-2.396C2.806 8.756 3.63 8.25 4.51 8.25H6.75Z"
/>
</svg>
{:else}
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
class="w-4 h-4"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M19.114 5.636a9 9 0 010 12.728M16.463 8.288a5.25 5.25 0 010 7.424M6.75 8.25l4.72-4.72a.75.75 0 011.28.53v15.88a.75.75 0 01-1.28.53l-4.72-4.72H4.51c-.88 0-1.704-.507-1.938-1.354A9.01 9.01 0 012.25 12c0-.83.112-1.633.322-2.396C2.806 8.756 3.63 8.25 4.51 8.25H6.75z"
/>
</svg>
{/if}
</button>
{#if $config.images}
<button <button
class="{isLastMessage class="{isLastMessage
? 'visible' ? 'visible'
: 'invisible group-hover:visible'} p-1 rounded dark:hover:text-white hover:text-black transition" : 'invisible group-hover:visible'} p-1 rounded dark:hover:text-white hover:text-black transition"
on:click={() => { on:click={() => {
if (!generatingImage) { editMessageHandler();
generateImage(message); }}
>
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
class="w-4 h-4"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M16.862 4.487l1.687-1.688a1.875 1.875 0 112.652 2.652L6.832 19.82a4.5 4.5 0 01-1.897 1.13l-2.685.8.8-2.685a4.5 4.5 0 011.13-1.897L16.863 4.487zm0 0L19.5 7.125"
/>
</svg>
</button>
</Tooltip>
<Tooltip content="Copy" placement="bottom">
<button
class="{isLastMessage
? 'visible'
: 'invisible group-hover:visible'} p-1 rounded dark:hover:text-white hover:text-black transition copy-response-button"
on:click={() => {
copyToClipboard(message.content);
}}
>
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
class="w-4 h-4"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M15.666 3.888A2.25 2.25 0 0013.5 2.25h-3c-1.03 0-1.9.693-2.166 1.638m7.332 0c.055.194.084.4.084.612v0a.75.75 0 01-.75.75H9a.75.75 0 01-.75-.75v0c0-.212.03-.418.084-.612m7.332 0c.646.049 1.288.11 1.927.184 1.1.128 1.907 1.077 1.907 2.185V19.5a2.25 2.25 0 01-2.25 2.25H6.75A2.25 2.25 0 014.5 19.5V6.257c0-1.108.806-2.057 1.907-2.185a48.208 48.208 0 011.927-.184"
/>
</svg>
</button>
</Tooltip>
<Tooltip content="Good Response" placement="bottom">
<button
class="{isLastMessage
? 'visible'
: 'invisible group-hover:visible'} p-1 rounded {message.rating === 1
? 'bg-gray-100 dark:bg-gray-800'
: ''} dark:hover:text-white hover:text-black transition"
on:click={() => {
rateMessage(message.id, 1);
}}
>
<svg
stroke="currentColor"
fill="none"
stroke-width="2"
viewBox="0 0 24 24"
stroke-linecap="round"
stroke-linejoin="round"
class="w-4 h-4"
xmlns="http://www.w3.org/2000/svg"
><path
d="M14 9V5a3 3 0 0 0-3-3l-4 9v11h11.28a2 2 0 0 0 2-1.7l1.38-9a2 2 0 0 0-2-2.3zM7 22H4a2 2 0 0 1-2-2v-7a2 2 0 0 1 2-2h3"
/></svg
>
</button>
</Tooltip>
<Tooltip content="Bad Response" placement="bottom">
<button
class="{isLastMessage
? 'visible'
: 'invisible group-hover:visible'} p-1 rounded {message.rating === -1
? 'bg-gray-100 dark:bg-gray-800'
: ''} dark:hover:text-white hover:text-black transition"
on:click={() => {
rateMessage(message.id, -1);
}}
>
<svg
stroke="currentColor"
fill="none"
stroke-width="2"
viewBox="0 0 24 24"
stroke-linecap="round"
stroke-linejoin="round"
class="w-4 h-4"
xmlns="http://www.w3.org/2000/svg"
><path
d="M10 15v4a3 3 0 0 0 3 3l4-9V2H5.72a2 2 0 0 0-2 1.7l-1.38 9a2 2 0 0 0 2 2.3zm7-13h2.67A2.31 2.31 0 0 1 22 4v7a2.31 2.31 0 0 1-2.33 2H17"
/></svg
>
</button>
</Tooltip>
<Tooltip content="Read Aloud" placement="bottom">
<button
id="speak-button-{message.id}"
class="{isLastMessage
? 'visible'
: 'invisible group-hover:visible'} p-1 rounded dark:hover:text-white hover:text-black transition"
on:click={() => {
if (!loadingSpeech) {
toggleSpeakMessage(message);
} }
}} }}
> >
{#if generatingImage} {#if loadingSpeech}
<svg <svg
class=" w-4 h-4" class=" w-4 h-4"
fill="currentColor" fill="currentColor"
@ -681,6 +619,21 @@
r="3" r="3"
/></svg /></svg
> >
{:else if speaking}
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
class="w-4 h-4"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M17.25 9.75 19.5 12m0 0 2.25 2.25M19.5 12l2.25-2.25M19.5 12l-2.25 2.25m-10.5-6 4.72-4.72a.75.75 0 0 1 1.28.53v15.88a.75.75 0 0 1-1.28.53l-4.72-4.72H4.51c-.88 0-1.704-.507-1.938-1.354A9.009 9.009 0 0 1 2.25 12c0-.83.112-1.633.322-2.396C2.806 8.756 3.63 8.25 4.51 8.25H6.75Z"
/>
</svg>
{:else} {:else}
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
@ -693,93 +646,166 @@
<path <path
stroke-linecap="round" stroke-linecap="round"
stroke-linejoin="round" stroke-linejoin="round"
d="m2.25 15.75 5.159-5.159a2.25 2.25 0 0 1 3.182 0l5.159 5.159m-1.5-1.5 1.409-1.409a2.25 2.25 0 0 1 3.182 0l2.909 2.909m-18 3.75h16.5a1.5 1.5 0 0 0 1.5-1.5V6a1.5 1.5 0 0 0-1.5-1.5H3.75A1.5 1.5 0 0 0 2.25 6v12a1.5 1.5 0 0 0 1.5 1.5Zm10.5-11.25h.008v.008h-.008V8.25Zm.375 0a.375.375 0 1 1-.75 0 .375.375 0 0 1 .75 0Z" d="M19.114 5.636a9 9 0 010 12.728M16.463 8.288a5.25 5.25 0 010 7.424M6.75 8.25l4.72-4.72a.75.75 0 011.28.53v15.88a.75.75 0 01-1.28.53l-4.72-4.72H4.51c-.88 0-1.704-.507-1.938-1.354A9.01 9.01 0 012.25 12c0-.83.112-1.633.322-2.396C2.806 8.756 3.63 8.25 4.51 8.25H6.75z"
/> />
</svg> </svg>
{/if} {/if}
</button> </button>
</Tooltip>
{#if $config.images}
<Tooltip content="Generate Image" placement="bottom">
<button
class="{isLastMessage
? 'visible'
: 'invisible group-hover:visible'} p-1 rounded dark:hover:text-white hover:text-black transition"
on:click={() => {
if (!generatingImage) {
generateImage(message);
}
}}
>
{#if generatingImage}
<svg
class=" w-4 h-4"
fill="currentColor"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
><style>
.spinner_S1WN {
animation: spinner_MGfb 0.8s linear infinite;
animation-delay: -0.8s;
}
.spinner_Km9P {
animation-delay: -0.65s;
}
.spinner_JApP {
animation-delay: -0.5s;
}
@keyframes spinner_MGfb {
93.75%,
100% {
opacity: 0.2;
}
}
</style><circle class="spinner_S1WN" cx="4" cy="12" r="3" /><circle
class="spinner_S1WN spinner_Km9P"
cx="12"
cy="12"
r="3"
/><circle
class="spinner_S1WN spinner_JApP"
cx="20"
cy="12"
r="3"
/></svg
>
{:else}
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
class="w-4 h-4"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="m2.25 15.75 5.159-5.159a2.25 2.25 0 0 1 3.182 0l5.159 5.159m-1.5-1.5 1.409-1.409a2.25 2.25 0 0 1 3.182 0l2.909 2.909m-18 3.75h16.5a1.5 1.5 0 0 0 1.5-1.5V6a1.5 1.5 0 0 0-1.5-1.5H3.75A1.5 1.5 0 0 0 2.25 6v12a1.5 1.5 0 0 0 1.5 1.5Zm10.5-11.25h.008v.008h-.008V8.25Zm.375 0a.375.375 0 1 1-.75 0 .375.375 0 0 1 .75 0Z"
/>
</svg>
{/if}
</button>
</Tooltip>
{/if} {/if}
{#if message.info} {#if message.info}
<button <Tooltip content="Generation Info" placement="bottom">
class=" {isLastMessage <button
? 'visible' class=" {isLastMessage
: 'invisible group-hover:visible'} p-1 rounded dark:hover:text-white hover:text-black transition whitespace-pre-wrap" ? 'visible'
on:click={() => { : 'invisible group-hover:visible'} p-1 rounded dark:hover:text-white hover:text-black transition whitespace-pre-wrap"
console.log(message); on:click={() => {
}} console.log(message);
id="info-{message.id}" }}
> id="info-{message.id}"
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
class="w-4 h-4"
> >
<path <svg
stroke-linecap="round" xmlns="http://www.w3.org/2000/svg"
stroke-linejoin="round" fill="none"
d="M11.25 11.25l.041-.02a.75.75 0 011.063.852l-.708 2.836a.75.75 0 001.063.853l.041-.021M21 12a9 9 0 11-18 0 9 9 0 0118 0zm-9-3.75h.008v.008H12V8.25z" viewBox="0 0 24 24"
/> stroke-width="1.5"
</svg> stroke="currentColor"
</button> class="w-4 h-4"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M11.25 11.25l.041-.02a.75.75 0 011.063.852l-.708 2.836a.75.75 0 001.063.853l.041-.021M21 12a9 9 0 11-18 0 9 9 0 0118 0zm-9-3.75h.008v.008H12V8.25z"
/>
</svg>
</button>
</Tooltip>
{/if} {/if}
{#if isLastMessage} {#if isLastMessage}
<button <Tooltip content="Continue Response" placement="bottom">
type="button" <button
class="{isLastMessage type="button"
? 'visible' class="{isLastMessage
: 'invisible group-hover:visible'} p-1 rounded dark:hover:text-white hover:text-black transition regenerate-response-button" ? 'visible'
on:click={() => { : 'invisible group-hover:visible'} p-1 rounded dark:hover:text-white hover:text-black transition regenerate-response-button"
continueGeneration(); on:click={() => {
}} continueGeneration();
> }}
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
class="w-4 h-4"
> >
<path <svg
stroke-linecap="round" xmlns="http://www.w3.org/2000/svg"
stroke-linejoin="round" fill="none"
d="M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z" viewBox="0 0 24 24"
/> stroke-width="1.5"
<path stroke="currentColor"
stroke-linecap="round" class="w-4 h-4"
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" <path
/> stroke-linecap="round"
</svg> stroke-linejoin="round"
</button> 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>
<button <Tooltip content="Regenerate" placement="bottom">
type="button" <button
class="{isLastMessage type="button"
? 'visible' class="{isLastMessage
: 'invisible group-hover:visible'} p-1 rounded dark:hover:text-white hover:text-black transition regenerate-response-button" ? 'visible'
on:click={regenerateResponse} : 'invisible group-hover:visible'} p-1 rounded dark:hover:text-white hover:text-black transition regenerate-response-button"
> on:click={regenerateResponse}
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
class="w-4 h-4"
> >
<path <svg
stroke-linecap="round" xmlns="http://www.w3.org/2000/svg"
stroke-linejoin="round" fill="none"
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" viewBox="0 0 24 24"
/> stroke-width="1.5"
</svg> stroke="currentColor"
</button> 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}
</div> </div>
{/if} {/if}

View file

@ -5,6 +5,7 @@
import Name from './Name.svelte'; import Name from './Name.svelte';
import ProfileImage from './ProfileImage.svelte'; import ProfileImage from './ProfileImage.svelte';
import { modelfiles, settings } from '$lib/stores'; import { modelfiles, settings } from '$lib/stores';
import Tooltip from '$lib/components/common/Tooltip.svelte';
const i18n = getContext('i18n'); const i18n = getContext('i18n');
@ -171,7 +172,8 @@
class=" bg-transparent outline-none w-full resize-none" class=" bg-transparent outline-none w-full resize-none"
bind:value={editedContent} bind:value={editedContent}
on:input={(e) => { on:input={(e) => {
messageEditTextAreaElement.style.height = `${messageEditTextAreaElement.scrollHeight}px`; e.target.style.height = '';
e.target.style.height = `${e.target.scrollHeight}px`;
}} }}
/> />
@ -248,55 +250,11 @@
</div> </div>
{/if} {/if}
<button <Tooltip content="Edit" placement="bottom">
class="invisible group-hover:visible p-1 rounded dark:hover:text-white hover:text-black transition edit-user-message-button"
on:click={() => {
editMessageHandler();
}}
>
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
class="w-4 h-4"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M16.862 4.487l1.687-1.688a1.875 1.875 0 112.652 2.652L6.832 19.82a4.5 4.5 0 01-1.897 1.13l-2.685.8.8-2.685a4.5 4.5 0 011.13-1.897L16.863 4.487zm0 0L19.5 7.125"
/>
</svg>
</button>
<button
class="invisible group-hover:visible p-1 rounded dark:hover:text-white hover:text-black transition"
on:click={() => {
copyToClipboard(message.content);
}}
>
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
class="w-4 h-4"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M15.666 3.888A2.25 2.25 0 0013.5 2.25h-3c-1.03 0-1.9.693-2.166 1.638m7.332 0c.055.194.084.4.084.612v0a.75.75 0 01-.75.75H9a.75.75 0 01-.75-.75v0c0-.212.03-.418.084-.612m7.332 0c.646.049 1.288.11 1.927.184 1.1.128 1.907 1.077 1.907 2.185V19.5a2.25 2.25 0 01-2.25 2.25H6.75A2.25 2.25 0 014.5 19.5V6.257c0-1.108.806-2.057 1.907-2.185a48.208 48.208 0 011.927-.184"
/>
</svg>
</button>
{#if !isFirstMessage}
<button <button
class="invisible group-hover:visible p-1 rounded dark:hover:text-white hover:text-black transition" class="invisible group-hover:visible p-1 rounded dark:hover:text-white hover:text-black transition edit-user-message-button"
on:click={() => { on:click={() => {
deleteMessageHandler(); editMessageHandler();
}} }}
> >
<svg <svg
@ -310,10 +268,60 @@
<path <path
stroke-linecap="round" stroke-linecap="round"
stroke-linejoin="round" stroke-linejoin="round"
d="m14.74 9-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 0 1-2.244 2.077H8.084a2.25 2.25 0 0 1-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 0 0-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 0 1 3.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 0 0-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 0 0-7.5 0" d="M16.862 4.487l1.687-1.688a1.875 1.875 0 112.652 2.652L6.832 19.82a4.5 4.5 0 01-1.897 1.13l-2.685.8.8-2.685a4.5 4.5 0 011.13-1.897L16.863 4.487zm0 0L19.5 7.125"
/> />
</svg> </svg>
</button> </button>
</Tooltip>
<Tooltip content="Copy" placement="bottom">
<button
class="invisible group-hover:visible p-1 rounded dark:hover:text-white hover:text-black transition"
on:click={() => {
copyToClipboard(message.content);
}}
>
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
class="w-4 h-4"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M15.666 3.888A2.25 2.25 0 0013.5 2.25h-3c-1.03 0-1.9.693-2.166 1.638m7.332 0c.055.194.084.4.084.612v0a.75.75 0 01-.75.75H9a.75.75 0 01-.75-.75v0c0-.212.03-.418.084-.612m7.332 0c.646.049 1.288.11 1.927.184 1.1.128 1.907 1.077 1.907 2.185V19.5a2.25 2.25 0 01-2.25 2.25H6.75A2.25 2.25 0 014.5 19.5V6.257c0-1.108.806-2.057 1.907-2.185a48.208 48.208 0 011.927-.184"
/>
</svg>
</button>
</Tooltip>
{#if !isFirstMessage}
<Tooltip content="Delete" placement="bottom">
<button
class="invisible group-hover:visible p-1 rounded dark:hover:text-white hover:text-black transition"
on:click={() => {
deleteMessageHandler();
}}
>
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
class="w-4 h-4"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="m14.74 9-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 0 1-2.244 2.077H8.084a2.25 2.25 0 0 1-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 0 0-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 0 1 3.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 0 0-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 0 0-7.5 0"
/>
</svg>
</button>
</Tooltip>
{/if} {/if}
</div> </div>
</div> </div>

View file

@ -116,7 +116,7 @@
d="M10 2a.75.75 0 01.75.75v1.5a.75.75 0 01-1.5 0v-1.5A.75.75 0 0110 2zM10 15a.75.75 0 01.75.75v1.5a.75.75 0 01-1.5 0v-1.5A.75.75 0 0110 15zM10 7a3 3 0 100 6 3 3 0 000-6zM15.657 5.404a.75.75 0 10-1.06-1.06l-1.061 1.06a.75.75 0 001.06 1.06l1.06-1.06zM6.464 14.596a.75.75 0 10-1.06-1.06l-1.06 1.06a.75.75 0 001.06 1.06l1.06-1.06zM18 10a.75.75 0 01-.75.75h-1.5a.75.75 0 010-1.5h1.5A.75.75 0 0118 10zM5 10a.75.75 0 01-.75.75h-1.5a.75.75 0 010-1.5h1.5A.75.75 0 015 10zM14.596 15.657a.75.75 0 001.06-1.06l-1.06-1.061a.75.75 0 10-1.06 1.06l1.06 1.06zM5.404 6.464a.75.75 0 001.06-1.06l-1.06-1.06a.75.75 0 10-1.061 1.06l1.06 1.06z" d="M10 2a.75.75 0 01.75.75v1.5a.75.75 0 01-1.5 0v-1.5A.75.75 0 0110 2zM10 15a.75.75 0 01.75.75v1.5a.75.75 0 01-1.5 0v-1.5A.75.75 0 0110 15zM10 7a3 3 0 100 6 3 3 0 000-6zM15.657 5.404a.75.75 0 10-1.06-1.06l-1.061 1.06a.75.75 0 001.06 1.06l1.06-1.06zM6.464 14.596a.75.75 0 10-1.06-1.06l-1.06 1.06a.75.75 0 001.06 1.06l1.06-1.06zM18 10a.75.75 0 01-.75.75h-1.5a.75.75 0 010-1.5h1.5A.75.75 0 0118 10zM5 10a.75.75 0 01-.75.75h-1.5a.75.75 0 010-1.5h1.5A.75.75 0 015 10zM14.596 15.657a.75.75 0 001.06-1.06l-1.06-1.061a.75.75 0 10-1.06 1.06l1.06 1.06zM5.404 6.464a.75.75 0 001.06-1.06l-1.06-1.06a.75.75 0 10-1.061 1.06l1.06 1.06z"
/> />
</svg> --> </svg> -->
<span class="ml-2 self-center">{$i18n.t(' JSON ')}</span> <span class="ml-2 self-center">{$i18n.t('JSON')}</span>
{/if} {/if}
</button> </button>
</div> </div>

View file

@ -4,7 +4,12 @@
const dispatch = createEventDispatcher(); const dispatch = createEventDispatcher();
import { getOllamaUrls, getOllamaVersion, updateOllamaUrls } from '$lib/apis/ollama'; import { getOllamaUrls, getOllamaVersion, updateOllamaUrls } from '$lib/apis/ollama';
import { getOpenAIKey, getOpenAIUrl, updateOpenAIKey, updateOpenAIUrl } from '$lib/apis/openai'; import {
getOpenAIKeys,
getOpenAIUrls,
updateOpenAIKeys,
updateOpenAIUrls
} from '$lib/apis/openai';
import { toast } from 'svelte-sonner'; import { toast } from 'svelte-sonner';
const i18n = getContext('i18n'); const i18n = getContext('i18n');
@ -18,12 +23,14 @@
let OPENAI_API_KEY = ''; let OPENAI_API_KEY = '';
let OPENAI_API_BASE_URL = ''; let OPENAI_API_BASE_URL = '';
let OPENAI_API_KEYS = [''];
let OPENAI_API_BASE_URLS = [''];
let showOpenAI = false; let showOpenAI = false;
let showLiteLLM = false;
const updateOpenAIHandler = async () => { const updateOpenAIHandler = async () => {
OPENAI_API_BASE_URL = await updateOpenAIUrl(localStorage.token, OPENAI_API_BASE_URL); OPENAI_API_BASE_URLS = await updateOpenAIUrls(localStorage.token, OPENAI_API_BASE_URLS);
OPENAI_API_KEY = await updateOpenAIKey(localStorage.token, OPENAI_API_KEY); OPENAI_API_KEYS = await updateOpenAIKeys(localStorage.token, OPENAI_API_KEYS);
await models.set(await getModels()); await models.set(await getModels());
}; };
@ -45,8 +52,8 @@
onMount(async () => { onMount(async () => {
if ($user.role === 'admin') { if ($user.role === 'admin') {
OLLAMA_BASE_URLS = await getOllamaUrls(localStorage.token); OLLAMA_BASE_URLS = await getOllamaUrls(localStorage.token);
OPENAI_API_BASE_URL = await getOpenAIUrl(localStorage.token); OPENAI_API_BASE_URLS = await getOpenAIUrls(localStorage.token);
OPENAI_API_KEY = await getOpenAIKey(localStorage.token); OPENAI_API_KEYS = await getOpenAIKeys(localStorage.token);
} }
}); });
</script> </script>
@ -73,37 +80,74 @@
</div> </div>
{#if showOpenAI} {#if showOpenAI}
<div> <div class="flex flex-col gap-1">
<div class=" mb-2.5 text-sm font-medium">{$i18n.t('API Key')}</div> {#each OPENAI_API_BASE_URLS as url, idx}
<div class="flex w-full"> <div class="flex w-full gap-2">
<div class="flex-1"> <div class="flex-1">
<input <input
class="w-full rounded py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-800 outline-none" class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
placeholder={$i18n.t('Enter OpenAI API Key')} placeholder={$i18n.t('API Base URL')}
bind:value={OPENAI_API_KEY} bind:value={url}
autocomplete="off" autocomplete="off"
/> />
</div> </div>
</div>
</div>
<div> <div class="flex-1">
<div class=" mb-2.5 text-sm font-medium">{$i18n.t('API Base URL')}</div> <input
<div class="flex w-full"> class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
<div class="flex-1"> placeholder={$i18n.t('API Key')}
<input bind:value={OPENAI_API_KEYS[idx]}
class="w-full rounded py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-800 outline-none" autocomplete="off"
placeholder="Enter OpenAI API Base URL" />
bind:value={OPENAI_API_BASE_URL} </div>
autocomplete="off" <div class="self-center flex items-center">
/> {#if idx === 0}
<button
class="px-1"
on:click={() => {
OPENAI_API_BASE_URLS = [...OPENAI_API_BASE_URLS, ''];
OPENAI_API_KEYS = [...OPENAI_API_KEYS, ''];
}}
type="button"
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 16 16"
fill="currentColor"
class="w-4 h-4"
>
<path
d="M8.75 3.75a.75.75 0 0 0-1.5 0v3.5h-3.5a.75.75 0 0 0 0 1.5h3.5v3.5a.75.75 0 0 0 1.5 0v-3.5h3.5a.75.75 0 0 0 0-1.5h-3.5v-3.5Z"
/>
</svg>
</button>
{:else}
<button
class="px-1"
on:click={() => {
OPENAI_API_BASE_URLS = OPENAI_API_BASE_URLS.filter(
(url, urlIdx) => idx !== urlIdx
);
OPENAI_API_KEYS = OPENAI_API_KEYS.filter((key, keyIdx) => idx !== keyIdx);
}}
type="button"
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 16 16"
fill="currentColor"
class="w-4 h-4"
>
<path d="M3.75 7.25a.75.75 0 0 0 0 1.5h8.5a.75.75 0 0 0 0-1.5h-8.5Z" />
</svg>
</button>
{/if}
</div>
</div> </div>
</div> <div class=" mb-1 text-xs text-gray-400 dark:text-gray-500">
<div class="mt-2 text-xs text-gray-400 dark:text-gray-500"> WebUI will make requests to <span class=" text-gray-200">'{url}/models'</span>
WebUI will make requests to <span class=" text-gray-200" </div>
>'{OPENAI_API_BASE_URL}/chat'</span {/each}
>
</div>
</div> </div>
{/if} {/if}
</div> </div>
@ -193,7 +237,7 @@
<div class="mt-2 text-xs text-gray-400 dark:text-gray-500"> <div class="mt-2 text-xs text-gray-400 dark:text-gray-500">
{$i18n.t('Trouble accessing Ollama?')} {$i18n.t('Trouble accessing Ollama?')}
<a <a
class=" text-gray-300 font-medium" class=" text-gray-300 font-medium underline"
href="https://github.com/open-webui/open-webui#troubleshooting" href="https://github.com/open-webui/open-webui#troubleshooting"
target="_blank" target="_blank"
> >

View file

@ -149,7 +149,7 @@
<br /> <br />
{$i18n.t('You can help us translate the WebUI.')} {$i18n.t('You can help us translate the WebUI.')}
<a <a
class=" text-gray-300 font-medium" class=" text-gray-300 font-medium underline"
href="https://github.com/open-webui/open-webui/blob/main/docs/CONTRIBUTING.md#-translations-and-internationalization" href="https://github.com/open-webui/open-webui/blob/main/docs/CONTRIBUTING.md#-translations-and-internationalization"
target="_blank" target="_blank"
> >

View file

@ -56,7 +56,7 @@
let modelUploadMode = 'file'; let modelUploadMode = 'file';
let modelInputFile = ''; let modelInputFile = '';
let modelFileUrl = ''; let modelFileUrl = '';
let modelFileContent = `TEMPLATE """{{ .System }}\nUSER: {{ .Prompt }}\nASSSISTANT: """\nPARAMETER num_ctx 4096\nPARAMETER stop "</s>"\nPARAMETER stop "USER:"\nPARAMETER stop "ASSSISTANT:"`; let modelFileContent = `TEMPLATE """{{ .System }}\nUSER: {{ .Prompt }}\nASSISTANT: """\nPARAMETER num_ctx 4096\nPARAMETER stop "</s>"\nPARAMETER stop "USER:"\nPARAMETER stop "ASSISTANT:"`;
let modelFileDigest = ''; let modelFileDigest = '';
let uploadProgress = null; let uploadProgress = null;
@ -517,7 +517,7 @@
{#if !deleteModelTag} {#if !deleteModelTag}
<option value="" disabled selected>Select a model</option> <option value="" disabled selected>Select a model</option>
{/if} {/if}
{#each $models.filter((m) => m.size != null) as model} {#each $models.filter((m) => m.size != null && (selectedOllamaUrlIdx === null ? true : (m?.urls ?? []).includes(selectedOllamaUrlIdx))) as model}
<option value={model.name} class="bg-gray-100 dark:bg-gray-700" <option value={model.name} class="bg-gray-100 dark:bg-gray-700"
>{model.name + ' (' + (model.size / 1024 ** 3).toFixed(1) + ' GB)'}</option >{model.name + ' (' + (model.size / 1024 ** 3).toFixed(1) + ' GB)'}</option
> >
@ -599,7 +599,7 @@
on:change={() => { on:change={() => {
console.log(modelInputFile); console.log(modelInputFile);
}} }}
accept=".gguf" accept=".gguf,.safetensors"
required required
hidden hidden
/> />

View file

@ -140,7 +140,9 @@
<button <button
class="w-full text-sm font-medium py-3 bg-gray-850 hover:bg-gray-800 text-center rounded-xl" class="w-full text-sm font-medium py-3 bg-gray-850 hover:bg-gray-800 text-center rounded-xl"
type="button" type="button"
on:click={uploadDocInputElement.click} on:click={() => {
uploadDocInputElement.click();
}}
> >
{#if inputFiles} {#if inputFiles}
{inputFiles.length > 0 ? `${inputFiles.length}` : ''} document(s) selected. {inputFiles.length > 0 ? `${inputFiles.length}` : ''} document(s) selected.

View file

@ -90,8 +90,3 @@ export const SUPPORTED_FILE_EXTENSIONS = [
// This feature, akin to $env/static/private, exclusively incorporates environment variables // This feature, akin to $env/static/private, exclusively incorporates environment variables
// that are prefixed with config.kit.env.publicPrefix (usually set to PUBLIC_). // that are prefixed with config.kit.env.publicPrefix (usually set to PUBLIC_).
// Consequently, these variables can be securely exposed to client-side code. // Consequently, these variables can be securely exposed to client-side code.
// Example of the .env configuration:
// OLLAMA_API_BASE_URL="http://localhost:11434/api"
// # Public
// PUBLIC_API_BASE_URL=$OLLAMA_API_BASE_URL

View file

@ -4,7 +4,7 @@ import LanguageDetector from 'i18next-browser-languagedetector';
import type { i18n as i18nType } from 'i18next'; import type { i18n as i18nType } from 'i18next';
import { writable } from 'svelte/store'; import { writable } from 'svelte/store';
const createI18nStore = (i18n: i18n) => { const createI18nStore = (i18n: i18nType) => {
const i18nWritable = writable(i18n); const i18nWritable = writable(i18n);
i18n.on('initialized', () => { i18n.on('initialized', () => {
@ -20,7 +20,7 @@ const createI18nStore = (i18n: i18n) => {
return i18nWritable; return i18nWritable;
}; };
const createIsLoadingStore = (i18n: i18n) => { const createIsLoadingStore = (i18n: i18nType) => {
const isLoading = writable(false); const isLoading = writable(false);
// if loaded resources are empty || {}, set loading to true // if loaded resources are empty || {}, set loading to true
@ -39,11 +39,13 @@ const createIsLoadingStore = (i18n: i18n) => {
i18next i18next
.use( .use(
resourcesToBackend((language, namespace) => import(`./locales/${language}/${namespace}.json`)) resourcesToBackend(
(language: string, namespace: string) => import(`./locales/${language}/${namespace}.json`)
)
) )
.use(LanguageDetector) .use(LanguageDetector)
.init({ .init({
debug: true, debug: false,
detection: { detection: {
order: ['querystring', 'localStorage', 'navigator'], order: ['querystring', 'localStorage', 'navigator'],
caches: ['localStorage'], caches: ['localStorage'],

View file

@ -18,10 +18,9 @@
"Add message": "Nachricht eingeben", "Add message": "Nachricht eingeben",
"add tags": "Tags hinzufügen", "add tags": "Tags hinzufügen",
"Adjusting these settings will apply changes universally to all users.": "Das Anpassen dieser Einstellungen wirkt sich universell auf alle Benutzer aus.", "Adjusting these settings will apply changes universally to all users.": "Das Anpassen dieser Einstellungen wirkt sich universell auf alle Benutzer aus.",
"admin": "Administrator", "Admin": "",
"Admin Panel": "Admin Panel", "Admin Panel": "Admin Panel",
"Admin Settings": "Admin Einstellungen", "Admin Settings": "Admin Einstellungen",
"Advanced": "Angepasst",
"Advanced Model Params": "Erweiterte Modell Parameter", "Advanced Model Params": "Erweiterte Modell Parameter",
"Advanced Parameters": "Erweiterte Parameter", "Advanced Parameters": "Erweiterte Parameter",
"all": "Alle", "all": "Alle",
@ -35,7 +34,6 @@
"API Key": "API Key", "API Key": "API Key",
"API RPM": "API RPM", "API RPM": "API RPM",
"are allowed - Activate this command by typing": "sind erlaubt - Aktiviere diesen Befehl, indem du", "are allowed - Activate this command by typing": "sind erlaubt - Aktiviere diesen Befehl, indem du",
"assistant": "Assistent",
"Audio": "Audio", "Audio": "Audio",
"Auto-playback response": "Automatische Wiedergabe der Antwort", "Auto-playback response": "Automatische Wiedergabe der Antwort",
"Auto-send input after 3 sec.": "Automatisches Senden der Eingabe nach 3 Sek", "Auto-send input after 3 sec.": "Automatisches Senden der Eingabe nach 3 Sek",
@ -98,7 +96,7 @@
"Deleted {tagName}": "{tagName} gelöscht", "Deleted {tagName}": "{tagName} gelöscht",
"Description": "Beschreibung", "Description": "Beschreibung",
"Desktop Notifications": "Desktop-Benachrichtigungen", "Desktop Notifications": "Desktop-Benachrichtigungen",
"Didn't find your language?": "Deine Sprache nicht gefunden?", "Didn't find your language?": "Deine Sprache nicht vorhanden?",
"Disabled": "Deaktiviert", "Disabled": "Deaktiviert",
"Discover a modelfile": "Eine Modelfiles entdecken", "Discover a modelfile": "Eine Modelfiles entdecken",
"Discover a prompt": "Einen Prompt entdecken", "Discover a prompt": "Einen Prompt entdecken",
@ -121,21 +119,17 @@
"Enable Chat History": "Chat-Verlauf aktivieren", "Enable Chat History": "Chat-Verlauf aktivieren",
"Enable New Sign Ups": "Neue Anmeldungen aktivieren", "Enable New Sign Ups": "Neue Anmeldungen aktivieren",
"Enabled": "Aktiviert", "Enabled": "Aktiviert",
"Enter a user message here": "Gebe hier eine Benutzernachricht ein",
"Enter an assistant message here": "Gebe hier eine Assistentennachricht ein",
"Enter OpenAI API Key": "OpenAI-API-Key eingeben", "Enter OpenAI API Key": "OpenAI-API-Key eingeben",
"Enter stop sequence": "Stop-Sequenz eingeben", "Enter stop sequence": "Stop-Sequenz eingeben",
"Enter Your Email": "Geben Deine E-Mail-Adresse ein", "Enter Your Email": "Geben Deine E-Mail-Adresse ein",
"Enter Your Full Name": "Gebe Deinen vollständigen Namen ein", "Enter Your Full Name": "Gebe Deinen vollständigen Namen ein",
"Enter Your Password": "Gebe Dein Passwort ein", "Enter Your Password": "Gebe Dein Passwort ein",
"Experimental": "Experimentell",
"Export All Chats (All Users)": "Alle Chats exportieren (alle Benutzer)", "Export All Chats (All Users)": "Alle Chats exportieren (alle Benutzer)",
"Export Chats": "Chats exportieren", "Export Chats": "Chats exportieren",
"Export Documents Mapping": "Dokumentenmapping exportieren", "Export Documents Mapping": "Dokumentenmapping exportieren",
"Export Modelfiles": "Modelfiles exportieren", "Export Modelfiles": "Modelfiles exportieren",
"Export Prompts": "Prompts exportieren", "Export Prompts": "Prompts exportieren",
"Failed to read clipboard contents": "Fehler beim Lesen des Zwischenablageninhalts", "Failed to read clipboard contents": "Fehler beim Lesen des Zwischenablageninhalts",
"File Mode": "Dateimodus",
"File not found.": "Datei nicht gefunden.", "File not found.": "Datei nicht gefunden.",
"Focus chat input": "Chat-Eingabe fokussieren", "Focus chat input": "Chat-Eingabe fokussieren",
"Format your variables using square brackets like this:": "Formatiere Deine Variablen mit eckigen Klammern wie folgt:", "Format your variables using square brackets like this:": "Formatiere Deine Variablen mit eckigen Klammern wie folgt:",
@ -187,7 +181,6 @@
"Model Tag Name": "Modell-Tag-Name", "Model Tag Name": "Modell-Tag-Name",
"Modelfile": "Modelfiles", "Modelfile": "Modelfiles",
"Modelfile Advanced Settings": "Erweiterte Modelfileseinstellungen", "Modelfile Advanced Settings": "Erweiterte Modelfileseinstellungen",
"Modelfile Content": "Modelfilesinhalt",
"Modelfiles": "Modelfiles", "Modelfiles": "Modelfiles",
"Models": "Modelle", "Models": "Modelle",
"My Documents": "Meine Dokumente", "My Documents": "Meine Dokumente",
@ -195,19 +188,20 @@
"My Prompts": "Meine Prompts", "My Prompts": "Meine Prompts",
"Name": "Name", "Name": "Name",
"Name Tag": "Namens-Tag", "Name Tag": "Namens-Tag",
"Name your Modelfile": "Benenne Dein Modelfile", "Name your modelfile": "",
"New Chat": "Neuer Chat", "New Chat": "Neuer Chat",
"New Password": "Neues Passwort", "New Password": "Neues Passwort",
"Not sure what to add?": "Nicht sicher, was hinzugefügt werden soll?", "Not sure what to add?": "Nicht sicher, was hinzugefügt werden soll?",
"Not sure what to write? Switch to": "Nicht sicher, was Du schreiben sollst? Wechsel zu", "Not sure what to write? Switch to": "Nicht sicher, was Du schreiben sollst? Wechsel zu",
"Off": "Aus", "Off": "Aus",
"Okay, Let's Go!": "Okay, los geht's!", "Okay, Let's Go!": "Okay, los geht's!",
"Ollama API URL": "Ollama-API-URL",
"Ollama Version": "Ollama-Version", "Ollama Version": "Ollama-Version",
"On": "Ein", "On": "Ein",
"Only": "Nur", "Only": "Nur",
"Only alphanumeric characters and hyphens are allowed in the command string.": "Nur alphanumerische Zeichen und Bindestriche sind im Befehlsstring erlaubt.", "Only alphanumeric characters and hyphens are allowed in the command string.": "Nur alphanumerische Zeichen und Bindestriche sind im Befehlsstring erlaubt.",
"Oops! Hold tight! Your files are still in the processing oven. We're cooking them up to perfection. Please be patient and we'll let you know once they're ready.": "Hoppla! Warte noch einen Moment! Die Dateien sind noch im der Verarbeitung. Bitte habe etwas Geduld und wir informieren Dich, sobald sie bereit sind.", "Oops! Hold tight! Your files are still in the processing oven. We're cooking them up to perfection. Please be patient and we'll let you know once they're ready.": "Hoppla! Warte noch einen Moment! Die Dateien sind noch im der Verarbeitung. Bitte habe etwas Geduld und wir informieren Dich, sobald sie bereit sind.",
"Oops! Looks like the URL is invalid. Please double-check and try again.": "",
"Oops! You're using an unsupported method (frontend only). Please serve the WebUI from the backend.": "",
"Open": "Öffne", "Open": "Öffne",
"Open AI": "Open AI", "Open AI": "Open AI",
"Open new chat": "Neuen Chat öffnen", "Open new chat": "Neuen Chat öffnen",
@ -215,7 +209,7 @@
"or": "oder", "or": "oder",
"Parameters": "Parameter", "Parameters": "Parameter",
"Password": "Passwort", "Password": "Passwort",
"pending": "ausstehend", "Pending": "",
"Permission denied when accessing microphone: {{error}}": "Zugriff auf das Mikrofon verweigert: {{error}}", "Permission denied when accessing microphone: {{error}}": "Zugriff auf das Mikrofon verweigert: {{error}}",
"Playground": "Playground", "Playground": "Playground",
"Profile": "Profil", "Profile": "Profil",
@ -277,6 +271,7 @@
"STT Settings": "STT-Einstellungen", "STT Settings": "STT-Einstellungen",
"Submit": "Senden", "Submit": "Senden",
"Success": "Erfolg", "Success": "Erfolg",
"Successfully updated": "",
"Successfully updated.": "Erfolgreich aktualisiert.", "Successfully updated.": "Erfolgreich aktualisiert.",
"Sync All": "Alles synchronisieren", "Sync All": "Alles synchronisieren",
"System": "System", "System": "System",
@ -290,12 +285,12 @@
"Theme": "Design", "Theme": "Design",
"This ensures that your valuable conversations are securely saved to your backend database. Thank you!": "Dadurch werden Deine wertvollen Unterhaltungen sicher in der Backend-Datenbank gespeichert. Vielen Dank!", "This ensures that your valuable conversations are securely saved to your backend database. Thank you!": "Dadurch werden Deine wertvollen Unterhaltungen sicher in der Backend-Datenbank gespeichert. Vielen Dank!",
"This setting does not sync across browsers or devices.": "Diese Einstellung wird nicht zwischen Browsern oder Geräten synchronisiert.", "This setting does not sync across browsers or devices.": "Diese Einstellung wird nicht zwischen Browsern oder Geräten synchronisiert.",
"Tip: Update multiple variable slots consecutively by pressing the tab key in the chat input after each replacement.": "",
"Title": "Titel", "Title": "Titel",
"Title Auto-Generation": "Automatische Titelgenerierung", "Title Auto-Generation": "Automatische Titelgenerierung",
"Title Generation Prompt": "Prompt für Titelgenerierung", "Title Generation Prompt": "Prompt für Titelgenerierung",
"to": "für", "to": "für",
"To access the available model names for downloading,": "Um auf die verfügbaren Modellnamen zum Herunterladen zuzugreifen,", "To access the available model names for downloading,": "Um auf die verfügbaren Modellnamen zum Herunterladen zuzugreifen,",
"to chat input.": "im Chat eingeben.",
"Toggle settings": "Einstellungen umschalten", "Toggle settings": "Einstellungen umschalten",
"Toggle sidebar": "Seitenleiste umschalten", "Toggle sidebar": "Seitenleiste umschalten",
"Top K": "Top K", "Top K": "Top K",
@ -303,12 +298,10 @@
"Trouble accessing Ollama?": "Probleme beim Zugriff auf Ollama?", "Trouble accessing Ollama?": "Probleme beim Zugriff auf Ollama?",
"TTS Settings": "TTS-Einstellungen", "TTS Settings": "TTS-Einstellungen",
"Uh-oh! There was an issue connecting to {{provider}}.": "Ups! Es gab ein Problem bei der Verbindung mit {{provider}}.", "Uh-oh! There was an issue connecting to {{provider}}.": "Ups! Es gab ein Problem bei der Verbindung mit {{provider}}.",
"Upload a GGUF model": "Ein GGUF-Modell hochladen", "Unknown File Type '{{file_type}}', but accepting and treating as plain text": "",
"Upload files": "Dateien hochladen", "Upload files": "Dateien hochladen",
"Upload Progress": "Upload-Fortschritt",
"URL Mode": "URL-Modus",
"Use '#' in the prompt input to load and select your documents.": "Verwende '#' in der Prompt-Eingabe, um Deine Dokumente zu laden und auszuwählen.", "Use '#' in the prompt input to load and select your documents.": "Verwende '#' in der Prompt-Eingabe, um Deine Dokumente zu laden und auszuwählen.",
"user": "Benutzer", "User": "",
"User Permissions": "Benutzerberechtigungen", "User Permissions": "Benutzerberechtigungen",
"Users": "Benutzer", "Users": "Benutzer",
"Utilize": "Nutze die", "Utilize": "Nutze die",
@ -322,8 +315,9 @@
"When history is turned off, new chats on this browser won't appear in your history on any of your devices.": "Wenn die Historie ausgeschaltet ist, werden neue Chats nicht in Deiner Historie auf Deine Geräte angezeigt.", "When history is turned off, new chats on this browser won't appear in your history on any of your devices.": "Wenn die Historie ausgeschaltet ist, werden neue Chats nicht in Deiner Historie auf Deine Geräte angezeigt.",
"Whisper (Local)": "Whisper (Lokal)", "Whisper (Local)": "Whisper (Lokal)",
"Write a prompt suggestion (e.g. Who are you?)": "Gebe einen Prompt-Vorschlag ein (z.B. Wer bist du?)", "Write a prompt suggestion (e.g. Who are you?)": "Gebe einen Prompt-Vorschlag ein (z.B. Wer bist du?)",
"Write a summary in 50 words that summarizes [topic or keyword]": "Schreibe eine kurze Zusammenfassung in 50 Wörtern, die [Thema oder Schlüsselwort] zusammenfasst.", "Write a summary in 50 words that summarizes [topic or keyword].": "Schreibe eine kurze Zusammenfassung in 50 Wörtern, die [Thema oder Schlüsselwort] zusammenfasst.",
"You": "Du", "You": "Du",
"You can help us translate the WebUI.": "Du kannst uns bei der Übersetzung der WebUI helfen.", "You're a helpful assistant.": "Du bist ein hilfreicher Assistent.",
"You're a helpful assistant.": "Du bist ein hilfreicher Assistent." "You're now logged in.": "Du bist nun eingeloggt.",
"You can help us translate the WebUI.": "Du kannst uns bei der Übersetzung der WebUI helfen."
} }

View file

@ -18,10 +18,9 @@
"Add message": "Add message", "Add message": "Add message",
"add tags": "add tags", "add tags": "add tags",
"Adjusting these settings will apply changes universally to all users.": "Adjusting these settings will apply changes universally to all users.", "Adjusting these settings will apply changes universally to all users.": "Adjusting these settings will apply changes universally to all users.",
"admin": "Admin", "Admin": "",
"Admin Panel": "Admin Panel", "Admin Panel": "Admin Panel",
"Admin Settings": "Admin Settings", "Admin Settings": "Admin Settings",
"Advanced": "Advanced",
"Advanced Model Params": "Advanced Model Params", "Advanced Model Params": "Advanced Model Params",
"Advanced Parameters": "Advanced Parameters", "Advanced Parameters": "Advanced Parameters",
"all": "all", "all": "all",
@ -35,7 +34,6 @@
"API Key": "API Key", "API Key": "API Key",
"API RPM": "API RPM", "API RPM": "API RPM",
"are allowed - Activate this command by typing": "are allowed - Activate this command by typing", "are allowed - Activate this command by typing": "are allowed - Activate this command by typing",
"assistant": "Assistant",
"Audio": "Audio", "Audio": "Audio",
"Auto-playback response": "Auto-playback response", "Auto-playback response": "Auto-playback response",
"Auto-send input after 3 sec.": "Auto-send input after 3 sec.", "Auto-send input after 3 sec.": "Auto-send input after 3 sec.",
@ -121,21 +119,17 @@
"Enable Chat History": "Enable Chat History", "Enable Chat History": "Enable Chat History",
"Enable New Sign Ups": "Enable New Sign Ups", "Enable New Sign Ups": "Enable New Sign Ups",
"Enabled": "Enabled", "Enabled": "Enabled",
"Enter a user message here": "Enter a user message here",
"Enter an assistant message here": "Enter an assistant message here",
"Enter OpenAI API Key": "Enter OpenAI API Key", "Enter OpenAI API Key": "Enter OpenAI API Key",
"Enter stop sequence": "Enter stop sequence", "Enter stop sequence": "Enter stop sequence",
"Enter Your Email": "Enter Your Email", "Enter Your Email": "Enter Your Email",
"Enter Your Full Name": "Enter Your Full Name", "Enter Your Full Name": "Enter Your Full Name",
"Enter Your Password": "Enter Your Password", "Enter Your Password": "Enter Your Password",
"Experimental": "Experimental",
"Export All Chats (All Users)": "Export All Chats (All Users)", "Export All Chats (All Users)": "Export All Chats (All Users)",
"Export Chats": "Export Chats", "Export Chats": "Export Chats",
"Export Documents Mapping": "Export Documents Mapping", "Export Documents Mapping": "Export Documents Mapping",
"Export Modelfiles": "Export Modelfiles", "Export Modelfiles": "Export Modelfiles",
"Export Prompts": "Export Prompts", "Export Prompts": "Export Prompts",
"Failed to read clipboard contents": "Failed to read clipboard contents", "Failed to read clipboard contents": "Failed to read clipboard contents",
"File Mode": "File Mode",
"File not found.": "File not found.", "File not found.": "File not found.",
"Focus chat input": "Focus chat input", "Focus chat input": "Focus chat input",
"Format your variables using square brackets like this:": "Format your variables using square brackets like this:", "Format your variables using square brackets like this:": "Format your variables using square brackets like this:",
@ -187,7 +181,6 @@
"Model Tag Name": "Model Tag Name", "Model Tag Name": "Model Tag Name",
"Modelfile": "Modelfile", "Modelfile": "Modelfile",
"Modelfile Advanced Settings": "Modelfile Advanced Settings", "Modelfile Advanced Settings": "Modelfile Advanced Settings",
"Modelfile Content": "Modelfile Content",
"Modelfiles": "Modelfiles", "Modelfiles": "Modelfiles",
"Models": "Models", "Models": "Models",
"My Documents": "My Documents", "My Documents": "My Documents",
@ -195,19 +188,20 @@
"My Prompts": "My Prompts", "My Prompts": "My Prompts",
"Name": "Name", "Name": "Name",
"Name Tag": "Name Tag", "Name Tag": "Name Tag",
"Name your Modelfile": "Name your Modelfile", "Name your modelfile": "",
"New Chat": "New Chat", "New Chat": "New Chat",
"New Password": "New Password", "New Password": "New Password",
"Not sure what to add?": "Not sure what to add?", "Not sure what to add?": "Not sure what to add?",
"Not sure what to write? Switch to": "Not sure what to write? Switch to", "Not sure what to write? Switch to": "Not sure what to write? Switch to",
"Off": "Off", "Off": "Off",
"Okay, Let's Go!": "Okay, Let's Go!", "Okay, Let's Go!": "Okay, Let's Go!",
"Ollama API URL": "Ollama API URL",
"Ollama Version": "Ollama Version", "Ollama Version": "Ollama Version",
"On": "On", "On": "On",
"Only": "Only", "Only": "Only",
"Only alphanumeric characters and hyphens are allowed in the command string.": "Only alphanumeric characters and hyphens are allowed in the command string.", "Only alphanumeric characters and hyphens are allowed in the command string.": "Only alphanumeric characters and hyphens are allowed in the command string.",
"Oops! Hold tight! Your files are still in the processing oven. We're cooking them up to perfection. Please be patient and we'll let you know once they're ready.": "Oops! Hold tight! Your files are still in the processing oven. We're cooking them up to perfection. Please be patient and we'll let you know once they're ready.", "Oops! Hold tight! Your files are still in the processing oven. We're cooking them up to perfection. Please be patient and we'll let you know once they're ready.": "Oops! Hold tight! Your files are still in the processing oven. We're cooking them up to perfection. Please be patient and we'll let you know once they're ready.",
"Oops! Looks like the URL is invalid. Please double-check and try again.": "",
"Oops! You're using an unsupported method (frontend only). Please serve the WebUI from the backend.": "",
"Open": "Open", "Open": "Open",
"Open AI": "Open AI", "Open AI": "Open AI",
"Open new chat": "Open new chat", "Open new chat": "Open new chat",
@ -215,7 +209,7 @@
"or": "or", "or": "or",
"Parameters": "Parameters", "Parameters": "Parameters",
"Password": "Password", "Password": "Password",
"pending": "Pending", "Pending": "",
"Permission denied when accessing microphone: {{error}}": "Permission denied when accessing microphone: {{error}}", "Permission denied when accessing microphone: {{error}}": "Permission denied when accessing microphone: {{error}}",
"Playground": "Playground", "Playground": "Playground",
"Profile": "Profile", "Profile": "Profile",
@ -277,6 +271,7 @@
"STT Settings": "STT Settings", "STT Settings": "STT Settings",
"Submit": "Submit", "Submit": "Submit",
"Success": "Success", "Success": "Success",
"Successfully updated": "",
"Successfully updated.": "Successfully updated.", "Successfully updated.": "Successfully updated.",
"Sync All": "Sync All", "Sync All": "Sync All",
"System": "System", "System": "System",
@ -290,12 +285,12 @@
"Theme": "Theme", "Theme": "Theme",
"This ensures that your valuable conversations are securely saved to your backend database. Thank you!": "This ensures that your valuable conversations are securely saved to your backend database. Thank you!", "This ensures that your valuable conversations are securely saved to your backend database. Thank you!": "This ensures that your valuable conversations are securely saved to your backend database. Thank you!",
"This setting does not sync across browsers or devices.": "This setting does not sync across browsers or devices.", "This setting does not sync across browsers or devices.": "This setting does not sync across browsers or devices.",
"Tip: Update multiple variable slots consecutively by pressing the tab key in the chat input after each replacement.": "",
"Title": "Title", "Title": "Title",
"Title Auto-Generation": "Title Auto-Generation", "Title Auto-Generation": "Title Auto-Generation",
"Title Generation Prompt": "Title Generation Prompt", "Title Generation Prompt": "Title Generation Prompt",
"to": "to", "to": "to",
"To access the available model names for downloading,": "To access the available model names for downloading,", "To access the available model names for downloading,": "To access the available model names for downloading,",
"to chat input.": "to chat input.",
"Toggle settings": "Toggle settings", "Toggle settings": "Toggle settings",
"Toggle sidebar": "Toggle sidebar", "Toggle sidebar": "Toggle sidebar",
"Top K": "Top K", "Top K": "Top K",
@ -303,12 +298,10 @@
"Trouble accessing Ollama?": "Trouble accessing Ollama?", "Trouble accessing Ollama?": "Trouble accessing Ollama?",
"TTS Settings": "TTS Settings", "TTS Settings": "TTS Settings",
"Uh-oh! There was an issue connecting to {{provider}}.": "Uh-oh! There was an issue connecting to {{provider}}.", "Uh-oh! There was an issue connecting to {{provider}}.": "Uh-oh! There was an issue connecting to {{provider}}.",
"Upload a GGUF model": "Upload a GGUF model", "Unknown File Type '{{file_type}}', but accepting and treating as plain text": "",
"Upload files": "Upload files", "Upload files": "Upload files",
"Upload Progress": "Upload Progress",
"URL Mode": "URL Mode",
"Use '#' in the prompt input to load and select your documents.": "Use '#' in the prompt input to load and select your documents.", "Use '#' in the prompt input to load and select your documents.": "Use '#' in the prompt input to load and select your documents.",
"user": "User", "User": "",
"User Permissions": "User Permissions", "User Permissions": "User Permissions",
"Users": "Users", "Users": "Users",
"Utilize": "Utilize", "Utilize": "Utilize",
@ -322,8 +315,9 @@
"When history is turned off, new chats on this browser won't appear in your history on any of your devices.": "When history is turned off, new chats on this browser won't appear in your history on any of your devices.", "When history is turned off, new chats on this browser won't appear in your history on any of your devices.": "When history is turned off, new chats on this browser won't appear in your history on any of your devices.",
"Whisper (Local)": "Whisper (Local)", "Whisper (Local)": "Whisper (Local)",
"Write a prompt suggestion (e.g. Who are you?)": "Write a prompt suggestion (e.g. Who are you?)", "Write a prompt suggestion (e.g. Who are you?)": "Write a prompt suggestion (e.g. Who are you?)",
"Write a summary in 50 words that summarizes [topic or keyword]": "Write a summary in 50 words that summarizes [topic or keyword]", "Write a summary in 50 words that summarizes [topic or keyword].": "Write a summary in 50 words that summarizes [topic or keyword].",
"You can help us translate the WebUI.": "You can help us translate the WebUI.", "You": "You",
"You're a helpful assistant.": "You're a helpful assistant.", "You're a helpful assistant.": "You're a helpful assistant.",
"You're now logged in.": "You're now logged in." "You're now logged in.": "You're now logged in.",
"You can help us translate the WebUI.": "You can help us translate the WebUI."
} }

View file

@ -1,42 +1,65 @@
{ {
"'s', 'm', 'h', 'd', 'w' or '-1' for no expiration.": "'s', 'm', 'h', 'd', 'w' یا '-1' برای غیر فعال کردن انقضا.",
"(Beta)": "(بتا)",
"(e.g. `sh webui.sh --api`)": "(e.g. `sh webui.sh --api`)",
"{{item}} not provided": "{{item}} ارائه نشده است",
"{{modelName}} is thinking...": "{{modelName}} در حال فکر کردن است...",
"{{webUIName}} Backend Required": "بکند {{webUIName}} نیاز است.",
"About": "درباره", "About": "درباره",
"Account": "حساب کاربری", "Account": "حساب کاربری",
"Action": "عمل", "Action": "عمل",
"Add a model": "اضافه کردن یک مدل", "Add a model": "اضافه کردن یک مدل",
"Add a model tag name": "اضافه کردن یک نام تگ برای مدل",
"Add a short description about what this modelfile does": "توضیح کوتاهی در مورد کاری که این فایل\u200cمدل انجام می دهد اضافه کنید",
"Add a short title for this prompt": "یک عنوان کوتاه برای این درخواست اضافه کنید",
"Add Docs": "اضافه کردن اسناد", "Add Docs": "اضافه کردن اسناد",
"Add Files": "اضافه کردن فایل‌ها", "Add Files": "اضافه کردن فایل\u200cها",
"Add LiteLLM Model": "اضافه کردن مدل LiteLLM", "Add LiteLLM Model": "اضافه کردن مدل LiteLLM",
"add tags": "اضافه کردن تگ‌ها", "Add message": "اضافه کردن پیغام",
"admin": "مدیریت", "add tags": "اضافه کردن تگ\u200cها",
"Adjusting these settings will apply changes universally to all users.": "با تنظیم این تنظیمات، تغییرات به طور کلی برای همه کاربران اعمال می شود.",
"Admin": "",
"Admin Panel": "پنل مدیریت", "Admin Panel": "پنل مدیریت",
"Admin Settings": "تنظیمات مدیریت", "Admin Settings": "تنظیمات مدیریت",
"Advanced Model Params": "پارامترهای پیشرفته مدل", "Advanced Model Params": "پارامترهای پیشرفته مدل",
"Advanced Parameters": "پارامترهای پیشرفته", "Advanced Parameters": "پارامترهای پیشرفته",
"all": "همه", "all": "همه",
"All Users": "همه کاربران",
"Allow": "اجازه دادن", "Allow": "اجازه دادن",
"Allow Chat Deletion": "اجازه حذف گپ", "Allow Chat Deletion": "اجازه حذف گپ",
"alphanumeric characters and hyphens": "حروف الفبایی و خط فاصله",
"Already have an account?": "از قبل حساب کاربری دارید؟",
"and": "و",
"API Base URL": "API Base URL", "API Base URL": "API Base URL",
"API Key": "API Key", "API Key": "API Key",
"API RPM": "API RPM", "API RPM": "API RPM",
"are allowed - Activate this command by typing": "مجاز هستند - این دستور را با تایپ کردن این فعال کنید:",
"Audio": "صدا", "Audio": "صدا",
"AUTOMATIC1111 Base URL": "پایه URL AUTOMATIC1111 ",
"Auto-playback response": "پخش خودکار پاسخ ", "Auto-playback response": "پخش خودکار پاسخ ",
"Auto-send input after 3 sec.": "به طور خودکار ورودی را پس از 3 ثانیه ارسال کن.", "Auto-send input after 3 sec.": "به طور خودکار ورودی را پس از 3 ثانیه ارسال کن.",
"AUTOMATIC1111 Base URL": "پایه URL AUTOMATIC1111 ",
"available!": "در دسترس!",
"Back": "بازگشت", "Back": "بازگشت",
"Builder Mode": "حالت سازنده", "Builder Mode": "حالت سازنده",
"Cancel": "لغو", "Cancel": "لغو",
"Categories": "دسته‌بندی‌ها", "Categories": "دسته\u200cبندی\u200cها",
"Change Password": "تغییر رمز عبور", "Change Password": "تغییر رمز عبور",
"Chat History": "تاریخچه‌ی گفتگو", "Chat": "گپ",
"Chat History": "تاریخچه\u200cی گفتگو",
"Chat History is off for this browser.": "سابقه گپ برای این مرورگر خاموش است.", "Chat History is off for this browser.": "سابقه گپ برای این مرورگر خاموش است.",
"Chats": "گپها", "Chats": "گپ\u200cها",
"Check Again": "چک مجدد", "Check Again": "چک مجدد",
"Check for updates": "بررسی به\u200cروزرسانی",
"Checking for updates...": "در حال بررسی برای به\u200cروزرسانی..",
"Choose a model before saving...": "قبل از ذخیره یک مدل را انتخاب کنید...", "Choose a model before saving...": "قبل از ذخیره یک مدل را انتخاب کنید...",
"Chunk Overlap": "همپوشانی تکه", "Chunk Overlap": "همپوشانی تکه",
"Chunk Params": "پارامترهای تکه", "Chunk Params": "پارامترهای تکه",
"Chunk Size": "اندازه تکه", "Chunk Size": "اندازه تکه",
"Click here for help": "برای کمک اینجا را کلیک کنید",
"Click here to check other modelfiles.": "برای بررسی سایر فایل\u200cهای مدل اینجا را کلیک کنید.",
"click here.": "اینجا کلیک کنید.", "click here.": "اینجا کلیک کنید.",
"Click here to check other modelfiles.": "برای بررسی سایر فایل‌های مدل اینجا را کلیک کنید.", "Click on the user role button to change a user's role.": "برای تغییر نقش کاربر، روی دکمه نقش کاربر کلیک کنید.",
"Close": "بسته",
"Collection": "مجموعه", "Collection": "مجموعه",
"Command": "دستور", "Command": "دستور",
"Confirm Password": "تایید رمز عبور", "Confirm Password": "تایید رمز عبور",
@ -44,68 +67,86 @@
"Content": "محتوا", "Content": "محتوا",
"Context Length": "طول زمینه", "Context Length": "طول زمینه",
"Conversation Mode": "حالت مکالمه", "Conversation Mode": "حالت مکالمه",
"Copying to clipboard was successful!": "کپی کردن در کلیپ بورد با موفقیت انجام شد!",
"Copy last code block": "کپی آخرین بلوک کد", "Copy last code block": "کپی آخرین بلوک کد",
"Copy last response": "کپی آخرین پاسخ", "Copy last response": "کپی آخرین پاسخ",
"Copying to clipboard was successful!": "کپی کردن در کلیپ بورد با موفقیت انجام شد!",
"Create a concise, 3-5 word phrase as a header for the following query, strictly adhering to the 3-5 word limit and avoiding the use of the word 'title':": "یک عبارت مختصر و ۳ تا ۵ کلمه ای را به عنوان سرفصل برای پرس و جو زیر ایجاد کنید، به شدت محدودیت ۳-۵ کلمه را رعایت کنید و از استفاده از کلمه 'عنوان' خودداری کنید:",
"Create a modelfile": "ایجاد یک فایل مدل", "Create a modelfile": "ایجاد یک فایل مدل",
"Create Account": "ساخت حساب کاربری",
"Created at": "ایجاد شده در",
"Created by": "ایجاد شده توسط",
"Current Model": "مدل فعلی",
"Current Password": "رمز عبور فعلی", "Current Password": "رمز عبور فعلی",
"Custom": "دلخواه", "Custom": "دلخواه",
"Customize Ollama models for a specific purpose": "مدل های اولاما را برای یک هدف خاص سفارشی کنید", "Customize Ollama models for a specific purpose": "مدل های اولاما را برای یک هدف خاص سفارشی کنید",
"Dark": "تیره", "Dark": "تیره",
"Database": "پایگاه داده", "Database": "پایگاه داده",
"DD/MM/YYYY HH:mm": "DD/MM/YYYY HH:mm",
"Default": "پیشفرض", "Default": "پیشفرض",
"Default model updated": "مدل پیشفرض به‌روزرسانی شد", "Default (Web API)": "پیشفرض (Web API)",
"Default model updated": "مدل پیشفرض به\u200cروزرسانی شد",
"Default Prompt Suggestions": "پیشنهادات پرامپت پیش فرض", "Default Prompt Suggestions": "پیشنهادات پرامپت پیش فرض",
"Default User Role": "نقش کاربر پیش فرض", "Default User Role": "نقش کاربر پیش فرض",
"Default (Web API)": "پیشفرض (Web API)", "delete": "حذف",
"Delete a model": "حذف یک مدل", "Delete a model": "حذف یک مدل",
"Delete chat": "حذف گپ", "Delete chat": "حذف گپ",
"Delete Chats": "حذف گپها", "Delete Chats": "حذف گپ\u200cها",
"Deleted {{deleteModelTag}}": "{{deleteModelTag}} پاک شد", "Deleted {{deleteModelTag}}": "{{deleteModelTag}} پاک شد",
"delete": "حذف",
"Deleted {tagName}": "{tagName} حذف شد", "Deleted {tagName}": "{tagName} حذف شد",
"Description": "توضیحات", "Description": "توضیحات",
"Desktop Notifications": "اعلان",
"Disabled": "غیرفعال", "Disabled": "غیرفعال",
"Discover a modelfile": "فایل مدل را کشف کنید", "Discover a modelfile": "فایل مدل را کشف کنید",
"Discover a prompt": "یک اعلان را کشف کنید", "Discover a prompt": "یک اعلان را کشف کنید",
"Discover, download, and explore custom prompts": "پرامپتهای سفارشی را کشف، دانلود و کاوش کنید", "Discover, download, and explore custom prompts": "پرامپت\u200cهای سفارشی را کشف، دانلود و کاوش کنید",
"Discover, download, and explore model presets": "پیش تنظیمات مدل را کشف، دانلود و کاوش کنید", "Discover, download, and explore model presets": "پیش تنظیمات مدل را کشف، دانلود و کاوش کنید",
"Display the username instead of You in the Chat": "نمایش نام کاربری به جای «شما» در چت",
"Document": "سند", "Document": "سند",
"Documents": "اسناد",
"Document Settings": "تنظیمات سند", "Document Settings": "تنظیمات سند",
"Documents": "اسناد",
"does not make any external connections, and your data stays securely on your locally hosted server.": "هیچ اتصال خارجی ایجاد نمی کند و داده های شما به طور ایمن در سرور میزبان محلی شما باقی می ماند.",
"Don't Allow": "اجازه نده", "Don't Allow": "اجازه نده",
"Don't have an account?": "حساب کاربری ندارید؟",
"Download as a File": "دانلود به صورت فایل", "Download as a File": "دانلود به صورت فایل",
"Download Database": "دانلود پایگاه داده", "Download Database": "دانلود پایگاه داده",
"Drop any files here to add to the conversation": "هر فایلی را اینجا رها کنید تا به مکالمه اضافه شود",
"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "به طور مثال '30s','10m'. واحد\u200cهای زمانی معتبر 's', 'm', 'h' هستند.",
"Edit Doc": "ویرایش سند", "Edit Doc": "ویرایش سند",
"Edit User": "ویرایش کاربر", "Edit User": "ویرایش کاربر",
"Email": "ایمیل", "Email": "ایمیل",
"Enable Chat History": "تاریخچه چت را فعال کنید",
"Enable New Sign Ups": "فعال کردن ثبت نام\u200cهای جدید",
"Enabled": "فعال", "Enabled": "فعال",
"Enable New Sign Ups": "فعال کردن ثبت نام‌های جدید", "Enter OpenAI API Key": "کلید OpenAI API را وارد کنید",
"Experimental": "آزمایشی", "Enter stop sequence": "توالی توقف را وارد کنید",
"Export All Chats (All Users)": "اکسپورت از همه گپ‌ها(همه کاربران)", "Enter Your Email": "ایمیل خود را وارد کنید",
"Export Chats": "اکسپورت از گپ‌ها", "Enter Your Full Name": "نام کامل خود را وارد کنید",
"Enter Your Password": "رمز عبور خود را وارد کنید",
"Export All Chats (All Users)": "اکسپورت از همه گپ\u200cها(همه کاربران)",
"Export Chats": "اکسپورت از گپ\u200cها",
"Export Documents Mapping": "اکسپورت از نگاشت اسناد", "Export Documents Mapping": "اکسپورت از نگاشت اسناد",
"Export Modelfiles": "اکسپورت از فایل‌های مدل", "Export Modelfiles": "اکسپورت از فایل\u200cهای مدل",
"Export Prompts": "اکسپورت از پرامپت‌ها", "Export Prompts": "اکسپورت از پرامپت\u200cها",
"Failed to read clipboard contents": "خواندن محتوای کلیپ بورد ناموفق بود", "Failed to read clipboard contents": "خواندن محتوای کلیپ بورد ناموفق بود",
"File Mode": "حالت فایل",
"File not found.": "فایل یافت نشد.", "File not found.": "فایل یافت نشد.",
"Focus chat input": "فوکوس کردن ورودی گپ", "Focus chat input": "فوکوس کردن ورودی گپ",
"Format your variables using square brackets like this:": "متغیرهای خود را با استفاده از براکت مربع به شکل زیر قالب بندی کنید:",
"From (Base Model)": "از (مدل پایه)", "From (Base Model)": "از (مدل پایه)",
"Full Screen Mode": "حالت تمام صفحه", "Full Screen Mode": "حالت تمام صفحه",
"General": "عمومی", "General": "عمومی",
"General Settings": "تنظیمات عمومی", "General Settings": "تنظیمات عمومی",
"Hello, {{name}}": "سلام، {{name}}", "Hello, {{name}}": "سلام، {{name}}",
"Hide": "پنهان",
"How can I help you today?": "امروز چطور می توانم کمک تان کنم؟", "How can I help you today?": "امروز چطور می توانم کمک تان کنم؟",
"Image Generation (Experimental)": "تولید تصویر (آزمایشی)", "Image Generation (Experimental)": "تولید تصویر (آزمایشی)",
"Image Settings": "تنظیمات تصویر", "Image Settings": "تنظیمات تصویر",
"Images": "تصاویر", "Images": "تصاویر",
"Import Chats": "ایمپورت گپها", "Import Chats": "ایمپورت گپ\u200cها",
"Import Documents Mapping": "ایمپورت نگاشت اسناد", "Import Documents Mapping": "ایمپورت نگاشت اسناد",
"Import Modelfiles": "ایمپورت فایل‌های مدل", "Import Modelfiles": "ایمپورت فایل\u200cهای مدل",
"Import Prompts": "ایمپورت پرامپت‌ها", "Import Prompts": "ایمپورت پرامپت\u200cها",
"Include `--api` flag when running stable-diffusion-webui": "فلگ `--api` را هنکام اجرای stable-diffusion-webui استفاده کنید.",
"Interface": "رابط", "Interface": "رابط",
"{{item}} not provided": "{{item}} ارائه نشده است",
"join our Discord for help.": "برای کمک به دیسکورد ما بپیوندید.", "join our Discord for help.": "برای کمک به دیسکورد ما بپیوندید.",
"JSON": "JSON", "JSON": "JSON",
"JWT Expiration": "JWT انقضای", "JWT Expiration": "JWT انقضای",
@ -118,80 +159,90 @@
"LiteLLM API Base URL": "LiteLLM API Base URL", "LiteLLM API Base URL": "LiteLLM API Base URL",
"LiteLLM API Key": "LiteLLM API Key", "LiteLLM API Key": "LiteLLM API Key",
"LiteLLM API RPM": "LiteLLM API RPM", "LiteLLM API RPM": "LiteLLM API RPM",
"LLMs can make mistakes. Verify important information.": "مدل‌های زبانی بزرگ می‌توانند اشتباه کنند. اطلاعات مهم را راستی‌آزمایی کنید.", "LLMs can make mistakes. Verify important information.": "مدل\u200cهای زبانی بزرگ می\u200cتوانند اشتباه کنند. اطلاعات مهم را راستی\u200cآزمایی کنید.",
"Made by OpenWebUI Community": "ساخته شده توسط OpenWebUI Community", "Made by OpenWebUI Community": "ساخته شده توسط OpenWebUI Community",
"Make sure to enclose them with": "مطمئن شوید که آنها را با این محصور کنید:",
"Manage LiteLLM Models": "Manage LiteLLM Models", "Manage LiteLLM Models": "Manage LiteLLM Models",
"Maximum of 3 models can be downloaded simultaneously. Please try again later.": "حداکثر 3 مدل را می توان به طور همزمان دانلود کرد. لطفاً بعداً دوباره امتحان کنید.", "Manage Ollama Models": "مدیریت مدل\u200cهای اولاما",
"Max Tokens": "حداکثر توکن", "Max Tokens": "حداکثر توکن",
"Mirostat Eta": "Mirostat Eta", "Maximum of 3 models can be downloaded simultaneously. Please try again later.": "حداکثر 3 مدل را می توان به طور همزمان دانلود کرد. لطفاً بعداً دوباره امتحان کنید.",
"Mirostat": "Mirostat", "Mirostat": "Mirostat",
"Mirostat Eta": "Mirostat Eta",
"Mirostat Tau": "Mirostat Tau", "Mirostat Tau": "Mirostat Tau",
"MMMM DD, YYYY": "MMMM DD, YYYY",
"Model '{{modelName}}' has been successfully downloaded.": "مدل '{{modelName}}' با موفقیت دانلود شد.", "Model '{{modelName}}' has been successfully downloaded.": "مدل '{{modelName}}' با موفقیت دانلود شد.",
"Modelfile Advanced Settings": "تنظیمات پیشرفته فایل‌مدل", "Model '{{modelTag}}' is already in queue for downloading.": "مدل '{{modelTag}}' در حال حاضر در صف برای دانلود است.",
"Modelfile Content": "محتویات فایل‌مدل",
"Modelfile": "فایل مدل",
"Modelfiles": "فایل‌های مدل",
"Model {{modelId}} not found": "مدل {{modelId}} یافت نشد", "Model {{modelId}} not found": "مدل {{modelId}} یافت نشد",
"Model {{modelName}} already exists.": "مدل {{modelName}} در حال حاضر وجود دارد.", "Model {{modelName}} already exists.": "مدل {{modelName}} در حال حاضر وجود دارد.",
"Model '{{modelTag}}' is already in queue for downloading.": "مدل '{{modelTag}}' در حال حاضر در صف برای دانلود است.",
"{{modelName}} is thinking...": "{{modelName}} در حال فکر کردن است...",
"Model Name": "نام مدل", "Model Name": "نام مدل",
"Model not selected": "مدل انتخاب نشده", "Model not selected": "مدل انتخاب نشده",
"Models": "مدل‌ها",
"Model Tag Name": "نام تگ مدل", "Model Tag Name": "نام تگ مدل",
"Modelfile": "فایل مدل",
"Modelfile Advanced Settings": "تنظیمات پیشرفته فایل\u200cمدل",
"Modelfiles": "فایل\u200cهای مدل",
"Models": "مدل\u200cها",
"My Documents": "اسناد من", "My Documents": "اسناد من",
"My Modelfiles": "فایل‌های مدل من", "My Modelfiles": "فایل\u200cهای مدل من",
"My Prompts": "پرامپت‌های من", "My Prompts": "پرامپت\u200cهای من",
"Name": "نام", "Name": "نام",
"Name Tag": "نام تگ", "Name Tag": "نام تگ",
"Name your modelfile": "",
"New Chat": "گپ جدید", "New Chat": "گپ جدید",
"New Password": "رمز عبور جدید", "New Password": "رمز عبور جدید",
"Desktop Notifications": "اعلان", "Not sure what to add?": "مطمئن نیستید چه چیزی را اضافه کنید؟",
"Not sure what to write? Switch to": "مطمئن نیستید چه بنویسید؟ تغییر به", "Not sure what to write? Switch to": "مطمئن نیستید چه بنویسید؟ تغییر به",
"Off": "خاموش", "Off": "خاموش",
"Okay, Let's Go!": "باشه، بزن بریم!", "Okay, Let's Go!": "باشه، بزن بریم!",
"Ollama API URL": "اولاما API URL",
"Ollama Version": "نسخه Ollama", "Ollama Version": "نسخه Ollama",
"Only alphanumeric characters and hyphens are allowed in the command string.": "فقط کاراکترهای الفبایی و خط فاصله در رشته فرمان مجاز هستند.",
"On": "روشن", "On": "روشن",
"Only": "فقط",
"Only alphanumeric characters and hyphens are allowed in the command string.": "فقط کاراکترهای الفبایی و خط فاصله در رشته فرمان مجاز هستند.",
"Oops! Hold tight! Your files are still in the processing oven. We're cooking them up to perfection. Please be patient and we'll let you know once they're ready.": "اوه! فایل های شما هنوز در فر پردازش هستند. ما آنها را کامل می پزیم. لطفا صبور باشید، به محض آماده شدن به شما اطلاع خواهیم داد.", "Oops! Hold tight! Your files are still in the processing oven. We're cooking them up to perfection. Please be patient and we'll let you know once they're ready.": "اوه! فایل های شما هنوز در فر پردازش هستند. ما آنها را کامل می پزیم. لطفا صبور باشید، به محض آماده شدن به شما اطلاع خواهیم داد.",
"OpenAI API": "OpenAI API", "Oops! Looks like the URL is invalid. Please double-check and try again.": "",
"Oops! You're using an unsupported method (frontend only). Please serve the WebUI from the backend.": "",
"Open": "باز",
"Open AI": "Open AI", "Open AI": "Open AI",
"Open new chat": "باز کردن گپ جدید", "Open new chat": "باز کردن گپ جدید",
"OpenAI API": "OpenAI API",
"or": "روشن", "or": "روشن",
"Parameters": "پارامترها", "Parameters": "پارامترها",
"Password": "رمز عبور", "Password": "رمز عبور",
"pending": "Pending", "Pending": "",
"Permission denied when accessing microphone: {{error}}": "هنگام دسترسی به میکروفون، اجازه داده نشد: {{error}}", "Permission denied when accessing microphone: {{error}}": "هنگام دسترسی به میکروفون، اجازه داده نشد: {{error}}",
"Playground": "زمین بازی",
"Profile": "پروفایل", "Profile": "پروفایل",
"Prompt Content": "محتویات پرامپت", "Prompt Content": "محتویات پرامپت",
"Prompts": "پرامپت‌ها",
"Prompt suggestions": "پیشنهادات پرامپت", "Prompt suggestions": "پیشنهادات پرامپت",
"Prompts": "پرامپت\u200cها",
"Pull a model from Ollama.com": "دریافت یک مدل از Ollama.com", "Pull a model from Ollama.com": "دریافت یک مدل از Ollama.com",
"Pull Progress": "پیشرفت دریافت", "Pull Progress": "پیشرفت دریافت",
"RAG Template": "RAG الگوی", "RAG Template": "RAG الگوی",
"Raw Format": "فرمت خام", "Raw Format": "فرمت خام",
"Record voice": "ضبط صدا", "Record voice": "ضبط صدا",
"Redirecting you to OpenWebUI Community": "در حال هدایت به OpenWebUI Community", "Redirecting you to OpenWebUI Community": "در حال هدایت به OpenWebUI Community",
"Release Notes": "یادداشتهای انتشار", "Release Notes": "یادداشت\u200cهای انتشار",
"Repeat Last N": "Repeat Last N", "Repeat Last N": "Repeat Last N",
"Repeat Penalty": "Repeat Penalty", "Repeat Penalty": "Repeat Penalty",
"Request Mode": "حالت درخواست", "Request Mode": "حالت درخواست",
"Reset Vector Storage": "بازنشانی ذخیره سازی برداری", "Reset Vector Storage": "بازنشانی ذخیره سازی برداری",
"Response AutoCopy to Clipboard": "کپی خودکار پاسخ به کلیپ بورد", "Response AutoCopy to Clipboard": "کپی خودکار پاسخ به کلیپ بورد",
"Role": "نقش", "Role": "نقش",
"Ros\u00e9 Pine Dawn": "Ros\u00e9 Pine Dawn", "Rosé Pine": "Rosé Pine",
"Ros\u00e9 Pine": "Ros\u00e9 Pine", "Rosé Pine Dawn": "Rosé Pine Dawn",
"Save & Create": "ذخیره و ایجاد",
"Save": "ذخیره", "Save": "ذخیره",
"Save & Create": "ذخیره و ایجاد",
"Save & Submit": "ذخیره و ارسال", "Save & Submit": "ذخیره و ارسال",
"Save & Update": "ذخیره و به‌روزرسانی", "Save & Update": "ذخیره و به\u200cروزرسانی",
"Saving chat logs directly to your browser's storage is no longer supported. Please take a moment to download and delete your chat logs by clicking the button below. Don't worry, you can easily re-import your chat logs to the backend through": "ذخیره گزارش‌های چت مستقیماً در حافظه مرورگر شما دیگر پشتیبانی نمی‌شود. لطفاً با کلیک بر روی دکمه زیر، چند لحظه برای دانلود و حذف گزارش های چت خود وقت بگذارید. نگران نباشید، شما به راحتی می توانید گزارش های چت خود را از طریق بکند دوباره وارد کنید", "Saving chat logs directly to your browser's storage is no longer supported. Please take a moment to download and delete your chat logs by clicking the button below. Don't worry, you can easily re-import your chat logs to the backend through": "ذخیره گزارش\u200cهای چت مستقیماً در حافظه مرورگر شما دیگر پشتیبانی نمی\u200cشود. لطفاً با کلیک بر روی دکمه زیر، چند لحظه برای دانلود و حذف گزارش های چت خود وقت بگذارید. نگران نباشید، شما به راحتی می توانید گزارش های چت خود را از طریق بکند دوباره وارد کنید",
"Scan": "اسکن",
"Scan complete!": "اسکن کامل شد!", "Scan complete!": "اسکن کامل شد!",
"Scan for documents from {{path}}": "اسکن اسناد از {{path}}", "Scan for documents from {{path}}": "اسکن اسناد از {{path}}",
"Scan": "اسکن", "Search": "جستجو",
"Search Documents": "جستجوی اسناد",
"Search Prompts": "جستجوی پرامپت\u200cها",
"See readme.md for instructions": "برای مشاهده دستورالعمل\u200cها به readme.md مراجعه کنید",
"See what's new": "ببینید موارد جدید چه بوده",
"Seed": "Seed", "Seed": "Seed",
"See readme.md for instructions": "برای مشاهده دستورالعمل‌ها به readme.md مراجعه کنید",
"Select a model": "انتخاب یک مدل", "Select a model": "انتخاب یک مدل",
"Send a Messsage": "ارسال یک پیام", "Send a Messsage": "ارسال یک پیام",
"Send message": "ارسال پیام", "Send message": "ارسال پیام",
@ -199,129 +250,71 @@
"Set as default": "تنظیم به عنوان پیشفرض", "Set as default": "تنظیم به عنوان پیشفرض",
"Set Default Model": "تنظیم مدل پیش فرض", "Set Default Model": "تنظیم مدل پیش فرض",
"Set Image Size": "تنظیم اندازه تصویر", "Set Image Size": "تنظیم اندازه تصویر",
"Set Steps": "تنظیم گام‌ها", "Set Steps": "تنظیم گام\u200cها",
"Settings": "تنظیمات",
"Set Title Auto-Generation Model": "تنظیم مدل تولید خودکار عنوان", "Set Title Auto-Generation Model": "تنظیم مدل تولید خودکار عنوان",
"Set Voice": "تنظیم صدا", "Set Voice": "تنظیم صدا",
"Settings": "تنظیمات",
"Share to OpenWebUI Community": "اشتراک گذاری با OpenWebUI Community", "Share to OpenWebUI Community": "اشتراک گذاری با OpenWebUI Community",
"short-summary": "خلاصه کوتاه",
"Show": "نمایش",
"Show shortcuts": "نمایش میانبرها", "Show shortcuts": "نمایش میانبرها",
"sidebar": "نوار کناری",
"Sign in": "ورود",
"Sign Out": "خروج", "Sign Out": "خروج",
"SpeechRecognition API is not supported in this browser.": "API تشخیص گفتار در این مرورگر پشتیبانی نمی شود.", "Sign up": "ثبت نام",
"Speech recognition error: {{error}}": "خطای تشخیص گفتار: {{error}}", "Speech recognition error: {{error}}": "خطای تشخیص گفتار: {{error}}",
"Speech-to-Text Engine": "موتور گفتار به متن", "Speech-to-Text Engine": "موتور گفتار به متن",
"SpeechRecognition API is not supported in this browser.": "API تشخیص گفتار در این مرورگر پشتیبانی نمی شود.",
"Stop Sequence": "توالی توقف", "Stop Sequence": "توالی توقف",
"STT Settings": "STT تنظیمات", "STT Settings": "STT تنظیمات",
"Successfully updated.": "با موفقیت به روز شد", "Submit": "ارسال",
"Success": "موفقیت", "Success": "موفقیت",
"Successfully updated": "",
"Successfully updated.": "با موفقیت به روز شد",
"Sync All": "همگام سازی همه", "Sync All": "همگام سازی همه",
"System": "سیستم",
"System Prompt": "پرامپت سیستم", "System Prompt": "پرامپت سیستم",
"Tags": "تگها", "Tags": "تگ\u200cها",
"Temperature": "دما", "Temperature": "دما",
"Template": "الگو", "Template": "الگو",
"Text Completion": "تکمیل متن",
"Text-to-Speech Engine": "موتور تبدیل متن به گفتار", "Text-to-Speech Engine": "موتور تبدیل متن به گفتار",
"Tfs Z": "Tfs Z", "Tfs Z": "Tfs Z",
"Theme": "قالب", "Theme": "قالب",
"This ensures that your valuable conversations are securely saved to your backend database. Thank you!": "این تضمین می کند که مکالمات ارزشمند شما به طور ایمن در پایگاه داده بکند ذخیره می شود. تشکر!", "This ensures that your valuable conversations are securely saved to your backend database. Thank you!": "این تضمین می کند که مکالمات ارزشمند شما به طور ایمن در پایگاه داده بکند ذخیره می شود. تشکر!",
"This setting does not sync across browsers or devices.": "این تنظیم در مرورگرها یا دستگاه\u200cها همگام\u200cسازی نمی\u200cشود.",
"Tip: Update multiple variable slots consecutively by pressing the tab key in the chat input after each replacement.": "",
"Title": "عنوان",
"Title Auto-Generation": "تولید خودکار عنوان", "Title Auto-Generation": "تولید خودکار عنوان",
"Title Generation Prompt": "پرامپت تولید عنوان", "Title Generation Prompt": "پرامپت تولید عنوان",
"Title": "عنوان", "to": "به",
"To access the available model names for downloading,": "برای دسترسی به نام مدل های موجود برای دانلود،", "To access the available model names for downloading,": "برای دسترسی به نام مدل های موجود برای دانلود،",
"Toggle settings": "نمایش/عدم نمایش تنظیمات", "Toggle settings": "نمایش/عدم نمایش تنظیمات",
"Toggle sidebar": "نمایش/عدم نمایش نوار کناری", "Toggle sidebar": "نمایش/عدم نمایش نوار کناری",
"Top K": "Top K", "Top K": "Top K",
"Top P": "Top P", "Top P": "Top P",
"Trouble accessing Ollama?": "در دسترسی به اولاما مشکل دارید؟",
"TTS Settings": "تنظیمات TTS", "TTS Settings": "تنظیمات TTS",
"Uh-oh! There was an issue connecting to {{provider}}.": "اوه اوه! مشکلی در اتصال به {{provider}} وجود داشت.", "Uh-oh! There was an issue connecting to {{provider}}.": "اوه اوه! مشکلی در اتصال به {{provider}} وجود داشت.",
"Upload a GGUF model": "بارگذاری یک مدل GGUF", "Unknown File Type '{{file_type}}', but accepting and treating as plain text": "",
"Upload files": "بارگذاری فایلها", "Upload files": "بارگذاری فایل\u200cها",
"Upload Progress": "میزان پیشرفت بارگذاری", "Use '#' in the prompt input to load and select your documents.": "در پرامپت از '#' برای لود و انتخاب اسناد خود استفاده کنید.",
"URL Mode": "حالت URL", "User": "",
"User Permissions": "مجوزهای کاربر", "User Permissions": "مجوزهای کاربر",
"Users": "کاربران", "Users": "کاربران",
"user": "کاربر",
"WebUI Add-ons": "WebUI افزونه‌های",
"{{webUIName}} Backend Required": "بکند {{webUIName}} نیاز است.",
"WebUI Settings": "تنظیمات WebUI",
"Web": "وب",
"Whisper (Local)": "ویسپر (محلی)",
"You're now logged in.": "شما اکنون وارد شده‌اید.",
"Hide": "پنهان",
"Show": "نمایش",
"Enter stop sequence": "توالی توقف را وارد کنید",
"Enter OpenAI API Key": "کلید OpenAI API را وارد کنید",
"Current Model": "مدل فعلی",
"Display the username instead of You in the Chat": "نمایش نام کاربری به جای «شما» در چت",
"Create a concise, 3-5 word phrase as a header for the following query, strictly adhering to the 3-5 word limit and avoiding the use of the word 'title':": "یک عبارت مختصر و ۳ تا ۵ کلمه ای را به عنوان سرفصل برای پرس و جو زیر ایجاد کنید، به شدت محدودیت ۳-۵ کلمه را رعایت کنید و از استفاده از کلمه 'عنوان' خودداری کنید:",
"Search": "جستجو",
"Search Documents": "جستجوی اسناد",
"Use '#' in the prompt input to load and select your documents.": "در پرامپت از '#' برای لود و انتخاب اسناد خود استفاده کنید.",
"All Users": "همه کاربران",
"Click on the user role button to change a user's role.": "برای تغییر نقش کاربر، روی دکمه نقش کاربر کلیک کنید.",
"Manage Ollama Models": "مدیریت مدل‌های اولاما",
"Advanced": "پیشرفته",
"Click here for help": "برای کمک اینجا را کلیک کنید",
"Not sure what to add?": "مطمئن نیستید چه چیزی را اضافه کنید؟",
"Adjusting these settings will apply changes universally to all users.": "با تنظیم این تنظیمات، تغییرات به طور کلی برای همه کاربران اعمال می شود.",
"Trouble accessing Ollama?": "در دسترسی به اولاما مشکل دارید؟",
"(e.g. `sh webui.sh --api`)": "(e.g. `sh webui.sh --api`)",
"Include `--api` flag when running stable-diffusion-webui": "فلگ `--api` را هنکام اجرای stable-diffusion-webui استفاده کنید.",
"This setting does not sync across browsers or devices.": "این تنظیم در مرورگرها یا دستگاه‌ها همگام‌سازی نمی‌شود.",
"When history is turned off, new chats on this browser won't appear in your history on any of your devices.": "وقتی سابقه خاموش است، چت‌های جدید در این مرورگر در سابقه شما در هیچ یک از دستگاه‌هایتان ظاهر نمی‌شوند.",
"Enable Chat History": "تاریخچه چت را فعال کنید",
"Search Prompts": "جستجوی پرامپت‌ها",
"available!": "در دسترس!",
"Created by": "ایجاد شده توسط",
"Check for updates": "بررسی به‌روزرسانی",
"See what's new": "ببینید موارد جدید چه بوده",
"Checking for updates...": "در حال بررسی برای به‌روزرسانی..",
"Whats New in": "موارد جدید در",
"Enter Your Email": "ایمیل خود را وارد کنید",
"Enter Your Password": "رمز عبور خود را وارد کنید",
"Enter Your Full Name": "نام کامل خود را وارد کنید",
"Sign in": "ورود",
"Sign up": "ثبت نام",
"Create Account": "ساخت حساب کاربری",
"Don't have an account?": "حساب کاربری ندارید؟",
"Already have an account?": "از قبل حساب کاربری دارید؟",
"to": "به",
"does not make any external connections, and your data stays securely on your locally hosted server.": "هیچ اتصال خارجی ایجاد نمی کند و داده های شما به طور ایمن در سرور میزبان محلی شما باقی می ماند.",
"DD/MM/YYYY HH:mm": "DD/MM/YYYY HH:mm",
"MMMM DD, YYYY": "MMMM DD, YYYY",
"Created at": "ایجاد شده در",
"'s', 'm', 'h', 'd', 'w' or '-1' for no expiration.": "'s', 'm', 'h', 'd', 'w' یا '-1' برای غیر فعال کردن انقضا.",
"Valid time units:": "واحدهای زمانی معتبر:",
"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "به طور مثال '30s','10m'. واحد‌های زمانی معتبر 's', 'm', 'h' هستند.",
"Name your Modelfile": "فایل‌مدل خود را نام‌گذاری کنید",
"Add a model tag name": "اضافه کردن یک نام تگ برای مدل",
"Add a short description about what this modelfile does": "توضیح کوتاهی در مورد کاری که این فایل‌مدل انجام می دهد اضافه کنید",
"Write a prompt suggestion (e.g. Who are you?)": "یک پیشنهاد پرامپت بنویسید (مثلاً شما کی هستید؟)",
"Only": "فقط",
"alphanumeric characters and hyphens": "حروف الفبایی و خط فاصله",
"are allowed - Activate this command by typing": "مجاز هستند - این دستور را با تایپ کردن این فعال کنید:",
"to chat input.": "در ورودی گپ",
"Write a summary in 50 words that summarizes [topic or keyword]": "خلاصه ای در 50 کلمه بنویسید که [موضوع یا کلمه کلیدی] را خلاصه کند.",
"variable": "متغیر",
"Format your variables using square brackets like this:": "متغیرهای خود را با استفاده از براکت مربع به شکل زیر قالب بندی کنید:",
"Make sure to enclose them with": "مطمئن شوید که آنها را با این محصور کنید:",
"and": "و",
"Utilize": "استفاده کنید", "Utilize": "استفاده کنید",
"Valid time units:": "واحدهای زمانی معتبر:",
"variable": "متغیر",
"variable to have them replaced with clipboard content.": "متغیر برای جایگزینی آنها با محتوای کلیپ بورد.", "variable to have them replaced with clipboard content.": "متغیر برای جایگزینی آنها با محتوای کلیپ بورد.",
"short-summary": "خلاصه کوتاه", "Web": "وب",
"Add a short title for this prompt": "یک عنوان کوتاه برای این درخواست اضافه کنید", "WebUI Add-ons": "WebUI افزونه\u200cهای",
"Open": "باز", "WebUI Settings": "تنظیمات WebUI",
"Close": "بسته", "Whats New in": "موارد جدید در",
"sidebar": "نوار کناری", "When history is turned off, new chats on this browser won't appear in your history on any of your devices.": "وقتی سابقه خاموش است، چت\u200cهای جدید در این مرورگر در سابقه شما در هیچ یک از دستگاه\u200cهایتان ظاهر نمی\u200cشوند.",
"Playground": "زمین بازی", "Whisper (Local)": "ویسپر (محلی)",
"Submit": "ارسال", "Write a prompt suggestion (e.g. Who are you?)": "یک پیشنهاد پرامپت بنویسید (مثلاً شما کی هستید؟)",
"(Beta)": "(بتا)", "Write a summary in 50 words that summarizes [topic or keyword].": "خلاصه ای در 50 کلمه بنویسید که [موضوع یا کلمه کلیدی] را خلاصه کند.",
"Text Completion": "تکمیل متن", "You": "شما",
"Chat": "گپ",
"You're a helpful assistant.": "تو یک دستیار سودمند هستی.", "You're a helpful assistant.": "تو یک دستیار سودمند هستی.",
"System": "سیستم", "You're now logged in.": "شما اکنون وارد شده\u200cاید."
"assistant": "دستیار",
"Add message": "اضافه کردن پیغام",
"Enter a user message here": "در اینجا یک پیام کاربر را وارد کنید",
"Enter an assistant message here": "در اینجا یک پیام دستیار را وارد کنید",
"Drop any files here to add to the conversation": "هر فایلی را اینجا رها کنید تا به مکالمه اضافه شود",
"You": "شما"
} }

View file

@ -18,10 +18,9 @@
"Add message": "Ajouter un message", "Add message": "Ajouter un message",
"add tags": "ajouter des tags", "add tags": "ajouter des tags",
"Adjusting these settings will apply changes universally to all users.": "L'ajustement de ces paramètres appliquera les changements à tous les utilisateurs.", "Adjusting these settings will apply changes universally to all users.": "L'ajustement de ces paramètres appliquera les changements à tous les utilisateurs.",
"admin": "Administrateur", "Admin": "",
"Admin Panel": "Panneau d'administration", "Admin Panel": "Panneau d'administration",
"Admin Settings": "Paramètres d'administration", "Admin Settings": "Paramètres d'administration",
"Advanced": "Avancé",
"Advanced Model Params": "Paramètres avancés du modèle", "Advanced Model Params": "Paramètres avancés du modèle",
"Advanced Parameters": "Paramètres avancés", "Advanced Parameters": "Paramètres avancés",
"all": "tous", "all": "tous",
@ -35,7 +34,6 @@
"API Key": "Clé API", "API Key": "Clé API",
"API RPM": "RPM API", "API RPM": "RPM API",
"are allowed - Activate this command by typing": "sont autorisés - Activez cette commande en tapant", "are allowed - Activate this command by typing": "sont autorisés - Activez cette commande en tapant",
"assistant": "Assistant",
"Audio": "Audio", "Audio": "Audio",
"Auto-playback response": "Réponse en lecture automatique", "Auto-playback response": "Réponse en lecture automatique",
"Auto-send input after 3 sec.": "Envoyer automatiquement l'entrée après 3 sec.", "Auto-send input after 3 sec.": "Envoyer automatiquement l'entrée après 3 sec.",
@ -72,6 +70,7 @@
"Copy last code block": "Copier le dernier bloc de code", "Copy last code block": "Copier le dernier bloc de code",
"Copy last response": "Copier la dernière réponse", "Copy last response": "Copier la dernière réponse",
"Copying to clipboard was successful!": "La copie dans le presse-papiers a réussi !", "Copying to clipboard was successful!": "La copie dans le presse-papiers a réussi !",
"Create a concise, 3-5 word phrase as a header for the following query, strictly adhering to the 3-5 word limit and avoiding the use of the word 'title':": "Créez une phrase concise de 3-5 mots comme en-tête pour la requête suivante, en respectant strictement la limite de 3-5 mots et en évitant l'utilisation du mot 'titre' :",
"Create a modelfile": "Créer un fichier de modèle", "Create a modelfile": "Créer un fichier de modèle",
"Create Account": "Créer un compte", "Create Account": "Créer un compte",
"Created at": "Créé le", "Created at": "Créé le",
@ -87,7 +86,6 @@
"Default (Web API)": "Par défaut (API Web)", "Default (Web API)": "Par défaut (API Web)",
"Default model updated": "Modèle par défaut mis à jour", "Default model updated": "Modèle par défaut mis à jour",
"Default Prompt Suggestions": "Suggestions de prompt par défaut", "Default Prompt Suggestions": "Suggestions de prompt par défaut",
"Create a concise, 3-5 word phrase as a header for the following query, strictly adhering to the 3-5 word limit and avoiding the use of the word 'title':": "Créez une phrase concise de 3-5 mots comme en-tête pour la requête suivante, en respectant strictement la limite de 3-5 mots et en évitant l'utilisation du mot 'titre' :",
"Default User Role": "Rôle d'utilisateur par défaut", "Default User Role": "Rôle d'utilisateur par défaut",
"delete": "supprimer", "delete": "supprimer",
"Delete a model": "Supprimer un modèle", "Delete a model": "Supprimer un modèle",
@ -119,21 +117,17 @@
"Enable Chat History": "Activer l'historique du chat", "Enable Chat History": "Activer l'historique du chat",
"Enable New Sign Ups": "Activer les nouvelles inscriptions", "Enable New Sign Ups": "Activer les nouvelles inscriptions",
"Enabled": "Activé", "Enabled": "Activé",
"Enter a user message here": "Entrez un message d'utilisateur ici",
"Enter an assistant message here": "Entrez un message d'assistant ici",
"Enter OpenAI API Key": "Entrez la clé API OpenAI", "Enter OpenAI API Key": "Entrez la clé API OpenAI",
"Enter stop sequence": "Entrez la séquence d'arrêt", "Enter stop sequence": "Entrez la séquence d'arrêt",
"Enter Your Email": "Entrez votre email", "Enter Your Email": "Entrez votre email",
"Enter Your Full Name": "Entrez votre nom complet", "Enter Your Full Name": "Entrez votre nom complet",
"Enter Your Password": "Entrez votre mot de passe", "Enter Your Password": "Entrez votre mot de passe",
"Experimental": "Expérimental",
"Export All Chats (All Users)": "Exporter tous les chats (tous les utilisateurs)", "Export All Chats (All Users)": "Exporter tous les chats (tous les utilisateurs)",
"Export Chats": "Exporter les chats", "Export Chats": "Exporter les chats",
"Export Documents Mapping": "Exporter la correspondance des documents", "Export Documents Mapping": "Exporter la correspondance des documents",
"Export Modelfiles": "Exporter les fichiers de modèle", "Export Modelfiles": "Exporter les fichiers de modèle",
"Export Prompts": "Exporter les prompts", "Export Prompts": "Exporter les prompts",
"Failed to read clipboard contents": "Échec de la lecture du contenu du presse-papiers", "Failed to read clipboard contents": "Échec de la lecture du contenu du presse-papiers",
"File Mode": "Mode fichier",
"File not found.": "Fichier non trouvé.", "File not found.": "Fichier non trouvé.",
"Focus chat input": "Concentrer sur l'entrée du chat", "Focus chat input": "Concentrer sur l'entrée du chat",
"Format your variables using square brackets like this:": "Formatez vos variables en utilisant des crochets comme ceci :", "Format your variables using square brackets like this:": "Formatez vos variables en utilisant des crochets comme ceci :",
@ -185,7 +179,6 @@
"Model Tag Name": "Nom de tag du modèle", "Model Tag Name": "Nom de tag du modèle",
"Modelfile": "Fichier de modèle", "Modelfile": "Fichier de modèle",
"Modelfile Advanced Settings": "Paramètres avancés du fichier de modèle", "Modelfile Advanced Settings": "Paramètres avancés du fichier de modèle",
"Modelfile Content": "Contenu du fichier de modèle",
"Modelfiles": "Fichiers de modèle", "Modelfiles": "Fichiers de modèle",
"Models": "Modèles", "Models": "Modèles",
"My Documents": "Mes documents", "My Documents": "Mes documents",
@ -193,19 +186,20 @@
"My Prompts": "Mes prompts", "My Prompts": "Mes prompts",
"Name": "Nom", "Name": "Nom",
"Name Tag": "Tag de nom", "Name Tag": "Tag de nom",
"Name your Modelfile": "Nommez votre fichier de modèle", "Name your modelfile": "",
"New Chat": "Nouveau chat", "New Chat": "Nouveau chat",
"New Password": "Nouveau mot de passe", "New Password": "Nouveau mot de passe",
"Not sure what to add?": "Vous ne savez pas quoi ajouter ?", "Not sure what to add?": "Vous ne savez pas quoi ajouter ?",
"Not sure what to write? Switch to": "Vous ne savez pas quoi écrire ? Basculer vers", "Not sure what to write? Switch to": "Vous ne savez pas quoi écrire ? Basculer vers",
"Off": "Désactivé", "Off": "Désactivé",
"Okay, Let's Go!": "D'accord, allons-y !", "Okay, Let's Go!": "D'accord, allons-y !",
"Ollama API URL": "URL de l'API Ollama",
"Ollama Version": "Version Ollama", "Ollama Version": "Version Ollama",
"On": "Activé", "On": "Activé",
"Only": "Seulement", "Only": "Seulement",
"Only alphanumeric characters and hyphens are allowed in the command string.": "Seuls les caractères alphanumériques et les tirets sont autorisés dans la chaîne de commande.", "Only alphanumeric characters and hyphens are allowed in the command string.": "Seuls les caractères alphanumériques et les tirets sont autorisés dans la chaîne de commande.",
"Oops! Hold tight! Your files are still in the processing oven. We're cooking them up to perfection. Please be patient and we'll let you know once they're ready.": "Oups ! Tenez bon ! Vos fichiers sont encore dans le four. Nous les cuisinons à la perfection. Soyez patient et nous vous informerons dès qu'ils seront prêts.", "Oops! Hold tight! Your files are still in the processing oven. We're cooking them up to perfection. Please be patient and we'll let you know once they're ready.": "Oups ! Tenez bon ! Vos fichiers sont encore dans le four. Nous les cuisinons à la perfection. Soyez patient et nous vous informerons dès qu'ils seront prêts.",
"Oops! Looks like the URL is invalid. Please double-check and try again.": "",
"Oops! You're using an unsupported method (frontend only). Please serve the WebUI from the backend.": "",
"Open": "Ouvrir", "Open": "Ouvrir",
"Open AI": "Open AI", "Open AI": "Open AI",
"Open new chat": "Ouvrir un nouveau chat", "Open new chat": "Ouvrir un nouveau chat",
@ -213,7 +207,7 @@
"or": "ou", "or": "ou",
"Parameters": "Paramètres", "Parameters": "Paramètres",
"Password": "Mot de passe", "Password": "Mot de passe",
"pending": "en attente", "Pending": "",
"Permission denied when accessing microphone: {{error}}": "Permission refusée lors de l'accès au microphone : {{error}}", "Permission denied when accessing microphone: {{error}}": "Permission refusée lors de l'accès au microphone : {{error}}",
"Playground": "Aire de jeu", "Playground": "Aire de jeu",
"Profile": "Profil", "Profile": "Profil",
@ -275,6 +269,7 @@
"STT Settings": "Paramètres STT", "STT Settings": "Paramètres STT",
"Submit": "Soumettre", "Submit": "Soumettre",
"Success": "Succès", "Success": "Succès",
"Successfully updated": "",
"Successfully updated.": "Mis à jour avec succès.", "Successfully updated.": "Mis à jour avec succès.",
"Sync All": "Synchroniser tout", "Sync All": "Synchroniser tout",
"System": "Système", "System": "Système",
@ -288,12 +283,12 @@
"Theme": "Thème", "Theme": "Thème",
"This ensures that your valuable conversations are securely saved to your backend database. Thank you!": "Cela garantit que vos précieuses conversations sont en sécurité dans votre base de données. Merci !", "This ensures that your valuable conversations are securely saved to your backend database. Thank you!": "Cela garantit que vos précieuses conversations sont en sécurité dans votre base de données. Merci !",
"This setting does not sync across browsers or devices.": "Ce paramètre ne se synchronise pas entre les navigateurs ou les appareils.", "This setting does not sync across browsers or devices.": "Ce paramètre ne se synchronise pas entre les navigateurs ou les appareils.",
"Tip: Update multiple variable slots consecutively by pressing the tab key in the chat input after each replacement.": "",
"Title": "Titre", "Title": "Titre",
"Title Auto-Generation": "Génération automatique de titre", "Title Auto-Generation": "Génération automatique de titre",
"Title Generation Prompt": "Prompt de génération de titre", "Title Generation Prompt": "Prompt de génération de titre",
"to": "à", "to": "à",
"To access the available model names for downloading,": "Pour accéder aux noms de modèles disponibles pour le téléchargement,", "To access the available model names for downloading,": "Pour accéder aux noms de modèles disponibles pour le téléchargement,",
"to chat input.": "à l'entrée du chat.",
"Toggle settings": "Basculer les paramètres", "Toggle settings": "Basculer les paramètres",
"Toggle sidebar": "Basculer la barre latérale", "Toggle sidebar": "Basculer la barre latérale",
"Top K": "Top K", "Top K": "Top K",
@ -301,12 +296,10 @@
"Trouble accessing Ollama?": "Problèmes d'accès à Ollama ?", "Trouble accessing Ollama?": "Problèmes d'accès à Ollama ?",
"TTS Settings": "Paramètres TTS", "TTS Settings": "Paramètres TTS",
"Uh-oh! There was an issue connecting to {{provider}}.": "Uh-oh ! Il y a eu un problème de connexion à {{provider}}.", "Uh-oh! There was an issue connecting to {{provider}}.": "Uh-oh ! Il y a eu un problème de connexion à {{provider}}.",
"Upload a GGUF model": "Téléverser un modèle GGUF", "Unknown File Type '{{file_type}}', but accepting and treating as plain text": "",
"Upload files": "Téléverser des fichiers", "Upload files": "Téléverser des fichiers",
"Upload Progress": "Progression du téléversement",
"URL Mode": "Mode URL",
"Use '#' in the prompt input to load and select your documents.": "Utilisez '#' dans l'entrée du prompt pour charger et sélectionner vos documents.", "Use '#' in the prompt input to load and select your documents.": "Utilisez '#' dans l'entrée du prompt pour charger et sélectionner vos documents.",
"user": "Utilisateur", "User": "",
"User Permissions": "Permissions d'utilisateur", "User Permissions": "Permissions d'utilisateur",
"Users": "Utilisateurs", "Users": "Utilisateurs",
"Utilize": "Utiliser", "Utilize": "Utiliser",
@ -320,7 +313,8 @@
"When history is turned off, new chats on this browser won't appear in your history on any of your devices.": "Lorsque l'historique est désactivé, les nouveaux chats sur ce navigateur n'apparaîtront pas dans votre historique sur aucun de vos appareils.", "When history is turned off, new chats on this browser won't appear in your history on any of your devices.": "Lorsque l'historique est désactivé, les nouveaux chats sur ce navigateur n'apparaîtront pas dans votre historique sur aucun de vos appareils.",
"Whisper (Local)": "Chuchotement (Local)", "Whisper (Local)": "Chuchotement (Local)",
"Write a prompt suggestion (e.g. Who are you?)": "Écrivez un prompt (e.x. Qui est-tu ?)", "Write a prompt suggestion (e.g. Who are you?)": "Écrivez un prompt (e.x. Qui est-tu ?)",
"Write a summary in 50 words that summarizes [topic or keyword]": "Ecrivez un résumé en 50 mots [sujet ou mot-clé]", "Write a summary in 50 words that summarizes [topic or keyword].": "Ecrivez un résumé en 50 mots [sujet ou mot-clé]",
"You": "",
"You're a helpful assistant.": "Vous êtes un assistant utile", "You're a helpful assistant.": "Vous êtes un assistant utile",
"You're now logged in.": "Vous êtes maintenant connecté." "You're now logged in.": "Vous êtes maintenant connecté."
} }

View file

@ -1,5 +1,6 @@
{ {
"'s', 'm', 'h', 'd', 'w' or '-1' for no expiration.": "'s', 'm', 'h', 'd', 'w' or '-1' для відсутності терміну дії.", "'s', 'm', 'h', 'd', 'w' or '-1' for no expiration.": "'s', 'm', 'h', 'd', 'w' or '-1' для відсутності терміну дії.",
"(Beta)": "(Beta)",
"(e.g. `sh webui.sh --api`)": "(e.g. `sh webui.sh --api`)", "(e.g. `sh webui.sh --api`)": "(e.g. `sh webui.sh --api`)",
"{{item}} not provided": "{{item}} не надано", "{{item}} not provided": "{{item}} не надано",
"{{modelName}} is thinking...": "{{modelName}} думає...", "{{modelName}} is thinking...": "{{modelName}} думає...",
@ -14,12 +15,12 @@
"Add Docs": "Додати документи", "Add Docs": "Додати документи",
"Add Files": "Додати файли", "Add Files": "Додати файли",
"Add LiteLLM Model": "Додати модель LiteLLM", "Add LiteLLM Model": "Додати модель LiteLLM",
"Add message": "Додати повідомлення",
"add tags": "додати теги", "add tags": "додати теги",
"Adjusting these settings will apply changes universally to all users.": "Корегування цих налаштувань застосовуватиме зміни для всіх користувачів.", "Adjusting these settings will apply changes universally to all users.": "Корегування цих налаштувань застосовуватиме зміни для всіх користувачів.",
"admin": "адміністратор", "Admin": "Адмін",
"Admin Panel": "Панель адміністратора", "Admin Panel": "Панель адміністратора",
"Admin Settings": "Налаштування адміністратора", "Admin Settings": "Налаштування адміністратора",
"Advanced": "Розширені",
"Advanced Model Params": "Розширені параметри моделі", "Advanced Model Params": "Розширені параметри моделі",
"Advanced Parameters": "Розширені параметри", "Advanced Parameters": "Розширені параметри",
"all": "всі", "all": "всі",
@ -43,6 +44,7 @@
"Cancel": "Скасувати", "Cancel": "Скасувати",
"Categories": "Категорії", "Categories": "Категорії",
"Change Password": "Змінити пароль", "Change Password": "Змінити пароль",
"Chat": "Чат",
"Chat History": "Історія чату", "Chat History": "Історія чату",
"Chat History is off for this browser.": "Історія чату вимкнена для цього браузера.", "Chat History is off for this browser.": "Історія чату вимкнена для цього браузера.",
"Chats": "Чати", "Chats": "Чати",
@ -68,6 +70,7 @@
"Copy last code block": "Копіювати останній блок коду", "Copy last code block": "Копіювати останній блок коду",
"Copy last response": "Копіювати останню відповідь", "Copy last response": "Копіювати останню відповідь",
"Copying to clipboard was successful!": "Копіювання в буфер обміну виконано успішно!", "Copying to clipboard was successful!": "Копіювання в буфер обміну виконано успішно!",
"Create a concise, 3-5 word phrase as a header for the following query, strictly adhering to the 3-5 word limit and avoiding the use of the word 'title':": "Створіть стислий, 3-5 слів заголовок для наступного запиту, суворо дотримуючись 3-5 слів обмеження та уникаючи використання слова 'заголовок':",
"Create a modelfile": "Створити modelfile", "Create a modelfile": "Створити modelfile",
"Create Account": "Створити обліковий запис", "Create Account": "Створити обліковий запис",
"Created at": "Створено", "Created at": "Створено",
@ -83,7 +86,6 @@
"Default (Web API)": "За замовчуванням (Web API)", "Default (Web API)": "За замовчуванням (Web API)",
"Default model updated": "Модель за замовчуванням оновлено", "Default model updated": "Модель за замовчуванням оновлено",
"Default Prompt Suggestions": "Запропоновані запити за замовчуванням", "Default Prompt Suggestions": "Запропоновані запити за замовчуванням",
"Create a concise, 3-5 word phrase as a header for the following query, strictly adhering to the 3-5 word limit and avoiding the use of the word 'title':": "Створіть стислий, 3-5 слів заголовок для наступного запиту, суворо дотримуючись 3-5 слів обмеження та уникаючи використання слова 'заголовок':",
"Default User Role": "Роль користувача за замовчуванням", "Default User Role": "Роль користувача за замовчуванням",
"delete": "видалити", "delete": "видалити",
"Delete a model": "Видалити модель", "Delete a model": "Видалити модель",
@ -107,6 +109,7 @@
"Don't have an account?": "Немає облікового запису?", "Don't have an account?": "Немає облікового запису?",
"Download as a File": "Завантажити як файл", "Download as a File": "Завантажити як файл",
"Download Database": "Завантажити базу даних", "Download Database": "Завантажити базу даних",
"Drop any files here to add to the conversation": "Перетягніть сюди файли, щоб додати до розмови",
"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "напр. '30s','10m'. Дійсні одиниці часу: 'с', 'хв', 'г'.", "e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "напр. '30s','10m'. Дійсні одиниці часу: 'с', 'хв', 'г'.",
"Edit Doc": "Редагувати документ", "Edit Doc": "Редагувати документ",
"Edit User": "Редагувати користувача", "Edit User": "Редагувати користувача",
@ -119,15 +122,13 @@
"Enter Your Email": "Введіть вашу електронну пошту", "Enter Your Email": "Введіть вашу електронну пошту",
"Enter Your Full Name": "Введіть ваше повне ім'я", "Enter Your Full Name": "Введіть ваше повне ім'я",
"Enter Your Password": "Введіть ваш пароль", "Enter Your Password": "Введіть ваш пароль",
"Experimental": "Експериментальний",
"Export All Chats (All Users)": "Експортувати всі чати (всі користувачі)", "Export All Chats (All Users)": "Експортувати всі чати (всі користувачі)",
"Export Chats": "Експортувати чати", "Export Chats": "Експортувати чати",
"Export Documents Mapping": "Експортувати відображення документів", "Export Documents Mapping": "Експортувати відображення документів",
"Export Modelfiles": "Експортувати modelfiles", "Export Modelfiles": "Експортувати modelfiles",
"Export Prompts": "Експортувати запити", "Export Prompts": "Експортувати запити",
"Failed to read clipboard contents": "Не вдалося прочитати вміст буфера обміну", "Failed to read clipboard contents": "Не вдалося прочитати вміст буфера обміну",
"File Mode": "Режим файлу", "File not found.": "Файл не знайдено.",
"File not found.`": "Файл не знайдено.`",
"Focus chat input": "Фокус вводу чату", "Focus chat input": "Фокус вводу чату",
"Format your variables using square brackets like this:": "Форматуйте свої змінні квадратними дужками так:", "Format your variables using square brackets like this:": "Форматуйте свої змінні квадратними дужками так:",
"From (Base Model)": "Від (базова модель)", "From (Base Model)": "Від (базова модель)",
@ -169,36 +170,36 @@
"Mirostat Eta": "Mirostat Eta", "Mirostat Eta": "Mirostat Eta",
"Mirostat Tau": "Mirostat Tau", "Mirostat Tau": "Mirostat Tau",
"MMMM DD, YYYY": "MMMM ДД, РРРР", "MMMM DD, YYYY": "MMMM ДД, РРРР",
"Model '{{modelName}}' has been successfully downloaded.`": "Модель '{{modelName}}' успішно завантажено.`", "Model '{{modelName}}' has been successfully downloaded.": "Модель '{{modelName}}' успішно завантажено.",
"Model '{{modelTag}}' is already in queue for downloading.": "Модель '{{modelTag}}' вже знаходиться в черзі на завантаження.", "Model '{{modelTag}}' is already in queue for downloading.": "Модель '{{modelTag}}' вже знаходиться в черзі на завантаження.",
"Model {{modelId}} not found": "Модель {{modelId}} не знайдено", "Model {{modelId}} not found": "Модель {{modelId}} не знайдено",
"Model {{modelName}} already exists.": "Модель {{modelName}} вже існує.", "Model {{modelName}} already exists.": "Модель {{modelName}} вже існує.",
"Model Name": "Назва моделі", "Model Name": "Назва моделі",
"Model not selected": "Модель не вибрана", "Model not selected": "Модель не вибрана",
"Model Tag Name": "Ім'я тегу моделі", "Model Tag Name": "Ім'я тегу моделі",
"Modelfile": "Modelfile", "Modelfile": "Файли моделі",
"Modelfile Advanced Settings": "Розширені налаштування modelfile", "Modelfile Advanced Settings": "Розширені налаштування modelfile",
"Modelfile Content": "Зміст modelfile", "Modelfiles": "Файли моделей",
"Modelfiles": "Modelfiles",
"Models": "Моделі", "Models": "Моделі",
"My Documents": "Мої документи", "My Documents": "Мої документи",
"My Modelfiles": "Мої modelfiles", "My Modelfiles": "Мої файли моделей",
"My Prompts": "Мої запити", "My Prompts": "Мої запити",
"Name": "Назва", "Name": "Назва",
"Name Tag": "Назва тегу", "Name Tag": "Назва тегу",
"Name your Modelfile": "Назвіть свій modelfile", "Name your modelfile": "Назвіть свій файл моделі",
"New Chat": "Новий чат", "New Chat": "Новий чат",
"New Password": "Новий пароль", "New Password": "Новий пароль",
"Not sure what to add?": "Не впевнений, що додати?", "Not sure what to add?": "Не впевнений, що додати?",
"Not sure what to write? Switch to": "Не впевнений, що писати? Переключитися на", "Not sure what to write? Switch to": "Не впевнений, що писати? Переключитися на",
"Off": "Вимк", "Off": "Вимк",
"Okay, Let's Go!": "Гаразд, давайте почнемо!", "Okay, Let's Go!": "Гаразд, давайте почнемо!",
"Ollama API URL": "URL API Ollama",
"Ollama Version": "Версія Ollama", "Ollama Version": "Версія Ollama",
"On": "Увімк", "On": "Увімк",
"Only": "Тільки", "Only": "Тільки",
"Only alphanumeric characters and hyphens are allowed in the command string.": "У рядку команди дозволено використовувати лише алфавітно-цифрові символи та дефіси.", "Only alphanumeric characters and hyphens are allowed in the command string.": "У рядку команди дозволено використовувати лише алфавітно-цифрові символи та дефіси.",
"Oops! Hold tight! Your files are still in the processing oven. We're cooking them up to perfection. Please be patient and we'll let you know once they're ready.": "Ой! Зачекайте, будь ласка! Ваші файли ще готуються. Ми робимо все, щоб вони були ідеальними. Будь ласка, будьте терплячі, ми повідомимо вам, коли вони будуть готові.", "Oops! Hold tight! Your files are still in the processing oven. We're cooking them up to perfection. Please be patient and we'll let you know once they're ready.": "Ой! Зачекайте, будь ласка! Ваші файли ще готуються. Ми робимо все, щоб вони були ідеальними. Будь ласка, будьте терплячі, ми повідомимо вам, коли вони будуть готові.",
"Oops! Looks like the URL is invalid. Please double-check and try again.": "Упс! Схоже, що URL-адреса невірна. Будь ласка, перевірте ще раз та спробуйте ще раз.",
"Oops! You're using an unsupported method (frontend only). Please serve the WebUI from the backend.": "Упс! Ви використовуєте непідтримуваний метод (тільки для фронтенду). Будь ласка, обслуговуйте WebUI з бекенду.",
"Open": "Відкрити", "Open": "Відкрити",
"Open AI": "Open AI", "Open AI": "Open AI",
"Open new chat": "Відкрити новий чат", "Open new chat": "Відкрити новий чат",
@ -206,8 +207,9 @@
"or": "або", "or": "або",
"Parameters": "Параметри", "Parameters": "Параметри",
"Password": "Пароль", "Password": "Пароль",
"pending": "очікування", "Pending": "На розгляді",
"Permission denied when accessing microphone: {{error}}": "Доступ до мікрофона заборонено: {{error}}", "Permission denied when accessing microphone: {{error}}": "Доступ до мікрофона заборонено: {{error}}",
"Playground": "Майданчик",
"Profile": "Профіль", "Profile": "Профіль",
"Prompt Content": "Зміст запиту", "Prompt Content": "Зміст запиту",
"Prompt suggestions": "Швидкі запити", "Prompt suggestions": "Швидкі запити",
@ -265,38 +267,39 @@
"SpeechRecognition API is not supported in this browser.": "SpeechRecognition API не підтримується в цьому браузері.", "SpeechRecognition API is not supported in this browser.": "SpeechRecognition API не підтримується в цьому браузері.",
"Stop Sequence": "Символ зупинки", "Stop Sequence": "Символ зупинки",
"STT Settings": "Налаштування STT", "STT Settings": "Налаштування STT",
"Submit": "Надіслати",
"Success": "Успіх", "Success": "Успіх",
"Successfully updated": "Успішно оновлено", "Successfully updated": "Успішно оновлено",
"Successfully updated.": "Успішно оновлено.", "Successfully updated.": "Успішно оновлено.",
"Sync All": "Синхронізувати все", "Sync All": "Синхронізувати все",
"System": "Система",
"System Prompt": "Системний запит", "System Prompt": "Системний запит",
"Tags": "Теги", "Tags": "Теги",
"Temperature": "Температура", "Temperature": "Температура",
"Template": "Шаблон", "Template": "Шаблон",
"Text Completion": "Завершення тексту",
"Text-to-Speech Engine": "Система синтезу мови", "Text-to-Speech Engine": "Система синтезу мови",
"Tfs Z": "Tfs Z", "Tfs Z": "Tfs Z",
"Theme": "Тема", "Theme": "Тема",
"This ensures that your valuable conversations are securely saved to your backend database. Thank you!": "Це забезпечує збереження ваших цінних розмов у безпечному бекенд-сховищі. Дякуємо!", "This ensures that your valuable conversations are securely saved to your backend database. Thank you!": "Це забезпечує збереження ваших цінних розмов у безпечному бекенд-сховищі. Дякуємо!",
"This setting does not sync across browsers or devices.": "Це налаштування не синхронізується між браузерами або пристроями.", "This setting does not sync across browsers or devices.": "Це налаштування не синхронізується між браузерами або пристроями.",
"Tip: Update multiple variable slots consecutively by pressing the tab key in the chat input after each replacement.": "",
"Title": "Заголовок", "Title": "Заголовок",
"Title Auto-Generation": "Автогенерація заголовків", "Title Auto-Generation": "Автогенерація заголовків",
"Title Generation Prompt": "Запит на генерування заголовків", "Title Generation Prompt": "Запит на генерування заголовків",
"to": "до", "to": "до",
"To access the available model names for downloading,": "Щоб отримати доступ до назв доступних для завантаження моделей,", "To access the available model names for downloading,": "Щоб отримати доступ до назв доступних для завантаження моделей,",
"to chat input.": "до введення чату.",
"Toggle settings": "Переключити налаштування", "Toggle settings": "Переключити налаштування",
"Toggle sidebar": "Переключити бокову панель", "Toggle sidebar": "Переключити бокову панель",
"Top K": "Вершина K", "Top K": "Top K",
"Top P": "Вершина P", "Top P": "Top P",
"Trouble accessing Ollama?": "Проблеми з доступом до Ollama?", "Trouble accessing Ollama?": "Проблеми з доступом до Ollama?",
"TTS Settings": "Налаштування TTS", "TTS Settings": "Налаштування TTS",
"Uh-oh! There was an issue connecting to {{provider}}.": "Ой! Виникла проблема при підключенні до {{provider}}.", "Uh-oh! There was an issue connecting to {{provider}}.": "Ой! Виникла проблема при підключенні до {{provider}}.",
"Upload a GGUF model": "Завантажити модель GGUF", "Unknown File Type '{{file_type}}', but accepting and treating as plain text": "Невідомий тип файлу '{{file_type}}', але приймається та обробляється як звичайний текст",
"Upload files": "Завантажити файли", "Upload files": "Завантажити файли",
"Upload Progress": "Прогрес завантаження",
"URL Mode": "Режим URL",
"Use '#' in the prompt input to load and select your documents.": "Використовуйте '#' у введенні запиту для завантаження та вибору ваших документів.", "Use '#' in the prompt input to load and select your documents.": "Використовуйте '#' у введенні запиту для завантаження та вибору ваших документів.",
"user": ористувач", "User": "Користувач",
"User Permissions": "Дозволи користувача", "User Permissions": "Дозволи користувача",
"Users": "Користувачі", "Users": "Користувачі",
"Utilize": "Використовувати", "Utilize": "Використовувати",
@ -308,19 +311,10 @@
"WebUI Settings": "Налаштування WebUI", "WebUI Settings": "Налаштування WebUI",
"Whats New in": "Що нового в", "Whats New in": "Що нового в",
"When history is turned off, new chats on this browser won't appear in your history on any of your devices.": "Коли історія вимкнена, нові чати в цьому браузері не будуть відображатися в історії на жодному з ваших пристроїв.", "When history is turned off, new chats on this browser won't appear in your history on any of your devices.": "Коли історія вимкнена, нові чати в цьому браузері не будуть відображатися в історії на жодному з ваших пристроїв.",
"Whisper (Local)": "Шепіт (локально)", "Whisper (Local)": "Whisper (локально)",
"Write a prompt suggestion (e.g. Who are you?)": "Напишіть запит (напр. Хто ти?)", "Write a prompt suggestion (e.g. Who are you?)": "Напишіть запит (напр. Хто ти?)",
"Write a summary in 50 words that summarizes [topic or keyword]": "Напишіть стислий зміст у 50 слів, який узагальнює [тему або ключове слово]", "Write a summary in 50 words that summarizes [topic or keyword].": "Напишіть стислий зміст у 50 слів, який узагальнює [тема або ключове слово].",
"You're now logged in.": "Ви увійшли в систему.",
"You": "Ви", "You": "Ви",
"Playground": "Майданчик",
"Add message": "Додати повідомлення",
"Enter a user message here": "Введіть тут повідомлення користувача",
"Enter an assistant message here": "Введіть тут повідомлення асистента",
"Drop any files here to add to the conversation": "Перетягніть сюди файли, щоб додати до розмови",
"You're a helpful assistant.": "Ви корисний асистент.", "You're a helpful assistant.": "Ви корисний асистент.",
"assistant": "асистент", "You're now logged in.": "Ви увійшли в систему."
"Submit": "Надіслати",
"Chat": "Чат",
"Text Completion": "Завершення тексту"
} }

View file

@ -99,14 +99,11 @@
if (localDBChats.length === 0) { if (localDBChats.length === 0) {
await deleteDB('Chats'); await deleteDB('Chats');
} }
console.log('localdb', localDBChats);
} }
console.log(DB); console.log(DB);
} catch (error) { } catch (error) {
// IndexedDB Not Found // IndexedDB Not Found
console.log('IDB Not Found');
} }
console.log(); console.log();

View file

@ -344,7 +344,7 @@
content: $settings.system content: $settings.system
} }
: undefined, : undefined,
...messages.filter((message) => !message.deleted) ...messages
] ]
.filter((message) => message) .filter((message) => message)
.map((message, idx, arr) => ({ .map((message, idx, arr) => ({
@ -558,7 +558,7 @@
content: $settings.system content: $settings.system
} }
: undefined, : undefined,
...messages.filter((message) => !message.deleted) ...messages
] ]
.filter((message) => message) .filter((message) => message)
.map((message, idx, arr) => ({ .map((message, idx, arr) => ({

View file

@ -354,7 +354,7 @@
content: $settings.system content: $settings.system
} }
: undefined, : undefined,
...messages.filter((message) => !message.deleted) ...messages
] ]
.filter((message) => message) .filter((message) => message)
.map((message, idx, arr) => ({ .map((message, idx, arr) => ({
@ -568,7 +568,7 @@
content: $settings.system content: $settings.system
} }
: undefined, : undefined,
...messages.filter((message) => !message.deleted) ...messages
] ]
.filter((message) => message) .filter((message) => message)
.map((message, idx, arr) => ({ .map((message, idx, arr) => ({

View file

@ -562,9 +562,9 @@ SYSTEM """${system}"""`.replace(/^\s*\n/gm, '');
}} }}
> >
{#if advanced} {#if advanced}
<span class="ml-2 self-center">{$i18n.t(' Custom ')}</span> <span class="ml-2 self-center">{$i18n.t('Custom')}</span>
{:else} {:else}
<span class="ml-2 self-center">{$i18n.t(' Default ')}</span> <span class="ml-2 self-center">{$i18n.t('Default')}</span>
{/if} {/if}
</button> </button>
</div> </div>

View file

@ -181,7 +181,7 @@
<textarea <textarea
class="px-3 py-1.5 text-sm w-full bg-transparent border dark:border-gray-600 outline-none rounded-lg" class="px-3 py-1.5 text-sm w-full bg-transparent border dark:border-gray-600 outline-none rounded-lg"
placeholder={$i18n.t( placeholder={$i18n.t(
'Write a summary in 50 words that summarizes [topic or keyword]' 'Write a summary in 50 words that summarizes [topic or keyword].'
)} )}
rows="6" rows="6"
bind:value={content} bind:value={content}