forked from open-webui/open-webui
Merge branch 'dev' into feat/customizable-title-prompt-length
This commit is contained in:
commit
a938ffb586
16 changed files with 187 additions and 43 deletions
|
@ -28,6 +28,7 @@ from config import (
|
||||||
UPLOAD_DIR,
|
UPLOAD_DIR,
|
||||||
WHISPER_MODEL,
|
WHISPER_MODEL,
|
||||||
WHISPER_MODEL_DIR,
|
WHISPER_MODEL_DIR,
|
||||||
|
WHISPER_MODEL_AUTO_UPDATE,
|
||||||
DEVICE_TYPE,
|
DEVICE_TYPE,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -69,12 +70,24 @@ def transcribe(
|
||||||
f.write(contents)
|
f.write(contents)
|
||||||
f.close()
|
f.close()
|
||||||
|
|
||||||
model = WhisperModel(
|
whisper_kwargs = {
|
||||||
WHISPER_MODEL,
|
"model_size_or_path": WHISPER_MODEL,
|
||||||
device=whisper_device_type,
|
"device": whisper_device_type,
|
||||||
compute_type="int8",
|
"compute_type": "int8",
|
||||||
download_root=WHISPER_MODEL_DIR,
|
"download_root": WHISPER_MODEL_DIR,
|
||||||
|
"local_files_only": not WHISPER_MODEL_AUTO_UPDATE,
|
||||||
|
}
|
||||||
|
|
||||||
|
log.debug(f"whisper_kwargs: {whisper_kwargs}")
|
||||||
|
|
||||||
|
try:
|
||||||
|
model = WhisperModel(**whisper_kwargs)
|
||||||
|
except:
|
||||||
|
log.warning(
|
||||||
|
"WhisperModel initialization failed, attempting download with local_files_only=False"
|
||||||
)
|
)
|
||||||
|
whisper_kwargs["local_files_only"] = False
|
||||||
|
model = WhisperModel(**whisper_kwargs)
|
||||||
|
|
||||||
segments, info = model.transcribe(file_path, beam_size=5)
|
segments, info = model.transcribe(file_path, beam_size=5)
|
||||||
log.info(
|
log.info(
|
||||||
|
|
|
@ -29,7 +29,13 @@ import base64
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from config import SRC_LOG_LEVELS, CACHE_DIR, AUTOMATIC1111_BASE_URL, COMFYUI_BASE_URL
|
from config import (
|
||||||
|
SRC_LOG_LEVELS,
|
||||||
|
CACHE_DIR,
|
||||||
|
ENABLE_IMAGE_GENERATION,
|
||||||
|
AUTOMATIC1111_BASE_URL,
|
||||||
|
COMFYUI_BASE_URL,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
@ -48,7 +54,7 @@ app.add_middleware(
|
||||||
)
|
)
|
||||||
|
|
||||||
app.state.ENGINE = ""
|
app.state.ENGINE = ""
|
||||||
app.state.ENABLED = False
|
app.state.ENABLED = ENABLE_IMAGE_GENERATION
|
||||||
|
|
||||||
app.state.OPENAI_API_KEY = ""
|
app.state.OPENAI_API_KEY = ""
|
||||||
app.state.MODEL = ""
|
app.state.MODEL = ""
|
||||||
|
|
|
@ -612,8 +612,13 @@ async def generate_embeddings(
|
||||||
user=Depends(get_current_user),
|
user=Depends(get_current_user),
|
||||||
):
|
):
|
||||||
if url_idx == None:
|
if url_idx == None:
|
||||||
if form_data.model in app.state.MODELS:
|
model = form_data.model
|
||||||
url_idx = random.choice(app.state.MODELS[form_data.model]["urls"])
|
|
||||||
|
if ":" not in model:
|
||||||
|
model = f"{model}:latest"
|
||||||
|
|
||||||
|
if model in app.state.MODELS:
|
||||||
|
url_idx = random.choice(app.state.MODELS[model]["urls"])
|
||||||
else:
|
else:
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=400,
|
status_code=400,
|
||||||
|
@ -672,8 +677,13 @@ async def generate_completion(
|
||||||
):
|
):
|
||||||
|
|
||||||
if url_idx == None:
|
if url_idx == None:
|
||||||
if form_data.model in app.state.MODELS:
|
model = form_data.model
|
||||||
url_idx = random.choice(app.state.MODELS[form_data.model]["urls"])
|
|
||||||
|
if ":" not in model:
|
||||||
|
model = f"{model}:latest"
|
||||||
|
|
||||||
|
if model in app.state.MODELS:
|
||||||
|
url_idx = random.choice(app.state.MODELS[model]["urls"])
|
||||||
else:
|
else:
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=400,
|
status_code=400,
|
||||||
|
@ -770,8 +780,13 @@ async def generate_chat_completion(
|
||||||
):
|
):
|
||||||
|
|
||||||
if url_idx == None:
|
if url_idx == None:
|
||||||
if form_data.model in app.state.MODELS:
|
model = form_data.model
|
||||||
url_idx = random.choice(app.state.MODELS[form_data.model]["urls"])
|
|
||||||
|
if ":" not in model:
|
||||||
|
model = f"{model}:latest"
|
||||||
|
|
||||||
|
if model in app.state.MODELS:
|
||||||
|
url_idx = random.choice(app.state.MODELS[model]["urls"])
|
||||||
else:
|
else:
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=400,
|
status_code=400,
|
||||||
|
@ -874,8 +889,13 @@ async def generate_openai_chat_completion(
|
||||||
):
|
):
|
||||||
|
|
||||||
if url_idx == None:
|
if url_idx == None:
|
||||||
if form_data.model in app.state.MODELS:
|
model = form_data.model
|
||||||
url_idx = random.choice(app.state.MODELS[form_data.model]["urls"])
|
|
||||||
|
if ":" not in model:
|
||||||
|
model = f"{model}:latest"
|
||||||
|
|
||||||
|
if model in app.state.MODELS:
|
||||||
|
url_idx = random.choice(app.state.MODELS[model]["urls"])
|
||||||
else:
|
else:
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=400,
|
status_code=400,
|
||||||
|
|
|
@ -413,7 +413,7 @@ RAG_EMBEDDING_MODEL_AUTO_UPDATE = (
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
# device type embbeding models - "cpu" (default), "cuda" (nvidia gpu required) or "mps" (apple silicon) - choosing this right can lead to better performance
|
# device type embedding models - "cpu" (default), "cuda" (nvidia gpu required) or "mps" (apple silicon) - choosing this right can lead to better performance
|
||||||
USE_CUDA = os.environ.get("USE_CUDA_DOCKER", "false")
|
USE_CUDA = os.environ.get("USE_CUDA_DOCKER", "false")
|
||||||
|
|
||||||
if USE_CUDA.lower() == "true":
|
if USE_CUDA.lower() == "true":
|
||||||
|
@ -450,11 +450,17 @@ Query: [query]"""
|
||||||
|
|
||||||
WHISPER_MODEL = os.getenv("WHISPER_MODEL", "base")
|
WHISPER_MODEL = os.getenv("WHISPER_MODEL", "base")
|
||||||
WHISPER_MODEL_DIR = os.getenv("WHISPER_MODEL_DIR", f"{CACHE_DIR}/whisper/models")
|
WHISPER_MODEL_DIR = os.getenv("WHISPER_MODEL_DIR", f"{CACHE_DIR}/whisper/models")
|
||||||
|
WHISPER_MODEL_AUTO_UPDATE = (
|
||||||
|
os.environ.get("WHISPER_MODEL_AUTO_UPDATE", "").lower() == "true"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
####################################
|
####################################
|
||||||
# Images
|
# Images
|
||||||
####################################
|
####################################
|
||||||
|
|
||||||
|
ENABLE_IMAGE_GENERATION = (
|
||||||
|
os.environ.get("ENABLE_IMAGE_GENERATION", "").lower() == "true"
|
||||||
|
)
|
||||||
AUTOMATIC1111_BASE_URL = os.getenv("AUTOMATIC1111_BASE_URL", "")
|
AUTOMATIC1111_BASE_URL = os.getenv("AUTOMATIC1111_BASE_URL", "")
|
||||||
COMFYUI_BASE_URL = os.getenv("COMFYUI_BASE_URL", "")
|
COMFYUI_BASE_URL = os.getenv("COMFYUI_BASE_URL", "")
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<link rel="icon" href="%sveltekit.assets%/favicon.png" />
|
<link rel="icon" href="%sveltekit.assets%/favicon.png" />
|
||||||
<link rel="manifest" href="%sveltekit.assets%/manifest.json" />
|
<link rel="manifest" href="%sveltekit.assets%/manifest.json" crossorigin="use-credentials" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" />
|
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" />
|
||||||
<meta name="robots" content="noindex,nofollow" />
|
<meta name="robots" content="noindex,nofollow" />
|
||||||
<script>
|
<script>
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
import { synthesizeOpenAISpeech } from '$lib/apis/openai';
|
import { synthesizeOpenAISpeech } from '$lib/apis/openai';
|
||||||
import { imageGenerations } from '$lib/apis/images';
|
import { imageGenerations } from '$lib/apis/images';
|
||||||
import {
|
import {
|
||||||
|
approximateToHumanReadable,
|
||||||
extractSentences,
|
extractSentences,
|
||||||
revertSanitizedResponseContent,
|
revertSanitizedResponseContent,
|
||||||
sanitizeResponseContent
|
sanitizeResponseContent
|
||||||
|
@ -122,7 +123,10 @@
|
||||||
eval_count: ${message.info.eval_count ?? 'N/A'}<br/>
|
eval_count: ${message.info.eval_count ?? 'N/A'}<br/>
|
||||||
eval_duration: ${
|
eval_duration: ${
|
||||||
Math.round(((message.info.eval_duration ?? 0) / 1000000) * 100) / 100 ?? 'N/A'
|
Math.round(((message.info.eval_duration ?? 0) / 1000000) * 100) / 100 ?? 'N/A'
|
||||||
}ms</span>`,
|
}ms<br/>
|
||||||
|
approximate_total: ${approximateToHumanReadable(
|
||||||
|
message.info.total_duration
|
||||||
|
)}</span>`,
|
||||||
allowHTML: true
|
allowHTML: true
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,7 +33,7 @@
|
||||||
: items;
|
: items;
|
||||||
|
|
||||||
const pullModelHandler = async () => {
|
const pullModelHandler = async () => {
|
||||||
const sanitizedModelTag = searchValue.trim();
|
const sanitizedModelTag = searchValue.trim().replace(/^ollama\s+(run|pull)\s+/, '');
|
||||||
|
|
||||||
console.log($MODEL_DOWNLOAD_POOL);
|
console.log($MODEL_DOWNLOAD_POOL);
|
||||||
if ($MODEL_DOWNLOAD_POOL[sanitizedModelTag]) {
|
if ($MODEL_DOWNLOAD_POOL[sanitizedModelTag]) {
|
||||||
|
|
|
@ -139,7 +139,7 @@
|
||||||
};
|
};
|
||||||
|
|
||||||
const pullModelHandler = async () => {
|
const pullModelHandler = async () => {
|
||||||
const sanitizedModelTag = modelTag.trim();
|
const sanitizedModelTag = modelTag.trim().replace(/^ollama\s+(run|pull)\s+/, '');
|
||||||
if (modelDownloadStatus[sanitizedModelTag]) {
|
if (modelDownloadStatus[sanitizedModelTag]) {
|
||||||
toast.error(
|
toast.error(
|
||||||
$i18n.t(`Model '{{modelTag}}' is already in queue for downloading.`, {
|
$i18n.t(`Model '{{modelTag}}' is already in queue for downloading.`, {
|
||||||
|
|
42
src/lib/components/common/Pagination.svelte
Normal file
42
src/lib/components/common/Pagination.svelte
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { Pagination } from 'bits-ui';
|
||||||
|
import { createEventDispatcher } from 'svelte';
|
||||||
|
|
||||||
|
import ChevronLeft from '../icons/ChevronLeft.svelte';
|
||||||
|
import ChevronRight from '../icons/ChevronRight.svelte';
|
||||||
|
|
||||||
|
export let page = 0;
|
||||||
|
export let count = 0;
|
||||||
|
export let perPage = 20;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="flex justify-center">
|
||||||
|
<Pagination.Root bind:page {count} {perPage} let:pages>
|
||||||
|
<div class="my-2 flex items-center">
|
||||||
|
<Pagination.PrevButton
|
||||||
|
class="mr-[25px] inline-flex size-8 items-center justify-center rounded-[9px] bg-transparent hover:bg-gray-100 dark:hover:bg-gray-800 active:scale-98 disabled:cursor-not-allowed disabled:text-gray-400 dark:disabled:text-gray-700 hover:disabled:bg-transparent dark:hover:disabled:bg-transparent"
|
||||||
|
>
|
||||||
|
<ChevronLeft className="size-4" strokeWidth="2" />
|
||||||
|
</Pagination.PrevButton>
|
||||||
|
<div class="flex items-center gap-2.5">
|
||||||
|
{#each pages as page (page.key)}
|
||||||
|
{#if page.type === 'ellipsis'}
|
||||||
|
<div class="text-sm font-medium text-foreground-alt">...</div>
|
||||||
|
{:else}
|
||||||
|
<Pagination.Page
|
||||||
|
{page}
|
||||||
|
class="inline-flex size-8 items-center justify-center rounded-[9px] bg-transparent hover:bg-gray-100 dark:hover:bg-gray-800 text-sm font-medium hover:bg-dark-10 active:scale-98 disabled:cursor-not-allowed disabled:opacity-50 hover:disabled:bg-transparent data-[selected]:bg-black data-[selected]:text-gray-100 data-[selected]:hover:bg-black dark:data-[selected]:bg-white dark:data-[selected]:text-gray-900 dark:data-[selected]:hover:bg-white"
|
||||||
|
>
|
||||||
|
{page.value}
|
||||||
|
</Pagination.Page>
|
||||||
|
{/if}
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
<Pagination.NextButton
|
||||||
|
class="ml-[25px] inline-flex size-8 items-center justify-center rounded-[9px] bg-transparent hover:bg-gray-100 dark:hover:bg-gray-800 active:scale-98 disabled:cursor-not-allowed disabled:text-gray-400 dark:disabled:text-gray-700 hover:disabled:bg-transparent dark:hover:disabled:bg-transparent"
|
||||||
|
>
|
||||||
|
<ChevronRight className="size-4" strokeWidth="2" />
|
||||||
|
</Pagination.NextButton>
|
||||||
|
</div>
|
||||||
|
</Pagination.Root>
|
||||||
|
</div>
|
15
src/lib/components/icons/ChevronLeft.svelte
Normal file
15
src/lib/components/icons/ChevronLeft.svelte
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
<script lang="ts">
|
||||||
|
export let className = 'w-4 h-4';
|
||||||
|
export let strokeWidth = '1.5';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
stroke-width={strokeWidth}
|
||||||
|
stroke="currentColor"
|
||||||
|
class={className}
|
||||||
|
>
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" d="M15.75 19.5 8.25 12l7.5-7.5" />
|
||||||
|
</svg>
|
15
src/lib/components/icons/ChevronRight.svelte
Normal file
15
src/lib/components/icons/ChevronRight.svelte
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
<script lang="ts">
|
||||||
|
export let className = 'w-4 h-4';
|
||||||
|
export let strokeWidth = '1.5';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
stroke-width={strokeWidth}
|
||||||
|
stroke="currentColor"
|
||||||
|
class={className}
|
||||||
|
>
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" d="m8.25 4.5 7.5 7.5-7.5 7.5" />
|
||||||
|
</svg>
|
|
@ -26,14 +26,14 @@
|
||||||
|
|
||||||
<div slot="content">
|
<div slot="content">
|
||||||
<DropdownMenu.Content
|
<DropdownMenu.Content
|
||||||
class="w-full max-w-[130px] rounded-lg px-1 py-1.5 border border-gray-300/30 dark:border-gray-700/50 z-50 bg-white dark:bg-gray-850 dark:text-white shadow"
|
class="w-full max-w-[150px] rounded-lg px-1 py-1.5 border border-gray-300/30 dark:border-gray-700/50 z-50 bg-white dark:bg-gray-900 dark:text-white shadow"
|
||||||
sideOffset={-2}
|
sideOffset={-2}
|
||||||
side="bottom"
|
side="bottom"
|
||||||
align="start"
|
align="start"
|
||||||
transition={flyAndScale}
|
transition={flyAndScale}
|
||||||
>
|
>
|
||||||
<DropdownMenu.Item
|
<DropdownMenu.Item
|
||||||
class="flex gap-2 items-center px-3 py-2 text-sm font-medium cursor-pointer"
|
class="flex gap-2 items-center px-3 py-2 text-sm font-medium cursor-pointer dark:hover:bg-gray-850 rounded-md"
|
||||||
on:click={() => {
|
on:click={() => {
|
||||||
renameHandler();
|
renameHandler();
|
||||||
}}
|
}}
|
||||||
|
@ -43,7 +43,7 @@
|
||||||
</DropdownMenu.Item>
|
</DropdownMenu.Item>
|
||||||
|
|
||||||
<DropdownMenu.Item
|
<DropdownMenu.Item
|
||||||
class="flex gap-2 items-center px-3 py-2 text-sm font-medium cursor-pointer"
|
class="flex gap-2 items-center px-3 py-2 text-sm font-medium cursor-pointer dark:hover:bg-gray-850 rounded-md"
|
||||||
on:click={() => {
|
on:click={() => {
|
||||||
deleteHandler();
|
deleteHandler();
|
||||||
}}
|
}}
|
||||||
|
|
|
@ -55,9 +55,9 @@
|
||||||
"Check for updates": "Kiểm tra cập nhật",
|
"Check for updates": "Kiểm tra cập nhật",
|
||||||
"Checking for updates...": "Đang kiểm tra cập nhật...",
|
"Checking for updates...": "Đang kiểm tra cập nhật...",
|
||||||
"Choose a model before saving...": "Chọn mô hình trước khi lưu...",
|
"Choose a model before saving...": "Chọn mô hình trước khi lưu...",
|
||||||
"Chunk Overlap": "Kích thước chồng lấn (overlap)",
|
"Chunk Overlap": "Chồng lấn (overlap)",
|
||||||
"Chunk Params": "Cài đặt số lượng ký tự cho khối ký tự (chunk)",
|
"Chunk Params": "Cài đặt số lượng ký tự cho khối ký tự (chunk)",
|
||||||
"Chunk Size": "Kích thức khối (size)",
|
"Chunk Size": "Kích thước khối (size)",
|
||||||
"Click here for help.": "Bấm vào đây để được trợ giúp.",
|
"Click here for help.": "Bấm vào đây để được trợ giúp.",
|
||||||
"Click here to check other modelfiles.": "Bấm vào đây để kiểm tra các tệp mô tả mô hình (modelfiles) khác.",
|
"Click here to check other modelfiles.": "Bấm vào đây để kiểm tra các tệp mô tả mô hình (modelfiles) khác.",
|
||||||
"Click here to select": "Bấm vào đây để chọn",
|
"Click here to select": "Bấm vào đây để chọn",
|
||||||
|
@ -65,7 +65,7 @@
|
||||||
"click here.": "bấm vào đây.",
|
"click here.": "bấm vào đây.",
|
||||||
"Click on the user role button to change a user's role.": "Bấm vào nút trong cột VAI TRÒ để thay đổi quyền của người sử dụng.",
|
"Click on the user role button to change a user's role.": "Bấm vào nút trong cột VAI TRÒ để thay đổi quyền của người sử dụng.",
|
||||||
"Close": "Đóng",
|
"Close": "Đóng",
|
||||||
"Collection": "Bộ sưu tập",
|
"Collection": "Tổng hợp mọi tài liệu",
|
||||||
"Command": "Lệnh",
|
"Command": "Lệnh",
|
||||||
"Confirm Password": "Xác nhận Mật khẩu",
|
"Confirm Password": "Xác nhận Mật khẩu",
|
||||||
"Connections": "Kết nối",
|
"Connections": "Kết nối",
|
||||||
|
@ -76,7 +76,7 @@
|
||||||
"Copy last response": "Sao chép phản hồi cuối cùng",
|
"Copy last response": "Sao chép phản hồi cuối cùng",
|
||||||
"Copying to clipboard was successful!": "Sao chép vào clipboard thành công!",
|
"Copying to clipboard was successful!": "Sao chép vào clipboard thành công!",
|
||||||
"Create a concise, 3-5 word phrase as a header for the following query, strictly adhering to the 3-5 word limit and avoiding the use of the word 'title':": "Tạo một cụm từ súc tích, 3-5 từ làm tiêu đề cho truy vấn sau, tuân thủ nghiêm ngặt giới hạn 3-5 từ và tránh sử dụng từ 'tiêu đề':",
|
"Create a concise, 3-5 word phrase as a header for the following query, strictly adhering to the 3-5 word limit and avoiding the use of the word 'title':": "Tạo một cụm từ súc tích, 3-5 từ làm tiêu đề cho truy vấn sau, tuân thủ nghiêm ngặt giới hạn 3-5 từ và tránh sử dụng từ 'tiêu đề':",
|
||||||
"Create a modelfile": "Tạo tệp mô tả mô hình",
|
"Create a modelfile": "Tạo tệp mô tả cho mô hình",
|
||||||
"Create Account": "Tạo Tài khoản",
|
"Create Account": "Tạo Tài khoản",
|
||||||
"Created at": "Được tạo vào lúc",
|
"Created at": "Được tạo vào lúc",
|
||||||
"Created by": "Được tạo bởi",
|
"Created by": "Được tạo bởi",
|
||||||
|
@ -347,7 +347,7 @@
|
||||||
"Valid time units:": "Đơn vị thời gian hợp lệ:",
|
"Valid time units:": "Đơn vị thời gian hợp lệ:",
|
||||||
"variable": "biến",
|
"variable": "biến",
|
||||||
"variable to have them replaced with clipboard content.": "biến để có chúng được thay thế bằng nội dung clipboard.",
|
"variable to have them replaced with clipboard content.": "biến để có chúng được thay thế bằng nội dung clipboard.",
|
||||||
"Version": "Phiên bản",
|
"Version": "Version",
|
||||||
"Web": "Web",
|
"Web": "Web",
|
||||||
"WebUI Add-ons": "Tiện ích WebUI",
|
"WebUI Add-ons": "Tiện ích WebUI",
|
||||||
"WebUI Settings": "Cài đặt WebUI",
|
"WebUI Settings": "Cài đặt WebUI",
|
||||||
|
|
|
@ -493,4 +493,25 @@ export const templatePrompt = (template: string, prompt: string) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
return template;
|
return template;
|
||||||
|
|
||||||
|
export const approximateToHumanReadable = (nanoseconds: number) => {
|
||||||
|
const seconds = Math.floor((nanoseconds / 1e9) % 60);
|
||||||
|
const minutes = Math.floor((nanoseconds / 6e10) % 60);
|
||||||
|
const hours = Math.floor((nanoseconds / 3.6e12) % 24);
|
||||||
|
|
||||||
|
const results: string[] = [];
|
||||||
|
|
||||||
|
if (seconds >= 0) {
|
||||||
|
results.push(`${seconds}s`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (minutes > 0) {
|
||||||
|
results.push(`${minutes}m`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hours > 0) {
|
||||||
|
results.push(`${hours}h`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return results.reverse().join(' ');
|
||||||
};
|
};
|
||||||
|
|
|
@ -106,11 +106,6 @@
|
||||||
// IndexedDB Not Found
|
// IndexedDB Not Found
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log();
|
|
||||||
|
|
||||||
await models.set(await getModels());
|
|
||||||
await tick();
|
|
||||||
|
|
||||||
await settings.set(JSON.parse(localStorage.getItem('settings') ?? '{}'));
|
await settings.set(JSON.parse(localStorage.getItem('settings') ?? '{}'));
|
||||||
|
|
||||||
await modelfiles.set(await getModelfiles(localStorage.token));
|
await modelfiles.set(await getModelfiles(localStorage.token));
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
import { getSignUpEnabledStatus, toggleSignUpEnabledStatus } from '$lib/apis/auths';
|
import { getSignUpEnabledStatus, toggleSignUpEnabledStatus } from '$lib/apis/auths';
|
||||||
import EditUserModal from '$lib/components/admin/EditUserModal.svelte';
|
import EditUserModal from '$lib/components/admin/EditUserModal.svelte';
|
||||||
import SettingsModal from '$lib/components/admin/SettingsModal.svelte';
|
import SettingsModal from '$lib/components/admin/SettingsModal.svelte';
|
||||||
|
import Pagination from '$lib/components/common/Pagination.svelte';
|
||||||
|
|
||||||
const i18n = getContext('i18n');
|
const i18n = getContext('i18n');
|
||||||
|
|
||||||
|
@ -21,6 +22,8 @@
|
||||||
let search = '';
|
let search = '';
|
||||||
let selectedUser = null;
|
let selectedUser = null;
|
||||||
|
|
||||||
|
let page = 1;
|
||||||
|
|
||||||
let showSettingsModal = false;
|
let showSettingsModal = false;
|
||||||
let showEditUserModal = false;
|
let showEditUserModal = false;
|
||||||
|
|
||||||
|
@ -159,7 +162,8 @@
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{#each users.filter((user) => {
|
{#each users
|
||||||
|
.filter((user) => {
|
||||||
if (search === '') {
|
if (search === '') {
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
|
@ -167,7 +171,8 @@
|
||||||
const query = search.toLowerCase();
|
const query = search.toLowerCase();
|
||||||
return name.includes(query);
|
return name.includes(query);
|
||||||
}
|
}
|
||||||
}) as user}
|
})
|
||||||
|
.slice((page - 1) * 20, page * 20) as user}
|
||||||
<tr class="bg-white border-b dark:bg-gray-900 dark:border-gray-700 text-xs">
|
<tr class="bg-white border-b dark:bg-gray-900 dark:border-gray-700 text-xs">
|
||||||
<td class="px-3 py-2 min-w-[7rem] w-28">
|
<td class="px-3 py-2 min-w-[7rem] w-28">
|
||||||
<button
|
<button
|
||||||
|
@ -270,6 +275,8 @@
|
||||||
<div class=" text-gray-500 text-xs mt-2 text-right">
|
<div class=" text-gray-500 text-xs mt-2 text-right">
|
||||||
ⓘ {$i18n.t("Click on the user role button to change a user's role.")}
|
ⓘ {$i18n.t("Click on the user role button to change a user's role.")}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<Pagination bind:page count={users.length} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
Loading…
Reference in a new issue