From 70029d9bed8db11dcc2d3b222b85192d9e70bba4 Mon Sep 17 00:00:00 2001 From: "Timothy J. Baek" Date: Tue, 9 Jan 2024 22:47:31 -0800 Subject: [PATCH 1/5] feat: @model group convo --- src/lib/apis/ollama/index.ts | 39 +++++ src/lib/components/chat/MessageInput.svelte | 48 ++++-- .../chat/MessageInput/Models.svelte | 153 ++++++++++++++++++ .../chat/MessageInput/Suggestions.svelte | 2 +- .../chat/Messages/UserMessage.svelte | 20 ++- src/routes/(app)/+page.svelte | 3 +- src/routes/(app)/c/[id]/+page.svelte | 11 +- 7 files changed, 261 insertions(+), 15 deletions(-) create mode 100644 src/lib/components/chat/MessageInput/Models.svelte diff --git a/src/lib/apis/ollama/index.ts b/src/lib/apis/ollama/index.ts index 10eddc1c..4dbcd70d 100644 --- a/src/lib/apis/ollama/index.ts +++ b/src/lib/apis/ollama/index.ts @@ -167,6 +167,45 @@ export const generateTitle = async (token: string = '', model: string, prompt: s return res?.response ?? 'New Chat'; }; +export const generatePrompt = async (token: string = '', model: string, conversation: string) => { + let error = null; + + if (conversation === '') { + conversation = '[You need to start the conversation]'; + } + + const res = await fetch(`${OLLAMA_API_BASE_URL}/generate`, { + method: 'POST', + headers: { + 'Content-Type': 'text/event-stream', + Authorization: `Bearer ${token}` + }, + body: JSON.stringify({ + model: model, + prompt: `Based on the following conversation, you are playing the role of 'USER.' Your task is to provide a thoughtful and appropriate response to the last message in the conversation, taking into account the context and tone of the discussion. + + Conversation: + ${conversation} + + As USER, how would you respond to the latest message? If no previous conversation is provided, start a new conversation with a common, friendly greeting or a relevant question. If there is an existing conversation, continue it by providing a thoughtful, relevant, and engaging response. + Response: + ` + }) + }).catch((err) => { + console.log(err); + if ('detail' in err) { + error = err.detail; + } + return null; + }); + + if (error) { + throw error; + } + + return res; +}; + export const generateChatCompletion = async (token: string = '', body: object) => { let error = null; diff --git a/src/lib/components/chat/MessageInput.svelte b/src/lib/components/chat/MessageInput.svelte index 72782da9..67b8ebf7 100644 --- a/src/lib/components/chat/MessageInput.svelte +++ b/src/lib/components/chat/MessageInput.svelte @@ -10,6 +10,7 @@ import AddFilesPlaceholder from '../AddFilesPlaceholder.svelte'; import { SUPPORTED_FILE_TYPE } from '$lib/constants'; import Documents from './MessageInput/Documents.svelte'; + import Models from './MessageInput/Models.svelte'; export let submitPrompt: Function; export let stopResponse: Function; @@ -18,12 +19,17 @@ export let autoScroll = true; let filesInputElement; + let promptsElement; let documentsElement; + let modelsElement; let inputFiles; let dragged = false; + let user = null; + let chatInputPlaceholder = ''; + export let files = []; export let fileUploadEnabled = true; @@ -35,6 +41,15 @@ let speechRecognition; + $: if (prompt) { + const chatInput = document.getElementById('chat-textarea'); + + if (chatInput) { + chatInput.style.height = ''; + chatInput.style.height = Math.min(chatInput.scrollHeight, 200) + 'px'; + } + } + const speechRecognitionHandler = () => { // Check if SpeechRecognition is supported @@ -79,7 +94,7 @@ console.log('recognition ended'); speechRecognitionListening = false; if (prompt !== '' && $settings?.speechAutoSend === true) { - submitPrompt(prompt); + submitPrompt(prompt, user); } }; @@ -242,6 +257,14 @@ ]; }} /> + {:else if prompt.charAt(0) === '@'} + {:else if messages.length == 0 && suggestionPrompts.length !== 0} {/if} @@ -289,7 +312,7 @@
{ - submitPrompt(prompt); + submitPrompt(prompt, user); }} > {#if files.length > 0} @@ -431,14 +454,18 @@ class=" dark:bg-gray-800 dark:text-gray-100 outline-none w-full py-3 px-2 {fileUploadEnabled ? '' : ' pl-4'} rounded-xl resize-none h-[48px]" - placeholder={speechRecognitionListening ? 'Listening...' : 'Send a message'} + placeholder={chatInputPlaceholder !== '' + ? chatInputPlaceholder + : speechRecognitionListening + ? 'Listening...' + : 'Send a message'} bind:value={prompt} on:keypress={(e) => { if (e.keyCode == 13 && !e.shiftKey) { e.preventDefault(); } if (prompt !== '' && e.keyCode == 13 && !e.shiftKey) { - submitPrompt(prompt); + submitPrompt(prompt, user); } }} on:keydown={async (e) => { @@ -473,10 +500,10 @@ editButton?.click(); } - if (['/', '#'].includes(prompt.charAt(0)) && e.key === 'ArrowUp') { + if (['/', '#', '@'].includes(prompt.charAt(0)) && e.key === 'ArrowUp') { e.preventDefault(); - (promptsElement || documentsElement).selectUp(); + (promptsElement || documentsElement || modelsElement).selectUp(); const commandOptionButton = [ ...document.getElementsByClassName('selected-command-option-button') @@ -484,10 +511,10 @@ commandOptionButton.scrollIntoView({ block: 'center' }); } - if (['/', '#'].includes(prompt.charAt(0)) && e.key === 'ArrowDown') { + if (['/', '#', '@'].includes(prompt.charAt(0)) && e.key === 'ArrowDown') { e.preventDefault(); - (promptsElement || documentsElement).selectDown(); + (promptsElement || documentsElement || modelsElement).selectDown(); const commandOptionButton = [ ...document.getElementsByClassName('selected-command-option-button') @@ -495,7 +522,7 @@ commandOptionButton.scrollIntoView({ block: 'center' }); } - if (['/', '#'].includes(prompt.charAt(0)) && e.key === 'Enter') { + if (['/', '#', '@'].includes(prompt.charAt(0)) && e.key === 'Enter') { e.preventDefault(); const commandOptionButton = [ @@ -505,7 +532,7 @@ commandOptionButton?.click(); } - if (['/', '#'].includes(prompt.charAt(0)) && e.key === 'Tab') { + if (['/', '#', '@'].includes(prompt.charAt(0)) && e.key === 'Tab') { e.preventDefault(); const commandOptionButton = [ @@ -536,6 +563,7 @@ on:input={(e) => { e.target.style.height = ''; e.target.style.height = Math.min(e.target.scrollHeight, 200) + 'px'; + user = null; }} on:paste={(e) => { const clipboardData = e.clipboardData || window.clipboardData; diff --git a/src/lib/components/chat/MessageInput/Models.svelte b/src/lib/components/chat/MessageInput/Models.svelte new file mode 100644 index 00000000..aa29db3d --- /dev/null +++ b/src/lib/components/chat/MessageInput/Models.svelte @@ -0,0 +1,153 @@ + + +{#if filteredModels.length > 0} +
+
+
+
@
+
+ +
+
+ {#each filteredModels as model, modelIdx} + + {/each} +
+
+
+
+{/if} diff --git a/src/lib/components/chat/MessageInput/Suggestions.svelte b/src/lib/components/chat/MessageInput/Suggestions.svelte index f0d4b881..aa42aa1a 100644 --- a/src/lib/components/chat/MessageInput/Suggestions.svelte +++ b/src/lib/components/chat/MessageInput/Suggestions.svelte @@ -9,7 +9,7 @@