Skip to content

Add tool/function calling support for Anthropic adapter#146

Merged
wachterjohannes merged 1 commit into
0.4from
feature/anthropic-tool-support
Jun 1, 2026
Merged

Add tool/function calling support for Anthropic adapter#146
wachterjohannes merged 1 commit into
0.4from
feature/anthropic-tool-support

Conversation

@wachterjohannes

@wachterjohannes wachterjohannes commented Feb 11, 2026

Copy link
Copy Markdown
Member
  • Create ToolFormatter for Anthropic-specific tool format (uses input_schema instead of parameters, no type/function wrapper like OpenAI)
  • Update AnthropicChatAdapter to handle tool requests and responses:
    • Add tools to request parameters when hasTools() is true
    • Handle ToolCallsPart (converts to tool_use content blocks)
    • Handle ToolCallPart (converts to tool_result content blocks)
    • Parse tool_use from response to create AIChatToolCall objects
  • Update Messages.php to allow streaming with tools
  • Update MessagesInterface types to include tool role and improved input_schema
  • Add comprehensive tests for tool support

Summary by CodeRabbit

  • New Features

    • Anthropic adapter adds end-to-end tool/function calling: tools can be sent, invoked, and their results integrated into chats; tool-role messages are supported and mapped for Anthropic.
    • Tool input schema generation enhanced for richer parameter types (nested objects/arrays, nullable, enums).
  • Bug Fixes / Improvements

    • Streaming now supports tool-related content blocks and no longer rejects tool payloads.
  • Tests

    • New unit tests cover tool formatting, request/response mapping, and streaming behavior.

@coderabbitai

coderabbitai Bot commented Feb 11, 2026

Copy link
Copy Markdown

Review Change Stack

Walkthrough

Adds ToolFormatter to emit Anthropic-compatible tool schemas, extends AnthropicChatAdapter to send/receive tool_use/tool_result blocks (including TOOL role mapping), widens streaming/message PHPDoc shapes, and adds unit tests plus PHPStan baseline updates.

Changes

Tool Integration

Layer / File(s) Summary
ToolFormatter implementation
packages/anthropic-adapter/src/Chat/ToolFormatter.php
ToolFormatter::formatTool()/formatTools() and recursive formatParameter() produce an input_schema with properties, nullable types, enums, formats, nested object/array handling, and required lists.
ToolFormatter tests
packages/anthropic-adapter/tests/Unit/Chat/ToolFormatterTest.php
Unit tests covering single/multiple tools, nested object parameters, array-of-objects items, and optional/required parameter behavior.
Adapter: request formatting & serialization
packages/anthropic-adapter/src/Chat/AnthropicChatAdapter.php
Adds imports for tool parts/types, includes AIChatMessageRoleEnum::TOOL in EXPECTED_ROLES, formats tools request parameter, maps TOOL role to Anthropic user, and serializes ToolCallsPart/ToolCallPart to tool_use/tool_result content blocks.
Adapter: response handling & supports()
packages/anthropic-adapter/src/Chat/AnthropicChatAdapter.php
create() now concatenates all text blocks, extracts tool_use blocks into AIChatToolCall[] passed via AIChatResponseMessage, and supports() no longer excludes requests containing tools.
Adapter tests
packages/anthropic-adapter/tests/Unit/Chat/AnthropicChatAdapterTest.php
Adds tests asserting adapter supports tool-enabled requests, request payload structure with tools, tool call/result serialization, TOOL role mapping, and helper getWeatherMethod() for deterministic tool output.
Streaming schemas & Interface
packages/anthropic/src/Resources/Messages.php, packages/anthropic/src/Resources/MessagesInterface.php
createStreamed no longer rejects tools; streaming content_block/content_block_delta PHPDoc types widened to include tool_use/input_json_delta shapes; Tool PHPStan type now allows input_schema.properties as array<string, array<string, mixed>> and optional required.
PHPStan baseline updates
packages/anthropic-adapter/phpstan-baseline.neon, packages/anthropic/phpstan-baseline.neon
Adjusted ignoreErrors entries to match increased test assertion counts and broadened type/message shape patterns related to text/tool_use content blocks and specific assertSame/assertInstanceOf warnings.

Sequence Diagram

