publish as customized pipeline

This commit is contained in:
zxhlyh 2025-06-11 10:40:32 +08:00
parent eff8108f1c
commit 0564651f6f
9 changed files with 238 additions and 10 deletions

View File

@ -3,7 +3,7 @@ import { usePipelineTemplateList } from '@/service/use-pipeline'
const CustomizedList = () => {
const { data: pipelineList, isLoading } = usePipelineTemplateList({ type: 'customized' })
const list = pipelineList?.pipelines
const list = pipelineList?.pipeline_templates
if (isLoading || !list)
return null

View File

@ -0,0 +1,150 @@
'use client'
import { useCallback, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { RiCloseLine } from '@remixicon/react'
import AppIconPicker from '@/app/components/base/app-icon-picker'
import type { AppIconSelection } from '@/app/components/base/app-icon-picker'
import Modal from '@/app/components/base/modal'
import Button from '@/app/components/base/button'
import Input from '@/app/components/base/input'
import Textarea from '@/app/components/base/textarea'
import AppIcon from '@/app/components/base/app-icon'
import { noop } from 'lodash-es'
import { useStore } from '@/app/components/workflow/store'
import type { IconInfo } from '@/models/datasets'
type PublishAsKnowledgePipelineModalProps = {
confirmDisabled?: boolean
onCancel: () => void
onConfirm: (
name: string,
icon: IconInfo,
description?: string,
) => Promise<void>
}
const PublishAsKnowledgePipelineModal = ({
confirmDisabled,
onCancel,
onConfirm,
}: PublishAsKnowledgePipelineModalProps) => {
const { t } = useTranslation()
const knowledgeName = useStore(s => s.knowledgeName)
const knowledgeIcon = useStore(s => s.knowledgeIcon)
const [pipelineName, setPipelineName] = useState(knowledgeName!)
const [pipelineIcon, setPipelineIcon] = useState(knowledgeIcon!)
const [description, setDescription] = useState('')
const [showAppIconPicker, setShowAppIconPicker] = useState(false)
const handleSelectIcon = useCallback((item: AppIconSelection) => {
if (item.type === 'image') {
setPipelineIcon({
icon_type: 'image',
icon_url: item.url,
icon_background: '',
icon: '',
})
}
if (item.type === 'emoji') {
setPipelineIcon({
icon_type: 'emoji',
icon: item.icon,
icon_background: item.background,
icon_url: '',
})
}
setShowAppIconPicker(false)
}, [])
const handleCloseIconPicker = useCallback(() => {
setPipelineIcon({
icon_type: pipelineIcon.icon_type,
icon: pipelineIcon.icon,
icon_background: pipelineIcon.icon_background,
icon_url: pipelineIcon.icon_url,
})
setShowAppIconPicker(false)
}, [pipelineIcon])
const handleConfirm = () => {
if (confirmDisabled)
return
onConfirm(
pipelineName?.trim() || '',
pipelineIcon,
description?.trim(),
)
}
return (
<>
<Modal
isShow
onClose={noop}
className='relative !w-[520px] !p-0'
>
<div className='title-2xl-semi-bold relative flex items-center p-6 pb-3 pr-14 text-text-primary'>
{t('pipeline.common.publishAs')}
<div className='absolute right-5 top-5 flex h-8 w-8 cursor-pointer items-center justify-center' onClick={onCancel}>
<RiCloseLine className='h-4 w-4 text-text-tertiary' />
</div>
</div>
<div className='px-6 py-3'>
<div className='mb-5 flex'>
<div className='mr-3 grow'>
<div className='system-sm-medium mb-1 flex h-6 items-center text-text-secondary'>
{t('pipeline.common.publishAsPipeline.name')}
</div>
<Input
value={pipelineName}
onChange={e => setPipelineName(e.target.value)}
placeholder={t('pipeline.common.publishAsPipeline.namePlaceholder') || ''}
/>
</div>
<AppIcon
size='xxl'
onClick={() => { setShowAppIconPicker(true) }}
className='mt-2 shrink-0 cursor-pointer'
iconType={pipelineIcon?.icon_type}
icon={pipelineIcon?.icon}
background={pipelineIcon?.icon_background}
imageUrl={pipelineIcon?.icon_url}
/>
</div>
<div>
<div className='system-sm-medium mb-1 flex h-6 items-center text-text-secondary '>
{t('pipeline.common.publishAsPipeline.description')}
</div>
<Textarea
className='resize-none'
placeholder={t('pipeline.common.publishAsPipeline.descriptionPlaceholder') || ''}
value={description}
onChange={e => setDescription(e.target.value)}
/>
</div>
</div>
<div className='flex items-center justify-end px-6 py-5'>
<Button
className='mr-2'
onClick={onCancel}
>
{t('common.operation.cancel')}
</Button>
<Button
disabled={!pipelineName?.trim() || confirmDisabled}
variant='primary'
onClick={() => handleConfirm()}
>
{t('workflow.common.publish')}
</Button>
</div>
</Modal>
{showAppIconPicker && <AppIconPicker
onSelect={handleSelectIcon}
onClose={handleCloseIconPicker}
/>}
</>
)
}
export default PublishAsKnowledgePipelineModal

View File

@ -36,6 +36,8 @@ import {
usePublishAsCustomizedPipeline,
} from '@/service/use-pipeline'
import Confirm from '@/app/components/base/confirm'
import PublishAsKnowledgePipelineModal from '../../publish-as-knowledge-pipeline-modal'
import type { IconInfo } from '@/models/datasets'
const PUBLISH_SHORTCUT = ['⌘', '⇧', 'P']
@ -62,9 +64,16 @@ const Popup = () => {
setTrue: showPublishing,
}] = useBoolean(false)
const {
mutate: publishAsCustomizedPipeline,
isPending: isPublishingAsCustomizedPipeline,
mutateAsync: publishAsCustomizedPipeline,
} = usePublishAsCustomizedPipeline()
const [showPublishAsKnowledgePipelineModal, {
setFalse: hidePublishAsKnowledgePipelineModal,
setTrue: setShowPublishAsKnowledgePipelineModal,
}] = useBoolean(false)
const [isPublishingAsCustomizedPipeline, {
setFalse: hidePublishingAsCustomizedPipeline,
setTrue: showPublishingAsCustomizedPipeline,
}] = useBoolean(false)
const invalidPublishedPipelineInfo = useInvalid([...publishedPipelineInfoQueryKeyPrefix, pipelineId])
@ -117,6 +126,35 @@ const Popup = () => {
push(`/datasets/${datasetId}/documents/create-from-pipeline`)
}, [datasetId, push])
const handlePublishAsKnowledgePipeline = useCallback(async (
name: string,
icon: IconInfo,
description?: string,
) => {
try {
showPublishingAsCustomizedPipeline()
await publishAsCustomizedPipeline({
pipelineId: pipelineId || '',
name,
icon_info: icon,
description,
})
notify({ type: 'success', message: t('common.api.actionSuccess') })
}
finally {
hidePublishingAsCustomizedPipeline()
hidePublishAsKnowledgePipelineModal()
}
}, [
pipelineId,
publishAsCustomizedPipeline,
showPublishingAsCustomizedPipeline,
hidePublishingAsCustomizedPipeline,
hidePublishAsKnowledgePipelineModal,
notify,
t,
])
return (
<div className='w-[320px] rounded-2xl border-[0.5px] border-components-panel-border bg-components-panel-bg shadow-xl shadow-shadow-shadow-5'>
<div className='p-4 pt-3'>
@ -192,7 +230,7 @@ const Popup = () => {
<Button
className='w-full hover:bg-state-accent-hover hover:text-text-accent'
variant='tertiary'
onClick={() => publishAsCustomizedPipeline({ pipelineId: pipelineId || '' })}
onClick={setShowPublishAsKnowledgePipelineModal}
disabled={!publishedAt || isPublishingAsCustomizedPipeline}
>
<div className='flex grow items-center'>
@ -213,6 +251,15 @@ const Popup = () => {
/>
)
}
{
showPublishAsKnowledgePipelineModal && (
<PublishAsKnowledgePipelineModal
confirmDisabled={isPublishingAsCustomizedPipeline}
onConfirm={handlePublishAsKnowledgePipeline}
onCancel={hidePublishAsKnowledgePipelineModal}
/>
)
}
</div>
)
}

