Skip to content

Commit 2c4eb9f

Browse files
emir-karabegcursoragentwaleedlatif1
authored
fix(terminal): start precision (#3078)
* fix(executor): use performance.now() for precise block timing Replace Date.now() with performance.now() for timing measurements in the executor to provide sub-millisecond precision. This fixes timing discrepancies with fast-executing blocks like the start block where millisecond precision was insufficient. Changes: - block-executor.ts: Use performance.now() for block execution timing - engine.ts: Use performance.now() for overall execution timing Co-authored-by: emir <emir@simstudio.ai> * format ms as whole nums,round secs to 2 decimal places and compute all started/ended times on server and passback to clinet --------- Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: waleed <walif6@gmail.com>
1 parent aec0de0 commit 2c4eb9f

File tree

12 files changed

+107
-69
lines changed

12 files changed

+107
-69
lines changed

apps/sim/app/api/workflows/[id]/execute/route.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -616,6 +616,8 @@ export async function POST(req: NextRequest, { params }: { params: Promise<{ id:
616616
input: callbackData.input,
617617
error: callbackData.output.error,
618618
durationMs: callbackData.executionTime || 0,
619+
startedAt: callbackData.startedAt,
620+
endedAt: callbackData.endedAt,
619621
...(iterationContext && {
620622
iterationCurrent: iterationContext.iterationCurrent,
621623
iterationTotal: iterationContext.iterationTotal,
@@ -641,6 +643,8 @@ export async function POST(req: NextRequest, { params }: { params: Promise<{ id:
641643
input: callbackData.input,
642644
output: callbackData.output,
643645
durationMs: callbackData.executionTime || 0,
646+
startedAt: callbackData.startedAt,
647+
endedAt: callbackData.endedAt,
644648
...(iterationContext && {
645649
iterationCurrent: iterationContext.iterationCurrent,
646650
iterationTotal: iterationContext.iterationTotal,

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/terminal/utils.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,9 @@ export function getBlockColor(blockType: string): string {
5858
*/
5959
export function formatDuration(ms?: number): string {
6060
if (ms === undefined || ms === null) return '-'
61-
if (ms < 1000) return `${ms}ms`
61+
if (ms < 1000) {
62+
return `${Math.round(ms)}ms`
63+
}
6264
return `${(ms / 1000).toFixed(2)}s`
6365
}
6466

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/hooks/use-workflow-execution.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -964,8 +964,8 @@ export function useWorkflowExecution() {
964964
const isContainerBlock = data.blockType === 'loop' || data.blockType === 'parallel'
965965
if (isContainerBlock) return
966966

967-
const startedAt = new Date(Date.now() - data.durationMs).toISOString()
968-
const endedAt = new Date().toISOString()
967+
const startedAt = data.startedAt
968+
const endedAt = data.endedAt
969969

970970
accumulatedBlockLogs.push({
971971
blockId: data.blockId,
@@ -1013,8 +1013,8 @@ export function useWorkflowExecution() {
10131013
// Track failed block execution in run path
10141014
setBlockRunStatus(data.blockId, 'error')
10151015

1016-
const startedAt = new Date(Date.now() - data.durationMs).toISOString()
1017-
const endedAt = new Date().toISOString()
1016+
const startedAt = data.startedAt
1017+
const endedAt = data.endedAt
10181018

10191019
// Accumulate block error log for the execution result
10201020
accumulatedBlockLogs.push({
@@ -1603,8 +1603,8 @@ export function useWorkflowExecution() {
16031603
const isContainerBlock = data.blockType === 'loop' || data.blockType === 'parallel'
16041604
if (isContainerBlock) return
16051605

1606-
const startedAt = new Date(Date.now() - data.durationMs).toISOString()
1607-
const endedAt = new Date().toISOString()
1606+
const startedAt = data.startedAt
1607+
const endedAt = data.endedAt
16081608

16091609
accumulatedBlockLogs.push({
16101610
blockId: data.blockId,
@@ -1642,8 +1642,8 @@ export function useWorkflowExecution() {
16421642

16431643
setBlockRunStatus(data.blockId, 'error')
16441644

1645-
const startedAt = new Date(Date.now() - data.durationMs).toISOString()
1646-
const endedAt = new Date().toISOString()
1645+
const startedAt = data.startedAt
1646+
const endedAt = data.endedAt
16471647

16481648
accumulatedBlockLogs.push({
16491649
blockId: data.blockId,

apps/sim/executor/execution/block-executor.ts

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ export class BlockExecutor {
7171
this.callOnBlockStart(ctx, node, block)
7272
}
7373

74-
const startTime = Date.now()
74+
const startTime = performance.now()
7575
let resolvedInputs: Record<string, any> = {}
7676

7777
const nodeMetadata = this.buildNodeMetadata(node)
@@ -145,7 +145,7 @@ export class BlockExecutor {
145145
})) as NormalizedBlockOutput
146146
}
147147

148-
const duration = Date.now() - startTime
148+
const duration = performance.now() - startTime
149149

150150
if (blockLog) {
151151
blockLog.endedAt = new Date().toISOString()
@@ -169,7 +169,9 @@ export class BlockExecutor {
169169
block,
170170
this.sanitizeInputsForLog(resolvedInputs),
171171
displayOutput,
172-
duration
172+
duration,
173+
blockLog!.startedAt,
174+
blockLog!.endedAt
173175
)
174176
}
175177

@@ -221,7 +223,7 @@ export class BlockExecutor {
221223
isSentinel: boolean,
222224
phase: 'input_resolution' | 'execution'
223225
): NormalizedBlockOutput {
224-
const duration = Date.now() - startTime
226+
const duration = performance.now() - startTime
225227
const errorMessage = normalizeError(error)
226228
const hasResolvedInputs =
227229
resolvedInputs && typeof resolvedInputs === 'object' && Object.keys(resolvedInputs).length > 0
@@ -274,7 +276,9 @@ export class BlockExecutor {
274276
block,
275277
this.sanitizeInputsForLog(input),
276278
displayOutput,
277-
duration
279+
duration,
280+
blockLog!.startedAt,
281+
blockLog!.endedAt
278282
)
279283
}
280284

@@ -423,7 +427,9 @@ export class BlockExecutor {
423427
block: SerializedBlock,
424428
input: Record<string, any>,
425429
output: NormalizedBlockOutput,
426-
duration: number
430+
duration: number,
431+
startedAt: string,
432+
endedAt: string
427433
): void {
428434
const blockId = node.id
429435
const blockName = block.metadata?.name ?? blockId
@@ -440,6 +446,8 @@ export class BlockExecutor {
440446
input,
441447
output,
442448
executionTime: duration,
449+
startedAt,
450+
endedAt,
443451
},
444452
iterationContext
445453
)

apps/sim/executor/execution/engine.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ export class ExecutionEngine {
101101
}
102102

103103
async run(triggerBlockId?: string): Promise<ExecutionResult> {
104-
const startTime = Date.now()
104+
const startTime = performance.now()
105105
try {
106106
this.initializeQueue(triggerBlockId)
107107

@@ -125,8 +125,8 @@ export class ExecutionEngine {
125125
return this.buildPausedResult(startTime)
126126
}
127127

128-
const endTime = Date.now()
129-
this.context.metadata.endTime = new Date(endTime).toISOString()
128+
const endTime = performance.now()
129+
this.context.metadata.endTime = new Date().toISOString()
130130
this.context.metadata.duration = endTime - startTime
131131

132132
if (this.cancelledFlag) {
@@ -146,8 +146,8 @@ export class ExecutionEngine {
146146
metadata: this.context.metadata,
147147
}
148148
} catch (error) {
149-
const endTime = Date.now()
150-
this.context.metadata.endTime = new Date(endTime).toISOString()
149+
const endTime = performance.now()
150+
this.context.metadata.endTime = new Date().toISOString()
151151
this.context.metadata.duration = endTime - startTime
152152

153153
if (this.cancelledFlag) {
@@ -433,8 +433,8 @@ export class ExecutionEngine {
433433
}
434434

435435
private buildPausedResult(startTime: number): ExecutionResult {
436-
const endTime = Date.now()
437-
this.context.metadata.endTime = new Date(endTime).toISOString()
436+
const endTime = performance.now()
437+
this.context.metadata.endTime = new Date().toISOString()
438438
this.context.metadata.duration = endTime - startTime
439439
this.context.metadata.status = 'paused'
440440

apps/sim/executor/execution/types.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,13 @@ export interface ContextExtensions {
103103
blockId: string,
104104
blockName: string,
105105
blockType: string,
106-
output: { input?: any; output: NormalizedBlockOutput; executionTime: number },
106+
output: {
107+
input?: any
108+
output: NormalizedBlockOutput
109+
executionTime: number
110+
startedAt: string
111+
endedAt: string
112+
},
107113
iterationContext?: IterationContext
108114
) => Promise<void>
109115

apps/sim/executor/orchestrators/loop.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -281,9 +281,12 @@ export class LoopOrchestrator {
281281

282282
// Emit onBlockComplete for the loop container so the UI can track it
283283
if (this.contextExtensions?.onBlockComplete) {
284+
const now = new Date().toISOString()
284285
this.contextExtensions.onBlockComplete(loopId, 'Loop', 'loop', {
285286
output,
286287
executionTime: DEFAULTS.EXECUTION_TIME,
288+
startedAt: now,
289+
endedAt: now,
287290
})
288291
}
289292

apps/sim/executor/orchestrators/parallel.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,9 +265,12 @@ export class ParallelOrchestrator {
265265

266266
// Emit onBlockComplete for the parallel container so the UI can track it
267267
if (this.contextExtensions?.onBlockComplete) {
268+
const now = new Date().toISOString()
268269
this.contextExtensions.onBlockComplete(parallelId, 'Parallel', 'parallel', {
269270
output,
270271
executionTime: 0,
272+
startedAt: now,
273+
endedAt: now,
271274
})
272275
}
273276

apps/sim/executor/utils/subflow-utils.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,8 @@ export function addSubflowErrorLog(
232232
input: inputData,
233233
output: { error: errorMessage },
234234
executionTime: 0,
235+
startedAt: now,
236+
endedAt: now,
235237
})
236238
}
237239
}

apps/sim/hooks/use-execution-stream.ts

Lines changed: 21 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,18 @@
11
import { useCallback, useRef } from 'react'
22
import { createLogger } from '@sim/logger'
3-
import type { ExecutionEvent } from '@/lib/workflows/executor/execution-events'
3+
import type {
4+
BlockCompletedData,
5+
BlockErrorData,
6+
BlockStartedData,
7+
ExecutionCancelledData,
8+
ExecutionCompletedData,
9+
ExecutionErrorData,
10+
ExecutionEvent,
11+
ExecutionStartedData,
12+
StreamChunkData,
13+
StreamDoneData,
14+
} from '@/lib/workflows/executor/execution-events'
415
import type { SerializableExecutionState } from '@/executor/execution/types'
5-
import type { SubflowType } from '@/stores/workflows/workflow/types'
616

717
const logger = createLogger('useExecutionStream')
818

@@ -81,48 +91,15 @@ async function processSSEStream(
8191
}
8292

8393
export interface ExecutionStreamCallbacks {
84-
onExecutionStarted?: (data: { startTime: string }) => void
85-
onExecutionCompleted?: (data: {
86-
success: boolean
87-
output: any
88-
duration: number
89-
startTime: string
90-
endTime: string
91-
}) => void
92-
onExecutionError?: (data: { error: string; duration: number }) => void
93-
onExecutionCancelled?: (data: { duration: number }) => void
94-
onBlockStarted?: (data: {
95-
blockId: string
96-
blockName: string
97-
blockType: string
98-
iterationCurrent?: number
99-
iterationTotal?: number
100-
iterationType?: SubflowType
101-
}) => void
102-
onBlockCompleted?: (data: {
103-
blockId: string
104-
blockName: string
105-
blockType: string
106-
input?: any
107-
output: any
108-
durationMs: number
109-
iterationCurrent?: number
110-
iterationTotal?: number
111-
iterationType?: SubflowType
112-
}) => void
113-
onBlockError?: (data: {
114-
blockId: string
115-
blockName: string
116-
blockType: string
117-
input?: any
118-
error: string
119-
durationMs: number
120-
iterationCurrent?: number
121-
iterationTotal?: number
122-
iterationType?: SubflowType
123-
}) => void
124-
onStreamChunk?: (data: { blockId: string; chunk: string }) => void
125-
onStreamDone?: (data: { blockId: string }) => void
94+
onExecutionStarted?: (data: ExecutionStartedData) => void
95+
onExecutionCompleted?: (data: ExecutionCompletedData) => void
96+
onExecutionError?: (data: ExecutionErrorData) => void
97+
onExecutionCancelled?: (data: ExecutionCancelledData) => void
98+
onBlockStarted?: (data: BlockStartedData) => void
99+
onBlockCompleted?: (data: BlockCompletedData) => void
100+
onBlockError?: (data: BlockErrorData) => void
101+
onStreamChunk?: (data: StreamChunkData) => void
102+
onStreamDone?: (data: StreamDoneData) => void
126103
}
127104

128105
export interface ExecuteStreamOptions {

0 commit comments

Comments
 (0)