sequenceDiagram
    participant Client
    participant Adapter as AnthropicChatAdapter
    participant Formatter as ToolFormatter
    participant API as Anthropic API

    Client->>Adapter: create(request with tools)
    Adapter->>Formatter: formatTools(toolInfo[])
    Formatter->>Formatter: formatTool -> formatParameter (recursive)
    Formatter-->>Adapter: formatted tools array
    Adapter->>API: POST /messages (with tools and messages)
    API-->>Adapter: response with content_blocks (text, tool_use, etc.)
    Adapter->>Adapter: aggregate text and build AIChatToolCall[] from tool_use
    Adapter-->>Client: AIChatResponse (text + optional toolCalls)
Loading

Estimated Code Review Effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 I hop through code with schema in paw,
Formatting tools for the Anthropic maw,
Adapter listens, maps role to user,
Tool calls returned, the tests get their suitor,
Hooray — tools dance, precise and raw!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 29.17% 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
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Add tool/function calling support for Anthropic adapter' directly and clearly summarizes the main purpose of the PR, which is to add tool/function calling support to the Anthropic adapter.
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.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/anthropic-tool-support

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
packages/anthropic-adapter/src/Chat/AnthropicChatAdapter.php (2)

41-46: ⚠️ Potential issue | 🟡 Minor

Remove AIChatMessageRoleEnum::TOOL from EXPECTED_ROLES.

Anthropic's API only accepts user and assistant roles in messages. The adapter currently allows TOOL role to pass validation (line 80), which would then be sent directly to the API at line 85 as 'role' => 'tool', causing a rejection.

Tool result messages should use USER role with ToolCallPart, as the tests correctly demonstrate. The TOOL enum value should not appear in EXPECTED_ROLES since it cannot be used at the message level.


220-256: ⚠️ Potential issue | 🟠 Major

Streaming path does not handle tool_use content blocks from streamed responses.

The createStreamedMessages method only extracts $delta->text (Line 244). Anthropic's streaming API supports sending tool_use content blocks via content_block_start events (with id/name fields) and content_block_delta events (with input_json_delta type for partial input). Currently, these tool call events will be silently dropped during streaming while the non-streaming path correctly handles them (Lines 168-175).

Consider either:

  1. Accumulating tool call data from streamed content_block_start (type tool_use) and content_block_delta (type input_json_delta) events and yielding them after stream completion, or
  2. Documenting that streamed tool use is not yet supported and throwing an exception if tools are present in a streamed request.
packages/anthropic/src/Resources/Messages.php (1)

234-234: ⚠️ Potential issue | 🟠 Major

Remove invalid 'tool' role from message role validator — it will cause API errors.

The validator at line 234 accepts 'tool' as a valid message role, but the Anthropic Messages API only supports 'user' and 'assistant' roles. Tool interactions are represented as content blocks (tool_use and tool_result types within messages), not as separate message roles. Any message with role 'tool' will be rejected by the API at runtime.

Remove 'tool' from the allowed roles array: ['user', 'assistant'] (note: 'system' is correctly extracted and moved to the top-level system parameter by extractSystemPrompts()).

🤖 Fix all issues with AI agents
In `@packages/anthropic/src/Resources/MessagesInterface.php`:
- Line 25: Remove the unsupported "tool" role from the Message type alias and
runtime validation: update the phpstan-type Message definition (the Message type
alias) to use the union "system"|"assistant"|"user" (remove "tool") and change
the role validation in Messages.php (the validation that currently allows
'tool') to only allow ['system','user','assistant'] so messages conform to
Anthropic's API.
🧹 Nitpick comments (3)
packages/anthropic-adapter/src/Chat/ToolFormatter.php (2)

80-92: Use specific exception classes instead of generic \Exception.

Lines 92 and 122 throw \Exception. Per coding guidelines, specific exception classes should be used for error handling.

♻️ Proposed fix
-            throw new \Exception('Array type parameter must have items description. Define a type or use the Parameter class for object.');
+            throw new \InvalidArgumentException('Array type parameter must have items description. Define a type or use the Parameter class for object.');
-            throw new \Exception('Object type parameter must have properties description. You need to pass an array of Parameter.');
+            throw new \InvalidArgumentException('Object type parameter must have properties description. You need to pass an array of Parameter.');

