diff --git a/src/Platform/Bridge/Anthropic/Contract/AnthropicContract.php b/src/Platform/Bridge/Anthropic/Contract/AnthropicContract.php new file mode 100644 index 00000000..40dabf6a --- /dev/null +++ b/src/Platform/Bridge/Anthropic/Contract/AnthropicContract.php @@ -0,0 +1,29 @@ + + */ +final readonly class AnthropicContract extends Contract +{ + public static function create(NormalizerInterface ...$normalizer): Contract + { + return parent::create( + new AssistantMessageNormalizer(), + new DocumentNormalizer(), + new DocumentUrlNormalizer(), + new ImageNormalizer(), + new ImageUrlNormalizer(), + new MessageBagNormalizer(), + new ToolCallMessageNormalizer(), + new ToolNormalizer(), + ...$normalizer, + ); + } +} diff --git a/src/Platform/Bridge/Anthropic/PlatformFactory.php b/src/Platform/Bridge/Anthropic/PlatformFactory.php index 87abe1f5..dbb6abc3 100644 --- a/src/Platform/Bridge/Anthropic/PlatformFactory.php +++ b/src/Platform/Bridge/Anthropic/PlatformFactory.php @@ -4,14 +4,7 @@ namespace PhpLlm\LlmChain\Platform\Bridge\Anthropic; -use PhpLlm\LlmChain\Platform\Bridge\Anthropic\Contract\AssistantMessageNormalizer; -use PhpLlm\LlmChain\Platform\Bridge\Anthropic\Contract\DocumentNormalizer; -use PhpLlm\LlmChain\Platform\Bridge\Anthropic\Contract\DocumentUrlNormalizer; -use PhpLlm\LlmChain\Platform\Bridge\Anthropic\Contract\ImageNormalizer; -use PhpLlm\LlmChain\Platform\Bridge\Anthropic\Contract\ImageUrlNormalizer; -use PhpLlm\LlmChain\Platform\Bridge\Anthropic\Contract\MessageBagNormalizer; -use PhpLlm\LlmChain\Platform\Bridge\Anthropic\Contract\ToolCallMessageNormalizer; -use PhpLlm\LlmChain\Platform\Bridge\Anthropic\Contract\ToolNormalizer; +use PhpLlm\LlmChain\Platform\Bridge\Anthropic\Contract\AnthropicContract; use PhpLlm\LlmChain\Platform\Contract; use PhpLlm\LlmChain\Platform\Platform; use Symfony\Component\HttpClient\EventSourceHttpClient; @@ -27,22 +20,14 @@ public static function create( string $apiKey, string $version = '2023-06-01', ?HttpClientInterface $httpClient = null, + ?Contract $contract = null, ): Platform { $httpClient = $httpClient instanceof EventSourceHttpClient ? $httpClient : new EventSourceHttpClient($httpClient); return new Platform( [new ModelClient($httpClient, $apiKey, $version)], [new ResponseConverter()], - Contract::create( - new AssistantMessageNormalizer(), - new DocumentNormalizer(), - new DocumentUrlNormalizer(), - new ImageNormalizer(), - new ImageUrlNormalizer(), - new MessageBagNormalizer(), - new ToolCallMessageNormalizer(), - new ToolNormalizer(), - ) + $contract ?? AnthropicContract::create(), ); } } diff --git a/src/Platform/Bridge/Azure/Meta/PlatformFactory.php b/src/Platform/Bridge/Azure/Meta/PlatformFactory.php index 51e4c3d5..a63ef001 100644 --- a/src/Platform/Bridge/Azure/Meta/PlatformFactory.php +++ b/src/Platform/Bridge/Azure/Meta/PlatformFactory.php @@ -4,6 +4,7 @@ namespace PhpLlm\LlmChain\Platform\Bridge\Azure\Meta; +use PhpLlm\LlmChain\Platform\Contract; use PhpLlm\LlmChain\Platform\Platform; use Symfony\Component\HttpClient\HttpClient; use Symfony\Contracts\HttpClient\HttpClientInterface; @@ -18,9 +19,10 @@ public static function create( #[\SensitiveParameter] string $apiKey, ?HttpClientInterface $httpClient = null, + ?Contract $contract = null, ): Platform { $modelClient = new LlamaHandler($httpClient ?? HttpClient::create(), $baseUrl, $apiKey); - return new Platform([$modelClient], [$modelClient]); + return new Platform([$modelClient], [$modelClient], $contract); } } diff --git a/src/Platform/Bridge/Azure/OpenAI/PlatformFactory.php b/src/Platform/Bridge/Azure/OpenAI/PlatformFactory.php index 6e4e1288..86c58423 100644 --- a/src/Platform/Bridge/Azure/OpenAI/PlatformFactory.php +++ b/src/Platform/Bridge/Azure/OpenAI/PlatformFactory.php @@ -24,6 +24,7 @@ public static function create( #[\SensitiveParameter] string $apiKey, ?HttpClientInterface $httpClient = null, + ?Contract $contract = null, ): Platform { $httpClient = $httpClient instanceof EventSourceHttpClient ? $httpClient : new EventSourceHttpClient($httpClient); $embeddingsResponseFactory = new EmbeddingsModelClient($httpClient, $baseUrl, $deployment, $apiVersion, $apiKey); @@ -33,7 +34,7 @@ public static function create( return new Platform( [$GPTResponseFactory, $embeddingsResponseFactory, $whisperResponseFactory], [new ResponseConverter(), new Embeddings\ResponseConverter(), new \PhpLlm\LlmChain\Platform\Bridge\OpenAI\Whisper\ResponseConverter()], - Contract::create(new AudioNormalizer()), + $contract ?? Contract::create(new AudioNormalizer()), ); } } diff --git a/src/Platform/Bridge/Bedrock/PlatformFactory.php b/src/Platform/Bridge/Bedrock/PlatformFactory.php index e2315682..113aee7e 100644 --- a/src/Platform/Bridge/Bedrock/PlatformFactory.php +++ b/src/Platform/Bridge/Bedrock/PlatformFactory.php @@ -8,6 +8,7 @@ use PhpLlm\LlmChain\Platform\Bridge\Bedrock\Anthropic\ClaudeHandler; use PhpLlm\LlmChain\Platform\Bridge\Bedrock\Meta\LlamaModelClient; use PhpLlm\LlmChain\Platform\Bridge\Bedrock\Nova\NovaHandler; +use PhpLlm\LlmChain\Platform\Contract; /** * @author Björn Altmann @@ -16,11 +17,12 @@ { public static function create( BedrockRuntimeClient $bedrockRuntimeClient = new BedrockRuntimeClient(), + ?Contract $contract = null, ): Platform { $modelClient[] = new ClaudeHandler($bedrockRuntimeClient); $modelClient[] = new NovaHandler($bedrockRuntimeClient); $modelClient[] = new LlamaModelClient($bedrockRuntimeClient); - return new Platform($modelClient); + return new Platform($modelClient, $contract); } } diff --git a/src/Platform/Bridge/Google/Contract/GoogleContract.php b/src/Platform/Bridge/Google/Contract/GoogleContract.php new file mode 100644 index 00000000..fcf6c990 --- /dev/null +++ b/src/Platform/Bridge/Google/Contract/GoogleContract.php @@ -0,0 +1,26 @@ + + */ +final readonly class GoogleContract extends Contract +{ + public static function create(NormalizerInterface ...$normalizer): Contract + { + return parent::create( + new AssistantMessageNormalizer(), + new MessageBagNormalizer(), + new ToolNormalizer(), + new ToolCallMessageNormalizer(), + new UserMessageNormalizer(), + ...$normalizer, + ); + } +} diff --git a/src/Platform/Bridge/Google/PlatformFactory.php b/src/Platform/Bridge/Google/PlatformFactory.php index da224f29..4fc927c4 100644 --- a/src/Platform/Bridge/Google/PlatformFactory.php +++ b/src/Platform/Bridge/Google/PlatformFactory.php @@ -4,11 +4,7 @@ namespace PhpLlm\LlmChain\Platform\Bridge\Google; -use PhpLlm\LlmChain\Platform\Bridge\Google\Contract\AssistantMessageNormalizer; -use PhpLlm\LlmChain\Platform\Bridge\Google\Contract\MessageBagNormalizer; -use PhpLlm\LlmChain\Platform\Bridge\Google\Contract\ToolCallMessageNormalizer; -use PhpLlm\LlmChain\Platform\Bridge\Google\Contract\ToolNormalizer; -use PhpLlm\LlmChain\Platform\Bridge\Google\Contract\UserMessageNormalizer; +use PhpLlm\LlmChain\Platform\Bridge\Google\Contract\GoogleContract; use PhpLlm\LlmChain\Platform\Bridge\Google\Embeddings\ModelClient; use PhpLlm\LlmChain\Platform\Contract; use PhpLlm\LlmChain\Platform\Platform; @@ -24,17 +20,16 @@ public static function create( #[\SensitiveParameter] string $apiKey, ?HttpClientInterface $httpClient = null, + ?Contract $contract = null, ): Platform { $httpClient = $httpClient instanceof EventSourceHttpClient ? $httpClient : new EventSourceHttpClient($httpClient); $responseHandler = new ModelHandler($httpClient, $apiKey); $embeddings = new ModelClient($httpClient, $apiKey); - return new Platform([$responseHandler, $embeddings], [$responseHandler, $embeddings], Contract::create( - new AssistantMessageNormalizer(), - new MessageBagNormalizer(), - new ToolNormalizer(), - new ToolCallMessageNormalizer(), - new UserMessageNormalizer(), - )); + return new Platform( + [$responseHandler, $embeddings], + [$responseHandler, $embeddings], + $contract ?? GoogleContract::create(), + ); } } diff --git a/src/Platform/Bridge/HuggingFace/PlatformFactory.php b/src/Platform/Bridge/HuggingFace/PlatformFactory.php index e46ae69a..d71b4e22 100644 --- a/src/Platform/Bridge/HuggingFace/PlatformFactory.php +++ b/src/Platform/Bridge/HuggingFace/PlatformFactory.php @@ -21,13 +21,14 @@ public static function create( string $apiKey, string $provider = Provider::HF_INFERENCE, ?HttpClientInterface $httpClient = null, + ?Contract $contract = null, ): Platform { $httpClient = $httpClient instanceof EventSourceHttpClient ? $httpClient : new EventSourceHttpClient($httpClient); return new Platform( [new ModelClient($httpClient, $provider, $apiKey)], [new ResponseConverter()], - Contract::create( + $contract ?? Contract::create( new FileNormalizer(), new MessageBagNormalizer(), ), diff --git a/src/Platform/Bridge/Mistral/PlatformFactory.php b/src/Platform/Bridge/Mistral/PlatformFactory.php index 47ab83a0..436597f6 100644 --- a/src/Platform/Bridge/Mistral/PlatformFactory.php +++ b/src/Platform/Bridge/Mistral/PlatformFactory.php @@ -23,13 +23,14 @@ public static function create( #[\SensitiveParameter] string $apiKey, ?HttpClientInterface $httpClient = null, + ?Contract $contract = null, ): Platform { $httpClient = $httpClient instanceof EventSourceHttpClient ? $httpClient : new EventSourceHttpClient($httpClient); return new Platform( [new EmbeddingsModelClient($httpClient, $apiKey), new MistralModelClient($httpClient, $apiKey)], [new EmbeddingsResponseConverter(), new MistralResponseConverter()], - Contract::create(new ToolNormalizer()), + $contract ?? Contract::create(new ToolNormalizer()), ); } } diff --git a/src/Platform/Bridge/Ollama/PlatformFactory.php b/src/Platform/Bridge/Ollama/PlatformFactory.php index c07e9699..5926aeb9 100644 --- a/src/Platform/Bridge/Ollama/PlatformFactory.php +++ b/src/Platform/Bridge/Ollama/PlatformFactory.php @@ -4,6 +4,7 @@ namespace PhpLlm\LlmChain\Platform\Bridge\Ollama; +use PhpLlm\LlmChain\Platform\Contract; use PhpLlm\LlmChain\Platform\Platform; use Symfony\Component\HttpClient\EventSourceHttpClient; use Symfony\Contracts\HttpClient\HttpClientInterface; @@ -16,10 +17,11 @@ final class PlatformFactory public static function create( string $hostUrl = 'http://localhost:11434', ?HttpClientInterface $httpClient = null, + ?Contract $contract = null, ): Platform { $httpClient = $httpClient instanceof EventSourceHttpClient ? $httpClient : new EventSourceHttpClient($httpClient); $handler = new LlamaModelHandler($httpClient, $hostUrl); - return new Platform([$handler], [$handler]); + return new Platform([$handler], [$handler], $contract); } } diff --git a/src/Platform/Bridge/OpenAI/PlatformFactory.php b/src/Platform/Bridge/OpenAI/PlatformFactory.php index f27c76b7..5531fbfe 100644 --- a/src/Platform/Bridge/OpenAI/PlatformFactory.php +++ b/src/Platform/Bridge/OpenAI/PlatformFactory.php @@ -26,6 +26,7 @@ public static function create( #[\SensitiveParameter] string $apiKey, ?HttpClientInterface $httpClient = null, + ?Contract $contract = null, ): Platform { $httpClient = $httpClient instanceof EventSourceHttpClient ? $httpClient : new EventSourceHttpClient($httpClient); @@ -44,7 +45,7 @@ public static function create( $dallEModelClient, new WhisperResponseConverter(), ], - Contract::create(new AudioNormalizer()), + $contract ?? Contract::create(new AudioNormalizer()), ); } } diff --git a/src/Platform/Bridge/OpenRouter/PlatformFactory.php b/src/Platform/Bridge/OpenRouter/PlatformFactory.php index bd0cb684..cc988748 100644 --- a/src/Platform/Bridge/OpenRouter/PlatformFactory.php +++ b/src/Platform/Bridge/OpenRouter/PlatformFactory.php @@ -21,14 +21,19 @@ public static function create( #[\SensitiveParameter] string $apiKey, ?HttpClientInterface $httpClient = null, + ?Contract $contract = null, ): Platform { $httpClient = $httpClient instanceof EventSourceHttpClient ? $httpClient : new EventSourceHttpClient($httpClient); $handler = new Client($httpClient, $apiKey); - return new Platform([$handler], [$handler], Contract::create( - new AssistantMessageNormalizer(), - new MessageBagNormalizer(), - new UserMessageNormalizer(), - )); + return new Platform( + [$handler], + [$handler], + $contract ?? Contract::create( + new AssistantMessageNormalizer(), + new MessageBagNormalizer(), + new UserMessageNormalizer(), + ), + ); } } diff --git a/src/Platform/Bridge/Replicate/PlatformFactory.php b/src/Platform/Bridge/Replicate/PlatformFactory.php index f150ff0a..12f0553e 100644 --- a/src/Platform/Bridge/Replicate/PlatformFactory.php +++ b/src/Platform/Bridge/Replicate/PlatformFactory.php @@ -20,11 +20,12 @@ public static function create( #[\SensitiveParameter] string $apiKey, ?HttpClientInterface $httpClient = null, + ?Contract $contract = null, ): Platform { return new Platform( [new LlamaModelClient(new Client($httpClient ?? HttpClient::create(), new Clock(), $apiKey))], [new LlamaResponseConverter()], - Contract::create(new LlamaMessageBagNormalizer()), + $contract ?? Contract::create(new LlamaMessageBagNormalizer()), ); } } diff --git a/src/Platform/Bridge/Voyage/PlatformFactory.php b/src/Platform/Bridge/Voyage/PlatformFactory.php index f4bb9ec6..ea8359f6 100644 --- a/src/Platform/Bridge/Voyage/PlatformFactory.php +++ b/src/Platform/Bridge/Voyage/PlatformFactory.php @@ -4,6 +4,7 @@ namespace PhpLlm\LlmChain\Platform\Bridge\Voyage; +use PhpLlm\LlmChain\Platform\Contract; use PhpLlm\LlmChain\Platform\Platform; use Symfony\Component\HttpClient\EventSourceHttpClient; use Symfony\Contracts\HttpClient\HttpClientInterface; @@ -17,10 +18,11 @@ public static function create( #[\SensitiveParameter] string $apiKey, ?HttpClientInterface $httpClient = null, + ?Contract $contract = null, ): Platform { $httpClient = $httpClient instanceof EventSourceHttpClient ? $httpClient : new EventSourceHttpClient($httpClient); $handler = new ModelHandler($httpClient, $apiKey); - return new Platform([$handler], [$handler]); + return new Platform([$handler], [$handler], $contract); } } diff --git a/src/Platform/Contract.php b/src/Platform/Contract.php index dff53424..775ed91c 100644 --- a/src/Platform/Contract.php +++ b/src/Platform/Contract.php @@ -23,12 +23,12 @@ /** * @author Christopher Hertel */ -final readonly class Contract +readonly class Contract { public const CONTEXT_MODEL = 'model'; - public function __construct( - private NormalizerInterface $normalizer, + final public function __construct( + protected NormalizerInterface $normalizer, ) { } @@ -63,7 +63,7 @@ public static function create(NormalizerInterface ...$normalizer): self * * @return array|string */ - public function createRequestPayload(Model $model, object|array|string $input): string|array + final public function createRequestPayload(Model $model, object|array|string $input): string|array { return $this->normalizer->normalize($input, context: [self::CONTEXT_MODEL => $model]); } @@ -73,7 +73,7 @@ public function createRequestPayload(Model $model, object|array|string $input): * * @return array */ - public function createToolOption(array $tools, Model $model): array + final public function createToolOption(array $tools, Model $model): array { return $this->normalizer->normalize($tools, context: [self::CONTEXT_MODEL => $model, AbstractObjectNormalizer::PRESERVE_EMPTY_OBJECTS => true]); }