Skip to content

Commit c921292

Browse files
committed
fix(logs): skip span fields not in their expected shape when summarizing
Per-field compaction can spill an oversized modelToolCalls, toolCalls, or providerTiming to a large-value reference; the skeleton now drops such a field instead of reading it.
1 parent 2e2206d commit c921292

2 files changed

Lines changed: 34 additions & 6 deletions

File tree

‎apps/sim/lib/execution/payloads/serializer.test.ts‎

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -418,6 +418,26 @@ describe('compacting span trees', () => {
418418
expect(compacted.childTraceSpans).toHaveLength(1)
419419
})
420420

421+
it('keeps the skeleton when span metadata itself was spilled', async () => {
422+
const spans = spansTooLargeAsAWhole()
423+
const blockSpan = spans[0].children?.[0].children?.[0]
424+
Object.assign(blockSpan ?? {}, {
425+
modelToolCalls: Array.from({ length: 8 }, (_, index) => ({
426+
name: `tool-${index}`,
427+
arguments: { query: 'q'.repeat(1024) },
428+
})),
429+
toolCalls: Array.from({ length: 8 }, (_, index) => ({
430+
name: `tool-${index}`,
431+
input: 'i'.repeat(1024),
432+
})),
433+
providerTiming: { segments: [{ assistantContent: 'a'.repeat(8192) }] },
434+
})
435+
436+
const compacted = await compactBlockOutput({ childTraceSpans: spans }, options)
437+
438+
expectSpanTree(compacted.childTraceSpans)
439+
})
440+
421441
it('keeps nested child workflow trees in the skeleton', async () => {
422442
const nestedWorkflowSpan: TraceSpan = {
423443
id: 'nested-workflow',

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

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ export function stripProviderTimingContent(
2727
...segment
2828
}) => ({
2929
...segment,
30-
...(toolCalls ? { toolCalls: stripModelToolCallArguments(toolCalls) } : {}),
30+
...(Array.isArray(toolCalls) ? { toolCalls: stripModelToolCallArguments(toolCalls) } : {}),
3131
})
3232
),
3333
}
@@ -37,7 +37,9 @@ export function stripProviderTimingContent(
3737
* A trace span tree with every span's content removed: inputs, outputs,
3838
* thinking, error text, tool-call arguments, and provider content. Keeps the
3939
* tree's shape, names, timing, status, and cost, including a nested child
40-
* workflow's spans carried on `output.childTraceSpans`.
40+
* workflow's spans carried on `output.childTraceSpans`. A field that is not in
41+
* its expected shape (for example one spilled to a large-value reference) is
42+
* dropped rather than read.
4143
*/
4244
export function summarizeTraceSpansWithoutIo(traceSpans?: TraceSpan[]): TraceSpan[] | undefined {
4345
if (!traceSpans) {
@@ -62,10 +64,16 @@ export function summarizeTraceSpansWithoutIo(traceSpans?: TraceSpan[]): TraceSpa
6264
...(Array.isArray(nestedSpans) && nestedSpans.length
6365
? { output: { childTraceSpans: summarizeTraceSpansWithoutIo(nestedSpans) } }
6466
: {}),
65-
...(modelToolCalls ? { modelToolCalls: stripModelToolCallArguments(modelToolCalls) } : {}),
66-
...(toolCalls ? { toolCalls: stripLegacyToolCallContent(toolCalls) } : {}),
67-
...(providerTiming ? { providerTiming: stripProviderTimingContent(providerTiming) } : {}),
68-
...(children?.length ? { children: summarizeTraceSpansWithoutIo(children) } : {}),
67+
...(Array.isArray(modelToolCalls)
68+
? { modelToolCalls: stripModelToolCallArguments(modelToolCalls) }
69+
: {}),
70+
...(Array.isArray(toolCalls) ? { toolCalls: stripLegacyToolCallContent(toolCalls) } : {}),
71+
...(Array.isArray(providerTiming?.segments)
72+
? { providerTiming: stripProviderTimingContent(providerTiming) }
73+
: {}),
74+
...(Array.isArray(children) && children.length
75+
? { children: summarizeTraceSpansWithoutIo(children) }
76+
: {}),
6977
}
7078
})
7179
}

0 commit comments

Comments
 (0)