mirror of
https://github.com/simstudioai/sim.git
synced 2026-01-31 09:48:06 -05:00
* fix(billing): should allow restoring subscription (#1728) * fix(already-cancelled-sub): UI should allow restoring subscription * restore functionality fixed * fix * improvement(start): revert to start block * make it work with start block * fix start block persistence * cleanup triggers * debounce status checks * update docs * improvement(start): revert to start block * make it work with start block * fix start block persistence * cleanup triggers * debounce status checks * update docs * SSE v0.1 * v0.2 * v0.3 * v0.4 * v0.5 * v0.6 * broken checkpoint * Executor progress - everything preliminarily tested except while loops and triggers * Executor fixes * Fix var typing * Implement while loop execution * Loop and parallel result agg * Refactor v1 - loops work * Fix var resolution in for each loop * Fix while loop condition and variable resolution * Fix loop iteration counts * Fix loop badges * Clean logs * Fix variable references from start block * Fix condition block * Fix conditional convergence * Dont execute orphaned nodse * Code cleanup 1 and error surfacing * compile time try catch * Some fixes * Fix error throwing * Sentinels v1 * Fix multiple start and end nodes in loop * Edge restoration * Fix reachable nodes execution * Parallel subflows * Fix loop/parallel sentinel convergence * Loops and parallels orchestrator * Split executor * Variable resolution split * Dag phase * Refactor * Refactor * Refactor 3 * Lint + refactor * Lint + cleanup + refactor * Readability * Initial logs * Fix trace spans * Console pills for iters * Add input/output pills * Checkpoint * remove unused code * THIS IS THE COMMIT THAT CAN BREAK A LOT OF THINGS * ANOTHER BIG REFACTOR * Lint + fix tests * Fix webhook * Remove comment * Merge stash * Fix triggers? * Stuff * Fix error port * Lint * Consolidate state * Clean up some var resolution * Remove some var resolution logs * Fix chat * Fix chat triggers * Fix chat trigger fully * Snapshot refactor * Fix mcp and custom tools * Lint * Fix parallel default count and trace span overlay * Agent purple * Fix test * Fix test --------- Co-authored-by: Waleed <walif6@gmail.com> Co-authored-by: Vikhyath Mondreti <vikhyathvikku@gmail.com> Co-authored-by: Vikhyath Mondreti <vikhyath@simstudio.ai>
103 lines
3.1 KiB
TypeScript
103 lines
3.1 KiB
TypeScript
import { createLogger } from '@/lib/logs/console/logger'
|
|
import { BlockType } from '@/executor/consts'
|
|
import type { BlockHandler, ExecutionContext } from '@/executor/types'
|
|
import type { SerializedBlock } from '@/serializer/types'
|
|
|
|
const logger = createLogger('WaitBlockHandler')
|
|
|
|
/**
|
|
* Helper function to sleep for a specified number of milliseconds
|
|
* On client-side: checks for cancellation every 100ms (non-blocking for UI)
|
|
* On server-side: simple sleep without polling (server execution can't be cancelled mid-flight)
|
|
*/
|
|
const sleep = async (ms: number, checkCancelled?: () => boolean): Promise<boolean> => {
|
|
const isClientSide = typeof window !== 'undefined'
|
|
|
|
// Server-side: simple sleep without polling
|
|
if (!isClientSide) {
|
|
await new Promise((resolve) => setTimeout(resolve, ms))
|
|
return true
|
|
}
|
|
|
|
// Client-side: check for cancellation every 100ms
|
|
const chunkMs = 100
|
|
let elapsed = 0
|
|
|
|
while (elapsed < ms) {
|
|
// Check if execution was cancelled
|
|
if (checkCancelled?.()) {
|
|
return false // Sleep was interrupted
|
|
}
|
|
|
|
// Sleep for a chunk or remaining time, whichever is smaller
|
|
const sleepTime = Math.min(chunkMs, ms - elapsed)
|
|
await new Promise((resolve) => setTimeout(resolve, sleepTime))
|
|
elapsed += sleepTime
|
|
}
|
|
|
|
return true // Sleep completed normally
|
|
}
|
|
|
|
/**
|
|
* Handler for Wait blocks that pause workflow execution for a time delay
|
|
*/
|
|
export class WaitBlockHandler implements BlockHandler {
|
|
canHandle(block: SerializedBlock): boolean {
|
|
return block.metadata?.id === BlockType.WAIT
|
|
}
|
|
|
|
async execute(
|
|
ctx: ExecutionContext,
|
|
block: SerializedBlock,
|
|
inputs: Record<string, any>
|
|
): Promise<any> {
|
|
logger.info(`Executing Wait block: ${block.id}`, { inputs })
|
|
|
|
// Parse the wait duration
|
|
const timeValue = Number.parseInt(inputs.timeValue || '10', 10)
|
|
const timeUnit = inputs.timeUnit || 'seconds'
|
|
|
|
// Validate time value
|
|
if (Number.isNaN(timeValue) || timeValue <= 0) {
|
|
throw new Error('Wait amount must be a positive number')
|
|
}
|
|
|
|
// Calculate wait time in milliseconds
|
|
let waitMs = timeValue * 1000 // Default to seconds
|
|
if (timeUnit === 'minutes') {
|
|
waitMs = timeValue * 60 * 1000
|
|
}
|
|
|
|
// Enforce 10-minute maximum (600,000 ms)
|
|
const maxWaitMs = 10 * 60 * 1000
|
|
if (waitMs > maxWaitMs) {
|
|
const maxDisplay = timeUnit === 'minutes' ? '10 minutes' : '600 seconds'
|
|
throw new Error(`Wait time exceeds maximum of ${maxDisplay}`)
|
|
}
|
|
|
|
logger.info(`Waiting for ${waitMs}ms (${timeValue} ${timeUnit})`)
|
|
|
|
// Actually sleep for the specified duration
|
|
// The executor updates context.isCancelled when cancel() is called
|
|
const checkCancelled = () => {
|
|
return (ctx as any).isCancelled === true
|
|
}
|
|
|
|
const completed = await sleep(waitMs, checkCancelled)
|
|
|
|
if (!completed) {
|
|
logger.info('Wait was interrupted by cancellation')
|
|
return {
|
|
waitDuration: waitMs,
|
|
status: 'cancelled',
|
|
}
|
|
}
|
|
|
|
logger.info('Wait completed successfully')
|
|
return {
|
|
waitDuration: waitMs,
|
|
status: 'completed',
|
|
}
|
|
}
|
|
}
|