feat: add collapsible reasoning content to MessageBubble component, refactor message for response
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
import { XMarkIcon } from "@heroicons/react/24/outline";
|
import { LightBulbIcon, XMarkIcon } from "@heroicons/react/24/outline";
|
||||||
import Markdown from "react-markdown";
|
import Markdown from "react-markdown";
|
||||||
import { useContext, useState, useMemo } from "react";
|
import { useContext, useState, useMemo } from "react";
|
||||||
import { ChatStoreMessage } from "@/types/chatstore";
|
import { ChatStoreMessage } from "@/types/chatstore";
|
||||||
@@ -16,6 +16,11 @@ import {
|
|||||||
} from "@/components/ui/chat/chat-bubble";
|
} from "@/components/ui/chat/chat-bubble";
|
||||||
import { Badge } from "@/components/ui/badge";
|
import { Badge } from "@/components/ui/badge";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
|
import {
|
||||||
|
Collapsible,
|
||||||
|
CollapsibleContent,
|
||||||
|
CollapsibleTrigger,
|
||||||
|
} from "@/components/ui/collapsible";
|
||||||
import { useToast } from "@/hooks/use-toast";
|
import { useToast } from "@/hooks/use-toast";
|
||||||
import {
|
import {
|
||||||
ClipboardIcon,
|
ClipboardIcon,
|
||||||
@@ -24,8 +29,10 @@ import {
|
|||||||
MessageSquarePlusIcon,
|
MessageSquarePlusIcon,
|
||||||
AudioLinesIcon,
|
AudioLinesIcon,
|
||||||
LoaderCircleIcon,
|
LoaderCircleIcon,
|
||||||
|
ChevronsUpDownIcon,
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
import { AppChatStoreContext, AppContext } from "@/pages/App";
|
import { AppChatStoreContext, AppContext } from "@/pages/App";
|
||||||
|
import { Card, CardContent, CardHeader, CardTitle } from "./ui/card";
|
||||||
|
|
||||||
interface HideMessageProps {
|
interface HideMessageProps {
|
||||||
chat: ChatStoreMessage;
|
chat: ChatStoreMessage;
|
||||||
@@ -283,10 +290,97 @@ export default function Message(props: { messageIndex: number }) {
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<ChatBubble
|
{chat.role === "assistant" || chat.role === "received" ? (
|
||||||
variant={chat.role === "assistant" ? "received" : "sent"}
|
<div className="border-b border-border dark:border-border-dark pb-4">
|
||||||
className={chat.role !== "assistant" ? "flex-row-reverse" : ""}
|
{chat.reasoning_content ? (
|
||||||
|
<Collapsible className="mb-3 w-[450px]">
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<h4 className="text-sm font-semibold text-gray-500">
|
||||||
|
{chat.response_model_name}
|
||||||
|
</h4>
|
||||||
|
<CollapsibleTrigger asChild>
|
||||||
|
<Button variant="ghost" size="sm">
|
||||||
|
<LightBulbIcon className="h-3 w-3 text-gray-500" />
|
||||||
|
<span className="sr-only">Toggle</span>
|
||||||
|
</Button>
|
||||||
|
</CollapsibleTrigger>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<CollapsibleContent className="ml-5 text-gray-500">
|
||||||
|
{chat.reasoning_content}
|
||||||
|
</CollapsibleContent>
|
||||||
|
</Collapsible>
|
||||||
|
) : null}
|
||||||
|
<div>
|
||||||
|
{chat.hide ? (
|
||||||
|
<MessageHide chat={chat} />
|
||||||
|
) : typeof chat.content !== "string" ? (
|
||||||
|
<MessageDetail chat={chat} renderMarkdown={renderMarkdown} />
|
||||||
|
) : chat.tool_calls ? (
|
||||||
|
<MessageToolCall chat={chat} copyToClipboard={copyToClipboard} />
|
||||||
|
) : chat.role === "tool" ? (
|
||||||
|
<MessageToolResp chat={chat} copyToClipboard={copyToClipboard} />
|
||||||
|
) : renderMarkdown ? (
|
||||||
|
<Markdown>{getMessageText(chat)}</Markdown>
|
||||||
|
) : (
|
||||||
|
<div className="message-content max-w-full md:max-w-[75%]">
|
||||||
|
{chat.content &&
|
||||||
|
(chat.logprobs && renderColor
|
||||||
|
? chat.logprobs.content
|
||||||
|
.filter((c) => c.token)
|
||||||
|
.map((c) => (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
backgroundColor: logprobToColor(c.logprob),
|
||||||
|
display: "inline",
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
|
{c.token}
|
||||||
|
</div>
|
||||||
|
))
|
||||||
|
: getMessageText(chat))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="flex md:opacity-0 hover:opacity-100 transition-opacity">
|
||||||
|
<ChatBubbleAction
|
||||||
|
icon={
|
||||||
|
chat.hide ? (
|
||||||
|
<MessageSquarePlusIcon className="size-4" />
|
||||||
|
) : (
|
||||||
|
<MessageSquareOffIcon className="size-4" />
|
||||||
|
)
|
||||||
|
}
|
||||||
|
onClick={() => {
|
||||||
|
chatStore.history[messageIndex].hide =
|
||||||
|
!chatStore.history[messageIndex].hide;
|
||||||
|
chatStore.totalTokens = 0;
|
||||||
|
for (const i of chatStore.history
|
||||||
|
.filter(({ hide }) => !hide)
|
||||||
|
.slice(chatStore.postBeginIndex)
|
||||||
|
.map(({ token }) => token)) {
|
||||||
|
chatStore.totalTokens += i;
|
||||||
|
}
|
||||||
|
setChatStore({ ...chatStore });
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<ChatBubbleAction
|
||||||
|
icon={<PencilIcon className="size-4" />}
|
||||||
|
onClick={() => setShowEdit(true)}
|
||||||
|
/>
|
||||||
|
<ChatBubbleAction
|
||||||
|
icon={<ClipboardIcon className="size-4" />}
|
||||||
|
onClick={() => copyToClipboard(getMessageText(chat))}
|
||||||
|
/>
|
||||||
|
{chatStore.tts_api && chatStore.tts_key && (
|
||||||
|
<TTSButton chat={chat} />
|
||||||
|
)}
|
||||||
|
<TTSPlay chat={chat} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<ChatBubble variant="sent" className="flex-row-reverse">
|
||||||
<ChatBubbleMessage isLoading={false}>
|
<ChatBubbleMessage isLoading={false}>
|
||||||
{chat.hide ? (
|
{chat.hide ? (
|
||||||
<MessageHide chat={chat} />
|
<MessageHide chat={chat} />
|
||||||
@@ -348,10 +442,13 @@ export default function Message(props: { messageIndex: number }) {
|
|||||||
icon={<ClipboardIcon className="size-4" />}
|
icon={<ClipboardIcon className="size-4" />}
|
||||||
onClick={() => copyToClipboard(getMessageText(chat))}
|
onClick={() => copyToClipboard(getMessageText(chat))}
|
||||||
/>
|
/>
|
||||||
{chatStore.tts_api && chatStore.tts_key && <TTSButton chat={chat} />}
|
{chatStore.tts_api && chatStore.tts_key && (
|
||||||
|
<TTSButton chat={chat} />
|
||||||
|
)}
|
||||||
<TTSPlay chat={chat} />
|
<TTSPlay chat={chat} />
|
||||||
</ChatBubbleActionWrapper>
|
</ChatBubbleActionWrapper>
|
||||||
</ChatBubble>
|
</ChatBubble>
|
||||||
|
)}
|
||||||
<EditMessage showEdit={showEdit} setShowEdit={setShowEdit} chat={chat} />
|
<EditMessage showEdit={showEdit} setShowEdit={setShowEdit} chat={chat} />
|
||||||
{chatStore.develop_mode && (
|
{chatStore.develop_mode && (
|
||||||
<div
|
<div
|
||||||
|
|||||||
Reference in New Issue
Block a user