As per coding guidelines, "Use exception hierarchies with specific exception classes for error handling".


82-83: Consider removing defensive property_exists checks.

Parameter is a typed class with known public properties (nullable, required). Using \property_exists() at Lines 82, 111, and 133 is a defensive pattern — likely carried over from the OpenAI adapter for backward compatibility with older Parameter versions. If the Parameter class is stable (which it appears to be given the 0.4.0 version), direct property access would be cleaner.

♻️ Example simplification
-       $nullable = \property_exists($parameter, 'nullable') ? $parameter->nullable : false;
+       $nullable = $parameter->nullable;
packages/anthropic-adapter/tests/Unit/Chat/AnthropicChatAdapterTest.php (1)

89-90: Consider adding a streaming + tools test.

The supports() method now returns true for all AIChatRequest instances, including streamed requests with tools. However, as noted in the adapter review, the streaming path doesn't handle tool_use content blocks. A test for this scenario would help document the current behavior (or catch future regressions once streaming tool support is added).

Comment thread packages/anthropic/src/Resources/MessagesInterface.php Outdated
@wachterjohannes wachterjohannes force-pushed the feature/anthropic-tool-support branch from 9114527 to 4ebe02b Compare June 1, 2026 17:05
- Create ToolFormatter for Anthropic-specific tool format (uses input_schema
  instead of parameters, no type/function wrapper like OpenAI)
- Update AnthropicChatAdapter to handle tool requests and responses:
  - Add tools to request parameters when hasTools() is true
  - Handle ToolCallsPart (converts to tool_use content blocks)
  - Handle ToolCallPart (converts to tool_result content blocks)
  - Parse tool_use from response to create AIChatToolCall objects
- Update Messages.php to allow streaming with tools
- Update MessagesInterface types to include tool role and improved input_schema
- Add comprehensive tests for tool support
@wachterjohannes wachterjohannes force-pushed the feature/anthropic-tool-support branch from 4ebe02b to b8fd359 Compare June 1, 2026 17:13

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick comments (3)
packages/anthropic-adapter/tests/Unit/Chat/AnthropicChatAdapterTest.php (1)

618-623: ⚡ Quick win

Drop redundant @param annotations from getWeatherMethod docblock.

The method already has explicit scalar types and return type, so the parameter tags are unnecessary noise.

