From 72316663d1a6194ce4317ea5bfda17d348e663bd Mon Sep 17 00:00:00 2001 From: waleedlatif1 Date: Tue, 19 Aug 2025 15:22:53 -0700 Subject: [PATCH 1/6] added sub-workflow logs --- .../handlers/workflow/workflow-handler.ts | 120 +++++++++++++++++- apps/sim/executor/index.ts | 42 ++++++ .../logs/execution/trace-spans/trace-spans.ts | 78 ++++++++++++ 3 files changed, 238 insertions(+), 2 deletions(-) diff --git a/apps/sim/executor/handlers/workflow/workflow-handler.ts b/apps/sim/executor/handlers/workflow/workflow-handler.ts index 0291cc26076..a2e90c3496a 100644 --- a/apps/sim/executor/handlers/workflow/workflow-handler.ts +++ b/apps/sim/executor/handlers/workflow/workflow-handler.ts @@ -1,5 +1,6 @@ import { generateInternalToken } from '@/lib/auth/internal' import { createLogger } from '@/lib/logs/console/logger' +import { buildTraceSpans } from '@/lib/logs/execution/trace-spans/trace-spans' import { getBaseUrl } from '@/lib/urls/utils' import type { BlockOutput } from '@/blocks/types' import { Executor } from '@/executor' @@ -107,12 +108,16 @@ export class WorkflowBlockHandler implements BlockHandler { // Log execution completion logger.info(`Child workflow ${childWorkflowName} completed in ${Math.round(duration)}ms`) + // Capture child workflow logs and build trace spans for integration into parent logs + const childTraceSpans = this.captureChildWorkflowLogs(result, childWorkflowName, context) + // Map child workflow output to parent block output const mappedResult = this.mapChildOutputToParent( result, workflowId, childWorkflowName, - duration + duration, + childTraceSpans ) // If the child workflow failed, throw an error to trigger proper error handling in the parent @@ -221,6 +226,114 @@ export class WorkflowBlockHandler implements BlockHandler { } } + /** + * Captures child workflow execution logs and converts them to trace spans + * that can be integrated into the parent workflow's execution context + */ + private captureChildWorkflowLogs( + childResult: any, + childWorkflowName: string, + parentContext: ExecutionContext + ): any[] { + try { + // Child workflow results contain execution logs that we need to capture + if (!childResult.logs || !Array.isArray(childResult.logs)) { + logger.debug(`No child workflow logs found for ${childWorkflowName}`) + return [] + } + + // Build trace spans from child workflow logs using the same logic as parent workflows + const { traceSpans } = buildTraceSpans(childResult) + + if (!traceSpans || traceSpans.length === 0) { + logger.debug(`No trace spans generated from child workflow logs for ${childWorkflowName}`) + return [] + } + + // Transform child trace spans to have a child workflow context + // This helps distinguish them in the parent workflow's log viewer + const transformedSpans = traceSpans.map((span: any) => { + return this.transformSpanForChildWorkflow(span, childWorkflowName) + }) + + logger.info( + `Captured ${transformedSpans.length} trace spans from child workflow: ${childWorkflowName}` + ) + + return transformedSpans + } catch (error) { + logger.error(`Error capturing child workflow logs for ${childWorkflowName}:`, error) + return [] + } + } + + /** + * Transforms a trace span to indicate it came from a child workflow + */ + private transformSpanForChildWorkflow(span: any, childWorkflowName: string): any { + const transformedSpan = { + ...span, + // Clean up the span name - remove redundant workflow name prefixes and improve formatting + name: this.cleanChildSpanName(span.name, childWorkflowName), + // Add metadata to indicate this is from a child workflow + metadata: { + ...span.metadata, + isFromChildWorkflow: true, + childWorkflowName, + }, + } + + // Recursively transform any children + if (span.children && Array.isArray(span.children)) { + transformedSpan.children = span.children.map((childSpan: any) => + this.transformSpanForChildWorkflow(childSpan, childWorkflowName) + ) + } + + // Preserve childTraceSpans in output if they exist (for nested workflow blocks) + // This ensures deeply nested workflows maintain their structure + if (span.output?.childTraceSpans) { + transformedSpan.output = { + ...transformedSpan.output, + childTraceSpans: span.output.childTraceSpans, // Keep the original childTraceSpans + } + } + + return transformedSpan + } + + /** + * Cleans up child span names to be more readable and less verbose + */ + private cleanChildSpanName(spanName: string, childWorkflowName: string): string { + // If the span name already contains the workflow name, clean it up + if (spanName.includes(`${childWorkflowName}:`)) { + // Remove the workflow name prefix and clean up the remaining name + const cleanName = spanName.replace(`${childWorkflowName}:`, '').trim() + + // Handle special cases for better readability + if (cleanName === 'Workflow Execution') { + return `${childWorkflowName} workflow` + } + + // For agent blocks, make it clearer + if (cleanName.startsWith('Agent ')) { + return `${cleanName}` + } + + // For other blocks, return clean name without prefix + return `${cleanName}` + } + + // If span name doesn't contain workflow name, add it for context + if (spanName === 'Workflow Execution') { + return `${childWorkflowName} workflow` + } + + // For regular blocks without workflow name prefix, return clean name + return `${spanName}` + } + /** * Maps child workflow output to parent block output format */ @@ -228,7 +341,8 @@ export class WorkflowBlockHandler implements BlockHandler { childResult: any, childWorkflowId: string, childWorkflowName: string, - duration: number + duration: number, + childTraceSpans?: any[] ): BlockOutput { const success = childResult.success !== false @@ -249,10 +363,12 @@ export class WorkflowBlockHandler implements BlockHandler { } // Return a properly structured response with all required fields + // Include child trace spans so they can be integrated into parent execution logs return { success: true, childWorkflowName, result, + childTraceSpans: childTraceSpans || [], } as Record } } diff --git a/apps/sim/executor/index.ts b/apps/sim/executor/index.ts index b7d752b9dcc..da9440fca18 100644 --- a/apps/sim/executor/index.ts +++ b/apps/sim/executor/index.ts @@ -1,5 +1,6 @@ import { BlockPathCalculator } from '@/lib/block-path-calculator' import { createLogger } from '@/lib/logs/console/logger' +import type { TraceSpan } from '@/lib/logs/types' import type { BlockOutput } from '@/blocks/types' import { BlockType } from '@/executor/consts' import { @@ -1510,6 +1511,9 @@ export class Executor { blockLog.durationMs = Math.round(executionTime) blockLog.endedAt = new Date().toISOString() + // Handle child workflow logs integration + this.integrateChildWorkflowLogs(block, output) + context.blockLogs.push(blockLog) // Skip console logging for infrastructure blocks like loops and parallels @@ -1617,6 +1621,9 @@ export class Executor { blockLog.durationMs = Math.round(executionTime) blockLog.endedAt = new Date().toISOString() + // Handle child workflow logs integration + this.integrateChildWorkflowLogs(block, output) + context.blockLogs.push(blockLog) // Skip console logging for infrastructure blocks like loops and parallels @@ -2003,4 +2010,39 @@ export class Executor { context.blockLogs.push(starterBlockLog) } } + + /** + * Integrates child workflow logs into parent execution context + * This stores child trace spans in the block output so they can be properly nested + * when trace spans are built, rather than adding them as separate root-level logs + */ + private integrateChildWorkflowLogs(block: SerializedBlock, output: NormalizedBlockOutput): void { + // Only process workflow blocks + if (block.metadata?.id !== BlockType.WORKFLOW) { + return + } + + // Check if output contains child trace spans + if (!output || typeof output !== 'object' || !output.childTraceSpans) { + return + } + + const childTraceSpans = output.childTraceSpans as TraceSpan[] + if (!Array.isArray(childTraceSpans) || childTraceSpans.length === 0) { + return + } + + // Instead of adding child logs directly to context.blockLogs, we keep the childTraceSpans + // in the output. The buildTraceSpans function will be modified to handle this properly + // and nest them under the workflow block span. + logger.info( + `Child workflow trace spans preserved in workflow block output for proper nesting`, + { + blockId: block.id, + blockName: block.metadata?.name, + childWorkflowName: output.childWorkflowName, + childSpansCount: childTraceSpans.length, + } + ) + } } diff --git a/apps/sim/lib/logs/execution/trace-spans/trace-spans.ts b/apps/sim/lib/logs/execution/trace-spans/trace-spans.ts index 04553f48f1b..a0443ab4c69 100644 --- a/apps/sim/lib/logs/execution/trace-spans/trace-spans.ts +++ b/apps/sim/lib/logs/execution/trace-spans/trace-spans.ts @@ -115,6 +115,45 @@ export function buildTraceSpans(result: ExecutionResult): { }) } + // Handle child workflow spans for workflow blocks + if ( + log.blockType === 'workflow' && + log.output?.childTraceSpans && + Array.isArray(log.output.childTraceSpans) + ) { + // Convert child trace spans to be direct children of this workflow block span + const childTraceSpans = log.output.childTraceSpans as TraceSpan[] + + // Process child workflow spans and add them as children + const flatChildSpans: TraceSpan[] = [] + childTraceSpans.forEach((childSpan) => { + // Skip the synthetic workflow span wrapper - we only want the actual block executions + if (childSpan.type === 'workflow' && childSpan.name === 'Workflow Execution') { + // Add its children directly, skipping the synthetic wrapper + if (childSpan.children && Array.isArray(childSpan.children)) { + flatChildSpans.push(...childSpan.children) + } + } else { + // This is a regular span, add it directly + // But first, ensure nested workflow blocks in this span are also processed + const processedSpan = ensureNestedWorkflowsProcessed(childSpan) + flatChildSpans.push(processedSpan) + } + }) + + // Add the child spans as children of this workflow block + span.children = flatChildSpans + + logger.debug( + `Added ${flatChildSpans.length} child workflow spans to workflow block ${span.id}`, + { + blockId: log.blockId, + blockType: log.blockType, + childWorkflowName: log.output.childWorkflowName, + } + ) + } + // Enhanced approach: Use timeSegments for sequential flow if available // This provides the actual model→tool→model execution sequence if ( @@ -384,6 +423,45 @@ export function buildTraceSpans(result: ExecutionResult): { return { traceSpans: rootSpans, totalDuration } } +// Helper function to recursively process nested workflow blocks in trace spans +function ensureNestedWorkflowsProcessed(span: TraceSpan): TraceSpan { + // Create a copy to avoid mutating the original + const processedSpan = { ...span } + + // If this is a workflow block and it has childTraceSpans in its output, process them + if ( + span.type === 'workflow' && + span.output?.childTraceSpans && + Array.isArray(span.output.childTraceSpans) + ) { + const childTraceSpans = span.output.childTraceSpans as TraceSpan[] + const nestedChildren: TraceSpan[] = [] + + childTraceSpans.forEach((childSpan) => { + // Skip synthetic workflow wrappers and get the actual blocks + if (childSpan.type === 'workflow' && childSpan.name === 'Workflow Execution') { + if (childSpan.children && Array.isArray(childSpan.children)) { + // Recursively process each child to handle deeper nesting + childSpan.children.forEach((grandchildSpan) => { + nestedChildren.push(ensureNestedWorkflowsProcessed(grandchildSpan)) + }) + } + } else { + // Regular span, recursively process it for potential deeper nesting + nestedChildren.push(ensureNestedWorkflowsProcessed(childSpan)) + } + }) + + // Set the processed children on this workflow block + processedSpan.children = nestedChildren + } else if (span.children && Array.isArray(span.children)) { + // Recursively process regular children too + processedSpan.children = span.children.map((child) => ensureNestedWorkflowsProcessed(child)) + } + + return processedSpan +} + export function stripCustomToolPrefix(name: string) { return name.startsWith('custom_') ? name.replace('custom_', '') : name } From b1614689843fab7f0930b74d0c54a3c5867cde30 Mon Sep 17 00:00:00 2001 From: waleedlatif1 Date: Tue, 19 Aug 2025 15:58:44 -0700 Subject: [PATCH 2/6] indent input/output in trace spans display --- .../trace-spans/trace-spans-display.tsx | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/apps/sim/app/workspace/[workspaceId]/logs/components/trace-spans/trace-spans-display.tsx b/apps/sim/app/workspace/[workspaceId]/logs/components/trace-spans/trace-spans-display.tsx index 36a66245e5e..8cef4077525 100644 --- a/apps/sim/app/workspace/[workspaceId]/logs/components/trace-spans/trace-spans-display.tsx +++ b/apps/sim/app/workspace/[workspaceId]/logs/components/trace-spans/trace-spans-display.tsx @@ -82,14 +82,21 @@ function transformBlockData(data: any, blockType: string, isInput: boolean) { interface CollapsibleInputOutputProps { span: TraceSpan spanId: string + depth: number } -function CollapsibleInputOutput({ span, spanId }: CollapsibleInputOutputProps) { +function CollapsibleInputOutput({ span, spanId, depth }: CollapsibleInputOutputProps) { const [inputExpanded, setInputExpanded] = useState(false) const [outputExpanded, setOutputExpanded] = useState(false) + // Calculate the left margin based on depth to match the parent span's indentation + const leftMargin = depth * 16 + 8 + 24 // Base depth indentation + icon width + extra padding + return ( -
+
{/* Input Data - Collapsible */} {span.input && (
@@ -592,7 +599,9 @@ function TraceSpanItem({ {expanded && (
{/* Block Input/Output Data - Collapsible */} - {(span.input || span.output) && } + {(span.input || span.output) && ( + + )} {/* Children and tool calls */} {/* Render child spans */} From 864bf33951feaf41a49f9538768e9549135d409a Mon Sep 17 00:00:00 2001 From: waleedlatif1 Date: Tue, 19 Aug 2025 16:07:08 -0700 Subject: [PATCH 3/6] better color scheme for workflow logs --- .../trace-spans/trace-spans-display.tsx | 30 +++++++++++-------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/apps/sim/app/workspace/[workspaceId]/logs/components/trace-spans/trace-spans-display.tsx b/apps/sim/app/workspace/[workspaceId]/logs/components/trace-spans/trace-spans-display.tsx index 8cef4077525..0d3361816b9 100644 --- a/apps/sim/app/workspace/[workspaceId]/logs/components/trace-spans/trace-spans-display.tsx +++ b/apps/sim/app/workspace/[workspaceId]/logs/components/trace-spans/trace-spans-display.tsx @@ -169,26 +169,30 @@ function BlockDataDisplay({ if (value === undefined) return undefined if (typeof value === 'string') { - return "{value}" + return "{value}" } if (typeof value === 'number') { - return {value} + return {value} } if (typeof value === 'boolean') { - return {value.toString()} + return ( + {value.toString()} + ) } if (Array.isArray(value)) { if (value.length === 0) return [] return ( -
+
[ -
+
{value.map((item, index) => ( -
- {index}: +
+ + {index}: +
{renderValue(item)}
))} @@ -203,10 +207,10 @@ function BlockDataDisplay({ if (entries.length === 0) return {'{}'} return ( -
+
{entries.map(([objKey, objValue]) => ( -
- +
+ {objKey}:
{renderValue(objValue, objKey)}
@@ -234,12 +238,12 @@ function BlockDataDisplay({ {transformedData && Object.keys(transformedData).filter((key) => key !== 'error' && key !== 'success') .length > 0 && ( -
+
{Object.entries(transformedData) .filter(([key]) => key !== 'error' && key !== 'success') .map(([key, value]) => ( -
- {key}: +
+ {key}: {renderValue(value, key)}
))} From 020b5674745f2ef20817800d8658ba5d181fadcb Mon Sep 17 00:00:00 2001 From: waleedlatif1 Date: Tue, 19 Aug 2025 16:16:40 -0700 Subject: [PATCH 4/6] scroll behavior in sidebar updated --- .../w/components/sidebar/sidebar.tsx | 48 ++++++++++++++----- 1 file changed, 37 insertions(+), 11 deletions(-) diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/sidebar.tsx b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/sidebar.tsx index 7eff8464613..89b58db560d 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/sidebar.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/sidebar.tsx @@ -720,21 +720,47 @@ export function Sidebar() { `[data-workflow-id="${workflowId}"]` ) as HTMLElement if (activeWorkflow) { - activeWorkflow.scrollIntoView({ - block: 'start', - }) - - // Adjust scroll position to eliminate the small gap at the top - const scrollViewport = scrollContainer.querySelector( - '[data-radix-scroll-area-viewport]' - ) as HTMLElement - if (scrollViewport && scrollViewport.scrollTop > 0) { - scrollViewport.scrollTop = Math.max(0, scrollViewport.scrollTop - 8) + // Check if this is a newly created workflow (created within the last 5 seconds) + const currentWorkflow = workflows[workflowId] + const isNewlyCreated = + currentWorkflow && + currentWorkflow.lastModified instanceof Date && + Date.now() - currentWorkflow.lastModified.getTime() < 5000 // 5 seconds + + if (isNewlyCreated) { + // For newly created workflows, use the original behavior - scroll to top + activeWorkflow.scrollIntoView({ + block: 'start', + }) + + // Adjust scroll position to eliminate the small gap at the top + const scrollViewport = scrollContainer.querySelector( + '[data-radix-scroll-area-viewport]' + ) as HTMLElement + if (scrollViewport && scrollViewport.scrollTop > 0) { + scrollViewport.scrollTop = Math.max(0, scrollViewport.scrollTop - 8) + } + } else { + // For existing workflows, check if already visible and scroll minimally + const containerRect = scrollContainer.getBoundingClientRect() + const workflowRect = activeWorkflow.getBoundingClientRect() + + // Only scroll if the workflow is not fully visible + const isFullyVisible = + workflowRect.top >= containerRect.top && workflowRect.bottom <= containerRect.bottom + + if (!isFullyVisible) { + // Use 'nearest' to scroll minimally - only bring into view, don't force to top + activeWorkflow.scrollIntoView({ + block: 'nearest', + behavior: 'smooth', + }) + } } } } } - }, [workflowId, isLoading]) + }, [workflowId, isLoading, workflows]) const [showSettings, setShowSettings] = useState(false) const [showHelp, setShowHelp] = useState(false) From 8c530c19a4956e3b7051333199778f2eb5c0cdb1 Mon Sep 17 00:00:00 2001 From: waleedlatif1 Date: Tue, 19 Aug 2025 16:29:34 -0700 Subject: [PATCH 5/6] cleanup --- .../handlers/workflow/workflow-handler.ts | 58 ++----------------- apps/sim/executor/index.ts | 19 +----- .../logs/execution/trace-spans/trace-spans.ts | 44 -------------- 3 files changed, 6 insertions(+), 115 deletions(-) diff --git a/apps/sim/executor/handlers/workflow/workflow-handler.ts b/apps/sim/executor/handlers/workflow/workflow-handler.ts index a2e90c3496a..9467a5740c0 100644 --- a/apps/sim/executor/handlers/workflow/workflow-handler.ts +++ b/apps/sim/executor/handlers/workflow/workflow-handler.ts @@ -105,13 +105,9 @@ export class WorkflowBlockHandler implements BlockHandler { // Remove current execution from stack after completion WorkflowBlockHandler.executionStack.delete(executionId) - // Log execution completion logger.info(`Child workflow ${childWorkflowName} completed in ${Math.round(duration)}ms`) - // Capture child workflow logs and build trace spans for integration into parent logs const childTraceSpans = this.captureChildWorkflowLogs(result, childWorkflowName, context) - - // Map child workflow output to parent block output const mappedResult = this.mapChildOutputToParent( result, workflowId, @@ -120,7 +116,6 @@ export class WorkflowBlockHandler implements BlockHandler { childTraceSpans ) - // If the child workflow failed, throw an error to trigger proper error handling in the parent if ((mappedResult as any).success === false) { const childError = (mappedResult as any).error || 'Unknown error' throw new Error(`Error in child workflow "${childWorkflowName}": ${childError}`) @@ -130,19 +125,13 @@ export class WorkflowBlockHandler implements BlockHandler { } catch (error: any) { logger.error(`Error executing child workflow ${workflowId}:`, error) - // Clean up execution stack in case of error const executionId = `${context.workflowId}_sub_${workflowId}_${block.id}` WorkflowBlockHandler.executionStack.delete(executionId) - - // Get workflow name for error reporting const { workflows } = useWorkflowRegistry.getState() const workflowMetadata = workflows[workflowId] const childWorkflowName = workflowMetadata?.name || workflowId - // Enhance error message with child workflow context const originalError = error.message || 'Unknown error' - - // Check if error message already has child workflow context to avoid duplication if (originalError.startsWith('Error in child workflow')) { throw error // Re-throw as-is to avoid duplication } @@ -156,12 +145,9 @@ export class WorkflowBlockHandler implements BlockHandler { */ private async loadChildWorkflow(workflowId: string) { try { - // Fetch workflow from API with internal authentication header const headers: Record = { 'Content-Type': 'application/json', } - - // Add internal auth header for server-side calls if (typeof window === 'undefined') { const token = await generateInternalToken() headers.Authorization = `Bearer ${token}` @@ -187,16 +173,12 @@ export class WorkflowBlockHandler implements BlockHandler { } logger.info(`Loaded child workflow: ${workflowData.name} (${workflowId})`) - - // Extract the workflow state (API returns normalized data in state field) const workflowState = workflowData.state if (!workflowState || !workflowState.blocks) { logger.error(`Child workflow ${workflowId} has invalid state`) return null } - - // Use blocks directly since API returns data from normalized tables const serializedWorkflow = this.serializer.serializeWorkflow( workflowState.blocks, workflowState.edges || [], @@ -227,8 +209,7 @@ export class WorkflowBlockHandler implements BlockHandler { } /** - * Captures child workflow execution logs and converts them to trace spans - * that can be integrated into the parent workflow's execution context + * Captures and transforms child workflow logs into trace spans */ private captureChildWorkflowLogs( childResult: any, @@ -236,30 +217,20 @@ export class WorkflowBlockHandler implements BlockHandler { parentContext: ExecutionContext ): any[] { try { - // Child workflow results contain execution logs that we need to capture if (!childResult.logs || !Array.isArray(childResult.logs)) { - logger.debug(`No child workflow logs found for ${childWorkflowName}`) return [] } - // Build trace spans from child workflow logs using the same logic as parent workflows const { traceSpans } = buildTraceSpans(childResult) if (!traceSpans || traceSpans.length === 0) { - logger.debug(`No trace spans generated from child workflow logs for ${childWorkflowName}`) return [] } - // Transform child trace spans to have a child workflow context - // This helps distinguish them in the parent workflow's log viewer const transformedSpans = traceSpans.map((span: any) => { return this.transformSpanForChildWorkflow(span, childWorkflowName) }) - logger.info( - `Captured ${transformedSpans.length} trace spans from child workflow: ${childWorkflowName}` - ) - return transformedSpans } catch (error) { logger.error(`Error capturing child workflow logs for ${childWorkflowName}:`, error) @@ -268,14 +239,12 @@ export class WorkflowBlockHandler implements BlockHandler { } /** - * Transforms a trace span to indicate it came from a child workflow + * Transforms trace span for child workflow context */ private transformSpanForChildWorkflow(span: any, childWorkflowName: string): any { const transformedSpan = { ...span, - // Clean up the span name - remove redundant workflow name prefixes and improve formatting name: this.cleanChildSpanName(span.name, childWorkflowName), - // Add metadata to indicate this is from a child workflow metadata: { ...span.metadata, isFromChildWorkflow: true, @@ -283,19 +252,16 @@ export class WorkflowBlockHandler implements BlockHandler { }, } - // Recursively transform any children if (span.children && Array.isArray(span.children)) { transformedSpan.children = span.children.map((childSpan: any) => this.transformSpanForChildWorkflow(childSpan, childWorkflowName) ) } - // Preserve childTraceSpans in output if they exist (for nested workflow blocks) - // This ensures deeply nested workflows maintain their structure if (span.output?.childTraceSpans) { transformedSpan.output = { ...transformedSpan.output, - childTraceSpans: span.output.childTraceSpans, // Keep the original childTraceSpans + childTraceSpans: span.output.childTraceSpans, } } @@ -303,39 +269,32 @@ export class WorkflowBlockHandler implements BlockHandler { } /** - * Cleans up child span names to be more readable and less verbose + * Cleans up child span names for readability */ private cleanChildSpanName(spanName: string, childWorkflowName: string): string { - // If the span name already contains the workflow name, clean it up if (spanName.includes(`${childWorkflowName}:`)) { - // Remove the workflow name prefix and clean up the remaining name const cleanName = spanName.replace(`${childWorkflowName}:`, '').trim() - // Handle special cases for better readability if (cleanName === 'Workflow Execution') { return `${childWorkflowName} workflow` } - // For agent blocks, make it clearer if (cleanName.startsWith('Agent ')) { return `${cleanName}` } - // For other blocks, return clean name without prefix return `${cleanName}` } - // If span name doesn't contain workflow name, add it for context if (spanName === 'Workflow Execution') { return `${childWorkflowName} workflow` } - // For regular blocks without workflow name prefix, return clean name return `${spanName}` } /** - * Maps child workflow output to parent block output format + * Maps child workflow output to parent block output */ private mapChildOutputToParent( childResult: any, @@ -345,8 +304,6 @@ export class WorkflowBlockHandler implements BlockHandler { childTraceSpans?: any[] ): BlockOutput { const success = childResult.success !== false - - // If child workflow failed, return minimal output if (!success) { logger.warn(`Child workflow ${childWorkflowName} failed`) return { @@ -355,15 +312,10 @@ export class WorkflowBlockHandler implements BlockHandler { error: childResult.error || 'Child workflow execution failed', } as Record } - - // Extract the actual result content from the flattened structure let result = childResult if (childResult?.output) { result = childResult.output } - - // Return a properly structured response with all required fields - // Include child trace spans so they can be integrated into parent execution logs return { success: true, childWorkflowName, diff --git a/apps/sim/executor/index.ts b/apps/sim/executor/index.ts index da9440fca18..37eba4102c3 100644 --- a/apps/sim/executor/index.ts +++ b/apps/sim/executor/index.ts @@ -2012,17 +2012,13 @@ export class Executor { } /** - * Integrates child workflow logs into parent execution context - * This stores child trace spans in the block output so they can be properly nested - * when trace spans are built, rather than adding them as separate root-level logs + * Preserves child workflow trace spans for proper nesting */ private integrateChildWorkflowLogs(block: SerializedBlock, output: NormalizedBlockOutput): void { - // Only process workflow blocks if (block.metadata?.id !== BlockType.WORKFLOW) { return } - // Check if output contains child trace spans if (!output || typeof output !== 'object' || !output.childTraceSpans) { return } @@ -2031,18 +2027,5 @@ export class Executor { if (!Array.isArray(childTraceSpans) || childTraceSpans.length === 0) { return } - - // Instead of adding child logs directly to context.blockLogs, we keep the childTraceSpans - // in the output. The buildTraceSpans function will be modified to handle this properly - // and nest them under the workflow block span. - logger.info( - `Child workflow trace spans preserved in workflow block output for proper nesting`, - { - blockId: block.id, - blockName: block.metadata?.name, - childWorkflowName: output.childWorkflowName, - childSpansCount: childTraceSpans.length, - } - ) } } diff --git a/apps/sim/lib/logs/execution/trace-spans/trace-spans.ts b/apps/sim/lib/logs/execution/trace-spans/trace-spans.ts index a0443ab4c69..63a97511306 100644 --- a/apps/sim/lib/logs/execution/trace-spans/trace-spans.ts +++ b/apps/sim/lib/logs/execution/trace-spans/trace-spans.ts @@ -90,29 +90,14 @@ export function buildTraceSpans(result: ExecutionResult): { // Always add cost, token, and model information if available (regardless of provider timing) if (log.output?.cost) { ;(span as any).cost = log.output.cost - logger.debug(`Added cost to span ${span.id}`, { - blockId: log.blockId, - blockType: log.blockType, - cost: log.output.cost, - }) } if (log.output?.tokens) { ;(span as any).tokens = log.output.tokens - logger.debug(`Added tokens to span ${span.id}`, { - blockId: log.blockId, - blockType: log.blockType, - tokens: log.output.tokens, - }) } if (log.output?.model) { ;(span as any).model = log.output.model - logger.debug(`Added model to span ${span.id}`, { - blockId: log.blockId, - blockType: log.blockType, - model: log.output.model, - }) } // Handle child workflow spans for workflow blocks @@ -143,15 +128,6 @@ export function buildTraceSpans(result: ExecutionResult): { // Add the child spans as children of this workflow block span.children = flatChildSpans - - logger.debug( - `Added ${flatChildSpans.length} child workflow spans to workflow block ${span.id}`, - { - blockId: log.blockId, - blockType: log.blockType, - childWorkflowName: log.output.childWorkflowName, - } - ) } // Enhanced approach: Use timeSegments for sequential flow if available @@ -202,20 +178,6 @@ export function buildTraceSpans(result: ExecutionResult): { status: 'success', } }) - - logger.debug( - `Created ${span.children?.length || 0} sequential segments for span ${span.id}`, - { - blockId: log.blockId, - blockType: log.blockType, - segments: - span.children?.map((child) => ({ - name: child.name, - type: child.type, - duration: child.duration, - })) || [], - } - ) } else { // Fallback: Extract tool calls using the original approach for backwards compatibility // Tool calls handling for different formats: @@ -276,12 +238,6 @@ export function buildTraceSpans(result: ExecutionResult): { } }) .filter(Boolean) // Remove any null entries from failed processing - - logger.debug(`Added ${span.toolCalls?.length || 0} tool calls to span ${span.id}`, { - blockId: log.blockId, - blockType: log.blockType, - toolCallNames: span.toolCalls?.map((tc) => tc.name) || [], - }) } } From 776b60b017a7a4e954bbb892088dae52774bcf72 Mon Sep 17 00:00:00 2001 From: waleedlatif1 Date: Tue, 19 Aug 2025 17:40:42 -0700 Subject: [PATCH 6/6] fixed failing tests --- apps/sim/executor/handlers/workflow/workflow-handler.test.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/sim/executor/handlers/workflow/workflow-handler.test.ts b/apps/sim/executor/handlers/workflow/workflow-handler.test.ts index e53dc8271f2..d6df0cd77d6 100644 --- a/apps/sim/executor/handlers/workflow/workflow-handler.test.ts +++ b/apps/sim/executor/handlers/workflow/workflow-handler.test.ts @@ -209,6 +209,7 @@ describe('WorkflowBlockHandler', () => { success: true, childWorkflowName: 'Child Workflow', result: { data: 'test result' }, + childTraceSpans: [], }) }) @@ -248,6 +249,7 @@ describe('WorkflowBlockHandler', () => { success: true, childWorkflowName: 'Child Workflow', result: { nested: 'data' }, + childTraceSpans: [], }) }) })