Skip to content

fix(scorers): detect AI SDK v5 tool parts in toolCallAccuracy - #1364

Open
VihaanAgarwal wants to merge 1 commit into
VoltAgent:mainfrom
VihaanAgarwal:fix/tool-call-accuracy-v5-parts
Open

fix(scorers): detect AI SDK v5 tool parts in toolCallAccuracy#1364
VihaanAgarwal wants to merge 1 commit into
VoltAgent:mainfrom
VihaanAgarwal:fix/tool-call-accuracy-v5-parts

Conversation

@VihaanAgarwal

@VihaanAgarwal VihaanAgarwal commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

While using the code-based toolCallAccuracy scorer on agent outputs, I hit a case where it reported the expected tool as never called even though it was. The cause is in how tool calls are read from message parts.

What was wrong

extractToolNamesFromParts only accepted a part when its type was exactly "tool_call":

const partType = normalizeMessageType(part.type);
if (partType !== "tool_call") {
  continue;
}

AI SDK v5 encodes a tool call in message parts as type: "tool-<toolName>" (for example "tool-getWeather"), which normalizes to "tool_getweather" and never equals "tool_call". So tool calls living in message.parts were skipped, and the scorer scored 0 for a tool that was actually called.

This is inconsistent with the tool-call list path, which already extracts tool-<name> / tool_<name> via extractToolName (covered by the existing "keeps extracting from tool_ type in tool call lists" test).

Fix

Accept tool-typed parts as well, reusing the existing shouldExtractToolNameFromType guard so tool results and streaming events stay excluded:

if (partType !== "tool_call" && !shouldExtractToolNameFromType(partType)) {
  continue;
}

extractToolName then reads the name from the type, exactly as it already does for lists.

Test

Added a regression test that feeds an AI SDK v5 assistant message with a type: "tool-searchProducts" part. It fails on main (score 0) and passes with this change (score 1). Full @voltagent/scorers suite stays green (77 tests).


Summary by cubic

Fixes toolCallAccuracy in @voltagent/scorers to detect tool calls encoded as type: "tool-<name>" in message parts, so expected tools aren’t reported as missing.

  • Bug Fixes
    • Accepts tool-<name> part types when extracting tool names; still excludes tool results and streaming events via shouldExtractToolNameFromType.
    • Adds a regression test for type: "tool-searchProducts"; full suite remains green.

Written for commit ce5afd5. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

  • Bug Fixes
    • Improved tool-call scoring so tool calls are recognized when message parts use the newer tool-<name> format.
    • Prevents valid tool calls from being missed, which makes scorer results more accurate.
    • Added test coverage for the newer message-part format to confirm the scorer returns a full score when expected.

The code-based tool-call-accuracy scorer read tool calls from message
parts only when a part's type was exactly "tool_call". AI SDK v5 encodes a
tool call in message parts as `type: "tool-<toolName>"`, so those parts were
skipped and the scorer reported the expected tool as not called even when it
was. Accept tool-typed parts as well, using the existing extraction that
already reads `tool-<name>` from tool-call lists; tool results and streaming
events stay excluded via shouldExtractToolNameFromType.
@changeset-bot

changeset-bot Bot commented Jun 30, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: ce5afd5

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@voltagent/scorers Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai

coderabbitai Bot commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: ebf9cdac-f05d-4d42-a150-54707e44a562

📥 Commits

Reviewing files that changed from the base of the PR and between af4f6f2 and ce5afd5.

📒 Files selected for processing (3)
  • .changeset/tool-call-accuracy-v5-parts.md
  • packages/scorers/src/tool-call-accuracy.spec.ts
  • packages/scorers/src/tool-call-accuracy.ts

📝 Walkthrough

Walkthrough

Extends extractToolNamesFromParts in the @voltagent/scorers package to accept AI SDK v5 message-part types encoded as tool-<toolName> in addition to the existing tool_call type. A test case and a patch changeset are added.

toolCallAccuracy AI SDK v5 part type support

Layer / File(s) Summary
Filter logic fix, test, and changeset
packages/scorers/src/tool-call-accuracy.ts, packages/scorers/src/tool-call-accuracy.spec.ts, .changeset/tool-call-accuracy-v5-parts.md
extractToolNamesFromParts skip condition is changed from requiring partType === "tool_call" to also passing when shouldExtractToolNameFromType returns true, covering tool-<name> formats. A new test verifies a tool-searchProducts part produces a score of 1.

Estimated code review effort

🎯 1 (Trivial) | ⏱️ ~3 minutes

Possibly related PRs

  • VoltAgent/voltagent#1055: Touches the same createToolCallAccuracyScorerCode and extractToolNamesFromParts logic that this PR extends.

Suggested reviewers

  • lzj960515

