diff --git a/web/app/components/base/form/components/field/mixed-variable-text-input/index.tsx b/web/app/components/base/form/components/field/mixed-variable-text-input/index.tsx
new file mode 100644
index 0000000000..4bb562ba3a
--- /dev/null
+++ b/web/app/components/base/form/components/field/mixed-variable-text-input/index.tsx
@@ -0,0 +1,39 @@
+import {
+ memo,
+} from 'react'
+import PromptEditor from '@/app/components/base/prompt-editor'
+import cn from '@/utils/classnames'
+import Placeholder from './placeholder'
+
+type MixedVariableTextInputProps = {
+ editable?: boolean
+ value?: string
+ onChange?: (text: string) => void
+}
+const MixedVariableTextInput = ({
+ editable = true,
+ value = '',
+ onChange,
+}: MixedVariableTextInputProps) => {
+ return (
+ }
+ onChange={onChange}
+ />
+ )
+}
+
+export default memo(MixedVariableTextInput)
diff --git a/web/app/components/base/form/components/field/mixed-variable-text-input/placeholder.tsx b/web/app/components/base/form/components/field/mixed-variable-text-input/placeholder.tsx
new file mode 100644
index 0000000000..e84ffbeb28
--- /dev/null
+++ b/web/app/components/base/form/components/field/mixed-variable-text-input/placeholder.tsx
@@ -0,0 +1,49 @@
+import { useCallback } from 'react'
+import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext'
+import { FOCUS_COMMAND } from 'lexical'
+import { $insertNodes } from 'lexical'
+import { CustomTextNode } from '@/app/components/base/prompt-editor/plugins/custom-text/node'
+import Badge from '@/app/components/base/badge'
+
+const Placeholder = () => {
+ const [editor] = useLexicalComposerContext()
+
+ const handleInsert = useCallback((text: string) => {
+ editor.update(() => {
+ const textNode = new CustomTextNode(text)
+ $insertNodes([textNode])
+ })
+ editor.dispatchCommand(FOCUS_COMMAND, undefined as any)
+ }, [editor])
+
+ return (
+
{
+ e.stopPropagation()
+ handleInsert('')
+ }}
+ >
+
+ Type or press
+
/
+
{
+ e.stopPropagation()
+ handleInsert('/')
+ })}
+ >
+ insert variable
+
+
+
+
+ )
+}
+
+export default Placeholder
diff --git a/web/app/components/base/prompt-editor/index.tsx b/web/app/components/base/prompt-editor/index.tsx
index 94a65e4b62..a87a51cd50 100644
--- a/web/app/components/base/prompt-editor/index.tsx
+++ b/web/app/components/base/prompt-editor/index.tsx
@@ -64,8 +64,9 @@ import cn from '@/utils/classnames'
export type PromptEditorProps = {
instanceId?: string
compact?: boolean
+ wrapperClassName?: string
className?: string
- placeholder?: string
+ placeholder?: string | JSX.Element
placeholderClassName?: string
style?: React.CSSProperties
value?: string
@@ -85,6 +86,7 @@ export type PromptEditorProps = {
const PromptEditor: FC = ({
instanceId,
compact,
+ wrapperClassName,
className,
placeholder,
placeholderClassName,
@@ -147,10 +149,25 @@ const PromptEditor: FC = ({
return (
-
+
}
- placeholder={
}
+ contentEditable={
+
+ }
+ placeholder={
+
+ }
ErrorBoundary={LexicalErrorBoundary}
/>
{
const { t } = useTranslation()
return (
{value || t('common.promptEditor.placeholder')}
diff --git a/web/app/components/rag-pipeline/hooks/use-pipeline-config.ts b/web/app/components/rag-pipeline/hooks/use-pipeline-config.ts
new file mode 100644
index 0000000000..f16cd03938
--- /dev/null
+++ b/web/app/components/rag-pipeline/hooks/use-pipeline-config.ts
@@ -0,0 +1,53 @@
+import { useCallback } from 'react'
+import {
+ useStore,
+ useWorkflowStore,
+} from '@/app/components/workflow/store'
+import { useWorkflowConfig } from '@/service/use-workflow'
+import type { ToolWithProvider } from '@/app/components/workflow/types'
+import type { FetchWorkflowDraftResponse } from '@/types/workflow'
+
+export const usePipelineConfig = () => {
+ const pipelineId = useStore(s => s.pipelineId)
+ const workflowStore = useWorkflowStore()
+
+ const handleUpdateWorkflowConfig = useCallback((config: Record) => {
+ const { setWorkflowConfig } = workflowStore.getState()
+
+ setWorkflowConfig(config)
+ }, [workflowStore])
+ useWorkflowConfig(
+ pipelineId ? `/rag/pipeline/${pipelineId}/workflows/draft/config` : '',
+ handleUpdateWorkflowConfig,
+ )
+
+ const handleUpdateDataSourceList = useCallback((dataSourceList: ToolWithProvider[]) => {
+ const { setDataSourceList } = workflowStore.getState()
+
+ setDataSourceList!(dataSourceList)
+ }, [workflowStore])
+ useWorkflowConfig(
+ '/rag/pipelines/datasource-plugins',
+ handleUpdateDataSourceList,
+ )
+
+ const handleUpdateNodesDefaultConfigs = useCallback((nodesDefaultConfigs: Record) => {
+ const { setNodesDefaultConfigs } = workflowStore.getState()
+
+ setNodesDefaultConfigs!(nodesDefaultConfigs)
+ }, [workflowStore])
+ useWorkflowConfig(
+ pipelineId ? `/rag/pipeline/${pipelineId}/workflows/default-workflow-block-configs` : '',
+ handleUpdateNodesDefaultConfigs,
+ )
+
+ const handleUpdatePublishedAt = useCallback((publishedWorkflow: FetchWorkflowDraftResponse) => {
+ const { setPublishedAt } = workflowStore.getState()
+
+ setPublishedAt(publishedWorkflow?.created_at)
+ }, [workflowStore])
+ useWorkflowConfig(
+ pipelineId ? `/rag/pipeline/${pipelineId}/workflows/publish` : '',
+ handleUpdatePublishedAt,
+ )
+}
diff --git a/web/app/components/rag-pipeline/hooks/use-pipeline-init.ts b/web/app/components/rag-pipeline/hooks/use-pipeline-init.ts
index 7d7bdcb531..ab1ee1ea59 100644
--- a/web/app/components/rag-pipeline/hooks/use-pipeline-init.ts
+++ b/web/app/components/rag-pipeline/hooks/use-pipeline-init.ts
@@ -5,18 +5,15 @@ import {
} from 'react'
import { useParams } from 'next/navigation'
import {
- useStore,
useWorkflowStore,
} from '@/app/components/workflow/store'
import { usePipelineTemplate } from './use-pipeline-template'
import {
- fetchNodesDefaultConfigs,
- fetchPublishedWorkflow,
fetchWorkflowDraft,
syncWorkflowDraft,
} from '@/service/workflow'
import type { FetchWorkflowDraftResponse } from '@/types/workflow'
-// import { useWorkflowConfig } from '@/service/use-workflow'
+import { usePipelineConfig } from './use-pipeline-config'
export const usePipelineInit = () => {
const { datasetId } = useParams()
@@ -25,31 +22,33 @@ export const usePipelineInit = () => {
nodes: nodesTemplate,
edges: edgesTemplate,
} = usePipelineTemplate()
- const setSyncWorkflowDraftHash = useStore(s => s.setSyncWorkflowDraftHash)
const [data, setData] = useState()
const [isLoading, setIsLoading] = useState(true)
+
useEffect(() => {
- // workflowStore.setState({ pipelineId: datasetId as string })
+ workflowStore.setState({ pipelineId: datasetId as string })
}, [datasetId, workflowStore])
- const handleUpdateWorkflowConfig = useCallback((config: Record) => {
- const { setWorkflowConfig } = workflowStore.getState()
-
- setWorkflowConfig(config)
- }, [workflowStore])
- // useWorkflowConfig(`/rag/pipeline/${datasetId}/workflows/draft/config`, handleUpdateWorkflowConfig)
+ usePipelineConfig()
const handleGetInitialWorkflowData = useCallback(async () => {
+ const {
+ setEnvSecrets,
+ setEnvironmentVariables,
+ setSyncWorkflowDraftHash,
+ setDraftUpdatedAt,
+ setToolPublished,
+ } = workflowStore.getState()
try {
const res = await fetchWorkflowDraft(`/rag/pipeline/${datasetId}/workflows/draft`)
setData(res)
- workflowStore.setState({
- envSecrets: (res.environment_variables || []).filter(env => env.value_type === 'secret').reduce((acc, env) => {
+ setDraftUpdatedAt(res.updated_at)
+ setToolPublished(res.tool_published)
+ setEnvSecrets((res.environment_variables || []).filter(env => env.value_type === 'secret').reduce((acc, env) => {
acc[env.id] = env.value
return acc
- }, {} as Record),
- environmentVariables: res.environment_variables?.map(env => env.value_type === 'secret' ? { ...env, value: '[__HIDDEN__]' } : env) || [],
- })
+ }, {} as Record))
+ setEnvironmentVariables(res.environment_variables?.map(env => env.value_type === 'secret' ? { ...env, value: '[__HIDDEN__]' } : env) || [])
setSyncWorkflowDraftHash(res.hash)
setIsLoading(false)
}
@@ -68,49 +67,21 @@ export const usePipelineInit = () => {
environment_variables: [],
},
}).then((res) => {
- workflowStore.getState().setDraftUpdatedAt(res.updated_at)
+ const { setDraftUpdatedAt } = workflowStore.getState()
+ setDraftUpdatedAt(res.updated_at)
handleGetInitialWorkflowData()
})
}
})
}
}
- }, [nodesTemplate, edgesTemplate, workflowStore, setSyncWorkflowDraftHash, datasetId])
+ }, [nodesTemplate, edgesTemplate, workflowStore, datasetId])
useEffect(() => {
- // handleGetInitialWorkflowData()
-
+ handleGetInitialWorkflowData()
+ // eslint-disable-next-line react-hooks/exhaustive-deps
}, [])
- const handleFetchPreloadData = useCallback(async () => {
- try {
- const nodesDefaultConfigsData = await fetchNodesDefaultConfigs(`/rag/pipeline/${datasetId}/workflows/default-workflow-block-configs`)
- const publishedWorkflow = await fetchPublishedWorkflow(`/rag/pipeline/${datasetId}/workflows/publish`)
- workflowStore.setState({
- nodesDefaultConfigs: nodesDefaultConfigsData.reduce((acc, block) => {
- if (!acc[block.type])
- acc[block.type] = { ...block.config }
- return acc
- }, {} as Record),
- })
- workflowStore.getState().setPublishedAt(publishedWorkflow?.created_at)
- }
- catch (e) {
- console.error(e)
- }
- }, [workflowStore, datasetId])
-
- useEffect(() => {
- // handleFetchPreloadData()
- }, [handleFetchPreloadData])
-
- useEffect(() => {
- if (data) {
- workflowStore.getState().setDraftUpdatedAt(data.updated_at)
- workflowStore.getState().setToolPublished(data.tool_published)
- }
- }, [data, workflowStore])
-
return {
data,
isLoading,
diff --git a/web/app/components/rag-pipeline/store/index.ts b/web/app/components/rag-pipeline/store/index.ts
index 769d7f69f2..5d06543e22 100644
--- a/web/app/components/rag-pipeline/store/index.ts
+++ b/web/app/components/rag-pipeline/store/index.ts
@@ -1,5 +1,6 @@
import type { RAGPipelineVariables } from '@/models/pipeline'
import type { StateCreator } from 'zustand'
+import type { ToolWithProvider } from '../../workflow/types'
import { InputVarType } from '../../workflow/types'
export type RagPipelineSliceShape = {
@@ -10,6 +11,8 @@ export type RagPipelineSliceShape = {
setNodesDefaultConfigs: (nodesDefaultConfigs: Record) => void
ragPipelineVariables: RAGPipelineVariables
setRagPipelineVariables: (ragPipelineVariables: RAGPipelineVariables) => void
+ dataSourceList: ToolWithProvider[]
+ setDataSourceList: (dataSourceList: ToolWithProvider[]) => void
}
export type CreateRagPipelineSliceSlice = StateCreator
@@ -50,4 +53,6 @@ export const createRagPipelineSliceSlice: StateCreator =
}],
}],
setRagPipelineVariables: (ragPipelineVariables: RAGPipelineVariables) => set(() => ({ ragPipelineVariables })),
+ dataSourceList: [],
+ setDataSourceList: (dataSourceList: ToolWithProvider[]) => set(() => ({ dataSourceList })),
})
diff --git a/web/app/components/tools/types.ts b/web/app/components/tools/types.ts
index 32c468cde8..4a66ac3063 100644
--- a/web/app/components/tools/types.ts
+++ b/web/app/components/tools/types.ts
@@ -29,6 +29,7 @@ export enum CollectionType {
custom = 'api',
model = 'model',
workflow = 'workflow',
+ datasource = 'datasource',
}
export type Emoji = {
diff --git a/web/app/components/workflow/block-selector/data-sources.tsx b/web/app/components/workflow/block-selector/data-sources.tsx
new file mode 100644
index 0000000000..f54c113b83
--- /dev/null
+++ b/web/app/components/workflow/block-selector/data-sources.tsx
@@ -0,0 +1,51 @@
+import {
+ useRef,
+} from 'react'
+import type {
+ OnSelectBlock,
+ ToolWithProvider,
+} from '../types'
+import Tools from './tools'
+import { ViewType } from './view-type-select'
+import cn from '@/utils/classnames'
+import type { ListRef } from '@/app/components/workflow/block-selector/market-place-plugin/list'
+
+type AllToolsProps = {
+ className?: string
+ toolContentClassName?: string
+ searchText: string
+ onSelect: OnSelectBlock
+ dataSources: ToolWithProvider[]
+}
+
+const DataSources = ({
+ className,
+ toolContentClassName,
+ searchText,
+ onSelect,
+ dataSources,
+}: AllToolsProps) => {
+ const pluginRef = useRef(null)
+ const wrapElemRef = useRef(null)
+
+ return (
+
+ )
+}
+
+export default DataSources
diff --git a/web/app/components/workflow/block-selector/hooks.ts b/web/app/components/workflow/block-selector/hooks.ts
index 5f435a76c2..208a44dff6 100644
--- a/web/app/components/workflow/block-selector/hooks.ts
+++ b/web/app/components/workflow/block-selector/hooks.ts
@@ -8,7 +8,7 @@ import {
ToolTypeEnum,
} from './types'
-export const useTabs = (noBlocks?: boolean) => {
+export const useTabs = (noBlocks?: boolean, noSources?: boolean) => {
const { t } = useTranslation()
const tabs = useMemo(() => {
return [
@@ -22,16 +22,22 @@ export const useTabs = (noBlocks?: boolean) => {
},
]
),
- {
- key: TabsEnum.Sources,
- name: t('workflow.tabs.sources'),
- },
+ ...(
+ noSources
+ ? []
+ : [
+ {
+ key: TabsEnum.Sources,
+ name: t('workflow.tabs.sources'),
+ },
+ ]
+ ),
{
key: TabsEnum.Tools,
name: t('workflow.tabs.tools'),
},
]
- }, [t, noBlocks])
+ }, [t, noBlocks, noSources])
const initialTab = useMemo(() => {
if (noBlocks)
return TabsEnum.Sources
diff --git a/web/app/components/workflow/block-selector/index.tsx b/web/app/components/workflow/block-selector/index.tsx
index b2d11288b6..949567886d 100644
--- a/web/app/components/workflow/block-selector/index.tsx
+++ b/web/app/components/workflow/block-selector/index.tsx
@@ -3,6 +3,7 @@ import type { NodeSelectorProps } from './main'
import NodeSelector from './main'
import { useHooksStore } from '@/app/components/workflow/hooks-store/store'
import { BlockEnum } from '@/app/components/workflow/types'
+import { useStore } from '@/app/components/workflow/store'
const NodeSelectorWrapper = (props: NodeSelectorProps) => {
const availableNodesMetaData = useHooksStore(s => s.availableNodesMetaData)
@@ -27,10 +28,13 @@ const NodeSelectorWrapper = (props: NodeSelectorProps) => {
})
}, [availableNodesMetaData?.nodes])
+ const dataSourceList = useStore(s => s.dataSourceList)
+
return (
)
}
diff --git a/web/app/components/workflow/block-selector/main.tsx b/web/app/components/workflow/block-selector/main.tsx
index 254045e231..e1bcd84c24 100644
--- a/web/app/components/workflow/block-selector/main.tsx
+++ b/web/app/components/workflow/block-selector/main.tsx
@@ -17,6 +17,7 @@ import type {
BlockEnum,
NodeDefault,
OnSelectBlock,
+ ToolWithProvider,
} from '../types'
import Tabs from './tabs'
import { TabsEnum } from './types'
@@ -49,6 +50,7 @@ export type NodeSelectorProps = {
availableBlocksTypes?: BlockEnum[]
disabled?: boolean
blocks?: NodeDefault[]
+ dataSources?: ToolWithProvider[]
}
const NodeSelector: FC = ({
open: openFromProps,
@@ -65,6 +67,7 @@ const NodeSelector: FC = ({
availableBlocksTypes,
disabled,
blocks = [],
+ dataSources = [],
}) => {
const { t } = useTranslation()
const [searchText, setSearchText] = useState('')
@@ -95,7 +98,7 @@ const NodeSelector: FC = ({
activeTab,
setActiveTab,
tabs,
- } = useTabs(!blocks.length)
+ } = useTabs(!blocks.length, !dataSources.length)
const searchPlaceholder = useMemo(() => {
if (activeTab === TabsEnum.Blocks)
@@ -193,6 +196,7 @@ const NodeSelector: FC = ({
tags={tags}
availableBlocksTypes={availableBlocksTypes}
blocks={blocks}
+ dataSources={dataSources}
/>
diff --git a/web/app/components/workflow/block-selector/tabs.tsx b/web/app/components/workflow/block-selector/tabs.tsx
index bad9481c21..c4dc4532cf 100644
--- a/web/app/components/workflow/block-selector/tabs.tsx
+++ b/web/app/components/workflow/block-selector/tabs.tsx
@@ -4,11 +4,13 @@ import { useAllBuiltInTools, useAllCustomTools, useAllWorkflowTools } from '@/se
import type {
BlockEnum,
NodeDefault,
+ ToolWithProvider,
} from '../types'
import type { ToolDefaultValue } from './types'
import { TabsEnum } from './types'
import Blocks from './blocks'
import AllTools from './all-tools'
+import DataSources from './data-sources'
export type TabsProps = {
activeTab: TabsEnum
@@ -17,6 +19,7 @@ export type TabsProps = {
onSelect: (type: BlockEnum, tool?: ToolDefaultValue) => void
availableBlocksTypes?: BlockEnum[]
blocks: NodeDefault[]
+ dataSources?: ToolWithProvider[]
}
const Tabs: FC
= ({
activeTab,
@@ -25,6 +28,7 @@ const Tabs: FC = ({
onSelect,
availableBlocksTypes,
blocks,
+ dataSources = [],
}) => {
const { data: buildInTools } = useAllBuiltInTools()
const { data: customTools } = useAllCustomTools()
@@ -42,6 +46,15 @@ const Tabs: FC = ({
/>
)
}
+ {
+ activeTab === TabsEnum.Sources && !!dataSources.length && (
+
+ )
+ }
{
activeTab === TabsEnum.Tools && (
{
})
}
-export const useWorkflowConfig = (url: string, onSuccess: (v: WorkflowConfigResponse) => void) => {
+export const useWorkflowConfig = (url: string, onSuccess: (v: T) => void) => {
return useQuery({
+ enabled: !!url,
queryKey: [NAME_SPACE, 'config', url],
queryFn: async () => {
- const data = await get(url)
+ const data = await get(url)
onSuccess(data)
return data
},