Skip to content
Toggle navigation
P
Projects
G
Groups
S
Snippets
Help
赵月辉
/
fastgpt-migrated
This project
Loading...
Sign in
Toggle navigation
Go to a project
Project
Repository
Issues
0
Merge Requests
0
Pipelines
Wiki
Snippets
Members
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Unverified
Commit
85ea1174
authored
Sep 04, 2025
by
Archer
Committed by
GitHub
Sep 04, 2025
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Fix workflow (#5592)
* fix: fileselector default * fix: workflow run process
parent
9be1e591
Hide whitespace changes
Inline
Side-by-side
Showing
13 changed files
with
229 additions
and
191 deletions
+229
-191
document/content/docs/upgrading/4-12/4123.mdx
+2
-0
document/data/doc-last-modified.json
+1
-1
packages/global/core/app/constants.ts
+1
-1
packages/global/core/workflow/template/system/interactive/type.d.ts
+1
-0
packages/service/core/workflow/dispatch/index.ts
+116
-47
packages/service/core/workflow/dispatch/type.d.ts
+15
-5
projects/app/src/components/core/app/FileSelect.tsx
+1
-1
projects/app/src/global/core/workflow/api.d.ts
+3
-6
projects/app/src/pageComponents/app/detail/SimpleApp/EditForm.tsx
+1
-2
projects/app/src/pageComponents/app/detail/SimpleApp/index.tsx
+8
-1
projects/app/src/pageComponents/app/detail/WorkflowComponents/Flow/nodes/render/RenderDebug/NodeDebugResponse.tsx
+6
-8
projects/app/src/pageComponents/app/detail/WorkflowComponents/context/index.tsx
+45
-89
projects/app/src/pages/api/core/workflow/debug.ts
+29
-30
No files found.
document/content/docs/upgrading/4-12/4123.mdx
View file @
85ea1174
...
...
@@ -14,5 +14,7 @@ description: 'FastGPT V4.12.3 更新说明'
## 🐛 修复
1. 单团队模式下,如果用户离开,则无法重新进入团队。
2. 工作流文件上传默认打开,但输入侧未添加文件输出。
3. 连续用户选择,分支无法正常运行。
## 🔨 工具更新
document/data/doc-last-modified.json
View file @
85ea1174
...
...
@@ -105,7 +105,7 @@
"document/content/docs/upgrading/4-12/4120.mdx"
:
"2025-08-12T22:45:19+08:00"
,
"document/content/docs/upgrading/4-12/4121.mdx"
:
"2025-08-15T22:53:06+08:00"
,
"document/content/docs/upgrading/4-12/4122.mdx"
:
"2025-08-27T00:31:33+08:00"
,
"document/content/docs/upgrading/4-12/4123.mdx"
:
"2025-0
8-29T01:24:19
+08:00"
,
"document/content/docs/upgrading/4-12/4123.mdx"
:
"2025-0
9-04T13:48:03
+08:00"
,
"document/content/docs/upgrading/4-8/40.mdx"
:
"2025-08-02T19:38:37+08:00"
,
"document/content/docs/upgrading/4-8/41.mdx"
:
"2025-08-02T19:38:37+08:00"
,
"document/content/docs/upgrading/4-8/42.mdx"
:
"2025-08-02T19:38:37+08:00"
,
...
...
packages/global/core/app/constants.ts
View file @
85ea1174
...
...
@@ -45,7 +45,7 @@ export const defaultChatInputGuideConfig = {
};
export
const
defaultAppSelectFileConfig
:
AppFileSelectConfigType
=
{
canSelectFile
:
tru
e
,
canSelectFile
:
fals
e
,
canSelectImg
:
false
,
maxFiles
:
10
};
...
...
packages/global/core/workflow/template/system/interactive/type.d.ts
View file @
85ea1174
...
...
@@ -8,6 +8,7 @@ type InteractiveBasicType = {
entryNodeIds
:
string
[];
memoryEdges
:
RuntimeEdgeItemType
[];
nodeOutputs
:
NodeOutputItemType
[];
skipNodeQueue
?:
{
id
:
string
;
skippedNodeIdList
:
string
[]
}[];
// 需要记录目前在 queue 里的节点
toolParams
?:
{
entryNodeIds
:
string
[];
// 记录工具中,交互节点的 Id,而不是起始工作流的入口
memoryMessages
:
ChatCompletionMessageParam
[];
// 这轮工具中,产生的新的 messages
...
...
packages/service/core/workflow/dispatch/index.ts
View file @
85ea1174
...
...
@@ -42,7 +42,7 @@ import type { RuntimeEdgeItemType } from '@fastgpt/global/core/workflow/type/edg
import
type
{
ChatNodeUsageType
}
from
'@fastgpt/global/support/wallet/bill/type'
;
import
{
addLog
}
from
'../../../common/system/log'
;
import
{
surrenderProcess
}
from
'../../../common/system/tools'
;
import
type
{
DispatchFlowResponse
}
from
'./type'
;
import
type
{
DispatchFlowResponse
,
WorkflowDebugResponse
}
from
'./type'
;
import
{
removeSystemVariable
,
rewriteRuntimeWorkFlow
}
from
'./utils'
;
import
{
getHandleId
}
from
'@fastgpt/global/core/workflow/utils'
;
import
{
callbackMap
}
from
'./constants'
;
...
...
@@ -50,6 +50,7 @@ import { callbackMap } from './constants';
type
Props
=
Omit
<
ChatDispatchProps
,
'workflowDispatchDeep'
>
&
{
runtimeNodes
:
RuntimeNodeItemType
[];
runtimeEdges
:
RuntimeEdgeItemType
[];
defaultSkipNodeQueue
?:
WorkflowDebugResponse
[
'skipNodeQueue'
];
};
type
NodeResponseType
=
DispatchNodeResultType
<
{
[
key
:
string
]:
any
;
...
...
@@ -100,6 +101,7 @@ export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowRespons
// Init some props
return
runWorkflow
({
...
data
,
defaultSkipNodeQueue
:
data
.
lastInteractive
?.
skipNodeQueue
||
data
.
defaultSkipNodeQueue
,
variables
:
defaultVariables
,
workflowDispatchDeep
:
0
}).
finally
(()
=>
{
...
...
@@ -112,12 +114,14 @@ export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowRespons
type
RunWorkflowProps
=
ChatDispatchProps
&
{
runtimeNodes
:
RuntimeNodeItemType
[];
runtimeEdges
:
RuntimeEdgeItemType
[];
defaultSkipNodeQueue
?:
WorkflowDebugResponse
[
'skipNodeQueue'
];
};
export
const
runWorkflow
=
async
(
data
:
RunWorkflowProps
):
Promise
<
DispatchFlowResponse
>
=>
{
let
{
res
,
runtimeNodes
=
[],
runtimeEdges
=
[],
defaultSkipNodeQueue
,
histories
=
[],
variables
=
{},
externalProvider
,
...
...
@@ -135,9 +139,10 @@ export const runWorkflow = async (data: RunWorkflowProps): Promise<DispatchFlowR
flowResponses
:
[],
flowUsages
:
[],
debugResponse
:
{
finishedNodes
:
[],
finishedEdges
:
[],
nextStepRunNodes
:
[]
memoryEdges
:
[],
entryNodeIds
:
[],
nodeResponses
:
{},
skipNodeQueue
:
[]
},
[
DispatchNodeResponseKeyEnum
.
runTimes
]:
1
,
[
DispatchNodeResponseKeyEnum
.
assistantResponses
]:
[],
...
...
@@ -151,6 +156,8 @@ export const runWorkflow = async (data: RunWorkflowProps): Promise<DispatchFlowR
await
rewriteRuntimeWorkFlow
({
nodes
:
runtimeNodes
,
edges
:
runtimeEdges
,
lang
:
data
.
lang
});
const
isDebugMode
=
data
.
mode
===
'debug'
;
/*
工作流队列控制
特点:
...
...
@@ -176,7 +183,6 @@ export const runWorkflow = async (data: RunWorkflowProps): Promise<DispatchFlowR
chatAssistantResponse
:
AIChatItemValueItemType
[]
=
[];
// The value will be returned to the user
chatNodeUsages
:
ChatNodeUsageType
[]
=
[];
toolRunResponse
:
ToolRunResponseItemType
;
// Run with tool mode. Result will response to tool node.
debugNextStepRunNodes
:
RuntimeNodeItemType
[]
=
[];
// 记录 Debug 模式下,下一个阶段需要执行的节点。
// 记录交互节点,交互节点需要在工作流完全结束后再进行计算
nodeInteractiveResponse
:
|
{
...
...
@@ -186,22 +192,38 @@ export const runWorkflow = async (data: RunWorkflowProps): Promise<DispatchFlowR
|
undefined
;
system_memories
:
Record
<
string
,
any
>
=
{};
// Workflow node memories
// Debug
debugNextStepRunNodes
:
RuntimeNodeItemType
[]
=
[];
// 记录 Debug 模式下,下一个阶段需要执行的节点。
debugNodeResponses
:
WorkflowDebugResponse
[
'nodeResponses'
]
=
{};
// Queue variables
private
activeRunQueue
=
new
Set
<
string
>
();
private
skipNodeQueue
:
{
node
:
RuntimeNodeItemType
;
skippedNodeIdList
:
Set
<
string
>
}[]
=
[];
private
skipNodeQueue
=
new
Map
<
string
,
{
node
:
RuntimeNodeItemType
;
skippedNodeIdList
:
Set
<
string
>
}
>
();
private
runningNodeCount
=
0
;
private
maxConcurrency
:
number
;
private
resolve
:
(
e
:
WorkflowQueue
)
=>
void
;
constructor
({
maxConcurrency
=
10
,
defaultSkipNodeQueue
,
resolve
}:
{
maxConcurrency
?:
number
;
defaultSkipNodeQueue
?:
WorkflowDebugResponse
[
'skipNodeQueue'
];
resolve
:
(
e
:
WorkflowQueue
)
=>
void
;
})
{
this
.
maxConcurrency
=
maxConcurrency
;
this
.
resolve
=
resolve
;
// Init skip node queue
defaultSkipNodeQueue
?.
forEach
(({
id
,
skippedNodeIdList
})
=>
{
const
node
=
this
.
runtimeNodesMap
.
get
(
id
);
if
(
!
node
)
return
;
this
.
addSkipNode
(
node
,
new
Set
(
skippedNodeIdList
));
});
}
// Add active node to queue (if already in the queue, it will not be added again)
...
...
@@ -217,7 +239,18 @@ export const runWorkflow = async (data: RunWorkflowProps): Promise<DispatchFlowR
private
processActiveNode
()
{
// Finish
if
(
this
.
activeRunQueue
.
size
===
0
&&
this
.
runningNodeCount
===
0
)
{
if
(
this
.
skipNodeQueue
.
length
>
0
&&
!
this
.
nodeInteractiveResponse
)
{
if
(
isDebugMode
)
{
// 没有下一个激活节点,说明debug 进入了一个“即将结束”状态。可以开始处理 skip 节点
if
(
this
.
debugNextStepRunNodes
.
length
===
0
&&
this
.
skipNodeQueue
.
size
>
0
)
{
this
.
processSkipNodes
();
}
else
{
this
.
resolve
(
this
);
}
return
;
}
// 如果没有交互响应,则开始处理 skip(交互响应的 skip 需要留给后续处理)
if
(
this
.
skipNodeQueue
.
size
>
0
&&
!
this
.
nodeInteractiveResponse
)
{
this
.
processSkipNodes
();
}
else
{
this
.
resolve
(
this
);
...
...
@@ -251,11 +284,19 @@ export const runWorkflow = async (data: RunWorkflowProps): Promise<DispatchFlowR
}
private
addSkipNode
(
node
:
RuntimeNodeItemType
,
skippedNodeIdList
:
Set
<
string
>
)
{
this
.
skipNodeQueue
.
push
({
node
,
skippedNodeIdList
});
// 保证一个node 只在queue里记录一次
const
skipNodeSkippedNodeIdList
=
this
.
skipNodeQueue
.
get
(
node
.
nodeId
)?.
skippedNodeIdList
||
new
Set
<
string
>
();
const
concatSkippedNodeIdList
=
new
Set
([...
skippedNodeIdList
,
...
skipNodeSkippedNodeIdList
]);
this
.
skipNodeQueue
.
set
(
node
.
nodeId
,
{
node
,
skippedNodeIdList
:
concatSkippedNodeIdList
});
}
private
processSkipNodes
()
{
const
skipItem
=
this
.
skipNodeQueue
.
shift
();
// 取一个 node,并且从队列里删除
const
skipItem
=
this
.
skipNodeQueue
.
values
().
next
().
value
;
if
(
skipItem
)
{
this
.
skipNodeQueue
.
delete
(
skipItem
.
node
.
nodeId
);
this
.
checkNodeCanRun
(
skipItem
.
node
,
skipItem
.
skippedNodeIdList
).
finally
(()
=>
{
this
.
processActiveNode
();
});
...
...
@@ -351,7 +392,7 @@ export const runWorkflow = async (data: RunWorkflowProps): Promise<DispatchFlowR
runtimeNodes
,
runtimeEdges
,
params
,
mode
:
data
.
mode
===
'debug'
?
'test'
:
data
.
mode
mode
:
isDebugMode
?
'test'
:
data
.
mode
};
// run module
...
...
@@ -620,18 +661,6 @@ export const runWorkflow = async (data: RunWorkflowProps): Promise<DispatchFlowR
const
nextStepActiveNodes
=
Array
.
from
(
nextStepActiveNodesMap
.
values
());
const
nextStepSkipNodes
=
Array
.
from
(
nextStepSkipNodesMap
.
values
());
if
(
data
.
mode
===
'debug'
)
{
this
.
debugNextStepRunNodes
=
this
.
debugNextStepRunNodes
.
concat
(
data
.
lastInteractive
?
nextStepActiveNodes
:
[...
nextStepActiveNodes
,
...
nextStepSkipNodes
]
);
return
{
nextStepActiveNodes
:
[],
nextStepSkipNodes
:
[]
};
}
return
{
nextStepActiveNodes
,
nextStepSkipNodes
...
...
@@ -690,8 +719,31 @@ export const runWorkflow = async (data: RunWorkflowProps): Promise<DispatchFlowR
return
this
.
nodeRunWithSkip
(
node
);
}
})();
if
(
!
nodeRunResult
)
return
;
// Store debug data
if
(
isDebugMode
)
{
if
(
status
===
'run'
)
{
this
.
debugNodeResponses
[
node
.
nodeId
]
=
{
nodeId
:
node
.
nodeId
,
type
:
'run'
,
interactiveResponse
:
nodeRunResult
.
result
[
DispatchNodeResponseKeyEnum
.
interactive
],
response
:
nodeRunResult
.
result
[
DispatchNodeResponseKeyEnum
.
nodeResponse
]
};
}
else
if
(
status
===
'skip'
)
{
this
.
debugNodeResponses
[
node
.
nodeId
]
=
{
nodeId
:
node
.
nodeId
,
type
:
'skip'
,
response
:
nodeRunResult
.
result
[
DispatchNodeResponseKeyEnum
.
nodeResponse
]
};
}
}
// 如果一个节点 active 运行了,则需要把它从 skip queue 里删除
if
(
status
===
'run'
)
{
this
.
skipNodeQueue
.
delete
(
node
.
nodeId
);
}
/*
特殊情况:
通过 skipEdges 可以判断是运行了分支节点。
...
...
@@ -704,12 +756,20 @@ export const runWorkflow = async (data: RunWorkflowProps): Promise<DispatchFlowR
skippedNodeIdList
.
add
(
node
.
nodeId
);
}
// Update the node output at the end of the run and get the next nodes
const
{
nextStepActiveNodes
,
nextStepSkipNodes
}
=
nodeOutput
(
nodeRunResult
.
node
,
nodeRunResult
.
result
);
nextStepSkipNodes
.
forEach
((
node
)
=>
{
this
.
addSkipNode
(
node
,
skippedNodeIdList
);
});
// In the current version, only one interactive node is allowed at the same time
const
interactiveResponse
=
nodeRunResult
.
result
?.
[
DispatchNodeResponseKeyEnum
.
interactive
];
const
interactiveResponse
=
nodeRunResult
.
result
[
DispatchNodeResponseKeyEnum
.
interactive
];
if
(
interactiveResponse
)
{
pushStore
(
nodeRunResult
.
result
);
if
(
data
.
mode
===
'debug'
)
{
if
(
isDebugMode
)
{
this
.
debugNextStepRunNodes
=
this
.
debugNextStepRunNodes
.
concat
([
nodeRunResult
.
node
]);
}
...
...
@@ -718,22 +778,14 @@ export const runWorkflow = async (data: RunWorkflowProps): Promise<DispatchFlowR
interactiveResponse
};
return
;
}
else
if
(
isDebugMode
)
{
// Debug 模式下一步时候,会自己增加 activeNode
this
.
debugNextStepRunNodes
=
this
.
debugNextStepRunNodes
.
concat
(
nextStepActiveNodes
);
}
else
{
nextStepActiveNodes
.
forEach
((
node
)
=>
{
this
.
addActiveNode
(
node
.
nodeId
);
});
}
// Update the node output at the end of the run and get the next nodes
const
{
nextStepActiveNodes
,
nextStepSkipNodes
}
=
nodeOutput
(
nodeRunResult
.
node
,
nodeRunResult
.
result
);
nextStepSkipNodes
.
forEach
((
node
)
=>
{
this
.
addSkipNode
(
node
,
skippedNodeIdList
);
});
// Run next nodes
nextStepActiveNodes
.
forEach
((
node
)
=>
{
this
.
addActiveNode
(
node
.
nodeId
);
});
}
/* Have interactive result, computed edges and node outputs */
...
...
@@ -760,6 +812,10 @@ export const runWorkflow = async (data: RunWorkflowProps): Promise<DispatchFlowR
const
interactiveResult
:
WorkflowInteractiveResponseType
=
{
...
interactiveResponse
,
skipNodeQueue
:
Array
.
from
(
this
.
skipNodeQueue
.
values
()).
map
((
item
)
=>
({
id
:
item
.
node
.
nodeId
,
skippedNodeIdList
:
Array
.
from
(
item
.
skippedNodeIdList
)
})),
entryNodeIds
,
memoryEdges
:
runtimeEdges
.
map
((
edge
)
=>
({
...
edge
,
...
...
@@ -781,6 +837,22 @@ export const runWorkflow = async (data: RunWorkflowProps): Promise<DispatchFlowR
interactive
:
interactiveResult
};
}
getDebugResponse
():
WorkflowDebugResponse
{
const
entryNodeIds
=
this
.
debugNextStepRunNodes
.
map
((
item
)
=>
item
.
nodeId
);
return
{
memoryEdges
:
runtimeEdges
.
map
((
edge
)
=>
({
...
edge
,
status
:
entryNodeIds
.
includes
(
edge
.
target
)
?
'active'
:
edge
.
status
})),
entryNodeIds
,
nodeResponses
:
this
.
debugNodeResponses
,
skipNodeQueue
:
Array
.
from
(
this
.
skipNodeQueue
.
values
()).
map
((
item
)
=>
({
id
:
item
.
node
.
nodeId
,
skippedNodeIdList
:
Array
.
from
(
item
.
skippedNodeIdList
)
}))
};
}
}
// Start process width initInput
...
...
@@ -799,7 +871,8 @@ export const runWorkflow = async (data: RunWorkflowProps): Promise<DispatchFlowR
const
workflowQueue
=
await
new
Promise
<
WorkflowQueue
>
((
resolve
)
=>
{
const
workflowQueue
=
new
WorkflowQueue
({
resolve
resolve
,
defaultSkipNodeQueue
});
entryNodes
.
forEach
((
node
)
=>
{
...
...
@@ -833,11 +906,7 @@ export const runWorkflow = async (data: RunWorkflowProps): Promise<DispatchFlowR
return
{
flowResponses
:
workflowQueue
.
chatResponses
,
flowUsages
:
workflowQueue
.
chatNodeUsages
,
debugResponse
:
{
finishedNodes
:
runtimeNodes
,
finishedEdges
:
runtimeEdges
,
nextStepRunNodes
:
workflowQueue
.
debugNextStepRunNodes
},
debugResponse
:
workflowQueue
.
getDebugResponse
(),
workflowInteractiveResponse
:
interactiveResult
,
[
DispatchNodeResponseKeyEnum
.
runTimes
]:
workflowQueue
.
workflowRunTimes
,
[
DispatchNodeResponseKeyEnum
.
assistantResponses
]:
mergeAssistantResponseAnswerText
(
...
...
packages/service/core/workflow/dispatch/type.d.ts
View file @
85ea1174
...
...
@@ -13,14 +13,24 @@ import type { WorkflowInteractiveResponseType } from '@fastgpt/global/core/workf
import
type
{
RuntimeEdgeItemType
}
from
'@fastgpt/global/core/workflow/type/edge'
;
import
type
{
ChatNodeUsageType
}
from
'@fastgpt/global/support/wallet/bill/type'
;
export
type
WorkflowDebugResponse
=
{
memoryEdges
:
RuntimeEdgeItemType
[];
entryNodeIds
:
string
[];
// Next step entry nodes
nodeResponses
:
Record
<
string
,
{
nodeId
:
string
;
type
:
'skip'
|
'run'
;
response
?:
ChatHistoryItemResType
;
interactiveResponse
?:
InteractiveNodeResponseType
;
}
>
;
skipNodeQueue
?:
{
id
:
string
;
skippedNodeIdList
:
string
[]
}[];
// Cache
};
export
type
DispatchFlowResponse
=
{
flowResponses
:
ChatHistoryItemResType
[];
flowUsages
:
ChatNodeUsageType
[];
debugResponse
:
{
finishedNodes
:
RuntimeNodeItemType
[];
finishedEdges
:
RuntimeEdgeItemType
[];
nextStepRunNodes
:
RuntimeNodeItemType
[];
};
debugResponse
:
WorkflowDebugResponse
;
workflowInteractiveResponse
?:
WorkflowInteractiveResponseType
;
[
DispatchNodeResponseKeyEnum
.
toolResponses
]:
ToolRunResponseItemType
;
[
DispatchNodeResponseKeyEnum
.
assistantResponses
]:
AIChatItemValueItemType
[];
...
...
projects/app/src/components/core/app/FileSelect.tsx
View file @
85ea1174
...
...
@@ -17,7 +17,6 @@ import { useTranslation } from 'next-i18next';
import
type
{
AppFileSelectConfigType
}
from
'@fastgpt/global/core/app/type.d'
;
import
MyModal
from
'@fastgpt/web/components/common/MyModal'
;
import
MySlider
from
'@/components/Slider'
;
import
{
defaultAppSelectFileConfig
}
from
'@fastgpt/global/core/app/constants'
;
import
ChatFunctionTip
from
'./Tip'
;
import
FormLabel
from
'@fastgpt/web/components/common/MyBox/FormLabel'
;
import
{
useMount
}
from
'ahooks'
;
...
...
@@ -25,6 +24,7 @@ import { useSystemStore } from '@/web/common/system/useSystemStore';
import
QuestionTip
from
'@fastgpt/web/components/common/MyTooltip/QuestionTip'
;
import
MyTag
from
'@fastgpt/web/components/common/Tag/index'
;
import
MyDivider
from
'@fastgpt/web/components/common/MyDivider'
;
import
{
defaultAppSelectFileConfig
}
from
'@fastgpt/global/core/app/constants'
;
const
FileSelect
=
({
forbidVision
=
false
,
...
...
projects/app/src/global/core/workflow/api.d.ts
View file @
85ea1174
...
...
@@ -5,21 +5,18 @@ import type { WorkflowInteractiveResponseType } from '@fastgpt/global/core/workf
import
{
StoreNodeItemType
}
from
'@fastgpt/global/core/workflow/type'
;
import
type
{
RuntimeEdgeItemType
}
from
'@fastgpt/global/core/workflow/type/edge'
;
import
{
StoreEdgeItemType
}
from
'@fastgpt/global/core/workflow/type/edge'
;
import
type
{
WorkflowDebugResponse
}
from
'@fastgpt/service/core/workflow/dispatch/type'
;
export
type
PostWorkflowDebugProps
=
{
nodes
:
RuntimeNodeItemType
[];
edges
:
RuntimeEdgeItemType
[];
skipNodeQueue
?:
WorkflowDebugResponse
[
'skipNodeQueue'
];
variables
:
Record
<
string
,
any
>
;
appId
:
string
;
query
?:
UserChatItemValueItemType
[];
history
?:
ChatItemType
[];
};
export
type
PostWorkflowDebugResponse
=
{
finishedNodes
:
RuntimeNodeItemType
[];
finishedEdges
:
RuntimeEdgeItemType
[];
nextStepRunNodes
:
RuntimeNodeItemType
[];
flowResponses
:
ChatHistoryItemResType
[];
workflowInteractiveResponse
?:
WorkflowInteractiveResponseType
;
export
type
PostWorkflowDebugResponse
=
WorkflowDebugResponse
&
{
newVariables
:
Record
<
string
,
any
>
;
};
projects/app/src/pageComponents/app/detail/SimpleApp/EditForm.tsx
View file @
85ea1174
...
...
@@ -31,7 +31,6 @@ import FormLabel from '@fastgpt/web/components/common/MyBox/FormLabel';
import
VariableTip
from
'@/components/common/Textarea/MyTextarea/VariableTip'
;
import
{
getWebLLMModel
}
from
'@/web/common/system/utils'
;
import
ToolSelect
from
'./components/ToolSelect'
;
import
{
useSystemStore
}
from
'@/web/common/system/useSystemStore'
;
import
OptimizerPopover
from
'@/components/common/PromptEditor/OptimizerPopover'
;
const
DatasetSelectModal
=
dynamic
(()
=>
import
(
'@/components/core/app/DatasetSelectModal'
));
...
...
@@ -148,7 +147,7 @@ const EditForm = ({
},
[
appForm
.
aiSettings
.
systemPrompt
,
setAppForm
]
);
console
.
log
(
appForm
.
chatConfig
.
fileSelectConfig
);
return
(
<>
<
Box
>
...
...
projects/app/src/pageComponents/app/detail/SimpleApp/index.tsx
View file @
85ea1174
...
...
@@ -11,6 +11,7 @@ import { type SimpleAppSnapshotType, useSimpleAppSnapshots } from './useSnapshot
import
{
useDebounceEffect
,
useMount
}
from
'ahooks'
;
import
{
v1Workflow2V2
}
from
'@/web/core/workflow/adapt'
;
import
{
getAppConfigByDiff
}
from
'@/web/core/app/diff'
;
import
{
defaultAppSelectFileConfig
}
from
'@fastgpt/global/core/app/constants'
;
const
Edit
=
dynamic
(()
=>
import
(
'./Edit'
));
const
Logs
=
dynamic
(()
=>
import
(
'../Logs/index'
));
...
...
@@ -79,7 +80,13 @@ const SimpleEdit = () => {
if
(
past
.
length
===
0
)
{
const
appForm
=
appWorkflow2Form
({
nodes
:
appDetail
.
modules
,
chatConfig
:
appDetail
.
chatConfig
chatConfig
:
{
...
appDetail
.
chatConfig
,
fileSelectConfig
:
appDetail
.
chatConfig
.
fileSelectConfig
||
{
...
defaultAppSelectFileConfig
,
canSelectFile
:
true
}
}
});
saveSnapshot
({
appForm
,
...
...
projects/app/src/pageComponents/app/detail/WorkflowComponents/Flow/nodes/render/RenderDebug/NodeDebugResponse.tsx
View file @
85ea1174
...
...
@@ -127,11 +127,9 @@ const NodeDebugResponse = ({ nodeId, debugResult }: NodeDebugResponseProps) => {
}
];
const
lastInteractive
=
getLastInteractiveValue
(
mockHistory
);
onNextNodeDebug
({
...
workflowDebugData
,
// Rewrite runtimeEdges
runtimeEdges
:
storeEdges2RuntimeEdges
(
workflowDebugData
.
runtimeEdges
,
lastInteractive
),
runtimeEdges
:
workflowDebugData
.
runtimeEdges
,
query
:
updatedQuery
,
history
:
mockHistory
});
...
...
@@ -189,7 +187,7 @@ const NodeDebugResponse = ({ nodeId, debugResult }: NodeDebugResponseProps) => {
<
Box
fontWeight=
{
'bold'
}
flex=
{
'1'
}
>
{
t
(
'common:core.workflow.debug.Run result'
)
}
</
Box
>
{
workflowDebugData
?.
nextRunNode
s
.
length
!==
0
&&
(
{
workflowDebugData
?.
entryNodeId
s
.
length
!==
0
&&
(
<
PopoverConfirm
Trigger=
{
<
Button
...
...
@@ -209,8 +207,8 @@ const NodeDebugResponse = ({ nodeId, debugResult }: NodeDebugResponseProps) => {
<>
{
(
debugResult
.
status
===
'success'
||
debugResult
.
status
===
'skipped'
)
&&
!
debugResult
.
isExpired
&&
workflowDebugData
?.
nextRunNode
s
&&
workflowDebugData
.
nextRunNode
s
.
length
>
0
&&
(
workflowDebugData
?.
entryNodeId
s
&&
workflowDebugData
.
entryNodeId
s
.
length
>
0
&&
(
<
Button
ml=
{
2
}
size=
{
'sm'
}
...
...
@@ -221,8 +219,8 @@ const NodeDebugResponse = ({ nodeId, debugResult }: NodeDebugResponseProps) => {
{
t
(
'common:next_step'
)
}
</
Button
>
)
}
{
workflowDebugData
?.
nextRunNode
s
&&
workflowDebugData
?.
nextRunNode
s
.
length
===
0
&&
(
{
workflowDebugData
?.
entryNodeId
s
&&
workflowDebugData
?.
entryNodeId
s
.
length
===
0
&&
(
<
Button
ml=
{
2
}
size=
{
'sm'
}
variant=
{
'primary'
}
onClick=
{
onStopNodeDebug
}
>
{
t
(
'common:core.workflow.debug.Done'
)
}
</
Button
>
...
...
projects/app/src/pageComponents/app/detail/WorkflowComponents/context/index.tsx
View file @
85ea1174
...
...
@@ -54,12 +54,12 @@ import { cloneDeep } from 'lodash';
import
{
type
AppVersionSchemaType
}
from
'@fastgpt/global/core/app/version'
;
import
WorkflowInitContextProvider
,
{
WorkflowNodeEdgeContext
}
from
'./workflowInitContext'
;
import
WorkflowEventContextProvider
from
'./workflowEventContext'
;
import
{
getAppConfigByDiff
}
from
'@/web/core/app/diff'
;
import
WorkflowStatusContextProvider
from
'./workflowStatusContext'
;
import
{
type
ChatItemType
,
type
UserChatItemValueItemType
}
from
'@fastgpt/global/core/chat/type'
;
import
{
type
WorkflowInteractiveResponseType
}
from
'@fastgpt/global/core/workflow/template/system/interactive/type'
;
import
{
FlowNodeOutputTypeEnum
}
from
'@fastgpt/global/core/workflow/node/constant'
;
import
{
useChatStore
}
from
'@/web/core/chat/context/useChatStore'
;
import
type
{
WorkflowDebugResponse
}
from
'@fastgpt/service/core/workflow/dispatch/type'
;
/*
Context
...
...
@@ -266,7 +266,9 @@ type WorkflowContextType = {
export
type
DebugDataType
=
{
runtimeNodes
:
RuntimeNodeItemType
[];
runtimeEdges
:
RuntimeEdgeItemType
[];
nextRunNodes
:
RuntimeNodeItemType
[];
entryNodeIds
:
string
[];
skipNodeQueue
?:
WorkflowDebugResponse
[
'skipNodeQueue'
];
variables
:
Record
<
string
,
any
>
;
history
?:
ChatItemType
[];
query
?:
UserChatItemValueItemType
[];
...
...
@@ -686,112 +688,67 @@ const WorkflowContextProvider = ({
}))
);
// 2. Set isEntry field and get entryNodes
// 2. Set isEntry field and get entryNodes
, and set running status
const
runtimeNodes
=
debugData
.
runtimeNodes
.
map
((
item
)
=>
({
...
item
,
isEntry
:
debugData
.
nextRunNodes
.
some
((
node
)
=>
node
.
nodeI
d
===
item
.
nodeId
)
isEntry
:
debugData
.
entryNodeIds
.
some
((
id
)
=>
i
d
===
item
.
nodeId
)
}));
const
entryNodes
=
runtimeNodes
.
filter
((
item
)
=>
item
.
isEntry
);
const
runtimeNodeStatus
:
Record
<
string
,
string
>
=
entryNodes
.
map
((
node
)
=>
{
const
status
=
checkNodeRunStatus
({
node
,
nodesMap
:
new
Map
(
runtimeNodes
.
map
((
item
)
=>
[
item
.
nodeId
,
item
])),
runtimeEdges
:
debugData
?.
runtimeEdges
||
[]
});
return
{
nodeId
:
node
.
nodeId
,
status
};
})
.
reduce
(
(
acc
,
cur
)
=>
({
...
acc
,
[
cur
.
nodeId
]:
cur
.
status
}),
{}
);
// 3. Set entry node status to running
entryNodes
.
forEach
((
node
)
=>
{
if
(
runtimeNodeStatus
[
node
.
nodeId
]
!==
'wait'
)
{
const
entryNodes
=
runtimeNodes
.
filter
((
item
)
=>
{
if
(
item
.
isEntry
)
{
onChangeNode
({
nodeId
:
node
.
nodeId
,
nodeId
:
item
.
nodeId
,
type
:
'attr'
,
key
:
'debugResult'
,
value
:
defaultRunningStatus
});
return
true
;
}
});
try
{
// 4. Run one step
const
{
finishedEdges
,
finishedNodes
,
nextStepRunNodes
,
flowResponses
,
newVariables
,
workflowInteractiveResponse
}
=
await
postWorkflowDebug
({
nodes
:
runtimeNodes
,
edges
:
debugData
.
runtimeEdges
,
variables
:
{
appId
,
cTime
:
formatTime2YMDHMW
(),
...
debugData
.
variables
},
query
:
debugData
.
query
,
// 添加 query 参数
history
:
debugData
.
history
,
appId
});
// 3. Run one step
const
{
memoryEdges
,
entryNodeIds
,
skipNodeQueue
,
nodeResponses
,
newVariables
}
=
await
postWorkflowDebug
({
nodes
:
runtimeNodes
,
edges
:
debugData
.
runtimeEdges
,
skipNodeQueue
:
debugData
.
skipNodeQueue
,
variables
:
{
appId
,
cTime
:
formatTime2YMDHMW
(),
...
debugData
.
variables
},
query
:
debugData
.
query
,
// 添加 query 参数
history
:
debugData
.
history
,
appId
});
//
5
. Store debug result
//
4
. Store debug result
setWorkflowDebugData
({
runtimeNodes
:
finishedNodes
,
// edges need to save status
runtimeEdges
:
finishedEdges
,
nextRunNodes
:
nextStepRunNodes
,
variables
:
newVariables
,
workflowInteractiveResponse
:
workflowInteractiveResponse
runtimeNodes
:
debugData
.
runtimeNodes
,
runtimeEdges
:
memoryEdges
,
entryNodeIds
,
skipNodeQueue
,
variables
:
newVariables
});
//
6
. selected entry node and Update entry node debug result
//
5
. selected entry node and Update entry node debug result
setNodes
((
state
)
=>
state
.
map
((
node
)
=>
{
const
isEntryNode
=
entryNodes
.
some
((
item
)
=>
item
.
nodeId
===
node
.
data
.
nodeId
);
if
(
!
isEntryNode
||
runtimeNodeStatus
[
node
.
data
.
nodeId
]
===
'wait'
)
return
node
;
const
result
=
flowResponses
.
find
((
item
)
=>
item
.
nodeId
===
node
.
data
.
nodeId
);
if
(
runtimeNodeStatus
[
node
.
data
.
nodeId
]
===
'skip'
)
{
return
{
...
node
,
selected
:
isEntryNode
,
data
:
{
...
node
.
data
,
debugResult
:
{
status
:
'skipped'
,
showResult
:
true
,
isExpired
:
false
}
}
};
}
const
result
=
nodeResponses
[
node
.
data
.
nodeId
];
if
(
!
result
)
return
node
;
return
{
...
node
,
selected
:
isEntryNode
,
selected
:
result
.
type
===
'run'
&&
isEntryNode
,
data
:
{
...
node
.
data
,
debugResult
:
{
status
:
'success
'
,
response
:
result
,
status
:
result
.
type
===
'run'
?
'success'
:
'skipped
'
,
response
:
result
.
response
,
showResult
:
true
,
isExpired
:
false
,
workflowInteractiveResponse
:
workflowI
nteractiveResponse
workflowInteractiveResponse
:
result
.
i
nteractiveResponse
}
}
};
...
...
@@ -799,13 +756,9 @@ const WorkflowContextProvider = ({
);
// Check for an empty response(Skip node)
if
(
!
workflowInteractiveResponse
&&
flowResponses
.
length
===
0
&&
nextStepRunNodes
.
length
>
0
)
{
onNextNodeDebug
(
debugData
);
}
// if (!workflowInteractiveResponse && flowResponses.length === 0 && entryNodeIds.length > 0) {
// onNextNodeDebug(debugData);
// }
}
catch
(
error
)
{
entryNodes
.
forEach
((
node
)
=>
{
onChangeNode
({
...
...
@@ -856,7 +809,10 @@ const WorkflowContextProvider = ({
const
data
:
DebugDataType
=
{
runtimeNodes
,
runtimeEdges
,
nextRunNodes
:
runtimeNodes
.
filter
((
node
)
=>
node
.
nodeId
===
entryNodeId
),
entryNodeIds
:
runtimeNodes
.
filter
((
node
)
=>
node
.
nodeId
===
entryNodeId
)
.
map
((
node
)
=>
node
.
nodeId
),
skipNodeQueue
:
[],
variables
,
query
,
history
...
...
projects/app/src/pages/api/core/workflow/debug.ts
View file @
85ea1174
...
...
@@ -21,6 +21,7 @@ async function handler(
const
{
nodes
=
[],
edges
=
[],
skipNodeQueue
,
variables
=
{},
appId
,
query
=
[],
...
...
@@ -47,34 +48,34 @@ async function handler(
// auth balance
const
{
timezone
,
externalProvider
}
=
await
getUserChatInfoAndAuthTeamPoints
(
tmbId
);
const
lastI
nteractive
=
getLastInteractiveValue
(
history
);
const
i
nteractive
=
getLastInteractiveValue
(
history
);
/* start process */
const
{
flowUsages
,
flowResponses
,
debugResponse
,
newVariables
,
workflowInteractiveResponse
}
=
await
dispatchWorkFlow
({
res
,
lang
:
getLocale
(
req
)
,
requestOrigin
:
req
.
headers
.
origin
,
mode
:
'debug'
,
timezone
,
externalProvider
,
uid
:
tmbId
,
runningAppInfo
:
{
id
:
app
.
_i
d
,
teamId
:
app
.
teamId
,
tmbId
:
app
.
tmbId
}
,
runningUserInfo
:
await
getRunningUserInfoByTmbId
(
tmbId
)
,
runtimeNodes
:
nod
es
,
runtimeEdges
:
edges
,
lastI
nteractive
,
variables
,
query
:
query
,
chatConfig
:
defaultApp
.
chatConfig
,
histories
:
history
,
stream
:
false
,
maxRunTimes
:
WORKFLOW_MAX_RUN_TIMES
});
const
{
flowUsages
,
debugResponse
,
newVariables
}
=
await
dispatchWorkFlow
({
res
,
lang
:
getLocale
(
req
)
,
requestOrigin
:
req
.
headers
.
origin
,
mode
:
'debug'
,
timezone
,
externalProvider
,
uid
:
tmbId
,
runningAppInfo
:
{
id
:
app
.
_id
,
teamId
:
app
.
teamI
d
,
tmbId
:
app
.
tmbId
},
runningUserInfo
:
await
getRunningUserInfoByTmbId
(
tmbId
)
,
runtimeNodes
:
nodes
,
runtimeEdges
:
edg
es
,
defaultSkipNodeQueue
:
skipNodeQueue
,
lastInteractive
:
i
nteractive
,
variables
,
query
:
query
,
chatConfig
:
defaultApp
.
chatConfig
,
histories
:
history
,
stream
:
false
,
maxRunTimes
:
WORKFLOW_MAX_RUN_TIMES
});
createChatUsage
({
appName
:
`
${
app
.
name
}
-Debug`
,
...
...
@@ -86,10 +87,8 @@ async function handler(
});
return
{
...
debugResponse
,
newVariables
,
flowResponses
,
workflowInteractiveResponse
...
debugResponse
!
,
newVariables
};
}
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment