forked from open-webui/open-webui
		
	main #2
					 41 changed files with 1104 additions and 814 deletions
				
			
		|  | @ -7,7 +7,6 @@ node_modules | |||
| /package | ||||
| .env | ||||
| .env.* | ||||
| !.env.example | ||||
| vite.config.js.timestamp-* | ||||
| vite.config.ts.timestamp-* | ||||
| __pycache__ | ||||
|  |  | |||
|  | @ -1,6 +1,6 @@ | |||
| # Ollama URL for the backend to connect | ||||
| # The path '/ollama/api' will be redirected to the specified backend URL | ||||
| OLLAMA_API_BASE_URL='http://localhost:11434/api' | ||||
| # The path '/ollama' will be redirected to the specified backend URL | ||||
| OLLAMA_BASE_URL='http://localhost:11434' | ||||
| 
 | ||||
| OPENAI_API_BASE_URL='' | ||||
| OPENAI_API_KEY='' | ||||
|  |  | |||
							
								
								
									
										12
									
								
								.github/workflows/build-release.yml
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										12
									
								
								.github/workflows/build-release.yml
									
										
									
									
										vendored
									
									
								
							|  | @ -26,17 +26,27 @@ jobs: | |||
|         VERSION=$(jq -r '.version' package.json) | ||||
|         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 | ||||
|       uses: actions/github-script@v5 | ||||
|       with: | ||||
|         github-token: ${{ secrets.GITHUB_TOKEN }} | ||||
|         script: | | ||||
|           const changelog = `${{ steps.changelog.outputs.content }}`; | ||||
|           const release = await github.rest.repos.createRelease({ | ||||
|             owner: context.repo.owner, | ||||
|             repo: context.repo.repo, | ||||
|             tag_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}`) | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										32
									
								
								CHANGELOG.md
									
										
									
									
									
								
							
							
						
						
									
										32
									
								
								CHANGELOG.md
									
										
									
									
									
								
							|  | @ -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/), | ||||
| 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 | ||||
| 
 | ||||
| ### Added | ||||
|  |  | |||
|  | @ -20,7 +20,7 @@ FROM python:3.11-slim-bookworm as base | |||
| ENV ENV=prod | ||||
| ENV PORT "" | ||||
| 
 | ||||
| ENV OLLAMA_API_BASE_URL "/ollama/api" | ||||
| ENV OLLAMA_BASE_URL "/ollama" | ||||
| 
 | ||||
| ENV OPENAI_API_BASE_URL "" | ||||
| ENV OPENAI_API_KEY "" | ||||
|  | @ -53,6 +53,8 @@ WORKDIR /app/backend | |||
| # install python dependencies | ||||
| 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 -r requirements.txt --no-cache-dir | ||||
| 
 | ||||
|  |  | |||
|  | @ -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: | ||||
| 
 | ||||
| - 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 | ||||
|   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! 😄 | ||||
|  | @ -110,7 +110,7 @@ If you're experiencing connection issues, it’s often due to the WebUI docker c | |||
| **Example Docker Command**: | ||||
| 
 | ||||
| ```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 | ||||
|  |  | |||
|  | @ -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. | ||||
| 
 | ||||
| - **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. | ||||
| 
 | ||||
|  | @ -15,7 +15,7 @@ If you're experiencing connection issues, it’s often due to the WebUI docker c | |||
| **Example Docker Command**: | ||||
| 
 | ||||
| ```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 | ||||
|  | @ -25,8 +25,8 @@ docker run -d --network=host -v open-webui:/app/backend/data -e OLLAMA_API_BASE_ | |||
| **Troubleshooting Steps**: | ||||
| 
 | ||||
| 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". | ||||
|    - 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. | ||||
|  |  | |||
|  | @ -15,7 +15,7 @@ import asyncio | |||
| from apps.web.models.users import Users | ||||
| from constants import ERROR_MESSAGES | ||||
| 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 | ||||
| 
 | ||||
|  | @ -29,8 +29,7 @@ app.add_middleware( | |||
|     allow_headers=["*"], | ||||
| ) | ||||
| 
 | ||||
| app.state.OLLAMA_BASE_URL = OLLAMA_BASE_URL | ||||
| app.state.OLLAMA_BASE_URLS = [OLLAMA_BASE_URL] | ||||
| app.state.OLLAMA_BASE_URLS = OLLAMA_BASE_URLS | ||||
| app.state.MODELS = {} | ||||
| 
 | ||||
| 
 | ||||
|  | @ -223,7 +222,7 @@ async def pull_model( | |||
|             r = requests.request( | ||||
|                 method="POST", | ||||
|                 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, | ||||
|             ) | ||||
| 
 | ||||
|  | @ -295,7 +294,7 @@ async def push_model( | |||
|             r = requests.request( | ||||
|                 method="POST", | ||||
|                 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() | ||||
|  | @ -357,7 +356,7 @@ async def create_model( | |||
|             r = requests.request( | ||||
|                 method="POST", | ||||
|                 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, | ||||
|             ) | ||||
| 
 | ||||
|  | @ -420,7 +419,7 @@ async def copy_model( | |||
|         r = requests.request( | ||||
|             method="POST", | ||||
|             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() | ||||
| 
 | ||||
|  | @ -467,7 +466,7 @@ async def delete_model( | |||
|         r = requests.request( | ||||
|             method="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() | ||||
| 
 | ||||
|  | @ -507,7 +506,7 @@ async def show_model_info(form_data: ModelNameForm, user=Depends(get_current_use | |||
|         r = requests.request( | ||||
|             method="POST", | ||||
|             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() | ||||
| 
 | ||||
|  | @ -559,7 +558,7 @@ async def generate_embeddings( | |||
|         r = requests.request( | ||||
|             method="POST", | ||||
|             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() | ||||
| 
 | ||||
|  | @ -645,7 +644,7 @@ async def generate_completion( | |||
|             r = requests.request( | ||||
|                 method="POST", | ||||
|                 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, | ||||
|             ) | ||||
| 
 | ||||
|  | @ -715,7 +714,7 @@ async def generate_chat_completion( | |||
| 
 | ||||
|     r = None | ||||
| 
 | ||||
|     print(form_data.model_dump_json(exclude_none=True)) | ||||
|     print(form_data.model_dump_json(exclude_none=True).encode()) | ||||
| 
 | ||||
|     def get_request(): | ||||
|         nonlocal form_data | ||||
|  | @ -745,7 +744,7 @@ async def generate_chat_completion( | |||
|             r = requests.request( | ||||
|                 method="POST", | ||||
|                 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, | ||||
|             ) | ||||
| 
 | ||||
|  | @ -757,6 +756,7 @@ async def generate_chat_completion( | |||
|                 headers=dict(r.headers), | ||||
|             ) | ||||
|         except Exception as e: | ||||
|             print(e) | ||||
|             raise e | ||||
| 
 | ||||
|     try: | ||||
|  | @ -844,7 +844,7 @@ async def generate_openai_chat_completion( | |||
|             r = requests.request( | ||||
|                 method="POST", | ||||
|                 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, | ||||
|             ) | ||||
| 
 | ||||
|  |  | |||
|  | @ -3,7 +3,10 @@ from fastapi.middleware.cors import CORSMiddleware | |||
| from fastapi.responses import StreamingResponse, JSONResponse, FileResponse | ||||
| 
 | ||||
| import requests | ||||
| import aiohttp | ||||
| import asyncio | ||||
| import json | ||||
| 
 | ||||
| from pydantic import BaseModel | ||||
| 
 | ||||
| 
 | ||||
|  | @ -15,7 +18,9 @@ from utils.utils import ( | |||
|     get_verified_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 | ||||
| from pathlib import Path | ||||
|  | @ -29,116 +34,207 @@ app.add_middleware( | |||
|     allow_headers=["*"], | ||||
| ) | ||||
| 
 | ||||
| app.state.OPENAI_API_BASE_URL = OPENAI_API_BASE_URL | ||||
| app.state.OPENAI_API_KEY = OPENAI_API_KEY | ||||
| app.state.OPENAI_API_BASE_URLS = OPENAI_API_BASE_URLS | ||||
| app.state.OPENAI_API_KEYS = OPENAI_API_KEYS | ||||
| 
 | ||||
| app.state.MODELS = {} | ||||
| 
 | ||||
| 
 | ||||
| class UrlUpdateForm(BaseModel): | ||||
|     url: str | ||||
| @app.middleware("http") | ||||
| 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): | ||||
|     key: str | ||||
| class UrlsUpdateForm(BaseModel): | ||||
|     urls: List[str] | ||||
| 
 | ||||
| 
 | ||||
| @app.get("/url") | ||||
| async def get_openai_url(user=Depends(get_admin_user)): | ||||
|     return {"OPENAI_API_BASE_URL": app.state.OPENAI_API_BASE_URL} | ||||
| class KeysUpdateForm(BaseModel): | ||||
|     keys: List[str] | ||||
| 
 | ||||
| 
 | ||||
| @app.post("/url/update") | ||||
| async def update_openai_url(form_data: UrlUpdateForm, user=Depends(get_admin_user)): | ||||
|     app.state.OPENAI_API_BASE_URL = form_data.url | ||||
|     return {"OPENAI_API_BASE_URL": app.state.OPENAI_API_BASE_URL} | ||||
| @app.get("/urls") | ||||
| async def get_openai_urls(user=Depends(get_admin_user)): | ||||
|     return {"OPENAI_API_BASE_URLS": app.state.OPENAI_API_BASE_URLS} | ||||
| 
 | ||||
| 
 | ||||
| @app.get("/key") | ||||
| async def get_openai_key(user=Depends(get_admin_user)): | ||||
|     return {"OPENAI_API_KEY": app.state.OPENAI_API_KEY} | ||||
| @app.post("/urls/update") | ||||
| async def update_openai_urls(form_data: UrlsUpdateForm, user=Depends(get_admin_user)): | ||||
|     app.state.OPENAI_API_BASE_URLS = form_data.urls | ||||
|     return {"OPENAI_API_BASE_URLS": app.state.OPENAI_API_BASE_URLS} | ||||
| 
 | ||||
| 
 | ||||
| @app.post("/key/update") | ||||
| async def update_openai_key(form_data: KeyUpdateForm, user=Depends(get_admin_user)): | ||||
|     app.state.OPENAI_API_KEY = form_data.key | ||||
|     return {"OPENAI_API_KEY": app.state.OPENAI_API_KEY} | ||||
| @app.get("/keys") | ||||
| async def get_openai_keys(user=Depends(get_admin_user)): | ||||
|     return {"OPENAI_API_KEYS": app.state.OPENAI_API_KEYS} | ||||
| 
 | ||||
| 
 | ||||
| @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") | ||||
| async def speech(request: Request, user=Depends(get_verified_user)): | ||||
|     target_url = f"{app.state.OPENAI_API_BASE_URL}/audio/speech" | ||||
| 
 | ||||
|     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" | ||||
| 
 | ||||
|     idx = None | ||||
|     try: | ||||
|         print("openai") | ||||
|         r = requests.post( | ||||
|             url=target_url, | ||||
|             data=body, | ||||
|             headers=headers, | ||||
|             stream=True, | ||||
|         idx = app.state.OPENAI_API_BASE_URLS.index("https://api.openai.com/v1") | ||||
|         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_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: | ||||
|             json.dump(json.loads(body.decode("utf-8")), f) | ||||
| async def get_all_models(): | ||||
|     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 FileResponse(file_path) | ||||
|     return models | ||||
| 
 | ||||
|     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"]) | ||||
| async def proxy(path: str, request: Request, user=Depends(get_verified_user)): | ||||
|     target_url = f"{app.state.OPENAI_API_BASE_URL}/{path}" | ||||
|     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) | ||||
|     idx = 0 | ||||
| 
 | ||||
|     body = await request.body() | ||||
| 
 | ||||
|     # 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: | ||||
|         body = body.decode("utf-8") | ||||
|         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 | ||||
|         # This is a workaround until OpenAI fixes the issue with this model | ||||
|         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: | ||||
|         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["Authorization"] = f"Bearer {app.state.OPENAI_API_KEY}" | ||||
|     headers["Authorization"] = f"Bearer {key}" | ||||
|     headers["Content-Type"] = "application/json" | ||||
| 
 | ||||
|     try: | ||||
|  | @ -181,21 +285,7 @@ async def proxy(path: str, request: Request, user=Depends(get_verified_user)): | |||
|                 headers=dict(r.headers), | ||||
|             ) | ||||
|         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() | ||||
| 
 | ||||
|             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 | ||||
|     except Exception as e: | ||||
|         print(e) | ||||
|  |  | |||
|  | @ -425,7 +425,7 @@ def get_loader(filename: str, file_content_type: str, file_path: str): | |||
|     ] | ||||
| 
 | ||||
|     if file_ext == "pdf": | ||||
|         loader = PyPDFLoader(file_path) | ||||
|         loader = PyPDFLoader(file_path, extract_images=True) | ||||
|     elif file_ext == "csv": | ||||
|         loader = CSVLoader(file_path) | ||||
|     elif file_ext == "rst": | ||||
|  |  | |||
|  | @ -14,7 +14,7 @@ import json | |||
| from utils.utils import get_admin_user | ||||
| 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 | ||||
| 
 | ||||
| 
 | ||||
|  | @ -75,7 +75,7 @@ async def download_file_stream(url, file_path, file_name, chunk_size=1024 * 1024 | |||
|                     hashed = calculate_sha256(file) | ||||
|                     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) | ||||
| 
 | ||||
|                     if response.ok: | ||||
|  | @ -147,7 +147,7 @@ def upload(file: UploadFile = File(...)): | |||
|                     hashed = calculate_sha256(f) | ||||
|                     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) | ||||
| 
 | ||||
|                     if response.ok: | ||||
|  |  | |||
|  | @ -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", "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", "") | ||||
| 
 | ||||
| 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_API_BASE_URL[:-4] | ||||
|         if OLLAMA_API_BASE_URL.endswith("/api") | ||||
|         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 | ||||
|  | @ -229,9 +234,25 @@ if OLLAMA_BASE_URL == "": | |||
| OPENAI_API_KEY = os.environ.get("OPENAI_API_KEY", "") | ||||
| OPENAI_API_BASE_URL = os.environ.get("OPENAI_API_BASE_URL", "") | ||||
| 
 | ||||
| if OPENAI_API_KEY == "": | ||||
|     OPENAI_API_KEY = "none" | ||||
| 
 | ||||
| if OPENAI_API_BASE_URL == "": | ||||
|     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 | ||||
|  |  | |||
|  | @ -41,6 +41,7 @@ class ERROR_MESSAGES(str, Enum): | |||
|     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." | ||||
| 
 | ||||
|     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." | ||||
|  | @ -50,3 +51,4 @@ class ERROR_MESSAGES(str, Enum): | |||
|     RATE_LIMIT_EXCEEDED = "API rate limit exceeded" | ||||
| 
 | ||||
|     MODEL_NOT_FOUND = lambda name="": f"Model '{name}' was not found" | ||||
|     OPENAI_NOT_FOUND = lambda name="": f"OpenAI API was not found" | ||||
|  |  | |||
|  | @ -35,6 +35,9 @@ openpyxl | |||
| pyxlsb | ||||
| xlrd | ||||
| 
 | ||||
| opencv-python-headless | ||||
| rapidocr-onnxruntime | ||||
| 
 | ||||
| faster-whisper | ||||
| 
 | ||||
| PyJWT | ||||
|  |  | |||
|  | @ -14,7 +14,7 @@ services: | |||
|     build: | ||||
|       context: . | ||||
|       args: | ||||
|         OLLAMA_API_BASE_URL: '/ollama/api' | ||||
|         OLLAMA_BASE_URL: '/ollama' | ||||
|       dockerfile: Dockerfile | ||||
|     image: ghcr.io/open-webui/open-webui:main | ||||
|     container_name: open-webui | ||||
|  | @ -25,7 +25,7 @@ services: | |||
|     ports: | ||||
|       - ${OPEN_WEBUI_PORT-3000}:8080 | ||||
|     environment: | ||||
|       - 'OLLAMA_API_BASE_URL=http://ollama:11434/api' | ||||
|       - 'OLLAMA_BASE_URL=http://ollama:11434' | ||||
|       - 'WEBUI_SECRET_KEY=' | ||||
|     extra_hosts: | ||||
|       - host.docker.internal:host-gateway | ||||
|  |  | |||
							
								
								
									
										38
									
								
								i18next-parser.config.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								i18next-parser.config.ts
									
										
									
									
									
										Normal 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 | ||||
| }; | ||||
|  | @ -40,7 +40,7 @@ spec: | |||
|         - name: data | ||||
|           mountPath: /app/backend/data | ||||
|         env: | ||||
|         - name: OLLAMA_API_BASE_URL | ||||
|         - name: OLLAMA_BASE_URL | ||||
|           value: {{ include "ollama.url" . | quote }} | ||||
|         tty: true | ||||
|       {{- with .Values.webui.nodeSelector }} | ||||
|  |  | |||
|  | @ -26,8 +26,8 @@ spec: | |||
|             cpu: "1000m" | ||||
|             memory: "1Gi" | ||||
|         env: | ||||
|         - name: OLLAMA_API_BASE_URL | ||||
|           value: "http://ollama-service.open-webui.svc.cluster.local:11434/api" | ||||
|         - name: OLLAMA_BASE_URL | ||||
|           value: "http://ollama-service.open-webui.svc.cluster.local:11434" | ||||
|         tty: true | ||||
|         volumeMounts: | ||||
|         - name: webui-volume | ||||
|  |  | |||
|  | @ -1,6 +1,6 @@ | |||
| { | ||||
| 	"name": "open-webui", | ||||
| 	"version": "0.1.108", | ||||
| 	"version": "0.1.110", | ||||
| 	"private": true, | ||||
| 	"scripts": { | ||||
| 		"dev": "vite dev --host", | ||||
|  | @ -27,6 +27,7 @@ | |||
| 		"eslint": "^8.56.0", | ||||
| 		"eslint-config-prettier": "^8.5.0", | ||||
| 		"eslint-plugin-svelte": "^2.30.0", | ||||
| 		"i18next-parser": "^8.13.0", | ||||
| 		"postcss": "^8.4.31", | ||||
| 		"prettier": "^2.8.0", | ||||
| 		"prettier-plugin-svelte": "^2.10.1", | ||||
|  |  | |||
|  | @ -43,6 +43,10 @@ ol > li { | |||
| 	font-weight: 400; | ||||
| } | ||||
| 
 | ||||
| li p { | ||||
| 	display: inline; | ||||
| } | ||||
| 
 | ||||
| ::-webkit-scrollbar-thumb { | ||||
| 	--tw-border-opacity: 1; | ||||
| 	background-color: rgba(217, 217, 227, 0.8); | ||||
|  |  | |||
|  | @ -1,9 +1,9 @@ | |||
| import { OPENAI_API_BASE_URL } from '$lib/constants'; | ||||
| 
 | ||||
| export const getOpenAIUrl = async (token: string = '') => { | ||||
| export const getOpenAIUrls = async (token: string = '') => { | ||||
| 	let error = null; | ||||
| 
 | ||||
| 	const res = await fetch(`${OPENAI_API_BASE_URL}/url`, { | ||||
| 	const res = await fetch(`${OPENAI_API_BASE_URL}/urls`, { | ||||
| 		method: 'GET', | ||||
| 		headers: { | ||||
| 			Accept: 'application/json', | ||||
|  | @ -29,13 +29,13 @@ export const getOpenAIUrl = async (token: string = '') => { | |||
| 		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; | ||||
| 
 | ||||
| 	const res = await fetch(`${OPENAI_API_BASE_URL}/url/update`, { | ||||
| 	const res = await fetch(`${OPENAI_API_BASE_URL}/urls/update`, { | ||||
| 		method: 'POST', | ||||
| 		headers: { | ||||
| 			Accept: 'application/json', | ||||
|  | @ -43,7 +43,7 @@ export const updateOpenAIUrl = async (token: string = '', url: string) => { | |||
| 			...(token && { authorization: `Bearer ${token}` }) | ||||
| 		}, | ||||
| 		body: JSON.stringify({ | ||||
| 			url: url | ||||
| 			urls: urls | ||||
| 		}) | ||||
| 	}) | ||||
| 		.then(async (res) => { | ||||
|  | @ -64,13 +64,13 @@ export const updateOpenAIUrl = async (token: string = '', url: string) => { | |||
| 		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; | ||||
| 
 | ||||
| 	const res = await fetch(`${OPENAI_API_BASE_URL}/key`, { | ||||
| 	const res = await fetch(`${OPENAI_API_BASE_URL}/keys`, { | ||||
| 		method: 'GET', | ||||
| 		headers: { | ||||
| 			Accept: 'application/json', | ||||
|  | @ -96,13 +96,13 @@ export const getOpenAIKey = async (token: string = '') => { | |||
| 		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; | ||||
| 
 | ||||
| 	const res = await fetch(`${OPENAI_API_BASE_URL}/key/update`, { | ||||
| 	const res = await fetch(`${OPENAI_API_BASE_URL}/keys/update`, { | ||||
| 		method: 'POST', | ||||
| 		headers: { | ||||
| 			Accept: 'application/json', | ||||
|  | @ -110,7 +110,7 @@ export const updateOpenAIKey = async (token: string = '', key: string) => { | |||
| 			...(token && { authorization: `Bearer ${token}` }) | ||||
| 		}, | ||||
| 		body: JSON.stringify({ | ||||
| 			key: key | ||||
| 			keys: keys | ||||
| 		}) | ||||
| 	}) | ||||
| 		.then(async (res) => { | ||||
|  | @ -131,7 +131,7 @@ export const updateOpenAIKey = async (token: string = '', key: string) => { | |||
| 		throw error; | ||||
| 	} | ||||
| 
 | ||||
| 	return res.OPENAI_API_KEY; | ||||
| 	return res.OPENAI_API_KEYS; | ||||
| }; | ||||
| 
 | ||||
| export const getOpenAIModels = async (token: string = '') => { | ||||
|  |  | |||
|  | @ -225,33 +225,80 @@ | |||
| 		}, 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) => { | ||||
| 		if (history.messages[messageId]) { | ||||
| 			history.messages[messageId].deleted = true; | ||||
| 
 | ||||
| 			for (const childId of history.messages[messageId].childrenIds) { | ||||
| 				history.messages[childId].deleted = true; | ||||
| 		const messageToDelete = history.messages[messageId]; | ||||
| 		const messageParentId = messageToDelete.parentId; | ||||
| 		const messageChildrenIds = messageToDelete.childrenIds ?? []; | ||||
| 		const hasSibling = messageChildrenIds.some( | ||||
| 			(childId) => history.messages[childId]?.childrenIds?.length > 0 | ||||
| 		); | ||||
| 		messageChildrenIds.forEach((childId) => { | ||||
| 			const child = history.messages[childId]; | ||||
| 			if (child && child.childrenIds) { | ||||
| 				if (child.childrenIds.length === 0 && !hasSibling) { | ||||
| 					// if last prompt/response pair | ||||
| 					history.messages[messageParentId].childrenIds = []; | ||||
| 					history.currentId = messageParentId; | ||||
| 				} else { | ||||
| 					child.childrenIds.forEach((grandChildId) => { | ||||
| 						if (history.messages[grandChildId]) { | ||||
| 							history.messages[grandChildId].parentId = messageParentId; | ||||
| 							history.messages[messageParentId].childrenIds.push(grandChildId); | ||||
| 						} | ||||
| 					}); | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		await updateChatById(localStorage.token, chatId, { history }); | ||||
| 			// remove response | ||||
| 			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> | ||||
| 
 | ||||
| {#if messages.length == 0} | ||||
|  | @ -260,57 +307,55 @@ | |||
| 	<div class=" pb-10"> | ||||
| 		{#key chatId} | ||||
| 			{#each messages as message, messageIdx} | ||||
| 				{#if !message.deleted} | ||||
| 					<div class=" w-full"> | ||||
| 						<div | ||||
| 							class="flex flex-col justify-between px-5 mb-3 {$settings?.fullScreenMode ?? null | ||||
| 								? 'max-w-full' | ||||
| 								: 'max-w-3xl'} mx-auto rounded-lg group" | ||||
| 						> | ||||
| 							{#if message.role === 'user'} | ||||
| 								<UserMessage | ||||
| 									on:delete={() => messageDeleteHandler(message.id)} | ||||
| 									user={$user} | ||||
| 									{message} | ||||
| 									isFirstMessage={messageIdx === 0} | ||||
| 									siblings={message.parentId !== null | ||||
| 										? history.messages[message.parentId]?.childrenIds ?? [] | ||||
| 										: Object.values(history.messages) | ||||
| 												.filter((message) => message.parentId === null) | ||||
| 												.map((message) => message.id) ?? []} | ||||
| 									{confirmEditMessage} | ||||
| 									{showPreviousMessage} | ||||
| 									{showNextMessage} | ||||
| 									{copyToClipboard} | ||||
| 								/> | ||||
| 							{:else} | ||||
| 								<ResponseMessage | ||||
| 									{message} | ||||
| 									modelfiles={selectedModelfiles} | ||||
| 									siblings={history.messages[message.parentId]?.childrenIds ?? []} | ||||
| 									isLastMessage={messageIdx + 1 === messages.length} | ||||
| 									{confirmEditResponseMessage} | ||||
| 									{showPreviousMessage} | ||||
| 									{showNextMessage} | ||||
| 									{rateMessage} | ||||
| 									{copyToClipboard} | ||||
| 									{continueGeneration} | ||||
| 									{regenerateResponse} | ||||
| 									on:save={async (e) => { | ||||
| 										console.log('save', e); | ||||
| 				<div class=" w-full"> | ||||
| 					<div | ||||
| 						class="flex flex-col justify-between px-5 mb-3 {$settings?.fullScreenMode ?? null | ||||
| 							? 'max-w-full' | ||||
| 							: 'max-w-3xl'} mx-auto rounded-lg group" | ||||
| 					> | ||||
| 						{#if message.role === 'user'} | ||||
| 							<UserMessage | ||||
| 								on:delete={() => messageDeleteHandler(message.id)} | ||||
| 								user={$user} | ||||
| 								{message} | ||||
| 								isFirstMessage={messageIdx === 0} | ||||
| 								siblings={message.parentId !== null | ||||
| 									? history.messages[message.parentId]?.childrenIds ?? [] | ||||
| 									: Object.values(history.messages) | ||||
| 											.filter((message) => message.parentId === null) | ||||
| 											.map((message) => message.id) ?? []} | ||||
| 								{confirmEditMessage} | ||||
| 								{showPreviousMessage} | ||||
| 								{showNextMessage} | ||||
| 								{copyToClipboard} | ||||
| 							/> | ||||
| 						{:else} | ||||
| 							<ResponseMessage | ||||
| 								{message} | ||||
| 								modelfiles={selectedModelfiles} | ||||
| 								siblings={history.messages[message.parentId]?.childrenIds ?? []} | ||||
| 								isLastMessage={messageIdx + 1 === messages.length} | ||||
| 								{confirmEditResponseMessage} | ||||
| 								{showPreviousMessage} | ||||
| 								{showNextMessage} | ||||
| 								{rateMessage} | ||||
| 								{copyToClipboard} | ||||
| 								{continueGeneration} | ||||
| 								{regenerateResponse} | ||||
| 								on:save={async (e) => { | ||||
| 									console.log('save', e); | ||||
| 
 | ||||
| 										const message = e.detail; | ||||
| 										history.messages[message.id] = message; | ||||
| 										await updateChatById(localStorage.token, chatId, { | ||||
| 											messages: messages, | ||||
| 											history: history | ||||
| 										}); | ||||
| 									}} | ||||
| 								/> | ||||
| 							{/if} | ||||
| 						</div> | ||||
| 									const message = e.detail; | ||||
| 									history.messages[message.id] = message; | ||||
| 									await updateChatById(localStorage.token, chatId, { | ||||
| 										messages: messages, | ||||
| 										history: history | ||||
| 									}); | ||||
| 								}} | ||||
| 							/> | ||||
| 						{/if} | ||||
| 					</div> | ||||
| 				{/if} | ||||
| 				</div> | ||||
| 			{/each} | ||||
| 
 | ||||
| 			{#if bottomPadding} | ||||
|  |  | |||
|  | @ -24,6 +24,7 @@ | |||
| 	import CodeBlock from './CodeBlock.svelte'; | ||||
| 	import Image from '$lib/components/common/Image.svelte'; | ||||
| 	import { WEBUI_BASE_URL } from '$lib/constants'; | ||||
| 	import Tooltip from '$lib/components/common/Tooltip.svelte'; | ||||
| 
 | ||||
| 	export let modelfiles = []; | ||||
| 	export let message; | ||||
|  | @ -346,6 +347,7 @@ | |||
| 									class=" bg-transparent outline-none w-full resize-none" | ||||
| 									bind:value={editedContent} | ||||
| 									on:input={(e) => { | ||||
| 										e.target.style.height = ''; | ||||
| 										e.target.style.height = `${e.target.scrollHeight}px`; | ||||
| 									}} | ||||
| 								/> | ||||
|  | @ -464,189 +466,125 @@ | |||
| 											</div> | ||||
| 										{/if} | ||||
| 
 | ||||
| 										<button | ||||
| 											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} | ||||
| 										<Tooltip content="Edit" 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); | ||||
| 													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> | ||||
| 										</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 | ||||
| 														class=" w-4 h-4" | ||||
| 														fill="currentColor" | ||||
|  | @ -681,6 +619,21 @@ | |||
| 															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" | ||||
|  | @ -693,93 +646,166 @@ | |||
| 														<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" | ||||
| 															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> | ||||
| 										</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 message.info} | ||||
| 											<button | ||||
| 												class=" {isLastMessage | ||||
| 													? 'visible' | ||||
| 													: 'invisible group-hover:visible'} p-1 rounded dark:hover:text-white hover:text-black transition whitespace-pre-wrap" | ||||
| 												on:click={() => { | ||||
| 													console.log(message); | ||||
| 												}} | ||||
| 												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" | ||||
| 											<Tooltip content="Generation Info" placement="bottom"> | ||||
| 												<button | ||||
| 													class=" {isLastMessage | ||||
| 														? 'visible' | ||||
| 														: 'invisible group-hover:visible'} p-1 rounded dark:hover:text-white hover:text-black transition whitespace-pre-wrap" | ||||
| 													on:click={() => { | ||||
| 														console.log(message); | ||||
| 													}} | ||||
| 													id="info-{message.id}" | ||||
| 												> | ||||
| 													<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> | ||||
| 													<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="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 isLastMessage} | ||||
| 											<button | ||||
| 												type="button" | ||||
| 												class="{isLastMessage | ||||
| 													? 'visible' | ||||
| 													: 'invisible group-hover:visible'} p-1 rounded dark:hover:text-white hover:text-black transition regenerate-response-button" | ||||
| 												on:click={() => { | ||||
| 													continueGeneration(); | ||||
| 												}} | ||||
| 											> | ||||
| 												<svg | ||||
| 													xmlns="http://www.w3.org/2000/svg" | ||||
| 													fill="none" | ||||
| 													viewBox="0 0 24 24" | ||||
| 													stroke-width="1.5" | ||||
| 													stroke="currentColor" | ||||
| 													class="w-4 h-4" | ||||
| 											<Tooltip content="Continue Response" placement="bottom"> | ||||
| 												<button | ||||
| 													type="button" | ||||
| 													class="{isLastMessage | ||||
| 														? 'visible' | ||||
| 														: 'invisible group-hover:visible'} p-1 rounded dark:hover:text-white hover:text-black transition regenerate-response-button" | ||||
| 													on:click={() => { | ||||
| 														continueGeneration(); | ||||
| 													}} | ||||
| 												> | ||||
| 													<path | ||||
| 														stroke-linecap="round" | ||||
| 														stroke-linejoin="round" | ||||
| 														d="M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z" | ||||
| 													/> | ||||
| 													<path | ||||
| 														stroke-linecap="round" | ||||
| 														stroke-linejoin="round" | ||||
| 														d="M15.91 11.672a.375.375 0 0 1 0 .656l-5.603 3.113a.375.375 0 0 1-.557-.328V8.887c0-.286.307-.466.557-.327l5.603 3.112Z" | ||||
| 													/> | ||||
| 												</svg> | ||||
| 											</button> | ||||
| 													<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="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 | ||||
| 												type="button" | ||||
| 												class="{isLastMessage | ||||
| 													? 'visible' | ||||
| 													: '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" | ||||
| 											<Tooltip content="Regenerate" placement="bottom"> | ||||
| 												<button | ||||
| 													type="button" | ||||
| 													class="{isLastMessage | ||||
| 														? 'visible' | ||||
| 														: 'invisible group-hover:visible'} p-1 rounded dark:hover:text-white hover:text-black transition regenerate-response-button" | ||||
| 													on:click={regenerateResponse} | ||||
| 												> | ||||
| 													<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> | ||||
| 													<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.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} | ||||
| 									</div> | ||||
| 								{/if} | ||||
|  |  | |||
|  | @ -5,6 +5,7 @@ | |||
| 	import Name from './Name.svelte'; | ||||
| 	import ProfileImage from './ProfileImage.svelte'; | ||||
| 	import { modelfiles, settings } from '$lib/stores'; | ||||
| 	import Tooltip from '$lib/components/common/Tooltip.svelte'; | ||||
| 
 | ||||
| 	const i18n = getContext('i18n'); | ||||
| 
 | ||||
|  | @ -171,7 +172,8 @@ | |||
| 						class=" bg-transparent outline-none w-full resize-none" | ||||
| 						bind:value={editedContent} | ||||
| 						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> | ||||
| 						{/if} | ||||
| 
 | ||||
| 						<button | ||||
| 							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} | ||||
| 						<Tooltip content="Edit" placement="bottom"> | ||||
| 							<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={() => { | ||||
| 									deleteMessageHandler(); | ||||
| 									editMessageHandler(); | ||||
| 								}} | ||||
| 							> | ||||
| 								<svg | ||||
|  | @ -310,10 +268,60 @@ | |||
| 									<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" | ||||
| 										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="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} | ||||
| 					</div> | ||||
| 				</div> | ||||
|  |  | |||
|  | @ -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" | ||||
|                             /> | ||||
|                         </svg> --> | ||||
| 						<span class="ml-2 self-center">{$i18n.t(' JSON ')}</span> | ||||
| 						<span class="ml-2 self-center">{$i18n.t('JSON')}</span> | ||||
| 					{/if} | ||||
| 				</button> | ||||
| 			</div> | ||||
|  |  | |||
|  | @ -4,7 +4,12 @@ | |||
| 	const dispatch = createEventDispatcher(); | ||||
| 
 | ||||
| 	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'; | ||||
| 
 | ||||
| 	const i18n = getContext('i18n'); | ||||
|  | @ -18,12 +23,14 @@ | |||
| 	let OPENAI_API_KEY = ''; | ||||
| 	let OPENAI_API_BASE_URL = ''; | ||||
| 
 | ||||
| 	let OPENAI_API_KEYS = ['']; | ||||
| 	let OPENAI_API_BASE_URLS = ['']; | ||||
| 
 | ||||
| 	let showOpenAI = false; | ||||
| 	let showLiteLLM = false; | ||||
| 
 | ||||
| 	const updateOpenAIHandler = async () => { | ||||
| 		OPENAI_API_BASE_URL = await updateOpenAIUrl(localStorage.token, OPENAI_API_BASE_URL); | ||||
| 		OPENAI_API_KEY = await updateOpenAIKey(localStorage.token, OPENAI_API_KEY); | ||||
| 		OPENAI_API_BASE_URLS = await updateOpenAIUrls(localStorage.token, OPENAI_API_BASE_URLS); | ||||
| 		OPENAI_API_KEYS = await updateOpenAIKeys(localStorage.token, OPENAI_API_KEYS); | ||||
| 
 | ||||
| 		await models.set(await getModels()); | ||||
| 	}; | ||||
|  | @ -45,8 +52,8 @@ | |||
| 	onMount(async () => { | ||||
| 		if ($user.role === 'admin') { | ||||
| 			OLLAMA_BASE_URLS = await getOllamaUrls(localStorage.token); | ||||
| 			OPENAI_API_BASE_URL = await getOpenAIUrl(localStorage.token); | ||||
| 			OPENAI_API_KEY = await getOpenAIKey(localStorage.token); | ||||
| 			OPENAI_API_BASE_URLS = await getOpenAIUrls(localStorage.token); | ||||
| 			OPENAI_API_KEYS = await getOpenAIKeys(localStorage.token); | ||||
| 		} | ||||
| 	}); | ||||
| </script> | ||||
|  | @ -73,37 +80,74 @@ | |||
| 				</div> | ||||
| 
 | ||||
| 				{#if showOpenAI} | ||||
| 					<div> | ||||
| 						<div class=" mb-2.5 text-sm font-medium">{$i18n.t('API Key')}</div> | ||||
| 						<div class="flex w-full"> | ||||
| 							<div class="flex-1"> | ||||
| 								<input | ||||
| 									class="w-full rounded py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-800 outline-none" | ||||
| 									placeholder={$i18n.t('Enter OpenAI API Key')} | ||||
| 									bind:value={OPENAI_API_KEY} | ||||
| 									autocomplete="off" | ||||
| 								/> | ||||
| 							</div> | ||||
| 						</div> | ||||
| 					</div> | ||||
| 					<div class="flex flex-col gap-1"> | ||||
| 						{#each OPENAI_API_BASE_URLS as url, idx} | ||||
| 							<div class="flex w-full gap-2"> | ||||
| 								<div class="flex-1"> | ||||
| 									<input | ||||
| 										class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none" | ||||
| 										placeholder={$i18n.t('API Base URL')} | ||||
| 										bind:value={url} | ||||
| 										autocomplete="off" | ||||
| 									/> | ||||
| 								</div> | ||||
| 
 | ||||
| 					<div> | ||||
| 						<div class=" mb-2.5 text-sm font-medium">{$i18n.t('API Base URL')}</div> | ||||
| 						<div class="flex w-full"> | ||||
| 							<div class="flex-1"> | ||||
| 								<input | ||||
| 									class="w-full rounded py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-800 outline-none" | ||||
| 									placeholder="Enter OpenAI API Base URL" | ||||
| 									bind:value={OPENAI_API_BASE_URL} | ||||
| 									autocomplete="off" | ||||
| 								/> | ||||
| 								<div class="flex-1"> | ||||
| 									<input | ||||
| 										class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none" | ||||
| 										placeholder={$i18n.t('API Key')} | ||||
| 										bind:value={OPENAI_API_KEYS[idx]} | ||||
| 										autocomplete="off" | ||||
| 									/> | ||||
| 								</div> | ||||
| 								<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 class="mt-2 text-xs text-gray-400 dark:text-gray-500"> | ||||
| 							WebUI will make requests to <span class=" text-gray-200" | ||||
| 								>'{OPENAI_API_BASE_URL}/chat'</span | ||||
| 							> | ||||
| 						</div> | ||||
| 							<div class=" mb-1 text-xs text-gray-400 dark:text-gray-500"> | ||||
| 								WebUI will make requests to <span class=" text-gray-200">'{url}/models'</span> | ||||
| 							</div> | ||||
| 						{/each} | ||||
| 					</div> | ||||
| 				{/if} | ||||
| 			</div> | ||||
|  | @ -193,7 +237,7 @@ | |||
| 			<div class="mt-2 text-xs text-gray-400 dark:text-gray-500"> | ||||
| 				{$i18n.t('Trouble accessing Ollama?')} | ||||
| 				<a | ||||
| 					class=" text-gray-300 font-medium" | ||||
| 					class=" text-gray-300 font-medium underline" | ||||
| 					href="https://github.com/open-webui/open-webui#troubleshooting" | ||||
| 					target="_blank" | ||||
| 				> | ||||
|  |  | |||
|  | @ -149,7 +149,7 @@ | |||
| 				<br /> | ||||
| 				{$i18n.t('You can help us translate the WebUI.')} | ||||
| 				<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" | ||||
| 					target="_blank" | ||||
| 				> | ||||
|  |  | |||
|  | @ -56,7 +56,7 @@ | |||
| 	let modelUploadMode = 'file'; | ||||
| 	let modelInputFile = ''; | ||||
| 	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 uploadProgress = null; | ||||
| 
 | ||||
|  | @ -517,7 +517,7 @@ | |||
| 									{#if !deleteModelTag} | ||||
| 										<option value="" disabled selected>Select a model</option> | ||||
| 									{/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" | ||||
| 											>{model.name + ' (' + (model.size / 1024 ** 3).toFixed(1) + ' GB)'}</option | ||||
| 										> | ||||
|  | @ -599,7 +599,7 @@ | |||
| 												on:change={() => { | ||||
| 													console.log(modelInputFile); | ||||
| 												}} | ||||
| 												accept=".gguf" | ||||
| 												accept=".gguf,.safetensors" | ||||
| 												required | ||||
| 												hidden | ||||
| 											/> | ||||
|  |  | |||
|  | @ -140,7 +140,9 @@ | |||
| 						<button | ||||
| 							class="w-full text-sm font-medium py-3 bg-gray-850 hover:bg-gray-800 text-center rounded-xl" | ||||
| 							type="button" | ||||
| 							on:click={uploadDocInputElement.click} | ||||
| 							on:click={() => { | ||||
| 								uploadDocInputElement.click(); | ||||
| 							}} | ||||
| 						> | ||||
| 							{#if inputFiles} | ||||
| 								{inputFiles.length > 0 ? `${inputFiles.length}` : ''} document(s) selected. | ||||
|  |  | |||
|  | @ -90,8 +90,3 @@ export const SUPPORTED_FILE_EXTENSIONS = [ | |||
| // This feature, akin to $env/static/private, exclusively incorporates environment variables
 | ||||
| // that are prefixed with config.kit.env.publicPrefix (usually set to PUBLIC_).
 | ||||
| // 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
 | ||||
|  |  | |||
|  | @ -4,7 +4,7 @@ import LanguageDetector from 'i18next-browser-languagedetector'; | |||
| import type { i18n as i18nType } from 'i18next'; | ||||
| import { writable } from 'svelte/store'; | ||||
| 
 | ||||
| const createI18nStore = (i18n: i18n) => { | ||||
| const createI18nStore = (i18n: i18nType) => { | ||||
| 	const i18nWritable = writable(i18n); | ||||
| 
 | ||||
| 	i18n.on('initialized', () => { | ||||
|  | @ -20,7 +20,7 @@ const createI18nStore = (i18n: i18n) => { | |||
| 	return i18nWritable; | ||||
| }; | ||||
| 
 | ||||
| const createIsLoadingStore = (i18n: i18n) => { | ||||
| const createIsLoadingStore = (i18n: i18nType) => { | ||||
| 	const isLoading = writable(false); | ||||
| 
 | ||||
| 	// if loaded resources are empty || {}, set loading to true
 | ||||
|  | @ -39,11 +39,13 @@ const createIsLoadingStore = (i18n: i18n) => { | |||
| 
 | ||||
| i18next | ||||
| 	.use( | ||||
| 		resourcesToBackend((language, namespace) => import(`./locales/${language}/${namespace}.json`)) | ||||
| 		resourcesToBackend( | ||||
| 			(language: string, namespace: string) => import(`./locales/${language}/${namespace}.json`) | ||||
| 		) | ||||
| 	) | ||||
| 	.use(LanguageDetector) | ||||
| 	.init({ | ||||
| 		debug: true, | ||||
| 		debug: false, | ||||
| 		detection: { | ||||
| 			order: ['querystring', 'localStorage', 'navigator'], | ||||
| 			caches: ['localStorage'], | ||||
|  |  | |||
|  | @ -18,10 +18,9 @@ | |||
| 	"Add message": "Nachricht eingeben", | ||||
| 	"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.", | ||||
| 	"admin": "Administrator", | ||||
| 	"Admin": "", | ||||
| 	"Admin Panel": "Admin Panel", | ||||
| 	"Admin Settings": "Admin Einstellungen", | ||||
| 	"Advanced": "Angepasst", | ||||
| 	"Advanced Model Params": "Erweiterte Modell Parameter", | ||||
| 	"Advanced Parameters": "Erweiterte Parameter", | ||||
| 	"all": "Alle", | ||||
|  | @ -35,7 +34,6 @@ | |||
| 	"API Key": "API Key", | ||||
| 	"API RPM": "API RPM", | ||||
| 	"are allowed - Activate this command by typing": "sind erlaubt - Aktiviere diesen Befehl, indem du", | ||||
| 	"assistant": "Assistent", | ||||
| 	"Audio": "Audio", | ||||
| 	"Auto-playback response": "Automatische Wiedergabe der Antwort", | ||||
| 	"Auto-send input after 3 sec.": "Automatisches Senden der Eingabe nach 3 Sek", | ||||
|  | @ -98,7 +96,7 @@ | |||
| 	"Deleted {tagName}": "{tagName} gelöscht", | ||||
| 	"Description": "Beschreibung", | ||||
| 	"Desktop Notifications": "Desktop-Benachrichtigungen", | ||||
| 	"Didn't find your language?": "Deine Sprache nicht gefunden?", | ||||
| 	"Didn't find your language?": "Deine Sprache nicht vorhanden?", | ||||
| 	"Disabled": "Deaktiviert", | ||||
| 	"Discover a modelfile": "Eine Modelfiles entdecken", | ||||
| 	"Discover a prompt": "Einen Prompt entdecken", | ||||
|  | @ -121,21 +119,17 @@ | |||
| 	"Enable Chat History": "Chat-Verlauf aktivieren", | ||||
| 	"Enable New Sign Ups": "Neue Anmeldungen aktivieren", | ||||
| 	"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 stop sequence": "Stop-Sequenz eingeben", | ||||
| 	"Enter Your Email": "Geben Deine E-Mail-Adresse ein", | ||||
| 	"Enter Your Full Name": "Gebe Deinen vollständigen Namen ein", | ||||
| 	"Enter Your Password": "Gebe Dein Passwort ein", | ||||
| 	"Experimental": "Experimentell", | ||||
| 	"Export All Chats (All Users)": "Alle Chats exportieren (alle Benutzer)", | ||||
| 	"Export Chats": "Chats exportieren", | ||||
| 	"Export Documents Mapping": "Dokumentenmapping exportieren", | ||||
| 	"Export Modelfiles": "Modelfiles exportieren", | ||||
| 	"Export Prompts": "Prompts exportieren", | ||||
| 	"Failed to read clipboard contents": "Fehler beim Lesen des Zwischenablageninhalts", | ||||
| 	"File Mode": "Dateimodus", | ||||
| 	"File not found.": "Datei nicht gefunden.", | ||||
| 	"Focus chat input": "Chat-Eingabe fokussieren", | ||||
| 	"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", | ||||
| 	"Modelfile": "Modelfiles", | ||||
| 	"Modelfile Advanced Settings": "Erweiterte Modelfileseinstellungen", | ||||
| 	"Modelfile Content": "Modelfilesinhalt", | ||||
| 	"Modelfiles": "Modelfiles", | ||||
| 	"Models": "Modelle", | ||||
| 	"My Documents": "Meine Dokumente", | ||||
|  | @ -195,19 +188,20 @@ | |||
| 	"My Prompts": "Meine Prompts", | ||||
| 	"Name": "Name", | ||||
| 	"Name Tag": "Namens-Tag", | ||||
| 	"Name your Modelfile": "Benenne Dein Modelfile", | ||||
| 	"Name your modelfile": "", | ||||
| 	"New Chat": "Neuer Chat", | ||||
| 	"New Password": "Neues Passwort", | ||||
| 	"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", | ||||
| 	"Off": "Aus", | ||||
| 	"Okay, Let's Go!": "Okay, los geht's!", | ||||
| 	"Ollama API URL": "Ollama-API-URL", | ||||
| 	"Ollama Version": "Ollama-Version", | ||||
| 	"On": "Ein", | ||||
| 	"Only": "Nur", | ||||
| 	"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! 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 AI": "Open AI", | ||||
| 	"Open new chat": "Neuen Chat öffnen", | ||||
|  | @ -215,7 +209,7 @@ | |||
| 	"or": "oder", | ||||
| 	"Parameters": "Parameter", | ||||
| 	"Password": "Passwort", | ||||
| 	"pending": "ausstehend", | ||||
| 	"Pending": "", | ||||
| 	"Permission denied when accessing microphone: {{error}}": "Zugriff auf das Mikrofon verweigert: {{error}}", | ||||
| 	"Playground": "Playground", | ||||
| 	"Profile": "Profil", | ||||
|  | @ -277,6 +271,7 @@ | |||
| 	"STT Settings": "STT-Einstellungen", | ||||
| 	"Submit": "Senden", | ||||
| 	"Success": "Erfolg", | ||||
| 	"Successfully updated": "", | ||||
| 	"Successfully updated.": "Erfolgreich aktualisiert.", | ||||
| 	"Sync All": "Alles synchronisieren", | ||||
| 	"System": "System", | ||||
|  | @ -290,12 +285,12 @@ | |||
| 	"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 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 Auto-Generation": "Automatische Titelgenerierung", | ||||
| 	"Title Generation Prompt": "Prompt für Titelgenerierung", | ||||
| 	"to": "für", | ||||
| 	"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 sidebar": "Seitenleiste umschalten", | ||||
| 	"Top K": "Top K", | ||||
|  | @ -303,12 +298,10 @@ | |||
| 	"Trouble accessing Ollama?": "Probleme beim Zugriff auf Ollama?", | ||||
| 	"TTS Settings": "TTS-Einstellungen", | ||||
| 	"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 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.", | ||||
| 	"user": "Benutzer", | ||||
| 	"User": "", | ||||
| 	"User Permissions": "Benutzerberechtigungen", | ||||
| 	"Users": "Benutzer", | ||||
| 	"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.", | ||||
| 	"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 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 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." | ||||
| } | ||||
|  |  | |||
|  | @ -18,10 +18,9 @@ | |||
| 	"Add message": "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": "", | ||||
| 	"Admin Panel": "Admin Panel", | ||||
| 	"Admin Settings": "Admin Settings", | ||||
| 	"Advanced": "Advanced", | ||||
| 	"Advanced Model Params": "Advanced Model Params", | ||||
| 	"Advanced Parameters": "Advanced Parameters", | ||||
| 	"all": "all", | ||||
|  | @ -35,7 +34,6 @@ | |||
| 	"API Key": "API Key", | ||||
| 	"API RPM": "API RPM", | ||||
| 	"are allowed - Activate this command by typing": "are allowed - Activate this command by typing", | ||||
| 	"assistant": "Assistant", | ||||
| 	"Audio": "Audio", | ||||
| 	"Auto-playback response": "Auto-playback response", | ||||
| 	"Auto-send input after 3 sec.": "Auto-send input after 3 sec.", | ||||
|  | @ -121,21 +119,17 @@ | |||
| 	"Enable Chat History": "Enable Chat History", | ||||
| 	"Enable New Sign Ups": "Enable New Sign Ups", | ||||
| 	"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 stop sequence": "Enter stop sequence", | ||||
| 	"Enter Your Email": "Enter Your Email", | ||||
| 	"Enter Your Full Name": "Enter Your Full Name", | ||||
| 	"Enter Your Password": "Enter Your Password", | ||||
| 	"Experimental": "Experimental", | ||||
| 	"Export All Chats (All Users)": "Export All Chats (All Users)", | ||||
| 	"Export Chats": "Export Chats", | ||||
| 	"Export Documents Mapping": "Export Documents Mapping", | ||||
| 	"Export Modelfiles": "Export Modelfiles", | ||||
| 	"Export Prompts": "Export Prompts", | ||||
| 	"Failed to read clipboard contents": "Failed to read clipboard contents", | ||||
| 	"File Mode": "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:", | ||||
|  | @ -187,7 +181,6 @@ | |||
| 	"Model Tag Name": "Model Tag Name", | ||||
| 	"Modelfile": "Modelfile", | ||||
| 	"Modelfile Advanced Settings": "Modelfile Advanced Settings", | ||||
| 	"Modelfile Content": "Modelfile Content", | ||||
| 	"Modelfiles": "Modelfiles", | ||||
| 	"Models": "Models", | ||||
| 	"My Documents": "My Documents", | ||||
|  | @ -195,19 +188,20 @@ | |||
| 	"My Prompts": "My Prompts", | ||||
| 	"Name": "Name", | ||||
| 	"Name Tag": "Name Tag", | ||||
| 	"Name your Modelfile": "Name your 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": "Ollama API URL", | ||||
| 	"Ollama Version": "Ollama Version", | ||||
| 	"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.": "", | ||||
| 	"Oops! You're using an unsupported method (frontend only). Please serve the WebUI from the backend.": "", | ||||
| 	"Open": "Open", | ||||
| 	"Open AI": "Open AI", | ||||
| 	"Open new chat": "Open new chat", | ||||
|  | @ -215,7 +209,7 @@ | |||
| 	"or": "or", | ||||
| 	"Parameters": "Parameters", | ||||
| 	"Password": "Password", | ||||
| 	"pending": "Pending", | ||||
| 	"Pending": "", | ||||
| 	"Permission denied when accessing microphone: {{error}}": "Permission denied when accessing microphone: {{error}}", | ||||
| 	"Playground": "Playground", | ||||
| 	"Profile": "Profile", | ||||
|  | @ -277,6 +271,7 @@ | |||
| 	"STT Settings": "STT Settings", | ||||
| 	"Submit": "Submit", | ||||
| 	"Success": "Success", | ||||
| 	"Successfully updated": "", | ||||
| 	"Successfully updated.": "Successfully updated.", | ||||
| 	"Sync All": "Sync All", | ||||
| 	"System": "System", | ||||
|  | @ -290,12 +285,12 @@ | |||
| 	"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.": "to chat input.", | ||||
| 	"Toggle settings": "Toggle settings", | ||||
| 	"Toggle sidebar": "Toggle sidebar", | ||||
| 	"Top K": "Top K", | ||||
|  | @ -303,12 +298,10 @@ | |||
| 	"Trouble accessing Ollama?": "Trouble accessing Ollama?", | ||||
| 	"TTS Settings": "TTS Settings", | ||||
| 	"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 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.", | ||||
| 	"user": "User", | ||||
| 	"User": "", | ||||
| 	"User Permissions": "User Permissions", | ||||
| 	"Users": "Users", | ||||
| 	"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.", | ||||
| 	"Whisper (Local)": "Whisper (Local)", | ||||
| 	"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]", | ||||
| 	"You can help us translate the WebUI.": "You can help us translate the WebUI.", | ||||
| 	"Write a summary in 50 words that summarizes [topic or keyword].": "Write a summary in 50 words that summarizes [topic or keyword].", | ||||
| 	"You": "You", | ||||
| 	"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." | ||||
| } | ||||
|  |  | |||
|  | @ -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": "درباره", | ||||
| 	"Account": "حساب کاربری", | ||||
| 	"Action": "عمل", | ||||
| 	"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 Files": "اضافه کردن فایلها", | ||||
| 	"Add Files": "اضافه کردن فایل\u200cها", | ||||
| 	"Add LiteLLM Model": "اضافه کردن مدل LiteLLM", | ||||
| 	"add tags": "اضافه کردن تگها", | ||||
| 	"admin": "مدیریت", | ||||
| 	"Add message": "اضافه کردن پیغام", | ||||
| 	"add tags": "اضافه کردن تگ\u200cها", | ||||
| 	"Adjusting these settings will apply changes universally to all users.": "با تنظیم این تنظیمات، تغییرات به طور کلی برای همه کاربران اعمال می شود.", | ||||
| 	"Admin": "", | ||||
| 	"Admin Panel": "پنل مدیریت", | ||||
| 	"Admin Settings": "تنظیمات مدیریت", | ||||
| 	"Advanced Model Params": "پارامترهای پیشرفته مدل", | ||||
| 	"Advanced Parameters": "پارامترهای پیشرفته", | ||||
| 	"all": "همه", | ||||
| 	"All Users": "همه کاربران", | ||||
| 	"Allow": "اجازه دادن", | ||||
| 	"Allow Chat Deletion": "اجازه حذف گپ", | ||||
| 	"alphanumeric characters and hyphens": "حروف الفبایی و خط فاصله", | ||||
| 	"Already have an account?": "از قبل حساب کاربری دارید؟", | ||||
| 	"and": "و", | ||||
| 	"API Base URL": "API Base URL", | ||||
| 	"API Key": "API Key", | ||||
| 	"API RPM": "API RPM", | ||||
| 	"are allowed - Activate this command by typing": "مجاز هستند - این دستور را با تایپ کردن این فعال کنید:", | ||||
| 	"Audio": "صدا", | ||||
| 	"AUTOMATIC1111 Base URL": "پایه URL AUTOMATIC1111 ", | ||||
| 	"Auto-playback response": "پخش خودکار پاسخ ", | ||||
| 	"Auto-send input after 3 sec.": "به طور خودکار ورودی را پس از 3 ثانیه ارسال کن.", | ||||
| 	"AUTOMATIC1111 Base URL": "پایه URL AUTOMATIC1111 ", | ||||
| 	"available!": "در دسترس!", | ||||
| 	"Back": "بازگشت", | ||||
| 	"Builder Mode": "حالت سازنده", | ||||
| 	"Cancel": "لغو", | ||||
| 	"Categories": "دستهبندیها", | ||||
| 	"Categories": "دسته\u200cبندی\u200cها", | ||||
| 	"Change Password": "تغییر رمز عبور", | ||||
| 	"Chat History": "تاریخچهی گفتگو", | ||||
| 	"Chat": "گپ", | ||||
| 	"Chat History": "تاریخچه\u200cی گفتگو", | ||||
| 	"Chat History is off for this browser.": "سابقه گپ برای این مرورگر خاموش است.", | ||||
| 	"Chats": "گپها", | ||||
| 	"Chats": "گپ\u200cها", | ||||
| 	"Check Again": "چک مجدد", | ||||
| 	"Check for updates": "بررسی به\u200cروزرسانی", | ||||
| 	"Checking for updates...": "در حال بررسی برای به\u200cروزرسانی..", | ||||
| 	"Choose a model before saving...": "قبل از ذخیره یک مدل را انتخاب کنید...", | ||||
| 	"Chunk Overlap": "همپوشانی تکه", | ||||
| 	"Chunk Params": "پارامترهای تکه", | ||||
| 	"Chunk Size": "اندازه تکه", | ||||
| 	"Click here for help": "برای کمک اینجا را کلیک کنید", | ||||
| 	"Click here to check other modelfiles.": "برای بررسی سایر فایل\u200cهای مدل اینجا را کلیک کنید.", | ||||
| 	"click here.": "اینجا کلیک کنید.", | ||||
| 	"Click here to check other modelfiles.": "برای بررسی سایر فایلهای مدل اینجا را کلیک کنید.", | ||||
| 	"Click on the user role button to change a user's role.": "برای تغییر نقش کاربر، روی دکمه نقش کاربر کلیک کنید.", | ||||
| 	"Close": "بسته", | ||||
| 	"Collection": "مجموعه", | ||||
| 	"Command": "دستور", | ||||
| 	"Confirm Password": "تایید رمز عبور", | ||||
|  | @ -44,68 +67,86 @@ | |||
| 	"Content": "محتوا", | ||||
| 	"Context Length": "طول زمینه", | ||||
| 	"Conversation Mode": "حالت مکالمه", | ||||
| 	"Copying to clipboard was successful!": "کپی کردن در کلیپ بورد با موفقیت انجام شد!", | ||||
| 	"Copy last code block": "کپی آخرین بلوک کد", | ||||
| 	"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 Account": "ساخت حساب کاربری", | ||||
| 	"Created at": "ایجاد شده در", | ||||
| 	"Created by": "ایجاد شده توسط", | ||||
| 	"Current Model": "مدل فعلی", | ||||
| 	"Current Password": "رمز عبور فعلی", | ||||
| 	"Custom": "دلخواه", | ||||
| 	"Customize Ollama models for a specific purpose": "مدل های اولاما را برای یک هدف خاص سفارشی کنید", | ||||
| 	"Dark": "تیره", | ||||
| 	"Database": "پایگاه داده", | ||||
| 	"DD/MM/YYYY HH:mm": "DD/MM/YYYY HH:mm", | ||||
| 	"Default": "پیشفرض", | ||||
| 	"Default model updated": "مدل پیشفرض بهروزرسانی شد", | ||||
| 	"Default (Web API)": "پیشفرض (Web API)", | ||||
| 	"Default model updated": "مدل پیشفرض به\u200cروزرسانی شد", | ||||
| 	"Default Prompt Suggestions": "پیشنهادات پرامپت پیش فرض", | ||||
| 	"Default User Role": "نقش کاربر پیش فرض", | ||||
| 	"Default (Web API)": "پیشفرض (Web API)", | ||||
| 	"delete": "حذف", | ||||
| 	"Delete a model": "حذف یک مدل", | ||||
| 	"Delete chat": "حذف گپ", | ||||
| 	"Delete Chats": "حذف گپها", | ||||
| 	"Delete Chats": "حذف گپ\u200cها", | ||||
| 	"Deleted {{deleteModelTag}}": "{{deleteModelTag}} پاک شد", | ||||
| 	"delete": "حذف", | ||||
| 	"Deleted {tagName}": "{tagName} حذف شد", | ||||
| 	"Description": "توضیحات", | ||||
| 	"Desktop Notifications": "اعلان", | ||||
| 	"Disabled": "غیرفعال", | ||||
| 	"Discover a modelfile": "فایل مدل را کشف کنید", | ||||
| 	"Discover a prompt": "یک اعلان را کشف کنید", | ||||
| 	"Discover, download, and explore custom prompts": "پرامپتهای سفارشی را کشف، دانلود و کاوش کنید", | ||||
| 	"Discover, download, and explore custom prompts": "پرامپت\u200cهای سفارشی را کشف، دانلود و کاوش کنید", | ||||
| 	"Discover, download, and explore model presets": "پیش تنظیمات مدل را کشف، دانلود و کاوش کنید", | ||||
| 	"Display the username instead of You in the Chat": "نمایش نام کاربری به جای «شما» در چت", | ||||
| 	"Document": "سند", | ||||
| 	"Documents": "اسناد", | ||||
| 	"Document Settings": "تنظیمات سند", | ||||
| 	"Documents": "اسناد", | ||||
| 	"does not make any external connections, and your data stays securely on your locally hosted server.": "هیچ اتصال خارجی ایجاد نمی کند و داده های شما به طور ایمن در سرور میزبان محلی شما باقی می ماند.", | ||||
| 	"Don't Allow": "اجازه نده", | ||||
| 	"Don't have an account?": "حساب کاربری ندارید؟", | ||||
| 	"Download as a File": "دانلود به صورت فایل", | ||||
| 	"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 User": "ویرایش کاربر", | ||||
| 	"Email": "ایمیل", | ||||
| 	"Enable Chat History": "تاریخچه چت را فعال کنید", | ||||
| 	"Enable New Sign Ups": "فعال کردن ثبت نام\u200cهای جدید", | ||||
| 	"Enabled": "فعال", | ||||
| 	"Enable New Sign Ups": "فعال کردن ثبت نامهای جدید", | ||||
| 	"Experimental": "آزمایشی", | ||||
| 	"Export All Chats (All Users)": "اکسپورت از همه گپها(همه کاربران)", | ||||
| 	"Export Chats": "اکسپورت از گپها", | ||||
| 	"Enter OpenAI API Key": "کلید OpenAI API را وارد کنید", | ||||
| 	"Enter stop sequence": "توالی توقف را وارد کنید", | ||||
| 	"Enter Your Email": "ایمیل خود را وارد کنید", | ||||
| 	"Enter Your Full Name": "نام کامل خود را وارد کنید", | ||||
| 	"Enter Your Password": "رمز عبور خود را وارد کنید", | ||||
| 	"Export All Chats (All Users)": "اکسپورت از همه گپ\u200cها(همه کاربران)", | ||||
| 	"Export Chats": "اکسپورت از گپ\u200cها", | ||||
| 	"Export Documents Mapping": "اکسپورت از نگاشت اسناد", | ||||
| 	"Export Modelfiles": "اکسپورت از فایلهای مدل", | ||||
| 	"Export Prompts": "اکسپورت از پرامپتها", | ||||
| 	"Export Modelfiles": "اکسپورت از فایل\u200cهای مدل", | ||||
| 	"Export Prompts": "اکسپورت از پرامپت\u200cها", | ||||
| 	"Failed to read clipboard contents": "خواندن محتوای کلیپ بورد ناموفق بود", | ||||
| 	"File Mode": "حالت فایل", | ||||
| 	"File not found.": "فایل یافت نشد.", | ||||
| 	"Focus chat input": "فوکوس کردن ورودی گپ", | ||||
| 	"Format your variables using square brackets like this:": "متغیرهای خود را با استفاده از براکت مربع به شکل زیر قالب بندی کنید:", | ||||
| 	"From (Base Model)": "از (مدل پایه)", | ||||
| 	"Full Screen Mode": "حالت تمام صفحه", | ||||
| 	"General": "عمومی", | ||||
| 	"General Settings": "تنظیمات عمومی", | ||||
| 	"Hello, {{name}}": "سلام، {{name}}", | ||||
| 	"Hide": "پنهان", | ||||
| 	"How can I help you today?": "امروز چطور می توانم کمک تان کنم؟", | ||||
| 	"Image Generation (Experimental)": "تولید تصویر (آزمایشی)", | ||||
| 	"Image Settings": "تنظیمات تصویر", | ||||
| 	"Images": "تصاویر", | ||||
| 	"Import Chats": "ایمپورت گپها", | ||||
| 	"Import Chats": "ایمپورت گپ\u200cها", | ||||
| 	"Import Documents Mapping": "ایمپورت نگاشت اسناد", | ||||
| 	"Import Modelfiles": "ایمپورت فایلهای مدل", | ||||
| 	"Import Prompts": "ایمپورت پرامپتها", | ||||
| 	"Import Modelfiles": "ایمپورت فایل\u200cهای مدل", | ||||
| 	"Import Prompts": "ایمپورت پرامپت\u200cها", | ||||
| 	"Include `--api` flag when running stable-diffusion-webui": "فلگ `--api` را هنکام اجرای stable-diffusion-webui استفاده کنید.", | ||||
| 	"Interface": "رابط", | ||||
| 	"{{item}} not provided": "{{item}} ارائه نشده است", | ||||
| 	"join our Discord for help.": "برای کمک به دیسکورد ما بپیوندید.", | ||||
| 	"JSON": "JSON", | ||||
| 	"JWT Expiration": "JWT انقضای", | ||||
|  | @ -118,80 +159,90 @@ | |||
| 	"LiteLLM API Base URL": "LiteLLM API Base URL", | ||||
| 	"LiteLLM API Key": "LiteLLM API Key", | ||||
| 	"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", | ||||
| 	"Make sure to enclose them with": "مطمئن شوید که آنها را با این محصور کنید:", | ||||
| 	"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": "حداکثر توکن", | ||||
| 	"Mirostat Eta": "Mirostat Eta", | ||||
| 	"Maximum of 3 models can be downloaded simultaneously. Please try again later.": "حداکثر 3 مدل را می توان به طور همزمان دانلود کرد. لطفاً بعداً دوباره امتحان کنید.", | ||||
| 	"Mirostat": "Mirostat", | ||||
| 	"Mirostat Eta": "Mirostat Eta", | ||||
| 	"Mirostat Tau": "Mirostat Tau", | ||||
| 	"MMMM DD, YYYY": "MMMM DD, YYYY", | ||||
| 	"Model '{{modelName}}' has been successfully downloaded.": "مدل '{{modelName}}' با موفقیت دانلود شد.", | ||||
| 	"Modelfile Advanced Settings": "تنظیمات پیشرفته فایلمدل", | ||||
| 	"Modelfile Content": "محتویات فایلمدل", | ||||
| 	"Modelfile": "فایل مدل", | ||||
| 	"Modelfiles": "فایلهای مدل", | ||||
| 	"Model '{{modelTag}}' is already in queue for downloading.": "مدل '{{modelTag}}' در حال حاضر در صف برای دانلود است.", | ||||
| 	"Model {{modelId}} not found": "مدل {{modelId}} یافت نشد", | ||||
| 	"Model {{modelName}} already exists.": "مدل {{modelName}} در حال حاضر وجود دارد.", | ||||
| 	"Model '{{modelTag}}' is already in queue for downloading.": "مدل '{{modelTag}}' در حال حاضر در صف برای دانلود است.", | ||||
| 	"{{modelName}} is thinking...": "{{modelName}} در حال فکر کردن است...", | ||||
| 	"Model Name": "نام مدل", | ||||
| 	"Model not selected": "مدل انتخاب نشده", | ||||
| 	"Models": "مدلها", | ||||
| 	"Model Tag Name": "نام تگ مدل", | ||||
| 	"Modelfile": "فایل مدل", | ||||
| 	"Modelfile Advanced Settings": "تنظیمات پیشرفته فایل\u200cمدل", | ||||
| 	"Modelfiles": "فایل\u200cهای مدل", | ||||
| 	"Models": "مدل\u200cها", | ||||
| 	"My Documents": "اسناد من", | ||||
| 	"My Modelfiles": "فایلهای مدل من", | ||||
| 	"My Prompts": "پرامپتهای من", | ||||
| 	"My Modelfiles": "فایل\u200cهای مدل من", | ||||
| 	"My Prompts": "پرامپت\u200cهای من", | ||||
| 	"Name": "نام", | ||||
| 	"Name Tag": "نام تگ", | ||||
| 	"Name your modelfile": "", | ||||
| 	"New Chat": "گپ جدید", | ||||
| 	"New Password": "رمز عبور جدید", | ||||
| 	"Desktop Notifications": "اعلان", | ||||
| 	"Not sure what to add?": "مطمئن نیستید چه چیزی را اضافه کنید؟", | ||||
| 	"Not sure what to write? Switch to": "مطمئن نیستید چه بنویسید؟ تغییر به", | ||||
| 	"Off": "خاموش", | ||||
| 	"Okay, Let's Go!": "باشه، بزن بریم!", | ||||
| 	"Ollama API URL": "اولاما API URL", | ||||
| 	"Ollama Version": "نسخه Ollama", | ||||
| 	"Only alphanumeric characters and hyphens are allowed in the command string.": "فقط کاراکترهای الفبایی و خط فاصله در رشته فرمان مجاز هستند.", | ||||
| 	"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.": "اوه! فایل های شما هنوز در فر پردازش هستند. ما آنها را کامل می پزیم. لطفا صبور باشید، به محض آماده شدن به شما اطلاع خواهیم داد.", | ||||
| 	"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 new chat": "باز کردن گپ جدید", | ||||
| 	"OpenAI API": "OpenAI API", | ||||
| 	"or": "روشن", | ||||
| 	"Parameters": "پارامترها", | ||||
| 	"Password": "رمز عبور", | ||||
| 	"pending": "Pending", | ||||
| 	"Pending": "", | ||||
| 	"Permission denied when accessing microphone: {{error}}": "هنگام دسترسی به میکروفون، اجازه داده نشد: {{error}}", | ||||
| 	"Playground": "زمین بازی", | ||||
| 	"Profile": "پروفایل", | ||||
| 	"Prompt Content": "محتویات پرامپت", | ||||
| 	"Prompts": "پرامپتها", | ||||
| 	"Prompt suggestions": "پیشنهادات پرامپت", | ||||
| 	"Prompts": "پرامپت\u200cها", | ||||
| 	"Pull a model from Ollama.com": "دریافت یک مدل از Ollama.com", | ||||
| 	"Pull Progress": "پیشرفت دریافت", | ||||
| 	"RAG Template": "RAG الگوی", | ||||
| 	"Raw Format": "فرمت خام", | ||||
| 	"Record voice": "ضبط صدا", | ||||
| 	"Redirecting you to OpenWebUI Community": "در حال هدایت به OpenWebUI Community", | ||||
| 	"Release Notes": "یادداشتهای انتشار", | ||||
| 	"Release Notes": "یادداشت\u200cهای انتشار", | ||||
| 	"Repeat Last N": "Repeat Last N", | ||||
| 	"Repeat Penalty": "Repeat Penalty", | ||||
| 	"Request Mode": "حالت درخواست", | ||||
| 	"Reset Vector Storage": "بازنشانی ذخیره سازی برداری", | ||||
| 	"Response AutoCopy to Clipboard": "کپی خودکار پاسخ به کلیپ بورد", | ||||
| 	"Role": "نقش", | ||||
| 	"Ros\u00e9 Pine Dawn": "Ros\u00e9 Pine Dawn", | ||||
| 	"Ros\u00e9 Pine": "Ros\u00e9 Pine", | ||||
| 	"Save & Create": "ذخیره و ایجاد", | ||||
| 	"Rosé Pine": "Rosé Pine", | ||||
| 	"Rosé Pine Dawn": "Rosé Pine Dawn", | ||||
| 	"Save": "ذخیره", | ||||
| 	"Save & Create": "ذخیره و ایجاد", | ||||
| 	"Save & Submit": "ذخیره و ارسال", | ||||
| 	"Save & Update": "ذخیره و بهروزرسانی", | ||||
| 	"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": "ذخیره گزارشهای چت مستقیماً در حافظه مرورگر شما دیگر پشتیبانی نمیشود. لطفاً با کلیک بر روی دکمه زیر، چند لحظه برای دانلود و حذف گزارش های چت خود وقت بگذارید. نگران نباشید، شما به راحتی می توانید گزارش های چت خود را از طریق بکند دوباره وارد کنید", | ||||
| 	"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": "ذخیره گزارش\u200cهای چت مستقیماً در حافظه مرورگر شما دیگر پشتیبانی نمی\u200cشود. لطفاً با کلیک بر روی دکمه زیر، چند لحظه برای دانلود و حذف گزارش های چت خود وقت بگذارید. نگران نباشید، شما به راحتی می توانید گزارش های چت خود را از طریق بکند دوباره وارد کنید", | ||||
| 	"Scan": "اسکن", | ||||
| 	"Scan complete!": "اسکن کامل شد!", | ||||
| 	"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", | ||||
| 	"See readme.md for instructions": "برای مشاهده دستورالعملها به readme.md مراجعه کنید", | ||||
| 	"Select a model": "انتخاب یک مدل", | ||||
| 	"Send a Messsage": "ارسال یک پیام", | ||||
| 	"Send message": "ارسال پیام", | ||||
|  | @ -199,129 +250,71 @@ | |||
| 	"Set as default": "تنظیم به عنوان پیشفرض", | ||||
| 	"Set Default Model": "تنظیم مدل پیش فرض", | ||||
| 	"Set Image Size": "تنظیم اندازه تصویر", | ||||
| 	"Set Steps": "تنظیم گامها", | ||||
| 	"Settings": "تنظیمات", | ||||
| 	"Set Steps": "تنظیم گام\u200cها", | ||||
| 	"Set Title Auto-Generation Model": "تنظیم مدل تولید خودکار عنوان", | ||||
| 	"Set Voice": "تنظیم صدا", | ||||
| 	"Settings": "تنظیمات", | ||||
| 	"Share to OpenWebUI Community": "اشتراک گذاری با OpenWebUI Community", | ||||
| 	"short-summary": "خلاصه کوتاه", | ||||
| 	"Show": "نمایش", | ||||
| 	"Show shortcuts": "نمایش میانبرها", | ||||
| 	"sidebar": "نوار کناری", | ||||
| 	"Sign in": "ورود", | ||||
| 	"Sign Out": "خروج", | ||||
| 	"SpeechRecognition API is not supported in this browser.": "API تشخیص گفتار در این مرورگر پشتیبانی نمی شود.", | ||||
| 	"Sign up": "ثبت نام", | ||||
| 	"Speech recognition error: {{error}}": "خطای تشخیص گفتار: {{error}}", | ||||
| 	"Speech-to-Text Engine": "موتور گفتار به متن", | ||||
| 	"SpeechRecognition API is not supported in this browser.": "API تشخیص گفتار در این مرورگر پشتیبانی نمی شود.", | ||||
| 	"Stop Sequence": "توالی توقف", | ||||
| 	"STT Settings": "STT تنظیمات", | ||||
| 	"Successfully updated.": "با موفقیت به روز شد", | ||||
| 	"Submit": "ارسال", | ||||
| 	"Success": "موفقیت", | ||||
| 	"Successfully updated": "", | ||||
| 	"Successfully updated.": "با موفقیت به روز شد", | ||||
| 	"Sync All": "همگام سازی همه", | ||||
| 	"System": "سیستم", | ||||
| 	"System Prompt": "پرامپت سیستم", | ||||
| 	"Tags": "تگها", | ||||
| 	"Tags": "تگ\u200cها", | ||||
| 	"Temperature": "دما", | ||||
| 	"Template": "الگو", | ||||
| 	"Text Completion": "تکمیل متن", | ||||
| 	"Text-to-Speech Engine": "موتور تبدیل متن به گفتار", | ||||
| 	"Tfs Z": "Tfs Z", | ||||
| 	"Theme": "قالب", | ||||
| 	"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 Generation Prompt": "پرامپت تولید عنوان", | ||||
| 	"Title": "عنوان", | ||||
| 	"to": "به", | ||||
| 	"To access the available model names for downloading,": "برای دسترسی به نام مدل های موجود برای دانلود،", | ||||
| 	"Toggle settings": "نمایش/عدم نمایش تنظیمات", | ||||
| 	"Toggle sidebar": "نمایش/عدم نمایش نوار کناری", | ||||
| 	"Top K": "Top K", | ||||
| 	"Top P": "Top P", | ||||
| 	"Trouble accessing Ollama?": "در دسترسی به اولاما مشکل دارید؟", | ||||
| 	"TTS Settings": "تنظیمات TTS", | ||||
| 	"Uh-oh! There was an issue connecting to {{provider}}.": "اوه اوه! مشکلی در اتصال به {{provider}} وجود داشت.", | ||||
| 	"Upload a GGUF model": "بارگذاری یک مدل GGUF", | ||||
| 	"Upload files": "بارگذاری فایلها", | ||||
| 	"Upload Progress": "میزان پیشرفت بارگذاری", | ||||
| 	"URL Mode": "حالت URL", | ||||
| 	"Unknown File Type '{{file_type}}', but accepting and treating as plain text": "", | ||||
| 	"Upload files": "بارگذاری فایل\u200cها", | ||||
| 	"Use '#' in the prompt input to load and select your documents.": "در پرامپت از '#' برای لود و انتخاب اسناد خود استفاده کنید.", | ||||
| 	"User": "", | ||||
| 	"User Permissions": "مجوزهای کاربر", | ||||
| 	"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...": "در حال بررسی برای بهروزرسانی..", | ||||
| 	"What’s 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": "استفاده کنید", | ||||
| 	"Valid time units:": "واحدهای زمانی معتبر:", | ||||
| 	"variable": "متغیر", | ||||
| 	"variable to have them replaced with clipboard content.": "متغیر برای جایگزینی آنها با محتوای کلیپ بورد.", | ||||
| 	"short-summary": "خلاصه کوتاه", | ||||
| 	"Add a short title for this prompt": "یک عنوان کوتاه برای این درخواست اضافه کنید", | ||||
| 	"Open": "باز", | ||||
| 	"Close": "بسته", | ||||
| 	"sidebar": "نوار کناری", | ||||
| 	"Playground": "زمین بازی", | ||||
| 	"Submit": "ارسال", | ||||
| 	"(Beta)": "(بتا)", | ||||
| 	"Text Completion": "تکمیل متن", | ||||
| 	"Chat": "گپ", | ||||
| 	"Web": "وب", | ||||
| 	"WebUI Add-ons": "WebUI افزونه\u200cهای", | ||||
| 	"WebUI Settings": "تنظیمات WebUI", | ||||
| 	"What’s New in": "موارد جدید در", | ||||
| 	"When history is turned off, new chats on this browser won't appear in your history on any of your devices.": "وقتی سابقه خاموش است، چت\u200cهای جدید در این مرورگر در سابقه شما در هیچ یک از دستگاه\u200cهایتان ظاهر نمی\u200cشوند.", | ||||
| 	"Whisper (Local)": "ویسپر (محلی)", | ||||
| 	"Write a prompt suggestion (e.g. Who are you?)": "یک پیشنهاد پرامپت بنویسید (مثلاً شما کی هستید؟)", | ||||
| 	"Write a summary in 50 words that summarizes [topic or keyword].": "خلاصه ای در 50 کلمه بنویسید که [موضوع یا کلمه کلیدی] را خلاصه کند.", | ||||
| 	"You": "شما", | ||||
| 	"You're a helpful assistant.": "تو یک دستیار سودمند هستی.", | ||||
| 	"System": "سیستم", | ||||
| 	"assistant": "دستیار", | ||||
| 	"Add message": "اضافه کردن پیغام", | ||||
| 	"Enter a user message here": "در اینجا یک پیام کاربر را وارد کنید", | ||||
| 	"Enter an assistant message here": "در اینجا یک پیام دستیار را وارد کنید", | ||||
| 	"Drop any files here to add to the conversation": "هر فایلی را اینجا رها کنید تا به مکالمه اضافه شود", | ||||
| 	"You": "شما" | ||||
| 	"You're now logged in.": "شما اکنون وارد شده\u200cاید." | ||||
| } | ||||
|  |  | |||
|  | @ -18,10 +18,9 @@ | |||
| 	"Add message": "Ajouter un message", | ||||
| 	"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.", | ||||
| 	"admin": "Administrateur", | ||||
| 	"Admin": "", | ||||
| 	"Admin Panel": "Panneau d'administration", | ||||
| 	"Admin Settings": "Paramètres d'administration", | ||||
| 	"Advanced": "Avancé", | ||||
| 	"Advanced Model Params": "Paramètres avancés du modèle", | ||||
| 	"Advanced Parameters": "Paramètres avancés", | ||||
| 	"all": "tous", | ||||
|  | @ -35,7 +34,6 @@ | |||
| 	"API Key": "Clé API", | ||||
| 	"API RPM": "RPM API", | ||||
| 	"are allowed - Activate this command by typing": "sont autorisés - Activez cette commande en tapant", | ||||
| 	"assistant": "Assistant", | ||||
| 	"Audio": "Audio", | ||||
| 	"Auto-playback response": "Réponse en lecture automatique", | ||||
| 	"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 response": "Copier la dernière réponse", | ||||
| 	"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 Account": "Créer un compte", | ||||
| 	"Created at": "Créé le", | ||||
|  | @ -87,7 +86,6 @@ | |||
| 	"Default (Web API)": "Par défaut (API Web)", | ||||
| 	"Default model updated": "Modèle par défaut mis à jour", | ||||
| 	"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", | ||||
| 	"delete": "supprimer", | ||||
| 	"Delete a model": "Supprimer un modèle", | ||||
|  | @ -119,21 +117,17 @@ | |||
| 	"Enable Chat History": "Activer l'historique du chat", | ||||
| 	"Enable New Sign Ups": "Activer les nouvelles inscriptions", | ||||
| 	"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 stop sequence": "Entrez la séquence d'arrêt", | ||||
| 	"Enter Your Email": "Entrez votre email", | ||||
| 	"Enter Your Full Name": "Entrez votre nom complet", | ||||
| 	"Enter Your Password": "Entrez votre mot de passe", | ||||
| 	"Experimental": "Expérimental", | ||||
| 	"Export All Chats (All Users)": "Exporter tous les chats (tous les utilisateurs)", | ||||
| 	"Export Chats": "Exporter les chats", | ||||
| 	"Export Documents Mapping": "Exporter la correspondance des documents", | ||||
| 	"Export Modelfiles": "Exporter les fichiers de modèle", | ||||
| 	"Export Prompts": "Exporter les prompts", | ||||
| 	"Failed to read clipboard contents": "Échec de la lecture du contenu du presse-papiers", | ||||
| 	"File Mode": "Mode fichier", | ||||
| 	"File not found.": "Fichier non trouvé.", | ||||
| 	"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 :", | ||||
|  | @ -185,7 +179,6 @@ | |||
| 	"Model Tag Name": "Nom de tag du modèle", | ||||
| 	"Modelfile": "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", | ||||
| 	"Models": "Modèles", | ||||
| 	"My Documents": "Mes documents", | ||||
|  | @ -193,19 +186,20 @@ | |||
| 	"My Prompts": "Mes prompts", | ||||
| 	"Name": "Nom", | ||||
| 	"Name Tag": "Tag de nom", | ||||
| 	"Name your Modelfile": "Nommez votre fichier de modèle", | ||||
| 	"Name your modelfile": "", | ||||
| 	"New Chat": "Nouveau chat", | ||||
| 	"New Password": "Nouveau mot de passe", | ||||
| 	"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", | ||||
| 	"Off": "Désactivé", | ||||
| 	"Okay, Let's Go!": "D'accord, allons-y !", | ||||
| 	"Ollama API URL": "URL de l'API Ollama", | ||||
| 	"Ollama Version": "Version Ollama", | ||||
| 	"On": "Activé", | ||||
| 	"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.", | ||||
| 	"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 AI": "Open AI", | ||||
| 	"Open new chat": "Ouvrir un nouveau chat", | ||||
|  | @ -213,7 +207,7 @@ | |||
| 	"or": "ou", | ||||
| 	"Parameters": "Paramètres", | ||||
| 	"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}}", | ||||
| 	"Playground": "Aire de jeu", | ||||
| 	"Profile": "Profil", | ||||
|  | @ -275,6 +269,7 @@ | |||
| 	"STT Settings": "Paramètres STT", | ||||
| 	"Submit": "Soumettre", | ||||
| 	"Success": "Succès", | ||||
| 	"Successfully updated": "", | ||||
| 	"Successfully updated.": "Mis à jour avec succès.", | ||||
| 	"Sync All": "Synchroniser tout", | ||||
| 	"System": "Système", | ||||
|  | @ -288,12 +283,12 @@ | |||
| 	"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 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 Auto-Generation": "Génération automatique de titre", | ||||
| 	"Title Generation Prompt": "Prompt de génération de titre", | ||||
| 	"to": "à", | ||||
| 	"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 sidebar": "Basculer la barre latérale", | ||||
| 	"Top K": "Top K", | ||||
|  | @ -301,12 +296,10 @@ | |||
| 	"Trouble accessing Ollama?": "Problèmes d'accès à Ollama ?", | ||||
| 	"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}}.", | ||||
| 	"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 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.", | ||||
| 	"user": "Utilisateur", | ||||
| 	"User": "", | ||||
| 	"User Permissions": "Permissions d'utilisateur", | ||||
| 	"Users": "Utilisateurs", | ||||
| 	"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.", | ||||
| 	"Whisper (Local)": "Chuchotement (Local)", | ||||
| 	"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 now logged in.": "Vous êtes maintenant connecté." | ||||
| } | ||||
|  |  | |||
|  | @ -1,5 +1,6 @@ | |||
| { | ||||
| 	"'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`)", | ||||
| 	"{{item}} not provided": "{{item}} не надано", | ||||
| 	"{{modelName}} is thinking...": "{{modelName}} думає...", | ||||
|  | @ -14,12 +15,12 @@ | |||
| 	"Add Docs": "Додати документи", | ||||
| 	"Add Files": "Додати файли", | ||||
| 	"Add LiteLLM Model": "Додати модель LiteLLM", | ||||
| 	"Add message": "Додати повідомлення", | ||||
| 	"add tags": "додати теги", | ||||
| 	"Adjusting these settings will apply changes universally to all users.": "Корегування цих налаштувань застосовуватиме зміни для всіх користувачів.", | ||||
| 	"admin": "адміністратор", | ||||
| 	"Admin": "Адмін", | ||||
| 	"Admin Panel": "Панель адміністратора", | ||||
| 	"Admin Settings": "Налаштування адміністратора", | ||||
| 	"Advanced": "Розширені", | ||||
| 	"Advanced Model Params": "Розширені параметри моделі", | ||||
| 	"Advanced Parameters": "Розширені параметри", | ||||
| 	"all": "всі", | ||||
|  | @ -43,6 +44,7 @@ | |||
| 	"Cancel": "Скасувати", | ||||
| 	"Categories": "Категорії", | ||||
| 	"Change Password": "Змінити пароль", | ||||
| 	"Chat": "Чат", | ||||
| 	"Chat History": "Історія чату", | ||||
| 	"Chat History is off for this browser.": "Історія чату вимкнена для цього браузера.", | ||||
| 	"Chats": "Чати", | ||||
|  | @ -68,6 +70,7 @@ | |||
| 	"Copy last code block": "Копіювати останній блок коду", | ||||
| 	"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':": "Створіть стислий, 3-5 слів заголовок для наступного запиту, суворо дотримуючись 3-5 слів обмеження та уникаючи використання слова 'заголовок':", | ||||
| 	"Create a modelfile": "Створити modelfile", | ||||
| 	"Create Account": "Створити обліковий запис", | ||||
| 	"Created at": "Створено", | ||||
|  | @ -83,7 +86,6 @@ | |||
| 	"Default (Web API)": "За замовчуванням (Web API)", | ||||
| 	"Default model updated": "Модель за замовчуванням оновлено", | ||||
| 	"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": "Роль користувача за замовчуванням", | ||||
| 	"delete": "видалити", | ||||
| 	"Delete a model": "Видалити модель", | ||||
|  | @ -107,6 +109,7 @@ | |||
| 	"Don't have an account?": "Немає облікового запису?", | ||||
| 	"Download as a File": "Завантажити як файл", | ||||
| 	"Download Database": "Завантажити базу даних", | ||||
| 	"Drop any files here to add to the conversation": "Перетягніть сюди файли, щоб додати до розмови", | ||||
| 	"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "напр. '30s','10m'. Дійсні одиниці часу: 'с', 'хв', 'г'.", | ||||
| 	"Edit Doc": "Редагувати документ", | ||||
| 	"Edit User": "Редагувати користувача", | ||||
|  | @ -119,15 +122,13 @@ | |||
| 	"Enter Your Email": "Введіть вашу електронну пошту", | ||||
| 	"Enter Your Full Name": "Введіть ваше повне ім'я", | ||||
| 	"Enter Your Password": "Введіть ваш пароль", | ||||
| 	"Experimental": "Експериментальний", | ||||
| 	"Export All Chats (All Users)": "Експортувати всі чати (всі користувачі)", | ||||
| 	"Export Chats": "Експортувати чати", | ||||
| 	"Export Documents Mapping": "Експортувати відображення документів", | ||||
| 	"Export Modelfiles": "Експортувати modelfiles", | ||||
| 	"Export Prompts": "Експортувати запити", | ||||
| 	"Failed to read clipboard contents": "Не вдалося прочитати вміст буфера обміну", | ||||
| 	"File Mode": "Режим файлу", | ||||
| 	"File not found.`": "Файл не знайдено.`", | ||||
| 	"File not found.": "Файл не знайдено.", | ||||
| 	"Focus chat input": "Фокус вводу чату", | ||||
| 	"Format your variables using square brackets like this:": "Форматуйте свої змінні квадратними дужками так:", | ||||
| 	"From (Base Model)": "Від (базова модель)", | ||||
|  | @ -169,36 +170,36 @@ | |||
| 	"Mirostat Eta": "Mirostat Eta", | ||||
| 	"Mirostat Tau": "Mirostat Tau", | ||||
| 	"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 {{modelId}} not found": "Модель {{modelId}} не знайдено", | ||||
| 	"Model {{modelName}} already exists.": "Модель {{modelName}} вже існує.", | ||||
| 	"Model Name": "Назва моделі", | ||||
| 	"Model not selected": "Модель не вибрана", | ||||
| 	"Model Tag Name": "Ім'я тегу моделі", | ||||
| 	"Modelfile": "Modelfile", | ||||
| 	"Modelfile": "Файли моделі", | ||||
| 	"Modelfile Advanced Settings": "Розширені налаштування modelfile", | ||||
| 	"Modelfile Content": "Зміст modelfile", | ||||
| 	"Modelfiles": "Modelfiles", | ||||
| 	"Modelfiles": "Файли моделей", | ||||
| 	"Models": "Моделі", | ||||
| 	"My Documents": "Мої документи", | ||||
| 	"My Modelfiles": "Мої modelfiles", | ||||
| 	"My Modelfiles": "Мої файли моделей", | ||||
| 	"My Prompts": "Мої запити", | ||||
| 	"Name": "Назва", | ||||
| 	"Name Tag": "Назва тегу", | ||||
| 	"Name your Modelfile": "Назвіть свій modelfile", | ||||
| 	"Name your modelfile": "Назвіть свій файл моделі", | ||||
| 	"New Chat": "Новий чат", | ||||
| 	"New Password": "Новий пароль", | ||||
| 	"Not sure what to add?": "Не впевнений, що додати?", | ||||
| 	"Not sure what to write? Switch to": "Не впевнений, що писати? Переключитися на", | ||||
| 	"Off": "Вимк", | ||||
| 	"Okay, Let's Go!": "Гаразд, давайте почнемо!", | ||||
| 	"Ollama API URL": "URL API Ollama", | ||||
| 	"Ollama Version": "Версія Ollama", | ||||
| 	"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! 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 AI": "Open AI", | ||||
| 	"Open new chat": "Відкрити новий чат", | ||||
|  | @ -206,8 +207,9 @@ | |||
| 	"or": "або", | ||||
| 	"Parameters": "Параметри", | ||||
| 	"Password": "Пароль", | ||||
| 	"pending": "очікування", | ||||
| 	"Pending": "На розгляді", | ||||
| 	"Permission denied when accessing microphone: {{error}}": "Доступ до мікрофона заборонено: {{error}}", | ||||
| 	"Playground": "Майданчик", | ||||
| 	"Profile": "Профіль", | ||||
| 	"Prompt Content": "Зміст запиту", | ||||
| 	"Prompt suggestions": "Швидкі запити", | ||||
|  | @ -265,38 +267,39 @@ | |||
| 	"SpeechRecognition API is not supported in this browser.": "SpeechRecognition API не підтримується в цьому браузері.", | ||||
| 	"Stop Sequence": "Символ зупинки", | ||||
| 	"STT Settings": "Налаштування STT", | ||||
| 	"Submit": "Надіслати", | ||||
| 	"Success": "Успіх", | ||||
| 	"Successfully updated": "Успішно оновлено", | ||||
| 	"Successfully updated.": "Успішно оновлено.", | ||||
| 	"Sync All": "Синхронізувати все", | ||||
| 	"System": "Система", | ||||
| 	"System Prompt": "Системний запит", | ||||
| 	"Tags": "Теги", | ||||
| 	"Temperature": "Температура", | ||||
| 	"Template": "Шаблон", | ||||
| 	"Text Completion": "Завершення тексту", | ||||
| 	"Text-to-Speech Engine": "Система синтезу мови", | ||||
| 	"Tfs Z": "Tfs Z", | ||||
| 	"Theme": "Тема", | ||||
| 	"This ensures that your valuable conversations are securely saved to your backend database. Thank you!": "Це забезпечує збереження ваших цінних розмов у безпечному бекенд-сховищі. Дякуємо!", | ||||
| 	"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 Auto-Generation": "Автогенерація заголовків", | ||||
| 	"Title Generation Prompt": "Запит на генерування заголовків", | ||||
| 	"to": "до", | ||||
| 	"To access the available model names for downloading,": "Щоб отримати доступ до назв доступних для завантаження моделей,", | ||||
| 	"to chat input.": "до введення чату.", | ||||
| 	"Toggle settings": "Переключити налаштування", | ||||
| 	"Toggle sidebar": "Переключити бокову панель", | ||||
| 	"Top K": "Вершина K", | ||||
| 	"Top P": "Вершина P", | ||||
| 	"Top K": "Top K", | ||||
| 	"Top P": "Top P", | ||||
| 	"Trouble accessing Ollama?": "Проблеми з доступом до Ollama?", | ||||
| 	"TTS Settings": "Налаштування TTS", | ||||
| 	"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 Progress": "Прогрес завантаження", | ||||
| 	"URL Mode": "Режим URL", | ||||
| 	"Use '#' in the prompt input to load and select your documents.": "Використовуйте '#' у введенні запиту для завантаження та вибору ваших документів.", | ||||
| 	"user": "користувач", | ||||
| 	"User": "Користувач", | ||||
| 	"User Permissions": "Дозволи користувача", | ||||
| 	"Users": "Користувачі", | ||||
| 	"Utilize": "Використовувати", | ||||
|  | @ -308,19 +311,10 @@ | |||
| 	"WebUI Settings": "Налаштування WebUI", | ||||
| 	"What’s New in": "Що нового в", | ||||
| 	"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 summary in 50 words that summarizes [topic or keyword]": "Напишіть стислий зміст у 50 слів, який узагальнює [тему або ключове слово]", | ||||
| 	"You're now logged in.": "Ви увійшли в систему.", | ||||
| 	"Write a summary in 50 words that summarizes [topic or keyword].": "Напишіть стислий зміст у 50 слів, який узагальнює [тема або ключове слово].", | ||||
| 	"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.": "Ви корисний асистент.", | ||||
| 	"assistant": "асистент", | ||||
| 	"Submit": "Надіслати", | ||||
| 	"Chat": "Чат", | ||||
| 	"Text Completion": "Завершення тексту" | ||||
| 	"You're now logged in.": "Ви увійшли в систему." | ||||
| } | ||||
|  |  | |||
|  | @ -99,14 +99,11 @@ | |||
| 					if (localDBChats.length === 0) { | ||||
| 						await deleteDB('Chats'); | ||||
| 					} | ||||
| 
 | ||||
