mirror of
https://github.com/langgenius/dify.git
synced 2026-05-06 02:18:08 +08:00
Merge branch 'feat/plugins' into dev/plugin-deploy
This commit is contained in:
@ -72,6 +72,7 @@ const AgentModelTrigger: FC<AgentModelTriggerProps> = ({
|
|||||||
const [inModelList, setInModelList] = useState(false)
|
const [inModelList, setInModelList] = useState(false)
|
||||||
const invalidateInstalledPluginList = useInvalidateInstalledPluginList()
|
const invalidateInstalledPluginList = useInvalidateInstalledPluginList()
|
||||||
const handleOpenModal = useModelModalHandler()
|
const handleOpenModal = useModelModalHandler()
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
(async () => {
|
(async () => {
|
||||||
if (modelId && currentProvider) {
|
if (modelId && currentProvider) {
|
||||||
@ -96,11 +97,8 @@ const AgentModelTrigger: FC<AgentModelTriggerProps> = ({
|
|||||||
catch (error) {
|
catch (error) {
|
||||||
// pass
|
// pass
|
||||||
}
|
}
|
||||||
setIsPluginChecked(true)
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
setIsPluginChecked(true)
|
|
||||||
}
|
}
|
||||||
|
setIsPluginChecked(true)
|
||||||
})()
|
})()
|
||||||
}, [providerName, modelId, currentProvider])
|
}, [providerName, modelId, currentProvider])
|
||||||
|
|
||||||
|
|||||||
@ -119,7 +119,7 @@ const ToolSelector: FC<Props> = ({
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
onSelect(toolValue)
|
onSelect(toolValue)
|
||||||
setIsShowChooseTool(false)
|
// setIsShowChooseTool(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleDescriptionChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
|
const handleDescriptionChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
|
||||||
|
|||||||
@ -130,11 +130,15 @@ const PluginItem: FC<Props> = ({
|
|||||||
packageName={name}
|
packageName={name}
|
||||||
packageNameClassName='w-auto max-w-[150px]'
|
packageNameClassName='w-auto max-w-[150px]'
|
||||||
/>
|
/>
|
||||||
<div className='mx-2 text-text-quaternary system-xs-regular'>·</div>
|
{category === PluginType.extension && (
|
||||||
<div className='flex text-text-tertiary system-xs-regular space-x-1'>
|
<>
|
||||||
<RiLoginCircleLine className='w-4 h-4' />
|
<div className='mx-2 text-text-quaternary system-xs-regular'>·</div>
|
||||||
<span>{t('plugin.endpointsEnabled', { num: endpoints_active })}</span>
|
<div className='flex text-text-tertiary system-xs-regular space-x-1'>
|
||||||
</div>
|
<RiLoginCircleLine className='w-4 h-4' />
|
||||||
|
<span>{t('plugin.endpointsEnabled', { num: endpoints_active })}</span>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='flex items-center'>
|
<div className='flex items-center'>
|
||||||
@ -154,7 +158,7 @@ const PluginItem: FC<Props> = ({
|
|||||||
&& <>
|
&& <>
|
||||||
<a href={`${MARKETPLACE_URL_PREFIX}/plugins/${author}/${name}`} target='_blank' className='flex items-center gap-0.5'>
|
<a href={`${MARKETPLACE_URL_PREFIX}/plugins/${author}/${name}`} target='_blank' className='flex items-center gap-0.5'>
|
||||||
<div className='text-text-tertiary system-2xs-medium-uppercase'>{t('plugin.from')} <span className='text-text-secondary'>marketplace</span></div>
|
<div className='text-text-tertiary system-2xs-medium-uppercase'>{t('plugin.from')} <span className='text-text-secondary'>marketplace</span></div>
|
||||||
<RiArrowRightUpLine className='w-3 h-3' />
|
<RiArrowRightUpLine className='w-3 h-3 text-text-tertiary' />
|
||||||
</a>
|
</a>
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
|
|||||||
@ -35,7 +35,7 @@ export const useChecklist = (nodes: Node[], edges: Edge[]) => {
|
|||||||
const buildInTools = useStore(s => s.buildInTools)
|
const buildInTools = useStore(s => s.buildInTools)
|
||||||
const customTools = useStore(s => s.customTools)
|
const customTools = useStore(s => s.customTools)
|
||||||
const workflowTools = useStore(s => s.workflowTools)
|
const workflowTools = useStore(s => s.workflowTools)
|
||||||
const { data: agentStrategies } = useStrategyProviders()
|
const { data: strategyProviders } = useStrategyProviders()
|
||||||
|
|
||||||
const needWarningNodes = useMemo(() => {
|
const needWarningNodes = useMemo(() => {
|
||||||
const list = []
|
const list = []
|
||||||
@ -62,12 +62,14 @@ export const useChecklist = (nodes: Node[], edges: Edge[]) => {
|
|||||||
|
|
||||||
if (node.data.type === BlockEnum.Agent) {
|
if (node.data.type === BlockEnum.Agent) {
|
||||||
const data = node.data as AgentNodeType
|
const data = node.data as AgentNodeType
|
||||||
const provider = agentStrategies?.find(s => s.plugin_unique_identifier === data.plugin_unique_identifier)
|
const isReadyForCheckValid = !!strategyProviders
|
||||||
|
const provider = strategyProviders?.find(provider => provider.declaration.identity.name === data.agent_strategy_provider_name)
|
||||||
const strategy = provider?.declaration.strategies?.find(s => s.identity.name === data.agent_strategy_name)
|
const strategy = provider?.declaration.strategies?.find(s => s.identity.name === data.agent_strategy_name)
|
||||||
moreDataForCheckValid = {
|
moreDataForCheckValid = {
|
||||||
provider,
|
provider,
|
||||||
strategy,
|
strategy,
|
||||||
language,
|
language,
|
||||||
|
isReadyForCheckValid,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -106,7 +108,7 @@ export const useChecklist = (nodes: Node[], edges: Edge[]) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return list
|
return list
|
||||||
}, [nodes, edges, isChatMode, buildInTools, customTools, workflowTools, language, nodesExtraData, t, agentStrategies])
|
}, [nodes, edges, isChatMode, buildInTools, customTools, workflowTools, language, nodesExtraData, t, strategyProviders])
|
||||||
|
|
||||||
return needWarningNodes
|
return needWarningNodes
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,3 +9,4 @@ export * from './use-workflow-node-iteration-finished'
|
|||||||
export * from './use-workflow-node-retry'
|
export * from './use-workflow-node-retry'
|
||||||
export * from './use-workflow-text-chunk'
|
export * from './use-workflow-text-chunk'
|
||||||
export * from './use-workflow-text-replace'
|
export * from './use-workflow-text-replace'
|
||||||
|
export * from './use-workflow-agent-log'
|
||||||
|
|||||||
@ -0,0 +1,50 @@
|
|||||||
|
import { useCallback } from 'react'
|
||||||
|
import produce from 'immer'
|
||||||
|
import type { AgentLogResponse } from '@/types/workflow'
|
||||||
|
import { useWorkflowStore } from '@/app/components/workflow/store'
|
||||||
|
|
||||||
|
export const useWorkflowAgentLog = () => {
|
||||||
|
const workflowStore = useWorkflowStore()
|
||||||
|
|
||||||
|
const handleWorkflowAgentLog = useCallback((params: AgentLogResponse) => {
|
||||||
|
const { data } = params
|
||||||
|
const {
|
||||||
|
workflowRunningData,
|
||||||
|
setWorkflowRunningData,
|
||||||
|
} = workflowStore.getState()
|
||||||
|
|
||||||
|
setWorkflowRunningData(produce(workflowRunningData!, (draft) => {
|
||||||
|
const currentIndex = draft.tracing!.findIndex(item => item.node_id === data.node_id)
|
||||||
|
if (currentIndex > -1) {
|
||||||
|
const current = draft.tracing![currentIndex]
|
||||||
|
|
||||||
|
if (current.execution_metadata) {
|
||||||
|
if (current.execution_metadata.agent_log) {
|
||||||
|
const currentLogIndex = current.execution_metadata.agent_log.findIndex(log => log.id === data.id)
|
||||||
|
if (currentLogIndex > -1) {
|
||||||
|
current.execution_metadata.agent_log[currentLogIndex] = {
|
||||||
|
...current.execution_metadata.agent_log[currentLogIndex],
|
||||||
|
...data,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
current.execution_metadata.agent_log.push(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
current.execution_metadata.agent_log = [data]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
current.execution_metadata = {
|
||||||
|
agent_log: [data],
|
||||||
|
} as any
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
}, [workflowStore])
|
||||||
|
|
||||||
|
return {
|
||||||
|
handleWorkflowAgentLog,
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,4 +1,5 @@
|
|||||||
import {
|
import {
|
||||||
|
useWorkflowAgentLog,
|
||||||
useWorkflowFailed,
|
useWorkflowFailed,
|
||||||
useWorkflowFinished,
|
useWorkflowFinished,
|
||||||
useWorkflowNodeFinished,
|
useWorkflowNodeFinished,
|
||||||
@ -24,6 +25,7 @@ export const useWorkflowRunEvent = () => {
|
|||||||
const { handleWorkflowNodeRetry } = useWorkflowNodeRetry()
|
const { handleWorkflowNodeRetry } = useWorkflowNodeRetry()
|
||||||
const { handleWorkflowTextChunk } = useWorkflowTextChunk()
|
const { handleWorkflowTextChunk } = useWorkflowTextChunk()
|
||||||
const { handleWorkflowTextReplace } = useWorkflowTextReplace()
|
const { handleWorkflowTextReplace } = useWorkflowTextReplace()
|
||||||
|
const { handleWorkflowAgentLog } = useWorkflowAgentLog()
|
||||||
|
|
||||||
return {
|
return {
|
||||||
handleWorkflowStarted,
|
handleWorkflowStarted,
|
||||||
@ -37,5 +39,6 @@ export const useWorkflowRunEvent = () => {
|
|||||||
handleWorkflowNodeRetry,
|
handleWorkflowNodeRetry,
|
||||||
handleWorkflowTextChunk,
|
handleWorkflowTextChunk,
|
||||||
handleWorkflowTextReplace,
|
handleWorkflowTextReplace,
|
||||||
|
handleWorkflowAgentLog,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -37,6 +37,7 @@ export const useWorkflowRun = () => {
|
|||||||
handleWorkflowNodeIterationNext,
|
handleWorkflowNodeIterationNext,
|
||||||
handleWorkflowNodeIterationFinished,
|
handleWorkflowNodeIterationFinished,
|
||||||
handleWorkflowNodeRetry,
|
handleWorkflowNodeRetry,
|
||||||
|
handleWorkflowAgentLog,
|
||||||
handleWorkflowTextChunk,
|
handleWorkflowTextChunk,
|
||||||
handleWorkflowTextReplace,
|
handleWorkflowTextReplace,
|
||||||
} = useWorkflowRunEvent()
|
} = useWorkflowRunEvent()
|
||||||
@ -118,6 +119,7 @@ export const useWorkflowRun = () => {
|
|||||||
onIterationNext,
|
onIterationNext,
|
||||||
onIterationFinish,
|
onIterationFinish,
|
||||||
onNodeRetry,
|
onNodeRetry,
|
||||||
|
onAgentLog,
|
||||||
onError,
|
onError,
|
||||||
...restCallback
|
...restCallback
|
||||||
} = callback || {}
|
} = callback || {}
|
||||||
@ -234,6 +236,12 @@ export const useWorkflowRun = () => {
|
|||||||
if (onNodeRetry)
|
if (onNodeRetry)
|
||||||
onNodeRetry(params)
|
onNodeRetry(params)
|
||||||
},
|
},
|
||||||
|
onAgentLog: (params) => {
|
||||||
|
handleWorkflowAgentLog(params)
|
||||||
|
|
||||||
|
if (onAgentLog)
|
||||||
|
onAgentLog(params)
|
||||||
|
},
|
||||||
onTextChunk: (params) => {
|
onTextChunk: (params) => {
|
||||||
handleWorkflowTextChunk(params)
|
handleWorkflowTextChunk(params)
|
||||||
},
|
},
|
||||||
@ -252,7 +260,7 @@ export const useWorkflowRun = () => {
|
|||||||
...restCallback,
|
...restCallback,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}, [store, workflowStore, doSyncWorkflowDraft, handleWorkflowStarted, handleWorkflowFinished, handleWorkflowFailed, handleWorkflowNodeStarted, handleWorkflowNodeFinished, handleWorkflowNodeIterationStarted, handleWorkflowNodeIterationNext, handleWorkflowNodeIterationFinished, handleWorkflowNodeRetry, handleWorkflowTextChunk, handleWorkflowTextReplace, pathname])
|
}, [store, workflowStore, doSyncWorkflowDraft, handleWorkflowStarted, handleWorkflowFinished, handleWorkflowFailed, handleWorkflowNodeStarted, handleWorkflowNodeFinished, handleWorkflowNodeIterationStarted, handleWorkflowNodeIterationNext, handleWorkflowNodeIterationFinished, handleWorkflowNodeRetry, handleWorkflowTextChunk, handleWorkflowTextReplace, handleWorkflowAgentLog, pathname])
|
||||||
|
|
||||||
const handleStopRun = useCallback((taskId: string) => {
|
const handleStopRun = useCallback((taskId: string) => {
|
||||||
const appId = useAppStore.getState().appDetail?.id
|
const appId = useAppStore.getState().appDetail?.id
|
||||||
|
|||||||
@ -35,14 +35,22 @@ export const ModelBar: FC<ModelBarProps> = (props) => {
|
|||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const modelList = useAllModel()
|
const modelList = useAllModel()
|
||||||
if (!('provider' in props)) {
|
if (!('provider' in props)) {
|
||||||
return <ModelSelector
|
return <Tooltip
|
||||||
modelList={[]}
|
popupContent={t('workflow.nodes.agent.modelNotSelected')}
|
||||||
triggerClassName='bg-workflow-block-parma-bg !h-6 !rounded-md'
|
triggerMethod='hover'
|
||||||
defaultModel={undefined}
|
>
|
||||||
showDeprecatedWarnIcon={false}
|
<div className='relative'>
|
||||||
readonly
|
<ModelSelector
|
||||||
deprecatedClassName='opacity-50'
|
modelList={[]}
|
||||||
/>
|
triggerClassName='bg-workflow-block-parma-bg !h-6 !rounded-md'
|
||||||
|
defaultModel={undefined}
|
||||||
|
showDeprecatedWarnIcon={false}
|
||||||
|
readonly
|
||||||
|
deprecatedClassName='opacity-50'
|
||||||
|
/>
|
||||||
|
<Indicator color={'red'} className='absolute -right-0.5 -top-0.5' />
|
||||||
|
</div>
|
||||||
|
</Tooltip>
|
||||||
}
|
}
|
||||||
const modelInstalled = modelList?.some(
|
const modelInstalled = modelList?.some(
|
||||||
provider => provider.provider === props.provider && provider.models.some(model => model.model === props.model))
|
provider => provider.provider === props.provider && provider.models.some(model => model.model === props.model))
|
||||||
|
|||||||
@ -18,6 +18,7 @@ export const ToolIcon = memo(({ providerName }: ToolIconProps) => {
|
|||||||
const { data: buildInTools } = useAllBuiltInTools()
|
const { data: buildInTools } = useAllBuiltInTools()
|
||||||
const { data: customTools } = useAllCustomTools()
|
const { data: customTools } = useAllCustomTools()
|
||||||
const { data: workflowTools } = useAllWorkflowTools()
|
const { data: workflowTools } = useAllWorkflowTools()
|
||||||
|
const isDataReady = !!buildInTools && !!customTools && !!workflowTools
|
||||||
const currentProvider = useMemo(() => {
|
const currentProvider = useMemo(() => {
|
||||||
const mergedTools = [...(buildInTools || []), ...(customTools || []), ...(workflowTools || [])]
|
const mergedTools = [...(buildInTools || []), ...(customTools || []), ...(workflowTools || [])]
|
||||||
return mergedTools.find((toolWithProvider) => {
|
return mergedTools.find((toolWithProvider) => {
|
||||||
@ -33,10 +34,11 @@ export const ToolIcon = memo(({ providerName }: ToolIconProps) => {
|
|||||||
return iconFromMarketPlace
|
return iconFromMarketPlace
|
||||||
}, [author, currentProvider, name])
|
}, [author, currentProvider, name])
|
||||||
const status: Status = useMemo(() => {
|
const status: Status = useMemo(() => {
|
||||||
|
if (!isDataReady) return undefined
|
||||||
if (!currentProvider) return 'not-installed'
|
if (!currentProvider) return 'not-installed'
|
||||||
if (currentProvider.is_team_authorization === false) return 'not-authorized'
|
if (currentProvider.is_team_authorization === false) return 'not-authorized'
|
||||||
return undefined
|
return undefined
|
||||||
}, [currentProvider])
|
}, [currentProvider, isDataReady])
|
||||||
const indicator = status === 'not-installed' ? 'red' : status === 'not-authorized' ? 'yellow' : undefined
|
const indicator = status === 'not-installed' ? 'red' : status === 'not-authorized' ? 'yellow' : undefined
|
||||||
const notSuccess = (['not-installed', 'not-authorized'] as Array<Status>).includes(status)
|
const notSuccess = (['not-installed', 'not-authorized'] as Array<Status>).includes(status)
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
|||||||
@ -21,8 +21,15 @@ const nodeDefault: NodeDefault<AgentNodeType> = {
|
|||||||
strategyProvider?: StrategyPluginDetail,
|
strategyProvider?: StrategyPluginDetail,
|
||||||
strategy?: StrategyDetail
|
strategy?: StrategyDetail
|
||||||
language: string
|
language: string
|
||||||
|
isReadyForCheckValid: boolean
|
||||||
}) {
|
}) {
|
||||||
const { strategy, language } = moreDataForCheckValid
|
const { strategy, language, isReadyForCheckValid } = moreDataForCheckValid
|
||||||
|
if (!isReadyForCheckValid) {
|
||||||
|
return {
|
||||||
|
isValid: true,
|
||||||
|
errorMessage: '',
|
||||||
|
}
|
||||||
|
}
|
||||||
if (!strategy) {
|
if (!strategy) {
|
||||||
return {
|
return {
|
||||||
isValid: false,
|
isValid: false,
|
||||||
|
|||||||
@ -24,7 +24,6 @@ const AgentNode: FC<NodeProps<AgentNodeType>> = (props) => {
|
|||||||
.filter(param => param.type === FormTypeEnum.modelSelector)
|
.filter(param => param.type === FormTypeEnum.modelSelector)
|
||||||
.reduce((acc, param) => {
|
.reduce((acc, param) => {
|
||||||
const item = inputs.agent_parameters?.[param.name]?.value
|
const item = inputs.agent_parameters?.[param.name]?.value
|
||||||
console.log({ item })
|
|
||||||
if (!item) {
|
if (!item) {
|
||||||
if (param.required) {
|
if (param.required) {
|
||||||
acc.push({ param: param.name })
|
acc.push({ param: param.name })
|
||||||
@ -68,20 +67,26 @@ const AgentNode: FC<NodeProps<AgentNodeType>> = (props) => {
|
|||||||
{inputs.agent_strategy_name
|
{inputs.agent_strategy_name
|
||||||
? <SettingItem
|
? <SettingItem
|
||||||
label={t('workflow.nodes.agent.strategy.shortLabel')}
|
label={t('workflow.nodes.agent.strategy.shortLabel')}
|
||||||
status={!currentStrategyStatus?.isExistInPlugin ? 'error' : undefined}
|
status={
|
||||||
|
currentStrategyStatus && !currentStrategyStatus.isExistInPlugin
|
||||||
|
? 'error'
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
tooltip={
|
tooltip={
|
||||||
!currentStrategyStatus?.isExistInPlugin ? t('workflow.nodes.agent.strategyNotInstallTooltip', {
|
(currentStrategyStatus && !currentStrategyStatus.isExistInPlugin)
|
||||||
plugin: pluginDetail?.declaration.label
|
? t('workflow.nodes.agent.strategyNotInstallTooltip', {
|
||||||
? renderI18nObject(pluginDetail?.declaration.label)
|
plugin: pluginDetail?.declaration.label
|
||||||
: undefined,
|
? renderI18nObject(pluginDetail?.declaration.label)
|
||||||
strategy: inputs.agent_strategy_label,
|
: undefined,
|
||||||
}) : undefined
|
strategy: inputs.agent_strategy_label,
|
||||||
|
})
|
||||||
|
: undefined
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{inputs.agent_strategy_label}
|
{inputs.agent_strategy_label}
|
||||||
</SettingItem>
|
</SettingItem>
|
||||||
: <SettingItem label={t('workflow.nodes.agent.strategyNotSet')} />}
|
: <SettingItem label={t('workflow.nodes.agent.strategyNotSet')} />}
|
||||||
<Group
|
{models.length > 0 && <Group
|
||||||
label={<GroupLabel className='mt-1'>
|
label={<GroupLabel className='mt-1'>
|
||||||
{t('workflow.nodes.agent.model')}
|
{t('workflow.nodes.agent.model')}
|
||||||
</GroupLabel>}
|
</GroupLabel>}
|
||||||
@ -92,7 +97,7 @@ const AgentNode: FC<NodeProps<AgentNodeType>> = (props) => {
|
|||||||
key={model.param}
|
key={model.param}
|
||||||
/>
|
/>
|
||||||
})}
|
})}
|
||||||
</Group>
|
</Group>}
|
||||||
{tools.length > 0 && <Group label={<GroupLabel className='mt-1'>
|
{tools.length > 0 && <Group label={<GroupLabel className='mt-1'>
|
||||||
{t('workflow.nodes.agent.toolbox')}
|
{t('workflow.nodes.agent.toolbox')}
|
||||||
</GroupLabel>}>
|
</GroupLabel>}>
|
||||||
|
|||||||
@ -404,6 +404,63 @@ export const useChat = (
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
onAgentLog: ({ data }) => {
|
||||||
|
const currentNodeIndex = responseItem.workflowProcess!.tracing!.findIndex(item => item.node_id === data.node_id)
|
||||||
|
if (currentNodeIndex > -1) {
|
||||||
|
const current = responseItem.workflowProcess!.tracing![currentNodeIndex]
|
||||||
|
|
||||||
|
if (current.execution_metadata) {
|
||||||
|
if (current.execution_metadata.agent_log) {
|
||||||
|
const currentLogIndex = current.execution_metadata.agent_log.findIndex(log => log.id === data.id)
|
||||||
|
if (currentLogIndex > -1) {
|
||||||
|
current.execution_metadata.agent_log[currentLogIndex] = {
|
||||||
|
...current.execution_metadata.agent_log[currentLogIndex],
|
||||||
|
...data,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
current.execution_metadata.agent_log.push(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
current.execution_metadata.agent_log = [data]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
current.execution_metadata = {
|
||||||
|
agent_log: [data],
|
||||||
|
} as any
|
||||||
|
}
|
||||||
|
// if (current.agentLog) {
|
||||||
|
// const currentLogIndex = current.agentLog.findIndex(log => log.id === data.id)
|
||||||
|
|
||||||
|
// if (currentLogIndex > -1) {
|
||||||
|
// current.agentLog[currentLogIndex] = {
|
||||||
|
// ...current.agentLog[currentLogIndex],
|
||||||
|
// ...data,
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// else {
|
||||||
|
// current.agentLog.push(data)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// else {
|
||||||
|
// current.agentLog = [data]
|
||||||
|
// }
|
||||||
|
|
||||||
|
responseItem.workflowProcess!.tracing[currentNodeIndex] = {
|
||||||
|
...current,
|
||||||
|
}
|
||||||
|
|
||||||
|
handleUpdateChatList(produce(chatListRef.current, (draft) => {
|
||||||
|
const currentIndex = draft.findIndex(item => item.id === responseItem.id)
|
||||||
|
draft[currentIndex] = {
|
||||||
|
...draft[currentIndex],
|
||||||
|
...responseItem,
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
},
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}, [handleRun, handleResponding, handleUpdateChatList, notify, t, updateCurrentQA, config.suggested_questions_after_answer?.enabled, formSettings])
|
}, [handleRun, handleResponding, handleUpdateChatList, notify, t, updateCurrentQA, config.suggested_questions_after_answer?.enabled, formSettings])
|
||||||
|
|||||||
@ -1,4 +1,7 @@
|
|||||||
import { useState } from 'react'
|
import {
|
||||||
|
useMemo,
|
||||||
|
useState,
|
||||||
|
} from 'react'
|
||||||
import {
|
import {
|
||||||
RiArrowRightSLine,
|
RiArrowRightSLine,
|
||||||
RiListView,
|
RiListView,
|
||||||
@ -9,6 +12,9 @@ import type { AgentLogItemWithChildren } from '@/types/workflow'
|
|||||||
import NodeStatusIcon from '@/app/components/workflow/nodes/_base/components/node-status-icon'
|
import NodeStatusIcon from '@/app/components/workflow/nodes/_base/components/node-status-icon'
|
||||||
import CodeEditor from '@/app/components/workflow/nodes/_base/components/editor/code-editor'
|
import CodeEditor from '@/app/components/workflow/nodes/_base/components/editor/code-editor'
|
||||||
import { CodeLanguage } from '@/app/components/workflow/nodes/code/types'
|
import { CodeLanguage } from '@/app/components/workflow/nodes/code/types'
|
||||||
|
import BlockIcon from '@/app/components/workflow/block-icon'
|
||||||
|
import { BlockEnum } from '@/app/components/workflow/types'
|
||||||
|
import useGetIcon from '@/app/components/plugins/install-plugin/base/use-get-icon'
|
||||||
|
|
||||||
type AgentLogItemProps = {
|
type AgentLogItemProps = {
|
||||||
item: AgentLogItemWithChildren
|
item: AgentLogItemWithChildren
|
||||||
@ -26,6 +32,26 @@ const AgentLogItem = ({
|
|||||||
metadata,
|
metadata,
|
||||||
} = item
|
} = item
|
||||||
const [expanded, setExpanded] = useState(false)
|
const [expanded, setExpanded] = useState(false)
|
||||||
|
const { getIconUrl } = useGetIcon()
|
||||||
|
const toolIcon = useMemo(() => {
|
||||||
|
const icon = metadata?.icon
|
||||||
|
|
||||||
|
if (icon) {
|
||||||
|
if (icon.includes('http'))
|
||||||
|
return icon
|
||||||
|
|
||||||
|
return getIconUrl(icon)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ''
|
||||||
|
}, [getIconUrl, metadata?.icon])
|
||||||
|
|
||||||
|
const mergeStatus = useMemo(() => {
|
||||||
|
if (status === 'start')
|
||||||
|
return 'running'
|
||||||
|
|
||||||
|
return status
|
||||||
|
}, [status])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='bg-background-default border-[0.5px] border-components-panel-border rounded-[10px]'>
|
<div className='bg-background-default border-[0.5px] border-components-panel-border rounded-[10px]'>
|
||||||
@ -41,7 +67,11 @@ const AgentLogItem = ({
|
|||||||
? <RiArrowRightSLine className='shrink-0 w-4 h-4 rotate-90 text-text-quaternary' />
|
? <RiArrowRightSLine className='shrink-0 w-4 h-4 rotate-90 text-text-quaternary' />
|
||||||
: <RiArrowRightSLine className='shrink-0 w-4 h-4 text-text-quaternary' />
|
: <RiArrowRightSLine className='shrink-0 w-4 h-4 text-text-quaternary' />
|
||||||
}
|
}
|
||||||
<div className='shrink-0 mr-1.5 w-5 h-5'></div>
|
<BlockIcon
|
||||||
|
className='shrink-0 mr-1.5'
|
||||||
|
type={toolIcon ? BlockEnum.Tool : BlockEnum.Agent}
|
||||||
|
toolIcon={toolIcon}
|
||||||
|
/>
|
||||||
<div
|
<div
|
||||||
className='grow system-sm-semibold-uppercase text-text-secondary truncate'
|
className='grow system-sm-semibold-uppercase text-text-secondary truncate'
|
||||||
title={label}
|
title={label}
|
||||||
@ -53,7 +83,7 @@ const AgentLogItem = ({
|
|||||||
<div className='shrink-0 mr-2 system-xs-regular text-text-tertiary'>{metadata?.elapsed_time?.toFixed(3)}s</div>
|
<div className='shrink-0 mr-2 system-xs-regular text-text-tertiary'>{metadata?.elapsed_time?.toFixed(3)}s</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
<NodeStatusIcon status={status} />
|
<NodeStatusIcon status={mergeStatus} />
|
||||||
</div>
|
</div>
|
||||||
{
|
{
|
||||||
expanded && (
|
expanded && (
|
||||||
|
|||||||
@ -14,21 +14,29 @@ const AgentLogTrigger = ({
|
|||||||
onShowAgentOrToolLog,
|
onShowAgentOrToolLog,
|
||||||
}: AgentLogTriggerProps) => {
|
}: AgentLogTriggerProps) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const { agentLog } = nodeInfo
|
const { agentLog, execution_metadata } = nodeInfo
|
||||||
|
const agentStrategy = execution_metadata?.tool_info?.agent_strategy
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='bg-components-button-tertiary-bg rounded-[10px]'>
|
<div
|
||||||
|
className='bg-components-button-tertiary-bg rounded-[10px] cursor-pointer'
|
||||||
|
onClick={() => {
|
||||||
|
onShowAgentOrToolLog({ id: nodeInfo.id, children: agentLog || [] } as AgentLogItemWithChildren)
|
||||||
|
}}
|
||||||
|
>
|
||||||
<div className='flex items-center px-3 pt-2 system-2xs-medium-uppercase text-text-tertiary'>
|
<div className='flex items-center px-3 pt-2 system-2xs-medium-uppercase text-text-tertiary'>
|
||||||
{t('workflow.nodes.agent.strategy.label')}
|
{t('workflow.nodes.agent.strategy.label')}
|
||||||
</div>
|
</div>
|
||||||
<div className='flex items-center pl-3 pt-1 pr-2 pb-1.5'>
|
<div className='flex items-center pl-3 pt-1 pr-2 pb-1.5'>
|
||||||
<div className='shrink-0 w-5 h-5'></div>
|
{
|
||||||
<div className='grow mx-0.5 px-1 system-xs-medium text-text-secondary'></div>
|
agentStrategy && (
|
||||||
|
<div className='grow system-xs-medium text-text-secondary'>
|
||||||
|
{agentStrategy}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
<div
|
<div
|
||||||
className='shrink-0 flex items-center px-[1px] system-xs-regular-uppercase text-text-tertiary cursor-pointer'
|
className='shrink-0 flex items-center px-[1px] system-xs-regular-uppercase text-text-tertiary cursor-pointer'
|
||||||
onClick={() => {
|
|
||||||
onShowAgentOrToolLog({ id: nodeInfo.id, children: agentLog || [] } as AgentLogItemWithChildren)
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
{t('runLog.detail')}
|
{t('runLog.detail')}
|
||||||
<RiArrowRightLine className='ml-0.5 w-3.5 h-3.5' />
|
<RiArrowRightLine className='ml-0.5 w-3.5 h-3.5' />
|
||||||
|
|||||||
@ -735,6 +735,7 @@ const translation = {
|
|||||||
strategyNotSet: 'Agentic strategy Not Set',
|
strategyNotSet: 'Agentic strategy Not Set',
|
||||||
tools: 'Tools',
|
tools: 'Tools',
|
||||||
maxIterations: 'Max Iterations',
|
maxIterations: 'Max Iterations',
|
||||||
|
modelNotSelected: 'Model not selected',
|
||||||
modelNotInstallTooltip: 'This model is not installed',
|
modelNotInstallTooltip: 'This model is not installed',
|
||||||
toolNotInstallTooltip: '{{tool}} is not installed',
|
toolNotInstallTooltip: '{{tool}} is not installed',
|
||||||
toolNotAuthorizedTooltip: '{{tool}} Not Authorized',
|
toolNotAuthorizedTooltip: '{{tool}} Not Authorized',
|
||||||
|
|||||||
@ -736,6 +736,7 @@ const translation = {
|
|||||||
tools: '工具',
|
tools: '工具',
|
||||||
maxIterations: '最大迭代次数',
|
maxIterations: '最大迭代次数',
|
||||||
modelNotInstallTooltip: '此模型未安装',
|
modelNotInstallTooltip: '此模型未安装',
|
||||||
|
modelNotSelected: '未选择模型',
|
||||||
toolNotInstallTooltip: '{{tool}} 未安装',
|
toolNotInstallTooltip: '{{tool}} 未安装',
|
||||||
toolNotAuthorizedTooltip: '{{tool}} 未授权',
|
toolNotAuthorizedTooltip: '{{tool}} 未授权',
|
||||||
strategyNotInstallTooltip: '{{strategy}} 未安装',
|
strategyNotInstallTooltip: '{{strategy}} 未安装',
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import Toast from '@/app/components/base/toast'
|
|||||||
import type { AnnotationReply, MessageEnd, MessageReplace, ThoughtItem } from '@/app/components/base/chat/chat/type'
|
import type { AnnotationReply, MessageEnd, MessageReplace, ThoughtItem } from '@/app/components/base/chat/chat/type'
|
||||||
import type { VisionFile } from '@/types/app'
|
import type { VisionFile } from '@/types/app'
|
||||||
import type {
|
import type {
|
||||||
|
AgentLogResponse,
|
||||||
IterationFinishedResponse,
|
IterationFinishedResponse,
|
||||||
IterationNextResponse,
|
IterationNextResponse,
|
||||||
IterationStartedResponse,
|
IterationStartedResponse,
|
||||||
@ -53,6 +54,7 @@ export type IOnTextChunk = (textChunk: TextChunkResponse) => void
|
|||||||
export type IOnTTSChunk = (messageId: string, audioStr: string, audioType?: string) => void
|
export type IOnTTSChunk = (messageId: string, audioStr: string, audioType?: string) => void
|
||||||
export type IOnTTSEnd = (messageId: string, audioStr: string, audioType?: string) => void
|
export type IOnTTSEnd = (messageId: string, audioStr: string, audioType?: string) => void
|
||||||
export type IOnTextReplace = (textReplace: TextReplaceResponse) => void
|
export type IOnTextReplace = (textReplace: TextReplaceResponse) => void
|
||||||
|
export type IOnAgentLog = (agentLog: AgentLogResponse) => void
|
||||||
|
|
||||||
export type IOtherOptions = {
|
export type IOtherOptions = {
|
||||||
isPublicAPI?: boolean
|
isPublicAPI?: boolean
|
||||||
@ -84,6 +86,7 @@ export type IOtherOptions = {
|
|||||||
onTTSChunk?: IOnTTSChunk
|
onTTSChunk?: IOnTTSChunk
|
||||||
onTTSEnd?: IOnTTSEnd
|
onTTSEnd?: IOnTTSEnd
|
||||||
onTextReplace?: IOnTextReplace
|
onTextReplace?: IOnTextReplace
|
||||||
|
onAgentLog?: IOnAgentLog
|
||||||
}
|
}
|
||||||
|
|
||||||
function unicodeToChar(text: string) {
|
function unicodeToChar(text: string) {
|
||||||
@ -129,6 +132,7 @@ const handleStream = (
|
|||||||
onTTSChunk?: IOnTTSChunk,
|
onTTSChunk?: IOnTTSChunk,
|
||||||
onTTSEnd?: IOnTTSEnd,
|
onTTSEnd?: IOnTTSEnd,
|
||||||
onTextReplace?: IOnTextReplace,
|
onTextReplace?: IOnTextReplace,
|
||||||
|
onAgentLog?: IOnAgentLog,
|
||||||
) => {
|
) => {
|
||||||
if (!response.ok)
|
if (!response.ok)
|
||||||
throw new Error('Network response was not ok')
|
throw new Error('Network response was not ok')
|
||||||
@ -229,6 +233,9 @@ const handleStream = (
|
|||||||
else if (bufferObj.event === 'text_replace') {
|
else if (bufferObj.event === 'text_replace') {
|
||||||
onTextReplace?.(bufferObj as TextReplaceResponse)
|
onTextReplace?.(bufferObj as TextReplaceResponse)
|
||||||
}
|
}
|
||||||
|
else if (bufferObj.event === 'agent_log') {
|
||||||
|
onAgentLog?.(bufferObj as AgentLogResponse)
|
||||||
|
}
|
||||||
else if (bufferObj.event === 'tts_message') {
|
else if (bufferObj.event === 'tts_message') {
|
||||||
onTTSChunk?.(bufferObj.message_id, bufferObj.audio, bufferObj.audio_type)
|
onTTSChunk?.(bufferObj.message_id, bufferObj.audio, bufferObj.audio_type)
|
||||||
}
|
}
|
||||||
@ -322,6 +329,7 @@ export const ssePost = (
|
|||||||
onTTSChunk,
|
onTTSChunk,
|
||||||
onTTSEnd,
|
onTTSEnd,
|
||||||
onTextReplace,
|
onTextReplace,
|
||||||
|
onAgentLog,
|
||||||
onError,
|
onError,
|
||||||
getAbortController,
|
getAbortController,
|
||||||
} = otherOptions
|
} = otherOptions
|
||||||
@ -392,7 +400,7 @@ export const ssePost = (
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
onData?.(str, isFirstMessage, moreInfo)
|
onData?.(str, isFirstMessage, moreInfo)
|
||||||
}, onCompleted, onThought, onMessageEnd, onMessageReplace, onFile, onWorkflowStarted, onWorkflowFinished, onNodeStarted, onNodeFinished, onIterationStart, onIterationNext, onIterationFinish, onNodeRetry, onParallelBranchStarted, onParallelBranchFinished, onTextChunk, onTTSChunk, onTTSEnd, onTextReplace)
|
}, onCompleted, onThought, onMessageEnd, onMessageReplace, onFile, onWorkflowStarted, onWorkflowFinished, onNodeStarted, onNodeFinished, onIterationStart, onIterationNext, onIterationFinish, onNodeRetry, onParallelBranchStarted, onParallelBranchFinished, onTextChunk, onTTSChunk, onTTSEnd, onTextReplace, onAgentLog)
|
||||||
}).catch((e) => {
|
}).catch((e) => {
|
||||||
if (e.toString() !== 'AbortError: The user aborted a request.' && !e.toString().errorMessage.includes('TypeError: Cannot assign to read only property'))
|
if (e.toString() !== 'AbortError: The user aborted a request.' && !e.toString().errorMessage.includes('TypeError: Cannot assign to read only property'))
|
||||||
Toast.notify({ type: 'error', message: e })
|
Toast.notify({ type: 'error', message: e })
|
||||||
|
|||||||
@ -6,6 +6,7 @@ import type { ErrorHandleTypeEnum } from '@/app/components/workflow/nodes/_base/
|
|||||||
export type AgentLogItem = {
|
export type AgentLogItem = {
|
||||||
node_execution_id: string,
|
node_execution_id: string,
|
||||||
id: string,
|
id: string,
|
||||||
|
node_id: string,
|
||||||
parent_id?: string,
|
parent_id?: string,
|
||||||
label: string,
|
label: string,
|
||||||
data: object, // debug data
|
data: object, // debug data
|
||||||
@ -14,6 +15,7 @@ export type AgentLogItem = {
|
|||||||
metadata?: {
|
metadata?: {
|
||||||
elapsed_time?: number
|
elapsed_time?: number
|
||||||
provider?: string
|
provider?: string
|
||||||
|
icon?: string
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -51,6 +53,10 @@ export type NodeTracing = {
|
|||||||
iteration_duration_map?: IterationDurationMap
|
iteration_duration_map?: IterationDurationMap
|
||||||
error_strategy?: ErrorHandleTypeEnum
|
error_strategy?: ErrorHandleTypeEnum
|
||||||
agent_log?: AgentLogItem[]
|
agent_log?: AgentLogItem[]
|
||||||
|
tool_info?: {
|
||||||
|
agent_strategy?: string
|
||||||
|
icon?: string
|
||||||
|
}
|
||||||
}
|
}
|
||||||
metadata: {
|
metadata: {
|
||||||
iterator_length: number
|
iterator_length: number
|
||||||
@ -230,6 +236,12 @@ export type TextReplaceResponse = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type AgentLogResponse = {
|
||||||
|
task_id: string
|
||||||
|
event: string
|
||||||
|
data: AgentLogItemWithChildren
|
||||||
|
}
|
||||||
|
|
||||||
export type WorkflowRunHistory = {
|
export type WorkflowRunHistory = {
|
||||||
id: string
|
id: string
|
||||||
sequence_number: number
|
sequence_number: number
|
||||||
|
|||||||
Reference in New Issue
Block a user