This commit is contained in:
Diego Imbert
2026-01-14 20:59:55 +01:00
parent 89ea61cbad
commit eae6213cdd
@@ -49,6 +49,7 @@
import JobAssetsViewer from './assets/JobAssetsViewer.svelte'
import McpToolCallDetails from './McpToolCallDetails.svelte'
import { SelectionManager } from './graph/selectionUtils.svelte'
import { useThrottle } from 'runed'
let {
flowState: flowStateStore,
@@ -503,211 +504,187 @@
}
let jobMissingStartedAt: Record<string, number | 'P'> = {}
let lastSelectedLoopSwitch: number | undefined
let selectedLoopSwitchTimeout: number | undefined = undefined
let setSelectedLoopSwitch = useThrottle((lastStarted: string, mod: FlowStatusModule) => {
let position = mod.flow_jobs?.indexOf(lastStarted)
if (!position) return
// we wait a bit for suspended steps, to make sure the current iteration is updated
// when resumed before going forward. This avoid the duplicate resume forms bug.
setIteration(position, lastStarted, false, mod.id ?? '', true)
}, 2000)
function updateInnerModules() {
if (localModuleStates) {
innerModules?.forEach((mod, i) => {
if (mod.type === 'WaitingForEvents' && innerModules?.[i - 1]?.type === 'Success') {
setModuleState(mod.id ?? '', {
type: mod.type,
args: job?.args,
tag: job?.tag,
script_hash: job?.script_hash
if (!localModuleStates || !innerModules) return
for (let i = 0; i < innerModules.length; i++) {
let mod = innerModules[i]
let prev = innerModules[i - 1]
if (mod.type === 'WaitingForEvents' && prev?.type === 'Success') {
setModuleState(mod.id ?? '', {
type: mod.type,
args: job?.args,
tag: job?.tag,
script_hash: job?.script_hash
})
} else if (
mod.type === 'WaitingForExecutor' &&
localModuleStates[mod.id ?? '']?.scheduled_for == undefined
) {
JobService.getJob({
workspace: workspaceId ?? $workspaceStore ?? '',
id: mod.job ?? '',
noLogs: true,
noCode: true
})
.then((job) => {
const newState = {
type: mod.type,
scheduled_for: job?.['scheduled_for'],
job_id: job?.id,
parent_module: mod['parent_module'],
args: job?.args,
tag: job?.tag,
script_hash: job?.script_hash
}
setModuleState(mod.id ?? '', newState)
})
} else if (
mod.type === 'WaitingForExecutor' &&
localModuleStates[mod.id ?? '']?.scheduled_for == undefined
) {
JobService.getJob({
workspace: workspaceId ?? $workspaceStore ?? '',
id: mod.job ?? '',
noLogs: true,
noCode: true
.catch((e) => {
console.error(`Could not load inner module for job ${mod.job}`, e)
})
.then((job) => {
const newState = {
type: mod.type,
scheduled_for: job?.['scheduled_for'],
job_id: job?.id,
parent_module: mod['parent_module'],
args: job?.args,
tag: job?.tag,
script_hash: job?.script_hash
}
} else if (
(mod.flow_jobs || mod.branch_chosen) &&
(mod.type == 'Success' || mod.type == 'Failure') &&
!['Success', 'Failure'].includes(localModuleStates?.[mod.id ?? '']?.type)
) {
let branchChosen = mod.branch_chosen
? {
branchChosen:
mod.branch_chosen.type == 'default' ? 0 : (mod.branch_chosen.branch ?? 0) + 1
}
: {}
setModuleState(mod.id ?? '', { type: mod.type, ...branchChosen }, true)
} else if (isForloopSelected) {
setModuleState(mod.id ?? '', {}, true)
}
setModuleState(mod.id ?? '', newState)
if (mod.flow_jobs_success || mod.flow_jobs_duration || mod.flow_jobs) {
setModuleState(mod.id ?? '', {
flow_jobs_success: mod.flow_jobs_success,
flow_jobs_duration: mod.flow_jobs_duration,
flow_jobs: mod.flow_jobs,
iteration_total: mod.iterator?.itered_len ?? mod.flow_jobs?.length
})
}
if (mod.flow_jobs_duration && mod.flow_jobs) {
let key = buildSubflowKey(mod.id ?? '', prefix)
let durationStatuses = Object.fromEntries(
mod.flow_jobs.map((flowJobId, idx) => {
let started_at_str = mod.flow_jobs_duration?.started_at?.[idx]
let started_at = started_at_str ? new Date(started_at_str).getTime() : undefined
let duration_ms = mod.flow_jobs_duration?.duration_ms?.[idx]
if (started_at == undefined) {
let missingStartedAt = jobMissingStartedAt[flowJobId]
if (missingStartedAt != 'P') {
started_at = missingStartedAt
}
} else {
delete jobMissingStartedAt[flowJobId]
}
return [
flowJobId,
{
created_at: started_at,
started_at: started_at,
duration_ms: duration_ms
}
]
})
)
let missingStartedAtIds = Object.keys(durationStatuses)
.filter(
(id) => durationStatuses[id].created_at == undefined && jobMissingStartedAt[id] != 'P'
)
.slice(0, 100)
updateDurationStatuses(key, durationStatuses)
if (missingStartedAtIds.length > 0) {
missingStartedAtIds.forEach((id) => {
jobMissingStartedAt[id] = 'P'
})
JobService.getStartedAtByIds({
workspace: workspaceId ?? $workspaceStore ?? '',
requestBody: missingStartedAtIds
})
.then((jobs) => {
let lastStarted: string | undefined = undefined
let anySet = false
let nDurationStatuses = localDurationStatuses[key]?.byJob
missingStartedAtIds.forEach((id, idx) => {
const startedAt = jobs[idx]
const time = startedAt ? new Date(startedAt).getTime() : undefined
if (time) {
jobMissingStartedAt[id] = time
} else {
delete jobMissingStartedAt[id]
}
if (nDurationStatuses && time) {
if (!nDurationStatuses[id]?.duration_ms) {
anySet = true
lastStarted = id
nDurationStatuses[id] = {
created_at: time,
started_at: time
}
}
}
})
if (anySet) {
updateDurationStatuses(key, nDurationStatuses)
lastStarted && setSelectedLoopSwitch(lastStarted, mod)
}
})
.catch((e) => {
console.error(`Could not load inner module for job ${mod.job}`, e)
console.error(`Could not load inner module duration status for job ${mod.job}`, e)
})
} else if (
(mod.flow_jobs || mod.branch_chosen) &&
(mod.type == 'Success' || mod.type == 'Failure') &&
!['Success', 'Failure'].includes(localModuleStates?.[mod.id ?? '']?.type)
) {
let branchChosen = mod.branch_chosen
? {
branchChosen:
mod.branch_chosen.type == 'default' ? 0 : (mod.branch_chosen.branch ?? 0) + 1
}
: {}
// console.debug('updateInnerModules', mod.id, mod.type, branchChosen)
setModuleState(
mod.id ?? '',
{
type: mod.type,
...branchChosen
},
true
)
} else if (isForloopSelected) {
setModuleState(mod.id ?? '', {}, true)
} else {
setIteration(0, mod.flow_jobs?.[0] ?? '', false, mod.id ?? '', true)
}
}
if (mod.flow_jobs_success || mod.flow_jobs_duration || mod.flow_jobs) {
setModuleState(mod.id ?? '', {
flow_jobs_success: mod.flow_jobs_success,
flow_jobs_duration: mod.flow_jobs_duration,
flow_jobs: mod.flow_jobs,
iteration_total: mod.iterator?.itered_len ?? mod.flow_jobs?.length
})
}
if (mod.flow_jobs_duration && mod.flow_jobs) {
let key = buildSubflowKey(mod.id ?? '', prefix)
let durationStatuses = Object.fromEntries(
mod.flow_jobs.map((flowJobId, idx) => {
let started_at_str = mod.flow_jobs_duration?.started_at?.[idx]
let started_at = started_at_str ? new Date(started_at_str).getTime() : undefined
let duration_ms = mod.flow_jobs_duration?.duration_ms?.[idx]
if (started_at == undefined) {
let missingStartedAt = jobMissingStartedAt[flowJobId]
if (missingStartedAt != 'P') {
started_at = missingStartedAt
}
} else {
delete jobMissingStartedAt[flowJobId]
}
return [
flowJobId,
{
created_at: started_at,
started_at: started_at,
duration_ms: duration_ms
}
]
})
)
let missingStartedAtIds = Object.keys(durationStatuses)
.filter(
(id) => durationStatuses[id].created_at == undefined && jobMissingStartedAt[id] != 'P'
)
.slice(0, 100)
updateDurationStatuses(key, durationStatuses)
if (missingStartedAtIds.length > 0) {
missingStartedAtIds.forEach((id) => {
jobMissingStartedAt[id] = 'P'
})
JobService.getStartedAtByIds({
workspace: workspaceId ?? $workspaceStore ?? '',
requestBody: missingStartedAtIds
})
.then((jobs) => {
let lastStarted: string | undefined = undefined
let anySet = false
let nDurationStatuses = localDurationStatuses[key]?.byJob
missingStartedAtIds.forEach((id, idx) => {
const startedAt = jobs[idx]
const time = startedAt ? new Date(startedAt).getTime() : undefined
if (time) {
jobMissingStartedAt[id] = time
} else {
delete jobMissingStartedAt[id]
}
if (nDurationStatuses && time) {
if (!nDurationStatuses[id]?.duration_ms) {
anySet = true
lastStarted = id
nDurationStatuses[id] = {
created_at: time,
started_at: time
}
}
}
})
if (anySet) {
updateDurationStatuses(key, nDurationStatuses)
selectedLoopSwitchTimeout && clearTimeout(selectedLoopSwitchTimeout)
function setSelectedLoopSwitch() {
if (lastStarted) {
let position = mod.flow_jobs?.indexOf(lastStarted)
if (position != undefined) {
lastSelectedLoopSwitch = new Date().getTime()
console.log('setSelectedLoopSwitch', position, lastStarted)
setIteration(position, lastStarted, false, mod.id ?? '', true)
}
}
}
if (
lastSelectedLoopSwitch &&
new Date().getTime() - lastSelectedLoopSwitch < 3000
) {
selectedLoopSwitchTimeout = setTimeout(() => {
setSelectedLoopSwitch()
}, 2000)
} else {
console.log('setSelectedLoopSwitch')
setSelectedLoopSwitch()
}
}
if (mod.agent_actions && mod.id) {
setModuleState(mod.id, {
agent_actions: mod.agent_actions
})
mod.agent_actions.forEach((action, idx) => {
if (mod.id) {
if (action.type == 'tool_call') {
const toolCallId = getToolCallId(idx, mod.id, action.module_id)
const success = mod.agent_actions_success?.[idx]
setModuleState(toolCallId, {
job_id: action.job_id,
type: success != undefined ? (success ? 'Success' : 'Failure') : 'InProgress'
})
.catch((e) => {
console.error(`Could not load inner module duration status for job ${mod.job}`, e)
} else if (action.type == 'mcp_tool_call') {
const mcpToolCallId = AI_MCP_TOOL_CALL_PREFIX + '-' + mod.id + '-' + idx
const success = mod.agent_actions_success?.[idx]
setModuleState(mcpToolCallId, {
type: success != undefined ? (success ? 'Success' : 'Failure') : 'InProgress'
})
} else if (action.type == 'web_search') {
const websearchId = AI_WEBSEARCH_PREFIX + '-' + mod.id + '-' + idx
setModuleState(websearchId, {
type: 'Success'
})
} else if (action.type == 'message') {
const toolCallId = getToolCallId(idx, mod.id)
setModuleState(toolCallId, {
type: 'Success'
})
} else {
setIteration(0, mod.flow_jobs?.[0] ?? '', false, mod.id ?? '', true)
}
}
if (mod.agent_actions && mod.id) {
setModuleState(mod.id, {
agent_actions: mod.agent_actions
})
mod.agent_actions.forEach((action, idx) => {
if (mod.id) {
if (action.type == 'tool_call') {
const toolCallId = getToolCallId(idx, mod.id, action.module_id)
const success = mod.agent_actions_success?.[idx]
setModuleState(toolCallId, {
job_id: action.job_id,
type: success != undefined ? (success ? 'Success' : 'Failure') : 'InProgress'
})
} else if (action.type == 'mcp_tool_call') {
const mcpToolCallId = AI_MCP_TOOL_CALL_PREFIX + '-' + mod.id + '-' + idx
const success = mod.agent_actions_success?.[idx]
setModuleState(mcpToolCallId, {
type: success != undefined ? (success ? 'Success' : 'Failure') : 'InProgress'
})
} else if (action.type == 'web_search') {
const websearchId = AI_WEBSEARCH_PREFIX + '-' + mod.id + '-' + idx
setModuleState(websearchId, {
type: 'Success'
})
} else if (action.type == 'message') {
const toolCallId = getToolCallId(idx, mod.id)
setModuleState(toolCallId, {
type: 'Success'
})
}
}
})
}
})
}
})
}
}
}
@@ -746,7 +723,7 @@
} else {
job = newJob
}
job?.flow_status && updateStatus(job?.flow_status)
if (job?.flow_status) updateStatus(job?.flow_status)
onJobsLoaded?.({ job, force: false })
notAnonynmous = false
if (job?.type == 'CompletedJob' && !destroyed) {