| 					console.log('localdb', localDBChats); | ||||
| 				} | ||||
| 
 | ||||
| 				console.log(DB); | ||||
| 			} catch (error) { | ||||
| 				// IndexedDB Not Found | ||||
| 				console.log('IDB Not Found'); | ||||
| 			} | ||||
| 
 | ||||
| 			console.log(); | ||||
|  |  | |||
|  | @ -344,7 +344,7 @@ | |||
| 						content: $settings.system | ||||
| 				  } | ||||
| 				: undefined, | ||||
| 			...messages.filter((message) => !message.deleted) | ||||
| 			...messages | ||||
| 		] | ||||
| 			.filter((message) => message) | ||||
| 			.map((message, idx, arr) => ({ | ||||
|  | @ -558,7 +558,7 @@ | |||
| 								content: $settings.system | ||||
| 						  } | ||||
| 						: undefined, | ||||
| 					...messages.filter((message) => !message.deleted) | ||||
| 					...messages | ||||
| 				] | ||||
| 					.filter((message) => message) | ||||
| 					.map((message, idx, arr) => ({ | ||||
|  |  | |||
|  | @ -354,7 +354,7 @@ | |||
| 						content: $settings.system | ||||
| 				  } | ||||
| 				: undefined, | ||||
| 			...messages.filter((message) => !message.deleted) | ||||
| 			...messages | ||||
| 		] | ||||
| 			.filter((message) => message) | ||||
| 			.map((message, idx, arr) => ({ | ||||
|  | @ -568,7 +568,7 @@ | |||
| 								content: $settings.system | ||||
| 						  } | ||||
| 						: undefined, | ||||
| 					...messages.filter((message) => !message.deleted) | ||||
| 					...messages | ||||
| 				] | ||||
| 					.filter((message) => message) | ||||
| 					.map((message, idx, arr) => ({ | ||||
|  |  | |||
|  | @ -562,9 +562,9 @@ SYSTEM """${system}"""`.replace(/^\s*\n/gm, ''); | |||
| 								}} | ||||
| 							> | ||||
| 								{#if advanced} | ||||
| 									<span class="ml-2 self-center">{$i18n.t(' Custom ')}</span> | ||||
| 									<span class="ml-2 self-center">{$i18n.t('Custom')}</span> | ||||
| 								{:else} | ||||
| 									<span class="ml-2 self-center">{$i18n.t(' Default ')}</span> | ||||
| 									<span class="ml-2 self-center">{$i18n.t('Default')}</span> | ||||
| 								{/if} | ||||
| 							</button> | ||||
| 						</div> | ||||
|  |  | |||
|  | @ -181,7 +181,7 @@ | |||
| 							<textarea | ||||
| 								class="px-3 py-1.5 text-sm w-full bg-transparent border dark:border-gray-600 outline-none rounded-lg" | ||||
| 								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" | ||||
| 								bind:value={content} | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue