This repository has been archived by the owner on Jan 8, 2025. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Add a chat component * Update test to check for correct URL
- Loading branch information
1 parent
745b5f8
commit a5983d7
Showing
9 changed files
with
206 additions
and
27 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
export interface Prompts { | ||
system: { [key: string]: string }; | ||
user: { [key: string]: string }; | ||
} | ||
|
||
export interface Message { | ||
role: 'user' | 'system' | 'assistant'; | ||
content: string; | ||
timestamp: string; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
import { redirect } from '@sveltejs/kit'; | ||
|
||
export function load() { | ||
redirect(308, '/text-to-speech'); | ||
} |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
import OpenAI from 'openai'; | ||
import { OPENAI_API_KEY } from '$lib/server/secrets.js'; | ||
import type { Message } from '$lib/types'; | ||
|
||
const openai = new OpenAI({ apiKey: OPENAI_API_KEY }); | ||
|
||
export async function POST({ request }) { | ||
const json = await request.json(); | ||
const messages = json.messages as Message[]; | ||
const messagesOpenai = messages.map((message) => { | ||
return { | ||
role: message.role, | ||
content: message.content | ||
}; | ||
}); | ||
const model = json.model as string; | ||
|
||
if (messages.length === 0) { | ||
return new Response('Missing input.', { status: 422 }); | ||
} | ||
if (messages[messages.length - 1].role !== 'user') { | ||
return new Response('Last message must be from user.', { status: 422 }); | ||
} | ||
|
||
const response = await openai.chat.completions.create({ | ||
model, | ||
messages: messagesOpenai | ||
}); | ||
const response_message = response.choices[0].message.content; | ||
if (!response_message) { | ||
return new Response('No response from OpenAI.', { status: 500 }); | ||
} | ||
|
||
return new Response(JSON.stringify({ message: response_message }), { | ||
status: 200, | ||
headers: { 'Content-Type': 'application/json' } | ||
}); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
<script lang="ts"> | ||
import { Avatar } from '@skeletonlabs/skeleton'; | ||
import { onMount } from 'svelte'; | ||
export let systemPrompt: string; | ||
let messages = [ | ||
{ role: 'system', content: systemPrompt, timestamp: new Date().toLocaleTimeString() } | ||
]; | ||
let currentMessage = ''; | ||
let elemChat: HTMLElement; | ||
let loading = false; | ||
const model = 'gpt-4'; | ||
const user_avatar = 'https://i.pravatar.cc/150?img=31'; | ||
const server_avatar = 'https://i.pravatar.cc/150?img=54'; | ||
async function addUserMessage(event: KeyboardEvent) { | ||
if (event.key !== 'Enter') return; | ||
if (currentMessage === '') return; | ||
if (loading) return; | ||
event.preventDefault(); | ||
addMessage(currentMessage, 'user'); | ||
loading = true; | ||
await fetch('/api/gpt', { | ||
method: 'POST', | ||
headers: { | ||
'Content-Type': 'application/json' | ||
}, | ||
body: JSON.stringify({ messages, model }) | ||
}) | ||
.then((response) => { | ||
if (response.ok) { | ||
return response.json(); | ||
} | ||
throw new Error('Network response was not ok.'); | ||
}) | ||
.then((data) => { | ||
addMessage(data.message, 'assistant'); | ||
loading = false; | ||
}) | ||
.catch((error) => { | ||
console.error('There was a problem with the fetch operation:', error); | ||
}); | ||
loading = false; | ||
} | ||
function addMessage(content: string, role: 'assistant' | 'user') { | ||
const timestamp = new Date().toLocaleTimeString(); | ||
messages = [ | ||
...messages, | ||
{ | ||
content, | ||
role, | ||
timestamp | ||
} | ||
]; | ||
currentMessage = ''; | ||
// Timeout prevents race condition | ||
setTimeout(() => { | ||
scrollChatBottom(); | ||
}, 0); | ||
} | ||
function scrollChatBottom(): void { | ||
if (!elemChat) return; | ||
elemChat.scrollTo({ top: elemChat.scrollHeight, behavior: 'smooth' }); | ||
} | ||
onMount(() => { | ||
scrollChatBottom(); | ||
}); | ||
</script> | ||
|
||
<div class="chat w-full h-full"> | ||
<section | ||
bind:this={elemChat} | ||
id="chat-container" | ||
class="w-full max-h-[600px] min-h-[600px] p-4 overflow-y-auto space-y-4" | ||
> | ||
{#each messages as bubble} | ||
{#if bubble.role === 'assistant'} | ||
<div class="grid grid-cols-[auto_1fr] gap-2"> | ||
<Avatar src={server_avatar} width="w-12" /> | ||
<div class="card p-4 rounded-tl-none space-y-2 bg-primary-200/30"> | ||
<header class="flex justify-between items-center"> | ||
<p class="font-bold">Agate</p> | ||
<small class="opacity-50">{bubble.timestamp}</small> | ||
</header> | ||
<p class="whitespace-pre">{bubble.content}</p> | ||
</div> | ||
</div> | ||
{:else if bubble.role === 'user'} | ||
<div class="grid grid-cols-[1fr_auto] gap-2"> | ||
<div class="card p-4 rounded-tr-none space-y-2 bg-primary-500/30"> | ||
<header class="flex justify-between items-center"> | ||
<p class="font-bold">You</p> | ||
<small class="opacity-50">{bubble.timestamp}</small> | ||
</header> | ||
<p class="whitespace-pre">{bubble.content}</p> | ||
</div> | ||
<Avatar src={user_avatar} width="w-12" /> | ||
</div> | ||
{/if} | ||
{/each} | ||
{#if loading} | ||
<p>Agate is typing...</p> | ||
{/if} | ||
</section> | ||
<div class="input-group input-group-divider grid-cols-[auto_1fr_auto] rounded-container-token"> | ||
<button class="input-group-shim">+</button> | ||
<textarea | ||
bind:value={currentMessage} | ||
class="bg-transparent border-0 ring-0" | ||
name="prompt" | ||
id="prompt" | ||
placeholder="Write a message..." | ||
rows="4" | ||
on:keydown={addUserMessage} | ||
/> | ||
</div> | ||
</div> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters