modify prompt log

This commit is contained in:
JzoNg 2024-03-15 18:26:35 +08:00
parent e3f1e143e5
commit 4835358f24
15 changed files with 217 additions and 209 deletions

View File

@ -1,13 +1,13 @@
'use client'
import type { FC, ReactNode } from 'react'
import React, { useState } from 'react'
import React, { useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { UserCircleIcon } from '@heroicons/react/24/solid'
import cn from 'classnames'
import type { CitationItem, DisplayScene, FeedbackFunc, Feedbacktype, IChatItem } from '../type'
import OperationBtn from '../operation'
import LoadingAnim from '../loading-anim'
import { EditIconSolid, RatingIcon } from '../icon-component'
import { RatingIcon } from '../icon-component'
import s from '../style.module.css'
import MoreInfo from '../more-info'
import CopyBtn from '../copy-btn'
@ -26,16 +26,8 @@ import { MessageFast } from '@/app/components/base/icons/src/vender/solid/commun
import type { Emoji } from '@/app/components/tools/types'
import type { VisionFile } from '@/types/app'
import ImageGallery from '@/app/components/base/image-gallery'
import Log from '@/app/components/app/chat/log'
const Divider: FC<{ name: string }> = ({ name }) => {
const { t } = useTranslation()
return <div className='flex items-center my-2'>
<span className='text-xs text-gray-500 inline-flex items-center mr-2'>
<EditIconSolid className='mr-1' />{t('appLog.detail.annotationTip', { user: name })}
</span>
<div className='h-[1px] bg-gray-200 flex-1'></div>
</div>
}
const IconWrapper: FC<{ children: React.ReactNode | string }> = ({ children }) => {
return <div className={'rounded-lg h-6 w-6 flex items-center justify-center hover:bg-gray-100'}>
{children}
@ -64,6 +56,7 @@ export type IAnswerProps = {
onAnnotationAdded?: (annotationId: string, authorName: string, question: string, answer: string, index: number) => void
onAnnotationRemoved?: (index: number) => void
allToolIcons?: Record<string, string | Emoji>
isShowPromptLog?: boolean
}
// The component needs to maintain its own state to control whether to display input component
const Answer: FC<IAnswerProps> = ({
@ -87,12 +80,11 @@ const Answer: FC<IAnswerProps> = ({
onAnnotationAdded,
onAnnotationRemoved,
allToolIcons,
isShowPromptLog,
}) => {
const { id, content, more, feedback, adminFeedback, annotation, agent_thoughts } = item
const isAgentMode = !!agent_thoughts && agent_thoughts.length > 0
const hasAnnotation = !!annotation?.id
const [showEdit, setShowEdit] = useState(false)
const [loading, setLoading] = useState(false)
// const [annotation, setAnnotation] = useState<Annotation | undefined | null>(initAnnotation)
// const [inputValue, setInputValue] = useState<string>(initAnnotation?.content ?? '')
const [localAdminFeedback, setLocalAdminFeedback] = useState<Feedbacktype | undefined | null>(adminFeedback)
@ -241,9 +233,11 @@ const Answer: FC<IAnswerProps> = ({
</div>
)
const ref = useRef(null)
return (
// data-id for debug the item message is right
<div key={id} data-id={id}>
<div key={id} data-id={id} ref={ref}>
<div className='flex items-start'>
{
answerIcon || (
@ -257,7 +251,7 @@ const Answer: FC<IAnswerProps> = ({
)
}
<div className={cn(s.answerWrapWrap, 'chat-answer-container group')}>
<div className={`${s.answerWrap} ${showEdit ? 'w-full' : ''}`}>
<div className={s.answerWrap}>
<div className={`${s.answer} relative text-sm text-gray-900`}>
<div className={'ml-2 py-3 px-4 bg-gray-100 rounded-tr-2xl rounded-b-2xl'}>
{(isResponding && (isAgentMode ? (!content && (agent_thoughts || []).filter(item => !!item.thought || !!item.tool).length === 0) : !content))
@ -326,11 +320,21 @@ const Answer: FC<IAnswerProps> = ({
className={cn(s.copyBtn, 'mr-1')}
/>
)}
{!item.isOpeningStatement && isShowTextToSpeech && (
<AudioBtn
value={content}
className={cn(s.playBtn, 'mr-1')}
/>
{((isShowPromptLog && !isResponding) || (!item.isOpeningStatement && isShowTextToSpeech)) && (
<div className='hidden group-hover:flex items-center h-[28px] p-0.5 rounded-lg bg-white border-[0.5px] border-gray-100 shadow-md'>
{isShowPromptLog && !isResponding && (
<Log runID={item.workflow_run_id} log={item.log!} containerRef={ref} />
)}
{!item.isOpeningStatement && isShowTextToSpeech && (
<>
<div className='mx-1 w-[1px] h-[14px] bg-gray-200'/>
<AudioBtn
value={content}
className={cn(s.playBtn)}
/>
</>
)}
</div>
)}
{(!item.isOpeningStatement && supportAnnotation) && (
<AnnotationCtrlBtn

View File

@ -300,6 +300,7 @@ const Chat: FC<IChatProps> = ({
onAnnotationAdded={handleAnnotationAdded}
onAnnotationRemoved={handleAnnotationRemoved}
allToolIcons={allToolIcons}
isShowPromptLog={isShowPromptLog}
/>
}
return (
@ -316,140 +317,132 @@ const Chat: FC<IChatProps> = ({
)
})}
</div>
{
!isHideSendInput && (
<div className={cn(!feedbackDisabled && '!left-3.5 !right-3.5', 'absolute z-10 bottom-0 left-0 right-0')}>
{/* Thinking is sync and can not be stopped */}
{(isResponding && canStopResponding && ((!!chatList[chatList.length - 1]?.content) || (chatList[chatList.length - 1]?.agent_thoughts && chatList[chatList.length - 1].agent_thoughts!.length > 0))) && (
<div className='flex justify-center mb-4'>
<Button className='flex items-center space-x-1 bg-white' onClick={() => abortResponding?.()}>
{stopIcon}
<span className='text-xs text-gray-500 font-normal'>{t('appDebug.operation.stopResponding')}</span>
</Button>
{!isHideSendInput && (
<div className={cn(!feedbackDisabled && '!left-3.5 !right-3.5', 'absolute z-10 bottom-0 left-0 right-0')}>
{/* Thinking is sync and can not be stopped */}
{(isResponding && canStopResponding && ((!!chatList[chatList.length - 1]?.content) || (chatList[chatList.length - 1]?.agent_thoughts && chatList[chatList.length - 1].agent_thoughts!.length > 0))) && (
<div className='flex justify-center mb-4'>
<Button className='flex items-center space-x-1 bg-white' onClick={() => abortResponding?.()}>
{stopIcon}
<span className='text-xs text-gray-500 font-normal'>{t('appDebug.operation.stopResponding')}</span>
</Button>
</div>
)}
{isShowSuggestion && (
<div className='pt-2'>
<div className='flex items-center justify-center mb-2.5'>
<div className='grow h-[1px]'
style={{
background: 'linear-gradient(270deg, #F3F4F6 0%, rgba(243, 244, 246, 0) 100%)',
}}></div>
<div className='shrink-0 flex items-center px-3 space-x-1'>
{TryToAskIcon}
<span className='text-xs text-gray-500 font-medium'>{t('appDebug.feature.suggestedQuestionsAfterAnswer.tryToAsk')}</span>
</div>
<div className='grow h-[1px]'
style={{
background: 'linear-gradient(270deg, rgba(243, 244, 246, 0) 0%, #F3F4F6 100%)',
}}></div>
</div>
{/* has scrollbar would hide part of first item */}
<div ref={suggestionListRef} className={cn(!hasScrollbar && 'justify-center', 'flex overflow-x-auto pb-2')}>
{suggestionList?.map((item, index) => (
<div key={item} className='shrink-0 flex justify-center mr-2'>
<Button
key={index}
onClick={() => onQueryChange(item)}
>
<span className='text-primary-600 text-xs font-medium'>{item}</span>
</Button>
</div>
))}
</div>
</div>
)}
<div className={cn('p-[5.5px] max-h-[150px] bg-white border-[1.5px] border-gray-200 rounded-xl overflow-y-auto', isDragActive && 'border-primary-600')}>
{visionConfig?.enabled && (
<>
<div className='absolute bottom-2 left-2 flex items-center'>
<ChatImageUploader
settings={visionConfig}
onUpload={onUpload}
disabled={files.length >= visionConfig.number_limits}
/>
<div className='mx-1 w-[1px] h-4 bg-black/5' />
</div>
<div className='pl-[52px]'>
<ImageList
list={files}
onRemove={onRemove}
onReUpload={onReUpload}
onImageLinkLoadSuccess={onImageLinkLoadSuccess}
onImageLinkLoadError={onImageLinkLoadError}
/>
</div>
</>
)}
{
isShowSuggestion && (
<div className='pt-2'>
<div className='flex items-center justify-center mb-2.5'>
<div className='grow h-[1px]'
style={{
background: 'linear-gradient(270deg, #F3F4F6 0%, rgba(243, 244, 246, 0) 100%)',
}}></div>
<div className='shrink-0 flex items-center px-3 space-x-1'>
{TryToAskIcon}
<span className='text-xs text-gray-500 font-medium'>{t('appDebug.feature.suggestedQuestionsAfterAnswer.tryToAsk')}</span>
</div>
<div className='grow h-[1px]'
style={{
background: 'linear-gradient(270deg, rgba(243, 244, 246, 0) 0%, #F3F4F6 100%)',
}}></div>
</div>
{/* has scrollbar would hide part of first item */}
<div ref={suggestionListRef} className={cn(!hasScrollbar && 'justify-center', 'flex overflow-x-auto pb-2')}>
{suggestionList?.map((item, index) => (
<div key={item} className='shrink-0 flex justify-center mr-2'>
<Button
key={index}
onClick={() => onQueryChange(item)}
>
<span className='text-primary-600 text-xs font-medium'>{item}</span>
</Button>
</div>
))}
</div>
</div>)
}
<div className={cn('p-[5.5px] max-h-[150px] bg-white border-[1.5px] border-gray-200 rounded-xl overflow-y-auto', isDragActive && 'border-primary-600')}>
<Textarea
className={`
block w-full px-2 pr-[118px] py-[7px] leading-5 max-h-none text-sm text-gray-700 outline-none appearance-none resize-none
${visionConfig?.enabled && 'pl-12'}
`}
value={query}
onChange={handleContentChange}
onKeyUp={handleKeyUp}
onKeyDown={handleKeyDown}
onPaste={onPaste}
onDragEnter={onDragEnter}
onDragLeave={onDragLeave}
onDragOver={onDragOver}
onDrop={onDrop}
autoSize
/>
<div className="absolute bottom-2 right-2 flex items-center h-8">
<div className={`${s.count} mr-4 h-5 leading-5 text-sm bg-gray-50 text-gray-500`}>{query.trim().length}</div>
{
visionConfig?.enabled && (
<>
<div className='absolute bottom-2 left-2 flex items-center'>
<ChatImageUploader
settings={visionConfig}
onUpload={onUpload}
disabled={files.length >= visionConfig.number_limits}
/>
<div className='mx-1 w-[1px] h-4 bg-black/5' />
query
? (
<div className='flex justify-center items-center w-8 h-8 cursor-pointer hover:bg-gray-100 rounded-lg' onClick={() => onQueryChange('')}>
<XCircle className='w-4 h-4 text-[#98A2B3]' />
</div>
<div className='pl-[52px]'>
<ImageList
list={files}
onRemove={onRemove}
onReUpload={onReUpload}
onImageLinkLoadSuccess={onImageLinkLoadSuccess}
onImageLinkLoadError={onImageLinkLoadError}
/>
</div>
</>
)
}
<Textarea
className={`
block w-full px-2 pr-[118px] py-[7px] leading-5 max-h-none text-sm text-gray-700 outline-none appearance-none resize-none
${visionConfig?.enabled && 'pl-12'}
`}
value={query}
onChange={handleContentChange}
onKeyUp={handleKeyUp}
onKeyDown={handleKeyDown}
onPaste={onPaste}
onDragEnter={onDragEnter}
onDragLeave={onDragLeave}
onDragOver={onDragOver}
onDrop={onDrop}
autoSize
/>
<div className="absolute bottom-2 right-2 flex items-center h-8">
<div className={`${s.count} mr-4 h-5 leading-5 text-sm bg-gray-50 text-gray-500`}>{query.trim().length}</div>
{
query
)
: isShowSpeechToText
? (
<div className='flex justify-center items-center w-8 h-8 cursor-pointer hover:bg-gray-100 rounded-lg' onClick={() => onQueryChange('')}>
<XCircle className='w-4 h-4 text-[#98A2B3]' />
<div
className='group flex justify-center items-center w-8 h-8 hover:bg-primary-50 rounded-lg cursor-pointer'
onClick={handleVoiceInputShow}
>
<Microphone01 className='block w-4 h-4 text-gray-500 group-hover:hidden' />
<Microphone01Solid className='hidden w-4 h-4 text-primary-600 group-hover:block' />
</div>
)
: isShowSpeechToText
? (
<div
className='group flex justify-center items-center w-8 h-8 hover:bg-primary-50 rounded-lg cursor-pointer'
onClick={handleVoiceInputShow}
>
<Microphone01 className='block w-4 h-4 text-gray-500 group-hover:hidden' />
<Microphone01Solid className='hidden w-4 h-4 text-primary-600 group-hover:block' />
</div>
)
: null
}
<div className='mx-2 w-[1px] h-4 bg-black opacity-5' />
{isMobile
? sendBtn
: (
<TooltipPlus
popupContent={
<div>
<div>{t('common.operation.send')} Enter</div>
<div>{t('common.operation.lineBreak')} Shift Enter</div>
</div>
}
>
{sendBtn}
</TooltipPlus>
)}
</div>
{
voiceInputShow && (
<VoiceInput
onCancel={() => setVoiceInputShow(false)}
onConverted={text => onQueryChange(text)}
/>
)
: null
}
<div className='mx-2 w-[1px] h-4 bg-black opacity-5' />
{isMobile
? sendBtn
: (
<TooltipPlus
popupContent={
<div>
<div>{t('common.operation.send')} Enter</div>
<div>{t('common.operation.lineBreak')} Shift Enter</div>
</div>
}
>
{sendBtn}
</TooltipPlus>
)}
</div>
{voiceInputShow && (
<VoiceInput
onCancel={() => setVoiceInputShow(false)}
onConverted={text => onQueryChange(text)}
/>
)}
</div>
)
}
</div>
)}
</div>
)
}

View File

@ -1,10 +1,8 @@
import type { Dispatch, FC, ReactNode, RefObject, SetStateAction } from 'react'
import { useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { uniqueId } from 'lodash-es'
import { File02 } from '@/app/components/base/icons/src/vender/line/files'
import PromptLogModal from '@/app/components/base/prompt-log-modal'
import Tooltip from '@/app/components/base/tooltip'
export type LogData = {
role: string
@ -14,12 +12,14 @@ export type LogData = {
type LogProps = {
containerRef: RefObject<HTMLElement>
log: LogData[]
runID?: string
children?: (v: Dispatch<SetStateAction<boolean>>) => ReactNode
}
const Log: FC<LogProps> = ({
containerRef,
children,
log,
runID,
}) => {
const { t } = useTranslation()
const [showModal, setShowModal] = useState(false)
@ -40,23 +40,16 @@ const Log: FC<LogProps> = ({
children
? children(setShowModal)
: (
<Tooltip selector={`prompt-log-modal-trigger-${uniqueId()}`} content={t('common.operation.log') || ''}>
<div className={`
hidden absolute -left-[14px] -top-[14px] group-hover:block w-7 h-7
p-0.5 rounded-lg border-[0.5px] border-gray-100 bg-white shadow-md cursor-pointer
`}>
<div
className='flex items-center justify-center rounded-md w-full h-full hover:bg-gray-100'
onClick={(e) => {
e.stopPropagation()
setShowModal(true)
}
}
>
<File02 className='w-4 h-4 text-gray-500' />
</div>
</div>
</Tooltip>
<div
className='p-1 flex items-center justify-center rounded-[6px] hover:bg-gray-50 cursor-pointer'
onClick={(e) => {
e.stopPropagation()
setShowModal(true)
}}
>
<File02 className='mr-1 w-4 h-4 text-gray-500' />
<div className='text-xs leading-4 text-gray-500'>{runID ? t('appLog.viewLog') : t('appLog.promptLog')}</div>
</div>
)
}
{

View File

@ -4,7 +4,6 @@ import React, { useRef } from 'react'
import { useContext } from 'use-context-selector'
import s from '../style.module.css'
import type { IChatItem } from '../type'
import Log from '../log'
import MoreInfo from '../more-info'
import AppContext from '@/context/app-context'
import { Markdown } from '@/app/components/base/markdown'
@ -16,7 +15,7 @@ type IQuestionProps = Pick<IChatItem, 'id' | 'content' | 'more' | 'useCurrentUse
isResponding?: boolean
}
const Question: FC<IQuestionProps> = ({ id, content, more, useCurrentUserAvatar, isShowPromptLog, item, isResponding }) => {
const Question: FC<IQuestionProps> = ({ id, content, more, useCurrentUserAvatar, isShowPromptLog, item }) => {
const { userProfile } = useContext(AppContext)
const userName = userProfile?.name
const ref = useRef(null)
@ -27,11 +26,6 @@ const Question: FC<IQuestionProps> = ({ id, content, more, useCurrentUserAvatar,
<div className={s.questionWrapWrap}>
<div className={`${s.question} group relative text-sm text-gray-900`}>
{
isShowPromptLog && !isResponding && (
<Log log={item.log!} containerRef={ref} />
)
}
<div
className={'mr-2 py-3 px-4 bg-blue-500 rounded-tl-2xl rounded-b-2xl'}
>

View File

@ -79,9 +79,10 @@ export type IChatItem = {
useCurrentUserAvatar?: boolean
isOpeningStatement?: boolean
suggestedQuestions?: string[]
log?: { role: string; text: string }[]
log?: { role: string; text: string; files?: VisionFile[] }[]
agent_thoughts?: ThoughtItem[]
message_files?: VisionFile[]
workflow_run_id?: string
}
export type MessageEnd = {

View File

@ -80,7 +80,6 @@ const getFormattedChatList = (messages: ChatMessage[]) => {
id: `question-${item.id}`,
content: item.inputs.query || item.inputs.default_input || item.query, // text generation: item.inputs.query; chat: item.query
isAnswer: false,
log: item.message as any,
message_files: item.message_files?.filter((file: any) => file.belongs_to === 'user') || [],
})
newChatList.push({
@ -92,6 +91,15 @@ const getFormattedChatList = (messages: ChatMessage[]) => {
feedbackDisabled: false,
isAnswer: true,
message_files: item.message_files?.filter((file: any) => file.belongs_to === 'assistant') || [],
log: [
...item.message,
{
role: 'assistant',
text: item.answer,
files: item.message_files?.filter((file: any) => file.belongs_to === 'assistant') || [],
},
],
workflow_run_id: item.workflow_run_id,
more: {
time: dayjs.unix(item.created_at).format('hh:mm A'),
tokens: item.answer_tokens + item.message_tokens,
@ -175,7 +183,7 @@ function DetailPanel<T extends ChatConversationFullDetailResponse | CompletionCo
}
useEffect(() => {
if (appDetail?.id && detail.id && appDetail?.mode === 'completion')
if (appDetail?.id && detail.id && appDetail?.mode !== 'completion')
fetchData()
}, [appDetail?.id, detail.id, appDetail?.mode])

View File

@ -115,10 +115,9 @@ const AudioBtn = ({
className='z-10'
>
<div
className={`box-border p-0.5 flex items-center justify-center cursor-pointer ${isAudition || 'rounded-md bg-white'}`}
style={{ boxShadow: !isAudition ? '0px 4px 8px -2px rgba(16, 24, 40, 0.1), 0px 2px 4px -2px rgba(16, 24, 40, 0.06)' : '' }}
className={`box-border p-0.5 flex items-center justify-center cursor-pointer ${isAudition || '!p-0 rounded-md bg-white'}`}
onClick={togglePlayPause}>
<div className={`w-6 h-6 rounded-md ${!isAudition ? 'hover:bg-gray-200' : 'hover:bg-gray-50'} ${(isPlaying && !hasEnded) ? s.pauseIcon : s.playIcon}`}></div>
<div className={`w-6 h-6 rounded-md ${!isAudition ? 'w-4 h-4 hover:bg-gray-50' : 'hover:bg-gray-50'} ${(isPlaying && !hasEnded) ? s.pauseIcon : s.playIcon}`}></div>
</div>
</Tooltip>
</div>

View File

@ -2,7 +2,7 @@ import type {
FC,
ReactNode,
} from 'react'
import { memo } from 'react'
import { memo, useRef } from 'react'
import { useTranslation } from 'react-i18next'
import type {
ChatConfig,
@ -27,6 +27,7 @@ type AnswerProps = {
answerIcon?: ReactNode
responding?: boolean
allToolIcons?: Record<string, string | Emoji>
showPromptLog?: boolean
}
const Answer: FC<AnswerProps> = ({
item,
@ -36,8 +37,10 @@ const Answer: FC<AnswerProps> = ({
answerIcon,
responding,
allToolIcons,
showPromptLog,
}) => {
const { t } = useTranslation()
const ref = useRef(null)
const {
content,
citation,
@ -48,7 +51,7 @@ const Answer: FC<AnswerProps> = ({
const hasAgentThoughts = !!agent_thoughts?.length
return (
<div className='flex mb-2 last:mb-0'>
<div className='flex mb-2 last:mb-0' ref={ref}>
<div className='shrink-0 relative w-10 h-10'>
{
answerIcon || (
@ -75,6 +78,8 @@ const Answer: FC<AnswerProps> = ({
item={item}
question={question}
index={index}
showPromptLog={showPromptLog}
containerRef={ref}
/>
)
}

View File

@ -1,4 +1,4 @@
import type { FC } from 'react'
import type { FC, RefObject } from 'react'
import {
memo,
useMemo,
@ -17,16 +17,21 @@ import {
ThumbsUp,
} from '@/app/components/base/icons/src/vender/line/alertsAndFeedback'
import TooltipPlus from '@/app/components/base/tooltip-plus'
import Log from '@/app/components/app/chat/log'
type OperationProps = {
item: ChatItem
question: string
index: number
showPromptLog?: boolean
containerRef: RefObject<HTMLElement>
}
const Operation: FC<OperationProps> = ({
item,
question,
index,
showPromptLog,
containerRef,
}) => {
const { t } = useTranslation()
const {
@ -65,22 +70,29 @@ const Operation: FC<OperationProps> = ({
return (
<div className='absolute top-[-14px] right-[-14px] flex justify-end gap-1'>
{
!isOpeningStatement && (
<CopyBtn
value={content}
className='hidden group-hover:block'
/>
)
}
{(!isOpeningStatement && config?.text_to_speech?.enabled) && (
<AudioBtn
{!isOpeningStatement && (
<CopyBtn
value={content}
voice={config?.text_to_speech?.voice}
className='hidden group-hover:block'
/>
)}
<div className='hidden group-hover:flex items-center h-[28px] p-0.5 rounded-lg bg-white border-[0.5px] border-gray-100 shadow-md'>
{showPromptLog && (
<Log runID={item.workflow_run_id} log={item.log!} containerRef={containerRef} />
)}
{(!isOpeningStatement && config?.text_to_speech?.enabled) && (
<>
<div className='mx-1 w-[1px] h-[14px] bg-gray-200'/>
<AudioBtn
value={content}
voice={config?.text_to_speech?.voice}
className='hidden group-hover:block'
/>
</>
)}
</div>
{(!isOpeningStatement && config?.supportAnnotation && config.annotation_reply?.enabled) && (
<AnnotationCtrlBtn
appId={config?.appId || ''}

View File

@ -318,10 +318,17 @@ export const useChat = (
const requestion = draft[index - 1]
draft[index - 1] = {
...requestion,
log: newResponseItem.message,
}
draft[index] = {
...draft[index],
log: [
...newResponseItem.message,
{
role: 'assistant',
text: newResponseItem.answer,
files: newResponseItem.message_files?.filter((file: any) => file.belongs_to === 'assistant') || [],
},
],
more: {
time: dayjs.unix(newResponseItem.created_at).format('hh:mm A'),
tokens: newResponseItem.answer_tokens + newResponseItem.message_tokens,

View File

@ -160,6 +160,7 @@ const Chat: FC<ChatProps> = ({
answerIcon={answerIcon}
responding={isLast && isResponding}
allToolIcons={allToolIcons}
showPromptLog={showPromptLog}
/>
)
}
@ -167,9 +168,7 @@ const Chat: FC<ChatProps> = ({
<Question
key={item.id}
item={item}
showPromptLog={showPromptLog}
questionIcon={questionIcon}
isResponding={isResponding}
/>
)
})

View File

@ -4,28 +4,21 @@ import type {
} from 'react'
import {
memo,
useRef,
} from 'react'
import type { ChatItem } from '../types'
import { QuestionTriangle } from '@/app/components/base/icons/src/vender/solid/general'
import { User } from '@/app/components/base/icons/src/public/avatar'
import Log from '@/app/components/app/chat/log'
import { Markdown } from '@/app/components/base/markdown'
import ImageGallery from '@/app/components/base/image-gallery'
type QuestionProps = {
item: ChatItem
showPromptLog?: boolean
questionIcon?: ReactNode
isResponding?: boolean
}
const Question: FC<QuestionProps> = ({
item,
showPromptLog,
isResponding,
questionIcon,
}) => {
const ref = useRef(null)
const {
content,
message_files,
@ -34,14 +27,9 @@ const Question: FC<QuestionProps> = ({
const imgSrcs = message_files?.length ? message_files.map(item => item.url) : []
return (
<div className='flex justify-end mb-2 last:mb-0 pl-10' ref={ref}>
<div className='flex justify-end mb-2 last:mb-0 pl-10'>
<div className='group relative mr-4'>
<QuestionTriangle className='absolute -right-2 top-0 w-2 h-3 text-[#D1E9FF]/50' />
{
showPromptLog && !isResponding && (
<Log log={item.log!} containerRef={ref} />
)
}
<div className='px-4 py-3 bg-[#D1E9FF]/50 rounded-b-2xl rounded-tl-2xl text-sm text-gray-900'>
{
!!imgSrcs.length && (

View File

@ -76,6 +76,8 @@ const translation = {
title: 'Conversation Log',
workflowTitle: 'Log Detail',
},
promptLog: 'Prompt Log',
viewLog: 'View Log',
}
export default translation

View File

@ -72,6 +72,8 @@ const translation = {
},
workflowTitle: '日志',
workflowSubtitle: '日志记录了应用的执行情况',
promptLog: 'Prompt 日志',
viewLog: '查看日志',
}
export default translation

View File

@ -77,8 +77,7 @@ export type MessageContent = {
conversation_id: string
query: string
inputs: Record<string, any>
// message: Record<string, any>
message: string
message: { role: string; text: string; files?: VisionFile[] }[]
message_tokens: number
answer_tokens: number
answer: string
@ -101,6 +100,8 @@ export type MessageContent = {
from_end_user_id?: string
}>
message_files: VisionFile[]
agent_thoughts: any[] // TODO
workflow_run_id: string
}
export type CompletionConversationGeneralDetail = {