forked from open-webui/open-webui
Merge branch 'dev' into feat/trusted-email-header
This commit is contained in:
commit
562e40a7bd
58 changed files with 2915 additions and 2152 deletions
34
.github/workflows/format-backend.yaml
vendored
34
.github/workflows/format-backend.yaml
vendored
|
@ -1,27 +1,39 @@
|
||||||
name: Python CI
|
name: Python CI
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: ['main']
|
branches:
|
||||||
|
- main
|
||||||
|
- dev
|
||||||
pull_request:
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
- dev
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
name: 'Format Backend'
|
name: 'Format Backend'
|
||||||
env:
|
|
||||||
PUBLIC_API_BASE_URL: ''
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
node-version:
|
python-version: [3.11]
|
||||||
- latest
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- name: Use Python
|
|
||||||
uses: actions/setup-python@v4
|
- name: Set up Python
|
||||||
- name: Use Bun
|
uses: actions/setup-python@v2
|
||||||
uses: oven-sh/setup-bun@v1
|
with:
|
||||||
|
python-version: ${{ matrix.python-version }}
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: |
|
run: |
|
||||||
python -m pip install --upgrade pip
|
python -m pip install --upgrade pip
|
||||||
pip install yapf
|
pip install black
|
||||||
|
|
||||||
- name: Format backend
|
- name: Format backend
|
||||||
run: bun run format:backend
|
run: black . --exclude "/venv/"
|
||||||
|
|
||||||
|
- name: Check for changes after format
|
||||||
|
run: git diff --exit-code
|
||||||
|
|
42
.github/workflows/format-build-frontend.yaml
vendored
42
.github/workflows/format-build-frontend.yaml
vendored
|
@ -1,22 +1,36 @@
|
||||||
name: Bun CI
|
name: Frontend Build
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: ['main']
|
branches:
|
||||||
|
- main
|
||||||
|
- dev
|
||||||
pull_request:
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
- dev
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
name: 'Format & Build Frontend'
|
name: 'Format & Build Frontend'
|
||||||
env:
|
|
||||||
PUBLIC_API_BASE_URL: ''
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- name: Checkout Repository
|
||||||
- name: Use Bun
|
uses: actions/checkout@v4
|
||||||
uses: oven-sh/setup-bun@v1
|
|
||||||
- run: bun --version
|
- name: Setup Node.js
|
||||||
- name: Install frontend dependencies
|
uses: actions/setup-node@v3
|
||||||
run: bun install
|
with:
|
||||||
- name: Format frontend
|
node-version: '20' # Or specify any other version you want to use
|
||||||
run: bun run format
|
|
||||||
- name: Build frontend
|
- name: Install Dependencies
|
||||||
run: bun run build
|
run: npm install
|
||||||
|
|
||||||
|
- name: Format Frontend
|
||||||
|
run: npm run format
|
||||||
|
|
||||||
|
- name: Check for Changes After Format
|
||||||
|
run: git diff --exit-code
|
||||||
|
|
||||||
|
- name: Build Frontend
|
||||||
|
run: npm run build
|
||||||
|
|
18
CHANGELOG.md
18
CHANGELOG.md
|
@ -5,6 +5,24 @@ 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.116] - 2024-03-31
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- **🔄 Enhanced UI**: Model selector now conveniently located in the navbar, enabling seamless switching between multiple models during conversations.
|
||||||
|
- **🔍 Improved Model Selector**: Directly pull a model from the selector/Models now display detailed information for better understanding.
|
||||||
|
- **💬 Webhook Support**: Now compatible with Google Chat and Microsoft Teams.
|
||||||
|
- **🌐 Localization**: Korean translation (I18n) now available.
|
||||||
|
- **🌑 Dark Theme**: OLED dark theme introduced for reduced strain during prolonged usage.
|
||||||
|
- **🏷️ Tag Autocomplete**: Dropdown feature added for effortless chat tagging.
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- **🔽 Auto-Scrolling**: Addressed OpenAI auto-scrolling issue.
|
||||||
|
- **🏷️ Tag Validation**: Implemented tag validation to prevent empty string tags.
|
||||||
|
- **🚫 Model Whitelisting**: Resolved LiteLLM model whitelisting issue.
|
||||||
|
- **✅ Spelling**: Corrected various spelling issues for improved readability.
|
||||||
|
|
||||||
## [0.1.115] - 2024-03-24
|
## [0.1.115] - 2024-03-24
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
|
@ -22,7 +22,13 @@ from utils.utils import (
|
||||||
)
|
)
|
||||||
from utils.misc import calculate_sha256
|
from utils.misc import calculate_sha256
|
||||||
|
|
||||||
from config import SRC_LOG_LEVELS, CACHE_DIR, UPLOAD_DIR, WHISPER_MODEL, WHISPER_MODEL_DIR
|
from config import (
|
||||||
|
SRC_LOG_LEVELS,
|
||||||
|
CACHE_DIR,
|
||||||
|
UPLOAD_DIR,
|
||||||
|
WHISPER_MODEL,
|
||||||
|
WHISPER_MODEL_DIR,
|
||||||
|
)
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
log.setLevel(SRC_LOG_LEVELS["AUDIO"])
|
log.setLevel(SRC_LOG_LEVELS["AUDIO"])
|
||||||
|
|
|
@ -325,7 +325,7 @@ def save_url_image(url):
|
||||||
|
|
||||||
return image_id
|
return image_id
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Error saving image: {e}")
|
log.exception(f"Error saving image: {e}")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
@ -397,7 +397,7 @@ def generate_image(
|
||||||
user.id,
|
user.id,
|
||||||
app.state.COMFYUI_BASE_URL,
|
app.state.COMFYUI_BASE_URL,
|
||||||
)
|
)
|
||||||
print(res)
|
log.debug(f"res: {res}")
|
||||||
|
|
||||||
images = []
|
images = []
|
||||||
|
|
||||||
|
@ -409,7 +409,7 @@ def generate_image(
|
||||||
with open(file_body_path, "w") as f:
|
with open(file_body_path, "w") as f:
|
||||||
json.dump(data.model_dump(exclude_none=True), f)
|
json.dump(data.model_dump(exclude_none=True), f)
|
||||||
|
|
||||||
print(images)
|
log.debug(f"images: {images}")
|
||||||
return images
|
return images
|
||||||
else:
|
else:
|
||||||
if form_data.model:
|
if form_data.model:
|
||||||
|
|
|
@ -4,6 +4,12 @@ import json
|
||||||
import urllib.request
|
import urllib.request
|
||||||
import urllib.parse
|
import urllib.parse
|
||||||
import random
|
import random
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from config import SRC_LOG_LEVELS
|
||||||
|
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
log.setLevel(SRC_LOG_LEVELS["COMFYUI"])
|
||||||
|
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
@ -121,7 +127,7 @@ COMFYUI_DEFAULT_PROMPT = """
|
||||||
|
|
||||||
|
|
||||||
def queue_prompt(prompt, client_id, base_url):
|
def queue_prompt(prompt, client_id, base_url):
|
||||||
print("queue_prompt")
|
log.info("queue_prompt")
|
||||||
p = {"prompt": prompt, "client_id": client_id}
|
p = {"prompt": prompt, "client_id": client_id}
|
||||||
data = json.dumps(p).encode("utf-8")
|
data = json.dumps(p).encode("utf-8")
|
||||||
req = urllib.request.Request(f"{base_url}/prompt", data=data)
|
req = urllib.request.Request(f"{base_url}/prompt", data=data)
|
||||||
|
@ -129,7 +135,7 @@ def queue_prompt(prompt, client_id, base_url):
|
||||||
|
|
||||||
|
|
||||||
def get_image(filename, subfolder, folder_type, base_url):
|
def get_image(filename, subfolder, folder_type, base_url):
|
||||||
print("get_image")
|
log.info("get_image")
|
||||||
data = {"filename": filename, "subfolder": subfolder, "type": folder_type}
|
data = {"filename": filename, "subfolder": subfolder, "type": folder_type}
|
||||||
url_values = urllib.parse.urlencode(data)
|
url_values = urllib.parse.urlencode(data)
|
||||||
with urllib.request.urlopen(f"{base_url}/view?{url_values}") as response:
|
with urllib.request.urlopen(f"{base_url}/view?{url_values}") as response:
|
||||||
|
@ -137,14 +143,14 @@ def get_image(filename, subfolder, folder_type, base_url):
|
||||||
|
|
||||||
|
|
||||||
def get_image_url(filename, subfolder, folder_type, base_url):
|
def get_image_url(filename, subfolder, folder_type, base_url):
|
||||||
print("get_image")
|
log.info("get_image")
|
||||||
data = {"filename": filename, "subfolder": subfolder, "type": folder_type}
|
data = {"filename": filename, "subfolder": subfolder, "type": folder_type}
|
||||||
url_values = urllib.parse.urlencode(data)
|
url_values = urllib.parse.urlencode(data)
|
||||||
return f"{base_url}/view?{url_values}"
|
return f"{base_url}/view?{url_values}"
|
||||||
|
|
||||||
|
|
||||||
def get_history(prompt_id, base_url):
|
def get_history(prompt_id, base_url):
|
||||||
print("get_history")
|
log.info("get_history")
|
||||||
with urllib.request.urlopen(f"{base_url}/history/{prompt_id}") as response:
|
with urllib.request.urlopen(f"{base_url}/history/{prompt_id}") as response:
|
||||||
return json.loads(response.read())
|
return json.loads(response.read())
|
||||||
|
|
||||||
|
@ -212,15 +218,15 @@ def comfyui_generate_image(
|
||||||
try:
|
try:
|
||||||
ws = websocket.WebSocket()
|
ws = websocket.WebSocket()
|
||||||
ws.connect(f"ws://{host}/ws?clientId={client_id}")
|
ws.connect(f"ws://{host}/ws?clientId={client_id}")
|
||||||
print("WebSocket connection established.")
|
log.info("WebSocket connection established.")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Failed to connect to WebSocket server: {e}")
|
log.exception(f"Failed to connect to WebSocket server: {e}")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
try:
|
try:
|
||||||
images = get_images(ws, comfyui_prompt, client_id, base_url)
|
images = get_images(ws, comfyui_prompt, client_id, base_url)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Error while receiving images: {e}")
|
log.exception(f"Error while receiving images: {e}")
|
||||||
images = None
|
images = None
|
||||||
|
|
||||||
ws.close()
|
ws.close()
|
||||||
|
|
|
@ -33,7 +33,13 @@ 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 SRC_LOG_LEVELS, OLLAMA_BASE_URLS, MODEL_FILTER_ENABLED, MODEL_FILTER_LIST, UPLOAD_DIR
|
from config import (
|
||||||
|
SRC_LOG_LEVELS,
|
||||||
|
OLLAMA_BASE_URLS,
|
||||||
|
MODEL_FILTER_ENABLED,
|
||||||
|
MODEL_FILTER_LIST,
|
||||||
|
UPLOAD_DIR,
|
||||||
|
)
|
||||||
from utils.misc import calculate_sha256
|
from utils.misc import calculate_sha256
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
@ -266,7 +272,7 @@ async def pull_model(
|
||||||
if request_id in REQUEST_POOL:
|
if request_id in REQUEST_POOL:
|
||||||
yield chunk
|
yield chunk
|
||||||
else:
|
else:
|
||||||
print("User: canceled request")
|
log.warning("User: canceled request")
|
||||||
break
|
break
|
||||||
finally:
|
finally:
|
||||||
if hasattr(r, "close"):
|
if hasattr(r, "close"):
|
||||||
|
@ -664,7 +670,7 @@ async def generate_completion(
|
||||||
else:
|
else:
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=400,
|
status_code=400,
|
||||||
detail="error_detail",
|
detail=ERROR_MESSAGES.MODEL_NOT_FOUND(form_data.model),
|
||||||
)
|
)
|
||||||
|
|
||||||
url = app.state.OLLAMA_BASE_URLS[url_idx]
|
url = app.state.OLLAMA_BASE_URLS[url_idx]
|
||||||
|
@ -770,7 +776,11 @@ async def generate_chat_completion(
|
||||||
|
|
||||||
r = None
|
r = None
|
||||||
|
|
||||||
log.debug("form_data.model_dump_json(exclude_none=True).encode(): {0} ".format(form_data.model_dump_json(exclude_none=True).encode()))
|
log.debug(
|
||||||
|
"form_data.model_dump_json(exclude_none=True).encode(): {0} ".format(
|
||||||
|
form_data.model_dump_json(exclude_none=True).encode()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
def get_request():
|
def get_request():
|
||||||
nonlocal form_data
|
nonlocal form_data
|
||||||
|
|
|
@ -333,7 +333,7 @@ def store_docs_in_vector_db(docs, collection_name, overwrite: bool = False) -> b
|
||||||
if overwrite:
|
if overwrite:
|
||||||
for collection in CHROMA_CLIENT.list_collections():
|
for collection in CHROMA_CLIENT.list_collections():
|
||||||
if collection_name == collection.name:
|
if collection_name == collection.name:
|
||||||
print(f"deleting existing collection {collection_name}")
|
log.info(f"deleting existing collection {collection_name}")
|
||||||
CHROMA_CLIENT.delete_collection(name=collection_name)
|
CHROMA_CLIENT.delete_collection(name=collection_name)
|
||||||
|
|
||||||
collection = CHROMA_CLIENT.create_collection(
|
collection = CHROMA_CLIENT.create_collection(
|
||||||
|
@ -346,7 +346,7 @@ def store_docs_in_vector_db(docs, collection_name, overwrite: bool = False) -> b
|
||||||
)
|
)
|
||||||
return True
|
return True
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(e)
|
log.exception(e)
|
||||||
if e.__class__.__name__ == "UniqueConstraintError":
|
if e.__class__.__name__ == "UniqueConstraintError":
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
@ -575,7 +575,7 @@ def scan_docs_dir(user=Depends(get_admin_user)):
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(e)
|
log.exception(e)
|
||||||
pass
|
pass
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|
|
@ -11,6 +11,7 @@ from utils.utils import verify_password
|
||||||
from apps.web.internal.db import DB
|
from apps.web.internal.db import DB
|
||||||
|
|
||||||
from config import SRC_LOG_LEVELS
|
from config import SRC_LOG_LEVELS
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
log.setLevel(SRC_LOG_LEVELS["MODELS"])
|
log.setLevel(SRC_LOG_LEVELS["MODELS"])
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,7 @@ from apps.web.internal.db import DB
|
||||||
import json
|
import json
|
||||||
|
|
||||||
from config import SRC_LOG_LEVELS
|
from config import SRC_LOG_LEVELS
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
log.setLevel(SRC_LOG_LEVELS["MODELS"])
|
log.setLevel(SRC_LOG_LEVELS["MODELS"])
|
||||||
|
|
||||||
|
|
|
@ -64,8 +64,8 @@ class ModelfilesTable:
|
||||||
self.db.create_tables([Modelfile])
|
self.db.create_tables([Modelfile])
|
||||||
|
|
||||||
def insert_new_modelfile(
|
def insert_new_modelfile(
|
||||||
self, user_id: str,
|
self, user_id: str, form_data: ModelfileForm
|
||||||
form_data: ModelfileForm) -> Optional[ModelfileModel]:
|
) -> Optional[ModelfileModel]:
|
||||||
if "tagName" in form_data.modelfile:
|
if "tagName" in form_data.modelfile:
|
||||||
modelfile = ModelfileModel(
|
modelfile = ModelfileModel(
|
||||||
**{
|
**{
|
||||||
|
@ -73,7 +73,8 @@ class ModelfilesTable:
|
||||||
"tag_name": form_data.modelfile["tagName"],
|
"tag_name": form_data.modelfile["tagName"],
|
||||||
"modelfile": json.dumps(form_data.modelfile),
|
"modelfile": json.dumps(form_data.modelfile),
|
||||||
"timestamp": int(time.time()),
|
"timestamp": int(time.time()),
|
||||||
})
|
}
|
||||||
|
)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
result = Modelfile.create(**modelfile.model_dump())
|
result = Modelfile.create(**modelfile.model_dump())
|
||||||
|
@ -87,29 +88,28 @@ class ModelfilesTable:
|
||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def get_modelfile_by_tag_name(self,
|
def get_modelfile_by_tag_name(self, tag_name: str) -> Optional[ModelfileModel]:
|
||||||
tag_name: str) -> Optional[ModelfileModel]:
|
|
||||||
try:
|
try:
|
||||||
modelfile = Modelfile.get(Modelfile.tag_name == tag_name)
|
modelfile = Modelfile.get(Modelfile.tag_name == tag_name)
|
||||||
return ModelfileModel(**model_to_dict(modelfile))
|
return ModelfileModel(**model_to_dict(modelfile))
|
||||||
except:
|
except:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def get_modelfiles(self,
|
def get_modelfiles(self, skip: int = 0, limit: int = 50) -> List[ModelfileResponse]:
|
||||||
skip: int = 0,
|
|
||||||
limit: int = 50) -> List[ModelfileResponse]:
|
|
||||||
return [
|
return [
|
||||||
ModelfileResponse(
|
ModelfileResponse(
|
||||||
**{
|
**{
|
||||||
**model_to_dict(modelfile),
|
**model_to_dict(modelfile),
|
||||||
"modelfile":
|
"modelfile": json.loads(modelfile.modelfile),
|
||||||
json.loads(modelfile.modelfile),
|
}
|
||||||
}) for modelfile in Modelfile.select()
|
)
|
||||||
|
for modelfile in Modelfile.select()
|
||||||
# .limit(limit).offset(skip)
|
# .limit(limit).offset(skip)
|
||||||
]
|
]
|
||||||
|
|
||||||
def update_modelfile_by_tag_name(
|
def update_modelfile_by_tag_name(
|
||||||
self, tag_name: str, modelfile: dict) -> Optional[ModelfileModel]:
|
self, tag_name: str, modelfile: dict
|
||||||
|
) -> Optional[ModelfileModel]:
|
||||||
try:
|
try:
|
||||||
query = Modelfile.update(
|
query = Modelfile.update(
|
||||||
modelfile=json.dumps(modelfile),
|
modelfile=json.dumps(modelfile),
|
||||||
|
|
|
@ -52,8 +52,9 @@ class PromptsTable:
|
||||||
self.db = db
|
self.db = db
|
||||||
self.db.create_tables([Prompt])
|
self.db.create_tables([Prompt])
|
||||||
|
|
||||||
def insert_new_prompt(self, user_id: str,
|
def insert_new_prompt(
|
||||||
form_data: PromptForm) -> Optional[PromptModel]:
|
self, user_id: str, form_data: PromptForm
|
||||||
|
) -> Optional[PromptModel]:
|
||||||
prompt = PromptModel(
|
prompt = PromptModel(
|
||||||
**{
|
**{
|
||||||
"user_id": user_id,
|
"user_id": user_id,
|
||||||
|
@ -61,7 +62,8 @@ class PromptsTable:
|
||||||
"title": form_data.title,
|
"title": form_data.title,
|
||||||
"content": form_data.content,
|
"content": form_data.content,
|
||||||
"timestamp": int(time.time()),
|
"timestamp": int(time.time()),
|
||||||
})
|
}
|
||||||
|
)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
result = Prompt.create(**prompt.model_dump())
|
result = Prompt.create(**prompt.model_dump())
|
||||||
|
@ -81,13 +83,14 @@ class PromptsTable:
|
||||||
|
|
||||||
def get_prompts(self) -> List[PromptModel]:
|
def get_prompts(self) -> List[PromptModel]:
|
||||||
return [
|
return [
|
||||||
PromptModel(**model_to_dict(prompt)) for prompt in Prompt.select()
|
PromptModel(**model_to_dict(prompt))
|
||||||
|
for prompt in Prompt.select()
|
||||||
# .limit(limit).offset(skip)
|
# .limit(limit).offset(skip)
|
||||||
]
|
]
|
||||||
|
|
||||||
def update_prompt_by_command(
|
def update_prompt_by_command(
|
||||||
self, command: str,
|
self, command: str, form_data: PromptForm
|
||||||
form_data: PromptForm) -> Optional[PromptModel]:
|
) -> Optional[PromptModel]:
|
||||||
try:
|
try:
|
||||||
query = Prompt.update(
|
query = Prompt.update(
|
||||||
title=form_data.title,
|
title=form_data.title,
|
||||||
|
|
|
@ -11,6 +11,7 @@ import logging
|
||||||
from apps.web.internal.db import DB
|
from apps.web.internal.db import DB
|
||||||
|
|
||||||
from config import SRC_LOG_LEVELS
|
from config import SRC_LOG_LEVELS
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
log.setLevel(SRC_LOG_LEVELS["MODELS"])
|
log.setLevel(SRC_LOG_LEVELS["MODELS"])
|
||||||
|
|
||||||
|
|
|
@ -29,6 +29,7 @@ from apps.web.models.tags import (
|
||||||
from constants import ERROR_MESSAGES
|
from constants import ERROR_MESSAGES
|
||||||
|
|
||||||
from config import SRC_LOG_LEVELS
|
from config import SRC_LOG_LEVELS
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
log.setLevel(SRC_LOG_LEVELS["MODELS"])
|
log.setLevel(SRC_LOG_LEVELS["MODELS"])
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,12 @@ import uuid
|
||||||
|
|
||||||
from apps.web.models.users import Users
|
from apps.web.models.users import Users
|
||||||
|
|
||||||
from utils.utils import get_password_hash, get_current_user, get_admin_user, create_token
|
from utils.utils import (
|
||||||
|
get_password_hash,
|
||||||
|
get_current_user,
|
||||||
|
get_admin_user,
|
||||||
|
create_token,
|
||||||
|
)
|
||||||
from utils.misc import get_gravatar_url, validate_email_format
|
from utils.misc import get_gravatar_url, validate_email_format
|
||||||
from constants import ERROR_MESSAGES
|
from constants import ERROR_MESSAGES
|
||||||
|
|
||||||
|
@ -43,7 +48,6 @@ async def set_global_default_models(
|
||||||
return request.app.state.DEFAULT_MODELS
|
return request.app.state.DEFAULT_MODELS
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@router.post("/default/suggestions", response_model=List[PromptSuggestion])
|
@router.post("/default/suggestions", response_model=List[PromptSuggestion])
|
||||||
async def set_global_default_suggestions(
|
async def set_global_default_suggestions(
|
||||||
request: Request,
|
request: Request,
|
||||||
|
|
|
@ -24,9 +24,9 @@ router = APIRouter()
|
||||||
|
|
||||||
|
|
||||||
@router.get("/", response_model=List[ModelfileResponse])
|
@router.get("/", response_model=List[ModelfileResponse])
|
||||||
async def get_modelfiles(skip: int = 0,
|
async def get_modelfiles(
|
||||||
limit: int = 50,
|
skip: int = 0, limit: int = 50, user=Depends(get_current_user)
|
||||||
user=Depends(get_current_user)):
|
):
|
||||||
return Modelfiles.get_modelfiles(skip, limit)
|
return Modelfiles.get_modelfiles(skip, limit)
|
||||||
|
|
||||||
|
|
||||||
|
@ -36,17 +36,16 @@ async def get_modelfiles(skip: int = 0,
|
||||||
|
|
||||||
|
|
||||||
@router.post("/create", response_model=Optional[ModelfileResponse])
|
@router.post("/create", response_model=Optional[ModelfileResponse])
|
||||||
async def create_new_modelfile(form_data: ModelfileForm,
|
async def create_new_modelfile(form_data: ModelfileForm, user=Depends(get_admin_user)):
|
||||||
user=Depends(get_admin_user)):
|
|
||||||
modelfile = Modelfiles.insert_new_modelfile(user.id, form_data)
|
modelfile = Modelfiles.insert_new_modelfile(user.id, form_data)
|
||||||
|
|
||||||
if modelfile:
|
if modelfile:
|
||||||
return ModelfileResponse(
|
return ModelfileResponse(
|
||||||
**{
|
**{
|
||||||
**modelfile.model_dump(),
|
**modelfile.model_dump(),
|
||||||
"modelfile":
|
"modelfile": json.loads(modelfile.modelfile),
|
||||||
json.loads(modelfile.modelfile),
|
}
|
||||||
})
|
)
|
||||||
else:
|
else:
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||||
|
@ -60,17 +59,18 @@ async def create_new_modelfile(form_data: ModelfileForm,
|
||||||
|
|
||||||
|
|
||||||
@router.post("/", response_model=Optional[ModelfileResponse])
|
@router.post("/", response_model=Optional[ModelfileResponse])
|
||||||
async def get_modelfile_by_tag_name(form_data: ModelfileTagNameForm,
|
async def get_modelfile_by_tag_name(
|
||||||
user=Depends(get_current_user)):
|
form_data: ModelfileTagNameForm, user=Depends(get_current_user)
|
||||||
|
):
|
||||||
modelfile = Modelfiles.get_modelfile_by_tag_name(form_data.tag_name)
|
modelfile = Modelfiles.get_modelfile_by_tag_name(form_data.tag_name)
|
||||||
|
|
||||||
if modelfile:
|
if modelfile:
|
||||||
return ModelfileResponse(
|
return ModelfileResponse(
|
||||||
**{
|
**{
|
||||||
**modelfile.model_dump(),
|
**modelfile.model_dump(),
|
||||||
"modelfile":
|
"modelfile": json.loads(modelfile.modelfile),
|
||||||
json.loads(modelfile.modelfile),
|
}
|
||||||
})
|
)
|
||||||
else:
|
else:
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||||
|
@ -84,8 +84,9 @@ async def get_modelfile_by_tag_name(form_data: ModelfileTagNameForm,
|
||||||
|
|
||||||
|
|
||||||
@router.post("/update", response_model=Optional[ModelfileResponse])
|
@router.post("/update", response_model=Optional[ModelfileResponse])
|
||||||
async def update_modelfile_by_tag_name(form_data: ModelfileUpdateForm,
|
async def update_modelfile_by_tag_name(
|
||||||
user=Depends(get_admin_user)):
|
form_data: ModelfileUpdateForm, user=Depends(get_admin_user)
|
||||||
|
):
|
||||||
modelfile = Modelfiles.get_modelfile_by_tag_name(form_data.tag_name)
|
modelfile = Modelfiles.get_modelfile_by_tag_name(form_data.tag_name)
|
||||||
if modelfile:
|
if modelfile:
|
||||||
updated_modelfile = {
|
updated_modelfile = {
|
||||||
|
@ -94,14 +95,15 @@ async def update_modelfile_by_tag_name(form_data: ModelfileUpdateForm,
|
||||||
}
|
}
|
||||||
|
|
||||||
modelfile = Modelfiles.update_modelfile_by_tag_name(
|
modelfile = Modelfiles.update_modelfile_by_tag_name(
|
||||||
form_data.tag_name, updated_modelfile)
|
form_data.tag_name, updated_modelfile
|
||||||
|
)
|
||||||
|
|
||||||
return ModelfileResponse(
|
return ModelfileResponse(
|
||||||
**{
|
**{
|
||||||
**modelfile.model_dump(),
|
**modelfile.model_dump(),
|
||||||
"modelfile":
|
"modelfile": json.loads(modelfile.modelfile),
|
||||||
json.loads(modelfile.modelfile),
|
}
|
||||||
})
|
)
|
||||||
else:
|
else:
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||||
|
@ -115,7 +117,8 @@ async def update_modelfile_by_tag_name(form_data: ModelfileUpdateForm,
|
||||||
|
|
||||||
|
|
||||||
@router.delete("/delete", response_model=bool)
|
@router.delete("/delete", response_model=bool)
|
||||||
async def delete_modelfile_by_tag_name(form_data: ModelfileTagNameForm,
|
async def delete_modelfile_by_tag_name(
|
||||||
user=Depends(get_admin_user)):
|
form_data: ModelfileTagNameForm, user=Depends(get_admin_user)
|
||||||
|
):
|
||||||
result = Modelfiles.delete_modelfile_by_tag_name(form_data.tag_name)
|
result = Modelfiles.delete_modelfile_by_tag_name(form_data.tag_name)
|
||||||
return result
|
return result
|
||||||
|
|
|
@ -16,6 +16,7 @@ from utils.utils import get_current_user, get_password_hash, get_admin_user
|
||||||
from constants import ERROR_MESSAGES
|
from constants import ERROR_MESSAGES
|
||||||
|
|
||||||
from config import SRC_LOG_LEVELS
|
from config import SRC_LOG_LEVELS
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
log.setLevel(SRC_LOG_LEVELS["MODELS"])
|
log.setLevel(SRC_LOG_LEVELS["MODELS"])
|
||||||
|
|
||||||
|
|
|
@ -117,7 +117,20 @@ else:
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
log.info(f"GLOBAL_LOG_LEVEL: {GLOBAL_LOG_LEVEL}")
|
log.info(f"GLOBAL_LOG_LEVEL: {GLOBAL_LOG_LEVEL}")
|
||||||
|
|
||||||
log_sources = ["AUDIO", "CONFIG", "DB", "IMAGES", "LITELLM", "MAIN", "MODELS", "OLLAMA", "OPENAI", "RAG"]
|
log_sources = [
|
||||||
|
"AUDIO",
|
||||||
|
"COMFYUI",
|
||||||
|
"CONFIG",
|
||||||
|
"DB",
|
||||||
|
"IMAGES",
|
||||||
|
"LITELLM",
|
||||||
|
"MAIN",
|
||||||
|
"MODELS",
|
||||||
|
"OLLAMA",
|
||||||
|
"OPENAI",
|
||||||
|
"RAG",
|
||||||
|
"WEBHOOK",
|
||||||
|
]
|
||||||
|
|
||||||
SRC_LOG_LEVELS = {}
|
SRC_LOG_LEVELS = {}
|
||||||
|
|
||||||
|
@ -239,7 +252,7 @@ OLLAMA_API_BASE_URL = os.environ.get(
|
||||||
)
|
)
|
||||||
|
|
||||||
OLLAMA_BASE_URL = os.environ.get("OLLAMA_BASE_URL", "")
|
OLLAMA_BASE_URL = os.environ.get("OLLAMA_BASE_URL", "")
|
||||||
KUBERNETES_SERVICE_HOST = os.environ.get("KUBERNETES_SERVICE_HOST", "")
|
K8S_FLAG = os.environ.get("K8S_FLAG", "")
|
||||||
|
|
||||||
if OLLAMA_BASE_URL == "" and OLLAMA_API_BASE_URL != "":
|
if OLLAMA_BASE_URL == "" and OLLAMA_API_BASE_URL != "":
|
||||||
OLLAMA_BASE_URL = (
|
OLLAMA_BASE_URL = (
|
||||||
|
@ -249,9 +262,10 @@ if OLLAMA_BASE_URL == "" and OLLAMA_API_BASE_URL != "":
|
||||||
)
|
)
|
||||||
|
|
||||||
if ENV == "prod":
|
if ENV == "prod":
|
||||||
if OLLAMA_BASE_URL == "/ollama" and KUBERNETES_SERVICE_HOST == "":
|
if OLLAMA_BASE_URL == "/ollama":
|
||||||
OLLAMA_BASE_URL = "http://host.docker.internal:11434"
|
OLLAMA_BASE_URL = "http://host.docker.internal:11434"
|
||||||
else:
|
|
||||||
|
elif K8S_FLAG:
|
||||||
OLLAMA_BASE_URL = "http://ollama-service.open-webui.svc.cluster.local:11434"
|
OLLAMA_BASE_URL = "http://ollama-service.open-webui.svc.cluster.local:11434"
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,33 +1,22 @@
|
||||||
{
|
{
|
||||||
"version": 0,
|
"version": 0,
|
||||||
"ui": {
|
"ui": {
|
||||||
|
"default_locale": "en-US",
|
||||||
"prompt_suggestions": [
|
"prompt_suggestions": [
|
||||||
{
|
{
|
||||||
"title": [
|
"title": ["Help me study", "vocabulary for a college entrance exam"],
|
||||||
"Help me study",
|
|
||||||
"vocabulary for a college entrance exam"
|
|
||||||
],
|
|
||||||
"content": "Help me study vocabulary: write a sentence for me to fill in the blank, and I'll try to pick the correct option."
|
"content": "Help me study vocabulary: write a sentence for me to fill in the blank, and I'll try to pick the correct option."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"title": [
|
"title": ["Give me ideas", "for what to do with my kids' art"],
|
||||||
"Give me ideas",
|
|
||||||
"for what to do with my kids' art"
|
|
||||||
],
|
|
||||||
"content": "What are 5 creative things I could do with my kids' art? I don't want to throw them away, but it's also so much clutter."
|
"content": "What are 5 creative things I could do with my kids' art? I don't want to throw them away, but it's also so much clutter."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"title": [
|
"title": ["Tell me a fun fact", "about the Roman Empire"],
|
||||||
"Tell me a fun fact",
|
|
||||||
"about the Roman Empire"
|
|
||||||
],
|
|
||||||
"content": "Tell me a random fun fact about the Roman Empire"
|
"content": "Tell me a random fun fact about the Roman Empire"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"title": [
|
"title": ["Show me a code snippet", "of a website's sticky header"],
|
||||||
"Show me a code snippet",
|
|
||||||
"of a website's sticky header"
|
|
||||||
],
|
|
||||||
"content": "Show me a code snippet of a website's sticky header in CSS and JavaScript."
|
"content": "Show me a code snippet of a website's sticky header in CSS and JavaScript."
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
@ -32,6 +32,7 @@ from utils.utils import get_admin_user
|
||||||
from apps.rag.utils import rag_messages
|
from apps.rag.utils import rag_messages
|
||||||
|
|
||||||
from config import (
|
from config import (
|
||||||
|
CONFIG_DATA,
|
||||||
WEBUI_NAME,
|
WEBUI_NAME,
|
||||||
ENV,
|
ENV,
|
||||||
VERSION,
|
VERSION,
|
||||||
|
@ -49,6 +50,7 @@ logging.basicConfig(stream=sys.stdout, level=GLOBAL_LOG_LEVEL)
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
log.setLevel(SRC_LOG_LEVELS["MAIN"])
|
log.setLevel(SRC_LOG_LEVELS["MAIN"])
|
||||||
|
|
||||||
|
|
||||||
class SPAStaticFiles(StaticFiles):
|
class SPAStaticFiles(StaticFiles):
|
||||||
async def get_response(self, path: str, scope):
|
async def get_response(self, path: str, scope):
|
||||||
try:
|
try:
|
||||||
|
@ -88,7 +90,6 @@ class RAGMiddleware(BaseHTTPMiddleware):
|
||||||
# Example: Add a new key-value pair or modify existing ones
|
# Example: Add a new key-value pair or modify existing ones
|
||||||
# data["modified"] = True # Example modification
|
# data["modified"] = True # Example modification
|
||||||
if "docs" in data:
|
if "docs" in data:
|
||||||
|
|
||||||
data = {**data}
|
data = {**data}
|
||||||
data["messages"] = rag_messages(
|
data["messages"] = rag_messages(
|
||||||
data["docs"],
|
data["docs"],
|
||||||
|
@ -163,11 +164,15 @@ app.mount("/rag/api/v1", rag_app)
|
||||||
|
|
||||||
@app.get("/api/config")
|
@app.get("/api/config")
|
||||||
async def get_app_config():
|
async def get_app_config():
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"status": True,
|
"status": True,
|
||||||
"name": WEBUI_NAME,
|
"name": WEBUI_NAME,
|
||||||
"version": VERSION,
|
"version": VERSION,
|
||||||
|
"default_locale": (
|
||||||
|
CONFIG_DATA["ui"]["default_locale"]
|
||||||
|
if "ui" in CONFIG_DATA and "default_locale" in CONFIG_DATA["ui"]
|
||||||
|
else "en-US"
|
||||||
|
),
|
||||||
"images": images_app.state.ENABLED,
|
"images": images_app.state.ENABLED,
|
||||||
"default_models": webui_app.state.DEFAULT_MODELS,
|
"default_models": webui_app.state.DEFAULT_MODELS,
|
||||||
"default_prompt_suggestions": webui_app.state.DEFAULT_PROMPT_SUGGESTIONS,
|
"default_prompt_suggestions": webui_app.state.DEFAULT_PROMPT_SUGGESTIONS,
|
||||||
|
@ -192,7 +197,6 @@ class ModelFilterConfigForm(BaseModel):
|
||||||
async def update_model_filter_config(
|
async def update_model_filter_config(
|
||||||
form_data: ModelFilterConfigForm, user=Depends(get_admin_user)
|
form_data: ModelFilterConfigForm, user=Depends(get_admin_user)
|
||||||
):
|
):
|
||||||
|
|
||||||
app.state.MODEL_FILTER_ENABLED = form_data.enabled
|
app.state.MODEL_FILTER_ENABLED = form_data.enabled
|
||||||
app.state.MODEL_FILTER_LIST = form_data.models
|
app.state.MODEL_FILTER_LIST = form_data.models
|
||||||
|
|
||||||
|
@ -202,6 +206,9 @@ async def update_model_filter_config(
|
||||||
openai_app.state.MODEL_FILTER_ENABLED = app.state.MODEL_FILTER_ENABLED
|
openai_app.state.MODEL_FILTER_ENABLED = app.state.MODEL_FILTER_ENABLED
|
||||||
openai_app.state.MODEL_FILTER_LIST = app.state.MODEL_FILTER_LIST
|
openai_app.state.MODEL_FILTER_LIST = app.state.MODEL_FILTER_LIST
|
||||||
|
|
||||||
|
litellm_app.state.MODEL_FILTER_ENABLED = app.state.MODEL_FILTER_ENABLED
|
||||||
|
litellm_app.state.MODEL_FILTER_LIST = app.state.MODEL_FILTER_LIST
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"enabled": app.state.MODEL_FILTER_ENABLED,
|
"enabled": app.state.MODEL_FILTER_ENABLED,
|
||||||
"models": app.state.MODEL_FILTER_LIST,
|
"models": app.state.MODEL_FILTER_LIST,
|
||||||
|
@ -232,7 +239,6 @@ async def update_webhook_url(form_data: UrlForm, user=Depends(get_admin_user)):
|
||||||
|
|
||||||
@app.get("/api/version")
|
@app.get("/api/version")
|
||||||
async def get_app_config():
|
async def get_app_config():
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"version": VERSION,
|
"version": VERSION,
|
||||||
}
|
}
|
||||||
|
@ -240,7 +246,7 @@ async def get_app_config():
|
||||||
|
|
||||||
@app.get("/api/changelog")
|
@app.get("/api/changelog")
|
||||||
async def get_app_changelog():
|
async def get_app_changelog():
|
||||||
return CHANGELOG
|
return {key: CHANGELOG[key] for idx, key in enumerate(CHANGELOG) if idx < 5}
|
||||||
|
|
||||||
|
|
||||||
@app.get("/api/version/updates")
|
@app.get("/api/version/updates")
|
||||||
|
|
|
@ -1,6 +1,12 @@
|
||||||
import json
|
import json
|
||||||
import requests
|
import requests
|
||||||
from config import VERSION, WEBUI_FAVICON_URL, WEBUI_NAME
|
import logging
|
||||||
|
|
||||||
|
from config import SRC_LOG_LEVELS, VERSION, WEBUI_FAVICON_URL, WEBUI_NAME
|
||||||
|
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
log.setLevel(SRC_LOG_LEVELS["WEBHOOK"])
|
||||||
|
|
||||||
|
|
||||||
def post_webhook(url: str, message: str, event_data: dict) -> bool:
|
def post_webhook(url: str, message: str, event_data: dict) -> bool:
|
||||||
try:
|
try:
|
||||||
|
@ -38,9 +44,11 @@ def post_webhook(url: str, message: str, event_data: dict) -> bool:
|
||||||
else:
|
else:
|
||||||
payload = {**event_data}
|
payload = {**event_data}
|
||||||
|
|
||||||
|
log.debug(f"payload: {payload}")
|
||||||
r = requests.post(url, json=payload)
|
r = requests.post(url, json=payload)
|
||||||
r.raise_for_status()
|
r.raise_for_status()
|
||||||
|
log.debug(f"r.text: {r.text}")
|
||||||
return True
|
return True
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(e)
|
log.exception(e)
|
||||||
return False
|
return False
|
|
@ -88,7 +88,7 @@ spec:
|
||||||
resources:
|
resources:
|
||||||
requests:
|
requests:
|
||||||
storage: {{ .Values.ollama.persistence.size | quote }}
|
storage: {{ .Values.ollama.persistence.size | quote }}
|
||||||
storageClass: {{ .Values.ollama.persistence.storageClass }}
|
storageClassName: {{ .Values.ollama.persistence.storageClass }}
|
||||||
{{- with .Values.ollama.persistence.selector }}
|
{{- with .Values.ollama.persistence.selector }}
|
||||||
selector:
|
selector:
|
||||||
{{- toYaml . | nindent 8 }}
|
{{- toYaml . | nindent 8 }}
|
||||||
|
|
|
@ -17,7 +17,7 @@ spec:
|
||||||
resources:
|
resources:
|
||||||
requests:
|
requests:
|
||||||
storage: {{ .Values.webui.persistence.size }}
|
storage: {{ .Values.webui.persistence.size }}
|
||||||
storageClass: {{ .Values.webui.persistence.storageClass }}
|
storageClassName: {{ .Values.webui.persistence.storageClass }}
|
||||||
{{- with .Values.webui.persistence.selector }}
|
{{- with .Values.webui.persistence.selector }}
|
||||||
selector:
|
selector:
|
||||||
{{- toYaml . | nindent 4 }}
|
{{- toYaml . | nindent 4 }}
|
||||||
|
|
4
package-lock.json
generated
4
package-lock.json
generated
|
@ -1,12 +1,12 @@
|
||||||
{
|
{
|
||||||
"name": "open-webui",
|
"name": "open-webui",
|
||||||
"version": "0.1.114",
|
"version": "0.1.116",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "open-webui",
|
"name": "open-webui",
|
||||||
"version": "0.1.114",
|
"version": "0.1.116",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@sveltejs/adapter-node": "^1.3.1",
|
"@sveltejs/adapter-node": "^1.3.1",
|
||||||
"async": "^3.2.5",
|
"async": "^3.2.5",
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "open-webui",
|
"name": "open-webui",
|
||||||
"version": "0.1.115",
|
"version": "0.1.116",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite dev --host",
|
"dev": "vite dev --host",
|
||||||
|
@ -13,7 +13,6 @@
|
||||||
"lint:types": "npm run check",
|
"lint:types": "npm run check",
|
||||||
"lint:backend": "pylint backend/",
|
"lint:backend": "pylint backend/",
|
||||||
"format": "prettier --plugin-search-dir --write '**/*.{js,ts,svelte,css,md,html,json}'",
|
"format": "prettier --plugin-search-dir --write '**/*.{js,ts,svelte,css,md,html,json}'",
|
||||||
"format:backend": "yapf --recursive backend -p -i",
|
|
||||||
"i18n:parse": "i18next --config i18next-parser.config.ts && prettier --write 'src/lib/i18n/**/*.{js,json}'"
|
"i18n:parse": "i18next --config i18next-parser.config.ts && prettier --write 'src/lib/i18n/**/*.{js,json}'"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|
|
@ -9,7 +9,11 @@
|
||||||
<script>
|
<script>
|
||||||
// On page load or when changing themes, best to add inline in `head` to avoid FOUC
|
// On page load or when changing themes, best to add inline in `head` to avoid FOUC
|
||||||
(() => {
|
(() => {
|
||||||
if (
|
if (localStorage?.theme && localStorage?.theme.includes('oled')) {
|
||||||
|
document.documentElement.style.setProperty('--color-gray-900', '#000000');
|
||||||
|
document.documentElement.style.setProperty('--color-gray-950', '#000000');
|
||||||
|
document.documentElement.classList.add('dark');
|
||||||
|
} else if (
|
||||||
localStorage.theme === 'light' ||
|
localStorage.theme === 'light' ||
|
||||||
(!('theme' in localStorage) && window.matchMedia('(prefers-color-scheme: light)').matches)
|
(!('theme' in localStorage) && window.matchMedia('(prefers-color-scheme: light)').matches)
|
||||||
) {
|
) {
|
||||||
|
|
|
@ -1,9 +1,12 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import { Collapsible } from 'bits-ui';
|
||||||
|
|
||||||
import { setDefaultModels } from '$lib/apis/configs';
|
import { setDefaultModels } from '$lib/apis/configs';
|
||||||
import { models, showSettings, settings, user } from '$lib/stores';
|
import { models, showSettings, settings, user } from '$lib/stores';
|
||||||
import { onMount, tick, getContext } from 'svelte';
|
import { onMount, tick, getContext } from 'svelte';
|
||||||
import { toast } from 'svelte-sonner';
|
import { toast } from 'svelte-sonner';
|
||||||
import Selector from './ModelSelector/Selector.svelte';
|
import Selector from './ModelSelector/Selector.svelte';
|
||||||
|
import Tooltip from '../common/Tooltip.svelte';
|
||||||
|
|
||||||
const i18n = getContext('i18n');
|
const i18n = getContext('i18n');
|
||||||
|
|
||||||
|
@ -33,11 +36,11 @@
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="flex flex-col my-2 w-full">
|
<div class="flex flex-col mt-0.5 w-full">
|
||||||
{#each selectedModels as selectedModel, selectedModelIdx}
|
{#each selectedModels as selectedModel, selectedModelIdx}
|
||||||
<div class="flex w-full">
|
<div class="flex w-full">
|
||||||
<div class="overflow-hidden w-full">
|
<div class="overflow-hidden w-full">
|
||||||
<div class="mr-2 max-w-full">
|
<div class="mr-0.5 max-w-full">
|
||||||
<Selector
|
<Selector
|
||||||
placeholder={$i18n.t('Select a model')}
|
placeholder={$i18n.t('Select a model')}
|
||||||
items={$models
|
items={$models
|
||||||
|
@ -53,10 +56,10 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{#if selectedModelIdx === 0}
|
{#if selectedModelIdx === 0}
|
||||||
|
<div class=" self-center mr-2 disabled:text-gray-600 disabled:hover:text-gray-600">
|
||||||
|
<Tooltip content="Add Model">
|
||||||
<button
|
<button
|
||||||
class=" self-center {selectedModelIdx === 0
|
class=" "
|
||||||
? 'mr-3'
|
|
||||||
: 'mr-7'} disabled:text-gray-600 disabled:hover:text-gray-600"
|
|
||||||
{disabled}
|
{disabled}
|
||||||
on:click={() => {
|
on:click={() => {
|
||||||
selectedModels = [...selectedModels, ''];
|
selectedModels = [...selectedModels, ''];
|
||||||
|
@ -73,12 +76,12 @@
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" d="M12 6v12m6-6H6" />
|
<path stroke-linecap="round" stroke-linejoin="round" d="M12 6v12m6-6H6" />
|
||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
{:else}
|
{:else}
|
||||||
|
<div class=" self-center disabled:text-gray-600 disabled:hover:text-gray-600 mr-2">
|
||||||
|
<Tooltip content="Remove Model">
|
||||||
<button
|
<button
|
||||||
class=" self-center disabled:text-gray-600 disabled:hover:text-gray-600 {selectedModelIdx ===
|
|
||||||
0
|
|
||||||
? 'mr-3'
|
|
||||||
: 'mr-7'}"
|
|
||||||
{disabled}
|
{disabled}
|
||||||
on:click={() => {
|
on:click={() => {
|
||||||
selectedModels.splice(selectedModelIdx, 1);
|
selectedModels.splice(selectedModelIdx, 1);
|
||||||
|
@ -96,41 +99,13 @@
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" d="M19.5 12h-15" />
|
<path stroke-linecap="round" stroke-linejoin="round" d="M19.5 12h-15" />
|
||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
{/if}
|
</Tooltip>
|
||||||
|
</div>
|
||||||
{#if selectedModelIdx === 0}
|
|
||||||
<button
|
|
||||||
class=" self-center dark:hover:text-gray-300"
|
|
||||||
id="open-settings-button"
|
|
||||||
on:click={async () => {
|
|
||||||
await showSettings.set(!$showSettings);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<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="M10.343 3.94c.09-.542.56-.94 1.11-.94h1.093c.55 0 1.02.398 1.11.94l.149.894c.07.424.384.764.78.93.398.164.855.142 1.205-.108l.737-.527a1.125 1.125 0 011.45.12l.773.774c.39.389.44 1.002.12 1.45l-.527.737c-.25.35-.272.806-.107 1.204.165.397.505.71.93.78l.893.15c.543.09.94.56.94 1.109v1.094c0 .55-.397 1.02-.94 1.11l-.893.149c-.425.07-.765.383-.93.78-.165.398-.143.854.107 1.204l.527.738c.32.447.269 1.06-.12 1.45l-.774.773a1.125 1.125 0 01-1.449.12l-.738-.527c-.35-.25-.806-.272-1.203-.107-.397.165-.71.505-.781.929l-.149.894c-.09.542-.56.94-1.11.94h-1.094c-.55 0-1.019-.398-1.11-.94l-.148-.894c-.071-.424-.384-.764-.781-.93-.398-.164-.854-.142-1.204.108l-.738.527c-.447.32-1.06.269-1.45-.12l-.773-.774a1.125 1.125 0 01-.12-1.45l.527-.737c.25-.35.273-.806.108-1.204-.165-.397-.505-.71-.93-.78l-.894-.15c-.542-.09-.94-.56-.94-1.109v-1.094c0-.55.398-1.02.94-1.11l.894-.149c.424-.07.765-.383.93-.78.165-.398.143-.854-.107-1.204l-.527-.738a1.125 1.125 0 01.12-1.45l.773-.773a1.125 1.125 0 011.45-.12l.737.527c.35.25.807.272 1.204.107.397-.165.71-.505.78-.929l.15-.894z"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="text-left mt-1.5 ml-1 text-xs text-gray-500">
|
<div class="text-left mt-0.5 ml-1 text-[0.7rem] text-gray-500">
|
||||||
<button on:click={saveDefaultModel}> {$i18n.t('Set as default')}</button>
|
<button on:click={saveDefaultModel}> {$i18n.t('Set as default')}</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -181,20 +181,20 @@
|
||||||
searchValue = '';
|
searchValue = '';
|
||||||
window.setTimeout(() => document.getElementById('model-search-input')?.focus(), 0);
|
window.setTimeout(() => document.getElementById('model-search-input')?.focus(), 0);
|
||||||
}}
|
}}
|
||||||
selected={items.find((item) => item.value === value)}
|
selected={items.find((item) => item.value === value) ?? ''}
|
||||||
onSelectedChange={(selectedItem) => {
|
onSelectedChange={(selectedItem) => {
|
||||||
value = selectedItem.value;
|
value = selectedItem.value;
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Select.Trigger class="relative w-full" aria-label={placeholder}>
|
<Select.Trigger class="relative w-full" aria-label={placeholder}>
|
||||||
<Select.Value
|
<Select.Value
|
||||||
class="inline-flex h-input px-0.5 w-full outline-none bg-transparent truncate text-lg font-semibold placeholder-gray-400 focus:outline-none"
|
class="flex text-left px-0.5 outline-none bg-transparent truncate text-lg font-semibold placeholder-gray-400 focus:outline-none"
|
||||||
{placeholder}
|
{placeholder}
|
||||||
/>
|
/>
|
||||||
<ChevronDown className="absolute end-2 top-1/2 -translate-y-[45%] size-3.5" strokeWidth="2.5" />
|
<ChevronDown className="absolute end-2 top-1/2 -translate-y-[45%] size-3.5" strokeWidth="2.5" />
|
||||||
</Select.Trigger>
|
</Select.Trigger>
|
||||||
<Select.Content
|
<Select.Content
|
||||||
class="w-full rounded-lg bg-white dark:bg-gray-900 dark:text-white shadow-lg border border-gray-300/30 dark:border-gray-700/50 outline-none"
|
class=" z-40 w-full rounded-lg bg-white dark:bg-gray-900 dark:text-white shadow-lg border border-gray-300/30 dark:border-gray-700/50 outline-none"
|
||||||
transition={flyAndScale}
|
transition={flyAndScale}
|
||||||
sideOffset={4}
|
sideOffset={4}
|
||||||
>
|
>
|
||||||
|
@ -214,7 +214,7 @@
|
||||||
<hr class="border-gray-100 dark:border-gray-800" />
|
<hr class="border-gray-100 dark:border-gray-800" />
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<div class="px-3 my-2 max-h-80 overflow-y-auto">
|
<div class="px-3 my-2 max-h-72 overflow-y-auto">
|
||||||
{#each filteredItems as item}
|
{#each filteredItems as item}
|
||||||
<Select.Item
|
<Select.Item
|
||||||
class="flex w-full font-medium line-clamp-1 select-none items-center rounded-button py-2 pl-3 pr-1.5 text-sm text-gray-700 dark:text-gray-100 outline-none transition-all duration-75 hover:bg-gray-100 dark:hover:bg-gray-850 rounded-lg cursor-pointer data-[highlighted]:bg-muted"
|
class="flex w-full font-medium line-clamp-1 select-none items-center rounded-button py-2 pl-3 pr-1.5 text-sm text-gray-700 dark:text-gray-100 outline-none transition-all duration-75 hover:bg-gray-100 dark:hover:bg-gray-850 rounded-lg cursor-pointer data-[highlighted]:bg-muted"
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
export let getModels: Function;
|
export let getModels: Function;
|
||||||
|
|
||||||
// General
|
// General
|
||||||
let themes = ['dark', 'light', 'rose-pine dark', 'rose-pine-dawn light'];
|
let themes = ['dark', 'light', 'rose-pine dark', 'rose-pine-dawn light', 'oled-dark'];
|
||||||
let selectedTheme = 'system';
|
let selectedTheme = 'system';
|
||||||
|
|
||||||
let languages = [];
|
let languages = [];
|
||||||
|
@ -91,12 +91,17 @@
|
||||||
});
|
});
|
||||||
|
|
||||||
const applyTheme = (_theme: string) => {
|
const applyTheme = (_theme: string) => {
|
||||||
let themeToApply = _theme;
|
let themeToApply = _theme === 'oled-dark' ? 'dark' : _theme;
|
||||||
|
|
||||||
if (_theme === 'system') {
|
if (_theme === 'system') {
|
||||||
themeToApply = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
|
themeToApply = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (themeToApply === 'dark' && !_theme.includes('oled')) {
|
||||||
|
document.documentElement.style.setProperty('--color-gray-900', '#171717');
|
||||||
|
document.documentElement.style.setProperty('--color-gray-950', '#0d0d0d');
|
||||||
|
}
|
||||||
|
|
||||||
themes
|
themes
|
||||||
.filter((e) => e !== themeToApply)
|
.filter((e) => e !== themeToApply)
|
||||||
.forEach((e) => {
|
.forEach((e) => {
|
||||||
|
@ -115,7 +120,11 @@
|
||||||
const themeChangeHandler = (_theme: string) => {
|
const themeChangeHandler = (_theme: string) => {
|
||||||
theme.set(_theme);
|
theme.set(_theme);
|
||||||
localStorage.setItem('theme', _theme);
|
localStorage.setItem('theme', _theme);
|
||||||
|
if (_theme.includes('oled')) {
|
||||||
|
document.documentElement.style.setProperty('--color-gray-900', '#000000');
|
||||||
|
document.documentElement.style.setProperty('--color-gray-950', '#000000');
|
||||||
|
document.documentElement.classList.add('dark');
|
||||||
|
}
|
||||||
applyTheme(_theme);
|
applyTheme(_theme);
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
@ -136,6 +145,7 @@
|
||||||
>
|
>
|
||||||
<option value="system">⚙️ {$i18n.t('System')}</option>
|
<option value="system">⚙️ {$i18n.t('System')}</option>
|
||||||
<option value="dark">🌑 {$i18n.t('Dark')}</option>
|
<option value="dark">🌑 {$i18n.t('Dark')}</option>
|
||||||
|
<option value="oled-dark">🌃 {$i18n.t('OLED Dark')}</option>
|
||||||
<option value="light">☀️ {$i18n.t('Light')}</option>
|
<option value="light">☀️ {$i18n.t('Light')}</option>
|
||||||
<option value="rose-pine dark">🪻 {$i18n.t('Rosé Pine')}</option>
|
<option value="rose-pine dark">🪻 {$i18n.t('Rosé Pine')}</option>
|
||||||
<option value="rose-pine-dawn light">🌷 {$i18n.t('Rosé Pine Dawn')}</option>
|
<option value="rose-pine-dawn light">🌷 {$i18n.t('Rosé Pine Dawn')}</option>
|
||||||
|
|
20
src/lib/components/chat/TagChatModal.svelte
Normal file
20
src/lib/components/chat/TagChatModal.svelte
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { getContext } from 'svelte';
|
||||||
|
import Modal from '../common/Modal.svelte';
|
||||||
|
|
||||||
|
import Tags from '../common/Tags.svelte';
|
||||||
|
|
||||||
|
const i18n = getContext('i18n');
|
||||||
|
|
||||||
|
export let tags;
|
||||||
|
export let deleteTag: Function;
|
||||||
|
export let addTag: Function;
|
||||||
|
|
||||||
|
export let show = false;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Modal bind:show size="xs">
|
||||||
|
<div class="px-4 pt-4 pb-5 w-full flex flex-col justify-center">
|
||||||
|
<Tags {tags} {deleteTag} {addTag} />
|
||||||
|
</div>
|
||||||
|
</Modal>
|
|
@ -8,7 +8,7 @@
|
||||||
export let addTag: Function;
|
export let addTag: Function;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="flex flex-row space-x-0.5 line-clamp-1">
|
<div class="flex flex-row flex-wrap gap-0.5 line-clamp-1">
|
||||||
<TagList
|
<TagList
|
||||||
{tags}
|
{tags}
|
||||||
on:delete={(e) => {
|
on:delete={(e) => {
|
||||||
|
@ -17,6 +17,7 @@
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<TagInput
|
<TagInput
|
||||||
|
label={tags.length == 0 ? 'Add Tags' : ''}
|
||||||
on:add={(e) => {
|
on:add={(e) => {
|
||||||
addTag(e.detail);
|
addTag(e.detail);
|
||||||
}}
|
}}
|
||||||
|
|
|
@ -1,24 +1,31 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { createEventDispatcher, getContext } from 'svelte';
|
import { createEventDispatcher, getContext } from 'svelte';
|
||||||
|
import { tags } from '$lib/stores';
|
||||||
|
import { toast } from 'svelte-sonner';
|
||||||
const dispatch = createEventDispatcher();
|
const dispatch = createEventDispatcher();
|
||||||
|
|
||||||
const i18n = getContext('i18n');
|
const i18n = getContext('i18n');
|
||||||
|
|
||||||
|
export let label = '';
|
||||||
let showTagInput = false;
|
let showTagInput = false;
|
||||||
let tagName = '';
|
let tagName = '';
|
||||||
|
|
||||||
|
const addTagHandler = async () => {
|
||||||
|
tagName = tagName.trim();
|
||||||
|
if (tagName !== '') {
|
||||||
|
dispatch('add', tagName);
|
||||||
|
tagName = '';
|
||||||
|
showTagInput = false;
|
||||||
|
} else {
|
||||||
|
toast.error('Invalid Tag');
|
||||||
|
}
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="flex space-x-1 pl-1.5">
|
<div class="flex space-x-1 pl-1.5">
|
||||||
{#if showTagInput}
|
{#if showTagInput}
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<button
|
<button type="button" on:click={addTagHandler}>
|
||||||
type="button"
|
|
||||||
on:click={() => {
|
|
||||||
dispatch('add', tagName);
|
|
||||||
tagName = '';
|
|
||||||
showTagInput = false;
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<svg
|
<svg
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
viewBox="0 0 16 16"
|
viewBox="0 0 16 16"
|
||||||
|
@ -34,12 +41,21 @@
|
||||||
</button>
|
</button>
|
||||||
<input
|
<input
|
||||||
bind:value={tagName}
|
bind:value={tagName}
|
||||||
class=" pl-2 cursor-pointer self-center text-xs h-fit bg-transparent outline-none line-clamp-1 w-[8rem]"
|
class=" pl-2 cursor-pointer self-center text-xs h-fit bg-transparent outline-none line-clamp-1 w-[5.5rem]"
|
||||||
placeholder={$i18n.t('Add a tag')}
|
placeholder={$i18n.t('Add a tag')}
|
||||||
|
list="tagOptions"
|
||||||
|
on:keydown={(event) => {
|
||||||
|
if (event.key === 'Enter') {
|
||||||
|
addTagHandler();
|
||||||
|
}
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
|
<datalist id="tagOptions">
|
||||||
|
{#each $tags as tag}
|
||||||
|
<option value={tag.name} />
|
||||||
|
{/each}
|
||||||
|
</datalist>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- TODO: Tag Suggestions -->
|
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<button
|
<button
|
||||||
|
@ -62,4 +78,8 @@
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
|
{#if label && !showTagInput}
|
||||||
|
<span class="text-xs pl-1.5 self-center">{label}</span>
|
||||||
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
|
|
||||||
{#each tags as tag}
|
{#each tags as tag}
|
||||||
<div
|
<div
|
||||||
class="px-2 py-0.5 space-x-1 flex h-fit items-center rounded-full transition border dark:border-gray-600 dark:text-white"
|
class="px-2 py-0.5 space-x-1 flex h-fit items-center rounded-full transition border dark:border-gray-800 dark:text-white"
|
||||||
>
|
>
|
||||||
<div class=" text-[0.7rem] font-medium self-center line-clamp-1">
|
<div class=" text-[0.7rem] font-medium self-center line-clamp-1">
|
||||||
{tag.name}
|
{tag.name}
|
||||||
|
|
|
@ -29,6 +29,6 @@
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div bind:this={tooltipElement} aria-label={content}>
|
<div bind:this={tooltipElement} aria-label={content} class="flex">
|
||||||
<slot />
|
<slot />
|
||||||
</div>
|
</div>
|
||||||
|
|
19
src/lib/components/icons/ChevronUpDown.svelte
Normal file
19
src/lib/components/icons/ChevronUpDown.svelte
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
<script lang="ts">
|
||||||
|
export let className = 'w-4 h-4';
|
||||||
|
export let strokeWidth = '1.5';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
stroke-width={strokeWidth}
|
||||||
|
stroke="currentColor"
|
||||||
|
class={className}
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
d="M8.25 15 12 18.75 15.75 15m-7.5-6L12 5.25 15.75 9"
|
||||||
|
/>
|
||||||
|
</svg>
|
19
src/lib/components/icons/EllipsisVertical.svelte
Normal file
19
src/lib/components/icons/EllipsisVertical.svelte
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
<script lang="ts">
|
||||||
|
export let className = 'w-4 h-4';
|
||||||
|
export let strokeWidth = '1.5';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
stroke-width={strokeWidth}
|
||||||
|
stroke="currentColor"
|
||||||
|
class={className}
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
d="M12 6.75a.75.75 0 1 1 0-1.5.75.75 0 0 1 0 1.5ZM12 12.75a.75.75 0 1 1 0-1.5.75.75 0 0 1 0 1.5ZM12 18.75a.75.75 0 1 1 0-1.5.75.75 0 0 1 0 1.5Z"
|
||||||
|
/>
|
||||||
|
</svg>
|
|
@ -4,11 +4,21 @@
|
||||||
import fileSaver from 'file-saver';
|
import fileSaver from 'file-saver';
|
||||||
const { saveAs } = fileSaver;
|
const { saveAs } = fileSaver;
|
||||||
|
|
||||||
|
import { Separator } from 'bits-ui';
|
||||||
import { getChatById } from '$lib/apis/chats';
|
import { getChatById } from '$lib/apis/chats';
|
||||||
import { WEBUI_NAME, chatId, modelfiles, settings } from '$lib/stores';
|
import { WEBUI_NAME, chatId, modelfiles, settings, showSettings } from '$lib/stores';
|
||||||
|
|
||||||
|
import { slide } from 'svelte/transition';
|
||||||
import ShareChatModal from '../chat/ShareChatModal.svelte';
|
import ShareChatModal from '../chat/ShareChatModal.svelte';
|
||||||
import TagInput from '../common/Tags/TagInput.svelte';
|
import TagInput from '../common/Tags/TagInput.svelte';
|
||||||
import Tags from '../common/Tags.svelte';
|
import ModelSelector from '../chat/ModelSelector.svelte';
|
||||||
|
import Tooltip from '../common/Tooltip.svelte';
|
||||||
|
|
||||||
|
import EllipsisVertical from '../icons/EllipsisVertical.svelte';
|
||||||
|
import ChevronDown from '../icons/ChevronDown.svelte';
|
||||||
|
import ChevronUpDown from '../icons/ChevronUpDown.svelte';
|
||||||
|
import Menu from './Navbar/Menu.svelte';
|
||||||
|
import TagChatModal from '../chat/TagChatModal.svelte';
|
||||||
|
|
||||||
const i18n = getContext('i18n');
|
const i18n = getContext('i18n');
|
||||||
|
|
||||||
|
@ -16,14 +26,16 @@
|
||||||
export let title: string = $WEBUI_NAME;
|
export let title: string = $WEBUI_NAME;
|
||||||
export let shareEnabled: boolean = false;
|
export let shareEnabled: boolean = false;
|
||||||
|
|
||||||
|
export let selectedModels;
|
||||||
|
|
||||||
export let tags = [];
|
export let tags = [];
|
||||||
export let addTag: Function;
|
export let addTag: Function;
|
||||||
export let deleteTag: Function;
|
export let deleteTag: Function;
|
||||||
|
|
||||||
let showShareChatModal = false;
|
export let showModelSelector = false;
|
||||||
|
|
||||||
let tagName = '';
|
let showShareChatModal = false;
|
||||||
let showTagInput = false;
|
let showTagChatModal = false;
|
||||||
|
|
||||||
const shareChat = async () => {
|
const shareChat = async () => {
|
||||||
const chat = (await getChatById(localStorage.token, $chatId)).chat;
|
const chat = (await getChatById(localStorage.token, $chatId)).chat;
|
||||||
|
@ -69,48 +81,27 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<ShareChatModal bind:show={showShareChatModal} {downloadChat} {shareChat} />
|
<ShareChatModal bind:show={showShareChatModal} {downloadChat} {shareChat} />
|
||||||
|
<!-- <TagChatModal bind:show={showTagChatModal} {tags} {deleteTag} {addTag} /> -->
|
||||||
<nav id="nav" class=" sticky py-2.5 top-0 flex flex-row justify-center z-30">
|
<nav id="nav" class=" sticky py-2.5 top-0 flex flex-row justify-center z-30">
|
||||||
<div
|
<div
|
||||||
class=" flex {$settings?.fullScreenMode ?? null
|
class=" flex {$settings?.fullScreenMode ?? null ? 'max-w-full' : 'max-w-3xl'}
|
||||||
? 'max-w-full'
|
w-full mx-auto px-3"
|
||||||
: 'max-w-3xl'} w-full mx-auto px-3"
|
|
||||||
>
|
>
|
||||||
|
<!-- {#if shareEnabled}
|
||||||
<div class="flex items-center w-full max-w-full">
|
<div class="flex items-center w-full max-w-full">
|
||||||
<div class="pr-2 self-start">
|
|
||||||
<button
|
|
||||||
id="new-chat-button"
|
|
||||||
class=" cursor-pointer p-1.5 flex dark:hover:bg-gray-700 rounded-lg transition"
|
|
||||||
on:click={initNewChat}
|
|
||||||
>
|
|
||||||
<div class=" m-auto self-center">
|
|
||||||
<svg
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
viewBox="0 0 20 20"
|
|
||||||
fill="currentColor"
|
|
||||||
class="w-5 h-5"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
d="M5.433 13.917l1.262-3.155A4 4 0 017.58 9.42l6.92-6.918a2.121 2.121 0 013 3l-6.92 6.918c-.383.383-.84.685-1.343.886l-3.154 1.262a.5.5 0 01-.65-.65z"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
d="M3.5 5.75c0-.69.56-1.25 1.25-1.25H10A.75.75 0 0010 3H4.75A2.75 2.75 0 002 5.75v9.5A2.75 2.75 0 004.75 18h9.5A2.75 2.75 0 0017 15.25V10a.75.75 0 00-1.5 0v5.25c0 .69-.56 1.25-1.25 1.25h-9.5c-.69 0-1.25-.56-1.25-1.25v-9.5z"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div class=" flex-1 self-center font-medium line-clamp-1">
|
<div class=" flex-1 self-center font-medium line-clamp-1">
|
||||||
<div>
|
<div>
|
||||||
{title != '' ? title : $WEBUI_NAME}
|
{title != '' ? title : $WEBUI_NAME}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="pl-2 self-center flex items-center">
|
||||||
<div class="pl-2 self-center flex items-center space-x-2">
|
<div class=" mr-1">
|
||||||
{#if shareEnabled}
|
|
||||||
<Tags {tags} {deleteTag} {addTag} />
|
<Tags {tags} {deleteTag} {addTag} />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Tooltip content="Share">
|
||||||
<button
|
<button
|
||||||
class=" cursor-pointer p-1.5 flex dark:hover:bg-gray-700 rounded-lg transition"
|
class="cursor-pointer p-1.5 flex dark:hover:bg-gray-700 rounded-full transition"
|
||||||
on:click={async () => {
|
on:click={async () => {
|
||||||
showShareChatModal = !showShareChatModal;
|
showShareChatModal = !showShareChatModal;
|
||||||
|
|
||||||
|
@ -132,7 +123,112 @@
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/if} -->
|
||||||
|
|
||||||
|
<!-- <div class=" flex-1 self-center font-medium line-clamp-1">
|
||||||
|
<div>
|
||||||
|
{title != '' ? title : $WEBUI_NAME}
|
||||||
|
</div>
|
||||||
|
</div> -->
|
||||||
|
|
||||||
|
<div class="flex items-center w-full max-w-full">
|
||||||
|
<div class="w-full flex-1 overflow-hidden max-w-full">
|
||||||
|
<ModelSelector bind:selectedModels />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="self-start flex flex-none items-center">
|
||||||
|
<div class="flex self-center w-[1px] h-5 mx-2 bg-stone-700" />
|
||||||
|
|
||||||
|
{#if !shareEnabled}
|
||||||
|
<Tooltip content="Settings">
|
||||||
|
<button
|
||||||
|
class="cursor-pointer p-1.5 flex dark:hover:bg-gray-700 rounded-full transition"
|
||||||
|
id="open-settings-button"
|
||||||
|
on:click={async () => {
|
||||||
|
await showSettings.set(!$showSettings);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
stroke-width="1.5"
|
||||||
|
stroke="currentColor"
|
||||||
|
class="w-5 h-5"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
d="M9.594 3.94c.09-.542.56-.94 1.11-.94h2.593c.55 0 1.02.398 1.11.94l.213 1.281c.063.374.313.686.645.87.074.04.147.083.22.127.325.196.72.257 1.075.124l1.217-.456a1.125 1.125 0 0 1 1.37.49l1.296 2.247a1.125 1.125 0 0 1-.26 1.431l-1.003.827c-.293.241-.438.613-.43.992a7.723 7.723 0 0 1 0 .255c-.008.378.137.75.43.991l1.004.827c.424.35.534.955.26 1.43l-1.298 2.247a1.125 1.125 0 0 1-1.369.491l-1.217-.456c-.355-.133-.75-.072-1.076.124a6.47 6.47 0 0 1-.22.128c-.331.183-.581.495-.644.869l-.213 1.281c-.09.543-.56.94-1.11.94h-2.594c-.55 0-1.019-.398-1.11-.94l-.213-1.281c-.062-.374-.312-.686-.644-.87a6.52 6.52 0 0 1-.22-.127c-.325-.196-.72-.257-1.076-.124l-1.217.456a1.125 1.125 0 0 1-1.369-.49l-1.297-2.247a1.125 1.125 0 0 1 .26-1.431l1.004-.827c.292-.24.437-.613.43-.991a6.932 6.932 0 0 1 0-.255c.007-.38-.138-.751-.43-.992l-1.004-.827a1.125 1.125 0 0 1-.26-1.43l1.297-2.247a1.125 1.125 0 0 1 1.37-.491l1.216.456c.356.133.751.072 1.076-.124.072-.044.146-.086.22-.128.332-.183.582-.495.644-.869l.214-1.28Z"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
d="M15 12a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</Tooltip>
|
||||||
|
{:else}
|
||||||
|
<Menu
|
||||||
|
{shareEnabled}
|
||||||
|
shareHandler={() => {
|
||||||
|
showShareChatModal = !showShareChatModal;
|
||||||
|
}}
|
||||||
|
{tags}
|
||||||
|
{deleteTag}
|
||||||
|
{addTag}
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
class="cursor-pointer p-1.5 flex dark:hover:bg-gray-700 rounded-full transition"
|
||||||
|
>
|
||||||
|
<div class=" m-auto self-center">
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
stroke-width="1.5"
|
||||||
|
stroke="currentColor"
|
||||||
|
class="size-5"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
d="M6.75 12a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0ZM12.75 12a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0ZM18.75 12a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0Z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
</Menu>
|
||||||
{/if}
|
{/if}
|
||||||
|
<Tooltip content="New Chat">
|
||||||
|
<button
|
||||||
|
id="new-chat-button"
|
||||||
|
class=" cursor-pointer p-1.5 flex dark:hover:bg-gray-700 rounded-full transition"
|
||||||
|
on:click={() => {
|
||||||
|
initNewChat();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div class=" m-auto self-center">
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
viewBox="0 0 20 20"
|
||||||
|
fill="currentColor"
|
||||||
|
class="w-5 h-5"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M5.433 13.917l1.262-3.155A4 4 0 017.58 9.42l6.92-6.918a2.121 2.121 0 013 3l-6.92 6.918c-.383.383-.84.685-1.343.886l-3.154 1.262a.5.5 0 01-.65-.65z"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M3.5 5.75c0-.69.56-1.25 1.25-1.25H10A.75.75 0 0010 3H4.75A2.75 2.75 0 002 5.75v9.5A2.75 2.75 0 004.75 18h9.5A2.75 2.75 0 0017 15.25V10a.75.75 0 00-1.5 0v5.25c0 .69-.56 1.25-1.25 1.25h-9.5c-.69 0-1.25-.56-1.25-1.25v-9.5z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
</Tooltip>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
123
src/lib/components/layout/Navbar/Menu.svelte
Normal file
123
src/lib/components/layout/Navbar/Menu.svelte
Normal file
|
@ -0,0 +1,123 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { DropdownMenu } from 'bits-ui';
|
||||||
|
import { flyAndScale } from '$lib/utils/transitions';
|
||||||
|
|
||||||
|
import Dropdown from '$lib/components/common/Dropdown.svelte';
|
||||||
|
import GarbageBin from '$lib/components/icons/GarbageBin.svelte';
|
||||||
|
import Pencil from '$lib/components/icons/Pencil.svelte';
|
||||||
|
import Tooltip from '$lib/components/common/Tooltip.svelte';
|
||||||
|
import { showSettings } from '$lib/stores';
|
||||||
|
import Tags from '$lib/components/common/Tags.svelte';
|
||||||
|
|
||||||
|
export let shareEnabled: boolean = false;
|
||||||
|
export let shareHandler: Function;
|
||||||
|
// export let tagHandler: Function;
|
||||||
|
|
||||||
|
export let tags;
|
||||||
|
export let deleteTag: Function;
|
||||||
|
export let addTag: Function;
|
||||||
|
|
||||||
|
export let onClose: Function = () => {};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Dropdown
|
||||||
|
on:change={(e) => {
|
||||||
|
if (e.detail === false) {
|
||||||
|
onClose();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
|
||||||
|
<div slot="content">
|
||||||
|
<DropdownMenu.Content
|
||||||
|
class="w-full max-w-[150px] rounded-lg px-1 py-1.5 border border-gray-300/30 dark:border-gray-700/50 z-50 bg-white dark:bg-gray-850 dark:text-white shadow"
|
||||||
|
sideOffset={8}
|
||||||
|
side="bottom"
|
||||||
|
align="end"
|
||||||
|
transition={flyAndScale}
|
||||||
|
>
|
||||||
|
<DropdownMenu.Item
|
||||||
|
class="flex gap-2 items-center px-3 py-2 text-sm font-medium cursor-pointer"
|
||||||
|
on:click={async () => {
|
||||||
|
await showSettings.set(!$showSettings);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
stroke-width="1.5"
|
||||||
|
stroke="currentColor"
|
||||||
|
class="w-5 h-5"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
d="M9.594 3.94c.09-.542.56-.94 1.11-.94h2.593c.55 0 1.02.398 1.11.94l.213 1.281c.063.374.313.686.645.87.074.04.147.083.22.127.325.196.72.257 1.075.124l1.217-.456a1.125 1.125 0 0 1 1.37.49l1.296 2.247a1.125 1.125 0 0 1-.26 1.431l-1.003.827c-.293.241-.438.613-.43.992a7.723 7.723 0 0 1 0 .255c-.008.378.137.75.43.991l1.004.827c.424.35.534.955.26 1.43l-1.298 2.247a1.125 1.125 0 0 1-1.369.491l-1.217-.456c-.355-.133-.75-.072-1.076.124a6.47 6.47 0 0 1-.22.128c-.331.183-.581.495-.644.869l-.213 1.281c-.09.543-.56.94-1.11.94h-2.594c-.55 0-1.019-.398-1.11-.94l-.213-1.281c-.062-.374-.312-.686-.644-.87a6.52 6.52 0 0 1-.22-.127c-.325-.196-.72-.257-1.076-.124l-1.217.456a1.125 1.125 0 0 1-1.369-.49l-1.297-2.247a1.125 1.125 0 0 1 .26-1.431l1.004-.827c.292-.24.437-.613.43-.991a6.932 6.932 0 0 1 0-.255c.007-.38-.138-.751-.43-.992l-1.004-.827a1.125 1.125 0 0 1-.26-1.43l1.297-2.247a1.125 1.125 0 0 1 1.37-.491l1.216.456c.356.133.751.072 1.076-.124.072-.044.146-.086.22-.128.332-.183.582-.495.644-.869l.214-1.28Z"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
d="M15 12a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
<div class="flex items-center">Settings</div>
|
||||||
|
</DropdownMenu.Item>
|
||||||
|
|
||||||
|
{#if shareEnabled}
|
||||||
|
<DropdownMenu.Item
|
||||||
|
class="flex gap-2 items-center px-3 py-2 text-sm font-medium cursor-pointer"
|
||||||
|
on:click={() => {
|
||||||
|
shareHandler();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="currentColor"
|
||||||
|
class="size-4"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
fill-rule="evenodd"
|
||||||
|
d="M15.75 4.5a3 3 0 1 1 .825 2.066l-8.421 4.679a3.002 3.002 0 0 1 0 1.51l8.421 4.679a3 3 0 1 1-.729 1.31l-8.421-4.678a3 3 0 1 1 0-4.132l8.421-4.679a3 3 0 0 1-.096-.755Z"
|
||||||
|
clip-rule="evenodd"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
<div class="flex items-center">Share</div>
|
||||||
|
</DropdownMenu.Item>
|
||||||
|
|
||||||
|
<hr class="border-gray-100 dark:border-gray-800 my-1" />
|
||||||
|
|
||||||
|
<div class="flex p-1">
|
||||||
|
<Tags {tags} {deleteTag} {addTag} />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- <DropdownMenu.Item
|
||||||
|
class="flex gap-2 items-center px-3 py-2 text-sm font-medium cursor-pointer"
|
||||||
|
on:click={() => {
|
||||||
|
tagHandler();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
stroke-width="2"
|
||||||
|
stroke="currentColor"
|
||||||
|
class="size-4"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
d="M9.568 3H5.25A2.25 2.25 0 0 0 3 5.25v4.318c0 .597.237 1.17.659 1.591l9.581 9.581c.699.699 1.78.872 2.607.33a18.095 18.095 0 0 0 5.223-5.223c.542-.827.369-1.908-.33-2.607L11.16 3.66A2.25 2.25 0 0 0 9.568 3Z"
|
||||||
|
/>
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" d="M6 6h.008v.008H6V6Z" />
|
||||||
|
</svg>
|
||||||
|
|
||||||
|
<div class="flex items-center">Tag</div>
|
||||||
|
</DropdownMenu.Item> -->
|
||||||
|
{/if}
|
||||||
|
</DropdownMenu.Content>
|
||||||
|
</div>
|
||||||
|
</Dropdown>
|
|
@ -107,7 +107,7 @@
|
||||||
bind:this={navElement}
|
bind:this={navElement}
|
||||||
class="h-screen max-h-[100dvh] min-h-screen {show
|
class="h-screen max-h-[100dvh] min-h-screen {show
|
||||||
? 'lg:relative w-[260px]'
|
? 'lg:relative w-[260px]'
|
||||||
: '-translate-x-[260px] w-[0px]'} bg-gray-50 text-gray-900 dark:bg-gray-950 dark:text-gray-200 text-sm transition z-40 fixed top-0 left-0
|
: '-translate-x-[260px] w-[0px]'} bg-gray-50 text-gray-900 dark:bg-gray-950 dark:text-gray-200 text-sm transition fixed z-50 top-0 left-0
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
@ -706,7 +706,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class="fixed left-0 top-[50dvh] z-40 -translate-y-1/2 transition-transform translate-x-[255px] md:translate-x-[260px] rotate-0"
|
class="fixed left-0 top-[50dvh] -translate-y-1/2 transition-transform translate-x-[255px] md:translate-x-[260px] rotate-0"
|
||||||
>
|
>
|
||||||
<Tooltip
|
<Tooltip
|
||||||
placement="right"
|
placement="right"
|
||||||
|
|
|
@ -37,23 +37,28 @@ const createIsLoadingStore = (i18n: i18nType) => {
|
||||||
return isLoading;
|
return isLoading;
|
||||||
};
|
};
|
||||||
|
|
||||||
i18next
|
export const initI18n = (defaultLocale: string) => {
|
||||||
.use(
|
let detectionOrder = defaultLocale
|
||||||
resourcesToBackend(
|
? ['querystring', 'localStorage']
|
||||||
(language: string, namespace: string) => import(`./locales/${language}/${namespace}.json`)
|
: ['querystring', 'localStorage', 'navigator'];
|
||||||
)
|
let fallbackDefaultLocale = defaultLocale ? [defaultLocale] : ['en-US'];
|
||||||
)
|
|
||||||
|
const loadResource = (language: string, namespace: string) =>
|
||||||
|
import(`./locales/${language}/${namespace}.json`);
|
||||||
|
|
||||||
|
i18next
|
||||||
|
.use(resourcesToBackend(loadResource))
|
||||||
.use(LanguageDetector)
|
.use(LanguageDetector)
|
||||||
.init({
|
.init({
|
||||||
debug: false,
|
debug: false,
|
||||||
detection: {
|
detection: {
|
||||||
order: ['querystring', 'localStorage', 'navigator'],
|
order: detectionOrder,
|
||||||
caches: ['localStorage'],
|
caches: ['localStorage'],
|
||||||
lookupQuerystring: 'lang',
|
lookupQuerystring: 'lang',
|
||||||
lookupLocalStorage: 'locale'
|
lookupLocalStorage: 'locale'
|
||||||
},
|
},
|
||||||
fallbackLng: {
|
fallbackLng: {
|
||||||
default: ['en-US']
|
default: fallbackDefaultLocale
|
||||||
},
|
},
|
||||||
ns: 'translation',
|
ns: 'translation',
|
||||||
returnEmptyString: false,
|
returnEmptyString: false,
|
||||||
|
@ -61,6 +66,7 @@ i18next
|
||||||
escapeValue: false // not needed for svelte as it escapes by default
|
escapeValue: false // not needed for svelte as it escapes by default
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
};
|
||||||
|
|
||||||
const i18n = createI18nStore(i18next);
|
const i18n = createI18nStore(i18next);
|
||||||
const isLoadingStore = createIsLoadingStore(i18next);
|
const isLoadingStore = createIsLoadingStore(i18next);
|
||||||
|
|
|
@ -1,128 +1,128 @@
|
||||||
{
|
{
|
||||||
"'s', 'm', 'h', 'd', 'w' or '-1' for no expiration.": "'s', 'm', 'h', 'd', 'w' or '-1' for no expiration.",
|
"'s', 'm', 'h', 'd', 'w' or '-1' for no expiration.": "",
|
||||||
"(Beta)": "(Beta)",
|
"(Beta)": "",
|
||||||
"(e.g. `sh webui.sh --api`)": "(e.g. `sh webui.sh --api`)",
|
"(e.g. `sh webui.sh --api`)": "",
|
||||||
"(latest)": "(latest)",
|
"(latest)": "",
|
||||||
"{{modelName}} is thinking...": "{{modelName}} is thinking...",
|
"{{modelName}} is thinking...": "",
|
||||||
"{{webUIName}} Backend Required": "{{webUIName}} Backend Required",
|
"{{webUIName}} Backend Required": "",
|
||||||
"a user": "",
|
"a user": "",
|
||||||
"About": "About",
|
"About": "",
|
||||||
"Account": "Account",
|
"Account": "",
|
||||||
"Action": "Action",
|
"Action": "",
|
||||||
"Add a model": "Add a model",
|
"Add a model": "",
|
||||||
"Add a model tag name": "Add a model tag name",
|
"Add a model tag name": "",
|
||||||
"Add a short description about what this modelfile does": "Add a short description about what this modelfile does",
|
"Add a short description about what this modelfile does": "",
|
||||||
"Add a short title for this prompt": "Add a short title for this prompt",
|
"Add a short title for this prompt": "",
|
||||||
"Add a tag": "Add a tag",
|
"Add a tag": "",
|
||||||
"Add Docs": "Add Docs",
|
"Add Docs": "",
|
||||||
"Add Files": "Add Files",
|
"Add Files": "",
|
||||||
"Add message": "Add message",
|
"Add message": "",
|
||||||
"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.": "",
|
||||||
"admin": "admin",
|
"admin": "",
|
||||||
"Admin Panel": "Admin Panel",
|
"Admin Panel": "",
|
||||||
"Admin Settings": "Admin Settings",
|
"Admin Settings": "",
|
||||||
"Advanced Parameters": "Advanced Parameters",
|
"Advanced Parameters": "",
|
||||||
"all": "all",
|
"all": "",
|
||||||
"All Users": "All Users",
|
"All Users": "",
|
||||||
"Allow": "Allow",
|
"Allow": "",
|
||||||
"Allow Chat Deletion": "Allow Chat Deletion",
|
"Allow Chat Deletion": "",
|
||||||
"alphanumeric characters and hyphens": "alphanumeric characters and hyphens",
|
"alphanumeric characters and hyphens": "",
|
||||||
"Already have an account?": "Already have an account?",
|
"Already have an account?": "",
|
||||||
"an assistant": "",
|
"an assistant": "",
|
||||||
"and": "and",
|
"and": "",
|
||||||
"API Base URL": "API Base URL",
|
"API Base URL": "",
|
||||||
"API Key": "API Key",
|
"API Key": "",
|
||||||
"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 you sure?": "",
|
"Are you sure?": "",
|
||||||
"Audio": "Audio",
|
"Audio": "",
|
||||||
"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.": "",
|
||||||
"AUTOMATIC1111 Base URL": "AUTOMATIC1111 Base URL",
|
"AUTOMATIC1111 Base URL": "",
|
||||||
"AUTOMATIC1111 Base URL is required.": "",
|
"AUTOMATIC1111 Base URL is required.": "",
|
||||||
"available!": "available!",
|
"available!": "",
|
||||||
"Back": "Back",
|
"Back": "",
|
||||||
"Builder Mode": "Builder Mode",
|
"Builder Mode": "",
|
||||||
"Cancel": "Cancel",
|
"Cancel": "",
|
||||||
"Categories": "Categories",
|
"Categories": "",
|
||||||
"Change Password": "Change Password",
|
"Change Password": "",
|
||||||
"Chat": "Chat",
|
"Chat": "",
|
||||||
"Chat History": "Chat History",
|
"Chat History": "",
|
||||||
"Chat History is off for this browser.": "Chat History is off for this browser.",
|
"Chat History is off for this browser.": "",
|
||||||
"Chats": "Chats",
|
"Chats": "",
|
||||||
"Check Again": "Check Again",
|
"Check Again": "",
|
||||||
"Check for updates": "Check for updates",
|
"Check for updates": "",
|
||||||
"Checking for updates...": "Checking for updates...",
|
"Checking for updates...": "",
|
||||||
"Choose a model before saving...": "Choose a model before saving...",
|
"Choose a model before saving...": "",
|
||||||
"Chunk Overlap": "Chunk Overlap",
|
"Chunk Overlap": "",
|
||||||
"Chunk Params": "Chunk Params",
|
"Chunk Params": "",
|
||||||
"Chunk Size": "Chunk Size",
|
"Chunk Size": "",
|
||||||
"Click here for help.": "Click here for help.",
|
"Click here for help.": "",
|
||||||
"Click here to check other modelfiles.": "Click here to check other modelfiles.",
|
"Click here to check other modelfiles.": "",
|
||||||
"Click here to select": "",
|
"Click here to select": "",
|
||||||
"Click here to select documents.": "",
|
"Click here to select documents.": "",
|
||||||
"click here.": "click here.",
|
"click here.": "",
|
||||||
"Click on the user role button to change a user's role.": "Click on the user role button to change a user's role.",
|
"Click on the user role button to change a user's role.": "",
|
||||||
"Close": "Close",
|
"Close": "",
|
||||||
"Collection": "Collection",
|
"Collection": "",
|
||||||
"Command": "Command",
|
"Command": "",
|
||||||
"Confirm Password": "Confirm Password",
|
"Confirm Password": "",
|
||||||
"Connections": "Connections",
|
"Connections": "",
|
||||||
"Content": "Content",
|
"Content": "",
|
||||||
"Context Length": "Context Length",
|
"Context Length": "",
|
||||||
"Conversation Mode": "Conversation Mode",
|
"Conversation Mode": "",
|
||||||
"Copy last code block": "Copy last code block",
|
"Copy last code block": "",
|
||||||
"Copy last response": "Copy last response",
|
"Copy last response": "",
|
||||||
"Copying to clipboard was successful!": "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':": "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 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 a modelfile": "",
|
||||||
"Create Account": "Create Account",
|
"Create Account": "",
|
||||||
"Created at": "Created at",
|
"Created at": "",
|
||||||
"Created by": "Created by",
|
"Created by": "",
|
||||||
"Current Model": "Current Model",
|
"Current Model": "",
|
||||||
"Current Password": "Current Password",
|
"Current Password": "",
|
||||||
"Custom": "Custom",
|
"Custom": "",
|
||||||
"Customize Ollama models for a specific purpose": "Customize Ollama models for a specific purpose",
|
"Customize Ollama models for a specific purpose": "",
|
||||||
"Dark": "Dark",
|
"Dark": "",
|
||||||
"Database": "Database",
|
"Database": "",
|
||||||
"DD/MM/YYYY HH:mm": "DD/MM/YYYY HH:mm",
|
"DD/MM/YYYY HH:mm": "",
|
||||||
"Default": "Default",
|
"Default": "",
|
||||||
"Default (Automatic1111)": "",
|
"Default (Automatic1111)": "",
|
||||||
"Default (Web API)": "Default (Web API)",
|
"Default (Web API)": "",
|
||||||
"Default model updated": "Default model updated",
|
"Default model updated": "",
|
||||||
"Default Prompt Suggestions": "Default Prompt Suggestions",
|
"Default Prompt Suggestions": "",
|
||||||
"Default User Role": "Default User Role",
|
"Default User Role": "",
|
||||||
"delete": "delete",
|
"delete": "",
|
||||||
"Delete a model": "Delete a model",
|
"Delete a model": "",
|
||||||
"Delete chat": "Delete chat",
|
"Delete chat": "",
|
||||||
"Delete Chats": "Delete Chats",
|
"Delete Chats": "",
|
||||||
"Deleted {{deleteModelTag}}": "Deleted {{deleteModelTag}}",
|
"Deleted {{deleteModelTag}}": "",
|
||||||
"Deleted {tagName}": "Deleted {tagName}",
|
"Deleted {tagName}": "",
|
||||||
"Description": "Description",
|
"Description": "",
|
||||||
"Desktop Notifications": "Notification",
|
"Desktop Notifications": "",
|
||||||
"Disabled": "Disabled",
|
"Disabled": "",
|
||||||
"Discover a modelfile": "Discover a modelfile",
|
"Discover a modelfile": "",
|
||||||
"Discover a prompt": "Discover a prompt",
|
"Discover a prompt": "",
|
||||||
"Discover, download, and explore custom prompts": "Discover, download, and explore custom prompts",
|
"Discover, download, and explore custom prompts": "",
|
||||||
"Discover, download, and explore model presets": "Discover, download, and explore model presets",
|
"Discover, download, and explore model presets": "",
|
||||||
"Display the username instead of You in the Chat": "Display the username instead of 'You' in the Chat",
|
"Display the username instead of You in the Chat": "",
|
||||||
"Document": "Document",
|
"Document": "",
|
||||||
"Document Settings": "Document Settings",
|
"Document Settings": "",
|
||||||
"Documents": "Documents",
|
"Documents": "",
|
||||||
"does not make any external connections, and your data stays securely on your locally hosted server.": "does not make any external connections, and your data stays securely on your locally hosted server.",
|
"does not make any external connections, and your data stays securely on your locally hosted server.": "",
|
||||||
"Don't Allow": "Don't Allow",
|
"Don't Allow": "",
|
||||||
"Don't have an account?": "Don't have an account?",
|
"Don't have an account?": "",
|
||||||
"Download as a File": "Download as a File",
|
"Download as a File": "",
|
||||||
"Download Database": "Download Database",
|
"Download Database": "",
|
||||||
"Drop any files here to add to the conversation": "Drop any files here to add to the conversation",
|
"Drop any files here to add to the conversation": "",
|
||||||
"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.",
|
"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "",
|
||||||
"Edit Doc": "Edit Doc",
|
"Edit Doc": "",
|
||||||
"Edit User": "Edit User",
|
"Edit User": "",
|
||||||
"Email": "Email",
|
"Email": "",
|
||||||
"Enable Chat History": "Enable Chat History",
|
"Enable Chat History": "",
|
||||||
"Enable New Sign Ups": "Enable New Sign Ups",
|
"Enable New Sign Ups": "",
|
||||||
"Enabled": "Enabled",
|
"Enabled": "",
|
||||||
"Enter {{role}} message here": "",
|
"Enter {{role}} message here": "",
|
||||||
"Enter API Key": "",
|
"Enter API Key": "",
|
||||||
"Enter Chunk Overlap": "",
|
"Enter Chunk Overlap": "",
|
||||||
|
@ -135,229 +135,229 @@
|
||||||
"Enter Max Tokens (litellm_params.max_tokens)": "",
|
"Enter Max Tokens (litellm_params.max_tokens)": "",
|
||||||
"Enter model tag (e.g. {{modelTag}})": "",
|
"Enter model tag (e.g. {{modelTag}})": "",
|
||||||
"Enter Number of Steps (e.g. 50)": "",
|
"Enter Number of Steps (e.g. 50)": "",
|
||||||
"Enter stop sequence": "Enter stop sequence",
|
"Enter stop sequence": "",
|
||||||
"Enter Top K": "",
|
"Enter Top K": "",
|
||||||
"Enter URL (e.g. http://127.0.0.1:7860/)": "",
|
"Enter URL (e.g. http://127.0.0.1:7860/)": "",
|
||||||
"Enter Your Email": "Enter Your Email",
|
"Enter Your Email": "",
|
||||||
"Enter Your Full Name": "Enter Your Full Name",
|
"Enter Your Full Name": "",
|
||||||
"Enter Your Password": "Enter Your Password",
|
"Enter Your Password": "",
|
||||||
"Experimental": "Experimental",
|
"Experimental": "",
|
||||||
"Export All Chats (All Users)": "Export All Chats (All Users)",
|
"Export All Chats (All Users)": "",
|
||||||
"Export Chats": "Export Chats",
|
"Export Chats": "",
|
||||||
"Export Documents Mapping": "Export Documents Mapping",
|
"Export Documents Mapping": "",
|
||||||
"Export Modelfiles": "Export Modelfiles",
|
"Export Modelfiles": "",
|
||||||
"Export Prompts": "Export Prompts",
|
"Export Prompts": "",
|
||||||
"Failed to read clipboard contents": "Failed to read clipboard contents",
|
"Failed to read clipboard contents": "",
|
||||||
"File Mode": "File Mode",
|
"File Mode": "",
|
||||||
"File not found.": "File not found.",
|
"File not found.": "",
|
||||||
"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:": "",
|
||||||
"From (Base Model)": "From (Base Model)",
|
"From (Base Model)": "",
|
||||||
"Full Screen Mode": "Full Screen Mode",
|
"Full Screen Mode": "",
|
||||||
"General": "General",
|
"General": "",
|
||||||
"General Settings": "General Settings",
|
"General Settings": "",
|
||||||
"Hello, {{name}}": "Hello, {{name}}",
|
"Hello, {{name}}": "",
|
||||||
"Hide": "Hide",
|
"Hide": "",
|
||||||
"Hide Additional Params": "Hide Additional Params",
|
"Hide Additional Params": "",
|
||||||
"How can I help you today?": "How can I help you today?",
|
"How can I help you today?": "",
|
||||||
"Image Generation (Experimental)": "Image Generation (Experimental)",
|
"Image Generation (Experimental)": "",
|
||||||
"Image Generation Engine": "",
|
"Image Generation Engine": "",
|
||||||
"Image Settings": "Image Settings",
|
"Image Settings": "",
|
||||||
"Images": "Images",
|
"Images": "",
|
||||||
"Import Chats": "Import Chats",
|
"Import Chats": "",
|
||||||
"Import Documents Mapping": "Import Documents Mapping",
|
"Import Documents Mapping": "",
|
||||||
"Import Modelfiles": "Import Modelfiles",
|
"Import Modelfiles": "",
|
||||||
"Import Prompts": "Import Prompts",
|
"Import Prompts": "",
|
||||||
"Include `--api` flag when running stable-diffusion-webui": "Include `--api` flag when running stable-diffusion-webui",
|
"Include `--api` flag when running stable-diffusion-webui": "",
|
||||||
"Interface": "Interface",
|
"Interface": "",
|
||||||
"join our Discord for help.": "join our Discord for help.",
|
"join our Discord for help.": "",
|
||||||
"JSON": "JSON",
|
"JSON": "",
|
||||||
"JWT Expiration": "JWT Expiration",
|
"JWT Expiration": "",
|
||||||
"JWT Token": "JWT Token",
|
"JWT Token": "",
|
||||||
"Keep Alive": "Keep Alive",
|
"Keep Alive": "",
|
||||||
"Keyboard shortcuts": "Keyboard shortcuts",
|
"Keyboard shortcuts": "",
|
||||||
"Language": "Language",
|
"Language": "",
|
||||||
"Light": "Light",
|
"Light": "",
|
||||||
"Listening...": "Listening...",
|
"Listening...": "",
|
||||||
"LLMs can make mistakes. Verify important information.": "LLMs can make mistakes. Verify important information.",
|
"LLMs can make mistakes. Verify important information.": "",
|
||||||
"Made by OpenWebUI Community": "Made by OpenWebUI Community",
|
"Made by OpenWebUI Community": "",
|
||||||
"Make sure to enclose them with": "Make sure to enclose them with",
|
"Make sure to enclose them with": "",
|
||||||
"Manage LiteLLM Models": "Manage LiteLLM Models",
|
"Manage LiteLLM Models": "",
|
||||||
"Manage Models": "Manage Models",
|
"Manage Models": "",
|
||||||
"Manage Ollama Models": "Manage Ollama Models",
|
"Manage Ollama Models": "",
|
||||||
"Max Tokens": "Max Tokens",
|
"Max Tokens": "",
|
||||||
"Maximum of 3 models can be downloaded simultaneously. Please try again later.": "Maximum of 3 models can be downloaded simultaneously. Please try again later.",
|
"Maximum of 3 models can be downloaded simultaneously. Please try again later.": "",
|
||||||
"Mirostat": "Mirostat",
|
"Mirostat": "",
|
||||||
"Mirostat Eta": "Mirostat Eta",
|
"Mirostat Eta": "",
|
||||||
"Mirostat Tau": "Mirostat Tau",
|
"Mirostat Tau": "",
|
||||||
"MMMM DD, YYYY": "MMMM DD, YYYY",
|
"MMMM DD, YYYY": "",
|
||||||
"Model '{{modelName}}' has been successfully downloaded.": "Model '{{modelName}}' has been successfully downloaded.",
|
"Model '{{modelName}}' has been successfully downloaded.": "",
|
||||||
"Model '{{modelTag}}' is already in queue for downloading.": "Model '{{modelTag}}' is already in queue for downloading.",
|
"Model '{{modelTag}}' is already in queue for downloading.": "",
|
||||||
"Model {{modelId}} not found": "Model {{modelId}} not found",
|
"Model {{modelId}} not found": "",
|
||||||
"Model {{modelName}} already exists.": "Model {{modelName}} already exists.",
|
"Model {{modelName}} already exists.": "",
|
||||||
"Model Name": "Model Name",
|
"Model Name": "",
|
||||||
"Model not selected": "Model not selected",
|
"Model not selected": "",
|
||||||
"Model Tag Name": "Model Tag Name",
|
"Model Tag Name": "",
|
||||||
"Model Whitelisting": "Model Whitelisting",
|
"Model Whitelisting": "",
|
||||||
"Model(s) Whitelisted": "Model(s) Whitelisted",
|
"Model(s) Whitelisted": "",
|
||||||
"Modelfile": "Modelfile",
|
"Modelfile": "",
|
||||||
"Modelfile Advanced Settings": "Modelfile Advanced Settings",
|
"Modelfile Advanced Settings": "",
|
||||||
"Modelfile Content": "Modelfile Content",
|
"Modelfile Content": "",
|
||||||
"Modelfiles": "Modelfiles",
|
"Modelfiles": "",
|
||||||
"Models": "Models",
|
"Models": "",
|
||||||
"My Documents": "My Documents",
|
"My Documents": "",
|
||||||
"My Modelfiles": "My Modelfiles",
|
"My Modelfiles": "",
|
||||||
"My Prompts": "My Prompts",
|
"My Prompts": "",
|
||||||
"Name": "Name",
|
"Name": "",
|
||||||
"Name Tag": "Name Tag",
|
"Name Tag": "",
|
||||||
"Name your modelfile": "Name your modelfile",
|
"Name your modelfile": "",
|
||||||
"New Chat": "New Chat",
|
"New Chat": "",
|
||||||
"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 write? Switch to": "Not sure what to write? Switch to",
|
"Not sure what to write? Switch to": "",
|
||||||
"Off": "Off",
|
"Off": "",
|
||||||
"Okay, Let's Go!": "Okay, Let's Go!",
|
"Okay, Let's Go!": "",
|
||||||
"Ollama Base URL": "",
|
"Ollama Base URL": "",
|
||||||
"Ollama Version": "Ollama Version",
|
"Ollama Version": "",
|
||||||
"On": "On",
|
"On": "",
|
||||||
"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.": "",
|
||||||
"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! Looks like the URL is invalid. Please double-check and try again.",
|
"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.": "Oops! You're using an unsupported method (frontend only). Please serve the WebUI from the backend.",
|
"Oops! You're using an unsupported method (frontend only). Please serve the WebUI from the backend.": "",
|
||||||
"Open": "Open",
|
"Open": "",
|
||||||
"Open AI": "Open AI",
|
"Open AI": "",
|
||||||
"Open AI (Dall-E)": "",
|
"Open AI (Dall-E)": "",
|
||||||
"Open new chat": "Open new chat",
|
"Open new chat": "",
|
||||||
"OpenAI API": "OpenAI API",
|
"OpenAI API": "",
|
||||||
"OpenAI API Key": "",
|
"OpenAI API Key": "",
|
||||||
"OpenAI API Key is required.": "",
|
"OpenAI API Key is required.": "",
|
||||||
"or": "or",
|
"or": "",
|
||||||
"Parameters": "Parameters",
|
"Parameters": "",
|
||||||
"Password": "Password",
|
"Password": "",
|
||||||
"PDF Extract Images (OCR)": "PDF Extract Images (OCR)",
|
"PDF Extract Images (OCR)": "",
|
||||||
"pending": "pending",
|
"pending": "",
|
||||||
"Permission denied when accessing microphone: {{error}}": "Permission denied when accessing microphone: {{error}}",
|
"Permission denied when accessing microphone: {{error}}": "",
|
||||||
"Playground": "Playground",
|
"Playground": "",
|
||||||
"Profile": "Profile",
|
"Profile": "",
|
||||||
"Prompt Content": "Prompt Content",
|
"Prompt Content": "",
|
||||||
"Prompt suggestions": "Prompt suggestions",
|
"Prompt suggestions": "",
|
||||||
"Prompts": "Prompts",
|
"Prompts": "",
|
||||||
"Pull a model from Ollama.com": "Pull a model from Ollama.com",
|
"Pull a model from Ollama.com": "",
|
||||||
"Pull Progress": "Pull Progress",
|
"Pull Progress": "",
|
||||||
"Query Params": "Query Params",
|
"Query Params": "",
|
||||||
"RAG Template": "RAG Template",
|
"RAG Template": "",
|
||||||
"Raw Format": "Raw Format",
|
"Raw Format": "",
|
||||||
"Record voice": "Record voice",
|
"Record voice": "",
|
||||||
"Redirecting you to OpenWebUI Community": "Redirecting you to OpenWebUI Community",
|
"Redirecting you to OpenWebUI Community": "",
|
||||||
"Release Notes": "Release Notes",
|
"Release Notes": "",
|
||||||
"Repeat Last N": "Repeat Last N",
|
"Repeat Last N": "",
|
||||||
"Repeat Penalty": "Repeat Penalty",
|
"Repeat Penalty": "",
|
||||||
"Request Mode": "Request Mode",
|
"Request Mode": "",
|
||||||
"Reset Vector Storage": "Reset Vector Storage",
|
"Reset Vector Storage": "",
|
||||||
"Response AutoCopy to Clipboard": "Response AutoCopy to Clipboard",
|
"Response AutoCopy to Clipboard": "",
|
||||||
"Role": "Role",
|
"Role": "",
|
||||||
"Rosé Pine": "Rosé Pine",
|
"Rosé Pine": "",
|
||||||
"Rosé Pine Dawn": "Rosé Pine Dawn",
|
"Rosé Pine Dawn": "",
|
||||||
"Save": "Save",
|
"Save": "",
|
||||||
"Save & Create": "Save & Create",
|
"Save & Create": "",
|
||||||
"Save & Submit": "Save & Submit",
|
"Save & Submit": "",
|
||||||
"Save & Update": "Save & Update",
|
"Save & Update": "",
|
||||||
"Saving chat logs directly to your browser's storage is no longer supported. Please take a moment to download and delete your chat logs by clicking the button below. Don't worry, you can easily re-import your chat logs to the backend through": "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": "",
|
||||||
"Scan": "Scan",
|
"Scan": "",
|
||||||
"Scan complete!": "Scan complete!",
|
"Scan complete!": "",
|
||||||
"Scan for documents from {{path}}": "Scan for documents from {{path}}",
|
"Scan for documents from {{path}}": "",
|
||||||
"Search": "Search",
|
"Search": "",
|
||||||
"Search Documents": "Search Documents",
|
"Search Documents": "",
|
||||||
"Search Prompts": "Search Prompts",
|
"Search Prompts": "",
|
||||||
"See readme.md for instructions": "See readme.md for instructions",
|
"See readme.md for instructions": "",
|
||||||
"See what's new": "See what's new",
|
"See what's new": "",
|
||||||
"Seed": "Seed",
|
"Seed": "",
|
||||||
"Select a mode": "",
|
"Select a mode": "",
|
||||||
"Select a model": "Select a model",
|
"Select a model": "",
|
||||||
"Select an Ollama instance": "",
|
"Select an Ollama instance": "",
|
||||||
"Send a Message": "Send a Message",
|
"Send a Message": "",
|
||||||
"Send message": "Send message",
|
"Send message": "",
|
||||||
"Server connection verified": "Server connection verified",
|
"Server connection verified": "",
|
||||||
"Set as default": "Set as default",
|
"Set as default": "",
|
||||||
"Set Default Model": "Set Default Model",
|
"Set Default Model": "",
|
||||||
"Set Image Size": "Set Image Size",
|
"Set Image Size": "",
|
||||||
"Set Steps": "Set Steps",
|
"Set Steps": "",
|
||||||
"Set Title Auto-Generation Model": "Set Title Auto-Generation Model",
|
"Set Title Auto-Generation Model": "",
|
||||||
"Set Voice": "Set Voice",
|
"Set Voice": "",
|
||||||
"Settings": "Settings",
|
"Settings": "",
|
||||||
"Settings saved successfully!": "Settings saved successfully!",
|
"Settings saved successfully!": "",
|
||||||
"Share to OpenWebUI Community": "Share to OpenWebUI Community",
|
"Share to OpenWebUI Community": "",
|
||||||
"short-summary": "short-summary",
|
"short-summary": "",
|
||||||
"Show": "Show",
|
"Show": "",
|
||||||
"Show Additional Params": "Show Additional Params",
|
"Show Additional Params": "",
|
||||||
"Show shortcuts": "Show shortcuts",
|
"Show shortcuts": "",
|
||||||
"sidebar": "sidebar",
|
"sidebar": "",
|
||||||
"Sign in": "Sign in",
|
"Sign in": "",
|
||||||
"Sign Out": "Sign Out",
|
"Sign Out": "",
|
||||||
"Sign up": "Sign up",
|
"Sign up": "",
|
||||||
"Speech recognition error: {{error}}": "Speech recognition error: {{error}}",
|
"Speech recognition error: {{error}}": "",
|
||||||
"Speech-to-Text Engine": "Speech-to-Text Engine",
|
"Speech-to-Text Engine": "",
|
||||||
"SpeechRecognition API is not supported in this browser.": "SpeechRecognition API is not supported in this browser.",
|
"SpeechRecognition API is not supported in this browser.": "",
|
||||||
"Stop Sequence": "Stop Sequence",
|
"Stop Sequence": "",
|
||||||
"STT Settings": "STT Settings",
|
"STT Settings": "",
|
||||||
"Submit": "Submit",
|
"Submit": "",
|
||||||
"Success": "Success",
|
"Success": "",
|
||||||
"Successfully updated.": "Successfully updated.",
|
"Successfully updated.": "",
|
||||||
"Sync All": "Sync All",
|
"Sync All": "",
|
||||||
"System": "System",
|
"System": "",
|
||||||
"System Prompt": "System Prompt",
|
"System Prompt": "",
|
||||||
"Tags": "Tags",
|
"Tags": "",
|
||||||
"Temperature": "Temperature",
|
"Temperature": "",
|
||||||
"Template": "Template",
|
"Template": "",
|
||||||
"Text Completion": "Text Completion",
|
"Text Completion": "",
|
||||||
"Text-to-Speech Engine": "Text-to-Speech Engine",
|
"Text-to-Speech Engine": "",
|
||||||
"Tfs Z": "Tfs Z",
|
"Tfs Z": "",
|
||||||
"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 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.": "Tip: Update multiple variable slots consecutively by pressing the tab key in the chat input after each replacement.",
|
"Tip: Update multiple variable slots consecutively by pressing the tab key in the chat input after each replacement.": "",
|
||||||
"Title": "Title",
|
"Title": "",
|
||||||
"Title Auto-Generation": "Title Auto-Generation",
|
"Title Auto-Generation": "",
|
||||||
"Title Generation Prompt": "Title Generation Prompt",
|
"Title Generation Prompt": "",
|
||||||
"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 GGUF models available for downloading,": "To access the GGUF models available for downloading,",
|
"To access the GGUF models available for downloading,": "",
|
||||||
"to chat input.": "to chat input.",
|
"to chat input.": "",
|
||||||
"Toggle settings": "Toggle settings",
|
"Toggle settings": "",
|
||||||
"Toggle sidebar": "Toggle sidebar",
|
"Toggle sidebar": "",
|
||||||
"Top K": "Top K",
|
"Top K": "",
|
||||||
"Top P": "Top P",
|
"Top P": "",
|
||||||
"Trouble accessing Ollama?": "Trouble accessing Ollama?",
|
"Trouble accessing Ollama?": "",
|
||||||
"TTS Settings": "TTS Settings",
|
"TTS Settings": "",
|
||||||
"Type Hugging Face Resolve (Download) URL": "",
|
"Type Hugging Face Resolve (Download) URL": "",
|
||||||
"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}}.": "",
|
||||||
"Unknown File Type '{{file_type}}', but accepting and treating as plain text": "Unknown File Type '{{file_type}}', but accepting and treating as plain text",
|
"Unknown File Type '{{file_type}}', but accepting and treating as plain text": "",
|
||||||
"Update password": "Update password",
|
"Update password": "",
|
||||||
"Upload a GGUF model": "Upload a GGUF model",
|
"Upload a GGUF model": "",
|
||||||
"Upload files": "Upload files",
|
"Upload files": "",
|
||||||
"Upload Progress": "Upload Progress",
|
"Upload Progress": "",
|
||||||
"URL Mode": "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 Gravatar": "",
|
"Use Gravatar": "",
|
||||||
"user": "user",
|
"user": "",
|
||||||
"User Permissions": "User Permissions",
|
"User Permissions": "",
|
||||||
"Users": "Users",
|
"Users": "",
|
||||||
"Utilize": "Utilize",
|
"Utilize": "",
|
||||||
"Valid time units:": "Valid time units:",
|
"Valid time units:": "",
|
||||||
"variable": "variable",
|
"variable": "",
|
||||||
"variable to have them replaced with clipboard content.": "variable to have them replaced with clipboard content.",
|
"variable to have them replaced with clipboard content.": "",
|
||||||
"Version": "Version",
|
"Version": "",
|
||||||
"Web": "Web",
|
"Web": "",
|
||||||
"WebUI Add-ons": "WebUI Add-ons",
|
"WebUI Add-ons": "",
|
||||||
"WebUI Settings": "WebUI Settings",
|
"WebUI Settings": "",
|
||||||
"WebUI will make requests to": "WebUI will make requests to",
|
"WebUI will make requests to": "",
|
||||||
"What’s New in": "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.",
|
"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)": "",
|
||||||
"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].": "",
|
||||||
"You": "You",
|
"You": "",
|
||||||
"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.": ""
|
||||||
}
|
}
|
||||||
|
|
|
@ -360,4 +360,4 @@
|
||||||
"You": "Tu",
|
"You": "Tu",
|
||||||
"You're a helpful assistant.": "Sei un assistente utile.",
|
"You're a helpful assistant.": "Sei un assistente utile.",
|
||||||
"You're now logged in.": "Ora hai effettuato l'accesso."
|
"You're now logged in.": "Ora hai effettuato l'accesso."
|
||||||
}
|
}
|
||||||
|
|
|
@ -360,4 +360,4 @@
|
||||||
"You": "あなた",
|
"You": "あなた",
|
||||||
"You're a helpful assistant.": "あなたは役に立つアシスタントです。",
|
"You're a helpful assistant.": "あなたは役に立つアシスタントです。",
|
||||||
"You're now logged in.": "ログインしました。"
|
"You're now logged in.": "ログインしました。"
|
||||||
}
|
}
|
||||||
|
|
362
src/lib/i18n/locales/ko-KR/translation.json
Normal file
362
src/lib/i18n/locales/ko-KR/translation.json
Normal file
|
@ -0,0 +1,362 @@
|
||||||
|
{
|
||||||
|
"'s', 'm', 'h', 'd', 'w' or '-1' for no expiration.": "'초', '분', '시간', '일', '주' 또는 만료 없음 '-1'",
|
||||||
|
"(Beta)": "(Beta)",
|
||||||
|
"(e.g. `sh webui.sh --api`)": "(예: `sh webui.sh --api`)",
|
||||||
|
"(latest)": "(latest)",
|
||||||
|
"{{modelName}} is thinking...": "{{modelName}} 이(가) 생각중입니다....",
|
||||||
|
"{{webUIName}} Backend Required": "{{webUIName}} 백엔드가 필요합니다.",
|
||||||
|
"a user": "사용자",
|
||||||
|
"About": "소개",
|
||||||
|
"Account": "계정",
|
||||||
|
"Action": "액션",
|
||||||
|
"Add a model": "모델 추가",
|
||||||
|
"Add a model tag name": "모델 태그명 추가",
|
||||||
|
"Add a short description about what this modelfile does": "이 모델파일이 하는 일에 대한 간단한 설명 추가",
|
||||||
|
"Add a short title for this prompt": "이 프롬프트에 대한 간단한 제목 추가",
|
||||||
|
"Add a tag": "태그 추가",
|
||||||
|
"Add Docs": "문서 추가",
|
||||||
|
"Add Files": "파일 추가",
|
||||||
|
"Add message": "메시지 추가",
|
||||||
|
"add tags": "태그들 추가",
|
||||||
|
"Adjusting these settings will apply changes universally to all users.": "이 설정을 조정하면 모든 사용자에게 적용됩니다.",
|
||||||
|
"admin": "관리자",
|
||||||
|
"Admin Panel": "관리자 패널",
|
||||||
|
"Admin Settings": "관리자 설정",
|
||||||
|
"Advanced Parameters": "고급 매개변수",
|
||||||
|
"all": "모두",
|
||||||
|
"All Users": "모든 사용자",
|
||||||
|
"Allow": "허용",
|
||||||
|
"Allow Chat Deletion": "채팅 삭제 허용",
|
||||||
|
"alphanumeric characters and hyphens": "영문자,숫자 및 하이픈",
|
||||||
|
"Already have an account?": "이미 계정이 있으신가요?",
|
||||||
|
"an assistant": "어시스턴트",
|
||||||
|
"and": "그리고",
|
||||||
|
"API Base URL": "API 기본 URL",
|
||||||
|
"API Key": "API키",
|
||||||
|
"API RPM": "API RPM",
|
||||||
|
"are allowed - Activate this command by typing": "허용됩니다 - 이 명령을 활성화하려면 입력하세요.",
|
||||||
|
"Are you sure?": "확실합니까?",
|
||||||
|
"Audio": "오디오",
|
||||||
|
"Auto-playback response": "응답 자동 재생",
|
||||||
|
"Auto-send input after 3 sec.": "3초 후 입력 자동 전송",
|
||||||
|
"AUTOMATIC1111 Base URL": "AUTOMATIC1111 Base URL",
|
||||||
|
"AUTOMATIC1111 Base URL is required.": "AUTOMATIC1111 Base URL이 필요합니다.",
|
||||||
|
"available!": "사용 가능!",
|
||||||
|
"Back": "뒤로가기",
|
||||||
|
"Builder Mode": "빌더 모드",
|
||||||
|
"Cancel": "취소",
|
||||||
|
"Categories": "분류",
|
||||||
|
"Change Password": "비밀번호 변경",
|
||||||
|
"Chat": "채팅",
|
||||||
|
"Chat History": "채팅 기록",
|
||||||
|
"Chat History is off for this browser.": "이 브라우저에서 채팅 기록이 꺼져 있습니다.",
|
||||||
|
"Chats": "채팅",
|
||||||
|
"Check Again": "다시 확인",
|
||||||
|
"Check for updates": "업데이트 확인",
|
||||||
|
"Checking for updates...": "업데이트 확인중...",
|
||||||
|
"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.": "다른 모델파일을 확인하려면 여기를 클릭하세요.",
|
||||||
|
"Click here to select": "선택하려면 여기를 클릭하세요.",
|
||||||
|
"Click here to select documents.": "문서를 선택하려면 여기를 클릭하세요.",
|
||||||
|
"click here.": "여기를 클릭하세요.",
|
||||||
|
"Click on the user role button to change a user's role.": "사용자 역할 버튼을 클릭하여 사용자의 역할을 변경하세요.",
|
||||||
|
"Close": "닫기",
|
||||||
|
"Collection": "컬렉션",
|
||||||
|
"Command": "명령",
|
||||||
|
"Confirm Password": "비밀번호 확인",
|
||||||
|
"Connections": "연결",
|
||||||
|
"Content": "내용",
|
||||||
|
"Context Length": "내용 길이",
|
||||||
|
"Conversation Mode": "대화 모드",
|
||||||
|
"Copy last code block": "마지막 코드 블록 복사",
|
||||||
|
"Copy last response": "마지막 응답 복사",
|
||||||
|
"Copying to clipboard was successful!": "클립보드에 복사되었습니다!",
|
||||||
|
"Create a concise, 3-5 word phrase as a header for the following query, strictly adhering to the 3-5 word limit and avoiding the use of the word 'title':": "다음 질문에 대한 제목으로 간결한 3-5 단어 문구를 만드되 3-5 단어 제한을 엄격히 준수하고 'title' 단어 사용을 피하세요:",
|
||||||
|
"Create a modelfile": "모델파일 만들기",
|
||||||
|
"Create Account": "계정 만들기",
|
||||||
|
"Created at": "생성일",
|
||||||
|
"Created by": "생성자",
|
||||||
|
"Current Model": "현재 모델",
|
||||||
|
"Current Password": "현재 비밀번호",
|
||||||
|
"Custom": "사용자 정의",
|
||||||
|
"Customize Ollama models for a specific purpose": "특정 목적으로 Ollama 모델 사용자 정의",
|
||||||
|
"Dark": "어두운",
|
||||||
|
"Database": "데이터베이스",
|
||||||
|
"DD/MM/YYYY HH:mm": "DD/MM/YYYY HH:mm",
|
||||||
|
"Default": "기본값",
|
||||||
|
"Default (Automatic1111)": "기본값 (Automatic1111)",
|
||||||
|
"Default (Web API)": "기본값 (Web API)",
|
||||||
|
"Default model updated": "기본 모델이 업데이트되었습니다.",
|
||||||
|
"Default Prompt Suggestions": "기본 프롬프트 제안",
|
||||||
|
"Default User Role": "기본 사용자 역할",
|
||||||
|
"delete": "삭제",
|
||||||
|
"Delete a model": "모델 삭제",
|
||||||
|
"Delete chat": "채팅 삭제",
|
||||||
|
"Delete Chats": "채팅들 삭제",
|
||||||
|
"Deleted {{deleteModelTag}}": "{{deleteModelTag}} 삭제됨",
|
||||||
|
"Deleted {tagName}": "{tagName} 삭제됨",
|
||||||
|
"Description": "설명",
|
||||||
|
"Desktop Notifications": "알림",
|
||||||
|
"Disabled": "비활성화",
|
||||||
|
"Discover a modelfile": "모델파일 검색",
|
||||||
|
"Discover a prompt": "프롬프트 검색",
|
||||||
|
"Discover, download, and explore custom prompts": "사용자 정의 프롬프트 검색, 다운로드 및 탐색",
|
||||||
|
"Discover, download, and explore model presets": "모델 사전 설정 검색, 다운로드 및 탐색",
|
||||||
|
"Display the username instead of You in the Chat": "채팅에서 'You' 대신 사용자 이름 표시",
|
||||||
|
"Document": "문서",
|
||||||
|
"Document Settings": "문서 설정",
|
||||||
|
"Documents": "문서들",
|
||||||
|
"does not make any external connections, and your data stays securely on your locally hosted server.": "어떠한 외부 연결도 하지 않으며, 데이터는 로컬에서 호스팅되는 서버에 안전하게 유지됩니다.",
|
||||||
|
"Don't Allow": "허용 안 함",
|
||||||
|
"Don't have an account?": "계정이 없으신가요?",
|
||||||
|
"Download as a File": "파일로 다운로드",
|
||||||
|
"Download Database": "데이터베이스 다운로드",
|
||||||
|
"Drop any files here to add to the conversation": "대화에 추가할 파일을 여기에 드롭하세요.",
|
||||||
|
"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "예: '30s','10m'. 유효한 시간 단위는 's', 'm', 'h'입니다.",
|
||||||
|
"Edit Doc": "문서 편집",
|
||||||
|
"Edit User": "사용자 편집",
|
||||||
|
"Email": "이메일",
|
||||||
|
"Enable Chat History": "채팅 기록 활성화",
|
||||||
|
"Enable New Sign Ups": "새 회원가입 활성화",
|
||||||
|
"Enabled": "활성화",
|
||||||
|
"Enter {{role}} message here": "여기에 {{role}} 메시지 입력",
|
||||||
|
"Enter API Key": "API 키 입력",
|
||||||
|
"Enter Chunk Overlap": "청크 오버랩 입력",
|
||||||
|
"Enter Chunk Size": "청크 크기 입력",
|
||||||
|
"Enter Image Size (e.g. 512x512)": "이미지 크기 입력(예: 512x512)",
|
||||||
|
"Enter LiteLLM API Base URL (litellm_params.api_base)": "LiteLLM API 기본 URL 입력(litellm_params.api_base)",
|
||||||
|
"Enter LiteLLM API Key (litellm_params.api_key)": "LiteLLM API 키 입력(litellm_params.api_key)",
|
||||||
|
"Enter LiteLLM API RPM (litellm_params.rpm)": "LiteLLM API RPM 입력(litellm_params.rpm)",
|
||||||
|
"Enter LiteLLM Model (litellm_params.model)": "LiteLLM 모델 입력(litellm_params.model)",
|
||||||
|
"Enter Max Tokens (litellm_params.max_tokens)": "최대 토큰 수 입력(litellm_params.max_tokens)",
|
||||||
|
"Enter model tag (e.g. {{modelTag}})": "모델 태그 입력(예: {{modelTag}})",
|
||||||
|
"Enter Number of Steps (e.g. 50)": "단계 수 입력(예: 50)",
|
||||||
|
"Enter stop sequence": "중지 시퀀스 입력",
|
||||||
|
"Enter Top K": "Top K 입력",
|
||||||
|
"Enter URL (e.g. http://127.0.0.1:7860/)": "URL 입력(예: http://127.0.0.1:7860/)",
|
||||||
|
"Enter Your Email": "이메일 입력",
|
||||||
|
"Enter Your Full Name": "전체 이름 입력",
|
||||||
|
"Enter Your Password": "비밀번호 입력",
|
||||||
|
"Experimental": "실험적",
|
||||||
|
"Export All Chats (All Users)": "모든 채팅 내보내기 (모든 사용자)",
|
||||||
|
"Export Chats": "채팅 내보내기",
|
||||||
|
"Export Documents Mapping": "문서 매핑 내보내기",
|
||||||
|
"Export Modelfiles": "모델파일 내보내기",
|
||||||
|
"Export Prompts": "프롬프트 내보내기",
|
||||||
|
"Failed to read clipboard contents": "클립보드 내용을 읽는 데 실패했습니다.",
|
||||||
|
"File Mode": "파일 모드",
|
||||||
|
"File not found.": "파일을 찾을 수 없습니다.",
|
||||||
|
"Focus chat input": "채팅 입력 포커스",
|
||||||
|
"Format your variables using square brackets like this:": "이렇게 대괄호를 사용하여 변수를 형식화하세요:",
|
||||||
|
"From (Base Model)": "출처(기본 모델)",
|
||||||
|
"Full Screen Mode": "전체 화면 모드",
|
||||||
|
"General": "일반",
|
||||||
|
"General Settings": "일반 설정",
|
||||||
|
"Hello, {{name}}": "안녕하세요, {{name}}",
|
||||||
|
"Hide": "숨기기",
|
||||||
|
"Hide Additional Params": "추가 매개변수 숨기기",
|
||||||
|
"How can I help you today?": "오늘 어떻게 도와드릴까요?",
|
||||||
|
"Image Generation (Experimental)": "이미지 생성(실험적)",
|
||||||
|
"Image Generation Engine": "이미지 생성 엔진",
|
||||||
|
"Image Settings": "이미지 설정",
|
||||||
|
"Images": "이미지",
|
||||||
|
"Import Chats": "채팅 가져오기",
|
||||||
|
"Import Documents Mapping": "문서 매핑 가져오기",
|
||||||
|
"Import Modelfiles": "모델파일 가져오기",
|
||||||
|
"Import Prompts": "프롬프트 가져오기",
|
||||||
|
"Include --api flag when running stable-diffusion-webui": "stable-diffusion-webui를 실행할 때 --api 플래그 포함",
|
||||||
|
"Interface": "인터페이스",
|
||||||
|
"join our Discord for help.": "도움말을 보려면 Discord에 가입하세요.",
|
||||||
|
"JSON": "JSON",
|
||||||
|
"JWT Expiration": "JWT 만료",
|
||||||
|
"JWT Token": "JWT 토큰",
|
||||||
|
"Keep Alive": "계속 유지하기",
|
||||||
|
"Keyboard shortcuts": "키보드 단축키",
|
||||||
|
"Language": "언어",
|
||||||
|
"Light": "밝음",
|
||||||
|
"Listening...": "청취 중...",
|
||||||
|
"LLMs can make mistakes. Verify important information.": "LLM은 실수를 할 수 있습니다. 중요한 정보를 확인하세요.",
|
||||||
|
"Made by OpenWebUI Community": "OpenWebUI 커뮤니티에서 제작",
|
||||||
|
"Make sure to enclose them with": "다음으로 묶는 것을 잊지 마세요:",
|
||||||
|
"Manage LiteLLM Models": "LiteLLM 모델 관리",
|
||||||
|
"Manage Models": "모델 관리",
|
||||||
|
"Manage Ollama Models": "Ollama 모델 관리",
|
||||||
|
"Max Tokens": "최대 토큰 수",
|
||||||
|
"Maximum of 3 models can be downloaded simultaneously. Please try again later.": "최대 3개의 모델을 동시에 다운로드할 수 있습니다. 나중에 다시 시도하세요.",
|
||||||
|
"Mirostat": "Mirostat",
|
||||||
|
"Mirostat Eta": "Mirostat Eta",
|
||||||
|
"Mirostat Tau": "Mirostat Tau",
|
||||||
|
"Model '{{modelName}}' has been successfully downloaded.": "모델 '{{modelName}}'이(가) 성공적으로 다운로드되었습니다.",
|
||||||
|
"Model '{{modelTag}}' is already in queue for downloading.": "모델 '{{modelTag}}'이(가) 이미 다운로드 대기열에 있습니다.",
|
||||||
|
"Model {{modelId}} not found": "모델 {{modelId}}를 찾을 수 없습니다.",
|
||||||
|
"Model {{modelName}} already exists.": "모델 {{modelName}}이(가) 이미 존재합니다.",
|
||||||
|
"Model Name": "모델 이름",
|
||||||
|
"Model not selected": "모델이 선택되지 않았습니다.",
|
||||||
|
"Model Tag Name": "모델 태그 이름",
|
||||||
|
"Model Whitelisting": "모델 허용 목록",
|
||||||
|
"Model(s) Whitelisted": "허용된 모델",
|
||||||
|
"Modelfile": "모델파일",
|
||||||
|
"Modelfile Advanced Settings": "모델파일 고급 설정",
|
||||||
|
"Modelfile Content": "모델파일 내용",
|
||||||
|
"Modelfiles": "모델파일",
|
||||||
|
"Models": "모델",
|
||||||
|
"My Documents": "내 문서",
|
||||||
|
"My Modelfiles": "내 모델파일",
|
||||||
|
"My Prompts": "내 프롬프트",
|
||||||
|
"Name": "이름",
|
||||||
|
"Name Tag": "이름 태그",
|
||||||
|
"Name your modelfile": "모델파일 이름 지정",
|
||||||
|
"New Chat": "새 채팅",
|
||||||
|
"New Password": "새 비밀번호",
|
||||||
|
"Not sure what to add?": "추가할 것이 궁금하세요?",
|
||||||
|
"Not sure what to write? Switch to": "무엇을 쓸지 모르겠나요? 전환하세요.",
|
||||||
|
"Off": "끄기",
|
||||||
|
"Okay, Let's Go!": "그렇습니다, 시작합시다!",
|
||||||
|
"Ollama Base URL": "Ollama 기본 URL",
|
||||||
|
"Ollama Version": "Ollama 버전",
|
||||||
|
"On": "켜기",
|
||||||
|
"Only": "오직",
|
||||||
|
"Only alphanumeric characters and hyphens are allowed in the command string.": "명령어 문자열에는 영문자, 숫자 및 하이픈만 허용됩니다.",
|
||||||
|
"Oops! Hold tight! Your files are still in the processing oven. We're cooking them up to perfection. Please be patient and we'll let you know once they're ready.": "이런! 잠시만 기다려 주세요! 파일이 아직 처리 중입니다. 완벽하게 준비하고 있습니다. 잠시만 기다려주시면 준비가 되면 알려드리겠습니다.",
|
||||||
|
"Oops! Looks like the URL is invalid. Please double-check and try again.": "이런! URL이 잘못된 것 같습니다. 다시 한번 확인하고 다시 시도해주세요.",
|
||||||
|
"Oops! You're using an unsupported method (frontend only). Please serve the WebUI from the backend.": "이런! 지원되지 않는 방식(프론트엔드 전용)을 사용하고 계십니다. 백엔드에서 WebUI를 제공해주세요.",
|
||||||
|
"Open": "열기",
|
||||||
|
"Open AI": "Open AI",
|
||||||
|
"Open AI (Dall-E)": "OpenAI (Dall-E)",
|
||||||
|
"Open new chat": "새 채팅 열기",
|
||||||
|
"OpenAI API": "OpenAI API",
|
||||||
|
"OpenAI API Key": "OpenAI API 키",
|
||||||
|
"OpenAI API Key is required.": "OpenAI API 키가 필요합니다.",
|
||||||
|
"or": "또는",
|
||||||
|
"Parameters": "매개변수",
|
||||||
|
"Password": "비밀번호",
|
||||||
|
"PDF Extract Images (OCR)": "PDF에서 이미지 추출 (OCR)",
|
||||||
|
"pending": "보류 중",
|
||||||
|
"Permission denied when accessing microphone: {{error}}": "마이크 액세스가 거부되었습니다: {{error}}",
|
||||||
|
"Playground": "놀이터",
|
||||||
|
"Profile": "프로필",
|
||||||
|
"Prompt Content": "프롬프트 내용",
|
||||||
|
"Prompt suggestions": "프롬프트 제안",
|
||||||
|
"Prompts": "프롬프트",
|
||||||
|
"Pull a model from Ollama.com": "Ollama.com에서 모델 가져오기",
|
||||||
|
"Pull Progress": "가져오기 진행 상황",
|
||||||
|
"Query Params": "쿼리 매개변수",
|
||||||
|
"RAG Template": "RAG 템플릿",
|
||||||
|
"Raw Format": "Raw 형식",
|
||||||
|
"Record voice": "음성 녹음",
|
||||||
|
"Redirecting you to OpenWebUI Community": "OpenWebUI 커뮤니티로 리디렉션하는 중",
|
||||||
|
"Release Notes": "릴리스 노트",
|
||||||
|
"Repeat Last N": "마지막 N 반복",
|
||||||
|
"Repeat Penalty": "반복 패널티",
|
||||||
|
"Request Mode": "요청 모드",
|
||||||
|
"Reset Vector Storage": "벡터 스토리지 초기화",
|
||||||
|
"Response AutoCopy to Clipboard": "응답 자동 클립보드 복사",
|
||||||
|
"Role": "역할",
|
||||||
|
"Rosé Pine": "로제 파인",
|
||||||
|
"Rosé Pine Dawn": "로제 파인 던",
|
||||||
|
"Save": "저장",
|
||||||
|
"Save & Create": "저장 및 생성",
|
||||||
|
"Save & Submit": "저장 및 제출",
|
||||||
|
"Save & Update": "저장 및 업데이트",
|
||||||
|
"Saving chat logs directly to your browser's storage is no longer supported. Please take a moment to download and delete your chat logs by clicking the button below. Don't worry, you can easily re-import your chat logs to the backend through": "브라우저의 저장소에 채팅 로그를 직접 저장하는 것은 더 이상 지원되지 않습니다. 아래 버튼을 클릭하여 채팅 로그를 다운로드하고 삭제하세요. 걱정 마세요. 백엔드를 통해 채팅 로그를 쉽게 다시 가져올 수 있습니다.",
|
||||||
|
"Scan": "스캔",
|
||||||
|
"Scan complete!": "스캔 완료!",
|
||||||
|
"Scan for documents from {{path}}": "{{path}}에서 문서 스캔",
|
||||||
|
"Search": "검색",
|
||||||
|
"Search Documents": "문서 검색",
|
||||||
|
"Search Prompts": "프롬프트 검색",
|
||||||
|
"See readme.md for instructions": "설명은 readme.md를 참조하세요.",
|
||||||
|
"See what's new": "새로운 기능 보기",
|
||||||
|
"Seed": "시드",
|
||||||
|
"Select a mode": "모드 선택",
|
||||||
|
"Select a model": "모델 선택",
|
||||||
|
"Select an Ollama instance": "Ollama 인스턴스 선택",
|
||||||
|
"Send a Message": "메시지 보내기",
|
||||||
|
"Send message": "메시지 보내기",
|
||||||
|
"Server connection verified": "서버 연결 확인됨",
|
||||||
|
"Set as default": "기본값으로 설정",
|
||||||
|
"Set Default Model": "기본 모델 설정",
|
||||||
|
"Set Image Size": "이미지 크기 설정",
|
||||||
|
"Set Steps": "단계 설정",
|
||||||
|
"Set Title Auto-Generation Model": "제목 자동 생성 모델 설정",
|
||||||
|
"Set Voice": "음성 설정",
|
||||||
|
"Settings": "설정",
|
||||||
|
"Settings saved successfully!": "설정이 성공적으로 저장되었습니다!",
|
||||||
|
"Share to OpenWebUI Community": "OpenWebUI 커뮤니티에 공유",
|
||||||
|
"short-summary": "간단한 요약",
|
||||||
|
"Show": "보이기",
|
||||||
|
"Show Additional Params": "추가 매개변수 보기",
|
||||||
|
"Show shortcuts": "단축키 보기",
|
||||||
|
"sidebar": "사이드바",
|
||||||
|
"Sign in": "로그인",
|
||||||
|
"Sign Out": "로그아웃",
|
||||||
|
"Sign up": "가입",
|
||||||
|
"Speech recognition error: {{error}}": "음성 인식 오류: {{error}}",
|
||||||
|
"Speech-to-Text Engine": "음성-텍스트 엔진",
|
||||||
|
"SpeechRecognition API is not supported in this browser.": "이 브라우저에서는 SpeechRecognition API를 지원하지 않습니다.",
|
||||||
|
"Stop Sequence": "중지 시퀀스",
|
||||||
|
"STT Settings": "STT 설정",
|
||||||
|
"Submit": "제출",
|
||||||
|
"Success": "성공",
|
||||||
|
"Successfully updated.": "성공적으로 업데이트되었습니다.",
|
||||||
|
"Sync All": "모두 동기화",
|
||||||
|
"System": "시스템",
|
||||||
|
"System Prompt": "시스템 프롬프트",
|
||||||
|
"Tags": "Tags",
|
||||||
|
"Temperature": "Temperature",
|
||||||
|
"Template": "Template",
|
||||||
|
"Text Completion": "텍스트 완성",
|
||||||
|
"Text-to-Speech Engine": "텍스트-음성 엔진",
|
||||||
|
"Tfs Z": "Tfs Z",
|
||||||
|
"Theme": "테마",
|
||||||
|
"This ensures that your valuable conversations are securely saved to your backend database. Thank you!": "이렇게 하면 소중한 대화 내용이 백엔드 데이터베이스에 안전하게 저장됩니다. 감사합니다!",
|
||||||
|
"This setting does not sync across browsers or devices.": "이 설정은 브라우저 또는 장치 간에 동기화되지 않습니다.",
|
||||||
|
"Tip: Update multiple variable slots consecutively by pressing the tab key in the chat input after each replacement.": "팁: 각 대체 후 채팅 입력에서 탭 키를 눌러 여러 개의 변수 슬롯을 연속적으로 업데이트하세요.",
|
||||||
|
"Title": "제목",
|
||||||
|
"Title Auto-Generation": "제목 자동 생성",
|
||||||
|
"Title Generation Prompt": "제목 생성 프롬프트",
|
||||||
|
"to": "~까지",
|
||||||
|
"To access the available model names for downloading,": "다운로드 가능한 모델명을 확인하려면,",
|
||||||
|
"To access the GGUF models available for downloading,": "다운로드 가능한 GGUF 모델을 확인하려면,",
|
||||||
|
"to chat input.": "채팅 입력으로.",
|
||||||
|
"Toggle settings": "설정 전환",
|
||||||
|
"Toggle sidebar": "사이드바 전환",
|
||||||
|
"Top K": "Top K",
|
||||||
|
"Top P": "Top P",
|
||||||
|
"Trouble accessing Ollama?": "Ollama에 접근하는 데 문제가 있나요?",
|
||||||
|
"TTS Settings": "TTS 설정",
|
||||||
|
"Type Hugging Face Resolve (Download) URL": "Hugging Face Resolve (다운로드) URL 입력",
|
||||||
|
"Uh-oh! There was an issue connecting to {{provider}}.": "앗! {{provider}}에 연결하는 데 문제가 있었습니다.",
|
||||||
|
"Unknown File Type '{{file_type}}', but accepting and treating as plain text": "알 수 없는 파일 유형 '{{file_type}}', 하지만 일반 텍스트로 허용하고 처리합니다.",
|
||||||
|
"Update password": "비밀번호 업데이트",
|
||||||
|
"Upload a GGUF model": "GGUF 모델 업로드",
|
||||||
|
"Upload files": "파일 업로드",
|
||||||
|
"Upload Progress": "업로드 진행 상황",
|
||||||
|
"URL Mode": "URL 모드",
|
||||||
|
"Use '#' in the prompt input to load and select your documents.": "프롬프트 입력에서 '#'를 사용하여 문서를 로드하고 선택하세요.",
|
||||||
|
"Use Gravatar": "Gravatar 사용",
|
||||||
|
"user": "사용자",
|
||||||
|
"User Permissions": "사용자 권한",
|
||||||
|
"Users": "사용자",
|
||||||
|
"Utilize": "활용",
|
||||||
|
"Valid time units:": "유효한 시간 단위:",
|
||||||
|
"variable": "변수",
|
||||||
|
"variable to have them replaced with clipboard content.": "변수를 사용하여 클립보드 내용으로 바꾸세요.",
|
||||||
|
"Version": "버전",
|
||||||
|
"Web": "웹",
|
||||||
|
"WebUI Add-ons": "WebUI 애드온",
|
||||||
|
"WebUI Settings": "WebUI 설정",
|
||||||
|
"WebUI will make requests to": "WebUI가 요청할 대상:",
|
||||||
|
"What's New in": "새로운 기능",
|
||||||
|
"When history is turned off, new chats on this browser won't appear in your history on any of your devices.": "기록 기능이 꺼져 있으면 이 브라우저의 새 채팅이 다른 장치의 채팅 기록에 나타나지 않습니다.",
|
||||||
|
"Whisper (Local)": "위스퍼 (Local)",
|
||||||
|
"Write a prompt suggestion (e.g. Who are you?)": "프롬프트 제안 작성 (예: 당신은 누구인가요?)",
|
||||||
|
"Write a summary in 50 words that summarizes [topic or keyword].": "[주제 또는 키워드]에 대한 50단어 요약문 작성.",
|
||||||
|
"You": "당신",
|
||||||
|
"You're a helpful assistant.": "당신은 유용한 어시스턴트입니다.",
|
||||||
|
"You're now logged in.": "로그인되었습니다."
|
||||||
|
}
|
|
@ -39,6 +39,10 @@
|
||||||
"code": "ja-JP",
|
"code": "ja-JP",
|
||||||
"title": "Japanese"
|
"title": "Japanese"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"code": "ko-KR",
|
||||||
|
"title": "Korean"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"code": "nl-NL",
|
"code": "nl-NL",
|
||||||
"title": "Dutch (Netherlands)"
|
"title": "Dutch (Netherlands)"
|
||||||
|
|
|
@ -48,6 +48,7 @@
|
||||||
let messagesContainerElement: HTMLDivElement;
|
let messagesContainerElement: HTMLDivElement;
|
||||||
let currentRequestId = null;
|
let currentRequestId = null;
|
||||||
|
|
||||||
|
let showModelSelector = false;
|
||||||
let selectedModels = [''];
|
let selectedModels = [''];
|
||||||
|
|
||||||
let selectedModelfile = null;
|
let selectedModelfile = null;
|
||||||
|
@ -533,6 +534,8 @@
|
||||||
|
|
||||||
console.log(docs);
|
console.log(docs);
|
||||||
|
|
||||||
|
console.log(model);
|
||||||
|
|
||||||
const res = await generateOpenAIChatCompletion(
|
const res = await generateOpenAIChatCompletion(
|
||||||
localStorage.token,
|
localStorage.token,
|
||||||
{
|
{
|
||||||
|
@ -585,7 +588,9 @@
|
||||||
max_tokens: $settings?.options?.num_predict ?? undefined,
|
max_tokens: $settings?.options?.num_predict ?? undefined,
|
||||||
docs: docs.length > 0 ? docs : undefined
|
docs: docs.length > 0 ? docs : undefined
|
||||||
},
|
},
|
||||||
model.source === 'litellm' ? `${LITELLM_API_BASE_URL}/v1` : `${OPENAI_API_BASE_URL}`
|
model?.source?.toLowerCase() === 'litellm'
|
||||||
|
? `${LITELLM_API_BASE_URL}/v1`
|
||||||
|
: `${OPENAI_API_BASE_URL}`
|
||||||
);
|
);
|
||||||
|
|
||||||
if (res && res.ok) {
|
if (res && res.ok) {
|
||||||
|
@ -776,7 +781,7 @@
|
||||||
titleModelId,
|
titleModelId,
|
||||||
userPrompt,
|
userPrompt,
|
||||||
titleModel?.external ?? false
|
titleModel?.external ?? false
|
||||||
? titleModel.source === 'litellm'
|
? titleModel?.source?.toLowerCase() === 'litellm'
|
||||||
? `${LITELLM_API_BASE_URL}/v1`
|
? `${LITELLM_API_BASE_URL}/v1`
|
||||||
: `${OPENAI_API_BASE_URL}`
|
: `${OPENAI_API_BASE_URL}`
|
||||||
: `${OLLAMA_API_BASE_URL}/v1`
|
: `${OLLAMA_API_BASE_URL}/v1`
|
||||||
|
@ -837,7 +842,16 @@
|
||||||
</svelte:head>
|
</svelte:head>
|
||||||
|
|
||||||
<div class="h-screen max-h-[100dvh] w-full flex flex-col">
|
<div class="h-screen max-h-[100dvh] w-full flex flex-col">
|
||||||
<Navbar {title} shareEnabled={messages.length > 0} {initNewChat} {tags} {addTag} {deleteTag} />
|
<Navbar
|
||||||
|
{title}
|
||||||
|
bind:selectedModels
|
||||||
|
bind:showModelSelector
|
||||||
|
shareEnabled={messages.length > 0}
|
||||||
|
{initNewChat}
|
||||||
|
{tags}
|
||||||
|
{addTag}
|
||||||
|
{deleteTag}
|
||||||
|
/>
|
||||||
<div class="flex flex-col flex-auto">
|
<div class="flex flex-col flex-auto">
|
||||||
<div
|
<div
|
||||||
class=" pb-2.5 flex flex-col justify-between w-full flex-auto overflow-auto h-0"
|
class=" pb-2.5 flex flex-col justify-between w-full flex-auto overflow-auto h-0"
|
||||||
|
@ -849,15 +863,7 @@
|
||||||
messagesContainerElement.clientHeight + 5;
|
messagesContainerElement.clientHeight + 5;
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div
|
<div class=" h-full w-full flex flex-col pt-2 pb-4">
|
||||||
class="{$settings?.fullScreenMode ?? null
|
|
||||||
? 'max-w-full'
|
|
||||||
: 'max-w-2xl md:px-0'} mx-auto w-full px-4"
|
|
||||||
>
|
|
||||||
<ModelSelector bind:selectedModels />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class=" h-full w-full flex flex-col py-8">
|
|
||||||
<Messages
|
<Messages
|
||||||
chatId={$chatId}
|
chatId={$chatId}
|
||||||
{selectedModels}
|
{selectedModels}
|
||||||
|
|
|
@ -56,8 +56,10 @@
|
||||||
let currentRequestId = null;
|
let currentRequestId = null;
|
||||||
|
|
||||||
// let chatId = $page.params.id;
|
// let chatId = $page.params.id;
|
||||||
|
let showModelSelector = false;
|
||||||
let selectedModels = [''];
|
let selectedModels = [''];
|
||||||
let selectedModelfile = null;
|
let selectedModelfile = null;
|
||||||
|
|
||||||
$: selectedModelfile =
|
$: selectedModelfile =
|
||||||
selectedModels.length === 1 &&
|
selectedModels.length === 1 &&
|
||||||
$modelfiles.filter((modelfile) => modelfile.tagName === selectedModels[0]).length > 0
|
$modelfiles.filter((modelfile) => modelfile.tagName === selectedModels[0]).length > 0
|
||||||
|
@ -600,7 +602,9 @@
|
||||||
max_tokens: $settings?.options?.num_predict ?? undefined,
|
max_tokens: $settings?.options?.num_predict ?? undefined,
|
||||||
docs: docs.length > 0 ? docs : undefined
|
docs: docs.length > 0 ? docs : undefined
|
||||||
},
|
},
|
||||||
model.source === 'litellm' ? `${LITELLM_API_BASE_URL}/v1` : `${OPENAI_API_BASE_URL}`
|
model?.source?.toLowerCase() === 'litellm'
|
||||||
|
? `${LITELLM_API_BASE_URL}/v1`
|
||||||
|
: `${OPENAI_API_BASE_URL}`
|
||||||
);
|
);
|
||||||
|
|
||||||
if (res && res.ok) {
|
if (res && res.ok) {
|
||||||
|
@ -791,7 +795,7 @@
|
||||||
titleModelId,
|
titleModelId,
|
||||||
userPrompt,
|
userPrompt,
|
||||||
titleModel?.external ?? false
|
titleModel?.external ?? false
|
||||||
? titleModel.source === 'litellm'
|
? titleModel?.source?.toLowerCase() === 'litellm'
|
||||||
? `${LITELLM_API_BASE_URL}/v1`
|
? `${LITELLM_API_BASE_URL}/v1`
|
||||||
: `${OPENAI_API_BASE_URL}`
|
: `${OPENAI_API_BASE_URL}`
|
||||||
: `${OLLAMA_API_BASE_URL}/v1`
|
: `${OLLAMA_API_BASE_URL}/v1`
|
||||||
|
@ -861,6 +865,8 @@
|
||||||
<div class="min-h-screen max-h-screen w-full flex flex-col">
|
<div class="min-h-screen max-h-screen w-full flex flex-col">
|
||||||
<Navbar
|
<Navbar
|
||||||
{title}
|
{title}
|
||||||
|
bind:selectedModels
|
||||||
|
bind:showModelSelector
|
||||||
shareEnabled={messages.length > 0}
|
shareEnabled={messages.length > 0}
|
||||||
initNewChat={async () => {
|
initNewChat={async () => {
|
||||||
if (currentRequestId !== null) {
|
if (currentRequestId !== null) {
|
||||||
|
@ -885,15 +891,7 @@
|
||||||
messagesContainerElement.clientHeight + 5;
|
messagesContainerElement.clientHeight + 5;
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div
|
<div class=" h-full w-full flex flex-col py-4">
|
||||||
class="{$settings?.fullScreenMode ?? null
|
|
||||||
? 'max-w-full'
|
|
||||||
: 'max-w-2xl md:px-0'} mx-auto w-full px-4"
|
|
||||||
>
|
|
||||||
<ModelSelector bind:selectedModels />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class=" h-full w-full flex flex-col py-8">
|
|
||||||
<Messages
|
<Messages
|
||||||
chatId={$chatId}
|
chatId={$chatId}
|
||||||
{selectedModels}
|
{selectedModels}
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
|
|
||||||
import { splitStream } from '$lib/utils';
|
import { splitStream } from '$lib/utils';
|
||||||
import ChatCompletion from '$lib/components/playground/ChatCompletion.svelte';
|
import ChatCompletion from '$lib/components/playground/ChatCompletion.svelte';
|
||||||
|
import Selector from '$lib/components/chat/ModelSelector/Selector.svelte';
|
||||||
|
|
||||||
const i18n = getContext('i18n');
|
const i18n = getContext('i18n');
|
||||||
|
|
||||||
|
@ -315,27 +316,24 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class=" flex gap-1 px-1">
|
<div class="flex flex-col gap-1 px-1 w-full">
|
||||||
<select
|
<div class="flex w-full">
|
||||||
id="models"
|
<div class="overflow-hidden w-full">
|
||||||
class="outline-none bg-transparent text-sm font-medium rounded-lg w-full placeholder-gray-400"
|
<div class="max-w-full">
|
||||||
|
<Selector
|
||||||
|
placeholder={$i18n.t('Select a model')}
|
||||||
|
items={$models
|
||||||
|
.filter((model) => model.name !== 'hr')
|
||||||
|
.map((model) => ({
|
||||||
|
value: model.id,
|
||||||
|
label: model.name,
|
||||||
|
info: model
|
||||||
|
}))}
|
||||||
bind:value={selectedModelId}
|
bind:value={selectedModelId}
|
||||||
>
|
/>
|
||||||
<option class=" text-gray-800" value="" selected disabled
|
</div>
|
||||||
>{$i18n.t('Select a model')}</option
|
</div>
|
||||||
>
|
</div>
|
||||||
|
|
||||||
{#each $models as model}
|
|
||||||
{#if model.name === 'hr'}
|
|
||||||
<hr />
|
|
||||||
{:else}
|
|
||||||
<option value={model.id} class="text-gray-800 text-lg"
|
|
||||||
>{model.name +
|
|
||||||
`${model.size ? ` (${(model.size / 1024 ** 3).toFixed(1)}GB)` : ''}`}</option
|
|
||||||
>
|
|
||||||
{/if}
|
|
||||||
{/each}
|
|
||||||
</select>
|
|
||||||
|
|
||||||
<!-- <button
|
<!-- <button
|
||||||
class=" self-center dark:hover:text-gray-300"
|
class=" self-center dark:hover:text-gray-300"
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
import '../tailwind.css';
|
import '../tailwind.css';
|
||||||
import 'tippy.js/dist/tippy.css';
|
import 'tippy.js/dist/tippy.css';
|
||||||
import { WEBUI_BASE_URL } from '$lib/constants';
|
import { WEBUI_BASE_URL } from '$lib/constants';
|
||||||
import i18n from '$lib/i18n';
|
import i18n, { initI18n } from '$lib/i18n';
|
||||||
|
|
||||||
setContext('i18n', i18n);
|
setContext('i18n', i18n);
|
||||||
|
|
||||||
|
@ -25,6 +25,11 @@
|
||||||
if (backendConfig) {
|
if (backendConfig) {
|
||||||
// Save Backend Status to Store
|
// Save Backend Status to Store
|
||||||
await config.set(backendConfig);
|
await config.set(backendConfig);
|
||||||
|
if ($config.default_locale) {
|
||||||
|
initI18n($config.default_locale);
|
||||||
|
} else {
|
||||||
|
initI18n();
|
||||||
|
}
|
||||||
|
|
||||||
await WEBUI_NAME.set(backendConfig.name);
|
await WEBUI_NAME.set(backendConfig.name);
|
||||||
console.log(backendConfig);
|
console.log(backendConfig);
|
||||||
|
|
|
@ -3,7 +3,8 @@
|
||||||
@tailwind utilities;
|
@tailwind utilities;
|
||||||
|
|
||||||
@layer base {
|
@layer base {
|
||||||
html, pre {
|
html,
|
||||||
|
pre {
|
||||||
font-family: -apple-system, 'Arimo', ui-sans-serif, system-ui, 'Segoe UI', Roboto, Ubuntu,
|
font-family: -apple-system, 'Arimo', ui-sans-serif, system-ui, 'Segoe UI', Roboto, Ubuntu,
|
||||||
Cantarell, 'Noto Sans', sans-serif, 'Helvetica Neue', Arial, 'Apple Color Emoji',
|
Cantarell, 'Noto Sans', sans-serif, 'Helvetica Neue', Arial, 'Apple Color Emoji',
|
||||||
'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji';
|
'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji';
|
||||||
|
|
|
@ -16,6 +16,12 @@ const config = {
|
||||||
assets: 'build',
|
assets: 'build',
|
||||||
fallback: 'index.html'
|
fallback: 'index.html'
|
||||||
})
|
})
|
||||||
|
},
|
||||||
|
onwarn: (warning, handler) => {
|
||||||
|
const { code, _ } = warning;
|
||||||
|
if (code === 'css-unused-selector') return;
|
||||||
|
|
||||||
|
handler(warning);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -16,9 +16,8 @@ export default {
|
||||||
700: '#4e4e4e',
|
700: '#4e4e4e',
|
||||||
800: '#333',
|
800: '#333',
|
||||||
850: '#262626',
|
850: '#262626',
|
||||||
|
900: 'var(--color-gray-900, #171717)',
|
||||||
900: '#171717',
|
950: 'var(--color-gray-950, #0d0d0d)'
|
||||||
950: '#0d0d0d'
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
typography: {
|
typography: {
|
||||||
|
|
|
@ -1,6 +0,0 @@
|
||||||
{
|
|
||||||
"model_name": "string",
|
|
||||||
"litellm_params": {
|
|
||||||
"model": "ollama/mistral"
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in a new issue