160 lines
4.4 KiB
Svelte
160 lines
4.4 KiB
Svelte
<script lang="ts">
|
|
//import { fetchEventSource } from "@microsoft/fetch-event-source";
|
|
|
|
import { apiKeyStorage, chatsStorage, addMessage, clearMessages } from "./Storage.svelte";
|
|
import type { Message } from "./Types.svelte";
|
|
|
|
import { marked } from "marked";
|
|
import { afterUpdate, onMount } from "svelte";
|
|
|
|
export let chatId: number;
|
|
let updating: boolean = false;
|
|
|
|
let input: HTMLInputElement;
|
|
$: chat = $chatsStorage.find((chat) => chat.id === chatId);
|
|
const token_price = 0.000002; // $0.002 per 1000 tokens
|
|
|
|
// Focus the input on mount
|
|
onMount(() => input.focus());
|
|
|
|
// Scroll to the bottom of the chat on update
|
|
afterUpdate(() => {
|
|
// Scroll to the bottom of the page after any updates to the messages array
|
|
window.scrollTo(0, document.body.scrollHeight);
|
|
input.focus();
|
|
});
|
|
|
|
const send = async () => {
|
|
// Compose the input message
|
|
const inputMessage: Message = { role: "user", content: input.value };
|
|
addMessage(chatId, inputMessage);
|
|
|
|
// Clear the input value
|
|
input.value = "";
|
|
|
|
// Show updating bar
|
|
updating = true;
|
|
|
|
// Send API request
|
|
/*
|
|
await fetchEventSource("https://api.openai.com/v1/chat/completions", {
|
|
method: "POST",
|
|
headers: {
|
|
Authorization:
|
|
`Bearer ${$apiKeyStorage}`,
|
|
"Content-Type": "text/event-stream",
|
|
},
|
|
body: JSON.stringify({
|
|
model: "gpt-3.5-turbo",
|
|
messages, // Provide the previous messages as well for context
|
|
// temperature: 1
|
|
// top_p: 1
|
|
// n: 1
|
|
stream: false,
|
|
// stop: null
|
|
max_tokens: 4096,
|
|
}),
|
|
onmessage(ev) {
|
|
console.log(ev);
|
|
},
|
|
onerror(err) {
|
|
throw err;
|
|
},
|
|
});
|
|
*/
|
|
const response = await (
|
|
await fetch("https://api.openai.com/v1/chat/completions", {
|
|
method: "POST",
|
|
headers: {
|
|
Authorization: `Bearer ${$apiKeyStorage}`,
|
|
"Content-Type": "application/json",
|
|
},
|
|
body: JSON.stringify({
|
|
model: "gpt-3.5-turbo",
|
|
// Remove the usage property from all messages
|
|
messages: chat.messages.map((message): Message => {
|
|
const { usage, ...rest } = message;
|
|
return rest;
|
|
}),
|
|
// Provide the previous messages as well for context
|
|
// temperature: 1
|
|
// top_p: 1
|
|
// n: 1
|
|
//stream: false,
|
|
// stop: null
|
|
//max_tokens: 4096,
|
|
}),
|
|
})
|
|
).json();
|
|
|
|
console.log(response);
|
|
|
|
// Hide updating bar
|
|
updating = false;
|
|
|
|
if (response.error) {
|
|
addMessage(chatId, {
|
|
role: "system",
|
|
content: `Error: ${response.error.message}`,
|
|
});
|
|
} else {
|
|
response.choices.map((choice) => {
|
|
choice.message.usage = response.usage;
|
|
addMessage(chatId, choice.message);
|
|
});
|
|
}
|
|
};
|
|
</script>
|
|
|
|
<button
|
|
class="button is-warning is-pulled-right"
|
|
on:click={() => {
|
|
clearMessages(chatId);
|
|
}}><span class="greyscale mr-2">🗑️</span> Clear messages</button
|
|
>
|
|
|
|
<p class="subtitle">Chat {chatId}</p>
|
|
|
|
{#each chat.messages as message}
|
|
{#if message.role === "user"}
|
|
<article class="message is-info has-text-right">
|
|
<div class="message-body">{@html marked(message.content)}</div>
|
|
</article>
|
|
{:else if message.role === "system"}
|
|
<article class="message is-danger">
|
|
<div class="message-body">{@html marked(message.content)}</div>
|
|
</article>
|
|
{:else}
|
|
<article class="message is-success">
|
|
<div class="message-body">
|
|
{@html marked(message.content)}
|
|
{#if message.usage}
|
|
<p class="is-size-7">
|
|
This message was generated using <span class="has-text-weight-bold">{message.usage.total_tokens}</span>
|
|
tokens ~=
|
|
<span class="has-text-weight-bold">${(message.usage.total_tokens * token_price).toFixed(6)}</span>
|
|
</p>
|
|
{/if}
|
|
</div>
|
|
</article>
|
|
{/if}
|
|
{/each}
|
|
|
|
{#if updating}
|
|
<progress class="progress is-small is-dark" max="100" />
|
|
{/if}
|
|
|
|
<form class="field has-addons has-addons-right" on:submit|preventDefault={send}>
|
|
<p class="control is-expanded">
|
|
<input
|
|
class="input is-info is-medium is-focused"
|
|
type="text"
|
|
placeholder="Type your message here..."
|
|
bind:this={input}
|
|
/>
|
|
</p>
|
|
<p class="control">
|
|
<button class="button is-info is-medium" type="submit">Send</button>
|
|
</p>
|
|
</form>
|