forked from open-webui/open-webui
		
	main #2
					 41 changed files with 1104 additions and 814 deletions
				
			
		|  | @ -7,7 +7,6 @@ node_modules | ||||||
| /package | /package | ||||||
| .env | .env | ||||||
| .env.* | .env.* | ||||||
| !.env.example |  | ||||||
| vite.config.js.timestamp-* | vite.config.js.timestamp-* | ||||||
| vite.config.ts.timestamp-* | vite.config.ts.timestamp-* | ||||||
| __pycache__ | __pycache__ | ||||||
|  |  | ||||||
|  | @ -1,6 +1,6 @@ | ||||||
| # Ollama URL for the backend to connect | # Ollama URL for the backend to connect | ||||||
| # The path '/ollama/api' will be redirected to the specified backend URL | # The path '/ollama' will be redirected to the specified backend URL | ||||||
| OLLAMA_API_BASE_URL='http://localhost:11434/api' | OLLAMA_BASE_URL='http://localhost:11434' | ||||||
| 
 | 
 | ||||||
| OPENAI_API_BASE_URL='' | OPENAI_API_BASE_URL='' | ||||||
| OPENAI_API_KEY='' | OPENAI_API_KEY='' | ||||||
|  |  | ||||||
							
								
								
									
										14
									
								
								.github/workflows/build-release.yml
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										14
									
								
								.github/workflows/build-release.yml
									
										
									
									
										vendored
									
									
								
							|  | @ -19,24 +19,34 @@ jobs: | ||||||
|           echo "No changes to package.json" |           echo "No changes to package.json" | ||||||
|           exit 1 |           exit 1 | ||||||
|         } |         } | ||||||
| 
 |      | ||||||
|     - name: Get version number from package.json |     - name: Get version number from package.json | ||||||
|       id: get_version |       id: get_version | ||||||
|       run: | |       run: | | ||||||
|         VERSION=$(jq -r '.version' package.json) |         VERSION=$(jq -r '.version' package.json) | ||||||
|         echo "::set-output name=version::$VERSION" |         echo "::set-output name=version::$VERSION" | ||||||
| 
 | 
 | ||||||
|  |     - name: Extract latest CHANGELOG entry | ||||||
|  |       id: changelog | ||||||
|  |       run: | | ||||||
|  |         CHANGELOG_CONTENT=$(awk '/^## \[/{n++} n==1' CHANGELOG.md) | ||||||
|  |         echo "CHANGELOG_CONTENT<<EOF"  | ||||||
|  |         echo "$CHANGELOG_CONTENT" | ||||||
|  |         echo "EOF"  | ||||||
|  |         echo "::set-output name=content::${CHANGELOG_CONTENT}" | ||||||
|  | 
 | ||||||
|     - name: Create GitHub release |     - name: Create GitHub release | ||||||
|       uses: actions/github-script@v5 |       uses: actions/github-script@v5 | ||||||
|       with: |       with: | ||||||
|         github-token: ${{ secrets.GITHUB_TOKEN }} |         github-token: ${{ secrets.GITHUB_TOKEN }} | ||||||
|         script: | |         script: | | ||||||
|  |           const changelog = `${{ steps.changelog.outputs.content }}`; | ||||||
|           const release = await github.rest.repos.createRelease({ |           const release = await github.rest.repos.createRelease({ | ||||||
|             owner: context.repo.owner, |             owner: context.repo.owner, | ||||||
|             repo: context.repo.repo, |             repo: context.repo.repo, | ||||||
|             tag_name: `v${{ steps.get_version.outputs.version }}`, |             tag_name: `v${{ steps.get_version.outputs.version }}`, | ||||||
|             name: `v${{ steps.get_version.outputs.version }}`, |             name: `v${{ steps.get_version.outputs.version }}`, | ||||||
|             body: 'Automatically created new release', |             body: changelog, | ||||||
|           }) |           }) | ||||||
|           console.log(`Created release ${release.data.html_url}`) |           console.log(`Created release ${release.data.html_url}`) | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
							
								
								
									
										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/), | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), | ||||||
| and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). | ||||||
| 
 | 
 | ||||||
|  | ## [0.1.110] - 2024-03-06 | ||||||
|  | 
 | ||||||
|  | ### Added | ||||||
|  | 
 | ||||||
|  | - **🌐 Multiple OpenAI Servers Support**: Enjoy seamless integration with multiple OpenAI-compatible APIs, now supported natively. | ||||||
|  | 
 | ||||||
|  | ### Fixed | ||||||
|  | 
 | ||||||
|  | - **🔍 OCR Issue**: Resolved PDF parsing issue caused by OCR malfunction. | ||||||
|  | - **🚫 RAG Issue**: Fixed the RAG functionality, ensuring it operates smoothly. | ||||||
|  | - **📄 "Add Docs" Model Button**: Addressed the non-functional behavior of the "Add Docs" model button. | ||||||
|  | 
 | ||||||
|  | ## [0.1.109] - 2024-03-06 | ||||||
|  | 
 | ||||||
|  | ### Added | ||||||
|  | 
 | ||||||
