Commit e31cf081 authored by Jannik Streidl's avatar Jannik Streidl
Browse files

merged conflicts in PL locale

parents e57c0c30 e92311b4
...@@ -317,12 +317,13 @@ class GenerateImageForm(BaseModel): ...@@ -317,12 +317,13 @@ class GenerateImageForm(BaseModel):
def save_b64_image(b64_str): def save_b64_image(b64_str):
try: try:
image_id = str(uuid.uuid4())
if "," in b64_str:
header, encoded = b64_str.split(",", 1) header, encoded = b64_str.split(",", 1)
mime_type = header.split(";")[0] mime_type = header.split(";")[0]
img_data = base64.b64decode(encoded) img_data = base64.b64decode(encoded)
image_id = str(uuid.uuid4())
image_format = mimetypes.guess_extension(mime_type) image_format = mimetypes.guess_extension(mime_type)
image_filename = f"{image_id}{image_format}" image_filename = f"{image_id}{image_format}"
...@@ -330,6 +331,17 @@ def save_b64_image(b64_str): ...@@ -330,6 +331,17 @@ def save_b64_image(b64_str):
with open(file_path, "wb") as f: with open(file_path, "wb") as f:
f.write(img_data) f.write(img_data)
return image_filename return image_filename
else:
image_filename = f"{image_id}.png"
file_path = IMAGE_CACHE_DIR.joinpath(image_filename)
img_data = base64.b64decode(b64_str)
# Write the image data to a file
with open(file_path, "wb") as f:
f.write(img_data)
return image_filename
except Exception as e: except Exception as e:
log.exception(f"Error saving image: {e}") log.exception(f"Error saving image: {e}")
return None return None
...@@ -348,18 +360,20 @@ def save_url_image(url): ...@@ -348,18 +360,20 @@ def save_url_image(url):
if not image_format: if not image_format:
raise ValueError("Could not determine image type from MIME type") raise ValueError("Could not determine image type from MIME type")
file_path = IMAGE_CACHE_DIR.joinpath(f"{image_id}{image_format}") image_filename = f"{image_id}{image_format}"
file_path = IMAGE_CACHE_DIR.joinpath(f"{image_filename}")
with open(file_path, "wb") as image_file: with open(file_path, "wb") as image_file:
for chunk in r.iter_content(chunk_size=8192): for chunk in r.iter_content(chunk_size=8192):
image_file.write(chunk) image_file.write(chunk)
return image_id, image_format return image_filename
else: else:
log.error(f"Url does not point to an image.") log.error(f"Url does not point to an image.")
return None, None return None
except Exception as e: except Exception as e:
log.exception(f"Error saving image: {e}") log.exception(f"Error saving image: {e}")
return None, None return None
@app.post("/generations") @app.post("/generations")
...@@ -400,7 +414,7 @@ def generate_image( ...@@ -400,7 +414,7 @@ def generate_image(
for image in res["data"]: for image in res["data"]:
image_filename = save_b64_image(image["b64_json"]) image_filename = save_b64_image(image["b64_json"])
images.append({"url": f"/cache/image/generations/{image_filename}"}) images.append({"url": f"/cache/image/generations/{image_filename}"})
file_body_path = IMAGE_CACHE_DIR.joinpath(f"{image_id}.json") file_body_path = IMAGE_CACHE_DIR.joinpath(f"{image_filename}.json")
with open(file_body_path, "w") as f: with open(file_body_path, "w") as f:
json.dump(data, f) json.dump(data, f)
...@@ -435,11 +449,9 @@ def generate_image( ...@@ -435,11 +449,9 @@ def generate_image(
images = [] images = []
for image in res["data"]: for image in res["data"]:
image_id, image_format = save_url_image(image["url"]) image_filename = save_url_image(image["url"])
images.append( images.append({"url": f"/cache/image/generations/{image_filename}"})
{"url": f"/cache/image/generations/{image_id}{image_format}"} file_body_path = IMAGE_CACHE_DIR.joinpath(f"{image_filename}.json")
)
file_body_path = IMAGE_CACHE_DIR.joinpath(f"{image_id}.json")
with open(file_body_path, "w") as f: with open(file_body_path, "w") as f:
json.dump(data.model_dump(exclude_none=True), f) json.dump(data.model_dump(exclude_none=True), f)
...@@ -477,7 +489,7 @@ def generate_image( ...@@ -477,7 +489,7 @@ def generate_image(
for image in res["images"]: for image in res["images"]:
image_filename = save_b64_image(image) image_filename = save_b64_image(image)
images.append({"url": f"/cache/image/generations/{image_filename}"}) images.append({"url": f"/cache/image/generations/{image_filename}"})
file_body_path = IMAGE_CACHE_DIR.joinpath(f"{image_id}.json") file_body_path = IMAGE_CACHE_DIR.joinpath(f"{image_filename}.json")
with open(file_body_path, "w") as f: with open(file_body_path, "w") as f:
json.dump({**data, "info": res["info"]}, f) json.dump({**data, "info": res["info"]}, f)
......
...@@ -36,6 +36,10 @@ from config import ( ...@@ -36,6 +36,10 @@ from config import (
LITELLM_PROXY_HOST, LITELLM_PROXY_HOST,
) )
import warnings
warnings.simplefilter("ignore")
from litellm.utils import get_llm_provider from litellm.utils import get_llm_provider
import asyncio import asyncio
......
...@@ -31,7 +31,12 @@ from typing import Optional, List, Union ...@@ -31,7 +31,12 @@ from typing import Optional, List, Union
from apps.web.models.users import Users from apps.web.models.users import Users
from constants import ERROR_MESSAGES from constants import ERROR_MESSAGES
from utils.utils import decode_token, get_current_user, get_admin_user from utils.utils import (
decode_token,
get_current_user,
get_verified_user,
get_admin_user,
)
from config import ( from config import (
...@@ -164,7 +169,7 @@ async def get_all_models(): ...@@ -164,7 +169,7 @@ async def get_all_models():
@app.get("/api/tags") @app.get("/api/tags")
@app.get("/api/tags/{url_idx}") @app.get("/api/tags/{url_idx}")
async def get_ollama_tags( async def get_ollama_tags(
url_idx: Optional[int] = None, user=Depends(get_current_user) url_idx: Optional[int] = None, user=Depends(get_verified_user)
): ):
if url_idx == None: if url_idx == None:
models = await get_all_models() models = await get_all_models()
...@@ -563,7 +568,7 @@ async def delete_model( ...@@ -563,7 +568,7 @@ async def delete_model(
@app.post("/api/show") @app.post("/api/show")
async def show_model_info(form_data: ModelNameForm, user=Depends(get_current_user)): async def show_model_info(form_data: ModelNameForm, user=Depends(get_verified_user)):
if form_data.name not in app.state.MODELS: if form_data.name not in app.state.MODELS:
raise HTTPException( raise HTTPException(
status_code=400, status_code=400,
...@@ -612,7 +617,7 @@ class GenerateEmbeddingsForm(BaseModel): ...@@ -612,7 +617,7 @@ class GenerateEmbeddingsForm(BaseModel):
async def generate_embeddings( async def generate_embeddings(
form_data: GenerateEmbeddingsForm, form_data: GenerateEmbeddingsForm,
url_idx: Optional[int] = None, url_idx: Optional[int] = None,
user=Depends(get_current_user), user=Depends(get_verified_user),
): ):
if url_idx == None: if url_idx == None:
model = form_data.model model = form_data.model
...@@ -730,7 +735,7 @@ class GenerateCompletionForm(BaseModel): ...@@ -730,7 +735,7 @@ class GenerateCompletionForm(BaseModel):
async def generate_completion( async def generate_completion(
form_data: GenerateCompletionForm, form_data: GenerateCompletionForm,
url_idx: Optional[int] = None, url_idx: Optional[int] = None,
user=Depends(get_current_user), user=Depends(get_verified_user),
): ):
if url_idx == None: if url_idx == None:
...@@ -833,7 +838,7 @@ class GenerateChatCompletionForm(BaseModel): ...@@ -833,7 +838,7 @@ class GenerateChatCompletionForm(BaseModel):
async def generate_chat_completion( async def generate_chat_completion(
form_data: GenerateChatCompletionForm, form_data: GenerateChatCompletionForm,
url_idx: Optional[int] = None, url_idx: Optional[int] = None,
user=Depends(get_current_user), user=Depends(get_verified_user),
): ):
if url_idx == None: if url_idx == None:
...@@ -942,7 +947,7 @@ class OpenAIChatCompletionForm(BaseModel): ...@@ -942,7 +947,7 @@ class OpenAIChatCompletionForm(BaseModel):
async def generate_openai_chat_completion( async def generate_openai_chat_completion(
form_data: OpenAIChatCompletionForm, form_data: OpenAIChatCompletionForm,
url_idx: Optional[int] = None, url_idx: Optional[int] = None,
user=Depends(get_current_user), user=Depends(get_verified_user),
): ):
if url_idx == None: if url_idx == None:
...@@ -1241,7 +1246,9 @@ def upload_model(file: UploadFile = File(...), url_idx: Optional[int] = None): ...@@ -1241,7 +1246,9 @@ def upload_model(file: UploadFile = File(...), url_idx: Optional[int] = None):
@app.api_route("/{path:path}", methods=["GET", "POST", "PUT", "DELETE"]) @app.api_route("/{path:path}", methods=["GET", "POST", "PUT", "DELETE"])
async def deprecated_proxy(path: str, request: Request, user=Depends(get_current_user)): async def deprecated_proxy(
path: str, request: Request, user=Depends(get_verified_user)
):
url = app.state.OLLAMA_BASE_URLS[0] url = app.state.OLLAMA_BASE_URLS[0]
target_url = f"{url}/{path}" target_url = f"{url}/{path}"
......
...@@ -93,6 +93,31 @@ async def get_archived_session_user_chat_list( ...@@ -93,6 +93,31 @@ async def get_archived_session_user_chat_list(
return Chats.get_archived_chat_list_by_user_id(user.id, skip, limit) return Chats.get_archived_chat_list_by_user_id(user.id, skip, limit)
############################
# GetSharedChatById
############################
@router.get("/share/{share_id}", response_model=Optional[ChatResponse])
async def get_shared_chat_by_id(share_id: str, user=Depends(get_current_user)):
if user.role == "pending":
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED, detail=ERROR_MESSAGES.NOT_FOUND
)
if user.role == "user":
chat = Chats.get_chat_by_share_id(share_id)
elif user.role == "admin":
chat = Chats.get_chat_by_id(share_id)
if chat:
return ChatResponse(**{**chat.model_dump(), "chat": json.loads(chat.chat)})
else:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED, detail=ERROR_MESSAGES.NOT_FOUND
)
############################ ############################
# GetChats # GetChats
############################ ############################
...@@ -141,6 +166,55 @@ async def create_new_chat(form_data: ChatForm, user=Depends(get_current_user)): ...@@ -141,6 +166,55 @@ async def create_new_chat(form_data: ChatForm, user=Depends(get_current_user)):
) )
############################
# GetChatsByTags
############################
class TagNameForm(BaseModel):
name: str
skip: Optional[int] = 0
limit: Optional[int] = 50
@router.post("/tags", response_model=List[ChatTitleIdResponse])
async def get_user_chat_list_by_tag_name(
form_data: TagNameForm, user=Depends(get_current_user)
):
print(form_data)
chat_ids = [
chat_id_tag.chat_id
for chat_id_tag in Tags.get_chat_ids_by_tag_name_and_user_id(
form_data.name, user.id
)
]
chats = Chats.get_chat_list_by_chat_ids(chat_ids, form_data.skip, form_data.limit)
if len(chats) == 0:
Tags.delete_tag_by_tag_name_and_user_id(form_data.name, user.id)
return chats
############################
# GetAllTags
############################
@router.get("/tags/all", response_model=List[TagModel])
async def get_all_tags(user=Depends(get_current_user)):
try:
tags = Tags.get_tags_by_user_id(user.id)
return tags
except Exception as e:
log.exception(e)
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST, detail=ERROR_MESSAGES.DEFAULT()
)
############################ ############################
# GetChatById # GetChatById
############################ ############################
...@@ -274,70 +348,6 @@ async def delete_shared_chat_by_id(id: str, user=Depends(get_current_user)): ...@@ -274,70 +348,6 @@ async def delete_shared_chat_by_id(id: str, user=Depends(get_current_user)):
) )
############################
# GetSharedChatById
############################
@router.get("/share/{share_id}", response_model=Optional[ChatResponse])
async def get_shared_chat_by_id(share_id: str, user=Depends(get_current_user)):
if user.role == "pending":
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED, detail=ERROR_MESSAGES.NOT_FOUND
)
if user.role == "user":
chat = Chats.get_chat_by_share_id(share_id)
elif user.role == "admin":
chat = Chats.get_chat_by_id(share_id)
if chat:
return ChatResponse(**{**chat.model_dump(), "chat": json.loads(chat.chat)})
else:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED, detail=ERROR_MESSAGES.NOT_FOUND
)
############################
# GetAllTags
############################
@router.get("/tags/all", response_model=List[TagModel])
async def get_all_tags(user=Depends(get_current_user)):
try:
tags = Tags.get_tags_by_user_id(user.id)
return tags
except Exception as e:
log.exception(e)
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST, detail=ERROR_MESSAGES.DEFAULT()
)
############################
# GetChatsByTags
############################
@router.get("/tags/tag/{tag_name}", response_model=List[ChatTitleIdResponse])
async def get_user_chat_list_by_tag_name(
tag_name: str, user=Depends(get_current_user), skip: int = 0, limit: int = 50
):
chat_ids = [
chat_id_tag.chat_id
for chat_id_tag in Tags.get_chat_ids_by_tag_name_and_user_id(tag_name, user.id)
]
chats = Chats.get_chat_list_by_chat_ids(chat_ids, skip, limit)
if len(chats) == 0:
Tags.delete_tag_by_tag_name_and_user_id(tag_name, user.id)
return chats
############################ ############################
# GetChatTagsById # GetChatTagsById
############################ ############################
......
...@@ -25,6 +25,8 @@ from apps.litellm.main import ( ...@@ -25,6 +25,8 @@ from apps.litellm.main import (
start_litellm_background, start_litellm_background,
shutdown_litellm_background, shutdown_litellm_background,
) )
from apps.audio.main import app as audio_app from apps.audio.main import app as audio_app
from apps.images.main import app as images_app from apps.images.main import app as images_app
from apps.rag.main import app as rag_app from apps.rag.main import app as rag_app
...@@ -74,7 +76,7 @@ class SPAStaticFiles(StaticFiles): ...@@ -74,7 +76,7 @@ class SPAStaticFiles(StaticFiles):
print( print(
f""" rf"""
___ __ __ _ _ _ ___ ___ __ __ _ _ _ ___
/ _ \ _ __ ___ _ __ \ \ / /__| |__ | | | |_ _| / _ \ _ __ ___ _ __ \ \ / /__| |__ | | | |_ _|
| | | | '_ \ / _ \ '_ \ \ \ /\ / / _ \ '_ \| | | || | | | | | '_ \ / _ \ '_ \ \ \ /\ / / _ \ '_ \| | | || |
......
...@@ -21,14 +21,14 @@ describe('Settings', () => { ...@@ -21,14 +21,14 @@ describe('Settings', () => {
// Click on the model selector // Click on the model selector
cy.get('button[aria-label="Select a model"]').click(); cy.get('button[aria-label="Select a model"]').click();
// Select the first model // Select the first model
cy.get('div[role="option"][data-value]').first().click(); cy.get('button[aria-label="model-item"]').first().click();
}); });
it('user can perform text chat', () => { it('user can perform text chat', () => {
// Click on the model selector // Click on the model selector
cy.get('button[aria-label="Select a model"]').click(); cy.get('button[aria-label="Select a model"]').click();
// Select the first model // Select the first model
cy.get('div[role="option"][data-value]').first().click(); cy.get('button[aria-label="model-item"]').first().click();
// Type a message // Type a message
cy.get('#chat-textarea').type('Hi, what can you do? A single sentence only please.', { cy.get('#chat-textarea').type('Hi, what can you do? A single sentence only please.', {
force: true force: true
......
import { WEBUI_API_BASE_URL } from '$lib/constants'; import { WEBUI_API_BASE_URL } from '$lib/constants';
import { getTimeRange } from '$lib/utils';
export const createNewChat = async (token: string, chat: object) => { export const createNewChat = async (token: string, chat: object) => {
let error = null; let error = null;
...@@ -59,7 +60,10 @@ export const getChatList = async (token: string = '') => { ...@@ -59,7 +60,10 @@ export const getChatList = async (token: string = '') => {
throw error; throw error;
} }
return res; return res.map((chat) => ({
...chat,
time_range: getTimeRange(chat.updated_at)
}));
}; };
export const getChatListByUserId = async (token: string = '', userId: string) => { export const getChatListByUserId = async (token: string = '', userId: string) => {
...@@ -90,7 +94,10 @@ export const getChatListByUserId = async (token: string = '', userId: string) => ...@@ -90,7 +94,10 @@ export const getChatListByUserId = async (token: string = '', userId: string) =>
throw error; throw error;
} }
return res; return res.map((chat) => ({
...chat,
time_range: getTimeRange(chat.updated_at)
}));
}; };
export const getArchivedChatList = async (token: string = '') => { export const getArchivedChatList = async (token: string = '') => {
...@@ -220,13 +227,16 @@ export const getAllChatTags = async (token: string) => { ...@@ -220,13 +227,16 @@ export const getAllChatTags = async (token: string) => {
export const getChatListByTagName = async (token: string = '', tagName: string) => { export const getChatListByTagName = async (token: string = '', tagName: string) => {
let error = null; let error = null;
const res = await fetch(`${WEBUI_API_BASE_URL}/chats/tags/tag/${tagName}`, { const res = await fetch(`${WEBUI_API_BASE_URL}/chats/tags`, {
method: 'GET', method: 'POST',
headers: { headers: {
Accept: 'application/json', Accept: 'application/json',
'Content-Type': 'application/json', 'Content-Type': 'application/json',
...(token && { authorization: `Bearer ${token}` }) ...(token && { authorization: `Bearer ${token}` })
} },
body: JSON.stringify({
name: tagName
})
}) })
.then(async (res) => { .then(async (res) => {
if (!res.ok) throw await res.json(); if (!res.ok) throw await res.json();
...@@ -245,7 +255,10 @@ export const getChatListByTagName = async (token: string = '', tagName: string) ...@@ -245,7 +255,10 @@ export const getChatListByTagName = async (token: string = '', tagName: string)
throw error; throw error;
} }
return res; return res.map((chat) => ({
...chat,
time_range: getTimeRange(chat.updated_at)
}));
}; };
export const getChatById = async (token: string, id: string) => { export const getChatById = async (token: string, id: string) => {
......
...@@ -411,7 +411,9 @@ ...@@ -411,7 +411,9 @@
{#if dragged} {#if dragged}
<div <div
class="fixed lg:w-[calc(100%-260px)] w-full h-full flex z-50 touch-none pointer-events-none" class="fixed {$showSidebar
? 'left-0 lg:left-[260px] lg:w-[calc(100%-260px)]'
: 'left-0'} w-full h-full flex z-50 touch-none pointer-events-none"
id="dropzone" id="dropzone"
role="region" role="region"
aria-label="Drag and Drop Container" aria-label="Drag and Drop Container"
...@@ -763,6 +765,7 @@ ...@@ -763,6 +765,7 @@
bind:value={prompt} bind:value={prompt}
on:keypress={(e) => { on:keypress={(e) => {
if ( if (
window.innerWidth > 1024 ||
!( !(
'ontouchstart' in window || 'ontouchstart' in window ||
navigator.maxTouchPoints > 0 || navigator.maxTouchPoints > 0 ||
......
...@@ -25,7 +25,9 @@ ...@@ -25,7 +25,9 @@
export let items = [{ value: 'mango', label: 'Mango' }]; export let items = [{ value: 'mango', label: 'Mango' }];
export let className = 'max-w-lg'; export let className = ' w-[32rem]';
let show = false;
let selectedModel = ''; let selectedModel = '';
$: selectedModel = items.find((item) => item.value === value) ?? ''; $: selectedModel = items.find((item) => item.value === value) ?? '';
...@@ -181,6 +183,7 @@ ...@@ -181,6 +183,7 @@
</script> </script>
<DropdownMenu.Root <DropdownMenu.Root
bind:open={show}
onOpenChange={async () => { onOpenChange={async () => {
searchValue = ''; searchValue = '';
window.setTimeout(() => document.getElementById('model-search-input')?.focus(), 0); window.setTimeout(() => document.getElementById('model-search-input')?.focus(), 0);
...@@ -199,7 +202,7 @@ ...@@ -199,7 +202,7 @@
</div> </div>
</DropdownMenu.Trigger> </DropdownMenu.Trigger>
<DropdownMenu.Content <DropdownMenu.Content
class=" z-40 w-full {className} justify-start rounded-lg bg-white dark:bg-gray-900 dark:text-white shadow-lg border border-gray-300/30 dark:border-gray-700/50 outline-none " class=" z-40 {className} max-w-[calc(100vw-1rem)] justify-start rounded-lg bg-white dark:bg-gray-900 dark:text-white shadow-lg border border-gray-300/30 dark:border-gray-700/50 outline-none "
transition={flyAndScale} transition={flyAndScale}
side={'bottom-start'} side={'bottom-start'}
sideOffset={4} sideOffset={4}
...@@ -214,6 +217,7 @@ ...@@ -214,6 +217,7 @@
bind:value={searchValue} bind:value={searchValue}
class="w-full text-sm bg-transparent outline-none" class="w-full text-sm bg-transparent outline-none"
placeholder={searchPlaceholder} placeholder={searchPlaceholder}
autocomplete="off"
/> />
</div> </div>
...@@ -222,10 +226,13 @@ ...@@ -222,10 +226,13 @@
<div class="px-3 my-2 max-h-72 overflow-y-auto scrollbar-none"> <div class="px-3 my-2 max-h-72 overflow-y-auto scrollbar-none">
{#each filteredItems as item} {#each filteredItems as item}
<DropdownMenu.Item <button
class="flex w-full font-medium line-clamp-1 select-none items-center rounded-button py-2 pl-3 pr-1.5 text-sm text-gray-700 dark:text-gray-100 outline-none transition-all duration-75 hover:bg-gray-100 dark:hover:bg-gray-850 rounded-lg cursor-pointer data-[highlighted]:bg-muted" aria-label="model-item"
class="flex w-full text-left font-medium line-clamp-1 select-none items-center rounded-button py-2 pl-3 pr-1.5 text-sm text-gray-700 dark:text-gray-100 outline-none transition-all duration-75 hover:bg-gray-100 dark:hover:bg-gray-850 rounded-lg cursor-pointer data-[highlighted]:bg-muted"
on:click={() => { on:click={() => {
value = item.value; value = item.value;
show = false;
}} }}
> >
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
...@@ -294,7 +301,7 @@ ...@@ -294,7 +301,7 @@
<Check /> <Check />
</div> </div>
{/if} {/if}
</DropdownMenu.Item> </button>
{:else} {:else}
<div> <div>
<div class="block px-3 py-2 text-sm text-gray-700 dark:text-gray-100"> <div class="block px-3 py-2 text-sm text-gray-700 dark:text-gray-100">
...@@ -392,6 +399,9 @@ ...@@ -392,6 +399,9 @@
</div> </div>
{/each} {/each}
</div> </div>
<div class="hidden w-[42rem]" />
<div class="hidden w-[32rem]" />
</slot> </slot>
</DropdownMenu.Content> </DropdownMenu.Content>
</DropdownMenu.Root> </DropdownMenu.Root>
......
...@@ -209,6 +209,58 @@ ...@@ -209,6 +209,58 @@
</div> </div>
</div> </div>
</div> </div>
<div class=" flex justify-between dark:text-gray-300 px-5">
<div class=" text-lg font-medium self-center">{$i18n.t('Input commands')}</div>
</div>
<div class="flex flex-col md:flex-row w-full p-5 md:space-x-4 dark:text-gray-200">
<div class=" flex flex-col w-full sm:flex-row sm:justify-center sm:space-x-6">
<div class="flex flex-col space-y-3 w-full self-start">
<div class="w-full flex justify-between items-center">
<div class=" text-sm">
{$i18n.t('Attach file')}
</div>
<div class="flex space-x-1 text-xs">
<div
class=" h-fit py-1 px-2 flex items-center justify-center rounded border border-black/10 capitalize text-gray-600 dark:border-white/10 dark:text-gray-300"
>
#
</div>
</div>
</div>
<div class="w-full flex justify-between items-center">
<div class=" text-sm">
{$i18n.t('Add custom prompt')}
</div>
<div class="flex space-x-1 text-xs">
<div
class=" h-fit py-1 px-2 flex items-center justify-center rounded border border-black/10 capitalize text-gray-600 dark:border-white/10 dark:text-gray-300"
>
/
</div>
</div>
</div>
<div class="w-full flex justify-between items-center">
<div class=" text-sm">
{$i18n.t('Select model')}
</div>
<div class="flex space-x-1 text-xs">
<div
class=" h-fit py-1 px-2 flex items-center justify-center rounded border border-black/10 capitalize text-gray-600 dark:border-white/10 dark:text-gray-300"
>
@
</div>
</div>
</div>
</div>
</div>
</div>
</div> </div>
</Modal> </Modal>
......
...@@ -3,11 +3,15 @@ ...@@ -3,11 +3,15 @@
addTagById, addTagById,
deleteTagById, deleteTagById,
getAllChatTags, getAllChatTags,
getChatList,
getChatListByTagName,
getTagsById, getTagsById,
updateChatById updateChatById
} from '$lib/apis/chats'; } from '$lib/apis/chats';
import { tags as _tags } from '$lib/stores'; import { tags as _tags, chats } from '$lib/stores';
import { onMount } from 'svelte'; import { createEventDispatcher, onMount } from 'svelte';
const dispatch = createEventDispatcher();
import Tags from '../common/Tags.svelte'; import Tags from '../common/Tags.svelte';
...@@ -39,7 +43,21 @@ ...@@ -39,7 +43,21 @@
tags: tags tags: tags
}); });
_tags.set(await getAllChatTags(localStorage.token)); console.log($_tags);
await _tags.set(await getAllChatTags(localStorage.token));
console.log($_tags);
if ($_tags.map((t) => t.name).includes(tagName)) {
await chats.set(await getChatListByTagName(localStorage.token, tagName));
if ($chats.find((chat) => chat.id === chatId)) {
dispatch('close');
}
} else {
await chats.set(await getChatList(localStorage.token));
}
}; };
onMount(async () => { onMount(async () => {
......
...@@ -51,7 +51,7 @@ ...@@ -51,7 +51,7 @@
<button <button
class=" p-5" class=" p-5"
on:click={() => { on:click={() => {
downloadImage(src, 'Image.png'); downloadImage(src, src.substring(src.lastIndexOf('/') + 1));
}} }}
> >
<svg <svg
......
...@@ -44,6 +44,28 @@ ...@@ -44,6 +44,28 @@
let showDropdown = false; let showDropdown = false;
let isEditing = false; let isEditing = false;
let filteredChatList = [];
$: filteredChatList = $chats.filter((chat) => {
if (search === '') {
return true;
} else {
let title = chat.title.toLowerCase();
const query = search.toLowerCase();
let contentMatches = false;
// Access the messages within chat.chat.messages
if (chat.chat && chat.chat.messages && Array.isArray(chat.chat.messages)) {
contentMatches = chat.chat.messages.some((message) => {
// Check if message.content exists and includes the search query
return message.content && message.content.toLowerCase().includes(query);
});
}
return title.includes(query) || contentMatches;
}
});
onMount(async () => { onMount(async () => {
showSidebar.set(window.innerWidth > BREAKPOINT); showSidebar.set(window.innerWidth > BREAKPOINT);
await chats.set(await getChatList(localStorage.token)); await chats.set(await getChatList(localStorage.token));
...@@ -418,25 +440,17 @@ ...@@ -418,25 +440,17 @@
{/if} {/if}
<div class="pl-2 my-2 flex-1 flex flex-col space-y-1 overflow-y-auto scrollbar-none"> <div class="pl-2 my-2 flex-1 flex flex-col space-y-1 overflow-y-auto scrollbar-none">
{#each $chats.filter((chat) => { {#each filteredChatList as chat, idx}
if (search === '') { {#if idx === 0 || (idx > 0 && chat.time_range !== filteredChatList[idx - 1].time_range)}
return true; <div
} else { class="w-full pl-2.5 text-xs text-gray-500 dark:text-gray-500 font-medium {idx === 0
let title = chat.title.toLowerCase(); ? ''
const query = search.toLowerCase(); : 'pt-5'} pb-0.5"
>
let contentMatches = false; {chat.time_range}
// Access the messages within chat.chat.messages </div>
if (chat.chat && chat.chat.messages && Array.isArray(chat.chat.messages)) { {/if}
contentMatches = chat.chat.messages.some((message) => {
// Check if message.content exists and includes the search query
return message.content && message.content.toLowerCase().includes(query);
});
}
return title.includes(query) || contentMatches;
}
}) as chat, i}
<div class=" w-full pr-2 relative group"> <div class=" w-full pr-2 relative group">
{#if chatTitleEditId === chat.id} {#if chatTitleEditId === chat.id}
<div <div
...@@ -836,12 +850,12 @@ ...@@ -836,12 +850,12 @@
> >
<div class="flex h-6 w-6 flex-col items-center"> <div class="flex h-6 w-6 flex-col items-center">
<div <div
class="h-3 w-1 rounded-full bg-[#0f0f0f] dark:bg-white rotate-0 translate-y-[0.15rem] {show class="h-3 w-1 rounded-full bg-[#0f0f0f] dark:bg-white rotate-0 translate-y-[0.15rem] {$showSidebar
? 'group-hover:rotate-[15deg]' ? 'group-hover:rotate-[15deg]'
: 'group-hover:rotate-[-15deg]'}" : 'group-hover:rotate-[-15deg]'}"
/> />
<div <div
class="h-3 w-1 rounded-full bg-[#0f0f0f] dark:bg-white rotate-0 translate-y-[-0.15rem] {show class="h-3 w-1 rounded-full bg-[#0f0f0f] dark:bg-white rotate-0 translate-y-[-0.15rem] {$showSidebar
? 'group-hover:rotate-[-15deg]' ? 'group-hover:rotate-[-15deg]'
: 'group-hover:rotate-[15deg]'}" : 'group-hover:rotate-[15deg]'}"
/> />
......
...@@ -75,7 +75,13 @@ ...@@ -75,7 +75,13 @@
<hr class="border-gray-100 dark:border-gray-800 mt-2.5 mb-1.5" /> <hr class="border-gray-100 dark:border-gray-800 mt-2.5 mb-1.5" />
<div class="flex p-1"> <div class="flex p-1">
<Tags {chatId} /> <Tags
{chatId}
on:close={() => {
show = false;
onClose();
}}
/>
</div> </div>
</DropdownMenu.Content> </DropdownMenu.Content>
</div> </div>
......
This diff is collapsed.
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
"Add a short description about what this modelfile does": "Добавяне на кратко описание за това какво прави този модфайл", "Add a short description about what this modelfile does": "Добавяне на кратко описание за това какво прави този модфайл",
"Add a short title for this prompt": "Добавяне на кратко заглавие за този промпт", "Add a short title for this prompt": "Добавяне на кратко заглавие за този промпт",
"Add a tag": "Добавяне на таг", "Add a tag": "Добавяне на таг",
"Add custom prompt": "",
"Add Docs": "Добавяне на Документи", "Add Docs": "Добавяне на Документи",
"Add Files": "Добавяне на Файлове", "Add Files": "Добавяне на Файлове",
"Add message": "Добавяне на съобщение", "Add message": "Добавяне на съобщение",
...@@ -44,6 +45,7 @@ ...@@ -44,6 +45,7 @@
"Archived Chats": "", "Archived Chats": "",
"are allowed - Activate this command by typing": "са разрешени - Активирайте тази команда чрез въвеждане", "are allowed - Activate this command by typing": "са разрешени - Активирайте тази команда чрез въвеждане",
"Are you sure?": "Сигурни ли сте?", "Are you sure?": "Сигурни ли сте?",
"Attach file": "",
"Attention to detail": "", "Attention to detail": "",
"Audio": "Аудио", "Audio": "Аудио",
"Auto-playback response": "Аувтоматично възпроизвеждане на Отговора", "Auto-playback response": "Аувтоматично възпроизвеждане на Отговора",
...@@ -214,6 +216,7 @@ ...@@ -214,6 +216,7 @@
"Import Modelfiles": "Импортване на модфайлове", "Import Modelfiles": "Импортване на модфайлове",
"Import Prompts": "Импортване на промптове", "Import Prompts": "Импортване на промптове",
"Include `--api` flag when running stable-diffusion-webui": "Включете флага `--api`, когато стартирате stable-diffusion-webui", "Include `--api` flag when running stable-diffusion-webui": "Включете флага `--api`, когато стартирате stable-diffusion-webui",
"Input commands": "",
"Interface": "Интерфейс", "Interface": "Интерфейс",
"join our Discord for help.": "свържете се с нашия Discord за помощ.", "join our Discord for help.": "свържете се с нашия Discord за помощ.",
"JSON": "JSON", "JSON": "JSON",
...@@ -350,6 +353,7 @@ ...@@ -350,6 +353,7 @@
"Select a mode": "Изберете режим", "Select a mode": "Изберете режим",
"Select a model": "Изберете модел", "Select a model": "Изберете модел",
"Select an Ollama instance": "Изберете Ollama инстанция", "Select an Ollama instance": "Изберете Ollama инстанция",
"Select model": "",
"Send a Message": "Изпращане на Съобщение", "Send a Message": "Изпращане на Съобщение",
"Send message": "Изпращане на съобщение", "Send message": "Изпращане на съобщение",
"Server connection verified": "Server connection verified", "Server connection verified": "Server connection verified",
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
"Add a short description about what this modelfile does": "এই মডেলফাইলটির সম্পর্কে সংক্ষিপ্ত বিবরণ যোগ করুন", "Add a short description about what this modelfile does": "এই মডেলফাইলটির সম্পর্কে সংক্ষিপ্ত বিবরণ যোগ করুন",
"Add a short title for this prompt": "এই প্রম্পটের জন্য একটি সংক্ষিপ্ত টাইটেল যোগ করুন", "Add a short title for this prompt": "এই প্রম্পটের জন্য একটি সংক্ষিপ্ত টাইটেল যোগ করুন",
"Add a tag": "একটি ট্যাগ যোগ করুন", "Add a tag": "একটি ট্যাগ যোগ করুন",
"Add custom prompt": "",
"Add Docs": "ডকুমেন্ট যোগ করুন", "Add Docs": "ডকুমেন্ট যোগ করুন",
"Add Files": "ফাইল যোগ করুন", "Add Files": "ফাইল যোগ করুন",
"Add message": "মেসেজ যোগ করুন", "Add message": "মেসেজ যোগ করুন",
...@@ -44,6 +45,7 @@ ...@@ -44,6 +45,7 @@
"Archived Chats": "চ্যাট ইতিহাস সংরক্ষণাগার", "Archived Chats": "চ্যাট ইতিহাস সংরক্ষণাগার",
"are allowed - Activate this command by typing": "অনুমোদিত - কমান্ডটি চালু করার জন্য লিখুন", "are allowed - Activate this command by typing": "অনুমোদিত - কমান্ডটি চালু করার জন্য লিখুন",
"Are you sure?": "আপনি নিশ্চিত?", "Are you sure?": "আপনি নিশ্চিত?",
"Attach file": "",
"Attention to detail": "", "Attention to detail": "",
"Audio": "অডিও", "Audio": "অডিও",
"Auto-playback response": "রেসপন্স অটো-প্লেব্যাক", "Auto-playback response": "রেসপন্স অটো-প্লেব্যাক",
...@@ -214,6 +216,7 @@ ...@@ -214,6 +216,7 @@
"Import Modelfiles": "মডেলফাইলগুলো ইমপোর্ট করুন", "Import Modelfiles": "মডেলফাইলগুলো ইমপোর্ট করুন",
"Import Prompts": "প্রম্পটগুলো ইমপোর্ট করুন", "Import Prompts": "প্রম্পটগুলো ইমপোর্ট করুন",
"Include `--api` flag when running stable-diffusion-webui": "stable-diffusion-webui চালু করার সময় `--api` ফ্ল্যাগ সংযুক্ত করুন", "Include `--api` flag when running stable-diffusion-webui": "stable-diffusion-webui চালু করার সময় `--api` ফ্ল্যাগ সংযুক্ত করুন",
"Input commands": "",
"Interface": "ইন্টারফেস", "Interface": "ইন্টারফেস",
"join our Discord for help.": "সাহায্যের জন্য আমাদের Discord-এ যুক্ত হোন", "join our Discord for help.": "সাহায্যের জন্য আমাদের Discord-এ যুক্ত হোন",
"JSON": "JSON", "JSON": "JSON",
...@@ -351,6 +354,7 @@ ...@@ -351,6 +354,7 @@
"Select a mode": "একটি মডেল নির্বাচন করুন", "Select a mode": "একটি মডেল নির্বাচন করুন",
"Select a model": "একটি মডেল নির্বাচন করুন", "Select a model": "একটি মডেল নির্বাচন করুন",
"Select an Ollama instance": "একটি Ollama ইন্সট্যান্স নির্বাচন করুন", "Select an Ollama instance": "একটি Ollama ইন্সট্যান্স নির্বাচন করুন",
"Select model": "",
"Send a Message": "একটি মেসেজ পাঠান", "Send a Message": "একটি মেসেজ পাঠান",
"Send message": "মেসেজ পাঠান", "Send message": "মেসেজ পাঠান",
"Server connection verified": "সার্ভার কানেকশন যাচাই করা হয়েছে", "Server connection verified": "সার্ভার কানেকশন যাচাই করা হয়েছে",
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
"Add a short description about what this modelfile does": "Afegeix una descripció curta del que fa aquest arxiu de model", "Add a short description about what this modelfile does": "Afegeix una descripció curta del que fa aquest arxiu de model",
"Add a short title for this prompt": "Afegeix un títol curt per aquest prompt", "Add a short title for this prompt": "Afegeix un títol curt per aquest prompt",
"Add a tag": "Afegeix una etiqueta", "Add a tag": "Afegeix una etiqueta",
"Add custom prompt": "",
"Add Docs": "Afegeix Documents", "Add Docs": "Afegeix Documents",
"Add Files": "Afegeix Arxius", "Add Files": "Afegeix Arxius",
"Add message": "Afegeix missatge", "Add message": "Afegeix missatge",
...@@ -44,6 +45,7 @@ ...@@ -44,6 +45,7 @@
"Archived Chats": "Arxiu d'historial de xat", "Archived Chats": "Arxiu d'historial de xat",
"are allowed - Activate this command by typing": "estan permesos - Activa aquesta comanda escrivint", "are allowed - Activate this command by typing": "estan permesos - Activa aquesta comanda escrivint",
"Are you sure?": "Estàs segur?", "Are you sure?": "Estàs segur?",
"Attach file": "",
"Attention to detail": "", "Attention to detail": "",
"Audio": "Àudio", "Audio": "Àudio",
"Auto-playback response": "Resposta de reproducció automàtica", "Auto-playback response": "Resposta de reproducció automàtica",
...@@ -214,6 +216,7 @@ ...@@ -214,6 +216,7 @@
"Import Modelfiles": "Importa Fitxers de Model", "Import Modelfiles": "Importa Fitxers de Model",
"Import Prompts": "Importa Prompts", "Import Prompts": "Importa Prompts",
"Include `--api` flag when running stable-diffusion-webui": "Inclou la bandera `--api` quan executis stable-diffusion-webui", "Include `--api` flag when running stable-diffusion-webui": "Inclou la bandera `--api` quan executis stable-diffusion-webui",
"Input commands": "",
"Interface": "Interfície", "Interface": "Interfície",
"join our Discord for help.": "uneix-te al nostre Discord per ajuda.", "join our Discord for help.": "uneix-te al nostre Discord per ajuda.",
"JSON": "JSON", "JSON": "JSON",
...@@ -351,6 +354,7 @@ ...@@ -351,6 +354,7 @@
"Select a mode": "Selecciona un mode", "Select a mode": "Selecciona un mode",
"Select a model": "Selecciona un model", "Select a model": "Selecciona un model",
"Select an Ollama instance": "Selecciona una instància d'Ollama", "Select an Ollama instance": "Selecciona una instància d'Ollama",
"Select model": "",
"Send a Message": "Envia un Missatge", "Send a Message": "Envia un Missatge",
"Send message": "Envia missatge", "Send message": "Envia missatge",
"Server connection verified": "Connexió al servidor verificada", "Server connection verified": "Connexió al servidor verificada",
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
"Add a short description about what this modelfile does": "Füge eine kurze Beschreibung hinzu, was dieses Modelfile kann", "Add a short description about what this modelfile does": "Füge eine kurze Beschreibung hinzu, was dieses Modelfile kann",
"Add a short title for this prompt": "Füge einen kurzen Titel für diesen Prompt hinzu", "Add a short title for this prompt": "Füge einen kurzen Titel für diesen Prompt hinzu",
"Add a tag": "Tag hinzufügen", "Add a tag": "Tag hinzufügen",
"Add custom prompt": "",
"Add Docs": "Dokumente hinzufügen", "Add Docs": "Dokumente hinzufügen",
"Add Files": "Dateien hinzufügen", "Add Files": "Dateien hinzufügen",
"Add message": "Nachricht eingeben", "Add message": "Nachricht eingeben",
...@@ -44,6 +45,7 @@ ...@@ -44,6 +45,7 @@
"Archived Chats": "Archivierte Chats", "Archived Chats": "Archivierte Chats",
"are allowed - Activate this command by typing": "sind erlaubt - Aktiviere diesen Befehl, indem du", "are allowed - Activate this command by typing": "sind erlaubt - Aktiviere diesen Befehl, indem du",
"Are you sure?": "Bist du sicher?", "Are you sure?": "Bist du sicher?",
"Attach file": "",
"Attention to detail": "Auge fürs Detail", "Attention to detail": "Auge fürs Detail",
"Audio": "Audio", "Audio": "Audio",
"Auto-playback response": "Automatische Wiedergabe der Antwort", "Auto-playback response": "Automatische Wiedergabe der Antwort",
...@@ -214,6 +216,7 @@ ...@@ -214,6 +216,7 @@
"Import Modelfiles": "Modelfiles importieren", "Import Modelfiles": "Modelfiles importieren",
"Import Prompts": "Prompts importieren", "Import Prompts": "Prompts importieren",
"Include `--api` flag when running stable-diffusion-webui": "Füge das `--api`-Flag hinzu, wenn Du stable-diffusion-webui nutzt", "Include `--api` flag when running stable-diffusion-webui": "Füge das `--api`-Flag hinzu, wenn Du stable-diffusion-webui nutzt",
"Input commands": "",
"Interface": "Benutzeroberfläche", "Interface": "Benutzeroberfläche",
"join our Discord for help.": "Trete unserem Discord bei, um Hilfe zu erhalten.", "join our Discord for help.": "Trete unserem Discord bei, um Hilfe zu erhalten.",
"JSON": "JSON", "JSON": "JSON",
...@@ -351,6 +354,7 @@ ...@@ -351,6 +354,7 @@
"Select a mode": "Einen Modus auswählen", "Select a mode": "Einen Modus auswählen",
"Select a model": "Ein Modell auswählen", "Select a model": "Ein Modell auswählen",
"Select an Ollama instance": "Eine Ollama Instanz auswählen", "Select an Ollama instance": "Eine Ollama Instanz auswählen",
"Select model": "",
"Send a Message": "Eine Nachricht senden", "Send a Message": "Eine Nachricht senden",
"Send message": "Nachricht senden", "Send message": "Nachricht senden",
"Server connection verified": "Serververbindung überprüft", "Server connection verified": "Serververbindung überprüft",
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
"Add a short description about what this modelfile does": "Add short description about what this modelfile does", "Add a short description about what this modelfile does": "Add short description about what this modelfile does",
"Add a short title for this prompt": "Add short title for this prompt", "Add a short title for this prompt": "Add short title for this prompt",
"Add a tag": "Add such tag", "Add a tag": "Add such tag",
"Add custom prompt": "",
"Add Docs": "Add Docs", "Add Docs": "Add Docs",
"Add Files": "Add Files", "Add Files": "Add Files",
"Add message": "Add Prompt", "Add message": "Add Prompt",
...@@ -44,6 +45,7 @@ ...@@ -44,6 +45,7 @@
"Archived Chats": "", "Archived Chats": "",
"are allowed - Activate this command by typing": "are allowed. Activate typing", "are allowed - Activate this command by typing": "are allowed. Activate typing",
"Are you sure?": "Such certainty?", "Are you sure?": "Such certainty?",
"Attach file": "",
"Attention to detail": "", "Attention to detail": "",
"Audio": "Audio", "Audio": "Audio",
"Auto-playback response": "Auto-playback response", "Auto-playback response": "Auto-playback response",
...@@ -214,6 +216,7 @@ ...@@ -214,6 +216,7 @@
"Import Modelfiles": "Import Modelfiles", "Import Modelfiles": "Import Modelfiles",
"Import Prompts": "Import Promptos", "Import Prompts": "Import Promptos",
"Include `--api` flag when running stable-diffusion-webui": "Include `--api` flag when running stable-diffusion-webui", "Include `--api` flag when running stable-diffusion-webui": "Include `--api` flag when running stable-diffusion-webui",
"Input commands": "",
"Interface": "Interface", "Interface": "Interface",
"join our Discord for help.": "join our Discord for help.", "join our Discord for help.": "join our Discord for help.",
"JSON": "JSON", "JSON": "JSON",
...@@ -351,6 +354,7 @@ ...@@ -351,6 +354,7 @@
"Select a mode": "Select a mode very choose", "Select a mode": "Select a mode very choose",
"Select a model": "Select a model much choice", "Select a model": "Select a model much choice",
"Select an Ollama instance": "Select an Ollama instance very choose", "Select an Ollama instance": "Select an Ollama instance very choose",
"Select model": "",
"Send a Message": "Send a Message much message", "Send a Message": "Send a Message much message",
"Send message": "Send message very send", "Send message": "Send message very send",
"Server connection verified": "Server connection verified much secure", "Server connection verified": "Server connection verified much secure",
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment