mirror of
https://github.com/simstudioai/sim.git
synced 2026-02-03 19:24:57 -05:00
* feat(byok): byok for hosted model capabilities * fix type * add ignore lint * accidentally added feature flags * centralize byok fetch for LLM calls * remove feature flags ts * fix tests * update docs
114 lines
3.0 KiB
TypeScript
114 lines
3.0 KiB
TypeScript
import { isExecutionCancelled, isRedisCancellationEnabled } from '@/lib/execution/cancellation'
|
|
import { BlockType } from '@/executor/constants'
|
|
import type { BlockHandler, ExecutionContext } from '@/executor/types'
|
|
import type { SerializedBlock } from '@/serializer/types'
|
|
|
|
const CANCELLATION_CHECK_INTERVAL_MS = 500
|
|
|
|
interface SleepOptions {
|
|
signal?: AbortSignal
|
|
executionId?: string
|
|
}
|
|
|
|
const sleep = async (ms: number, options: SleepOptions = {}): Promise<boolean> => {
|
|
const { signal, executionId } = options
|
|
const useRedis = isRedisCancellationEnabled() && !!executionId
|
|
|
|
if (!useRedis && signal?.aborted) {
|
|
return false
|
|
}
|
|
|
|
return new Promise((resolve) => {
|
|
// biome-ignore lint/style/useConst: needs to be declared before cleanup() but assigned later
|
|
let mainTimeoutId: NodeJS.Timeout | undefined
|
|
let checkIntervalId: NodeJS.Timeout | undefined
|
|
let resolved = false
|
|
|
|
const cleanup = () => {
|
|
if (mainTimeoutId) clearTimeout(mainTimeoutId)
|
|
if (checkIntervalId) clearInterval(checkIntervalId)
|
|
if (!useRedis && signal) signal.removeEventListener('abort', onAbort)
|
|
}
|
|
|
|
const onAbort = () => {
|
|
if (resolved) return
|
|
resolved = true
|
|
cleanup()
|
|
resolve(false)
|
|
}
|
|
|
|
if (useRedis) {
|
|
checkIntervalId = setInterval(async () => {
|
|
if (resolved) return
|
|
try {
|
|
const cancelled = await isExecutionCancelled(executionId!)
|
|
if (cancelled) {
|
|
resolved = true
|
|
cleanup()
|
|
resolve(false)
|
|
}
|
|
} catch {}
|
|
}, CANCELLATION_CHECK_INTERVAL_MS)
|
|
} else if (signal) {
|
|
signal.addEventListener('abort', onAbort, { once: true })
|
|
}
|
|
|
|
mainTimeoutId = setTimeout(() => {
|
|
if (resolved) return
|
|
resolved = true
|
|
cleanup()
|
|
resolve(true)
|
|
}, ms)
|
|
})
|
|
}
|
|
|
|
/**
|
|
* 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> {
|
|
const timeValue = Number.parseInt(inputs.timeValue || '10', 10)
|
|
const timeUnit = inputs.timeUnit || 'seconds'
|
|
|
|
if (Number.isNaN(timeValue) || timeValue <= 0) {
|
|
throw new Error('Wait amount must be a positive number')
|
|
}
|
|
|
|
let waitMs = timeValue * 1000
|
|
if (timeUnit === 'minutes') {
|
|
waitMs = timeValue * 60 * 1000
|
|
}
|
|
|
|
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}`)
|
|
}
|
|
|
|
const completed = await sleep(waitMs, {
|
|
signal: ctx.abortSignal,
|
|
executionId: ctx.executionId,
|
|
})
|
|
|
|
if (!completed) {
|
|
return {
|
|
waitDuration: waitMs,
|
|
status: 'cancelled',
|
|
}
|
|
}
|
|
|
|
return {
|
|
waitDuration: waitMs,
|
|
status: 'completed',
|
|
}
|
|
}
|
|
}
|