|  | - **🔄 Multiple Ollama Servers Support**: Enjoy enhanced scalability and performance with support for multiple Ollama servers in a single WebUI. Load balancing features are now available, providing improved efficiency (#788, #278). | ||||||
|  | - **🔧 Support for Claude 3 and Gemini**: Responding to user requests, we've expanded our toolset to include Claude 3 and Gemini, offering a wider range of functionalities within our platform (#1064). | ||||||
|  | - **🔍 OCR Functionality for PDF Loader**: We've augmented our PDF loader with Optical Character Recognition (OCR) capabilities. Now, extract text from scanned documents and images within PDFs, broadening the scope of content processing (#1050). | ||||||
|  | 
 | ||||||
|  | ### Fixed | ||||||
|  | 
 | ||||||
|  | - **🛠️ RAG Collection**: Implemented a dynamic mechanism to recreate RAG collections, ensuring users have up-to-date and accurate data (#1031). | ||||||
|  | - **📝 User Agent Headers**: Fixed issue of RAG web requests being sent with empty user_agent headers, reducing rejections from certain websites. Realistic headers are now utilized for these requests (#1024). | ||||||
|  | - **⏹️ Playground Cancel Functionality**: Introducing a new "Cancel" option for stopping Ollama generation in the Playground, enhancing user control and usability (#1006). | ||||||
|  | - **🔤 Typographical Error in 'ASSISTANT' Field**: Corrected a typographical error in the 'ASSISTANT' field within the GGUF model upload template for accuracy and consistency (#1061). | ||||||
|  | 
 | ||||||
|  | ### Changed | ||||||
|  | 
 | ||||||
|  | - **🔄 Refactored Message Deletion Logic**: Streamlined message deletion process for improved efficiency and user experience, simplifying interactions within the platform (#1004). | ||||||
|  | - **⚠️ Deprecation of `OLLAMA_API_BASE_URL`**: Deprecated `OLLAMA_API_BASE_URL` environment variable; recommend using `OLLAMA_BASE_URL` instead. Refer to our documentation for further details. | ||||||
|  | 
 | ||||||
| ## [0.1.108] - 2024-03-02 | ## [0.1.108] - 2024-03-02 | ||||||
| 
 | 
 | ||||||
| ### Added | ### Added | ||||||
|  |  | ||||||
|  | @ -20,7 +20,7 @@ FROM python:3.11-slim-bookworm as base | ||||||
| ENV ENV=prod | ENV ENV=prod | ||||||
| ENV PORT "" | ENV PORT "" | ||||||
| 
 | 
 | ||||||
| ENV OLLAMA_API_BASE_URL "/ollama/api" | ENV OLLAMA_BASE_URL "/ollama" | ||||||
| 
 | 
 | ||||||
| ENV OPENAI_API_BASE_URL "" | ENV OPENAI_API_BASE_URL "" | ||||||
| ENV OPENAI_API_KEY "" | ENV OPENAI_API_KEY "" | ||||||
|  | @ -53,6 +53,8 @@ WORKDIR /app/backend | ||||||
| # install python dependencies | # install python dependencies | ||||||
| COPY ./backend/requirements.txt ./requirements.txt | COPY ./backend/requirements.txt ./requirements.txt | ||||||
| 
 | 
 | ||||||
|  | RUN apt-get update && apt-get install ffmpeg libsm6 libxext6  -y | ||||||
|  | 
 | ||||||
| RUN pip3 install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cpu --no-cache-dir | RUN pip3 install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cpu --no-cache-dir | ||||||
| RUN pip3 install -r requirements.txt --no-cache-dir | RUN pip3 install -r requirements.txt --no-cache-dir | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -95,10 +95,10 @@ Don't forget to explore our sibling project, [Open WebUI Community](https://open | ||||||
| 
 | 
 | ||||||
| - **If Ollama is on a Different Server**, use this command: | - **If Ollama is on a Different Server**, use this command: | ||||||
| 
 | 
 | ||||||
| - To connect to Ollama on another server, change the `OLLAMA_API_BASE_URL` to the server's URL: | - To connect to Ollama on another server, change the `OLLAMA_BASE_URL` to the server's URL: | ||||||
| 
 | 
 | ||||||
|   ```bash |   ```bash | ||||||
|   docker run -d -p 3000:8080 -e OLLAMA_API_BASE_URL=https://example.com/api -v open-webui:/app/backend/data --name open-webui --restart always ghcr.io/open-webui/open-webui:main |   docker run -d -p 3000:8080 -e OLLAMA_BASE_URL=https://example.com -v open-webui:/app/backend/data --name open-webui --restart always ghcr.io/open-webui/open-webui:main | ||||||
|   ``` |   ``` | ||||||
| 
 | 
 | ||||||
| - After installation, you can access Open WebUI at [http://localhost:3000](http://localhost:3000). Enjoy! 😄 | - After installation, you can access Open WebUI at [http://localhost:3000](http://localhost:3000). Enjoy! 😄 | ||||||
|  | @ -110,7 +110,7 @@ If you're experiencing connection issues, it’s often due to the WebUI docker c | ||||||
| **Example Docker Command**: | **Example Docker Command**: | ||||||
| 
 | 
 | ||||||
| ```bash | ```bash | ||||||
| docker run -d --network=host -v open-webui:/app/backend/data -e OLLAMA_API_BASE_URL=http://127.0.0.1:11434/api --name open-webui --restart always ghcr.io/open-webui/open-webui:main | docker run -d --network=host -v open-webui:/app/backend/data -e OLLAMA_BASE_URL=http://127.0.0.1:11434 --name open-webui --restart always ghcr.io/open-webui/open-webui:main | ||||||
| ``` | ``` | ||||||
| 
 | 
 | ||||||
| ### Other Installation Methods | ### Other Installation Methods | ||||||
|  |  | ||||||
|  | @ -4,7 +4,7 @@ | ||||||
| 
 | 
 | ||||||
| The Open WebUI system is designed to streamline interactions between the client (your browser) and the Ollama API. At the heart of this design is a backend reverse proxy, enhancing security and resolving CORS issues. | The Open WebUI system is designed to streamline interactions between the client (your browser) and the Ollama API. At the heart of this design is a backend reverse proxy, enhancing security and resolving CORS issues. | ||||||
| 
 | 
 | ||||||
| - **How it Works**: The Open WebUI is designed to interact with the Ollama API through a specific route. When a request is made from the WebUI to Ollama, it is not directly sent to the Ollama API. Initially, the request is sent to the Open WebUI backend via `/ollama/api` route. From there, the backend is responsible for forwarding the request to the Ollama API. This forwarding is accomplished by using the route specified in the `OLLAMA_API_BASE_URL` environment variable. Therefore, a request made to `/ollama/api` in the WebUI is effectively the same as making a request to `OLLAMA_API_BASE_URL` in the backend. For instance, a request to `/ollama/api/tags` in the WebUI is equivalent to `OLLAMA_API_BASE_URL/tags` in the backend. | - **How it Works**: The Open WebUI is designed to interact with the Ollama API through a specific route. When a request is made from the WebUI to Ollama, it is not directly sent to the Ollama API. Initially, the request is sent to the Open WebUI backend via `/ollama` route. From there, the backend is responsible for forwarding the request to the Ollama API. This forwarding is accomplished by using the route specified in the `OLLAMA_BASE_URL` environment variable. Therefore, a request made to `/ollama` in the WebUI is effectively the same as making a request to `OLLAMA_BASE_URL` in the backend. For instance, a request to `/ollama/api/tags` in the WebUI is equivalent to `OLLAMA_BASE_URL/api/tags` in the backend. | ||||||
| 
 | 
 | ||||||
| - **Security Benefits**: This design prevents direct exposure of the Ollama API to the frontend, safeguarding against potential CORS (Cross-Origin Resource Sharing) issues and unauthorized access. Requiring authentication to access the Ollama API further enhances this security layer. | - **Security Benefits**: This design prevents direct exposure of the Ollama API to the frontend, safeguarding against potential CORS (Cross-Origin Resource Sharing) issues and unauthorized access. Requiring authentication to access the Ollama API further enhances this security layer. | ||||||
| 
 | 
 | ||||||
|  | @ -15,7 +15,7 @@ If you're experiencing connection issues, it’s often due to the WebUI docker c | ||||||
| **Example Docker Command**: | **Example Docker Command**: | ||||||
| 
 | 
 | ||||||
| ```bash | ```bash | ||||||
| docker run -d --network=host -v open-webui:/app/backend/data -e OLLAMA_API_BASE_URL=http://127.0.0.1:11434/api --name open-webui --restart always ghcr.io/open-webui/open-webui:main | docker run -d --network=host -v open-webui:/app/backend/data -e OLLAMA_BASE_URL=http://127.0.0.1:11434 --name open-webui --restart always ghcr.io/open-webui/open-webui:main | ||||||
| ``` | ``` | ||||||
| 
 | 
 | ||||||
| ### General Connection Errors | ### General Connection Errors | ||||||
|  | @ -25,8 +25,8 @@ docker run -d --network=host -v open-webui:/app/backend/data -e OLLAMA_API_BASE_ | ||||||
| **Troubleshooting Steps**: | **Troubleshooting Steps**: | ||||||
| 
 | 
 | ||||||
| 1. **Verify Ollama URL Format**: | 1. **Verify Ollama URL Format**: | ||||||
|    - When running the Web UI container, ensure the `OLLAMA_API_BASE_URL` is correctly set, including the `/api` suffix. (e.g., `http://192.168.1.1:11434/api` for different host setups). |    - When running the Web UI container, ensure the `OLLAMA_BASE_URL` is correctly set. (e.g., `http://192.168.1.1:11434` for different host setups). | ||||||
|    - In the Open WebUI, navigate to "Settings" > "General". |    - In the Open WebUI, navigate to "Settings" > "General". | ||||||
|    - Confirm that the Ollama Server URL is correctly set to `[OLLAMA URL]/api` (e.g., `http://localhost:11434/api`), including the `/api` suffix. |    - Confirm that the Ollama Server URL is correctly set to `[OLLAMA URL]` (e.g., `http://localhost:11434`). | ||||||
| 
 | 
 | ||||||
| By following these enhanced troubleshooting steps, connection issues should be effectively resolved. For further assistance or queries, feel free to reach out to us on our community Discord. | By following these enhanced troubleshooting steps, connection issues should be effectively resolved. For further assistance or queries, feel free to reach out to us on our community Discord. | ||||||
|  |  | ||||||
|  | @ -15,7 +15,7 @@ import asyncio | ||||||
| from apps.web.models.users import Users | from apps.web.models.users import Users | ||||||
| from constants import ERROR_MESSAGES | from constants import ERROR_MESSAGES | ||||||
| from utils.utils import decode_token, get_current_user, get_admin_user | from utils.utils import decode_token, get_current_user, get_admin_user | ||||||
| from config import OLLAMA_BASE_URL, WEBUI_AUTH | from config import OLLAMA_BASE_URLS | ||||||
| 
 | 
 | ||||||
| from typing import Optional, List, Union | from typing import Optional, List, Union | ||||||
| 
 | 
 | ||||||
|  | @ -29,8 +29,7 @@ app.add_middleware( | ||||||
|     allow_headers=["*"], |     allow_headers=["*"], | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| app.state.OLLAMA_BASE_URL = OLLAMA_BASE_URL | app.state.OLLAMA_BASE_URLS = OLLAMA_BASE_URLS | ||||||
| app.state.OLLAMA_BASE_URLS = [OLLAMA_BASE_URL] |  | ||||||
| app.state.MODELS = {} | app.state.MODELS = {} | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | @ -223,7 +222,7 @@ async def pull_model( | ||||||
|             r = requests.request( |             r = requests.request( | ||||||
|                 method="POST", |                 method="POST", | ||||||
|                 url=f"{url}/api/pull", |                 url=f"{url}/api/pull", | ||||||
|                 data=form_data.model_dump_json(exclude_none=True), |                 data=form_data.model_dump_json(exclude_none=True).encode(), | ||||||
|                 stream=True, |                 stream=True, | ||||||
|             ) |             ) | ||||||
| 
 | 
 | ||||||
|  | @ -295,7 +294,7 @@ async def push_model( | ||||||
|             r = requests.request( |             r = requests.request( | ||||||
|                 method="POST", |                 method="POST", | ||||||
|                 url=f"{url}/api/push", |                 url=f"{url}/api/push", | ||||||
|                 data=form_data.model_dump_json(exclude_none=True), |                 data=form_data.model_dump_json(exclude_none=True).encode(), | ||||||
|             ) |             ) | ||||||
| 
 | 
 | ||||||
|             r.raise_for_status() |             r.raise_for_status() | ||||||
|  | @ -357,7 +356,7 @@ async def create_model( | ||||||
|             r = requests.request( |             r = requests.request( | ||||||
|                 method="POST", |                 method="POST", | ||||||
|                 url=f"{url}/api/create", |                 url=f"{url}/api/create", | ||||||
|                 data=form_data.model_dump_json(exclude_none=True), |                 data=form_data.model_dump_json(exclude_none=True).encode(), | ||||||
|                 stream=True, |                 stream=True, | ||||||
|             ) |             ) | ||||||
| 
 | 
 | ||||||
|  | @ -420,7 +419,7 @@ async def copy_model( | ||||||
|         r = requests.request( |         r = requests.request( | ||||||
|             method="POST", |             method="POST", | ||||||
|             url=f"{url}/api/copy", |             url=f"{url}/api/copy", | ||||||
|             data=form_data.model_dump_json(exclude_none=True), |             data=form_data.model_dump_json(exclude_none=True).encode(), | ||||||
|         ) |         ) | ||||||
|         r.raise_for_status() |         r.raise_for_status() | ||||||
| 
 | 
 | ||||||
|  | @ -467,7 +466,7 @@ async def delete_model( | ||||||
|         r = requests.request( |         r = requests.request( | ||||||
|             method="DELETE", |             method="DELETE", | ||||||
|             url=f"{url}/api/delete", |             url=f"{url}/api/delete", | ||||||
|             data=form_data.model_dump_json(exclude_none=True), |             data=form_data.model_dump_json(exclude_none=True).encode(), | ||||||
|         ) |         ) | ||||||
|         r.raise_for_status() |         r.raise_for_status() | ||||||
| 
 | 
 | ||||||
|  | @ -507,7 +506,7 @@ async def show_model_info(form_data: ModelNameForm, user=Depends(get_current_use | ||||||
|         r = requests.request( |         r = requests.request( | ||||||
|             method="POST", |             method="POST", | ||||||
|             url=f"{url}/api/show", |             url=f"{url}/api/show", | ||||||
|             data=form_data.model_dump_json(exclude_none=True), |             data=form_data.model_dump_json(exclude_none=True).encode(), | ||||||
|         ) |         ) | ||||||
|         r.raise_for_status() |         r.raise_for_status() | ||||||
| 
 | 
 | ||||||
|  | @ -559,7 +558,7 @@ async def generate_embeddings( | ||||||
|         r = requests.request( |         r = requests.request( | ||||||
|             method="POST", |             method="POST", | ||||||
|             url=f"{url}/api/embeddings", |             url=f"{url}/api/embeddings", | ||||||
|             data=form_data.model_dump_json(exclude_none=True), |             data=form_data.model_dump_json(exclude_none=True).encode(), | ||||||
|         ) |         ) | ||||||
|         r.raise_for_status() |         r.raise_for_status() | ||||||
| 
 | 
 | ||||||
|  | @ -645,7 +644,7 @@ async def generate_completion( | ||||||
|             r = requests.request( |             r = requests.request( | ||||||
|                 method="POST", |                 method="POST", | ||||||
|                 url=f"{url}/api/generate", |                 url=f"{url}/api/generate", | ||||||
|                 data=form_data.model_dump_json(exclude_none=True), |                 data=form_data.model_dump_json(exclude_none=True).encode(), | ||||||
|                 stream=True, |                 stream=True, | ||||||
|             ) |             ) | ||||||
| 
 | 
 | ||||||
|  | @ -715,7 +714,7 @@ async def generate_chat_completion( | ||||||
| 
 | 
 | ||||||
|     r = None |     r = None | ||||||
| 
 | 
 | ||||||
|     print(form_data.model_dump_json(exclude_none=True)) |     print(form_data.model_dump_json(exclude_none=True).encode()) | ||||||
| 
 | 
 | ||||||
|     def get_request(): |     def get_request(): | ||||||
|         nonlocal form_data |         nonlocal form_data | ||||||
|  | @ -745,7 +744,7 @@ async def generate_chat_completion( | ||||||
|             r = requests.request( |             r = requests.request( | ||||||
|                 method="POST", |                 method="POST", | ||||||
|                 url=f"{url}/api/chat", |                 url=f"{url}/api/chat", | ||||||
|                 data=form_data.model_dump_json(exclude_none=True), |                 data=form_data.model_dump_json(exclude_none=True).encode(), | ||||||
|                 stream=True, |                 stream=True, | ||||||
|             ) |             ) | ||||||
| 
 | 
 | ||||||
|  | @ -757,6 +756,7 @@ async def generate_chat_completion( | ||||||
|                 headers=dict(r.headers), |                 headers=dict(r.headers), | ||||||
|             ) |             ) | ||||||
|         except Exception as e: |         except Exception as e: | ||||||
|  |             print(e) | ||||||
|             raise e |             raise e | ||||||
| 
 | 
 | ||||||
|     try: |     try: | ||||||
|  | @ -844,7 +844,7 @@ async def generate_openai_chat_completion( | ||||||
|             r = requests.request( |             r = requests.request( | ||||||
|                 method="POST", |                 method="POST", | ||||||
|                 url=f"{url}/v1/chat/completions", |                 url=f"{url}/v1/chat/completions", | ||||||
|                 data=form_data.model_dump_json(exclude_none=True), |                 data=form_data.model_dump_json(exclude_none=True).encode(), | ||||||
|                 stream=True, |                 stream=True, | ||||||
|             ) |             ) | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -3,7 +3,10 @@ from fastapi.middleware.cors import CORSMiddleware | ||||||
| from fastapi.responses import StreamingResponse, JSONResponse, FileResponse | from fastapi.responses import StreamingResponse, JSONResponse, FileResponse | ||||||
| 
 | 
 | ||||||
| import requests | import requests | ||||||
|  | import aiohttp | ||||||
|  | import asyncio | ||||||
| import json | import json | ||||||
|  | 
 | ||||||
| from pydantic import BaseModel | from pydantic import BaseModel | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | @ -15,7 +18,9 @@ from utils.utils import ( | ||||||
|     get_verified_user, |     get_verified_user, | ||||||
|     get_admin_user, |     get_admin_user, | ||||||
| ) | ) | ||||||
| from config import OPENAI_API_BASE_URL, OPENAI_API_KEY, CACHE_DIR | from config import OPENAI_API_BASE_URLS, OPENAI_API_KEYS, CACHE_DIR | ||||||
|  | from typing import List, Optional | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
| import hashlib | import hashlib | ||||||
| from pathlib import Path | from pathlib import Path | ||||||
|  | @ -29,116 +34,207 @@ app.add_middleware( | ||||||
|     allow_headers=["*"], |     allow_headers=["*"], | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| app.state.OPENAI_API_BASE_URL = OPENAI_API_BASE_URL | app.state.OPENAI_API_BASE_URLS = OPENAI_API_BASE_URLS | ||||||
| app.state.OPENAI_API_KEY = OPENAI_API_KEY | app.state.OPENAI_API_KEYS = OPENAI_API_KEYS | ||||||
|  | 
 | ||||||
|  | app.state.MODELS = {} | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class UrlUpdateForm(BaseModel): | @app.middleware("http") | ||||||
|     url: str | async def check_url(request: Request, call_next): | ||||||
|  |     if len(app.state.MODELS) == 0: | ||||||
|  |         await get_all_models() | ||||||
|  |     else: | ||||||
|  |         pass | ||||||
|  | 
 | ||||||
|  |     response = await call_next(request) | ||||||
|  |     return response | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class KeyUpdateForm(BaseModel): | class UrlsUpdateForm(BaseModel): | ||||||
|     key: str |     urls: List[str] | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @app.get("/url") | class KeysUpdateForm(BaseModel): | ||||||
| async def get_openai_url(user=Depends(get_admin_user)): |     keys: List[str] | ||||||
|     return {"OPENAI_API_BASE_URL": app.state.OPENAI_API_BASE_URL} |  | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @app.post("/url/update") | @app.get("/urls") | ||||||
| async def update_openai_url(form_data: UrlUpdateForm, user=Depends(get_admin_user)): | async def get_openai_urls(user=Depends(get_admin_user)): | ||||||
|     app.state.OPENAI_API_BASE_URL = form_data.url |     return {"OPENAI_API_BASE_URLS": app.state.OPENAI_API_BASE_URLS} | ||||||
|     return {"OPENAI_API_BASE_URL": app.state.OPENAI_API_BASE_URL} |  | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @app.get("/key") | @app.post("/urls/update") | ||||||
| async def get_openai_key(user=Depends(get_admin_user)): | async def update_openai_urls(form_data: UrlsUpdateForm, user=Depends(get_admin_user)): | ||||||
|     return {"OPENAI_API_KEY": app.state.OPENAI_API_KEY} |     app.state.OPENAI_API_BASE_URLS = form_data.urls | ||||||
|  |     return {"OPENAI_API_BASE_URLS": app.state.OPENAI_API_BASE_URLS} | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @app.post("/key/update") | @app.get("/keys") | ||||||
| async def update_openai_key(form_data: KeyUpdateForm, user=Depends(get_admin_user)): | async def get_openai_keys(user=Depends(get_admin_user)): | ||||||
|     app.state.OPENAI_API_KEY = form_data.key |     return {"OPENAI_API_KEYS": app.state.OPENAI_API_KEYS} | ||||||
|     return {"OPENAI_API_KEY": app.state.OPENAI_API_KEY} | 
 | ||||||
|  | 
 | ||||||
|  | @app.post("/keys/update") | ||||||
|  | async def update_openai_key(form_data: KeysUpdateForm, user=Depends(get_admin_user)): | ||||||
|  |     app.state.OPENAI_API_KEYS = form_data.keys | ||||||
|  |     return {"OPENAI_API_KEYS": app.state.OPENAI_API_KEYS} | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @app.post("/audio/speech") | @app.post("/audio/speech") | ||||||
| async def speech(request: Request, user=Depends(get_verified_user)): | async def speech(request: Request, user=Depends(get_verified_user)): | ||||||
|     target_url = f"{app.state.OPENAI_API_BASE_URL}/audio/speech" |     idx = None | ||||||
| 
 |  | ||||||
|     if app.state.OPENAI_API_KEY == "": |  | ||||||
|         raise HTTPException(status_code=401, detail=ERROR_MESSAGES.API_KEY_NOT_FOUND) |  | ||||||
| 
 |  | ||||||
|     body = await request.body() |  | ||||||
| 
 |  | ||||||
|     name = hashlib.sha256(body).hexdigest() |  | ||||||
| 
 |  | ||||||
|     SPEECH_CACHE_DIR = Path(CACHE_DIR).joinpath("./audio/speech/") |  | ||||||
|     SPEECH_CACHE_DIR.mkdir(parents=True, exist_ok=True) |  | ||||||
|     file_path = SPEECH_CACHE_DIR.joinpath(f"{name}.mp3") |  | ||||||
|     file_body_path = SPEECH_CACHE_DIR.joinpath(f"{name}.json") |  | ||||||
| 
 |  | ||||||
|     # Check if the file already exists in the cache |  | ||||||
|     if file_path.is_file(): |  | ||||||
|         return FileResponse(file_path) |  | ||||||
| 
 |  | ||||||
|     headers = {} |  | ||||||
|     headers["Authorization"] = f"Bearer {app.state.OPENAI_API_KEY}" |  | ||||||
|     headers["Content-Type"] = "application/json" |  | ||||||
| 
 |  | ||||||
|     try: |     try: | ||||||
|         print("openai") |         idx = app.state.OPENAI_API_BASE_URLS.index("https://api.openai.com/v1") | ||||||
|         r = requests.post( |         body = await request.body() | ||||||
|             url=target_url, |         name = hashlib.sha256(body).hexdigest() | ||||||
|             data=body, | 
 | ||||||
|             headers=headers, |         SPEECH_CACHE_DIR = Path(CACHE_DIR).joinpath("./audio/speech/") | ||||||
|             stream=True, |         SPEECH_CACHE_DIR.mkdir(parents=True, exist_ok=True) | ||||||
|  |         file_path = SPEECH_CACHE_DIR.joinpath(f"{name}.mp3") | ||||||
|  |         file_body_path = SPEECH_CACHE_DIR.joinpath(f"{name}.json") | ||||||
|  | 
 | ||||||
|  |         # Check if the file already exists in the cache | ||||||
|  |         if file_path.is_file(): | ||||||
|  |             return FileResponse(file_path) | ||||||
|  | 
 | ||||||
|  |         headers = {} | ||||||
|  |         headers["Authorization"] = f"Bearer {app.state.OPENAI_API_KEYS[idx]}" | ||||||
|  |         headers["Content-Type"] = "application/json" | ||||||
|  | 
 | ||||||
|  |         try: | ||||||
|  |             r = requests.post( | ||||||
|  |                 url=f"{app.state.OPENAI_API_BASE_URLS[idx]}/audio/speech", | ||||||
|  |                 data=body, | ||||||
|  |                 headers=headers, | ||||||
|  |                 stream=True, | ||||||
|  |             ) | ||||||
|  | 
 | ||||||
|  |             r.raise_for_status() | ||||||
|  | 
 | ||||||
|  |             # Save the streaming content to a file | ||||||
|  |             with open(file_path, "wb") as f: | ||||||
|  |                 for chunk in r.iter_content(chunk_size=8192): | ||||||
|  |                     f.write(chunk) | ||||||
|  | 
 | ||||||
|  |             with open(file_body_path, "w") as f: | ||||||
|  |                 json.dump(json.loads(body.decode("utf-8")), f) | ||||||
|  | 
 | ||||||
|  |             # Return the saved file | ||||||
|  |             return FileResponse(file_path) | ||||||
|  | 
 | ||||||
|  |         except Exception as e: | ||||||
|  |             print(e) | ||||||
|  |             error_detail = "Open WebUI: Server Connection Error" | ||||||
|  |             if r is not None: | ||||||
|  |                 try: | ||||||
|  |                     res = r.json() | ||||||
|  |                     if "error" in res: | ||||||
|  |                         error_detail = f"External: {res['error']}" | ||||||
|  |                 except: | ||||||
|  |                     error_detail = f"External: {e}" | ||||||
|  | 
 | ||||||
|  |             raise HTTPException(status_code=r.status_code, detail=error_detail) | ||||||
|  | 
 | ||||||
|  |     except ValueError: | ||||||
|  |         raise HTTPException(status_code=401, detail=ERROR_MESSAGES.OPENAI_NOT_FOUND) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | async def fetch_url(url, key): | ||||||
|  |     try: | ||||||
|  |         headers = {"Authorization": f"Bearer {key}"} | ||||||
|  |         async with aiohttp.ClientSession() as session: | ||||||
|  |             async with session.get(url, headers=headers) as response: | ||||||
|  |                 return await response.json() | ||||||
|  |     except Exception as e: | ||||||
|  |         # Handle connection error here | ||||||
|  |         print(f"Connection error: {e}") | ||||||
|  |         return None | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def merge_models_lists(model_lists): | ||||||
|  |     merged_list = [] | ||||||
|  | 
 | ||||||
|  |     for idx, models in enumerate(model_lists): | ||||||
|  |         merged_list.extend( | ||||||
|  |             [ | ||||||
|  |                 {**model, "urlIdx": idx} | ||||||
|  |                 for model in models | ||||||
|  |                 if "api.openai.com" not in app.state.OPENAI_API_BASE_URLS[idx] | ||||||
|  |                 or "gpt" in model["id"] | ||||||
|  |             ] | ||||||
|         ) |         ) | ||||||
| 
 | 
 | ||||||
|         r.raise_for_status() |     return merged_list | ||||||
| 
 | 
 | ||||||
|         # Save the streaming content to a file |  | ||||||
|         with open(file_path, "wb") as f: |  | ||||||
|             for chunk in r.iter_content(chunk_size=8192): |  | ||||||
|                 f.write(chunk) |  | ||||||
| 
 | 
 | ||||||
|         with open(file_body_path, "w") as f: | async def get_all_models(): | ||||||
|             json.dump(json.loads(body.decode("utf-8")), f) |     print("get_all_models") | ||||||
|  |     tasks = [ | ||||||
|  |         fetch_url(f"{url}/models", app.state.OPENAI_API_KEYS[idx]) | ||||||
|  |         for idx, url in enumerate(app.state.OPENAI_API_BASE_URLS) | ||||||
|  |     ] | ||||||
|  |     responses = await asyncio.gather(*tasks) | ||||||
|  |     responses = list(filter(lambda x: x is not None and "error" not in x, responses)) | ||||||
|  |     models = { | ||||||
|  |         "data": merge_models_lists( | ||||||
|  |             list(map(lambda response: response["data"], responses)) | ||||||
|  |         ) | ||||||
|  |     } | ||||||
|  |     app.state.MODELS = {model["id"]: model for model in models["data"]} | ||||||
| 
 | 
 | ||||||
|         # Return the saved file |     return models | ||||||
|         return FileResponse(file_path) |  | ||||||
| 
 | 
 | ||||||
|     except Exception as e: |  | ||||||
|         print(e) |  | ||||||
|         error_detail = "Open WebUI: Server Connection Error" |  | ||||||
|         if r is not None: |  | ||||||
|             try: |  | ||||||
|                 res = r.json() |  | ||||||
|                 if "error" in res: |  | ||||||
|                     error_detail = f"External: {res['error']}" |  | ||||||
|             except: |  | ||||||
|                 error_detail = f"External: {e}" |  | ||||||
| 
 | 
 | ||||||
|         raise HTTPException(status_code=r.status_code, detail=error_detail) | # , user=Depends(get_current_user) | ||||||
|  | @app.get("/models") | ||||||
|  | @app.get("/models/{url_idx}") | ||||||
|  | async def get_models(url_idx: Optional[int] = None): | ||||||
|  |     if url_idx == None: | ||||||
|  |         return await get_all_models() | ||||||
|  |     else: | ||||||
|  |         url = app.state.OPENAI_API_BASE_URLS[url_idx] | ||||||
|  |         try: | ||||||
|  |             r = requests.request(method="GET", url=f"{url}/models") | ||||||
|  |             r.raise_for_status() | ||||||
|  | 
 | ||||||
|  |             response_data = r.json() | ||||||
|  |             if "api.openai.com" in url: | ||||||
|  |                 response_data["data"] = list( | ||||||
|  |                     filter(lambda model: "gpt" in model["id"], response_data["data"]) | ||||||
|  |                 ) | ||||||
|  | 
 | ||||||
|  |             return response_data | ||||||
|  |         except Exception as e: | ||||||
|  |             print(e) | ||||||
|  |             error_detail = "Open WebUI: Server Connection Error" | ||||||
|  |             if r is not None: | ||||||
|  |                 try: | ||||||
|  |                     res = r.json() | ||||||
|  |                     if "error" in res: | ||||||
|  |                         error_detail = f"External: {res['error']}" | ||||||
|  |                 except: | ||||||
|  |                     error_detail = f"External: {e}" | ||||||
|  | 
 | ||||||
|  |             raise HTTPException( | ||||||
|  |                 status_code=r.status_code if r else 500, | ||||||
|  |                 detail=error_detail, | ||||||
|  |             ) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @app.api_route("/{path:path}", methods=["GET", "POST", "PUT", "DELETE"]) | @app.api_route("/{path:path}", methods=["GET", "POST", "PUT", "DELETE"]) | ||||||
| async def proxy(path: str, request: Request, user=Depends(get_verified_user)): | async def proxy(path: str, request: Request, user=Depends(get_verified_user)): | ||||||
|     target_url = f"{app.state.OPENAI_API_BASE_URL}/{path}" |     idx = 0 | ||||||
|     print(target_url, app.state.OPENAI_API_KEY) |  | ||||||
| 
 |  | ||||||
|     if app.state.OPENAI_API_KEY == "": |  | ||||||
|         raise HTTPException(status_code=401, detail=ERROR_MESSAGES.API_KEY_NOT_FOUND) |  | ||||||
| 
 | 
 | ||||||
|     body = await request.body() |     body = await request.body() | ||||||
| 
 |  | ||||||
|     # TODO: Remove below after gpt-4-vision fix from Open AI |     # TODO: Remove below after gpt-4-vision fix from Open AI | ||||||
|     # Try to decode the body of the request from bytes to a UTF-8 string (Require add max_token to fix gpt-4-vision) |     # Try to decode the body of the request from bytes to a UTF-8 string (Require add max_token to fix gpt-4-vision) | ||||||
|     try: |     try: | ||||||
|         body = body.decode("utf-8") |         body = body.decode("utf-8") | ||||||
|         body = json.loads(body) |         body = json.loads(body) | ||||||
| 
 | 
 | ||||||
|  |         idx = app.state.MODELS[body.get("model")]["urlIdx"] | ||||||
|  | 
 | ||||||
|         # Check if the model is "gpt-4-vision-preview" and set "max_tokens" to 4000 |         # Check if the model is "gpt-4-vision-preview" and set "max_tokens" to 4000 | ||||||
|         # This is a workaround until OpenAI fixes the issue with this model |         # This is a workaround until OpenAI fixes the issue with this model | ||||||
|         if body.get("model") == "gpt-4-vision-preview": |         if body.get("model") == "gpt-4-vision-preview": | ||||||
|  | @ -158,8 +254,16 @@ async def proxy(path: str, request: Request, user=Depends(get_verified_user)): | ||||||
|     except json.JSONDecodeError as e: |     except json.JSONDecodeError as e: | ||||||
|         print("Error loading request body into a dictionary:", e) |         print("Error loading request body into a dictionary:", e) | ||||||
| 
 | 
 | ||||||
|  |     url = app.state.OPENAI_API_BASE_URLS[idx] | ||||||
|  |     key = app.state.OPENAI_API_KEYS[idx] | ||||||
|  | 
 | ||||||
|  |     target_url = f"{url}/{path}" | ||||||
|  | 
 | ||||||
|  |     if key == "": | ||||||
|  |         raise HTTPException(status_code=401, detail=ERROR_MESSAGES.API_KEY_NOT_FOUND) | ||||||
|  | 
 | ||||||
|     headers = {} |     headers = {} | ||||||
|     headers["Authorization"] = f"Bearer {app.state.OPENAI_API_KEY}" |     headers["Authorization"] = f"Bearer {key}" | ||||||
|     headers["Content-Type"] = "application/json" |     headers["Content-Type"] = "application/json" | ||||||
| 
 | 
 | ||||||
|     try: |     try: | ||||||
|  | @ -181,21 +285,7 @@ async def proxy(path: str, request: Request, user=Depends(get_verified_user)): | ||||||
|                 headers=dict(r.headers), |                 headers=dict(r.headers), | ||||||
|             ) |             ) | ||||||
|         else: |         else: | ||||||
|             # For non-SSE, read the response and return it |  | ||||||
|             # response_data = ( |  | ||||||
|             #     r.json() |  | ||||||
|             #     if r.headers.get("Content-Type", "") |  | ||||||
|             #     == "application/json" |  | ||||||
|             #     else r.text |  | ||||||
|             # ) |  | ||||||
| 
 |  | ||||||
|             response_data = r.json() |             response_data = r.json() | ||||||
| 
 |  | ||||||
|             if "api.openai.com" in app.state.OPENAI_API_BASE_URL and path == "models": |  | ||||||
|                 response_data["data"] = list( |  | ||||||
|                     filter(lambda model: "gpt" in model["id"], response_data["data"]) |  | ||||||
|                 ) |  | ||||||
| 
 |  | ||||||
|             return response_data |             return response_data | ||||||
|     except Exception as e: |     except Exception as e: | ||||||
|         print(e) |         print(e) | ||||||
|  |  | ||||||
|  | @ -425,7 +425,7 @@ def get_loader(filename: str, file_content_type: str, file_path: str): | ||||||
|     ] |     ] | ||||||
| 
 | 
 | ||||||
|     if file_ext == "pdf": |     if file_ext == "pdf": | ||||||
|         loader = PyPDFLoader(file_path) |         loader = PyPDFLoader(file_path, extract_images=True) | ||||||
|     elif file_ext == "csv": |     elif file_ext == "csv": | ||||||
|         loader = CSVLoader(file_path) |         loader = CSVLoader(file_path) | ||||||
|     elif file_ext == "rst": |     elif file_ext == "rst": | ||||||
|  |  | ||||||
|  | @ -14,7 +14,7 @@ import json | ||||||
| from utils.utils import get_admin_user | from utils.utils import get_admin_user | ||||||
| from utils.misc import calculate_sha256, get_gravatar_url | from utils.misc import calculate_sha256, get_gravatar_url | ||||||
| 
 | 
 | ||||||
| from config import OLLAMA_API_BASE_URL, DATA_DIR, UPLOAD_DIR | from config import OLLAMA_BASE_URLS, DATA_DIR, UPLOAD_DIR | ||||||
| from constants import ERROR_MESSAGES | from constants import ERROR_MESSAGES | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | @ -75,7 +75,7 @@ async def download_file_stream(url, file_path, file_name, chunk_size=1024 * 1024 | ||||||
|                     hashed = calculate_sha256(file) |                     hashed = calculate_sha256(file) | ||||||
|                     file.seek(0) |                     file.seek(0) | ||||||
| 
 | 
 | ||||||
|                     url = f"{OLLAMA_API_BASE_URL}/blobs/sha256:{hashed}" |                     url = f"{OLLAMA_BASE_URLS[0]}/blobs/sha256:{hashed}" | ||||||
|                     response = requests.post(url, data=file) |                     response = requests.post(url, data=file) | ||||||
| 
 | 
 | ||||||
|                     if response.ok: |                     if response.ok: | ||||||
|  | @ -147,7 +147,7 @@ def upload(file: UploadFile = File(...)): | ||||||
|                     hashed = calculate_sha256(f) |                     hashed = calculate_sha256(f) | ||||||
|                     f.seek(0) |                     f.seek(0) | ||||||
| 
 | 
 | ||||||
|                     url = f"{OLLAMA_API_BASE_URL}/blobs/sha256:{hashed}" |                     url = f"{OLLAMA_BASE_URLS[0]}/blobs/sha256:{hashed}" | ||||||
|                     response = requests.post(url, data=f) |                     response = requests.post(url, data=f) | ||||||
| 
 | 
 | ||||||
|                     if response.ok: |                     if response.ok: | ||||||
|  |  | ||||||
|  | @ -200,27 +200,32 @@ if not os.path.exists(LITELLM_CONFIG_PATH): | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| #################################### | #################################### | ||||||
| # OLLAMA_API_BASE_URL | # OLLAMA_BASE_URL | ||||||
| #################################### | #################################### | ||||||
| 
 | 
 | ||||||
| OLLAMA_API_BASE_URL = os.environ.get( | OLLAMA_API_BASE_URL = os.environ.get( | ||||||
|     "OLLAMA_API_BASE_URL", "http://localhost:11434/api" |     "OLLAMA_API_BASE_URL", "http://localhost:11434/api" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| if ENV == "prod": |  | ||||||
|     if OLLAMA_API_BASE_URL == "/ollama/api": |  | ||||||
|         OLLAMA_API_BASE_URL = "http://host.docker.internal:11434/api" |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| OLLAMA_BASE_URL = os.environ.get("OLLAMA_BASE_URL", "") | OLLAMA_BASE_URL = os.environ.get("OLLAMA_BASE_URL", "") | ||||||
| 
 | 
 | ||||||
| if OLLAMA_BASE_URL == "": | if ENV == "prod": | ||||||
|  |     if OLLAMA_BASE_URL == "/ollama": | ||||||
|  |         OLLAMA_BASE_URL = "http://host.docker.internal:11434" | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | if OLLAMA_BASE_URL == "" and OLLAMA_API_BASE_URL != "": | ||||||
|     OLLAMA_BASE_URL = ( |     OLLAMA_BASE_URL = ( | ||||||
|         OLLAMA_API_BASE_URL[:-4] |         OLLAMA_API_BASE_URL[:-4] | ||||||
|         if OLLAMA_API_BASE_URL.endswith("/api") |         if OLLAMA_API_BASE_URL.endswith("/api") | ||||||
|         else OLLAMA_API_BASE_URL |         else OLLAMA_API_BASE_URL | ||||||
|     ) |     ) | ||||||
| 
 | 
 | ||||||
|  | OLLAMA_BASE_URLS = os.environ.get("OLLAMA_BASE_URLS", "") | ||||||
|  | OLLAMA_BASE_URLS = OLLAMA_BASE_URLS if OLLAMA_BASE_URLS != "" else OLLAMA_BASE_URL | ||||||
|  | 
 | ||||||
|  | OLLAMA_BASE_URLS = [url.strip() for url in OLLAMA_BASE_URLS.split(";")] | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
| #################################### | #################################### | ||||||
| # OPENAI_API | # OPENAI_API | ||||||
|  | @ -229,9 +234,25 @@ if OLLAMA_BASE_URL == "": | ||||||
| OPENAI_API_KEY = os.environ.get("OPENAI_API_KEY", "") | OPENAI_API_KEY = os.environ.get("OPENAI_API_KEY", "") | ||||||
| OPENAI_API_BASE_URL = os.environ.get("OPENAI_API_BASE_URL", "") | OPENAI_API_BASE_URL = os.environ.get("OPENAI_API_BASE_URL", "") | ||||||
| 
 | 
 | ||||||
|  | if OPENAI_API_KEY == "": | ||||||
|  |     OPENAI_API_KEY = "none" | ||||||
|  | 
 | ||||||
| if OPENAI_API_BASE_URL == "": | if OPENAI_API_BASE_URL == "": | ||||||
|     OPENAI_API_BASE_URL = "https://api.openai.com/v1" |     OPENAI_API_BASE_URL = "https://api.openai.com/v1" | ||||||
| 
 | 
 | ||||||
|  | OPENAI_API_KEYS = os.environ.get("OPENAI_API_KEYS", "") | ||||||
|  | OPENAI_API_KEYS = OPENAI_API_KEYS if OPENAI_API_KEYS != "" else OPENAI_API_KEY | ||||||
|  | 
 | ||||||
|  | OPENAI_API_KEYS = [url.strip() for url in OPENAI_API_KEYS.split(";")] | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | OPENAI_API_BASE_URLS = os.environ.get("OPENAI_API_BASE_URLS", "") | ||||||
|  | OPENAI_API_BASE_URLS = ( | ||||||
|  |     OPENAI_API_BASE_URLS if OPENAI_API_BASE_URLS != "" else OPENAI_API_BASE_URL | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | OPENAI_API_BASE_URLS = [url.strip() for url in OPENAI_API_BASE_URL.split(";")] | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
| #################################### | #################################### | ||||||
| # WEBUI | # WEBUI | ||||||
|  |  | ||||||
|  | @ -41,6 +41,7 @@ class ERROR_MESSAGES(str, Enum): | ||||||
|     NOT_FOUND = "We could not find what you're looking for :/" |     NOT_FOUND = "We could not find what you're looking for :/" | ||||||
|     USER_NOT_FOUND = "We could not find what you're looking for :/" |     USER_NOT_FOUND = "We could not find what you're looking for :/" | ||||||
|     API_KEY_NOT_FOUND = "Oops! It looks like there's a hiccup. The API key is missing. Please make sure to provide a valid API key to access this feature." |     API_KEY_NOT_FOUND = "Oops! It looks like there's a hiccup. The API key is missing. Please make sure to provide a valid API key to access this feature." | ||||||
|  | 
 | ||||||
|     MALICIOUS = "Unusual activities detected, please try again in a few minutes." |     MALICIOUS = "Unusual activities detected, please try again in a few minutes." | ||||||
| 
 | 
 | ||||||
|     PANDOC_NOT_INSTALLED = "Pandoc is not installed on the server. Please contact your administrator for assistance." |     PANDOC_NOT_INSTALLED = "Pandoc is not installed on the server. Please contact your administrator for assistance." | ||||||
|  | @ -50,3 +51,4 @@ class ERROR_MESSAGES(str, Enum): | ||||||
|     RATE_LIMIT_EXCEEDED = "API rate limit exceeded" |     RATE_LIMIT_EXCEEDED = "API rate limit exceeded" | ||||||
| 
 | 
 | ||||||
|     MODEL_NOT_FOUND = lambda name="": f"Model '{name}' was not found" |     MODEL_NOT_FOUND = lambda name="": f"Model '{name}' was not found" | ||||||
|  |     OPENAI_NOT_FOUND = lambda name="": f"OpenAI API was not found" | ||||||
|  |  | ||||||
|  | @ -35,6 +35,9 @@ openpyxl | ||||||
| pyxlsb | pyxlsb | ||||||
| xlrd | xlrd | ||||||
| 
 | 
 | ||||||
|  | opencv-python-headless | ||||||
|  | rapidocr-onnxruntime | ||||||
|  | 
 | ||||||
| faster-whisper | faster-whisper | ||||||
| 
 | 
 | ||||||
| PyJWT | PyJWT | ||||||
|  |  | ||||||
|  | @ -14,7 +14,7 @@ services: | ||||||
|     build: |     build: | ||||||
|       context: . |       context: . | ||||||
|       args: |       args: | ||||||
|         OLLAMA_API_BASE_URL: '/ollama/api' |         OLLAMA_BASE_URL: '/ollama' | ||||||
|       dockerfile: Dockerfile |       dockerfile: Dockerfile | ||||||
|     image: ghcr.io/open-webui/open-webui:main |     image: ghcr.io/open-webui/open-webui:main | ||||||
|     container_name: open-webui |     container_name: open-webui | ||||||
|  | @ -25,7 +25,7 @@ services: | ||||||
|     ports: |     ports: | ||||||
|       - ${OPEN_WEBUI_PORT-3000}:8080 |       - ${OPEN_WEBUI_PORT-3000}:8080 | ||||||
|     environment: |     environment: | ||||||
|       - 'OLLAMA_API_BASE_URL=http://ollama:11434/api' |       - 'OLLAMA_BASE_URL=http://ollama:11434' | ||||||
|       - 'WEBUI_SECRET_KEY=' |       - 'WEBUI_SECRET_KEY=' | ||||||
|     extra_hosts: |     extra_hosts: | ||||||
|       - host.docker.internal:host-gateway |       - host.docker.internal:host-gateway | ||||||
|  |  | ||||||
							
								
								
									
										38
									
								
								i18next-parser.config.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										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 |         - name: data | ||||||
|           mountPath: /app/backend/data |           mountPath: /app/backend/data | ||||||
|         env: |         env: | ||||||
|         - name: OLLAMA_API_BASE_URL |         - name: OLLAMA_BASE_URL | ||||||
|           value: {{ include "ollama.url" . | quote }} |           value: {{ include "ollama.url" . | quote }} | ||||||
|         tty: true |         tty: true | ||||||
|       {{- with .Values.webui.nodeSelector }} |       {{- with .Values.webui.nodeSelector }} | ||||||
|  |  | ||||||
|  | @ -26,8 +26,8 @@ spec: | ||||||
|             cpu: "1000m" |             cpu: "1000m" | ||||||
|             memory: "1Gi" |             memory: "1Gi" | ||||||
|         env: |         env: | ||||||
|         - name: OLLAMA_API_BASE_URL |         - name: OLLAMA_BASE_URL | ||||||
|           value: "http://ollama-service.open-webui.svc.cluster.local:11434/api" |           value: "http://ollama-service.open-webui.svc.cluster.local:11434" | ||||||
|         tty: true |         tty: true | ||||||
|         volumeMounts: |         volumeMounts: | ||||||
|         - name: webui-volume |         - name: webui-volume | ||||||
|  |  | ||||||
|  | @ -1,6 +1,6 @@ | ||||||
| { | { | ||||||
| 	"name": "open-webui", | 	"name": "open-webui", | ||||||
| 	"version": "0.1.108", | 	"version": "0.1.110", | ||||||
| 	"private": true, | 	"private": true, | ||||||
| 	"scripts": { | 	"scripts": { | ||||||
| 		"dev": "vite dev --host", | 		"dev": "vite dev --host", | ||||||
|  | @ -27,6 +27,7 @@ | ||||||
| 		"eslint": "^8.56.0", | 		"eslint": "^8.56.0", | ||||||
| 		"eslint-config-prettier": "^8.5.0", | 		"eslint-config-prettier": "^8.5.0", | ||||||
| 		"eslint-plugin-svelte": "^2.30.0", | 		"eslint-plugin-svelte": "^2.30.0", | ||||||
|  | 		"i18next-parser": "^8.13.0", | ||||||
| 		"postcss": "^8.4.31", | 		"postcss": "^8.4.31", | ||||||
| 		"prettier": "^2.8.0", | 		"prettier": "^2.8.0", | ||||||
| 		"prettier-plugin-svelte": "^2.10.1", | 		"prettier-plugin-svelte": "^2.10.1", | ||||||
|  |  | ||||||
|  | @ -43,6 +43,10 @@ ol > li { | ||||||
| 	font-weight: 400; | 	font-weight: 400; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | li p { | ||||||
|  | 	display: inline; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| ::-webkit-scrollbar-thumb { | ::-webkit-scrollbar-thumb { | ||||||
| 	--tw-border-opacity: 1; | 	--tw-border-opacity: 1; | ||||||
| 	background-color: rgba(217, 217, 227, 0.8); | 	background-color: rgba(217, 217, 227, 0.8); | ||||||
|  |  | ||||||
|  | @ -1,9 +1,9 @@ | ||||||
| import { OPENAI_API_BASE_URL } from '$lib/constants'; | import { OPENAI_API_BASE_URL } from '$lib/constants'; | ||||||
| 
 | 
 | ||||||
| export const getOpenAIUrl = async (token: string = '') => { | export const getOpenAIUrls = async (token: string = '') => { | ||||||
| 	let error = null; | 	let error = null; | ||||||
| 
 | 
 | ||||||
| 	const res = await fetch(`${OPENAI_API_BASE_URL}/url`, { | 	const res = await fetch(`${OPENAI_API_BASE_URL}/urls`, { | ||||||
| 		method: 'GET', | 		method: 'GET', | ||||||
| 		headers: { | 		headers: { | ||||||
| 			Accept: 'application/json', | 			Accept: 'application/json', | ||||||
|  | @ -29,13 +29,13 @@ export const getOpenAIUrl = async (token: string = '') => { | ||||||
| 		throw error; | 		throw error; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	return res.OPENAI_API_BASE_URL; | 	return res.OPENAI_API_BASE_URLS; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| export const updateOpenAIUrl = async (token: string = '', url: string) => { | export const updateOpenAIUrls = async (token: string = '', urls: string[]) => { | ||||||
| 	let error = null; | 	let error = null; | ||||||
| 
 | 
 | ||||||
| 	const res = await fetch(`${OPENAI_API_BASE_URL}/url/update`, { | 	const res = await fetch(`${OPENAI_API_BASE_URL}/urls/update`, { | ||||||
| 		method: 'POST', | 		method: 'POST', | ||||||
| 		headers: { | 		headers: { | ||||||
| 			Accept: 'application/json', | 			Accept: 'application/json', | ||||||
|  | @ -43,7 +43,7 @@ export const updateOpenAIUrl = async (token: string = '', url: string) => { | ||||||
| 			...(token && { authorization: `Bearer ${token}` }) | 			...(token && { authorization: `Bearer ${token}` }) | ||||||
| 		}, | 		}, | ||||||
| 		body: JSON.stringify({ | 		body: JSON.stringify({ | ||||||
| 			url: url | 			urls: urls | ||||||
| 		}) | 		}) | ||||||
| 	}) | 	}) | ||||||
| 		.then(async (res) => { | 		.then(async (res) => { | ||||||
|  | @ -64,13 +64,13 @@ export const updateOpenAIUrl = async (token: string = '', url: string) => { | ||||||
| 		throw error; | 		throw error; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	return res.OPENAI_API_BASE_URL; | 	return res.OPENAI_API_BASE_URLS; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| export const getOpenAIKey = async (token: string = '') => { | export const getOpenAIKeys = async (token: string = '') => { | ||||||
| 	let error = null; | 	let error = null; | ||||||
| 
 | 
 | ||||||
| 	const res = await fetch(`${OPENAI_API_BASE_URL}/key`, { | 	const res = await fetch(`${OPENAI_API_BASE_URL}/keys`, { | ||||||
| 		method: 'GET', | 		method: 'GET', | ||||||
| 		headers: { | 		headers: { | ||||||
| 			Accept: 'application/json', | 			Accept: 'application/json', | ||||||
|  | @ -96,13 +96,13 @@ export const getOpenAIKey = async (token: string = '') => { | ||||||
| 		throw error; | 		throw error; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	return res.OPENAI_API_KEY; | 	return res.OPENAI_API_KEYS; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| export const updateOpenAIKey = async (token: string = '', key: string) => { | export const updateOpenAIKeys = async (token: string = '', keys: string[]) => { | ||||||
| 	let error = null; | 	let error = null; | ||||||
| 
 | 
 | ||||||
| 	const res = await fetch(`${OPENAI_API_BASE_URL}/key/update`, { | 	const res = await fetch(`${OPENAI_API_BASE_URL}/keys/update`, { | ||||||
| 		method: 'POST', | 		method: 'POST', | ||||||
| 		headers: { | 		headers: { | ||||||
| 			Accept: 'application/json', | 			Accept: 'application/json', | ||||||
|  | @ -110,7 +110,7 @@ export const updateOpenAIKey = async (token: string = '', key: string) => { | ||||||
| 			...(token && { authorization: `Bearer ${token}` }) | 			...(token && { authorization: `Bearer ${token}` }) | ||||||
| 		}, | 		}, | ||||||
| 		body: JSON.stringify({ | 		body: JSON.stringify({ | ||||||
| 			key: key | 			keys: keys | ||||||
| 		}) | 		}) | ||||||
| 	}) | 	}) | ||||||
| 		.then(async (res) => { | 		.then(async (res) => { | ||||||
|  | @ -131,7 +131,7 @@ export const updateOpenAIKey = async (token: string = '', key: string) => { | ||||||
| 		throw error; | 		throw error; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	return res.OPENAI_API_KEY; | 	return res.OPENAI_API_KEYS; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| export const getOpenAIModels = async (token: string = '') => { | export const getOpenAIModels = async (token: string = '') => { | ||||||
|  |  | ||||||
|  | @ -225,33 +225,80 @@ | ||||||
| 		}, 100); | 		}, 100); | ||||||
| 	}; | 	}; | ||||||
| 
 | 
 | ||||||
| 	// TODO: change delete behaviour |  | ||||||
| 	// const deleteMessageAndDescendants = async (messageId: string) => { |  | ||||||
| 	// 	if (history.messages[messageId]) { |  | ||||||
| 	// 		history.messages[messageId].deleted = true; |  | ||||||
| 
 |  | ||||||
| 	// 		for (const childId of history.messages[messageId].childrenIds) { |  | ||||||
| 	// 			await deleteMessageAndDescendants(childId); |  | ||||||
| 	// 		} |  | ||||||
| 	// 	} |  | ||||||
| 	// }; |  | ||||||
| 
 |  | ||||||
| 	// const triggerDeleteMessageRecursive = async (messageId: string) => { |  | ||||||
| 	// 	await deleteMessageAndDescendants(messageId); |  | ||||||
| 	// 	await updateChatById(localStorage.token, chatId, { history }); |  | ||||||
| 	// 	await chats.set(await getChatList(localStorage.token)); |  | ||||||
| 	// }; |  | ||||||
| 
 |  | ||||||
| 	const messageDeleteHandler = async (messageId) => { | 	const messageDeleteHandler = async (messageId) => { | ||||||
| 		if (history.messages[messageId]) { | 		const messageToDelete = history.messages[messageId]; | ||||||
| 			history.messages[messageId].deleted = true; | 		const messageParentId = messageToDelete.parentId; | ||||||
| 
 | 		const messageChildrenIds = messageToDelete.childrenIds ?? []; | ||||||
| 			for (const childId of history.messages[messageId].childrenIds) { | 		const hasSibling = messageChildrenIds.some( | ||||||
| 				history.messages[childId].deleted = true; | 			(childId) => history.messages[childId]?.childrenIds?.length > 0 | ||||||
|  | 		); | ||||||
|  | 		messageChildrenIds.forEach((childId) => { | ||||||
|  | 			const child = history.messages[childId]; | ||||||
|  | 			if (child && child.childrenIds) { | ||||||
|  | 				if (child.childrenIds.length === 0 && !hasSibling) { | ||||||
|  | 					// if last prompt/response pair | ||||||
|  | 					history.messages[messageParentId].childrenIds = []; | ||||||
|  | 					history.currentId = messageParentId; | ||||||
|  | 				} else { | ||||||
|  | 					child.childrenIds.forEach((grandChildId) => { | ||||||
|  | 						if (history.messages[grandChildId]) { | ||||||
|  | 							history.messages[grandChildId].parentId = messageParentId; | ||||||
|  | 							history.messages[messageParentId].childrenIds.push(grandChildId); | ||||||
|  | 						} | ||||||
|  | 					}); | ||||||
|  | 				} | ||||||
| 			} | 			} | ||||||
| 		} | 			// remove response | ||||||
| 		await updateChatById(localStorage.token, chatId, { history }); | 			history.messages[messageParentId].childrenIds = history.messages[ | ||||||
|  | 				messageParentId | ||||||
|  | 			].childrenIds.filter((id) => id !== childId); | ||||||
|  | 		}); | ||||||
|  | 		// remove prompt | ||||||
|  | 		history.messages[messageParentId].childrenIds = history.messages[ | ||||||
|  | 			messageParentId | ||||||
|  | 		].childrenIds.filter((id) => id !== messageId); | ||||||
|  | 		await updateChatById(localStorage.token, chatId, { | ||||||
|  | 			messages: messages, | ||||||
|  | 			history: history | ||||||
|  | 		}); | ||||||
| 	}; | 	}; | ||||||
|  | 
 | ||||||
|  | 	// const messageDeleteHandler = async (messageId) => { | ||||||
|  | 	// 	const message = history.messages[messageId]; | ||||||
|  | 	// 	const parentId = message.parentId; | ||||||
|  | 	// 	const childrenIds = message.childrenIds ?? []; | ||||||
|  | 	// 	const grandchildrenIds = []; | ||||||
|  | 
 | ||||||
|  | 	// 	// Iterate through childrenIds to find grandchildrenIds | ||||||
|  | 	// 	for (const childId of childrenIds) { | ||||||
|  | 	// 		const childMessage = history.messages[childId]; | ||||||
|  | 	// 		const grandChildrenIds = childMessage.childrenIds ?? []; | ||||||
|  | 
 | ||||||
|  | 	// 		for (const grandchildId of grandchildrenIds) { | ||||||
|  | 	// 			const childMessage = history.messages[grandchildId]; | ||||||
|  | 	// 			childMessage.parentId = parentId; | ||||||
|  | 	// 		} | ||||||
|  | 	// 		grandchildrenIds.push(...grandChildrenIds); | ||||||
|  | 	// 	} | ||||||
|  | 
 | ||||||
|  | 	// 	history.messages[parentId].childrenIds.push(...grandchildrenIds); | ||||||
|  | 	// 	history.messages[parentId].childrenIds = history.messages[parentId].childrenIds.filter( | ||||||
|  | 	// 		(id) => id !== messageId | ||||||
|  | 	// 	); | ||||||
|  | 
 | ||||||
|  | 	// 	// Select latest message | ||||||
|  | 	// 	let currentMessageId = grandchildrenIds.at(-1); | ||||||
|  | 	// 	if (currentMessageId) { | ||||||
|  | 	// 		let messageChildrenIds = history.messages[currentMessageId].childrenIds; | ||||||
|  | 	// 		while (messageChildrenIds.length !== 0) { | ||||||
|  | 	// 			currentMessageId = messageChildrenIds.at(-1); | ||||||
|  | 	// 			messageChildrenIds = history.messages[currentMessageId].childrenIds; | ||||||
|  | 	// 		} | ||||||
|  | 	// 		history.currentId = currentMessageId; | ||||||
|  | 	// 	} | ||||||
|  | 
 | ||||||
|  | 	// 	await updateChatById(localStorage.token, chatId, { messages, history }); | ||||||
|  | 	// }; | ||||||
| </script> | </script> | ||||||
| 
 | 
 | ||||||
| {#if messages.length == 0} | {#if messages.length == 0} | ||||||
|  | @ -260,57 +307,55 @@ | ||||||
| 	<div class=" pb-10"> | 	<div class=" pb-10"> | ||||||
| 		{#key chatId} | 		{#key chatId} | ||||||
| 			{#each messages as message, messageIdx} | 			{#each messages as message, messageIdx} | ||||||
| 				{#if !message.deleted} | 				<div class=" w-full"> | ||||||
| 					<div class=" w-full"> | 					<div | ||||||
| 						<div | 						class="flex flex-col justify-between px-5 mb-3 {$settings?.fullScreenMode ?? null | ||||||
| 							class="flex flex-col justify-between px-5 mb-3 {$settings?.fullScreenMode ?? null | 							? 'max-w-full' | ||||||
| 								? 'max-w-full' | 							: 'max-w-3xl'} mx-auto rounded-lg group" | ||||||
| 								: 'max-w-3xl'} mx-auto rounded-lg group" | 					> | ||||||
| 						> | 						{#if message.role === 'user'} | ||||||
| 							{#if message.role === 'user'} | 							<UserMessage | ||||||
| 								<UserMessage | 								on:delete={() => messageDeleteHandler(message.id)} | ||||||
| 									on:delete={() => messageDeleteHandler(message.id)} | 								user={$user} | ||||||
| 									user={$user} | 								{message} | ||||||
| 									{message} | 								isFirstMessage={messageIdx === 0} | ||||||
| 									isFirstMessage={messageIdx === 0} | 								siblings={message.parentId !== null | ||||||
| 									siblings={message.parentId !== null | 									? history.messages[message.parentId]?.childrenIds ?? [] | ||||||
| 										? history.messages[message.parentId]?.childrenIds ?? [] | 									: Object.values(history.messages) | ||||||
| 										: Object.values(history.messages) | 											.filter((message) => message.parentId === null) | ||||||
| 												.filter((message) => message.parentId === null) | 											.map((message) => message.id) ?? []} | ||||||
| 												.map((message) => message.id) ?? []} | 								{confirmEditMessage} | ||||||
| 									{confirmEditMessage} | 								{showPreviousMessage} | ||||||
| 									{showPreviousMessage} | 								{showNextMessage} | ||||||
| 									{showNextMessage} | 								{copyToClipboard} | ||||||
| 									{copyToClipboard} | 							/> | ||||||
| 								/> | 						{:else} | ||||||
| 							{:else} | 							<ResponseMessage | ||||||
| 								<ResponseMessage | 								{message} | ||||||
| 									{message} | 								modelfiles={selectedModelfiles} | ||||||
| 									modelfiles={selectedModelfiles} | 								siblings={history.messages[message.parentId]?.childrenIds ?? []} | ||||||
| 									siblings={history.messages[message.parentId]?.childrenIds ?? []} | 								isLastMessage={messageIdx + 1 === messages.length} | ||||||
| 									isLastMessage={messageIdx + 1 === messages.length} | 								{confirmEditResponseMessage} | ||||||
| 									{confirmEditResponseMessage} | 								{showPreviousMessage} | ||||||
| 									{showPreviousMessage} | 								{showNextMessage} | ||||||
| 									{showNextMessage} | 								{rateMessage} | ||||||
| 									{rateMessage} | 								{copyToClipboard} | ||||||
| 									{copyToClipboard} | 								{continueGeneration} | ||||||
| 									{continueGeneration} | 								{regenerateResponse} | ||||||
| 									{regenerateResponse} | 								on:save={async (e) => { | ||||||
| 									on:save={async (e) => { | 									console.log('save', e); | ||||||
| 										console.log('save', e); |  | ||||||
| 
 | 
 | ||||||
| 										const message = e.detail; | 									const message = e.detail; | ||||||
| 										history.messages[message.id] = message; | 									history.messages[message.id] = message; | ||||||
| 										await updateChatById(localStorage.token, chatId, { | 									await updateChatById(localStorage.token, chatId, { | ||||||
| 											messages: messages, | 										messages: messages, | ||||||
| 											history: history | 										history: history | ||||||
| 										}); | 									}); | ||||||
| 									}} | 								}} | ||||||
| 								/> | 							/> | ||||||
| 							{/if} | 						{/if} | ||||||
| 						</div> |  | ||||||
| 					</div> | 					</div> | ||||||
| 				{/if} | 				</div> | ||||||
| 			{/each} | 			{/each} | ||||||
| 
 | 
 | ||||||
| 			{#if bottomPadding} | 			{#if bottomPadding} | ||||||
|  |  | ||||||
|  | @ -24,6 +24,7 @@ | ||||||
| 	import CodeBlock from './CodeBlock.svelte'; | 	import CodeBlock from './CodeBlock.svelte'; | ||||||
| 	import Image from '$lib/components/common/Image.svelte'; | 	import Image from '$lib/components/common/Image.svelte'; | ||||||
| 	import { WEBUI_BASE_URL } from '$lib/constants'; | 	import { WEBUI_BASE_URL } from '$lib/constants'; | ||||||
|  | 	import Tooltip from '$lib/components/common/Tooltip.svelte'; | ||||||
| 
 | 
 | ||||||
| 	export let modelfiles = []; | 	export let modelfiles = []; | ||||||
| 	export let message; | 	export let message; | ||||||
|  | @ -346,6 +347,7 @@ | ||||||
| 									class=" bg-transparent outline-none w-full resize-none" | 									class=" bg-transparent outline-none w-full resize-none" | ||||||
| 									bind:value={editedContent} | 									bind:value={editedContent} | ||||||
| 									on:input={(e) => { | 									on:input={(e) => { | ||||||
|  | 										e.target.style.height = ''; | ||||||
| 										e.target.style.height = `${e.target.scrollHeight}px`; | 										e.target.style.height = `${e.target.scrollHeight}px`; | ||||||
| 									}} | 									}} | ||||||
| 								/> | 								/> | ||||||
|  | @ -464,189 +466,125 @@ | ||||||
| 											</div> | 											</div> | ||||||
| 										{/if} | 										{/if} | ||||||
| 
 | 
 | ||||||
| 										<button | 										<Tooltip content="Edit" placement="bottom"> | ||||||
| 											class="{isLastMessage |  | ||||||
| 												? 'visible' |  | ||||||
| 												: 'invisible group-hover:visible'} p-1 rounded dark:hover:text-white hover:text-black transition" |  | ||||||
| 											on:click={() => { |  | ||||||
| 												editMessageHandler(); |  | ||||||
| 											}} |  | ||||||
| 										> |  | ||||||
| 											<svg |  | ||||||
| 												xmlns="http://www.w3.org/2000/svg" |  | ||||||
| 												fill="none" |  | ||||||
| 												viewBox="0 0 24 24" |  | ||||||
| 												stroke-width="1.5" |  | ||||||
| 												stroke="currentColor" |  | ||||||
| 												class="w-4 h-4" |  | ||||||
| 											> |  | ||||||
| 												<path |  | ||||||
| 													stroke-linecap="round" |  | ||||||
| 													stroke-linejoin="round" |  | ||||||
| 													d="M16.862 4.487l1.687-1.688a1.875 1.875 0 112.652 2.652L6.832 19.82a4.5 4.5 0 01-1.897 1.13l-2.685.8.8-2.685a4.5 4.5 0 011.13-1.897L16.863 4.487zm0 0L19.5 7.125" |  | ||||||
| 												/> |  | ||||||
| 											</svg> |  | ||||||
| 										</button> |  | ||||||
| 
 |  | ||||||
| 										<button |  | ||||||
| 											class="{isLastMessage |  | ||||||
| 												? 'visible' |  | ||||||
| 												: 'invisible group-hover:visible'} p-1 rounded dark:hover:text-white hover:text-black transition copy-response-button" |  | ||||||
| 											on:click={() => { |  | ||||||
| 												copyToClipboard(message.content); |  | ||||||
| 											}} |  | ||||||
| 										> |  | ||||||
| 											<svg |  | ||||||
| 												xmlns="http://www.w3.org/2000/svg" |  | ||||||
| 												fill="none" |  | ||||||
| 												viewBox="0 0 24 24" |  | ||||||
| 												stroke-width="1.5" |  | ||||||
| 												stroke="currentColor" |  | ||||||
| 												class="w-4 h-4" |  | ||||||
| 											> |  | ||||||
| 												<path |  | ||||||
| 													stroke-linecap="round" |  | ||||||
| 													stroke-linejoin="round" |  | ||||||
| 													d="M15.666 3.888A2.25 2.25 0 0013.5 2.25h-3c-1.03 0-1.9.693-2.166 1.638m7.332 0c.055.194.084.4.084.612v0a.75.75 0 01-.75.75H9a.75.75 0 01-.75-.75v0c0-.212.03-.418.084-.612m7.332 0c.646.049 1.288.11 1.927.184 1.1.128 1.907 1.077 1.907 2.185V19.5a2.25 2.25 0 01-2.25 2.25H6.75A2.25 2.25 0 014.5 19.5V6.257c0-1.108.806-2.057 1.907-2.185a48.208 48.208 0 011.927-.184" |  | ||||||
| 												/> |  | ||||||
| 											</svg> |  | ||||||
| 										</button> |  | ||||||
| 
 |  | ||||||
| 										<button |  | ||||||
| 											class="{isLastMessage |  | ||||||
| 												? 'visible' |  | ||||||
| 												: 'invisible group-hover:visible'} p-1 rounded {message.rating === 1 |  | ||||||
| 												? 'bg-gray-100 dark:bg-gray-800' |  | ||||||
| 												: ''} dark:hover:text-white hover:text-black transition" |  | ||||||
| 											on:click={() => { |  | ||||||
| 												rateMessage(message.id, 1); |  | ||||||
| 											}} |  | ||||||
| 										> |  | ||||||
| 											<svg |  | ||||||
| 												stroke="currentColor" |  | ||||||
| 												fill="none" |  | ||||||
| 												stroke-width="2" |  | ||||||
| 												viewBox="0 0 24 24" |  | ||||||
| 												stroke-linecap="round" |  | ||||||
| 												stroke-linejoin="round" |  | ||||||
| 												class="w-4 h-4" |  | ||||||
| 												xmlns="http://www.w3.org/2000/svg" |  | ||||||
| 												><path |  | ||||||
| 													d="M14 9V5a3 3 0 0 0-3-3l-4 9v11h11.28a2 2 0 0 0 2-1.7l1.38-9a2 2 0 0 0-2-2.3zM7 22H4a2 2 0 0 1-2-2v-7a2 2 0 0 1 2-2h3" |  | ||||||
| 												/></svg |  | ||||||
| 											> |  | ||||||
| 										</button> |  | ||||||
| 										<button |  | ||||||
| 											class="{isLastMessage |  | ||||||
| 												? 'visible' |  | ||||||
| 												: 'invisible group-hover:visible'} p-1 rounded {message.rating === -1 |  | ||||||
| 												? 'bg-gray-100 dark:bg-gray-800' |  | ||||||
| 												: ''} dark:hover:text-white hover:text-black transition" |  | ||||||
| 											on:click={() => { |  | ||||||
| 												rateMessage(message.id, -1); |  | ||||||
| 											}} |  | ||||||
| 										> |  | ||||||
| 											<svg |  | ||||||
| 												stroke="currentColor" |  | ||||||
| 												fill="none" |  | ||||||
| 												stroke-width="2" |  | ||||||
| 												viewBox="0 0 24 24" |  | ||||||
| 												stroke-linecap="round" |  | ||||||
| 												stroke-linejoin="round" |  | ||||||
| 												class="w-4 h-4" |  | ||||||
| 												xmlns="http://www.w3.org/2000/svg" |  | ||||||
| 												><path |  | ||||||
| 													d="M10 15v4a3 3 0 0 0 3 3l4-9V2H5.72a2 2 0 0 0-2 1.7l-1.38 9a2 2 0 0 0 2 2.3zm7-13h2.67A2.31 2.31 0 0 1 22 4v7a2.31 2.31 0 0 1-2.33 2H17" |  | ||||||
| 												/></svg |  | ||||||
| 											> |  | ||||||
| 										</button> |  | ||||||
| 
 |  | ||||||
| 										<button |  | ||||||
| 											id="speak-button-{message.id}" |  | ||||||
| 											class="{isLastMessage |  | ||||||
| 												? 'visible' |  | ||||||
| 												: 'invisible group-hover:visible'} p-1 rounded dark:hover:text-white hover:text-black transition" |  | ||||||
| 											on:click={() => { |  | ||||||
| 												if (!loadingSpeech) { |  | ||||||
| 													toggleSpeakMessage(message); |  | ||||||
| 												} |  | ||||||
| 											}} |  | ||||||
| 										> |  | ||||||
| 											{#if loadingSpeech} |  | ||||||
| 												<svg |  | ||||||
| 													class=" w-4 h-4" |  | ||||||
| 													fill="currentColor" |  | ||||||
| 													viewBox="0 0 24 24" |  | ||||||
| 													xmlns="http://www.w3.org/2000/svg" |  | ||||||
| 													><style> |  | ||||||
| 														.spinner_S1WN { |  | ||||||
| 															animation: spinner_MGfb 0.8s linear infinite; |  | ||||||
| 															animation-delay: -0.8s; |  | ||||||
| 														} |  | ||||||
| 														.spinner_Km9P { |  | ||||||
| 															animation-delay: -0.65s; |  | ||||||
| 														} |  | ||||||
| 														.spinner_JApP { |  | ||||||
| 															animation-delay: -0.5s; |  | ||||||
| 														} |  | ||||||
| 														@keyframes spinner_MGfb { |  | ||||||
| 															93.75%, |  | ||||||
| 															100% { |  | ||||||
| 																opacity: 0.2; |  | ||||||
| 															} |  | ||||||
| 														} |  | ||||||
| 													</style><circle class="spinner_S1WN" cx="4" cy="12" r="3" /><circle |  | ||||||
| 														class="spinner_S1WN spinner_Km9P" |  | ||||||
| 														cx="12" |  | ||||||
| 														cy="12" |  | ||||||
| 														r="3" |  | ||||||
| 													/><circle class="spinner_S1WN spinner_JApP" cx="20" cy="12" r="3" /></svg |  | ||||||
| 												> |  | ||||||
| 											{:else if speaking} |  | ||||||
| 												<svg |  | ||||||
| 													xmlns="http://www.w3.org/2000/svg" |  | ||||||
| 													fill="none" |  | ||||||
| 													viewBox="0 0 24 24" |  | ||||||
| 													stroke-width="1.5" |  | ||||||
| 													stroke="currentColor" |  | ||||||
| 													class="w-4 h-4" |  | ||||||
| 												> |  | ||||||
| 													<path |  | ||||||
| 														stroke-linecap="round" |  | ||||||
| 														stroke-linejoin="round" |  | ||||||
| 														d="M17.25 9.75 19.5 12m0 0 2.25 2.25M19.5 12l2.25-2.25M19.5 12l-2.25 2.25m-10.5-6 4.72-4.72a.75.75 0 0 1 1.28.53v15.88a.75.75 0 0 1-1.28.53l-4.72-4.72H4.51c-.88 0-1.704-.507-1.938-1.354A9.009 9.009 0 0 1 2.25 12c0-.83.112-1.633.322-2.396C2.806 8.756 3.63 8.25 4.51 8.25H6.75Z" |  | ||||||
| 													/> |  | ||||||
| 												</svg> |  | ||||||
| 											{:else} |  | ||||||
| 												<svg |  | ||||||
| 													xmlns="http://www.w3.org/2000/svg" |  | ||||||
| 													fill="none" |  | ||||||
| 													viewBox="0 0 24 24" |  | ||||||
| 													stroke-width="1.5" |  | ||||||
| 													stroke="currentColor" |  | ||||||
| 													class="w-4 h-4" |  | ||||||
| 												> |  | ||||||
| 													<path |  | ||||||
| 														stroke-linecap="round" |  | ||||||
| 														stroke-linejoin="round" |  | ||||||
| 														d="M19.114 5.636a9 9 0 010 12.728M16.463 8.288a5.25 5.25 0 010 7.424M6.75 8.25l4.72-4.72a.75.75 0 011.28.53v15.88a.75.75 0 01-1.28.53l-4.72-4.72H4.51c-.88 0-1.704-.507-1.938-1.354A9.01 9.01 0 012.25 12c0-.83.112-1.633.322-2.396C2.806 8.756 3.63 8.25 4.51 8.25H6.75z" |  | ||||||
| 													/> |  | ||||||
| 												</svg> |  | ||||||
| 											{/if} |  | ||||||
| 										</button> |  | ||||||
| 
 |  | ||||||
| 										{#if $config.images} |  | ||||||
| 											<button | 											<button | ||||||
| 												class="{isLastMessage | 												class="{isLastMessage | ||||||
| 													? 'visible' | 													? 'visible' | ||||||
| 													: 'invisible group-hover:visible'} p-1 rounded dark:hover:text-white hover:text-black transition" | 													: 'invisible group-hover:visible'} p-1 rounded dark:hover:text-white hover:text-black transition" | ||||||
| 												on:click={() => { | 												on:click={() => { | ||||||
| 													if (!generatingImage) { | 													editMessageHandler(); | ||||||
| 														generateImage(message); | 												}} | ||||||
|  | 											> | ||||||
|  | 												<svg | ||||||
|  | 													xmlns="http://www.w3.org/2000/svg" | ||||||
|  | 													fill="none" | ||||||
|  | 													viewBox="0 0 24 24" | ||||||
|  | 													stroke-width="1.5" | ||||||
|  | 													stroke="currentColor" | ||||||
|  | 													class="w-4 h-4" | ||||||
|  | 												> | ||||||
|  | 													<path | ||||||
|  | 														stroke-linecap="round" | ||||||
|  | 														stroke-linejoin="round" | ||||||
|  | 														d="M16.862 4.487l1.687-1.688a1.875 1.875 0 112.652 2.652L6.832 19.82a4.5 4.5 0 01-1.897 1.13l-2.685.8.8-2.685a4.5 4.5 0 011.13-1.897L16.863 4.487zm0 0L19.5 7.125" | ||||||
|  | 													/> | ||||||
|  | 												</svg> | ||||||
|  | 											</button> | ||||||
|  | 										</Tooltip> | ||||||
|  | 
 | ||||||
|  | 										<Tooltip content="Copy" placement="bottom"> | ||||||
|  | 											<button | ||||||
|  | 												class="{isLastMessage | ||||||
|  | 													? 'visible' | ||||||
|  | 													: 'invisible group-hover:visible'} p-1 rounded dark:hover:text-white hover:text-black transition copy-response-button" | ||||||
|  | 												on:click={() => { | ||||||
|  | 													copyToClipboard(message.content); | ||||||
|  | 												}} | ||||||
|  | 											> | ||||||
|  | 												<svg | ||||||
|  | 													xmlns="http://www.w3.org/2000/svg" | ||||||
|  | 													fill="none" | ||||||
|  | 													viewBox="0 0 24 24" | ||||||
|  | 													stroke-width="1.5" | ||||||
|  | 													stroke="currentColor" | ||||||
|  | 													class="w-4 h-4" | ||||||
|  | 												> | ||||||
|  | 													<path | ||||||
|  | 														stroke-linecap="round" | ||||||
|  | 														stroke-linejoin="round" | ||||||
|  | 														d="M15.666 3.888A2.25 2.25 0 0013.5 2.25h-3c-1.03 0-1.9.693-2.166 1.638m7.332 0c.055.194.084.4.084.612v0a.75.75 0 01-.75.75H9a.75.75 0 01-.75-.75v0c0-.212.03-.418.084-.612m7.332 0c.646.049 1.288.11 1.927.184 1.1.128 1.907 1.077 1.907 2.185V19.5a2.25 2.25 0 01-2.25 2.25H6.75A2.25 2.25 0 014.5 19.5V6.257c0-1.108.806-2.057 1.907-2.185a48.208 48.208 0 011.927-.184" | ||||||
|  | 													/> | ||||||
|  | 												</svg> | ||||||
|  | 											</button> | ||||||
|  | 										</Tooltip> | ||||||
|  | 
 | ||||||
|  | 										<Tooltip content="Good Response" placement="bottom"> | ||||||
|  | 											<button | ||||||
|  | 												class="{isLastMessage | ||||||
|  | 													? 'visible' | ||||||
|  | 													: 'invisible group-hover:visible'} p-1 rounded {message.rating === 1 | ||||||
|  | 													? 'bg-gray-100 dark:bg-gray-800' | ||||||
|  | 													: ''} dark:hover:text-white hover:text-black transition" | ||||||
|  | 												on:click={() => { | ||||||
|  | 													rateMessage(message.id, 1); | ||||||
|  | 												}} | ||||||
|  | 											> | ||||||
|  | 												<svg | ||||||
|  | 													stroke="currentColor" | ||||||
|  | 													fill="none" | ||||||
|  | 													stroke-width="2" | ||||||
|  | 													viewBox="0 0 24 24" | ||||||
|  | 													stroke-linecap="round" | ||||||
|  | 													stroke-linejoin="round" | ||||||
|  | 													class="w-4 h-4" | ||||||
|  | 													xmlns="http://www.w3.org/2000/svg" | ||||||
|  | 													><path | ||||||
|  | 														d="M14 9V5a3 3 0 0 0-3-3l-4 9v11h11.28a2 2 0 0 0 2-1.7l1.38-9a2 2 0 0 0-2-2.3zM7 22H4a2 2 0 0 1-2-2v-7a2 2 0 0 1 2-2h3" | ||||||
|  | 													/></svg | ||||||
|  | 												> | ||||||
|  | 											</button> | ||||||
|  | 										</Tooltip> | ||||||
|  | 
 | ||||||
|  | 										<Tooltip content="Bad Response" placement="bottom"> | ||||||
|  | 											<button | ||||||
|  | 												class="{isLastMessage | ||||||
|  | 													? 'visible' | ||||||
|  | 													: 'invisible group-hover:visible'} p-1 rounded {message.rating === -1 | ||||||
|  | 													? 'bg-gray-100 dark:bg-gray-800' | ||||||
|  | 													: ''} dark:hover:text-white hover:text-black transition" | ||||||
|  | 												on:click={() => { | ||||||
|  | 													rateMessage(message.id, -1); | ||||||
|  | 												}} | ||||||
|  | 											> | ||||||
|  | 												<svg | ||||||
|  | 													stroke="currentColor" | ||||||
|  | 													fill="none" | ||||||
|  | 													stroke-width="2" | ||||||
|  | 													viewBox="0 0 24 24" | ||||||
|  | 													stroke-linecap="round" | ||||||
|  | 													stroke-linejoin="round" | ||||||
|  | 													class="w-4 h-4" | ||||||
|  | 													xmlns="http://www.w3.org/2000/svg" | ||||||
|  | 													><path | ||||||
|  | 														d="M10 15v4a3 3 0 0 0 3 3l4-9V2H5.72a2 2 0 0 0-2 1.7l-1.38 9a2 2 0 0 0 2 2.3zm7-13h2.67A2.31 2.31 0 0 1 22 4v7a2.31 2.31 0 0 1-2.33 2H17" | ||||||
|  | 													/></svg | ||||||
|  | 												> | ||||||
|  | 											</button> | ||||||
|  | 										</Tooltip> | ||||||
|  | 
 | ||||||
|  | 										<Tooltip content="Read Aloud" placement="bottom"> | ||||||
|  | 											<button | ||||||
|  | 												id="speak-button-{message.id}" | ||||||
|  | 												class="{isLastMessage | ||||||
|  | 													? 'visible' | ||||||
|  | 													: 'invisible group-hover:visible'} p-1 rounded dark:hover:text-white hover:text-black transition" | ||||||
|  | 												on:click={() => { | ||||||
|  | 													if (!loadingSpeech) { | ||||||
|  | 														toggleSpeakMessage(message); | ||||||
| 													} | 													} | ||||||
| 												}} | 												}} | ||||||
| 											> | 											> | ||||||
| 												{#if generatingImage} | 												{#if loadingSpeech} | ||||||
| 													<svg | 													<svg | ||||||
| 														class=" w-4 h-4" | 														class=" w-4 h-4" | ||||||
| 														fill="currentColor" | 														fill="currentColor" | ||||||
|  | @ -681,6 +619,21 @@ | ||||||
| 															r="3" | 															r="3" | ||||||
| 														/></svg | 														/></svg | ||||||
| 													> | 													> | ||||||
|  | 												{:else if speaking} | ||||||
|  | 													<svg | ||||||
|  | 														xmlns="http://www.w3.org/2000/svg" | ||||||
|  | 														fill="none" | ||||||
|  | 														viewBox="0 0 24 24" | ||||||
|  | 														stroke-width="1.5" | ||||||
|  | 														stroke="currentColor" | ||||||
|  | 														class="w-4 h-4" | ||||||
|  | 													> | ||||||
|  | 														<path | ||||||
|  | 															stroke-linecap="round" | ||||||
|  | 															stroke-linejoin="round" | ||||||
|  | 															d="M17.25 9.75 19.5 12m0 0 2.25 2.25M19.5 12l2.25-2.25M19.5 12l-2.25 2.25m-10.5-6 4.72-4.72a.75.75 0 0 1 1.28.53v15.88a.75.75 0 0 1-1.28.53l-4.72-4.72H4.51c-.88 0-1.704-.507-1.938-1.354A9.009 9.009 0 0 1 2.25 12c0-.83.112-1.633.322-2.396C2.806 8.756 3.63 8.25 4.51 8.25H6.75Z" | ||||||
|  | 														/> | ||||||
|  | 													</svg> | ||||||
| 												{:else} | 												{:else} | ||||||
| 													<svg | 													<svg | ||||||
| 														xmlns="http://www.w3.org/2000/svg" | 														xmlns="http://www.w3.org/2000/svg" | ||||||
|  | @ -693,93 +646,166 @@ | ||||||
| 														<path | 														<path | ||||||
| 															stroke-linecap="round" | 															stroke-linecap="round" | ||||||
| 															stroke-linejoin="round" | 															stroke-linejoin="round" | ||||||
| 															d="m2.25 15.75 5.159-5.159a2.25 2.25 0 0 1 3.182 0l5.159 5.159m-1.5-1.5 1.409-1.409a2.25 2.25 0 0 1 3.182 0l2.909 2.909m-18 3.75h16.5a1.5 1.5 0 0 0 1.5-1.5V6a1.5 1.5 0 0 0-1.5-1.5H3.75A1.5 1.5 0 0 0 2.25 6v12a1.5 1.5 0 0 0 1.5 1.5Zm10.5-11.25h.008v.008h-.008V8.25Zm.375 0a.375.375 0 1 1-.75 0 .375.375 0 0 1 .75 0Z" | 															d="M19.114 5.636a9 9 0 010 12.728M16.463 8.288a5.25 5.25 0 010 7.424M6.75 8.25l4.72-4.72a.75.75 0 011.28.53v15.88a.75.75 0 01-1.28.53l-4.72-4.72H4.51c-.88 0-1.704-.507-1.938-1.354A9.01 9.01 0 012.25 12c0-.83.112-1.633.322-2.396C2.806 8.756 3.63 8.25 4.51 8.25H6.75z" | ||||||
| 														/> | 														/> | ||||||
| 													</svg> | 													</svg> | ||||||
| 												{/if} | 												{/if} | ||||||
| 											</button> | 											</button> | ||||||
|  | 										</Tooltip> | ||||||
|  | 
 | ||||||
|  | 										{#if $config.images} | ||||||
|  | 											<Tooltip content="Generate Image" placement="bottom"> | ||||||
|  | 												<button | ||||||
|  | 													class="{isLastMessage | ||||||
|  | 														? 'visible' | ||||||
|  | 														: 'invisible group-hover:visible'} p-1 rounded dark:hover:text-white hover:text-black transition" | ||||||
|  | 													on:click={() => { | ||||||
|  | 														if (!generatingImage) { | ||||||
|  | 															generateImage(message); | ||||||
|  | 														} | ||||||
|  | 													}} | ||||||
|  | 												> | ||||||
|  | 													{#if generatingImage} | ||||||
|  | 														<svg | ||||||
|  | 															class=" w-4 h-4" | ||||||
|  | 															fill="currentColor" | ||||||
|  | 															viewBox="0 0 24 24" | ||||||
|  | 															xmlns="http://www.w3.org/2000/svg" | ||||||
|  | 															><style> | ||||||
|  | 																.spinner_S1WN { | ||||||
|  | 																	animation: spinner_MGfb 0.8s linear infinite; | ||||||
|  | 																	animation-delay: -0.8s; | ||||||
|  | 																} | ||||||
|  | 																.spinner_Km9P { | ||||||
|  | 																	animation-delay: -0.65s; | ||||||
|  | 																} | ||||||
|  | 																.spinner_JApP { | ||||||
|  | 																	animation-delay: -0.5s; | ||||||
|  | 																} | ||||||
|  | 																@keyframes spinner_MGfb { | ||||||
|  | 																	93.75%, | ||||||
|  | 																	100% { | ||||||
|  | 																		opacity: 0.2; | ||||||
|  | 																	} | ||||||
|  | 																} | ||||||
|  | 															</style><circle class="spinner_S1WN" cx="4" cy="12" r="3" /><circle | ||||||
|  | 																class="spinner_S1WN spinner_Km9P" | ||||||
|  | 																cx="12" | ||||||
|  | 																cy="12" | ||||||
|  | 																r="3" | ||||||
|  | 															/><circle | ||||||
|  | 																class="spinner_S1WN spinner_JApP" | ||||||
|  | 																cx="20" | ||||||
|  | 																cy="12" | ||||||
|  | 																r="3" | ||||||
|  | 															/></svg | ||||||
|  | 														> | ||||||
|  | 													{:else} | ||||||
|  | 														<svg | ||||||
|  | 															xmlns="http://www.w3.org/2000/svg" | ||||||
|  | 															fill="none" | ||||||
|  | 															viewBox="0 0 24 24" | ||||||
|  | 															stroke-width="1.5" | ||||||
|  | 															stroke="currentColor" | ||||||
|  | 															class="w-4 h-4" | ||||||
|  | 														> | ||||||
|  | 															<path | ||||||
|  | 																stroke-linecap="round" | ||||||
|  | 																stroke-linejoin="round" | ||||||
|  | 																d="m2.25 15.75 5.159-5.159a2.25 2.25 0 0 1 3.182 0l5.159 5.159m-1.5-1.5 1.409-1.409a2.25 2.25 0 0 1 3.182 0l2.909 2.909m-18 3.75h16.5a1.5 1.5 0 0 0 1.5-1.5V6a1.5 1.5 0 0 0-1.5-1.5H3.75A1.5 1.5 0 0 0 2.25 6v12a1.5 1.5 0 0 0 1.5 1.5Zm10.5-11.25h.008v.008h-.008V8.25Zm.375 0a.375.375 0 1 1-.75 0 .375.375 0 0 1 .75 0Z" | ||||||
|  | 															/> | ||||||
|  | 														</svg> | ||||||
|  | 													{/if} | ||||||
|  | 												</button> | ||||||
|  | 											</Tooltip> | ||||||
| 										{/if} | 										{/if} | ||||||
| 
 | 
 | ||||||
| 										{#if message.info} | 										{#if message.info} | ||||||
| 											<button | 											<Tooltip content="Generation Info" placement="bottom"> | ||||||
| 												class=" {isLastMessage | 												<button | ||||||
| 													? 'visible' | 													class=" {isLastMessage | ||||||
| 													: 'invisible group-hover:visible'} p-1 rounded dark:hover:text-white hover:text-black transition whitespace-pre-wrap" | 														? 'visible' | ||||||
| 												on:click={() => { | 														: 'invisible group-hover:visible'} p-1 rounded dark:hover:text-white hover:text-black transition whitespace-pre-wrap" | ||||||
| 													console.log(message); | 													on:click={() => { | ||||||
| 												}} | 														console.log(message); | ||||||
| 												id="info-{message.id}" | 													}} | ||||||
| 											> | 													id="info-{message.id}" | ||||||
| 												<svg |  | ||||||
| 													xmlns="http://www.w3.org/2000/svg" |  | ||||||
| 													fill="none" |  | ||||||
| 													viewBox="0 0 24 24" |  | ||||||
| 													stroke-width="1.5" |  | ||||||
| 													stroke="currentColor" |  | ||||||
| 													class="w-4 h-4" |  | ||||||
| 												> | 												> | ||||||
| 													<path | 													<svg | ||||||
| 														stroke-linecap="round" | 														xmlns="http://www.w3.org/2000/svg" | ||||||
| 														stroke-linejoin="round" | 														fill="none" | ||||||
| 														d="M11.25 11.25l.041-.02a.75.75 0 011.063.852l-.708 2.836a.75.75 0 001.063.853l.041-.021M21 12a9 9 0 11-18 0 9 9 0 0118 0zm-9-3.75h.008v.008H12V8.25z" | 														viewBox="0 0 24 24" | ||||||
| 													/> | 														stroke-width="1.5" | ||||||
| 												</svg> | 														stroke="currentColor" | ||||||
| 											</button> | 														class="w-4 h-4" | ||||||
|  | 													> | ||||||
|  | 														<path | ||||||
|  | 															stroke-linecap="round" | ||||||
|  | 															stroke-linejoin="round" | ||||||
|  | 															d="M11.25 11.25l.041-.02a.75.75 0 011.063.852l-.708 2.836a.75.75 0 001.063.853l.041-.021M21 12a9 9 0 11-18 0 9 9 0 0118 0zm-9-3.75h.008v.008H12V8.25z" | ||||||
|  | 														/> | ||||||
|  | 													</svg> | ||||||
|  | 												</button> | ||||||
|  | 											</Tooltip> | ||||||
| 										{/if} | 										{/if} | ||||||
| 
 | 
 | ||||||
| 										{#if isLastMessage} | 										{#if isLastMessage} | ||||||
| 											<button | 											<Tooltip content="Continue Response" placement="bottom"> | ||||||
| 												type="button" | 												<button | ||||||
| 												class="{isLastMessage | 													type="button" | ||||||
| 													? 'visible' | 													class="{isLastMessage | ||||||
| 													: 'invisible group-hover:visible'} p-1 rounded dark:hover:text-white hover:text-black transition regenerate-response-button" | 														? 'visible' | ||||||
| 												on:click={() => { | 														: 'invisible group-hover:visible'} p-1 rounded dark:hover:text-white hover:text-black transition regenerate-response-button" | ||||||
| 													continueGeneration(); | 													on:click={() => { | ||||||
| 												}} | 														continueGeneration(); | ||||||
| 											> | 													}} | ||||||
| 												<svg |  | ||||||
| 													xmlns="http://www.w3.org/2000/svg" |  | ||||||
| 													fill="none" |  | ||||||
| 													viewBox="0 0 24 24" |  | ||||||
| 													stroke-width="1.5" |  | ||||||
| 													stroke="currentColor" |  | ||||||
| 													class="w-4 h-4" |  | ||||||
| 												> | 												> | ||||||
| 													<path | 													<svg | ||||||
| 														stroke-linecap="round" | 														xmlns="http://www.w3.org/2000/svg" | ||||||
| 														stroke-linejoin="round" | 														fill="none" | ||||||
| 														d="M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z" | 														viewBox="0 0 24 24" | ||||||
| 													/> | 														stroke-width="1.5" | ||||||
| 													<path | 														stroke="currentColor" | ||||||
| 														stroke-linecap="round" | 														class="w-4 h-4" | ||||||
| 														stroke-linejoin="round" | 													> | ||||||
| 														d="M15.91 11.672a.375.375 0 0 1 0 .656l-5.603 3.113a.375.375 0 0 1-.557-.328V8.887c0-.286.307-.466.557-.327l5.603 3.112Z" | 														<path | ||||||
| 													/> | 															stroke-linecap="round" | ||||||
| 												</svg> | 															stroke-linejoin="round" | ||||||
| 											</button> | 															d="M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z" | ||||||
|  | 														/> | ||||||
|  | 														<path | ||||||
|  | 															stroke-linecap="round" | ||||||
|  | 															stroke-linejoin="round" | ||||||
|  | 															d="M15.91 11.672a.375.375 0 0 1 0 .656l-5.603 3.113a.375.375 0 0 1-.557-.328V8.887c0-.286.307-.466.557-.327l5.603 3.112Z" | ||||||
|  | 														/> | ||||||
|  | 													</svg> | ||||||
|  | 												</button> | ||||||
|  | 											</Tooltip> | ||||||
| 
 | 
 | ||||||
| 											<button | 											<Tooltip content="Regenerate" placement="bottom"> | ||||||
| 												type="button" | 												<button | ||||||
| 												class="{isLastMessage | 													type="button" | ||||||
| 													? 'visible' | 													class="{isLastMessage | ||||||
| 													: 'invisible group-hover:visible'} p-1 rounded dark:hover:text-white hover:text-black transition regenerate-response-button" | 														? 'visible' | ||||||
| 												on:click={regenerateResponse} | 														: 'invisible group-hover:visible'} p-1 rounded dark:hover:text-white hover:text-black transition regenerate-response-button" | ||||||
| 											> | 													on:click={regenerateResponse} | ||||||
| 												<svg |  | ||||||
| 													xmlns="http://www.w3.org/2000/svg" |  | ||||||
| 													fill="none" |  | ||||||
| 													viewBox="0 0 24 24" |  | ||||||
| 													stroke-width="1.5" |  | ||||||
| 													stroke="currentColor" |  | ||||||
| 													class="w-4 h-4" |  | ||||||
| 												> | 												> | ||||||
| 													<path | 													<svg | ||||||
| 														stroke-linecap="round" | 														xmlns="http://www.w3.org/2000/svg" | ||||||
| 														stroke-linejoin="round" | 														fill="none" | ||||||
| 														d="M16.023 9.348h4.992v-.001M2.985 19.644v-4.992m0 0h4.992m-4.993 0l3.181 3.183a8.25 8.25 0 0013.803-3.7M4.031 9.865a8.25 8.25 0 0113.803-3.7l3.181 3.182m0-4.991v4.99" | 														viewBox="0 0 24 24" | ||||||
| 													/> | 														stroke-width="1.5" | ||||||
| 												</svg> | 														stroke="currentColor" | ||||||
| 											</button> | 														class="w-4 h-4" | ||||||
|  | 													> | ||||||
|  | 														<path | ||||||
|  | 															stroke-linecap="round" | ||||||
|  | 															stroke-linejoin="round" | ||||||
|  | 															d="M16.023 9.348h4.992v-.001M2.985 19.644v-4.992m0 0h4.992m-4.993 0l3.181 3.183a8.25 8.25 0 0013.803-3.7M4.031 9.865a8.25 8.25 0 0113.803-3.7l3.181 3.182m0-4.991v4.99" | ||||||
|  | 														/> | ||||||
|  | 													</svg> | ||||||
|  | 												</button> | ||||||
|  | 											</Tooltip> | ||||||
| 										{/if} | 										{/if} | ||||||
| 									</div> | 									</div> | ||||||
| 								{/if} | 								{/if} | ||||||
|  |  | ||||||
|  | @ -5,6 +5,7 @@ | ||||||
| 	import Name from './Name.svelte'; | 	import Name from './Name.svelte'; | ||||||
| 	import ProfileImage from './ProfileImage.svelte'; | 	import ProfileImage from './ProfileImage.svelte'; | ||||||
| 	import { modelfiles, settings } from '$lib/stores'; | 	import { modelfiles, settings } from '$lib/stores'; | ||||||
|  | 	import Tooltip from '$lib/components/common/Tooltip.svelte'; | ||||||
| 
 | 
 | ||||||
| 	const i18n = getContext('i18n'); | 	const i18n = getContext('i18n'); | ||||||
| 
 | 
 | ||||||
|  | @ -171,7 +172,8 @@ | ||||||
| 						class=" bg-transparent outline-none w-full resize-none" | 						class=" bg-transparent outline-none w-full resize-none" | ||||||
| 						bind:value={editedContent} | 						bind:value={editedContent} | ||||||
| 						on:input={(e) => { | 						on:input={(e) => { | ||||||
| 							messageEditTextAreaElement.style.height = `${messageEditTextAreaElement.scrollHeight}px`; | 							e.target.style.height = ''; | ||||||
|  | 							e.target.style.height = `${e.target.scrollHeight}px`; | ||||||
| 						}} | 						}} | ||||||
| 					/> | 					/> | ||||||
| 
 | 
 | ||||||
|  | @ -248,55 +250,11 @@ | ||||||
| 							</div> | 							</div> | ||||||
| 						{/if} | 						{/if} | ||||||
| 
 | 
 | ||||||
| 						<button | 						<Tooltip content="Edit" placement="bottom"> | ||||||
| 							class="invisible group-hover:visible p-1 rounded dark:hover:text-white hover:text-black transition edit-user-message-button" |  | ||||||
| 							on:click={() => { |  | ||||||
| 								editMessageHandler(); |  | ||||||
| 							}} |  | ||||||
| 						> |  | ||||||
| 							<svg |  | ||||||
| 								xmlns="http://www.w3.org/2000/svg" |  | ||||||
| 								fill="none" |  | ||||||
| 								viewBox="0 0 24 24" |  | ||||||
| 								stroke-width="1.5" |  | ||||||
| 								stroke="currentColor" |  | ||||||
| 								class="w-4 h-4" |  | ||||||
| 							> |  | ||||||
| 								<path |  | ||||||
| 									stroke-linecap="round" |  | ||||||
| 									stroke-linejoin="round" |  | ||||||
| 									d="M16.862 4.487l1.687-1.688a1.875 1.875 0 112.652 2.652L6.832 19.82a4.5 4.5 0 01-1.897 1.13l-2.685.8.8-2.685a4.5 4.5 0 011.13-1.897L16.863 4.487zm0 0L19.5 7.125" |  | ||||||
| 								/> |  | ||||||
| 							</svg> |  | ||||||
| 						</button> |  | ||||||
| 
 |  | ||||||
| 						<button |  | ||||||
| 							class="invisible group-hover:visible p-1 rounded dark:hover:text-white hover:text-black transition" |  | ||||||
| 							on:click={() => { |  | ||||||
| 								copyToClipboard(message.content); |  | ||||||
| 							}} |  | ||||||
| 						> |  | ||||||
| 							<svg |  | ||||||
| 								xmlns="http://www.w3.org/2000/svg" |  | ||||||
| 								fill="none" |  | ||||||
| 								viewBox="0 0 24 24" |  | ||||||
| 								stroke-width="1.5" |  | ||||||
| 								stroke="currentColor" |  | ||||||
| 								class="w-4 h-4" |  | ||||||
| 							> |  | ||||||
| 								<path |  | ||||||
| 									stroke-linecap="round" |  | ||||||
| 									stroke-linejoin="round" |  | ||||||
| 									d="M15.666 3.888A2.25 2.25 0 0013.5 2.25h-3c-1.03 0-1.9.693-2.166 1.638m7.332 0c.055.194.084.4.084.612v0a.75.75 0 01-.75.75H9a.75.75 0 01-.75-.75v0c0-.212.03-.418.084-.612m7.332 0c.646.049 1.288.11 1.927.184 1.1.128 1.907 1.077 1.907 2.185V19.5a2.25 2.25 0 01-2.25 2.25H6.75A2.25 2.25 0 014.5 19.5V6.257c0-1.108.806-2.057 1.907-2.185a48.208 48.208 0 011.927-.184" |  | ||||||
| 								/> |  | ||||||
| 							</svg> |  | ||||||
| 						</button> |  | ||||||
| 
 |  | ||||||
| 						{#if !isFirstMessage} |  | ||||||
| 							<button | 							<button | ||||||
| 								class="invisible group-hover:visible p-1 rounded dark:hover:text-white hover:text-black transition" | 								class="invisible group-hover:visible p-1 rounded dark:hover:text-white hover:text-black transition edit-user-message-button" | ||||||
| 								on:click={() => { | 								on:click={() => { | ||||||
| 									deleteMessageHandler(); | 									editMessageHandler(); | ||||||
| 								}} | 								}} | ||||||
| 							> | 							> | ||||||
| 								<svg | 								<svg | ||||||
|  | @ -310,10 +268,60 @@ | ||||||
| 									<path | 									<path | ||||||
| 										stroke-linecap="round" | 										stroke-linecap="round" | ||||||
| 										stroke-linejoin="round" | 										stroke-linejoin="round" | ||||||
| 										d="m14.74 9-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 0 1-2.244 2.077H8.084a2.25 2.25 0 0 1-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 0 0-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 0 1 3.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 0 0-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 0 0-7.5 0" | 										d="M16.862 4.487l1.687-1.688a1.875 1.875 0 112.652 2.652L6.832 19.82a4.5 4.5 0 01-1.897 1.13l-2.685.8.8-2.685a4.5 4.5 0 011.13-1.897L16.863 4.487zm0 0L19.5 7.125" | ||||||
| 									/> | 									/> | ||||||
| 								</svg> | 								</svg> | ||||||
| 							</button> | 							</button> | ||||||
|  | 						</Tooltip> | ||||||
|  | 
 | ||||||
|  | 						<Tooltip content="Copy" placement="bottom"> | ||||||
|  | 							<button | ||||||
|  | 								class="invisible group-hover:visible p-1 rounded dark:hover:text-white hover:text-black transition" | ||||||
|  | 								on:click={() => { | ||||||
|  | 									copyToClipboard(message.content); | ||||||
|  | 								}} | ||||||
|  | 							> | ||||||
|  | 								<svg | ||||||
|  | 									xmlns="http://www.w3.org/2000/svg" | ||||||
|  | 									fill="none" | ||||||
|  | 									viewBox="0 0 24 24" | ||||||
|  | 									stroke-width="1.5" | ||||||
|  | 									stroke="currentColor" | ||||||
|  | 									class="w-4 h-4" | ||||||
|  | 								> | ||||||
|  | 									<path | ||||||
|  | 										stroke-linecap="round" | ||||||
|  | 										stroke-linejoin="round" | ||||||
|  | 										d="M15.666 3.888A2.25 2.25 0 0013.5 2.25h-3c-1.03 0-1.9.693-2.166 1.638m7.332 0c.055.194.084.4.084.612v0a.75.75 0 01-.75.75H9a.75.75 0 01-.75-.75v0c0-.212.03-.418.084-.612m7.332 0c.646.049 1.288.11 1.927.184 1.1.128 1.907 1.077 1.907 2.185V19.5a2.25 2.25 0 01-2.25 2.25H6.75A2.25 2.25 0 014.5 19.5V6.257c0-1.108.806-2.057 1.907-2.185a48.208 48.208 0 011.927-.184" | ||||||
|  | 									/> | ||||||
|  | 								</svg> | ||||||
|  | 							</button> | ||||||
|  | 						</Tooltip> | ||||||
|  | 
 | ||||||
|  | 						{#if !isFirstMessage} | ||||||
|  | 							<Tooltip content="Delete" placement="bottom"> | ||||||
|  | 								<button | ||||||
|  | 									class="invisible group-hover:visible p-1 rounded dark:hover:text-white hover:text-black transition" | ||||||
|  | 									on:click={() => { | ||||||
|  | 										deleteMessageHandler(); | ||||||
|  | 									}} | ||||||
|  | 								> | ||||||
|  | 									<svg | ||||||
|  | 										xmlns="http://www.w3.org/2000/svg" | ||||||
|  | 										fill="none" | ||||||
|  | 										viewBox="0 0 24 24" | ||||||
|  | 										stroke-width="1.5" | ||||||
|  | 										stroke="currentColor" | ||||||
|  | 										class="w-4 h-4" | ||||||
|  | 									> | ||||||
|  | 										<path | ||||||
|  | 											stroke-linecap="round" | ||||||
|  | 											stroke-linejoin="round" | ||||||
|  | 											d="m14.74 9-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 0 1-2.244 2.077H8.084a2.25 2.25 0 0 1-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 0 0-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 0 1 3.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 0 0-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 0 0-7.5 0" | ||||||
|  | 										/> | ||||||
|  | 									</svg> | ||||||
|  | 								</button> | ||||||
|  | 							</Tooltip> | ||||||
| 						{/if} | 						{/if} | ||||||
| 					</div> | 					</div> | ||||||
| 				</div> | 				</div> | ||||||
|  |  | ||||||
|  | @ -116,7 +116,7 @@ | ||||||
|                                 d="M10 2a.75.75 0 01.75.75v1.5a.75.75 0 01-1.5 0v-1.5A.75.75 0 0110 2zM10 15a.75.75 0 01.75.75v1.5a.75.75 0 01-1.5 0v-1.5A.75.75 0 0110 15zM10 7a3 3 0 100 6 3 3 0 000-6zM15.657 5.404a.75.75 0 10-1.06-1.06l-1.061 1.06a.75.75 0 001.06 1.06l1.06-1.06zM6.464 14.596a.75.75 0 10-1.06-1.06l-1.06 1.06a.75.75 0 001.06 1.06l1.06-1.06zM18 10a.75.75 0 01-.75.75h-1.5a.75.75 0 010-1.5h1.5A.75.75 0 0118 10zM5 10a.75.75 0 01-.75.75h-1.5a.75.75 0 010-1.5h1.5A.75.75 0 015 10zM14.596 15.657a.75.75 0 001.06-1.06l-1.06-1.061a.75.75 0 10-1.06 1.06l1.06 1.06zM5.404 6.464a.75.75 0 001.06-1.06l-1.06-1.06a.75.75 0 10-1.061 1.06l1.06 1.06z" |                                 d="M10 2a.75.75 0 01.75.75v1.5a.75.75 0 01-1.5 0v-1.5A.75.75 0 0110 2zM10 15a.75.75 0 01.75.75v1.5a.75.75 0 01-1.5 0v-1.5A.75.75 0 0110 15zM10 7a3 3 0 100 6 3 3 0 000-6zM15.657 5.404a.75.75 0 10-1.06-1.06l-1.061 1.06a.75.75 0 001.06 1.06l1.06-1.06zM6.464 14.596a.75.75 0 10-1.06-1.06l-1.06 1.06a.75.75 0 001.06 1.06l1.06-1.06zM18 10a.75.75 0 01-.75.75h-1.5a.75.75 0 010-1.5h1.5A.75.75 0 0118 10zM5 10a.75.75 0 01-.75.75h-1.5a.75.75 0 010-1.5h1.5A.75.75 0 015 10zM14.596 15.657a.75.75 0 001.06-1.06l-1.06-1.061a.75.75 0 10-1.06 1.06l1.06 1.06zM5.404 6.464a.75.75 0 001.06-1.06l-1.06-1.06a.75.75 0 10-1.061 1.06l1.06 1.06z" | ||||||
|                             /> |                             /> | ||||||
|                         </svg> --> |                         </svg> --> | ||||||
| 						<span class="ml-2 self-center">{$i18n.t(' JSON ')}</span> | 						<span class="ml-2 self-center">{$i18n.t('JSON')}</span> | ||||||
| 					{/if} | 					{/if} | ||||||
| 				</button> | 				</button> | ||||||
| 			</div> | 			</div> | ||||||
|  |  | ||||||
|  | @ -4,7 +4,12 @@ | ||||||
| 	const dispatch = createEventDispatcher(); | 	const dispatch = createEventDispatcher(); | ||||||
| 
 | 
 | ||||||
| 	import { getOllamaUrls, getOllamaVersion, updateOllamaUrls } from '$lib/apis/ollama'; | 	import { getOllamaUrls, getOllamaVersion, updateOllamaUrls } from '$lib/apis/ollama'; | ||||||
| 	import { getOpenAIKey, getOpenAIUrl, updateOpenAIKey, updateOpenAIUrl } from '$lib/apis/openai'; | 	import { | ||||||
|  | 		getOpenAIKeys, | ||||||
|  | 		getOpenAIUrls, | ||||||
|  | 		updateOpenAIKeys, | ||||||
|  | 		updateOpenAIUrls | ||||||
|  | 	} from '$lib/apis/openai'; | ||||||
| 	import { toast } from 'svelte-sonner'; | 	import { toast } from 'svelte-sonner'; | ||||||
| 
 | 
 | ||||||
| 	const i18n = getContext('i18n'); | 	const i18n = getContext('i18n'); | ||||||
|  | @ -18,12 +23,14 @@ | ||||||
| 	let OPENAI_API_KEY = ''; | 	let OPENAI_API_KEY = ''; | ||||||
| 	let OPENAI_API_BASE_URL = ''; | 	let OPENAI_API_BASE_URL = ''; | ||||||
| 
 | 
 | ||||||
|  | 	let OPENAI_API_KEYS = ['']; | ||||||
|  | 	let OPENAI_API_BASE_URLS = ['']; | ||||||
|  | 
 | ||||||
| 	let showOpenAI = false; | 	let showOpenAI = false; | ||||||
| 	let showLiteLLM = false; |  | ||||||
| 
 | 
 | ||||||
| 	const updateOpenAIHandler = async () => { | 	const updateOpenAIHandler = async () => { | ||||||
| 		OPENAI_API_BASE_URL = await updateOpenAIUrl(localStorage.token, OPENAI_API_BASE_URL); | 		OPENAI_API_BASE_URLS = await updateOpenAIUrls(localStorage.token, OPENAI_API_BASE_URLS); | ||||||
| 		OPENAI_API_KEY = await updateOpenAIKey(localStorage.token, OPENAI_API_KEY); | 		OPENAI_API_KEYS = await updateOpenAIKeys(localStorage.token, OPENAI_API_KEYS); | ||||||
| 
 | 
 | ||||||
| 		await models.set(await getModels()); | 		await models.set(await getModels()); | ||||||
| 	}; | 	}; | ||||||
|  | @ -45,8 +52,8 @@ | ||||||
| 	onMount(async () => { | 	onMount(async () => { | ||||||
| 		if ($user.role === 'admin') { | 		if ($user.role === 'admin') { | ||||||
| 			OLLAMA_BASE_URLS = await getOllamaUrls(localStorage.token); | 			OLLAMA_BASE_URLS = await getOllamaUrls(localStorage.token); | ||||||
| 			OPENAI_API_BASE_URL = await getOpenAIUrl(localStorage.token); | 			OPENAI_API_BASE_URLS = await getOpenAIUrls(localStorage.token); | ||||||
| 			OPENAI_API_KEY = await getOpenAIKey(localStorage.token); | 			OPENAI_API_KEYS = await getOpenAIKeys(localStorage.token); | ||||||
| 		} | 		} | ||||||
| 	}); | 	}); | ||||||
| </script> | </script> | ||||||
|  | @ -73,37 +80,74 @@ | ||||||
| 				</div> | 				</div> | ||||||
| 
 | 
 | ||||||
| 				{#if showOpenAI} | 				{#if showOpenAI} | ||||||
| 					<div> | 					<div class="flex flex-col gap-1"> | ||||||
| 						<div class=" mb-2.5 text-sm font-medium">{$i18n.t('API Key')}</div> | 						{#each OPENAI_API_BASE_URLS as url, idx} | ||||||
| 						<div class="flex w-full"> | 							<div class="flex w-full gap-2"> | ||||||
| 							<div class="flex-1"> | 								<div class="flex-1"> | ||||||
| 								<input | 									<input | ||||||
| 									class="w-full rounded py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-800 outline-none" | 										class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none" | ||||||
| 									placeholder={$i18n.t('Enter OpenAI API Key')} | 										placeholder={$i18n.t('API Base URL')} | ||||||
| 									bind:value={OPENAI_API_KEY} | 										bind:value={url} | ||||||
| 									autocomplete="off" | 										autocomplete="off" | ||||||
| 								/> | 									/> | ||||||
| 							</div> | 								</div> | ||||||
| 						</div> |  | ||||||
| 					</div> |  | ||||||
| 
 | 
 | ||||||
| 					<div> | 								<div class="flex-1"> | ||||||
| 						<div class=" mb-2.5 text-sm font-medium">{$i18n.t('API Base URL')}</div> | 									<input | ||||||
| 						<div class="flex w-full"> | 										class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none" | ||||||
| 							<div class="flex-1"> | 										placeholder={$i18n.t('API Key')} | ||||||
| 								<input | 										bind:value={OPENAI_API_KEYS[idx]} | ||||||
| 									class="w-full rounded py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-800 outline-none" | 										autocomplete="off" | ||||||
| 									placeholder="Enter OpenAI API Base URL" | 									/> | ||||||
| 									bind:value={OPENAI_API_BASE_URL} | 								</div> | ||||||
| 									autocomplete="off" | 								<div class="self-center flex items-center"> | ||||||
| 								/> | 									{#if idx === 0} | ||||||
|  | 										<button | ||||||
|  | 											class="px-1" | ||||||
|  | 											on:click={() => { | ||||||
|  | 												OPENAI_API_BASE_URLS = [...OPENAI_API_BASE_URLS, '']; | ||||||
|  | 												OPENAI_API_KEYS = [...OPENAI_API_KEYS, '']; | ||||||
|  | 											}} | ||||||
|  | 											type="button" | ||||||
|  | 										> | ||||||
|  | 											<svg | ||||||
|  | 												xmlns="http://www.w3.org/2000/svg" | ||||||
|  | 												viewBox="0 0 16 16" | ||||||
|  | 												fill="currentColor" | ||||||
|  | 												class="w-4 h-4" | ||||||
|  | 											> | ||||||
|  | 												<path | ||||||
|  | 													d="M8.75 3.75a.75.75 0 0 0-1.5 0v3.5h-3.5a.75.75 0 0 0 0 1.5h3.5v3.5a.75.75 0 0 0 1.5 0v-3.5h3.5a.75.75 0 0 0 0-1.5h-3.5v-3.5Z" | ||||||
|  | 												/> | ||||||
|  | 											</svg> | ||||||
|  | 										</button> | ||||||
|  | 									{:else} | ||||||
|  | 										<button | ||||||
|  | 											class="px-1" | ||||||
|  | 											on:click={() => { | ||||||
|  | 												OPENAI_API_BASE_URLS = OPENAI_API_BASE_URLS.filter( | ||||||
|  | 													(url, urlIdx) => idx !== urlIdx | ||||||
|  | 												); | ||||||
|  | 												OPENAI_API_KEYS = OPENAI_API_KEYS.filter((key, keyIdx) => idx !== keyIdx); | ||||||
|  | 											}} | ||||||
|  | 											type="button" | ||||||
|  | 										> | ||||||
|  | 											<svg | ||||||
|  | 												xmlns="http://www.w3.org/2000/svg" | ||||||
|  | 												viewBox="0 0 16 16" | ||||||
|  | 												fill="currentColor" | ||||||
|  | 												class="w-4 h-4" | ||||||
|  | 											> | ||||||
|  | 												<path d="M3.75 7.25a.75.75 0 0 0 0 1.5h8.5a.75.75 0 0 0 0-1.5h-8.5Z" /> | ||||||
|  | 											</svg> | ||||||
|  | 										</button> | ||||||
|  | 									{/if} | ||||||
|  | 								</div> | ||||||
| 							</div> | 							</div> | ||||||
| 						</div> | 							<div class=" mb-1 text-xs text-gray-400 dark:text-gray-500"> | ||||||
| 						<div class="mt-2 text-xs text-gray-400 dark:text-gray-500"> | 								WebUI will make requests to <span class=" text-gray-200">'{url}/models'</span> | ||||||
| 							WebUI will make requests to <span class=" text-gray-200" | 							</div> | ||||||
| 								>'{OPENAI_API_BASE_URL}/chat'</span | 						{/each} | ||||||
| 							> |  | ||||||
| 						</div> |  | ||||||
| 					</div> | 					</div> | ||||||
| 				{/if} | 				{/if} | ||||||
| 			</div> | 			</div> | ||||||
|  | @ -193,7 +237,7 @@ | ||||||
| 			<div class="mt-2 text-xs text-gray-400 dark:text-gray-500"> | 			<div class="mt-2 text-xs text-gray-400 dark:text-gray-500"> | ||||||
| 				{$i18n.t('Trouble accessing Ollama?')} | 				{$i18n.t('Trouble accessing Ollama?')} | ||||||
| 				<a | 				<a | ||||||
| 					class=" text-gray-300 font-medium" | 					class=" text-gray-300 font-medium underline" | ||||||
| 					href="https://github.com/open-webui/open-webui#troubleshooting" | 					href="https://github.com/open-webui/open-webui#troubleshooting" | ||||||
| 					target="_blank" | 					target="_blank" | ||||||
| 				> | 				> | ||||||
|  |  | ||||||
|  | @ -149,7 +149,7 @@ | ||||||
| 				<br /> | 				<br /> | ||||||
| 				{$i18n.t('You can help us translate the WebUI.')} | 				{$i18n.t('You can help us translate the WebUI.')} | ||||||
| 				<a | 				<a | ||||||
| 					class=" text-gray-300 font-medium" | 					class=" text-gray-300 font-medium underline" | ||||||
| 					href="https://github.com/open-webui/open-webui/blob/main/docs/CONTRIBUTING.md#-translations-and-internationalization" | 					href="https://github.com/open-webui/open-webui/blob/main/docs/CONTRIBUTING.md#-translations-and-internationalization" | ||||||
| 					target="_blank" | 					target="_blank" | ||||||
| 				> | 				> | ||||||
|  |  | ||||||
|  | @ -56,7 +56,7 @@ | ||||||
| 	let modelUploadMode = 'file'; | 	let modelUploadMode = 'file'; | ||||||
| 	let modelInputFile = ''; | 	let modelInputFile = ''; | ||||||
| 	let modelFileUrl = ''; | 	let modelFileUrl = ''; | ||||||
| 	let modelFileContent = `TEMPLATE """{{ .System }}\nUSER: {{ .Prompt }}\nASSSISTANT: """\nPARAMETER num_ctx 4096\nPARAMETER stop "</s>"\nPARAMETER stop "USER:"\nPARAMETER stop "ASSSISTANT:"`; | 	let modelFileContent = `TEMPLATE """{{ .System }}\nUSER: {{ .Prompt }}\nASSISTANT: """\nPARAMETER num_ctx 4096\nPARAMETER stop "</s>"\nPARAMETER stop "USER:"\nPARAMETER stop "ASSISTANT:"`; | ||||||
| 	let modelFileDigest = ''; | 	let modelFileDigest = ''; | ||||||
| 	let uploadProgress = null; | 	let uploadProgress = null; | ||||||
| 
 | 
 | ||||||
|  | @ -517,7 +517,7 @@ | ||||||
| 									{#if !deleteModelTag} | 									{#if !deleteModelTag} | ||||||
| 										<option value="" disabled selected>Select a model</option> | 										<option value="" disabled selected>Select a model</option> | ||||||
| 									{/if} | 									{/if} | ||||||
| 									{#each $models.filter((m) => m.size != null) as model} | 									{#each $models.filter((m) => m.size != null && (selectedOllamaUrlIdx === null ? true : (m?.urls ?? []).includes(selectedOllamaUrlIdx))) as model} | ||||||
| 										<option value={model.name} class="bg-gray-100 dark:bg-gray-700" | 										<option value={model.name} class="bg-gray-100 dark:bg-gray-700" | ||||||
| 											>{model.name + ' (' + (model.size / 1024 ** 3).toFixed(1) + ' GB)'}</option | 											>{model.name + ' (' + (model.size / 1024 ** 3).toFixed(1) + ' GB)'}</option | ||||||
| 										> | 										> | ||||||
|  | @ -599,7 +599,7 @@ | ||||||
| 												on:change={() => { | 												on:change={() => { | ||||||
| 													console.log(modelInputFile); | 													console.log(modelInputFile); | ||||||
| 												}} | 												}} | ||||||
| 												accept=".gguf" | 												accept=".gguf,.safetensors" | ||||||
| 												required | 												required | ||||||
| 												hidden | 												hidden | ||||||
| 											/> | 											/> | ||||||
|  |  | ||||||
|  | @ -140,7 +140,9 @@ | ||||||
| 						<button | 						<button | ||||||
| 							class="w-full text-sm font-medium py-3 bg-gray-850 hover:bg-gray-800 text-center rounded-xl" | 							class="w-full text-sm font-medium py-3 bg-gray-850 hover:bg-gray-800 text-center rounded-xl" | ||||||
| 							type="button" | 							type="button" | ||||||
| 							on:click={uploadDocInputElement.click} | 							on:click={() => { | ||||||
|  | 								uploadDocInputElement.click(); | ||||||
|  | 							}} | ||||||
| 						> | 						> | ||||||
| 							{#if inputFiles} | 							{#if inputFiles} | ||||||
| 								{inputFiles.length > 0 ? `${inputFiles.length}` : ''} document(s) selected. | 								{inputFiles.length > 0 ? `${inputFiles.length}` : ''} document(s) selected. | ||||||
|  |  | ||||||
|  | @ -90,8 +90,3 @@ export const SUPPORTED_FILE_EXTENSIONS = [ | ||||||
| // This feature, akin to $env/static/private, exclusively incorporates environment variables
 | // This feature, akin to $env/static/private, exclusively incorporates environment variables
 | ||||||
| // that are prefixed with config.kit.env.publicPrefix (usually set to PUBLIC_).
 | // that are prefixed with config.kit.env.publicPrefix (usually set to PUBLIC_).
 | ||||||
| // Consequently, these variables can be securely exposed to client-side code.
 | // Consequently, these variables can be securely exposed to client-side code.
 | ||||||
| 
 |  | ||||||
| // Example of the .env configuration:
 |  | ||||||
| // OLLAMA_API_BASE_URL="http://localhost:11434/api"
 |  | ||||||
| // # Public
 |  | ||||||
| // PUBLIC_API_BASE_URL=$OLLAMA_API_BASE_URL
 |  | ||||||
|  |  | ||||||
|  | @ -4,7 +4,7 @@ import LanguageDetector from 'i18next-browser-languagedetector'; | ||||||
| import type { i18n as i18nType } from 'i18next'; | import type { i18n as i18nType } from 'i18next'; | ||||||
| import { writable } from 'svelte/store'; | import { writable } from 'svelte/store'; | ||||||
| 
 | 
 | ||||||
| const createI18nStore = (i18n: i18n) => { | const createI18nStore = (i18n: i18nType) => { | ||||||
| 	const i18nWritable = writable(i18n); | 	const i18nWritable = writable(i18n); | ||||||
| 
 | 
 | ||||||
| 	i18n.on('initialized', () => { | 	i18n.on('initialized', () => { | ||||||
|  | @ -20,7 +20,7 @@ const createI18nStore = (i18n: i18n) => { | ||||||
| 	return i18nWritable; | 	return i18nWritable; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| const createIsLoadingStore = (i18n: i18n) => { | const createIsLoadingStore = (i18n: i18nType) => { | ||||||
| 	const isLoading = writable(false); | 	const isLoading = writable(false); | ||||||
| 
 | 
 | ||||||
| 	// if loaded resources are empty || {}, set loading to true
 | 	// if loaded resources are empty || {}, set loading to true
 | ||||||
|  | @ -39,11 +39,13 @@ const createIsLoadingStore = (i18n: i18n) => { | ||||||
| 
 | 
 | ||||||
| i18next | i18next | ||||||
| 	.use( | 	.use( | ||||||
| 		resourcesToBackend((language, namespace) => import(`./locales/${language}/${namespace}.json`)) | 		resourcesToBackend( | ||||||
|  | 			(language: string, namespace: string) => import(`./locales/${language}/${namespace}.json`) | ||||||
|  | 		) | ||||||
| 	) | 	) | ||||||
| 	.use(LanguageDetector) | 	.use(LanguageDetector) | ||||||
| 	.init({ | 	.init({ | ||||||
| 		debug: true, | 		debug: false, | ||||||
| 		detection: { | 		detection: { | ||||||
| 			order: ['querystring', 'localStorage', 'navigator'], | 			order: ['querystring', 'localStorage', 'navigator'], | ||||||
| 			caches: ['localStorage'], | 			caches: ['localStorage'], | ||||||
|  |  | ||||||
|  | @ -18,10 +18,9 @@ | ||||||
| 	"Add message": "Nachricht eingeben", | 	"Add message": "Nachricht eingeben", | ||||||
| 	"add tags": "Tags hinzufügen", | 	"add tags": "Tags hinzufügen", | ||||||
| 	"Adjusting these settings will apply changes universally to all users.": "Das Anpassen dieser Einstellungen wirkt sich universell auf alle Benutzer aus.", | 	"Adjusting these settings will apply changes universally to all users.": "Das Anpassen dieser Einstellungen wirkt sich universell auf alle Benutzer aus.", | ||||||
| 	"admin": "Administrator", | 	"Admin": "", | ||||||
| 	"Admin Panel": "Admin Panel", | 	"Admin Panel": "Admin Panel", | ||||||
| 	"Admin Settings": "Admin Einstellungen", | 	"Admin Settings": "Admin Einstellungen", | ||||||
| 	"Advanced": "Angepasst", |  | ||||||
| 	"Advanced Model Params": "Erweiterte Modell Parameter", | 	"Advanced Model Params": "Erweiterte Modell Parameter", | ||||||
| 	"Advanced Parameters": "Erweiterte Parameter", | 	"Advanced Parameters": "Erweiterte Parameter", | ||||||
| 	"all": "Alle", | 	"all": "Alle", | ||||||
|  | @ -35,7 +34,6 @@ | ||||||
| 	"API Key": "API Key", | 	"API Key": "API Key", | ||||||
| 	"API RPM": "API RPM", | 	"API RPM": "API RPM", | ||||||
| 	"are allowed - Activate this command by typing": "sind erlaubt - Aktiviere diesen Befehl, indem du", | 	"are allowed - Activate this command by typing": "sind erlaubt - Aktiviere diesen Befehl, indem du", | ||||||
| 	"assistant": "Assistent", |  | ||||||
| 	"Audio": "Audio", | 	"Audio": "Audio", | ||||||
| 	"Auto-playback response": "Automatische Wiedergabe der Antwort", | 	"Auto-playback response": "Automatische Wiedergabe der Antwort", | ||||||
| 	"Auto-send input after 3 sec.": "Automatisches Senden der Eingabe nach 3 Sek", | 	"Auto-send input after 3 sec.": "Automatisches Senden der Eingabe nach 3 Sek", | ||||||
|  | @ -98,7 +96,7 @@ | ||||||
| 	"Deleted {tagName}": "{tagName} gelöscht", | 	"Deleted {tagName}": "{tagName} gelöscht", | ||||||
| 	"Description": "Beschreibung", | 	"Description": "Beschreibung", | ||||||
| 	"Desktop Notifications": "Desktop-Benachrichtigungen", | 	"Desktop Notifications": "Desktop-Benachrichtigungen", | ||||||
| 	"Didn't find your language?": "Deine Sprache nicht gefunden?", | 	"Didn't find your language?": "Deine Sprache nicht vorhanden?", | ||||||
| 	"Disabled": "Deaktiviert", | 	"Disabled": "Deaktiviert", | ||||||
| 	"Discover a modelfile": "Eine Modelfiles entdecken", | 	"Discover a modelfile": "Eine Modelfiles entdecken", | ||||||
| 	"Discover a prompt": "Einen Prompt entdecken", | 	"Discover a prompt": "Einen Prompt entdecken", | ||||||
|  | @ -121,21 +119,17 @@ | ||||||
| 	"Enable Chat History": "Chat-Verlauf aktivieren", | 	"Enable Chat History": "Chat-Verlauf aktivieren", | ||||||
| 	"Enable New Sign Ups": "Neue Anmeldungen aktivieren", | 	"Enable New Sign Ups": "Neue Anmeldungen aktivieren", | ||||||
| 	"Enabled": "Aktiviert", | 	"Enabled": "Aktiviert", | ||||||
| 	"Enter a user message here": "Gebe hier eine Benutzernachricht ein", |  | ||||||
| 	"Enter an assistant message here": "Gebe hier eine Assistentennachricht ein", |  | ||||||
| 	"Enter OpenAI API Key": "OpenAI-API-Key eingeben", | 	"Enter OpenAI API Key": "OpenAI-API-Key eingeben", | ||||||
| 	"Enter stop sequence": "Stop-Sequenz eingeben", | 	"Enter stop sequence": "Stop-Sequenz eingeben", | ||||||
| 	"Enter Your Email": "Geben Deine E-Mail-Adresse ein", | 	"Enter Your Email": "Geben Deine E-Mail-Adresse ein", | ||||||
| 	"Enter Your Full Name": "Gebe Deinen vollständigen Namen ein", | 	"Enter Your Full Name": "Gebe Deinen vollständigen Namen ein", | ||||||
| 	"Enter Your Password": "Gebe Dein Passwort ein", | 	"Enter Your Password": "Gebe Dein Passwort ein", | ||||||
| 	"Experimental": "Experimentell", |  | ||||||
| 	"Export All Chats (All Users)": "Alle Chats exportieren (alle Benutzer)", | 	"Export All Chats (All Users)": "Alle Chats exportieren (alle Benutzer)", | ||||||
| 	"Export Chats": "Chats exportieren", | 	"Export Chats": "Chats exportieren", | ||||||
| 	"Export Documents Mapping": "Dokumentenmapping exportieren", | 	"Export Documents Mapping": "Dokumentenmapping exportieren", | ||||||
| 	"Export Modelfiles": "Modelfiles exportieren", | 	"Export Modelfiles": "Modelfiles exportieren", | ||||||
| 	"Export Prompts": "Prompts exportieren", | 	"Export Prompts": "Prompts exportieren", | ||||||
| 	"Failed to read clipboard contents": "Fehler beim Lesen des Zwischenablageninhalts", | 	"Failed to read clipboard contents": "Fehler beim Lesen des Zwischenablageninhalts", | ||||||
| 	"File Mode": "Dateimodus", |  | ||||||
| 	"File not found.": "Datei nicht gefunden.", | 	"File not found.": "Datei nicht gefunden.", | ||||||
| 	"Focus chat input": "Chat-Eingabe fokussieren", | 	"Focus chat input": "Chat-Eingabe fokussieren", | ||||||
| 	"Format your variables using square brackets like this:": "Formatiere Deine Variablen mit eckigen Klammern wie folgt:", | 	"Format your variables using square brackets like this:": "Formatiere Deine Variablen mit eckigen Klammern wie folgt:", | ||||||
|  | @ -187,7 +181,6 @@ | ||||||
| 	"Model Tag Name": "Modell-Tag-Name", | 	"Model Tag Name": "Modell-Tag-Name", | ||||||
| 	"Modelfile": "Modelfiles", | 	"Modelfile": "Modelfiles", | ||||||
| 	"Modelfile Advanced Settings": "Erweiterte Modelfileseinstellungen", | 	"Modelfile Advanced Settings": "Erweiterte Modelfileseinstellungen", | ||||||
| 	"Modelfile Content": "Modelfilesinhalt", |  | ||||||
| 	"Modelfiles": "Modelfiles", | 	"Modelfiles": "Modelfiles", | ||||||
| 	"Models": "Modelle", | 	"Models": "Modelle", | ||||||
| 	"My Documents": "Meine Dokumente", | 	"My Documents": "Meine Dokumente", | ||||||
|  | @ -195,19 +188,20 @@ | ||||||
| 	"My Prompts": "Meine Prompts", | 	"My Prompts": "Meine Prompts", | ||||||
| 	"Name": "Name", | 	"Name": "Name", | ||||||
| 	"Name Tag": "Namens-Tag", | 	"Name Tag": "Namens-Tag", | ||||||
| 	"Name your Modelfile": "Benenne Dein Modelfile", | 	"Name your modelfile": "", | ||||||
| 	"New Chat": "Neuer Chat", | 	"New Chat": "Neuer Chat", | ||||||
| 	"New Password": "Neues Passwort", | 	"New Password": "Neues Passwort", | ||||||
| 	"Not sure what to add?": "Nicht sicher, was hinzugefügt werden soll?", | 	"Not sure what to add?": "Nicht sicher, was hinzugefügt werden soll?", | ||||||
| 	"Not sure what to write? Switch to": "Nicht sicher, was Du schreiben sollst? Wechsel zu", | 	"Not sure what to write? Switch to": "Nicht sicher, was Du schreiben sollst? Wechsel zu", | ||||||
| 	"Off": "Aus", | 	"Off": "Aus", | ||||||
| 	"Okay, Let's Go!": "Okay, los geht's!", | 	"Okay, Let's Go!": "Okay, los geht's!", | ||||||
| 	"Ollama API URL": "Ollama-API-URL", |  | ||||||
| 	"Ollama Version": "Ollama-Version", | 	"Ollama Version": "Ollama-Version", | ||||||
| 	"On": "Ein", | 	"On": "Ein", | ||||||
| 	"Only": "Nur", | 	"Only": "Nur", | ||||||
| 	"Only alphanumeric characters and hyphens are allowed in the command string.": "Nur alphanumerische Zeichen und Bindestriche sind im Befehlsstring erlaubt.", | 	"Only alphanumeric characters and hyphens are allowed in the command string.": "Nur alphanumerische Zeichen und Bindestriche sind im Befehlsstring erlaubt.", | ||||||
| 	"Oops! Hold tight! Your files are still in the processing oven. We're cooking them up to perfection. Please be patient and we'll let you know once they're ready.": "Hoppla! Warte noch einen Moment! Die Dateien sind noch im der Verarbeitung. Bitte habe etwas Geduld und wir informieren Dich, sobald sie bereit sind.", | 	"Oops! Hold tight! Your files are still in the processing oven. We're cooking them up to perfection. Please be patient and we'll let you know once they're ready.": "Hoppla! Warte noch einen Moment! Die Dateien sind noch im der Verarbeitung. Bitte habe etwas Geduld und wir informieren Dich, sobald sie bereit sind.", | ||||||
|  | 	"Oops! Looks like the URL is invalid. Please double-check and try again.": "", | ||||||
|  | 	"Oops! You're using an unsupported method (frontend only). Please serve the WebUI from the backend.": "", | ||||||
| 	"Open": "Öffne", | 	"Open": "Öffne", | ||||||
| 	"Open AI": "Open AI", | 	"Open AI": "Open AI", | ||||||
| 	"Open new chat": "Neuen Chat öffnen", | 	"Open new chat": "Neuen Chat öffnen", | ||||||
|  | @ -215,7 +209,7 @@ | ||||||
| 	"or": "oder", | 	"or": "oder", | ||||||
| 	"Parameters": "Parameter", | 	"Parameters": "Parameter", | ||||||
| 	"Password": "Passwort", | 	"Password": "Passwort", | ||||||
| 	"pending": "ausstehend", | 	"Pending": "", | ||||||
| 	"Permission denied when accessing microphone: {{error}}": "Zugriff auf das Mikrofon verweigert: {{error}}", | 	"Permission denied when accessing microphone: {{error}}": "Zugriff auf das Mikrofon verweigert: {{error}}", | ||||||
| 	"Playground": "Playground", | 	"Playground": "Playground", | ||||||
| 	"Profile": "Profil", | 	"Profile": "Profil", | ||||||
|  | @ -277,6 +271,7 @@ | ||||||
| 	"STT Settings": "STT-Einstellungen", | 	"STT Settings": "STT-Einstellungen", | ||||||
| 	"Submit": "Senden", | 	"Submit": "Senden", | ||||||
| 	"Success": "Erfolg", | 	"Success": "Erfolg", | ||||||
|  | 	"Successfully updated": "", | ||||||
| 	"Successfully updated.": "Erfolgreich aktualisiert.", | 	"Successfully updated.": "Erfolgreich aktualisiert.", | ||||||
| 	"Sync All": "Alles synchronisieren", | 	"Sync All": "Alles synchronisieren", | ||||||
| 	"System": "System", | 	"System": "System", | ||||||
|  | @ -290,12 +285,12 @@ | ||||||
| 	"Theme": "Design", | 	"Theme": "Design", | ||||||
| 	"This ensures that your valuable conversations are securely saved to your backend database. Thank you!": "Dadurch werden Deine wertvollen Unterhaltungen sicher in der Backend-Datenbank gespeichert. Vielen Dank!", | 	"This ensures that your valuable conversations are securely saved to your backend database. Thank you!": "Dadurch werden Deine wertvollen Unterhaltungen sicher in der Backend-Datenbank gespeichert. Vielen Dank!", | ||||||
| 	"This setting does not sync across browsers or devices.": "Diese Einstellung wird nicht zwischen Browsern oder Geräten synchronisiert.", | 	"This setting does not sync across browsers or devices.": "Diese Einstellung wird nicht zwischen Browsern oder Geräten synchronisiert.", | ||||||
|  | 	"Tip: Update multiple variable slots consecutively by pressing the tab key in the chat input after each replacement.": "", | ||||||
| 	"Title": "Titel", | 	"Title": "Titel", | ||||||
| 	"Title Auto-Generation": "Automatische Titelgenerierung", | 	"Title Auto-Generation": "Automatische Titelgenerierung", | ||||||
| 	"Title Generation Prompt": "Prompt für Titelgenerierung", | 	"Title Generation Prompt": "Prompt für Titelgenerierung", | ||||||
| 	"to": "für", | 	"to": "für", | ||||||
| 	"To access the available model names for downloading,": "Um auf die verfügbaren Modellnamen zum Herunterladen zuzugreifen,", | 	"To access the available model names for downloading,": "Um auf die verfügbaren Modellnamen zum Herunterladen zuzugreifen,", | ||||||
| 	"to chat input.": "im Chat eingeben.", |  | ||||||
| 	"Toggle settings": "Einstellungen umschalten", | 	"Toggle settings": "Einstellungen umschalten", | ||||||
| 	"Toggle sidebar": "Seitenleiste umschalten", | 	"Toggle sidebar": "Seitenleiste umschalten", | ||||||
| 	"Top K": "Top K", | 	"Top K": "Top K", | ||||||
|  | @ -303,12 +298,10 @@ | ||||||
| 	"Trouble accessing Ollama?": "Probleme beim Zugriff auf Ollama?", | 	"Trouble accessing Ollama?": "Probleme beim Zugriff auf Ollama?", | ||||||
| 	"TTS Settings": "TTS-Einstellungen", | 	"TTS Settings": "TTS-Einstellungen", | ||||||
| 	"Uh-oh! There was an issue connecting to {{provider}}.": "Ups! Es gab ein Problem bei der Verbindung mit {{provider}}.", | 	"Uh-oh! There was an issue connecting to {{provider}}.": "Ups! Es gab ein Problem bei der Verbindung mit {{provider}}.", | ||||||
| 	"Upload a GGUF model": "Ein GGUF-Modell hochladen", | 	"Unknown File Type '{{file_type}}', but accepting and treating as plain text": "", | ||||||
| 	"Upload files": "Dateien hochladen", | 	"Upload files": "Dateien hochladen", | ||||||
| 	"Upload Progress": "Upload-Fortschritt", |  | ||||||
| 	"URL Mode": "URL-Modus", |  | ||||||
| 	"Use '#' in the prompt input to load and select your documents.": "Verwende '#' in der Prompt-Eingabe, um Deine Dokumente zu laden und auszuwählen.", | 	"Use '#' in the prompt input to load and select your documents.": "Verwende '#' in der Prompt-Eingabe, um Deine Dokumente zu laden und auszuwählen.", | ||||||
| 	"user": "Benutzer", | 	"User": "", | ||||||
| 	"User Permissions": "Benutzerberechtigungen", | 	"User Permissions": "Benutzerberechtigungen", | ||||||
| 	"Users": "Benutzer", | 	"Users": "Benutzer", | ||||||
| 	"Utilize": "Nutze die", | 	"Utilize": "Nutze die", | ||||||
|  | @ -322,8 +315,9 @@ | ||||||
| 	"When history is turned off, new chats on this browser won't appear in your history on any of your devices.": "Wenn die Historie ausgeschaltet ist, werden neue Chats nicht in Deiner Historie auf Deine Geräte angezeigt.", | 	"When history is turned off, new chats on this browser won't appear in your history on any of your devices.": "Wenn die Historie ausgeschaltet ist, werden neue Chats nicht in Deiner Historie auf Deine Geräte angezeigt.", | ||||||
| 	"Whisper (Local)": "Whisper (Lokal)", | 	"Whisper (Local)": "Whisper (Lokal)", | ||||||
| 	"Write a prompt suggestion (e.g. Who are you?)": "Gebe einen Prompt-Vorschlag ein (z.B. Wer bist du?)", | 	"Write a prompt suggestion (e.g. Who are you?)": "Gebe einen Prompt-Vorschlag ein (z.B. Wer bist du?)", | ||||||
| 	"Write a summary in 50 words that summarizes [topic or keyword]": "Schreibe eine kurze Zusammenfassung in 50 Wörtern, die [Thema oder Schlüsselwort] zusammenfasst.", | 	"Write a summary in 50 words that summarizes [topic or keyword].": "Schreibe eine kurze Zusammenfassung in 50 Wörtern, die [Thema oder Schlüsselwort] zusammenfasst.", | ||||||
| 	"You": "Du", | 	"You": "Du", | ||||||
| 	"You can help us translate the WebUI.": "Du kannst uns bei der Übersetzung der WebUI helfen.", | 	"You're a helpful assistant.": "Du bist ein hilfreicher Assistent.", | ||||||
| 	"You're a helpful assistant.": "Du bist ein hilfreicher Assistent." | 	"You're now logged in.": "Du bist nun eingeloggt.", | ||||||
|  | 	"You can help us translate the WebUI.": "Du kannst uns bei der Übersetzung der WebUI helfen." | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -18,10 +18,9 @@ | ||||||
| 	"Add message": "Add message", | 	"Add message": "Add message", | ||||||
| 	"add tags": "add tags", | 	"add tags": "add tags", | ||||||
| 	"Adjusting these settings will apply changes universally to all users.": "Adjusting these settings will apply changes universally to all users.", | 	"Adjusting these settings will apply changes universally to all users.": "Adjusting these settings will apply changes universally to all users.", | ||||||
| 	"admin": "Admin", | 	"Admin": "", | ||||||
| 	"Admin Panel": "Admin Panel", | 	"Admin Panel": "Admin Panel", | ||||||
| 	"Admin Settings": "Admin Settings", | 	"Admin Settings": "Admin Settings", | ||||||
| 	"Advanced": "Advanced", |  | ||||||
| 	"Advanced Model Params": "Advanced Model Params", | 	"Advanced Model Params": "Advanced Model Params", | ||||||
| 	"Advanced Parameters": "Advanced Parameters", | 	"Advanced Parameters": "Advanced Parameters", | ||||||
| 	"all": "all", | 	"all": "all", | ||||||
|  | @ -35,7 +34,6 @@ | ||||||
| 	"API Key": "API Key", | 	"API Key": "API Key", | ||||||
| 	"API RPM": "API RPM", | 	"API RPM": "API RPM", | ||||||
| 	"are allowed - Activate this command by typing": "are allowed - Activate this command by typing", | 	"are allowed - Activate this command by typing": "are allowed - Activate this command by typing", | ||||||
| 	"assistant": "Assistant", |  | ||||||
| 	"Audio": "Audio", | 	"Audio": "Audio", | ||||||
| 	"Auto-playback response": "Auto-playback response", | 	"Auto-playback response": "Auto-playback response", | ||||||
| 	"Auto-send input after 3 sec.": "Auto-send input after 3 sec.", | 	"Auto-send input after 3 sec.": "Auto-send input after 3 sec.", | ||||||
|  | @ -121,21 +119,17 @@ | ||||||
| 	"Enable Chat History": "Enable Chat History", | 	"Enable Chat History": "Enable Chat History", | ||||||
| 	"Enable New Sign Ups": "Enable New Sign Ups", | 	"Enable New Sign Ups": "Enable New Sign Ups", | ||||||
| 	"Enabled": "Enabled", | 	"Enabled": "Enabled", | ||||||
| 	"Enter a user message here": "Enter a user message here", |  | ||||||
| 	"Enter an assistant message here": "Enter an assistant message here", |  | ||||||
| 	"Enter OpenAI API Key": "Enter OpenAI API Key", | 	"Enter OpenAI API Key": "Enter OpenAI API Key", | ||||||
| 	"Enter stop sequence": "Enter stop sequence", | 	"Enter stop sequence": "Enter stop sequence", | ||||||
| 	"Enter Your Email": "Enter Your Email", | 	"Enter Your Email": "Enter Your Email", | ||||||
| 	"Enter Your Full Name": "Enter Your Full Name", | 	"Enter Your Full Name": "Enter Your Full Name", | ||||||
| 	"Enter Your Password": "Enter Your Password", | 	"Enter Your Password": "Enter Your Password", | ||||||
| 	"Experimental": "Experimental", |  | ||||||
| 	"Export All Chats (All Users)": "Export All Chats (All Users)", | 	"Export All Chats (All Users)": "Export All Chats (All Users)", | ||||||
| 	"Export Chats": "Export Chats", | 	"Export Chats": "Export Chats", | ||||||
| 	"Export Documents Mapping": "Export Documents Mapping", | 	"Export Documents Mapping": "Export Documents Mapping", | ||||||
| 	"Export Modelfiles": "Export Modelfiles", | 	"Export Modelfiles": "Export Modelfiles", | ||||||
| 	"Export Prompts": "Export Prompts", | 	"Export Prompts": "Export Prompts", | ||||||
| 	"Failed to read clipboard contents": "Failed to read clipboard contents", | 	"Failed to read clipboard contents": "Failed to read clipboard contents", | ||||||
| 	"File Mode": "File Mode", |  | ||||||
| 	"File not found.": "File not found.", | 	"File not found.": "File not found.", | ||||||
| 	"Focus chat input": "Focus chat input", | 	"Focus chat input": "Focus chat input", | ||||||
| 	"Format your variables using square brackets like this:": "Format your variables using square brackets like this:", | 	"Format your variables using square brackets like this:": "Format your variables using square brackets like this:", | ||||||
|  | @ -187,7 +181,6 @@ | ||||||
| 	"Model Tag Name": "Model Tag Name", | 	"Model Tag Name": "Model Tag Name", | ||||||
| 	"Modelfile": "Modelfile", | 	"Modelfile": "Modelfile", | ||||||
| 	"Modelfile Advanced Settings": "Modelfile Advanced Settings", | 	"Modelfile Advanced Settings": "Modelfile Advanced Settings", | ||||||
| 	"Modelfile Content": "Modelfile Content", |  | ||||||
| 	"Modelfiles": "Modelfiles", | 	"Modelfiles": "Modelfiles", | ||||||
| 	"Models": "Models", | 	"Models": "Models", | ||||||
| 	"My Documents": "My Documents", | 	"My Documents": "My Documents", | ||||||
|  | @ -195,19 +188,20 @@ | ||||||
| 	"My Prompts": "My Prompts", | 	"My Prompts": "My Prompts", | ||||||
| 	"Name": "Name", | 	"Name": "Name", | ||||||
| 	"Name Tag": "Name Tag", | 	"Name Tag": "Name Tag", | ||||||
| 	"Name your Modelfile": "Name your Modelfile", | 	"Name your modelfile": "", | ||||||
| 	"New Chat": "New Chat", | 	"New Chat": "New Chat", | ||||||
| 	"New Password": "New Password", | 	"New Password": "New Password", | ||||||
| 	"Not sure what to add?": "Not sure what to add?", | 	"Not sure what to add?": "Not sure what to add?", | ||||||
| 	"Not sure what to write? Switch to": "Not sure what to write? Switch to", | 	"Not sure what to write? Switch to": "Not sure what to write? Switch to", | ||||||
| 	"Off": "Off", | 	"Off": "Off", | ||||||
| 	"Okay, Let's Go!": "Okay, Let's Go!", | 	"Okay, Let's Go!": "Okay, Let's Go!", | ||||||
| 	"Ollama API URL": "Ollama API URL", |  | ||||||
| 	"Ollama Version": "Ollama Version", | 	"Ollama Version": "Ollama Version", | ||||||
| 	"On": "On", | 	"On": "On", | ||||||
| 	"Only": "Only", | 	"Only": "Only", | ||||||
| 	"Only alphanumeric characters and hyphens are allowed in the command string.": "Only alphanumeric characters and hyphens are allowed in the command string.", | 	"Only alphanumeric characters and hyphens are allowed in the command string.": "Only alphanumeric characters and hyphens are allowed in the command string.", | ||||||
| 	"Oops! Hold tight! Your files are still in the processing oven. We're cooking them up to perfection. Please be patient and we'll let you know once they're ready.": "Oops! Hold tight! Your files are still in the processing oven. We're cooking them up to perfection. Please be patient and we'll let you know once they're ready.", | 	"Oops! Hold tight! Your files are still in the processing oven. We're cooking them up to perfection. Please be patient and we'll let you know once they're ready.": "Oops! Hold tight! Your files are still in the processing oven. We're cooking them up to perfection. Please be patient and we'll let you know once they're ready.", | ||||||
|  | 	"Oops! Looks like the URL is invalid. Please double-check and try again.": "", | ||||||
|  | 	"Oops! You're using an unsupported method (frontend only). Please serve the WebUI from the backend.": "", | ||||||
| 	"Open": "Open", | 	"Open": "Open", | ||||||
| 	"Open AI": "Open AI", | 	"Open AI": "Open AI", | ||||||
| 	"Open new chat": "Open new chat", | 	"Open new chat": "Open new chat", | ||||||
|  | @ -215,7 +209,7 @@ | ||||||
| 	"or": "or", | 	"or": "or", | ||||||
| 	"Parameters": "Parameters", | 	"Parameters": "Parameters", | ||||||
| 	"Password": "Password", | 	"Password": "Password", | ||||||
| 	"pending": "Pending", | 	"Pending": "", | ||||||
| 	"Permission denied when accessing microphone: {{error}}": "Permission denied when accessing microphone: {{error}}", | 	"Permission denied when accessing microphone: {{error}}": "Permission denied when accessing microphone: {{error}}", | ||||||
| 	"Playground": "Playground", | 	"Playground": "Playground", | ||||||
| 	"Profile": "Profile", | 	"Profile": "Profile", | ||||||
|  | @ -277,6 +271,7 @@ | ||||||
| 	"STT Settings": "STT Settings", | 	"STT Settings": "STT Settings", | ||||||
| 	"Submit": "Submit", | 	"Submit": "Submit", | ||||||
| 	"Success": "Success", | 	"Success": "Success", | ||||||
|  | 	"Successfully updated": "", | ||||||
| 	"Successfully updated.": "Successfully updated.", | 	"Successfully updated.": "Successfully updated.", | ||||||
| 	"Sync All": "Sync All", | 	"Sync All": "Sync All", | ||||||
| 	"System": "System", | 	"System": "System", | ||||||
|  | @ -290,12 +285,12 @@ | ||||||
| 	"Theme": "Theme", | 	"Theme": "Theme", | ||||||
| 	"This ensures that your valuable conversations are securely saved to your backend database. Thank you!": "This ensures that your valuable conversations are securely saved to your backend database. Thank you!", | 	"This ensures that your valuable conversations are securely saved to your backend database. Thank you!": "This ensures that your valuable conversations are securely saved to your backend database. Thank you!", | ||||||
| 	"This setting does not sync across browsers or devices.": "This setting does not sync across browsers or devices.", | 	"This setting does not sync across browsers or devices.": "This setting does not sync across browsers or devices.", | ||||||
|  | 	"Tip: Update multiple variable slots consecutively by pressing the tab key in the chat input after each replacement.": "", | ||||||
| 	"Title": "Title", | 	"Title": "Title", | ||||||
| 	"Title Auto-Generation": "Title Auto-Generation", | 	"Title Auto-Generation": "Title Auto-Generation", | ||||||
| 	"Title Generation Prompt": "Title Generation Prompt", | 	"Title Generation Prompt": "Title Generation Prompt", | ||||||
| 	"to": "to", | 	"to": "to", | ||||||
| 	"To access the available model names for downloading,": "To access the available model names for downloading,", | 	"To access the available model names for downloading,": "To access the available model names for downloading,", | ||||||
| 	"to chat input.": "to chat input.", |  | ||||||
| 	"Toggle settings": "Toggle settings", | 	"Toggle settings": "Toggle settings", | ||||||
| 	"Toggle sidebar": "Toggle sidebar", | 	"Toggle sidebar": "Toggle sidebar", | ||||||
| 	"Top K": "Top K", | 	"Top K": "Top K", | ||||||
|  | @ -303,12 +298,10 @@ | ||||||
| 	"Trouble accessing Ollama?": "Trouble accessing Ollama?", | 	"Trouble accessing Ollama?": "Trouble accessing Ollama?", | ||||||
| 	"TTS Settings": "TTS Settings", | 	"TTS Settings": "TTS Settings", | ||||||
| 	"Uh-oh! There was an issue connecting to {{provider}}.": "Uh-oh! There was an issue connecting to {{provider}}.", | 	"Uh-oh! There was an issue connecting to {{provider}}.": "Uh-oh! There was an issue connecting to {{provider}}.", | ||||||
| 	"Upload a GGUF model": "Upload a GGUF model", | 	"Unknown File Type '{{file_type}}', but accepting and treating as plain text": "", | ||||||
| 	"Upload files": "Upload files", | 	"Upload files": "Upload files", | ||||||
| 	"Upload Progress": "Upload Progress", |  | ||||||
| 	"URL Mode": "URL Mode", |  | ||||||
| 	"Use '#' in the prompt input to load and select your documents.": "Use '#' in the prompt input to load and select your documents.", | 	"Use '#' in the prompt input to load and select your documents.": "Use '#' in the prompt input to load and select your documents.", | ||||||
| 	"user": "User", | 	"User": "", | ||||||
| 	"User Permissions": "User Permissions", | 	"User Permissions": "User Permissions", | ||||||
| 	"Users": "Users", | 	"Users": "Users", | ||||||
| 	"Utilize": "Utilize", | 	"Utilize": "Utilize", | ||||||
|  | @ -322,8 +315,9 @@ | ||||||
| 	"When history is turned off, new chats on this browser won't appear in your history on any of your devices.": "When history is turned off, new chats on this browser won't appear in your history on any of your devices.", | 	"When history is turned off, new chats on this browser won't appear in your history on any of your devices.": "When history is turned off, new chats on this browser won't appear in your history on any of your devices.", | ||||||
| 	"Whisper (Local)": "Whisper (Local)", | 	"Whisper (Local)": "Whisper (Local)", | ||||||
| 	"Write a prompt suggestion (e.g. Who are you?)": "Write a prompt suggestion (e.g. Who are you?)", | 	"Write a prompt suggestion (e.g. Who are you?)": "Write a prompt suggestion (e.g. Who are you?)", | ||||||
| 	"Write a summary in 50 words that summarizes [topic or keyword]": "Write a summary in 50 words that summarizes [topic or keyword]", | 	"Write a summary in 50 words that summarizes [topic or keyword].": "Write a summary in 50 words that summarizes [topic or keyword].", | ||||||
| 	"You can help us translate the WebUI.": "You can help us translate the WebUI.", | 	"You": "You", | ||||||
| 	"You're a helpful assistant.": "You're a helpful assistant.", | 	"You're a helpful assistant.": "You're a helpful assistant.", | ||||||
| 	"You're now logged in.": "You're now logged in." | 	"You're now logged in.": "You're now logged in.", | ||||||
|  | 	"You can help us translate the WebUI.": "You can help us translate the WebUI." | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -1,42 +1,65 @@ | ||||||
| { | { | ||||||
|  | 	"'s', 'm', 'h', 'd', 'w' or '-1' for no expiration.": "'s', 'm', 'h', 'd', 'w' یا '-1' برای غیر فعال کردن انقضا.", | ||||||
|  | 	"(Beta)": "(بتا)", | ||||||
|  | 	"(e.g. `sh webui.sh --api`)": "(e.g. `sh webui.sh --api`)", | ||||||
|  | 	"{{item}} not provided": "{{item}} ارائه نشده است", | ||||||
|  | 	"{{modelName}} is thinking...": "{{modelName}} در حال فکر کردن است...", | ||||||
|  | 	"{{webUIName}} Backend Required": "بکند {{webUIName}} نیاز است.", | ||||||
| 	"About": "درباره", | 	"About": "درباره", | ||||||
| 	"Account": "حساب کاربری", | 	"Account": "حساب کاربری", | ||||||
| 	"Action": "عمل", | 	"Action": "عمل", | ||||||
| 	"Add a model": "اضافه کردن یک مدل", | 	"Add a model": "اضافه کردن یک مدل", | ||||||
|  | 	"Add a model tag name": "اضافه کردن یک نام تگ برای مدل", | ||||||
|  | 	"Add a short description about what this modelfile does": "توضیح کوتاهی در مورد کاری که این فایل\u200cمدل انجام می دهد اضافه کنید", | ||||||
|  | 	"Add a short title for this prompt": "یک عنوان کوتاه برای این درخواست اضافه کنید", | ||||||
| 	"Add Docs": "اضافه کردن اسناد", | 	"Add Docs": "اضافه کردن اسناد", | ||||||
| 	"Add Files": "اضافه کردن فایلها", | 	"Add Files": "اضافه کردن فایل\u200cها", | ||||||
| 	"Add LiteLLM Model": "اضافه کردن مدل LiteLLM", | 	"Add LiteLLM Model": "اضافه کردن مدل LiteLLM", | ||||||
| 	"add tags": "اضافه کردن تگها", | 	"Add message": "اضافه کردن پیغام", | ||||||
| 	"admin": "مدیریت", | 	"add tags": "اضافه کردن تگ\u200cها", | ||||||
|  | 	"Adjusting these settings will apply changes universally to all users.": "با تنظیم این تنظیمات، تغییرات به طور کلی برای همه کاربران اعمال می شود.", | ||||||
|  | 	"Admin": "", | ||||||
| 	"Admin Panel": "پنل مدیریت", | 	"Admin Panel": "پنل مدیریت", | ||||||
| 	"Admin Settings": "تنظیمات مدیریت", | 	"Admin Settings": "تنظیمات مدیریت", | ||||||
| 	"Advanced Model Params": "پارامترهای پیشرفته مدل", | 	"Advanced Model Params": "پارامترهای پیشرفته مدل", | ||||||
| 	"Advanced Parameters": "پارامترهای پیشرفته", | 	"Advanced Parameters": "پارامترهای پیشرفته", | ||||||
| 	"all": "همه", | 	"all": "همه", | ||||||
|  | 	"All Users": "همه کاربران", | ||||||
| 	"Allow": "اجازه دادن", | 	"Allow": "اجازه دادن", | ||||||
| 	"Allow Chat Deletion": "اجازه حذف گپ", | 	"Allow Chat Deletion": "اجازه حذف گپ", | ||||||
|  | 	"alphanumeric characters and hyphens": "حروف الفبایی و خط فاصله", | ||||||
|  | 	"Already have an account?": "از قبل حساب کاربری دارید؟", | ||||||
|  | 	"and": "و", | ||||||
| 	"API Base URL": "API Base URL", | 	"API Base URL": "API Base URL", | ||||||
| 	"API Key": "API Key", | 	"API Key": "API Key", | ||||||
| 	"API RPM": "API RPM", | 	"API RPM": "API RPM", | ||||||
|  | 	"are allowed - Activate this command by typing": "مجاز هستند - این دستور را با تایپ کردن این فعال کنید:", | ||||||
| 	"Audio": "صدا", | 	"Audio": "صدا", | ||||||
| 	"AUTOMATIC1111 Base URL": "پایه URL AUTOMATIC1111 ", |  | ||||||
| 	"Auto-playback response": "پخش خودکار پاسخ ", | 	"Auto-playback response": "پخش خودکار پاسخ ", | ||||||
| 	"Auto-send input after 3 sec.": "به طور خودکار ورودی را پس از 3 ثانیه ارسال کن.", | 	"Auto-send input after 3 sec.": "به طور خودکار ورودی را پس از 3 ثانیه ارسال کن.", | ||||||
|  | 	"AUTOMATIC1111 Base URL": "پایه URL AUTOMATIC1111 ", | ||||||
|  | 	"available!": "در دسترس!", | ||||||
| 	"Back": "بازگشت", | 	"Back": "بازگشت", | ||||||
| 	"Builder Mode": "حالت سازنده", | 	"Builder Mode": "حالت سازنده", | ||||||
| 	"Cancel": "لغو", | 	"Cancel": "لغو", | ||||||
| 	"Categories": "دستهبندیها", | 	"Categories": "دسته\u200cبندی\u200cها", | ||||||
| 	"Change Password": "تغییر رمز عبور", | 	"Change Password": "تغییر رمز عبور", | ||||||
| 	"Chat History": "تاریخچهی گفتگو", | 	"Chat": "گپ", | ||||||
|  | 	"Chat History": "تاریخچه\u200cی گفتگو", | ||||||
| 	"Chat History is off for this browser.": "سابقه گپ برای این مرورگر خاموش است.", | 	"Chat History is off for this browser.": "سابقه گپ برای این مرورگر خاموش است.", | ||||||
| 	"Chats": "گپها", | 	"Chats": "گپ\u200cها", | ||||||
| 	"Check Again": "چک مجدد", | 	"Check Again": "چک مجدد", | ||||||
|  | 	"Check for updates": "بررسی به\u200cروزرسانی", | ||||||
|  | 	"Checking for updates...": "در حال بررسی برای به\u200cروزرسانی..", | ||||||
| 	"Choose a model before saving...": "قبل از ذخیره یک مدل را انتخاب کنید...", | 	"Choose a model before saving...": "قبل از ذخیره یک مدل را انتخاب کنید...", | ||||||
| 	"Chunk Overlap": "همپوشانی تکه", | 	"Chunk Overlap": "همپوشانی تکه", | ||||||
| 	"Chunk Params": "پارامترهای تکه", | 	"Chunk Params": "پارامترهای تکه", | ||||||
| 	"Chunk Size": "اندازه تکه", | 	"Chunk Size": "اندازه تکه", | ||||||
|  | 	"Click here for help": "برای کمک اینجا را کلیک کنید", | ||||||
|  | 	"Click here to check other modelfiles.": "برای بررسی سایر فایل\u200cهای مدل اینجا را کلیک کنید.", | ||||||
| 	"click here.": "اینجا کلیک کنید.", | 	"click here.": "اینجا کلیک کنید.", | ||||||
| 	"Click here to check other modelfiles.": "برای بررسی سایر فایلهای مدل اینجا را کلیک کنید.", | 	"Click on the user role button to change a user's role.": "برای تغییر نقش کاربر، روی دکمه نقش کاربر کلیک کنید.", | ||||||
|  | 	"Close": "بسته", | ||||||
| 	"Collection": "مجموعه", | 	"Collection": "مجموعه", | ||||||
| 	"Command": "دستور", | 	"Command": "دستور", | ||||||
| 	"Confirm Password": "تایید رمز عبور", | 	"Confirm Password": "تایید رمز عبور", | ||||||
|  | @ -44,68 +67,86 @@ | ||||||
| 	"Content": "محتوا", | 	"Content": "محتوا", | ||||||
| 	"Context Length": "طول زمینه", | 	"Context Length": "طول زمینه", | ||||||
| 	"Conversation Mode": "حالت مکالمه", | 	"Conversation Mode": "حالت مکالمه", | ||||||
| 	"Copying to clipboard was successful!": "کپی کردن در کلیپ بورد با موفقیت انجام شد!", |  | ||||||
| 	"Copy last code block": "کپی آخرین بلوک کد", | 	"Copy last code block": "کپی آخرین بلوک کد", | ||||||
| 	"Copy last response": "کپی آخرین پاسخ", | 	"Copy last response": "کپی آخرین پاسخ", | ||||||
|  | 	"Copying to clipboard was successful!": "کپی کردن در کلیپ بورد با موفقیت انجام شد!", | ||||||
|  | 	"Create a concise, 3-5 word phrase as a header for the following query, strictly adhering to the 3-5 word limit and avoiding the use of the word 'title':": "یک عبارت مختصر و ۳ تا ۵ کلمه ای را به عنوان سرفصل برای پرس و جو زیر ایجاد کنید، به شدت محدودیت ۳-۵ کلمه را رعایت کنید و از استفاده از کلمه 'عنوان' خودداری کنید:", | ||||||
| 	"Create a modelfile": "ایجاد یک فایل مدل", | 	"Create a modelfile": "ایجاد یک فایل مدل", | ||||||
|  | 	"Create Account": "ساخت حساب کاربری", | ||||||
|  | 	"Created at": "ایجاد شده در", | ||||||
|  | 	"Created by": "ایجاد شده توسط", | ||||||
|  | 	"Current Model": "مدل فعلی", | ||||||
| 	"Current Password": "رمز عبور فعلی", | 	"Current Password": "رمز عبور فعلی", | ||||||
| 	"Custom": "دلخواه", | 	"Custom": "دلخواه", | ||||||
| 	"Customize Ollama models for a specific purpose": "مدل های اولاما را برای یک هدف خاص سفارشی کنید", | 	"Customize Ollama models for a specific purpose": "مدل های اولاما را برای یک هدف خاص سفارشی کنید", | ||||||
| 	"Dark": "تیره", | 	"Dark": "تیره", | ||||||
| 	"Database": "پایگاه داده", | 	"Database": "پایگاه داده", | ||||||
|  | 	"DD/MM/YYYY HH:mm": "DD/MM/YYYY HH:mm", | ||||||
| 	"Default": "پیشفرض", | 	"Default": "پیشفرض", | ||||||
| 	"Default model updated": "مدل پیشفرض بهروزرسانی شد", | 	"Default (Web API)": "پیشفرض (Web API)", | ||||||
|  | 	"Default model updated": "مدل پیشفرض به\u200cروزرسانی شد", | ||||||
| 	"Default Prompt Suggestions": "پیشنهادات پرامپت پیش فرض", | 	"Default Prompt Suggestions": "پیشنهادات پرامپت پیش فرض", | ||||||
| 	"Default User Role": "نقش کاربر پیش فرض", | 	"Default User Role": "نقش کاربر پیش فرض", | ||||||
| 	"Default (Web API)": "پیشفرض (Web API)", | 	"delete": "حذف", | ||||||
| 	"Delete a model": "حذف یک مدل", | 	"Delete a model": "حذف یک مدل", | ||||||
| 	"Delete chat": "حذف گپ", | 	"Delete chat": "حذف گپ", | ||||||
| 	"Delete Chats": "حذف گپها", | 	"Delete Chats": "حذف گپ\u200cها", | ||||||
| 	"Deleted {{deleteModelTag}}": "{{deleteModelTag}} پاک شد", | 	"Deleted {{deleteModelTag}}": "{{deleteModelTag}} پاک شد", | ||||||
| 	"delete": "حذف", |  | ||||||
| 	"Deleted {tagName}": "{tagName} حذف شد", | 	"Deleted {tagName}": "{tagName} حذف شد", | ||||||
| 	"Description": "توضیحات", | 	"Description": "توضیحات", | ||||||
|  | 	"Desktop Notifications": "اعلان", | ||||||
| 	"Disabled": "غیرفعال", | 	"Disabled": "غیرفعال", | ||||||
| 	"Discover a modelfile": "فایل مدل را کشف کنید", | 	"Discover a modelfile": "فایل مدل را کشف کنید", | ||||||
| 	"Discover a prompt": "یک اعلان را کشف کنید", | 	"Discover a prompt": "یک اعلان را کشف کنید", | ||||||
| 	"Discover, download, and explore custom prompts": "پرامپتهای سفارشی را کشف، دانلود و کاوش کنید", | 	"Discover, download, and explore custom prompts": "پرامپت\u200cهای سفارشی را کشف، دانلود و کاوش کنید", | ||||||
| 	"Discover, download, and explore model presets": "پیش تنظیمات مدل را کشف، دانلود و کاوش کنید", | 	"Discover, download, and explore model presets": "پیش تنظیمات مدل را کشف، دانلود و کاوش کنید", | ||||||
|  | 	"Display the username instead of You in the Chat": "نمایش نام کاربری به جای «شما» در چت", | ||||||
| 	"Document": "سند", | 	"Document": "سند", | ||||||
| 	"Documents": "اسناد", |  | ||||||
| 	"Document Settings": "تنظیمات سند", | 	"Document Settings": "تنظیمات سند", | ||||||
|  | 	"Documents": "اسناد", | ||||||
|  | 	"does not make any external connections, and your data stays securely on your locally hosted server.": "هیچ اتصال خارجی ایجاد نمی کند و داده های شما به طور ایمن در سرور میزبان محلی شما باقی می ماند.", | ||||||
| 	"Don't Allow": "اجازه نده", | 	"Don't Allow": "اجازه نده", | ||||||
|  | 	"Don't have an account?": "حساب کاربری ندارید؟", | ||||||
| 	"Download as a File": "دانلود به صورت فایل", | 	"Download as a File": "دانلود به صورت فایل", | ||||||
| 	"Download Database": "دانلود پایگاه داده", | 	"Download Database": "دانلود پایگاه داده", | ||||||
|  | 	"Drop any files here to add to the conversation": "هر فایلی را اینجا رها کنید تا به مکالمه اضافه شود", | ||||||
|  | 	"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "به طور مثال '30s','10m'. واحد\u200cهای زمانی معتبر 's', 'm', 'h' هستند.", | ||||||
| 	"Edit Doc": "ویرایش سند", | 	"Edit Doc": "ویرایش سند", | ||||||
| 	"Edit User": "ویرایش کاربر", | 	"Edit User": "ویرایش کاربر", | ||||||
| 	"Email": "ایمیل", | 	"Email": "ایمیل", | ||||||
|  | 	"Enable Chat History": "تاریخچه چت را فعال کنید", | ||||||
|  | 	"Enable New Sign Ups": "فعال کردن ثبت نام\u200cهای جدید", | ||||||
| 	"Enabled": "فعال", | 	"Enabled": "فعال", | ||||||
| 	"Enable New Sign Ups": "فعال کردن ثبت نامهای جدید", | 	"Enter OpenAI API Key": "کلید OpenAI API را وارد کنید", | ||||||
| 	"Experimental": "آزمایشی", | 	"Enter stop sequence": "توالی توقف را وارد کنید", | ||||||
| 	"Export All Chats (All Users)": "اکسپورت از همه گپها(همه کاربران)", | 	"Enter Your Email": "ایمیل خود را وارد کنید", | ||||||
| 	"Export Chats": "اکسپورت از گپها", | 	"Enter Your Full Name": "نام کامل خود را وارد کنید", | ||||||
|  | 	"Enter Your Password": "رمز عبور خود را وارد کنید", | ||||||
|  | 	"Export All Chats (All Users)": "اکسپورت از همه گپ\u200cها(همه کاربران)", | ||||||
|  | 	"Export Chats": "اکسپورت از گپ\u200cها", | ||||||
| 	"Export Documents Mapping": "اکسپورت از نگاشت اسناد", | 	"Export Documents Mapping": "اکسپورت از نگاشت اسناد", | ||||||
| 	"Export Modelfiles": "اکسپورت از فایلهای مدل", | 	"Export Modelfiles": "اکسپورت از فایل\u200cهای مدل", | ||||||
| 	"Export Prompts": "اکسپورت از پرامپتها", | 	"Export Prompts": "اکسپورت از پرامپت\u200cها", | ||||||
| 	"Failed to read clipboard contents": "خواندن محتوای کلیپ بورد ناموفق بود", | 	"Failed to read clipboard contents": "خواندن محتوای کلیپ بورد ناموفق بود", | ||||||
| 	"File Mode": "حالت فایل", |  | ||||||
| 	"File not found.": "فایل یافت نشد.", | 	"File not found.": "فایل یافت نشد.", | ||||||
| 	"Focus chat input": "فوکوس کردن ورودی گپ", | 	"Focus chat input": "فوکوس کردن ورودی گپ", | ||||||
|  | 	"Format your variables using square brackets like this:": "متغیرهای خود را با استفاده از براکت مربع به شکل زیر قالب بندی کنید:", | ||||||
| 	"From (Base Model)": "از (مدل پایه)", | 	"From (Base Model)": "از (مدل پایه)", | ||||||
| 	"Full Screen Mode": "حالت تمام صفحه", | 	"Full Screen Mode": "حالت تمام صفحه", | ||||||
| 	"General": "عمومی", | 	"General": "عمومی", | ||||||
| 	"General Settings": "تنظیمات عمومی", | 	"General Settings": "تنظیمات عمومی", | ||||||
| 	"Hello, {{name}}": "سلام، {{name}}", | 	"Hello, {{name}}": "سلام، {{name}}", | ||||||
|  | 	"Hide": "پنهان", | ||||||
| 	"How can I help you today?": "امروز چطور می توانم کمک تان کنم؟", | 	"How can I help you today?": "امروز چطور می توانم کمک تان کنم؟", | ||||||
| 	"Image Generation (Experimental)": "تولید تصویر (آزمایشی)", | 	"Image Generation (Experimental)": "تولید تصویر (آزمایشی)", | ||||||
| 	"Image Settings": "تنظیمات تصویر", | 	"Image Settings": "تنظیمات تصویر", | ||||||
| 	"Images": "تصاویر", | 	"Images": "تصاویر", | ||||||
| 	"Import Chats": "ایمپورت گپها", | 	"Import Chats": "ایمپورت گپ\u200cها", | ||||||
| 	"Import Documents Mapping": "ایمپورت نگاشت اسناد", | 	"Import Documents Mapping": "ایمپورت نگاشت اسناد", | ||||||
| 	"Import Modelfiles": "ایمپورت فایلهای مدل", | 	"Import Modelfiles": "ایمپورت فایل\u200cهای مدل", | ||||||
| 	"Import Prompts": "ایمپورت پرامپتها", | 	"Import Prompts": "ایمپورت پرامپت\u200cها", | ||||||
|  | 	"Include `--api` flag when running stable-diffusion-webui": "فلگ `--api` را هنکام اجرای stable-diffusion-webui استفاده کنید.", | ||||||
| 	"Interface": "رابط", | 	"Interface": "رابط", | ||||||
| 	"{{item}} not provided": "{{item}} ارائه نشده است", |  | ||||||
| 	"join our Discord for help.": "برای کمک به دیسکورد ما بپیوندید.", | 	"join our Discord for help.": "برای کمک به دیسکورد ما بپیوندید.", | ||||||
| 	"JSON": "JSON", | 	"JSON": "JSON", | ||||||
| 	"JWT Expiration": "JWT انقضای", | 	"JWT Expiration": "JWT انقضای", | ||||||
|  | @ -118,80 +159,90 @@ | ||||||
| 	"LiteLLM API Base URL": "LiteLLM API Base URL", | 	"LiteLLM API Base URL": "LiteLLM API Base URL", | ||||||
| 	"LiteLLM API Key": "LiteLLM API Key", | 	"LiteLLM API Key": "LiteLLM API Key", | ||||||
| 	"LiteLLM API RPM": "LiteLLM API RPM", | 	"LiteLLM API RPM": "LiteLLM API RPM", | ||||||
| 	"LLMs can make mistakes. Verify important information.": "مدلهای زبانی بزرگ میتوانند اشتباه کنند. اطلاعات مهم را راستیآزمایی کنید.", | 	"LLMs can make mistakes. Verify important information.": "مدل\u200cهای زبانی بزرگ می\u200cتوانند اشتباه کنند. اطلاعات مهم را راستی\u200cآزمایی کنید.", | ||||||
| 	"Made by OpenWebUI Community": "ساخته شده توسط OpenWebUI Community", | 	"Made by OpenWebUI Community": "ساخته شده توسط OpenWebUI Community", | ||||||
|  | 	"Make sure to enclose them with": "مطمئن شوید که آنها را با این محصور کنید:", | ||||||
| 	"Manage LiteLLM Models": "Manage LiteLLM Models", | 	"Manage LiteLLM Models": "Manage LiteLLM Models", | ||||||
| 	"Maximum of 3 models can be downloaded simultaneously. Please try again later.": "حداکثر 3 مدل را می توان به طور همزمان دانلود کرد. لطفاً بعداً دوباره امتحان کنید.", | 	"Manage Ollama Models": "مدیریت مدل\u200cهای اولاما", | ||||||
| 	"Max Tokens": "حداکثر توکن", | 	"Max Tokens": "حداکثر توکن", | ||||||
| 	"Mirostat Eta": "Mirostat Eta", | 	"Maximum of 3 models can be downloaded simultaneously. Please try again later.": "حداکثر 3 مدل را می توان به طور همزمان دانلود کرد. لطفاً بعداً دوباره امتحان کنید.", | ||||||
| 	"Mirostat": "Mirostat", | 	"Mirostat": "Mirostat", | ||||||
|  | 	"Mirostat Eta": "Mirostat Eta", | ||||||
| 	"Mirostat Tau": "Mirostat Tau", | 	"Mirostat Tau": "Mirostat Tau", | ||||||
|  | 	"MMMM DD, YYYY": "MMMM DD, YYYY", | ||||||
| 	"Model '{{modelName}}' has been successfully downloaded.": "مدل '{{modelName}}' با موفقیت دانلود شد.", | 	"Model '{{modelName}}' has been successfully downloaded.": "مدل '{{modelName}}' با موفقیت دانلود شد.", | ||||||
| 	"Modelfile Advanced Settings": "تنظیمات پیشرفته فایلمدل", | 	"Model '{{modelTag}}' is already in queue for downloading.": "مدل '{{modelTag}}' در حال حاضر در صف برای دانلود است.", | ||||||
| 	"Modelfile Content": "محتویات فایلمدل", |  | ||||||
| 	"Modelfile": "فایل مدل", |  | ||||||
| 	"Modelfiles": "فایلهای مدل", |  | ||||||
| 	"Model {{modelId}} not found": "مدل {{modelId}} یافت نشد", | 	"Model {{modelId}} not found": "مدل {{modelId}} یافت نشد", | ||||||
| 	"Model {{modelName}} already exists.": "مدل {{modelName}} در حال حاضر وجود دارد.", | 	"Model {{modelName}} already exists.": "مدل {{modelName}} در حال حاضر وجود دارد.", | ||||||
| 	"Model '{{modelTag}}' is already in queue for downloading.": "مدل '{{modelTag}}' در حال حاضر در صف برای دانلود است.", |  | ||||||
| 	"{{modelName}} is thinking...": "{{modelName}} در حال فکر کردن است...", |  | ||||||
| 	"Model Name": "نام مدل", | 	"Model Name": "نام مدل", | ||||||
| 	"Model not selected": "مدل انتخاب نشده", | 	"Model not selected": "مدل انتخاب نشده", | ||||||
| 	"Models": "مدلها", |  | ||||||
| 	"Model Tag Name": "نام تگ مدل", | 	"Model Tag Name": "نام تگ مدل", | ||||||
|  | 	"Modelfile": "فایل مدل", | ||||||
|  | 	"Modelfile Advanced Settings": "تنظیمات پیشرفته فایل\u200cمدل", | ||||||
|  | 	"Modelfiles": "فایل\u200cهای مدل", | ||||||
|  | 	"Models": "مدل\u200cها", | ||||||
| 	"My Documents": "اسناد من", | 	"My Documents": "اسناد من", | ||||||
| 	"My Modelfiles": "فایلهای مدل من", | 	"My Modelfiles": "فایل\u200cهای مدل من", | ||||||
| 	"My Prompts": "پرامپتهای من", | 	"My Prompts": "پرامپت\u200cهای من", | ||||||
| 	"Name": "نام", | 	"Name": "نام", | ||||||
| 	"Name Tag": "نام تگ", | 	"Name Tag": "نام تگ", | ||||||
|  | 	"Name your modelfile": "", | ||||||
| 	"New Chat": "گپ جدید", | 	"New Chat": "گپ جدید", | ||||||
| 	"New Password": "رمز عبور جدید", | 	"New Password": "رمز عبور جدید", | ||||||
| 	"Desktop Notifications": "اعلان", | 	"Not sure what to add?": "مطمئن نیستید چه چیزی را اضافه کنید؟", | ||||||
| 	"Not sure what to write? Switch to": "مطمئن نیستید چه بنویسید؟ تغییر به", | 	"Not sure what to write? Switch to": "مطمئن نیستید چه بنویسید؟ تغییر به", | ||||||
| 	"Off": "خاموش", | 	"Off": "خاموش", | ||||||
| 	"Okay, Let's Go!": "باشه، بزن بریم!", | 	"Okay, Let's Go!": "باشه، بزن بریم!", | ||||||
| 	"Ollama API URL": "اولاما API URL", |  | ||||||
| 	"Ollama Version": "نسخه Ollama", | 	"Ollama Version": "نسخه Ollama", | ||||||
| 	"Only alphanumeric characters and hyphens are allowed in the command string.": "فقط کاراکترهای الفبایی و خط فاصله در رشته فرمان مجاز هستند.", |  | ||||||
| 	"On": "روشن", | 	"On": "روشن", | ||||||
|  | 	"Only": "فقط", | ||||||
|  | 	"Only alphanumeric characters and hyphens are allowed in the command string.": "فقط کاراکترهای الفبایی و خط فاصله در رشته فرمان مجاز هستند.", | ||||||
| 	"Oops! Hold tight! Your files are still in the processing oven. We're cooking them up to perfection. Please be patient and we'll let you know once they're ready.": "اوه! فایل های شما هنوز در فر پردازش هستند. ما آنها را کامل می پزیم. لطفا صبور باشید، به محض آماده شدن به شما اطلاع خواهیم داد.", | 	"Oops! Hold tight! Your files are still in the processing oven. We're cooking them up to perfection. Please be patient and we'll let you know once they're ready.": "اوه! فایل های شما هنوز در فر پردازش هستند. ما آنها را کامل می پزیم. لطفا صبور باشید، به محض آماده شدن به شما اطلاع خواهیم داد.", | ||||||
| 	"OpenAI API": "OpenAI API", | 	"Oops! Looks like the URL is invalid. Please double-check and try again.": "", | ||||||
|  | 	"Oops! You're using an unsupported method (frontend only). Please serve the WebUI from the backend.": "", | ||||||
|  | 	"Open": "باز", | ||||||
| 	"Open AI": "Open AI", | 	"Open AI": "Open AI", | ||||||
| 	"Open new chat": "باز کردن گپ جدید", | 	"Open new chat": "باز کردن گپ جدید", | ||||||
|  | 	"OpenAI API": "OpenAI API", | ||||||
| 	"or": "روشن", | 	"or": "روشن", | ||||||
| 	"Parameters": "پارامترها", | 	"Parameters": "پارامترها", | ||||||
| 	"Password": "رمز عبور", | 	"Password": "رمز عبور", | ||||||
| 	"pending": "Pending", | 	"Pending": "", | ||||||
| 	"Permission denied when accessing microphone: {{error}}": "هنگام دسترسی به میکروفون، اجازه داده نشد: {{error}}", | 	"Permission denied when accessing microphone: {{error}}": "هنگام دسترسی به میکروفون، اجازه داده نشد: {{error}}", | ||||||
|  | 	"Playground": "زمین بازی", | ||||||
| 	"Profile": "پروفایل", | 	"Profile": "پروفایل", | ||||||
| 	"Prompt Content": "محتویات پرامپت", | 	"Prompt Content": "محتویات پرامپت", | ||||||
| 	"Prompts": "پرامپتها", |  | ||||||
| 	"Prompt suggestions": "پیشنهادات پرامپت", | 	"Prompt suggestions": "پیشنهادات پرامپت", | ||||||
|  | 	"Prompts": "پرامپت\u200cها", | ||||||
| 	"Pull a model from Ollama.com": "دریافت یک مدل از Ollama.com", | 	"Pull a model from Ollama.com": "دریافت یک مدل از Ollama.com", | ||||||
| 	"Pull Progress": "پیشرفت دریافت", | 	"Pull Progress": "پیشرفت دریافت", | ||||||
| 	"RAG Template": "RAG الگوی", | 	"RAG Template": "RAG الگوی", | ||||||
| 	"Raw Format": "فرمت خام", | 	"Raw Format": "فرمت خام", | ||||||
| 	"Record voice": "ضبط صدا", | 	"Record voice": "ضبط صدا", | ||||||
| 	"Redirecting you to OpenWebUI Community": "در حال هدایت به OpenWebUI Community", | 	"Redirecting you to OpenWebUI Community": "در حال هدایت به OpenWebUI Community", | ||||||
| 	"Release Notes": "یادداشتهای انتشار", | 	"Release Notes": "یادداشت\u200cهای انتشار", | ||||||
| 	"Repeat Last N": "Repeat Last N", | 	"Repeat Last N": "Repeat Last N", | ||||||
| 	"Repeat Penalty": "Repeat Penalty", | 	"Repeat Penalty": "Repeat Penalty", | ||||||
| 	"Request Mode": "حالت درخواست", | 	"Request Mode": "حالت درخواست", | ||||||
| 	"Reset Vector Storage": "بازنشانی ذخیره سازی برداری", | 	"Reset Vector Storage": "بازنشانی ذخیره سازی برداری", | ||||||
| 	"Response AutoCopy to Clipboard": "کپی خودکار پاسخ به کلیپ بورد", | 	"Response AutoCopy to Clipboard": "کپی خودکار پاسخ به کلیپ بورد", | ||||||
| 	"Role": "نقش", | 	"Role": "نقش", | ||||||
| 	"Ros\u00e9 Pine Dawn": "Ros\u00e9 Pine Dawn", | 	"Rosé Pine": "Rosé Pine", | ||||||
| 	"Ros\u00e9 Pine": "Ros\u00e9 Pine", | 	"Rosé Pine Dawn": "Rosé Pine Dawn", | ||||||
| 	"Save & Create": "ذخیره و ایجاد", |  | ||||||
| 	"Save": "ذخیره", | 	"Save": "ذخیره", | ||||||
|  | 	"Save & Create": "ذخیره و ایجاد", | ||||||
| 	"Save & Submit": "ذخیره و ارسال", | 	"Save & Submit": "ذخیره و ارسال", | ||||||
| 	"Save & Update": "ذخیره و بهروزرسانی", | 	"Save & Update": "ذخیره و به\u200cروزرسانی", | ||||||
| 	"Saving chat logs directly to your browser's storage is no longer supported. Please take a moment to download and delete your chat logs by clicking the button below. Don't worry, you can easily re-import your chat logs to the backend through": "ذخیره گزارشهای چت مستقیماً در حافظه مرورگر شما دیگر پشتیبانی نمیشود. لطفاً با کلیک بر روی دکمه زیر، چند لحظه برای دانلود و حذف گزارش های چت خود وقت بگذارید. نگران نباشید، شما به راحتی می توانید گزارش های چت خود را از طریق بکند دوباره وارد کنید", | 	"Saving chat logs directly to your browser's storage is no longer supported. Please take a moment to download and delete your chat logs by clicking the button below. Don't worry, you can easily re-import your chat logs to the backend through": "ذخیره گزارش\u200cهای چت مستقیماً در حافظه مرورگر شما دیگر پشتیبانی نمی\u200cشود. لطفاً با کلیک بر روی دکمه زیر، چند لحظه برای دانلود و حذف گزارش های چت خود وقت بگذارید. نگران نباشید، شما به راحتی می توانید گزارش های چت خود را از طریق بکند دوباره وارد کنید", | ||||||
|  | 	"Scan": "اسکن", | ||||||
| 	"Scan complete!": "اسکن کامل شد!", | 	"Scan complete!": "اسکن کامل شد!", | ||||||
| 	"Scan for documents from {{path}}": "اسکن اسناد از {{path}}", | 	"Scan for documents from {{path}}": "اسکن اسناد از {{path}}", | ||||||
| 	"Scan": "اسکن", | 	"Search": "جستجو", | ||||||
|  | 	"Search Documents": "جستجوی اسناد", | ||||||
|  | 	"Search Prompts": "جستجوی پرامپت\u200cها", | ||||||
|  | 	"See readme.md for instructions": "برای مشاهده دستورالعمل\u200cها به readme.md مراجعه کنید", | ||||||
|  | 	"See what's new": "ببینید موارد جدید چه بوده", | ||||||
| 	"Seed": "Seed", | 	"Seed": "Seed", | ||||||
| 	"See readme.md for instructions": "برای مشاهده دستورالعملها به readme.md مراجعه کنید", |  | ||||||
| 	"Select a model": "انتخاب یک مدل", | 	"Select a model": "انتخاب یک مدل", | ||||||
| 	"Send a Messsage": "ارسال یک پیام", | 	"Send a Messsage": "ارسال یک پیام", | ||||||
| 	"Send message": "ارسال پیام", | 	"Send message": "ارسال پیام", | ||||||
|  | @ -199,129 +250,71 @@ | ||||||
| 	"Set as default": "تنظیم به عنوان پیشفرض", | 	"Set as default": "تنظیم به عنوان پیشفرض", | ||||||
| 	"Set Default Model": "تنظیم مدل پیش فرض", | 	"Set Default Model": "تنظیم مدل پیش فرض", | ||||||
| 	"Set Image Size": "تنظیم اندازه تصویر", | 	"Set Image Size": "تنظیم اندازه تصویر", | ||||||
| 	"Set Steps": "تنظیم گامها", | 	"Set Steps": "تنظیم گام\u200cها", | ||||||
| 	"Settings": "تنظیمات", |  | ||||||
| 	"Set Title Auto-Generation Model": "تنظیم مدل تولید خودکار عنوان", | 	"Set Title Auto-Generation Model": "تنظیم مدل تولید خودکار عنوان", | ||||||
| 	"Set Voice": "تنظیم صدا", | 	"Set Voice": "تنظیم صدا", | ||||||
|  | 	"Settings": "تنظیمات", | ||||||
| 	"Share to OpenWebUI Community": "اشتراک گذاری با OpenWebUI Community", | 	"Share to OpenWebUI Community": "اشتراک گذاری با OpenWebUI Community", | ||||||
|  | 	"short-summary": "خلاصه کوتاه", | ||||||
|  | 	"Show": "نمایش", | ||||||
| 	"Show shortcuts": "نمایش میانبرها", | 	"Show shortcuts": "نمایش میانبرها", | ||||||
|  | 	"sidebar": "نوار کناری", | ||||||
|  | 	"Sign in": "ورود", | ||||||
| 	"Sign Out": "خروج", | 	"Sign Out": "خروج", | ||||||
| 	"SpeechRecognition API is not supported in this browser.": "API تشخیص گفتار در این مرورگر پشتیبانی نمی شود.", | 	"Sign up": "ثبت نام", | ||||||
| 	"Speech recognition error: {{error}}": "خطای تشخیص گفتار: {{error}}", | 	"Speech recognition error: {{error}}": "خطای تشخیص گفتار: {{error}}", | ||||||
| 	"Speech-to-Text Engine": "موتور گفتار به متن", | 	"Speech-to-Text Engine": "موتور گفتار به متن", | ||||||
|  | 	"SpeechRecognition API is not supported in this browser.": "API تشخیص گفتار در این مرورگر پشتیبانی نمی شود.", | ||||||
| 	"Stop Sequence": "توالی توقف", | 	"Stop Sequence": "توالی توقف", | ||||||
| 	"STT Settings": "STT تنظیمات", | 	"STT Settings": "STT تنظیمات", | ||||||
| 	"Successfully updated.": "با موفقیت به روز شد", | 	"Submit": "ارسال", | ||||||
| 	"Success": "موفقیت", | 	"Success": "موفقیت", | ||||||
|  | 	"Successfully updated": "", | ||||||
|  | 	"Successfully updated.": "با موفقیت به روز شد", | ||||||
| 	"Sync All": "همگام سازی همه", | 	"Sync All": "همگام سازی همه", | ||||||
|  | 	"System": "سیستم", | ||||||
| 	"System Prompt": "پرامپت سیستم", | 	"System Prompt": "پرامپت سیستم", | ||||||
| 	"Tags": "تگها", | 	"Tags": "تگ\u200cها", | ||||||
| 	"Temperature": "دما", | 	"Temperature": "دما", | ||||||
| 	"Template": "الگو", | 	"Template": "الگو", | ||||||
|  | 	"Text Completion": "تکمیل متن", | ||||||
| 	"Text-to-Speech Engine": "موتور تبدیل متن به گفتار", | 	"Text-to-Speech Engine": "موتور تبدیل متن به گفتار", | ||||||
| 	"Tfs Z": "Tfs Z", | 	"Tfs Z": "Tfs Z", | ||||||
| 	"Theme": "قالب", | 	"Theme": "قالب", | ||||||
| 	"This ensures that your valuable conversations are securely saved to your backend database. Thank you!": "این تضمین می کند که مکالمات ارزشمند شما به طور ایمن در پایگاه داده بکند ذخیره می شود. تشکر!", | 	"This ensures that your valuable conversations are securely saved to your backend database. Thank you!": "این تضمین می کند که مکالمات ارزشمند شما به طور ایمن در پایگاه داده بکند ذخیره می شود. تشکر!", | ||||||
|  | 	"This setting does not sync across browsers or devices.": "این تنظیم در مرورگرها یا دستگاه\u200cها همگام\u200cسازی نمی\u200cشود.", | ||||||
|  | 	"Tip: Update multiple variable slots consecutively by pressing the tab key in the chat input after each replacement.": "", | ||||||
|  | 	"Title": "عنوان", | ||||||
| 	"Title Auto-Generation": "تولید خودکار عنوان", | 	"Title Auto-Generation": "تولید خودکار عنوان", | ||||||
| 	"Title Generation Prompt": "پرامپت تولید عنوان", | 	"Title Generation Prompt": "پرامپت تولید عنوان", | ||||||
| 	"Title": "عنوان", | 	"to": "به", | ||||||
| 	"To access the available model names for downloading,": "برای دسترسی به نام مدل های موجود برای دانلود،", | 	"To access the available model names for downloading,": "برای دسترسی به نام مدل های موجود برای دانلود،", | ||||||
| 	"Toggle settings": "نمایش/عدم نمایش تنظیمات", | 	"Toggle settings": "نمایش/عدم نمایش تنظیمات", | ||||||
| 	"Toggle sidebar": "نمایش/عدم نمایش نوار کناری", | 	"Toggle sidebar": "نمایش/عدم نمایش نوار کناری", | ||||||
| 	"Top K": "Top K", | 	"Top K": "Top K", | ||||||
| 	"Top P": "Top P", | 	"Top P": "Top P", | ||||||
|  | 	"Trouble accessing Ollama?": "در دسترسی به اولاما مشکل دارید؟", | ||||||
| 	"TTS Settings": "تنظیمات TTS", | 	"TTS Settings": "تنظیمات TTS", | ||||||
| 	"Uh-oh! There was an issue connecting to {{provider}}.": "اوه اوه! مشکلی در اتصال به {{provider}} وجود داشت.", | 	"Uh-oh! There was an issue connecting to {{provider}}.": "اوه اوه! مشکلی در اتصال به {{provider}} وجود داشت.", | ||||||
| 	"Upload a GGUF model": "بارگذاری یک مدل GGUF", | 	"Unknown File Type '{{file_type}}', but accepting and treating as plain text": "", | ||||||
| 	"Upload files": "بارگذاری فایلها", | 	"Upload files": "بارگذاری فایل\u200cها", | ||||||
| 	"Upload Progress": "میزان پیشرفت بارگذاری", | 	"Use '#' in the prompt input to load and select your documents.": "در پرامپت از '#' برای لود و انتخاب اسناد خود استفاده کنید.", | ||||||
| 	"URL Mode": "حالت URL", | 	"User": "", | ||||||
| 	"User Permissions": "مجوزهای کاربر", | 	"User Permissions": "مجوزهای کاربر", | ||||||
| 	"Users": "کاربران", | 	"Users": "کاربران", | ||||||
| 	"user": "کاربر", |  | ||||||
| 	"WebUI Add-ons": "WebUI افزونههای", |  | ||||||
| 	"{{webUIName}} Backend Required": "بکند {{webUIName}} نیاز است.", |  | ||||||
| 	"WebUI Settings": "تنظیمات WebUI", |  | ||||||
| 	"Web": "وب", |  | ||||||
| 	"Whisper (Local)": "ویسپر (محلی)", |  | ||||||
| 	"You're now logged in.": "شما اکنون وارد شدهاید.", |  | ||||||
| 	"Hide": "پنهان", |  | ||||||
| 	"Show": "نمایش", |  | ||||||
| 	"Enter stop sequence": "توالی توقف را وارد کنید", |  | ||||||
| 	"Enter OpenAI API Key": "کلید OpenAI API را وارد کنید", |  | ||||||
| 	"Current Model": "مدل فعلی", |  | ||||||
| 	"Display the username instead of You in the Chat": "نمایش نام کاربری به جای «شما» در چت", |  | ||||||
| 	"Create a concise, 3-5 word phrase as a header for the following query, strictly adhering to the 3-5 word limit and avoiding the use of the word 'title':": "یک عبارت مختصر و ۳ تا ۵ کلمه ای را به عنوان سرفصل برای پرس و جو زیر ایجاد کنید، به شدت محدودیت ۳-۵ کلمه را رعایت کنید و از استفاده از کلمه 'عنوان' خودداری کنید:", |  | ||||||
| 	"Search": "جستجو", |  | ||||||
| 	"Search Documents": "جستجوی اسناد", |  | ||||||
| 	"Use '#' in the prompt input to load and select your documents.": "در پرامپت از '#' برای لود و انتخاب اسناد خود استفاده کنید.", |  | ||||||
| 	"All Users": "همه کاربران", |  | ||||||
| 	"Click on the user role button to change a user's role.": "برای تغییر نقش کاربر، روی دکمه نقش کاربر کلیک کنید.", |  | ||||||
| 	"Manage Ollama Models": "مدیریت مدلهای اولاما", |  | ||||||
| 	"Advanced": "پیشرفته", |  | ||||||
| 	"Click here for help": "برای کمک اینجا را کلیک کنید", |  | ||||||
| 	"Not sure what to add?": "مطمئن نیستید چه چیزی را اضافه کنید؟", |  | ||||||
| 	"Adjusting these settings will apply changes universally to all users.": "با تنظیم این تنظیمات، تغییرات به طور کلی برای همه کاربران اعمال می شود.", |  | ||||||
| 	"Trouble accessing Ollama?": "در دسترسی به اولاما مشکل دارید؟", |  | ||||||
| 	"(e.g. `sh webui.sh --api`)": "(e.g. `sh webui.sh --api`)", |  | ||||||
| 	"Include `--api` flag when running stable-diffusion-webui": "فلگ `--api` را هنکام اجرای stable-diffusion-webui استفاده کنید.", |  | ||||||
| 	"This setting does not sync across browsers or devices.": "این تنظیم در مرورگرها یا دستگاهها همگامسازی نمیشود.", |  | ||||||
| 	"When history is turned off, new chats on this browser won't appear in your history on any of your devices.": "وقتی سابقه خاموش است، چتهای جدید در این مرورگر در سابقه شما در هیچ یک از دستگاههایتان ظاهر نمیشوند.", |  | ||||||
| 	"Enable Chat History": "تاریخچه چت را فعال کنید", |  | ||||||
| 	"Search Prompts": "جستجوی پرامپتها", |  | ||||||
| 	"available!": "در دسترس!", |  | ||||||
| 	"Created by": "ایجاد شده توسط", |  | ||||||
| 	"Check for updates": "بررسی بهروزرسانی", |  | ||||||
| 	"See what's new": "ببینید موارد جدید چه بوده", |  | ||||||
| 	"Checking for updates...": "در حال بررسی برای بهروزرسانی..", |  | ||||||
| 	"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": "استفاده کنید", | 	"Utilize": "استفاده کنید", | ||||||
|  | 	"Valid time units:": "واحدهای زمانی معتبر:", | ||||||
|  | 	"variable": "متغیر", | ||||||
| 	"variable to have them replaced with clipboard content.": "متغیر برای جایگزینی آنها با محتوای کلیپ بورد.", | 	"variable to have them replaced with clipboard content.": "متغیر برای جایگزینی آنها با محتوای کلیپ بورد.", | ||||||
| 	"short-summary": "خلاصه کوتاه", | 	"Web": "وب", | ||||||
| 	"Add a short title for this prompt": "یک عنوان کوتاه برای این درخواست اضافه کنید", | 	"WebUI Add-ons": "WebUI افزونه\u200cهای", | ||||||
| 	"Open": "باز", | 	"WebUI Settings": "تنظیمات WebUI", | ||||||
| 	"Close": "بسته", | 	"What’s New in": "موارد جدید در", | ||||||
| 	"sidebar": "نوار کناری", | 	"When history is turned off, new chats on this browser won't appear in your history on any of your devices.": "وقتی سابقه خاموش است، چت\u200cهای جدید در این مرورگر در سابقه شما در هیچ یک از دستگاه\u200cهایتان ظاهر نمی\u200cشوند.", | ||||||
| 	"Playground": "زمین بازی", | 	"Whisper (Local)": "ویسپر (محلی)", | ||||||
| 	"Submit": "ارسال", | 	"Write a prompt suggestion (e.g. Who are you?)": "یک پیشنهاد پرامپت بنویسید (مثلاً شما کی هستید؟)", | ||||||
| 	"(Beta)": "(بتا)", | 	"Write a summary in 50 words that summarizes [topic or keyword].": "خلاصه ای در 50 کلمه بنویسید که [موضوع یا کلمه کلیدی] را خلاصه کند.", | ||||||
| 	"Text Completion": "تکمیل متن", | 	"You": "شما", | ||||||
| 	"Chat": "گپ", |  | ||||||
| 	"You're a helpful assistant.": "تو یک دستیار سودمند هستی.", | 	"You're a helpful assistant.": "تو یک دستیار سودمند هستی.", | ||||||
| 	"System": "سیستم", | 	"You're now logged in.": "شما اکنون وارد شده\u200cاید." | ||||||
| 	"assistant": "دستیار", |  | ||||||
| 	"Add message": "اضافه کردن پیغام", |  | ||||||
| 	"Enter a user message here": "در اینجا یک پیام کاربر را وارد کنید", |  | ||||||
| 	"Enter an assistant message here": "در اینجا یک پیام دستیار را وارد کنید", |  | ||||||
| 	"Drop any files here to add to the conversation": "هر فایلی را اینجا رها کنید تا به مکالمه اضافه شود", |  | ||||||
| 	"You": "شما" |  | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -18,10 +18,9 @@ | ||||||
| 	"Add message": "Ajouter un message", | 	"Add message": "Ajouter un message", | ||||||
| 	"add tags": "ajouter des tags", | 	"add tags": "ajouter des tags", | ||||||
| 	"Adjusting these settings will apply changes universally to all users.": "L'ajustement de ces paramètres appliquera les changements à tous les utilisateurs.", | 	"Adjusting these settings will apply changes universally to all users.": "L'ajustement de ces paramètres appliquera les changements à tous les utilisateurs.", | ||||||
| 	"admin": "Administrateur", | 	"Admin": "", | ||||||
| 	"Admin Panel": "Panneau d'administration", | 	"Admin Panel": "Panneau d'administration", | ||||||
| 	"Admin Settings": "Paramètres d'administration", | 	"Admin Settings": "Paramètres d'administration", | ||||||
| 	"Advanced": "Avancé", |  | ||||||
| 	"Advanced Model Params": "Paramètres avancés du modèle", | 	"Advanced Model Params": "Paramètres avancés du modèle", | ||||||
| 	"Advanced Parameters": "Paramètres avancés", | 	"Advanced Parameters": "Paramètres avancés", | ||||||
| 	"all": "tous", | 	"all": "tous", | ||||||
|  | @ -35,7 +34,6 @@ | ||||||
| 	"API Key": "Clé API", | 	"API Key": "Clé API", | ||||||
| 	"API RPM": "RPM API", | 	"API RPM": "RPM API", | ||||||
| 	"are allowed - Activate this command by typing": "sont autorisés - Activez cette commande en tapant", | 	"are allowed - Activate this command by typing": "sont autorisés - Activez cette commande en tapant", | ||||||
| 	"assistant": "Assistant", |  | ||||||
| 	"Audio": "Audio", | 	"Audio": "Audio", | ||||||
| 	"Auto-playback response": "Réponse en lecture automatique", | 	"Auto-playback response": "Réponse en lecture automatique", | ||||||
| 	"Auto-send input after 3 sec.": "Envoyer automatiquement l'entrée après 3 sec.", | 	"Auto-send input after 3 sec.": "Envoyer automatiquement l'entrée après 3 sec.", | ||||||
|  | @ -72,6 +70,7 @@ | ||||||
| 	"Copy last code block": "Copier le dernier bloc de code", | 	"Copy last code block": "Copier le dernier bloc de code", | ||||||
| 	"Copy last response": "Copier la dernière réponse", | 	"Copy last response": "Copier la dernière réponse", | ||||||
| 	"Copying to clipboard was successful!": "La copie dans le presse-papiers a réussi !", | 	"Copying to clipboard was successful!": "La copie dans le presse-papiers a réussi !", | ||||||
|  | 	"Create a concise, 3-5 word phrase as a header for the following query, strictly adhering to the 3-5 word limit and avoiding the use of the word 'title':": "Créez une phrase concise de 3-5 mots comme en-tête pour la requête suivante, en respectant strictement la limite de 3-5 mots et en évitant l'utilisation du mot 'titre' :", | ||||||
| 	"Create a modelfile": "Créer un fichier de modèle", | 	"Create a modelfile": "Créer un fichier de modèle", | ||||||
| 	"Create Account": "Créer un compte", | 	"Create Account": "Créer un compte", | ||||||
| 	"Created at": "Créé le", | 	"Created at": "Créé le", | ||||||
|  | @ -87,7 +86,6 @@ | ||||||
| 	"Default (Web API)": "Par défaut (API Web)", | 	"Default (Web API)": "Par défaut (API Web)", | ||||||
| 	"Default model updated": "Modèle par défaut mis à jour", | 	"Default model updated": "Modèle par défaut mis à jour", | ||||||
| 	"Default Prompt Suggestions": "Suggestions de prompt par défaut", | 	"Default Prompt Suggestions": "Suggestions de prompt par défaut", | ||||||
| 	"Create a concise, 3-5 word phrase as a header for the following query, strictly adhering to the 3-5 word limit and avoiding the use of the word 'title':": "Créez une phrase concise de 3-5 mots comme en-tête pour la requête suivante, en respectant strictement la limite de 3-5 mots et en évitant l'utilisation du mot 'titre' :", |  | ||||||
| 	"Default User Role": "Rôle d'utilisateur par défaut", | 	"Default User Role": "Rôle d'utilisateur par défaut", | ||||||
| 	"delete": "supprimer", | 	"delete": "supprimer", | ||||||
| 	"Delete a model": "Supprimer un modèle", | 	"Delete a model": "Supprimer un modèle", | ||||||
|  | @ -119,21 +117,17 @@ | ||||||
| 	"Enable Chat History": "Activer l'historique du chat", | 	"Enable Chat History": "Activer l'historique du chat", | ||||||
| 	"Enable New Sign Ups": "Activer les nouvelles inscriptions", | 	"Enable New Sign Ups": "Activer les nouvelles inscriptions", | ||||||
| 	"Enabled": "Activé", | 	"Enabled": "Activé", | ||||||
| 	"Enter a user message here": "Entrez un message d'utilisateur ici", |  | ||||||
| 	"Enter an assistant message here": "Entrez un message d'assistant ici", |  | ||||||
| 	"Enter OpenAI API Key": "Entrez la clé API OpenAI", | 	"Enter OpenAI API Key": "Entrez la clé API OpenAI", | ||||||
| 	"Enter stop sequence": "Entrez la séquence d'arrêt", | 	"Enter stop sequence": "Entrez la séquence d'arrêt", | ||||||
| 	"Enter Your Email": "Entrez votre email", | 	"Enter Your Email": "Entrez votre email", | ||||||
| 	"Enter Your Full Name": "Entrez votre nom complet", | 	"Enter Your Full Name": "Entrez votre nom complet", | ||||||
| 	"Enter Your Password": "Entrez votre mot de passe", | 	"Enter Your Password": "Entrez votre mot de passe", | ||||||
| 	"Experimental": "Expérimental", |  | ||||||
| 	"Export All Chats (All Users)": "Exporter tous les chats (tous les utilisateurs)", | 	"Export All Chats (All Users)": "Exporter tous les chats (tous les utilisateurs)", | ||||||
| 	"Export Chats": "Exporter les chats", | 	"Export Chats": "Exporter les chats", | ||||||
| 	"Export Documents Mapping": "Exporter la correspondance des documents", | 	"Export Documents Mapping": "Exporter la correspondance des documents", | ||||||
| 	"Export Modelfiles": "Exporter les fichiers de modèle", | 	"Export Modelfiles": "Exporter les fichiers de modèle", | ||||||
| 	"Export Prompts": "Exporter les prompts", | 	"Export Prompts": "Exporter les prompts", | ||||||
| 	"Failed to read clipboard contents": "Échec de la lecture du contenu du presse-papiers", | 	"Failed to read clipboard contents": "Échec de la lecture du contenu du presse-papiers", | ||||||
| 	"File Mode": "Mode fichier", |  | ||||||
| 	"File not found.": "Fichier non trouvé.", | 	"File not found.": "Fichier non trouvé.", | ||||||
| 	"Focus chat input": "Concentrer sur l'entrée du chat", | 	"Focus chat input": "Concentrer sur l'entrée du chat", | ||||||
| 	"Format your variables using square brackets like this:": "Formatez vos variables en utilisant des crochets comme ceci :", | 	"Format your variables using square brackets like this:": "Formatez vos variables en utilisant des crochets comme ceci :", | ||||||
|  | @ -185,7 +179,6 @@ | ||||||
| 	"Model Tag Name": "Nom de tag du modèle", | 	"Model Tag Name": "Nom de tag du modèle", | ||||||
| 	"Modelfile": "Fichier de modèle", | 	"Modelfile": "Fichier de modèle", | ||||||
| 	"Modelfile Advanced Settings": "Paramètres avancés du fichier de modèle", | 	"Modelfile Advanced Settings": "Paramètres avancés du fichier de modèle", | ||||||
| 	"Modelfile Content": "Contenu du fichier de modèle", |  | ||||||
| 	"Modelfiles": "Fichiers de modèle", | 	"Modelfiles": "Fichiers de modèle", | ||||||
| 	"Models": "Modèles", | 	"Models": "Modèles", | ||||||
| 	"My Documents": "Mes documents", | 	"My Documents": "Mes documents", | ||||||
|  | @ -193,19 +186,20 @@ | ||||||
| 	"My Prompts": "Mes prompts", | 	"My Prompts": "Mes prompts", | ||||||
| 	"Name": "Nom", | 	"Name": "Nom", | ||||||
| 	"Name Tag": "Tag de nom", | 	"Name Tag": "Tag de nom", | ||||||
| 	"Name your Modelfile": "Nommez votre fichier de modèle", | 	"Name your modelfile": "", | ||||||
| 	"New Chat": "Nouveau chat", | 	"New Chat": "Nouveau chat", | ||||||
| 	"New Password": "Nouveau mot de passe", | 	"New Password": "Nouveau mot de passe", | ||||||
| 	"Not sure what to add?": "Vous ne savez pas quoi ajouter ?", | 	"Not sure what to add?": "Vous ne savez pas quoi ajouter ?", | ||||||
| 	"Not sure what to write? Switch to": "Vous ne savez pas quoi écrire ? Basculer vers", | 	"Not sure what to write? Switch to": "Vous ne savez pas quoi écrire ? Basculer vers", | ||||||
| 	"Off": "Désactivé", | 	"Off": "Désactivé", | ||||||
| 	"Okay, Let's Go!": "D'accord, allons-y !", | 	"Okay, Let's Go!": "D'accord, allons-y !", | ||||||
| 	"Ollama API URL": "URL de l'API Ollama", |  | ||||||
| 	"Ollama Version": "Version Ollama", | 	"Ollama Version": "Version Ollama", | ||||||
| 	"On": "Activé", | 	"On": "Activé", | ||||||
| 	"Only": "Seulement", | 	"Only": "Seulement", | ||||||
| 	"Only alphanumeric characters and hyphens are allowed in the command string.": "Seuls les caractères alphanumériques et les tirets sont autorisés dans la chaîne de commande.", | 	"Only alphanumeric characters and hyphens are allowed in the command string.": "Seuls les caractères alphanumériques et les tirets sont autorisés dans la chaîne de commande.", | ||||||
| 	"Oops! Hold tight! Your files are still in the processing oven. We're cooking them up to perfection. Please be patient and we'll let you know once they're ready.": "Oups ! Tenez bon ! Vos fichiers sont encore dans le four. Nous les cuisinons à la perfection. Soyez patient et nous vous informerons dès qu'ils seront prêts.", | 	"Oops! Hold tight! Your files are still in the processing oven. We're cooking them up to perfection. Please be patient and we'll let you know once they're ready.": "Oups ! Tenez bon ! Vos fichiers sont encore dans le four. Nous les cuisinons à la perfection. Soyez patient et nous vous informerons dès qu'ils seront prêts.", | ||||||
|  | 	"Oops! Looks like the URL is invalid. Please double-check and try again.": "", | ||||||
|  | 	"Oops! You're using an unsupported method (frontend only). Please serve the WebUI from the backend.": "", | ||||||
| 	"Open": "Ouvrir", | 	"Open": "Ouvrir", | ||||||
| 	"Open AI": "Open AI", | 	"Open AI": "Open AI", | ||||||
| 	"Open new chat": "Ouvrir un nouveau chat", | 	"Open new chat": "Ouvrir un nouveau chat", | ||||||
|  | @ -213,7 +207,7 @@ | ||||||
| 	"or": "ou", | 	"or": "ou", | ||||||
| 	"Parameters": "Paramètres", | 	"Parameters": "Paramètres", | ||||||
| 	"Password": "Mot de passe", | 	"Password": "Mot de passe", | ||||||
| 	"pending": "en attente", | 	"Pending": "", | ||||||
| 	"Permission denied when accessing microphone: {{error}}": "Permission refusée lors de l'accès au microphone : {{error}}", | 	"Permission denied when accessing microphone: {{error}}": "Permission refusée lors de l'accès au microphone : {{error}}", | ||||||
| 	"Playground": "Aire de jeu", | 	"Playground": "Aire de jeu", | ||||||
| 	"Profile": "Profil", | 	"Profile": "Profil", | ||||||
|  | @ -275,6 +269,7 @@ | ||||||
| 	"STT Settings": "Paramètres STT", | 	"STT Settings": "Paramètres STT", | ||||||
| 	"Submit": "Soumettre", | 	"Submit": "Soumettre", | ||||||
| 	"Success": "Succès", | 	"Success": "Succès", | ||||||
|  | 	"Successfully updated": "", | ||||||
| 	"Successfully updated.": "Mis à jour avec succès.", | 	"Successfully updated.": "Mis à jour avec succès.", | ||||||
| 	"Sync All": "Synchroniser tout", | 	"Sync All": "Synchroniser tout", | ||||||
| 	"System": "Système", | 	"System": "Système", | ||||||
|  | @ -288,12 +283,12 @@ | ||||||
| 	"Theme": "Thème", | 	"Theme": "Thème", | ||||||
| 	"This ensures that your valuable conversations are securely saved to your backend database. Thank you!": "Cela garantit que vos précieuses conversations sont en sécurité dans votre base de données. Merci !", | 	"This ensures that your valuable conversations are securely saved to your backend database. Thank you!": "Cela garantit que vos précieuses conversations sont en sécurité dans votre base de données. Merci !", | ||||||
| 	"This setting does not sync across browsers or devices.": "Ce paramètre ne se synchronise pas entre les navigateurs ou les appareils.", | 	"This setting does not sync across browsers or devices.": "Ce paramètre ne se synchronise pas entre les navigateurs ou les appareils.", | ||||||
|  | 	"Tip: Update multiple variable slots consecutively by pressing the tab key in the chat input after each replacement.": "", | ||||||
| 	"Title": "Titre", | 	"Title": "Titre", | ||||||
| 	"Title Auto-Generation": "Génération automatique de titre", | 	"Title Auto-Generation": "Génération automatique de titre", | ||||||
| 	"Title Generation Prompt": "Prompt de génération de titre", | 	"Title Generation Prompt": "Prompt de génération de titre", | ||||||
| 	"to": "à", | 	"to": "à", | ||||||
| 	"To access the available model names for downloading,": "Pour accéder aux noms de modèles disponibles pour le téléchargement,", | 	"To access the available model names for downloading,": "Pour accéder aux noms de modèles disponibles pour le téléchargement,", | ||||||
| 	"to chat input.": "à l'entrée du chat.", |  | ||||||
| 	"Toggle settings": "Basculer les paramètres", | 	"Toggle settings": "Basculer les paramètres", | ||||||
| 	"Toggle sidebar": "Basculer la barre latérale", | 	"Toggle sidebar": "Basculer la barre latérale", | ||||||
| 	"Top K": "Top K", | 	"Top K": "Top K", | ||||||
|  | @ -301,12 +296,10 @@ | ||||||
| 	"Trouble accessing Ollama?": "Problèmes d'accès à Ollama ?", | 	"Trouble accessing Ollama?": "Problèmes d'accès à Ollama ?", | ||||||
| 	"TTS Settings": "Paramètres TTS", | 	"TTS Settings": "Paramètres TTS", | ||||||
| 	"Uh-oh! There was an issue connecting to {{provider}}.": "Uh-oh ! Il y a eu un problème de connexion à {{provider}}.", | 	"Uh-oh! There was an issue connecting to {{provider}}.": "Uh-oh ! Il y a eu un problème de connexion à {{provider}}.", | ||||||
| 	"Upload a GGUF model": "Téléverser un modèle GGUF", | 	"Unknown File Type '{{file_type}}', but accepting and treating as plain text": "", | ||||||
| 	"Upload files": "Téléverser des fichiers", | 	"Upload files": "Téléverser des fichiers", | ||||||
| 	"Upload Progress": "Progression du téléversement", |  | ||||||
| 	"URL Mode": "Mode URL", |  | ||||||
| 	"Use '#' in the prompt input to load and select your documents.": "Utilisez '#' dans l'entrée du prompt pour charger et sélectionner vos documents.", | 	"Use '#' in the prompt input to load and select your documents.": "Utilisez '#' dans l'entrée du prompt pour charger et sélectionner vos documents.", | ||||||
| 	"user": "Utilisateur", | 	"User": "", | ||||||
| 	"User Permissions": "Permissions d'utilisateur", | 	"User Permissions": "Permissions d'utilisateur", | ||||||
| 	"Users": "Utilisateurs", | 	"Users": "Utilisateurs", | ||||||
| 	"Utilize": "Utiliser", | 	"Utilize": "Utiliser", | ||||||
|  | @ -320,7 +313,8 @@ | ||||||
| 	"When history is turned off, new chats on this browser won't appear in your history on any of your devices.": "Lorsque l'historique est désactivé, les nouveaux chats sur ce navigateur n'apparaîtront pas dans votre historique sur aucun de vos appareils.", | 	"When history is turned off, new chats on this browser won't appear in your history on any of your devices.": "Lorsque l'historique est désactivé, les nouveaux chats sur ce navigateur n'apparaîtront pas dans votre historique sur aucun de vos appareils.", | ||||||
| 	"Whisper (Local)": "Chuchotement (Local)", | 	"Whisper (Local)": "Chuchotement (Local)", | ||||||
| 	"Write a prompt suggestion (e.g. Who are you?)": "Écrivez un prompt (e.x. Qui est-tu ?)", | 	"Write a prompt suggestion (e.g. Who are you?)": "Écrivez un prompt (e.x. Qui est-tu ?)", | ||||||
| 	"Write a summary in 50 words that summarizes [topic or keyword]": "Ecrivez un résumé en 50 mots [sujet ou mot-clé]", | 	"Write a summary in 50 words that summarizes [topic or keyword].": "Ecrivez un résumé en 50 mots [sujet ou mot-clé]", | ||||||
|  | 	"You": "", | ||||||
| 	"You're a helpful assistant.": "Vous êtes un assistant utile", | 	"You're a helpful assistant.": "Vous êtes un assistant utile", | ||||||
| 	"You're now logged in.": "Vous êtes maintenant connecté." | 	"You're now logged in.": "Vous êtes maintenant connecté." | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -1,5 +1,6 @@ | ||||||
| { | { | ||||||
| 	"'s', 'm', 'h', 'd', 'w' or '-1' for no expiration.": "'s', 'm', 'h', 'd', 'w' or '-1' для відсутності терміну дії.", | 	"'s', 'm', 'h', 'd', 'w' or '-1' for no expiration.": "'s', 'm', 'h', 'd', 'w' or '-1' для відсутності терміну дії.", | ||||||
|  | 	"(Beta)": "(Beta)", | ||||||
| 	"(e.g. `sh webui.sh --api`)": "(e.g. `sh webui.sh --api`)", | 	"(e.g. `sh webui.sh --api`)": "(e.g. `sh webui.sh --api`)", | ||||||
| 	"{{item}} not provided": "{{item}} не надано", | 	"{{item}} not provided": "{{item}} не надано", | ||||||
| 	"{{modelName}} is thinking...": "{{modelName}} думає...", | 	"{{modelName}} is thinking...": "{{modelName}} думає...", | ||||||
|  | @ -14,12 +15,12 @@ | ||||||
| 	"Add Docs": "Додати документи", | 	"Add Docs": "Додати документи", | ||||||
| 	"Add Files": "Додати файли", | 	"Add Files": "Додати файли", | ||||||
| 	"Add LiteLLM Model": "Додати модель LiteLLM", | 	"Add LiteLLM Model": "Додати модель LiteLLM", | ||||||
|  | 	"Add message": "Додати повідомлення", | ||||||
| 	"add tags": "додати теги", | 	"add tags": "додати теги", | ||||||
| 	"Adjusting these settings will apply changes universally to all users.": "Корегування цих налаштувань застосовуватиме зміни для всіх користувачів.", | 	"Adjusting these settings will apply changes universally to all users.": "Корегування цих налаштувань застосовуватиме зміни для всіх користувачів.", | ||||||
| 	"admin": "адміністратор", | 	"Admin": "Адмін", | ||||||
| 	"Admin Panel": "Панель адміністратора", | 	"Admin Panel": "Панель адміністратора", | ||||||
| 	"Admin Settings": "Налаштування адміністратора", | 	"Admin Settings": "Налаштування адміністратора", | ||||||
| 	"Advanced": "Розширені", |  | ||||||
| 	"Advanced Model Params": "Розширені параметри моделі", | 	"Advanced Model Params": "Розширені параметри моделі", | ||||||
| 	"Advanced Parameters": "Розширені параметри", | 	"Advanced Parameters": "Розширені параметри", | ||||||
| 	"all": "всі", | 	"all": "всі", | ||||||
|  | @ -43,6 +44,7 @@ | ||||||
| 	"Cancel": "Скасувати", | 	"Cancel": "Скасувати", | ||||||
| 	"Categories": "Категорії", | 	"Categories": "Категорії", | ||||||
| 	"Change Password": "Змінити пароль", | 	"Change Password": "Змінити пароль", | ||||||
|  | 	"Chat": "Чат", | ||||||
| 	"Chat History": "Історія чату", | 	"Chat History": "Історія чату", | ||||||
| 	"Chat History is off for this browser.": "Історія чату вимкнена для цього браузера.", | 	"Chat History is off for this browser.": "Історія чату вимкнена для цього браузера.", | ||||||
| 	"Chats": "Чати", | 	"Chats": "Чати", | ||||||
|  | @ -68,6 +70,7 @@ | ||||||
| 	"Copy last code block": "Копіювати останній блок коду", | 	"Copy last code block": "Копіювати останній блок коду", | ||||||
| 	"Copy last response": "Копіювати останню відповідь", | 	"Copy last response": "Копіювати останню відповідь", | ||||||
| 	"Copying to clipboard was successful!": "Копіювання в буфер обміну виконано успішно!", | 	"Copying to clipboard was successful!": "Копіювання в буфер обміну виконано успішно!", | ||||||
|  | 	"Create a concise, 3-5 word phrase as a header for the following query, strictly adhering to the 3-5 word limit and avoiding the use of the word 'title':": "Створіть стислий, 3-5 слів заголовок для наступного запиту, суворо дотримуючись 3-5 слів обмеження та уникаючи використання слова 'заголовок':", | ||||||
| 	"Create a modelfile": "Створити modelfile", | 	"Create a modelfile": "Створити modelfile", | ||||||
| 	"Create Account": "Створити обліковий запис", | 	"Create Account": "Створити обліковий запис", | ||||||
| 	"Created at": "Створено", | 	"Created at": "Створено", | ||||||
|  | @ -83,7 +86,6 @@ | ||||||
| 	"Default (Web API)": "За замовчуванням (Web API)", | 	"Default (Web API)": "За замовчуванням (Web API)", | ||||||
| 	"Default model updated": "Модель за замовчуванням оновлено", | 	"Default model updated": "Модель за замовчуванням оновлено", | ||||||
| 	"Default Prompt Suggestions": "Запропоновані запити за замовчуванням", | 	"Default Prompt Suggestions": "Запропоновані запити за замовчуванням", | ||||||
| 	"Create a concise, 3-5 word phrase as a header for the following query, strictly adhering to the 3-5 word limit and avoiding the use of the word 'title':": "Створіть стислий, 3-5 слів заголовок для наступного запиту, суворо дотримуючись 3-5 слів обмеження та уникаючи використання слова 'заголовок':", |  | ||||||
| 	"Default User Role": "Роль користувача за замовчуванням", | 	"Default User Role": "Роль користувача за замовчуванням", | ||||||
| 	"delete": "видалити", | 	"delete": "видалити", | ||||||
| 	"Delete a model": "Видалити модель", | 	"Delete a model": "Видалити модель", | ||||||
|  | @ -107,6 +109,7 @@ | ||||||
| 	"Don't have an account?": "Немає облікового запису?", | 	"Don't have an account?": "Немає облікового запису?", | ||||||
| 	"Download as a File": "Завантажити як файл", | 	"Download as a File": "Завантажити як файл", | ||||||
| 	"Download Database": "Завантажити базу даних", | 	"Download Database": "Завантажити базу даних", | ||||||
|  | 	"Drop any files here to add to the conversation": "Перетягніть сюди файли, щоб додати до розмови", | ||||||
| 	"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "напр. '30s','10m'. Дійсні одиниці часу: 'с', 'хв', 'г'.", | 	"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "напр. '30s','10m'. Дійсні одиниці часу: 'с', 'хв', 'г'.", | ||||||
| 	"Edit Doc": "Редагувати документ", | 	"Edit Doc": "Редагувати документ", | ||||||
| 	"Edit User": "Редагувати користувача", | 	"Edit User": "Редагувати користувача", | ||||||
|  | @ -119,15 +122,13 @@ | ||||||
| 	"Enter Your Email": "Введіть вашу електронну пошту", | 	"Enter Your Email": "Введіть вашу електронну пошту", | ||||||
| 	"Enter Your Full Name": "Введіть ваше повне ім'я", | 	"Enter Your Full Name": "Введіть ваше повне ім'я", | ||||||
| 	"Enter Your Password": "Введіть ваш пароль", | 	"Enter Your Password": "Введіть ваш пароль", | ||||||
| 	"Experimental": "Експериментальний", |  | ||||||
| 	"Export All Chats (All Users)": "Експортувати всі чати (всі користувачі)", | 	"Export All Chats (All Users)": "Експортувати всі чати (всі користувачі)", | ||||||
| 	"Export Chats": "Експортувати чати", | 	"Export Chats": "Експортувати чати", | ||||||
| 	"Export Documents Mapping": "Експортувати відображення документів", | 	"Export Documents Mapping": "Експортувати відображення документів", | ||||||
| 	"Export Modelfiles": "Експортувати modelfiles", | 	"Export Modelfiles": "Експортувати modelfiles", | ||||||
| 	"Export Prompts": "Експортувати запити", | 	"Export Prompts": "Експортувати запити", | ||||||
| 	"Failed to read clipboard contents": "Не вдалося прочитати вміст буфера обміну", | 	"Failed to read clipboard contents": "Не вдалося прочитати вміст буфера обміну", | ||||||
| 	"File Mode": "Режим файлу", | 	"File not found.": "Файл не знайдено.", | ||||||
| 	"File not found.`": "Файл не знайдено.`", |  | ||||||
| 	"Focus chat input": "Фокус вводу чату", | 	"Focus chat input": "Фокус вводу чату", | ||||||
| 	"Format your variables using square brackets like this:": "Форматуйте свої змінні квадратними дужками так:", | 	"Format your variables using square brackets like this:": "Форматуйте свої змінні квадратними дужками так:", | ||||||
| 	"From (Base Model)": "Від (базова модель)", | 	"From (Base Model)": "Від (базова модель)", | ||||||
|  | @ -169,36 +170,36 @@ | ||||||
| 	"Mirostat Eta": "Mirostat Eta", | 	"Mirostat Eta": "Mirostat Eta", | ||||||
| 	"Mirostat Tau": "Mirostat Tau", | 	"Mirostat Tau": "Mirostat Tau", | ||||||
| 	"MMMM DD, YYYY": "MMMM ДД, РРРР", | 	"MMMM DD, YYYY": "MMMM ДД, РРРР", | ||||||
| 	"Model '{{modelName}}' has been successfully downloaded.`": "Модель '{{modelName}}' успішно завантажено.`", | 	"Model '{{modelName}}' has been successfully downloaded.": "Модель '{{modelName}}' успішно завантажено.", | ||||||
| 	"Model '{{modelTag}}' is already in queue for downloading.": "Модель '{{modelTag}}' вже знаходиться в черзі на завантаження.", | 	"Model '{{modelTag}}' is already in queue for downloading.": "Модель '{{modelTag}}' вже знаходиться в черзі на завантаження.", | ||||||
| 	"Model {{modelId}} not found": "Модель {{modelId}} не знайдено", | 	"Model {{modelId}} not found": "Модель {{modelId}} не знайдено", | ||||||
| 	"Model {{modelName}} already exists.": "Модель {{modelName}} вже існує.", | 	"Model {{modelName}} already exists.": "Модель {{modelName}} вже існує.", | ||||||
| 	"Model Name": "Назва моделі", | 	"Model Name": "Назва моделі", | ||||||
| 	"Model not selected": "Модель не вибрана", | 	"Model not selected": "Модель не вибрана", | ||||||
| 	"Model Tag Name": "Ім'я тегу моделі", | 	"Model Tag Name": "Ім'я тегу моделі", | ||||||
| 	"Modelfile": "Modelfile", | 	"Modelfile": "Файли моделі", | ||||||
| 	"Modelfile Advanced Settings": "Розширені налаштування modelfile", | 	"Modelfile Advanced Settings": "Розширені налаштування modelfile", | ||||||
| 	"Modelfile Content": "Зміст modelfile", | 	"Modelfiles": "Файли моделей", | ||||||
| 	"Modelfiles": "Modelfiles", |  | ||||||
| 	"Models": "Моделі", | 	"Models": "Моделі", | ||||||
| 	"My Documents": "Мої документи", | 	"My Documents": "Мої документи", | ||||||
| 	"My Modelfiles": "Мої modelfiles", | 	"My Modelfiles": "Мої файли моделей", | ||||||
| 	"My Prompts": "Мої запити", | 	"My Prompts": "Мої запити", | ||||||
| 	"Name": "Назва", | 	"Name": "Назва", | ||||||
| 	"Name Tag": "Назва тегу", | 	"Name Tag": "Назва тегу", | ||||||
| 	"Name your Modelfile": "Назвіть свій modelfile", | 	"Name your modelfile": "Назвіть свій файл моделі", | ||||||
| 	"New Chat": "Новий чат", | 	"New Chat": "Новий чат", | ||||||
| 	"New Password": "Новий пароль", | 	"New Password": "Новий пароль", | ||||||
| 	"Not sure what to add?": "Не впевнений, що додати?", | 	"Not sure what to add?": "Не впевнений, що додати?", | ||||||
| 	"Not sure what to write? Switch to": "Не впевнений, що писати? Переключитися на", | 	"Not sure what to write? Switch to": "Не впевнений, що писати? Переключитися на", | ||||||
| 	"Off": "Вимк", | 	"Off": "Вимк", | ||||||
| 	"Okay, Let's Go!": "Гаразд, давайте почнемо!", | 	"Okay, Let's Go!": "Гаразд, давайте почнемо!", | ||||||
| 	"Ollama API URL": "URL API Ollama", |  | ||||||
| 	"Ollama Version": "Версія Ollama", | 	"Ollama Version": "Версія Ollama", | ||||||
| 	"On": "Увімк", | 	"On": "Увімк", | ||||||
| 	"Only": "Тільки", | 	"Only": "Тільки", | ||||||
| 	"Only alphanumeric characters and hyphens are allowed in the command string.": "У рядку команди дозволено використовувати лише алфавітно-цифрові символи та дефіси.", | 	"Only alphanumeric characters and hyphens are allowed in the command string.": "У рядку команди дозволено використовувати лише алфавітно-цифрові символи та дефіси.", | ||||||
| 	"Oops! Hold tight! Your files are still in the processing oven. We're cooking them up to perfection. Please be patient and we'll let you know once they're ready.": "Ой! Зачекайте, будь ласка! Ваші файли ще готуються. Ми робимо все, щоб вони були ідеальними. Будь ласка, будьте терплячі, ми повідомимо вам, коли вони будуть готові.", | 	"Oops! Hold tight! Your files are still in the processing oven. We're cooking them up to perfection. Please be patient and we'll let you know once they're ready.": "Ой! Зачекайте, будь ласка! Ваші файли ще готуються. Ми робимо все, щоб вони були ідеальними. Будь ласка, будьте терплячі, ми повідомимо вам, коли вони будуть готові.", | ||||||
|  | 	"Oops! Looks like the URL is invalid. Please double-check and try again.": "Упс! Схоже, що URL-адреса невірна. Будь ласка, перевірте ще раз та спробуйте ще раз.", | ||||||
|  | 	"Oops! You're using an unsupported method (frontend only). Please serve the WebUI from the backend.": "Упс! Ви використовуєте непідтримуваний метод (тільки для фронтенду). Будь ласка, обслуговуйте WebUI з бекенду.", | ||||||
| 	"Open": "Відкрити", | 	"Open": "Відкрити", | ||||||
| 	"Open AI": "Open AI", | 	"Open AI": "Open AI", | ||||||
| 	"Open new chat": "Відкрити новий чат", | 	"Open new chat": "Відкрити новий чат", | ||||||
|  | @ -206,8 +207,9 @@ | ||||||
| 	"or": "або", | 	"or": "або", | ||||||
| 	"Parameters": "Параметри", | 	"Parameters": "Параметри", | ||||||
| 	"Password": "Пароль", | 	"Password": "Пароль", | ||||||
| 	"pending": "очікування", | 	"Pending": "На розгляді", | ||||||
| 	"Permission denied when accessing microphone: {{error}}": "Доступ до мікрофона заборонено: {{error}}", | 	"Permission denied when accessing microphone: {{error}}": "Доступ до мікрофона заборонено: {{error}}", | ||||||
|  | 	"Playground": "Майданчик", | ||||||
| 	"Profile": "Профіль", | 	"Profile": "Профіль", | ||||||
| 	"Prompt Content": "Зміст запиту", | 	"Prompt Content": "Зміст запиту", | ||||||
| 	"Prompt suggestions": "Швидкі запити", | 	"Prompt suggestions": "Швидкі запити", | ||||||
|  | @ -265,38 +267,39 @@ | ||||||
| 	"SpeechRecognition API is not supported in this browser.": "SpeechRecognition API не підтримується в цьому браузері.", | 	"SpeechRecognition API is not supported in this browser.": "SpeechRecognition API не підтримується в цьому браузері.", | ||||||
| 	"Stop Sequence": "Символ зупинки", | 	"Stop Sequence": "Символ зупинки", | ||||||
| 	"STT Settings": "Налаштування STT", | 	"STT Settings": "Налаштування STT", | ||||||
|  | 	"Submit": "Надіслати", | ||||||
| 	"Success": "Успіх", | 	"Success": "Успіх", | ||||||
| 	"Successfully updated": "Успішно оновлено", | 	"Successfully updated": "Успішно оновлено", | ||||||
| 	"Successfully updated.": "Успішно оновлено.", | 	"Successfully updated.": "Успішно оновлено.", | ||||||
| 	"Sync All": "Синхронізувати все", | 	"Sync All": "Синхронізувати все", | ||||||
|  | 	"System": "Система", | ||||||
| 	"System Prompt": "Системний запит", | 	"System Prompt": "Системний запит", | ||||||
| 	"Tags": "Теги", | 	"Tags": "Теги", | ||||||
| 	"Temperature": "Температура", | 	"Temperature": "Температура", | ||||||
| 	"Template": "Шаблон", | 	"Template": "Шаблон", | ||||||
|  | 	"Text Completion": "Завершення тексту", | ||||||
| 	"Text-to-Speech Engine": "Система синтезу мови", | 	"Text-to-Speech Engine": "Система синтезу мови", | ||||||
| 	"Tfs Z": "Tfs Z", | 	"Tfs Z": "Tfs Z", | ||||||
| 	"Theme": "Тема", | 	"Theme": "Тема", | ||||||
| 	"This ensures that your valuable conversations are securely saved to your backend database. Thank you!": "Це забезпечує збереження ваших цінних розмов у безпечному бекенд-сховищі. Дякуємо!", | 	"This ensures that your valuable conversations are securely saved to your backend database. Thank you!": "Це забезпечує збереження ваших цінних розмов у безпечному бекенд-сховищі. Дякуємо!", | ||||||
| 	"This setting does not sync across browsers or devices.": "Це налаштування не синхронізується між браузерами або пристроями.", | 	"This setting does not sync across browsers or devices.": "Це налаштування не синхронізується між браузерами або пристроями.", | ||||||
|  | 	"Tip: Update multiple variable slots consecutively by pressing the tab key in the chat input after each replacement.": "", | ||||||
| 	"Title": "Заголовок", | 	"Title": "Заголовок", | ||||||
| 	"Title Auto-Generation": "Автогенерація заголовків", | 	"Title Auto-Generation": "Автогенерація заголовків", | ||||||
| 	"Title Generation Prompt": "Запит на генерування заголовків", | 	"Title Generation Prompt": "Запит на генерування заголовків", | ||||||
| 	"to": "до", | 	"to": "до", | ||||||
| 	"To access the available model names for downloading,": "Щоб отримати доступ до назв доступних для завантаження моделей,", | 	"To access the available model names for downloading,": "Щоб отримати доступ до назв доступних для завантаження моделей,", | ||||||
| 	"to chat input.": "до введення чату.", |  | ||||||
| 	"Toggle settings": "Переключити налаштування", | 	"Toggle settings": "Переключити налаштування", | ||||||
| 	"Toggle sidebar": "Переключити бокову панель", | 	"Toggle sidebar": "Переключити бокову панель", | ||||||
| 	"Top K": "Вершина K", | 	"Top K": "Top K", | ||||||
| 	"Top P": "Вершина P", | 	"Top P": "Top P", | ||||||
| 	"Trouble accessing Ollama?": "Проблеми з доступом до Ollama?", | 	"Trouble accessing Ollama?": "Проблеми з доступом до Ollama?", | ||||||
| 	"TTS Settings": "Налаштування TTS", | 	"TTS Settings": "Налаштування TTS", | ||||||
| 	"Uh-oh! There was an issue connecting to {{provider}}.": "Ой! Виникла проблема при підключенні до {{provider}}.", | 	"Uh-oh! There was an issue connecting to {{provider}}.": "Ой! Виникла проблема при підключенні до {{provider}}.", | ||||||
| 	"Upload a GGUF model": "Завантажити модель GGUF", | 	"Unknown File Type '{{file_type}}', but accepting and treating as plain text": "Невідомий тип файлу '{{file_type}}', але приймається та обробляється як звичайний текст", | ||||||
| 	"Upload files": "Завантажити файли", | 	"Upload files": "Завантажити файли", | ||||||
| 	"Upload Progress": "Прогрес завантаження", |  | ||||||
| 	"URL Mode": "Режим URL", |  | ||||||
| 	"Use '#' in the prompt input to load and select your documents.": "Використовуйте '#' у введенні запиту для завантаження та вибору ваших документів.", | 	"Use '#' in the prompt input to load and select your documents.": "Використовуйте '#' у введенні запиту для завантаження та вибору ваших документів.", | ||||||
| 	"user": "користувач", | 	"User": "Користувач", | ||||||
| 	"User Permissions": "Дозволи користувача", | 	"User Permissions": "Дозволи користувача", | ||||||
| 	"Users": "Користувачі", | 	"Users": "Користувачі", | ||||||
| 	"Utilize": "Використовувати", | 	"Utilize": "Використовувати", | ||||||
|  | @ -308,19 +311,10 @@ | ||||||
| 	"WebUI Settings": "Налаштування WebUI", | 	"WebUI Settings": "Налаштування WebUI", | ||||||
| 	"What’s New in": "Що нового в", | 	"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.": "Коли історія вимкнена, нові чати в цьому браузері не будуть відображатися в історії на жодному з ваших пристроїв.", | 	"When history is turned off, new chats on this browser won't appear in your history on any of your devices.": "Коли історія вимкнена, нові чати в цьому браузері не будуть відображатися в історії на жодному з ваших пристроїв.", | ||||||
| 	"Whisper (Local)": "Шепіт (локально)", | 	"Whisper (Local)": "Whisper (локально)", | ||||||
| 	"Write a prompt suggestion (e.g. Who are you?)": "Напишіть запит (напр. Хто ти?)", | 	"Write a prompt suggestion (e.g. Who are you?)": "Напишіть запит (напр. Хто ти?)", | ||||||
| 	"Write a summary in 50 words that summarizes [topic or keyword]": "Напишіть стислий зміст у 50 слів, який узагальнює [тему або ключове слово]", | 	"Write a summary in 50 words that summarizes [topic or keyword].": "Напишіть стислий зміст у 50 слів, який узагальнює [тема або ключове слово].", | ||||||
| 	"You're now logged in.": "Ви увійшли в систему.", |  | ||||||
| 	"You": "Ви", | 	"You": "Ви", | ||||||
| 	"Playground": "Майданчик", |  | ||||||
| 	"Add message": "Додати повідомлення", |  | ||||||
| 	"Enter a user message here": "Введіть тут повідомлення користувача", |  | ||||||
| 	"Enter an assistant message here": "Введіть тут повідомлення асистента", |  | ||||||
| 	"Drop any files here to add to the conversation": "Перетягніть сюди файли, щоб додати до розмови", |  | ||||||
| 	"You're a helpful assistant.": "Ви корисний асистент.", | 	"You're a helpful assistant.": "Ви корисний асистент.", | ||||||
| 	"assistant": "асистент", | 	"You're now logged in.": "Ви увійшли в систему." | ||||||
| 	"Submit": "Надіслати", |  | ||||||
| 	"Chat": "Чат", |  | ||||||
| 	"Text Completion": "Завершення тексту" |  | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -99,14 +99,11 @@ | ||||||
| 					if (localDBChats.length === 0) { | 					if (localDBChats.length === 0) { | ||||||
| 						await deleteDB('Chats'); | 						await deleteDB('Chats'); | ||||||
| 					} | 					} | ||||||
| 
 |  | ||||||
| 					console.log('localdb', localDBChats); |  | ||||||
| 				} | 				} | ||||||
| 
 | 
 | ||||||
| 				console.log(DB); | 				console.log(DB); | ||||||
| 			} catch (error) { | 			} catch (error) { | ||||||
| 				// IndexedDB Not Found | 				// IndexedDB Not Found | ||||||
| 				console.log('IDB Not Found'); |  | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
| 			console.log(); | 			console.log(); | ||||||
|  |  | ||||||
|  | @ -344,7 +344,7 @@ | ||||||
| 						content: $settings.system | 						content: $settings.system | ||||||
| 				  } | 				  } | ||||||
| 				: undefined, | 				: undefined, | ||||||
| 			...messages.filter((message) => !message.deleted) | 			...messages | ||||||
| 		] | 		] | ||||||
| 			.filter((message) => message) | 			.filter((message) => message) | ||||||
| 			.map((message, idx, arr) => ({ | 			.map((message, idx, arr) => ({ | ||||||
|  | @ -558,7 +558,7 @@ | ||||||
| 								content: $settings.system | 								content: $settings.system | ||||||
| 						  } | 						  } | ||||||
| 						: undefined, | 						: undefined, | ||||||
| 					...messages.filter((message) => !message.deleted) | 					...messages | ||||||
| 				] | 				] | ||||||
| 					.filter((message) => message) | 					.filter((message) => message) | ||||||
| 					.map((message, idx, arr) => ({ | 					.map((message, idx, arr) => ({ | ||||||
|  |  | ||||||
|  | @ -354,7 +354,7 @@ | ||||||
| 						content: $settings.system | 						content: $settings.system | ||||||
| 				  } | 				  } | ||||||
| 				: undefined, | 				: undefined, | ||||||
| 			...messages.filter((message) => !message.deleted) | 			...messages | ||||||
| 		] | 		] | ||||||
| 			.filter((message) => message) | 			.filter((message) => message) | ||||||
| 			.map((message, idx, arr) => ({ | 			.map((message, idx, arr) => ({ | ||||||
|  | @ -568,7 +568,7 @@ | ||||||
| 								content: $settings.system | 								content: $settings.system | ||||||
| 						  } | 						  } | ||||||
| 						: undefined, | 						: undefined, | ||||||
| 					...messages.filter((message) => !message.deleted) | 					...messages | ||||||
| 				] | 				] | ||||||
| 					.filter((message) => message) | 					.filter((message) => message) | ||||||
| 					.map((message, idx, arr) => ({ | 					.map((message, idx, arr) => ({ | ||||||
|  |  | ||||||
|  | @ -562,9 +562,9 @@ SYSTEM """${system}"""`.replace(/^\s*\n/gm, ''); | ||||||
| 								}} | 								}} | ||||||
| 							> | 							> | ||||||
| 								{#if advanced} | 								{#if advanced} | ||||||
| 									<span class="ml-2 self-center">{$i18n.t(' Custom ')}</span> | 									<span class="ml-2 self-center">{$i18n.t('Custom')}</span> | ||||||
| 								{:else} | 								{:else} | ||||||
| 									<span class="ml-2 self-center">{$i18n.t(' Default ')}</span> | 									<span class="ml-2 self-center">{$i18n.t('Default')}</span> | ||||||
| 								{/if} | 								{/if} | ||||||
| 							</button> | 							</button> | ||||||
| 						</div> | 						</div> | ||||||
|  |  | ||||||
|  | @ -181,7 +181,7 @@ | ||||||
| 							<textarea | 							<textarea | ||||||
| 								class="px-3 py-1.5 text-sm w-full bg-transparent border dark:border-gray-600 outline-none rounded-lg" | 								class="px-3 py-1.5 text-sm w-full bg-transparent border dark:border-gray-600 outline-none rounded-lg" | ||||||
| 								placeholder={$i18n.t( | 								placeholder={$i18n.t( | ||||||
| 									'Write a summary in 50 words that summarizes [topic or keyword]' | 									'Write a summary in 50 words that summarizes [topic or keyword].' | ||||||
| 								)} | 								)} | ||||||
| 								rows="6" | 								rows="6" | ||||||
| 								bind:value={content} | 								bind:value={content} | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue