Skip to content

Commit 866ab8c

Browse files
committed
fix passing of spans to be in block log
1 parent 4879a93 commit 866ab8c

5 files changed

Lines changed: 50 additions & 141 deletions

File tree

apps/sim/app/workspace/[workspaceId]/logs/components/log-details/components/trace-spans/trace-spans.tsx

Lines changed: 4 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -57,40 +57,6 @@ function useSetToggle() {
5757
)
5858
}
5959

60-
/**
61-
* Generates a unique key for a trace span
62-
*/
63-
function getSpanKey(span: TraceSpan): string {
64-
if (span.id) {
65-
return span.id
66-
}
67-
const name = span.name || 'span'
68-
const start = span.startTime || 'unknown-start'
69-
const end = span.endTime || 'unknown-end'
70-
return `${name}|${start}|${end}`
71-
}
72-
73-
/**
74-
* Merges multiple arrays of trace span children, deduplicating by span key
75-
*/
76-
function mergeTraceSpanChildren(...groups: TraceSpan[][]): TraceSpan[] {
77-
const merged: TraceSpan[] = []
78-
const seen = new Set<string>()
79-
80-
groups.forEach((group) => {
81-
group.forEach((child) => {
82-
const key = getSpanKey(child)
83-
if (seen.has(key)) {
84-
return
85-
}
86-
seen.add(key)
87-
merged.push(child)
88-
})
89-
})
90-
91-
return merged
92-
}
93-
9460
/**
9561
* Parses a time value to milliseconds
9662
*/
@@ -116,34 +82,16 @@ function hasErrorInTree(span: TraceSpan): boolean {
11682

11783
/**
11884
* Normalizes and sorts trace spans recursively.
119-
* Merges children from both span.children and span.output.childTraceSpans,
120-
* deduplicates them, and sorts by start time.
85+
* Deduplicates children and sorts by start time.
12186
*/
12287
function normalizeAndSortSpans(spans: TraceSpan[]): TraceSpan[] {
12388
return spans
12489
.map((span) => {
12590
const enrichedSpan: TraceSpan = { ...span }
12691

127-
// Clean output by removing childTraceSpans after extracting
128-
if (enrichedSpan.output && typeof enrichedSpan.output === 'object') {
129-
enrichedSpan.output = { ...enrichedSpan.output }
130-
if ('childTraceSpans' in enrichedSpan.output) {
131-
const { childTraceSpans, ...cleanOutput } = enrichedSpan.output as {
132-
childTraceSpans?: TraceSpan[]
133-
} & Record<string, unknown>
134-
enrichedSpan.output = cleanOutput
135-
}
136-
}
137-
138-
// Merge and deduplicate children from both sources
139-
const directChildren = Array.isArray(span.children) ? span.children : []
140-
const outputChildren = Array.isArray(span.output?.childTraceSpans)
141-
? (span.output!.childTraceSpans as TraceSpan[])
142-
: []
143-
144-
const mergedChildren = mergeTraceSpanChildren(directChildren, outputChildren)
145-
enrichedSpan.children =
146-
mergedChildren.length > 0 ? normalizeAndSortSpans(mergedChildren) : undefined
92+
// Process and deduplicate children
93+
const children = Array.isArray(span.children) ? span.children : []
94+
enrichedSpan.children = children.length > 0 ? normalizeAndSortSpans(children) : undefined
14795

14896
return enrichedSpan
14997
})

apps/sim/app/workspace/[workspaceId]/logs/components/log-details/log-details.tsx

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import {
1818
import { ScrollArea } from '@/components/ui/scroll-area'
1919
import { BASE_EXECUTION_CHARGE } from '@/lib/billing/constants'
2020
import { cn } from '@/lib/core/utils/cn'
21+
import { filterHiddenOutputKeys } from '@/lib/logs/execution/trace-spans/trace-spans'
2122
import {
2223
ExecutionSnapshot,
2324
FileCards,
@@ -274,16 +275,13 @@ export const LogDetails = memo(function LogDetails({
274275
return isWorkflowExecutionLog && log?.cost
275276
}, [log, isWorkflowExecutionLog])
276277

277-
// Extract and clean the workflow final output (remove childTraceSpans for cleaner display)
278+
// Extract and clean the workflow final output (recursively remove hidden keys for cleaner display)
278279
const workflowOutput = useMemo(() => {
279280
const executionData = log?.executionData as
280281
| { finalOutput?: Record<string, unknown> }
281282
| undefined
282283
if (!executionData?.finalOutput) return null
283-
const { childTraceSpans, ...cleanOutput } = executionData.finalOutput as {
284-
childTraceSpans?: unknown
285-
} & Record<string, unknown>
286-
return cleanOutput
284+
return filterHiddenOutputKeys(executionData.finalOutput) as Record<string, unknown>
287285
}, [log?.executionData])
288286

289287
useEffect(() => {

apps/sim/executor/execution/block-executor.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -152,9 +152,6 @@ export class BlockExecutor {
152152
blockLog.durationMs = duration
153153
blockLog.success = true
154154
blockLog.output = filterOutputForLog(block.metadata?.id || '', normalizedOutput, { block })
155-
156-
// Extract childTraceSpans for nested workflow execution
157-
// Store separately to keep output clean for display while preserving for trace processing
158155
if (normalizedOutput.childTraceSpans && Array.isArray(normalizedOutput.childTraceSpans)) {
159156
blockLog.childTraceSpans = normalizedOutput.childTraceSpans
160157
}

apps/sim/executor/utils/output-filter.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { filterHiddenOutputKeys } from '@/lib/logs/execution/trace-spans/trace-spans'
12
import { getBlock } from '@/blocks'
23
import { isHiddenFromDisplay } from '@/blocks/types'
34
import { isTriggerBehavior, isTriggerInternalKey } from '@/executor/constants'
@@ -7,6 +8,7 @@ import type { SerializedBlock } from '@/serializer/types'
78
/**
89
* Filters block output for logging/display purposes.
910
* Removes internal fields and fields marked with hiddenFromDisplay.
11+
* Also recursively filters globally hidden keys from nested objects.
1012
*
1113
* @param blockType - The block type string (e.g., 'human_in_the_loop', 'workflow')
1214
* @param output - The raw block output to filter
@@ -44,7 +46,8 @@ export function filterOutputForLog(
4446
continue
4547
}
4648

47-
filtered[key] = value
49+
// Recursively filter globally hidden keys from nested objects
50+
filtered[key] = filterHiddenOutputKeys(value)
4851
}
4952

5053
return filtered

apps/sim/lib/logs/execution/trace-spans/trace-spans.ts

Lines changed: 39 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,39 @@ import type { ExecutionResult } from '@/executor/types'
55

66
const logger = createLogger('TraceSpans')
77

8+
/**
9+
* Keys that should be recursively filtered from output display.
10+
* These are internal fields used for execution tracking that shouldn't be shown to users.
11+
*/
12+
const HIDDEN_OUTPUT_KEYS = new Set(['childTraceSpans'])
13+
14+
/**
15+
* Recursively filters hidden keys from nested objects for cleaner display.
16+
* Used by both executor (for log output) and UI (for display).
17+
*/
18+
export function filterHiddenOutputKeys(value: unknown): unknown {
19+
if (value === null || value === undefined) {
20+
return value
21+
}
22+
23+
if (Array.isArray(value)) {
24+
return value.map((item) => filterHiddenOutputKeys(item))
25+
}
26+
27+
if (typeof value === 'object') {
28+
const filtered: Record<string, unknown> = {}
29+
for (const [key, val] of Object.entries(value as Record<string, unknown>)) {
30+
if (HIDDEN_OUTPUT_KEYS.has(key)) {
31+
continue
32+
}
33+
filtered[key] = filterHiddenOutputKeys(val)
34+
}
35+
return filtered
36+
}
37+
38+
return value
39+
}
40+
841
function isSyntheticWorkflowWrapper(span: TraceSpan | undefined): boolean {
942
if (!span || span.type !== 'workflow') return false
1043
return !span.blockId
@@ -21,43 +54,17 @@ function flattenWorkflowChildren(spans: TraceSpan[]): TraceSpan[] {
2154
return
2255
}
2356

24-
const processedSpan = ensureNestedWorkflowsProcessed(span)
57+
// Recursively process children - spans are already clean from buildTraceSpans
58+
const processedSpan: TraceSpan = {
59+
...span,
60+
children: Array.isArray(span.children) ? flattenWorkflowChildren(span.children) : undefined,
61+
}
2562
flattened.push(processedSpan)
2663
})
2764

2865
return flattened
2966
}
3067

31-
function getTraceSpanKey(span: TraceSpan): string {
32-
if (span.id) {
33-
return span.id
34-
}
35-
36-
const name = span.name || 'span'
37-
const start = span.startTime || 'unknown-start'
38-
const end = span.endTime || 'unknown-end'
39-
40-
return `${name}|${start}|${end}`
41-
}
42-
43-
function mergeTraceSpanChildren(...childGroups: TraceSpan[][]): TraceSpan[] {
44-
const merged: TraceSpan[] = []
45-
const seen = new Set<string>()
46-
47-
childGroups.forEach((group) => {
48-
group.forEach((child) => {
49-
const key = getTraceSpanKey(child)
50-
if (seen.has(key)) {
51-
return
52-
}
53-
seen.add(key)
54-
merged.push(child)
55-
})
56-
})
57-
58-
return merged
59-
}
60-
6168
export function buildTraceSpans(result: ExecutionResult): {
6269
traceSpans: TraceSpan[]
6370
totalDuration: number
@@ -332,7 +339,7 @@ export function buildTraceSpans(result: ExecutionResult): {
332339

333340
if (childTraceSpans) {
334341
const flattenedChildren = flattenWorkflowChildren(childTraceSpans)
335-
span.children = mergeTraceSpanChildren(span.children || [], flattenedChildren)
342+
span.children = flattenedChildren
336343

337344
// Clean childTraceSpans from output if present
338345
if (span.output && typeof span.output === 'object' && 'childTraceSpans' in span.output) {
@@ -741,47 +748,3 @@ function groupIterationBlocks(spans: TraceSpan[]): TraceSpan[] {
741748

742749
return result
743750
}
744-
745-
function ensureNestedWorkflowsProcessed(span: TraceSpan): TraceSpan {
746-
const processedSpan: TraceSpan = { ...span }
747-
748-
if (processedSpan.output && typeof processedSpan.output === 'object') {
749-
processedSpan.output = { ...processedSpan.output }
750-
}
751-
752-
const normalizedChildren = Array.isArray(span.children)
753-
? span.children.map((child) => ensureNestedWorkflowsProcessed(child))
754-
: []
755-
756-
const outputChildSpans = (() => {
757-
if (!processedSpan.output || typeof processedSpan.output !== 'object') {
758-
return [] as TraceSpan[]
759-
}
760-
761-
const maybeChildSpans = (processedSpan.output as { childTraceSpans?: TraceSpan[] })
762-
.childTraceSpans
763-
if (!Array.isArray(maybeChildSpans) || maybeChildSpans.length === 0) {
764-
return [] as TraceSpan[]
765-
}
766-
767-
return flattenWorkflowChildren(maybeChildSpans)
768-
})()
769-
770-
const mergedChildren = mergeTraceSpanChildren(normalizedChildren, outputChildSpans)
771-
772-
if (
773-
processedSpan.output &&
774-
typeof processedSpan.output === 'object' &&
775-
processedSpan.output !== null &&
776-
'childTraceSpans' in processedSpan.output
777-
) {
778-
const { childTraceSpans, ...cleanOutput } = processedSpan.output as {
779-
childTraceSpans?: TraceSpan[]
780-
} & Record<string, unknown>
781-
processedSpan.output = cleanOutput
782-
}
783-
784-
processedSpan.children = mergedChildren.length > 0 ? mergedChildren : undefined
785-
786-
return processedSpan
787-
}

0 commit comments

Comments
 (0)