Releases: claude-php/claude-php-agent
v1.5.0
Cross-Vendor Dynamic Model Fusion (DMF)
Adds multi-vendor LLM support, enabling Claude to orchestrate OpenAI and Google Gemini as callable tools during agent execution. Claude remains the primary orchestrator while delegating to vendor-specific capabilities via the existing ToolInterface.
New Features
- VendorRegistry — API key management for Anthropic, OpenAI, and Google Gemini with auto-detection from environment variables
- ModelRegistry — Catalog of 30+ models across all three vendors with capability metadata (model IDs, descriptions, defaults)
- Capability enum — Typed capabilities:
chat,web_search,image_generation,text_to_speech,speech_to_text,code_execution,grounding,deep_research - CrossVendorToolFactory — Auto-creates vendor tools from available API keys; supports per-capability tool creation
Vendor Tools
| Tool | Vendor | Capability |
|---|---|---|
vendor_chat |
OpenAI / Gemini | Cross-vendor chat delegation |
openai_web_search |
OpenAI | Real-time web search via Responses API |
openai_image_generation |
OpenAI | GPT Image 1.5 generation |
openai_text_to_speech |
OpenAI | TTS with voice styles and instructions |
gemini_grounding |
Google Search grounded responses with citations | |
gemini_code_execution |
Server-side Python execution in sandbox | |
gemini_image_generation |
Nano Banana image generation and editing |
Architecture
- Lightweight cURL-based HTTP adapters (no vendor SDK dependencies)
- Retry logic with exponential backoff
- Per-vendor configuration overrides (custom models, timeouts, base URLs)
- Full compatibility with existing
ToolInterface— vendor tools integrate seamlessly with any agent type
Testing
- 138 tests with 562 assertions (126 unit + 12 live integration)
- Live integration tests verify all vendor APIs (chat, web search, grounding, code execution, cross-vendor delegation)
Usage
$vendorRegistry = VendorRegistry::fromEnvironment();
$factory = new CrossVendorToolFactory($vendorRegistry);
$tools = $factory->createAllTools();
$agent = new ReactAgent($client, [
'tools' => $tools,
]);Set API keys in .env:
OPENAI_API_KEY=your_key
GEMINI_API_KEY=your_key
See examples/cross_vendor_dmf.php for a complete walkthrough.
v1.4.8
Bug Fix
- Skill relevance scoring: Filter out common English stop words and short tokens (< 3 chars) before scoring to prevent false positive skill matches (e.g. "in" matching "guidel-in-es", "f-in-ancial")
v1.4.7
Enhancement
Dynamic tool management at runtime via AgentContext
Added addTool() and removeTool() methods to AgentContext, allowing tools to be added or removed dynamically during agent execution. This enables more flexible workflows where the available toolset can change based on context or intermediate results.
Changes
AgentContext::addTool()— register a new tool so it is included in subsequent API callsAgentContext::removeTool()— remove a tool so it is excluded from future API calls- Added type annotations to the
toolsproperty for better IDE support - Updated
ToolExecutionTraitto source tools from the context's current tool list
Upgrade
composer update claude-php/agentNo breaking changes. Drop-in replacement for v1.4.6.
v1.4.6
Bug Fix
Fix orphaned tool_use blocks causing API validation errors
The agent loops (ReactLoop, PlanExecuteLoop, ReflectionLoop) previously relied on stop_reason === 'tool_use' to decide whether to add tool_result blocks. When the Claude API returned tool_use blocks with a different stop_reason (e.g. max_tokens), no tool_result was added, leaving orphaned tool_use blocks in the message array. The next API call then failed with:
messages.N: tool_use ids were found without tool_result blocks immediately after
Changes
- Added
contentHasToolUse()helper toToolExecutionTraitthat inspects response content directly ReactLoopnow always addstool_resultblocks when content containstool_use, regardless ofstop_reason- Same fix applied to
PlanExecuteLoopandReflectionLoop - Added regression test
testToolUseWithMaxTokensStopReasonStillAddsToolResults
Upgrade
composer update claude-php/agentNo breaking changes. Drop-in replacement for v1.4.5.
v1.4.5
Bug Fix
fix: prevent context compaction from corrupting tool_use/tool_result pairing
Fixed a critical bug where long-running agent workflows (15+ iterations) would fail with tool_use ids were found without tool_result blocks immediately after API errors.
Root Cause
Three interacting issues in the context compaction system:
-
Compaction fired between tool_use and tool_result addition —
AgentContext::addMessage()triggered auto-compaction after adding the assistant'stool_useresponse but before thetool_resultwas added, orphaning the tool_use block. -
Initial user message dropped during compaction — The compaction algorithm preserved
systemmessages but not the initialusertask message, causing compacted messages to start with anassistantrole and violating API requirements. -
Compaction algorithm misordered preserved messages — The
array_unshiftapproach inserted recent messages before preserved messages, breaking message ordering.
Changes
AgentContext::addMessage()now defers compaction when the last message is a danglingtool_use(no matchingtool_resultyet)ContextManager::compactMessages()preserves both the system message and initial user task message- Rewrote compaction to correctly build
preserved + recentmessage sets, keeping the maximum number of recent message units that fit - Added early return after
clearToolResultsif that alone brings context within limits - Added 7 regression tests covering multi-iteration compaction, tool_use pairing integrity, and message ordering
Impact
Fixes production failures for:
- Document analysis (PDFs, large files)
- Complex multi-step automation workflows
- Data extraction requiring 15+ agent iterations
- Any workflow using context management with
auto_compactenabled
v1.4.4
Full Changelog: v1.4.3...v1.4.4
v1.4.3
Bug Fix: Always Add tool_results After tool_use
Removes the if (! empty($toolResults)) guard that could skip adding a tool_result message after the assistant's tool_use blocks. The Anthropic API requires every tool_use block to have a corresponding tool_result in the immediately following user message — skipping it leaves the conversation in an invalid state.
Changes
- ReactLoop — always add tool_results message
- ReflectionLoop — same fix in both
generate()andrefine()methods - PlanExecuteLoop — same fix in
executeStep() - StreamingLoop — same fix
Full Changelog
v1.4.2
Bug Fix: Tool Exceptions Always Produce a tool_result
Fixes an issue where a tool that throws an exception would leave the conversation in an invalid state. The Anthropic API requires every tool_use block in an assistant message to have a corresponding tool_result block in the following user message. Previously, if $tool->execute() threw, the exception would bubble up without adding a tool_result, breaking the conversation.
Changes
- ToolExecutionTrait — wrapped tool lookup and execution in try-catch; exceptions now produce an error
ToolResultinstead of propagating - StreamingLoop::executeStreamingTools() — same fix applied
- ReactLoopTest — added
testToolExceptionProducesErrorToolResultto verify throwing tools still produce a valid errortool_result
Full Changelog
v1.4.1
Bug Fix: JSON Object Encoding for Tool Inputs
Fixes an issue where PHP's json_decode($json, true) converts empty JSON objects {} to empty arrays [], which then re-encode as [] instead of {}. This caused the Anthropic API to reject tool_use requests with "Input should be a valid dictionary".
Changes
- New
ToolExecutionTrait— shared trait withnormalizeContentBlocks(),executeTools(), andextractTextContent()methods, eliminating code duplication across all loop implementations - ReactLoop, ReflectionLoop, PlanExecuteLoop, StreamingLoop — all now use the trait and normalize
tool_use.inputtostdClassbefore adding to message history - Tool::toDefinition() — casts
propertiesto(object)for correct API serialization - Unit tests — added tests for empty and non-empty input normalization
- .gitignore — added runtime storage directories
Full Changelog
v1.3.0 - Error Handling Service 🎯
Error Handling Service with User-Friendly Error Messages
Inspired by Langflow's user-friendly error conversion, this comprehensive error handling service converts technical API errors into actionable user messages.
Key Features
- 🎯 User-friendly error messages for all Claude API errors
- 🔄 Smart retry logic with exponential backoff
- 📊 Detailed error context extraction for debugging
- ⚙️ 9 configurable default error patterns (rate limit, auth, timeout, etc.)
- 🏢 Full service layer integration with ServiceManager
- 📝 Comprehensive PSR-3 logging support
- 🛠️ Safe tool execution helpers with error handling
Error Pattern Coverage
- Rate limit errors (429)
- Authentication errors (401)
- Permission errors (403)
- Timeout errors
- Connection errors
- Overloaded errors (529)
- Bad request errors (400)
- Server errors (500)
- Validation errors (422)
Statistics
- New Code: ~600 lines (service + factory)
- Tests: 45+ tests across 4 test files
- Documentation: ~1,200 lines
- Examples: 9 working examples (~2,400 lines)
- Total: ~4,200+ lines of production-ready code
Documentation
- Complete service guide at
docs/services/error-handling.md - Comprehensive tutorial at
docs/tutorials/ErrorHandling_Tutorial.md - 9 example files in
examples/Services/andexamples/tutorials/error-handling/