From c885b22cb27a0aaab09708dc69f454e5cb96bad9 Mon Sep 17 00:00:00 2001 From: Christopher Hertel Date: Sun, 4 May 2025 19:32:36 +0200 Subject: [PATCH] feat: extend Claude support for images and pdf input --- README.md | 1 + examples/anthropic/image-input-binary.php | 32 +++++++ examples/anthropic/image-input-url.php | 32 +++++++ examples/anthropic/pdf-input-binary.php | 31 +++++++ examples/anthropic/pdf-input-url.php | 31 +++++++ examples/anthropic/toolbox-stream.php | 38 +++++++++ src/Bridge/Anthropic/Claude.php | 2 +- .../{ModelHandler.php => ModelClient.php} | 63 +------------- src/Bridge/Anthropic/PlatformFactory.php | 3 +- src/Bridge/Anthropic/ResponseConverter.php | 78 ++++++++++++++++++ src/Model/Message/Content/Document.php | 23 ++++++ src/Model/Message/Content/DocumentUrl.php | 27 ++++++ src/Model/Message/Content/Image.php | 10 ++- src/Model/Message/Content/ImageUrl.php | 9 +- tests/Fixture/document.pdf | Bin 0 -> 18404 bytes 15 files changed, 310 insertions(+), 70 deletions(-) create mode 100644 examples/anthropic/image-input-binary.php create mode 100644 examples/anthropic/image-input-url.php create mode 100644 examples/anthropic/pdf-input-binary.php create mode 100644 examples/anthropic/pdf-input-url.php create mode 100644 examples/anthropic/toolbox-stream.php rename src/Bridge/Anthropic/{ModelHandler.php => ModelClient.php} (62%) create mode 100644 src/Bridge/Anthropic/ResponseConverter.php create mode 100644 src/Model/Message/Content/Document.php create mode 100644 src/Model/Message/Content/DocumentUrl.php create mode 100644 tests/Fixture/document.pdf diff --git a/README.md b/README.md index 5d5057b..426c108 100644 --- a/README.md +++ b/README.md @@ -821,3 +821,4 @@ For testing multi-modal features, the repository contains binary media content, * `tests/Fixture/image.jpg`: Chris F., Creative Commons, see [pexels.com](https://www.pexels.com/photo/blauer-und-gruner-elefant-mit-licht-1680755/) * `tests/Fixture/audio.mp3`: davidbain, Creative Commons, see [freesound.org](https://freesound.org/people/davidbain/sounds/136777/) +* `tests/Fixture/document.pdf`: Chem8240ja, Public Domain, see [Wikipedia](https://en.m.wikipedia.org/wiki/File:Re_example.pdf) diff --git a/examples/anthropic/image-input-binary.php b/examples/anthropic/image-input-binary.php new file mode 100644 index 0000000..45fdd60 --- /dev/null +++ b/examples/anthropic/image-input-binary.php @@ -0,0 +1,32 @@ +loadEnv(dirname(__DIR__, 2).'/.env'); + +if (empty($_ENV['ANTHROPIC_API_KEY'])) { + echo 'Please set the ANTHROPIC_API_KEY environment variable.'.PHP_EOL; + exit(1); +} + +$platform = PlatformFactory::create($_ENV['ANTHROPIC_API_KEY']); +$llm = new Claude(Claude::SONNET_37); + +$chain = new Chain($platform, $llm); +$messages = new MessageBag( + Message::forSystem('You are an image analyzer bot that helps identify the content of images.'), + Message::ofUser( + Image::fromFile(dirname(__DIR__, 2).'/tests/Fixture/image.jpg'), + 'Describe this image.', + ), +); +$response = $chain->call($messages); + +echo $response->getContent().PHP_EOL; diff --git a/examples/anthropic/image-input-url.php b/examples/anthropic/image-input-url.php new file mode 100644 index 0000000..bc87d33 --- /dev/null +++ b/examples/anthropic/image-input-url.php @@ -0,0 +1,32 @@ +loadEnv(dirname(__DIR__, 2).'/.env'); + +if (empty($_ENV['ANTHROPIC_API_KEY'])) { + echo 'Please set the ANTHROPIC_API_KEY environment variable.'.PHP_EOL; + exit(1); +} + +$platform = PlatformFactory::create($_ENV['ANTHROPIC_API_KEY']); +$llm = new Claude(Claude::SONNET_37); + +$chain = new Chain($platform, $llm); +$messages = new MessageBag( + Message::forSystem('You are an image analyzer bot that helps identify the content of images.'), + Message::ofUser( + new ImageUrl('https://upload.wikimedia.org/wikipedia/commons/a/a7/Camponotus_flavomarginatus_ant.jpg'), + 'Describe this image.', + ), +); +$response = $chain->call($messages); + +echo $response->getContent().PHP_EOL; diff --git a/examples/anthropic/pdf-input-binary.php b/examples/anthropic/pdf-input-binary.php new file mode 100644 index 0000000..ea29fc6 --- /dev/null +++ b/examples/anthropic/pdf-input-binary.php @@ -0,0 +1,31 @@ +loadEnv(dirname(__DIR__, 2).'/.env'); + +if (empty($_ENV['ANTHROPIC_API_KEY'])) { + echo 'Please set the ANTHROPIC_API_KEY environment variable.'.PHP_EOL; + exit(1); +} + +$platform = PlatformFactory::create($_ENV['ANTHROPIC_API_KEY']); +$llm = new Claude(Claude::SONNET_37); + +$chain = new Chain($platform, $llm); +$messages = new MessageBag( + Message::ofUser( + Document::fromFile(dirname(__DIR__, 2).'/tests/Fixture/document.pdf'), + 'What is this document about?', + ), +); +$response = $chain->call($messages); + +echo $response->getContent().PHP_EOL; diff --git a/examples/anthropic/pdf-input-url.php b/examples/anthropic/pdf-input-url.php new file mode 100644 index 0000000..e7aa2d0 --- /dev/null +++ b/examples/anthropic/pdf-input-url.php @@ -0,0 +1,31 @@ +loadEnv(dirname(__DIR__, 2).'/.env'); + +if (empty($_ENV['ANTHROPIC_API_KEY'])) { + echo 'Please set the ANTHROPIC_API_KEY environment variable.'.PHP_EOL; + exit(1); +} + +$platform = PlatformFactory::create($_ENV['ANTHROPIC_API_KEY']); +$llm = new Claude(Claude::SONNET_37); + +$chain = new Chain($platform, $llm); +$messages = new MessageBag( + Message::ofUser( + new DocumentUrl('https://upload.wikimedia.org/wikipedia/commons/2/20/Re_example.pdf'), + 'What is this document about?', + ), +); +$response = $chain->call($messages); + +echo $response->getContent().PHP_EOL; diff --git a/examples/anthropic/toolbox-stream.php b/examples/anthropic/toolbox-stream.php new file mode 100644 index 0000000..9ce2b41 --- /dev/null +++ b/examples/anthropic/toolbox-stream.php @@ -0,0 +1,38 @@ +loadEnv(dirname(__DIR__, 2).'/.env'); + +if (empty($_ENV['ANTHROPIC_API_KEY'])) { + echo 'Please set the ANTHROPIC_API_KEY environment variable.'.PHP_EOL; + exit(1); +} + +$platform = PlatformFactory::create($_ENV['ANTHROPIC_API_KEY']); +$llm = new Claude(); + +$transcriber = new YouTubeTranscriber(HttpClient::create()); +$toolbox = Toolbox::create($transcriber); +$processor = new ChainProcessor($toolbox); +$chain = new Chain($platform, $llm, [$processor], [$processor]); + +$messages = new MessageBag(Message::ofUser('Please summarize this video for me: https://www.youtube.com/watch?v=6uXW-ulpj0s')); +$response = $chain->call($messages, [ + 'stream' => true, // enable streaming of response text +]); + +foreach ($response->getContent() as $word) { + echo $word; +} +echo PHP_EOL; diff --git a/src/Bridge/Anthropic/Claude.php b/src/Bridge/Anthropic/Claude.php index 3096a4b..6d75ec6 100644 --- a/src/Bridge/Anthropic/Claude.php +++ b/src/Bridge/Anthropic/Claude.php @@ -42,7 +42,7 @@ public function supportsAudioInput(): bool public function supportsImageInput(): bool { - return false; // it does, but implementation here is still open. + return true; } public function supportsStreaming(): bool diff --git a/src/Bridge/Anthropic/ModelHandler.php b/src/Bridge/Anthropic/ModelClient.php similarity index 62% rename from src/Bridge/Anthropic/ModelHandler.php rename to src/Bridge/Anthropic/ModelClient.php index 07f631f..b0dae82 100644 --- a/src/Bridge/Anthropic/ModelHandler.php +++ b/src/Bridge/Anthropic/ModelClient.php @@ -5,27 +5,19 @@ namespace PhpLlm\LlmChain\Bridge\Anthropic; use PhpLlm\LlmChain\Chain\Toolbox\Metadata; -use PhpLlm\LlmChain\Exception\RuntimeException; use PhpLlm\LlmChain\Model\Message\AssistantMessage; use PhpLlm\LlmChain\Model\Message\MessageBagInterface; use PhpLlm\LlmChain\Model\Message\MessageInterface; use PhpLlm\LlmChain\Model\Message\ToolCallMessage; use PhpLlm\LlmChain\Model\Model; -use PhpLlm\LlmChain\Model\Response\ResponseInterface as LlmResponse; -use PhpLlm\LlmChain\Model\Response\StreamResponse; -use PhpLlm\LlmChain\Model\Response\TextResponse; use PhpLlm\LlmChain\Model\Response\ToolCall; -use PhpLlm\LlmChain\Model\Response\ToolCallResponse; -use PhpLlm\LlmChain\Platform\ModelClient; -use PhpLlm\LlmChain\Platform\ResponseConverter; -use Symfony\Component\HttpClient\Chunk\ServerSentEvent; +use PhpLlm\LlmChain\Platform\ModelClient as PlatformModelClient; use Symfony\Component\HttpClient\EventSourceHttpClient; -use Symfony\Component\HttpClient\Exception\JsonException; use Symfony\Contracts\HttpClient\HttpClientInterface; use Symfony\Contracts\HttpClient\ResponseInterface; use Webmozart\Assert\Assert; -final readonly class ModelHandler implements ModelClient, ResponseConverter +final readonly class ModelClient implements PlatformModelClient { private EventSourceHttpClient $httpClient; @@ -108,55 +100,4 @@ public function request(Model $model, object|array|string $input, array $options 'json' => array_merge($options, $body), ]); } - - public function convert(ResponseInterface $response, array $options = []): LlmResponse - { - if ($options['stream'] ?? false) { - return new StreamResponse($this->convertStream($response)); - } - - $data = $response->toArray(); - - if (!isset($data['content']) || 0 === count($data['content'])) { - throw new RuntimeException('Response does not contain any content'); - } - - if (!isset($data['content'][0]['text'])) { - throw new RuntimeException('Response content does not contain any text'); - } - - $toolCalls = []; - foreach ($data['content'] as $content) { - if ('tool_use' === $content['type']) { - $toolCalls[] = new ToolCall($content['id'], $content['name'], $content['input']); - } - } - if (!empty($toolCalls)) { - return new ToolCallResponse(...$toolCalls); - } - - return new TextResponse($data['content'][0]['text']); - } - - private function convertStream(ResponseInterface $response): \Generator - { - foreach ((new EventSourceHttpClient())->stream($response) as $chunk) { - if (!$chunk instanceof ServerSentEvent || '[DONE]' === $chunk->getData()) { - continue; - } - - try { - $data = $chunk->getArrayData(); - } catch (JsonException) { - // try catch only needed for Symfony 6.4 - continue; - } - - if ('content_block_delta' != $data['type'] || !isset($data['delta']['text'])) { - continue; - } - - yield $data['delta']['text']; - } - } } diff --git a/src/Bridge/Anthropic/PlatformFactory.php b/src/Bridge/Anthropic/PlatformFactory.php index 644c462..f68ef49 100644 --- a/src/Bridge/Anthropic/PlatformFactory.php +++ b/src/Bridge/Anthropic/PlatformFactory.php @@ -17,8 +17,7 @@ public static function create( ?HttpClientInterface $httpClient = null, ): Platform { $httpClient = $httpClient instanceof EventSourceHttpClient ? $httpClient : new EventSourceHttpClient($httpClient); - $responseHandler = new ModelHandler($httpClient, $apiKey, $version); - return new Platform([$responseHandler], [$responseHandler]); + return new Platform([new ModelClient($httpClient, $apiKey, $version)], [new ResponseConverter()]); } } diff --git a/src/Bridge/Anthropic/ResponseConverter.php b/src/Bridge/Anthropic/ResponseConverter.php new file mode 100644 index 0000000..451f0d9 --- /dev/null +++ b/src/Bridge/Anthropic/ResponseConverter.php @@ -0,0 +1,78 @@ +convertStream($response)); + } + + $data = $response->toArray(); + + if (!isset($data['content']) || 0 === count($data['content'])) { + throw new RuntimeException('Response does not contain any content'); + } + + if (!isset($data['content'][0]['text'])) { + throw new RuntimeException('Response content does not contain any text'); + } + + $toolCalls = []; + foreach ($data['content'] as $content) { + if ('tool_use' === $content['type']) { + $toolCalls[] = new ToolCall($content['id'], $content['name'], $content['input']); + } + } + if (!empty($toolCalls)) { + return new ToolCallResponse(...$toolCalls); + } + + return new TextResponse($data['content'][0]['text']); + } + + private function convertStream(ResponseInterface $response): \Generator + { + foreach ((new EventSourceHttpClient())->stream($response) as $chunk) { + if (!$chunk instanceof ServerSentEvent || '[DONE]' === $chunk->getData()) { + continue; + } + + try { + $data = $chunk->getArrayData(); + } catch (JsonException) { + // try catch only needed for Symfony 6.4 + continue; + } + + if ('content_block_delta' != $data['type'] || !isset($data['delta']['text'])) { + continue; + } + + yield $data['delta']['text']; + } + } +} diff --git a/src/Model/Message/Content/Document.php b/src/Model/Message/Content/Document.php new file mode 100644 index 0000000..4ee1bd2 --- /dev/null +++ b/src/Model/Message/Content/Document.php @@ -0,0 +1,23 @@ + 'document', + 'source' => [ + 'type' => 'base64', + 'media_type' => $this->getFormat(), + 'data' => $this->asBase64(), + ], + ]; + } +} diff --git a/src/Model/Message/Content/DocumentUrl.php b/src/Model/Message/Content/DocumentUrl.php new file mode 100644 index 0000000..5534f6b --- /dev/null +++ b/src/Model/Message/Content/DocumentUrl.php @@ -0,0 +1,27 @@ + 'document', + 'source' => [ + 'type' => 'url', + 'url' => $this->url, + ], + ]; + } +} diff --git a/src/Model/Message/Content/Image.php b/src/Model/Message/Content/Image.php index 330b65c..5dd97a0 100644 --- a/src/Model/Message/Content/Image.php +++ b/src/Model/Message/Content/Image.php @@ -7,13 +7,17 @@ final readonly class Image extends File implements Content { /** - * @return array{type: 'image_url', image_url: array{url: string}} + * @return array{type: 'image', source: array{type: 'base64', media_type: string, data: string}} */ public function jsonSerialize(): array { return [ - 'type' => 'image_url', - 'image_url' => ['url' => $this->asDataUrl()], + 'type' => 'image', + 'source' => [ + 'type' => 'base64', + 'media_type' => $this->getFormat(), + 'data' => $this->asBase64(), + ], ]; } } diff --git a/src/Model/Message/Content/ImageUrl.php b/src/Model/Message/Content/ImageUrl.php index a9e6379..fbcc61a 100644 --- a/src/Model/Message/Content/ImageUrl.php +++ b/src/Model/Message/Content/ImageUrl.php @@ -12,13 +12,16 @@ public function __construct( } /** - * @return array{type: 'image_url', image_url: array{url: string}} + * @return array{type: 'image', source: array{type: 'url', url: string}} */ public function jsonSerialize(): array { return [ - 'type' => 'image_url', - 'image_url' => ['url' => $this->url], + 'type' => 'image', + 'source' => [ + 'type' => 'url', + 'url' => $this->url, + ], ]; } } diff --git a/tests/Fixture/document.pdf b/tests/Fixture/document.pdf new file mode 100644 index 0000000000000000000000000000000000000000..00172f2664eeb926ac57e750ca6b58b6d3e889ac GIT binary patch literal 18404 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