| import type { PlaygroundChatMessage } from '@/types/playground' |
|
|
| import { AgentMessage, UserMessage } from './MessageItem' |
| import Tooltip from '@/components/ui/tooltip' |
| import { memo } from 'react' |
| import { |
| ToolCallProps, |
| ReasoningStepProps, |
| ReasoningProps, |
| ReferenceData, |
| Reference |
| } from '@/types/playground' |
| import React, { type FC } from 'react' |
| import ChatBlankState from './ChatBlankState' |
| import Icon from '@/components/ui/icon' |
|
|
| interface MessageListProps { |
| messages: PlaygroundChatMessage[] |
| } |
|
|
| interface MessageWrapperProps { |
| message: PlaygroundChatMessage |
| isLastMessage: boolean |
| } |
|
|
| interface ReferenceProps { |
| references: ReferenceData[] |
| } |
|
|
| interface ReferenceItemProps { |
| reference: Reference |
| } |
|
|
| const ReferenceItem: FC<ReferenceItemProps> = ({ reference }) => ( |
| <div className="relative flex h-[63px] w-[190px] cursor-default flex-col justify-between overflow-hidden rounded-md bg-background-secondary p-3 transition-colors hover:bg-background-secondary/80"> |
| <p className="text-sm font-medium text-primary">{reference.name}</p> |
| <p className="truncate text-xs text-primary/40">{reference.content}</p> |
| </div> |
| ) |
|
|
| const References: FC<ReferenceProps> = ({ references }) => ( |
| <div className="flex flex-col gap-4"> |
| {references.map((referenceData, index) => ( |
| <div |
| key={`${referenceData.query}-${index}`} |
| className="flex flex-col gap-3" |
| > |
| <div className="flex flex-wrap gap-3"> |
| {referenceData.references.map((reference, refIndex) => ( |
| <ReferenceItem |
| key={`${reference.name}-${reference.meta_data.chunk}-${refIndex}`} |
| reference={reference} |
| /> |
| ))} |
| </div> |
| </div> |
| ))} |
| </div> |
| ) |
|
|
| const AgentMessageWrapper = ({ message }: MessageWrapperProps) => { |
| return ( |
| <div className="flex flex-col gap-y-9"> |
| {message.extra_data?.reasoning_steps && |
| message.extra_data.reasoning_steps.length > 0 && ( |
| <div className="flex items-start gap-4"> |
| <Tooltip |
| delayDuration={0} |
| content={<p className="text-accent">Reasoning</p>} |
| side="top" |
| > |
| <Icon type="reasoning" size="sm" /> |
| </Tooltip> |
| <div className="flex flex-col gap-3"> |
| <p className="text-xs uppercase">Reasoning</p> |
| <Reasonings reasoning={message.extra_data.reasoning_steps} /> |
| </div> |
| </div> |
| )} |
| {message.extra_data?.references && |
| message.extra_data.references.length > 0 && ( |
| <div className="flex items-start gap-4"> |
| <Tooltip |
| delayDuration={0} |
| content={<p className="text-accent">References</p>} |
| side="top" |
| > |
| <Icon type="references" size="sm" /> |
| </Tooltip> |
| <div className="flex flex-col gap-3"> |
| <References references={message.extra_data.references} /> |
| </div> |
| </div> |
| )} |
| {message.tool_calls && message.tool_calls.length > 0 && ( |
| <div className="flex items-center gap-3"> |
| <Tooltip |
| delayDuration={0} |
| content={<p className="text-accent">Tool Calls</p>} |
| side="top" |
| > |
| <Icon |
| type="hammer" |
| className="rounded-lg bg-background-secondary p-1" |
| size="sm" |
| color="secondary" |
| /> |
| </Tooltip> |
| |
| <div className="flex flex-wrap gap-2"> |
| {message.tool_calls.map((toolCall, index) => ( |
| <ToolComponent |
| key={ |
| toolCall.tool_call_id || |
| `${toolCall.tool_name}-${toolCall.created_at}-${index}` |
| } |
| tools={toolCall} |
| /> |
| ))} |
| </div> |
| </div> |
| )} |
| <AgentMessage message={message} /> |
| </div> |
| ) |
| } |
| const Reasoning: FC<ReasoningStepProps> = ({ index, stepTitle }) => ( |
| <div className="flex items-center gap-2 text-secondary"> |
| <div className="flex h-[20px] items-center rounded-md bg-background-secondary p-2"> |
| <p className="text-xs">STEP {index + 1}</p> |
| </div> |
| <p className="text-xs">{stepTitle}</p> |
| </div> |
| ) |
| const Reasonings: FC<ReasoningProps> = ({ reasoning }) => ( |
| <div className="flex flex-col items-start justify-center gap-2"> |
| {reasoning.map((title, index) => ( |
| <Reasoning |
| key={`${title.title}-${title.action}-${index}`} |
| stepTitle={title.title} |
| index={index} |
| /> |
| ))} |
| </div> |
| ) |
|
|
| const ToolComponent = memo(({ tools }: ToolCallProps) => ( |
| <div className="cursor-default rounded-full bg-accent px-2 py-1.5 text-xs"> |
| <p className="font-dmmono uppercase text-primary/80">{tools.tool_name}</p> |
| </div> |
| )) |
| ToolComponent.displayName = 'ToolComponent' |
| const Messages = ({ messages }: MessageListProps) => { |
| if (messages.length === 0) { |
| return <ChatBlankState /> |
| } |
|
|
| return ( |
| <> |
| {messages.map((message, index) => { |
| const key = `${message.role}-${message.created_at}-${index}` |
| const isLastMessage = index === messages.length - 1 |
| |
| if (message.role === 'agent') { |
| return ( |
| <AgentMessageWrapper |
| key={key} |
| message={message} |
| isLastMessage={isLastMessage} |
| /> |
| ) |
| } |
| return <UserMessage key={key} message={message} /> |
| })} |
| </> |
| ) |
| } |
|
|
| export default Messages |
|
|