render tool reponse

This commit is contained in:
2023-11-10 20:33:24 +08:00
parent 1217513ae3
commit 8f9d508a18
5 changed files with 125 additions and 61 deletions

View File

@@ -4,6 +4,10 @@ import { ChatStore, ChatStoreMessage } from "./app";
import { calculate_token_length, getMessageText } from "./chatgpt"; import { calculate_token_length, getMessageText } from "./chatgpt";
import Markdown from "preact-markdown"; import Markdown from "preact-markdown";
import TTSButton from "./tts"; import TTSButton from "./tts";
import { MessageHide } from "./messageHide";
import { MessageDetail } from "./messageDetail";
import { MessageToolCall } from "./messageToolCall";
import { MessageToolResp } from "./messageToolResp";
interface EditMessageProps { interface EditMessageProps {
chat: ChatStoreMessage; chat: ChatStoreMessage;
@@ -355,69 +359,13 @@ export default function Message(props: Props) {
} ${chat.hide ? "opacity-50" : ""}`} } ${chat.hide ? "opacity-50" : ""}`}
> >
{chat.hide ? ( {chat.hide ? (
getMessageText(chat).split("\n")[0].slice(0, 18) + "... (deleted)" <MessageHide chat={chat} />
) : typeof chat.content !== "string" ? ( ) : typeof chat.content !== "string" ? (
chat.content.map((mdt) => <MessageDetail chat={chat} renderMarkdown={renderMarkdown} />
mdt.type === "text" ? (
chat.hide ? (
mdt.text?.split("\n")[0].slice(0, 16) + "... (deleted)"
) : renderMarkdown ? (
// @ts-ignore
<Markdown markdown={mdt.text} />
) : (
mdt.text
)
) : (
<img
className="cursor-pointer max-w-xs max-h-32 p-1"
src={mdt.image_url?.url}
onClick={() => {
window.open(mdt.image_url?.url, "_blank");
}}
/>
)
)
) : chat.tool_calls ? ( ) : chat.tool_calls ? (
<div className="message-content"> <MessageToolCall chat={chat} copyToClipboard={copyToClipboard} />
<div> ) : chat.role === "tool" ? (
{chat.tool_calls?.map((tool_call) => ( <MessageToolResp chat={chat} copyToClipboard={copyToClipboard} />
<div className="bg-blue-300 dark:bg-blue-800 p-1 rounded my-1">
<strong>
Tool Call ID:{" "}
<span
className="p-1 m-1 rounded cursor-pointer hover:opacity-50 hover:underline"
onClick={() => copyToClipboard(String(tool_call.id))}
>
{tool_call?.id}
</span>
</strong>
<p>Type: {tool_call?.type}</p>
<p>
Function:
<span
className="p-1 m-1 rounded cursor-pointer hover:opacity-50 hover:underline"
onClick={() =>
copyToClipboard(tool_call.function.name)
}
>
{tool_call.function.name}
</span>
</p>
<p>
Arguments:
<span
className="p-1 m-1 rounded cursor-pointer hover:opacity-50 hover:underline"
onClick={() =>
copyToClipboard(tool_call.function.arguments)
}
>
{tool_call.function.arguments}
</span>
</p>
</div>
))}
</div>
</div>
) : renderMarkdown ? ( ) : renderMarkdown ? (
// @ts-ignore // @ts-ignore
<Markdown markdown={getMessageText(chat)} /> <Markdown markdown={getMessageText(chat)} />
@@ -425,6 +373,7 @@ export default function Message(props: Props) {
<div className="message-content"> <div className="message-content">
{ {
// only show when content is string or list of message // only show when content is string or list of message
// this check is used to avoid rendering tool call
chat.content && getMessageText(chat) chat.content && getMessageText(chat)
} }
</div> </div>

35
src/messageDetail.tsx Normal file
View File

@@ -0,0 +1,35 @@
import { ChatStoreMessage } from "./app";
interface Props {
chat: ChatStoreMessage;
renderMarkdown: boolean;
}
export function MessageDetail({ chat, renderMarkdown }: Props) {
if (typeof chat.content === "string") {
return <div></div>;
}
return (
<div>
{chat.content.map((mdt) =>
mdt.type === "text" ? (
chat.hide ? (
mdt.text?.split("\n")[0].slice(0, 16) + "... (deleted)"
) : renderMarkdown ? (
// @ts-ignore
<Markdown markdown={mdt.text} />
) : (
mdt.text
)
) : (
<img
className="cursor-pointer max-w-xs max-h-32 p-1"
src={mdt.image_url?.url}
onClick={() => {
window.open(mdt.image_url?.url, "_blank");
}}
/>
)
)}
</div>
);
}

12
src/messageHide.tsx Normal file
View File

@@ -0,0 +1,12 @@
import { ChatStoreMessage } from "./app";
import { getMessageText } from "./chatgpt";
interface Props {
chat: ChatStoreMessage;
}
export function MessageHide({ chat }: Props) {
return (
<div>{getMessageText(chat).split("\n")[0].slice(0, 18)} ... (deleted)</div>
);
}

44
src/messageToolCall.tsx Normal file
View File

@@ -0,0 +1,44 @@
import { ChatStoreMessage } from "./app";
interface Props {
chat: ChatStoreMessage;
copyToClipboard: (text: string) => void;
}
export function MessageToolCall({ chat, copyToClipboard }: Props) {
return (
<div className="message-content">
{chat.tool_calls?.map((tool_call) => (
<div className="bg-blue-300 dark:bg-blue-800 p-1 rounded my-1">
<strong>
Tool Call ID:{" "}
<span
className="p-1 m-1 rounded cursor-pointer hover:opacity-50 hover:underline"
onClick={() => copyToClipboard(String(tool_call.id))}
>
{tool_call?.id}
</span>
</strong>
<p>Type: {tool_call?.type}</p>
<p>
Function:
<span
className="p-1 m-1 rounded cursor-pointer hover:opacity-50 hover:underline"
onClick={() => copyToClipboard(tool_call.function.name)}
>
{tool_call.function.name}
</span>
</p>
<p>
Arguments:
<span
className="p-1 m-1 rounded cursor-pointer hover:opacity-50 hover:underline"
onClick={() => copyToClipboard(tool_call.function.arguments)}
>
{tool_call.function.arguments}
</span>
</p>
</div>
))}
</div>
);
}

24
src/messageToolResp.tsx Normal file
View File

@@ -0,0 +1,24 @@
import { ChatStoreMessage } from "./app";
interface Props {
chat: ChatStoreMessage;
copyToClipboard: (text: string) => void;
}
export function MessageToolResp({ chat, copyToClipboard }: Props) {
return (
<div className="message-content">
<div className="bg-blue-300 dark:bg-blue-800 p-1 rounded my-1">
<strong>
Tool Response ID:{" "}
<span
className="p-1 m-1 rounded cursor-pointer hover:opacity-50 hover:underline"
onClick={() => copyToClipboard(String(chat.tool_call_id))}
>
{chat.tool_call_id}
</span>
</strong>
<p>{chat.content}</p>
</div>
</div>
);
}