From f3b895fbdb40091a8cb429bd98ed32ea445731e1 Mon Sep 17 00:00:00 2001 From: Koji Wakayama Date: Mon, 6 Apr 2026 23:29:44 +0200 Subject: [PATCH] debug(runtime): instrument provider web tool stream payloads --- deno.json | 2 +- src/agent/runtime/ai-stream-handler.ts | 79 ++++++++++++++++++++++++++ src/utils/version-constant.ts | 2 +- 3 files changed, 81 insertions(+), 2 deletions(-) diff --git a/deno.json b/deno.json index 8a93cea1e6..2a0007a4f6 100644 --- a/deno.json +++ b/deno.json @@ -1,6 +1,6 @@ { "name": "veryfront", - "version": "0.1.139", + "version": "0.1.140", "license": "Apache-2.0", "nodeModulesDir": "auto", "exclude": [ diff --git a/src/agent/runtime/ai-stream-handler.ts b/src/agent/runtime/ai-stream-handler.ts index 8c6257cf5e..c66aabf6bd 100644 --- a/src/agent/runtime/ai-stream-handler.ts +++ b/src/agent/runtime/ai-stream-handler.ts @@ -12,7 +12,9 @@ import type { StreamTextResult, ToolSet } from "ai"; import { sendSSE } from "./sse-utils.ts"; import { isDynamicTool } from "./tool-helpers.ts"; import { serverLogger } from "#veryfront/utils"; +import { isAnyDebugEnabled } from "#veryfront/utils/constants/env.ts"; import { setActiveSpanAttributes, withSpan } from "#veryfront/observability/tracing/otlp-setup.ts"; +import { getHostEnv } from "#veryfront/platform/compat/process.ts"; const logger = serverLogger.component("agent"); @@ -115,6 +117,65 @@ function stringifyToolError(output: unknown): string { } } +function summarizeDebugValue(value: unknown): unknown { + if (value instanceof Error) { + return { + name: value.name, + message: value.message, + stack: value.stack, + }; + } + + if (typeof value === "string") { + return value.length > 500 ? `${value.slice(0, 500)}…` : value; + } + + return value; +} + +function logProviderToolPart( + partType: "tool-result" | "tool-error", + part: { + toolCallId: string; + toolName: string; + providerExecuted?: boolean; + dynamic?: boolean; + output?: unknown; + error?: unknown; + input?: unknown; + preliminary?: boolean; + isError?: boolean; + }, +): void { + if (!isAnyDebugEnabled({ get: getHostEnv })) { + return; + } + + if (part.providerExecuted !== true) { + return; + } + + if (part.toolName !== "web_search" && part.toolName !== "web_fetch") { + return; + } + + logger.debug("Provider tool stream part observed", { + partType, + toolCallId: part.toolCallId, + toolName: part.toolName, + providerExecuted: part.providerExecuted, + dynamic: part.dynamic, + preliminary: part.preliminary, + isError: part.isError, + outputType: typeof part.output, + errorType: typeof part.error, + inputType: typeof part.input, + output: summarizeDebugValue(part.output), + error: summarizeDebugValue(part.error), + input: summarizeDebugValue(part.input), + }); +} + export function createStreamState(): AIStreamState { return { accumulatedText: "", @@ -228,6 +289,16 @@ export function processStream( case "tool-result": { const isError = "isError" in part && part.isError === true; + logProviderToolPart("tool-result", { + toolCallId: part.toolCallId, + toolName: part.toolName, + providerExecuted: "providerExecuted" in part ? part.providerExecuted : undefined, + dynamic: "dynamic" in part ? part.dynamic : undefined, + output: part.output, + input: "input" in part ? part.input : undefined, + preliminary: "preliminary" in part ? part.preliminary : undefined, + isError, + }); if (isError) { state.toolResults.push({ toolCallId: part.toolCallId, @@ -278,6 +349,14 @@ export function processStream( } case "tool-error": { + logProviderToolPart("tool-error", { + toolCallId: part.toolCallId, + toolName: part.toolName, + providerExecuted: "providerExecuted" in part ? part.providerExecuted : undefined, + dynamic: "dynamic" in part ? part.dynamic : undefined, + error: part.error, + input: "input" in part ? part.input : undefined, + }); state.toolResults.push({ toolCallId: part.toolCallId, toolName: part.toolName, diff --git a/src/utils/version-constant.ts b/src/utils/version-constant.ts index fdb0f1a53e..cd14c4ea4a 100644 --- a/src/utils/version-constant.ts +++ b/src/utils/version-constant.ts @@ -1,3 +1,3 @@ // Keep in sync with deno.json version. // scripts/release.ts updates this constant during releases. -export const VERSION = "0.1.139"; +export const VERSION = "0.1.140";