add debug run schedule node

This commit is contained in:
hjlarry 2025-09-28 16:37:15 +08:00
parent 6795015d00
commit 2ff4af8ce3
8 changed files with 170 additions and 12 deletions

View File

@ -1151,3 +1151,66 @@ class DraftWorkflowTriggerRunApi(Resource):
}
), 500
@console_ns.route("/apps/<uuid:app_id>/workflows/draft/trigger/schedule/run")
class DraftWorkflowTriggerScheduleRunApi(Resource):
"""
Full workflow debug when the start node is a schedule trigger
Path: /apps/<uuid:app_id>/workflows/draft/trigger/schedule/run
"""
@api.doc("draft_workflow_trigger_schedule_run")
@api.doc(description="Full workflow debug when the start node is a schedule trigger")
@api.doc(params={"app_id": "Application ID"})
@api.expect(
api.model(
"DraftWorkflowTriggerScheduleRunRequest",
{
"node_id": fields.String(required=True, description="Node ID"),
}
)
)
@api.response(200, "Workflow executed successfully")
@api.response(403, "Permission denied")
@api.response(500, "Internal server error")
@setup_required
@login_required
@account_initialization_required
@get_app_model(mode=[AppMode.WORKFLOW])
def post(self, app_model: App):
"""
Full workflow debug when the start node is a schedule trigger
"""
if not isinstance(current_user, Account) or not current_user.has_edit_permission:
raise Forbidden()
parser = reqparse.RequestParser()
parser.add_argument("node_id", type=str, required=True, location="json", nullable=False)
args = parser.parse_args()
node_id = args["node_id"]
workflow_args = {
"inputs": {},
"query": "",
"files": [],
}
try:
response = AppGenerateService.generate(
app_model=app_model,
user=current_user,
args=workflow_args,
invoke_from=InvokeFrom.DEBUGGER,
streaming=True,
root_node_id=node_id
)
return helper.compact_generate_response(response)
except InvokeRateLimitError as ex:
raise InvokeRateLimitHttpError(ex.description)
except Exception:
logger.exception("Error running draft workflow trigger schedule run")
return jsonable_encoder(
{
"status": "error",
}
), 500

View File

