Skip to content

fix(ag-ui): use input instead of args for tool-call parts#1149

Open
corners99 wants to merge 1 commit intoVoltAgent:mainfrom
corners99:fix/ag-ui-tool-call-input
Open

fix(ag-ui): use input instead of args for tool-call parts#1149
corners99 wants to merge 1 commit intoVoltAgent:mainfrom
corners99:fix/ag-ui-tool-call-input

Conversation

@corners99
Copy link
Contributor

@corners99 corners99 commented Mar 12, 2026

Summary

  • Fixes tool-call message conversion in convertAGUIMessagesToVoltMessages() — renames args to input on tool-call parts to match the AI SDK's ToolCallPart interface

Problem

When CopilotKit replays assistant messages containing tool calls, the ag-ui adapter creates tool-call parts with an args field. The AI SDK's ToolCallPart interface (from @ai-sdk/provider-utils) expects input. When the Anthropic provider builds the API request, it reads part.input which is undefined, causing:

AI_APICallError: messages.N.content.N.tool_use.input: Input should be a valid dictionary

This breaks any multi-turn conversation where the assistant previously called a tool (the replayed message has the wrong field name).

Root Cause

In packages/ag-ui/src/voltagent-agent.ts, convertAGUIMessagesToVoltMessages() line 314-321:

// Before (broken)
const args = safelyParseJson(call.function.arguments);
parts.push({ type: "tool-call", toolCallId: call.id, toolName: call.function.name, args });

// After (fixed)
const input = safelyParseJson(call.function.arguments);
parts.push({ type: "tool-call", toolCallId: call.id, toolName: call.function.name, input });

The AI SDK ToolCallPart type:

interface ToolCallPart {
  type: 'tool-call';
  toolCallId: string;
  toolName: string;
  input: unknown;  // <-- expects "input", not "args"
}

Test plan

  • Verified ToolCallPart interface in @ai-sdk/provider-utils expects input field
  • Verified Anthropic provider reads part.input to construct tool_use blocks
  • Multi-turn conversation with tool calls no longer throws AI_APICallError

🤖 Generated with Claude Code


Summary by cubic

Use input instead of args for tool-call parts in @voltagent/ag-ui message conversion. This matches @ai-sdk/provider-utils ToolCallPart and prevents Anthropic requests from failing when replaying tool calls.

Written for commit 27387c6. Summary will update on new commits.

Summary by CodeRabbit

Release Notes

  • Bug Fixes
    • Fixed an issue where tool call arguments were incorrectly formatted in message conversions, causing API rejections. Tool calls now use the expected input field format.

…age conversion

When converting CopilotKit assistant messages with tool calls to VoltAgent
format, the adapter sets `args` on tool-call parts. The AI SDK's
`ToolCallPart` interface expects `input`, so the Anthropic provider sends
`undefined` as the tool_use input — rejected by the API with:

  "messages.N.content.N.tool_use.input: Input should be a valid dictionary"

This rename aligns with the AI SDK's ToolCallPart interface from
@ai-sdk/provider-utils.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@changeset-bot
Copy link

changeset-bot bot commented Mar 12, 2026

🦋 Changeset detected

Latest commit: 27387c6

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

This PR includes changesets to release 1 package
Name Type
@voltagent/ag-ui 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
Copy link
Contributor

coderabbitai bot commented Mar 12, 2026

📝 Walkthrough

Walkthrough

The change renames the tool-call payload field from args to input in the VoltUI protocol to align with Anthropic provider expectations, preventing API rejections from undefined tool_use.input values.

Changes

Cohort / File(s) Summary
Documentation
.changeset/fix-ag-ui-tool-call-input.md
Added patch note documenting the fix for tool-call input field naming to prevent Anthropic API rejection errors.
Protocol Definition & Implementation
packages/ag-ui/src/voltagent-agent.ts
Updated VoltUIPart type to use input?: unknown instead of args?: unknown for tool-call parts; renamed local variable and field reference from args to input in message conversion logic.

Estimated code review effort

🎯 1 (Trivial) | ⏱️ ~3 minutes

Poem

A rabbit hops with glee so bright,
Renaming args to input right,
The Anthropic API now sees clear,
No more undefined errors here! 🐇✨

🚥 Pre-merge checks | ✅ 2 | ❌ 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 (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically describes the main change: renaming the field from args to input for tool-call parts to match the AI SDK interface.
Description check ✅ Passed The description comprehensively covers the problem, root cause, solution, and test plan, though some checklist items are not marked as completed.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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 and usage tips.

Copy link
Contributor

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

Choose a reason for hiding this comment

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

No issues found across 2 files

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1


ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 5368cdb7-c75b-40a5-91cd-fa06e7044432

📥 Commits

Reviewing files that changed from the base of the PR and between a1b68cc and 27387c6.

📒 Files selected for processing (2)
  • .changeset/fix-ag-ui-tool-call-input.md
  • packages/ag-ui/src/voltagent-agent.ts

Comment on lines +279 to 280
| { type: "tool-call"; toolCallId: string; toolName: string; input?: unknown }
| { type: "tool-result"; toolCallId?: string; toolName?: string; output?: unknown };
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

In the current AI SDK / Anthropic tool-use contract, is tool_use.inputrequired to be a JSON object/dictionary, and what is the TypeScript shape ofToolCallPart.input?

💡 Result:

Anthropic tool-use contract (tool_use.input)

Yes—tool_use.input is required, and it is a parsed JSON object (i.e., a dictionary) containing the arguments for the tool, conforming to the tool’s input_schema. (docs.anthropic.com)

AI SDK TypeScript shape (ToolCallPart.input)

In the AI SDK, ToolCallPart.input is typed as unknown (i.e., not restricted to an object at the type level). The shape is: (app.unpkg.com)

export interface ToolCallPart {
  type: 'tool-call';
  toolCallId: string;
  toolName: string;
  input: unknown;
  providerOptions?: ProviderOptions;
  providerExecuted?: boolean;
}

Citations:


Normalize tool-call input to an object, not unknown.

The Anthropic API requires tool_use.input to be a JSON object, but the current code at line 314 can produce a string, array, primitive, or null via safelyParseJson(), and line 279 allows that invalid value through unchecked with the input?: unknown type. This will cause requests to fail at runtime.

Proposed fix
+type VoltToolInput = Record<string, unknown>;
+
 type VoltUIPart =
   | { type: "text"; text: string }
-  | { type: "tool-call"; toolCallId: string; toolName: string; input?: unknown }
+  | { type: "tool-call"; toolCallId: string; toolName: string; input: VoltToolInput }
   | { type: "tool-result"; toolCallId?: string; toolName?: string; output?: unknown };
@@
-        const input = safelyParseJson(call.function.arguments);
+        const parsedInput = safelyParseJson(call.function.arguments);
+        if (
+          parsedInput === null ||
+          typeof parsedInput !== "object" ||
+          Array.isArray(parsedInput)
+        ) {
+          throw new Error(
+            `Invalid tool-call arguments for "${call.function.name}": expected a JSON object`,
+          );
+        }
         toolNameById.set(call.id, call.function.name);
         parts.push({
           type: "tool-call",
           toolCallId: call.id,
           toolName: call.function.name,
-          input,
+          input: parsedInput,
         });

Lines 279–280, 314–320.

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