Commit 328a3e2e authored by StyleZhang's avatar StyleZhang

node about author

parent 597053c3
......@@ -42,7 +42,7 @@ const nodes = [
type: 'custom',
position: { x: 330, y: 30 + i * 300 },
data: {
_selected: i === 0, // for test: always select the first node
selected: i === 0, // for test: always select the first node
name: item,
...payload,
},
......
import type { FC } from 'react'
import { memo } from 'react'
import { BlockEnum } from './types'
import { useStore } from './store'
import {
Code,
DirectAnswer,
......@@ -20,7 +21,7 @@ type BlockIconProps = {
type: BlockEnum
size?: string
className?: string
icon?: any
toolProviderId?: string
}
const ICON_CONTAINER_CLASSNAME_SIZE_MAP: Record<string, string> = {
sm: 'w-5 h-5 rounded-md shadow-xs',
......@@ -59,8 +60,11 @@ const BlockIcon: FC<BlockIconProps> = ({
type,
size = 'sm',
className,
icon,
toolProviderId,
}) => {
const toolsets = useStore(s => s.toolsets)
const icon = toolsets.find(toolset => toolset.id === toolProviderId)?.icon
return (
<div className={`
flex items-center justify-center border-[0.5px] border-white/[0.02] text-white
......
......@@ -39,7 +39,7 @@ const Tools = ({
setToolsets(produce(toolsets, (draft) => {
const index = draft.findIndex(toolset => toolset.id === toolId)
if (!toolsMap[toolId].length && !currentToolset.fetching)
if (!toolsMap[toolId]?.length && !currentToolset.fetching)
draft[index].fetching = true
draft[index].expanded = true
......
......@@ -106,9 +106,6 @@ const Item = ({
provider_type: data.type,
tool_name: tool.name,
title: tool.label[language],
_icon: data.icon,
_about: tool.description[language],
_author: data.author,
})}
>
<div className='absolute left-[22px] w-[1px] h-8 bg-black/5' />
......
......@@ -30,7 +30,4 @@ export type ToolDefaultValue = {
provider_type: string
tool_name: string
title: string
_icon: Collection['icon']
_about: string
_author: string
}
......@@ -12,30 +12,77 @@ import ToolDefault from './nodes/tool/default'
import VariableAssignerDefault from './nodes/variable-assigner/default'
import EndNodeDefault from './nodes/end/default'
export const NODES_EXTRA_DATA = {
[BlockEnum.Start]: {
author: 'Dify',
about: '',
},
[BlockEnum.End]: {
author: 'Dify',
about: '',
},
[BlockEnum.DirectAnswer]: {
author: 'Dify',
about: '',
},
[BlockEnum.LLM]: {
author: 'Dify',
about: '',
},
[BlockEnum.KnowledgeRetrieval]: {
author: 'Dify',
about: '',
},
[BlockEnum.IfElse]: {
author: 'Dify',
about: '',
},
[BlockEnum.Code]: {
author: 'Dify',
about: '',
},
[BlockEnum.TemplateTransform]: {
author: 'Dify',
about: '',
},
[BlockEnum.QuestionClassifier]: {
author: 'Dify',
about: '',
},
[BlockEnum.HttpRequest]: {
author: 'Dify',
about: '',
},
[BlockEnum.VariableAssigner]: {
author: 'Dify',
about: '',
},
[BlockEnum.Tool]: {
author: 'Dify',
about: '',
},
}
export const NODES_INITIAL_DATA = {
[BlockEnum.Start]: {
_author: 'Dify',
type: BlockEnum.Start,
title: '',
desc: '',
...StartNodeDefault.defaultValue,
},
[BlockEnum.End]: {
_author: 'Dify',
type: BlockEnum.End,
title: '',
desc: '',
...EndNodeDefault.defaultValue,
},
[BlockEnum.DirectAnswer]: {
_author: 'Dify',
type: BlockEnum.DirectAnswer,
title: '',
desc: '',
...DirectAnswerDefault.defaultValue,
},
[BlockEnum.LLM]: {
_author: 'Dify',
type: BlockEnum.LLM,
title: '',
desc: '',
......@@ -43,7 +90,6 @@ export const NODES_INITIAL_DATA = {
...LLMDefault.defaultValue,
},
[BlockEnum.KnowledgeRetrieval]: {
_author: 'Dify',
type: BlockEnum.KnowledgeRetrieval,
title: '',
desc: '',
......@@ -53,14 +99,12 @@ export const NODES_INITIAL_DATA = {
...KnowledgeRetrievalDefault.defaultValue,
},
[BlockEnum.IfElse]: {
_author: 'Dify',
type: BlockEnum.IfElse,
title: '',
desc: '',
...IfElseDefault.defaultValue,
},
[BlockEnum.Code]: {
_author: 'Dify',
type: BlockEnum.Code,
title: '',
desc: '',
......@@ -71,7 +115,6 @@ export const NODES_INITIAL_DATA = {
...CodeDefault.defaultValue,
},
[BlockEnum.TemplateTransform]: {
_author: 'Dify',
type: BlockEnum.TemplateTransform,
title: '',
desc: '',
......@@ -80,7 +123,6 @@ export const NODES_INITIAL_DATA = {
...TemplateTransformDefault.defaultValue,
},
[BlockEnum.QuestionClassifier]: {
_author: 'Dify',
type: BlockEnum.QuestionClassifier,
title: '',
desc: '',
......@@ -89,7 +131,6 @@ export const NODES_INITIAL_DATA = {
...QuestionClassifierDefault.defaultValue,
},
[BlockEnum.HttpRequest]: {
_author: 'Dify',
type: BlockEnum.HttpRequest,
title: '',
desc: '',
......@@ -97,7 +138,6 @@ export const NODES_INITIAL_DATA = {
...HttpRequestDefault.defaultValue,
},
[BlockEnum.VariableAssigner]: {
_author: 'Dify',
type: BlockEnum.VariableAssigner,
title: '',
desc: '',
......
import { useCallback } from 'react'
import { useTranslation } from 'react-i18next'
import produce from 'immer'
import { debounce } from 'lodash-es'
import { useDebounceFn } from 'ahooks'
import type {
EdgeMouseHandler,
NodeDragHandler,
......@@ -21,7 +21,10 @@ import type {
BlockEnum,
Node,
} from './types'
import { NODES_INITIAL_DATA } from './constants'
import {
NODES_EXTRA_DATA,
NODES_INITIAL_DATA,
} from './constants'
import { getLayoutByDagre } from './utils'
import { useStore } from './store'
import type { ToolDefaultValue } from './block-selector/types'
......@@ -45,13 +48,23 @@ export const useNodesInitialData = () => {
})
}
export const useNodesExtraData = () => {
const { t } = useTranslation()
return produce(NODES_EXTRA_DATA, (draft) => {
Object.keys(draft).forEach((key) => {
draft[key as BlockEnum].about = t(`workflow.blocksAbout.${key}`)
})
})
}
export const useWorkflow = () => {
const store = useStoreApi()
const reactFlow = useReactFlow()
const nodesInitialData = useNodesInitialData()
const featuresStore = useFeaturesStore()
const handleSyncWorkflowDraft = useCallback(debounce(() => {
const shouldDebouncedSyncWorkflowDraft = () => {
const {
getNodes,
edges,
......@@ -84,7 +97,12 @@ export const useWorkflow = () => {
useStore.setState({ draftUpdatedAt: res.updated_at })
})
}
}, 2000, { trailing: true }), [store, reactFlow, featuresStore])
}
const { run: handleSyncWorkflowDraft } = useDebounceFn(shouldDebouncedSyncWorkflowDraft, {
wait: 2000,
trailing: true,
})
const handleLayout = useCallback(async () => {
const {
......@@ -203,17 +221,17 @@ export const useWorkflow = () => {
} = store.getState()
const nodes = getNodes()
const selectedNode = nodes.find(node => node.data._selected)
const selectedNode = nodes.find(node => node.data.selected)
if (!cancelSelection && selectedNode?.id === nodeId)
return
const newNodes = produce(getNodes(), (draft) => {
draft.forEach(node => node.data._selected = false)
draft.forEach(node => node.data.selected = false)
const selectedNode = draft.find(node => node.id === nodeId)!
if (!cancelSelection)
selectedNode.data._selected = true
selectedNode.data.selected = true
})
setNodes(newNodes)
handleSyncWorkflowDraft()
......@@ -312,7 +330,7 @@ export const useWorkflow = () => {
data: {
...nodesInitialData[nodeType],
...(toolDefaultValue || {}),
_selected: true,
selected: true,
},
position: {
x: currentNode.position.x + 304,
......@@ -330,7 +348,7 @@ export const useWorkflow = () => {
}
const newNodes = produce(nodes, (draft) => {
draft.forEach((node) => {
node.data._selected = false
node.data.selected = false
})
draft.push(nextNode)
})
......@@ -364,7 +382,7 @@ export const useWorkflow = () => {
data: {
...nodesInitialData[nodeType],
...(toolDefaultValue || {}),
_selected: currentNode.data._selected,
selected: currentNode.data.selected,
},
position: {
x: currentNode.position.x,
......
......@@ -40,7 +40,7 @@ const NextStep = ({
<div className='shrink-0 relative flex items-center justify-center w-9 h-9 bg-white rounded-lg border-[0.5px] border-gray-200 shadow-xs'>
<BlockIcon
type={selectedNode!.data.type}
icon={selectedNode!.data._icon}
toolProviderId={selectedNode!.data.provider_id}
/>
</div>
<Line linesNumber={branches ? branches.length : 1} />
......
......@@ -55,7 +55,7 @@ const Item = ({
}
<BlockIcon
type={data.type}
icon={data._icon}
toolProviderId={data.provider_id}
className='shrink-0 mr-1.5'
/>
<div className='grow'>{data.title}</div>
......
import {
memo,
useCallback,
useEffect,
useMemo,
useState,
} from 'react'
import produce from 'immer'
import { useContext } from 'use-context-selector'
import { useTranslation } from 'react-i18next'
import { useEdges } from 'reactflow'
import ChangeBlock from './change-block'
import { useWorkflow } from '@/app/components/workflow/hooks'
import { useStore } from '@/app/components/workflow/store'
import {
useNodesExtraData,
useWorkflow,
} from '@/app/components/workflow/hooks'
import { DotsHorizontal } from '@/app/components/base/icons/src/vender/line/general'
import {
PortalToFollowElem,
......@@ -14,6 +23,12 @@ import {
} from '@/app/components/base/portal-to-follow-elem'
import type { Node } from '@/app/components/workflow/types'
import { BlockEnum } from '@/app/components/workflow/types'
import I18n from '@/context/i18n'
import { getLanguage } from '@/i18n/language'
import {
fetchBuiltInToolList,
fetchCustomToolList,
} from '@/service/tools'
type PanelOperatorProps = {
id: string
......@@ -24,12 +39,53 @@ const PanelOperator = ({
data,
}: PanelOperatorProps) => {
const { t } = useTranslation()
const { locale } = useContext(I18n)
const language = getLanguage(locale)
const edges = useEdges()
const { handleNodeDelete } = useWorkflow()
const nodesExtraData = useNodesExtraData()
const toolsets = useStore(s => s.toolsets)
const toolsMap = useStore(s => s.toolsMap)
const setToolsMap = useStore(s => s.setToolsMap)
const [open, setOpen] = useState(false)
const fetchToolList = useMemo(() => {
const toolset = toolsets.find(toolset => toolset.id === data.provider_id)
return toolset?.type === 'api' ? fetchCustomToolList : fetchBuiltInToolList
}, [toolsets, data.provider_id])
const handleGetAbout = useCallback(() => {
if (data.provider_id && !toolsMap[data.provider_id]?.length) {
fetchToolList(data.provider_id).then((list: any) => {
setToolsMap(produce(toolsMap, (draft) => {
draft[data.provider_id as string] = list
}))
})
}
}, [data, toolsMap, fetchToolList, setToolsMap])
useEffect(() => {
handleGetAbout()
}, [handleGetAbout])
const edge = edges.find(edge => edge.target === id)
const author = useMemo(() => {
if (data.type !== BlockEnum.Tool)
return nodesExtraData[data.type].author
const toolset = toolsets.find(toolset => toolset.id === data.provider_id)
return toolset?.author
}, [data, nodesExtraData, toolsets])
const about = useMemo(() => {
if (data.type !== BlockEnum.Tool)
return nodesExtraData[data.type].about
const tool = toolsMap[data.provider_id as string]?.find(tool => tool.name === data.tool_name)
return tool?.description[language] || ''
}, [data, nodesExtraData, toolsMap, language])
return (
<PortalToFollowElem
placement='bottom-end'
......@@ -83,10 +139,10 @@ const PanelOperator = ({
<div className='flex items-center mb-1 h-[22px] font-medium'>
{t('workflow.panel.about')}
</div>
<div className='text-gray-500 leading-[18px]'>{data._about}</div>
<div className='text-gray-500 leading-[18px]'>{about}</div>
<div className='my-2 h-[0.5px] bg-black/5'></div>
<div className='leading-[18px]'>
{t('workflow.panel.createdBy')} {data._author}
{t('workflow.panel.createdBy')} {author}
</div>
</div>
</div>
......
......@@ -29,7 +29,7 @@ const BaseNode: FC<BaseNodeProps> = ({
<div
className={`
flex border-[2px] rounded-2xl
${data._selected ? 'border-primary-600' : 'border-transparent'}
${data.selected ? 'border-primary-600' : 'border-transparent'}
`}
>
<div
......@@ -75,7 +75,7 @@ const BaseNode: FC<BaseNodeProps> = ({
className='shrink-0 mr-2'
type={data.type}
size='md'
icon={data._icon}
toolProviderId={data.provider_id}
/>
<div
title={data.title}
......
......@@ -54,7 +54,7 @@ const BasePanel: FC<BasePanelProps> = ({
<BlockIcon
className='shrink-0 mr-1'
type={data.type}
icon={data._icon}
toolProviderId={data.provider_id}
size='md'
/>
<TitleInput
......
......@@ -17,7 +17,7 @@ const Panel: FC = () => {
const isChatMode = useIsChatMode()
const runTaskId = useStore(state => state.runTaskId)
const nodes = useNodes<CommonNodeType>()
const selectedNode = nodes.find(node => node.data._selected)
const selectedNode = nodes.find(node => node.data.selected)
const showRunHistory = useStore(state => state.showRunHistory)
const {
showWorkflowInfoPanel,
......
......@@ -2,7 +2,6 @@ import type {
Edge as ReactFlowEdge,
Node as ReactFlowNode,
} from 'reactflow'
import type { Collection } from '@/app/components/tools/types'
import type { ToolDefaultValue } from '@/app/components/workflow/block-selector/types'
export enum BlockEnum {
......@@ -26,16 +25,13 @@ export type Branch = {
}
export type CommonNodeType<T = {}> = {
_selected?: boolean
_targetBranches?: Branch[]
_isSingleRun?: boolean
_icon?: Collection['icon']
_about?: string
_author?: string
selected?: boolean
title: string
desc: string
type: BlockEnum
} & T
} & T & Partial<Pick<ToolDefaultValue, 'provider_id' | 'provider_type' | 'tool_name'>>
export type CommonEdgeType = {
_hovering: boolean
......
......@@ -50,6 +50,19 @@ const translation = {
'http-request': 'HTTP Request',
'variable-assigner': 'Variable Assigner',
},
blocksAbout: {
'start': 'Define the initial parameters for launching a workflow',
'end': 'Define the end and result type of a workflow',
'direct-answer': 'Specify a custom text reply',
'llm': 'Invoking large language models to answer questions or process natural language',
'knowledge-retrieval': 'Allows you to query text content related to user questions from the Knowledge',
'question-classifier': 'Define the classification conditions of user questions, LLM can define how the conversation progresses based on the classification description',
'if-else': 'Allows you to split the workflow into two branches based on if/else conditions',
'code': 'Execute a piece of Python or NodeJS code to implement custom logic',
'template-transform': 'Convert data to string using Jinja template syntax',
'http-request': 'Allow server requests to be sent over the HTTP protocol',
'variable-assigner': 'Assign variables in different branches to the same variable to achieve unified configuration of post-nodes',
},
operator: {
zoomIn: 'Zoom In',
zoomOut: 'Zoom Out',
......
......@@ -50,6 +50,19 @@ const translation = {
'http-request': 'HTTP 请求',
'variable-assigner': '变量赋值',
},
blocksAbout: {
'start': '定义一个 workflow 流程启动的初始参数',
'end': '定义一个 workflow 流程的结束和结果类型',
'direct-answer': '指定一段自定义的文本回复',
'llm': '调用大语言模型回答问题或者对自然语言进行处理',
'knowledge-retrieval': '允许你从知识库中查询与用户问题相关的文本内容',
'question-classifier': '定义用户问题的分类条件,LLM 能够根据分类描述定义对话的进展方式',
'if-else': '允许你根据 if/else 条件将 workflow 拆分成两个分支',
'code': '执行一段 Python 或 NodeJS 代码实现自定义逻辑',
'template-transform': '使用 Jinja 模板语法将数据转换为字符串',
'http-request': '允许通过 HTTP 协议发送服务器请求',
'variable-assigner': '将不同分支中的变量指派给同一个变量,以实现后置节点统一配置',
},
operator: {
zoomIn: '放大',
zoomOut: '缩小',
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment