Feat/add stop conversation button (#76)
* chore add in stop conversation button * feat: use abort controller * chore: formatting --------- Co-authored-by: Simon Holmes <srsholmes@gmail.com>
This commit is contained in:
parent
b0f2a0a3ba
commit
ec84eb2c49
|
@ -34,3 +34,4 @@ yarn-error.log*
|
|||
# typescript
|
||||
*.tsbuildinfo
|
||||
next-env.d.ts
|
||||
.idea
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { Conversation, KeyValuePair, Message, OpenAIModel } from "@/types";
|
||||
import { FC, useEffect, useRef, useState } from "react";
|
||||
import { FC, MutableRefObject, useEffect, useRef, useState } from 'react';
|
||||
import { ChatInput } from "./ChatInput";
|
||||
import { ChatLoader } from "./ChatLoader";
|
||||
import { ChatMessage } from "./ChatMessage";
|
||||
|
@ -17,9 +17,10 @@ interface Props {
|
|||
lightMode: "light" | "dark";
|
||||
onSend: (message: Message, isResend: boolean) => void;
|
||||
onUpdateConversation: (conversation: Conversation, data: KeyValuePair) => void;
|
||||
stopConversationRef: MutableRefObject<boolean>
|
||||
}
|
||||
|
||||
export const Chat: FC<Props> = ({ conversation, models, messageIsStreaming, modelError, messageError, loading, lightMode, onSend, onUpdateConversation }) => {
|
||||
export const Chat: FC<Props> = ({ conversation, models, messageIsStreaming, modelError, messageError, loading, lightMode, onSend, onUpdateConversation, stopConversationRef }) => {
|
||||
const [currentMessage, setCurrentMessage] = useState<Message>();
|
||||
|
||||
const messagesEndRef = useRef<HTMLDivElement>(null);
|
||||
|
@ -96,6 +97,7 @@ export const Chat: FC<Props> = ({ conversation, models, messageIsStreaming, mode
|
|||
/>
|
||||
) : (
|
||||
<ChatInput
|
||||
stopConversationRef={stopConversationRef}
|
||||
messageIsStreaming={messageIsStreaming}
|
||||
onSend={(message) => {
|
||||
setCurrentMessage(message);
|
||||
|
|
|
@ -1,14 +1,15 @@
|
|||
import { Message, OpenAIModel, OpenAIModelID } from "@/types";
|
||||
import { IconSend } from "@tabler/icons-react";
|
||||
import { FC, KeyboardEvent, useEffect, useRef, useState } from "react";
|
||||
import { IconHandStop, IconSend } from "@tabler/icons-react";
|
||||
import { FC, KeyboardEvent, MutableRefObject, useEffect, useRef, useState } from "react";
|
||||
|
||||
interface Props {
|
||||
messageIsStreaming: boolean;
|
||||
onSend: (message: Message) => void;
|
||||
model: OpenAIModel;
|
||||
stopConversationRef: MutableRefObject<boolean>;
|
||||
}
|
||||
|
||||
export const ChatInput: FC<Props> = ({ onSend, messageIsStreaming, model }) => {
|
||||
export const ChatInput: FC<Props> = ({ onSend, messageIsStreaming, model, stopConversationRef }) => {
|
||||
const [content, setContent] = useState<string>();
|
||||
const [isTyping, setIsTyping] = useState<boolean>(false);
|
||||
|
||||
|
@ -67,6 +68,13 @@ export const ChatInput: FC<Props> = ({ onSend, messageIsStreaming, model }) => {
|
|||
}
|
||||
}, [content]);
|
||||
|
||||
function handleStopConversation() {
|
||||
stopConversationRef.current = true;
|
||||
setTimeout(() => {
|
||||
stopConversationRef.current = false;
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="absolute bottom-0 left-0 w-full border-t md:border-t-0 dark:border-white/20 md:border-transparent md:dark:border-transparent dark:bg-[#444654] md:dark:bg-gradient-to-t from-[#343541] via-[#343541] to-[#343541]/0 bg-white md:dark:!bg-transparent dark:md:bg-vert-dark-gradient pt-2">
|
||||
<div className="stretch mx-2 md:mt-[52px] mt-4 flex flex-row gap-3 last:mb-2 md:mx-4 md:last:mb-6 lg:mx-auto lg:max-w-3xl">
|
||||
|
@ -98,6 +106,17 @@ export const ChatInput: FC<Props> = ({ onSend, messageIsStreaming, model }) => {
|
|||
className="opacity-60"
|
||||
/>
|
||||
</button>
|
||||
{messageIsStreaming ? (
|
||||
<button
|
||||
className="absolute right-12 focus:outline-none text-neutral-800 hover:text-neutral-900 dark:text-neutral-100 dark:hover:text-neutral-200 dark:bg-opacity-50 hover:bg-neutral-200 p-1 rounded-sm"
|
||||
onClick={handleStopConversation}
|
||||
>
|
||||
<IconHandStop
|
||||
size={16}
|
||||
className="opacity-60"
|
||||
/>
|
||||
</button>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
<div className="px-3 pt-2 pb-3 text-center text-xs text-black/50 dark:text-white/50 md:px-4 md:pt-3 md:pb-6">
|
||||
|
|
|
@ -8,7 +8,7 @@ import { saveConversation, saveConversations, updateConversation } from "@/utils
|
|||
import { exportConversations, importConversations } from "@/utils/app/data";
|
||||
import { IconArrowBarLeft, IconArrowBarRight } from "@tabler/icons-react";
|
||||
import Head from "next/head";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
|
||||
export default function Home() {
|
||||
const [conversations, setConversations] = useState<Conversation[]>([]);
|
||||
|
@ -21,6 +21,7 @@ export default function Home() {
|
|||
const [apiKey, setApiKey] = useState<string>("");
|
||||
const [messageError, setMessageError] = useState<boolean>(false);
|
||||
const [modelError, setModelError] = useState<boolean>(false);
|
||||
const stopConversationRef = useRef<boolean>(false);
|
||||
|
||||
const handleSend = async (message: Message, isResend: boolean) => {
|
||||
if (selectedConversation) {
|
||||
|
@ -53,11 +54,13 @@ export default function Home() {
|
|||
prompt: updatedConversation.prompt
|
||||
};
|
||||
|
||||
const controller = new AbortController()
|
||||
const response = await fetch("/api/chat", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json"
|
||||
},
|
||||
signal: controller.signal,
|
||||
body: JSON.stringify(chatBody)
|
||||
});
|
||||
|
||||
|
@ -87,6 +90,11 @@ export default function Home() {
|
|||
let text = "";
|
||||
|
||||
while (!done) {
|
||||
if (stopConversationRef.current === true) {
|
||||
controller.abort();
|
||||
done = true;
|
||||
break;
|
||||
}
|
||||
const { value, done: doneReading } = await reader.read();
|
||||
done = doneReading;
|
||||
const chunkValue = decoder.decode(value);
|
||||
|
@ -385,6 +393,7 @@ export default function Home() {
|
|||
lightMode={lightMode}
|
||||
onSend={handleSend}
|
||||
onUpdateConversation={handleUpdateConversation}
|
||||
stopConversationRef={stopConversationRef}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
|
Loading…
Reference in New Issue