mirror of https://github.com/langgenius/dify.git
modify prompt log
This commit is contained in:
parent
e3f1e143e5
commit
4835358f24
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
)
|
||||
}
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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'}
|
||||
>
|
||||
|
|
|
|||
|
|
@ -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 = {
|
||||
|
|
|
|||
|
|
@ -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])
|
||||
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 || ''}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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}
|
||||
/>
|
||||
)
|
||||
})
|
||||
|
|
|
|||
|
|
@ -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 && (
|
||||
|
|
|
|||
|
|
@ -76,6 +76,8 @@ const translation = {
|
|||
title: 'Conversation Log',
|
||||
workflowTitle: 'Log Detail',
|
||||
},
|
||||
promptLog: 'Prompt Log',
|
||||
viewLog: 'View Log',
|
||||
}
|
||||
|
||||
export default translation
|
||||
|
|
|
|||
|
|
@ -72,6 +72,8 @@ const translation = {
|
|||
},
|
||||
workflowTitle: '日志',
|
||||
workflowSubtitle: '日志记录了应用的执行情况',
|
||||
promptLog: 'Prompt 日志',
|
||||
viewLog: '查看日志',
|
||||
}
|
||||
|
||||
export default translation
|
||||
|
|
|
|||
|
|
@ -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 = {
|
||||
|
|
|
|||
Loading…
Reference in New Issue