diff --git a/src/app.tsx b/src/app.tsx index 9f7b7a5..d436bda 100644 --- a/src/app.tsx +++ b/src/app.tsx @@ -1,10 +1,9 @@ import { useEffect, useState } from "preact/hooks"; import "./global.css"; -import ChatGPT, { Message, ChunkMessage } from "./chatgpt"; -import { createRef } from "preact"; -import Settings from "./settings"; +import { Message } from "./chatgpt"; import getDefaultParams from "./getDefaultParam"; +import ChatBOX from "./chatbox"; export interface ChatStore { systemMessageContent: string; @@ -70,135 +69,8 @@ export function App() { localStorage.setItem(STORAGE_NAME, JSON.stringify(allChatStore)); }, [allChatStore]); - const [inputMsg, setInputMsg] = useState(""); - const [showGenerating, setShowGenerating] = useState(false); - const [generatingMessage, setGeneratingMessage] = useState(""); - - const client = new ChatGPT(chatStore.apiKey); - - const _completeWithStreamMode = async () => { - // call api, return reponse text - const response = await client.completeWithSteam(); - console.log("response", response); - const reader = response.body?.getReader(); - const allChunkMessage: string[] = []; - await new ReadableStream({ - async start(controller) { - while (true) { - let responseDone = false; - let state = await reader?.read(); - let done = state?.done; - let value = state?.value; - if (done) break; - let text = new TextDecoder().decode(value); - // console.log("text:", text); - const lines = text - .trim() - .split("\n") - .map((line) => line.trim()) - .filter((i) => { - if (!i) return false; - if (i === "data: [DONE]") { - responseDone = true; - return false; - } - return true; - }); - console.log("lines", lines); - const jsons: ChunkMessage[] = lines - .map((line) => { - return JSON.parse(line.trim().slice("data: ".length)); - }) - .filter((i) => i); - // console.log("jsons", jsons); - const chunkText = jsons - .map((j) => j.choices[0].delta.content ?? "") - .join(""); - // console.log("chunk text", chunkText); - allChunkMessage.push(chunkText); - setGeneratingMessage(allChunkMessage.join("")); - if (responseDone) break; - } - - // console.log("push to history", allChunkMessage); - chatStore.history.push({ - role: "assistant", - content: allChunkMessage.join(""), - }); - // manually copy status from client to chatStore - chatStore.maxTokens = client.max_tokens; - chatStore.tokenMargin = client.tokens_margin; - chatStore.totalTokens = - client.total_tokens + - 39 + - client.calculate_token_length(allChunkMessage.join("")); - setChatStore({ ...chatStore }); - setGeneratingMessage(""); - setShowGenerating(false); - }, - }); - }; - - const _completeWithFetchMode = async () => { - // call api, return reponse text - const response = await client.complete(); - chatStore.history.push({ role: "assistant", content: response }); - setShowGenerating(false); - }; - - // wrap the actuall complete api - const complete = async () => { - // manually copy status from chatStore to client - client.apiEndpoint = chatStore.apiEndpoint; - client.sysMessageContent = chatStore.systemMessageContent; - client.messages = chatStore.history.slice(chatStore.postBeginIndex); - try { - setShowGenerating(true); - if (chatStore.streamMode) { - await _completeWithStreamMode(); - } else { - await _completeWithFetchMode(); - } - // manually copy status from client to chatStore - chatStore.maxTokens = client.max_tokens; - chatStore.tokenMargin = client.tokens_margin; - chatStore.totalTokens = client.total_tokens; - // when total token > max token - margin token: - // ChatGPT will "forgot" some historical message - // so client.message.length will be less than chatStore.history.length - chatStore.postBeginIndex = - chatStore.history.length - client.messages.length; - console.log("postBeginIndex", chatStore.postBeginIndex); - setChatStore({ ...chatStore }); - } catch (error) { - alert(error); - } - }; - - // when user click the "send" button or ctrl+Enter in the textarea - const send = async (msg = "") => { - const inputMsg = msg; - if (!inputMsg) { - console.log("empty message"); - return; - } - chatStore.history.push({ role: "user", content: inputMsg.trim() }); - setChatStore({ ...chatStore }); - setInputMsg(""); - await complete(); - setChatStore({ ...chatStore }); - }; - - const [showSettings, setShowSettings] = useState(false); - return (
setShowSettings(true)}> -
- 喵喵,请先在上方设置 (OPENAI) API KEY -
- )} - {!chatStore.apiEndpoint && ( -- 喵喵,请先在上方设置 API Endpoint -
- )} - {chatStore.history.length === 0 && ( -- 这里什么都没有哦 QwQ -
- )} - {chatStore.history.map((chat, i) => { - const pClassName = - chat.role === "assistant" - ? "p-2 rounded relative bg-white my-2 text-left" - : "p-2 rounded relative bg-green-400 my-2 text-right"; - const iconClassName = - chat.role === "user" - ? "absolute bottom-0 left-0" - : "absolute bottom-0 right-0"; - const DeleteIcon = () => ( - - ); - return ( -- {chat.content - .split("\n") - .filter((line) => line) - .map((line) => ( -
{line}
- ))} -- {generatingMessage - ? generatingMessage.split("\n").map((line) =>
{line}
) - : "生成中,保持网络稳定喵"} - ... - - )} -setShowSettings(true)}> +
+ 喵喵,请先在上方设置 (OPENAI) API KEY +
+ )} + {!chatStore.apiEndpoint && ( ++ 喵喵,请先在上方设置 API Endpoint +
+ )} + {chatStore.history.length === 0 && ( ++ 这里什么都没有哦 QwQ +
+ )} + {chatStore.history.map((chat, i) => { + const pClassName = + chat.role === "assistant" + ? "p-2 rounded relative bg-white my-2 text-left" + : "p-2 rounded relative bg-green-400 my-2 text-right"; + const iconClassName = + chat.role === "user" + ? "absolute bottom-0 left-0" + : "absolute bottom-0 right-0"; + const DeleteIcon = () => ( + + ); + return ( ++ {chat.content + .split("\n") + .filter((line) => line) + .map((line) => ( +
{line}
+ ))} ++ {generatingMessage + ? generatingMessage.split("\n").map((line) =>
{line}
) + : "生成中,保持网络稳定喵"} + ... + + )} +