diff options
| author | Adam Malczewski <[email protected]> | 2026-06-05 15:35:21 +0900 |
|---|---|---|
| committer | Adam Malczewski <[email protected]> | 2026-06-05 15:35:21 +0900 |
| commit | 2bf8f9ec9714cbd33a0ecfb7b40dd7a335180fb7 (patch) | |
| tree | c9b0a6203621df44a0687e1aed1d8d4d923c8297 /packages/kernel/src/runtime/run-turn.ts | |
| parent | 20c6c675a11b887c603be5ff08165cb182c7db65 (diff) | |
| download | dispatch-2bf8f9ec9714cbd33a0ecfb7b40dd7a335180fb7.tar.gz dispatch-2bf8f9ec9714cbd33a0ecfb7b40dd7a335180fb7.zip | |
fix(observability): nest turn/step/prompt/provider.request spans into a tree (+ buildSpanOpen parent propagation)
run-turn: step is now turnSpan.child; prompt/provider.request/tool-call are step's children (stepSpan.log passed into provider.stream). logger.ts: buildSpanOpen now propagates the child's computed parentSpanId onto the span-open record — a latent bug where span.child(...) never set parentSpanId on open (close was already correct).
Verified: tsc -b clean, 279 tests, biome 0/0. Live: span tree turn->step->{prompt,provider.request}; the trace CLI easy-view renders the nesting.
Diffstat (limited to 'packages/kernel/src/runtime/run-turn.ts')
| -rw-r--r-- | packages/kernel/src/runtime/run-turn.ts | 32 |
1 files changed, 20 insertions, 12 deletions
diff --git a/packages/kernel/src/runtime/run-turn.ts b/packages/kernel/src/runtime/run-turn.ts index 5e60641..01b280b 100644 --- a/packages/kernel/src/runtime/run-turn.ts +++ b/packages/kernel/src/runtime/run-turn.ts @@ -78,7 +78,7 @@ interface StepContext { readonly conversationId: string; readonly turnId: string; readonly logger: Logger; - readonly stepLogger: Logger | undefined; + readonly turnSpan: Span | undefined; readonly toolSpans: Map<string, Span>; } @@ -96,6 +96,7 @@ function processEvent( toolCalls: ToolCall[], dispatcher: StepDispatcher, ctx: StepContext, + stepSpan: Span | undefined, ): void { switch (event.type) { case "text-delta": @@ -129,12 +130,18 @@ function processEvent( ), ); - // Open a tool-call span (attrs: name, toolCallId) + // Open a tool-call span as a child of the step span (attrs: name, toolCallId) try { - const tcSpan = ctx.logger.span("tool-call", { - name: event.toolName, - toolCallId: event.toolCallId, - }); + const tcSpan = + stepSpan !== undefined + ? stepSpan.child("tool-call", { + name: event.toolName, + toolCallId: event.toolCallId, + }) + : ctx.logger.span("tool-call", { + name: event.toolName, + toolCallId: event.toolCallId, + }); ctx.toolSpans.set(event.toolCallId, tcSpan); } catch { // Swallow — D7: logging never breaks the turn. @@ -167,11 +174,12 @@ async function executeStep(ctx: StepContext): Promise<StepResult> { let stepUsage = zeroUsage(); let finishReason = "stop"; - // Open a step span; capture the verbatim pre-mutation prompt via a - // "prompt" child span whose body holds the serialized messages+tools. + // Open a step span as a child of the turn span; capture the verbatim + // pre-mutation prompt via a "prompt" child span whose body holds the + // serialized messages+tools. let stepSpan: Span | undefined; try { - stepSpan = ctx.logger.span("step"); + stepSpan = ctx.turnSpan !== undefined ? ctx.turnSpan.child("step") : ctx.logger.span("step"); const promptBody = JSON.stringify({ messages: ctx.messages, tools: ctx.tools }); const promptSpan = stepSpan.child( "prompt", @@ -198,12 +206,12 @@ async function executeStep(ctx: StepContext): Promise<StepResult> { try { const opts = { - ...(ctx.stepLogger !== undefined ? { logger: ctx.stepLogger } : {}), + ...(ctx.turnSpan !== undefined && stepSpan !== undefined ? { logger: stepSpan.log } : {}), }; const stream = ctx.provider.stream(ctx.messages, ctx.tools, opts); for await (const event of stream) { if (ctx.signal.aborted) break; - processEvent(event, chunks, toolCalls, dispatcher, ctx); + processEvent(event, chunks, toolCalls, dispatcher, ctx, stepSpan); if (event.type === "usage") { stepUsage = addUsage(stepUsage, event.usage); } @@ -354,7 +362,7 @@ export async function runTurn(input: RunTurnInput): Promise<RunTurnResult> { conversationId, turnId, logger: turnSpan?.log ?? logger ?? createNoopLogger(), - stepLogger: logger, + turnSpan, toolSpans, }); |
