Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(ui): assistants chat #1151

Merged
merged 8 commits into from
Sep 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion src/leapfrogai_ui/src/app.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ declare global {
profile?: Profile;
threads?: LFThread[];
assistants?: LFAssistant[];
assistant?: LFAssistant;
files?: FileObject[];
keys?: APIKeyRow[];
}
Expand Down
18 changes: 8 additions & 10 deletions src/leapfrogai_ui/src/lib/components/AssistantAvatar.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,7 @@
ignoreLocation: true
};

$: fileNotUploaded = !$form.avatarFile; // if on upload tab, you must upload a file to enable save

$: fileNotUploaded = !$form.avatar && !$form.avatarFile; // if on upload tab, you must upload a file to enable save
$: avatarToShow = $form.avatarFile ? URL.createObjectURL($form.avatarFile) : $form.avatar;

$: fileTooBig = $form.avatarFile?.size > MAX_AVATAR_SIZE;
Expand Down Expand Up @@ -66,9 +65,7 @@
modalOpen = false;
$form.avatar = originalAvatar;
tempPictogram = selectedPictogramName; // reset to original pictogram
if ($form.avatar) {
$form.avatarFile = $form.avatar; // reset to original file
} else {
if (!$form.avatar) {
clearFileInput();
}
fileUploaderRef.value = ''; // Reset the file input value to ensure input event detection
Expand Down Expand Up @@ -102,7 +99,7 @@
}
} else {
// pictogram tab
selectedPictogramName = tempPictogram; // TODO - can we remove this line
selectedPictogramName = tempPictogram;
$form.pictogram = tempPictogram;
$form.avatar = ''; // remove saved avatar
clearFileInput();
Expand Down Expand Up @@ -197,8 +194,6 @@
>
Upload from computer
</Button>

<input type="hidden" name="avatar" bind:value={$form.avatar} />
</div>

{#if hideUploader}
Expand All @@ -222,7 +217,9 @@
</div>
</div>
</Modal>
<!-- Important! These inputs must be outside of the modal or the image will be lost when the modal closes-->
<!-- Important! These inputs must be outside of the modal or the image will be lost when the modal closes
The hidden inputs will not be nested inside the parent <form> if they are included inside the modal
-->
<input
bind:this={fileUploaderRef}
on:input={(e) => {
Expand All @@ -236,5 +233,6 @@
name="avatarFile"
class="sr-only"
/>
<input type="hidden" name="pictogram" value={selectedPictogramName} />
<input type="hidden" name="avatar" bind:value={$form.avatar} />
<input type="hidden" name="pictogram" bind:value={selectedPictogramName} />
</div>
55 changes: 30 additions & 25 deletions src/leapfrogai_ui/src/lib/components/AssistantCard.svelte
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
<script lang="ts">
import { fade } from 'svelte/transition';
import { goto, invalidate } from '$app/navigation';
import { goto } from '$app/navigation';
import { Avatar, Button, Card, Dropdown, DropdownItem, Modal, P } from 'flowbite-svelte';
import { DotsHorizontalOutline, ExclamationCircleOutline } from 'flowbite-svelte-icons';
import DynamicPictogram from '$components/DynamicPictogram.svelte';
import { threadsStore, toastStore } from '$stores';
import { assistantsStore, toastStore } from '$stores';
import { NO_SELECTED_ASSISTANT_ID, STANDARD_FADE_DURATION } from '$constants';
import type { LFAssistant } from '$lib/types/assistants';

Expand All @@ -13,33 +13,38 @@
let deleteModalOpen = false;

const handleDelete = async () => {
const res = await fetch('/api/assistants/delete', {
method: 'DELETE',
body: JSON.stringify({ id: assistant.id }),
headers: {
'Content-Type': 'application/json'
try {
const res = await fetch('/api/assistants/delete', {
method: 'DELETE',
body: JSON.stringify({ id: assistant.id }),
headers: {
'Content-Type': 'application/json'
}
});
if (res.ok) {
assistantsStore.removeAssistant(assistant.id);
if ($assistantsStore.selectedAssistantId === assistant.id)
assistantsStore.setSelectedAssistantId(NO_SELECTED_ASSISTANT_ID);
toastStore.addToast({
kind: 'info',
title: 'Assistant Deleted.',
subtitle: `${assistant.name} Assistant deleted.`
});
} else {
toastStore.addToast({
kind: 'error',
title: 'Error',
subtitle: 'Error deleting Assistant.'
});
}
});
if ($threadsStore.selectedAssistantId === assistant.id)
threadsStore.setSelectedAssistantId(NO_SELECTED_ASSISTANT_ID);

deleteModalOpen = false;

if (res.ok) {
await invalidate('lf:assistants');
} catch {
toastStore.addToast({
kind: 'info',
title: 'Assistant Deleted.',
subtitle: `${assistant.name} Assistant deleted.`
kind: 'error',
title: 'Error',
subtitle: 'Error deleting Assistant.'
});
return;
}

toastStore.addToast({
kind: 'error',
title: 'Error',
subtitle: 'Error deleting Assistant.'
});
deleteModalOpen = false;
};
</script>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import { fade } from 'svelte/transition';
import { filesStore } from '$stores';
import type { FilesForm } from '$lib/types/files';
import { ACCEPTED_FILE_TYPES, STANDARD_FADE_DURATION } from '$constants';
import { ACCEPTED_DOC_TYPES, STANDARD_FADE_DURATION } from '$constants';
import AssistantFileDropdown from '$components/AssistantFileDropdown.svelte';
import FileUploaderItem from '$components/FileUploaderItem.svelte';

Expand All @@ -17,7 +17,7 @@
.filter((id) => $filesStore.selectedAssistantFileIds.includes(id));
</script>

<AssistantFileDropdown accept={ACCEPTED_FILE_TYPES} {filesForm} class="mb-6" />
<AssistantFileDropdown accept={ACCEPTED_DOC_TYPES} {filesForm} class="mb-6" />

<div class="grid grid-cols-2 gap-4">
{#each filteredStoreFiles as file}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@ import AssistantFileSelect from '$components/AssistantFileSelect.svelte';
import { superValidate } from 'sveltekit-superforms';
import { yup } from 'sveltekit-superforms/adapters';
import { filesSchema } from '$schemas/files';
import type { FileRow } from '$lib/types/files';
import type { LFFileObject } from '$lib/types/files';
import { getUnixSeconds } from '$helpers/dates';
import userEvent from '@testing-library/user-event';

const filesForm = await superValidate({}, yup(filesSchema), { errors: false });

describe('AssistantFileSelect', () => {
const mockFiles: FileRow[] = [
const mockFiles: LFFileObject[] = [
{ id: '1', filename: 'file1.pdf', status: 'complete', created_at: getUnixSeconds(new Date()) },
{ id: '2', filename: 'file2.pdf', status: 'error', created_at: getUnixSeconds(new Date()) },
{ id: '3', filename: 'file3.txt', status: 'uploading', created_at: getUnixSeconds(new Date()) }
Expand Down
18 changes: 13 additions & 5 deletions src/leapfrogai_ui/src/lib/components/AssistantForm.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@
} from '$lib/constants';
import { superForm } from 'sveltekit-superforms';
import { page } from '$app/stores';
import { beforeNavigate, goto, invalidate } from '$app/navigation';
import { beforeNavigate, goto } from '$app/navigation';
import { Button, Modal, P } from 'flowbite-svelte';
import Slider from '$components/Slider.svelte';
import { yup } from 'sveltekit-superforms/adapters';
import { filesStore, toastStore, uiStore } from '$stores';
import { assistantsStore, filesStore, toastStore, uiStore } from '$stores';
import { assistantInputSchema, editAssistantInputSchema } from '$lib/schemas/assistants';
import type { NavigationTarget } from '@sveltejs/kit';
import { onMount } from 'svelte';
Expand All @@ -25,6 +25,10 @@

let bypassCancelWarning = false;

$: assistant = $assistantsStore.assistants.find(
(assistant) => assistant.id === $page.params.assistantId
);

const { form, errors, enhance, submitting, isTainted, delayed } = superForm(data.form, {
invalidateAll: false,
validators: yup(isEditMode ? editAssistantInputSchema : assistantInputSchema),
Expand Down Expand Up @@ -55,8 +59,12 @@
}

bypassCancelWarning = true;
await invalidate('lf:assistants');
goto(result.data.redirectUrl);
if (isEditMode) {
assistantsStore.updateAssistant(result.data.assistant);
} else {
assistantsStore.addAssistant(result.data.assistant);
}
await goto(result.data.redirectUrl);
} else if (result.type === 'failure') {
// 400 errors will show errors for the respective fields, do not show toast
if (result.status !== 400) {
Expand Down Expand Up @@ -174,7 +182,7 @@
<input
type="hidden"
name="vectorStoreId"
value={data?.assistant?.tool_resources?.file_search?.vector_store_ids[0] || undefined}
value={assistant?.tool_resources?.file_search?.vector_store_ids[0] || undefined}
/>

<div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import AssistantProgressToast from '$components/AssistantProgressToast.svelte';
import { render, screen } from '@testing-library/svelte';
import filesStore from '$stores/filesStore';
import { getFakeFiles } from '$testUtils/fakeData';
import { convertFileObjectToFileRows } from '$helpers/fileHelpers';
import { convertFileObjectToLFFileObject } from '$helpers/fileHelpers';
import { delay } from 'msw';
import { vi } from 'vitest';
import { toastStore } from '$stores';
Expand All @@ -27,7 +27,7 @@ describe('AssistantProgressToast', () => {
fileIds: files.map((file) => file.id),
vectorStoreId: '123'
};
filesStore.setFiles(convertFileObjectToFileRows(files));
filesStore.setFiles(convertFileObjectToLFFileObject(files));

const timeout = 10; //10ms
render(AssistantProgressToast, { timeout, toast }); //10ms timeout
Expand Down
19 changes: 9 additions & 10 deletions src/leapfrogai_ui/src/lib/components/ChatFileUpload.svelte
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<script lang="ts">
import { env } from '$env/dynamic/public';
import {
ACCEPTED_FILE_TYPES,
ACCEPTED_DOC_AND_AUDIO_FILE_TYPES,
APPROX_MAX_CHARACTERS,
FILE_UPLOAD_PROMPT,
MAX_NUM_FILES_UPLOAD
Expand Down Expand Up @@ -61,24 +61,23 @@
body: formData
})
.then(async (response) => {
if (!response.ok) {
if (response.ok) {
const result = await response.json();
return {
id: file.id,
name: shortenFileName(file.name),
type: file.type,
text: ERROR_UPLOADING_FILE_MSG,
status: 'error',
errorText: ERROR_UPLOADING_FILE_MSG
text: result.text,
status: 'complete'
};
}

const result = await response.json();
return {
id: file.id,
name: shortenFileName(file.name),
type: file.type,
text: result.text,
status: 'complete'
text: ERROR_UPLOADING_FILE_MSG,
status: 'error',
errorText: ERROR_UPLOADING_FILE_MSG
};
})
.catch(() => {
Expand Down Expand Up @@ -144,7 +143,7 @@
convertFiles(e.detail);
fileUploadBtnRef.value = '';
}}
accept={ACCEPTED_FILE_TYPES}
accept={ACCEPTED_DOC_AND_AUDIO_FILE_TYPES}
disabled={uploadingFiles}
class="remove-btn-style flex rounded-lg p-1.5 text-gray-500 hover:bg-inherit dark:hover:bg-inherit"
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import {
FILE_TRANSLATION_ERROR
} from '$constants/toastMessages';
import { getFakeThread } from '$testUtils/fakeData';
import { AUDIO_FILE_SIZE_ERROR_TEXT, NO_SELECTED_ASSISTANT_ID } from '$constants';
import { AUDIO_FILE_SIZE_ERROR_TEXT } from '$constants';

const thread = getFakeThread();

Expand Down Expand Up @@ -74,7 +74,6 @@ describe('FileChatActions', () => {
threadsStore.set({
threads: [thread], // uses date override starting in March
sendingBlocked: false,
selectedAssistantId: NO_SELECTED_ASSISTANT_ID,
lastVisitedThreadId: '',
streamingMessage: null
});
Expand Down
1 change: 0 additions & 1 deletion src/leapfrogai_ui/src/lib/components/LFHeader.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ describe('LFHeader', () => {
threadsStore.set({
threads: [thread],
lastVisitedThreadId: thread.id,
selectedAssistantId: '',
sendingBlocked: false,
streamingMessage: null
});
Expand Down
11 changes: 6 additions & 5 deletions src/leapfrogai_ui/src/lib/components/Message.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
UserCircleOutline
} from 'flowbite-svelte-icons';
import { twMerge } from 'tailwind-merge';
import { threadsStore, toastStore } from '$stores';
import { assistantsStore, threadsStore, toastStore } from '$stores';
import { convertTextToMessageContentArr, getMessageText } from '$helpers/threads';
import type { Message as OpenAIMessage } from 'openai/resources/beta/threads/messages';
import {
Expand Down Expand Up @@ -59,7 +59,7 @@
});

let assistantImage = isRunAssistantMessage(message)
? getAssistantImage($page.data.assistants || [], message.assistant_id!)
? getAssistantImage($assistantsStore.assistants || [], message.assistant_id!)
: null;

let messageIsHovered = false;
Expand All @@ -69,7 +69,8 @@
const getAssistantName = (id?: string) => {
if (!id) return 'LeapfrogAI Bot';
return (
$page.data.assistants?.find((assistant) => assistant.id === id)?.name || 'LeapfrogAI Bot'
$assistantsStore.assistants?.find((assistant) => assistant.id === id)?.name ||
'LeapfrogAI Bot'
);
};

Expand All @@ -83,7 +84,7 @@
message: { ...message, content: convertTextToMessageContentArr($value) },
setMessages: setMessages!,
append: append!,
selectedAssistantId: $threadsStore.selectedAssistantId
selectedAssistantId: $assistantsStore.selectedAssistantId
});
};

Expand Down Expand Up @@ -233,7 +234,7 @@
message: messages[messages.length - 2],
setMessages,
append: append,
selectedAssistantId: $threadsStore.selectedAssistantId
selectedAssistantId: $assistantsStore.selectedAssistantId
})}
aria-label="regenerate message"
tabindex="0"
Expand Down
Loading