'use client' import type { FC } from 'react' import type { FeedbackFunc } from '../type' import type { ChatItem, MessageRating, VisionFile } from '@/types/app' import type { Emoji } from '@/types/tools' import { HandThumbDownIcon, HandThumbUpIcon } from '@heroicons/react/24/outline' import React from 'react' import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' import StreamdownMarkdown from '@/app/components/base/streamdown-markdown' import Tooltip from '@/app/components/base/tooltip' import WorkflowProcess from '@/app/components/workflow/workflow-process' import { randomString } from '@/utils/string' import ImageGallery from '../../base/image-gallery' import LoadingAnim from '../loading-anim' import s from '../style.module.css' import Thought from '../thought' function OperationBtn({ innerContent, onClick, className }: { innerContent: React.ReactNode, onClick?: () => void, className?: string }) { return (
{innerContent}
) } const OpeningStatementIcon: FC<{ className?: string }> = ({ className }) => ( ) const RatingIcon: FC<{ isLike: boolean }> = ({ isLike }) => { return isLike ? : } const EditIcon: FC<{ className?: string }> = ({ className }) => { return ( ) } export const EditIconSolid: FC<{ className?: string }> = ({ className }) => { return ( ) } const IconWrapper: FC<{ children: React.ReactNode | string }> = ({ children }) => { return (
{children}
) } interface IAnswerProps { item: ChatItem feedbackDisabled: boolean onFeedback?: FeedbackFunc isResponding?: boolean allToolIcons?: Record suggestionClick?: (suggestion: string) => void } // The component needs to maintain its own state to control whether to display input component const Answer: FC = ({ item, feedbackDisabled = false, onFeedback, isResponding, allToolIcons, suggestionClick = () => { }, }) => { const { id, content, feedback, agent_thoughts, workflowProcess, suggestedQuestions = [] } = item const isAgentMode = !!agent_thoughts && agent_thoughts.length > 0 const { t } = useTranslation() /** * Render feedback results (distinguish between users and administrators) * User reviews cannot be cancelled in Console * @param rating feedback result * @param isUserFeedback Whether it is user's feedback * @returns comp */ const renderFeedbackRating = (rating: MessageRating | undefined) => { if (!rating) { return null } const isLike = rating === 'like' const ratingIconClassname = isLike ? 'text-primary-600 bg-primary-100 hover:bg-primary-200' : 'text-red-600 bg-red-100 hover:bg-red-200' // The tooltip is always displayed, but the content is different for different scenarios. return (
{ await onFeedback?.(id, { rating: null }) }} >
) } /** * Different scenarios have different operation items. * @returns comp */ const renderItemOperation = () => { const userOperation = () => { return feedback?.rating ? null : (
{OperationBtn({ innerContent: , onClick: () => onFeedback?.(id, { rating: 'like' }) })} {OperationBtn({ innerContent: , onClick: () => onFeedback?.(id, { rating: 'dislike' }) })}
) } return (
{userOperation()}
) } const getImgs = (list?: VisionFile[]) => { if (!list) { return [] } return list.filter(file => file.type === 'image' && file.belongs_to === 'assistant') } const agentModeAnswer = (
{agent_thoughts?.map((item, index) => (
{item.thought && ( )} {/* {item.tool} */} {/* perhaps not use tool */} {!!item.tool && ( )} {getImgs(item.message_files).length > 0 && ( item.url)} /> )}
))}
) return (
{isResponding && (
)}
{workflowProcess && ( )} {(isResponding && (isAgentMode ? (!content && (agent_thoughts || []).filter(item => !!item.thought || !!item.tool).length === 0) : !content)) ? (
) : (isAgentMode ? agentModeAnswer : ( ))} {suggestedQuestions.length > 0 && (
{suggestedQuestions.map((suggestion, index) => (
))}
)}
{!feedbackDisabled && !item.feedbackDisabled && renderItemOperation()} {/* User feedback must be displayed */} {!feedbackDisabled && renderFeedbackRating(feedback?.rating)}
) } export default React.memo(Answer)