@ -1,6 +1,6 @@
import uuid
from collections.abc import Generator, Mapping
from typing import Any, Union
from typing import Any, Optional, Union
from openai._exceptions import RateLimitError
@ -32,6 +32,7 @@ class AppGenerateService:
args: Mapping[str, Any],
invoke_from: InvokeFrom,
streaming: bool = True,
root_node_id: Optional[str] = None,
):
"""
App Content Generate
@ -115,6 +116,7 @@ class AppGenerateService:
args=args,
invoke_from=invoke_from,
streaming=streaming,
root_node_id=root_node_id,
call_depth=0,
),
),

View File

@ -66,6 +66,7 @@ const WorkflowMain = ({
handleStartWorkflowRun,
handleWorkflowStartRunInChatflow,
handleWorkflowStartRunInWorkflow,
handleWorkflowTriggerScheduleRunInWorkflow,
} = useWorkflowStartRun()
const availableNodesMetaData = useAvailableNodesMetaData()
const { getWorkflowRunAndTraceUrl } = useGetRunAndTraceUrl()
@ -108,6 +109,7 @@ const WorkflowMain = ({
handleStartWorkflowRun,
handleWorkflowStartRunInChatflow,
handleWorkflowStartRunInWorkflow,
handleWorkflowTriggerScheduleRunInWorkflow,
availableNodesMetaData,
getWorkflowRunAndTraceUrl,
exportCheck,
@ -141,6 +143,7 @@ const WorkflowMain = ({
handleStartWorkflowRun,
handleWorkflowStartRunInChatflow,
handleWorkflowStartRunInWorkflow,
handleWorkflowTriggerScheduleRunInWorkflow,
availableNodesMetaData,
getWorkflowRunAndTraceUrl,
exportCheck,

View File

@ -23,6 +23,13 @@ import { useInvalidAllLastRun } from '@/service/use-workflow'
import { useSetWorkflowVarsWithValue } from '../../workflow/hooks/use-fetch-workflow-inspect-vars'
import { useConfigsMap } from './use-configs-map'
type HandleRunMode = 'default' | 'schedule'
type HandleRunOptions = {
mode?: HandleRunMode
scheduleNodeId?: string
}
export const useWorkflowRun = () => {
const store = useStoreApi()
const workflowStore = useWorkflowStore()
@ -111,7 +118,10 @@ export const useWorkflowRun = () => {
const handleRun = useCallback(async (
params: any,
callback?: IOtherOptions,
options?: HandleRunOptions,
) => {
const runMode: HandleRunMode = options?.mode ?? 'default'
const resolvedParams = params ?? {}
const {
getNodes,
setNodes,
@ -153,11 +163,31 @@ export const useWorkflowRun = () => {
const isInWorkflowDebug = appDetail?.mode === 'workflow'
let url = ''
if (appDetail?.mode === 'advanced-chat')
if (runMode === 'schedule') {
if (!appDetail?.id) {
console.error('handleRun: missing app id for schedule trigger run')
return
}
url = `/apps/${appDetail.id}/workflows/draft/trigger/schedule/run`
}
else if (appDetail?.mode === 'advanced-chat') {
url = `/apps/${appDetail.id}/advanced-chat/workflows/draft/run`
if (isInWorkflowDebug)
}
else if (isInWorkflowDebug && appDetail?.id) {
url = `/apps/${appDetail.id}/workflows/draft/run`
}
const requestBody = runMode === 'schedule'
? { node_id: options?.scheduleNodeId }
: resolvedParams
if (!url)
return
if (runMode === 'schedule' && !options?.scheduleNodeId) {
console.error('handleRun: schedule trigger run requires node id')
return
}
const {
setWorkflowRunningData,
@ -172,22 +202,22 @@ export const useWorkflowRun = () => {
let ttsUrl = ''
let ttsIsPublic = false
if (params.token) {
if (resolvedParams.token) {
ttsUrl = '/text-to-audio'
ttsIsPublic = true
}
else if (params.appId) {
else if (resolvedParams.appId) {
if (pathname.search('explore/installed') > -1)
ttsUrl = `/installed-apps/${params.appId}/text-to-audio`
ttsUrl = `/installed-apps/${resolvedParams.appId}/text-to-audio`
else
ttsUrl = `/apps/${params.appId}/text-to-audio`
ttsUrl = `/apps/${resolvedParams.appId}/text-to-audio`
}
const player = AudioPlayerManager.getInstance().getAudioPlayer(ttsUrl, ttsIsPublic, uuidV4(), 'none', 'none', noop)
ssePost(
url,
{
body: params,
body: requestBody,
},
{
onWorkflowStarted: (params) => {

View File

@ -61,6 +61,50 @@ export const useWorkflowStartRun = () => {
}
}, [store, workflowStore, featuresStore, handleCancelDebugAndPreviewPanel, handleRun, doSyncWorkflowDraft])
const handleWorkflowTriggerScheduleRunInWorkflow = useCallback(async (nodeId?: string) => {
if (!nodeId)
return
const {
workflowRunningData,
showDebugAndPreviewPanel,
setShowDebugAndPreviewPanel,
setShowInputsPanel,
setShowEnvPanel,
} = workflowStore.getState()
if (workflowRunningData?.result.status === WorkflowRunningStatus.Running)
return
const { getNodes } = store.getState()
const nodes = getNodes()
const scheduleNode = nodes.find(node => node.id === nodeId && node.data.type === BlockEnum.TriggerSchedule)
if (!scheduleNode) {
console.warn('handleWorkflowTriggerScheduleRunInWorkflow: schedule node not found', nodeId)
return
}
setShowEnvPanel(false)
if (showDebugAndPreviewPanel) {
handleCancelDebugAndPreviewPanel()
return
}
await doSyncWorkflowDraft()
handleRun(
{},
undefined,
{
mode: 'schedule',
scheduleNodeId: nodeId,
},
)
setShowDebugAndPreviewPanel(true)
setShowInputsPanel(false)
}, [store, workflowStore, handleCancelDebugAndPreviewPanel, handleRun, doSyncWorkflowDraft])
const handleWorkflowStartRunInChatflow = useCallback(async () => {
const {
showDebugAndPreviewPanel,
@ -92,5 +136,6 @@ export const useWorkflowStartRun = () => {
handleStartWorkflowRun,
handleWorkflowStartRunInWorkflow,
handleWorkflowStartRunInChatflow,
handleWorkflowTriggerScheduleRunInWorkflow,
}
}

View File

@ -20,7 +20,10 @@ const RunMode = ({
text,
}: RunModeProps) => {
const { t } = useTranslation()
const { handleWorkflowStartRunInWorkflow } = useWorkflowStartRun()
const {
handleWorkflowStartRunInWorkflow,
handleWorkflowTriggerScheduleRunInWorkflow,
} = useWorkflowStartRun()
const { handleStopRun } = useWorkflowRun()
const { validateBeforeRun } = useWorkflowRunValidation()
const workflowRunningData = useStore(s => s.workflowRunningData)
@ -53,11 +56,18 @@ const RunMode = ({
if (option.type === 'user_input') {
handleWorkflowStartRunInWorkflow()
}
else if (option.type === 'schedule') {
handleWorkflowTriggerScheduleRunInWorkflow(option.nodeId)
}
else {
// Placeholder for trigger-specific execution logic for schedule, webhook, plugin types
console.log('TODO: Handle trigger execution for type:', option.type, 'nodeId:', option.nodeId)
}
}, [validateBeforeRun, handleWorkflowStartRunInWorkflow])
}, [
validateBeforeRun,
handleWorkflowStartRunInWorkflow,
handleWorkflowTriggerScheduleRunInWorkflow,
])
const { eventEmitter } = useEventEmitterContextContext()
eventEmitter?.useSubscription((v: any) => {

View File

@ -40,11 +40,12 @@ export type CommonHooksFnMap = {
handleBackupDraft: () => void
handleLoadBackupDraft: () => void
handleRestoreFromPublishedWorkflow: (...args: any[]) => void
handleRun: (params: any, callback?: IOtherOptions,) => void
handleRun: (params: any, callback?: IOtherOptions, options?: any) => void
handleStopRun: (...args: any[]) => void
handleStartWorkflowRun: () => void
handleWorkflowStartRunInWorkflow: () => void
handleWorkflowStartRunInChatflow: () => void
handleWorkflowTriggerScheduleRunInWorkflow: (nodeId?: string) => void
availableNodesMetaData?: AvailableNodesMetaData
getWorkflowRunAndTraceUrl: (runId?: string) => { runUrl: string; traceUrl: string }
exportCheck?: () => Promise<void>
@ -87,6 +88,7 @@ export const createHooksStore = ({
handleStartWorkflowRun = noop,
handleWorkflowStartRunInWorkflow = noop,
handleWorkflowStartRunInChatflow = noop,
handleWorkflowTriggerScheduleRunInWorkflow = noop,
availableNodesMetaData = {
nodes: [],
},
@ -125,6 +127,7 @@ export const createHooksStore = ({
handleStartWorkflowRun,
handleWorkflowStartRunInWorkflow,
handleWorkflowStartRunInChatflow,
handleWorkflowTriggerScheduleRunInWorkflow,
availableNodesMetaData,
getWorkflowRunAndTraceUrl,
exportCheck,

View File

@ -4,10 +4,12 @@ export const useWorkflowStartRun = () => {
const handleStartWorkflowRun = useHooksStore(s => s.handleStartWorkflowRun)
const handleWorkflowStartRunInWorkflow = useHooksStore(s => s.handleWorkflowStartRunInWorkflow)
const handleWorkflowStartRunInChatflow = useHooksStore(s => s.handleWorkflowStartRunInChatflow)
const handleWorkflowTriggerScheduleRunInWorkflow = useHooksStore(s => s.handleWorkflowTriggerScheduleRunInWorkflow)
return {
handleStartWorkflowRun,
handleWorkflowStartRunInWorkflow,
handleWorkflowStartRunInChatflow,
handleWorkflowTriggerScheduleRunInWorkflow,
}
}