| import { Modal } from "@theme"; | |
| import cn from "@utils/classnames.ts"; | |
| import { formatDuration } from "@utils/format.ts"; | |
| import { SquareCode, StopCircle } from "lucide-react"; | |
| import { useState } from "react"; | |
| import type { ChatMessageAssistant } from "../textGeneration/types.ts"; | |
| import MessageContent from "./MessageContent.tsx"; | |
| import MessageToolCall from "./MessageToolCall.tsx"; | |
| export default function Message({ | |
| message, | |
| className = "", | |
| }: { | |
| message: ChatMessageAssistant; | |
| className?: string; | |
| }) { | |
| const [templateOpen, setTemplateOpen] = useState<boolean>(false); | |
| return ( | |
| <div className={cn(className, "group relative flex flex-col gap-1")}> | |
| {message.metadata && ( | |
| <Modal | |
| title="Full Template" | |
| isOpen={templateOpen} | |
| onClose={() => setTemplateOpen(false)} | |
| size="xl" | |
| > | |
| <pre className="max-h-[70vh] overflow-auto rounded-lg border border-gray-300 bg-gray-50 p-4 text-sm leading-relaxed text-gray-900 dark:border-gray-600 dark:bg-gray-900 dark:text-gray-100"> | |
| {message.metadata.template} | |
| </pre> | |
| </Modal> | |
| )} | |
| {message.content.map((part) => | |
| part.type === "tool" ? ( | |
| <MessageToolCall tool={part} /> | |
| ) : ( | |
| <MessageContent content={part.content} /> | |
| ) | |
| )} | |
| {message.interrupted && ( | |
| <div className="mt-2 flex items-center gap-2 rounded-md border border-amber-300 bg-amber-50 px-3 py-2 text-sm text-amber-800 dark:border-amber-700 dark:bg-amber-950 dark:text-amber-200"> | |
| <StopCircle className="size-4 shrink-0" /> | |
| <span>Response interrupted by the user</span> | |
| </div> | |
| )} | |
| {message.metadata && ( | |
| <div className="mt-1 inline-flex flex-wrap items-center gap-1.5 self-end rounded-lg bg-gray-200 px-2.5 py-1 text-xs font-medium text-gray-700 opacity-0 transition-opacity group-hover:opacity-100 dark:bg-gray-700 dark:text-gray-300"> | |
| <span className="font-semibold">{message.metadata.model}</span> | |
| <span className="text-gray-400 dark:text-gray-500">•</span> | |
| <span> | |
| Output: {Math.round(message.metadata.outputTps)} tok/s ( | |
| {message.metadata.outputTokens} tokens in{" "} | |
| {formatDuration(message.metadata.outputDurationMs)}) | |
| </span> | |
| <span className="text-gray-400 dark:text-gray-500">•</span> | |
| <span title="Input processing time"> | |
| Prefill: {formatDuration(message.metadata.inputDurationMs)} | |
| </span> | |
| <span className="text-gray-400 dark:text-gray-500">•</span> | |
| <button | |
| className="flex items-center gap-1 rounded-md px-1.5 py-0.5 transition-colors hover:bg-gray-300 hover:text-gray-900 dark:hover:bg-gray-600 dark:hover:text-gray-100" | |
| onClick={() => setTemplateOpen(true)} | |
| > | |
| <SquareCode className="size-3 -translate-y-1/20" /> | |
| <span>Template</span> | |
| </button> | |
| </div> | |
| )} | |
| </div> | |
| ); | |
| } | |