🐇 A hop through the parts, a fix for the name,
tool-searchProducts joins the scoring game.
No longer ignored by a strict little check,
AI SDK v5 parts pass without wreck!
The rabbit rejoices — all tools are found! 🎉

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed Title clearly summarizes the scorer fix for AI SDK v5 tool-call parts.
Description check ✅ Passed The description covers the bug, fix, and regression test, though the checklist and issue-link fields are not explicitly filled out.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 issues found across 3 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="packages/scorers/src/tool-call-accuracy.ts">

<violation number="1" location="packages/scorers/src/tool-call-accuracy.ts:283">
P1: AI SDK v5 dynamic tool parts with `type: "dynamic-tool"` and a `toolName` field are still skipped because `normalizeMessageType` produces `"dynamic_tool"`, which fails both `"tool_call"` and `shouldExtractToolNameFromType` (`startsWith("tool")` is false). `extractToolName` would otherwise read the `toolName` from these parts, so dynamic tool calls will still score as not called.</violation>

<violation number="2" location="packages/scorers/src/tool-call-accuracy.ts:283">
P1: The stream-event guard `shouldExtractToolNameFromType` rejects valid AI SDK v5 `tool-<toolName>` parts when the tool name collides with excluded prefixes or suffixes (e.g., `input_validator` → `tool_input_validator` matches the `tool_input_` prefix, `generate_delta` matches the `_delta` suffix). This causes false negatives where the scorer silently skips actually-called tools, undermining the fix for AI SDK v5 support.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment on lines +283 to 286
if (partType !== "tool_call" && !shouldExtractToolNameFromType(partType)) {
continue;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: AI SDK v5 dynamic tool parts with type: "dynamic-tool" and a toolName field are still skipped because normalizeMessageType produces "dynamic_tool", which fails both "tool_call" and shouldExtractToolNameFromType (startsWith("tool") is false). extractToolName would otherwise read the toolName from these parts, so dynamic tool calls will still score as not called.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/scorers/src/tool-call-accuracy.ts, line 283:

<comment>AI SDK v5 dynamic tool parts with `type: "dynamic-tool"` and a `toolName` field are still skipped because `normalizeMessageType` produces `"dynamic_tool"`, which fails both `"tool_call"` and `shouldExtractToolNameFromType` (`startsWith("tool")` is false). `extractToolName` would otherwise read the `toolName` from these parts, so dynamic tool calls will still score as not called.</comment>

<file context>
@@ -276,7 +276,11 @@ function extractToolNamesFromParts(parts: unknown[]): string[] {
+    // not `"tool_call"`. Accept those too (extractToolName reads the name from
+    // the type); tool results and streaming events are still excluded because
+    // shouldExtractToolNameFromType returns false for them.
+    if (partType !== "tool_call" && !shouldExtractToolNameFromType(partType)) {
       continue;
     }
</file context>
Suggested change
if (partType !== "tool_call" && !shouldExtractToolNameFromType(partType)) {
continue;
}
// AI SDK v5 encodes a tool call in message parts as `type: "tool-<name>"`,
// not `"tool_call"`. Dynamic tools use `type: "dynamic-tool"` with a
// `toolName` field. Accept those too (extractToolName reads the name from
// the type or toolName); tool results and streaming events are still
// excluded because shouldExtractToolNameFromType returns false for them.
if (partType !== "tool_call" && partType !== "dynamic_tool" && !shouldExtractToolNameFromType(partType)) {
continue;
}

// not `"tool_call"`. Accept those too (extractToolName reads the name from
// the type); tool results and streaming events are still excluded because
// shouldExtractToolNameFromType returns false for them.
if (partType !== "tool_call" && !shouldExtractToolNameFromType(partType)) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: The stream-event guard shouldExtractToolNameFromType rejects valid AI SDK v5 tool-<toolName> parts when the tool name collides with excluded prefixes or suffixes (e.g., input_validatortool_input_validator matches the tool_input_ prefix, generate_delta matches the _delta suffix). This causes false negatives where the scorer silently skips actually-called tools, undermining the fix for AI SDK v5 support.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/scorers/src/tool-call-accuracy.ts, line 283:

<comment>The stream-event guard `shouldExtractToolNameFromType` rejects valid AI SDK v5 `tool-<toolName>` parts when the tool name collides with excluded prefixes or suffixes (e.g., `input_validator` → `tool_input_validator` matches the `tool_input_` prefix, `generate_delta` matches the `_delta` suffix). This causes false negatives where the scorer silently skips actually-called tools, undermining the fix for AI SDK v5 support.</comment>

<file context>
@@ -276,7 +276,11 @@ function extractToolNamesFromParts(parts: unknown[]): string[] {
+    // not `"tool_call"`. Accept those too (extractToolName reads the name from
+    // the type); tool results and streaming events are still excluded because
+    // shouldExtractToolNameFromType returns false for them.
+    if (partType !== "tool_call" && !shouldExtractToolNameFromType(partType)) {
       continue;
     }
</file context>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant