feat: multimodal support (image) (#27793)

Co-authored-by: zxhlyh <jasonapring2015@outlook.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
Wu Tianwei
2025-12-09 11:44:50 +08:00
committed by GitHub
parent a44b800c85
commit 14d1b3f9b3
77 changed files with 2932 additions and 579 deletions

View File

@@ -21,6 +21,8 @@ import Divider from '@/app/components/base/divider'
import { useAddSegment } from '@/service/knowledge/use-segment'
import { useDatasetDetailContextWithSelector } from '@/context/dataset-detail'
import { IndexingType } from '../../create/step-two'
import type { FileEntity } from '@/app/components/datasets/common/image-uploader/types'
import ImageUploaderInChunk from '@/app/components/datasets/common/image-uploader/image-uploader-in-chunk'
type NewSegmentModalProps = {
onCancel: () => void
@@ -39,6 +41,7 @@ const NewSegmentModal: FC<NewSegmentModalProps> = ({
const { notify } = useContext(ToastContext)
const [question, setQuestion] = useState('')
const [answer, setAnswer] = useState('')
const [attachments, setAttachments] = useState<FileEntity[]>([])
const { datasetId, documentId } = useParams<{ datasetId: string; documentId: string }>()
const [keywords, setKeywords] = useState<string[]>([])
const [loading, setLoading] = useState(false)
@@ -49,6 +52,7 @@ const NewSegmentModal: FC<NewSegmentModalProps> = ({
const { appSidebarExpand } = useAppStore(useShallow(state => ({
appSidebarExpand: state.appSidebarExpand,
})))
const [imageUploaderKey, setImageUploaderKey] = useState(Date.now())
const refreshTimer = useRef<any>(null)
const CustomButton = useMemo(() => (
@@ -71,10 +75,14 @@ const NewSegmentModal: FC<NewSegmentModalProps> = ({
onCancel()
}, [onCancel, addAnother])
const onAttachmentsChange = useCallback((attachments: FileEntity[]) => {
setAttachments(attachments)
}, [])
const { mutateAsync: addSegment } = useAddSegment()
const handleSave = useCallback(async () => {
const params: SegmentUpdater = { content: '' }
const params: SegmentUpdater = { content: '', attachment_ids: [] }
if (docForm === ChunkingMode.qa) {
if (!question.trim()) {
return notify({
@@ -106,6 +114,9 @@ const NewSegmentModal: FC<NewSegmentModalProps> = ({
if (keywords?.length)
params.keywords = keywords
if (attachments.length)
params.attachment_ids = attachments.filter(item => Boolean(item.uploadedId)).map(item => item.uploadedId!)
setLoading(true)
await addSegment({ datasetId, documentId, body: params }, {
onSuccess() {
@@ -119,6 +130,8 @@ const NewSegmentModal: FC<NewSegmentModalProps> = ({
handleCancel('add')
setQuestion('')
setAnswer('')
setAttachments([])
setImageUploaderKey(Date.now())
setKeywords([])
refreshTimer.current = setTimeout(() => {
onSave()
@@ -128,7 +141,7 @@ const NewSegmentModal: FC<NewSegmentModalProps> = ({
setLoading(false)
},
})
}, [docForm, keywords, addSegment, datasetId, documentId, question, answer, notify, t, appSidebarExpand, CustomButton, handleCancel, onSave])
}, [docForm, keywords, addSegment, datasetId, documentId, question, answer, attachments, notify, t, appSidebarExpand, CustomButton, handleCancel, onSave])
const wordCountText = useMemo(() => {
const count = docForm === ChunkingMode.qa ? (question.length + answer.length) : question.length
@@ -187,13 +200,22 @@ const NewSegmentModal: FC<NewSegmentModalProps> = ({
isEditMode={true}
/>
</div>
{isECOIndexing && <Keywords
className={fullScreen ? 'w-1/5' : ''}
actionType='add'
keywords={keywords}
isEditMode={true}
onKeywordsChange={keywords => setKeywords(keywords)}
/>}
<div className={classNames('flex flex-col', fullScreen ? 'w-[320px] gap-y-2' : 'w-full gap-y-1')}>
<ImageUploaderInChunk
key={imageUploaderKey}
value={attachments}
onChange={onAttachmentsChange}
/>
{isECOIndexing && (
<Keywords
className={fullScreen ? 'w-1/5' : ''}
actionType='add'
keywords={keywords}
isEditMode={true}
onKeywordsChange={keywords => setKeywords(keywords)}
/>
)}
</div>
</div>
{!fullScreen && (
<div className='flex items-center justify-between border-t-[1px] border-t-divider-subtle p-4 pt-3'>