Feat/attachments (#9526)

Co-authored-by: Joel <iamjoel007@gmail.com>
Co-authored-by: JzoNg <jzongcode@gmail.com>
This commit is contained in:
zxhlyh
2024-10-21 10:32:37 +08:00
committed by GitHub
parent 4fd2743efa
commit 7a1d6fe509
445 changed files with 11759 additions and 6922 deletions

View File

@@ -2,41 +2,32 @@ import type { FC } from 'react'
import { memo } from 'react'
import type {
ChatItem,
VisionFile,
} from '../../types'
import { Markdown } from '@/app/components/base/markdown'
import Thought from '@/app/components/base/chat/chat/thought'
import ImageGallery from '@/app/components/base/image-gallery'
import type { Emoji } from '@/app/components/tools/types'
import { FileList } from '@/app/components/base/file-uploader'
import { getProcessedFilesFromResponse } from '@/app/components/base/file-uploader/utils'
type AgentContentProps = {
item: ChatItem
responding?: boolean
allToolIcons?: Record<string, string | Emoji>
}
const AgentContent: FC<AgentContentProps> = ({
item,
responding,
allToolIcons,
}) => {
const {
annotation,
agent_thoughts,
} = item
const getImgs = (list?: VisionFile[]) => {
if (!list)
return []
return list.filter(file => file.type === 'image' && file.belongs_to === 'assistant')
}
if (annotation?.logAnnotation)
return <Markdown content={annotation?.logAnnotation.content || ''} />
return (
<div>
{agent_thoughts?.map((thought, index) => (
<div key={index}>
<div key={index} className='px-2 py-1'>
{thought.thought && (
<Markdown content={thought.thought} />
)}
@@ -45,14 +36,20 @@ const AgentContent: FC<AgentContentProps> = ({
{!!thought.tool && (
<Thought
thought={thought}
allToolIcons={allToolIcons || {}}
isFinished={!!thought.observation || !responding}
/>
)}
{getImgs(thought.message_files).length > 0 && (
<ImageGallery srcs={getImgs(thought.message_files).map(file => file.url)} />
)}
{
!!thought.message_files?.length && (
<FileList
files={getProcessedFilesFromResponse(thought.message_files.map((item: any) => ({ ...item, related_id: item.id })))}
showDeleteAction={false}
showDownloadAction={true}
canPreview={true}
/>
)
}
</div>
))}
</div>

View File

@@ -2,6 +2,7 @@ import type { FC } from 'react'
import { memo } from 'react'
import type { ChatItem } from '../../types'
import { Markdown } from '@/app/components/base/markdown'
import cn from '@/utils/classnames'
type BasicContentProps = {
item: ChatItem
@@ -15,9 +16,17 @@ const BasicContent: FC<BasicContentProps> = ({
} = item
if (annotation?.logAnnotation)
return <Markdown content={annotation?.logAnnotation.content || ''} />
return <Markdown content={annotation?.logAnnotation.content || ''} className='px-2 py-1' />
return <Markdown content={content} className={`${item.isError && '!text-[#F04438]'}`} />
return (
<Markdown
className={cn(
'px-2 py-1',
item.isError && '!text-[#F04438]',
)}
content={content}
/>
)
}
export default memo(BasicContent)

View File

@@ -18,10 +18,10 @@ import { AnswerTriangle } from '@/app/components/base/icons/src/vender/solid/gen
import LoadingAnim from '@/app/components/base/chat/chat/loading-anim'
import Citation from '@/app/components/base/chat/chat/citation'
import { EditTitle } from '@/app/components/app/annotation/edit-annotation-modal/edit-item'
import type { Emoji } from '@/app/components/tools/types'
import type { AppData } from '@/models/share'
import AnswerIcon from '@/app/components/base/answer-icon'
import cn from '@/utils/classnames'
import { FileList } from '@/app/components/base/file-uploader'
type AnswerProps = {
item: ChatItem
@@ -30,7 +30,6 @@ type AnswerProps = {
config?: ChatConfig
answerIcon?: ReactNode
responding?: boolean
allToolIcons?: Record<string, string | Emoji>
showPromptLog?: boolean
chatAnswerContainerInner?: string
hideProcessDetail?: boolean
@@ -44,7 +43,6 @@ const Answer: FC<AnswerProps> = ({
config,
answerIcon,
responding,
allToolIcons,
showPromptLog,
chatAnswerContainerInner,
hideProcessDetail,
@@ -59,6 +57,8 @@ const Answer: FC<AnswerProps> = ({
more,
annotation,
workflowProcess,
allFiles,
message_files,
} = item
const hasAgentThoughts = !!agent_thoughts?.length
@@ -135,7 +135,6 @@ const Answer: FC<AnswerProps> = ({
<WorkflowProcess
data={workflowProcess}
item={item}
hideInfo
hideProcessDetail={hideProcessDetail}
/>
)
@@ -146,7 +145,6 @@ const Answer: FC<AnswerProps> = ({
<WorkflowProcess
data={workflowProcess}
item={item}
hideInfo
hideProcessDetail={hideProcessDetail}
/>
)
@@ -168,7 +166,28 @@ const Answer: FC<AnswerProps> = ({
<AgentContent
item={item}
responding={responding}
allToolIcons={allToolIcons}
/>
)
}
{
!!allFiles?.length && (
<FileList
className='my-1'
files={allFiles}
showDeleteAction={false}
showDownloadAction
canPreview
/>
)
}
{
!!message_files?.length && (
<FileList
className='my-1'
files={message_files}
showDeleteAction={false}
showDownloadAction
canPreview
/>
)
}

View File

@@ -12,7 +12,7 @@ import cn from '@/utils/classnames'
import CopyBtn from '@/app/components/base/copy-btn'
import { MessageFast } from '@/app/components/base/icons/src/vender/solid/communication'
import AudioBtn from '@/app/components/base/audio-btn'
import AnnotationCtrlBtn from '@/app/components/app/configuration/toolbox/annotation/annotation-ctrl-btn'
import AnnotationCtrlBtn from '@/app/components/base/features/new-feature-panel/annotation-reply/annotation-ctrl-btn'
import EditReplyModal from '@/app/components/app/annotation/edit-annotation-modal'
import {
ThumbsDown,

View File

@@ -0,0 +1,71 @@
import { useState } from 'react'
import { useTranslation } from 'react-i18next'
import {
RiArrowDownSLine,
RiArrowRightSLine,
RiHammerFill,
RiLoader2Line,
} from '@remixicon/react'
import type { ToolInfoInThought } from '../type'
import cn from '@/utils/classnames'
type ToolDetailProps = {
payload: ToolInfoInThought
}
const ToolDetail = ({
payload,
}: ToolDetailProps) => {
const { t } = useTranslation()
const { name, label, input, isFinished, output } = payload
const toolLabel = name.startsWith('dataset_') ? t('dataset.knowledge') : label
const [expand, setExpand] = useState(false)
return (
<div
className={cn(
'rounded-xl',
!expand && 'border-l-[0.25px] border-components-panel-border bg-workflow-process-bg',
expand && 'border-[0.5px] border-components-panel-border-subtle bg-background-section-burn',
)}
>
<div
className={cn(
'flex items-center system-xs-medium text-text-tertiary px-2.5 py-2 cursor-pointer',
expand && 'pb-1.5',
)}
onClick={() => setExpand(!expand)}
>
{isFinished && <RiHammerFill className='mr-1 w-3.5 h-3.5' />}
{!isFinished && <RiLoader2Line className='mr-1 w-3.5 h-3.5 animate-spin' />}
{t(`tools.thought.${isFinished ? 'used' : 'using'}`)}
<div className='mx-1 text-text-secondary'>{toolLabel}</div>
{!expand && <RiArrowRightSLine className='w-4 h-4' />}
{expand && <RiArrowDownSLine className='ml-auto w-4 h-4' />}
</div>
{
expand && (
<>
<div className='mb-0.5 mx-1 rounded-[10px] bg-components-panel-on-panel-item-bg text-text-secondary'>
<div className='flex items-center justify-between px-2 pt-1 h-7 system-xs-semibold-uppercase'>
{t('tools.thought.requestTitle')}
</div>
<div className='pt-1 px-3 pb-2 code-xs-regular break-words'>
{input}
</div>
</div>
<div className='mx-1 mb-1 rounded-[10px] bg-components-panel-on-panel-item-bg text-text-secondary'>
<div className='flex items-center justify-between px-2 pt-1 h-7 system-xs-semibold-uppercase'>
{t('tools.thought.responseTitle')}
</div>
<div className='pt-1 px-3 pb-2 code-xs-regular break-words'>
{output}
</div>
</div>
</>
)
}
</div>
)
}
export default ToolDetail

View File

@@ -20,7 +20,6 @@ import { useStore as useAppStore } from '@/app/components/app/store'
type WorkflowProcessProps = {
data: WorkflowProcess
item?: ChatItem
grayBg?: boolean
expand?: boolean
hideInfo?: boolean
hideProcessDetail?: boolean
@@ -28,7 +27,6 @@ type WorkflowProcessProps = {
const WorkflowProcessItem = ({
data,
item,
grayBg,
expand = false,
hideInfo = false,
hideProcessDetail = false,
@@ -40,6 +38,8 @@ const WorkflowProcessItem = ({
const failed = data.status === WorkflowRunningStatus.Failed || data.status === WorkflowRunningStatus.Stopped
const background = useMemo(() => {
if (collapse)
return 'linear-gradient(90deg, rgba(200, 206, 218, 0.20) 0%, rgba(200, 206, 218, 0.04) 100%)'
if (running && !collapse)
return 'linear-gradient(180deg, #E1E4EA 0%, #EAECF0 100%)'
@@ -67,41 +67,36 @@ const WorkflowProcessItem = ({
return (
<div
className={cn(
'mb-2 rounded-xl border-[0.5px] border-black/8',
collapse ? 'py-[7px]' : hideInfo ? 'pt-2 pb-1' : 'py-2',
collapse && (!grayBg ? 'bg-white' : 'bg-gray-50'),
hideInfo ? 'mx-[-8px] px-1' : 'w-full px-3',
'-mx-1 px-2.5 rounded-xl border-[0.5px]',
collapse ? 'py-[7px] border-components-panel-border' : 'pt-[7px] px-1 pb-1 border-components-panel-border-subtle',
)}
style={{
background,
}}
>
<div
className={cn(
'flex items-center h-[18px] cursor-pointer',
hideInfo && 'px-[6px]',
)}
className={cn('flex items-center cursor-pointer', !collapse && 'px-1.5')}
onClick={() => setCollapse(!collapse)}
>
{
running && (
<RiLoader2Line className='shrink-0 mr-1 w-3 h-3 text-[#667085] animate-spin' />
<RiLoader2Line className='shrink-0 mr-1 w-3.5 h-3.5 text-text-tertiary' />
)
}
{
succeeded && (
<CheckCircle className='shrink-0 mr-1 w-3 h-3 text-[#12B76A]' />
<CheckCircle className='shrink-0 mr-1 w-3.5 h-3.5 text-text-success' />
)
}
{
failed && (
<RiErrorWarningFill className='shrink-0 mr-1 w-3 h-3 text-[#F04438]' />
<RiErrorWarningFill className='shrink-0 mr-1 w-3.5 h-3.5 text-text-destructive' />
)
}
<div className='grow text-xs font-medium text-gray-700'>
<div className={cn('system-xs-medium text-text-secondary', !collapse && 'grow')}>
{t('workflow.common.workflowProcess')}
</div>
<RiArrowRightSLine className={`'ml-1 w-3 h-3 text-gray-500' ${collapse ? '' : 'rotate-90'}`} />
<RiArrowRightSLine className={`'ml-1 w-4 h-4 text-text-tertiary' ${collapse ? '' : 'rotate-90'}`} />
</div>
{
!collapse && (