| import { Modal, Tooltip } from "@theme"; |
| import cn from "@utils/classnames.ts"; |
| import { formatDuration } from "@utils/format.ts"; |
| import { SquareCode, StopCircle, Thermometer } 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, i) => |
| part.type === "tool" ? ( |
| <MessageToolCall key={i} tool={part} /> |
| ) : ( |
| <MessageContent key={i} 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> |
| <Tooltip |
| textSize="xs" |
| text={`Generated ${message.metadata.outputTokens} tokens in ${formatDuration(message.metadata.outputDurationMs)}`} |
| > |
| <span>{Math.round(message.metadata.outputTps)} tps</span> |
| </Tooltip> |
| <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> |
| <Tooltip |
| textSize="xs" |
| text={`Temperature: ${message.metadata.temperature}`} |
| > |
| <span className="gao-1 flex items-center"> |
| <Thermometer className="h-3 w-3" /> |
| {message.metadata.temperature} |
| </span> |
| </Tooltip> |
| <span className="text-gray-400 dark:text-gray-500">•</span> |
| <span>KV Cache: {message.metadata.useKvCache ? "On" : "Off"}</span> |
| <span className="text-gray-400 dark:text-gray-500">•</span> |
| <button |
| className="-ml-1 flex cursor-pointer 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> |
| ); |
| } |
|
|