View File

@ -25,10 +25,11 @@ export const usePipelineInit = () => {
const [isLoading, setIsLoading] = useState(true)
const datasetId = useDatasetDetailContextWithSelector(s => s.dataset)?.pipeline_id
const knowledgeName = useDatasetDetailContextWithSelector(s => s.dataset)?.name
const knowledgeIcon = useDatasetDetailContextWithSelector(s => s.dataset)?.icon_info
useEffect(() => {
workflowStore.setState({ pipelineId: datasetId, knowledgeName })
}, [datasetId, workflowStore, knowledgeName])
workflowStore.setState({ pipelineId: datasetId, knowledgeName, knowledgeIcon })
}, [datasetId, workflowStore, knowledgeName, knowledgeIcon])
usePipelineConfig()

View File

@ -5,10 +5,12 @@ import type {
} from '@/app/components/workflow/types'
import type { DataSourceItem } from '@/app/components/workflow/block-selector/types'
import { transformDataSourceToTool } from '@/app/components/workflow/block-selector/utils'
import type { IconInfo } from '@/models/datasets'
export type RagPipelineSliceShape = {
pipelineId: string
knowledgeName: string
knowledgeIcon?: IconInfo
showInputFieldDialog: boolean
setShowInputFieldDialog: (showInputFieldPanel: boolean) => void
nodesDefaultConfigs: Record<string, any>

View File

@ -4,6 +4,12 @@ const translation = {
publishAs: 'Publish as a Knowledge Pipeline',
confirmPublish: 'Confirm Publish',
confirmPublishContent: 'After successfully publishing the knowledge pipeline, the chunk structure of this knowledge base cannot be modified. Are you sure you want to publish it?',
publishAsPipeline: {
name: 'Pipeline name & icon',
namePlaceholder: 'Please enter the name of this Knowledge Pipeline. (Required) ',
description: 'Knowledge description',
descriptionPlaceholder: 'Please enter the description of this Knowledge Pipeline. (Optional) ',
},
},
}

