mirror of
https://github.com/simstudioai/sim.git
synced 2026-04-06 03:00:16 -04:00
fix(hitl): add missing fields to block configs (#3027)
* fix(hitl): add missing fields to block configs * update copilot paths * one more case * update helper usage
This commit is contained in:
committed by
GitHub
parent
6b412c578d
commit
089427822e
@@ -1,5 +1,16 @@
|
||||
import type { LoopType, ParallelType } from '@/lib/workflows/types'
|
||||
|
||||
/**
|
||||
* Runtime-injected keys for trigger blocks that should be hidden from logs/display.
|
||||
* These are added during execution but aren't part of the block's static output schema.
|
||||
*/
|
||||
export const TRIGGER_INTERNAL_KEYS = ['webhook', 'workflowId'] as const
|
||||
export type TriggerInternalKey = (typeof TRIGGER_INTERNAL_KEYS)[number]
|
||||
|
||||
export function isTriggerInternalKey(key: string): key is TriggerInternalKey {
|
||||
return TRIGGER_INTERNAL_KEYS.includes(key as TriggerInternalKey)
|
||||
}
|
||||
|
||||
export enum BlockType {
|
||||
PARALLEL = 'parallel',
|
||||
LOOP = 'loop',
|
||||
|
||||
@@ -11,8 +11,6 @@ import {
|
||||
DEFAULTS,
|
||||
EDGE,
|
||||
isSentinelBlockType,
|
||||
isTriggerBehavior,
|
||||
isWorkflowBlockType,
|
||||
} from '@/executor/constants'
|
||||
import type { DAGNode } from '@/executor/dag/builder'
|
||||
import { ChildWorkflowError } from '@/executor/errors/child-workflow-error'
|
||||
@@ -30,6 +28,7 @@ import type {
|
||||
} from '@/executor/types'
|
||||
import { streamingResponseFormatProcessor } from '@/executor/utils'
|
||||
import { buildBlockExecutionError, normalizeError } from '@/executor/utils/errors'
|
||||
import { filterOutputForLog } from '@/executor/utils/output-filter'
|
||||
import { validateBlockType } from '@/executor/utils/permission-check'
|
||||
import type { VariableResolver } from '@/executor/variables/resolver'
|
||||
import type { SerializedBlock } from '@/serializer/types'
|
||||
@@ -149,13 +148,15 @@ export class BlockExecutor {
|
||||
blockLog.endedAt = new Date().toISOString()
|
||||
blockLog.durationMs = duration
|
||||
blockLog.success = true
|
||||
blockLog.output = this.filterOutputForLog(block, normalizedOutput)
|
||||
blockLog.output = filterOutputForLog(block.metadata?.id || '', normalizedOutput, { block })
|
||||
}
|
||||
|
||||
this.state.setBlockOutput(node.id, normalizedOutput, duration)
|
||||
|
||||
if (!isSentinel) {
|
||||
const displayOutput = this.filterOutputForDisplay(block, normalizedOutput)
|
||||
const displayOutput = filterOutputForLog(block.metadata?.id || '', normalizedOutput, {
|
||||
block,
|
||||
})
|
||||
this.callOnBlockComplete(ctx, node, block, resolvedInputs, displayOutput, duration)
|
||||
}
|
||||
|
||||
@@ -233,7 +234,7 @@ export class BlockExecutor {
|
||||
blockLog.success = false
|
||||
blockLog.error = errorMessage
|
||||
blockLog.input = input
|
||||
blockLog.output = this.filterOutputForLog(block, errorOutput)
|
||||
blockLog.output = filterOutputForLog(block.metadata?.id || '', errorOutput, { block })
|
||||
}
|
||||
|
||||
logger.error(
|
||||
@@ -246,7 +247,7 @@ export class BlockExecutor {
|
||||
)
|
||||
|
||||
if (!isSentinel) {
|
||||
const displayOutput = this.filterOutputForDisplay(block, errorOutput)
|
||||
const displayOutput = filterOutputForLog(block.metadata?.id || '', errorOutput, { block })
|
||||
this.callOnBlockComplete(ctx, node, block, input, displayOutput, duration)
|
||||
}
|
||||
|
||||
@@ -335,51 +336,6 @@ export class BlockExecutor {
|
||||
return { result: output }
|
||||
}
|
||||
|
||||
private filterOutputForLog(
|
||||
block: SerializedBlock,
|
||||
output: NormalizedBlockOutput
|
||||
): NormalizedBlockOutput {
|
||||
const blockType = block.metadata?.id
|
||||
|
||||
if (blockType === BlockType.HUMAN_IN_THE_LOOP) {
|
||||
const filtered: NormalizedBlockOutput = {}
|
||||
for (const [key, value] of Object.entries(output)) {
|
||||
if (key.startsWith('_')) continue
|
||||
if (key === 'response') continue
|
||||
filtered[key] = value
|
||||
}
|
||||
return filtered
|
||||
}
|
||||
|
||||
if (isTriggerBehavior(block)) {
|
||||
const filtered: NormalizedBlockOutput = {}
|
||||
const internalKeys = ['webhook', 'workflowId']
|
||||
for (const [key, value] of Object.entries(output)) {
|
||||
if (internalKeys.includes(key)) continue
|
||||
filtered[key] = value
|
||||
}
|
||||
return filtered
|
||||
}
|
||||
|
||||
return output
|
||||
}
|
||||
|
||||
private filterOutputForDisplay(
|
||||
block: SerializedBlock,
|
||||
output: NormalizedBlockOutput
|
||||
): NormalizedBlockOutput {
|
||||
const filtered = this.filterOutputForLog(block, output)
|
||||
|
||||
if (isWorkflowBlockType(block.metadata?.id)) {
|
||||
const { childTraceSpans: _, ...displayOutput } = filtered as {
|
||||
childTraceSpans?: unknown
|
||||
} & Record<string, unknown>
|
||||
return displayOutput
|
||||
}
|
||||
|
||||
return filtered
|
||||
}
|
||||
|
||||
private callOnBlockStart(ctx: ExecutionContext, node: DAGNode, block: SerializedBlock): void {
|
||||
const blockId = node.id
|
||||
const blockName = block.metadata?.name ?? blockId
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { BlockType, isTriggerBehavior } from '@/executor/constants'
|
||||
import { BlockType, isTriggerBehavior, isTriggerInternalKey } from '@/executor/constants'
|
||||
import type { BlockHandler, ExecutionContext } from '@/executor/types'
|
||||
import type { SerializedBlock } from '@/serializer/types'
|
||||
|
||||
@@ -33,7 +33,12 @@ export class TriggerBlockHandler implements BlockHandler {
|
||||
const starterOutput = starterState.output
|
||||
|
||||
if (starterOutput.webhook?.data) {
|
||||
const { webhook, workflowId, ...cleanOutput } = starterOutput
|
||||
const cleanOutput: Record<string, unknown> = {}
|
||||
for (const [key, value] of Object.entries(starterOutput)) {
|
||||
if (!isTriggerInternalKey(key)) {
|
||||
cleanOutput[key] = value
|
||||
}
|
||||
}
|
||||
return cleanOutput
|
||||
}
|
||||
|
||||
|
||||
51
apps/sim/executor/utils/output-filter.ts
Normal file
51
apps/sim/executor/utils/output-filter.ts
Normal file
@@ -0,0 +1,51 @@
|
||||
import { getBlock } from '@/blocks'
|
||||
import { isHiddenFromDisplay } from '@/blocks/types'
|
||||
import { isTriggerBehavior, isTriggerInternalKey } from '@/executor/constants'
|
||||
import type { NormalizedBlockOutput } from '@/executor/types'
|
||||
import type { SerializedBlock } from '@/serializer/types'
|
||||
|
||||
/**
|
||||
* Filters block output for logging/display purposes.
|
||||
* Removes internal fields and fields marked with hiddenFromDisplay.
|
||||
*
|
||||
* @param blockType - The block type string (e.g., 'human_in_the_loop', 'workflow')
|
||||
* @param output - The raw block output to filter
|
||||
* @param options - Optional configuration
|
||||
* @param options.block - Full SerializedBlock for trigger behavior detection
|
||||
* @param options.additionalHiddenKeys - Extra keys to filter out (e.g., 'resume')
|
||||
*/
|
||||
export function filterOutputForLog(
|
||||
blockType: string,
|
||||
output: NormalizedBlockOutput,
|
||||
options?: {
|
||||
block?: SerializedBlock
|
||||
additionalHiddenKeys?: string[]
|
||||
}
|
||||
): NormalizedBlockOutput {
|
||||
const blockConfig = blockType ? getBlock(blockType) : undefined
|
||||
const filtered: NormalizedBlockOutput = {}
|
||||
const additionalHiddenKeys = options?.additionalHiddenKeys ?? []
|
||||
|
||||
for (const [key, value] of Object.entries(output)) {
|
||||
// Skip internal keys (underscore prefix)
|
||||
if (key.startsWith('_')) continue
|
||||
|
||||
if (blockConfig?.outputs && isHiddenFromDisplay(blockConfig.outputs[key])) {
|
||||
continue
|
||||
}
|
||||
|
||||
// Skip runtime-injected trigger keys not in block config
|
||||
if (options?.block && isTriggerBehavior(options.block) && isTriggerInternalKey(key)) {
|
||||
continue
|
||||
}
|
||||
|
||||
// Skip additional hidden keys specified by caller
|
||||
if (additionalHiddenKeys.includes(key)) {
|
||||
continue
|
||||
}
|
||||
|
||||
filtered[key] = value
|
||||
}
|
||||
|
||||
return filtered
|
||||
}
|
||||
Reference in New Issue
Block a user