As per coding guidelines, "Type hints are documentation - Modern PHP 8.2+ with strict types makes @param/@return tags redundant".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/anthropic-adapter/tests/Unit/Chat/AnthropicChatAdapterTest.php`
around lines 618 - 623, Remove the redundant `@param` annotations from the
getWeatherMethod docblock in AnthropicChatAdapterTest.php: since the method
signature already uses explicit scalar types and a return type, delete the
`@param` (and any redundant `@return`) tags so the docblock only contains meaningful
description text; locate the docblock immediately above the getWeatherMethod
function and remove those parameter annotations.
packages/anthropic-adapter/tests/Unit/Chat/ToolFormatterTest.php (1)

102-107: ⚡ Quick win

Remove redundant @param tags in helper method docblock.

With strict types and full parameter/return type hints already present, this docblock can be simplified to description-only (or removed if unnecessary).

As per coding guidelines, "Type hints are documentation - Modern PHP 8.2+ with strict types makes @param/@return tags redundant".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/anthropic-adapter/tests/Unit/Chat/ToolFormatterTest.php` around
lines 102 - 107, Remove the redundant `@param` tags from the helper method
docblock in the ToolFormatterTest class: delete the two "`@param` string $required
..." and "`@param` string $optional ..." lines so the docblock is description-only
(or remove the entire docblock if it's unnecessary), keeping the method's strict
types and return type intact; locate the block above the helper method in
packages/anthropic-adapter/tests/Unit/Chat/ToolFormatterTest.php and update the
comment accordingly.
packages/anthropic-adapter/phpstan-baseline.neon (1)

36-43: ⚡ Quick win

Avoid expanding baseline for tautological test assertions.

These new ignores are for always-true assertions; prefer fixing the corresponding tests (remove or replace tautological checks) instead of suppressing, to keep PHPStan signal clean.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/anthropic-adapter/phpstan-baseline.neon` around lines 36 - 43, The
baseline entry suppresses a tautological assertion in
tests/Unit/Chat/AnthropicChatAdapterTest.php where
PHPUnit\Framework\Assert::assertSame() is being called with
ModelflowAi\Chat\ToolInfo\ToolTypeEnum::FUNCTION for both expected and actual;
locate the failing assertion in AnthropicChatAdapterTest (search for assertSame
and ToolTypeEnum::FUNCTION) and fix the test by removing or replacing the
tautology — either assert the actual value produced by the code (use
assertSame(expectedValue, $actualVariable) where $actualVariable comes from the
adapter under test) or replace with a meaningful check (e.g.,
assertEquals/assertInstanceOf/assertContains on the adapter output) so the test
asserts real behavior instead of comparing the same constant to itself.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@packages/anthropic-adapter/phpstan-baseline.neon`:
- Around line 36-43: The baseline entry suppresses a tautological assertion in
tests/Unit/Chat/AnthropicChatAdapterTest.php where
PHPUnit\Framework\Assert::assertSame() is being called with
ModelflowAi\Chat\ToolInfo\ToolTypeEnum::FUNCTION for both expected and actual;
locate the failing assertion in AnthropicChatAdapterTest (search for assertSame
and ToolTypeEnum::FUNCTION) and fix the test by removing or replacing the
tautology — either assert the actual value produced by the code (use
assertSame(expectedValue, $actualVariable) where $actualVariable comes from the
adapter under test) or replace with a meaningful check (e.g.,
assertEquals/assertInstanceOf/assertContains on the adapter output) so the test
asserts real behavior instead of comparing the same constant to itself.

In `@packages/anthropic-adapter/tests/Unit/Chat/AnthropicChatAdapterTest.php`:
- Around line 618-623: Remove the redundant `@param` annotations from the
getWeatherMethod docblock in AnthropicChatAdapterTest.php: since the method
signature already uses explicit scalar types and a return type, delete the
`@param` (and any redundant `@return`) tags so the docblock only contains meaningful
description text; locate the docblock immediately above the getWeatherMethod
function and remove those parameter annotations.

In `@packages/anthropic-adapter/tests/Unit/Chat/ToolFormatterTest.php`:
- Around line 102-107: Remove the redundant `@param` tags from the helper method
docblock in the ToolFormatterTest class: delete the two "`@param` string $required
..." and "`@param` string $optional ..." lines so the docblock is description-only
(or remove the entire docblock if it's unnecessary), keeping the method's strict
types and return type intact; locate the block above the helper method in
packages/anthropic-adapter/tests/Unit/Chat/ToolFormatterTest.php and update the
comment accordingly.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 7f88e229-73d2-4615-a573-2d5ec97ba9c3

📥 Commits

Reviewing files that changed from the base of the PR and between 9114527 and b8fd359.

⛔ Files ignored due to path filters (1)
  • packages/anthropic/composer.lock is excluded by !**/*.lock
📒 Files selected for processing (8)
  • packages/anthropic-adapter/phpstan-baseline.neon
  • packages/anthropic-adapter/src/Chat/AnthropicChatAdapter.php
  • packages/anthropic-adapter/src/Chat/ToolFormatter.php
  • packages/anthropic-adapter/tests/Unit/Chat/AnthropicChatAdapterTest.php
  • packages/anthropic-adapter/tests/Unit/Chat/ToolFormatterTest.php
  • packages/anthropic/phpstan-baseline.neon
  • packages/anthropic/src/Resources/Messages.php
  • packages/anthropic/src/Resources/MessagesInterface.php
✅ Files skipped from review due to trivial changes (1)
  • packages/anthropic/src/Resources/MessagesInterface.php
🚧 Files skipped from review as they are similar to previous changes (4)
  • packages/anthropic/phpstan-baseline.neon
  • packages/anthropic-adapter/src/Chat/ToolFormatter.php
  • packages/anthropic/src/Resources/Messages.php
  • packages/anthropic-adapter/src/Chat/AnthropicChatAdapter.php

@wachterjohannes wachterjohannes merged commit eae686d into 0.4 Jun 1, 2026
86 checks passed
@wachterjohannes wachterjohannes deleted the feature/anthropic-tool-support branch June 1, 2026 17:33
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