mirror of
https://git.mirrors.martin98.com/https://github.com/open-webui/open-webui
synced 2025-08-19 19:49:12 +08:00
Merge pull request #2505 from open-webui/dev-models
feat: openai api compatible model presets (profiles/modelfiles)
This commit is contained in:
commit
67a5020cdc
@ -18,8 +18,9 @@ import requests
|
||||
from pydantic import BaseModel, ConfigDict
|
||||
from typing import Optional, List
|
||||
|
||||
from apps.web.models.models import Models
|
||||
from utils.utils import get_verified_user, get_current_user, get_admin_user
|
||||
from config import SRC_LOG_LEVELS, ENV
|
||||
from config import SRC_LOG_LEVELS
|
||||
from constants import MESSAGES
|
||||
|
||||
import os
|
||||
@ -77,7 +78,7 @@ with open(LITELLM_CONFIG_DIR, "r") as file:
|
||||
|
||||
app.state.ENABLE_MODEL_FILTER = ENABLE_MODEL_FILTER.value
|
||||
app.state.MODEL_FILTER_LIST = MODEL_FILTER_LIST.value
|
||||
|
||||
app.state.MODEL_CONFIG = Models.get_all_models()
|
||||
|
||||
app.state.ENABLE = ENABLE_LITELLM
|
||||
app.state.CONFIG = litellm_config
|
||||
@ -261,6 +262,14 @@ async def get_models(user=Depends(get_current_user)):
|
||||
"object": "model",
|
||||
"created": int(time.time()),
|
||||
"owned_by": "openai",
|
||||
"custom_info": next(
|
||||
(
|
||||
item
|
||||
for item in app.state.MODEL_CONFIG
|
||||
if item.id == model["model_name"]
|
||||
),
|
||||
None,
|
||||
),
|
||||
}
|
||||
for model in app.state.CONFIG["model_list"]
|
||||
],
|
||||
|
@ -29,7 +29,7 @@ import time
|
||||
from urllib.parse import urlparse
|
||||
from typing import Optional, List, Union
|
||||
|
||||
|
||||
from apps.web.models.models import Models
|
||||
from apps.web.models.users import Users
|
||||
from constants import ERROR_MESSAGES
|
||||
from utils.utils import (
|
||||
@ -39,6 +39,8 @@ from utils.utils import (
|
||||
get_admin_user,
|
||||
)
|
||||
|
||||
from utils.models import get_model_id_from_custom_model_id
|
||||
|
||||
|
||||
from config import (
|
||||
SRC_LOG_LEVELS,
|
||||
@ -68,7 +70,6 @@ app.state.config = AppConfig()
|
||||
app.state.config.ENABLE_MODEL_FILTER = ENABLE_MODEL_FILTER
|
||||
app.state.config.MODEL_FILTER_LIST = MODEL_FILTER_LIST
|
||||
|
||||
|
||||
app.state.config.ENABLE_OLLAMA_API = ENABLE_OLLAMA_API
|
||||
app.state.config.OLLAMA_BASE_URLS = OLLAMA_BASE_URLS
|
||||
app.state.MODELS = {}
|
||||
@ -875,14 +876,93 @@ async def generate_chat_completion(
|
||||
user=Depends(get_verified_user),
|
||||
):
|
||||
|
||||
log.debug(
|
||||
"form_data.model_dump_json(exclude_none=True).encode(): {0} ".format(
|
||||
form_data.model_dump_json(exclude_none=True).encode()
|
||||
)
|
||||
)
|
||||
|
||||
payload = {
|
||||
**form_data.model_dump(exclude_none=True),
|
||||
}
|
||||
|
||||
model_id = form_data.model
|
||||
model_info = Models.get_model_by_id(model_id)
|
||||
|
||||
if model_info:
|
||||
print(model_info)
|
||||
if model_info.base_model_id:
|
||||
payload["model"] = model_info.base_model_id
|
||||
|
||||
model_info.params = model_info.params.model_dump()
|
||||
|
||||
if model_info.params:
|
||||
payload["options"] = {}
|
||||
|
||||
payload["options"]["mirostat"] = model_info.params.get("mirostat", None)
|
||||
payload["options"]["mirostat_eta"] = model_info.params.get(
|
||||
"mirostat_eta", None
|
||||
)
|
||||
payload["options"]["mirostat_tau"] = model_info.params.get(
|
||||
"mirostat_tau", None
|
||||
)
|
||||
payload["options"]["num_ctx"] = model_info.params.get("num_ctx", None)
|
||||
|
||||
payload["options"]["repeat_last_n"] = model_info.params.get(
|
||||
"repeat_last_n", None
|
||||
)
|
||||
payload["options"]["repeat_penalty"] = model_info.params.get(
|
||||
"frequency_penalty", None
|
||||
)
|
||||
|
||||
payload["options"]["temperature"] = model_info.params.get(
|
||||
"temperature", None
|
||||
)
|
||||
payload["options"]["seed"] = model_info.params.get("seed", None)
|
||||
|
||||
payload["options"]["stop"] = (
|
||||
[
|
||||
bytes(stop, "utf-8").decode("unicode_escape")
|
||||
for stop in model_info.params["stop"]
|
||||
]
|
||||
if model_info.params.get("stop", None)
|
||||
else None
|
||||
)
|
||||
|
||||
payload["options"]["tfs_z"] = model_info.params.get("tfs_z", None)
|
||||
|
||||
payload["options"]["num_predict"] = model_info.params.get(
|
||||
"max_tokens", None
|
||||
)
|
||||
payload["options"]["top_k"] = model_info.params.get("top_k", None)
|
||||
|
||||
payload["options"]["top_p"] = model_info.params.get("top_p", None)
|
||||
|
||||
if model_info.params.get("system", None):
|
||||
# Check if the payload already has a system message
|
||||
# If not, add a system message to the payload
|
||||
if payload.get("messages"):
|
||||
for message in payload["messages"]:
|
||||
if message.get("role") == "system":
|
||||
message["content"] = (
|
||||
model_info.params.get("system", None) + message["content"]
|
||||
)
|
||||
break
|
||||
else:
|
||||
payload["messages"].insert(
|
||||
0,
|
||||
{
|
||||
"role": "system",
|
||||
"content": model_info.params.get("system", None),
|
||||
},
|
||||
)
|
||||
|
||||
if url_idx == None:
|
||||
model = form_data.model
|
||||
if ":" not in payload["model"]:
|
||||
payload["model"] = f"{payload['model']}:latest"
|
||||
|
||||
if ":" not in model:
|
||||
model = f"{model}:latest"
|
||||
|
||||
if model in app.state.MODELS:
|
||||
url_idx = random.choice(app.state.MODELS[model]["urls"])
|
||||
if payload["model"] in app.state.MODELS:
|
||||
url_idx = random.choice(app.state.MODELS[payload["model"]]["urls"])
|
||||
else:
|
||||
raise HTTPException(
|
||||
status_code=400,
|
||||
@ -892,16 +972,12 @@ async def generate_chat_completion(
|
||||
url = app.state.config.OLLAMA_BASE_URLS[url_idx]
|
||||
log.info(f"url: {url}")
|
||||
|
||||
print(payload)
|
||||
|
||||
r = None
|
||||
|
||||
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():
|
||||
nonlocal form_data
|
||||
nonlocal payload
|
||||
nonlocal r
|
||||
|
||||
request_id = str(uuid.uuid4())
|
||||
@ -910,7 +986,7 @@ async def generate_chat_completion(
|
||||
|
||||
def stream_content():
|
||||
try:
|
||||
if form_data.stream:
|
||||
if payload.get("stream", None):
|
||||
yield json.dumps({"id": request_id, "done": False}) + "\n"
|
||||
|
||||
for chunk in r.iter_content(chunk_size=8192):
|
||||
@ -928,7 +1004,7 @@ async def generate_chat_completion(
|
||||
r = requests.request(
|
||||
method="POST",
|
||||
url=f"{url}/api/chat",
|
||||
data=form_data.model_dump_json(exclude_none=True).encode(),
|
||||
data=json.dumps(payload),
|
||||
stream=True,
|
||||
)
|
||||
|
||||
@ -984,14 +1060,62 @@ async def generate_openai_chat_completion(
|
||||
user=Depends(get_verified_user),
|
||||
):
|
||||
|
||||
payload = {
|
||||
**form_data.model_dump(exclude_none=True),
|
||||
}
|
||||
|
||||
model_id = form_data.model
|
||||
model_info = Models.get_model_by_id(model_id)
|
||||
|
||||
if model_info:
|
||||
print(model_info)
|
||||
if model_info.base_model_id:
|
||||
payload["model"] = model_info.base_model_id
|
||||
|
||||
model_info.params = model_info.params.model_dump()
|
||||
|
||||
if model_info.params:
|
||||
payload["temperature"] = model_info.params.get("temperature", None)
|
||||
payload["top_p"] = model_info.params.get("top_p", None)
|
||||
payload["max_tokens"] = model_info.params.get("max_tokens", None)
|
||||
payload["frequency_penalty"] = model_info.params.get(
|
||||
"frequency_penalty", None
|
||||
)
|
||||
payload["seed"] = model_info.params.get("seed", None)
|
||||
payload["stop"] = (
|
||||
[
|
||||
bytes(stop, "utf-8").decode("unicode_escape")
|
||||
for stop in model_info.params["stop"]
|
||||
]
|
||||
if model_info.params.get("stop", None)
|
||||
else None
|
||||
)
|
||||
|
||||
if model_info.params.get("system", None):
|
||||
# Check if the payload already has a system message
|
||||
# If not, add a system message to the payload
|
||||
if payload.get("messages"):
|
||||
for message in payload["messages"]:
|
||||
if message.get("role") == "system":
|
||||
message["content"] = (
|
||||
model_info.params.get("system", None) + message["content"]
|
||||
)
|
||||
break
|
||||
else:
|
||||
payload["messages"].insert(
|
||||
0,
|
||||
{
|
||||
"role": "system",
|
||||
"content": model_info.params.get("system", None),
|
||||
},
|
||||
)
|
||||
|
||||
if url_idx == None:
|
||||
model = form_data.model
|
||||
if ":" not in payload["model"]:
|
||||
payload["model"] = f"{payload['model']}:latest"
|
||||
|
||||
if ":" not in model:
|
||||
model = f"{model}:latest"
|
||||
|
||||
if model in app.state.MODELS:
|
||||
url_idx = random.choice(app.state.MODELS[model]["urls"])
|
||||
if payload["model"] in app.state.MODELS:
|
||||
url_idx = random.choice(app.state.MODELS[payload["model"]]["urls"])
|
||||
else:
|
||||
raise HTTPException(
|
||||
status_code=400,
|
||||
@ -1004,7 +1128,7 @@ async def generate_openai_chat_completion(
|
||||
r = None
|
||||
|
||||
def get_request():
|
||||
nonlocal form_data
|
||||
nonlocal payload
|
||||
nonlocal r
|
||||
|
||||
request_id = str(uuid.uuid4())
|
||||
@ -1013,7 +1137,7 @@ async def generate_openai_chat_completion(
|
||||
|
||||
def stream_content():
|
||||
try:
|
||||
if form_data.stream:
|
||||
if payload.get("stream"):
|
||||
yield json.dumps(
|
||||
{"request_id": request_id, "done": False}
|
||||
) + "\n"
|
||||
@ -1033,7 +1157,7 @@ async def generate_openai_chat_completion(
|
||||
r = requests.request(
|
||||
method="POST",
|
||||
url=f"{url}/v1/chat/completions",
|
||||
data=form_data.model_dump_json(exclude_none=True).encode(),
|
||||
data=json.dumps(payload),
|
||||
stream=True,
|
||||
)
|
||||
|
||||
|
@ -10,7 +10,7 @@ import logging
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
from apps.web.models.models import Models
|
||||
from apps.web.models.users import Users
|
||||
from constants import ERROR_MESSAGES
|
||||
from utils.utils import (
|
||||
@ -53,7 +53,6 @@ app.state.config = AppConfig()
|
||||
app.state.config.ENABLE_MODEL_FILTER = ENABLE_MODEL_FILTER
|
||||
app.state.config.MODEL_FILTER_LIST = MODEL_FILTER_LIST
|
||||
|
||||
|
||||
app.state.config.ENABLE_OPENAI_API = ENABLE_OPENAI_API
|
||||
app.state.config.OPENAI_API_BASE_URLS = OPENAI_API_BASE_URLS
|
||||
app.state.config.OPENAI_API_KEYS = OPENAI_API_KEYS
|
||||
@ -206,7 +205,13 @@ def merge_models_lists(model_lists):
|
||||
if models is not None and "error" not in models:
|
||||
merged_list.extend(
|
||||
[
|
||||
{**model, "urlIdx": idx}
|
||||
{
|
||||
**model,
|
||||
"name": model.get("name", model["id"]),
|
||||
"owned_by": "openai",
|
||||
"openai": model,
|
||||
"urlIdx": idx,
|
||||
}
|
||||
for model in models
|
||||
if "api.openai.com"
|
||||
not in app.state.config.OPENAI_API_BASE_URLS[idx]
|
||||
@ -252,7 +257,7 @@ async def get_all_models():
|
||||
log.info(f"models: {models}")
|
||||
app.state.MODELS = {model["id"]: model for model in models["data"]}
|
||||
|
||||
return models
|
||||
return models
|
||||
|
||||
|
||||
@app.get("/models")
|
||||
@ -310,39 +315,93 @@ async def proxy(path: str, request: Request, user=Depends(get_verified_user)):
|
||||
body = await request.body()
|
||||
# TODO: Remove below after gpt-4-vision fix from Open AI
|
||||
# Try to decode the body of the request from bytes to a UTF-8 string (Require add max_token to fix gpt-4-vision)
|
||||
|
||||
payload = None
|
||||
|
||||
try:
|
||||
body = body.decode("utf-8")
|
||||
body = json.loads(body)
|
||||
if "chat/completions" in path:
|
||||
body = body.decode("utf-8")
|
||||
body = json.loads(body)
|
||||
|
||||
model = app.state.MODELS[body.get("model")]
|
||||
payload = {**body}
|
||||
|
||||
idx = model["urlIdx"]
|
||||
model_id = body.get("model")
|
||||
model_info = Models.get_model_by_id(model_id)
|
||||
|
||||
if "pipeline" in model and model.get("pipeline"):
|
||||
body["user"] = {"name": user.name, "id": user.id}
|
||||
body["title"] = (
|
||||
True if body["stream"] == False and body["max_tokens"] == 50 else False
|
||||
)
|
||||
if model_info:
|
||||
print(model_info)
|
||||
if model_info.base_model_id:
|
||||
payload["model"] = model_info.base_model_id
|
||||
|
||||
# Check if the model is "gpt-4-vision-preview" and set "max_tokens" to 4000
|
||||
# This is a workaround until OpenAI fixes the issue with this model
|
||||
if body.get("model") == "gpt-4-vision-preview":
|
||||
if "max_tokens" not in body:
|
||||
body["max_tokens"] = 4000
|
||||
log.debug("Modified body_dict:", body)
|
||||
model_info.params = model_info.params.model_dump()
|
||||
|
||||
# Fix for ChatGPT calls failing because the num_ctx key is in body
|
||||
if "num_ctx" in body:
|
||||
# If 'num_ctx' is in the dictionary, delete it
|
||||
# Leaving it there generates an error with the
|
||||
# OpenAI API (Feb 2024)
|
||||
del body["num_ctx"]
|
||||
if model_info.params:
|
||||
payload["temperature"] = model_info.params.get("temperature", None)
|
||||
payload["top_p"] = model_info.params.get("top_p", None)
|
||||
payload["max_tokens"] = model_info.params.get("max_tokens", None)
|
||||
payload["frequency_penalty"] = model_info.params.get(
|
||||
"frequency_penalty", None
|
||||
)
|
||||
payload["seed"] = model_info.params.get("seed", None)
|
||||
payload["stop"] = (
|
||||
[
|
||||
bytes(stop, "utf-8").decode("unicode_escape")
|
||||
for stop in model_info.params["stop"]
|
||||
]
|
||||
if model_info.params.get("stop", None)
|
||||
else None
|
||||
)
|
||||
|
||||
if model_info.params.get("system", None):
|
||||
# Check if the payload already has a system message
|
||||
# If not, add a system message to the payload
|
||||
if payload.get("messages"):
|
||||
for message in payload["messages"]:
|
||||
if message.get("role") == "system":
|
||||
message["content"] = (
|
||||
model_info.params.get("system", None)
|
||||
+ message["content"]
|
||||
)
|
||||
break
|
||||
else:
|
||||
payload["messages"].insert(
|
||||
0,
|
||||
{
|
||||
"role": "system",
|
||||
"content": model_info.params.get("system", None),
|
||||
},
|
||||
)
|
||||
else:
|
||||
pass
|
||||
|
||||
print(app.state.MODELS)
|
||||
model = app.state.MODELS[payload.get("model")]
|
||||
|
||||
idx = model["urlIdx"]
|
||||
|
||||
if "pipeline" in model and model.get("pipeline"):
|
||||
payload["user"] = {"name": user.name, "id": user.id}
|
||||
payload["title"] = (
|
||||
True
|
||||
if payload["stream"] == False and payload["max_tokens"] == 50
|
||||
else False
|
||||
)
|
||||
|
||||
# Check if the model is "gpt-4-vision-preview" and set "max_tokens" to 4000
|
||||
# This is a workaround until OpenAI fixes the issue with this model
|
||||
if payload.get("model") == "gpt-4-vision-preview":
|
||||
if "max_tokens" not in payload:
|
||||
payload["max_tokens"] = 4000
|
||||
log.debug("Modified payload:", payload)
|
||||
|
||||
# Convert the modified body back to JSON
|
||||
payload = json.dumps(payload)
|
||||
|
||||
# Convert the modified body back to JSON
|
||||
body = json.dumps(body)
|
||||
except json.JSONDecodeError as e:
|
||||
log.error("Error loading request body into a dictionary:", e)
|
||||
|
||||
print(payload)
|
||||
|
||||
url = app.state.config.OPENAI_API_BASE_URLS[idx]
|
||||
key = app.state.config.OPENAI_API_KEYS[idx]
|
||||
|
||||
@ -361,7 +420,7 @@ async def proxy(path: str, request: Request, user=Depends(get_verified_user)):
|
||||
r = requests.request(
|
||||
method=request.method,
|
||||
url=target_url,
|
||||
data=body,
|
||||
data=payload if payload else body,
|
||||
headers=headers,
|
||||
stream=True,
|
||||
)
|
||||
|
@ -1,3 +1,5 @@
|
||||
import json
|
||||
|
||||
from peewee import *
|
||||
from peewee_migrate import Router
|
||||
from playhouse.db_url import connect
|
||||
@ -8,6 +10,16 @@ import logging
|
||||
log = logging.getLogger(__name__)
|
||||
log.setLevel(SRC_LOG_LEVELS["DB"])
|
||||
|
||||
|
||||
class JSONField(TextField):
|
||||
def db_value(self, value):
|
||||
return json.dumps(value)
|
||||
|
||||
def python_value(self, value):
|
||||
if value is not None:
|
||||
return json.loads(value)
|
||||
|
||||
|
||||
# Check if the file exists
|
||||
if os.path.exists(f"{DATA_DIR}/ollama.db"):
|
||||
# Rename the file
|
||||
|
61
backend/apps/web/internal/migrations/009_add_models.py
Normal file
61
backend/apps/web/internal/migrations/009_add_models.py
Normal file
@ -0,0 +1,61 @@
|
||||
"""Peewee migrations -- 009_add_models.py.
|
||||
|
||||
Some examples (model - class or model name)::
|
||||
|
||||
> Model = migrator.orm['table_name'] # Return model in current state by name
|
||||
> Model = migrator.ModelClass # Return model in current state by name
|
||||
|
||||
> migrator.sql(sql) # Run custom SQL
|
||||
> migrator.run(func, *args, **kwargs) # Run python function with the given args
|
||||
> migrator.create_model(Model) # Create a model (could be used as decorator)
|
||||
> migrator.remove_model(model, cascade=True) # Remove a model
|
||||
> migrator.add_fields(model, **fields) # Add fields to a model
|
||||
> migrator.change_fields(model, **fields) # Change fields
|
||||
> migrator.remove_fields(model, *field_names, cascade=True)
|
||||
> migrator.rename_field(model, old_field_name, new_field_name)
|
||||
> migrator.rename_table(model, new_table_name)
|
||||
> migrator.add_index(model, *col_names, unique=False)
|
||||
> migrator.add_not_null(model, *field_names)
|
||||
> migrator.add_default(model, field_name, default)
|
||||
> migrator.add_constraint(model, name, sql)
|
||||
> migrator.drop_index(model, *col_names)
|
||||
> migrator.drop_not_null(model, *field_names)
|
||||
> migrator.drop_constraints(model, *constraints)
|
||||
|
||||
"""
|
||||
|
||||
from contextlib import suppress
|
||||
|
||||
import peewee as pw
|
||||
from peewee_migrate import Migrator
|
||||
|
||||
|
||||
with suppress(ImportError):
|
||||
import playhouse.postgres_ext as pw_pext
|
||||
|
||||
|
||||
def migrate(migrator: Migrator, database: pw.Database, *, fake=False):
|
||||
"""Write your migrations here."""
|
||||
|
||||
@migrator.create_model
|
||||
class Model(pw.Model):
|
||||
id = pw.TextField(unique=True)
|
||||
user_id = pw.TextField()
|
||||
base_model_id = pw.TextField(null=True)
|
||||
|
||||
name = pw.TextField()
|
||||
|
||||
meta = pw.TextField()
|
||||
params = pw.TextField()
|
||||
|
||||
created_at = pw.BigIntegerField(null=False)
|
||||
updated_at = pw.BigIntegerField(null=False)
|
||||
|
||||
class Meta:
|
||||
table_name = "model"
|
||||
|
||||
|
||||
def rollback(migrator: Migrator, database: pw.Database, *, fake=False):
|
||||
"""Write your rollback migrations here."""
|
||||
|
||||
migrator.remove_model("model")
|
@ -0,0 +1,130 @@
|
||||
"""Peewee migrations -- 009_add_models.py.
|
||||
|
||||
Some examples (model - class or model name)::
|
||||
|
||||
> Model = migrator.orm['table_name'] # Return model in current state by name
|
||||
> Model = migrator.ModelClass # Return model in current state by name
|
||||
|
||||
> migrator.sql(sql) # Run custom SQL
|
||||
> migrator.run(func, *args, **kwargs) # Run python function with the given args
|
||||
> migrator.create_model(Model) # Create a model (could be used as decorator)
|
||||
> migrator.remove_model(model, cascade=True) # Remove a model
|
||||
> migrator.add_fields(model, **fields) # Add fields to a model
|
||||
> migrator.change_fields(model, **fields) # Change fields
|
||||
> migrator.remove_fields(model, *field_names, cascade=True)
|
||||
> migrator.rename_field(model, old_field_name, new_field_name)
|
||||
> migrator.rename_table(model, new_table_name)
|
||||
> migrator.add_index(model, *col_names, unique=False)
|
||||
> migrator.add_not_null(model, *field_names)
|
||||
> migrator.add_default(model, field_name, default)
|
||||
> migrator.add_constraint(model, name, sql)
|
||||
> migrator.drop_index(model, *col_names)
|
||||
> migrator.drop_not_null(model, *field_names)
|
||||
> migrator.drop_constraints(model, *constraints)
|
||||
|
||||
"""
|
||||
|
||||
from contextlib import suppress
|
||||
|
||||
import peewee as pw
|
||||
from peewee_migrate import Migrator
|
||||
import json
|
||||
|
||||
from utils.misc import parse_ollama_modelfile
|
||||
|
||||
with suppress(ImportError):
|
||||
import playhouse.postgres_ext as pw_pext
|
||||
|
||||
|
||||
def migrate(migrator: Migrator, database: pw.Database, *, fake=False):
|
||||
"""Write your migrations here."""
|
||||
|
||||
# Fetch data from 'modelfile' table and insert into 'model' table
|
||||
migrate_modelfile_to_model(migrator, database)
|
||||
# Drop the 'modelfile' table
|
||||
migrator.remove_model("modelfile")
|
||||
|
||||
|
||||
def migrate_modelfile_to_model(migrator: Migrator, database: pw.Database):
|
||||
ModelFile = migrator.orm["modelfile"]
|
||||
Model = migrator.orm["model"]
|
||||
|
||||
modelfiles = ModelFile.select()
|
||||
|
||||
for modelfile in modelfiles:
|
||||
# Extract and transform data in Python
|
||||
|
||||
modelfile.modelfile = json.loads(modelfile.modelfile)
|
||||
meta = json.dumps(
|
||||
{
|
||||
"description": modelfile.modelfile.get("desc"),
|
||||
"profile_image_url": modelfile.modelfile.get("imageUrl"),
|
||||
"ollama": {"modelfile": modelfile.modelfile.get("content")},
|
||||
"suggestion_prompts": modelfile.modelfile.get("suggestionPrompts"),
|
||||
"categories": modelfile.modelfile.get("categories"),
|
||||
"user": {**modelfile.modelfile.get("user", {}), "community": True},
|
||||
}
|
||||
)
|
||||
|
||||
info = parse_ollama_modelfile(modelfile.modelfile.get("content"))
|
||||
|
||||
# Insert the processed data into the 'model' table
|
||||
Model.create(
|
||||
id=f"ollama-{modelfile.tag_name}",
|
||||
user_id=modelfile.user_id,
|
||||
base_model_id=info.get("base_model_id"),
|
||||
name=modelfile.modelfile.get("title"),
|
||||
meta=meta,
|
||||
params=json.dumps(info.get("params", {})),
|
||||
created_at=modelfile.timestamp,
|
||||
updated_at=modelfile.timestamp,
|
||||
)
|
||||
|
||||
|
||||
def rollback(migrator: Migrator, database: pw.Database, *, fake=False):
|
||||
"""Write your rollback migrations here."""
|
||||
|
||||
recreate_modelfile_table(migrator, database)
|
||||
move_data_back_to_modelfile(migrator, database)
|
||||
migrator.remove_model("model")
|
||||
|
||||
|
||||
def recreate_modelfile_table(migrator: Migrator, database: pw.Database):
|
||||
query = """
|
||||
CREATE TABLE IF NOT EXISTS modelfile (
|
||||
user_id TEXT,
|
||||
tag_name TEXT,
|
||||
modelfile JSON,
|
||||
timestamp BIGINT
|
||||
)
|
||||
"""
|
||||
migrator.sql(query)
|
||||
|
||||
|
||||
def move_data_back_to_modelfile(migrator: Migrator, database: pw.Database):
|
||||
Model = migrator.orm["model"]
|
||||
Modelfile = migrator.orm["modelfile"]
|
||||
|
||||
models = Model.select()
|
||||
|
||||
for model in models:
|
||||
# Extract and transform data in Python
|
||||
meta = json.loads(model.meta)
|
||||
|
||||
modelfile_data = {
|
||||
"title": model.name,
|
||||
"desc": meta.get("description"),
|
||||
"imageUrl": meta.get("profile_image_url"),
|
||||
"content": meta.get("ollama", {}).get("modelfile"),
|
||||
"suggestionPrompts": meta.get("suggestion_prompts"),
|
||||
"categories": meta.get("categories"),
|
||||
"user": {k: v for k, v in meta.get("user", {}).items() if k != "community"},
|
||||
}
|
||||
|
||||
# Insert the processed data back into the 'modelfile' table
|
||||
Modelfile.create(
|
||||
user_id=model.user_id,
|
||||
tag_name=model.id,
|
||||
modelfile=modelfile_data,
|
||||
timestamp=model.created_at,
|
||||
)
|
@ -6,7 +6,7 @@ from apps.web.routers import (
|
||||
users,
|
||||
chats,
|
||||
documents,
|
||||
modelfiles,
|
||||
models,
|
||||
prompts,
|
||||
configs,
|
||||
memories,
|
||||
@ -40,6 +40,9 @@ app.state.config.DEFAULT_PROMPT_SUGGESTIONS = DEFAULT_PROMPT_SUGGESTIONS
|
||||
app.state.config.DEFAULT_USER_ROLE = DEFAULT_USER_ROLE
|
||||
app.state.config.USER_PERMISSIONS = USER_PERMISSIONS
|
||||
app.state.config.WEBHOOK_URL = WEBHOOK_URL
|
||||
|
||||
|
||||
app.state.MODELS = {}
|
||||
app.state.AUTH_TRUSTED_EMAIL_HEADER = WEBUI_AUTH_TRUSTED_EMAIL_HEADER
|
||||
|
||||
|
||||
@ -56,11 +59,10 @@ app.include_router(users.router, prefix="/users", tags=["users"])
|
||||
app.include_router(chats.router, prefix="/chats", tags=["chats"])
|
||||
|
||||
app.include_router(documents.router, prefix="/documents", tags=["documents"])
|
||||
app.include_router(modelfiles.router, prefix="/modelfiles", tags=["modelfiles"])
|
||||
app.include_router(models.router, prefix="/models", tags=["models"])
|
||||
app.include_router(prompts.router, prefix="/prompts", tags=["prompts"])
|
||||
app.include_router(memories.router, prefix="/memories", tags=["memories"])
|
||||
|
||||
|
||||
app.include_router(configs.router, prefix="/configs", tags=["configs"])
|
||||
app.include_router(utils.router, prefix="/utils", tags=["utils"])
|
||||
|
||||
|
@ -1,3 +1,11 @@
|
||||
################################################################################
|
||||
# DEPRECATION NOTICE #
|
||||
# #
|
||||
# This file has been deprecated since version 0.2.0. #
|
||||
# #
|
||||
################################################################################
|
||||
|
||||
|
||||
from pydantic import BaseModel
|
||||
from peewee import *
|
||||
from playhouse.shortcuts import model_to_dict
|
||||
|
179
backend/apps/web/models/models.py
Normal file
179
backend/apps/web/models/models.py
Normal file
@ -0,0 +1,179 @@
|
||||
import json
|
||||
import logging
|
||||
from typing import Optional
|
||||
|
||||
import peewee as pw
|
||||
from peewee import *
|
||||
|
||||
from playhouse.shortcuts import model_to_dict
|
||||
from pydantic import BaseModel, ConfigDict
|
||||
|
||||
from apps.web.internal.db import DB, JSONField
|
||||
|
||||
from typing import List, Union, Optional
|
||||
from config import SRC_LOG_LEVELS
|
||||
|
||||
import time
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
log.setLevel(SRC_LOG_LEVELS["MODELS"])
|
||||
|
||||
|
||||
####################
|
||||
# Models DB Schema
|
||||
####################
|
||||
|
||||
|
||||
# ModelParams is a model for the data stored in the params field of the Model table
|
||||
class ModelParams(BaseModel):
|
||||
model_config = ConfigDict(extra="allow")
|
||||
pass
|
||||
|
||||
|
||||
# ModelMeta is a model for the data stored in the meta field of the Model table
|
||||
class ModelMeta(BaseModel):
|
||||
profile_image_url: Optional[str] = "/favicon.png"
|
||||
|
||||
description: Optional[str] = None
|
||||
"""
|
||||
User-facing description of the model.
|
||||
"""
|
||||
|
||||
capabilities: Optional[dict] = None
|
||||
|
||||
model_config = ConfigDict(extra="allow")
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class Model(pw.Model):
|
||||
id = pw.TextField(unique=True)
|
||||
"""
|
||||
The model's id as used in the API. If set to an existing model, it will override the model.
|
||||
"""
|
||||
user_id = pw.TextField()
|
||||
|
||||
base_model_id = pw.TextField(null=True)
|
||||
"""
|
||||
An optional pointer to the actual model that should be used when proxying requests.
|
||||
"""
|
||||
|
||||
name = pw.TextField()
|
||||
"""
|
||||
The human-readable display name of the model.
|
||||
"""
|
||||
|
||||
params = JSONField()
|
||||
"""
|
||||
Holds a JSON encoded blob of parameters, see `ModelParams`.
|
||||
"""
|
||||
|
||||
meta = JSONField()
|
||||
"""
|
||||
Holds a JSON encoded blob of metadata, see `ModelMeta`.
|
||||
"""
|
||||
|
||||
updated_at = BigIntegerField()
|
||||
created_at = BigIntegerField()
|
||||
|
||||
class Meta:
|
||||
database = DB
|
||||
|
||||
|
||||
class ModelModel(BaseModel):
|
||||
id: str
|
||||
user_id: str
|
||||
base_model_id: Optional[str] = None
|
||||
|
||||
name: str
|
||||
params: ModelParams
|
||||
meta: ModelMeta
|
||||
|
||||
updated_at: int # timestamp in epoch
|
||||
created_at: int # timestamp in epoch
|
||||
|
||||
|
||||
####################
|
||||
# Forms
|
||||
####################
|
||||
|
||||
|
||||
class ModelResponse(BaseModel):
|
||||
id: str
|
||||
name: str
|
||||
meta: ModelMeta
|
||||
updated_at: int # timestamp in epoch
|
||||
created_at: int # timestamp in epoch
|
||||
|
||||
|
||||
class ModelForm(BaseModel):
|
||||
id: str
|
||||
base_model_id: Optional[str] = None
|
||||
name: str
|
||||
meta: ModelMeta
|
||||
params: ModelParams
|
||||
|
||||
|
||||
class ModelsTable:
|
||||
def __init__(
|
||||
self,
|
||||
db: pw.SqliteDatabase | pw.PostgresqlDatabase,
|
||||
):
|
||||
self.db = db
|
||||
self.db.create_tables([Model])
|
||||
|
||||
def insert_new_model(
|
||||
self, form_data: ModelForm, user_id: str
|
||||
) -> Optional[ModelModel]:
|
||||
model = ModelModel(
|
||||
**{
|
||||
**form_data.model_dump(),
|
||||
"user_id": user_id,
|
||||
"created_at": int(time.time()),
|
||||
"updated_at": int(time.time()),
|
||||
}
|
||||
)
|
||||
try:
|
||||
result = Model.create(**model.model_dump())
|
||||
|
||||
if result:
|
||||
return model
|
||||
else:
|
||||
return None
|
||||
except Exception as e:
|
||||
print(e)
|
||||
return None
|
||||
|
||||
def get_all_models(self) -> List[ModelModel]:
|
||||
return [ModelModel(**model_to_dict(model)) for model in Model.select()]
|
||||
|
||||
def get_model_by_id(self, id: str) -> Optional[ModelModel]:
|
||||
try:
|
||||
model = Model.get(Model.id == id)
|
||||
return ModelModel(**model_to_dict(model))
|
||||
except:
|
||||
return None
|
||||
|
||||
def update_model_by_id(self, id: str, model: ModelForm) -> Optional[ModelModel]:
|
||||
try:
|
||||
# update only the fields that are present in the model
|
||||
query = Model.update(**model.model_dump()).where(Model.id == id)
|
||||
query.execute()
|
||||
|
||||
model = Model.get(Model.id == id)
|
||||
return ModelModel(**model_to_dict(model))
|
||||
except Exception as e:
|
||||
print(e)
|
||||
|
||||
return None
|
||||
|
||||
def delete_model_by_id(self, id: str) -> bool:
|
||||
try:
|
||||
query = Model.delete().where(Model.id == id)
|
||||
query.execute()
|
||||
return True
|
||||
except:
|
||||
return False
|
||||
|
||||
|
||||
Models = ModelsTable(DB)
|
@ -1,124 +0,0 @@
|
||||
from fastapi import Depends, FastAPI, HTTPException, status
|
||||
from datetime import datetime, timedelta
|
||||
from typing import List, Union, Optional
|
||||
|
||||
from fastapi import APIRouter
|
||||
from pydantic import BaseModel
|
||||
import json
|
||||
from apps.web.models.modelfiles import (
|
||||
Modelfiles,
|
||||
ModelfileForm,
|
||||
ModelfileTagNameForm,
|
||||
ModelfileUpdateForm,
|
||||
ModelfileResponse,
|
||||
)
|
||||
|
||||
from utils.utils import get_current_user, get_admin_user
|
||||
from constants import ERROR_MESSAGES
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
############################
|
||||
# GetModelfiles
|
||||
############################
|
||||
|
||||
|
||||
@router.get("/", response_model=List[ModelfileResponse])
|
||||
async def get_modelfiles(
|
||||
skip: int = 0, limit: int = 50, user=Depends(get_current_user)
|
||||
):
|
||||
return Modelfiles.get_modelfiles(skip, limit)
|
||||
|
||||
|
||||
############################
|
||||
# CreateNewModelfile
|
||||
############################
|
||||
|
||||
|
||||
@router.post("/create", response_model=Optional[ModelfileResponse])
|
||||
async def create_new_modelfile(form_data: ModelfileForm, user=Depends(get_admin_user)):
|
||||
modelfile = Modelfiles.insert_new_modelfile(user.id, form_data)
|
||||
|
||||
if modelfile:
|
||||
return ModelfileResponse(
|
||||
**{
|
||||
**modelfile.model_dump(),
|
||||
"modelfile": json.loads(modelfile.modelfile),
|
||||
}
|
||||
)
|
||||
else:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
detail=ERROR_MESSAGES.DEFAULT(),
|
||||
)
|
||||
|
||||
|
||||
############################
|
||||
# GetModelfileByTagName
|
||||
############################
|
||||
|
||||
|
||||
@router.post("/", response_model=Optional[ModelfileResponse])
|
||||
async def get_modelfile_by_tag_name(
|
||||
form_data: ModelfileTagNameForm, user=Depends(get_current_user)
|
||||
):
|
||||
modelfile = Modelfiles.get_modelfile_by_tag_name(form_data.tag_name)
|
||||
|
||||
if modelfile:
|
||||
return ModelfileResponse(
|
||||
**{
|
||||
**modelfile.model_dump(),
|
||||
"modelfile": json.loads(modelfile.modelfile),
|
||||
}
|
||||
)
|
||||
else:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
detail=ERROR_MESSAGES.NOT_FOUND,
|
||||
)
|
||||
|
||||
|
||||
############################
|
||||
# UpdateModelfileByTagName
|
||||
############################
|
||||
|
||||
|
||||
@router.post("/update", response_model=Optional[ModelfileResponse])
|
||||
async def update_modelfile_by_tag_name(
|
||||
form_data: ModelfileUpdateForm, user=Depends(get_admin_user)
|
||||
):
|
||||
modelfile = Modelfiles.get_modelfile_by_tag_name(form_data.tag_name)
|
||||
if modelfile:
|
||||
updated_modelfile = {
|
||||
**json.loads(modelfile.modelfile),
|
||||
**form_data.modelfile,
|
||||
}
|
||||
|
||||
modelfile = Modelfiles.update_modelfile_by_tag_name(
|
||||
form_data.tag_name, updated_modelfile
|
||||
)
|
||||
|
||||
return ModelfileResponse(
|
||||
**{
|
||||
**modelfile.model_dump(),
|
||||
"modelfile": json.loads(modelfile.modelfile),
|
||||
}
|
||||
)
|
||||
else:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
detail=ERROR_MESSAGES.ACCESS_PROHIBITED,
|
||||
)
|
||||
|
||||
|
||||
############################
|
||||
# DeleteModelfileByTagName
|
||||
############################
|
||||
|
||||
|
||||
@router.delete("/delete", response_model=bool)
|
||||
async def delete_modelfile_by_tag_name(
|
||||
form_data: ModelfileTagNameForm, user=Depends(get_admin_user)
|
||||
):
|
||||
result = Modelfiles.delete_modelfile_by_tag_name(form_data.tag_name)
|
||||
return result
|
108
backend/apps/web/routers/models.py
Normal file
108
backend/apps/web/routers/models.py
Normal file
@ -0,0 +1,108 @@
|
||||
from fastapi import Depends, FastAPI, HTTPException, status, Request
|
||||
from datetime import datetime, timedelta
|
||||
from typing import List, Union, Optional
|
||||
|
||||
from fastapi import APIRouter
|
||||
from pydantic import BaseModel
|
||||
import json
|
||||
from apps.web.models.models import Models, ModelModel, ModelForm, ModelResponse
|
||||
|
||||
from utils.utils import get_verified_user, get_admin_user
|
||||
from constants import ERROR_MESSAGES
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
###########################
|
||||
# getModels
|
||||
###########################
|
||||
|
||||
|
||||
@router.get("/", response_model=List[ModelResponse])
|
||||
async def get_models(user=Depends(get_verified_user)):
|
||||
return Models.get_all_models()
|
||||
|
||||
|
||||
############################
|
||||
# AddNewModel
|
||||
############################
|
||||
|
||||
|
||||
@router.post("/add", response_model=Optional[ModelModel])
|
||||
async def add_new_model(
|
||||
request: Request, form_data: ModelForm, user=Depends(get_admin_user)
|
||||
):
|
||||
if form_data.id in request.app.state.MODELS:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
detail=ERROR_MESSAGES.MODEL_ID_TAKEN,
|
||||
)
|
||||
else:
|
||||
model = Models.insert_new_model(form_data, user.id)
|
||||
|
||||
if model:
|
||||
return model
|
||||
else:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
detail=ERROR_MESSAGES.DEFAULT(),
|
||||
)
|
||||
|
||||
|
||||
############################
|
||||
# GetModelById
|
||||
############################
|
||||
|
||||
|
||||
@router.get("/{id}", response_model=Optional[ModelModel])
|
||||
async def get_model_by_id(id: str, user=Depends(get_verified_user)):
|
||||
model = Models.get_model_by_id(id)
|
||||
|
||||
if model:
|
||||
return model
|
||||
else:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
detail=ERROR_MESSAGES.NOT_FOUND,
|
||||
)
|
||||
|
||||
|
||||
############################
|
||||
# UpdateModelById
|
||||
############################
|
||||
|
||||
|
||||
@router.post("/{id}/update", response_model=Optional[ModelModel])
|
||||
async def update_model_by_id(
|
||||
request: Request, id: str, form_data: ModelForm, user=Depends(get_admin_user)
|
||||
):
|
||||
model = Models.get_model_by_id(id)
|
||||
if model:
|
||||
model = Models.update_model_by_id(id, form_data)
|
||||
return model
|
||||
else:
|
||||
if form_data.id in request.app.state.MODELS:
|
||||
model = Models.insert_new_model(form_data, user.id)
|
||||
print(model)
|
||||
if model:
|
||||
return model
|
||||
else:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
detail=ERROR_MESSAGES.DEFAULT(),
|
||||
)
|
||||
else:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
detail=ERROR_MESSAGES.DEFAULT(),
|
||||
)
|
||||
|
||||
|
||||
############################
|
||||
# DeleteModelById
|
||||
############################
|
||||
|
||||
|
||||
@router.delete("/{id}/delete", response_model=bool)
|
||||
async def delete_model_by_id(id: str, user=Depends(get_admin_user)):
|
||||
result = Models.delete_model_by_id(id)
|
||||
return result
|
@ -32,6 +32,8 @@ class ERROR_MESSAGES(str, Enum):
|
||||
COMMAND_TAKEN = "Uh-oh! This command is already registered. Please choose another command string."
|
||||
FILE_EXISTS = "Uh-oh! This file is already registered. Please choose another file."
|
||||
|
||||
MODEL_ID_TAKEN = "Uh-oh! This model id is already registered. Please choose another model id string."
|
||||
|
||||
NAME_TAG_TAKEN = "Uh-oh! This name tag is already registered. Please choose another name tag string."
|
||||
INVALID_TOKEN = (
|
||||
"Your session has expired or the token is invalid. Please sign in again."
|
||||
|
110
backend/main.py
110
backend/main.py
@ -19,8 +19,8 @@ from starlette.exceptions import HTTPException as StarletteHTTPException
|
||||
from starlette.middleware.base import BaseHTTPMiddleware
|
||||
from starlette.responses import StreamingResponse, Response
|
||||
|
||||
from apps.ollama.main import app as ollama_app
|
||||
from apps.openai.main import app as openai_app
|
||||
from apps.ollama.main import app as ollama_app, get_all_models as get_ollama_models
|
||||
from apps.openai.main import app as openai_app, get_all_models as get_openai_models
|
||||
|
||||
from apps.litellm.main import (
|
||||
app as litellm_app,
|
||||
@ -36,10 +36,10 @@ from apps.web.main import app as webui_app
|
||||
|
||||
import asyncio
|
||||
from pydantic import BaseModel
|
||||
from typing import List
|
||||
from typing import List, Optional
|
||||
|
||||
|
||||
from utils.utils import get_admin_user
|
||||
from apps.web.models.models import Models, ModelModel
|
||||
from utils.utils import get_admin_user, get_verified_user
|
||||
from apps.rag.utils import rag_messages
|
||||
|
||||
from config import (
|
||||
@ -53,6 +53,8 @@ from config import (
|
||||
FRONTEND_BUILD_DIR,
|
||||
CACHE_DIR,
|
||||
STATIC_DIR,
|
||||
ENABLE_OPENAI_API,
|
||||
ENABLE_OLLAMA_API,
|
||||
ENABLE_LITELLM,
|
||||
ENABLE_MODEL_FILTER,
|
||||
MODEL_FILTER_LIST,
|
||||
@ -110,11 +112,19 @@ app = FastAPI(
|
||||
)
|
||||
|
||||
app.state.config = AppConfig()
|
||||
|
||||
app.state.config.ENABLE_OPENAI_API = ENABLE_OPENAI_API
|
||||
app.state.config.ENABLE_OLLAMA_API = ENABLE_OLLAMA_API
|
||||
|
||||
app.state.config.ENABLE_MODEL_FILTER = ENABLE_MODEL_FILTER
|
||||
app.state.config.MODEL_FILTER_LIST = MODEL_FILTER_LIST
|
||||
|
||||
|
||||
app.state.config.WEBHOOK_URL = WEBHOOK_URL
|
||||
|
||||
|
||||
app.state.MODELS = {}
|
||||
|
||||
origins = ["*"]
|
||||
|
||||
|
||||
@ -231,6 +241,11 @@ app.add_middleware(
|
||||
|
||||
@app.middleware("http")
|
||||
async def check_url(request: Request, call_next):
|
||||
if len(app.state.MODELS) == 0:
|
||||
await get_all_models()
|
||||
else:
|
||||
pass
|
||||
|
||||
start_time = int(time.time())
|
||||
response = await call_next(request)
|
||||
process_time = int(time.time()) - start_time
|
||||
@ -247,9 +262,11 @@ async def update_embedding_function(request: Request, call_next):
|
||||
return response
|
||||
|
||||
|
||||
# TODO: Deprecate LiteLLM
|
||||
app.mount("/litellm/api", litellm_app)
|
||||
|
||||
app.mount("/ollama", ollama_app)
|
||||
app.mount("/openai/api", openai_app)
|
||||
app.mount("/openai", openai_app)
|
||||
|
||||
app.mount("/images/api/v1", images_app)
|
||||
app.mount("/audio/api/v1", audio_app)
|
||||
@ -260,6 +277,87 @@ app.mount("/api/v1", webui_app)
|
||||
webui_app.state.EMBEDDING_FUNCTION = rag_app.state.EMBEDDING_FUNCTION
|
||||
|
||||
|
||||
async def get_all_models():
|
||||
openai_models = []
|
||||
ollama_models = []
|
||||
|
||||
if app.state.config.ENABLE_OPENAI_API:
|
||||
openai_models = await get_openai_models()
|
||||
|
||||
openai_models = openai_models["data"]
|
||||
|
||||
if app.state.config.ENABLE_OLLAMA_API:
|
||||
ollama_models = await get_ollama_models()
|
||||
|
||||
ollama_models = [
|
||||
{
|
||||
"id": model["model"],
|
||||
"name": model["name"],
|
||||
"object": "model",
|
||||
"created": int(time.time()),
|
||||
"owned_by": "ollama",
|
||||
"ollama": model,
|
||||
}
|
||||
for model in ollama_models["models"]
|
||||
]
|
||||
|
||||
models = openai_models + ollama_models
|
||||
custom_models = Models.get_all_models()
|
||||
|
||||
for custom_model in custom_models:
|
||||
if custom_model.base_model_id == None:
|
||||
for model in models:
|
||||
if (
|
||||
custom_model.id == model["id"]
|
||||
or custom_model.id == model["id"].split(":")[0]
|
||||
):
|
||||
model["name"] = custom_model.name
|
||||
model["info"] = custom_model.model_dump()
|
||||
else:
|
||||
owned_by = "openai"
|
||||
for model in models:
|
||||
if (
|
||||
custom_model.base_model_id == model["id"]
|
||||
or custom_model.base_model_id == model["id"].split(":")[0]
|
||||
):
|
||||
owned_by = model["owned_by"]
|
||||
break
|
||||
|
||||
models.append(
|
||||
{
|
||||
"id": custom_model.id,
|
||||
"name": custom_model.name,
|
||||
"object": "model",
|
||||
"created": custom_model.created_at,
|
||||
"owned_by": owned_by,
|
||||
"info": custom_model.model_dump(),
|
||||
"preset": True,
|
||||
}
|
||||
)
|
||||
|
||||
app.state.MODELS = {model["id"]: model for model in models}
|
||||
|
||||
webui_app.state.MODELS = app.state.MODELS
|
||||
|
||||
return models
|
||||
|
||||
|
||||
@app.get("/api/models")
|
||||
async def get_models(user=Depends(get_verified_user)):
|
||||
models = await get_all_models()
|
||||
if app.state.config.ENABLE_MODEL_FILTER:
|
||||
if user.role == "user":
|
||||
models = list(
|
||||
filter(
|
||||
lambda model: model["id"] in app.state.config.MODEL_FILTER_LIST,
|
||||
models,
|
||||
)
|
||||
)
|
||||
return {"data": models}
|
||||
|
||||
return {"data": models}
|
||||
|
||||
|
||||
@app.get("/api/config")
|
||||
async def get_app_config():
|
||||
# Checking and Handling the Absence of 'ui' in CONFIG_DATA
|
||||
|
@ -1,5 +1,6 @@
|
||||
from pathlib import Path
|
||||
import hashlib
|
||||
import json
|
||||
import re
|
||||
from datetime import timedelta
|
||||
from typing import Optional
|
||||
@ -110,3 +111,76 @@ def parse_duration(duration: str) -> Optional[timedelta]:
|
||||
total_duration += timedelta(weeks=number)
|
||||
|
||||
return total_duration
|
||||
|
||||
|
||||
def parse_ollama_modelfile(model_text):
|
||||
parameters_meta = {
|
||||
"mirostat": int,
|
||||
"mirostat_eta": float,
|
||||
"mirostat_tau": float,
|
||||
"num_ctx": int,
|
||||
"repeat_last_n": int,
|
||||
"repeat_penalty": float,
|
||||
"temperature": float,
|
||||
"seed": int,
|
||||
"stop": str,
|
||||
"tfs_z": float,
|
||||
"num_predict": int,
|
||||
"top_k": int,
|
||||
"top_p": float,
|
||||
}
|
||||
|
||||
data = {"base_model_id": None, "params": {}}
|
||||
|
||||
# Parse base model
|
||||
base_model_match = re.search(
|
||||
r"^FROM\s+(\w+)", model_text, re.MULTILINE | re.IGNORECASE
|
||||
)
|
||||
if base_model_match:
|
||||
data["base_model_id"] = base_model_match.group(1)
|
||||
|
||||
# Parse template
|
||||
template_match = re.search(
|
||||
r'TEMPLATE\s+"""(.+?)"""', model_text, re.DOTALL | re.IGNORECASE
|
||||
)
|
||||
if template_match:
|
||||
data["params"] = {"template": template_match.group(1).strip()}
|
||||
|
||||
# Parse stops
|
||||
stops = re.findall(r'PARAMETER stop "(.*?)"', model_text, re.IGNORECASE)
|
||||
if stops:
|
||||
data["params"]["stop"] = stops
|
||||
|
||||
# Parse other parameters from the provided list
|
||||
for param, param_type in parameters_meta.items():
|
||||
param_match = re.search(rf"PARAMETER {param} (.+)", model_text, re.IGNORECASE)
|
||||
if param_match:
|
||||
value = param_match.group(1)
|
||||
if param_type == int:
|
||||
value = int(value)
|
||||
elif param_type == float:
|
||||
value = float(value)
|
||||
data["params"][param] = value
|
||||
|
||||
# Parse adapter
|
||||
adapter_match = re.search(r"ADAPTER (.+)", model_text, re.IGNORECASE)
|
||||
if adapter_match:
|
||||
data["params"]["adapter"] = adapter_match.group(1)
|
||||
|
||||
# Parse system description
|
||||
system_desc_match = re.search(
|
||||
r'SYSTEM\s+"""(.+?)"""', model_text, re.DOTALL | re.IGNORECASE
|
||||
)
|
||||
if system_desc_match:
|
||||
data["params"]["system"] = system_desc_match.group(1).strip()
|
||||
|
||||
# Parse messages
|
||||
messages = []
|
||||
message_matches = re.findall(r"MESSAGE (\w+) (.+)", model_text, re.IGNORECASE)
|
||||
for role, content in message_matches:
|
||||
messages.append({"role": role, "content": content})
|
||||
|
||||
if messages:
|
||||
data["params"]["messages"] = messages
|
||||
|
||||
return data
|
||||
|
10
backend/utils/models.py
Normal file
10
backend/utils/models.py
Normal file
@ -0,0 +1,10 @@
|
||||
from apps.web.models.models import Models, ModelModel, ModelForm, ModelResponse
|
||||
|
||||
|
||||
def get_model_id_from_custom_model_id(id: str):
|
||||
model = Models.get_model_by_id(id)
|
||||
|
||||
if model:
|
||||
return model.id
|
||||
else:
|
||||
return id
|
@ -1,5 +1,54 @@
|
||||
import { WEBUI_BASE_URL } from '$lib/constants';
|
||||
|
||||
export const getModels = async (token: string = '') => {
|
||||
let error = null;
|
||||
|
||||
const res = await fetch(`${WEBUI_BASE_URL}/api/models`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
...(token && { authorization: `Bearer ${token}` })
|
||||
}
|
||||
})
|
||||
.then(async (res) => {
|
||||
if (!res.ok) throw await res.json();
|
||||
return res.json();
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
error = err;
|
||||
return null;
|
||||
});
|
||||
|
||||
if (error) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
let models = res?.data ?? [];
|
||||
|
||||
models = models
|
||||
.filter((models) => models)
|
||||
.sort((a, b) => {
|
||||
// Compare case-insensitively
|
||||
const lowerA = a.name.toLowerCase();
|
||||
const lowerB = b.name.toLowerCase();
|
||||
|
||||
if (lowerA < lowerB) return -1;
|
||||
if (lowerA > lowerB) return 1;
|
||||
|
||||
// If same case-insensitively, sort by original strings,
|
||||
// lowercase will come before uppercase due to ASCII values
|
||||
if (a < b) return -1;
|
||||
if (a > b) return 1;
|
||||
|
||||
return 0; // They are equal
|
||||
});
|
||||
|
||||
console.log(models);
|
||||
return models;
|
||||
};
|
||||
|
||||
export const getBackendConfig = async () => {
|
||||
let error = null;
|
||||
|
||||
@ -196,3 +245,77 @@ export const updateWebhookUrl = async (token: string, url: string) => {
|
||||
|
||||
return res.url;
|
||||
};
|
||||
|
||||
export const getModelConfig = async (token: string): Promise<GlobalModelConfig> => {
|
||||
let error = null;
|
||||
|
||||
const res = await fetch(`${WEBUI_BASE_URL}/api/config/models`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${token}`
|
||||
}
|
||||
})
|
||||
.then(async (res) => {
|
||||
if (!res.ok) throw await res.json();
|
||||
return res.json();
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
error = err;
|
||||
return null;
|
||||
});
|
||||
|
||||
if (error) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
return res.models;
|
||||
};
|
||||
|
||||
export interface ModelConfig {
|
||||
id: string;
|
||||
name: string;
|
||||
meta: ModelMeta;
|
||||
base_model_id?: string;
|
||||
params: ModelParams;
|
||||
}
|
||||
|
||||
export interface ModelMeta {
|
||||
description?: string;
|
||||
capabilities?: object;
|
||||
}
|
||||
|
||||
export interface ModelParams {}
|
||||
|
||||
export type GlobalModelConfig = ModelConfig[];
|
||||
|
||||
export const updateModelConfig = async (token: string, config: GlobalModelConfig) => {
|
||||
let error = null;
|
||||
|
||||
const res = await fetch(`${WEBUI_BASE_URL}/api/config/models`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${token}`
|
||||
},
|
||||
body: JSON.stringify({
|
||||
models: config
|
||||
})
|
||||
})
|
||||
.then(async (res) => {
|
||||
if (!res.ok) throw await res.json();
|
||||
return res.json();
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
error = err;
|
||||
return null;
|
||||
});
|
||||
|
||||
if (error) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
return res;
|
||||
};
|
||||
|
@ -33,7 +33,8 @@ export const getLiteLLMModels = async (token: string = '') => {
|
||||
id: model.id,
|
||||
name: model.name ?? model.id,
|
||||
external: true,
|
||||
source: 'LiteLLM'
|
||||
source: 'LiteLLM',
|
||||
custom_info: model.custom_info
|
||||
}))
|
||||
.sort((a, b) => {
|
||||
return a.name.localeCompare(b.name);
|
||||
|
@ -1,18 +1,16 @@
|
||||
import { WEBUI_API_BASE_URL } from '$lib/constants';
|
||||
|
||||
export const createNewModelfile = async (token: string, modelfile: object) => {
|
||||
export const addNewModel = async (token: string, model: object) => {
|
||||
let error = null;
|
||||
|
||||
const res = await fetch(`${WEBUI_API_BASE_URL}/modelfiles/create`, {
|
||||
const res = await fetch(`${WEBUI_API_BASE_URL}/models/add`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
authorization: `Bearer ${token}`
|
||||
},
|
||||
body: JSON.stringify({
|
||||
modelfile: modelfile
|
||||
})
|
||||
body: JSON.stringify(model)
|
||||
})
|
||||
.then(async (res) => {
|
||||
if (!res.ok) throw await res.json();
|
||||
@ -31,10 +29,10 @@ export const createNewModelfile = async (token: string, modelfile: object) => {
|
||||
return res;
|
||||
};
|
||||
|
||||
export const getModelfiles = async (token: string = '') => {
|
||||
export const getModelInfos = async (token: string = '') => {
|
||||
let error = null;
|
||||
|
||||
const res = await fetch(`${WEBUI_API_BASE_URL}/modelfiles/`, {
|
||||
const res = await fetch(`${WEBUI_API_BASE_URL}/models/`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
@ -59,62 +57,19 @@ export const getModelfiles = async (token: string = '') => {
|
||||
throw error;
|
||||
}
|
||||
|
||||
return res.map((modelfile) => modelfile.modelfile);
|
||||
return res;
|
||||
};
|
||||
|
||||
export const getModelfileByTagName = async (token: string, tagName: string) => {
|
||||
export const getModelById = async (token: string, id: string) => {
|
||||
let error = null;
|
||||
|
||||
const res = await fetch(`${WEBUI_API_BASE_URL}/modelfiles/`, {
|
||||
method: 'POST',
|
||||
const res = await fetch(`${WEBUI_API_BASE_URL}/models/${id}`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
authorization: `Bearer ${token}`
|
||||
},
|
||||
body: JSON.stringify({
|
||||
tag_name: tagName
|
||||
})
|
||||
})
|
||||
.then(async (res) => {
|
||||
if (!res.ok) throw await res.json();
|
||||
return res.json();
|
||||
})
|
||||
.then((json) => {
|
||||
return json;
|
||||
})
|
||||
.catch((err) => {
|
||||
error = err;
|
||||
|
||||
console.log(err);
|
||||
return null;
|
||||
});
|
||||
|
||||
if (error) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
return res.modelfile;
|
||||
};
|
||||
|
||||
export const updateModelfileByTagName = async (
|
||||
token: string,
|
||||
tagName: string,
|
||||
modelfile: object
|
||||
) => {
|
||||
let error = null;
|
||||
|
||||
const res = await fetch(`${WEBUI_API_BASE_URL}/modelfiles/update`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
authorization: `Bearer ${token}`
|
||||
},
|
||||
body: JSON.stringify({
|
||||
tag_name: tagName,
|
||||
modelfile: modelfile
|
||||
})
|
||||
}
|
||||
})
|
||||
.then(async (res) => {
|
||||
if (!res.ok) throw await res.json();
|
||||
@ -137,19 +92,49 @@ export const updateModelfileByTagName = async (
|
||||
return res;
|
||||
};
|
||||
|
||||
export const deleteModelfileByTagName = async (token: string, tagName: string) => {
|
||||
export const updateModelById = async (token: string, id: string, model: object) => {
|
||||
let error = null;
|
||||
|
||||
const res = await fetch(`${WEBUI_API_BASE_URL}/modelfiles/delete`, {
|
||||
const res = await fetch(`${WEBUI_API_BASE_URL}/models/${id}/update`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
authorization: `Bearer ${token}`
|
||||
},
|
||||
body: JSON.stringify(model)
|
||||
})
|
||||
.then(async (res) => {
|
||||
if (!res.ok) throw await res.json();
|
||||
return res.json();
|
||||
})
|
||||
.then((json) => {
|
||||
return json;
|
||||
})
|
||||
.catch((err) => {
|
||||
error = err;
|
||||
|
||||
console.log(err);
|
||||
return null;
|
||||
});
|
||||
|
||||
if (error) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
return res;
|
||||
};
|
||||
|
||||
export const deleteModelById = async (token: string, id: string) => {
|
||||
let error = null;
|
||||
|
||||
const res = await fetch(`${WEBUI_API_BASE_URL}/models/${id}/delete`, {
|
||||
method: 'DELETE',
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
authorization: `Bearer ${token}`
|
||||
},
|
||||
body: JSON.stringify({
|
||||
tag_name: tagName
|
||||
})
|
||||
}
|
||||
})
|
||||
.then(async (res) => {
|
||||
if (!res.ok) throw await res.json();
|
@ -230,7 +230,12 @@ export const getOpenAIModels = async (token: string = '') => {
|
||||
|
||||
return models
|
||||
? models
|
||||
.map((model) => ({ id: model.id, name: model.name ?? model.id, external: true }))
|
||||
.map((model) => ({
|
||||
id: model.id,
|
||||
name: model.name ?? model.id,
|
||||
external: true,
|
||||
custom_info: model.custom_info
|
||||
}))
|
||||
.sort((a, b) => {
|
||||
return a.name.localeCompare(b.name);
|
||||
})
|
||||
|
@ -10,7 +10,7 @@
|
||||
chatId,
|
||||
chats,
|
||||
config,
|
||||
modelfiles,
|
||||
type Model,
|
||||
models,
|
||||
settings,
|
||||
showSidebar,
|
||||
@ -60,25 +60,7 @@
|
||||
let showModelSelector = true;
|
||||
|
||||
let selectedModels = [''];
|
||||
let atSelectedModel = '';
|
||||
|
||||
let selectedModelfile = null;
|
||||
$: selectedModelfile =
|
||||
selectedModels.length === 1 &&
|
||||
$modelfiles.filter((modelfile) => modelfile.tagName === selectedModels[0]).length > 0
|
||||
? $modelfiles.filter((modelfile) => modelfile.tagName === selectedModels[0])[0]
|
||||
: null;
|
||||
|
||||
let selectedModelfiles = {};
|
||||
$: selectedModelfiles = selectedModels.reduce((a, tagName, i, arr) => {
|
||||
const modelfile =
|
||||
$modelfiles.filter((modelfile) => modelfile.tagName === tagName)?.at(0) ?? undefined;
|
||||
|
||||
return {
|
||||
...a,
|
||||
...(modelfile && { [tagName]: modelfile })
|
||||
};
|
||||
}, {});
|
||||
let atSelectedModel: Model | undefined;
|
||||
|
||||
let chat = null;
|
||||
let tags = [];
|
||||
@ -164,6 +146,7 @@
|
||||
|
||||
if ($page.url.searchParams.get('q')) {
|
||||
prompt = $page.url.searchParams.get('q') ?? '';
|
||||
|
||||
if (prompt) {
|
||||
await tick();
|
||||
submitPrompt(prompt);
|
||||
@ -211,7 +194,7 @@
|
||||
await settings.set({
|
||||
..._settings,
|
||||
system: chatContent.system ?? _settings.system,
|
||||
options: chatContent.options ?? _settings.options
|
||||
params: chatContent.options ?? _settings.params
|
||||
});
|
||||
autoScroll = true;
|
||||
await tick();
|
||||
@ -300,7 +283,7 @@
|
||||
models: selectedModels,
|
||||
system: $settings.system ?? undefined,
|
||||
options: {
|
||||
...($settings.options ?? {})
|
||||
...($settings.params ?? {})
|
||||
},
|
||||
messages: messages,
|
||||
history: history,
|
||||
@ -317,6 +300,7 @@
|
||||
|
||||
// Reset chat input textarea
|
||||
prompt = '';
|
||||
document.getElementById('chat-textarea').style.height = '';
|
||||
files = [];
|
||||
|
||||
// Send prompt
|
||||
@ -328,75 +312,92 @@
|
||||
const _chatId = JSON.parse(JSON.stringify($chatId));
|
||||
|
||||
await Promise.all(
|
||||
(modelId ? [modelId] : atSelectedModel !== '' ? [atSelectedModel.id] : selectedModels).map(
|
||||
async (modelId) => {
|
||||
console.log('modelId', modelId);
|
||||
const model = $models.filter((m) => m.id === modelId).at(0);
|
||||
(modelId
|
||||
? [modelId]
|
||||
: atSelectedModel !== undefined
|
||||
? [atSelectedModel.id]
|
||||
: selectedModels
|
||||
).map(async (modelId) => {
|
||||
console.log('modelId', modelId);
|
||||
const model = $models.filter((m) => m.id === modelId).at(0);
|
||||
|
||||
if (model) {
|
||||
// Create response message
|
||||
let responseMessageId = uuidv4();
|
||||
let responseMessage = {
|
||||
parentId: parentId,
|
||||
id: responseMessageId,
|
||||
childrenIds: [],
|
||||
role: 'assistant',
|
||||
content: '',
|
||||
model: model.id,
|
||||
userContext: null,
|
||||
timestamp: Math.floor(Date.now() / 1000) // Unix epoch
|
||||
};
|
||||
if (model) {
|
||||
// If there are image files, check if model is vision capable
|
||||
const hasImages = messages.some((message) =>
|
||||
message.files?.some((file) => file.type === 'image')
|
||||
);
|
||||
|
||||
// Add message to history and Set currentId to messageId
|
||||
history.messages[responseMessageId] = responseMessage;
|
||||
history.currentId = responseMessageId;
|
||||
if (hasImages && !(model.info?.meta?.capabilities?.vision ?? true)) {
|
||||
toast.error(
|
||||
$i18n.t('Model {{modelName}} is not vision capable', {
|
||||
modelName: model.name ?? model.id
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
// Append messageId to childrenIds of parent message
|
||||
if (parentId !== null) {
|
||||
history.messages[parentId].childrenIds = [
|
||||
...history.messages[parentId].childrenIds,
|
||||
responseMessageId
|
||||
];
|
||||
}
|
||||
// Create response message
|
||||
let responseMessageId = uuidv4();
|
||||
let responseMessage = {
|
||||
parentId: parentId,
|
||||
id: responseMessageId,
|
||||
childrenIds: [],
|
||||
role: 'assistant',
|
||||
content: '',
|
||||
model: model.id,
|
||||
modelName: model.name ?? model.id,
|
||||
userContext: null,
|
||||
timestamp: Math.floor(Date.now() / 1000) // Unix epoch
|
||||
};
|
||||
|
||||
await tick();
|
||||
// Add message to history and Set currentId to messageId
|
||||
history.messages[responseMessageId] = responseMessage;
|
||||
history.currentId = responseMessageId;
|
||||
|
||||
let userContext = null;
|
||||
if ($settings?.memory ?? false) {
|
||||
if (userContext === null) {
|
||||
const res = await queryMemory(localStorage.token, prompt).catch((error) => {
|
||||
toast.error(error);
|
||||
return null;
|
||||
});
|
||||
// Append messageId to childrenIds of parent message
|
||||
if (parentId !== null) {
|
||||
history.messages[parentId].childrenIds = [
|
||||
...history.messages[parentId].childrenIds,
|
||||
responseMessageId
|
||||
];
|
||||
}
|
||||
|
||||
if (res) {
|
||||
if (res.documents[0].length > 0) {
|
||||
userContext = res.documents.reduce((acc, doc, index) => {
|
||||
const createdAtTimestamp = res.metadatas[index][0].created_at;
|
||||
const createdAtDate = new Date(createdAtTimestamp * 1000)
|
||||
.toISOString()
|
||||
.split('T')[0];
|
||||
acc.push(`${index + 1}. [${createdAtDate}]. ${doc[0]}`);
|
||||
return acc;
|
||||
}, []);
|
||||
}
|
||||
await tick();
|
||||
|
||||
console.log(userContext);
|
||||
let userContext = null;
|
||||
if ($settings?.memory ?? false) {
|
||||
if (userContext === null) {
|
||||
const res = await queryMemory(localStorage.token, prompt).catch((error) => {
|
||||
toast.error(error);
|
||||
return null;
|
||||
});
|
||||
|
||||
if (res) {
|
||||
if (res.documents[0].length > 0) {
|
||||
userContext = res.documents.reduce((acc, doc, index) => {
|
||||
const createdAtTimestamp = res.metadatas[index][0].created_at;
|
||||
const createdAtDate = new Date(createdAtTimestamp * 1000)
|
||||
.toISOString()
|
||||
.split('T')[0];
|
||||
acc.push(`${index + 1}. [${createdAtDate}]. ${doc[0]}`);
|
||||
return acc;
|
||||
}, []);
|
||||
}
|
||||
|
||||
console.log(userContext);
|
||||
}
|
||||
}
|
||||
responseMessage.userContext = userContext;
|
||||
|
||||
if (model?.external) {
|
||||
await sendPromptOpenAI(model, prompt, responseMessageId, _chatId);
|
||||
} else if (model) {
|
||||
await sendPromptOllama(model, prompt, responseMessageId, _chatId);
|
||||
}
|
||||
} else {
|
||||
toast.error($i18n.t(`Model {{modelId}} not found`, { modelId }));
|
||||
}
|
||||
responseMessage.userContext = userContext;
|
||||
|
||||
if (model?.owned_by === 'openai') {
|
||||
await sendPromptOpenAI(model, prompt, responseMessageId, _chatId);
|
||||
} else if (model) {
|
||||
await sendPromptOllama(model, prompt, responseMessageId, _chatId);
|
||||
}
|
||||
} else {
|
||||
toast.error($i18n.t(`Model {{modelId}} not found`, { modelId }));
|
||||
}
|
||||
)
|
||||
})
|
||||
);
|
||||
|
||||
await chats.set(await getChatList(localStorage.token));
|
||||
@ -430,7 +431,7 @@
|
||||
// Prepare the base message object
|
||||
const baseMessage = {
|
||||
role: message.role,
|
||||
content: arr.length - 2 !== idx ? message.content : message?.raContent ?? message.content
|
||||
content: message.content
|
||||
};
|
||||
|
||||
// Extract and format image URLs if any exist
|
||||
@ -442,7 +443,6 @@
|
||||
if (imageUrls && imageUrls.length > 0 && message.role === 'user') {
|
||||
baseMessage.images = imageUrls;
|
||||
}
|
||||
|
||||
return baseMessage;
|
||||
});
|
||||
|
||||
@ -473,13 +473,15 @@
|
||||
model: model,
|
||||
messages: messagesBody,
|
||||
options: {
|
||||
...($settings.options ?? {}),
|
||||
...($settings.params ?? {}),
|
||||
stop:
|
||||
$settings?.options?.stop ?? undefined
|
||||
? $settings.options.stop.map((str) =>
|
||||
$settings?.params?.stop ?? undefined
|
||||
? $settings.params.stop.map((str) =>
|
||||
decodeURIComponent(JSON.parse('"' + str.replace(/\"/g, '\\"') + '"'))
|
||||
)
|
||||
: undefined
|
||||
: undefined,
|
||||
num_predict: $settings?.params?.max_tokens ?? undefined,
|
||||
repeat_penalty: $settings?.params?.frequency_penalty ?? undefined
|
||||
},
|
||||
format: $settings.requestFormat ?? undefined,
|
||||
keep_alive: $settings.keepAlive ?? undefined,
|
||||
@ -605,7 +607,8 @@
|
||||
if ($settings.saveChatHistory ?? true) {
|
||||
chat = await updateChatById(localStorage.token, _chatId, {
|
||||
messages: messages,
|
||||
history: history
|
||||
history: history,
|
||||
models: selectedModels
|
||||
});
|
||||
await chats.set(await getChatList(localStorage.token));
|
||||
}
|
||||
@ -716,18 +719,17 @@
|
||||
: message?.raContent ?? message.content
|
||||
})
|
||||
})),
|
||||
seed: $settings?.options?.seed ?? undefined,
|
||||
seed: $settings?.params?.seed ?? undefined,
|
||||
stop:
|
||||
$settings?.options?.stop ?? undefined
|
||||
? $settings.options.stop.map((str) =>
|
||||
$settings?.params?.stop ?? undefined
|
||||
? $settings.params.stop.map((str) =>
|
||||
decodeURIComponent(JSON.parse('"' + str.replace(/\"/g, '\\"') + '"'))
|
||||
)
|
||||
: undefined,
|
||||
temperature: $settings?.options?.temperature ?? undefined,
|
||||
top_p: $settings?.options?.top_p ?? undefined,
|
||||
num_ctx: $settings?.options?.num_ctx ?? undefined,
|
||||
frequency_penalty: $settings?.options?.repeat_penalty ?? undefined,
|
||||
max_tokens: $settings?.options?.num_predict ?? undefined,
|
||||
temperature: $settings?.params?.temperature ?? undefined,
|
||||
top_p: $settings?.params?.top_p ?? undefined,
|
||||
frequency_penalty: $settings?.params?.frequency_penalty ?? undefined,
|
||||
max_tokens: $settings?.params?.max_tokens ?? undefined,
|
||||
docs: docs.length > 0 ? docs : undefined,
|
||||
citations: docs.length > 0
|
||||
},
|
||||
@ -797,6 +799,7 @@
|
||||
if ($chatId == _chatId) {
|
||||
if ($settings.saveChatHistory ?? true) {
|
||||
chat = await updateChatById(localStorage.token, _chatId, {
|
||||
models: selectedModels,
|
||||
messages: messages,
|
||||
history: history
|
||||
});
|
||||
@ -935,10 +938,8 @@
|
||||
) + ' {{prompt}}',
|
||||
titleModelId,
|
||||
userPrompt,
|
||||
titleModel?.external ?? false
|
||||
? titleModel?.source?.toLowerCase() === 'litellm'
|
||||
? `${LITELLM_API_BASE_URL}/v1`
|
||||
: `${OPENAI_API_BASE_URL}`
|
||||
titleModel?.owned_by === 'openai' ?? false
|
||||
? `${OPENAI_API_BASE_URL}`
|
||||
: `${OLLAMA_API_BASE_URL}/v1`
|
||||
);
|
||||
|
||||
@ -1025,16 +1026,12 @@
|
||||
<Messages
|
||||
chatId={$chatId}
|
||||
{selectedModels}
|
||||
{selectedModelfiles}
|
||||
{processing}
|
||||
bind:history
|
||||
bind:messages
|
||||
bind:autoScroll
|
||||
bind:prompt
|
||||
bottomPadding={files.length > 0}
|
||||
suggestionPrompts={chatIdProp
|
||||
? []
|
||||
: selectedModelfile?.suggestionPrompts ?? $config.default_prompt_suggestions}
|
||||
{sendPrompt}
|
||||
{continueGeneration}
|
||||
{regenerateResponse}
|
||||
@ -1048,7 +1045,8 @@
|
||||
bind:files
|
||||
bind:prompt
|
||||
bind:autoScroll
|
||||
bind:selectedModel={atSelectedModel}
|
||||
bind:atSelectedModel
|
||||
{selectedModels}
|
||||
{messages}
|
||||
{submitPrompt}
|
||||
{stopResponse}
|
||||
|
@ -1,7 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { toast } from 'svelte-sonner';
|
||||
import { onMount, tick, getContext } from 'svelte';
|
||||
import { mobile, modelfiles, settings, showSidebar } from '$lib/stores';
|
||||
import { type Model, mobile, settings, showSidebar, models } from '$lib/stores';
|
||||
import { blobToFile, calculateSHA256, findWordIndices } from '$lib/utils';
|
||||
|
||||
import {
|
||||
@ -27,7 +27,9 @@
|
||||
export let stopResponse: Function;
|
||||
|
||||
export let autoScroll = true;
|
||||
export let selectedModel = '';
|
||||
|
||||
export let atSelectedModel: Model | undefined;
|
||||
export let selectedModels: [''];
|
||||
|
||||
let chatTextAreaElement: HTMLTextAreaElement;
|
||||
let filesInputElement;
|
||||
@ -52,6 +54,11 @@
|
||||
|
||||
let speechRecognition;
|
||||
|
||||
let visionCapableModels = [];
|
||||
$: visionCapableModels = [...(atSelectedModel ? [atSelectedModel] : selectedModels)].filter(
|
||||
(model) => $models.find((m) => m.id === model)?.info?.meta?.capabilities?.vision ?? true
|
||||
);
|
||||
|
||||
$: if (prompt) {
|
||||
if (chatTextAreaElement) {
|
||||
chatTextAreaElement.style.height = '';
|
||||
@ -358,6 +365,10 @@
|
||||
inputFiles.forEach((file) => {
|
||||
console.log(file, file.name.split('.').at(-1));
|
||||
if (['image/gif', 'image/webp', 'image/jpeg', 'image/png'].includes(file['type'])) {
|
||||
if (visionCapableModels.length === 0) {
|
||||
toast.error($i18n.t('Selected model(s) do not support image inputs'));
|
||||
return;
|
||||
}
|
||||
let reader = new FileReader();
|
||||
reader.onload = (event) => {
|
||||
files = [
|
||||
@ -429,8 +440,8 @@
|
||||
|
||||
<div class="fixed bottom-0 {$showSidebar ? 'left-0 md:left-[260px]' : 'left-0'} right-0">
|
||||
<div class="w-full">
|
||||
<div class="px-2.5 md:px-16 -mb-0.5 mx-auto inset-x-0 bg-transparent flex justify-center">
|
||||
<div class="flex flex-col max-w-5xl w-full">
|
||||
<div class=" -mb-0.5 mx-auto inset-x-0 bg-transparent flex justify-center">
|
||||
<div class="flex flex-col max-w-6xl px-2.5 md:px-6 w-full">
|
||||
<div class="relative">
|
||||
{#if autoScroll === false && messages.length > 0}
|
||||
<div class=" absolute -top-12 left-0 right-0 flex justify-center z-30">
|
||||
@ -494,12 +505,12 @@
|
||||
bind:chatInputPlaceholder
|
||||
{messages}
|
||||
on:select={(e) => {
|
||||
selectedModel = e.detail;
|
||||
atSelectedModel = e.detail;
|
||||
chatTextAreaElement?.focus();
|
||||
}}
|
||||
/>
|
||||
|
||||
{#if selectedModel !== ''}
|
||||
{#if atSelectedModel !== undefined}
|
||||
<div
|
||||
class="px-3 py-2.5 text-left w-full flex justify-between items-center absolute bottom-0 left-0 right-0 bg-gradient-to-t from-50% from-white dark:from-gray-900"
|
||||
>
|
||||
@ -508,21 +519,21 @@
|
||||
crossorigin="anonymous"
|
||||
alt="model profile"
|
||||
class="size-5 max-w-[28px] object-cover rounded-full"
|
||||
src={$modelfiles.find((modelfile) => modelfile.tagName === selectedModel.id)
|
||||
?.imageUrl ??
|
||||
src={$models.find((model) => model.id === atSelectedModel.id)?.info?.meta
|
||||
?.profile_image_url ??
|
||||
($i18n.language === 'dg-DG'
|
||||
? `/doge.png`
|
||||
: `${WEBUI_BASE_URL}/static/favicon.png`)}
|
||||
/>
|
||||
<div>
|
||||
Talking to <span class=" font-medium">{selectedModel.name} </span>
|
||||
Talking to <span class=" font-medium">{atSelectedModel.name}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<button
|
||||
class="flex items-center"
|
||||
on:click={() => {
|
||||
selectedModel = '';
|
||||
atSelectedModel = undefined;
|
||||
}}
|
||||
>
|
||||
<XMark />
|
||||
@ -535,7 +546,7 @@
|
||||
</div>
|
||||
|
||||
<div class="bg-white dark:bg-gray-900">
|
||||
<div class="max-w-6xl px-2.5 md:px-16 mx-auto inset-x-0">
|
||||
<div class="max-w-6xl px-2.5 md:px-6 mx-auto inset-x-0">
|
||||
<div class=" pb-2">
|
||||
<input
|
||||
bind:this={filesInputElement}
|
||||
@ -550,6 +561,12 @@
|
||||
if (
|
||||
['image/gif', 'image/webp', 'image/jpeg', 'image/png'].includes(file['type'])
|
||||
) {
|
||||
if (visionCapableModels.length === 0) {
|
||||
toast.error($i18n.t('Selected model(s) do not support image inputs'));
|
||||
inputFiles = null;
|
||||
filesInputElement.value = '';
|
||||
return;
|
||||
}
|
||||
let reader = new FileReader();
|
||||
reader.onload = (event) => {
|
||||
files = [
|
||||
@ -589,6 +606,7 @@
|
||||
dir={$settings?.chatDirection ?? 'LTR'}
|
||||
class=" flex flex-col relative w-full rounded-3xl px-1.5 bg-gray-50 dark:bg-gray-850 dark:text-gray-100"
|
||||
on:submit|preventDefault={() => {
|
||||
// check if selectedModels support image input
|
||||
submitPrompt(prompt, user);
|
||||
}}
|
||||
>
|
||||
@ -597,7 +615,36 @@
|
||||
{#each files as file, fileIdx}
|
||||
<div class=" relative group">
|
||||
{#if file.type === 'image'}
|
||||
<img src={file.url} alt="input" class=" h-16 w-16 rounded-xl object-cover" />
|
||||
<div class="relative">
|
||||
<img
|
||||
src={file.url}
|
||||
alt="input"
|
||||
class=" h-16 w-16 rounded-xl object-cover"
|
||||
/>
|
||||
{#if atSelectedModel ? visionCapableModels.length === 0 : selectedModels.length !== visionCapableModels.length}
|
||||
<Tooltip
|
||||
className=" absolute top-1 left-1"
|
||||
content={$i18n.t('{{ models }}', {
|
||||
models: [...(atSelectedModel ? [atSelectedModel] : selectedModels)]
|
||||
.filter((id) => !visionCapableModels.includes(id))
|
||||
.join(', ')
|
||||
})}
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
fill="currentColor"
|
||||
class="size-4 fill-yellow-300"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
d="M9.401 3.003c1.155-2 4.043-2 5.197 0l7.355 12.748c1.154 2-.29 4.5-2.599 4.5H4.645c-2.309 0-3.752-2.5-2.598-4.5L9.4 3.003ZM12 8.25a.75.75 0 0 1 .75.75v3.75a.75.75 0 0 1-1.5 0V9a.75.75 0 0 1 .75-.75Zm0 8.25a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5Z"
|
||||
clip-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</Tooltip>
|
||||
{/if}
|
||||
</div>
|
||||
{:else if file.type === 'doc'}
|
||||
<div
|
||||
class="h-16 w-[15rem] flex items-center space-x-3 px-2.5 dark:bg-gray-600 rounded-xl border border-gray-200 dark:border-none"
|
||||
@ -883,7 +930,7 @@
|
||||
|
||||
if (e.key === 'Escape') {
|
||||
console.log('Escape');
|
||||
selectedModel = '';
|
||||
atSelectedModel = undefined;
|
||||
}
|
||||
}}
|
||||
rows="1"
|
||||
|
@ -1,7 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
import { chats, config, modelfiles, settings, user as _user, mobile } from '$lib/stores';
|
||||
import { chats, config, settings, user as _user, mobile } from '$lib/stores';
|
||||
import { tick, getContext } from 'svelte';
|
||||
|
||||
import { toast } from 'svelte-sonner';
|
||||
@ -26,7 +26,6 @@
|
||||
|
||||
export let user = $_user;
|
||||
export let prompt;
|
||||
export let suggestionPrompts = [];
|
||||
export let processing = '';
|
||||
export let bottomPadding = false;
|
||||
export let autoScroll;
|
||||
@ -34,7 +33,6 @@
|
||||
export let messages = [];
|
||||
|
||||
export let selectedModels;
|
||||
export let selectedModelfiles = [];
|
||||
|
||||
$: if (autoScroll && bottomPadding) {
|
||||
(async () => {
|
||||
@ -247,9 +245,7 @@
|
||||
<div class="h-full flex mb-16">
|
||||
{#if messages.length == 0}
|
||||
<Placeholder
|
||||
models={selectedModels}
|
||||
modelfiles={selectedModelfiles}
|
||||
{suggestionPrompts}
|
||||
modelIds={selectedModels}
|
||||
submitPrompt={async (p) => {
|
||||
let text = p;
|
||||
|
||||
@ -316,7 +312,6 @@
|
||||
{#key message.id}
|
||||
<ResponseMessage
|
||||
{message}
|
||||
modelfiles={selectedModelfiles}
|
||||
siblings={history.messages[message.parentId]?.childrenIds ?? []}
|
||||
isLastMessage={messageIdx + 1 === messages.length}
|
||||
{readOnly}
|
||||
@ -348,7 +343,6 @@
|
||||
{chatId}
|
||||
parentMessage={history.messages[message.parentId]}
|
||||
{messageIdx}
|
||||
{selectedModelfiles}
|
||||
{updateChatMessages}
|
||||
{confirmEditResponseMessage}
|
||||
{rateMessage}
|
||||
|
@ -4,7 +4,7 @@
|
||||
import hljs from 'highlight.js';
|
||||
import 'highlight.js/styles/github-dark.min.css';
|
||||
import { loadPyodide } from 'pyodide';
|
||||
import { tick } from 'svelte';
|
||||
import { onMount, tick } from 'svelte';
|
||||
import PyodideWorker from '$lib/workers/pyodide.worker?worker';
|
||||
|
||||
export let id = '';
|
||||
@ -12,6 +12,7 @@
|
||||
export let lang = '';
|
||||
export let code = '';
|
||||
|
||||
let highlightedCode = null;
|
||||
let executing = false;
|
||||
|
||||
let stdout = null;
|
||||
@ -202,60 +203,60 @@ __builtins__.input = input`);
|
||||
};
|
||||
};
|
||||
|
||||
$: highlightedCode = code ? hljs.highlightAuto(code, hljs.getLanguage(lang)?.aliases).value : '';
|
||||
$: if (code) {
|
||||
highlightedCode = hljs.highlightAuto(code, hljs.getLanguage(lang)?.aliases).value || code;
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if code}
|
||||
<div class="mb-4" dir="ltr">
|
||||
<div
|
||||
class="flex justify-between bg-[#202123] text-white text-xs px-4 pt-1 pb-0.5 rounded-t-lg overflow-x-auto"
|
||||
>
|
||||
<div class="p-1">{@html lang}</div>
|
||||
<div class="mb-4" dir="ltr">
|
||||
<div
|
||||
class="flex justify-between bg-[#202123] text-white text-xs px-4 pt-1 pb-0.5 rounded-t-lg overflow-x-auto"
|
||||
>
|
||||
<div class="p-1">{@html lang}</div>
|
||||
|
||||
<div class="flex items-center">
|
||||
{#if lang === 'python' || (lang === '' && checkPythonCode(code))}
|
||||
{#if executing}
|
||||
<div class="copy-code-button bg-none border-none p-1 cursor-not-allowed">Running</div>
|
||||
{:else}
|
||||
<button
|
||||
class="copy-code-button bg-none border-none p-1"
|
||||
on:click={() => {
|
||||
executePython(code);
|
||||
}}>Run</button
|
||||
>
|
||||
{/if}
|
||||
<div class="flex items-center">
|
||||
{#if lang === 'python' || (lang === '' && checkPythonCode(code))}
|
||||
{#if executing}
|
||||
<div class="copy-code-button bg-none border-none p-1 cursor-not-allowed">Running</div>
|
||||
{:else}
|
||||
<button
|
||||
class="copy-code-button bg-none border-none p-1"
|
||||
on:click={() => {
|
||||
executePython(code);
|
||||
}}>Run</button
|
||||
>
|
||||
{/if}
|
||||
<button class="copy-code-button bg-none border-none p-1" on:click={copyCode}
|
||||
>{copied ? 'Copied' : 'Copy Code'}</button
|
||||
>
|
||||
</div>
|
||||
{/if}
|
||||
<button class="copy-code-button bg-none border-none p-1" on:click={copyCode}
|
||||
>{copied ? 'Copied' : 'Copy Code'}</button
|
||||
>
|
||||
</div>
|
||||
|
||||
<pre
|
||||
class=" hljs p-4 px-5 overflow-x-auto"
|
||||
style="border-top-left-radius: 0px; border-top-right-radius: 0px; {(executing ||
|
||||
stdout ||
|
||||
stderr ||
|
||||
result) &&
|
||||
'border-bottom-left-radius: 0px; border-bottom-right-radius: 0px;'}"><code
|
||||
class="language-{lang} rounded-t-none whitespace-pre">{@html highlightedCode || code}</code
|
||||
></pre>
|
||||
|
||||
<div
|
||||
id="plt-canvas-{id}"
|
||||
class="bg-[#202123] text-white max-w-full overflow-x-auto scrollbar-hidden"
|
||||
/>
|
||||
|
||||
{#if executing}
|
||||
<div class="bg-[#202123] text-white px-4 py-4 rounded-b-lg">
|
||||
<div class=" text-gray-500 text-xs mb-1">STDOUT/STDERR</div>
|
||||
<div class="text-sm">Running...</div>
|
||||
</div>
|
||||
{:else if stdout || stderr || result}
|
||||
<div class="bg-[#202123] text-white px-4 py-4 rounded-b-lg">
|
||||
<div class=" text-gray-500 text-xs mb-1">STDOUT/STDERR</div>
|
||||
<div class="text-sm">{stdout || stderr || result}</div>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<pre
|
||||
class=" hljs p-4 px-5 overflow-x-auto"
|
||||
style="border-top-left-radius: 0px; border-top-right-radius: 0px; {(executing ||
|
||||
stdout ||
|
||||
stderr ||
|
||||
result) &&
|
||||
'border-bottom-left-radius: 0px; border-bottom-right-radius: 0px;'}"><code
|
||||
class="language-{lang} rounded-t-none whitespace-pre">{@html highlightedCode || code}</code
|
||||
></pre>
|
||||
|
||||
<div
|
||||
id="plt-canvas-{id}"
|
||||
class="bg-[#202123] text-white max-w-full overflow-x-auto scrollbar-hidden"
|
||||
/>
|
||||
|
||||
{#if executing}
|
||||
<div class="bg-[#202123] text-white px-4 py-4 rounded-b-lg">
|
||||
<div class=" text-gray-500 text-xs mb-1">STDOUT/STDERR</div>
|
||||
<div class="text-sm">Running...</div>
|
||||
</div>
|
||||
{:else if stdout || stderr || result}
|
||||
<div class="bg-[#202123] text-white px-4 py-4 rounded-b-lg">
|
||||
<div class=" text-gray-500 text-xs mb-1">STDOUT/STDERR</div>
|
||||
<div class="text-sm">{stdout || stderr || result}</div>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
@ -13,8 +13,6 @@
|
||||
|
||||
export let parentMessage;
|
||||
|
||||
export let selectedModelfiles;
|
||||
|
||||
export let updateChatMessages: Function;
|
||||
export let confirmEditResponseMessage: Function;
|
||||
export let rateMessage: Function;
|
||||
@ -130,7 +128,6 @@
|
||||
>
|
||||
<ResponseMessage
|
||||
message={groupedMessages[model].messages[groupedMessagesIdx[model]]}
|
||||
modelfiles={selectedModelfiles}
|
||||
siblings={groupedMessages[model].messages.map((m) => m.id)}
|
||||
isLastMessage={true}
|
||||
{updateChatMessages}
|
||||
|
@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { WEBUI_BASE_URL } from '$lib/constants';
|
||||
import { user } from '$lib/stores';
|
||||
import { config, user, models as _models } from '$lib/stores';
|
||||
import { onMount, getContext } from 'svelte';
|
||||
|
||||
import { blur, fade } from 'svelte/transition';
|
||||
@ -9,23 +9,20 @@
|
||||
|
||||
const i18n = getContext('i18n');
|
||||
|
||||
export let modelIds = [];
|
||||
export let models = [];
|
||||
export let modelfiles = [];
|
||||
|
||||
export let submitPrompt;
|
||||
export let suggestionPrompts;
|
||||
|
||||
let mounted = false;
|
||||
let modelfile = null;
|
||||
let selectedModelIdx = 0;
|
||||
|
||||
$: modelfile =
|
||||
models[selectedModelIdx] in modelfiles ? modelfiles[models[selectedModelIdx]] : null;
|
||||
|
||||
$: if (models.length > 0) {
|
||||
$: if (modelIds.length > 0) {
|
||||
selectedModelIdx = models.length - 1;
|
||||
}
|
||||
|
||||
$: models = modelIds.map((id) => $_models.find((m) => m.id === id));
|
||||
|
||||
onMount(() => {
|
||||
mounted = true;
|
||||
});
|
||||
@ -41,25 +38,14 @@
|
||||
selectedModelIdx = modelIdx;
|
||||
}}
|
||||
>
|
||||
{#if model in modelfiles}
|
||||
<img
|
||||
crossorigin="anonymous"
|
||||
src={modelfiles[model]?.imageUrl ?? `${WEBUI_BASE_URL}/static/favicon.png`}
|
||||
alt="modelfile"
|
||||
class=" size-[2.7rem] rounded-full border-[1px] border-gray-200 dark:border-none"
|
||||
draggable="false"
|
||||
/>
|
||||
{:else}
|
||||
<img
|
||||
crossorigin="anonymous"
|
||||
src={$i18n.language === 'dg-DG'
|
||||
? `/doge.png`
|
||||
: `${WEBUI_BASE_URL}/static/favicon.png`}
|
||||
class=" size-[2.7rem] rounded-full border-[1px] border-gray-200 dark:border-none"
|
||||
alt="logo"
|
||||
draggable="false"
|
||||
/>
|
||||
{/if}
|
||||
<img
|
||||
crossorigin="anonymous"
|
||||
src={model?.info?.meta?.profile_image_url ??
|
||||
($i18n.language === 'dg-DG' ? `/doge.png` : `${WEBUI_BASE_URL}/static/favicon.png`)}
|
||||
class=" size-[2.7rem] rounded-full border-[1px] border-gray-200 dark:border-none"
|
||||
alt="logo"
|
||||
draggable="false"
|
||||
/>
|
||||
</button>
|
||||
{/each}
|
||||
</div>
|
||||
@ -70,23 +56,32 @@
|
||||
>
|
||||
<div>
|
||||
<div class=" capitalize line-clamp-1" in:fade={{ duration: 200 }}>
|
||||
{#if modelfile}
|
||||
{modelfile.title}
|
||||
{#if models[selectedModelIdx]?.info}
|
||||
{models[selectedModelIdx]?.info?.name}
|
||||
{:else}
|
||||
{$i18n.t('Hello, {{name}}', { name: $user.name })}
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<div in:fade={{ duration: 200, delay: 200 }}>
|
||||
{#if modelfile}
|
||||
<div class="mt-0.5 text-base font-normal text-gray-500 dark:text-gray-400">
|
||||
{modelfile.desc}
|
||||
{#if models[selectedModelIdx]?.info}
|
||||
<div class="mt-0.5 text-base font-normal text-gray-500 dark:text-gray-400 line-clamp-3">
|
||||
{models[selectedModelIdx]?.info?.meta?.description}
|
||||
</div>
|
||||
{#if modelfile.user}
|
||||
{#if models[selectedModelIdx]?.info?.meta?.user}
|
||||
<div class="mt-0.5 text-sm font-normal text-gray-400 dark:text-gray-500">
|
||||
By <a href="https://openwebui.com/m/{modelfile.user.username}"
|
||||
>{modelfile.user.name ? modelfile.user.name : `@${modelfile.user.username}`}</a
|
||||
>
|
||||
By
|
||||
{#if models[selectedModelIdx]?.info?.meta?.user.community}
|
||||
<a
|
||||
href="https://openwebui.com/m/{models[selectedModelIdx]?.info?.meta?.user
|
||||
.username}"
|
||||
>{models[selectedModelIdx]?.info?.meta?.user.name
|
||||
? models[selectedModelIdx]?.info?.meta?.user.name
|
||||
: `@${models[selectedModelIdx]?.info?.meta?.user.username}`}</a
|
||||
>
|
||||
{:else}
|
||||
{models[selectedModelIdx]?.info?.meta?.user.name}
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
{:else}
|
||||
@ -99,7 +94,11 @@
|
||||
</div>
|
||||
|
||||
<div class=" w-full" in:fade={{ duration: 200, delay: 300 }}>
|
||||
<Suggestions {suggestionPrompts} {submitPrompt} />
|
||||
<Suggestions
|
||||
suggestionPrompts={models[selectedModelIdx]?.info?.meta?.suggestion_prompts ??
|
||||
$config.default_prompt_suggestions}
|
||||
{submitPrompt}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{/key}
|
||||
|
@ -14,7 +14,7 @@
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
import { config, settings } from '$lib/stores';
|
||||
import { config, models, settings } from '$lib/stores';
|
||||
import { synthesizeOpenAISpeech } from '$lib/apis/audio';
|
||||
import { imageGenerations } from '$lib/apis/images';
|
||||
import {
|
||||
@ -34,7 +34,6 @@
|
||||
import RateComment from './RateComment.svelte';
|
||||
import CitationsModal from '$lib/components/chat/Messages/CitationsModal.svelte';
|
||||
|
||||
export let modelfiles = [];
|
||||
export let message;
|
||||
export let siblings;
|
||||
|
||||
@ -52,6 +51,9 @@
|
||||
export let continueGeneration: Function;
|
||||
export let regenerateResponse: Function;
|
||||
|
||||
let model = null;
|
||||
$: model = $models.find((m) => m.id === message.model);
|
||||
|
||||
let edit = false;
|
||||
let editedContent = '';
|
||||
let editTextAreaElement: HTMLTextAreaElement;
|
||||
@ -338,17 +340,13 @@
|
||||
dir={$settings.chatDirection}
|
||||
>
|
||||
<ProfileImage
|
||||
src={modelfiles[message.model]?.imageUrl ??
|
||||
src={model?.info?.meta?.profile_image_url ??
|
||||
($i18n.language === 'dg-DG' ? `/doge.png` : `${WEBUI_BASE_URL}/static/favicon.png`)}
|
||||
/>
|
||||
|
||||
<div class="w-full overflow-hidden pl-1">
|
||||
<Name>
|
||||
{#if message.model in modelfiles}
|
||||
{modelfiles[message.model]?.title}
|
||||
{:else}
|
||||
{message.model ? ` ${message.model}` : ''}
|
||||
{/if}
|
||||
{model?.name ?? message.model}
|
||||
|
||||
{#if message.timestamp}
|
||||
<span
|
||||
@ -442,8 +440,8 @@
|
||||
{#if token.type === 'code'}
|
||||
<CodeBlock
|
||||
id={`${message.id}-${tokenIdx}`}
|
||||
lang={token.lang}
|
||||
code={revertSanitizedResponseContent(token.text)}
|
||||
lang={token?.lang ?? ''}
|
||||
code={revertSanitizedResponseContent(token?.text ?? '')}
|
||||
/>
|
||||
{:else}
|
||||
{@html marked.parse(token.raw, {
|
||||
|
@ -4,7 +4,7 @@
|
||||
import { tick, createEventDispatcher, getContext } from 'svelte';
|
||||
import Name from './Name.svelte';
|
||||
import ProfileImage from './ProfileImage.svelte';
|
||||
import { modelfiles, settings } from '$lib/stores';
|
||||
import { models, settings } from '$lib/stores';
|
||||
import Tooltip from '$lib/components/common/Tooltip.svelte';
|
||||
|
||||
import { user as _user } from '$lib/stores';
|
||||
@ -60,8 +60,7 @@
|
||||
{#if !($settings?.chatBubble ?? true)}
|
||||
<ProfileImage
|
||||
src={message.user
|
||||
? $modelfiles.find((modelfile) => modelfile.tagName === message.user)?.imageUrl ??
|
||||
'/user.png'
|
||||
? $models.find((m) => m.id === message.user)?.info?.meta?.profile_image_url ?? '/user.png'
|
||||
: user?.profile_image_url ?? '/user.png'}
|
||||
/>
|
||||
{/if}
|
||||
@ -70,12 +69,8 @@
|
||||
<div>
|
||||
<Name>
|
||||
{#if message.user}
|
||||
{#if $modelfiles.map((modelfile) => modelfile.tagName).includes(message.user)}
|
||||
{$modelfiles.find((modelfile) => modelfile.tagName === message.user)?.title}
|
||||
{:else}
|
||||
{$i18n.t('You')}
|
||||
<span class=" text-gray-500 text-sm font-medium">{message?.user ?? ''}</span>
|
||||
{/if}
|
||||
{$i18n.t('You')}
|
||||
<span class=" text-gray-500 text-sm font-medium">{message?.user ?? ''}</span>
|
||||
{:else if $settings.showUsername || $_user.name !== user.name}
|
||||
{user.name}
|
||||
{:else}
|
||||
|
28
src/lib/components/chat/Messages/test.json
Normal file
28
src/lib/components/chat/Messages/test.json
Normal file
File diff suppressed because one or more lines are too long
@ -45,13 +45,11 @@
|
||||
<div class="mr-1 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
|
||||
}))}
|
||||
items={$models.map((model) => ({
|
||||
value: model.id,
|
||||
label: model.name,
|
||||
model: model
|
||||
}))}
|
||||
bind:value={selectedModel}
|
||||
/>
|
||||
</div>
|
||||
|
@ -12,7 +12,9 @@
|
||||
|
||||
import { user, MODEL_DOWNLOAD_POOL, models, mobile } from '$lib/stores';
|
||||
import { toast } from 'svelte-sonner';
|
||||
import { capitalizeFirstLetter, getModels, splitStream } from '$lib/utils';
|
||||
import { capitalizeFirstLetter, sanitizeResponseContent, splitStream } from '$lib/utils';
|
||||
import { getModels } from '$lib/apis';
|
||||
|
||||
import Tooltip from '$lib/components/common/Tooltip.svelte';
|
||||
|
||||
const i18n = getContext('i18n');
|
||||
@ -23,7 +25,12 @@
|
||||
export let searchEnabled = true;
|
||||
export let searchPlaceholder = $i18n.t('Search a model');
|
||||
|
||||
export let items = [{ value: 'mango', label: 'Mango' }];
|
||||
export let items: {
|
||||
label: string;
|
||||
value: string;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
[key: string]: any;
|
||||
} = [];
|
||||
|
||||
export let className = 'w-[30rem]';
|
||||
|
||||
@ -239,19 +246,37 @@
|
||||
}}
|
||||
>
|
||||
<div class="flex items-center gap-2">
|
||||
<div class="line-clamp-1">
|
||||
{item.label}
|
||||
|
||||
<span class=" text-xs font-medium text-gray-600 dark:text-gray-400"
|
||||
>{item.info?.details?.parameter_size ?? ''}</span
|
||||
>
|
||||
<div class="flex items-center">
|
||||
<div class="line-clamp-1">
|
||||
{item.label}
|
||||
</div>
|
||||
{#if item.model.owned_by === 'ollama' && (item.model.ollama?.details?.parameter_size ?? '') !== ''}
|
||||
<div class="flex ml-1 items-center">
|
||||
<Tooltip
|
||||
content={`${
|
||||
item.model.ollama?.details?.quantization_level
|
||||
? item.model.ollama?.details?.quantization_level + ' '
|
||||
: ''
|
||||
}${
|
||||
item.model.ollama?.size
|
||||
? `(${(item.model.ollama?.size / 1024 ** 3).toFixed(1)}GB)`
|
||||
: ''
|
||||
}`}
|
||||
className="self-end"
|
||||
>
|
||||
<span class=" text-xs font-medium text-gray-600 dark:text-gray-400"
|
||||
>{item.model.ollama?.details?.parameter_size ?? ''}</span
|
||||
>
|
||||
</Tooltip>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<!-- {JSON.stringify(item.info)} -->
|
||||
|
||||
{#if item.info.external}
|
||||
<Tooltip content={item.info?.source ?? 'External'}>
|
||||
<div class=" mr-2">
|
||||
{#if item.model.owned_by === 'openai'}
|
||||
<Tooltip content={`${'External'}`}>
|
||||
<div class="">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 16 16"
|
||||
@ -271,15 +296,15 @@
|
||||
</svg>
|
||||
</div>
|
||||
</Tooltip>
|
||||
{:else}
|
||||
{/if}
|
||||
|
||||
{#if item.model?.info?.meta?.description}
|
||||
<Tooltip
|
||||
content={`${
|
||||
item.info?.details?.quantization_level
|
||||
? item.info?.details?.quantization_level + ' '
|
||||
: ''
|
||||
}${item.info.size ? `(${(item.info.size / 1024 ** 3).toFixed(1)}GB)` : ''}`}
|
||||
content={`${sanitizeResponseContent(
|
||||
item.model?.info?.meta?.description
|
||||
).replaceAll('\n', '<br>')}`}
|
||||
>
|
||||
<div class=" mr-2">
|
||||
<div class="">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
|
@ -1,155 +0,0 @@
|
||||
<script lang="ts">
|
||||
import { createEventDispatcher, onMount, getContext } from 'svelte';
|
||||
import AdvancedParams from './Advanced/AdvancedParams.svelte';
|
||||
|
||||
const i18n = getContext('i18n');
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
export let saveSettings: Function;
|
||||
|
||||
// Advanced
|
||||
let requestFormat = '';
|
||||
let keepAlive = null;
|
||||
|
||||
let options = {
|
||||
// Advanced
|
||||
seed: 0,
|
||||
temperature: '',
|
||||
repeat_penalty: '',
|
||||
repeat_last_n: '',
|
||||
mirostat: '',
|
||||
mirostat_eta: '',
|
||||
mirostat_tau: '',
|
||||
top_k: '',
|
||||
top_p: '',
|
||||
stop: '',
|
||||
tfs_z: '',
|
||||
num_ctx: '',
|
||||
num_predict: ''
|
||||
};
|
||||
|
||||
const toggleRequestFormat = async () => {
|
||||
if (requestFormat === '') {
|
||||
requestFormat = 'json';
|
||||
} else {
|
||||
requestFormat = '';
|
||||
}
|
||||
|
||||
saveSettings({ requestFormat: requestFormat !== '' ? requestFormat : undefined });
|
||||
};
|
||||
|
||||
onMount(() => {
|
||||
let settings = JSON.parse(localStorage.getItem('settings') ?? '{}');
|
||||
|
||||
requestFormat = settings.requestFormat ?? '';
|
||||
keepAlive = settings.keepAlive ?? null;
|
||||
|
||||
options.seed = settings.seed ?? 0;
|
||||
options.temperature = settings.temperature ?? '';
|
||||
options.repeat_penalty = settings.repeat_penalty ?? '';
|
||||
options.top_k = settings.top_k ?? '';
|
||||
options.top_p = settings.top_p ?? '';
|
||||
options.num_ctx = settings.num_ctx ?? '';
|
||||
options = { ...options, ...settings.options };
|
||||
options.stop = (settings?.options?.stop ?? []).join(',');
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="flex flex-col h-full justify-between text-sm">
|
||||
<div class=" space-y-3 pr-1.5 overflow-y-scroll max-h-80">
|
||||
<div class=" text-sm font-medium">{$i18n.t('Parameters')}</div>
|
||||
|
||||
<AdvancedParams bind:options />
|
||||
<hr class=" dark:border-gray-700" />
|
||||
|
||||
<div class=" py-1 w-full justify-between">
|
||||
<div class="flex w-full justify-between">
|
||||
<div class=" self-center text-xs font-medium">{$i18n.t('Keep Alive')}</div>
|
||||
|
||||
<button
|
||||
class="p-1 px-3 text-xs flex rounded transition"
|
||||
type="button"
|
||||
on:click={() => {
|
||||
keepAlive = keepAlive === null ? '5m' : null;
|
||||
}}
|
||||
>
|
||||
{#if keepAlive === null}
|
||||
<span class="ml-2 self-center">{$i18n.t('Default')}</span>
|
||||
{:else}
|
||||
<span class="ml-2 self-center">{$i18n.t('Custom')}</span>
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{#if keepAlive !== null}
|
||||
<div class="flex mt-1 space-x-2">
|
||||
<input
|
||||
class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
|
||||
type="text"
|
||||
placeholder={$i18n.t("e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.")}
|
||||
bind:value={keepAlive}
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class=" py-1 flex w-full justify-between">
|
||||
<div class=" self-center text-sm font-medium">{$i18n.t('Request Mode')}</div>
|
||||
|
||||
<button
|
||||
class="p-1 px-3 text-xs flex rounded transition"
|
||||
on:click={() => {
|
||||
toggleRequestFormat();
|
||||
}}
|
||||
>
|
||||
{#if requestFormat === ''}
|
||||
<span class="ml-2 self-center"> {$i18n.t('Default')} </span>
|
||||
{:else if requestFormat === 'json'}
|
||||
<!-- <svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 20 20"
|
||||
fill="currentColor"
|
||||
class="w-4 h-4 self-center"
|
||||
>
|
||||
<path
|
||||
d="M10 2a.75.75 0 01.75.75v1.5a.75.75 0 01-1.5 0v-1.5A.75.75 0 0110 2zM10 15a.75.75 0 01.75.75v1.5a.75.75 0 01-1.5 0v-1.5A.75.75 0 0110 15zM10 7a3 3 0 100 6 3 3 0 000-6zM15.657 5.404a.75.75 0 10-1.06-1.06l-1.061 1.06a.75.75 0 001.06 1.06l1.06-1.06zM6.464 14.596a.75.75 0 10-1.06-1.06l-1.06 1.06a.75.75 0 001.06 1.06l1.06-1.06zM18 10a.75.75 0 01-.75.75h-1.5a.75.75 0 010-1.5h1.5A.75.75 0 0118 10zM5 10a.75.75 0 01-.75.75h-1.5a.75.75 0 010-1.5h1.5A.75.75 0 015 10zM14.596 15.657a.75.75 0 001.06-1.06l-1.06-1.061a.75.75 0 10-1.06 1.06l1.06 1.06zM5.404 6.464a.75.75 0 001.06-1.06l-1.06-1.06a.75.75 0 10-1.061 1.06l1.06 1.06z"
|
||||
/>
|
||||
</svg> -->
|
||||
<span class="ml-2 self-center">{$i18n.t('JSON')}</span>
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex justify-end pt-3 text-sm font-medium">
|
||||
<button
|
||||
class=" px-4 py-2 bg-emerald-700 hover:bg-emerald-800 text-gray-100 transition rounded-lg"
|
||||
on:click={() => {
|
||||
saveSettings({
|
||||
options: {
|
||||
seed: (options.seed !== 0 ? options.seed : undefined) ?? undefined,
|
||||
stop: options.stop !== '' ? options.stop.split(',').filter((e) => e) : undefined,
|
||||
temperature: options.temperature !== '' ? options.temperature : undefined,
|
||||
repeat_penalty: options.repeat_penalty !== '' ? options.repeat_penalty : undefined,
|
||||
repeat_last_n: options.repeat_last_n !== '' ? options.repeat_last_n : undefined,
|
||||
mirostat: options.mirostat !== '' ? options.mirostat : undefined,
|
||||
mirostat_eta: options.mirostat_eta !== '' ? options.mirostat_eta : undefined,
|
||||
mirostat_tau: options.mirostat_tau !== '' ? options.mirostat_tau : undefined,
|
||||
top_k: options.top_k !== '' ? options.top_k : undefined,
|
||||
top_p: options.top_p !== '' ? options.top_p : undefined,
|
||||
tfs_z: options.tfs_z !== '' ? options.tfs_z : undefined,
|
||||
num_ctx: options.num_ctx !== '' ? options.num_ctx : undefined,
|
||||
num_predict: options.num_predict !== '' ? options.num_predict : undefined
|
||||
},
|
||||
keepAlive: keepAlive ? (isNaN(keepAlive) ? keepAlive : parseInt(keepAlive)) : undefined
|
||||
});
|
||||
|
||||
dispatch('save');
|
||||
}}
|
||||
>
|
||||
{$i18n.t('Save')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
@ -1,14 +1,16 @@
|
||||
<script lang="ts">
|
||||
import { getContext } from 'svelte';
|
||||
import { getContext, createEventDispatcher } from 'svelte';
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
const i18n = getContext('i18n');
|
||||
|
||||
export let options = {
|
||||
export let params = {
|
||||
// Advanced
|
||||
seed: 0,
|
||||
stop: '',
|
||||
stop: null,
|
||||
temperature: '',
|
||||
repeat_penalty: '',
|
||||
frequency_penalty: '',
|
||||
repeat_last_n: '',
|
||||
mirostat: '',
|
||||
mirostat_eta: '',
|
||||
@ -17,40 +19,86 @@
|
||||
top_p: '',
|
||||
tfs_z: '',
|
||||
num_ctx: '',
|
||||
num_predict: ''
|
||||
max_tokens: '',
|
||||
template: null
|
||||
};
|
||||
|
||||
let customFieldName = '';
|
||||
let customFieldValue = '';
|
||||
|
||||
$: if (params) {
|
||||
dispatch('change', params);
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class=" space-y-3 text-xs">
|
||||
<div>
|
||||
<div class=" py-0.5 flex w-full justify-between">
|
||||
<div class=" w-20 text-xs font-medium self-center">{$i18n.t('Seed')}</div>
|
||||
<div class=" flex-1 self-center">
|
||||
<input
|
||||
class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
|
||||
type="number"
|
||||
placeholder="Enter Seed"
|
||||
bind:value={options.seed}
|
||||
autocomplete="off"
|
||||
min="0"
|
||||
/>
|
||||
</div>
|
||||
<div class=" space-y-1 text-xs">
|
||||
<div class=" py-0.5 w-full justify-between">
|
||||
<div class="flex w-full justify-between">
|
||||
<div class=" self-center text-xs font-medium">{$i18n.t('Seed')}</div>
|
||||
|
||||
<button
|
||||
class="p-1 px-3 text-xs flex rounded transition"
|
||||
type="button"
|
||||
on:click={() => {
|
||||
params.seed = (params?.seed ?? null) === null ? 0 : null;
|
||||
}}
|
||||
>
|
||||
{#if (params?.seed ?? null) === null}
|
||||
<span class="ml-2 self-center"> {$i18n.t('Default')} </span>
|
||||
{:else}
|
||||
<span class="ml-2 self-center"> {$i18n.t('Custom')} </span>
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{#if (params?.seed ?? null) !== null}
|
||||
<div class="flex mt-0.5 space-x-2">
|
||||
<div class=" flex-1">
|
||||
<input
|
||||
class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
|
||||
type="number"
|
||||
placeholder="Enter Seed"
|
||||
bind:value={params.seed}
|
||||
autocomplete="off"
|
||||
min="0"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class=" py-0.5 flex w-full justify-between">
|
||||
<div class=" w-20 text-xs font-medium self-center">{$i18n.t('Stop Sequence')}</div>
|
||||
<div class=" flex-1 self-center">
|
||||
<input
|
||||
class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
|
||||
type="text"
|
||||
placeholder={$i18n.t('Enter stop sequence')}
|
||||
bind:value={options.stop}
|
||||
autocomplete="off"
|
||||
/>
|
||||
</div>
|
||||
<div class=" py-0.5 w-full justify-between">
|
||||
<div class="flex w-full justify-between">
|
||||
<div class=" self-center text-xs font-medium">{$i18n.t('Stop Sequence')}</div>
|
||||
|
||||
<button
|
||||
class="p-1 px-3 text-xs flex rounded transition"
|
||||
type="button"
|
||||
on:click={() => {
|
||||
params.stop = (params?.stop ?? null) === null ? '' : null;
|
||||
}}
|
||||
>
|
||||
{#if (params?.stop ?? null) === null}
|
||||
<span class="ml-2 self-center"> {$i18n.t('Default')} </span>
|
||||
{:else}
|
||||
<span class="ml-2 self-center"> {$i18n.t('Custom')} </span>
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{#if (params?.stop ?? null) !== null}
|
||||
<div class="flex mt-0.5 space-x-2">
|
||||
<div class=" flex-1">
|
||||
<input
|
||||
class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
|
||||
type="text"
|
||||
placeholder={$i18n.t('Enter stop sequence')}
|
||||
bind:value={params.stop}
|
||||
autocomplete="off"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<div class=" py-0.5 w-full justify-between">
|
||||
@ -61,10 +109,10 @@
|
||||
class="p-1 px-3 text-xs flex rounded transition"
|
||||
type="button"
|
||||
on:click={() => {
|
||||
options.temperature = options.temperature === '' ? 0.8 : '';
|
||||
params.temperature = (params?.temperature ?? '') === '' ? 0.8 : '';
|
||||
}}
|
||||
>
|
||||
{#if options.temperature === ''}
|
||||
{#if (params?.temperature ?? '') === ''}
|
||||
<span class="ml-2 self-center"> {$i18n.t('Default')} </span>
|
||||
{:else}
|
||||
<span class="ml-2 self-center"> {$i18n.t('Custom')} </span>
|
||||
@ -72,7 +120,7 @@
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{#if options.temperature !== ''}
|
||||
{#if (params?.temperature ?? '') !== ''}
|
||||
<div class="flex mt-0.5 space-x-2">
|
||||
<div class=" flex-1">
|
||||
<input
|
||||
@ -81,13 +129,13 @@
|
||||
min="0"
|
||||
max="1"
|
||||
step="0.05"
|
||||
bind:value={options.temperature}
|
||||
bind:value={params.temperature}
|
||||
class="w-full h-2 rounded-lg appearance-none cursor-pointer dark:bg-gray-700"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<input
|
||||
bind:value={options.temperature}
|
||||
bind:value={params.temperature}
|
||||
type="number"
|
||||
class=" bg-transparent text-center w-14"
|
||||
min="0"
|
||||
@ -107,18 +155,18 @@
|
||||
class="p-1 px-3 text-xs flex rounded transition"
|
||||
type="button"
|
||||
on:click={() => {
|
||||
options.mirostat = options.mirostat === '' ? 0 : '';
|
||||
params.mirostat = (params?.mirostat ?? '') === '' ? 0 : '';
|
||||
}}
|
||||
>
|
||||
{#if options.mirostat === ''}
|
||||
{#if (params?.mirostat ?? '') === ''}
|
||||
<span class="ml-2 self-center">{$i18n.t('Default')}</span>
|
||||
{:else}
|
||||
<span class="ml-2 self-center">{$i18n.t('Default')}</span>
|
||||
<span class="ml-2 self-center">{$i18n.t('Custom')}</span>
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{#if options.mirostat !== ''}
|
||||
{#if (params?.mirostat ?? '') !== ''}
|
||||
<div class="flex mt-0.5 space-x-2">
|
||||
<div class=" flex-1">
|
||||
<input
|
||||
@ -127,13 +175,13 @@
|
||||
min="0"
|
||||
max="2"
|
||||
step="1"
|
||||
bind:value={options.mirostat}
|
||||
bind:value={params.mirostat}
|
||||
class="w-full h-2 rounded-lg appearance-none cursor-pointer dark:bg-gray-700"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<input
|
||||
bind:value={options.mirostat}
|
||||
bind:value={params.mirostat}
|
||||
type="number"
|
||||
class=" bg-transparent text-center w-14"
|
||||
min="0"
|
||||
@ -153,18 +201,18 @@
|
||||
class="p-1 px-3 text-xs flex rounded transition"
|
||||
type="button"
|
||||
on:click={() => {
|
||||
options.mirostat_eta = options.mirostat_eta === '' ? 0.1 : '';
|
||||
params.mirostat_eta = (params?.mirostat_eta ?? '') === '' ? 0.1 : '';
|
||||
}}
|
||||
>
|
||||
{#if options.mirostat_eta === ''}
|
||||
{#if (params?.mirostat_eta ?? '') === ''}
|
||||
<span class="ml-2 self-center">{$i18n.t('Default')}</span>
|
||||
{:else}
|
||||
<span class="ml-2 self-center">{$i18n.t('Default')}</span>
|
||||
<span class="ml-2 self-center">{$i18n.t('Custom')}</span>
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{#if options.mirostat_eta !== ''}
|
||||
{#if (params?.mirostat_eta ?? '') !== ''}
|
||||
<div class="flex mt-0.5 space-x-2">
|
||||
<div class=" flex-1">
|
||||
<input
|
||||
@ -173,13 +221,13 @@
|
||||
min="0"
|
||||
max="1"
|
||||
step="0.05"
|
||||
bind:value={options.mirostat_eta}
|
||||
bind:value={params.mirostat_eta}
|
||||
class="w-full h-2 rounded-lg appearance-none cursor-pointer dark:bg-gray-700"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<input
|
||||
bind:value={options.mirostat_eta}
|
||||
bind:value={params.mirostat_eta}
|
||||
type="number"
|
||||
class=" bg-transparent text-center w-14"
|
||||
min="0"
|
||||
@ -199,10 +247,10 @@
|
||||
class="p-1 px-3 text-xs flex rounded transition"
|
||||
type="button"
|
||||
on:click={() => {
|
||||
options.mirostat_tau = options.mirostat_tau === '' ? 5.0 : '';
|
||||
params.mirostat_tau = (params?.mirostat_tau ?? '') === '' ? 5.0 : '';
|
||||
}}
|
||||
>
|
||||
{#if options.mirostat_tau === ''}
|
||||
{#if (params?.mirostat_tau ?? '') === ''}
|
||||
<span class="ml-2 self-center">{$i18n.t('Default')}</span>
|
||||
{:else}
|
||||
<span class="ml-2 self-center">{$i18n.t('Custom')}</span>
|
||||
@ -210,7 +258,7 @@
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{#if options.mirostat_tau !== ''}
|
||||
{#if (params?.mirostat_tau ?? '') !== ''}
|
||||
<div class="flex mt-0.5 space-x-2">
|
||||
<div class=" flex-1">
|
||||
<input
|
||||
@ -219,13 +267,13 @@
|
||||
min="0"
|
||||
max="10"
|
||||
step="0.5"
|
||||
bind:value={options.mirostat_tau}
|
||||
bind:value={params.mirostat_tau}
|
||||
class="w-full h-2 rounded-lg appearance-none cursor-pointer dark:bg-gray-700"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<input
|
||||
bind:value={options.mirostat_tau}
|
||||
bind:value={params.mirostat_tau}
|
||||
type="number"
|
||||
class=" bg-transparent text-center w-14"
|
||||
min="0"
|
||||
@ -245,18 +293,18 @@
|
||||
class="p-1 px-3 text-xs flex rounded transition"
|
||||
type="button"
|
||||
on:click={() => {
|
||||
options.top_k = options.top_k === '' ? 40 : '';
|
||||
params.top_k = (params?.top_k ?? '') === '' ? 40 : '';
|
||||
}}
|
||||
>
|
||||
{#if options.top_k === ''}
|
||||
{#if (params?.top_k ?? '') === ''}
|
||||
<span class="ml-2 self-center">{$i18n.t('Default')}</span>
|
||||
{:else}
|
||||
<span class="ml-2 self-center">{$i18n.t('Default')}</span>
|
||||
<span class="ml-2 self-center">{$i18n.t('Custom')}</span>
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{#if options.top_k !== ''}
|
||||
{#if (params?.top_k ?? '') !== ''}
|
||||
<div class="flex mt-0.5 space-x-2">
|
||||
<div class=" flex-1">
|
||||
<input
|
||||
@ -265,13 +313,13 @@
|
||||
min="0"
|
||||
max="100"
|
||||
step="0.5"
|
||||
bind:value={options.top_k}
|
||||
bind:value={params.top_k}
|
||||
class="w-full h-2 rounded-lg appearance-none cursor-pointer dark:bg-gray-700"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<input
|
||||
bind:value={options.top_k}
|
||||
bind:value={params.top_k}
|
||||
type="number"
|
||||
class=" bg-transparent text-center w-14"
|
||||
min="0"
|
||||
@ -291,18 +339,18 @@
|
||||
class="p-1 px-3 text-xs flex rounded transition"
|
||||
type="button"
|
||||
on:click={() => {
|
||||
options.top_p = options.top_p === '' ? 0.9 : '';
|
||||
params.top_p = (params?.top_p ?? '') === '' ? 0.9 : '';
|
||||
}}
|
||||
>
|
||||
{#if options.top_p === ''}
|
||||
{#if (params?.top_p ?? '') === ''}
|
||||
<span class="ml-2 self-center">{$i18n.t('Default')}</span>
|
||||
{:else}
|
||||
<span class="ml-2 self-center">{$i18n.t('Default')}</span>
|
||||
<span class="ml-2 self-center">{$i18n.t('Custom')}</span>
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{#if options.top_p !== ''}
|
||||
{#if (params?.top_p ?? '') !== ''}
|
||||
<div class="flex mt-0.5 space-x-2">
|
||||
<div class=" flex-1">
|
||||
<input
|
||||
@ -311,13 +359,13 @@
|
||||
min="0"
|
||||
max="1"
|
||||
step="0.05"
|
||||
bind:value={options.top_p}
|
||||
bind:value={params.top_p}
|
||||
class="w-full h-2 rounded-lg appearance-none cursor-pointer dark:bg-gray-700"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<input
|
||||
bind:value={options.top_p}
|
||||
bind:value={params.top_p}
|
||||
type="number"
|
||||
class=" bg-transparent text-center w-14"
|
||||
min="0"
|
||||
@ -331,24 +379,24 @@
|
||||
|
||||
<div class=" py-0.5 w-full justify-between">
|
||||
<div class="flex w-full justify-between">
|
||||
<div class=" self-center text-xs font-medium">{$i18n.t('Repeat Penalty')}</div>
|
||||
<div class=" self-center text-xs font-medium">{$i18n.t('Frequencey Penalty')}</div>
|
||||
|
||||
<button
|
||||
class="p-1 px-3 text-xs flex rounded transition"
|
||||
type="button"
|
||||
on:click={() => {
|
||||
options.repeat_penalty = options.repeat_penalty === '' ? 1.1 : '';
|
||||
params.frequency_penalty = (params?.frequency_penalty ?? '') === '' ? 1.1 : '';
|
||||
}}
|
||||
>
|
||||
{#if options.repeat_penalty === ''}
|
||||
{#if (params?.frequency_penalty ?? '') === ''}
|
||||
<span class="ml-2 self-center">{$i18n.t('Default')}</span>
|
||||
{:else}
|
||||
<span class="ml-2 self-center">{$i18n.t('Default')}</span>
|
||||
<span class="ml-2 self-center">{$i18n.t('Custom')}</span>
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{#if options.repeat_penalty !== ''}
|
||||
{#if (params?.frequency_penalty ?? '') !== ''}
|
||||
<div class="flex mt-0.5 space-x-2">
|
||||
<div class=" flex-1">
|
||||
<input
|
||||
@ -357,13 +405,13 @@
|
||||
min="0"
|
||||
max="2"
|
||||
step="0.05"
|
||||
bind:value={options.repeat_penalty}
|
||||
bind:value={params.frequency_penalty}
|
||||
class="w-full h-2 rounded-lg appearance-none cursor-pointer dark:bg-gray-700"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<input
|
||||
bind:value={options.repeat_penalty}
|
||||
bind:value={params.frequency_penalty}
|
||||
type="number"
|
||||
class=" bg-transparent text-center w-14"
|
||||
min="0"
|
||||
@ -383,18 +431,18 @@
|
||||
class="p-1 px-3 text-xs flex rounded transition"
|
||||
type="button"
|
||||
on:click={() => {
|
||||
options.repeat_last_n = options.repeat_last_n === '' ? 64 : '';
|
||||
params.repeat_last_n = (params?.repeat_last_n ?? '') === '' ? 64 : '';
|
||||
}}
|
||||
>
|
||||
{#if options.repeat_last_n === ''}
|
||||
{#if (params?.repeat_last_n ?? '') === ''}
|
||||
<span class="ml-2 self-center">{$i18n.t('Default')}</span>
|
||||
{:else}
|
||||
<span class="ml-2 self-center">{$i18n.t('Default')}</span>
|
||||
<span class="ml-2 self-center">{$i18n.t('Custom')}</span>
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{#if options.repeat_last_n !== ''}
|
||||
{#if (params?.repeat_last_n ?? '') !== ''}
|
||||
<div class="flex mt-0.5 space-x-2">
|
||||
<div class=" flex-1">
|
||||
<input
|
||||
@ -403,13 +451,13 @@
|
||||
min="-1"
|
||||
max="128"
|
||||
step="1"
|
||||
bind:value={options.repeat_last_n}
|
||||
bind:value={params.repeat_last_n}
|
||||
class="w-full h-2 rounded-lg appearance-none cursor-pointer dark:bg-gray-700"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<input
|
||||
bind:value={options.repeat_last_n}
|
||||
bind:value={params.repeat_last_n}
|
||||
type="number"
|
||||
class=" bg-transparent text-center w-14"
|
||||
min="-1"
|
||||
@ -429,18 +477,18 @@
|
||||
class="p-1 px-3 text-xs flex rounded transition"
|
||||
type="button"
|
||||
on:click={() => {
|
||||
options.tfs_z = options.tfs_z === '' ? 1 : '';
|
||||
params.tfs_z = (params?.tfs_z ?? '') === '' ? 1 : '';
|
||||
}}
|
||||
>
|
||||
{#if options.tfs_z === ''}
|
||||
{#if (params?.tfs_z ?? '') === ''}
|
||||
<span class="ml-2 self-center">{$i18n.t('Default')}</span>
|
||||
{:else}
|
||||
<span class="ml-2 self-center">{$i18n.t('Default')}</span>
|
||||
<span class="ml-2 self-center">{$i18n.t('Custom')}</span>
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{#if options.tfs_z !== ''}
|
||||
{#if (params?.tfs_z ?? '') !== ''}
|
||||
<div class="flex mt-0.5 space-x-2">
|
||||
<div class=" flex-1">
|
||||
<input
|
||||
@ -449,13 +497,13 @@
|
||||
min="0"
|
||||
max="2"
|
||||
step="0.05"
|
||||
bind:value={options.tfs_z}
|
||||
bind:value={params.tfs_z}
|
||||
class="w-full h-2 rounded-lg appearance-none cursor-pointer dark:bg-gray-700"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<input
|
||||
bind:value={options.tfs_z}
|
||||
bind:value={params.tfs_z}
|
||||
type="number"
|
||||
class=" bg-transparent text-center w-14"
|
||||
min="0"
|
||||
@ -475,18 +523,18 @@
|
||||
class="p-1 px-3 text-xs flex rounded transition"
|
||||
type="button"
|
||||
on:click={() => {
|
||||
options.num_ctx = options.num_ctx === '' ? 2048 : '';
|
||||
params.num_ctx = (params?.num_ctx ?? '') === '' ? 2048 : '';
|
||||
}}
|
||||
>
|
||||
{#if options.num_ctx === ''}
|
||||
{#if (params?.num_ctx ?? '') === ''}
|
||||
<span class="ml-2 self-center">{$i18n.t('Default')}</span>
|
||||
{:else}
|
||||
<span class="ml-2 self-center">{$i18n.t('Default')}</span>
|
||||
<span class="ml-2 self-center">{$i18n.t('Custom')}</span>
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{#if options.num_ctx !== ''}
|
||||
{#if (params?.num_ctx ?? '') !== ''}
|
||||
<div class="flex mt-0.5 space-x-2">
|
||||
<div class=" flex-1">
|
||||
<input
|
||||
@ -495,13 +543,13 @@
|
||||
min="-1"
|
||||
max="10240000"
|
||||
step="1"
|
||||
bind:value={options.num_ctx}
|
||||
bind:value={params.num_ctx}
|
||||
class="w-full h-2 rounded-lg appearance-none cursor-pointer dark:bg-gray-700"
|
||||
/>
|
||||
</div>
|
||||
<div class="">
|
||||
<input
|
||||
bind:value={options.num_ctx}
|
||||
bind:value={params.num_ctx}
|
||||
type="number"
|
||||
class=" bg-transparent text-center w-14"
|
||||
min="-1"
|
||||
@ -513,24 +561,24 @@
|
||||
</div>
|
||||
<div class=" py-0.5 w-full justify-between">
|
||||
<div class="flex w-full justify-between">
|
||||
<div class=" self-center text-xs font-medium">{$i18n.t('Max Tokens')}</div>
|
||||
<div class=" self-center text-xs font-medium">{$i18n.t('Max Tokens (num_predict)')}</div>
|
||||
|
||||
<button
|
||||
class="p-1 px-3 text-xs flex rounded transition"
|
||||
type="button"
|
||||
on:click={() => {
|
||||
options.num_predict = options.num_predict === '' ? 128 : '';
|
||||
params.max_tokens = (params?.max_tokens ?? '') === '' ? 128 : '';
|
||||
}}
|
||||
>
|
||||
{#if options.num_predict === ''}
|
||||
{#if (params?.max_tokens ?? '') === ''}
|
||||
<span class="ml-2 self-center">{$i18n.t('Default')}</span>
|
||||
{:else}
|
||||
<span class="ml-2 self-center">{$i18n.t('Default')}</span>
|
||||
<span class="ml-2 self-center">{$i18n.t('Custom')}</span>
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{#if options.num_predict !== ''}
|
||||
{#if (params?.max_tokens ?? '') !== ''}
|
||||
<div class="flex mt-0.5 space-x-2">
|
||||
<div class=" flex-1">
|
||||
<input
|
||||
@ -539,13 +587,13 @@
|
||||
min="-2"
|
||||
max="16000"
|
||||
step="1"
|
||||
bind:value={options.num_predict}
|
||||
bind:value={params.max_tokens}
|
||||
class="w-full h-2 rounded-lg appearance-none cursor-pointer dark:bg-gray-700"
|
||||
/>
|
||||
</div>
|
||||
<div class="">
|
||||
<input
|
||||
bind:value={options.num_predict}
|
||||
bind:value={params.max_tokens}
|
||||
type="number"
|
||||
class=" bg-transparent text-center w-14"
|
||||
min="-2"
|
||||
@ -556,4 +604,36 @@
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
<div class=" py-0.5 w-full justify-between">
|
||||
<div class="flex w-full justify-between">
|
||||
<div class=" self-center text-xs font-medium">{$i18n.t('Template')}</div>
|
||||
|
||||
<button
|
||||
class="p-1 px-3 text-xs flex rounded transition"
|
||||
type="button"
|
||||
on:click={() => {
|
||||
params.template = (params?.template ?? null) === null ? '' : null;
|
||||
}}
|
||||
>
|
||||
{#if (params?.template ?? null) === null}
|
||||
<span class="ml-2 self-center">{$i18n.t('Default')}</span>
|
||||
{:else}
|
||||
<span class="ml-2 self-center">{$i18n.t('Custom')}</span>
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{#if (params?.template ?? null) !== null}
|
||||
<div class="flex mt-0.5 space-x-2">
|
||||
<div class=" flex-1">
|
||||
<textarea
|
||||
class="px-3 py-1.5 text-sm w-full bg-transparent border dark:border-gray-600 outline-none rounded-lg -mb-1"
|
||||
placeholder="Write your model template content here"
|
||||
rows="4"
|
||||
bind:value={params.template}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -41,21 +41,21 @@
|
||||
let requestFormat = '';
|
||||
let keepAlive = null;
|
||||
|
||||
let options = {
|
||||
let params = {
|
||||
// Advanced
|
||||
seed: 0,
|
||||
temperature: '',
|
||||
repeat_penalty: '',
|
||||
frequency_penalty: '',
|
||||
repeat_last_n: '',
|
||||
mirostat: '',
|
||||
mirostat_eta: '',
|
||||
mirostat_tau: '',
|
||||
top_k: '',
|
||||
top_p: '',
|
||||
stop: '',
|
||||
stop: null,
|
||||
tfs_z: '',
|
||||
num_ctx: '',
|
||||
num_predict: ''
|
||||
max_tokens: ''
|
||||
};
|
||||
|
||||
const toggleRequestFormat = async () => {
|
||||
@ -80,14 +80,14 @@
|
||||
requestFormat = settings.requestFormat ?? '';
|
||||
keepAlive = settings.keepAlive ?? null;
|
||||
|
||||
options.seed = settings.seed ?? 0;
|
||||
options.temperature = settings.temperature ?? '';
|
||||
options.repeat_penalty = settings.repeat_penalty ?? '';
|
||||
options.top_k = settings.top_k ?? '';
|
||||
options.top_p = settings.top_p ?? '';
|
||||
options.num_ctx = settings.num_ctx ?? '';
|
||||
options = { ...options, ...settings.options };
|
||||
options.stop = (settings?.options?.stop ?? []).join(',');
|
||||
params.seed = settings.seed ?? 0;
|
||||
params.temperature = settings.temperature ?? '';
|
||||
params.frequency_penalty = settings.frequency_penalty ?? '';
|
||||
params.top_k = settings.top_k ?? '';
|
||||
params.top_p = settings.top_p ?? '';
|
||||
params.num_ctx = settings.num_ctx ?? '';
|
||||
params = { ...params, ...settings.params };
|
||||
params.stop = settings?.params?.stop ? (settings?.params?.stop ?? []).join(',') : null;
|
||||
});
|
||||
|
||||
const applyTheme = (_theme: string) => {
|
||||
@ -228,7 +228,7 @@
|
||||
</div>
|
||||
|
||||
{#if showAdvanced}
|
||||
<AdvancedParams bind:options />
|
||||
<AdvancedParams bind:params />
|
||||
<hr class=" dark:border-gray-700" />
|
||||
|
||||
<div class=" py-1 w-full justify-between">
|
||||
@ -300,20 +300,21 @@
|
||||
on:click={() => {
|
||||
saveSettings({
|
||||
system: system !== '' ? system : undefined,
|
||||
options: {
|
||||
seed: (options.seed !== 0 ? options.seed : undefined) ?? undefined,
|
||||
stop: options.stop !== '' ? options.stop.split(',').filter((e) => e) : undefined,
|
||||
temperature: options.temperature !== '' ? options.temperature : undefined,
|
||||
repeat_penalty: options.repeat_penalty !== '' ? options.repeat_penalty : undefined,
|
||||
repeat_last_n: options.repeat_last_n !== '' ? options.repeat_last_n : undefined,
|
||||
mirostat: options.mirostat !== '' ? options.mirostat : undefined,
|
||||
mirostat_eta: options.mirostat_eta !== '' ? options.mirostat_eta : undefined,
|
||||
mirostat_tau: options.mirostat_tau !== '' ? options.mirostat_tau : undefined,
|
||||
top_k: options.top_k !== '' ? options.top_k : undefined,
|
||||
top_p: options.top_p !== '' ? options.top_p : undefined,
|
||||
tfs_z: options.tfs_z !== '' ? options.tfs_z : undefined,
|
||||
num_ctx: options.num_ctx !== '' ? options.num_ctx : undefined,
|
||||
num_predict: options.num_predict !== '' ? options.num_predict : undefined
|
||||
params: {
|
||||
seed: (params.seed !== 0 ? params.seed : undefined) ?? undefined,
|
||||
stop: params.stop !== null ? params.stop.split(',').filter((e) => e) : undefined,
|
||||
temperature: params.temperature !== '' ? params.temperature : undefined,
|
||||
frequency_penalty:
|
||||
params.frequency_penalty !== '' ? params.frequency_penalty : undefined,
|
||||
repeat_last_n: params.repeat_last_n !== '' ? params.repeat_last_n : undefined,
|
||||
mirostat: params.mirostat !== '' ? params.mirostat : undefined,
|
||||
mirostat_eta: params.mirostat_eta !== '' ? params.mirostat_eta : undefined,
|
||||
mirostat_tau: params.mirostat_tau !== '' ? params.mirostat_tau : undefined,
|
||||
top_k: params.top_k !== '' ? params.top_k : undefined,
|
||||
top_p: params.top_p !== '' ? params.top_p : undefined,
|
||||
tfs_z: params.tfs_z !== '' ? params.tfs_z : undefined,
|
||||
num_ctx: params.num_ctx !== '' ? params.num_ctx : undefined,
|
||||
max_tokens: params.max_tokens !== '' ? params.max_tokens : undefined
|
||||
},
|
||||
keepAlive: keepAlive ? (isNaN(keepAlive) ? keepAlive : parseInt(keepAlive)) : undefined
|
||||
});
|
||||
|
@ -1,5 +1,4 @@
|
||||
<script lang="ts">
|
||||
import queue from 'async/queue';
|
||||
import { toast } from 'svelte-sonner';
|
||||
|
||||
import {
|
||||
@ -12,32 +11,19 @@
|
||||
cancelOllamaRequest,
|
||||
uploadModel
|
||||
} from '$lib/apis/ollama';
|
||||
|
||||
import { WEBUI_API_BASE_URL, WEBUI_BASE_URL } from '$lib/constants';
|
||||
import { WEBUI_NAME, models, MODEL_DOWNLOAD_POOL, user } from '$lib/stores';
|
||||
import { WEBUI_NAME, models, MODEL_DOWNLOAD_POOL, user, config } from '$lib/stores';
|
||||
import { splitStream } from '$lib/utils';
|
||||
import { onMount, getContext } from 'svelte';
|
||||
import { addLiteLLMModel, deleteLiteLLMModel, getLiteLLMModelInfo } from '$lib/apis/litellm';
|
||||
|
||||
import Tooltip from '$lib/components/common/Tooltip.svelte';
|
||||
|
||||
const i18n = getContext('i18n');
|
||||
|
||||
export let getModels: Function;
|
||||
|
||||
let showLiteLLM = false;
|
||||
let showLiteLLMParams = false;
|
||||
let modelUploadInputElement: HTMLInputElement;
|
||||
let liteLLMModelInfo = [];
|
||||
|
||||
let liteLLMModel = '';
|
||||
let liteLLMModelName = '';
|
||||
let liteLLMAPIBase = '';
|
||||
let liteLLMAPIKey = '';
|
||||
let liteLLMRPM = '';
|
||||
let liteLLMMaxTokens = '';
|
||||
|
||||
let deleteLiteLLMModelName = '';
|
||||
|
||||
$: liteLLMModelName = liteLLMModel;
|
||||
|
||||
// Models
|
||||
|
||||
@ -439,71 +425,22 @@
|
||||
}
|
||||
};
|
||||
|
||||
const addLiteLLMModelHandler = async () => {
|
||||
if (!liteLLMModelInfo.find((info) => info.model_name === liteLLMModelName)) {
|
||||
const res = await addLiteLLMModel(localStorage.token, {
|
||||
name: liteLLMModelName,
|
||||
model: liteLLMModel,
|
||||
api_base: liteLLMAPIBase,
|
||||
api_key: liteLLMAPIKey,
|
||||
rpm: liteLLMRPM,
|
||||
max_tokens: liteLLMMaxTokens
|
||||
}).catch((error) => {
|
||||
toast.error(error);
|
||||
return null;
|
||||
});
|
||||
|
||||
if (res) {
|
||||
if (res.message) {
|
||||
toast.success(res.message);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
toast.error($i18n.t(`Model {{modelName}} already exists.`, { modelName: liteLLMModelName }));
|
||||
}
|
||||
|
||||
liteLLMModelName = '';
|
||||
liteLLMModel = '';
|
||||
liteLLMAPIBase = '';
|
||||
liteLLMAPIKey = '';
|
||||
liteLLMRPM = '';
|
||||
liteLLMMaxTokens = '';
|
||||
|
||||
liteLLMModelInfo = await getLiteLLMModelInfo(localStorage.token);
|
||||
models.set(await getModels());
|
||||
};
|
||||
|
||||
const deleteLiteLLMModelHandler = async () => {
|
||||
const res = await deleteLiteLLMModel(localStorage.token, deleteLiteLLMModelName).catch(
|
||||
(error) => {
|
||||
toast.error(error);
|
||||
return null;
|
||||
}
|
||||
);
|
||||
|
||||
if (res) {
|
||||
if (res.message) {
|
||||
toast.success(res.message);
|
||||
}
|
||||
}
|
||||
|
||||
deleteLiteLLMModelName = '';
|
||||
liteLLMModelInfo = await getLiteLLMModelInfo(localStorage.token);
|
||||
models.set(await getModels());
|
||||
};
|
||||
|
||||
onMount(async () => {
|
||||
OLLAMA_URLS = await getOllamaUrls(localStorage.token).catch((error) => {
|
||||
toast.error(error);
|
||||
return [];
|
||||
});
|
||||
await Promise.all([
|
||||
(async () => {
|
||||
OLLAMA_URLS = await getOllamaUrls(localStorage.token).catch((error) => {
|
||||
toast.error(error);
|
||||
return [];
|
||||
});
|
||||
|
||||
if (OLLAMA_URLS.length > 0) {
|
||||
selectedOllamaUrlIdx = 0;
|
||||
}
|
||||
|
||||
ollamaVersion = await getOllamaVersion(localStorage.token).catch((error) => false);
|
||||
liteLLMModelInfo = await getLiteLLMModelInfo(localStorage.token);
|
||||
if (OLLAMA_URLS.length > 0) {
|
||||
selectedOllamaUrlIdx = 0;
|
||||
}
|
||||
})(),
|
||||
(async () => {
|
||||
ollamaVersion = await getOllamaVersion(localStorage.token).catch((error) => false);
|
||||
})()
|
||||
]);
|
||||
});
|
||||
</script>
|
||||
|
||||
@ -587,24 +524,28 @@
|
||||
viewBox="0 0 24 24"
|
||||
fill="currentColor"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
><style>
|
||||
>
|
||||
<style>
|
||||
.spinner_ajPY {
|
||||
transform-origin: center;
|
||||
animation: spinner_AtaB 0.75s infinite linear;
|
||||
}
|
||||
|
||||
@keyframes spinner_AtaB {
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
</style><path
|
||||
</style>
|
||||
<path
|
||||
d="M12,1A11,11,0,1,0,23,12,11,11,0,0,0,12,1Zm0,19a8,8,0,1,1,8-8A8,8,0,0,1,12,20Z"
|
||||
opacity=".25"
|
||||
/><path
|
||||
/>
|
||||
<path
|
||||
d="M10.14,1.16a11,11,0,0,0-9,8.92A1.59,1.59,0,0,0,2.46,12,1.52,1.52,0,0,0,4.11,10.7a8,8,0,0,1,6.66-6.61A1.42,1.42,0,0,0,12,2.69h0A1.57,1.57,0,0,0,10.14,1.16Z"
|
||||
class="spinner_ajPY"
|
||||
/></svg
|
||||
>
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
{:else}
|
||||
<svg
|
||||
@ -833,24 +774,28 @@
|
||||
viewBox="0 0 24 24"
|
||||
fill="currentColor"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
><style>
|
||||
>
|
||||
<style>
|
||||
.spinner_ajPY {
|
||||
transform-origin: center;
|
||||
animation: spinner_AtaB 0.75s infinite linear;
|
||||
}
|
||||
|
||||
@keyframes spinner_AtaB {
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
</style><path
|
||||
</style>
|
||||
<path
|
||||
d="M12,1A11,11,0,1,0,23,12,11,11,0,0,0,12,1Zm0,19a8,8,0,1,1,8-8A8,8,0,0,1,12,20Z"
|
||||
opacity=".25"
|
||||
/><path
|
||||
/>
|
||||
<path
|
||||
d="M10.14,1.16a11,11,0,0,0-9,8.92A1.59,1.59,0,0,0,2.46,12,1.52,1.52,0,0,0,4.11,10.7a8,8,0,0,1,6.66-6.61A1.42,1.42,0,0,0,12,2.69h0A1.57,1.57,0,0,0,10.14,1.16Z"
|
||||
class="spinner_ajPY"
|
||||
/></svg
|
||||
>
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
{:else}
|
||||
<svg
|
||||
@ -929,203 +874,8 @@
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
<hr class=" dark:border-gray-700 my-2" />
|
||||
{:else}
|
||||
<div>Ollama Not Detected</div>
|
||||
{/if}
|
||||
|
||||
<div class=" space-y-3">
|
||||
<div class="mt-2 space-y-3 pr-1.5">
|
||||
<div>
|
||||
<div class="mb-2">
|
||||
<div class="flex justify-between items-center text-xs">
|
||||
<div class=" text-sm font-medium">{$i18n.t('Manage LiteLLM Models')}</div>
|
||||
<button
|
||||
class=" text-xs font-medium text-gray-500"
|
||||
type="button"
|
||||
on:click={() => {
|
||||
showLiteLLM = !showLiteLLM;
|
||||
}}>{showLiteLLM ? $i18n.t('Hide') : $i18n.t('Show')}</button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{#if showLiteLLM}
|
||||
<div>
|
||||
<div class="flex justify-between items-center text-xs">
|
||||
<div class=" text-sm font-medium">{$i18n.t('Add a model')}</div>
|
||||
<button
|
||||
class=" text-xs font-medium text-gray-500"
|
||||
type="button"
|
||||
on:click={() => {
|
||||
showLiteLLMParams = !showLiteLLMParams;
|
||||
}}
|
||||
>{showLiteLLMParams
|
||||
? $i18n.t('Hide Additional Params')
|
||||
: $i18n.t('Show Additional Params')}</button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="my-2 space-y-2">
|
||||
<div class="flex w-full mb-1.5">
|
||||
<div class="flex-1 mr-2">
|
||||
<input
|
||||
class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
|
||||
placeholder={$i18n.t('Enter LiteLLM Model (litellm_params.model)')}
|
||||
bind:value={liteLLMModel}
|
||||
autocomplete="off"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<button
|
||||
class="px-2.5 bg-gray-100 hover:bg-gray-200 text-gray-800 dark:bg-gray-850 dark:hover:bg-gray-800 dark:text-gray-100 rounded-lg transition"
|
||||
on:click={() => {
|
||||
addLiteLLMModelHandler();
|
||||
}}
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 16 16"
|
||||
fill="currentColor"
|
||||
class="w-4 h-4"
|
||||
>
|
||||
<path
|
||||
d="M8.75 3.75a.75.75 0 0 0-1.5 0v3.5h-3.5a.75.75 0 0 0 0 1.5h3.5v3.5a.75.75 0 0 0 1.5 0v-3.5h3.5a.75.75 0 0 0 0-1.5h-3.5v-3.5Z"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{#if showLiteLLMParams}
|
||||
<div>
|
||||
<div class=" mb-1.5 text-sm font-medium">{$i18n.t('Model Name')}</div>
|
||||
<div class="flex w-full">
|
||||
<div class="flex-1">
|
||||
<input
|
||||
class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
|
||||
placeholder="Enter Model Name (model_name)"
|
||||
bind:value={liteLLMModelName}
|
||||
autocomplete="off"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class=" mb-1.5 text-sm font-medium">{$i18n.t('API Base URL')}</div>
|
||||
<div class="flex w-full">
|
||||
<div class="flex-1">
|
||||
<input
|
||||
class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
|
||||
placeholder={$i18n.t(
|
||||
'Enter LiteLLM API Base URL (litellm_params.api_base)'
|
||||
)}
|
||||
bind:value={liteLLMAPIBase}
|
||||
autocomplete="off"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class=" mb-1.5 text-sm font-medium">{$i18n.t('API Key')}</div>
|
||||
<div class="flex w-full">
|
||||
<div class="flex-1">
|
||||
<input
|
||||
class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
|
||||
placeholder={$i18n.t('Enter LiteLLM API Key (litellm_params.api_key)')}
|
||||
bind:value={liteLLMAPIKey}
|
||||
autocomplete="off"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="mb-1.5 text-sm font-medium">{$i18n.t('API RPM')}</div>
|
||||
<div class="flex w-full">
|
||||
<div class="flex-1">
|
||||
<input
|
||||
class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
|
||||
placeholder={$i18n.t('Enter LiteLLM API RPM (litellm_params.rpm)')}
|
||||
bind:value={liteLLMRPM}
|
||||
autocomplete="off"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="mb-1.5 text-sm font-medium">{$i18n.t('Max Tokens')}</div>
|
||||
<div class="flex w-full">
|
||||
<div class="flex-1">
|
||||
<input
|
||||
class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
|
||||
placeholder={$i18n.t('Enter Max Tokens (litellm_params.max_tokens)')}
|
||||
bind:value={liteLLMMaxTokens}
|
||||
type="number"
|
||||
min="1"
|
||||
autocomplete="off"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<div class="mb-2 text-xs text-gray-400 dark:text-gray-500">
|
||||
{$i18n.t('Not sure what to add?')}
|
||||
<a
|
||||
class=" text-gray-300 font-medium underline"
|
||||
href="https://litellm.vercel.app/docs/proxy/configs#quick-start"
|
||||
target="_blank"
|
||||
>
|
||||
{$i18n.t('Click here for help.')}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class=" mb-2.5 text-sm font-medium">{$i18n.t('Delete a model')}</div>
|
||||
<div class="flex w-full">
|
||||
<div class="flex-1 mr-2">
|
||||
<select
|
||||
class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
|
||||
bind:value={deleteLiteLLMModelName}
|
||||
placeholder={$i18n.t('Select a model')}
|
||||
>
|
||||
{#if !deleteLiteLLMModelName}
|
||||
<option value="" disabled selected>{$i18n.t('Select a model')}</option>
|
||||
{/if}
|
||||
{#each liteLLMModelInfo as model}
|
||||
<option value={model.model_name} class="bg-gray-100 dark:bg-gray-700"
|
||||
>{model.model_name}</option
|
||||
>
|
||||
{/each}
|
||||
</select>
|
||||
</div>
|
||||
<button
|
||||
class="px-2.5 bg-gray-100 hover:bg-gray-200 text-gray-800 dark:bg-gray-850 dark:hover:bg-gray-800 dark:text-gray-100 rounded-lg transition"
|
||||
on:click={() => {
|
||||
deleteLiteLLMModelHandler();
|
||||
}}
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 16 16"
|
||||
fill="currentColor"
|
||||
class="w-4 h-4"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
d="M5 3.25V4H2.75a.75.75 0 0 0 0 1.5h.3l.815 8.15A1.5 1.5 0 0 0 5.357 15h5.285a1.5 1.5 0 0 0 1.493-1.35l.815-8.15h.3a.75.75 0 0 0 0-1.5H11v-.75A2.25 2.25 0 0 0 8.75 1h-1.5A2.25 2.25 0 0 0 5 3.25Zm2.25-.75a.75.75 0 0 0-.75.75V4h3v-.75a.75.75 0 0 0-.75-.75h-1.5ZM6.05 6a.75.75 0 0 1 .787.713l.275 5.5a.75.75 0 0 1-1.498.075l-.275-5.5A.75.75 0 0 1 6.05 6Zm3.9 0a.75.75 0 0 1 .712.787l-.275 5.5a.75.75 0 0 1-1.498-.075l.275-5.5a.75.75 0 0 1 .786-.711Z"
|
||||
clip-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -3,7 +3,7 @@
|
||||
import { toast } from 'svelte-sonner';
|
||||
import { models, settings, user } from '$lib/stores';
|
||||
|
||||
import { getModels as _getModels } from '$lib/utils';
|
||||
import { getModels as _getModels } from '$lib/apis';
|
||||
|
||||
import Modal from '../common/Modal.svelte';
|
||||
import Account from './Settings/Account.svelte';
|
||||
|
@ -1,9 +1,9 @@
|
||||
<script lang="ts">
|
||||
import { getContext, onMount } from 'svelte';
|
||||
import { models } from '$lib/stores';
|
||||
|
||||
import { toast } from 'svelte-sonner';
|
||||
import { deleteSharedChatById, getChatById, shareChatById } from '$lib/apis/chats';
|
||||
import { modelfiles } from '$lib/stores';
|
||||
import { copyToClipboard } from '$lib/utils';
|
||||
|
||||
import Modal from '../common/Modal.svelte';
|
||||
@ -43,9 +43,7 @@
|
||||
tab.postMessage(
|
||||
JSON.stringify({
|
||||
chat: _chat,
|
||||
modelfiles: $modelfiles.filter((modelfile) =>
|
||||
_chat.models.includes(modelfile.tagName)
|
||||
)
|
||||
models: $models.filter((m) => _chat.models.includes(m.id))
|
||||
}),
|
||||
'*'
|
||||
);
|
||||
|
@ -29,6 +29,7 @@
|
||||
dispatch('change', _state);
|
||||
}
|
||||
}}
|
||||
type="button"
|
||||
>
|
||||
<div class="top-0 left-0 absolute w-full flex justify-center">
|
||||
{#if _state === 'checked'}
|
||||
|
@ -5,6 +5,7 @@
|
||||
export let placement = 'top';
|
||||
export let content = `I'm a tooltip!`;
|
||||
export let touch = true;
|
||||
export let className = 'flex';
|
||||
|
||||
let tooltipElement;
|
||||
let tooltipInstance;
|
||||
@ -29,6 +30,6 @@
|
||||
});
|
||||
</script>
|
||||
|
||||
<div bind:this={tooltipElement} aria-label={content} class="flex">
|
||||
<div bind:this={tooltipElement} aria-label={content} class={className}>
|
||||
<slot />
|
||||
</div>
|
||||
|
@ -6,7 +6,6 @@
|
||||
WEBUI_NAME,
|
||||
chatId,
|
||||
mobile,
|
||||
modelfiles,
|
||||
settings,
|
||||
showArchivedChats,
|
||||
showSettings,
|
||||
|
@ -5,67 +5,82 @@
|
||||
|
||||
import { onMount, getContext } from 'svelte';
|
||||
|
||||
import { WEBUI_NAME, modelfiles, settings, user } from '$lib/stores';
|
||||
import { createModel, deleteModel } from '$lib/apis/ollama';
|
||||
import {
|
||||
createNewModelfile,
|
||||
deleteModelfileByTagName,
|
||||
getModelfiles
|
||||
} from '$lib/apis/modelfiles';
|
||||
import { WEBUI_NAME, modelfiles, models, settings, user } from '$lib/stores';
|
||||
import { addNewModel, deleteModelById, getModelInfos } from '$lib/apis/models';
|
||||
|
||||
import { deleteModel } from '$lib/apis/ollama';
|
||||
import { goto } from '$app/navigation';
|
||||
|
||||
import { getModels } from '$lib/apis';
|
||||
|
||||
const i18n = getContext('i18n');
|
||||
|
||||
let localModelfiles = [];
|
||||
|
||||
let importFiles;
|
||||
let modelfilesImportInputElement: HTMLInputElement;
|
||||
const deleteModelHandler = async (tagName) => {
|
||||
let success = null;
|
||||
let modelsImportInputElement: HTMLInputElement;
|
||||
|
||||
success = await deleteModel(localStorage.token, tagName).catch((err) => {
|
||||
toast.error(err);
|
||||
const deleteModelHandler = async (model) => {
|
||||
console.log(model.info);
|
||||
if (!model?.info) {
|
||||
toast.error(
|
||||
$i18n.t('{{ owner }}: You cannot delete a base model', {
|
||||
owner: model.owned_by.toUpperCase()
|
||||
})
|
||||
);
|
||||
return null;
|
||||
});
|
||||
|
||||
if (success) {
|
||||
toast.success($i18n.t(`Deleted {{tagName}}`, { tagName }));
|
||||
}
|
||||
|
||||
return success;
|
||||
const res = await deleteModelById(localStorage.token, model.id);
|
||||
|
||||
if (res) {
|
||||
toast.success($i18n.t(`Deleted {{name}}`, { name: model.id }));
|
||||
}
|
||||
|
||||
await models.set(await getModels(localStorage.token));
|
||||
};
|
||||
|
||||
const deleteModelfile = async (tagName) => {
|
||||
await deleteModelHandler(tagName);
|
||||
await deleteModelfileByTagName(localStorage.token, tagName);
|
||||
await modelfiles.set(await getModelfiles(localStorage.token));
|
||||
const cloneModelHandler = async (model) => {
|
||||
if ((model?.info?.base_model_id ?? null) === null) {
|
||||
toast.error($i18n.t('You cannot clone a base model'));
|
||||
return;
|
||||
} else {
|
||||
sessionStorage.model = JSON.stringify({
|
||||
...model,
|
||||
id: `${model.id}-clone`,
|
||||
name: `${model.name} (Clone)`
|
||||
});
|
||||
goto('/workspace/models/create');
|
||||
}
|
||||
};
|
||||
|
||||
const shareModelfile = async (modelfile) => {
|
||||
const shareModelHandler = async (model) => {
|
||||
toast.success($i18n.t('Redirecting you to OpenWebUI Community'));
|
||||
|
||||
const url = 'https://openwebui.com';
|
||||
|
||||
const tab = await window.open(`${url}/modelfiles/create`, '_blank');
|
||||
const tab = await window.open(`${url}/models/create`, '_blank');
|
||||
window.addEventListener(
|
||||
'message',
|
||||
(event) => {
|
||||
if (event.origin !== url) return;
|
||||
if (event.data === 'loaded') {
|
||||
tab.postMessage(JSON.stringify(modelfile), '*');
|
||||
tab.postMessage(JSON.stringify(model), '*');
|
||||
}
|
||||
},
|
||||
false
|
||||
);
|
||||
};
|
||||
|
||||
const saveModelfiles = async (modelfiles) => {
|
||||
let blob = new Blob([JSON.stringify(modelfiles)], {
|
||||
const downloadModels = async (models) => {
|
||||
let blob = new Blob([JSON.stringify(models)], {
|
||||
type: 'application/json'
|
||||
});
|
||||
saveAs(blob, `modelfiles-export-${Date.now()}.json`);
|
||||
saveAs(blob, `models-export-${Date.now()}.json`);
|
||||
};
|
||||
|
||||
onMount(() => {
|
||||
// Legacy code to sync localModelfiles with models
|
||||
localModelfiles = JSON.parse(localStorage.getItem('modelfiles') ?? '[]');
|
||||
|
||||
if (localModelfiles) {
|
||||
@ -76,13 +91,13 @@
|
||||
|
||||
<svelte:head>
|
||||
<title>
|
||||
{$i18n.t('Modelfiles')} | {$WEBUI_NAME}
|
||||
{$i18n.t('Models')} | {$WEBUI_NAME}
|
||||
</title>
|
||||
</svelte:head>
|
||||
|
||||
<div class=" text-lg font-semibold mb-3">{$i18n.t('Modelfiles')}</div>
|
||||
<div class=" text-lg font-semibold mb-3">{$i18n.t('Models')}</div>
|
||||
|
||||
<a class=" flex space-x-4 cursor-pointer w-full mb-2 px-3 py-2" href="/workspace/modelfiles/create">
|
||||
<a class=" flex space-x-4 cursor-pointer w-full mb-2 px-3 py-2" href="/workspace/models/create">
|
||||
<div class=" self-center w-10">
|
||||
<div
|
||||
class="w-full h-10 flex justify-center rounded-full bg-transparent dark:bg-gray-700 border border-dashed border-gray-200"
|
||||
@ -98,26 +113,26 @@
|
||||
</div>
|
||||
|
||||
<div class=" self-center">
|
||||
<div class=" font-bold">{$i18n.t('Create a modelfile')}</div>
|
||||
<div class=" text-sm">{$i18n.t('Customize Ollama models for a specific purpose')}</div>
|
||||
<div class=" font-bold">{$i18n.t('Create a model')}</div>
|
||||
<div class=" text-sm">{$i18n.t('Customize models for a specific purpose')}</div>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
<hr class=" dark:border-gray-850" />
|
||||
|
||||
<div class=" my-2 mb-5">
|
||||
{#each $modelfiles as modelfile}
|
||||
{#each $models as model}
|
||||
<div
|
||||
class=" flex space-x-4 cursor-pointer w-full px-3 py-2 dark:hover:bg-white/5 hover:bg-black/5 rounded-xl"
|
||||
>
|
||||
<a
|
||||
class=" flex flex-1 space-x-4 cursor-pointer w-full"
|
||||
href={`/?models=${encodeURIComponent(modelfile.tagName)}`}
|
||||
href={`/?models=${encodeURIComponent(model.id)}`}
|
||||
>
|
||||
<div class=" self-center w-10">
|
||||
<div class=" rounded-full bg-stone-700">
|
||||
<img
|
||||
src={modelfile.imageUrl ?? '/user.png'}
|
||||
src={model?.info?.meta?.profile_image_url ?? '/favicon.png'}
|
||||
alt="modelfile profile"
|
||||
class=" rounded-full w-full h-auto object-cover"
|
||||
/>
|
||||
@ -125,9 +140,9 @@
|
||||
</div>
|
||||
|
||||
<div class=" flex-1 self-center">
|
||||
<div class=" font-bold capitalize">{modelfile.title}</div>
|
||||
<div class=" font-bold line-clamp-1">{model.name}</div>
|
||||
<div class=" text-sm overflow-hidden text-ellipsis line-clamp-1">
|
||||
{modelfile.desc}
|
||||
{!!model?.info?.meta?.description ? model?.info?.meta?.description : model.id}
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
@ -135,7 +150,7 @@
|
||||
<a
|
||||
class="self-center w-fit text-sm px-2 py-2 dark:text-gray-300 dark:hover:text-white hover:bg-black/5 dark:hover:bg-white/5 rounded-xl"
|
||||
type="button"
|
||||
href={`/workspace/modelfiles/edit?tag=${encodeURIComponent(modelfile.tagName)}`}
|
||||
href={`/workspace/models/edit?id=${encodeURIComponent(model.id)}`}
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
@ -157,9 +172,7 @@
|
||||
class="self-center w-fit text-sm px-2 py-2 dark:text-gray-300 dark:hover:text-white hover:bg-black/5 dark:hover:bg-white/5 rounded-xl"
|
||||
type="button"
|
||||
on:click={() => {
|
||||
// console.log(modelfile);
|
||||
sessionStorage.modelfile = JSON.stringify(modelfile);
|
||||
goto('/workspace/modelfiles/create');
|
||||
cloneModelHandler(model);
|
||||
}}
|
||||
>
|
||||
<svg
|
||||
@ -182,7 +195,7 @@
|
||||
class="self-center w-fit text-sm px-2 py-2 dark:text-gray-300 dark:hover:text-white hover:bg-black/5 dark:hover:bg-white/5 rounded-xl"
|
||||
type="button"
|
||||
on:click={() => {
|
||||
shareModelfile(modelfile);
|
||||
shareModelHandler(model);
|
||||
}}
|
||||
>
|
||||
<svg
|
||||
@ -205,7 +218,7 @@
|
||||
class="self-center w-fit text-sm px-2 py-2 dark:text-gray-300 dark:hover:text-white hover:bg-black/5 dark:hover:bg-white/5 rounded-xl"
|
||||
type="button"
|
||||
on:click={() => {
|
||||
deleteModelfile(modelfile.tagName);
|
||||
deleteModelHandler(model);
|
||||
}}
|
||||
>
|
||||
<svg
|
||||
@ -231,8 +244,8 @@
|
||||
<div class=" flex justify-end w-full mb-3">
|
||||
<div class="flex space-x-1">
|
||||
<input
|
||||
id="modelfiles-import-input"
|
||||
bind:this={modelfilesImportInputElement}
|
||||
id="models-import-input"
|
||||
bind:this={modelsImportInputElement}
|
||||
bind:files={importFiles}
|
||||
type="file"
|
||||
accept=".json"
|
||||
@ -242,16 +255,18 @@
|
||||
|
||||
let reader = new FileReader();
|
||||
reader.onload = async (event) => {
|
||||
let savedModelfiles = JSON.parse(event.target.result);
|
||||
console.log(savedModelfiles);
|
||||
let savedModels = JSON.parse(event.target.result);
|
||||
console.log(savedModels);
|
||||
|
||||
for (const modelfile of savedModelfiles) {
|
||||
await createNewModelfile(localStorage.token, modelfile).catch((error) => {
|
||||
return null;
|
||||
});
|
||||
for (const model of savedModels) {
|
||||
if (model?.info ?? false) {
|
||||
await addNewModel(localStorage.token, model.info).catch((error) => {
|
||||
return null;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
await modelfiles.set(await getModelfiles(localStorage.token));
|
||||
await models.set(await getModels(localStorage.token));
|
||||
};
|
||||
|
||||
reader.readAsText(importFiles[0]);
|
||||
@ -261,10 +276,10 @@
|
||||
<button
|
||||
class="flex text-xs items-center space-x-1 px-3 py-1.5 rounded-xl bg-gray-50 hover:bg-gray-100 dark:bg-gray-800 dark:hover:bg-gray-700 dark:text-gray-200 transition"
|
||||
on:click={() => {
|
||||
modelfilesImportInputElement.click();
|
||||
modelsImportInputElement.click();
|
||||
}}
|
||||
>
|
||||
<div class=" self-center mr-2 font-medium">{$i18n.t('Import Modelfiles')}</div>
|
||||
<div class=" self-center mr-2 font-medium">{$i18n.t('Import Models')}</div>
|
||||
|
||||
<div class=" self-center">
|
||||
<svg
|
||||
@ -285,10 +300,10 @@
|
||||
<button
|
||||
class="flex text-xs items-center space-x-1 px-3 py-1.5 rounded-xl bg-gray-50 hover:bg-gray-100 dark:bg-gray-800 dark:hover:bg-gray-700 dark:text-gray-200 transition"
|
||||
on:click={async () => {
|
||||
saveModelfiles($modelfiles);
|
||||
downloadModels($models);
|
||||
}}
|
||||
>
|
||||
<div class=" self-center mr-2 font-medium">{$i18n.t('Export Modelfiles')}</div>
|
||||
<div class=" self-center mr-2 font-medium">{$i18n.t('Export Models')}</div>
|
||||
|
||||
<div class=" self-center">
|
||||
<svg
|
||||
@ -314,47 +329,13 @@
|
||||
</div>
|
||||
|
||||
<div class="flex space-x-1">
|
||||
<button
|
||||
class="self-center w-fit text-sm px-3 py-1 border dark:border-gray-600 rounded-xl flex"
|
||||
on:click={async () => {
|
||||
for (const modelfile of localModelfiles) {
|
||||
await createNewModelfile(localStorage.token, modelfile).catch((error) => {
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
saveModelfiles(localModelfiles);
|
||||
localStorage.removeItem('modelfiles');
|
||||
localModelfiles = JSON.parse(localStorage.getItem('modelfiles') ?? '[]');
|
||||
await modelfiles.set(await getModelfiles(localStorage.token));
|
||||
}}
|
||||
>
|
||||
<div class=" self-center mr-2 font-medium">{$i18n.t('Sync All')}</div>
|
||||
|
||||
<div class=" self-center">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 16 16"
|
||||
fill="currentColor"
|
||||
class="w-3.5 h-3.5"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
d="M13.836 2.477a.75.75 0 0 1 .75.75v3.182a.75.75 0 0 1-.75.75h-3.182a.75.75 0 0 1 0-1.5h1.37l-.84-.841a4.5 4.5 0 0 0-7.08.932.75.75 0 0 1-1.3-.75 6 6 0 0 1 9.44-1.242l.842.84V3.227a.75.75 0 0 1 .75-.75Zm-.911 7.5A.75.75 0 0 1 13.199 11a6 6 0 0 1-9.44 1.241l-.84-.84v1.371a.75.75 0 0 1-1.5 0V9.591a.75.75 0 0 1 .75-.75H5.35a.75.75 0 0 1 0 1.5H3.98l.841.841a4.5 4.5 0 0 0 7.08-.932.75.75 0 0 1 1.025-.273Z"
|
||||
clip-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</button>
|
||||
|
||||
<button
|
||||
class="self-center w-fit text-sm p-1.5 border dark:border-gray-600 rounded-xl flex"
|
||||
on:click={async () => {
|
||||
saveModelfiles(localModelfiles);
|
||||
downloadModels(localModelfiles);
|
||||
|
||||
localStorage.removeItem('modelfiles');
|
||||
localModelfiles = JSON.parse(localStorage.getItem('modelfiles') ?? '[]');
|
||||
await modelfiles.set(await getModelfiles(localStorage.token));
|
||||
}}
|
||||
>
|
||||
<div class=" self-center">
|
||||
@ -402,7 +383,7 @@
|
||||
</div>
|
||||
|
||||
<div class=" self-center">
|
||||
<div class=" font-bold">{$i18n.t('Discover a modelfile')}</div>
|
||||
<div class=" font-bold">{$i18n.t('Discover a model')}</div>
|
||||
<div class=" text-sm">{$i18n.t('Discover, download, and explore model presets')}</div>
|
||||
</div>
|
||||
</a>
|
@ -321,13 +321,11 @@
|
||||
<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
|
||||
}))}
|
||||
items={$models.map((model) => ({
|
||||
value: model.id,
|
||||
label: model.name,
|
||||
model: model
|
||||
}))}
|
||||
bind:value={selectedModelId}
|
||||
/>
|
||||
</div>
|
||||
|
@ -8,7 +8,7 @@ export const WEBUI_API_BASE_URL = `${WEBUI_BASE_URL}/api/v1`;
|
||||
|
||||
export const LITELLM_API_BASE_URL = `${WEBUI_BASE_URL}/litellm/api`;
|
||||
export const OLLAMA_API_BASE_URL = `${WEBUI_BASE_URL}/ollama`;
|
||||
export const OPENAI_API_BASE_URL = `${WEBUI_BASE_URL}/openai/api`;
|
||||
export const OPENAI_API_BASE_URL = `${WEBUI_BASE_URL}/openai`;
|
||||
export const AUDIO_API_BASE_URL = `${WEBUI_BASE_URL}/audio/api/v1`;
|
||||
export const IMAGES_API_BASE_URL = `${WEBUI_BASE_URL}/images/api/v1`;
|
||||
export const RAG_API_BASE_URL = `${WEBUI_BASE_URL}/rag/api/v1`;
|
||||
|
@ -6,6 +6,7 @@
|
||||
"{{modelName}} is thinking...": "{{modelName}} ...يفكر",
|
||||
"{{user}}'s Chats": "دردشات {{user}}",
|
||||
"{{webUIName}} Backend Required": "{{webUIName}} مطلوب",
|
||||
"A selected model does not support image input": "",
|
||||
"a user": "مستخدم",
|
||||
"About": "عن",
|
||||
"Account": "الحساب",
|
||||
@ -31,6 +32,7 @@
|
||||
"Advanced Parameters": "التعليمات المتقدمة",
|
||||
"all": "الكل",
|
||||
"All Documents": "جميع الملفات",
|
||||
"All selected models do not support image input, removed images": "",
|
||||
"All Users": "جميع المستخدمين",
|
||||
"Allow": "يسمح",
|
||||
"Allow Chat Deletion": "يستطيع حذف المحادثات",
|
||||
@ -115,6 +117,7 @@
|
||||
"Created at": "أنشئت في",
|
||||
"Created At": "أنشئت من",
|
||||
"Current Model": "الموديل المختار",
|
||||
"Current Models": "",
|
||||
"Current Password": "كلمة السر الحالية",
|
||||
"Custom": "مخصص",
|
||||
"Customize Ollama models for a specific purpose": "تخصيص الموديل Ollama لغرض محدد",
|
||||
@ -181,6 +184,7 @@
|
||||
"Enter LiteLLM API RPM (litellm_params.rpm)": "أدخل LiteLLM API RPM (litllm_params.rpm)",
|
||||
"Enter LiteLLM Model (litellm_params.model)": "أدخل LiteLLM الموديل (litellm_params.model)",
|
||||
"Enter Max Tokens (litellm_params.max_tokens)": "أدخل أكثر Tokens (litellm_params.max_tokens)",
|
||||
"Enter Model Display Name": "",
|
||||
"Enter model tag (e.g. {{modelTag}})": "(e.g. {{modelTag}}) أدخل الموديل تاق",
|
||||
"Enter Number of Steps (e.g. 50)": "(e.g. 50) أدخل عدد الخطوات",
|
||||
"Enter Score": "أدخل النتيجة",
|
||||
@ -235,6 +239,7 @@
|
||||
"Input commands": "إدخال الأوامر",
|
||||
"Interface": "واجهه المستخدم",
|
||||
"Invalid Tag": "تاق غير صالحة",
|
||||
"Is Model Vision Capable": "",
|
||||
"January": "يناير",
|
||||
"join our Discord for help.": "انضم إلى Discord للحصول على المساعدة.",
|
||||
"JSON": "JSON",
|
||||
@ -253,6 +258,7 @@
|
||||
"Made by OpenWebUI Community": "OpenWebUI تم إنشاؤه بواسطة مجتمع ",
|
||||
"Make sure to enclose them with": "تأكد من إرفاقها",
|
||||
"Manage LiteLLM Models": "LiteLLM إدارة نماذج ",
|
||||
"Manage Model Information": "",
|
||||
"Manage Models": "إدارة النماذج",
|
||||
"Manage Ollama Models": "Ollama إدارة موديلات ",
|
||||
"March": "مارس",
|
||||
@ -272,7 +278,12 @@
|
||||
"Model '{{modelTag}}' is already in queue for downloading.": "النموذج '{{modelTag}}' موجود بالفعل في قائمة الانتظار للتحميل",
|
||||
"Model {{modelId}} not found": "لم يتم العثور على النموذج {{modelId}}.",
|
||||
"Model {{modelName}} already exists.": "موجود {{modelName}} موديل بالفعل",
|
||||
"Model {{modelName}} is not vision capable": "",
|
||||
"Model Description": "",
|
||||
"Model Display Name": "",
|
||||
"Model filesystem path detected. Model shortname is required for update, cannot continue.": "تم اكتشاف مسار نظام الملفات النموذجي. الاسم المختصر للنموذج مطلوب للتحديث، ولا يمكن الاستمرار.",
|
||||
"Model info for {{modelName}} added successfully": "",
|
||||
"Model info for {{modelName}} deleted successfully": "",
|
||||
"Model Name": "أسم الموديل",
|
||||
"Model not selected": "لم تختار موديل",
|
||||
"Model Tag Name": "أسم التاق للموديل",
|
||||
@ -289,6 +300,7 @@
|
||||
"Name your modelfile": "قم بتسمية ملف النموذج الخاص بك",
|
||||
"New Chat": "دردشة جديدة",
|
||||
"New Password": "كلمة المرور الجديدة",
|
||||
"No": "",
|
||||
"No results found": "لا توجد نتايج",
|
||||
"No source available": "لا يوجد مصدر متاح",
|
||||
"Not factually correct": "ليس صحيحا من حيث الواقع",
|
||||
@ -385,6 +397,7 @@
|
||||
"Select a model": "أختار الموديل",
|
||||
"Select an Ollama instance": "أختار سيرفر ",
|
||||
"Select model": " أختار موديل",
|
||||
"Selected models do not support image inputs": "",
|
||||
"Send": "تم",
|
||||
"Send a Message": "يُرجى إدخال طلبك هنا",
|
||||
"Send message": "يُرجى إدخال طلبك هنا.",
|
||||
@ -492,6 +505,7 @@
|
||||
"Workspace": "مساحة العمل",
|
||||
"Write a prompt suggestion (e.g. Who are you?)": "اكتب اقتراحًا سريعًا (على سبيل المثال، من أنت؟)",
|
||||
"Write a summary in 50 words that summarizes [topic or keyword].": "اكتب ملخصًا في 50 كلمة يلخص [الموضوع أو الكلمة الرئيسية]",
|
||||
"Yes": "",
|
||||
"Yesterday": "أمس",
|
||||
"You": "انت",
|
||||
"You have no archived conversations.": "لا تملك محادثات محفوظه",
|
||||
|
@ -6,6 +6,7 @@
|
||||
"{{modelName}} is thinking...": "{{modelName}} мисли ...",
|
||||
"{{user}}'s Chats": "{{user}}'s чатове",
|
||||
"{{webUIName}} Backend Required": "{{webUIName}} Изисква се Бекенд",
|
||||
"A selected model does not support image input": "",
|
||||
"a user": "потребител",
|
||||
"About": "Относно",
|
||||
"Account": "Акаунт",
|
||||
@ -31,6 +32,7 @@
|
||||
"Advanced Parameters": "Разширени Параметри",
|
||||
"all": "всички",
|
||||
"All Documents": "Всички Документи",
|
||||
"All selected models do not support image input, removed images": "",
|
||||
"All Users": "Всички Потребители",
|
||||
"Allow": "Позволи",
|
||||
"Allow Chat Deletion": "Позволи Изтриване на Чат",
|
||||
@ -115,6 +117,7 @@
|
||||
"Created at": "Създадено на",
|
||||
"Created At": "Създадено на",
|
||||
"Current Model": "Текущ модел",
|
||||
"Current Models": "",
|
||||
"Current Password": "Текуща Парола",
|
||||
"Custom": "Персонализиран",
|
||||
"Customize Ollama models for a specific purpose": "Персонализиране на Ollama моделите за конкретна цел",
|
||||
@ -181,6 +184,7 @@
|
||||
"Enter LiteLLM API RPM (litellm_params.rpm)": "Въведете LiteLLM API RPM (litellm_params.rpm)",
|
||||
"Enter LiteLLM Model (litellm_params.model)": "Въведете LiteLLM Model (litellm_params.model)",
|
||||
"Enter Max Tokens (litellm_params.max_tokens)": "Въведете Max Tokens (litellm_params.max_tokens)",
|
||||
"Enter Model Display Name": "",
|
||||
"Enter model tag (e.g. {{modelTag}})": "Въведете таг на модел (напр. {{modelTag}})",
|
||||
"Enter Number of Steps (e.g. 50)": "Въведете брой стъпки (напр. 50)",
|
||||
"Enter Score": "Въведете оценка",
|
||||
@ -235,6 +239,7 @@
|
||||
"Input commands": "Въведете команди",
|
||||
"Interface": "Интерфейс",
|
||||
"Invalid Tag": "Невалиден тег",
|
||||
"Is Model Vision Capable": "",
|
||||
"January": "Януари",
|
||||
"join our Discord for help.": "свържете се с нашия Discord за помощ.",
|
||||
"JSON": "JSON",
|
||||
@ -253,6 +258,7 @@
|
||||
"Made by OpenWebUI Community": "Направено от OpenWebUI общността",
|
||||
"Make sure to enclose them with": "Уверете се, че са заключени с",
|
||||
"Manage LiteLLM Models": "Управление на LiteLLM Моделите",
|
||||
"Manage Model Information": "",
|
||||
"Manage Models": "Управление на Моделите",
|
||||
"Manage Ollama Models": "Управление на Ollama Моделите",
|
||||
"March": "Март",
|
||||
@ -272,7 +278,12 @@
|
||||
"Model '{{modelTag}}' is already in queue for downloading.": "Моделът '{{modelTag}}' е вече в очакване за сваляне.",
|
||||
"Model {{modelId}} not found": "Моделът {{modelId}} не е намерен",
|
||||
"Model {{modelName}} already exists.": "Моделът {{modelName}} вече съществува.",
|
||||
"Model {{modelName}} is not vision capable": "",
|
||||
"Model Description": "",
|
||||
"Model Display Name": "",
|
||||
"Model filesystem path detected. Model shortname is required for update, cannot continue.": "Открит е път до файловата система на модела. За актуализацията се изисква съкратено име на модела, не може да продължи.",
|
||||
"Model info for {{modelName}} added successfully": "",
|
||||
"Model info for {{modelName}} deleted successfully": "",
|
||||
"Model Name": "Име на модел",
|
||||
"Model not selected": "Не е избран модел",
|
||||
"Model Tag Name": "Име на таг на модел",
|
||||
@ -289,6 +300,7 @@
|
||||
"Name your modelfile": "Име на модфайла",
|
||||
"New Chat": "Нов чат",
|
||||
"New Password": "Нова парола",
|
||||
"No": "",
|
||||
"No results found": "Няма намерени резултати",
|
||||
"No source available": "Няма наличен източник",
|
||||
"Not factually correct": "Не е фактологически правилно",
|
||||
@ -385,6 +397,7 @@
|
||||
"Select a model": "Изберете модел",
|
||||
"Select an Ollama instance": "Изберете Ollama инстанция",
|
||||
"Select model": "Изберете модел",
|
||||
"Selected models do not support image inputs": "",
|
||||
"Send": "Изпрати",
|
||||
"Send a Message": "Изпращане на Съобщение",
|
||||
"Send message": "Изпращане на съобщение",
|
||||
@ -492,6 +505,7 @@
|
||||
"Workspace": "Работно пространство",
|
||||
"Write a prompt suggestion (e.g. Who are you?)": "Напиши предложение за промпт (напр. Кой сте вие?)",
|
||||
"Write a summary in 50 words that summarizes [topic or keyword].": "Напиши описание в 50 знака, което описва [тема или ключова дума].",
|
||||
"Yes": "",
|
||||
"Yesterday": "вчера",
|
||||
"You": "вие",
|
||||
"You have no archived conversations.": "Нямате архивирани разговори.",
|
||||
|
@ -6,6 +6,7 @@
|
||||
"{{modelName}} is thinking...": "{{modelName}} চিন্তা করছে...",
|
||||
"{{user}}'s Chats": "{{user}}র চ্যাটস",
|
||||
"{{webUIName}} Backend Required": "{{webUIName}} ব্যাকএন্ড আবশ্যক",
|
||||
"A selected model does not support image input": "",
|
||||
"a user": "একজন ব্যাবহারকারী",
|
||||
"About": "সম্পর্কে",
|
||||
"Account": "একাউন্ট",
|
||||
@ -31,6 +32,7 @@
|
||||
"Advanced Parameters": "এডভান্সড প্যারামিটার্স",
|
||||
"all": "সব",
|
||||
"All Documents": "সব ডকুমেন্ট",
|
||||
"All selected models do not support image input, removed images": "",
|
||||
"All Users": "সব ইউজার",
|
||||
"Allow": "অনুমোদন",
|
||||
"Allow Chat Deletion": "চ্যাট ডিলিট করতে দিন",
|
||||
@ -115,6 +117,7 @@
|
||||
"Created at": "নির্মানকাল",
|
||||
"Created At": "নির্মানকাল",
|
||||
"Current Model": "বর্তমান মডেল",
|
||||
"Current Models": "",
|
||||
"Current Password": "বর্তমান পাসওয়ার্ড",
|
||||
"Custom": "কাস্টম",
|
||||
"Customize Ollama models for a specific purpose": "নির্দিষ্ট উদ্দেশ্যে Ollama মডেল পরিবর্তন করুন",
|
||||
@ -181,6 +184,7 @@
|
||||
"Enter LiteLLM API RPM (litellm_params.rpm)": "LiteLLM এপিআই 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 Display Name": "",
|
||||
"Enter model tag (e.g. {{modelTag}})": "মডেল ট্যাগ লিখুন (e.g. {{modelTag}})",
|
||||
"Enter Number of Steps (e.g. 50)": "ধাপের সংখ্যা দিন (যেমন: 50)",
|
||||
"Enter Score": "স্কোর দিন",
|
||||
@ -235,6 +239,7 @@
|
||||
"Input commands": "ইনপুট কমান্ডস",
|
||||
"Interface": "ইন্টারফেস",
|
||||
"Invalid Tag": "অবৈধ ট্যাগ",
|
||||
"Is Model Vision Capable": "",
|
||||
"January": "জানুয়ারী",
|
||||
"join our Discord for help.": "সাহায্যের জন্য আমাদের Discord-এ যুক্ত হোন",
|
||||
"JSON": "JSON",
|
||||
@ -253,6 +258,7 @@
|
||||
"Made by OpenWebUI Community": "OpenWebUI কমিউনিটিকর্তৃক নির্মিত",
|
||||
"Make sure to enclose them with": "এটা দিয়ে বন্ধনী দিতে ভুলবেন না",
|
||||
"Manage LiteLLM Models": "LiteLLM মডেল ব্যবস্থাপনা করুন",
|
||||
"Manage Model Information": "",
|
||||
"Manage Models": "মডেলসমূহ ব্যবস্থাপনা করুন",
|
||||
"Manage Ollama Models": "Ollama মডেলসূহ ব্যবস্থাপনা করুন",
|
||||
"March": "মার্চ",
|
||||
@ -272,7 +278,12 @@
|
||||
"Model '{{modelTag}}' is already in queue for downloading.": "{{modelTag}} ডাউনলোডের জন্য আগে থেকেই অপেক্ষমান আছে।",
|
||||
"Model {{modelId}} not found": "{{modelId}} মডেল পাওয়া যায়নি",
|
||||
"Model {{modelName}} already exists.": "{{modelName}} মডেল আগে থেকেই আছে",
|
||||
"Model {{modelName}} is not vision capable": "",
|
||||
"Model Description": "",
|
||||
"Model Display Name": "",
|
||||
"Model filesystem path detected. Model shortname is required for update, cannot continue.": "মডেল ফাইলসিস্টেম পাথ পাওয়া গেছে। আপডেটের জন্য মডেলের শর্টনেম আবশ্যক, এগিয়ে যাওয়া যাচ্ছে না।",
|
||||
"Model info for {{modelName}} added successfully": "",
|
||||
"Model info for {{modelName}} deleted successfully": "",
|
||||
"Model Name": "মডেলের নাম",
|
||||
"Model not selected": "মডেল নির্বাচন করা হয়নি",
|
||||
"Model Tag Name": "মডেলের ট্যাগ নাম",
|
||||
@ -289,6 +300,7 @@
|
||||
"Name your modelfile": "আপনার মডেলফাইলের নাম দিন",
|
||||
"New Chat": "নতুন চ্যাট",
|
||||
"New Password": "নতুন পাসওয়ার্ড",
|
||||
"No": "",
|
||||
"No results found": "কোন ফলাফল পাওয়া যায়নি",
|
||||
"No source available": "কোন উৎস পাওয়া যায়নি",
|
||||
"Not factually correct": "তথ্যগত দিক থেকে সঠিক নয়",
|
||||
@ -385,6 +397,7 @@
|
||||
"Select a model": "একটি মডেল নির্বাচন করুন",
|
||||
"Select an Ollama instance": "একটি Ollama ইন্সট্যান্স নির্বাচন করুন",
|
||||
"Select model": "মডেল নির্বাচন করুন",
|
||||
"Selected models do not support image inputs": "",
|
||||
"Send": "পাঠান",
|
||||
"Send a Message": "একটি মেসেজ পাঠান",
|
||||
"Send message": "মেসেজ পাঠান",
|
||||
@ -492,6 +505,7 @@
|
||||
"Workspace": "ওয়ার্কস্পেস",
|
||||
"Write a prompt suggestion (e.g. Who are you?)": "একটি প্রম্পট সাজেশন লিখুন (যেমন Who are you?)",
|
||||
"Write a summary in 50 words that summarizes [topic or keyword].": "৫০ শব্দের মধ্যে [topic or keyword] এর একটি সারসংক্ষেপ লিখুন।",
|
||||
"Yes": "",
|
||||
"Yesterday": "আগামী",
|
||||
"You": "আপনি",
|
||||
"You have no archived conversations.": "আপনার কোনও আর্কাইভ করা কথোপকথন নেই।",
|
||||
|
@ -6,6 +6,7 @@
|
||||
"{{modelName}} is thinking...": "{{modelName}} està pensant...",
|
||||
"{{user}}'s Chats": "{{user}}'s Chats",
|
||||
"{{webUIName}} Backend Required": "Es requereix Backend de {{webUIName}}",
|
||||
"A selected model does not support image input": "",
|
||||
"a user": "un usuari",
|
||||
"About": "Sobre",
|
||||
"Account": "Compte",
|
||||
@ -31,6 +32,7 @@
|
||||
"Advanced Parameters": "Paràmetres Avançats",
|
||||
"all": "tots",
|
||||
"All Documents": "Tots els Documents",
|
||||
"All selected models do not support image input, removed images": "",
|
||||
"All Users": "Tots els Usuaris",
|
||||
"Allow": "Permet",
|
||||
"Allow Chat Deletion": "Permet la Supressió del Xat",
|
||||
@ -115,6 +117,7 @@
|
||||
"Created at": "Creat el",
|
||||
"Created At": "Creat el",
|
||||
"Current Model": "Model Actual",
|
||||
"Current Models": "",
|
||||
"Current Password": "Contrasenya Actual",
|
||||
"Custom": "Personalitzat",
|
||||
"Customize Ollama models for a specific purpose": "Personalitza els models Ollama per a un propòsit específic",
|
||||
@ -181,6 +184,7 @@
|
||||
"Enter LiteLLM API RPM (litellm_params.rpm)": "Introdueix RPM de LiteLLM API (litellm_params.rpm)",
|
||||
"Enter LiteLLM Model (litellm_params.model)": "Introdueix el Model de LiteLLM (litellm_params.model)",
|
||||
"Enter Max Tokens (litellm_params.max_tokens)": "Introdueix el Màxim de Tokens (litellm_params.max_tokens)",
|
||||
"Enter Model Display Name": "",
|
||||
"Enter model tag (e.g. {{modelTag}})": "Introdueix l'etiqueta del model (p. ex. {{modelTag}})",
|
||||
"Enter Number of Steps (e.g. 50)": "Introdueix el Nombre de Passos (p. ex. 50)",
|
||||
"Enter Score": "Introdueix el Puntuació",
|
||||
@ -235,6 +239,7 @@
|
||||
"Input commands": "Entra ordres",
|
||||
"Interface": "Interfície",
|
||||
"Invalid Tag": "Etiqueta Inválida",
|
||||
"Is Model Vision Capable": "",
|
||||
"January": "Gener",
|
||||
"join our Discord for help.": "uneix-te al nostre Discord per ajuda.",
|
||||
"JSON": "JSON",
|
||||
@ -253,6 +258,7 @@
|
||||
"Made by OpenWebUI Community": "Creat per la Comunitat OpenWebUI",
|
||||
"Make sure to enclose them with": "Assegura't d'envoltar-los amb",
|
||||
"Manage LiteLLM Models": "Gestiona Models LiteLLM",
|
||||
"Manage Model Information": "",
|
||||
"Manage Models": "Gestiona Models",
|
||||
"Manage Ollama Models": "Gestiona Models Ollama",
|
||||
"March": "Març",
|
||||
@ -272,7 +278,12 @@
|
||||
"Model '{{modelTag}}' is already in queue for downloading.": "El model '{{modelTag}}' ja està en cua per ser descarregat.",
|
||||
"Model {{modelId}} not found": "Model {{modelId}} no trobat",
|
||||
"Model {{modelName}} already exists.": "El model {{modelName}} ja existeix.",
|
||||
"Model {{modelName}} is not vision capable": "",
|
||||
"Model Description": "",
|
||||
"Model Display Name": "",
|
||||
"Model filesystem path detected. Model shortname is required for update, cannot continue.": "S'ha detectat el camí del sistema de fitxers del model. És necessari un nom curt del model per a actualitzar, no es pot continuar.",
|
||||
"Model info for {{modelName}} added successfully": "",
|
||||
"Model info for {{modelName}} deleted successfully": "",
|
||||
"Model Name": "Nom del Model",
|
||||
"Model not selected": "Model no seleccionat",
|
||||
"Model Tag Name": "Nom de l'Etiqueta del Model",
|
||||
@ -289,6 +300,7 @@
|
||||
"Name your modelfile": "Nomena el teu fitxer de model",
|
||||
"New Chat": "Xat Nou",
|
||||
"New Password": "Nova Contrasenya",
|
||||
"No": "",
|
||||
"No results found": "No s'han trobat resultats",
|
||||
"No source available": "Sense font disponible",
|
||||
"Not factually correct": "No està clarament correcte",
|
||||
@ -385,6 +397,7 @@
|
||||
"Select a model": "Selecciona un model",
|
||||
"Select an Ollama instance": "Selecciona una instància d'Ollama",
|
||||
"Select model": "Selecciona un model",
|
||||
"Selected models do not support image inputs": "",
|
||||
"Send": "Envia",
|
||||
"Send a Message": "Envia un Missatge",
|
||||
"Send message": "Envia missatge",
|
||||
@ -492,6 +505,7 @@
|
||||
"Workspace": "Treball",
|
||||
"Write a prompt suggestion (e.g. Who are you?)": "Escriu una suggerència de prompt (p. ex. Qui ets tu?)",
|
||||
"Write a summary in 50 words that summarizes [topic or keyword].": "Escriu un resum en 50 paraules que resumeixi [tema o paraula clau].",
|
||||
"Yes": "",
|
||||
"Yesterday": "Ayer",
|
||||
"You": "Tu",
|
||||
"You have no archived conversations.": "No tens converses arxivades.",
|
||||
|
@ -6,6 +6,7 @@
|
||||
"{{modelName}} is thinking...": "{{modelName}} denkt nach...",
|
||||
"{{user}}'s Chats": "{{user}}s Chats",
|
||||
"{{webUIName}} Backend Required": "{{webUIName}}-Backend erforderlich",
|
||||
"A selected model does not support image input": "",
|
||||
"a user": "ein Benutzer",
|
||||
"About": "Über",
|
||||
"Account": "Account",
|
||||
@ -31,6 +32,7 @@
|
||||
"Advanced Parameters": "Erweiterte Parameter",
|
||||
"all": "Alle",
|
||||
"All Documents": "Alle Dokumente",
|
||||
"All selected models do not support image input, removed images": "",
|
||||
"All Users": "Alle Benutzer",
|
||||
"Allow": "Erlauben",
|
||||
"Allow Chat Deletion": "Chat Löschung erlauben",
|
||||
@ -115,6 +117,7 @@
|
||||
"Created at": "Erstellt am",
|
||||
"Created At": "Erstellt am",
|
||||
"Current Model": "Aktuelles Modell",
|
||||
"Current Models": "",
|
||||
"Current Password": "Aktuelles Passwort",
|
||||
"Custom": "Benutzerdefiniert",
|
||||
"Customize Ollama models for a specific purpose": "Ollama-Modelle für einen bestimmten Zweck anpassen",
|
||||
@ -181,6 +184,7 @@
|
||||
"Enter LiteLLM API RPM (litellm_params.rpm)": "Gib die LiteLLM API RPM ein (litellm_params.rpm)",
|
||||
"Enter LiteLLM Model (litellm_params.model)": "Gib das LiteLLM Model ein (litellm_params.model)",
|
||||
"Enter Max Tokens (litellm_params.max_tokens)": "Gib die maximalen Token ein (litellm_params.max_tokens) an",
|
||||
"Enter Model Display Name": "",
|
||||
"Enter model tag (e.g. {{modelTag}})": "Gib den Model-Tag ein",
|
||||
"Enter Number of Steps (e.g. 50)": "Gib die Anzahl an Schritten ein (z.B. 50)",
|
||||
"Enter Score": "Score eingeben",
|
||||
@ -235,6 +239,7 @@
|
||||
"Input commands": "Eingabebefehle",
|
||||
"Interface": "Benutzeroberfläche",
|
||||
"Invalid Tag": "Ungültiger Tag",
|
||||
"Is Model Vision Capable": "",
|
||||
"January": "Januar",
|
||||
"join our Discord for help.": "Trete unserem Discord bei, um Hilfe zu erhalten.",
|
||||
"JSON": "JSON",
|
||||
@ -253,6 +258,7 @@
|
||||
"Made by OpenWebUI Community": "Von der OpenWebUI-Community",
|
||||
"Make sure to enclose them with": "Formatiere deine Variablen mit:",
|
||||
"Manage LiteLLM Models": "LiteLLM-Modelle verwalten",
|
||||
"Manage Model Information": "",
|
||||
"Manage Models": "Modelle verwalten",
|
||||
"Manage Ollama Models": "Ollama-Modelle verwalten",
|
||||
"March": "März",
|
||||
@ -272,7 +278,12 @@
|
||||
"Model '{{modelTag}}' is already in queue for downloading.": "Modell '{{modelTag}}' befindet sich bereits in der Warteschlange zum Herunterladen.",
|
||||
"Model {{modelId}} not found": "Modell {{modelId}} nicht gefunden",
|
||||
"Model {{modelName}} already exists.": "Modell {{modelName}} existiert bereits.",
|
||||
"Model {{modelName}} is not vision capable": "",
|
||||
"Model Description": "",
|
||||
"Model Display Name": "",
|
||||
"Model filesystem path detected. Model shortname is required for update, cannot continue.": "Modell-Dateisystempfad erkannt. Modellkurzname ist für das Update erforderlich, Fortsetzung nicht möglich.",
|
||||
"Model info for {{modelName}} added successfully": "",
|
||||
"Model info for {{modelName}} deleted successfully": "",
|
||||
"Model Name": "Modellname",
|
||||
"Model not selected": "Modell nicht ausgewählt",
|
||||
"Model Tag Name": "Modell-Tag-Name",
|
||||
@ -289,6 +300,7 @@
|
||||
"Name your modelfile": "Benenne dein modelfile",
|
||||
"New Chat": "Neuer Chat",
|
||||
"New Password": "Neues Passwort",
|
||||
"No": "",
|
||||
"No results found": "Keine Ergebnisse gefunden",
|
||||
"No source available": "Keine Quelle verfügbar.",
|
||||
"Not factually correct": "Nicht sachlich korrekt.",
|
||||
@ -385,6 +397,7 @@
|
||||
"Select a model": "Ein Modell auswählen",
|
||||
"Select an Ollama instance": "Eine Ollama Instanz auswählen",
|
||||
"Select model": "Modell auswählen",
|
||||
"Selected models do not support image inputs": "",
|
||||
"Send": "Senden",
|
||||
"Send a Message": "Eine Nachricht senden",
|
||||
"Send message": "Nachricht senden",
|
||||
@ -492,6 +505,7 @@
|
||||
"Workspace": "Arbeitsbereich",
|
||||
"Write a prompt suggestion (e.g. Who are you?)": "Gebe einen Prompt-Vorschlag ein (z.B. Wer bist du?)",
|
||||
"Write a summary in 50 words that summarizes [topic or keyword].": "Schreibe eine kurze Zusammenfassung in 50 Wörtern, die [Thema oder Schlüsselwort] zusammenfasst.",
|
||||
"Yes": "",
|
||||
"Yesterday": "Gestern",
|
||||
"You": "Du",
|
||||
"You have no archived conversations.": "Du hast keine archivierten Unterhaltungen.",
|
||||
|
@ -6,6 +6,7 @@
|
||||
"{{modelName}} is thinking...": "{{modelName}} is thinkin'...",
|
||||
"{{user}}'s Chats": "",
|
||||
"{{webUIName}} Backend Required": "{{webUIName}} Backend Much Required",
|
||||
"A selected model does not support image input": "",
|
||||
"a user": "such user",
|
||||
"About": "Much About",
|
||||
"Account": "Account",
|
||||
@ -31,6 +32,7 @@
|
||||
"Advanced Parameters": "Advanced Parameters",
|
||||
"all": "all",
|
||||
"All Documents": "",
|
||||
"All selected models do not support image input, removed images": "",
|
||||
"All Users": "All Users",
|
||||
"Allow": "Allow",
|
||||
"Allow Chat Deletion": "Allow Delete Chats",
|
||||
@ -115,6 +117,7 @@
|
||||
"Created at": "Created at",
|
||||
"Created At": "",
|
||||
"Current Model": "Current Model",
|
||||
"Current Models": "",
|
||||
"Current Password": "Current Password",
|
||||
"Custom": "Custom",
|
||||
"Customize Ollama models for a specific purpose": "Customize Ollama models for purpose",
|
||||
@ -181,6 +184,7 @@
|
||||
"Enter LiteLLM API RPM (litellm_params.rpm)": "Enter RPM of LiteLLM API (litellm_params.rpm)",
|
||||
"Enter LiteLLM Model (litellm_params.model)": "Enter Model of LiteLLM (litellm_params.model)",
|
||||
"Enter Max Tokens (litellm_params.max_tokens)": "Enter Maximum Tokens (litellm_params.max_tokens)",
|
||||
"Enter Model Display Name": "",
|
||||
"Enter model tag (e.g. {{modelTag}})": "Enter model doge tag (e.g. {{modelTag}})",
|
||||
"Enter Number of Steps (e.g. 50)": "Enter Number of Steps (e.g. 50)",
|
||||
"Enter Score": "",
|
||||
@ -235,6 +239,7 @@
|
||||
"Input commands": "Input commands",
|
||||
"Interface": "Interface",
|
||||
"Invalid Tag": "",
|
||||
"Is Model Vision Capable": "",
|
||||
"January": "",
|
||||
"join our Discord for help.": "join our Discord for help.",
|
||||
"JSON": "JSON",
|
||||
@ -253,6 +258,7 @@
|
||||
"Made by OpenWebUI Community": "Made by OpenWebUI Community",
|
||||
"Make sure to enclose them with": "Make sure to enclose them with",
|
||||
"Manage LiteLLM Models": "Manage LiteLLM Models",
|
||||
"Manage Model Information": "",
|
||||
"Manage Models": "Manage Wowdels",
|
||||
"Manage Ollama Models": "Manage Ollama Wowdels",
|
||||
"March": "",
|
||||
@ -272,7 +278,12 @@
|
||||
"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 {{modelName}} already exists.": "Model {{modelName}} already exists.",
|
||||
"Model {{modelName}} is not vision capable": "",
|
||||
"Model Description": "",
|
||||
"Model Display Name": "",
|
||||
"Model filesystem path detected. Model shortname is required for update, cannot continue.": "Model filesystem bark detected. Model shortname is required for update, cannot continue.",
|
||||
"Model info for {{modelName}} added successfully": "",
|
||||
"Model info for {{modelName}} deleted successfully": "",
|
||||
"Model Name": "Wowdel Name",
|
||||
"Model not selected": "Model not selected",
|
||||
"Model Tag Name": "Wowdel Tag Name",
|
||||
@ -289,6 +300,7 @@
|
||||
"Name your modelfile": "Name your modelfile",
|
||||
"New Chat": "New Bark",
|
||||
"New Password": "New Barkword",
|
||||
"No": "",
|
||||
"No results found": "",
|
||||
"No source available": "No source available",
|
||||
"Not factually correct": "",
|
||||
@ -385,6 +397,7 @@
|
||||
"Select a model": "Select a model much choice",
|
||||
"Select an Ollama instance": "Select an Ollama instance very choose",
|
||||
"Select model": "Select model much choice",
|
||||
"Selected models do not support image inputs": "",
|
||||
"Send": "",
|
||||
"Send a Message": "Send a Message much message",
|
||||
"Send message": "Send message very send",
|
||||
@ -492,6 +505,7 @@
|
||||
"Workspace": "",
|
||||
"Write a prompt suggestion (e.g. Who are you?)": "Write a prompt suggestion (e.g. Who are you?) much suggest",
|
||||
"Write a summary in 50 words that summarizes [topic or keyword].": "Write a summary in 50 words that summarizes [topic or keyword]. Much summarize.",
|
||||
"Yes": "",
|
||||
"Yesterday": "",
|
||||
"You": "",
|
||||
"You have no archived conversations.": "",
|
||||
|
@ -6,6 +6,7 @@
|
||||
"{{modelName}} is thinking...": "",
|
||||
"{{user}}'s Chats": "",
|
||||
"{{webUIName}} Backend Required": "",
|
||||
"A selected model does not support image input": "",
|
||||
"a user": "",
|
||||
"About": "",
|
||||
"Account": "",
|
||||
@ -31,6 +32,7 @@
|
||||
"Advanced Parameters": "",
|
||||
"all": "",
|
||||
"All Documents": "",
|
||||
"All selected models do not support image input, removed images": "",
|
||||
"All Users": "",
|
||||
"Allow": "",
|
||||
"Allow Chat Deletion": "",
|
||||
@ -115,6 +117,7 @@
|
||||
"Created at": "",
|
||||
"Created At": "",
|
||||
"Current Model": "",
|
||||
"Current Models": "",
|
||||
"Current Password": "",
|
||||
"Custom": "",
|
||||
"Customize Ollama models for a specific purpose": "",
|
||||
@ -181,6 +184,7 @@
|
||||
"Enter LiteLLM API RPM (litellm_params.rpm)": "",
|
||||
"Enter LiteLLM Model (litellm_params.model)": "",
|
||||
"Enter Max Tokens (litellm_params.max_tokens)": "",
|
||||
"Enter Model Display Name": "",
|
||||
"Enter model tag (e.g. {{modelTag}})": "",
|
||||
"Enter Number of Steps (e.g. 50)": "",
|
||||
"Enter Score": "",
|
||||
@ -235,6 +239,7 @@
|
||||
"Input commands": "",
|
||||
"Interface": "",
|
||||
"Invalid Tag": "",
|
||||
"Is Model Vision Capable": "",
|
||||
"January": "",
|
||||
"join our Discord for help.": "",
|
||||
"JSON": "",
|
||||
@ -253,6 +258,7 @@
|
||||
"Made by OpenWebUI Community": "",
|
||||
"Make sure to enclose them with": "",
|
||||
"Manage LiteLLM Models": "",
|
||||
"Manage Model Information": "",
|
||||
"Manage Models": "",
|
||||
"Manage Ollama Models": "",
|
||||
"March": "",
|
||||
@ -272,7 +278,12 @@
|
||||
"Model '{{modelTag}}' is already in queue for downloading.": "",
|
||||
"Model {{modelId}} not found": "",
|
||||
"Model {{modelName}} already exists.": "",
|
||||
"Model {{modelName}} is not vision capable": "",
|
||||
"Model Description": "",
|
||||
"Model Display Name": "",
|
||||
"Model filesystem path detected. Model shortname is required for update, cannot continue.": "",
|
||||
"Model info for {{modelName}} added successfully": "",
|
||||
"Model info for {{modelName}} deleted successfully": "",
|
||||
"Model Name": "",
|
||||
"Model not selected": "",
|
||||
"Model Tag Name": "",
|
||||
@ -289,6 +300,7 @@
|
||||
"Name your modelfile": "",
|
||||
"New Chat": "",
|
||||
"New Password": "",
|
||||
"No": "",
|
||||
"No results found": "",
|
||||
"No source available": "",
|
||||
"Not factually correct": "",
|
||||
@ -385,6 +397,7 @@
|
||||
"Select a model": "",
|
||||
"Select an Ollama instance": "",
|
||||
"Select model": "",
|
||||
"Selected models do not support image inputs": "",
|
||||
"Send": "",
|
||||
"Send a Message": "",
|
||||
"Send message": "",
|
||||
@ -492,6 +505,7 @@
|
||||
"Workspace": "",
|
||||
"Write a prompt suggestion (e.g. Who are you?)": "",
|
||||
"Write a summary in 50 words that summarizes [topic or keyword].": "",
|
||||
"Yes": "",
|
||||
"Yesterday": "",
|
||||
"You": "",
|
||||
"You have no archived conversations.": "",
|
||||
|
@ -6,6 +6,7 @@
|
||||
"{{modelName}} is thinking...": "",
|
||||
"{{user}}'s Chats": "",
|
||||
"{{webUIName}} Backend Required": "",
|
||||
"A selected model does not support image input": "",
|
||||
"a user": "",
|
||||
"About": "",
|
||||
"Account": "",
|
||||
@ -31,6 +32,7 @@
|
||||
"Advanced Parameters": "",
|
||||
"all": "",
|
||||
"All Documents": "",
|
||||
"All selected models do not support image input, removed images": "",
|
||||
"All Users": "",
|
||||
"Allow": "",
|
||||
"Allow Chat Deletion": "",
|
||||
@ -115,6 +117,7 @@
|
||||
"Created at": "",
|
||||
"Created At": "",
|
||||
"Current Model": "",
|
||||
"Current Models": "",
|
||||
"Current Password": "",
|
||||
"Custom": "",
|
||||
"Customize Ollama models for a specific purpose": "",
|
||||
@ -181,6 +184,7 @@
|
||||
"Enter LiteLLM API RPM (litellm_params.rpm)": "",
|
||||
"Enter LiteLLM Model (litellm_params.model)": "",
|
||||
"Enter Max Tokens (litellm_params.max_tokens)": "",
|
||||
"Enter Model Display Name": "",
|
||||
"Enter model tag (e.g. {{modelTag}})": "",
|
||||
"Enter Number of Steps (e.g. 50)": "",
|
||||
"Enter Score": "",
|
||||
@ -235,6 +239,7 @@
|
||||
"Input commands": "",
|
||||
"Interface": "",
|
||||
"Invalid Tag": "",
|
||||
"Is Model Vision Capable": "",
|
||||
"January": "",
|
||||
"join our Discord for help.": "",
|
||||
"JSON": "",
|
||||
@ -253,6 +258,7 @@
|
||||
"Made by OpenWebUI Community": "",
|
||||
"Make sure to enclose them with": "",
|
||||
"Manage LiteLLM Models": "",
|
||||
"Manage Model Information": "",
|
||||
"Manage Models": "",
|
||||
"Manage Ollama Models": "",
|
||||
"March": "",
|
||||
@ -272,7 +278,12 @@
|
||||
"Model '{{modelTag}}' is already in queue for downloading.": "",
|
||||
"Model {{modelId}} not found": "",
|
||||
"Model {{modelName}} already exists.": "",
|
||||
"Model {{modelName}} is not vision capable": "",
|
||||
"Model Description": "",
|
||||
"Model Display Name": "",
|
||||
"Model filesystem path detected. Model shortname is required for update, cannot continue.": "",
|
||||
"Model info for {{modelName}} added successfully": "",
|
||||
"Model info for {{modelName}} deleted successfully": "",
|
||||
"Model Name": "",
|
||||
"Model not selected": "",
|
||||
"Model Tag Name": "",
|
||||
@ -289,6 +300,7 @@
|
||||
"Name your modelfile": "",
|
||||
"New Chat": "",
|
||||
"New Password": "",
|
||||
"No": "",
|
||||
"No results found": "",
|
||||
"No source available": "",
|
||||
"Not factually correct": "",
|
||||
@ -385,6 +397,7 @@
|
||||
"Select a model": "",
|
||||
"Select an Ollama instance": "",
|
||||
"Select model": "",
|
||||
"Selected models do not support image inputs": "",
|
||||
"Send": "",
|
||||
"Send a Message": "",
|
||||
"Send message": "",
|
||||
@ -492,6 +505,7 @@
|
||||
"Workspace": "",
|
||||
"Write a prompt suggestion (e.g. Who are you?)": "",
|
||||
"Write a summary in 50 words that summarizes [topic or keyword].": "",
|
||||
"Yes": "",
|
||||
"Yesterday": "",
|
||||
"You": "",
|
||||
"You have no archived conversations.": "",
|
||||
|
@ -6,6 +6,7 @@
|
||||
"{{modelName}} is thinking...": "{{modelName}} está pensando...",
|
||||
"{{user}}'s Chats": "{{user}}'s Chats",
|
||||
"{{webUIName}} Backend Required": "{{webUIName}} Servidor Requerido",
|
||||
"A selected model does not support image input": "",
|
||||
"a user": "un usuario",
|
||||
"About": "Sobre nosotros",
|
||||
"Account": "Cuenta",
|
||||
@ -31,6 +32,7 @@
|
||||
"Advanced Parameters": "Parámetros Avanzados",
|
||||
"all": "todo",
|
||||
"All Documents": "Todos los Documentos",
|
||||
"All selected models do not support image input, removed images": "",
|
||||
"All Users": "Todos los Usuarios",
|
||||
"Allow": "Permitir",
|
||||
"Allow Chat Deletion": "Permitir Borrar Chats",
|
||||
@ -115,6 +117,7 @@
|
||||
"Created at": "Creado en",
|
||||
"Created At": "Creado en",
|
||||
"Current Model": "Modelo Actual",
|
||||
"Current Models": "",
|
||||
"Current Password": "Contraseña Actual",
|
||||
"Custom": "Personalizado",
|
||||
"Customize Ollama models for a specific purpose": "Personaliza los modelos de Ollama para un propósito específico",
|
||||
@ -181,6 +184,7 @@
|
||||
"Enter LiteLLM API RPM (litellm_params.rpm)": "Ingrese el RPM de la API LiteLLM (litellm_params.rpm)",
|
||||
"Enter LiteLLM Model (litellm_params.model)": "Ingrese el modelo LiteLLM (litellm_params.model)",
|
||||
"Enter Max Tokens (litellm_params.max_tokens)": "Ingrese tokens máximos (litellm_params.max_tokens)",
|
||||
"Enter Model Display Name": "",
|
||||
"Enter model tag (e.g. {{modelTag}})": "Ingrese la etiqueta del modelo (p.ej. {{modelTag}})",
|
||||
"Enter Number of Steps (e.g. 50)": "Ingrese el número de pasos (p.ej., 50)",
|
||||
"Enter Score": "Ingrese la puntuación",
|
||||
@ -235,6 +239,7 @@
|
||||
"Input commands": "Ingresar comandos",
|
||||
"Interface": "Interfaz",
|
||||
"Invalid Tag": "Etiqueta Inválida",
|
||||
"Is Model Vision Capable": "",
|
||||
"January": "Enero",
|
||||
"join our Discord for help.": "Únase a nuestro Discord para obtener ayuda.",
|
||||
"JSON": "JSON",
|
||||
@ -253,6 +258,7 @@
|
||||
"Made by OpenWebUI Community": "Hecho por la comunidad de OpenWebUI",
|
||||
"Make sure to enclose them with": "Asegúrese de adjuntarlos con",
|
||||
"Manage LiteLLM Models": "Administrar Modelos LiteLLM",
|
||||
"Manage Model Information": "",
|
||||
"Manage Models": "Administrar Modelos",
|
||||
"Manage Ollama Models": "Administrar Modelos Ollama",
|
||||
"March": "Marzo",
|
||||
@ -272,7 +278,12 @@
|
||||
"Model '{{modelTag}}' is already in queue for downloading.": "El modelo '{{modelTag}}' ya está en cola para descargar.",
|
||||
"Model {{modelId}} not found": "El modelo {{modelId}} no fue encontrado",
|
||||
"Model {{modelName}} already exists.": "El modelo {{modelName}} ya existe.",
|
||||
"Model {{modelName}} is not vision capable": "",
|
||||
"Model Description": "",
|
||||
"Model Display Name": "",
|
||||
"Model filesystem path detected. Model shortname is required for update, cannot continue.": "Se detectó la ruta del sistema de archivos del modelo. Se requiere el nombre corto del modelo para la actualización, no se puede continuar.",
|
||||
"Model info for {{modelName}} added successfully": "",
|
||||
"Model info for {{modelName}} deleted successfully": "",
|
||||
"Model Name": "Nombre del modelo",
|
||||
"Model not selected": "Modelo no seleccionado",
|
||||
"Model Tag Name": "Nombre de la etiqueta del modelo",
|
||||
@ -289,6 +300,7 @@
|
||||
"Name your modelfile": "Nombra tu modelfile",
|
||||
"New Chat": "Nuevo Chat",
|
||||
"New Password": "Nueva Contraseña",
|
||||
"No": "",
|
||||
"No results found": "No se han encontrado resultados",
|
||||
"No source available": "No hay fuente disponible",
|
||||
"Not factually correct": "No es correcto en todos los aspectos",
|
||||
@ -385,6 +397,7 @@
|
||||
"Select a model": "Selecciona un modelo",
|
||||
"Select an Ollama instance": "Seleccione una instancia de Ollama",
|
||||
"Select model": "Selecciona un modelo",
|
||||
"Selected models do not support image inputs": "",
|
||||
"Send": "Enviar",
|
||||
"Send a Message": "Enviar un Mensaje",
|
||||
"Send message": "Enviar Mensaje",
|
||||
@ -492,6 +505,7 @@
|
||||
"Workspace": "Espacio de trabajo",
|
||||
"Write a prompt suggestion (e.g. Who are you?)": "Escribe una sugerencia para un prompt (por ejemplo, ¿quién eres?)",
|
||||
"Write a summary in 50 words that summarizes [topic or keyword].": "Escribe un resumen en 50 palabras que resuma [tema o palabra clave].",
|
||||
"Yes": "",
|
||||
"Yesterday": "Ayer",
|
||||
"You": "Usted",
|
||||
"You have no archived conversations.": "No tiene conversaciones archivadas.",
|
||||
|
@ -6,6 +6,7 @@
|
||||
"{{modelName}} is thinking...": "{{modelName}} در حال فکر کردن است...",
|
||||
"{{user}}'s Chats": "{{user}} چت ها",
|
||||
"{{webUIName}} Backend Required": "بکند {{webUIName}} نیاز است.",
|
||||
"A selected model does not support image input": "",
|
||||
"a user": "یک کاربر",
|
||||
"About": "درباره",
|
||||
"Account": "حساب کاربری",
|
||||
@ -31,6 +32,7 @@
|
||||
"Advanced Parameters": "پارامترهای پیشرفته",
|
||||
"all": "همه",
|
||||
"All Documents": "تمام سند ها",
|
||||
"All selected models do not support image input, removed images": "",
|
||||
"All Users": "همه کاربران",
|
||||
"Allow": "اجازه دادن",
|
||||
"Allow Chat Deletion": "اجازه حذف گپ",
|
||||
@ -115,6 +117,7 @@
|
||||
"Created at": "ایجاد شده در",
|
||||
"Created At": "ایجاد شده در",
|
||||
"Current Model": "مدل فعلی",
|
||||
"Current Models": "",
|
||||
"Current Password": "رمز عبور فعلی",
|
||||
"Custom": "دلخواه",
|
||||
"Customize Ollama models for a specific purpose": "مدل های اولاما را برای یک هدف خاص سفارشی کنید",
|
||||
@ -181,6 +184,7 @@
|
||||
"Enter LiteLLM API RPM (litellm_params.rpm)": "RPM API مربوط به LiteLLM را وارد کنید (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 Display Name": "",
|
||||
"Enter model tag (e.g. {{modelTag}})": "تگ مدل را وارد کنید (مثلا {{modelTag}})",
|
||||
"Enter Number of Steps (e.g. 50)": "تعداد گام ها را وارد کنید (مثال: 50)",
|
||||
"Enter Score": "امتیاز را وارد کنید",
|
||||
@ -235,6 +239,7 @@
|
||||
"Input commands": "ورودی دستورات",
|
||||
"Interface": "رابط",
|
||||
"Invalid Tag": "تگ نامعتبر",
|
||||
"Is Model Vision Capable": "",
|
||||
"January": "ژانویه",
|
||||
"join our Discord for help.": "برای کمک به دیسکورد ما بپیوندید.",
|
||||
"JSON": "JSON",
|
||||
@ -253,6 +258,7 @@
|
||||
"Made by OpenWebUI Community": "ساخته شده توسط OpenWebUI Community",
|
||||
"Make sure to enclose them with": "مطمئن شوید که آنها را با این محصور کنید:",
|
||||
"Manage LiteLLM Models": "Manage LiteLLM Models",
|
||||
"Manage Model Information": "",
|
||||
"Manage Models": "مدیریت مدل\u200cها",
|
||||
"Manage Ollama Models": "مدیریت مدل\u200cهای اولاما",
|
||||
"March": "مارچ",
|
||||
@ -272,7 +278,12 @@
|
||||
"Model '{{modelTag}}' is already in queue for downloading.": "مدل '{{modelTag}}' در حال حاضر در صف برای دانلود است.",
|
||||
"Model {{modelId}} not found": "مدل {{modelId}} یافت نشد",
|
||||
"Model {{modelName}} already exists.": "مدل {{modelName}} در حال حاضر وجود دارد.",
|
||||
"Model {{modelName}} is not vision capable": "",
|
||||
"Model Description": "",
|
||||
"Model Display Name": "",
|
||||
"Model filesystem path detected. Model shortname is required for update, cannot continue.": "مسیر فایل سیستم مدل یافت شد. برای بروزرسانی نیاز است نام کوتاه مدل وجود داشته باشد.",
|
||||
"Model info for {{modelName}} added successfully": "",
|
||||
"Model info for {{modelName}} deleted successfully": "",
|
||||
"Model Name": "نام مدل",
|
||||
"Model not selected": "مدل انتخاب نشده",
|
||||
"Model Tag Name": "نام تگ مدل",
|
||||
@ -289,6 +300,7 @@
|
||||
"Name your modelfile": "فایل مدل را نام\u200cگذاری کنید",
|
||||
"New Chat": "گپ جدید",
|
||||
"New Password": "رمز عبور جدید",
|
||||
"No": "",
|
||||
"No results found": "نتیجه\u200cای یافت نشد",
|
||||
"No source available": "منبعی در دسترس نیست",
|
||||
"Not factually correct": "اشتباهی فکری نیست",
|
||||
@ -385,6 +397,7 @@
|
||||
"Select a model": "انتخاب یک مدل",
|
||||
"Select an Ollama instance": "انتخاب یک نمونه از اولاما",
|
||||
"Select model": "انتخاب یک مدل",
|
||||
"Selected models do not support image inputs": "",
|
||||
"Send": "ارسال",
|
||||
"Send a Message": "ارسال یک پیام",
|
||||
"Send message": "ارسال پیام",
|
||||
@ -492,6 +505,7 @@
|
||||
"Workspace": "محیط کار",
|
||||
"Write a prompt suggestion (e.g. Who are you?)": "یک پیشنهاد پرامپت بنویسید (مثلاً شما کی هستید؟)",
|
||||
"Write a summary in 50 words that summarizes [topic or keyword].": "خلاصه ای در 50 کلمه بنویسید که [موضوع یا کلمه کلیدی] را خلاصه کند.",
|
||||
"Yes": "",
|
||||
"Yesterday": "دیروز",
|
||||
"You": "شما",
|
||||
"You have no archived conversations.": "شما هیچ گفتگوی ذخیره شده ندارید.",
|
||||
|
@ -6,6 +6,7 @@
|
||||
"{{modelName}} is thinking...": "{{modelName}} miettii...",
|
||||
"{{user}}'s Chats": "{{user}}:n keskustelut",
|
||||
"{{webUIName}} Backend Required": "{{webUIName}} backend vaaditaan",
|
||||
"A selected model does not support image input": "",
|
||||
"a user": "käyttäjä",
|
||||
"About": "Tietoja",
|
||||
"Account": "Tili",
|
||||
@ -31,6 +32,7 @@
|
||||
"Advanced Parameters": "Edistyneet parametrit",
|
||||
"all": "kaikki",
|
||||
"All Documents": "Kaikki asiakirjat",
|
||||
"All selected models do not support image input, removed images": "",
|
||||
"All Users": "Kaikki käyttäjät",
|
||||
"Allow": "Salli",
|
||||
"Allow Chat Deletion": "Salli keskustelujen poisto",
|
||||
@ -115,6 +117,7 @@
|
||||
"Created at": "Luotu",
|
||||
"Created At": "Luotu",
|
||||
"Current Model": "Nykyinen malli",
|
||||
"Current Models": "",
|
||||
"Current Password": "Nykyinen salasana",
|
||||
"Custom": "Mukautettu",
|
||||
"Customize Ollama models for a specific purpose": "Mukauta Ollama-malleja tiettyyn tarkoitukseen",
|
||||
@ -181,6 +184,7 @@
|
||||
"Enter LiteLLM API RPM (litellm_params.rpm)": "Syötä LiteLLM-APIn RPM (litellm_params.rpm)",
|
||||
"Enter LiteLLM Model (litellm_params.model)": "Syötä LiteLLM-malli (litellm_params.model)",
|
||||
"Enter Max Tokens (litellm_params.max_tokens)": "Syötä maksimitokenit (litellm_params.max_tokens)",
|
||||
"Enter Model Display Name": "",
|
||||
"Enter model tag (e.g. {{modelTag}})": "Syötä mallitagi (esim. {{modelTag}})",
|
||||
"Enter Number of Steps (e.g. 50)": "Syötä askelien määrä (esim. 50)",
|
||||
"Enter Score": "Syötä pisteet",
|
||||
@ -235,6 +239,7 @@
|
||||
"Input commands": "Syötä komennot",
|
||||
"Interface": "Käyttöliittymä",
|
||||
"Invalid Tag": "Virheellinen tagi",
|
||||
"Is Model Vision Capable": "",
|
||||
"January": "tammikuu",
|
||||
"join our Discord for help.": "liity Discordiimme saadaksesi apua.",
|
||||
"JSON": "JSON",
|
||||
@ -253,6 +258,7 @@
|
||||
"Made by OpenWebUI Community": "Tehnyt OpenWebUI-yhteisö",
|
||||
"Make sure to enclose them with": "Varmista, että suljet ne",
|
||||
"Manage LiteLLM Models": "Hallitse LiteLLM-malleja",
|
||||
"Manage Model Information": "",
|
||||
"Manage Models": "Hallitse malleja",
|
||||
"Manage Ollama Models": "Hallitse Ollama-malleja",
|
||||
"March": "maaliskuu",
|
||||
@ -272,7 +278,12 @@
|
||||
"Model '{{modelTag}}' is already in queue for downloading.": "Malli '{{modelTag}}' on jo jonossa ladattavaksi.",
|
||||
"Model {{modelId}} not found": "Mallia {{modelId}} ei löytynyt",
|
||||
"Model {{modelName}} already exists.": "Malli {{modelName}} on jo olemassa.",
|
||||
"Model {{modelName}} is not vision capable": "",
|
||||
"Model Description": "",
|
||||
"Model Display Name": "",
|
||||
"Model filesystem path detected. Model shortname is required for update, cannot continue.": "Mallin tiedostojärjestelmäpolku havaittu. Mallin lyhytnimi vaaditaan päivitykseen, ei voi jatkaa.",
|
||||
"Model info for {{modelName}} added successfully": "",
|
||||
"Model info for {{modelName}} deleted successfully": "",
|
||||
"Model Name": "Mallin nimi",
|
||||
"Model not selected": "Mallia ei valittu",
|
||||
"Model Tag Name": "Mallitagin nimi",
|
||||
@ -289,6 +300,7 @@
|
||||
"Name your modelfile": "Nimeä mallitiedostosi",
|
||||
"New Chat": "Uusi keskustelu",
|
||||
"New Password": "Uusi salasana",
|
||||
"No": "",
|
||||
"No results found": "Ei tuloksia",
|
||||
"No source available": "Ei lähdettä saatavilla",
|
||||
"Not factually correct": "Ei faktisesti oikein",
|
||||
@ -385,6 +397,7 @@
|
||||
"Select a model": "Valitse malli",
|
||||
"Select an Ollama instance": "Valitse Ollama-instanssi",
|
||||
"Select model": "Valitse malli",
|
||||
"Selected models do not support image inputs": "",
|
||||
"Send": "Lähetä",
|
||||
"Send a Message": "Lähetä viesti",
|
||||
"Send message": "Lähetä viesti",
|
||||
@ -492,6 +505,7 @@
|
||||
"Workspace": "Työtilat",
|
||||
"Write a prompt suggestion (e.g. Who are you?)": "Kirjoita ehdotettu kehote (esim. Kuka olet?)",
|
||||
"Write a summary in 50 words that summarizes [topic or keyword].": "Kirjoita 50 sanan yhteenveto, joka tiivistää [aihe tai avainsana].",
|
||||
"Yes": "",
|
||||
"Yesterday": "Eilen",
|
||||
"You": "Sinä",
|
||||
"You have no archived conversations.": "Sinulla ei ole arkistoituja keskusteluja.",
|
||||
|
@ -6,6 +6,7 @@
|
||||
"{{modelName}} is thinking...": "{{modelName}} réfléchit...",
|
||||
"{{user}}'s Chats": "{{user}}'s Chats",
|
||||
"{{webUIName}} Backend Required": "Backend {{webUIName}} requis",
|
||||
"A selected model does not support image input": "",
|
||||
"a user": "un utilisateur",
|
||||
"About": "À propos",
|
||||
"Account": "Compte",
|
||||
@ -31,6 +32,7 @@
|
||||
"Advanced Parameters": "Paramètres avancés",
|
||||
"all": "tous",
|
||||
"All Documents": "Tous les documents",
|
||||
"All selected models do not support image input, removed images": "",
|
||||
"All Users": "Tous les utilisateurs",
|
||||
"Allow": "Autoriser",
|
||||
"Allow Chat Deletion": "Autoriser la suppression des discussions",
|
||||
@ -115,6 +117,7 @@
|
||||
"Created at": "Créé le",
|
||||
"Created At": "Créé le",
|
||||
"Current Model": "Modèle actuel",
|
||||
"Current Models": "",
|
||||
"Current Password": "Mot de passe actuel",
|
||||
"Custom": "Personnalisé",
|
||||
"Customize Ollama models for a specific purpose": "Personnaliser les modèles Ollama pour un objectif spécifique",
|
||||
@ -181,6 +184,7 @@
|
||||
"Enter LiteLLM API RPM (litellm_params.rpm)": "Entrez le RPM de l'API LiteLLM (litellm_params.rpm)",
|
||||
"Enter LiteLLM Model (litellm_params.model)": "Entrez le modèle LiteLLM (litellm_params.model)",
|
||||
"Enter Max Tokens (litellm_params.max_tokens)": "Entrez le nombre max de tokens (litellm_params.max_tokens)",
|
||||
"Enter Model Display Name": "",
|
||||
"Enter model tag (e.g. {{modelTag}})": "Entrez le tag du modèle (p. ex. {{modelTag}})",
|
||||
"Enter Number of Steps (e.g. 50)": "Entrez le nombre d'étapes (p. ex. 50)",
|
||||
"Enter Score": "Entrez le score",
|
||||
@ -235,6 +239,7 @@
|
||||
"Input commands": "Entrez des commandes d'entrée",
|
||||
"Interface": "Interface",
|
||||
"Invalid Tag": "Tag invalide",
|
||||
"Is Model Vision Capable": "",
|
||||
"January": "Janvier",
|
||||
"join our Discord for help.": "rejoignez notre Discord pour obtenir de l'aide.",
|
||||
"JSON": "JSON",
|
||||
@ -253,6 +258,7 @@
|
||||
"Made by OpenWebUI Community": "Réalisé par la communauté OpenWebUI",
|
||||
"Make sure to enclose them with": "Assurez-vous de les entourer avec",
|
||||
"Manage LiteLLM Models": "Gérer les modèles LiteLLM",
|
||||
"Manage Model Information": "",
|
||||
"Manage Models": "Gérer les modèles",
|
||||
"Manage Ollama Models": "Gérer les modèles Ollama",
|
||||
"March": "Mars",
|
||||
@ -272,7 +278,12 @@
|
||||
"Model '{{modelTag}}' is already in queue for downloading.": "Le modèle '{{modelTag}}' est déjà dans la file d'attente pour le téléchargement.",
|
||||
"Model {{modelId}} not found": "Modèle {{modelId}} non trouvé",
|
||||
"Model {{modelName}} already exists.": "Le modèle {{modelName}} existe déjà.",
|
||||
"Model {{modelName}} is not vision capable": "",
|
||||
"Model Description": "",
|
||||
"Model Display Name": "",
|
||||
"Model filesystem path detected. Model shortname is required for update, cannot continue.": "Le chemin du système de fichiers du modèle a été détecté. Le nom court du modèle est nécessaire pour la mise à jour, impossible de continuer.",
|
||||
"Model info for {{modelName}} added successfully": "",
|
||||
"Model info for {{modelName}} deleted successfully": "",
|
||||
"Model Name": "Nom du modèle",
|
||||
"Model not selected": "Modèle non sélectionné",
|
||||
"Model Tag Name": "Nom de tag du modèle",
|
||||
@ -289,6 +300,7 @@
|
||||
"Name your modelfile": "Nommez votre fichier de modèle",
|
||||
"New Chat": "Nouvelle discussion",
|
||||
"New Password": "Nouveau mot de passe",
|
||||
"No": "",
|
||||
"No results found": "Aucun résultat trouvé",
|
||||
"No source available": "Aucune source disponible",
|
||||
"Not factually correct": "Non, pas exactement correct",
|
||||
@ -385,6 +397,7 @@
|
||||
"Select a model": "Sélectionnez un modèle",
|
||||
"Select an Ollama instance": "Sélectionner une instance Ollama",
|
||||
"Select model": "Sélectionnez un modèle",
|
||||
"Selected models do not support image inputs": "",
|
||||
"Send": "Envoyer",
|
||||
"Send a Message": "Envoyer un message",
|
||||
"Send message": "Envoyer un message",
|
||||
@ -492,6 +505,7 @@
|
||||
"Workspace": "Espace de travail",
|
||||
"Write a prompt suggestion (e.g. Who are you?)": "Rédigez une suggestion de prompt (p. ex. Qui êtes-vous ?)",
|
||||
"Write a summary in 50 words that summarizes [topic or keyword].": "Rédigez un résumé en 50 mots qui résume [sujet ou mot-clé].",
|
||||
"Yes": "",
|
||||
"Yesterday": "hier",
|
||||
"You": "Vous",
|
||||
"You have no archived conversations.": "Vous n'avez aucune conversation archivée.",
|
||||
|
@ -6,6 +6,7 @@
|
||||
"{{modelName}} is thinking...": "{{modelName}} réfléchit...",
|
||||
"{{user}}'s Chats": "{{user}}'s Chats",
|
||||
"{{webUIName}} Backend Required": "Backend {{webUIName}} requis",
|
||||
"A selected model does not support image input": "",
|
||||
"a user": "un utilisateur",
|
||||
"About": "À propos",
|
||||
"Account": "Compte",
|
||||
@ -31,6 +32,7 @@
|
||||
"Advanced Parameters": "Paramètres avancés",
|
||||
"all": "tous",
|
||||
"All Documents": "Tous les documents",
|
||||
"All selected models do not support image input, removed images": "",
|
||||
"All Users": "Tous les utilisateurs",
|
||||
"Allow": "Autoriser",
|
||||
"Allow Chat Deletion": "Autoriser la suppression du chat",
|
||||
@ -115,6 +117,7 @@
|
||||
"Created at": "Créé le",
|
||||
"Created At": "Créé le",
|
||||
"Current Model": "Modèle actuel",
|
||||
"Current Models": "",
|
||||
"Current Password": "Mot de passe actuel",
|
||||
"Custom": "Personnalisé",
|
||||
"Customize Ollama models for a specific purpose": "Personnaliser les modèles Ollama pour un objectif spécifique",
|
||||
@ -181,6 +184,7 @@
|
||||
"Enter LiteLLM API RPM (litellm_params.rpm)": "Entrez le RPM de l'API LiteLLM (litellm_params.rpm)",
|
||||
"Enter LiteLLM Model (litellm_params.model)": "Entrez le modèle LiteLLM (litellm_params.model)",
|
||||
"Enter Max Tokens (litellm_params.max_tokens)": "Entrez le nombre max de tokens (litellm_params.max_tokens)",
|
||||
"Enter Model Display Name": "",
|
||||
"Enter model tag (e.g. {{modelTag}})": "Entrez le tag du modèle (p. ex. {{modelTag}})",
|
||||
"Enter Number of Steps (e.g. 50)": "Entrez le nombre d'étapes (p. ex. 50)",
|
||||
"Enter Score": "Entrez le score",
|
||||
@ -235,6 +239,7 @@
|
||||
"Input commands": "Entrez les commandes d'entrée",
|
||||
"Interface": "Interface",
|
||||
"Invalid Tag": "Tag invalide",
|
||||
"Is Model Vision Capable": "",
|
||||
"January": "Janvier",
|
||||
"join our Discord for help.": "rejoignez notre Discord pour obtenir de l'aide.",
|
||||
"JSON": "JSON",
|
||||
@ -253,6 +258,7 @@
|
||||
"Made by OpenWebUI Community": "Réalisé par la communauté OpenWebUI",
|
||||
"Make sure to enclose them with": "Assurez-vous de les entourer avec",
|
||||
"Manage LiteLLM Models": "Gérer les modèles LiteLLM",
|
||||
"Manage Model Information": "",
|
||||
"Manage Models": "Gérer les modèles",
|
||||
"Manage Ollama Models": "Gérer les modèles Ollama",
|
||||
"March": "Mars",
|
||||
@ -272,7 +278,12 @@
|
||||
"Model '{{modelTag}}' is already in queue for downloading.": "Le modèle '{{modelTag}}' est déjà dans la file d'attente pour le téléchargement.",
|
||||
"Model {{modelId}} not found": "Modèle {{modelId}} non trouvé",
|
||||
"Model {{modelName}} already exists.": "Le modèle {{modelName}} existe déjà.",
|
||||
"Model {{modelName}} is not vision capable": "",
|
||||
"Model Description": "",
|
||||
"Model Display Name": "",
|
||||
"Model filesystem path detected. Model shortname is required for update, cannot continue.": "Le chemin du système de fichiers du modèle a été détecté. Le nom court du modèle est nécessaire pour la mise à jour, impossible de continuer.",
|
||||
"Model info for {{modelName}} added successfully": "",
|
||||
"Model info for {{modelName}} deleted successfully": "",
|
||||
"Model Name": "Nom du modèle",
|
||||
"Model not selected": "Modèle non sélectionné",
|
||||
"Model Tag Name": "Nom de tag du modèle",
|
||||
@ -289,6 +300,7 @@
|
||||
"Name your modelfile": "Nommez votre fichier de modèle",
|
||||
"New Chat": "Nouveau chat",
|
||||
"New Password": "Nouveau mot de passe",
|
||||
"No": "",
|
||||
"No results found": "Aucun résultat trouvé",
|
||||
"No source available": "Aucune source disponible",
|
||||
"Not factually correct": "Non, pas exactement correct",
|
||||
@ -385,6 +397,7 @@
|
||||
"Select a model": "Sélectionner un modèle",
|
||||
"Select an Ollama instance": "Sélectionner une instance Ollama",
|
||||
"Select model": "Sélectionner un modèle",
|
||||
"Selected models do not support image inputs": "",
|
||||
"Send": "Envoyer",
|
||||
"Send a Message": "Envoyer un message",
|
||||
"Send message": "Envoyer un message",
|
||||
@ -492,6 +505,7 @@
|
||||
"Workspace": "Espace de travail",
|
||||
"Write a prompt suggestion (e.g. Who are you?)": "Écrivez un prompt (e.x. Qui est-tu ?)",
|
||||
"Write a summary in 50 words that summarizes [topic or keyword].": "Ecrivez un résumé en 50 mots [sujet ou mot-clé]",
|
||||
"Yes": "",
|
||||
"Yesterday": "hier",
|
||||
"You": "Vous",
|
||||
"You have no archived conversations.": "Vous n'avez aucune conversation archivée.",
|
||||
|
@ -6,6 +6,7 @@
|
||||
"{{modelName}} is thinking...": "{{modelName}} חושב...",
|
||||
"{{user}}'s Chats": "צ'אטים של {{user}}",
|
||||
"{{webUIName}} Backend Required": "נדרש Backend של {{webUIName}}",
|
||||
"A selected model does not support image input": "",
|
||||
"a user": "משתמש",
|
||||
"About": "אודות",
|
||||
"Account": "חשבון",
|
||||
@ -31,6 +32,7 @@
|
||||
"Advanced Parameters": "פרמטרים מתקדמים",
|
||||
"all": "הכל",
|
||||
"All Documents": "כל המסמכים",
|
||||
"All selected models do not support image input, removed images": "",
|
||||
"All Users": "כל המשתמשים",
|
||||
"Allow": "אפשר",
|
||||
"Allow Chat Deletion": "אפשר מחיקת צ'אט",
|
||||
@ -115,6 +117,7 @@
|
||||
"Created at": "נוצר ב",
|
||||
"Created At": "נוצר ב",
|
||||
"Current Model": "המודל הנוכחי",
|
||||
"Current Models": "",
|
||||
"Current Password": "הסיסמה הנוכחית",
|
||||
"Custom": "מותאם אישית",
|
||||
"Customize Ollama models for a specific purpose": "התאמה אישית של מודלים של Ollama למטרה מסוימת",
|
||||
@ -181,6 +184,7 @@
|
||||
"Enter LiteLLM API RPM (litellm_params.rpm)": "הזן RPM של API של LiteLLM (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 Display Name": "",
|
||||
"Enter model tag (e.g. {{modelTag}})": "הזן תג מודל (למשל {{modelTag}})",
|
||||
"Enter Number of Steps (e.g. 50)": "הזן מספר שלבים (למשל 50)",
|
||||
"Enter Score": "הזן ציון",
|
||||
@ -235,6 +239,7 @@
|
||||
"Input commands": "פקודות קלט",
|
||||
"Interface": "ממשק",
|
||||
"Invalid Tag": "תג לא חוקי",
|
||||
"Is Model Vision Capable": "",
|
||||
"January": "ינואר",
|
||||
"join our Discord for help.": "הצטרף ל-Discord שלנו לעזרה.",
|
||||
"JSON": "JSON",
|
||||
@ -253,6 +258,7 @@
|
||||
"Made by OpenWebUI Community": "נוצר על ידי קהילת OpenWebUI",
|
||||
"Make sure to enclose them with": "ודא להקיף אותם עם",
|
||||
"Manage LiteLLM Models": "נהל מודלים של LiteLLM",
|
||||
"Manage Model Information": "",
|
||||
"Manage Models": "נהל מודלים",
|
||||
"Manage Ollama Models": "נהל מודלים של Ollama",
|
||||
"March": "מרץ",
|
||||
@ -272,7 +278,12 @@
|
||||
"Model '{{modelTag}}' is already in queue for downloading.": "המודל '{{modelTag}}' כבר בתור להורדה.",
|
||||
"Model {{modelId}} not found": "המודל {{modelId}} לא נמצא",
|
||||
"Model {{modelName}} already exists.": "המודל {{modelName}} כבר קיים.",
|
||||
"Model {{modelName}} is not vision capable": "",
|
||||
"Model Description": "",
|
||||
"Model Display Name": "",
|
||||
"Model filesystem path detected. Model shortname is required for update, cannot continue.": "נתיב מערכת הקבצים של המודל זוהה. נדרש שם קצר של המודל לעדכון, לא ניתן להמשיך.",
|
||||
"Model info for {{modelName}} added successfully": "",
|
||||
"Model info for {{modelName}} deleted successfully": "",
|
||||
"Model Name": "שם המודל",
|
||||
"Model not selected": "לא נבחר מודל",
|
||||
"Model Tag Name": "שם תג המודל",
|
||||
@ -289,6 +300,7 @@
|
||||
"Name your modelfile": "תן שם לקובץ המודל שלך",
|
||||
"New Chat": "צ'אט חדש",
|
||||
"New Password": "סיסמה חדשה",
|
||||
"No": "",
|
||||
"No results found": "לא נמצאו תוצאות",
|
||||
"No source available": "אין מקור זמין",
|
||||
"Not factually correct": "לא נכון מבחינה עובדתית",
|
||||
@ -385,6 +397,7 @@
|
||||
"Select a model": "בחר מודל",
|
||||
"Select an Ollama instance": "בחר מופע של Ollama",
|
||||
"Select model": "בחר מודל",
|
||||
"Selected models do not support image inputs": "",
|
||||
"Send": "שלח",
|
||||
"Send a Message": "שלח הודעה",
|
||||
"Send message": "שלח הודעה",
|
||||
@ -492,6 +505,7 @@
|
||||
"Workspace": "סביבה",
|
||||
"Write a prompt suggestion (e.g. Who are you?)": "כתוב הצעה מהירה (למשל, מי אתה?)",
|
||||
"Write a summary in 50 words that summarizes [topic or keyword].": "כתוב סיכום ב-50 מילים שמסכם [נושא או מילת מפתח].",
|
||||
"Yes": "",
|
||||
"Yesterday": "אתמול",
|
||||
"You": "אתה",
|
||||
"You have no archived conversations.": "אין לך שיחות בארכיון.",
|
||||
|
@ -6,6 +6,7 @@
|
||||
"{{modelName}} is thinking...": "{{modelName}} सोच रहा है...",
|
||||
"{{user}}'s Chats": "{{user}} की चैट",
|
||||
"{{webUIName}} Backend Required": "{{webUIName}} बैकएंड आवश्यक",
|
||||
"A selected model does not support image input": "",
|
||||
"a user": "एक उपयोगकर्ता",
|
||||
"About": "हमारे बारे में",
|
||||
"Account": "खाता",
|
||||
@ -31,6 +32,7 @@
|
||||
"Advanced Parameters": "उन्नत पैरामीटर",
|
||||
"all": "सभी",
|
||||
"All Documents": "सभी डॉक्यूमेंट्स",
|
||||
"All selected models do not support image input, removed images": "",
|
||||
"All Users": "सभी उपयोगकर्ता",
|
||||
"Allow": "अनुमति दें",
|
||||
"Allow Chat Deletion": "चैट हटाने की अनुमति दें",
|
||||
@ -115,6 +117,7 @@
|
||||
"Created at": "किस समय बनाया गया",
|
||||
"Created At": "किस समय बनाया गया",
|
||||
"Current Model": "वर्तमान मॉडल",
|
||||
"Current Models": "",
|
||||
"Current Password": "वर्तमान पासवर्ड",
|
||||
"Custom": "कस्टम संस्करण",
|
||||
"Customize Ollama models for a specific purpose": "किसी विशिष्ट उद्देश्य के लिए ओलामा मॉडल को अनुकूलित करें",
|
||||
@ -181,6 +184,7 @@
|
||||
"Enter LiteLLM API RPM (litellm_params.rpm)": "LiteLLM API RPM दर्ज करें (litellm_params.rpm) ",
|
||||
"Enter LiteLLM Model (litellm_params.model)": "LiteLLM Model दर्ज करें (litellm_params.model)",
|
||||
"Enter Max Tokens (litellm_params.max_tokens)": "Max Tokens दर्ज करें (litellm_params.max_tokens)",
|
||||
"Enter Model Display Name": "",
|
||||
"Enter model tag (e.g. {{modelTag}})": "Model tag दर्ज करें (उदा. {{modelTag}})",
|
||||
"Enter Number of Steps (e.g. 50)": "चरणों की संख्या दर्ज करें (उदा. 50)",
|
||||
"Enter Score": "स्कोर दर्ज करें",
|
||||
@ -235,6 +239,7 @@
|
||||
"Input commands": "इनपुट क命",
|
||||
"Interface": "इंटरफेस",
|
||||
"Invalid Tag": "अवैध टैग",
|
||||
"Is Model Vision Capable": "",
|
||||
"January": "जनवरी",
|
||||
"join our Discord for help.": "मदद के लिए हमारे डिस्कोर्ड में शामिल हों।",
|
||||
"JSON": "ज्ञान प्रकार",
|
||||
@ -253,6 +258,7 @@
|
||||
"Made by OpenWebUI Community": "OpenWebUI समुदाय द्वारा निर्मित",
|
||||
"Make sure to enclose them with": "उन्हें संलग्न करना सुनिश्चित करें",
|
||||
"Manage LiteLLM Models": "LiteLLM मॉडल प्रबंधित करें",
|
||||
"Manage Model Information": "",
|
||||
"Manage Models": "मॉडल प्रबंधित करें",
|
||||
"Manage Ollama Models": "Ollama मॉडल प्रबंधित करें",
|
||||
"March": "मार्च",
|
||||
@ -272,7 +278,12 @@
|
||||
"Model '{{modelTag}}' is already in queue for downloading.": "मॉडल '{{modelTag}}' पहले से ही डाउनलोड करने के लिए कतार में है।",
|
||||
"Model {{modelId}} not found": "मॉडल {{modelId}} नहीं मिला",
|
||||
"Model {{modelName}} already exists.": "मॉडल {{modelName}} पहले से मौजूद है।",
|
||||
"Model {{modelName}} is not vision capable": "",
|
||||
"Model Description": "",
|
||||
"Model Display Name": "",
|
||||
"Model filesystem path detected. Model shortname is required for update, cannot continue.": "मॉडल फ़ाइल सिस्टम पथ का पता चला. अद्यतन के लिए मॉडल संक्षिप्त नाम आवश्यक है, जारी नहीं रखा जा सकता।",
|
||||
"Model info for {{modelName}} added successfully": "",
|
||||
"Model info for {{modelName}} deleted successfully": "",
|
||||
"Model Name": "मॉडल नाम",
|
||||
"Model not selected": "मॉडल चयनित नहीं है",
|
||||
"Model Tag Name": "मॉडल टैग नाम",
|
||||
@ -289,6 +300,7 @@
|
||||
"Name your modelfile": "अपनी मॉडलफ़ाइल को नाम दें",
|
||||
"New Chat": "नई चैट",
|
||||
"New Password": "नया पासवर्ड",
|
||||
"No": "",
|
||||
"No results found": "कोई परिणाम नहीं मिला",
|
||||
"No source available": "कोई स्रोत उपलब्ध नहीं है",
|
||||
"Not factually correct": "तथ्यात्मक रूप से सही नहीं है",
|
||||
@ -385,6 +397,7 @@
|
||||
"Select a model": "एक मॉडल चुनें",
|
||||
"Select an Ollama instance": "एक Ollama Instance चुनें",
|
||||
"Select model": "मॉडल चुनें",
|
||||
"Selected models do not support image inputs": "",
|
||||
"Send": "भेज",
|
||||
"Send a Message": "एक संदेश भेजो",
|
||||
"Send message": "मेसेज भेजें",
|
||||
@ -492,6 +505,7 @@
|
||||
"Workspace": "वर्कस्पेस",
|
||||
"Write a prompt suggestion (e.g. Who are you?)": "एक त्वरित सुझाव लिखें (जैसे कि आप कौन हैं?)",
|
||||
"Write a summary in 50 words that summarizes [topic or keyword].": "50 शब्दों में एक सारांश लिखें जो [विषय या कीवर्ड] का सारांश प्रस्तुत करता हो।",
|
||||
"Yes": "",
|
||||
"Yesterday": "कल",
|
||||
"You": "आप",
|
||||
"You have no archived conversations.": "आपको कोई अंकित चैट नहीं है।",
|
||||
|
@ -6,6 +6,7 @@
|
||||
"{{modelName}} is thinking...": "{{modelName}} razmišlja...",
|
||||
"{{user}}'s Chats": "Razgovori korisnika {{user}}",
|
||||
"{{webUIName}} Backend Required": "{{webUIName}} Backend je potreban",
|
||||
"A selected model does not support image input": "",
|
||||
"a user": "korisnik",
|
||||
"About": "O aplikaciji",
|
||||
"Account": "Račun",
|
||||
@ -31,6 +32,7 @@
|
||||
"Advanced Parameters": "Napredni parametri",
|
||||
"all": "sve",
|
||||
"All Documents": "Svi dokumenti",
|
||||
"All selected models do not support image input, removed images": "",
|
||||
"All Users": "Svi korisnici",
|
||||
"Allow": "Dopusti",
|
||||
"Allow Chat Deletion": "Dopusti brisanje razgovora",
|
||||
@ -115,6 +117,7 @@
|
||||
"Created at": "Stvoreno",
|
||||
"Created At": "Stvoreno",
|
||||
"Current Model": "Trenutni model",
|
||||
"Current Models": "",
|
||||
"Current Password": "Trenutna lozinka",
|
||||
"Custom": "Prilagođeno",
|
||||
"Customize Ollama models for a specific purpose": "Prilagodite Ollama modele za specifičnu svrhu",
|
||||
@ -181,6 +184,7 @@
|
||||
"Enter LiteLLM API RPM (litellm_params.rpm)": "Unesite LiteLLM API RPM (litellm_params.rpm)",
|
||||
"Enter LiteLLM Model (litellm_params.model)": "Unesite LiteLLM model (litellm_params.model)",
|
||||
"Enter Max Tokens (litellm_params.max_tokens)": "Unesite maksimalan broj tokena (litellm_params.max_tokens)",
|
||||
"Enter Model Display Name": "",
|
||||
"Enter model tag (e.g. {{modelTag}})": "Unesite oznaku modela (npr. {{modelTag}})",
|
||||
"Enter Number of Steps (e.g. 50)": "Unesite broj koraka (npr. 50)",
|
||||
"Enter Score": "Unesite ocjenu",
|
||||
@ -235,6 +239,7 @@
|
||||
"Input commands": "Unos naredbi",
|
||||
"Interface": "Sučelje",
|
||||
"Invalid Tag": "Nevažeća oznaka",
|
||||
"Is Model Vision Capable": "",
|
||||
"January": "Siječanj",
|
||||
"join our Discord for help.": "pridružite se našem Discordu za pomoć.",
|
||||
"JSON": "JSON",
|
||||
@ -253,6 +258,7 @@
|
||||
"Made by OpenWebUI Community": "Izradio OpenWebUI Community",
|
||||
"Make sure to enclose them with": "Provjerite da ih zatvorite s",
|
||||
"Manage LiteLLM Models": "Upravljajte LiteLLM modelima",
|
||||
"Manage Model Information": "",
|
||||
"Manage Models": "Upravljanje modelima",
|
||||
"Manage Ollama Models": "Upravljanje Ollama modelima",
|
||||
"March": "Ožujak",
|
||||
@ -272,7 +278,12 @@
|
||||
"Model '{{modelTag}}' is already in queue for downloading.": "Model '{{modelTag}}' je već u redu za preuzimanje.",
|
||||
"Model {{modelId}} not found": "Model {{modelId}} nije pronađen",
|
||||
"Model {{modelName}} already exists.": "Model {{modelName}} već postoji.",
|
||||
"Model {{modelName}} is not vision capable": "",
|
||||
"Model Description": "",
|
||||
"Model Display Name": "",
|
||||
"Model filesystem path detected. Model shortname is required for update, cannot continue.": "Otkriven put datotečnog sustava modela. Kratko ime modela je potrebno za ažuriranje, nije moguće nastaviti.",
|
||||
"Model info for {{modelName}} added successfully": "",
|
||||
"Model info for {{modelName}} deleted successfully": "",
|
||||
"Model Name": "Naziv modela",
|
||||
"Model not selected": "Model nije odabran",
|
||||
"Model Tag Name": "Naziv oznake modela",
|
||||
@ -289,6 +300,7 @@
|
||||
"Name your modelfile": "Nazovite svoju datoteku modela",
|
||||
"New Chat": "Novi razgovor",
|
||||
"New Password": "Nova lozinka",
|
||||
"No": "",
|
||||
"No results found": "Nema rezultata",
|
||||
"No source available": "Nema dostupnog izvora",
|
||||
"Not factually correct": "Nije činjenično točno",
|
||||
@ -385,6 +397,7 @@
|
||||
"Select a model": "Odaberite model",
|
||||
"Select an Ollama instance": "Odaberite Ollama instancu",
|
||||
"Select model": "Odaberite model",
|
||||
"Selected models do not support image inputs": "",
|
||||
"Send": "Pošalji",
|
||||
"Send a Message": "Pošaljite poruku",
|
||||
"Send message": "Pošalji poruku",
|
||||
@ -492,6 +505,7 @@
|
||||
"Workspace": "Radna ploča",
|
||||
"Write a prompt suggestion (e.g. Who are you?)": "Napišite prijedlog prompta (npr. Tko si ti?)",
|
||||
"Write a summary in 50 words that summarizes [topic or keyword].": "Napišite sažetak u 50 riječi koji sažima [temu ili ključnu riječ].",
|
||||
"Yes": "",
|
||||
"Yesterday": "Jučer",
|
||||
"You": "Vi",
|
||||
"You have no archived conversations.": "Nemate arhiviranih razgovora.",
|
||||
|
@ -6,6 +6,7 @@
|
||||
"{{modelName}} is thinking...": "{{modelName}} sta pensando...",
|
||||
"{{user}}'s Chats": "{{user}} Chat",
|
||||
"{{webUIName}} Backend Required": "{{webUIName}} Backend richiesto",
|
||||
"A selected model does not support image input": "",
|
||||
"a user": "un utente",
|
||||
"About": "Informazioni",
|
||||
"Account": "Account",
|
||||
@ -31,6 +32,7 @@
|
||||
"Advanced Parameters": "Parametri avanzati",
|
||||
"all": "tutti",
|
||||
"All Documents": "Tutti i documenti",
|
||||
"All selected models do not support image input, removed images": "",
|
||||
"All Users": "Tutti gli utenti",
|
||||
"Allow": "Consenti",
|
||||
"Allow Chat Deletion": "Consenti l'eliminazione della chat",
|
||||
@ -115,6 +117,7 @@
|
||||
"Created at": "Creato il",
|
||||
"Created At": "Creato il",
|
||||
"Current Model": "Modello corrente",
|
||||
"Current Models": "",
|
||||
"Current Password": "Password corrente",
|
||||
"Custom": "Personalizzato",
|
||||
"Customize Ollama models for a specific purpose": "Personalizza i modelli Ollama per uno scopo specifico",
|
||||
@ -181,6 +184,7 @@
|
||||
"Enter LiteLLM API RPM (litellm_params.rpm)": "Inserisci LiteLLM API RPM (litellm_params.rpm)",
|
||||
"Enter LiteLLM Model (litellm_params.model)": "Inserisci il modello LiteLLM (litellm_params.model)",
|
||||
"Enter Max Tokens (litellm_params.max_tokens)": "Inserisci Max Tokens (litellm_params.max_tokens)",
|
||||
"Enter Model Display Name": "",
|
||||
"Enter model tag (e.g. {{modelTag}})": "Inserisci il tag del modello (ad esempio {{modelTag}})",
|
||||
"Enter Number of Steps (e.g. 50)": "Inserisci il numero di passaggi (ad esempio 50)",
|
||||
"Enter Score": "Inserisci il punteggio",
|
||||
@ -235,6 +239,7 @@
|
||||
"Input commands": "Comandi di input",
|
||||
"Interface": "Interfaccia",
|
||||
"Invalid Tag": "Tag non valido",
|
||||
"Is Model Vision Capable": "",
|
||||
"January": "Gennaio",
|
||||
"join our Discord for help.": "unisciti al nostro Discord per ricevere aiuto.",
|
||||
"JSON": "JSON",
|
||||
@ -253,6 +258,7 @@
|
||||
"Made by OpenWebUI Community": "Realizzato dalla comunità OpenWebUI",
|
||||
"Make sure to enclose them with": "Assicurati di racchiuderli con",
|
||||
"Manage LiteLLM Models": "Gestisci modelli LiteLLM",
|
||||
"Manage Model Information": "",
|
||||
"Manage Models": "Gestisci modelli",
|
||||
"Manage Ollama Models": "Gestisci modelli Ollama",
|
||||
"March": "Marzo",
|
||||
@ -272,7 +278,12 @@
|
||||
"Model '{{modelTag}}' is already in queue for downloading.": "Il modello '{{modelTag}}' è già in coda per il download.",
|
||||
"Model {{modelId}} not found": "Modello {{modelId}} non trovato",
|
||||
"Model {{modelName}} already exists.": "Il modello {{modelName}} esiste già.",
|
||||
"Model {{modelName}} is not vision capable": "",
|
||||
"Model Description": "",
|
||||
"Model Display Name": "",
|
||||
"Model filesystem path detected. Model shortname is required for update, cannot continue.": "Percorso del filesystem del modello rilevato. Il nome breve del modello è richiesto per l'aggiornamento, impossibile continuare.",
|
||||
"Model info for {{modelName}} added successfully": "",
|
||||
"Model info for {{modelName}} deleted successfully": "",
|
||||
"Model Name": "Nome modello",
|
||||
"Model not selected": "Modello non selezionato",
|
||||
"Model Tag Name": "Nome tag del modello",
|
||||
@ -289,6 +300,7 @@
|
||||
"Name your modelfile": "Assegna un nome al tuo file modello",
|
||||
"New Chat": "Nuova chat",
|
||||
"New Password": "Nuova password",
|
||||
"No": "",
|
||||
"No results found": "Nessun risultato trovato",
|
||||
"No source available": "Nessuna fonte disponibile",
|
||||
"Not factually correct": "Non corretto dal punto di vista fattuale",
|
||||
@ -385,6 +397,7 @@
|
||||
"Select a model": "Seleziona un modello",
|
||||
"Select an Ollama instance": "Seleziona un'istanza Ollama",
|
||||
"Select model": "Seleziona modello",
|
||||
"Selected models do not support image inputs": "",
|
||||
"Send": "Invia",
|
||||
"Send a Message": "Invia un messaggio",
|
||||
"Send message": "Invia messaggio",
|
||||
@ -492,6 +505,7 @@
|
||||
"Workspace": "Area di lavoro",
|
||||
"Write a prompt suggestion (e.g. Who are you?)": "Scrivi un suggerimento per il prompt (ad esempio Chi sei?)",
|
||||
"Write a summary in 50 words that summarizes [topic or keyword].": "Scrivi un riassunto in 50 parole che riassume [argomento o parola chiave].",
|
||||
"Yes": "",
|
||||
"Yesterday": "Ieri",
|
||||
"You": "Tu",
|
||||
"You have no archived conversations.": "Non hai conversazioni archiviate.",
|
||||
|
@ -6,6 +6,7 @@
|
||||
"{{modelName}} is thinking...": "{{modelName}} は思考中です...",
|
||||
"{{user}}'s Chats": "{{user}} のチャット",
|
||||
"{{webUIName}} Backend Required": "{{webUIName}} バックエンドが必要です",
|
||||
"A selected model does not support image input": "",
|
||||
"a user": "ユーザー",
|
||||
"About": "概要",
|
||||
"Account": "アカウント",
|
||||
@ -31,6 +32,7 @@
|
||||
"Advanced Parameters": "詳細パラメーター",
|
||||
"all": "すべて",
|
||||
"All Documents": "全てのドキュメント",
|
||||
"All selected models do not support image input, removed images": "",
|
||||
"All Users": "すべてのユーザー",
|
||||
"Allow": "許可",
|
||||
"Allow Chat Deletion": "チャットの削除を許可",
|
||||
@ -115,6 +117,7 @@
|
||||
"Created at": "作成日時",
|
||||
"Created At": "作成日時",
|
||||
"Current Model": "現在のモデル",
|
||||
"Current Models": "",
|
||||
"Current Password": "現在のパスワード",
|
||||
"Custom": "カスタム",
|
||||
"Customize Ollama models for a specific purpose": "特定の目的に合わせて Ollama モデルをカスタマイズ",
|
||||
@ -181,6 +184,7 @@
|
||||
"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 Display Name": "",
|
||||
"Enter model tag (e.g. {{modelTag}})": "モデルタグを入力してください (例: {{modelTag}})",
|
||||
"Enter Number of Steps (e.g. 50)": "ステップ数を入力してください (例: 50)",
|
||||
"Enter Score": "スコアを入力してください",
|
||||
@ -235,6 +239,7 @@
|
||||
"Input commands": "入力コマンド",
|
||||
"Interface": "インターフェース",
|
||||
"Invalid Tag": "無効なタグ",
|
||||
"Is Model Vision Capable": "",
|
||||
"January": "1月",
|
||||
"join our Discord for help.": "ヘルプについては、Discord に参加してください。",
|
||||
"JSON": "JSON",
|
||||
@ -253,6 +258,7 @@
|
||||
"Made by OpenWebUI Community": "OpenWebUI コミュニティによって作成",
|
||||
"Make sure to enclose them with": "必ず次で囲んでください",
|
||||
"Manage LiteLLM Models": "LiteLLM モデルを管理",
|
||||
"Manage Model Information": "",
|
||||
"Manage Models": "モデルを管理",
|
||||
"Manage Ollama Models": "Ollama モデルを管理",
|
||||
"March": "3月",
|
||||
@ -272,7 +278,12 @@
|
||||
"Model '{{modelTag}}' is already in queue for downloading.": "モデル '{{modelTag}}' はすでにダウンロード待ち行列に入っています。",
|
||||
"Model {{modelId}} not found": "モデル {{modelId}} が見つかりません",
|
||||
"Model {{modelName}} already exists.": "モデル {{modelName}} はすでに存在します。",
|
||||
"Model {{modelName}} is not vision capable": "",
|
||||
"Model Description": "",
|
||||
"Model Display Name": "",
|
||||
"Model filesystem path detected. Model shortname is required for update, cannot continue.": "モデルファイルシステムパスが検出されました。モデルの短縮名が必要です。更新できません。",
|
||||
"Model info for {{modelName}} added successfully": "",
|
||||
"Model info for {{modelName}} deleted successfully": "",
|
||||
"Model Name": "モデル名",
|
||||
"Model not selected": "モデルが選択されていません",
|
||||
"Model Tag Name": "モデルタグ名",
|
||||
@ -289,6 +300,7 @@
|
||||
"Name your modelfile": "モデルファイルに名前を付ける",
|
||||
"New Chat": "新しいチャット",
|
||||
"New Password": "新しいパスワード",
|
||||
"No": "",
|
||||
"No results found": "結果が見つかりません",
|
||||
"No source available": "使用可能なソースがありません",
|
||||
"Not factually correct": "実事上正しくない",
|
||||
@ -385,6 +397,7 @@
|
||||
"Select a model": "モデルを選択",
|
||||
"Select an Ollama instance": "Ollama インスタンスを選択",
|
||||
"Select model": "モデルを選択",
|
||||
"Selected models do not support image inputs": "",
|
||||
"Send": "送信",
|
||||
"Send a Message": "メッセージを送信",
|
||||
"Send message": "メッセージを送信",
|
||||
@ -492,6 +505,7 @@
|
||||
"Workspace": "ワークスペース",
|
||||
"Write a prompt suggestion (e.g. Who are you?)": "プロンプトの提案を書いてください (例: あなたは誰ですか?)",
|
||||
"Write a summary in 50 words that summarizes [topic or keyword].": "[トピックまたはキーワード] を要約する 50 語の概要を書いてください。",
|
||||
"Yes": "",
|
||||
"Yesterday": "昨日",
|
||||
"You": "あなた",
|
||||
"You have no archived conversations.": "これまでにアーカイブされた会話はありません。",
|
||||
|
@ -6,6 +6,7 @@
|
||||
"{{modelName}} is thinking...": "{{modelName}} ფიქრობს...",
|
||||
"{{user}}'s Chats": "{{user}}-ის ჩათები",
|
||||
"{{webUIName}} Backend Required": "{{webUIName}} საჭიროა ბექენდი",
|
||||
"A selected model does not support image input": "",
|
||||
"a user": "მომხმარებელი",
|
||||
"About": "შესახებ",
|
||||
"Account": "ანგარიში",
|
||||
@ -31,6 +32,7 @@
|
||||
"Advanced Parameters": "დამატებითი პარამეტრები",
|
||||
"all": "ყველა",
|
||||
"All Documents": "ყველა დოკუმენტი",
|
||||
"All selected models do not support image input, removed images": "",
|
||||
"All Users": "ყველა მომხმარებელი",
|
||||
"Allow": "ნების დართვა",
|
||||
"Allow Chat Deletion": "მიმოწერის წაშლის დაშვება",
|
||||
@ -115,6 +117,7 @@
|
||||
"Created at": "შექმნილია",
|
||||
"Created At": "შექმნილია",
|
||||
"Current Model": "მიმდინარე მოდელი",
|
||||
"Current Models": "",
|
||||
"Current Password": "მიმდინარე პაროლი",
|
||||
"Custom": "საკუთარი",
|
||||
"Customize Ollama models for a specific purpose": "Ollama მოდელების დამუშავება სპეციფიური დანიშნულებისთვის",
|
||||
@ -181,6 +184,7 @@
|
||||
"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 Display Name": "",
|
||||
"Enter model tag (e.g. {{modelTag}})": "შეიყვანეთ მოდელის ტეგი (მაგ. {{modelTag}})",
|
||||
"Enter Number of Steps (e.g. 50)": "შეიყვანეთ ნაბიჯების რაოდენობა (მაგ. 50)",
|
||||
"Enter Score": "შეიყვანეთ ქულა",
|
||||
@ -235,6 +239,7 @@
|
||||
"Input commands": "შეყვანით ბრძანებებს",
|
||||
"Interface": "ინტერფეისი",
|
||||
"Invalid Tag": "არასწორი ტეგი",
|
||||
"Is Model Vision Capable": "",
|
||||
"January": "იანვარი",
|
||||
"join our Discord for help.": "შეუერთდით ჩვენს Discord-ს დახმარებისთვის",
|
||||
"JSON": "JSON",
|
||||
@ -253,6 +258,7 @@
|
||||
"Made by OpenWebUI Community": "დამზადებულია OpenWebUI საზოგადოების მიერ",
|
||||
"Make sure to enclose them with": "დარწმუნდით, რომ დაურთეთ ისინი",
|
||||
"Manage LiteLLM Models": "LiteLLM მოდელების მართვა",
|
||||
"Manage Model Information": "",
|
||||
"Manage Models": "მოდელების მართვა",
|
||||
"Manage Ollama Models": "Ollama მოდელების მართვა",
|
||||
"March": "მარტივი",
|
||||
@ -272,7 +278,12 @@
|
||||
"Model '{{modelTag}}' is already in queue for downloading.": "მოდელი „{{modelTag}}“ უკვე ჩამოტვირთვის რიგშია.",
|
||||
"Model {{modelId}} not found": "მოდელი {{modelId}} ვერ მოიძებნა",
|
||||
"Model {{modelName}} already exists.": "მოდელი {{modelName}} უკვე არსებობს.",
|
||||
"Model {{modelName}} is not vision capable": "",
|
||||
"Model Description": "",
|
||||
"Model Display Name": "",
|
||||
"Model filesystem path detected. Model shortname is required for update, cannot continue.": "აღმოჩენილია მოდელის ფაილური სისტემის გზა. განახლებისთვის საჭიროა მოდელის მოკლე სახელი, გაგრძელება შეუძლებელია.",
|
||||
"Model info for {{modelName}} added successfully": "",
|
||||
"Model info for {{modelName}} deleted successfully": "",
|
||||
"Model Name": "მოდელის სახელი",
|
||||
"Model not selected": "მოდელი არ არის არჩეული",
|
||||
"Model Tag Name": "მოდელის ტეგის სახელი",
|
||||
@ -289,6 +300,7 @@
|
||||
"Name your modelfile": "თქვენი მოდელური ფაილის სახელი",
|
||||
"New Chat": "ახალი მიმოწერა",
|
||||
"New Password": "ახალი პაროლი",
|
||||
"No": "",
|
||||
"No results found": "ჩვენ ვერ პოულობით ნაპოვნი ჩაწერები",
|
||||
"No source available": "წყარო არ არის ხელმისაწვდომი",
|
||||
"Not factually correct": "არ ვეთანხმები პირდაპირ ვერც ვეთანხმები",
|
||||
@ -385,6 +397,7 @@
|
||||
"Select a model": "მოდელის არჩევა",
|
||||
"Select an Ollama instance": "Ollama ინსტანსის არჩევა",
|
||||
"Select model": "მოდელის არჩევა",
|
||||
"Selected models do not support image inputs": "",
|
||||
"Send": "გაგზავნა",
|
||||
"Send a Message": "შეტყობინების გაგზავნა",
|
||||
"Send message": "შეტყობინების გაგზავნა",
|
||||
@ -492,6 +505,7 @@
|
||||
"Workspace": "ვულერი",
|
||||
"Write a prompt suggestion (e.g. Who are you?)": "დაწერეთ მოკლე წინადადება (მაგ. ვინ ხარ?",
|
||||
"Write a summary in 50 words that summarizes [topic or keyword].": "დაწერეთ რეზიუმე 50 სიტყვით, რომელიც აჯამებს [თემას ან საკვანძო სიტყვას].",
|
||||
"Yes": "",
|
||||
"Yesterday": "აღდგენა",
|
||||
"You": "ჩემი",
|
||||
"You have no archived conversations.": "არ ხართ არქივირებული განხილვები.",
|
||||
|
@ -6,6 +6,7 @@
|
||||
"{{modelName}} is thinking...": "{{modelName}} 이(가) 생각중입니다....",
|
||||
"{{user}}'s Chats": "{{user}}의 채팅",
|
||||
"{{webUIName}} Backend Required": "{{webUIName}} 백엔드가 필요합니다.",
|
||||
"A selected model does not support image input": "",
|
||||
"a user": "사용자",
|
||||
"About": "소개",
|
||||
"Account": "계정",
|
||||
@ -31,6 +32,7 @@
|
||||
"Advanced Parameters": "고급 매개변수",
|
||||
"all": "모두",
|
||||
"All Documents": "모든 문서",
|
||||
"All selected models do not support image input, removed images": "",
|
||||
"All Users": "모든 사용자",
|
||||
"Allow": "허용",
|
||||
"Allow Chat Deletion": "채팅 삭제 허용",
|
||||
@ -115,6 +117,7 @@
|
||||
"Created at": "생성일",
|
||||
"Created At": "생성일",
|
||||
"Current Model": "현재 모델",
|
||||
"Current Models": "",
|
||||
"Current Password": "현재 비밀번호",
|
||||
"Custom": "사용자 정의",
|
||||
"Customize Ollama models for a specific purpose": "특정 목적으로 Ollama 모델 사용자 정의",
|
||||
@ -181,6 +184,7 @@
|
||||
"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 Display Name": "",
|
||||
"Enter model tag (e.g. {{modelTag}})": "모델 태그 입력(예: {{modelTag}})",
|
||||
"Enter Number of Steps (e.g. 50)": "단계 수 입력(예: 50)",
|
||||
"Enter Score": "점수 입력",
|
||||
@ -235,6 +239,7 @@
|
||||
"Input commands": "입력 명령",
|
||||
"Interface": "인터페이스",
|
||||
"Invalid Tag": "잘못된 태그",
|
||||
"Is Model Vision Capable": "",
|
||||
"January": "1월",
|
||||
"join our Discord for help.": "도움말을 보려면 Discord에 가입하세요.",
|
||||
"JSON": "JSON",
|
||||
@ -253,6 +258,7 @@
|
||||
"Made by OpenWebUI Community": "OpenWebUI 커뮤니티에서 제작",
|
||||
"Make sure to enclose them with": "다음으로 묶는 것을 잊지 마세요:",
|
||||
"Manage LiteLLM Models": "LiteLLM 모델 관리",
|
||||
"Manage Model Information": "",
|
||||
"Manage Models": "모델 관리",
|
||||
"Manage Ollama Models": "Ollama 모델 관리",
|
||||
"March": "3월",
|
||||
@ -272,7 +278,12 @@
|
||||
"Model '{{modelTag}}' is already in queue for downloading.": "모델 '{{modelTag}}'이(가) 이미 다운로드 대기열에 있습니다.",
|
||||
"Model {{modelId}} not found": "모델 {{modelId}}를 찾을 수 없습니다.",
|
||||
"Model {{modelName}} already exists.": "모델 {{modelName}}이(가) 이미 존재합니다.",
|
||||
"Model {{modelName}} is not vision capable": "",
|
||||
"Model Description": "",
|
||||
"Model Display Name": "",
|
||||
"Model filesystem path detected. Model shortname is required for update, cannot continue.": "모델 파일 시스템 경로가 감지되었습니다. 업데이트하려면 모델 단축 이름이 필요하며 계속할 수 없습니다.",
|
||||
"Model info for {{modelName}} added successfully": "",
|
||||
"Model info for {{modelName}} deleted successfully": "",
|
||||
"Model Name": "모델 이름",
|
||||
"Model not selected": "모델이 선택되지 않았습니다.",
|
||||
"Model Tag Name": "모델 태그 이름",
|
||||
@ -289,6 +300,7 @@
|
||||
"Name your modelfile": "모델파일 이름 지정",
|
||||
"New Chat": "새 채팅",
|
||||
"New Password": "새 비밀번호",
|
||||
"No": "",
|
||||
"No results found": "결과 없음",
|
||||
"No source available": "사용 가능한 소스 없음",
|
||||
"Not factually correct": "사실상 맞지 않음",
|
||||
@ -385,6 +397,7 @@
|
||||
"Select a model": "모델 선택",
|
||||
"Select an Ollama instance": "Ollama 인스턴스 선택",
|
||||
"Select model": "모델 선택",
|
||||
"Selected models do not support image inputs": "",
|
||||
"Send": "보내기",
|
||||
"Send a Message": "메시지 보내기",
|
||||
"Send message": "메시지 보내기",
|
||||
@ -492,6 +505,7 @@
|
||||
"Workspace": "워크스페이스",
|
||||
"Write a prompt suggestion (e.g. Who are you?)": "프롬프트 제안 작성 (예: 당신은 누구인가요?)",
|
||||
"Write a summary in 50 words that summarizes [topic or keyword].": "[주제 또는 키워드]에 대한 50단어 요약문 작성.",
|
||||
"Yes": "",
|
||||
"Yesterday": "어제",
|
||||
"You": "당신",
|
||||
"You have no archived conversations.": "채팅을 아카이브한 적이 없습니다.",
|
||||
|
@ -6,6 +6,7 @@
|
||||
"{{modelName}} is thinking...": "{{modelName}} is aan het denken...",
|
||||
"{{user}}'s Chats": "{{user}}'s Chats",
|
||||
"{{webUIName}} Backend Required": "{{webUIName}} Backend Verlpicht",
|
||||
"A selected model does not support image input": "",
|
||||
"a user": "een gebruiker",
|
||||
"About": "Over",
|
||||
"Account": "Account",
|
||||
@ -31,6 +32,7 @@
|
||||
"Advanced Parameters": "Geavanceerde Parameters",
|
||||
"all": "alle",
|
||||
"All Documents": "Alle Documenten",
|
||||
"All selected models do not support image input, removed images": "",
|
||||
"All Users": "Alle Gebruikers",
|
||||
"Allow": "Toestaan",
|
||||
"Allow Chat Deletion": "Sta Chat Verwijdering toe",
|
||||
@ -115,6 +117,7 @@
|
||||
"Created at": "Gemaakt op",
|
||||
"Created At": "Gemaakt op",
|
||||
"Current Model": "Huidig Model",
|
||||
"Current Models": "",
|
||||
"Current Password": "Huidig Wachtwoord",
|
||||
"Custom": "Aangepast",
|
||||
"Customize Ollama models for a specific purpose": "Pas Ollama modellen aan voor een specifiek doel",
|
||||
@ -181,6 +184,7 @@
|
||||
"Enter LiteLLM API RPM (litellm_params.rpm)": "Voeg LiteLLM API RPM toe (litellm_params.rpm)",
|
||||
"Enter LiteLLM Model (litellm_params.model)": "Voeg LiteLLM Model toe (litellm_params.model)",
|
||||
"Enter Max Tokens (litellm_params.max_tokens)": "Voeg maximum aantal tokens toe (litellm_params.max_tokens)",
|
||||
"Enter Model Display Name": "",
|
||||
"Enter model tag (e.g. {{modelTag}})": "Voeg model tag toe (Bijv. {{modelTag}})",
|
||||
"Enter Number of Steps (e.g. 50)": "Voeg aantal stappen toe (Bijv. 50)",
|
||||
"Enter Score": "Voeg score toe",
|
||||
@ -235,6 +239,7 @@
|
||||
"Input commands": "Voer commando's in",
|
||||
"Interface": "Interface",
|
||||
"Invalid Tag": "Ongeldige Tag",
|
||||
"Is Model Vision Capable": "",
|
||||
"January": "Januari",
|
||||
"join our Discord for help.": "join onze Discord voor hulp.",
|
||||
"JSON": "JSON",
|
||||
@ -253,6 +258,7 @@
|
||||
"Made by OpenWebUI Community": "Gemaakt door OpenWebUI Community",
|
||||
"Make sure to enclose them with": "Zorg ervoor dat je ze omringt met",
|
||||
"Manage LiteLLM Models": "Beheer LiteLLM Modellen",
|
||||
"Manage Model Information": "",
|
||||
"Manage Models": "Beheer Modellen",
|
||||
"Manage Ollama Models": "Beheer Ollama Modellen",
|
||||
"March": "Maart",
|
||||
@ -272,7 +278,12 @@
|
||||
"Model '{{modelTag}}' is already in queue for downloading.": "Model '{{modelTag}}' staat al in de wachtrij voor downloaden.",
|
||||
"Model {{modelId}} not found": "Model {{modelId}} niet gevonden",
|
||||
"Model {{modelName}} already exists.": "Model {{modelName}} bestaat al.",
|
||||
"Model {{modelName}} is not vision capable": "",
|
||||
"Model Description": "",
|
||||
"Model Display Name": "",
|
||||
"Model filesystem path detected. Model shortname is required for update, cannot continue.": "Model filesystem path gedetecteerd. Model shortname is vereist voor update, kan niet doorgaan.",
|
||||
"Model info for {{modelName}} added successfully": "",
|
||||
"Model info for {{modelName}} deleted successfully": "",
|
||||
"Model Name": "Model Naam",
|
||||
"Model not selected": "Model niet geselecteerd",
|
||||
"Model Tag Name": "Model Tag Naam",
|
||||
@ -289,6 +300,7 @@
|
||||
"Name your modelfile": "Benoem je modelfile",
|
||||
"New Chat": "Nieuwe Chat",
|
||||
"New Password": "Nieuw Wachtwoord",
|
||||
"No": "",
|
||||
"No results found": "Geen resultaten gevonden",
|
||||
"No source available": "Geen bron beschikbaar",
|
||||
"Not factually correct": "Feitelijk niet juist",
|
||||
@ -385,6 +397,7 @@
|
||||
"Select a model": "Selecteer een model",
|
||||
"Select an Ollama instance": "Selecteer een Ollama instantie",
|
||||
"Select model": "Selecteer een model",
|
||||
"Selected models do not support image inputs": "",
|
||||
"Send": "Verzenden",
|
||||
"Send a Message": "Stuur een Bericht",
|
||||
"Send message": "Stuur bericht",
|
||||
@ -492,6 +505,7 @@
|
||||
"Workspace": "Werkruimte",
|
||||
"Write a prompt suggestion (e.g. Who are you?)": "Schrijf een prompt suggestie (bijv. Wie ben je?)",
|
||||
"Write a summary in 50 words that summarizes [topic or keyword].": "Schrijf een samenvatting in 50 woorden die [onderwerp of trefwoord] samenvat.",
|
||||
"Yes": "",
|
||||
"Yesterday": "gisteren",
|
||||
"You": "U",
|
||||
"You have no archived conversations.": "U heeft geen gearchiveerde gesprekken.",
|
||||
|
@ -6,6 +6,7 @@
|
||||
"{{modelName}} is thinking...": "{{modelName}} ਸੋਚ ਰਿਹਾ ਹੈ...",
|
||||
"{{user}}'s Chats": "{{user}} ਦੀਆਂ ਗੱਲਾਂ",
|
||||
"{{webUIName}} Backend Required": "{{webUIName}} ਬੈਕਐਂਡ ਲੋੜੀਂਦਾ ਹੈ",
|
||||
"A selected model does not support image input": "",
|
||||
"a user": "ਇੱਕ ਉਪਭੋਗਤਾ",
|
||||
"About": "ਬਾਰੇ",
|
||||
"Account": "ਖਾਤਾ",
|
||||
@ -31,6 +32,7 @@
|
||||
"Advanced Parameters": "ਉੱਚ ਸਤਰ ਦੇ ਪੈਰਾਮੀਟਰ",
|
||||
"all": "ਸਾਰੇ",
|
||||
"All Documents": "ਸਾਰੇ ਡਾਕੂਮੈਂਟ",
|
||||
"All selected models do not support image input, removed images": "",
|
||||
"All Users": "ਸਾਰੇ ਉਪਭੋਗਤਾ",
|
||||
"Allow": "ਅਨੁਮਤੀ",
|
||||
"Allow Chat Deletion": "ਗੱਲਬਾਤ ਮਿਟਾਉਣ ਦੀ ਆਗਿਆ ਦਿਓ",
|
||||
@ -115,6 +117,7 @@
|
||||
"Created at": "ਤੇ ਬਣਾਇਆ ਗਿਆ",
|
||||
"Created At": "ਤੇ ਬਣਾਇਆ ਗਿਆ",
|
||||
"Current Model": "ਮੌਜੂਦਾ ਮਾਡਲ",
|
||||
"Current Models": "",
|
||||
"Current Password": "ਮੌਜੂਦਾ ਪਾਸਵਰਡ",
|
||||
"Custom": "ਕਸਟਮ",
|
||||
"Customize Ollama models for a specific purpose": "ਇੱਕ ਖਾਸ ਉਦੇਸ਼ ਲਈ ਓਲਾਮਾ ਮਾਡਲਾਂ ਨੂੰ ਕਸਟਮਾਈਜ਼ ਕਰੋ",
|
||||
@ -181,6 +184,7 @@
|
||||
"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 Display Name": "",
|
||||
"Enter model tag (e.g. {{modelTag}})": "ਮਾਡਲ ਟੈਗ ਦਰਜ ਕਰੋ (ਉਦਾਹਰਣ ਲਈ {{modelTag}})",
|
||||
"Enter Number of Steps (e.g. 50)": "ਕਦਮਾਂ ਦੀ ਗਿਣਤੀ ਦਰਜ ਕਰੋ (ਉਦਾਹਰਣ ਲਈ 50)",
|
||||
"Enter Score": "ਸਕੋਰ ਦਰਜ ਕਰੋ",
|
||||
@ -235,6 +239,7 @@
|
||||
"Input commands": "ਇਨਪੁਟ ਕਮਾਂਡਾਂ",
|
||||
"Interface": "ਇੰਟਰਫੇਸ",
|
||||
"Invalid Tag": "ਗਲਤ ਟੈਗ",
|
||||
"Is Model Vision Capable": "",
|
||||
"January": "ਜਨਵਰੀ",
|
||||
"join our Discord for help.": "ਮਦਦ ਲਈ ਸਾਡੇ ਡਿਸਕੋਰਡ ਵਿੱਚ ਸ਼ਾਮਲ ਹੋਵੋ।",
|
||||
"JSON": "JSON",
|
||||
@ -253,6 +258,7 @@
|
||||
"Made by OpenWebUI Community": "ਓਪਨਵੈਬਯੂਆਈ ਕਮਿਊਨਿਟੀ ਦੁਆਰਾ ਬਣਾਇਆ ਗਿਆ",
|
||||
"Make sure to enclose them with": "ਸੁਨਿਸ਼ਚਿਤ ਕਰੋ ਕਿ ਉਨ੍ਹਾਂ ਨੂੰ ਘੇਰੋ",
|
||||
"Manage LiteLLM Models": "LiteLLM ਮਾਡਲਾਂ ਦਾ ਪ੍ਰਬੰਧਨ ਕਰੋ",
|
||||
"Manage Model Information": "",
|
||||
"Manage Models": "ਮਾਡਲਾਂ ਦਾ ਪ੍ਰਬੰਧਨ ਕਰੋ",
|
||||
"Manage Ollama Models": "ਓਲਾਮਾ ਮਾਡਲਾਂ ਦਾ ਪ੍ਰਬੰਧਨ ਕਰੋ",
|
||||
"March": "ਮਾਰਚ",
|
||||
@ -272,7 +278,12 @@
|
||||
"Model '{{modelTag}}' is already in queue for downloading.": "ਮਾਡਲ '{{modelTag}}' ਪਹਿਲਾਂ ਹੀ ਡਾਊਨਲੋਡ ਲਈ ਕਤਾਰ ਵਿੱਚ ਹੈ।",
|
||||
"Model {{modelId}} not found": "ਮਾਡਲ {{modelId}} ਨਹੀਂ ਮਿਲਿਆ",
|
||||
"Model {{modelName}} already exists.": "ਮਾਡਲ {{modelName}} ਪਹਿਲਾਂ ਹੀ ਮੌਜੂਦ ਹੈ।",
|
||||
"Model {{modelName}} is not vision capable": "",
|
||||
"Model Description": "",
|
||||
"Model Display Name": "",
|
||||
"Model filesystem path detected. Model shortname is required for update, cannot continue.": "ਮਾਡਲ ਫਾਈਲਸਿਸਟਮ ਪੱਥ ਪਾਇਆ ਗਿਆ। ਅੱਪਡੇਟ ਲਈ ਮਾਡਲ ਸ਼ੌਰਟਨੇਮ ਦੀ ਲੋੜ ਹੈ, ਜਾਰੀ ਨਹੀਂ ਰੱਖ ਸਕਦੇ।",
|
||||
"Model info for {{modelName}} added successfully": "",
|
||||
"Model info for {{modelName}} deleted successfully": "",
|
||||
"Model Name": "ਮਾਡਲ ਨਾਮ",
|
||||
"Model not selected": "ਮਾਡਲ ਚੁਣਿਆ ਨਹੀਂ ਗਿਆ",
|
||||
"Model Tag Name": "ਮਾਡਲ ਟੈਗ ਨਾਮ",
|
||||
@ -289,6 +300,7 @@
|
||||
"Name your modelfile": "ਆਪਣੀ ਮਾਡਲਫਾਈਲ ਦਾ ਨਾਮ ਰੱਖੋ",
|
||||
"New Chat": "ਨਵੀਂ ਗੱਲਬਾਤ",
|
||||
"New Password": "ਨਵਾਂ ਪਾਸਵਰਡ",
|
||||
"No": "",
|
||||
"No results found": "ਕੋਈ ਨਤੀਜੇ ਨਹੀਂ ਮਿਲੇ",
|
||||
"No source available": "ਕੋਈ ਸਰੋਤ ਉਪਲਬਧ ਨਹੀਂ",
|
||||
"Not factually correct": "ਤੱਥਕ ਰੂਪ ਵਿੱਚ ਸਹੀ ਨਹੀਂ",
|
||||
@ -385,6 +397,7 @@
|
||||
"Select a model": "ਇੱਕ ਮਾਡਲ ਚੁਣੋ",
|
||||
"Select an Ollama instance": "ਇੱਕ ਓਲਾਮਾ ਇੰਸਟੈਂਸ ਚੁਣੋ",
|
||||
"Select model": "ਮਾਡਲ ਚੁਣੋ",
|
||||
"Selected models do not support image inputs": "",
|
||||
"Send": "ਭੇਜੋ",
|
||||
"Send a Message": "ਇੱਕ ਸੁਨੇਹਾ ਭੇਜੋ",
|
||||
"Send message": "ਸੁਨੇਹਾ ਭੇਜੋ",
|
||||
@ -492,6 +505,7 @@
|
||||
"Workspace": "ਕਾਰਜਸਥਲ",
|
||||
"Write a prompt suggestion (e.g. Who are you?)": "ਇੱਕ ਪ੍ਰੰਪਟ ਸੁਝਾਅ ਲਿਖੋ (ਉਦਾਹਰਣ ਲਈ ਤੁਸੀਂ ਕੌਣ ਹੋ?)",
|
||||
"Write a summary in 50 words that summarizes [topic or keyword].": "50 ਸ਼ਬਦਾਂ ਵਿੱਚ ਇੱਕ ਸੰਖੇਪ ਲਿਖੋ ਜੋ [ਵਿਸ਼ਾ ਜਾਂ ਕੁੰਜੀ ਸ਼ਬਦ] ਨੂੰ ਸੰਖੇਪ ਕਰਦਾ ਹੈ।",
|
||||
"Yes": "",
|
||||
"Yesterday": "ਕੱਲ੍ਹ",
|
||||
"You": "ਤੁਸੀਂ",
|
||||
"You have no archived conversations.": "ਤੁਹਾਡੇ ਕੋਲ ਕੋਈ ਆਰਕਾਈਵ ਕੀਤੀਆਂ ਗੱਲਾਂ ਨਹੀਂ ਹਨ।",
|
||||
|
@ -6,6 +6,7 @@
|
||||
"{{modelName}} is thinking...": "{{modelName}} myśli...",
|
||||
"{{user}}'s Chats": "{{user}} - czaty",
|
||||
"{{webUIName}} Backend Required": "Backend {{webUIName}} wymagane",
|
||||
"A selected model does not support image input": "",
|
||||
"a user": "użytkownik",
|
||||
"About": "O nas",
|
||||
"Account": "Konto",
|
||||
@ -31,6 +32,7 @@
|
||||
"Advanced Parameters": "Zaawansowane parametry",
|
||||
"all": "wszyscy",
|
||||
"All Documents": "Wszystkie dokumenty",
|
||||
"All selected models do not support image input, removed images": "",
|
||||
"All Users": "Wszyscy użytkownicy",
|
||||
"Allow": "Pozwól",
|
||||
"Allow Chat Deletion": "Pozwól na usuwanie czatu",
|
||||
@ -115,6 +117,7 @@
|
||||
"Created at": "Utworzono o",
|
||||
"Created At": "Utworzono o",
|
||||
"Current Model": "Bieżący model",
|
||||
"Current Models": "",
|
||||
"Current Password": "Bieżące hasło",
|
||||
"Custom": "Niestandardowy",
|
||||
"Customize Ollama models for a specific purpose": "Dostosuj modele Ollama do określonego celu",
|
||||
@ -181,6 +184,7 @@
|
||||
"Enter LiteLLM API RPM (litellm_params.rpm)": "Wprowadź API LiteLLM RPM(litellm_params.rpm)",
|
||||
"Enter LiteLLM Model (litellm_params.model)": "Wprowadź model LiteLLM (litellm_params.model)",
|
||||
"Enter Max Tokens (litellm_params.max_tokens)": "Wprowadź maksymalną liczbę tokenów (litellm_params.max_tokens)",
|
||||
"Enter Model Display Name": "",
|
||||
"Enter model tag (e.g. {{modelTag}})": "Wprowadź tag modelu (np. {{modelTag}})",
|
||||
"Enter Number of Steps (e.g. 50)": "Wprowadź liczbę kroków (np. 50)",
|
||||
"Enter Score": "Wprowadź wynik",
|
||||
@ -235,6 +239,7 @@
|
||||
"Input commands": "Wprowadź komendy",
|
||||
"Interface": "Interfejs",
|
||||
"Invalid Tag": "Nieprawidłowy tag",
|
||||
"Is Model Vision Capable": "",
|
||||
"January": "Styczeń",
|
||||
"join our Discord for help.": "Dołącz do naszego Discorda po pomoc.",
|
||||
"JSON": "JSON",
|
||||
@ -253,6 +258,7 @@
|
||||
"Made by OpenWebUI Community": "Stworzone przez społeczność OpenWebUI",
|
||||
"Make sure to enclose them with": "Upewnij się, że są one zamknięte w",
|
||||
"Manage LiteLLM Models": "Zarządzaj modelami LiteLLM",
|
||||
"Manage Model Information": "",
|
||||
"Manage Models": "Zarządzaj modelami",
|
||||
"Manage Ollama Models": "Zarządzaj modelami Ollama",
|
||||
"March": "Marzec",
|
||||
@ -272,7 +278,12 @@
|
||||
"Model '{{modelTag}}' is already in queue for downloading.": "Model '{{modelTag}}' jest już w kolejce do pobrania.",
|
||||
"Model {{modelId}} not found": "Model {{modelId}} nie został znaleziony",
|
||||
"Model {{modelName}} already exists.": "Model {{modelName}} już istnieje.",
|
||||
"Model {{modelName}} is not vision capable": "",
|
||||
"Model Description": "",
|
||||
"Model Display Name": "",
|
||||
"Model filesystem path detected. Model shortname is required for update, cannot continue.": "Wykryto ścieżkę systemu plików modelu. Wymagana jest krótka nazwa modelu do aktualizacji, nie można kontynuować.",
|
||||
"Model info for {{modelName}} added successfully": "",
|
||||
"Model info for {{modelName}} deleted successfully": "",
|
||||
"Model Name": "Nazwa modelu",
|
||||
"Model not selected": "Model nie został wybrany",
|
||||
"Model Tag Name": "Nazwa tagu modelu",
|
||||
@ -289,6 +300,7 @@
|
||||
"Name your modelfile": "Nadaj nazwę swojemu plikowi modelu",
|
||||
"New Chat": "Nowy czat",
|
||||
"New Password": "Nowe hasło",
|
||||
"No": "",
|
||||
"No results found": "Nie znaleziono rezultatów",
|
||||
"No source available": "Źródło nie dostępne",
|
||||
"Not factually correct": "Nie zgodne z faktami",
|
||||
@ -385,6 +397,7 @@
|
||||
"Select a model": "Wybierz model",
|
||||
"Select an Ollama instance": "Wybierz instancję Ollama",
|
||||
"Select model": "Wybierz model",
|
||||
"Selected models do not support image inputs": "",
|
||||
"Send": "Wyślij",
|
||||
"Send a Message": "Wyślij Wiadomość",
|
||||
"Send message": "Wyślij wiadomość",
|
||||
@ -492,6 +505,7 @@
|
||||
"Workspace": "Obszar roboczy",
|
||||
"Write a prompt suggestion (e.g. Who are you?)": "Napisz sugestię do polecenia (np. Kim jesteś?)",
|
||||
"Write a summary in 50 words that summarizes [topic or keyword].": "Napisz podsumowanie w 50 słowach, które podsumowuje [temat lub słowo kluczowe].",
|
||||
"Yes": "",
|
||||
"Yesterday": "Wczoraj",
|
||||
"You": "Ty",
|
||||
"You have no archived conversations.": "Nie masz zarchiwizowanych rozmów.",
|
||||
|
@ -6,6 +6,7 @@
|
||||
"{{modelName}} is thinking...": "{{modelName}} está pensando...",
|
||||
"{{user}}'s Chats": "{{user}}'s Chats",
|
||||
"{{webUIName}} Backend Required": "{{webUIName}} Backend Necessário",
|
||||
"A selected model does not support image input": "",
|
||||
"a user": "um usuário",
|
||||
"About": "Sobre",
|
||||
"Account": "Conta",
|
||||
@ -31,6 +32,7 @@
|
||||
"Advanced Parameters": "Parâmetros Avançados",
|
||||
"all": "todos",
|
||||
"All Documents": "Todos os Documentos",
|
||||
"All selected models do not support image input, removed images": "",
|
||||
"All Users": "Todos os Usuários",
|
||||
"Allow": "Permitir",
|
||||
"Allow Chat Deletion": "Permitir Exclusão de Bate-papo",
|
||||
@ -115,6 +117,7 @@
|
||||
"Created at": "Criado em",
|
||||
"Created At": "Criado em",
|
||||
"Current Model": "Modelo Atual",
|
||||
"Current Models": "",
|
||||
"Current Password": "Senha Atual",
|
||||
"Custom": "Personalizado",
|
||||
"Customize Ollama models for a specific purpose": "Personalize os modelos Ollama para um propósito específico",
|
||||
@ -181,6 +184,7 @@
|
||||
"Enter LiteLLM API RPM (litellm_params.rpm)": "Digite o RPM da API LiteLLM (litellm_params.rpm)",
|
||||
"Enter LiteLLM Model (litellm_params.model)": "Digite o Modelo LiteLLM (litellm_params.model)",
|
||||
"Enter Max Tokens (litellm_params.max_tokens)": "Digite o Máximo de Tokens (litellm_params.max_tokens)",
|
||||
"Enter Model Display Name": "",
|
||||
"Enter model tag (e.g. {{modelTag}})": "Digite a tag do modelo (por exemplo, {{modelTag}})",
|
||||
"Enter Number of Steps (e.g. 50)": "Digite o Número de Etapas (por exemplo, 50)",
|
||||
"Enter Score": "Digite a Pontuação",
|
||||
@ -235,6 +239,7 @@
|
||||
"Input commands": "Comandos de entrada",
|
||||
"Interface": "Interface",
|
||||
"Invalid Tag": "Etiqueta Inválida",
|
||||
"Is Model Vision Capable": "",
|
||||
"January": "Janeiro",
|
||||
"join our Discord for help.": "junte-se ao nosso Discord para obter ajuda.",
|
||||
"JSON": "JSON",
|
||||
@ -253,6 +258,7 @@
|
||||
"Made by OpenWebUI Community": "Feito pela Comunidade OpenWebUI",
|
||||
"Make sure to enclose them with": "Certifique-se de colocá-los entre",
|
||||
"Manage LiteLLM Models": "Gerenciar Modelos LiteLLM",
|
||||
"Manage Model Information": "",
|
||||
"Manage Models": "Gerenciar Modelos",
|
||||
"Manage Ollama Models": "Gerenciar Modelos Ollama",
|
||||
"March": "Março",
|
||||
@ -272,7 +278,12 @@
|
||||
"Model '{{modelTag}}' is already in queue for downloading.": "O modelo '{{modelTag}}' já está na fila para download.",
|
||||
"Model {{modelId}} not found": "Modelo {{modelId}} não encontrado",
|
||||
"Model {{modelName}} already exists.": "O modelo {{modelName}} já existe.",
|
||||
"Model {{modelName}} is not vision capable": "",
|
||||
"Model Description": "",
|
||||
"Model Display Name": "",
|
||||
"Model filesystem path detected. Model shortname is required for update, cannot continue.": "Otkrivena putanja datoteke modela. Skraćeno ime modela je potrebno za ažuriranje, ne može se nastaviti.",
|
||||
"Model info for {{modelName}} added successfully": "",
|
||||
"Model info for {{modelName}} deleted successfully": "",
|
||||
"Model Name": "Nome do Modelo",
|
||||
"Model not selected": "Modelo não selecionado",
|
||||
"Model Tag Name": "Nome da Tag do Modelo",
|
||||
@ -289,6 +300,7 @@
|
||||
"Name your modelfile": "Nomeie seu arquivo de modelo",
|
||||
"New Chat": "Novo Bate-papo",
|
||||
"New Password": "Nova Senha",
|
||||
"No": "",
|
||||
"No results found": "Nenhum resultado encontrado",
|
||||
"No source available": "Nenhuma fonte disponível",
|
||||
"Not factually correct": "Não é correto em termos factuais",
|
||||
@ -385,6 +397,7 @@
|
||||
"Select a model": "Selecione um modelo",
|
||||
"Select an Ollama instance": "Selecione uma instância Ollama",
|
||||
"Select model": "Selecione um modelo",
|
||||
"Selected models do not support image inputs": "",
|
||||
"Send": "Enviar",
|
||||
"Send a Message": "Enviar uma Mensagem",
|
||||
"Send message": "Enviar mensagem",
|
||||
@ -492,6 +505,7 @@
|
||||
"Workspace": "Espaço de trabalho",
|
||||
"Write a prompt suggestion (e.g. Who are you?)": "Escreva uma sugestão de prompt (por exemplo, Quem é você?)",
|
||||
"Write a summary in 50 words that summarizes [topic or keyword].": "Escreva um resumo em 50 palavras que resuma [tópico ou palavra-chave].",
|
||||
"Yes": "",
|
||||
"Yesterday": "Ontem",
|
||||
"You": "Você",
|
||||
"You have no archived conversations.": "Você não tem conversas arquivadas.",
|
||||
|
@ -6,6 +6,7 @@
|
||||
"{{modelName}} is thinking...": "{{modelName}} está pensando...",
|
||||
"{{user}}'s Chats": "{{user}}'s Chats",
|
||||
"{{webUIName}} Backend Required": "{{webUIName}} Backend Necessário",
|
||||
"A selected model does not support image input": "",
|
||||
"a user": "um usuário",
|
||||
"About": "Sobre",
|
||||
"Account": "Conta",
|
||||
@ -31,6 +32,7 @@
|
||||
"Advanced Parameters": "Parâmetros Avançados",
|
||||
"all": "todos",
|
||||
"All Documents": "Todos os Documentos",
|
||||
"All selected models do not support image input, removed images": "",
|
||||
"All Users": "Todos os Usuários",
|
||||
"Allow": "Permitir",
|
||||
"Allow Chat Deletion": "Permitir Exclusão de Bate-papo",
|
||||
@ -115,6 +117,7 @@
|
||||
"Created at": "Criado em",
|
||||
"Created At": "Criado em",
|
||||
"Current Model": "Modelo Atual",
|
||||
"Current Models": "",
|
||||
"Current Password": "Senha Atual",
|
||||
"Custom": "Personalizado",
|
||||
"Customize Ollama models for a specific purpose": "Personalize os modelos Ollama para um propósito específico",
|
||||
@ -181,6 +184,7 @@
|
||||
"Enter LiteLLM API RPM (litellm_params.rpm)": "Digite o RPM da API LiteLLM (litellm_params.rpm)",
|
||||
"Enter LiteLLM Model (litellm_params.model)": "Digite o Modelo LiteLLM (litellm_params.model)",
|
||||
"Enter Max Tokens (litellm_params.max_tokens)": "Digite o Máximo de Tokens (litellm_params.max_tokens)",
|
||||
"Enter Model Display Name": "",
|
||||
"Enter model tag (e.g. {{modelTag}})": "Digite a tag do modelo (por exemplo, {{modelTag}})",
|
||||
"Enter Number of Steps (e.g. 50)": "Digite o Número de Etapas (por exemplo, 50)",
|
||||
"Enter Score": "Digite a Pontuação",
|
||||
@ -235,6 +239,7 @@
|
||||
"Input commands": "Comandos de entrada",
|
||||
"Interface": "Interface",
|
||||
"Invalid Tag": "Etiqueta Inválida",
|
||||
"Is Model Vision Capable": "",
|
||||
"January": "Janeiro",
|
||||
"join our Discord for help.": "junte-se ao nosso Discord para obter ajuda.",
|
||||
"JSON": "JSON",
|
||||
@ -253,6 +258,7 @@
|
||||
"Made by OpenWebUI Community": "Feito pela Comunidade OpenWebUI",
|
||||
"Make sure to enclose them with": "Certifique-se de colocá-los entre",
|
||||
"Manage LiteLLM Models": "Gerenciar Modelos LiteLLM",
|
||||
"Manage Model Information": "",
|
||||
"Manage Models": "Gerenciar Modelos",
|
||||
"Manage Ollama Models": "Gerenciar Modelos Ollama",
|
||||
"March": "Março",
|
||||
@ -272,7 +278,12 @@
|
||||
"Model '{{modelTag}}' is already in queue for downloading.": "O modelo '{{modelTag}}' já está na fila para download.",
|
||||
"Model {{modelId}} not found": "Modelo {{modelId}} não encontrado",
|
||||
"Model {{modelName}} already exists.": "O modelo {{modelName}} já existe.",
|
||||
"Model {{modelName}} is not vision capable": "",
|
||||
"Model Description": "",
|
||||
"Model Display Name": "",
|
||||
"Model filesystem path detected. Model shortname is required for update, cannot continue.": "Caminho do sistema de arquivos do modelo detectado. É necessário o nome curto do modelo para atualização, não é possível continuar.",
|
||||
"Model info for {{modelName}} added successfully": "",
|
||||
"Model info for {{modelName}} deleted successfully": "",
|
||||
"Model Name": "Nome do Modelo",
|
||||
"Model not selected": "Modelo não selecionado",
|
||||
"Model Tag Name": "Nome da Tag do Modelo",
|
||||
@ -289,6 +300,7 @@
|
||||
"Name your modelfile": "Nomeie seu arquivo de modelo",
|
||||
"New Chat": "Novo Bate-papo",
|
||||
"New Password": "Nova Senha",
|
||||
"No": "",
|
||||
"No results found": "Nenhum resultado encontrado",
|
||||
"No source available": "Nenhuma fonte disponível",
|
||||
"Not factually correct": "Não é correto em termos factuais",
|
||||
@ -385,6 +397,7 @@
|
||||
"Select a model": "Selecione um modelo",
|
||||
"Select an Ollama instance": "Selecione uma instância Ollama",
|
||||
"Select model": "Selecione um modelo",
|
||||
"Selected models do not support image inputs": "",
|
||||
"Send": "Enviar",
|
||||
"Send a Message": "Enviar uma Mensagem",
|
||||
"Send message": "Enviar mensagem",
|
||||
@ -492,6 +505,7 @@
|
||||
"Workspace": "Espaço de Trabalho",
|
||||
"Write a prompt suggestion (e.g. Who are you?)": "Escreva uma sugestão de prompt (por exemplo, Quem é você?)",
|
||||
"Write a summary in 50 words that summarizes [topic or keyword].": "Escreva um resumo em 50 palavras que resuma [tópico ou palavra-chave].",
|
||||
"Yes": "",
|
||||
"Yesterday": "Ontem",
|
||||
"You": "Você",
|
||||
"You have no archived conversations.": "Você não tem bate-papos arquivados.",
|
||||
|
@ -6,6 +6,7 @@
|
||||
"{{modelName}} is thinking...": "{{modelName}} думает...",
|
||||
"{{user}}'s Chats": "{{user}} чаты",
|
||||
"{{webUIName}} Backend Required": "{{webUIName}} бэкенд требуемый",
|
||||
"A selected model does not support image input": "",
|
||||
"a user": "пользователь",
|
||||
"About": "Об",
|
||||
"Account": "Аккаунт",
|
||||
@ -31,6 +32,7 @@
|
||||
"Advanced Parameters": "Расширенные Параметры",
|
||||
"all": "всё",
|
||||
"All Documents": "Все документы",
|
||||
"All selected models do not support image input, removed images": "",
|
||||
"All Users": "Все пользователи",
|
||||
"Allow": "Разрешить",
|
||||
"Allow Chat Deletion": "Дозволять удаление чат",
|
||||
@ -115,6 +117,7 @@
|
||||
"Created at": "Создано в",
|
||||
"Created At": "Создано в",
|
||||
"Current Model": "Текущая модель",
|
||||
"Current Models": "",
|
||||
"Current Password": "Текущий пароль",
|
||||
"Custom": "Пользовательский",
|
||||
"Customize Ollama models for a specific purpose": "Настроить модели Ollama для конкретной цели",
|
||||
@ -181,6 +184,7 @@
|
||||
"Enter LiteLLM API RPM (litellm_params.rpm)": "Введите RPM API LiteLLM (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 Display Name": "",
|
||||
"Enter model tag (e.g. {{modelTag}})": "Введите тег модели (например, {{modelTag}})",
|
||||
"Enter Number of Steps (e.g. 50)": "Введите количество шагов (например, 50)",
|
||||
"Enter Score": "Введите оценку",
|
||||
@ -235,6 +239,7 @@
|
||||
"Input commands": "Введите команды",
|
||||
"Interface": "Интерфейс",
|
||||
"Invalid Tag": "Недопустимый тег",
|
||||
"Is Model Vision Capable": "",
|
||||
"January": "Январь",
|
||||
"join our Discord for help.": "присоединяйтесь к нашему Discord для помощи.",
|
||||
"JSON": "JSON",
|
||||
@ -253,6 +258,7 @@
|
||||
"Made by OpenWebUI Community": "Сделано сообществом OpenWebUI",
|
||||
"Make sure to enclose them with": "Убедитесь, что они заключены в",
|
||||
"Manage LiteLLM Models": "Управление моделями LiteLLM",
|
||||
"Manage Model Information": "",
|
||||
"Manage Models": "Управление моделями",
|
||||
"Manage Ollama Models": "Управление моделями Ollama",
|
||||
"March": "Март",
|
||||
@ -272,7 +278,12 @@
|
||||
"Model '{{modelTag}}' is already in queue for downloading.": "Модель '{{modelTag}}' уже находится в очереди на загрузку.",
|
||||
"Model {{modelId}} not found": "Модель {{modelId}} не найдена",
|
||||
"Model {{modelName}} already exists.": "Модель {{modelName}} уже существует.",
|
||||
"Model {{modelName}} is not vision capable": "",
|
||||
"Model Description": "",
|
||||
"Model Display Name": "",
|
||||
"Model filesystem path detected. Model shortname is required for update, cannot continue.": "Модель файловой системы обнаружена. Требуется имя тега модели для обновления, не удается продолжить.",
|
||||
"Model info for {{modelName}} added successfully": "",
|
||||
"Model info for {{modelName}} deleted successfully": "",
|
||||
"Model Name": "Имя модели",
|
||||
"Model not selected": "Модель не выбрана",
|
||||
"Model Tag Name": "Имя тега модели",
|
||||
@ -289,6 +300,7 @@
|
||||
"Name your modelfile": "Назовите свой файл модели",
|
||||
"New Chat": "Новый чат",
|
||||
"New Password": "Новый пароль",
|
||||
"No": "",
|
||||
"No results found": "Результатов не найдено",
|
||||
"No source available": "Нет доступных источников",
|
||||
"Not factually correct": "Не фактически правильно",
|
||||
@ -385,6 +397,7 @@
|
||||
"Select a model": "Выберите модель",
|
||||
"Select an Ollama instance": "Выберите экземпляр Ollama",
|
||||
"Select model": "Выберите модель",
|
||||
"Selected models do not support image inputs": "",
|
||||
"Send": "Отправить",
|
||||
"Send a Message": "Отправить сообщение",
|
||||
"Send message": "Отправить сообщение",
|
||||
@ -492,6 +505,7 @@
|
||||
"Workspace": "Рабочая область",
|
||||
"Write a prompt suggestion (e.g. Who are you?)": "Напишите предложение промпта (например, Кто вы?)",
|
||||
"Write a summary in 50 words that summarizes [topic or keyword].": "Напишите резюме в 50 словах, которое кратко описывает [тему или ключевое слово].",
|
||||
"Yes": "",
|
||||
"Yesterday": "Вчера",
|
||||
"You": "Вы",
|
||||
"You have no archived conversations.": "У вас нет архивированных бесед.",
|
||||
|
@ -6,6 +6,7 @@
|
||||
"{{modelName}} is thinking...": "{{modelName}} размишља...",
|
||||
"{{user}}'s Chats": "Ћаскања корисника {{user}}",
|
||||
"{{webUIName}} Backend Required": "Захтева се {{webUIName}} позадинац",
|
||||
"A selected model does not support image input": "",
|
||||
"a user": "корисник",
|
||||
"About": "О нама",
|
||||
"Account": "Налог",
|
||||
@ -31,6 +32,7 @@
|
||||
"Advanced Parameters": "Напредни параметри",
|
||||
"all": "сви",
|
||||
"All Documents": "Сви документи",
|
||||
"All selected models do not support image input, removed images": "",
|
||||
"All Users": "Сви корисници",
|
||||
"Allow": "Дозволи",
|
||||
"Allow Chat Deletion": "Дозволи брисање ћаскања",
|
||||
@ -115,6 +117,7 @@
|
||||
"Created at": "Направљено у",
|
||||
"Created At": "Направљено у",
|
||||
"Current Model": "Тренутни модел",
|
||||
"Current Models": "",
|
||||
"Current Password": "Тренутна лозинка",
|
||||
"Custom": "Прилагођено",
|
||||
"Customize Ollama models for a specific purpose": "Прилагоди Ollama моделе за специфичну намену",
|
||||
@ -181,6 +184,7 @@
|
||||
"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 Display Name": "",
|
||||
"Enter model tag (e.g. {{modelTag}})": "Унесите ознаку модела (нпр. {{modelTag}})",
|
||||
"Enter Number of Steps (e.g. 50)": "Унесите број корака (нпр. 50)",
|
||||
"Enter Score": "Унесите резултат",
|
||||
@ -235,6 +239,7 @@
|
||||
"Input commands": "Унеси наредбе",
|
||||
"Interface": "Изглед",
|
||||
"Invalid Tag": "Неисправна ознака",
|
||||
"Is Model Vision Capable": "",
|
||||
"January": "Јануар",
|
||||
"join our Discord for help.": "придружите се нашем Дискорду за помоћ.",
|
||||
"JSON": "JSON",
|
||||
@ -253,6 +258,7 @@
|
||||
"Made by OpenWebUI Community": "Израдила OpenWebUI заједница",
|
||||
"Make sure to enclose them with": "Уверите се да их затворите са",
|
||||
"Manage LiteLLM Models": "Управљај LiteLLM моделима",
|
||||
"Manage Model Information": "",
|
||||
"Manage Models": "Управљај моделима",
|
||||
"Manage Ollama Models": "Управљај Ollama моделима",
|
||||
"March": "Март",
|
||||
@ -272,7 +278,12 @@
|
||||
"Model '{{modelTag}}' is already in queue for downloading.": "Модел „{{modelTag}}“ је већ у реду за преузимање.",
|
||||
"Model {{modelId}} not found": "Модел {{modelId}} није пронађен",
|
||||
"Model {{modelName}} already exists.": "Модел {{modelName}} већ постоји.",
|
||||
"Model {{modelName}} is not vision capable": "",
|
||||
"Model Description": "",
|
||||
"Model Display Name": "",
|
||||
"Model filesystem path detected. Model shortname is required for update, cannot continue.": "Откривена путања система датотека модела. За ажурирање је потребан кратак назив модела, не може се наставити.",
|
||||
"Model info for {{modelName}} added successfully": "",
|
||||
"Model info for {{modelName}} deleted successfully": "",
|
||||
"Model Name": "Назив модела",
|
||||
"Model not selected": "Модел није изабран",
|
||||
"Model Tag Name": "Назив ознаке модела",
|
||||
@ -289,6 +300,7 @@
|
||||
"Name your modelfile": "Назовите вашу модел-датотеку",
|
||||
"New Chat": "Ново ћаскање",
|
||||
"New Password": "Нова лозинка",
|
||||
"No": "",
|
||||
"No results found": "Нема резултата",
|
||||
"No source available": "Нема доступног извора",
|
||||
"Not factually correct": "Није чињенично тачно",
|
||||
@ -385,6 +397,7 @@
|
||||
"Select a model": "Изабери модел",
|
||||
"Select an Ollama instance": "Изабери Ollama инстанцу",
|
||||
"Select model": "Изабери модел",
|
||||
"Selected models do not support image inputs": "",
|
||||
"Send": "Пошаљи",
|
||||
"Send a Message": "Пошаљи поруку",
|
||||
"Send message": "Пошаљи поруку",
|
||||
@ -492,6 +505,7 @@
|
||||
"Workspace": "Радни простор",
|
||||
"Write a prompt suggestion (e.g. Who are you?)": "Напишите предлог упита (нпр. „ко си ти?“)",
|
||||
"Write a summary in 50 words that summarizes [topic or keyword].": "Напишите сажетак у 50 речи који резимира [тему или кључну реч].",
|
||||
"Yes": "",
|
||||
"Yesterday": "Јуче",
|
||||
"You": "Ти",
|
||||
"You have no archived conversations.": "Немате архивиране разговоре.",
|
||||
|
@ -6,6 +6,7 @@
|
||||
"{{modelName}} is thinking...": "{{modelName}} tänker...",
|
||||
"{{user}}'s Chats": "{{user}}s Chats",
|
||||
"{{webUIName}} Backend Required": "{{webUIName}} Backend krävs",
|
||||
"A selected model does not support image input": "",
|
||||
"a user": "en användare",
|
||||
"About": "Om",
|
||||
"Account": "Konto",
|
||||
@ -31,6 +32,7 @@
|
||||
"Advanced Parameters": "Avancerade parametrar",
|
||||
"all": "alla",
|
||||
"All Documents": "Alla dokument",
|
||||
"All selected models do not support image input, removed images": "",
|
||||
"All Users": "Alla användare",
|
||||
"Allow": "Tillåt",
|
||||
"Allow Chat Deletion": "Tillåt chattborttagning",
|
||||
@ -115,6 +117,7 @@
|
||||
"Created at": "Skapad",
|
||||
"Created At": "Skapad",
|
||||
"Current Model": "Aktuell modell",
|
||||
"Current Models": "",
|
||||
"Current Password": "Nuvarande lösenord",
|
||||
"Custom": "Anpassad",
|
||||
"Customize Ollama models for a specific purpose": "Anpassa Ollama-modeller för ett specifikt ändamål",
|
||||
@ -181,6 +184,7 @@
|
||||
"Enter LiteLLM API RPM (litellm_params.rpm)": "Ange LiteLLM API RPM (litellm_params.rpm)",
|
||||
"Enter LiteLLM Model (litellm_params.model)": "Ange LiteLLM-modell (litellm_params.model)",
|
||||
"Enter Max Tokens (litellm_params.max_tokens)": "Ange max antal tokens (litellm_params.max_tokens)",
|
||||
"Enter Model Display Name": "",
|
||||
"Enter model tag (e.g. {{modelTag}})": "Ange modelltagg (t.ex. {{modelTag}})",
|
||||
"Enter Number of Steps (e.g. 50)": "Ange antal steg (t.ex. 50)",
|
||||
"Enter Score": "Ange poäng",
|
||||
@ -235,6 +239,7 @@
|
||||
"Input commands": "Indatakommandon",
|
||||
"Interface": "Gränssnitt",
|
||||
"Invalid Tag": "Ogiltig tagg",
|
||||
"Is Model Vision Capable": "",
|
||||
"January": "januar",
|
||||
"join our Discord for help.": "gå med i vår Discord för hjälp.",
|
||||
"JSON": "JSON",
|
||||
@ -253,6 +258,7 @@
|
||||
"Made by OpenWebUI Community": "Skapad av OpenWebUI Community",
|
||||
"Make sure to enclose them with": "Se till att bifoga dem med",
|
||||
"Manage LiteLLM Models": "Hantera LiteLLM-modeller",
|
||||
"Manage Model Information": "",
|
||||
"Manage Models": "Hantera modeller",
|
||||
"Manage Ollama Models": "Hantera Ollama-modeller",
|
||||
"March": "mars",
|
||||
@ -272,7 +278,12 @@
|
||||
"Model '{{modelTag}}' is already in queue for downloading.": "Modellen '{{modelTag}}' är redan i kö för nedladdning.",
|
||||
"Model {{modelId}} not found": "Modell {{modelId}} hittades inte",
|
||||
"Model {{modelName}} already exists.": "Modellen {{modelName}} finns redan.",
|
||||
"Model {{modelName}} is not vision capable": "",
|
||||
"Model Description": "",
|
||||
"Model Display Name": "",
|
||||
"Model filesystem path detected. Model shortname is required for update, cannot continue.": "Modellens filsystemväg upptäckt. Modellens kortnamn krävs för uppdatering, kan inte fortsätta.",
|
||||
"Model info for {{modelName}} added successfully": "",
|
||||
"Model info for {{modelName}} deleted successfully": "",
|
||||
"Model Name": "Modellnamn",
|
||||
"Model not selected": "Modell inte vald",
|
||||
"Model Tag Name": "Modelltaggnamn",
|
||||
@ -289,6 +300,7 @@
|
||||
"Name your modelfile": "Namnge din modelfil",
|
||||
"New Chat": "Ny chatt",
|
||||
"New Password": "Nytt lösenord",
|
||||
"No": "",
|
||||
"No results found": "Inga resultat hittades",
|
||||
"No source available": "Ingen tilgjengelig kilde",
|
||||
"Not factually correct": "Inte faktiskt korrekt",
|
||||
@ -385,6 +397,7 @@
|
||||
"Select a model": "Välj en modell",
|
||||
"Select an Ollama instance": "Välj en Ollama-instans",
|
||||
"Select model": "Välj en modell",
|
||||
"Selected models do not support image inputs": "",
|
||||
"Send": "Skicka",
|
||||
"Send a Message": "Skicka ett meddelande",
|
||||
"Send message": "Skicka meddelande",
|
||||
@ -492,6 +505,7 @@
|
||||
"Workspace": "arbetsyta",
|
||||
"Write a prompt suggestion (e.g. Who are you?)": "Skriv ett förslag (t.ex. Vem är du?)",
|
||||
"Write a summary in 50 words that summarizes [topic or keyword].": "Skriv en sammanfattning på 50 ord som sammanfattar [ämne eller nyckelord].",
|
||||
"Yes": "",
|
||||
"Yesterday": "Igenom",
|
||||
"You": "du",
|
||||
"You have no archived conversations.": "Du har inga arkiverade konversationer.",
|
||||
|
@ -6,6 +6,7 @@
|
||||
"{{modelName}} is thinking...": "{{modelName}} düşünüyor...",
|
||||
"{{user}}'s Chats": "{{user}} Sohbetleri",
|
||||
"{{webUIName}} Backend Required": "{{webUIName}} Arkayüz Gerekli",
|
||||
"A selected model does not support image input": "",
|
||||
"a user": "bir kullanıcı",
|
||||
"About": "Hakkında",
|
||||
"Account": "Hesap",
|
||||
@ -31,6 +32,7 @@
|
||||
"Advanced Parameters": "Gelişmiş Parametreler",
|
||||
"all": "tümü",
|
||||
"All Documents": "Tüm Belgeler",
|
||||
"All selected models do not support image input, removed images": "",
|
||||
"All Users": "Tüm Kullanıcılar",
|
||||
"Allow": "İzin ver",
|
||||
"Allow Chat Deletion": "Sohbet Silmeye İzin Ver",
|
||||
@ -115,6 +117,7 @@
|
||||
"Created at": "Oluşturulma tarihi",
|
||||
"Created At": "Şu Tarihte Oluşturuldu:",
|
||||
"Current Model": "Mevcut Model",
|
||||
"Current Models": "",
|
||||
"Current Password": "Mevcut Parola",
|
||||
"Custom": "Özel",
|
||||
"Customize Ollama models for a specific purpose": "Ollama modellerini belirli bir amaç için özelleştirin",
|
||||
@ -181,6 +184,7 @@
|
||||
"Enter LiteLLM API RPM (litellm_params.rpm)": "LiteLLM API RPM'ini Girin (litellm_params.rpm)",
|
||||
"Enter LiteLLM Model (litellm_params.model)": "LiteLLM Modelini Girin (litellm_params.model)",
|
||||
"Enter Max Tokens (litellm_params.max_tokens)": "Maksimum Token Sayısını Girin (litellm_params.max_tokens)",
|
||||
"Enter Model Display Name": "",
|
||||
"Enter model tag (e.g. {{modelTag}})": "Model etiketini girin (örn. {{modelTag}})",
|
||||
"Enter Number of Steps (e.g. 50)": "Adım Sayısını Girin (örn. 50)",
|
||||
"Enter Score": "Skoru Girin",
|
||||
@ -235,6 +239,7 @@
|
||||
"Input commands": "Giriş komutları",
|
||||
"Interface": "Arayüz",
|
||||
"Invalid Tag": "Geçersiz etiket",
|
||||
"Is Model Vision Capable": "",
|
||||
"January": "Ocak",
|
||||
"join our Discord for help.": "yardım için Discord'umuza katılın.",
|
||||
"JSON": "JSON",
|
||||
@ -253,6 +258,7 @@
|
||||
"Made by OpenWebUI Community": "OpenWebUI Topluluğu tarafından yapılmıştır",
|
||||
"Make sure to enclose them with": "Değişkenlerinizi şu şekilde biçimlendirin:",
|
||||
"Manage LiteLLM Models": "LiteLLM Modellerini Yönet",
|
||||
"Manage Model Information": "",
|
||||
"Manage Models": "Modelleri Yönet",
|
||||
"Manage Ollama Models": "Ollama Modellerini Yönet",
|
||||
"March": "Mart",
|
||||
@ -272,7 +278,12 @@
|
||||
"Model '{{modelTag}}' is already in queue for downloading.": "'{{modelTag}}' zaten indirme sırasında.",
|
||||
"Model {{modelId}} not found": "{{modelId}} bulunamadı",
|
||||
"Model {{modelName}} already exists.": "{{modelName}} zaten mevcut.",
|
||||
"Model {{modelName}} is not vision capable": "",
|
||||
"Model Description": "",
|
||||
"Model Display Name": "",
|
||||
"Model filesystem path detected. Model shortname is required for update, cannot continue.": "Model dosya sistemi yolu algılandı. Güncelleme için model kısa adı gerekli, devam edilemiyor.",
|
||||
"Model info for {{modelName}} added successfully": "",
|
||||
"Model info for {{modelName}} deleted successfully": "",
|
||||
"Model Name": "Model Adı",
|
||||
"Model not selected": "Model seçilmedi",
|
||||
"Model Tag Name": "Model Etiket Adı",
|
||||
@ -289,6 +300,7 @@
|
||||
"Name your modelfile": "Model dosyanıza ad verin",
|
||||
"New Chat": "Yeni Sohbet",
|
||||
"New Password": "Yeni Parola",
|
||||
"No": "",
|
||||
"No results found": "Sonuç bulunamadı",
|
||||
"No source available": "Kaynak mevcut değil",
|
||||
"Not factually correct": "Gerçeklere göre doğru değil",
|
||||
@ -385,6 +397,7 @@
|
||||
"Select a model": "Bir model seç",
|
||||
"Select an Ollama instance": "Bir Ollama örneği seçin",
|
||||
"Select model": "Model seç",
|
||||
"Selected models do not support image inputs": "",
|
||||
"Send": "Gönder",
|
||||
"Send a Message": "Bir Mesaj Gönder",
|
||||
"Send message": "Mesaj gönder",
|
||||
@ -492,6 +505,7 @@
|
||||
"Workspace": "Çalışma Alanı",
|
||||
"Write a prompt suggestion (e.g. Who are you?)": "Bir prompt önerisi yazın (örn. Sen kimsin?)",
|
||||
"Write a summary in 50 words that summarizes [topic or keyword].": "[Konuyu veya anahtar kelimeyi] özetleyen 50 kelimelik bir özet yazın.",
|
||||
"Yes": "",
|
||||
"Yesterday": "Dün",
|
||||
"You": "Sen",
|
||||
"You have no archived conversations.": "Arşivlenmiş sohbetleriniz yok.",
|
||||
|
@ -6,6 +6,7 @@
|
||||
"{{modelName}} is thinking...": "{{modelName}} думає...",
|
||||
"{{user}}'s Chats": "Чати {{user}}а",
|
||||
"{{webUIName}} Backend Required": "Необхідно підключення бекенду {{webUIName}}",
|
||||
"A selected model does not support image input": "",
|
||||
"a user": "користувача",
|
||||
"About": "Про програму",
|
||||
"Account": "Обліковий запис",
|
||||
@ -31,6 +32,7 @@
|
||||
"Advanced Parameters": "Розширені параметри",
|
||||
"all": "всі",
|
||||
"All Documents": "Усі документи",
|
||||
"All selected models do not support image input, removed images": "",
|
||||
"All Users": "Всі користувачі",
|
||||
"Allow": "Дозволити",
|
||||
"Allow Chat Deletion": "Дозволити видалення чату",
|
||||
@ -115,6 +117,7 @@
|
||||
"Created at": "Створено у",
|
||||
"Created At": "Створено у",
|
||||
"Current Model": "Поточна модель",
|
||||
"Current Models": "",
|
||||
"Current Password": "Поточний пароль",
|
||||
"Custom": "Налаштувати",
|
||||
"Customize Ollama models for a specific purpose": "Налаштувати моделі Ollama для конкретної мети",
|
||||
@ -181,6 +184,7 @@
|
||||
"Enter LiteLLM API RPM (litellm_params.rpm)": "Введіть RPM API LiteLLM (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 Display Name": "",
|
||||
"Enter model tag (e.g. {{modelTag}})": "Введіть тег моделі (напр., {{modelTag}})",
|
||||
"Enter Number of Steps (e.g. 50)": "Введіть кількість кроків (напр., 50)",
|
||||
"Enter Score": "Введіть бал",
|
||||
@ -235,6 +239,7 @@
|
||||
"Input commands": "Команди вводу",
|
||||
"Interface": "Інтерфейс",
|
||||
"Invalid Tag": "Недійсний тег",
|
||||
"Is Model Vision Capable": "",
|
||||
"January": "Січень",
|
||||
"join our Discord for help.": "приєднуйтеся до нашого Discord для допомоги.",
|
||||
"JSON": "JSON",
|
||||
@ -253,6 +258,7 @@
|
||||
"Made by OpenWebUI Community": "Зроблено спільнотою OpenWebUI",
|
||||
"Make sure to enclose them with": "Переконайтеся, що вони закриті",
|
||||
"Manage LiteLLM Models": "Керування моделями LiteLLM",
|
||||
"Manage Model Information": "",
|
||||
"Manage Models": "Керування моделями",
|
||||
"Manage Ollama Models": "Керування моделями Ollama",
|
||||
"March": "Березень",
|
||||
@ -272,7 +278,12 @@
|
||||
"Model '{{modelTag}}' is already in queue for downloading.": "Модель '{{modelTag}}' вже знаходиться в черзі на завантаження.",
|
||||
"Model {{modelId}} not found": "Модель {{modelId}} не знайдено",
|
||||
"Model {{modelName}} already exists.": "Модель {{modelName}} вже існує.",
|
||||
"Model {{modelName}} is not vision capable": "",
|
||||
"Model Description": "",
|
||||
"Model Display Name": "",
|
||||
"Model filesystem path detected. Model shortname is required for update, cannot continue.": "Виявлено шлях до файлової системи моделі. Для оновлення потрібно вказати коротке ім'я моделі, не вдасться продовжити.",
|
||||
"Model info for {{modelName}} added successfully": "",
|
||||
"Model info for {{modelName}} deleted successfully": "",
|
||||
"Model Name": "Назва моделі",
|
||||
"Model not selected": "Модель не вибрана",
|
||||
"Model Tag Name": "Ім'я тегу моделі",
|
||||
@ -289,6 +300,7 @@
|
||||
"Name your modelfile": "Назвіть свій файл моделі",
|
||||
"New Chat": "Новий чат",
|
||||
"New Password": "Новий пароль",
|
||||
"No": "",
|
||||
"No results found": "Не знайдено жодного результату",
|
||||
"No source available": "Джерело не доступне",
|
||||
"Not factually correct": "Не відповідає дійсності",
|
||||
@ -385,6 +397,7 @@
|
||||
"Select a model": "Виберіть модель",
|
||||
"Select an Ollama instance": "Виберіть екземпляр Ollama",
|
||||
"Select model": "Вибрати модель",
|
||||
"Selected models do not support image inputs": "",
|
||||
"Send": "Надіслати",
|
||||
"Send a Message": "Надіслати повідомлення",
|
||||
"Send message": "Надіслати повідомлення",
|
||||
@ -492,6 +505,7 @@
|
||||
"Workspace": "Робочий простір",
|
||||
"Write a prompt suggestion (e.g. Who are you?)": "Напишіть промт (напр., Хто ти?)",
|
||||
"Write a summary in 50 words that summarizes [topic or keyword].": "Напишіть стислий зміст у 50 слів, який узагальнює [тема або ключове слово].",
|
||||
"Yes": "",
|
||||
"Yesterday": "Вчора",
|
||||
"You": "Ви",
|
||||
"You have no archived conversations.": "У вас немає архівованих розмов.",
|
||||
|
@ -6,6 +6,7 @@
|
||||
"{{modelName}} is thinking...": "{{modelName}} đang suy nghĩ...",
|
||||
"{{user}}'s Chats": "{{user}}'s Chats",
|
||||
"{{webUIName}} Backend Required": "{{webUIName}} Yêu cầu Backend",
|
||||
"A selected model does not support image input": "",
|
||||
"a user": "người sử dụng",
|
||||
"About": "Giới thiệu",
|
||||
"Account": "Tài khoản",
|
||||
@ -31,6 +32,7 @@
|
||||
"Advanced Parameters": "Các tham số Nâng cao",
|
||||
"all": "tất cả",
|
||||
"All Documents": "Tất cả tài liệu",
|
||||
"All selected models do not support image input, removed images": "",
|
||||
"All Users": "Danh sách người sử dụng",
|
||||
"Allow": "Cho phép",
|
||||
"Allow Chat Deletion": "Cho phép Xóa nội dung chat",
|
||||
@ -115,6 +117,7 @@
|
||||
"Created at": "Được tạo vào lúc",
|
||||
"Created At": "Tạo lúc",
|
||||
"Current Model": "Mô hình hiện tại",
|
||||
"Current Models": "",
|
||||
"Current Password": "Mật khẩu hiện tại",
|
||||
"Custom": "Tùy chỉnh",
|
||||
"Customize Ollama models for a specific purpose": "Tùy chỉnh các mô hình dựa trên Ollama cho một mục đích cụ thể",
|
||||
@ -181,6 +184,7 @@
|
||||
"Enter LiteLLM API RPM (litellm_params.rpm)": "Nhập RPM API LiteLLM (litellm_params.rpm)",
|
||||
"Enter LiteLLM Model (litellm_params.model)": "Nhập Mô hình LiteLLM (litellm_params.model)",
|
||||
"Enter Max Tokens (litellm_params.max_tokens)": "Nhập Số Token Tối đa (litellm_params.max_tokens)",
|
||||
"Enter Model Display Name": "",
|
||||
"Enter model tag (e.g. {{modelTag}})": "Nhập thẻ mô hình (vd: {{modelTag}})",
|
||||
"Enter Number of Steps (e.g. 50)": "Nhập số Steps (vd: 50)",
|
||||
"Enter Score": "Nhập Score",
|
||||
@ -235,6 +239,7 @@
|
||||
"Input commands": "Nhập các câu lệnh",
|
||||
"Interface": "Giao diện",
|
||||
"Invalid Tag": "Tag không hợp lệ",
|
||||
"Is Model Vision Capable": "",
|
||||
"January": "Tháng 1",
|
||||
"join our Discord for help.": "tham gia Discord của chúng tôi để được trợ giúp.",
|
||||
"JSON": "JSON",
|
||||
@ -253,6 +258,7 @@
|
||||
"Made by OpenWebUI Community": "Được tạo bởi Cộng đồng OpenWebUI",
|
||||
"Make sure to enclose them with": "Hãy chắc chắn bao quanh chúng bằng",
|
||||
"Manage LiteLLM Models": "Quản lý mô hình với LiteLLM",
|
||||
"Manage Model Information": "",
|
||||
"Manage Models": "Quản lý mô hình",
|
||||
"Manage Ollama Models": "Quản lý mô hình với Ollama",
|
||||
"March": "Tháng 3",
|
||||
@ -272,7 +278,12 @@
|
||||
"Model '{{modelTag}}' is already in queue for downloading.": "Mô hình '{{modelTag}}' đã có trong hàng đợi để tải xuống.",
|
||||
"Model {{modelId}} not found": "Không tìm thấy Mô hình {{modelId}}",
|
||||
"Model {{modelName}} already exists.": "Mô hình {{modelName}} đã tồn tại.",
|
||||
"Model {{modelName}} is not vision capable": "",
|
||||
"Model Description": "",
|
||||
"Model Display Name": "",
|
||||
"Model filesystem path detected. Model shortname is required for update, cannot continue.": "Đường dẫn hệ thống tệp mô hình được phát hiện. Tên viết tắt mô hình là bắt buộc để cập nhật, không thể tiếp tục.",
|
||||
"Model info for {{modelName}} added successfully": "",
|
||||
"Model info for {{modelName}} deleted successfully": "",
|
||||
"Model Name": "Tên Mô hình",
|
||||
"Model not selected": "Chưa chọn Mô hình",
|
||||
"Model Tag Name": "Tên thẻ Mô hình",
|
||||
@ -289,6 +300,7 @@
|
||||
"Name your modelfile": "Đặt tên cho tệp mô hình của bạn",
|
||||
"New Chat": "Tạo cuộc trò chuyện mới",
|
||||
"New Password": "Mật khẩu mới",
|
||||
"No": "",
|
||||
"No results found": "Không tìm thấy kết quả",
|
||||
"No source available": "Không có nguồn",
|
||||
"Not factually correct": "Không chính xác so với thực tế",
|
||||
@ -385,6 +397,7 @@
|
||||
"Select a model": "Chọn mô hình",
|
||||
"Select an Ollama instance": "Chọn một thực thể Ollama",
|
||||
"Select model": "Chọn model",
|
||||
"Selected models do not support image inputs": "",
|
||||
"Send": "Gửi",
|
||||
"Send a Message": "Gửi yêu cầu",
|
||||
"Send message": "Gửi yêu cầu",
|
||||
@ -492,6 +505,7 @@
|
||||
"Workspace": "Workspace",
|
||||
"Write a prompt suggestion (e.g. Who are you?)": "Hãy viết một prompt (vd: Bạn là ai?)",
|
||||
"Write a summary in 50 words that summarizes [topic or keyword].": "Viết một tóm tắt trong vòng 50 từ cho [chủ đề hoặc từ khóa].",
|
||||
"Yes": "",
|
||||
"Yesterday": "Hôm qua",
|
||||
"You": "Bạn",
|
||||
"You have no archived conversations.": "Bạn chưa lưu trữ một nội dung chat nào",
|
||||
|
@ -6,6 +6,7 @@
|
||||
"{{modelName}} is thinking...": "{{modelName}} 正在思考...",
|
||||
"{{user}}'s Chats": "{{user}} 的聊天记录",
|
||||
"{{webUIName}} Backend Required": "需要 {{webUIName}} 后端",
|
||||
"A selected model does not support image input": "",
|
||||
"a user": "用户",
|
||||
"About": "关于",
|
||||
"Account": "账户",
|
||||
@ -31,6 +32,7 @@
|
||||
"Advanced Parameters": "高级参数",
|
||||
"all": "所有",
|
||||
"All Documents": "所有文档",
|
||||
"All selected models do not support image input, removed images": "",
|
||||
"All Users": "所有用户",
|
||||
"Allow": "允许",
|
||||
"Allow Chat Deletion": "允许删除聊天记录",
|
||||
@ -115,6 +117,7 @@
|
||||
"Created at": "创建于",
|
||||
"Created At": "创建于",
|
||||
"Current Model": "当前模型",
|
||||
"Current Models": "",
|
||||
"Current Password": "当前密码",
|
||||
"Custom": "自定义",
|
||||
"Customize Ollama models for a specific purpose": "定制特定用途的 Ollama 模型",
|
||||
@ -181,6 +184,7 @@
|
||||
"Enter LiteLLM API RPM (litellm_params.rpm)": "输入 LiteLLM API 速率限制 (litellm_params.rpm)",
|
||||
"Enter LiteLLM Model (litellm_params.model)": "输入 LiteLLM 模型 (litellm_params.model)",
|
||||
"Enter Max Tokens (litellm_params.max_tokens)": "输入模型的 Max Tokens (litellm_params.max_tokens)",
|
||||
"Enter Model Display Name": "",
|
||||
"Enter model tag (e.g. {{modelTag}})": "输入模型标签 (例如{{modelTag}})",
|
||||
"Enter Number of Steps (e.g. 50)": "输入步数 (例如 50)",
|
||||
"Enter Score": "输入分",
|
||||
@ -235,6 +239,7 @@
|
||||
"Input commands": "输入命令",
|
||||
"Interface": "界面",
|
||||
"Invalid Tag": "无效标签",
|
||||
"Is Model Vision Capable": "",
|
||||
"January": "一月",
|
||||
"join our Discord for help.": "加入我们的 Discord 寻求帮助。",
|
||||
"JSON": "JSON",
|
||||
@ -253,6 +258,7 @@
|
||||
"Made by OpenWebUI Community": "由 OpenWebUI 社区制作",
|
||||
"Make sure to enclose them with": "确保将它们包含在内",
|
||||
"Manage LiteLLM Models": "管理 LiteLLM 模型",
|
||||
"Manage Model Information": "",
|
||||
"Manage Models": "管理模型",
|
||||
"Manage Ollama Models": "管理 Ollama 模型",
|
||||
"March": "三月",
|
||||
@ -272,7 +278,12 @@
|
||||
"Model '{{modelTag}}' is already in queue for downloading.": "模型'{{modelTag}}'已在下载队列中。",
|
||||
"Model {{modelId}} not found": "未找到模型{{modelId}}",
|
||||
"Model {{modelName}} already exists.": "模型{{modelName}}已存在。",
|
||||
"Model {{modelName}} is not vision capable": "",
|
||||
"Model Description": "",
|
||||
"Model Display Name": "",
|
||||
"Model filesystem path detected. Model shortname is required for update, cannot continue.": "检测到模型文件系统路径。模型简名是更新所必需的,无法继续。",
|
||||
"Model info for {{modelName}} added successfully": "",
|
||||
"Model info for {{modelName}} deleted successfully": "",
|
||||
"Model Name": "模型名称",
|
||||
"Model not selected": "未选择模型",
|
||||
"Model Tag Name": "模型标签名称",
|
||||
@ -289,6 +300,7 @@
|
||||
"Name your modelfile": "命名你的模型文件",
|
||||
"New Chat": "新聊天",
|
||||
"New Password": "新密码",
|
||||
"No": "",
|
||||
"No results found": "未找到结果",
|
||||
"No source available": "没有可用来源",
|
||||
"Not factually correct": "与事实不符",
|
||||
@ -385,6 +397,7 @@
|
||||
"Select a model": "选择一个模型",
|
||||
"Select an Ollama instance": "选择一个 Ollama 实例",
|
||||
"Select model": "选择模型",
|
||||
"Selected models do not support image inputs": "",
|
||||
"Send": "发送",
|
||||
"Send a Message": "发送消息",
|
||||
"Send message": "发送消息",
|
||||
@ -492,6 +505,7 @@
|
||||
"Workspace": "工作空间",
|
||||
"Write a prompt suggestion (e.g. Who are you?)": "写一个提示建议(例如:你是谁?)",
|
||||
"Write a summary in 50 words that summarizes [topic or keyword].": "用 50 个字写一个总结 [主题或关键词]。",
|
||||
"Yes": "",
|
||||
"Yesterday": "昨天",
|
||||
"You": "你",
|
||||
"You have no archived conversations.": "你没有存档的对话。",
|
||||
|
@ -6,6 +6,7 @@
|
||||
"{{modelName}} is thinking...": "{{modelName}} 正在思考...",
|
||||
"{{user}}'s Chats": "{{user}} 的聊天",
|
||||
"{{webUIName}} Backend Required": "需要 {{webUIName}} 後台",
|
||||
"A selected model does not support image input": "",
|
||||
"a user": "使用者",
|
||||
"About": "關於",
|
||||
"Account": "帳號",
|
||||
@ -31,6 +32,7 @@
|
||||
"Advanced Parameters": "進階參數",
|
||||
"all": "所有",
|
||||
"All Documents": "所有文件",
|
||||
"All selected models do not support image input, removed images": "",
|
||||
"All Users": "所有使用者",
|
||||
"Allow": "允許",
|
||||
"Allow Chat Deletion": "允許刪除聊天紀錄",
|
||||
@ -115,6 +117,7 @@
|
||||
"Created at": "建立於",
|
||||
"Created At": "建立於",
|
||||
"Current Model": "目前模型",
|
||||
"Current Models": "",
|
||||
"Current Password": "目前密碼",
|
||||
"Custom": "自訂",
|
||||
"Customize Ollama models for a specific purpose": "定制特定用途的 Ollama 模型",
|
||||
@ -181,6 +184,7 @@
|
||||
"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)": "輸入最大 Token 數(litellm_params.max_tokens)",
|
||||
"Enter Model Display Name": "",
|
||||
"Enter model tag (e.g. {{modelTag}})": "輸入模型標籤(例如 {{modelTag}})",
|
||||
"Enter Number of Steps (e.g. 50)": "輸入步數(例如 50)",
|
||||
"Enter Score": "輸入分數",
|
||||
@ -235,6 +239,7 @@
|
||||
"Input commands": "輸入命令",
|
||||
"Interface": "介面",
|
||||
"Invalid Tag": "無效標籤",
|
||||
"Is Model Vision Capable": "",
|
||||
"January": "1月",
|
||||
"join our Discord for help.": "加入我們的 Discord 尋找幫助。",
|
||||
"JSON": "JSON",
|
||||
@ -253,6 +258,7 @@
|
||||
"Made by OpenWebUI Community": "由 OpenWebUI 社區製作",
|
||||
"Make sure to enclose them with": "請確保變數有被以下符號框住:",
|
||||
"Manage LiteLLM Models": "管理 LiteLLM 模型",
|
||||
"Manage Model Information": "",
|
||||
"Manage Models": "管理模組",
|
||||
"Manage Ollama Models": "管理 Ollama 模型",
|
||||
"March": "3月",
|
||||
@ -272,7 +278,12 @@
|
||||
"Model '{{modelTag}}' is already in queue for downloading.": "'{{modelTag}}' 模型已經在下載佇列中。",
|
||||
"Model {{modelId}} not found": "找不到 {{modelId}} 模型",
|
||||
"Model {{modelName}} already exists.": "模型 {{modelName}} 已存在。",
|
||||
"Model {{modelName}} is not vision capable": "",
|
||||
"Model Description": "",
|
||||
"Model Display Name": "",
|
||||
"Model filesystem path detected. Model shortname is required for update, cannot continue.": "模型文件系統路徑已檢測。需要更新模型短名,無法繼續。",
|
||||
"Model info for {{modelName}} added successfully": "",
|
||||
"Model info for {{modelName}} deleted successfully": "",
|
||||
"Model Name": "模型名稱",
|
||||
"Model not selected": "未選擇模型",
|
||||
"Model Tag Name": "模型標籤",
|
||||
@ -289,6 +300,7 @@
|
||||
"Name your modelfile": "命名你的 Modelfile",
|
||||
"New Chat": "新增聊天",
|
||||
"New Password": "新密碼",
|
||||
"No": "",
|
||||
"No results found": "沒有找到結果",
|
||||
"No source available": "沒有可用的來源",
|
||||
"Not factually correct": "與真實資訊不相符",
|
||||
@ -385,6 +397,7 @@
|
||||
"Select a model": "選擇一個模型",
|
||||
"Select an Ollama instance": "選擇 Ollama 實例",
|
||||
"Select model": "選擇模型",
|
||||
"Selected models do not support image inputs": "",
|
||||
"Send": "傳送",
|
||||
"Send a Message": "傳送訊息",
|
||||
"Send message": "傳送訊息",
|
||||
@ -492,6 +505,7 @@
|
||||
"Workspace": "工作區",
|
||||
"Write a prompt suggestion (e.g. Who are you?)": "寫一個提示詞建議(例如:你是誰?)",
|
||||
"Write a summary in 50 words that summarizes [topic or keyword].": "寫一個 50 字的摘要來概括 [主題或關鍵詞]。",
|
||||
"Yes": "",
|
||||
"Yesterday": "昨天",
|
||||
"You": "你",
|
||||
"You have no archived conversations.": "你沒有任何已封存的對話",
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { APP_NAME } from '$lib/constants';
|
||||
import { type Writable, writable } from 'svelte/store';
|
||||
import type { GlobalModelConfig, ModelConfig } from '$lib/apis';
|
||||
|
||||
// Backend
|
||||
export const WEBUI_NAME = writable(APP_NAME);
|
||||
@ -42,27 +43,27 @@ export const showSettings = writable(false);
|
||||
export const showArchivedChats = writable(false);
|
||||
export const showChangelog = writable(false);
|
||||
|
||||
type Model = OpenAIModel | OllamaModel;
|
||||
export type Model = OpenAIModel | OllamaModel;
|
||||
|
||||
type OpenAIModel = {
|
||||
type BaseModel = {
|
||||
id: string;
|
||||
name: string;
|
||||
external: boolean;
|
||||
source?: string;
|
||||
info?: ModelConfig;
|
||||
};
|
||||
|
||||
type OllamaModel = {
|
||||
id: string;
|
||||
name: string;
|
||||
export interface OpenAIModel extends BaseModel {
|
||||
external: boolean;
|
||||
source?: string;
|
||||
}
|
||||
|
||||
// Ollama specific fields
|
||||
export interface OllamaModel extends BaseModel {
|
||||
details: OllamaModelDetails;
|
||||
size: number;
|
||||
description: string;
|
||||
model: string;
|
||||
modified_at: string;
|
||||
digest: string;
|
||||
};
|
||||
}
|
||||
|
||||
type OllamaModelDetails = {
|
||||
parent_model: string;
|
||||
@ -133,6 +134,7 @@ type Config = {
|
||||
default_models?: string[];
|
||||
default_prompt_suggestions?: PromptSuggestion[];
|
||||
trusted_header_auth?: boolean;
|
||||
model_config?: GlobalModelConfig;
|
||||
};
|
||||
|
||||
type PromptSuggestion = {
|
||||
|
@ -1,29 +1,5 @@
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import sha256 from 'js-sha256';
|
||||
import { getOllamaModels } from '$lib/apis/ollama';
|
||||
import { getOpenAIModels } from '$lib/apis/openai';
|
||||
import { getLiteLLMModels } from '$lib/apis/litellm';
|
||||
|
||||
export const getModels = async (token: string) => {
|
||||
let models = await Promise.all([
|
||||
getOllamaModels(token).catch((error) => {
|
||||
console.log(error);
|
||||
return null;
|
||||
}),
|
||||
getOpenAIModels(token).catch((error) => {
|
||||
console.log(error);
|
||||
return null;
|
||||
}),
|
||||
getLiteLLMModels(token).catch((error) => {
|
||||
console.log(error);
|
||||
return null;
|
||||
})
|
||||
]);
|
||||
|
||||
models = models.filter((models) => models).reduce((a, e, i, arr) => a.concat(e), []);
|
||||
|
||||
return models;
|
||||
};
|
||||
|
||||
//////////////////////////
|
||||
// Helper functions
|
||||
|
@ -7,9 +7,8 @@
|
||||
|
||||
import { goto } from '$app/navigation';
|
||||
|
||||
import { getModels as _getModels } from '$lib/utils';
|
||||
import { getModels as _getModels } from '$lib/apis';
|
||||
import { getOllamaVersion } from '$lib/apis/ollama';
|
||||
import { getModelfiles } from '$lib/apis/modelfiles';
|
||||
import { getPrompts } from '$lib/apis/prompts';
|
||||
|
||||
import { getDocs } from '$lib/apis/documents';
|
||||
@ -20,7 +19,6 @@
|
||||
showSettings,
|
||||
settings,
|
||||
models,
|
||||
modelfiles,
|
||||
prompts,
|
||||
documents,
|
||||
tags,
|
||||
@ -50,21 +48,6 @@
|
||||
return _getModels(localStorage.token);
|
||||
};
|
||||
|
||||
const setOllamaVersion = async (version: string = '') => {
|
||||
if (version === '') {
|
||||
version = await getOllamaVersion(localStorage.token).catch((error) => {
|
||||
return '';
|
||||
});
|
||||
}
|
||||
|
||||
ollamaVersion = version;
|
||||
|
||||
console.log(ollamaVersion);
|
||||
if (compareVersion(REQUIRED_OLLAMA_VERSION, ollamaVersion)) {
|
||||
toast.error(`Ollama Version: ${ollamaVersion !== '' ? ollamaVersion : 'Not Detected'}`);
|
||||
}
|
||||
};
|
||||
|
||||
onMount(async () => {
|
||||
if ($user === undefined) {
|
||||
await goto('/auth');
|
||||
@ -93,9 +76,6 @@
|
||||
(async () => {
|
||||
models.set(await getModels());
|
||||
})(),
|
||||
(async () => {
|
||||
modelfiles.set(await getModelfiles(localStorage.token));
|
||||
})(),
|
||||
(async () => {
|
||||
prompts.set(await getPrompts(localStorage.token));
|
||||
})(),
|
||||
@ -107,11 +87,6 @@
|
||||
})()
|
||||
]);
|
||||
|
||||
modelfiles.subscribe(async () => {
|
||||
// should fetch models
|
||||
models.set(await getModels());
|
||||
});
|
||||
|
||||
document.addEventListener('keydown', function (event) {
|
||||
const isCtrlPressed = event.ctrlKey || event.metaKey; // metaKey is for Cmd key on Mac
|
||||
// Check if the Shift key is pressed
|
||||
@ -188,12 +163,12 @@
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class=" hidden lg:flex fixed bottom-0 right-0 px-3 py-3 z-10">
|
||||
<div class=" hidden lg:flex fixed bottom-0 right-0 px-2 py-2 z-10">
|
||||
<Tooltip content={$i18n.t('Help')} placement="left">
|
||||
<button
|
||||
id="show-shortcuts-button"
|
||||
bind:this={showShortcutsButtonElement}
|
||||
class="text-gray-600 dark:text-gray-300 bg-gray-300/20 w-6 h-6 flex items-center justify-center text-xs rounded-full"
|
||||
class="text-gray-600 dark:text-gray-300 bg-gray-300/20 size-5 flex items-center justify-center text-[0.7rem] rounded-full"
|
||||
on:click={() => {
|
||||
showShortcuts = !showShortcuts;
|
||||
}}
|
||||
|
@ -39,10 +39,10 @@
|
||||
class="flex scrollbar-none overflow-x-auto w-fit text-center text-sm font-medium rounded-xl bg-transparent/10 p-1"
|
||||
>
|
||||
<a
|
||||
class="min-w-fit rounded-lg p-1.5 px-3 {$page.url.pathname.includes('/workspace/modelfiles')
|
||||
class="min-w-fit rounded-lg p-1.5 px-3 {$page.url.pathname.includes('/workspace/models')
|
||||
? 'bg-gray-50 dark:bg-gray-850'
|
||||
: ''} transition"
|
||||
href="/workspace/modelfiles">{$i18n.t('Modelfiles')}</a
|
||||
href="/workspace/models">{$i18n.t('Models')}</a
|
||||
>
|
||||
|
||||
<a
|
||||
|
@ -3,6 +3,6 @@
|
||||
import { onMount } from 'svelte';
|
||||
|
||||
onMount(() => {
|
||||
goto('/workspace/modelfiles');
|
||||
goto('/workspace/models');
|
||||
});
|
||||
</script>
|
||||
|
@ -1,5 +0,0 @@
|
||||
<script>
|
||||
import Modelfiles from '$lib/components/workspace/Modelfiles.svelte';
|
||||
</script>
|
||||
|
||||
<Modelfiles />
|
@ -1,721 +0,0 @@
|
||||
<script>
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import { toast } from 'svelte-sonner';
|
||||
import { goto } from '$app/navigation';
|
||||
import { settings, user, config, modelfiles, models } from '$lib/stores';
|
||||
|
||||
import AdvancedParams from '$lib/components/chat/Settings/Advanced/AdvancedParams.svelte';
|
||||
import { splitStream } from '$lib/utils';
|
||||
import { onMount, tick, getContext } from 'svelte';
|
||||
import { createModel } from '$lib/apis/ollama';
|
||||
import { createNewModelfile, getModelfileByTagName, getModelfiles } from '$lib/apis/modelfiles';
|
||||
|
||||
const i18n = getContext('i18n');
|
||||
|
||||
let loading = false;
|
||||
|
||||
let filesInputElement;
|
||||
let inputFiles;
|
||||
let imageUrl = null;
|
||||
let digest = '';
|
||||
let pullProgress = null;
|
||||
let success = false;
|
||||
|
||||
// ///////////
|
||||
// Modelfile
|
||||
// ///////////
|
||||
|
||||
let title = '';
|
||||
let tagName = '';
|
||||
let desc = '';
|
||||
|
||||
let raw = true;
|
||||
let advanced = false;
|
||||
|
||||
// Raw Mode
|
||||
let content = '';
|
||||
|
||||
// Builder Mode
|
||||
let model = '';
|
||||
let system = '';
|
||||
let template = '';
|
||||
let options = {
|
||||
// Advanced
|
||||
seed: 0,
|
||||
stop: '',
|
||||
temperature: '',
|
||||
repeat_penalty: '',
|
||||
repeat_last_n: '',
|
||||
mirostat: '',
|
||||
mirostat_eta: '',
|
||||
mirostat_tau: '',
|
||||
top_k: '',
|
||||
top_p: '',
|
||||
tfs_z: '',
|
||||
num_ctx: '',
|
||||
num_predict: ''
|
||||
};
|
||||
|
||||
let modelfileCreator = null;
|
||||
|
||||
$: tagName = title !== '' ? `${title.replace(/\s+/g, '-').toLowerCase()}:latest` : '';
|
||||
|
||||
$: if (!raw) {
|
||||
content = `FROM ${model}
|
||||
${template !== '' ? `TEMPLATE """${template}"""` : ''}
|
||||
${options.seed !== 0 ? `PARAMETER seed ${options.seed}` : ''}
|
||||
${options.stop !== '' ? `PARAMETER stop ${options.stop}` : ''}
|
||||
${options.temperature !== '' ? `PARAMETER temperature ${options.temperature}` : ''}
|
||||
${options.repeat_penalty !== '' ? `PARAMETER repeat_penalty ${options.repeat_penalty}` : ''}
|
||||
${options.repeat_last_n !== '' ? `PARAMETER repeat_last_n ${options.repeat_last_n}` : ''}
|
||||
${options.mirostat !== '' ? `PARAMETER mirostat ${options.mirostat}` : ''}
|
||||
${options.mirostat_eta !== '' ? `PARAMETER mirostat_eta ${options.mirostat_eta}` : ''}
|
||||
${options.mirostat_tau !== '' ? `PARAMETER mirostat_tau ${options.mirostat_tau}` : ''}
|
||||
${options.top_k !== '' ? `PARAMETER top_k ${options.top_k}` : ''}
|
||||
${options.top_p !== '' ? `PARAMETER top_p ${options.top_p}` : ''}
|
||||
${options.tfs_z !== '' ? `PARAMETER tfs_z ${options.tfs_z}` : ''}
|
||||
${options.num_ctx !== '' ? `PARAMETER num_ctx ${options.num_ctx}` : ''}
|
||||
${options.num_predict !== '' ? `PARAMETER num_predict ${options.num_predict}` : ''}
|
||||
SYSTEM """${system}"""`.replace(/^\s*\n/gm, '');
|
||||
}
|
||||
|
||||
let suggestions = [
|
||||
{
|
||||
content: ''
|
||||
}
|
||||
];
|
||||
|
||||
let categories = {
|
||||
character: false,
|
||||
assistant: false,
|
||||
writing: false,
|
||||
productivity: false,
|
||||
programming: false,
|
||||
'data analysis': false,
|
||||
lifestyle: false,
|
||||
education: false,
|
||||
business: false
|
||||
};
|
||||
|
||||
const saveModelfile = async (modelfile) => {
|
||||
await createNewModelfile(localStorage.token, modelfile);
|
||||
await modelfiles.set(await getModelfiles(localStorage.token));
|
||||
};
|
||||
|
||||
const submitHandler = async () => {
|
||||
loading = true;
|
||||
|
||||
if (Object.keys(categories).filter((category) => categories[category]).length == 0) {
|
||||
toast.error(
|
||||
'Uh-oh! It looks like you missed selecting a category. Please choose one to complete your modelfile.'
|
||||
);
|
||||
loading = false;
|
||||
success = false;
|
||||
return success;
|
||||
}
|
||||
|
||||
if (
|
||||
$models.map((model) => model.name).includes(tagName) ||
|
||||
(await getModelfileByTagName(localStorage.token, tagName).catch(() => false))
|
||||
) {
|
||||
toast.error(
|
||||
`Uh-oh! It looks like you already have a model named '${tagName}'. Please choose a different name to complete your modelfile.`
|
||||
);
|
||||
loading = false;
|
||||
success = false;
|
||||
return success;
|
||||
}
|
||||
|
||||
if (
|
||||
title !== '' &&
|
||||
desc !== '' &&
|
||||
content !== '' &&
|
||||
Object.keys(categories).filter((category) => categories[category]).length > 0 &&
|
||||
!$models.includes(tagName)
|
||||
) {
|
||||
const res = await createModel(localStorage.token, tagName, content);
|
||||
|
||||
if (res) {
|
||||
const reader = res.body
|
||||
.pipeThrough(new TextDecoderStream())
|
||||
.pipeThrough(splitStream('\n'))
|
||||
.getReader();
|
||||
|
||||
while (true) {
|
||||
const { value, done } = await reader.read();
|
||||
if (done) break;
|
||||
|
||||
try {
|
||||
let lines = value.split('\n');
|
||||
|
||||
for (const line of lines) {
|
||||
if (line !== '') {
|
||||
console.log(line);
|
||||
let data = JSON.parse(line);
|
||||
console.log(data);
|
||||
|
||||
if (data.error) {
|
||||
throw data.error;
|
||||
}
|
||||
if (data.detail) {
|
||||
throw data.detail;
|
||||
}
|
||||
|
||||
if (data.status) {
|
||||
if (
|
||||
!data.digest &&
|
||||
!data.status.includes('writing') &&
|
||||
!data.status.includes('sha256')
|
||||
) {
|
||||
toast.success(data.status);
|
||||
|
||||
if (data.status === 'success') {
|
||||
success = true;
|
||||
}
|
||||
} else {
|
||||
if (data.digest) {
|
||||
digest = data.digest;
|
||||
|
||||
if (data.completed) {
|
||||
pullProgress = Math.round((data.completed / data.total) * 1000) / 10;
|
||||
} else {
|
||||
pullProgress = 100;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
toast.error(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (success) {
|
||||
await saveModelfile({
|
||||
tagName: tagName,
|
||||
imageUrl: imageUrl,
|
||||
title: title,
|
||||
desc: desc,
|
||||
content: content,
|
||||
suggestionPrompts: suggestions.filter((prompt) => prompt.content !== ''),
|
||||
categories: Object.keys(categories).filter((category) => categories[category]),
|
||||
user: modelfileCreator !== null ? modelfileCreator : undefined
|
||||
});
|
||||
await goto('/workspace/modelfiles');
|
||||
}
|
||||
}
|
||||
loading = false;
|
||||
success = false;
|
||||
};
|
||||
|
||||
onMount(async () => {
|
||||
window.addEventListener('message', async (event) => {
|
||||
if (
|
||||
![
|
||||
'https://ollamahub.com',
|
||||
'https://www.ollamahub.com',
|
||||
'https://openwebui.com',
|
||||
'https://www.openwebui.com',
|
||||
'http://localhost:5173'
|
||||
].includes(event.origin)
|
||||
)
|
||||
return;
|
||||
const modelfile = JSON.parse(event.data);
|
||||
console.log(modelfile);
|
||||
|
||||
imageUrl = modelfile.imageUrl;
|
||||
title = modelfile.title;
|
||||
await tick();
|
||||
tagName = `${modelfile.user.username === 'hub' ? '' : `hub/`}${modelfile.user.username}/${
|
||||
modelfile.tagName
|
||||
}`;
|
||||
desc = modelfile.desc;
|
||||
content = modelfile.content;
|
||||
suggestions =
|
||||
modelfile.suggestionPrompts.length != 0
|
||||
? modelfile.suggestionPrompts
|
||||
: [
|
||||
{
|
||||
content: ''
|
||||
}
|
||||
];
|
||||
|
||||
modelfileCreator = {
|
||||
username: modelfile.user.username,
|
||||
name: modelfile.user.name
|
||||
};
|
||||
for (const category of modelfile.categories) {
|
||||
categories[category.toLowerCase()] = true;
|
||||
}
|
||||
});
|
||||
|
||||
if (window.opener ?? false) {
|
||||
window.opener.postMessage('loaded', '*');
|
||||
}
|
||||
|
||||
if (sessionStorage.modelfile) {
|
||||
const modelfile = JSON.parse(sessionStorage.modelfile);
|
||||
console.log(modelfile);
|
||||
imageUrl = modelfile.imageUrl;
|
||||
title = modelfile.title;
|
||||
await tick();
|
||||
tagName = modelfile.tagName;
|
||||
desc = modelfile.desc;
|
||||
content = modelfile.content;
|
||||
suggestions =
|
||||
modelfile.suggestionPrompts.length != 0
|
||||
? modelfile.suggestionPrompts
|
||||
: [
|
||||
{
|
||||
content: ''
|
||||
}
|
||||
];
|
||||
|
||||
for (const category of modelfile.categories) {
|
||||
categories[category.toLowerCase()] = true;
|
||||
}
|
||||
|
||||
sessionStorage.removeItem('modelfile');
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="w-full max-h-full">
|
||||
<input
|
||||
bind:this={filesInputElement}
|
||||
bind:files={inputFiles}
|
||||
type="file"
|
||||
hidden
|
||||
accept="image/*"
|
||||
on:change={() => {
|
||||
let reader = new FileReader();
|
||||
reader.onload = (event) => {
|
||||
let originalImageUrl = `${event.target.result}`;
|
||||
|
||||
const img = new Image();
|
||||
img.src = originalImageUrl;
|
||||
|
||||
img.onload = function () {
|
||||
const canvas = document.createElement('canvas');
|
||||
const ctx = canvas.getContext('2d');
|
||||
|
||||
// Calculate the aspect ratio of the image
|
||||
const aspectRatio = img.width / img.height;
|
||||
|
||||
// Calculate the new width and height to fit within 100x100
|
||||
let newWidth, newHeight;
|
||||
if (aspectRatio > 1) {
|
||||
newWidth = 100 * aspectRatio;
|
||||
newHeight = 100;
|
||||
} else {
|
||||
newWidth = 100;
|
||||
newHeight = 100 / aspectRatio;
|
||||
}
|
||||
|
||||
// Set the canvas size
|
||||
canvas.width = 100;
|
||||
canvas.height = 100;
|
||||
|
||||
// Calculate the position to center the image
|
||||
const offsetX = (100 - newWidth) / 2;
|
||||
const offsetY = (100 - newHeight) / 2;
|
||||
|
||||
// Draw the image on the canvas
|
||||
ctx.drawImage(img, offsetX, offsetY, newWidth, newHeight);
|
||||
|
||||
// Get the base64 representation of the compressed image
|
||||
const compressedSrc = canvas.toDataURL('image/jpeg');
|
||||
|
||||
// Display the compressed image
|
||||
imageUrl = compressedSrc;
|
||||
|
||||
inputFiles = null;
|
||||
};
|
||||
};
|
||||
|
||||
if (
|
||||
inputFiles &&
|
||||
inputFiles.length > 0 &&
|
||||
['image/gif', 'image/webp', 'image/jpeg', 'image/png'].includes(inputFiles[0]['type'])
|
||||
) {
|
||||
reader.readAsDataURL(inputFiles[0]);
|
||||
} else {
|
||||
console.log(`Unsupported File Type '${inputFiles[0]['type']}'.`);
|
||||
inputFiles = null;
|
||||
}
|
||||
}}
|
||||
/>
|
||||
|
||||
<button
|
||||
class="flex space-x-1"
|
||||
on:click={() => {
|
||||
history.back();
|
||||
}}
|
||||
>
|
||||
<div class=" self-center">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 20 20"
|
||||
fill="currentColor"
|
||||
class="w-4 h-4"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
d="M17 10a.75.75 0 01-.75.75H5.612l4.158 3.96a.75.75 0 11-1.04 1.08l-5.5-5.25a.75.75 0 010-1.08l5.5-5.25a.75.75 0 111.04 1.08L5.612 9.25H16.25A.75.75 0 0117 10z"
|
||||
clip-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class=" self-center font-medium text-sm">{$i18n.t('Back')}</div>
|
||||
</button>
|
||||
<!-- <hr class="my-3 dark:border-gray-700" /> -->
|
||||
|
||||
<form
|
||||
class="flex flex-col max-w-2xl mx-auto mt-4 mb-10"
|
||||
on:submit|preventDefault={() => {
|
||||
submitHandler();
|
||||
}}
|
||||
>
|
||||
<div class="flex justify-center my-4">
|
||||
<div class="self-center">
|
||||
<button
|
||||
class=" {imageUrl
|
||||
? ''
|
||||
: 'p-6'} rounded-full dark:bg-gray-700 border border-dashed border-gray-200"
|
||||
type="button"
|
||||
on:click={() => {
|
||||
filesInputElement.click();
|
||||
}}
|
||||
>
|
||||
{#if imageUrl}
|
||||
<img
|
||||
src={imageUrl}
|
||||
alt="modelfile profile"
|
||||
class=" rounded-full w-20 h-20 object-cover"
|
||||
/>
|
||||
{:else}
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
fill="currentColor"
|
||||
class="w-8"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
d="M12 3.75a.75.75 0 01.75.75v6.75h6.75a.75.75 0 010 1.5h-6.75v6.75a.75.75 0 01-1.5 0v-6.75H4.5a.75.75 0 010-1.5h6.75V4.5a.75.75 0 01.75-.75z"
|
||||
clip-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="my-2 flex space-x-2">
|
||||
<div class="flex-1">
|
||||
<div class=" text-sm font-semibold mb-2">{$i18n.t('Name')}*</div>
|
||||
|
||||
<div>
|
||||
<input
|
||||
class="px-3 py-1.5 text-sm w-full bg-transparent border dark:border-gray-600 outline-none rounded-lg"
|
||||
placeholder={$i18n.t('Name your modelfile')}
|
||||
bind:value={title}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex-1">
|
||||
<div class=" text-sm font-semibold mb-2">{$i18n.t('Model Tag Name')}*</div>
|
||||
|
||||
<div>
|
||||
<input
|
||||
class="px-3 py-1.5 text-sm w-full bg-transparent border dark:border-gray-600 outline-none rounded-lg"
|
||||
placeholder={$i18n.t('Add a model tag name')}
|
||||
bind:value={tagName}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="my-2">
|
||||
<div class=" text-sm font-semibold mb-2">{$i18n.t('Description')}*</div>
|
||||
|
||||
<div>
|
||||
<input
|
||||
class="px-3 py-1.5 text-sm w-full bg-transparent border dark:border-gray-600 outline-none rounded-lg"
|
||||
placeholder={$i18n.t('Add a short description about what this modelfile does')}
|
||||
bind:value={desc}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="my-2">
|
||||
<div class="flex w-full justify-between">
|
||||
<div class=" self-center text-sm font-semibold">{$i18n.t('Modelfile')}</div>
|
||||
|
||||
<button
|
||||
class="p-1 px-3 text-xs flex rounded transition"
|
||||
type="button"
|
||||
on:click={() => {
|
||||
raw = !raw;
|
||||
}}
|
||||
>
|
||||
{#if raw}
|
||||
<span class="ml-2 self-center"> {$i18n.t('Raw Format')} </span>
|
||||
{:else}
|
||||
<span class="ml-2 self-center"> {$i18n.t('Builder Mode')} </span>
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- <div class=" text-sm font-semibold mb-2"></div> -->
|
||||
|
||||
{#if raw}
|
||||
<div class="mt-2">
|
||||
<div class=" text-xs font-semibold mb-2">{$i18n.t('Content')}*</div>
|
||||
|
||||
<div>
|
||||
<textarea
|
||||
class="px-3 py-1.5 text-sm w-full bg-transparent border dark:border-gray-600 outline-none rounded-lg"
|
||||
placeholder={`FROM llama2\nPARAMETER temperature 1\nSYSTEM """\nYou are Mario from Super Mario Bros, acting as an assistant.\n"""`}
|
||||
rows="6"
|
||||
bind:value={content}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="text-xs text-gray-400 dark:text-gray-500">
|
||||
{$i18n.t('Not sure what to write? Switch to')}
|
||||
<button
|
||||
class="text-gray-500 dark:text-gray-300 font-medium cursor-pointer"
|
||||
type="button"
|
||||
on:click={() => {
|
||||
raw = !raw;
|
||||
}}>{$i18n.t('Builder Mode')}</button
|
||||
>
|
||||
or
|
||||
<a
|
||||
class=" text-gray-500 dark:text-gray-300 font-medium"
|
||||
href="https://openwebui.com"
|
||||
target="_blank"
|
||||
>
|
||||
{$i18n.t('Click here to check other modelfiles.')}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
{:else}
|
||||
<div class="my-2">
|
||||
<div class=" text-xs font-semibold mb-2">{$i18n.t('From (Base Model)')}*</div>
|
||||
|
||||
<div>
|
||||
<input
|
||||
class="px-3 py-1.5 text-sm w-full bg-transparent border dark:border-gray-600 outline-none rounded-lg"
|
||||
placeholder="Write a modelfile base model name (e.g. llama2, mistral)"
|
||||
bind:value={model}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="mt-1 text-xs text-gray-400 dark:text-gray-500">
|
||||
{$i18n.t('To access the available model names for downloading,')}
|
||||
<a
|
||||
class=" text-gray-500 dark:text-gray-300 font-medium"
|
||||
href="https://ollama.com/library"
|
||||
target="_blank">{$i18n.t('click here.')}</a
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="my-1">
|
||||
<div class=" text-xs font-semibold mb-2">{$i18n.t('System Prompt')}</div>
|
||||
|
||||
<div>
|
||||
<textarea
|
||||
class="px-3 py-1.5 text-sm w-full bg-transparent border dark:border-gray-600 outline-none rounded-lg -mb-1"
|
||||
placeholder={`Write your modelfile system prompt content here\ne.g.) You are Mario from Super Mario Bros, acting as an assistant.`}
|
||||
rows="4"
|
||||
bind:value={system}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex w-full justify-between">
|
||||
<div class=" self-center text-sm font-semibold">
|
||||
{$i18n.t('Modelfile Advanced Settings')}
|
||||
</div>
|
||||
|
||||
<button
|
||||
class="p-1 px-3 text-xs flex rounded transition"
|
||||
type="button"
|
||||
on:click={() => {
|
||||
advanced = !advanced;
|
||||
}}
|
||||
>
|
||||
{#if advanced}
|
||||
<span class="ml-2 self-center">{$i18n.t('Custom')}</span>
|
||||
{:else}
|
||||
<span class="ml-2 self-center">{$i18n.t('Default')}</span>
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{#if advanced}
|
||||
<div class="my-2">
|
||||
<div class=" text-xs font-semibold mb-2">{$i18n.t('Template')}</div>
|
||||
|
||||
<div>
|
||||
<textarea
|
||||
class="px-3 py-1.5 text-sm w-full bg-transparent border dark:border-gray-600 outline-none rounded-lg -mb-1"
|
||||
placeholder="Write your modelfile template content here"
|
||||
rows="4"
|
||||
bind:value={template}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="my-2">
|
||||
<div class=" text-xs font-semibold mb-2">{$i18n.t('Parameters')}</div>
|
||||
|
||||
<div>
|
||||
<AdvancedParams bind:options />
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<div class="my-2">
|
||||
<div class="flex w-full justify-between mb-2">
|
||||
<div class=" self-center text-sm font-semibold">{$i18n.t('Prompt suggestions')}</div>
|
||||
|
||||
<button
|
||||
class="p-1 px-3 text-xs flex rounded transition"
|
||||
type="button"
|
||||
on:click={() => {
|
||||
if (suggestions.length === 0 || suggestions.at(-1).content !== '') {
|
||||
suggestions = [...suggestions, { content: '' }];
|
||||
}
|
||||
}}
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 20 20"
|
||||
fill="currentColor"
|
||||
class="w-4 h-4"
|
||||
>
|
||||
<path
|
||||
d="M10.75 4.75a.75.75 0 00-1.5 0v4.5h-4.5a.75.75 0 000 1.5h4.5v4.5a.75.75 0 001.5 0v-4.5h4.5a.75.75 0 000-1.5h-4.5v-4.5z"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<div class="flex flex-col space-y-1">
|
||||
{#each suggestions as prompt, promptIdx}
|
||||
<div class=" flex border dark:border-gray-600 rounded-lg">
|
||||
<input
|
||||
class="px-3 py-1.5 text-sm w-full bg-transparent outline-none border-r dark:border-gray-600"
|
||||
placeholder={$i18n.t('Write a prompt suggestion (e.g. Who are you?)')}
|
||||
bind:value={prompt.content}
|
||||
/>
|
||||
|
||||
<button
|
||||
class="px-2"
|
||||
type="button"
|
||||
on:click={() => {
|
||||
suggestions.splice(promptIdx, 1);
|
||||
suggestions = suggestions;
|
||||
}}
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 20 20"
|
||||
fill="currentColor"
|
||||
class="w-4 h-4"
|
||||
>
|
||||
<path
|
||||
d="M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="my-2">
|
||||
<div class=" text-sm font-semibold mb-2">{$i18n.t('Categories')}</div>
|
||||
|
||||
<div class="grid grid-cols-4">
|
||||
{#each Object.keys(categories) as category}
|
||||
<div class="flex space-x-2 text-sm">
|
||||
<input type="checkbox" bind:checked={categories[category]} />
|
||||
<div class="capitalize">{category}</div>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{#if pullProgress !== null}
|
||||
<div class="my-2">
|
||||
<div class=" text-sm font-semibold mb-2">{$i18n.t('Pull Progress')}</div>
|
||||
<div class="w-full rounded-full dark:bg-gray-800">
|
||||
<div
|
||||
class="dark:bg-gray-600 bg-gray-500 text-xs font-medium text-gray-100 text-center p-0.5 leading-none rounded-full"
|
||||
style="width: {Math.max(15, pullProgress ?? 0)}%"
|
||||
>
|
||||
{pullProgress ?? 0}%
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-1 text-xs dark:text-gray-500" style="font-size: 0.5rem;">
|
||||
{digest}
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<div class="my-2 flex justify-end">
|
||||
<button
|
||||
class=" text-sm px-3 py-2 transition rounded-xl {loading
|
||||
? ' cursor-not-allowed bg-gray-100 dark:bg-gray-800'
|
||||
: ' bg-gray-50 hover:bg-gray-100 dark:bg-gray-700 dark:hover:bg-gray-800'} flex"
|
||||
type="submit"
|
||||
disabled={loading}
|
||||
>
|
||||
<div class=" self-center font-medium">{$i18n.t('Save & Create')}</div>
|
||||
|
||||
{#if loading}
|
||||
<div class="ml-1.5 self-center">
|
||||
<svg
|
||||
class=" w-4 h-4"
|
||||
viewBox="0 0 24 24"
|
||||
fill="currentColor"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
><style>
|
||||
.spinner_ajPY {
|
||||
transform-origin: center;
|
||||
animation: spinner_AtaB 0.75s infinite linear;
|
||||
}
|
||||
@keyframes spinner_AtaB {
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
</style><path
|
||||
d="M12,1A11,11,0,1,0,23,12,11,11,0,0,0,12,1Zm0,19a8,8,0,1,1,8-8A8,8,0,0,1,12,20Z"
|
||||
opacity=".25"
|
||||
/><path
|
||||
d="M10.14,1.16a11,11,0,0,0-9,8.92A1.59,1.59,0,0,0,2.46,12,1.52,1.52,0,0,0,4.11,10.7a8,8,0,0,1,6.66-6.61A1.42,1.42,0,0,0,12,2.69h0A1.57,1.57,0,0,0,10.14,1.16Z"
|
||||
class="spinner_ajPY"
|
||||
/></svg
|
||||
>
|
||||
</div>
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
@ -1,507 +0,0 @@
|
||||
<script>
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import { toast } from 'svelte-sonner';
|
||||
import { goto } from '$app/navigation';
|
||||
|
||||
import { onMount, getContext } from 'svelte';
|
||||
import { page } from '$app/stores';
|
||||
|
||||
import { settings, user, config, modelfiles } from '$lib/stores';
|
||||
import { splitStream } from '$lib/utils';
|
||||
|
||||
import { createModel } from '$lib/apis/ollama';
|
||||
import { getModelfiles, updateModelfileByTagName } from '$lib/apis/modelfiles';
|
||||
|
||||
import AdvancedParams from '$lib/components/chat/Settings/Advanced/AdvancedParams.svelte';
|
||||
|
||||
const i18n = getContext('i18n');
|
||||
|
||||
let loading = false;
|
||||
|
||||
let filesInputElement;
|
||||
let inputFiles;
|
||||
let imageUrl = null;
|
||||
let digest = '';
|
||||
let pullProgress = null;
|
||||
let success = false;
|
||||
|
||||
let modelfile = null;
|
||||
// ///////////
|
||||
// Modelfile
|
||||
// ///////////
|
||||
|
||||
let title = '';
|
||||
let tagName = '';
|
||||
let desc = '';
|
||||
|
||||
// Raw Mode
|
||||
let content = '';
|
||||
|
||||
let suggestions = [
|
||||
{
|
||||
content: ''
|
||||
}
|
||||
];
|
||||
|
||||
let categories = {
|
||||
character: false,
|
||||
assistant: false,
|
||||
writing: false,
|
||||
productivity: false,
|
||||
programming: false,
|
||||
'data analysis': false,
|
||||
lifestyle: false,
|
||||
education: false,
|
||||
business: false
|
||||
};
|
||||
|
||||
onMount(() => {
|
||||
tagName = $page.url.searchParams.get('tag');
|
||||
|
||||
if (tagName) {
|
||||
modelfile = $modelfiles.filter((modelfile) => modelfile.tagName === tagName)[0];
|
||||
|
||||
console.log(modelfile);
|
||||
|
||||
imageUrl = modelfile.imageUrl;
|
||||
title = modelfile.title;
|
||||
desc = modelfile.desc;
|
||||
content = modelfile.content;
|
||||
suggestions =
|
||||
modelfile.suggestionPrompts.length != 0
|
||||
? modelfile.suggestionPrompts
|
||||
: [
|
||||
{
|
||||
content: ''
|
||||
}
|
||||
];
|
||||
|
||||
for (const category of modelfile.categories) {
|
||||
categories[category.toLowerCase()] = true;
|
||||
}
|
||||
} else {
|
||||
goto('/workspace/modelfiles');
|
||||
}
|
||||
});
|
||||
|
||||
const updateModelfile = async (modelfile) => {
|
||||
await updateModelfileByTagName(localStorage.token, modelfile.tagName, modelfile);
|
||||
await modelfiles.set(await getModelfiles(localStorage.token));
|
||||
};
|
||||
|
||||
const updateHandler = async () => {
|
||||
loading = true;
|
||||
|
||||
if (Object.keys(categories).filter((category) => categories[category]).length == 0) {
|
||||
toast.error(
|
||||
'Uh-oh! It looks like you missed selecting a category. Please choose one to complete your modelfile.'
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
title !== '' &&
|
||||
desc !== '' &&
|
||||
content !== '' &&
|
||||
Object.keys(categories).filter((category) => categories[category]).length > 0
|
||||
) {
|
||||
const res = await createModel(localStorage.token, tagName, content);
|
||||
|
||||
if (res) {
|
||||
const reader = res.body
|
||||
.pipeThrough(new TextDecoderStream())
|
||||
.pipeThrough(splitStream('\n'))
|
||||
.getReader();
|
||||
|
||||
while (true) {
|
||||
const { value, done } = await reader.read();
|
||||
if (done) break;
|
||||
|
||||
try {
|
||||
let lines = value.split('\n');
|
||||
|
||||
for (const line of lines) {
|
||||
if (line !== '') {
|
||||
console.log(line);
|
||||
let data = JSON.parse(line);
|
||||
console.log(data);
|
||||
|
||||
if (data.error) {
|
||||
throw data.error;
|
||||
}
|
||||
if (data.detail) {
|
||||
throw data.detail;
|
||||
}
|
||||
|
||||
if (data.status) {
|
||||
if (
|
||||
!data.digest &&
|
||||
!data.status.includes('writing') &&
|
||||
!data.status.includes('sha256')
|
||||
) {
|
||||
toast.success(data.status);
|
||||
|
||||
if (data.status === 'success') {
|
||||
success = true;
|
||||
}
|
||||
} else {
|
||||
if (data.digest) {
|
||||
digest = data.digest;
|
||||
|
||||
if (data.completed) {
|
||||
pullProgress = Math.round((data.completed / data.total) * 1000) / 10;
|
||||
} else {
|
||||
pullProgress = 100;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
toast.error(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (success) {
|
||||
await updateModelfile({
|
||||
tagName: tagName,
|
||||
imageUrl: imageUrl,
|
||||
title: title,
|
||||
desc: desc,
|
||||
content: content,
|
||||
suggestionPrompts: suggestions.filter((prompt) => prompt.content !== ''),
|
||||
categories: Object.keys(categories).filter((category) => categories[category])
|
||||
});
|
||||
await goto('/workspace/modelfiles');
|
||||
}
|
||||
}
|
||||
loading = false;
|
||||
success = false;
|
||||
};
|
||||
</script>
|
||||
|
||||
<div class="w-full max-h-full">
|
||||
<input
|
||||
bind:this={filesInputElement}
|
||||
bind:files={inputFiles}
|
||||
type="file"
|
||||
hidden
|
||||
accept="image/*"
|
||||
on:change={() => {
|
||||
let reader = new FileReader();
|
||||
reader.onload = (event) => {
|
||||
let originalImageUrl = `${event.target.result}`;
|
||||
|
||||
const img = new Image();
|
||||
img.src = originalImageUrl;
|
||||
|
||||
img.onload = function () {
|
||||
const canvas = document.createElement('canvas');
|
||||
const ctx = canvas.getContext('2d');
|
||||
|
||||
// Calculate the aspect ratio of the image
|
||||
const aspectRatio = img.width / img.height;
|
||||
|
||||
// Calculate the new width and height to fit within 100x100
|
||||
let newWidth, newHeight;
|
||||
if (aspectRatio > 1) {
|
||||
newWidth = 100 * aspectRatio;
|
||||
newHeight = 100;
|
||||
} else {
|
||||
newWidth = 100;
|
||||
newHeight = 100 / aspectRatio;
|
||||
}
|
||||
|
||||
// Set the canvas size
|
||||
canvas.width = 100;
|
||||
canvas.height = 100;
|
||||
|
||||
// Calculate the position to center the image
|
||||
const offsetX = (100 - newWidth) / 2;
|
||||
const offsetY = (100 - newHeight) / 2;
|
||||
|
||||
// Draw the image on the canvas
|
||||
ctx.drawImage(img, offsetX, offsetY, newWidth, newHeight);
|
||||
|
||||
// Get the base64 representation of the compressed image
|
||||
const compressedSrc = canvas.toDataURL('image/jpeg');
|
||||
|
||||
// Display the compressed image
|
||||
imageUrl = compressedSrc;
|
||||
|
||||
inputFiles = null;
|
||||
};
|
||||
};
|
||||
|
||||
if (
|
||||
inputFiles &&
|
||||
inputFiles.length > 0 &&
|
||||
['image/gif', 'image/webp', 'image/jpeg', 'image/png'].includes(inputFiles[0]['type'])
|
||||
) {
|
||||
reader.readAsDataURL(inputFiles[0]);
|
||||
} else {
|
||||
console.log(`Unsupported File Type '${inputFiles[0]['type']}'.`);
|
||||
inputFiles = null;
|
||||
}
|
||||
}}
|
||||
/>
|
||||
|
||||
<button
|
||||
class="flex space-x-1"
|
||||
on:click={() => {
|
||||
history.back();
|
||||
}}
|
||||
>
|
||||
<div class=" self-center">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 20 20"
|
||||
fill="currentColor"
|
||||
class="w-4 h-4"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
d="M17 10a.75.75 0 01-.75.75H5.612l4.158 3.96a.75.75 0 11-1.04 1.08l-5.5-5.25a.75.75 0 010-1.08l5.5-5.25a.75.75 0 111.04 1.08L5.612 9.25H16.25A.75.75 0 0117 10z"
|
||||
clip-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class=" self-center font-medium text-sm">{$i18n.t('Back')}</div>
|
||||
</button>
|
||||
<form
|
||||
class="flex flex-col max-w-2xl mx-auto mt-4 mb-10"
|
||||
on:submit|preventDefault={() => {
|
||||
updateHandler();
|
||||
}}
|
||||
>
|
||||
<div class="flex justify-center my-4">
|
||||
<div class="self-center">
|
||||
<button
|
||||
class=" {imageUrl
|
||||
? ''
|
||||
: 'p-6'} rounded-full dark:bg-gray-700 border border-dashed border-gray-200"
|
||||
type="button"
|
||||
on:click={() => {
|
||||
filesInputElement.click();
|
||||
}}
|
||||
>
|
||||
{#if imageUrl}
|
||||
<img
|
||||
src={imageUrl}
|
||||
alt="modelfile profile"
|
||||
class=" rounded-full w-20 h-20 object-cover"
|
||||
/>
|
||||
{:else}
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
fill="currentColor"
|
||||
class="w-8"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
d="M12 3.75a.75.75 0 01.75.75v6.75h6.75a.75.75 0 010 1.5h-6.75v6.75a.75.75 0 01-1.5 0v-6.75H4.5a.75.75 0 010-1.5h6.75V4.5a.75.75 0 01.75-.75z"
|
||||
clip-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="my-2 flex space-x-2">
|
||||
<div class="flex-1">
|
||||
<div class=" text-sm font-semibold mb-2">{$i18n.t('Name')}*</div>
|
||||
|
||||
<div>
|
||||
<input
|
||||
class="px-3 py-1.5 text-sm w-full bg-transparent border dark:border-gray-600 outline-none rounded-lg"
|
||||
placeholder={$i18n.t('Name your modelfile')}
|
||||
bind:value={title}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex-1">
|
||||
<div class=" text-sm font-semibold mb-2">{$i18n.t('Model Tag Name')}*</div>
|
||||
|
||||
<div>
|
||||
<input
|
||||
class="px-3 py-1.5 text-sm w-full bg-transparent disabled:text-gray-500 border dark:border-gray-600 outline-none rounded-lg"
|
||||
placeholder={$i18n.t('Add a model tag name')}
|
||||
value={tagName}
|
||||
disabled
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="my-2">
|
||||
<div class=" text-sm font-semibold mb-2">{$i18n.t('Description')}*</div>
|
||||
|
||||
<div>
|
||||
<input
|
||||
class="px-3 py-1.5 text-sm w-full bg-transparent border dark:border-gray-600 outline-none rounded-lg"
|
||||
placeholder={$i18n.t('Add a short description about what this modelfile does')}
|
||||
bind:value={desc}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="my-2">
|
||||
<div class="flex w-full justify-between">
|
||||
<div class=" self-center text-sm font-semibold">{$i18n.t('Modelfile')}</div>
|
||||
</div>
|
||||
|
||||
<!-- <div class=" text-sm font-semibold mb-2"></div> -->
|
||||
|
||||
<div class="mt-2">
|
||||
<div class=" text-xs font-semibold mb-2">{$i18n.t('Content')}*</div>
|
||||
|
||||
<div>
|
||||
<textarea
|
||||
class="px-3 py-1.5 text-sm w-full bg-transparent border dark:border-gray-600 outline-none rounded-lg"
|
||||
placeholder={`FROM llama2\nPARAMETER temperature 1\nSYSTEM """\nYou are Mario from Super Mario Bros, acting as an assistant.\n"""`}
|
||||
rows="6"
|
||||
bind:value={content}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="my-2">
|
||||
<div class="flex w-full justify-between mb-2">
|
||||
<div class=" self-center text-sm font-semibold">{$i18n.t('Prompt suggestions')}</div>
|
||||
|
||||
<button
|
||||
class="p-1 px-3 text-xs flex rounded transition"
|
||||
type="button"
|
||||
on:click={() => {
|
||||
if (suggestions.length === 0 || suggestions.at(-1).content !== '') {
|
||||
suggestions = [...suggestions, { content: '' }];
|
||||
}
|
||||
}}
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 20 20"
|
||||
fill="currentColor"
|
||||
class="w-4 h-4"
|
||||
>
|
||||
<path
|
||||
d="M10.75 4.75a.75.75 0 00-1.5 0v4.5h-4.5a.75.75 0 000 1.5h4.5v4.5a.75.75 0 001.5 0v-4.5h4.5a.75.75 0 000-1.5h-4.5v-4.5z"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<div class="flex flex-col space-y-1">
|
||||
{#each suggestions as prompt, promptIdx}
|
||||
<div class=" flex border dark:border-gray-600 rounded-lg">
|
||||
<input
|
||||
class="px-3 py-1.5 text-sm w-full bg-transparent outline-none border-r dark:border-gray-600"
|
||||
placeholder={$i18n.t('Write a prompt suggestion (e.g. Who are you?)')}
|
||||
bind:value={prompt.content}
|
||||
/>
|
||||
|
||||
<button
|
||||
class="px-2"
|
||||
type="button"
|
||||
on:click={() => {
|
||||
suggestions.splice(promptIdx, 1);
|
||||
suggestions = suggestions;
|
||||
}}
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 20 20"
|
||||
fill="currentColor"
|
||||
class="w-4 h-4"
|
||||
>
|
||||
<path
|
||||
d="M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="my-2">
|
||||
<div class=" text-sm font-semibold mb-2">{$i18n.t('Categories')}</div>
|
||||
|
||||
<div class="grid grid-cols-4">
|
||||
{#each Object.keys(categories) as category}
|
||||
<div class="flex space-x-2 text-sm">
|
||||
<input type="checkbox" bind:checked={categories[category]} />
|
||||
|
||||
<div class=" capitalize">{category}</div>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{#if pullProgress !== null}
|
||||
<div class="my-2">
|
||||
<div class=" text-sm font-semibold mb-2">{$i18n.t('Pull Progress')}</div>
|
||||
<div class="w-full rounded-full dark:bg-gray-800">
|
||||
<div
|
||||
class="dark:bg-gray-600 text-xs font-medium text-blue-100 text-center p-0.5 leading-none rounded-full"
|
||||
style="width: {Math.max(15, pullProgress ?? 0)}%"
|
||||
>
|
||||
{pullProgress ?? 0}%
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-1 text-xs dark:text-gray-500" style="font-size: 0.5rem;">
|
||||
{digest}
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<div class="my-2 flex justify-end">
|
||||
<button
|
||||
class=" text-sm px-3 py-2 transition rounded-xl {loading
|
||||
? ' cursor-not-allowed bg-gray-100 dark:bg-gray-800'
|
||||
: ' bg-gray-50 hover:bg-gray-100 dark:bg-gray-700 dark:hover:bg-gray-800'} flex"
|
||||
type="submit"
|
||||
disabled={loading}
|
||||
>
|
||||
<div class=" self-center font-medium">{$i18n.t('Save & Update')}</div>
|
||||
|
||||
{#if loading}
|
||||
<div class="ml-1.5 self-center">
|
||||
<svg
|
||||
class=" w-4 h-4"
|
||||
viewBox="0 0 24 24"
|
||||
fill="currentColor"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
><style>
|
||||
.spinner_ajPY {
|
||||
transform-origin: center;
|
||||
animation: spinner_AtaB 0.75s infinite linear;
|
||||
}
|
||||
@keyframes spinner_AtaB {
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
</style><path
|
||||
d="M12,1A11,11,0,1,0,23,12,11,11,0,0,0,12,1Zm0,19a8,8,0,1,1,8-8A8,8,0,0,1,12,20Z"
|
||||
opacity=".25"
|
||||
/><path
|
||||
d="M10.14,1.16a11,11,0,0,0-9,8.92A1.59,1.59,0,0,0,2.46,12,1.52,1.52,0,0,0,4.11,10.7a8,8,0,0,1,6.66-6.61A1.42,1.42,0,0,0,12,2.69h0A1.57,1.57,0,0,0,10.14,1.16Z"
|
||||
class="spinner_ajPY"
|
||||
/></svg
|
||||
>
|
||||
</div>
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
5
src/routes/(app)/workspace/models/+page.svelte
Normal file
5
src/routes/(app)/workspace/models/+page.svelte
Normal file
@ -0,0 +1,5 @@
|
||||
<script>
|
||||
import Models from '$lib/components/workspace/Models.svelte';
|
||||
</script>
|
||||
|
||||
<Models />
|
576
src/routes/(app)/workspace/models/create/+page.svelte
Normal file
576
src/routes/(app)/workspace/models/create/+page.svelte
Normal file
@ -0,0 +1,576 @@
|
||||
<script>
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import { toast } from 'svelte-sonner';
|
||||
import { goto } from '$app/navigation';
|
||||
import { settings, user, config, models } from '$lib/stores';
|
||||
|
||||
import { onMount, tick, getContext } from 'svelte';
|
||||
import { addNewModel, getModelById, getModelInfos } from '$lib/apis/models';
|
||||
import { getModels } from '$lib/apis';
|
||||
|
||||
import AdvancedParams from '$lib/components/chat/Settings/Advanced/AdvancedParams.svelte';
|
||||
import Checkbox from '$lib/components/common/Checkbox.svelte';
|
||||
|
||||
const i18n = getContext('i18n');
|
||||
|
||||
let filesInputElement;
|
||||
let inputFiles;
|
||||
|
||||
let showAdvanced = false;
|
||||
let showPreview = false;
|
||||
|
||||
let loading = false;
|
||||
let success = false;
|
||||
|
||||
// ///////////
|
||||
// Model
|
||||
// ///////////
|
||||
|
||||
let id = '';
|
||||
let name = '';
|
||||
|
||||
let params = {};
|
||||
let capabilities = {
|
||||
vision: true
|
||||
};
|
||||
|
||||
let info = {
|
||||
id: '',
|
||||
base_model_id: null,
|
||||
name: '',
|
||||
meta: {
|
||||
profile_image_url: null,
|
||||
description: '',
|
||||
suggestion_prompts: [
|
||||
{
|
||||
content: ''
|
||||
}
|
||||
]
|
||||
},
|
||||
params: {
|
||||
system: ''
|
||||
}
|
||||
};
|
||||
|
||||
$: if (name) {
|
||||
id = name.replace(/\s+/g, '-').toLowerCase();
|
||||
}
|
||||
|
||||
const submitHandler = async () => {
|
||||
loading = true;
|
||||
|
||||
info.id = id;
|
||||
info.name = name;
|
||||
info.meta.capabilities = capabilities;
|
||||
info.params.stop = params.stop !== null ? params.stop.split(',').filter((s) => s.trim()) : null;
|
||||
|
||||
if ($models.find((m) => m.id === info.id)) {
|
||||
toast.error(
|
||||
`Error: A model with the ID '${info.id}' already exists. Please select a different ID to proceed.`
|
||||
);
|
||||
loading = false;
|
||||
success = false;
|
||||
return success;
|
||||
}
|
||||
|
||||
if (info) {
|
||||
const res = await addNewModel(localStorage.token, {
|
||||
...info,
|
||||
meta: {
|
||||
...info.meta,
|
||||
profile_image_url: info.meta.profile_image_url ?? '/favicon.png',
|
||||
suggestion_prompts: info.meta.suggestion_prompts
|
||||
? info.meta.suggestion_prompts.filter((prompt) => prompt.content !== '')
|
||||
: null
|
||||
},
|
||||
params: { ...info.params, ...params }
|
||||
});
|
||||
|
||||
if (res) {
|
||||
toast.success('Model created successfully!');
|
||||
await goto('/workspace/models');
|
||||
await models.set(await getModels(localStorage.token));
|
||||
}
|
||||
}
|
||||
|
||||
loading = false;
|
||||
success = false;
|
||||
};
|
||||
|
||||
const initModel = async (model) => {
|
||||
name = model.name;
|
||||
await tick();
|
||||
|
||||
id = model.id;
|
||||
|
||||
params = { ...params, ...model?.info?.params };
|
||||
params.stop = params?.stop ? (params?.stop ?? []).join(',') : null;
|
||||
|
||||
capabilities = { ...capabilities, ...(model?.info?.meta?.capabilities ?? {}) };
|
||||
|
||||
info = {
|
||||
...info,
|
||||
...model.info
|
||||
};
|
||||
};
|
||||
|
||||
onMount(async () => {
|
||||
window.addEventListener('message', async (event) => {
|
||||
if (
|
||||
![
|
||||
'https://ollamahub.com',
|
||||
'https://www.ollamahub.com',
|
||||
'https://openwebui.com',
|
||||
'https://www.openwebui.com',
|
||||
'http://localhost:5173'
|
||||
].includes(event.origin)
|
||||
)
|
||||
return;
|
||||
|
||||
const model = JSON.parse(event.data);
|
||||
console.log(model);
|
||||
|
||||
initModel(model);
|
||||
});
|
||||
|
||||
if (window.opener ?? false) {
|
||||
window.opener.postMessage('loaded', '*');
|
||||
}
|
||||
|
||||
if (sessionStorage.model) {
|
||||
const model = JSON.parse(sessionStorage.model);
|
||||
sessionStorage.removeItem('model');
|
||||
|
||||
console.log(model);
|
||||
initModel(model);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="w-full max-h-full">
|
||||
<input
|
||||
bind:this={filesInputElement}
|
||||
bind:files={inputFiles}
|
||||
type="file"
|
||||
hidden
|
||||
accept="image/*"
|
||||
on:change={() => {
|
||||
let reader = new FileReader();
|
||||
reader.onload = (event) => {
|
||||
let originalImageUrl = `${event.target.result}`;
|
||||
|
||||
const img = new Image();
|
||||
img.src = originalImageUrl;
|
||||
|
||||
img.onload = function () {
|
||||
const canvas = document.createElement('canvas');
|
||||
const ctx = canvas.getContext('2d');
|
||||
|
||||
// Calculate the aspect ratio of the image
|
||||
const aspectRatio = img.width / img.height;
|
||||
|
||||
// Calculate the new width and height to fit within 100x100
|
||||
let newWidth, newHeight;
|
||||
if (aspectRatio > 1) {
|
||||
newWidth = 100 * aspectRatio;
|
||||
newHeight = 100;
|
||||
} else {
|
||||
newWidth = 100;
|
||||
newHeight = 100 / aspectRatio;
|
||||
}
|
||||
|
||||
// Set the canvas size
|
||||
canvas.width = 100;
|
||||
canvas.height = 100;
|
||||
|
||||
// Calculate the position to center the image
|
||||
const offsetX = (100 - newWidth) / 2;
|
||||
const offsetY = (100 - newHeight) / 2;
|
||||
|
||||
// Draw the image on the canvas
|
||||
ctx.drawImage(img, offsetX, offsetY, newWidth, newHeight);
|
||||
|
||||
// Get the base64 representation of the compressed image
|
||||
const compressedSrc = canvas.toDataURL('image/jpeg');
|
||||
|
||||
// Display the compressed image
|
||||
info.meta.profile_image_url = compressedSrc;
|
||||
|
||||
inputFiles = null;
|
||||
};
|
||||
};
|
||||
|
||||
if (
|
||||
inputFiles &&
|
||||
inputFiles.length > 0 &&
|
||||
['image/gif', 'image/webp', 'image/jpeg', 'image/png'].includes(inputFiles[0]['type'])
|
||||
) {
|
||||
reader.readAsDataURL(inputFiles[0]);
|
||||
} else {
|
||||
console.log(`Unsupported File Type '${inputFiles[0]['type']}'.`);
|
||||
inputFiles = null;
|
||||
}
|
||||
}}
|
||||
/>
|
||||
|
||||
<button
|
||||
class="flex space-x-1"
|
||||
on:click={() => {
|
||||
history.back();
|
||||
}}
|
||||
>
|
||||
<div class=" self-center">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 20 20"
|
||||
fill="currentColor"
|
||||
class="w-4 h-4"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
d="M17 10a.75.75 0 01-.75.75H5.612l4.158 3.96a.75.75 0 11-1.04 1.08l-5.5-5.25a.75.75 0 010-1.08l5.5-5.25a.75.75 0 111.04 1.08L5.612 9.25H16.25A.75.75 0 0117 10z"
|
||||
clip-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class=" self-center font-medium text-sm">{$i18n.t('Back')}</div>
|
||||
</button>
|
||||
<!-- <hr class="my-3 dark:border-gray-700" /> -->
|
||||
|
||||
<form
|
||||
class="flex flex-col max-w-2xl mx-auto mt-4 mb-10"
|
||||
on:submit|preventDefault={() => {
|
||||
submitHandler();
|
||||
}}
|
||||
>
|
||||
<div class="flex justify-center my-4">
|
||||
<div class="self-center">
|
||||
<button
|
||||
class=" {info.meta.profile_image_url
|
||||
? ''
|
||||
: 'p-6'} rounded-full dark:bg-gray-700 border border-dashed border-gray-200"
|
||||
type="button"
|
||||
on:click={() => {
|
||||
filesInputElement.click();
|
||||
}}
|
||||
>
|
||||
{#if info.meta.profile_image_url}
|
||||
<img
|
||||
src={info.meta.profile_image_url}
|
||||
alt="modelfile profile"
|
||||
class=" rounded-full w-20 h-20 object-cover"
|
||||
/>
|
||||
{:else}
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
fill="currentColor"
|
||||
class="size-8"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
d="M12 3.75a.75.75 0 01.75.75v6.75h6.75a.75.75 0 010 1.5h-6.75v6.75a.75.75 0 01-1.5 0v-6.75H4.5a.75.75 0 010-1.5h6.75V4.5a.75.75 0 01.75-.75z"
|
||||
clip-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="my-2 flex space-x-2">
|
||||
<div class="flex-1">
|
||||
<div class=" text-sm font-semibold mb-2">{$i18n.t('Name')}*</div>
|
||||
|
||||
<div>
|
||||
<input
|
||||
class="px-3 py-1.5 text-sm w-full bg-transparent border dark:border-gray-600 outline-none rounded-lg"
|
||||
placeholder={$i18n.t('Name your model')}
|
||||
bind:value={name}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex-1">
|
||||
<div class=" text-sm font-semibold mb-2">{$i18n.t('Model ID')}*</div>
|
||||
|
||||
<div>
|
||||
<input
|
||||
class="px-3 py-1.5 text-sm w-full bg-transparent border dark:border-gray-600 outline-none rounded-lg"
|
||||
placeholder={$i18n.t('Add a model id')}
|
||||
bind:value={id}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="my-2">
|
||||
<div class=" text-sm font-semibold mb-2">{$i18n.t('Base Model (From)')}</div>
|
||||
|
||||
<div>
|
||||
<select
|
||||
class="px-3 py-1.5 text-sm w-full bg-transparent border dark:border-gray-600 outline-none rounded-lg"
|
||||
placeholder="Select a base model (e.g. llama3, gpt-4o)"
|
||||
bind:value={info.base_model_id}
|
||||
required
|
||||
>
|
||||
<option value={null} class=" placeholder:text-gray-500"
|
||||
>{$i18n.t('Select a base model')}</option
|
||||
>
|
||||
{#each $models.filter((m) => !m?.preset) as model}
|
||||
<option value={model.id}>{model.name}</option>
|
||||
{/each}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="my-2">
|
||||
<div class=" text-sm font-semibold mb-2">{$i18n.t('Description')}</div>
|
||||
|
||||
<div>
|
||||
<input
|
||||
class="px-3 py-1.5 text-sm w-full bg-transparent border dark:border-gray-600 outline-none rounded-lg"
|
||||
placeholder={$i18n.t('Add a short description about what this model does')}
|
||||
bind:value={info.meta.description}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="my-2">
|
||||
<div class="flex w-full justify-between">
|
||||
<div class=" self-center text-sm font-semibold">{$i18n.t('Model Params')}</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-2">
|
||||
<div class="my-1">
|
||||
<div class=" text-xs font-semibold mb-2">{$i18n.t('System Prompt')}</div>
|
||||
<div>
|
||||
<textarea
|
||||
class="px-3 py-1.5 text-sm w-full bg-transparent border dark:border-gray-600 outline-none rounded-lg -mb-1"
|
||||
placeholder={`Write your model system prompt content here\ne.g.) You are Mario from Super Mario Bros, acting as an assistant.`}
|
||||
rows="4"
|
||||
bind:value={info.params.system}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex w-full justify-between">
|
||||
<div class=" self-center text-xs font-semibold">
|
||||
{$i18n.t('Advanced Params')}
|
||||
</div>
|
||||
|
||||
<button
|
||||
class="p-1 px-3 text-xs flex rounded transition"
|
||||
type="button"
|
||||
on:click={() => {
|
||||
showAdvanced = !showAdvanced;
|
||||
}}
|
||||
>
|
||||
{#if showAdvanced}
|
||||
<span class="ml-2 self-center">{$i18n.t('Hide')}</span>
|
||||
{:else}
|
||||
<span class="ml-2 self-center">{$i18n.t('Show')}</span>
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{#if showAdvanced}
|
||||
<div class="my-2">
|
||||
<AdvancedParams
|
||||
bind:params
|
||||
on:change={(e) => {
|
||||
info.params = { ...info.params, ...params };
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="my-2">
|
||||
<div class="flex w-full justify-between items-center">
|
||||
<div class="flex w-full justify-between items-center">
|
||||
<div class=" self-center text-sm font-semibold">{$i18n.t('Prompt suggestions')}</div>
|
||||
|
||||
<button
|
||||
class="p-1 text-xs flex rounded transition"
|
||||
type="button"
|
||||
on:click={() => {
|
||||
if (info.meta.suggestion_prompts === null) {
|
||||
info.meta.suggestion_prompts = [{ content: '' }];
|
||||
} else {
|
||||
info.meta.suggestion_prompts = null;
|
||||
}
|
||||
}}
|
||||
>
|
||||
{#if info.meta.suggestion_prompts === null}
|
||||
<span class="ml-2 self-center">{$i18n.t('Default')}</span>
|
||||
{:else}
|
||||
<span class="ml-2 self-center">{$i18n.t('Custom')}</span>
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{#if info.meta.suggestion_prompts !== null}
|
||||
<button
|
||||
class="p-1 px-2 text-xs flex rounded transition"
|
||||
type="button"
|
||||
on:click={() => {
|
||||
if (
|
||||
info.meta.suggestion_prompts.length === 0 ||
|
||||
info.meta.suggestion_prompts.at(-1).content !== ''
|
||||
) {
|
||||
info.meta.suggestion_prompts = [...info.meta.suggestion_prompts, { content: '' }];
|
||||
}
|
||||
}}
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 20 20"
|
||||
fill="currentColor"
|
||||
class="w-4 h-4"
|
||||
>
|
||||
<path
|
||||
d="M10.75 4.75a.75.75 0 00-1.5 0v4.5h-4.5a.75.75 0 000 1.5h4.5v4.5a.75.75 0 001.5 0v-4.5h4.5a.75.75 0 000-1.5h-4.5v-4.5z"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
{#if info.meta.suggestion_prompts}
|
||||
<div class="flex flex-col space-y-1 mt-2">
|
||||
{#if info.meta.suggestion_prompts.length > 0}
|
||||
{#each info.meta.suggestion_prompts as prompt, promptIdx}
|
||||
<div class=" flex border dark:border-gray-600 rounded-lg">
|
||||
<input
|
||||
class="px-3 py-1.5 text-sm w-full bg-transparent outline-none border-r dark:border-gray-600"
|
||||
placeholder={$i18n.t('Write a prompt suggestion (e.g. Who are you?)')}
|
||||
bind:value={prompt.content}
|
||||
/>
|
||||
|
||||
<button
|
||||
class="px-2"
|
||||
type="button"
|
||||
on:click={() => {
|
||||
info.meta.suggestion_prompts.splice(promptIdx, 1);
|
||||
info.meta.suggestion_prompts = info.meta.suggestion_prompts;
|
||||
}}
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 20 20"
|
||||
fill="currentColor"
|
||||
class="w-4 h-4"
|
||||
>
|
||||
<path
|
||||
d="M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
{/each}
|
||||
{:else}
|
||||
<div class="text-xs text-center">No suggestion prompts</div>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<div class="my-2">
|
||||
<div class="flex w-full justify-between">
|
||||
<div class=" self-center text-sm font-semibold">{$i18n.t('Capabilities')}</div>
|
||||
</div>
|
||||
<div class="flex flex-col">
|
||||
{#each Object.keys(capabilities) as capability}
|
||||
<div class=" flex items-center gap-2">
|
||||
<Checkbox
|
||||
state={capabilities[capability] ? 'checked' : 'unchecked'}
|
||||
on:change={(e) => {
|
||||
capabilities[capability] = e.detail === 'checked';
|
||||
}}
|
||||
/>
|
||||
|
||||
<div class=" py-1.5 text-sm w-full capitalize">
|
||||
{$i18n.t(capability)}
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="my-2 text-gray-500">
|
||||
<div class="flex w-full justify-between mb-2">
|
||||
<div class=" self-center text-sm font-semibold">{$i18n.t('JSON Preview')}</div>
|
||||
|
||||
<button
|
||||
class="p-1 px-3 text-xs flex rounded transition"
|
||||
type="button"
|
||||
on:click={() => {
|
||||
showPreview = !showPreview;
|
||||
}}
|
||||
>
|
||||
{#if showPreview}
|
||||
<span class="ml-2 self-center">{$i18n.t('Hide')}</span>
|
||||
{:else}
|
||||
<span class="ml-2 self-center">{$i18n.t('Show')}</span>
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{#if showPreview}
|
||||
<div>
|
||||
<textarea
|
||||
class="px-3 py-1.5 text-sm w-full bg-transparent border dark:border-gray-600 outline-none rounded-lg"
|
||||
rows="10"
|
||||
value={JSON.stringify(info, null, 2)}
|
||||
disabled
|
||||
readonly
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<div class="my-2 flex justify-end mb-20">
|
||||
<button
|
||||
class=" text-sm px-3 py-2 transition rounded-xl {loading
|
||||
? ' cursor-not-allowed bg-gray-100 dark:bg-gray-800'
|
||||
: ' bg-gray-50 hover:bg-gray-100 dark:bg-gray-700 dark:hover:bg-gray-800'} flex"
|
||||
type="submit"
|
||||
disabled={loading}
|
||||
>
|
||||
<div class=" self-center font-medium">{$i18n.t('Save & Create')}</div>
|
||||
|
||||
{#if loading}
|
||||
<div class="ml-1.5 self-center">
|
||||
<svg
|
||||
class=" w-4 h-4"
|
||||
viewBox="0 0 24 24"
|
||||
fill="currentColor"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
><style>
|
||||
.spinner_ajPY {
|
||||
transform-origin: center;
|
||||
animation: spinner_AtaB 0.75s infinite linear;
|
||||
}
|
||||
@keyframes spinner_AtaB {
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
</style><path
|
||||
d="M12,1A11,11,0,1,0,23,12,11,11,0,0,0,12,1Zm0,19a8,8,0,1,1,8-8A8,8,0,0,1,12,20Z"
|
||||
opacity=".25"
|
||||
/><path
|
||||
d="M10.14,1.16a11,11,0,0,0-9,8.92A1.59,1.59,0,0,0,2.46,12,1.52,1.52,0,0,0,4.11,10.7a8,8,0,0,1,6.66-6.61A1.42,1.42,0,0,0,12,2.69h0A1.57,1.57,0,0,0,10.14,1.16Z"
|
||||
class="spinner_ajPY"
|
||||
/></svg
|
||||
>
|
||||
</div>
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
555
src/routes/(app)/workspace/models/edit/+page.svelte
Normal file
555
src/routes/(app)/workspace/models/edit/+page.svelte
Normal file
@ -0,0 +1,555 @@
|
||||
<script>
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import { toast } from 'svelte-sonner';
|
||||
import { goto } from '$app/navigation';
|
||||
|
||||
import { onMount, getContext } from 'svelte';
|
||||
import { page } from '$app/stores';
|
||||
import { settings, user, config, models } from '$lib/stores';
|
||||
import { splitStream } from '$lib/utils';
|
||||
|
||||
import { getModelInfos, updateModelById } from '$lib/apis/models';
|
||||
|
||||
import AdvancedParams from '$lib/components/chat/Settings/Advanced/AdvancedParams.svelte';
|
||||
import { getModels } from '$lib/apis';
|
||||
import Checkbox from '$lib/components/common/Checkbox.svelte';
|
||||
|
||||
const i18n = getContext('i18n');
|
||||
|
||||
let loading = false;
|
||||
let success = false;
|
||||
|
||||
let filesInputElement;
|
||||
let inputFiles;
|
||||
|
||||
let digest = '';
|
||||
let pullProgress = null;
|
||||
|
||||
let showAdvanced = false;
|
||||
let showPreview = false;
|
||||
|
||||
// ///////////
|
||||
// model
|
||||
// ///////////
|
||||
|
||||
let model = null;
|
||||
|
||||
let id = '';
|
||||
let name = '';
|
||||
|
||||
let info = {
|
||||
id: '',
|
||||
base_model_id: null,
|
||||
name: '',
|
||||
meta: {
|
||||
profile_image_url: '/favicon.png',
|
||||
description: '',
|
||||
suggestion_prompts: null
|
||||
},
|
||||
params: {
|
||||
system: ''
|
||||
}
|
||||
};
|
||||
|
||||
let params = {};
|
||||
|
||||
let capabilities = {
|
||||
vision: true
|
||||
};
|
||||
|
||||
const updateHandler = async () => {
|
||||
loading = true;
|
||||
|
||||
info.id = id;
|
||||
info.name = name;
|
||||
info.meta.capabilities = capabilities;
|
||||
info.params.stop = params.stop !== null ? params.stop.split(',').filter((s) => s.trim()) : null;
|
||||
|
||||
const res = await updateModelById(localStorage.token, info.id, info);
|
||||
|
||||
if (res) {
|
||||
toast.success('Model updated successfully');
|
||||
await goto('/workspace/models');
|
||||
await models.set(await getModels(localStorage.token));
|
||||
}
|
||||
|
||||
loading = false;
|
||||
success = false;
|
||||
};
|
||||
|
||||
onMount(() => {
|
||||
const _id = $page.url.searchParams.get('id');
|
||||
|
||||
if (_id) {
|
||||
model = $models.find((m) => m.id === _id);
|
||||
if (model) {
|
||||
id = model.id;
|
||||
name = model.name;
|
||||
|
||||
info = {
|
||||
...info,
|
||||
...JSON.parse(
|
||||
JSON.stringify(
|
||||
model?.info
|
||||
? model?.info
|
||||
: {
|
||||
id: model.id,
|
||||
name: model.name
|
||||
}
|
||||
)
|
||||
)
|
||||
};
|
||||
|
||||
if (model.preset && model.owned_by === 'ollama' && !info.base_model_id.includes(':')) {
|
||||
info.base_model_id = `${info.base_model_id}:latest`;
|
||||
}
|
||||
|
||||
params = { ...params, ...model?.info?.params };
|
||||
params.stop = params?.stop ? (params?.stop ?? []).join(',') : null;
|
||||
|
||||
if (model?.info?.meta?.capabilities) {
|
||||
capabilities = { ...capabilities, ...model?.info?.meta?.capabilities };
|
||||
}
|
||||
console.log(model);
|
||||
} else {
|
||||
goto('/workspace/models');
|
||||
}
|
||||
} else {
|
||||
goto('/workspace/models');
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="w-full max-h-full">
|
||||
<input
|
||||
bind:this={filesInputElement}
|
||||
bind:files={inputFiles}
|
||||
type="file"
|
||||
hidden
|
||||
accept="image/*"
|
||||
on:change={() => {
|
||||
let reader = new FileReader();
|
||||
reader.onload = (event) => {
|
||||
let originalImageUrl = `${event.target.result}`;
|
||||
|
||||
const img = new Image();
|
||||
img.src = originalImageUrl;
|
||||
|
||||
img.onload = function () {
|
||||
const canvas = document.createElement('canvas');
|
||||
const ctx = canvas.getContext('2d');
|
||||
|
||||
// Calculate the aspect ratio of the image
|
||||
const aspectRatio = img.width / img.height;
|
||||
|
||||
// Calculate the new width and height to fit within 100x100
|
||||
let newWidth, newHeight;
|
||||
if (aspectRatio > 1) {
|
||||
newWidth = 100 * aspectRatio;
|
||||
newHeight = 100;
|
||||
} else {
|
||||
newWidth = 100;
|
||||
newHeight = 100 / aspectRatio;
|
||||
}
|
||||
|
||||
// Set the canvas size
|
||||
canvas.width = 100;
|
||||
canvas.height = 100;
|
||||
|
||||
// Calculate the position to center the image
|
||||
const offsetX = (100 - newWidth) / 2;
|
||||
const offsetY = (100 - newHeight) / 2;
|
||||
|
||||
// Draw the image on the canvas
|
||||
ctx.drawImage(img, offsetX, offsetY, newWidth, newHeight);
|
||||
|
||||
// Get the base64 representation of the compressed image
|
||||
const compressedSrc = canvas.toDataURL('image/jpeg');
|
||||
|
||||
// Display the compressed image
|
||||
info.meta.profile_image_url = compressedSrc;
|
||||
|
||||
inputFiles = null;
|
||||
};
|
||||
};
|
||||
|
||||
if (
|
||||
inputFiles &&
|
||||
inputFiles.length > 0 &&
|
||||
['image/gif', 'image/webp', 'image/jpeg', 'image/png'].includes(inputFiles[0]['type'])
|
||||
) {
|
||||
reader.readAsDataURL(inputFiles[0]);
|
||||
} else {
|
||||
console.log(`Unsupported File Type '${inputFiles[0]['type']}'.`);
|
||||
inputFiles = null;
|
||||
}
|
||||
}}
|
||||
/>
|
||||
|
||||
<button
|
||||
class="flex space-x-1"
|
||||
on:click={() => {
|
||||
history.back();
|
||||
}}
|
||||
>
|
||||
<div class=" self-center">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 20 20"
|
||||
fill="currentColor"
|
||||
class="w-4 h-4"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
d="M17 10a.75.75 0 01-.75.75H5.612l4.158 3.96a.75.75 0 11-1.04 1.08l-5.5-5.25a.75.75 0 010-1.08l5.5-5.25a.75.75 0 111.04 1.08L5.612 9.25H16.25A.75.75 0 0117 10z"
|
||||
clip-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class=" self-center font-medium text-sm">{$i18n.t('Back')}</div>
|
||||
</button>
|
||||
|
||||
{#if model}
|
||||
<form
|
||||
class="flex flex-col max-w-2xl mx-auto mt-4 mb-10"
|
||||
on:submit|preventDefault={() => {
|
||||
updateHandler();
|
||||
}}
|
||||
>
|
||||
<div class="flex justify-center my-4">
|
||||
<div class="self-center">
|
||||
<button
|
||||
class=" {info?.meta?.profile_image_url
|
||||
? ''
|
||||
: 'p-6'} rounded-full dark:bg-gray-700 border border-dashed border-gray-200"
|
||||
type="button"
|
||||
on:click={() => {
|
||||
filesInputElement.click();
|
||||
}}
|
||||
>
|
||||
{#if info?.meta?.profile_image_url}
|
||||
<img
|
||||
src={info?.meta?.profile_image_url}
|
||||
alt="modelfile profile"
|
||||
class=" rounded-full w-20 h-20 object-cover"
|
||||
/>
|
||||
{:else}
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
fill="currentColor"
|
||||
class="w-8"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
d="M12 3.75a.75.75 0 01.75.75v6.75h6.75a.75.75 0 010 1.5h-6.75v6.75a.75.75 0 01-1.5 0v-6.75H4.5a.75.75 0 010-1.5h6.75V4.5a.75.75 0 01.75-.75z"
|
||||
clip-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="my-2 flex space-x-2">
|
||||
<div class="flex-1">
|
||||
<div class=" text-sm font-semibold mb-2">{$i18n.t('Name')}*</div>
|
||||
|
||||
<div>
|
||||
<input
|
||||
class="px-3 py-1.5 text-sm w-full bg-transparent border dark:border-gray-600 outline-none rounded-lg"
|
||||
placeholder={$i18n.t('Name your model')}
|
||||
bind:value={name}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex-1">
|
||||
<div class=" text-sm font-semibold mb-2">{$i18n.t('Model ID')}*</div>
|
||||
|
||||
<div>
|
||||
<input
|
||||
class="px-3 py-1.5 text-sm w-full bg-transparent disabled:text-gray-500 border dark:border-gray-600 outline-none rounded-lg"
|
||||
placeholder={$i18n.t('Add a model id')}
|
||||
value={id}
|
||||
disabled
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{#if model.preset}
|
||||
<div class="my-2">
|
||||
<div class=" text-sm font-semibold mb-2">{$i18n.t('Base Model (From)')}</div>
|
||||
|
||||
<div>
|
||||
<select
|
||||
class="px-3 py-1.5 text-sm w-full bg-transparent border dark:border-gray-600 outline-none rounded-lg"
|
||||
placeholder="Select a base model (e.g. llama3, gpt-4o)"
|
||||
bind:value={info.base_model_id}
|
||||
required
|
||||
>
|
||||
<option value={null} class=" placeholder:text-gray-500"
|
||||
>{$i18n.t('Select a base model')}</option
|
||||
>
|
||||
{#each $models.filter((m) => m.id !== model.id && !m?.preset) as model}
|
||||
<option value={model.id}>{model.name}</option>
|
||||
{/each}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<div class="my-2">
|
||||
<div class=" text-sm font-semibold mb-2">{$i18n.t('Description')}</div>
|
||||
|
||||
<div>
|
||||
<input
|
||||
class="px-3 py-1.5 text-sm w-full bg-transparent border dark:border-gray-600 outline-none rounded-lg"
|
||||
placeholder={$i18n.t('Add a short description about what this model does')}
|
||||
bind:value={info.meta.description}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="my-2">
|
||||
<div class="flex w-full justify-between">
|
||||
<div class=" self-center text-sm font-semibold">{$i18n.t('Model Params')}</div>
|
||||
</div>
|
||||
|
||||
<!-- <div class=" text-sm font-semibold mb-2"></div> -->
|
||||
|
||||
<div class="mt-2">
|
||||
<div class="my-1">
|
||||
<div class=" text-xs font-semibold mb-2">{$i18n.t('System Prompt')}</div>
|
||||
<div>
|
||||
<textarea
|
||||
class="px-3 py-1.5 text-sm w-full bg-transparent border dark:border-gray-600 outline-none rounded-lg -mb-1"
|
||||
placeholder={`Write your model system prompt content here\ne.g.) You are Mario from Super Mario Bros, acting as an assistant.`}
|
||||
rows="4"
|
||||
bind:value={info.params.system}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex w-full justify-between">
|
||||
<div class=" self-center text-xs font-semibold">
|
||||
{$i18n.t('Advanced Params')}
|
||||
</div>
|
||||
|
||||
<button
|
||||
class="p-1 px-3 text-xs flex rounded transition"
|
||||
type="button"
|
||||
on:click={() => {
|
||||
showAdvanced = !showAdvanced;
|
||||
}}
|
||||
>
|
||||
{#if showAdvanced}
|
||||
<span class="ml-2 self-center">{$i18n.t('Hide')}</span>
|
||||
{:else}
|
||||
<span class="ml-2 self-center">{$i18n.t('Show')}</span>
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{#if showAdvanced}
|
||||
<div class="my-2">
|
||||
<AdvancedParams
|
||||
bind:params
|
||||
on:change={(e) => {
|
||||
info.params = { ...info.params, ...params };
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="my-2">
|
||||
<div class="flex w-full justify-between items-center">
|
||||
<div class="flex w-full justify-between items-center">
|
||||
<div class=" self-center text-sm font-semibold">{$i18n.t('Prompt suggestions')}</div>
|
||||
|
||||
<button
|
||||
class="p-1 text-xs flex rounded transition"
|
||||
type="button"
|
||||
on:click={() => {
|
||||
if (info.meta.suggestion_prompts === null) {
|
||||
info.meta.suggestion_prompts = [{ content: '' }];
|
||||
} else {
|
||||
info.meta.suggestion_prompts = null;
|
||||
}
|
||||
}}
|
||||
>
|
||||
{#if info.meta.suggestion_prompts === null}
|
||||
<span class="ml-2 self-center">{$i18n.t('Default')}</span>
|
||||
{:else}
|
||||
<span class="ml-2 self-center">{$i18n.t('Custom')}</span>
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{#if info.meta.suggestion_prompts !== null}
|
||||
<button
|
||||
class="p-1 px-2 text-xs flex rounded transition"
|
||||
type="button"
|
||||
on:click={() => {
|
||||
if (
|
||||
info.meta.suggestion_prompts.length === 0 ||
|
||||
info.meta.suggestion_prompts.at(-1).content !== ''
|
||||
) {
|
||||
info.meta.suggestion_prompts = [...info.meta.suggestion_prompts, { content: '' }];
|
||||
}
|
||||
}}
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 20 20"
|
||||
fill="currentColor"
|
||||
class="w-4 h-4"
|
||||
>
|
||||
<path
|
||||
d="M10.75 4.75a.75.75 0 00-1.5 0v4.5h-4.5a.75.75 0 000 1.5h4.5v4.5a.75.75 0 001.5 0v-4.5h4.5a.75.75 0 000-1.5h-4.5v-4.5z"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
{#if info.meta.suggestion_prompts}
|
||||
<div class="flex flex-col space-y-1 mt-2">
|
||||
{#if info.meta.suggestion_prompts.length > 0}
|
||||
{#each info.meta.suggestion_prompts as prompt, promptIdx}
|
||||
<div class=" flex border dark:border-gray-600 rounded-lg">
|
||||
<input
|
||||
class="px-3 py-1.5 text-sm w-full bg-transparent outline-none border-r dark:border-gray-600"
|
||||
placeholder={$i18n.t('Write a prompt suggestion (e.g. Who are you?)')}
|
||||
bind:value={prompt.content}
|
||||
/>
|
||||
|
||||
<button
|
||||
class="px-2"
|
||||
type="button"
|
||||
on:click={() => {
|
||||
info.meta.suggestion_prompts.splice(promptIdx, 1);
|
||||
info.meta.suggestion_prompts = info.meta.suggestion_prompts;
|
||||
}}
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 20 20"
|
||||
fill="currentColor"
|
||||
class="w-4 h-4"
|
||||
>
|
||||
<path
|
||||
d="M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
{/each}
|
||||
{:else}
|
||||
<div class="text-xs text-center">No suggestion prompts</div>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<div class="my-2">
|
||||
<div class="flex w-full justify-between">
|
||||
<div class=" self-center text-sm font-semibold">{$i18n.t('Capabilities')}</div>
|
||||
</div>
|
||||
<div class="flex flex-col">
|
||||
{#each Object.keys(capabilities) as capability}
|
||||
<div class=" flex items-center gap-2">
|
||||
<Checkbox
|
||||
state={capabilities[capability] ? 'checked' : 'unchecked'}
|
||||
on:change={(e) => {
|
||||
capabilities[capability] = e.detail === 'checked';
|
||||
}}
|
||||
/>
|
||||
|
||||
<div class=" py-1.5 text-sm w-full capitalize">
|
||||
{$i18n.t(capability)}
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="my-2 text-gray-500">
|
||||
<div class="flex w-full justify-between mb-2">
|
||||
<div class=" self-center text-sm font-semibold">{$i18n.t('JSON Preview')}</div>
|
||||
|
||||
<button
|
||||
class="p-1 px-3 text-xs flex rounded transition"
|
||||
type="button"
|
||||
on:click={() => {
|
||||
showPreview = !showPreview;
|
||||
}}
|
||||
>
|
||||
{#if showPreview}
|
||||
<span class="ml-2 self-center">{$i18n.t('Hide')}</span>
|
||||
{:else}
|
||||
<span class="ml-2 self-center">{$i18n.t('Show')}</span>
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{#if showPreview}
|
||||
<div>
|
||||
<textarea
|
||||
class="px-3 py-1.5 text-sm w-full bg-transparent border dark:border-gray-600 outline-none rounded-lg"
|
||||
rows="10"
|
||||
value={JSON.stringify(info, null, 2)}
|
||||
disabled
|
||||
readonly
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<div class="my-2 flex justify-end mb-20">
|
||||
<button
|
||||
class=" text-sm px-3 py-2 transition rounded-xl {loading
|
||||
? ' cursor-not-allowed bg-gray-100 dark:bg-gray-800'
|
||||
: ' bg-gray-50 hover:bg-gray-100 dark:bg-gray-700 dark:hover:bg-gray-800'} flex"
|
||||
type="submit"
|
||||
disabled={loading}
|
||||
>
|
||||
<div class=" self-center font-medium">{$i18n.t('Save & Update')}</div>
|
||||
|
||||
{#if loading}
|
||||
<div class="ml-1.5 self-center">
|
||||
<svg
|
||||
class=" w-4 h-4"
|
||||
viewBox="0 0 24 24"
|
||||
fill="currentColor"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
><style>
|
||||
.spinner_ajPY {
|
||||
transform-origin: center;
|
||||
animation: spinner_AtaB 0.75s infinite linear;
|
||||
}
|
||||
@keyframes spinner_AtaB {
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
</style><path
|
||||
d="M12,1A11,11,0,1,0,23,12,11,11,0,0,0,12,1Zm0,19a8,8,0,1,1,8-8A8,8,0,0,1,12,20Z"
|
||||
opacity=".25"
|
||||
/><path
|
||||
d="M10.14,1.16a11,11,0,0,0-9,8.92A1.59,1.59,0,0,0,2.46,12,1.52,1.52,0,0,0,4.11,10.7a8,8,0,0,1,6.66-6.61A1.42,1.42,0,0,0,12,2.69h0A1.57,1.57,0,0,0,10.14,1.16Z"
|
||||
class="spinner_ajPY"
|
||||
/></svg
|
||||
>
|
||||
</div>
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
{/if}
|
||||
</div>
|
@ -1,27 +0,0 @@
|
||||
<script lang="ts">
|
||||
import { goto } from '$app/navigation';
|
||||
import { onMount } from 'svelte';
|
||||
|
||||
onMount(async () => {
|
||||
window.addEventListener('message', async (event) => {
|
||||
if (
|
||||
![
|
||||
'https://ollamahub.com',
|
||||
'https://www.ollamahub.com',
|
||||
'https://openwebui.com',
|
||||
'https://www.openwebui.com',
|
||||
'http://localhost:5173'
|
||||
].includes(event.origin)
|
||||
)
|
||||
return;
|
||||
const modelfile = JSON.parse(event.data);
|
||||
sessionStorage.modelfile = JSON.stringify(modelfile);
|
||||
|
||||
goto('/workspace/modelfiles/create');
|
||||
});
|
||||
|
||||
if (window.opener ?? false) {
|
||||
window.opener.postMessage('loaded', '*');
|
||||
}
|
||||
});
|
||||
</script>
|
@ -5,7 +5,7 @@
|
||||
|
||||
import dayjs from 'dayjs';
|
||||
|
||||
import { modelfiles, settings, chatId, WEBUI_NAME } from '$lib/stores';
|
||||
import { settings, chatId, WEBUI_NAME, models } from '$lib/stores';
|
||||
import { convertMessagesToHistory } from '$lib/utils';
|
||||
|
||||
import { getChatByShareId } from '$lib/apis/chats';
|
||||
@ -14,6 +14,7 @@
|
||||
import Navbar from '$lib/components/layout/Navbar.svelte';
|
||||
import { getUserById } from '$lib/apis/users';
|
||||
import { error } from '@sveltejs/kit';
|
||||
import { getModels } from '$lib/apis';
|
||||
|
||||
const i18n = getContext('i18n');
|
||||
|
||||
@ -27,17 +28,6 @@
|
||||
let showModelSelector = false;
|
||||
let selectedModels = [''];
|
||||
|
||||
let selectedModelfiles = {};
|
||||
$: selectedModelfiles = selectedModels.reduce((a, tagName, i, arr) => {
|
||||
const modelfile =
|
||||
$modelfiles.filter((modelfile) => modelfile.tagName === tagName)?.at(0) ?? undefined;
|
||||
|
||||
return {
|
||||
...a,
|
||||
...(modelfile && { [tagName]: modelfile })
|
||||
};
|
||||
}, {});
|
||||
|
||||
let chat = null;
|
||||
let user = null;
|
||||
|
||||
@ -69,10 +59,6 @@
|
||||
if (await loadSharedChat()) {
|
||||
await tick();
|
||||
loaded = true;
|
||||
|
||||
window.setTimeout(() => scrollToBottom(), 0);
|
||||
const chatInput = document.getElementById('chat-textarea');
|
||||
chatInput?.focus();
|
||||
} else {
|
||||
await goto('/');
|
||||
}
|
||||
@ -84,6 +70,7 @@
|
||||
//////////////////////////
|
||||
|
||||
const loadSharedChat = async () => {
|
||||
await models.set(await getModels(localStorage.token));
|
||||
await chatId.set($page.params.id);
|
||||
chat = await getChatByShareId(localStorage.token, $chatId).catch(async (error) => {
|
||||
await goto('/');
|
||||
@ -168,7 +155,6 @@
|
||||
chatId={$chatId}
|
||||
readOnly={true}
|
||||
{selectedModels}
|
||||
{selectedModelfiles}
|
||||
{processing}
|
||||
bind:history
|
||||
bind:messages
|
||||
|
Loading…
x
Reference in New Issue
Block a user