From d537fb8ae3254d1d6691ee0b7202a2f3ab7ea977 Mon Sep 17 00:00:00 2001 From: ivanmilevtues Date: Sat, 10 Oct 2026 21:27:05 +0200 Subject: [PATCH] feat(agent): count tool calls and failures per tool --- packages/agent/src/agent-loop.ts | 2 ++ packages/agent/src/index.ts | 1 + packages/agent/src/tool-call-stats.ts | 28 +++++++++++++++++++++++++++ 3 files changed, 31 insertions(+) create mode 100644 packages/agent/src/tool-call-stats.ts diff --git a/packages/agent/src/agent-loop.ts b/packages/agent/src/agent-loop.ts index f406aba5647a..d6b962e6d443 100644 --- a/packages/agent/src/agent-loop.ts +++ b/packages/agent/src/agent-loop.ts @@ -16,6 +16,7 @@ import { validateToolArguments, } from "@earendil-works/pi-ai"; import { getDefaultStreamFn } from "./stream-fn.ts"; +import { recordToolCall } from "./tool-call-stats.ts"; import type { AgentContext, AgentEvent, @@ -269,6 +270,7 @@ async function runLoop( ? await failToolCallsFromTruncatedMessage(toolCalls, emit) : await executeToolCalls(currentContext, message, config, signal, emit); toolResults.push(...executedToolBatch.messages); + for (const result of executedToolBatch.messages) recordToolCall(result.toolName, result.isError); hasMoreToolCalls = !executedToolBatch.terminate; for (const result of toolResults) { diff --git a/packages/agent/src/index.ts b/packages/agent/src/index.ts index ca00bc777266..2d39b6f4ed8e 100644 --- a/packages/agent/src/index.ts +++ b/packages/agent/src/index.ts @@ -2,4 +2,5 @@ export * from "./agent.ts"; export * from "./agent-loop.ts"; export * from "./proxy.ts"; export { setDefaultStreamFn } from "./stream-fn.ts"; +export * from "./tool-call-stats.ts"; export * from "./types.ts"; diff --git a/packages/agent/src/tool-call-stats.ts b/packages/agent/src/tool-call-stats.ts new file mode 100644 index 000000000000..9dc59a85d345 --- /dev/null +++ b/packages/agent/src/tool-call-stats.ts @@ -0,0 +1,28 @@ +/** + * Per-tool call counts for the current process, so a session can report which tools the + * agent leaned on and how many of their calls failed. + */ +export interface ToolCallStat { + calls: number; + errors: number; +} + +const stats = new Map(); + +export function recordToolCall(toolName: string, isError: boolean): void { + const stat = stats.get(toolName) ?? { calls: 0, errors: 0 }; + stat.calls += 1; + if (isError) stat.errors += 1; + stats.set(toolName, stat); +} + +/** A snapshot, most-called tool first. */ +export function getToolCallStats(): Array<{ toolName: string } & ToolCallStat> { + return [...stats.entries()] + .map(([toolName, stat]) => ({ toolName, ...stat })) + .sort((a, b) => b.calls - a.calls); +} + +export function resetToolCallStats(): void { + stats.clear(); +}