View File

@ -4,6 +4,12 @@ const translation = {
publishAs: '发布为知识管道',
confirmPublish: '确认发布',
confirmPublishContent: '成功发布知识管道后,此知识库的分段结构将无法修改。您确定要发布吗?',
publishAsPipeline: {
name: 'Pipeline 名称和图标',
namePlaceholder: '请输入此 Pipeline 的名称。 (必填)',
description: 'Pipeline 描述',
descriptionPlaceholder: '请输入此 Pipeline 的描述。 (可选)',
},
},
}

View File

@ -26,7 +26,7 @@ export type PipelineTemplate = {
}
export type PipelineTemplateListResponse = {
pipelines: PipelineTemplate[]
pipeline_templates: PipelineTemplate[]
}
export type PipelineTemplateByIdResponse = {

View File

@ -28,12 +28,14 @@ import type {
} from '@/models/pipeline'
import type { DataSourceItem } from '@/app/components/workflow/block-selector/types'
import type { ToolCredential } from '@/app/components/tools/types'
import type { IconInfo } from '@/models/datasets'
const NAME_SPACE = 'pipeline'
export const usePipelineTemplateList = (params: PipelineTemplateListParams) => {
console.log('params', params)
return useQuery<PipelineTemplateListResponse>({
queryKey: [NAME_SPACE, 'template', 'list'],
queryKey: [NAME_SPACE, 'template', 'list', params.type],
queryFn: () => {
return get<PipelineTemplateListResponse>('/rag/pipeline/templates', { params })
},
@ -348,8 +350,22 @@ export const usePublishAsCustomizedPipeline = () => {
mutationKey: [NAME_SPACE, 'publish-as-customized-pipeline'],
mutationFn: ({
pipelineId,
}: { pipelineId: string }) => {
return post(`/rag/pipelines/${pipelineId}/customized/publish`)
name,
icon_info,
description,
}: {
pipelineId: string,
name: string,
icon_info: IconInfo,
description?: string,
}) => {
return post(`/rag/pipelines/${pipelineId}/customized/publish`, {
body: {
name,
icon_info,
description,
},
})
},
})
}