diff --git a/src/chatbox.tsx b/src/chatbox.tsx index 2b52ea7..d523f08 100644 --- a/src/chatbox.tsx +++ b/src/chatbox.tsx @@ -13,6 +13,7 @@ import ChatGPT, { calculate_token_length, ChunkMessage, FetchResponse, + MessageDetail, } from "./chatgpt"; import Message from "./message"; import models from "./models"; @@ -258,6 +259,7 @@ export default function ChatBOX(props: { ); _setTemplateAPIs(templateAPIs); }; + const [images, setImages] = useState([]); return (
@@ -633,7 +635,13 @@ export default function ChatBOX(props: { chatStore.history .filter(({ hide }) => !hide) .slice(chatStore.postBeginIndex) - .map(({ content }) => content) + .map(({ content }) => { + if (typeof content === "string") { + return content; + } else { + return content.map((c) => c?.text).join(" "); + } + }) ) .concat([inputMsg]) .join(" "); diff --git a/src/chatgpt.ts b/src/chatgpt.ts index 1a1fc33..e96cd03 100644 --- a/src/chatgpt.ts +++ b/src/chatgpt.ts @@ -1,8 +1,22 @@ +export interface MessageDetail { + type: "text" | "image_url"; + text?: string; + image_url?: string; +} export interface Message { role: "system" | "user" | "assistant" | "function"; - content: string; + content: string | MessageDetail[]; name?: "example_user" | "example_assistant"; } +export const getMessageText = (message: Message): string => { + if (typeof message.content === "string") { + return message.content; + } + return message.content + .filter((c) => c.type === "text") + .map((c) => c?.text) + .join("\n"); +}; export interface ChunkMessage { model: string; @@ -29,9 +43,15 @@ export interface FetchResponse { }[]; } // https://help.openai.com/en/articles/4936856-what-are-tokens-and-how-to-count-them -export function calculate_token_length(content: string): number { - const totalCount = content.length; - const chineseCount = content.match(/[\u00ff-\uffff]|\S+/g)?.length ?? 0; +export function calculate_token_length( + content: string | MessageDetail[] +): number { + const text = + typeof content === "string" + ? content + : content.map((c) => c?.text).join(" "); + const totalCount = text.length; + const chineseCount = text.match(/[\u00ff-\uffff]|\S+/g)?.length ?? 0; const englishCount = totalCount - chineseCount; const tokenLength = englishCount / 4 + (chineseCount * 4) / 3; return ~~tokenLength; @@ -151,12 +171,6 @@ class Chat { return j; } - async say(content: string): Promise { - this.messages.push({ role: "user", content }); - await this.complete(); - return this.messages.slice(-1)[0].content; - } - async *processStreamResponse(resp: Response) { const reader = resp?.body?.pipeThrough(new TextDecoderStream()).getReader(); if (reader === undefined) { @@ -210,7 +224,8 @@ class Chat { } return ( - resp?.choices[0]?.message?.content ?? `Error: ${JSON.stringify(resp)}` + (resp?.choices[0]?.message?.content as string) ?? + `Error: ${JSON.stringify(resp)}` ); } @@ -221,7 +236,7 @@ class Chat { completeWithSteam() { this.total_tokens = this.messages - .map((msg) => this.calculate_token_length(msg.content) + 20) + .map((msg) => this.calculate_token_length(msg.content as string) + 20) .reduce((a, v) => a + v); return this._fetch(true); } diff --git a/src/message.tsx b/src/message.tsx index 3779cd4..efd6e75 100644 --- a/src/message.tsx +++ b/src/message.tsx @@ -1,7 +1,7 @@ import { Tr, langCodeContext, LANG_OPTIONS } from "./translate"; import { useState, useEffect, StateUpdater } from "preact/hooks"; import { ChatStore, ChatStoreMessage } from "./app"; -import { calculate_token_length } from "./chatgpt"; +import { calculate_token_length, getMessageText } from "./chatgpt"; import Markdown from "preact-markdown"; import TTSButton from "./tts"; @@ -14,20 +14,6 @@ interface EditMessageProps { function EditMessage(props: EditMessageProps) { const { setShowEdit, chat, setChatStore, chatStore } = props; - useEffect(() => { - const handleKeyPress = (event: any) => { - if (event.keyCode === 27) { - // keyCode for ESC key is 27 - setShowEdit(false); - } - }; - - document.addEventListener("keydown", handleKeyPress); - - return () => { - document.removeEventListener("keydown", handleKeyPress); - }; - }, []); // The empty dependency array ensures that the effect runs only once return (
setShowEdit(false)} >
- + {typeof chat.content === "string" && ( + + )}