Skip to content

Commit 375a11f

Browse files
authored
feat: add support for Anthropic tool use (#209)
I have added Anthropic tool use support but I cannot figure out how to preserve text content that is sent with tool use request. I have tried to copy OpenAI function calling but that seems to loose text content during function call too.
1 parent 375acf2 commit 375a11f

File tree

2 files changed

+63
-2
lines changed

2 files changed

+63
-2
lines changed

src/Bridge/Anthropic/Claude.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,6 @@ public function supportsStructuredOutput(): bool
5454

5555
public function supportsToolCalling(): bool
5656
{
57-
return false; // it does, but implementation here is still open.
57+
return true;
5858
}
5959
}

src/Bridge/Anthropic/ModelHandler.php

Lines changed: 62 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,18 @@
44

55
namespace PhpLlm\LlmChain\Bridge\Anthropic;
66

7+
use PhpLlm\LlmChain\Chain\ToolBox\Metadata;
78
use PhpLlm\LlmChain\Exception\RuntimeException;
9+
use PhpLlm\LlmChain\Model\Message\AssistantMessage;
810
use PhpLlm\LlmChain\Model\Message\MessageBagInterface;
11+
use PhpLlm\LlmChain\Model\Message\MessageInterface;
12+
use PhpLlm\LlmChain\Model\Message\ToolCallMessage;
913
use PhpLlm\LlmChain\Model\Model;
1014
use PhpLlm\LlmChain\Model\Response\ResponseInterface as LlmResponse;
1115
use PhpLlm\LlmChain\Model\Response\StreamResponse;
1216
use PhpLlm\LlmChain\Model\Response\TextResponse;
17+
use PhpLlm\LlmChain\Model\Response\ToolCall;
18+
use PhpLlm\LlmChain\Model\Response\ToolCallResponse;
1319
use PhpLlm\LlmChain\Platform\ModelClient;
1420
use PhpLlm\LlmChain\Platform\ResponseConverter;
1521
use Symfony\Component\HttpClient\Chunk\ServerSentEvent;
@@ -40,13 +46,58 @@ public function request(Model $model, object|array|string $input, array $options
4046
{
4147
Assert::isInstanceOf($input, MessageBagInterface::class);
4248

49+
if (isset($options['tools'])) {
50+
$tools = $options['tools'];
51+
$options['tools'] = [];
52+
/** @var Metadata $tool */
53+
foreach ($tools as $tool) {
54+
$toolDefinition = [
55+
'name' => $tool->name,
56+
'description' => $tool->description,
57+
'input_schema' => $tool->parameters ?? ['type' => 'object'],
58+
];
59+
$options['tools'][] = $toolDefinition;
60+
}
61+
$options['tool_choice'] = ['type' => 'auto'];
62+
}
63+
4364
$system = $input->getSystemMessage();
4465
$body = array_merge($options, [
4566
'model' => $model->getVersion(),
4667
'system' => $system->content,
47-
'messages' => $input->withoutSystemMessage(),
68+
'messages' => $input->withoutSystemMessage()->jsonSerialize(),
4869
]);
4970

71+
$body['messages'] = array_map(static function (MessageInterface $message) {
72+
if ($message instanceof ToolCallMessage) {
73+
return [
74+
'role' => 'user',
75+
'content' => [
76+
[
77+
'type' => 'tool_result',
78+
'tool_use_id' => $message->toolCall->id,
79+
'content' => $message->content,
80+
],
81+
],
82+
];
83+
}
84+
if ($message instanceof AssistantMessage && $message->hasToolCalls()) {
85+
return [
86+
'role' => 'assistant',
87+
'content' => array_map(static function (ToolCall $toolCall) {
88+
return [
89+
'type' => 'tool_use',
90+
'id' => $toolCall->id,
91+
'name' => $toolCall->name,
92+
'input' => empty($toolCall->arguments) ? new \stdClass() : $toolCall->arguments,
93+
];
94+
}, $message->toolCalls),
95+
];
96+
}
97+
98+
return $message;
99+
}, $body['messages']);
100+
50101
return $this->httpClient->request('POST', 'https://api.anthropic.com/v1/messages', [
51102
'headers' => [
52103
'x-api-key' => $this->apiKey,
@@ -72,6 +123,16 @@ public function convert(ResponseInterface $response, array $options = []): LlmRe
72123
throw new RuntimeException('Response content does not contain any text');
73124
}
74125

126+
$toolCalls = [];
127+
foreach ($data['content'] as $content) {
128+
if ('tool_use' === $content['type']) {
129+
$toolCalls[] = new ToolCall($content['id'], $content['name'], $content['input']);
130+
}
131+
}
132+
if (!empty($toolCalls)) {
133+
return new ToolCallResponse(...$toolCalls);
134+
}
135+
75136
return new TextResponse($data['content'][0]['text']);
76137
}
77138

0 commit comments

Comments
 (0)