chore: import upstream snapshot with attribution
CI / Migrate Dev DB (push) Has been skipped
CI / Detect Version (push) Has been cancelled
CI / Migrate DB (push) Has been cancelled
CI / Build Dev ECR (./docker/app.Dockerfile, ECR_APP) (push) Has been cancelled
CI / Build Dev ECR (./docker/db.Dockerfile, ECR_MIGRATIONS) (push) Has been cancelled
CI / Build Dev ECR (./docker/pii.Dockerfile, ECR_PII) (push) Has been cancelled
CI / Build Dev ECR (./docker/realtime.Dockerfile, ECR_REALTIME) (push) Has been cancelled
CI / Deploy Trigger.dev (Dev) (push) Has been cancelled
CI / Build AMD64 (./docker/app.Dockerfile, ECR_APP, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build AMD64 (./docker/db.Dockerfile, ECR_MIGRATIONS, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build AMD64 (./docker/pii.Dockerfile, ECR_PII, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build AMD64 (./docker/realtime.Dockerfile, ECR_REALTIME, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/app.Dockerfile, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/db.Dockerfile, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/pii.Dockerfile, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/realtime.Dockerfile, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Check Docs Changes (push) Has been cancelled
CI / Process Docs (push) Has been cancelled
CI / Create GitHub Release (push) Has been cancelled
CI / Test and Build (push) Has been cancelled
Publish CLI Package / publish-npm (push) Has been cancelled
Publish Python SDK / publish-pypi (push) Has been cancelled
Publish TypeScript SDK / publish-npm (push) Has been cancelled
CI / Migrate Dev DB (push) Has been skipped
CI / Detect Version (push) Has been cancelled
CI / Migrate DB (push) Has been cancelled
CI / Build Dev ECR (./docker/app.Dockerfile, ECR_APP) (push) Has been cancelled
CI / Build Dev ECR (./docker/db.Dockerfile, ECR_MIGRATIONS) (push) Has been cancelled
CI / Build Dev ECR (./docker/pii.Dockerfile, ECR_PII) (push) Has been cancelled
CI / Build Dev ECR (./docker/realtime.Dockerfile, ECR_REALTIME) (push) Has been cancelled
CI / Deploy Trigger.dev (Dev) (push) Has been cancelled
CI / Build AMD64 (./docker/app.Dockerfile, ECR_APP, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build AMD64 (./docker/db.Dockerfile, ECR_MIGRATIONS, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build AMD64 (./docker/pii.Dockerfile, ECR_PII, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build AMD64 (./docker/realtime.Dockerfile, ECR_REALTIME, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/app.Dockerfile, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/db.Dockerfile, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/pii.Dockerfile, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/realtime.Dockerfile, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Check Docs Changes (push) Has been cancelled
CI / Process Docs (push) Has been cancelled
CI / Create GitHub Release (push) Has been cancelled
CI / Test and Build (push) Has been cancelled
Publish CLI Package / publish-npm (push) Has been cancelled
Publish Python SDK / publish-pypi (push) Has been cancelled
Publish TypeScript SDK / publish-npm (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,193 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { toError } from '@sim/utils/errors'
|
||||
import { generateId } from '@sim/utils/id'
|
||||
import { LoggingSession } from '@/lib/logs/execution/logging-session'
|
||||
import { captureServerEvent } from '@/lib/posthog/server'
|
||||
import { executeWorkflowCore } from '@/lib/workflows/executor/execution-core'
|
||||
import { handlePostExecutionPauseState } from '@/lib/workflows/executor/pause-persistence'
|
||||
import { ExecutionSnapshot } from '@/executor/execution/snapshot'
|
||||
import type { ExecutionMetadata, SerializableExecutionState } from '@/executor/execution/types'
|
||||
import type { ExecutionResult, StreamingExecution } from '@/executor/types'
|
||||
|
||||
const logger = createLogger('WorkflowExecution')
|
||||
|
||||
export interface ExecuteWorkflowOptions {
|
||||
enabled: boolean
|
||||
selectedOutputs?: string[]
|
||||
isSecureMode?: boolean
|
||||
workflowTriggerType?: 'api' | 'chat' | 'copilot' | 'table'
|
||||
/**
|
||||
* If set, the executor enters the workflow at this block instead of resolving a Start block.
|
||||
* Use for trigger-originated runs (webhooks, table triggers, schedules) where the entry point
|
||||
* is the trigger block itself.
|
||||
*/
|
||||
triggerBlockId?: string
|
||||
onStream?: (streamingExec: StreamingExecution) => Promise<void>
|
||||
/** Fires before each block runs; lets callers track per-block lifecycle (e.g. table-cell live state). */
|
||||
onBlockStart?: (
|
||||
blockId: string,
|
||||
blockName: string,
|
||||
blockType: string,
|
||||
executionOrder: number
|
||||
) => Promise<void>
|
||||
onBlockComplete?: (blockId: string, output: unknown) => Promise<void>
|
||||
skipLoggingComplete?: boolean
|
||||
includeFileBase64?: boolean
|
||||
base64MaxBytes?: number
|
||||
largeValueKeys?: string[]
|
||||
fileKeys?: string[]
|
||||
abortSignal?: AbortSignal
|
||||
/** Use the live/draft workflow state instead of the deployed state. Used by copilot. */
|
||||
useDraftState?: boolean
|
||||
/** Stop execution after this block completes. Used for "run until block" feature. */
|
||||
stopAfterBlockId?: string
|
||||
/** Run-from-block configuration using a prior execution snapshot. */
|
||||
runFromBlock?: {
|
||||
startBlockId: string
|
||||
sourceSnapshot: SerializableExecutionState
|
||||
sourceExecutionId?: string
|
||||
}
|
||||
executionMode?: 'sync' | 'stream' | 'async'
|
||||
}
|
||||
|
||||
export interface WorkflowInfo {
|
||||
id: string
|
||||
userId: string
|
||||
workspaceId?: string | null
|
||||
isDeployed?: boolean
|
||||
variables?: Record<string, any>
|
||||
}
|
||||
|
||||
export async function executeWorkflow(
|
||||
workflow: WorkflowInfo,
|
||||
requestId: string,
|
||||
input: unknown | undefined,
|
||||
actorUserId: string,
|
||||
streamConfig?: ExecuteWorkflowOptions,
|
||||
providedExecutionId?: string
|
||||
): Promise<ExecutionResult> {
|
||||
if (!workflow.workspaceId) {
|
||||
throw new Error(`Workflow ${workflow.id} has no workspaceId`)
|
||||
}
|
||||
|
||||
const workflowId = workflow.id
|
||||
const workspaceId = workflow.workspaceId
|
||||
const executionId = providedExecutionId || generateId()
|
||||
const triggerType = streamConfig?.workflowTriggerType || 'api'
|
||||
const loggingSession = new LoggingSession(workflowId, executionId, triggerType, requestId)
|
||||
|
||||
try {
|
||||
const metadata: ExecutionMetadata = {
|
||||
requestId,
|
||||
executionId,
|
||||
workflowId,
|
||||
workspaceId,
|
||||
userId: actorUserId,
|
||||
workflowUserId: workflow.userId,
|
||||
triggerType,
|
||||
triggerBlockId: streamConfig?.triggerBlockId,
|
||||
useDraftState: streamConfig?.useDraftState ?? false,
|
||||
startTime: new Date().toISOString(),
|
||||
isClientSession: false,
|
||||
largeValueExecutionIds: Array.from(new Set([executionId])),
|
||||
largeValueKeys: streamConfig?.largeValueKeys,
|
||||
fileKeys: streamConfig?.fileKeys,
|
||||
executionMode: streamConfig?.executionMode,
|
||||
}
|
||||
|
||||
const snapshot = new ExecutionSnapshot(
|
||||
metadata,
|
||||
workflow,
|
||||
input,
|
||||
workflow.variables || {},
|
||||
streamConfig?.selectedOutputs || []
|
||||
)
|
||||
|
||||
const executionStartMs = Date.now()
|
||||
|
||||
const result = await executeWorkflowCore({
|
||||
snapshot,
|
||||
callbacks: {
|
||||
onStream: streamConfig?.onStream,
|
||||
onBlockStart: streamConfig?.onBlockStart
|
||||
? async (
|
||||
blockId: string,
|
||||
blockName: string,
|
||||
blockType: string,
|
||||
executionOrder: number
|
||||
) => {
|
||||
await streamConfig.onBlockStart!(blockId, blockName, blockType, executionOrder)
|
||||
}
|
||||
: undefined,
|
||||
onBlockComplete: streamConfig?.onBlockComplete
|
||||
? async (blockId: string, _blockName: string, _blockType: string, output: unknown) => {
|
||||
await streamConfig.onBlockComplete!(blockId, output)
|
||||
}
|
||||
: undefined,
|
||||
},
|
||||
loggingSession,
|
||||
includeFileBase64: streamConfig?.includeFileBase64,
|
||||
base64MaxBytes: streamConfig?.base64MaxBytes,
|
||||
abortSignal: streamConfig?.abortSignal,
|
||||
stopAfterBlockId: streamConfig?.stopAfterBlockId,
|
||||
runFromBlock: streamConfig?.runFromBlock,
|
||||
})
|
||||
|
||||
const blockTypes = [
|
||||
...new Set(
|
||||
(result.logs ?? [])
|
||||
.map((log) => log.blockType)
|
||||
.filter((t): t is string => typeof t === 'string')
|
||||
),
|
||||
]
|
||||
if (result.status !== 'paused') {
|
||||
captureServerEvent(
|
||||
actorUserId,
|
||||
'workflow_executed',
|
||||
{
|
||||
workflow_id: workflowId,
|
||||
workspace_id: workspaceId,
|
||||
trigger_type: triggerType,
|
||||
success: result.success,
|
||||
block_count: result.logs?.length ?? 0,
|
||||
block_types: blockTypes.join(','),
|
||||
duration_ms: Date.now() - executionStartMs,
|
||||
},
|
||||
{
|
||||
groups: { workspace: workspaceId },
|
||||
setOnce: { first_execution_at: new Date().toISOString() },
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
await handlePostExecutionPauseState({ result, workflowId, executionId, loggingSession })
|
||||
|
||||
if (streamConfig?.skipLoggingComplete) {
|
||||
return {
|
||||
...result,
|
||||
_streamingMetadata: {
|
||||
loggingSession,
|
||||
processedInput: input,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
} catch (error: unknown) {
|
||||
logger.error(`[${requestId}] Workflow execution failed:`, error)
|
||||
|
||||
captureServerEvent(
|
||||
actorUserId,
|
||||
'workflow_execution_failed',
|
||||
{
|
||||
workflow_id: workflow.id,
|
||||
workspace_id: workspaceId,
|
||||
trigger_type: streamConfig?.workflowTriggerType || 'api',
|
||||
error_message: toError(error).message,
|
||||
},
|
||||
{ groups: { workspace: workspaceId } }
|
||||
)
|
||||
|
||||
throw error
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user