feat: image upload support

This commit is contained in:
Timothy J. Baek 2023-12-13 16:21:50 -08:00
parent fb1f8b167c
commit 9b12cdcf83
4 changed files with 84 additions and 57 deletions

View file

@ -14,7 +14,7 @@
export let files = []; export let files = [];
export let fileUploadEnabled = false; export let fileUploadEnabled = true;
export let speechRecognitionEnabled = true; export let speechRecognitionEnabled = true;
export let speechRecognitionListening = false; export let speechRecognitionListening = false;
@ -84,11 +84,12 @@
}; };
</script> </script>
<div class="fixed bottom-0 w-full bg-white dark:bg-gray-800"> <div class="fixed bottom-0 w-full">
<div class=" absolute right-0 left-0 bottom-0 mb-20"> <div class="px-2.5 pt-2.5 -mb-0.5 mx-auto inset-x-0 bg-transparent flex justify-center">
<div class="max-w-3xl px-2.5 pt-2.5 -mb-0.5 mx-auto inset-x-0">
{#if messages.length == 0 && suggestionPrompts.length !== 0} {#if messages.length == 0 && suggestionPrompts.length !== 0}
<div class="max-w-3xl">
<Suggestions {suggestionPrompts} {submitPrompt} /> <Suggestions {suggestionPrompts} {submitPrompt} />
</div>
{/if} {/if}
{#if autoScroll === false && messages.length > 0} {#if autoScroll === false && messages.length > 0}
@ -116,8 +117,7 @@
</div> </div>
{/if} {/if}
</div> </div>
</div> <div class="bg-white dark:bg-gray-800">
<div>
<div class="max-w-3xl px-2.5 -mb-0.5 mx-auto inset-x-0"> <div class="max-w-3xl px-2.5 -mb-0.5 mx-auto inset-x-0">
<div class="bg-gradient-to-t from-white dark:from-gray-800 from-40% pb-2"> <div class="bg-gradient-to-t from-white dark:from-gray-800 from-40% pb-2">
<input <input
@ -136,6 +136,7 @@
} }
]; ];
inputFiles = null; inputFiles = null;
filesInputElement.value = '';
}; };
if ( if (

View file

@ -15,6 +15,7 @@
export let sendPrompt: Function; export let sendPrompt: Function;
export let regenerateResponse: Function; export let regenerateResponse: Function;
export let bottomPadding = false;
export let autoScroll; export let autoScroll;
export let selectedModels; export let selectedModels;
export let history = {}; export let history = {};
@ -31,6 +32,13 @@
})(); })();
} }
$: if (autoScroll && bottomPadding) {
(async () => {
await tick();
window.scrollTo({ top: document.body.scrollHeight, behavior: 'smooth' });
})();
}
const speakMessage = (message) => { const speakMessage = (message) => {
const speak = new SpeechSynthesisUtterance(message); const speak = new SpeechSynthesisUtterance(message);
speechSynthesis.speak(speak); speechSynthesis.speak(speak);
@ -184,7 +192,8 @@
parentId: history.messages[messageId].parentId, parentId: history.messages[messageId].parentId,
childrenIds: [], childrenIds: [],
role: 'user', role: 'user',
content: userPrompt content: userPrompt,
...(history.messages[messageId].files && { files: history.messages[messageId].files })
}; };
let messageParentId = history.messages[messageId].parentId; let messageParentId = history.messages[messageId].parentId;
@ -425,6 +434,18 @@
class="prose chat-{message.role} w-full max-w-full dark:prose-invert prose-headings:my-0 prose-p:my-0 prose-p:-mb-4 prose-pre:my-0 prose-table:my-0 prose-blockquote:my-0 prose-img:my-0 prose-ul:-my-4 prose-ol:-my-4 prose-li:-my-3 prose-ul:-mb-6 prose-ol:-mb-6 prose-li:-mb-4 whitespace-pre-line" class="prose chat-{message.role} w-full max-w-full dark:prose-invert prose-headings:my-0 prose-p:my-0 prose-p:-mb-4 prose-pre:my-0 prose-table:my-0 prose-blockquote:my-0 prose-img:my-0 prose-ul:-my-4 prose-ol:-my-4 prose-li:-my-3 prose-ul:-mb-6 prose-ol:-mb-6 prose-li:-mb-4 whitespace-pre-line"
> >
{#if message.role == 'user'} {#if message.role == 'user'}
{#if message.files}
<div class="my-3 w-full flex overflow-x-auto space-x-2">
{#each message.files as file}
<div>
{#if file.type === 'image'}
<img src={file.url} alt="input" class=" max-h-96 rounded-lg" />
{/if}
</div>
{/each}
</div>
{/if}
{#if message?.edit === true} {#if message?.edit === true}
<div class=" w-full"> <div class=" w-full">
<textarea <textarea
@ -458,17 +479,6 @@
</div> </div>
{:else} {:else}
<div class="w-full"> <div class="w-full">
{#if message.files}
<div class="my-3">
{#each message.files as file}
<div>
{#if file.type === 'image'}
<img src={file.url} alt="input" class=" max-h-96" />
{/if}
</div>
{/each}
</div>
{/if}
<pre id="user-message">{message.content}</pre> <pre id="user-message">{message.content}</pre>
<div class=" flex justify-start space-x-1"> <div class=" flex justify-start space-x-1">
@ -889,4 +899,8 @@
</div> </div>
</div> </div>
{/each} {/each}
{#if bottomPadding}
<div class=" mb-10" />
{/if}
{/if} {/if}

View file

@ -50,6 +50,10 @@
messages = []; messages = [];
} }
$: if (files) {
console.log(files);
}
onMount(async () => { onMount(async () => {
await chatId.set(uuidv4()); await chatId.set(uuidv4());
@ -146,7 +150,15 @@
...messages ...messages
] ]
.filter((message) => message) .filter((message) => message)
.map((message) => ({ role: message.role, content: message.content })), .map((message) => ({
role: message.role,
content: message.content,
...(message.files && {
images: message.files
.filter((file) => file.type === 'image')
.map((file) => file.url.slice(file.url.indexOf(',') + 1))
})
})),
options: { options: {
seed: $settings.seed ?? undefined, seed: $settings.seed ?? undefined,
temperature: $settings.temperature ?? undefined, temperature: $settings.temperature ?? undefined,
@ -548,6 +560,7 @@
bind:history bind:history
bind:messages bind:messages
bind:autoScroll bind:autoScroll
bottomPadding={files.length > 0}
{sendPrompt} {sendPrompt}
{regenerateResponse} {regenerateResponse}
/> />
@ -555,8 +568,8 @@
</div> </div>
<MessageInput <MessageInput
bind:prompt
bind:files bind:files
bind:prompt
bind:autoScroll bind:autoScroll
suggestionPrompts={selectedModelfile?.suggestionPrompts ?? [ suggestionPrompts={selectedModelfile?.suggestionPrompts ?? [
{ {

View file

@ -51,17 +51,6 @@
messages = []; messages = [];
} }
// onMount(async () => {
// let chat = await loadChat();
// await tick();
// if (chat) {
// loaded = true;
// } else {
// await goto('/');
// }
// });
$: if ($page.params.id) { $: if ($page.params.id) {
(async () => { (async () => {
let chat = await loadChat(); let chat = await loadChat();
@ -173,7 +162,15 @@
...messages ...messages
] ]
.filter((message) => message) .filter((message) => message)
.map((message) => ({ role: message.role, content: message.content })), .map((message) => ({
role: message.role,
content: message.content,
...(message.files && {
images: message.files
.filter((file) => file.type === 'image')
.map((file) => file.url.slice(file.url.indexOf(',') + 1))
})
})),
options: { options: {
seed: $settings.seed ?? undefined, seed: $settings.seed ?? undefined,
temperature: $settings.temperature ?? undefined, temperature: $settings.temperature ?? undefined,
@ -579,6 +576,7 @@
bind:history bind:history
bind:messages bind:messages
bind:autoScroll bind:autoScroll
bottomPadding={files.length > 0}
{sendPrompt} {sendPrompt}
{regenerateResponse} {regenerateResponse}
/> />
@ -586,6 +584,7 @@
</div> </div>
<MessageInput <MessageInput
bind:files
bind:prompt bind:prompt
bind:autoScroll bind:autoScroll
suggestionPrompts={selectedModelfile?.suggestionPrompts ?? [ suggestionPrompts={selectedModelfile?.suggestionPrompts ?? [