diff --git a/src/platform/src/Bridge/Anthropic/Contract/AnthropicContract.php b/src/platform/src/Bridge/Anthropic/Contract/AnthropicContract.php new file mode 100644 index 00000000..76ca37fd --- /dev/null +++ b/src/platform/src/Bridge/Anthropic/Contract/AnthropicContract.php @@ -0,0 +1,36 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\AI\Platform\Bridge\Anthropic\Contract; + +use Symfony\AI\Platform\Contract; +use Symfony\Component\Serializer\Normalizer\NormalizerInterface; + +/** + * @author Denis Zunke + */ +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/src/Bridge/Anthropic/PlatformFactory.php b/src/platform/src/Bridge/Anthropic/PlatformFactory.php index 5a674ca5..428949fe 100644 --- a/src/platform/src/Bridge/Anthropic/PlatformFactory.php +++ b/src/platform/src/Bridge/Anthropic/PlatformFactory.php @@ -11,14 +11,7 @@ namespace Symfony\AI\Platform\Bridge\Anthropic; -use Symfony\AI\Platform\Bridge\Anthropic\Contract\AssistantMessageNormalizer; -use Symfony\AI\Platform\Bridge\Anthropic\Contract\DocumentNormalizer; -use Symfony\AI\Platform\Bridge\Anthropic\Contract\DocumentUrlNormalizer; -use Symfony\AI\Platform\Bridge\Anthropic\Contract\ImageNormalizer; -use Symfony\AI\Platform\Bridge\Anthropic\Contract\ImageUrlNormalizer; -use Symfony\AI\Platform\Bridge\Anthropic\Contract\MessageBagNormalizer; -use Symfony\AI\Platform\Bridge\Anthropic\Contract\ToolCallMessageNormalizer; -use Symfony\AI\Platform\Bridge\Anthropic\Contract\ToolNormalizer; +use Symfony\AI\Platform\Bridge\Anthropic\Contract\AnthropicContract; use Symfony\AI\Platform\Contract; use Symfony\AI\Platform\Platform; use Symfony\Component\HttpClient\EventSourceHttpClient; @@ -34,22 +27,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/src/Bridge/Azure/Meta/PlatformFactory.php b/src/platform/src/Bridge/Azure/Meta/PlatformFactory.php index 59b8b5e6..0e8c30ef 100644 --- a/src/platform/src/Bridge/Azure/Meta/PlatformFactory.php +++ b/src/platform/src/Bridge/Azure/Meta/PlatformFactory.php @@ -11,6 +11,7 @@ namespace Symfony\AI\Platform\Bridge\Azure\Meta; +use Symfony\AI\Platform\Contract; use Symfony\AI\Platform\Platform; use Symfony\Component\HttpClient\HttpClient; use Symfony\Contracts\HttpClient\HttpClientInterface; @@ -25,9 +26,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/src/Bridge/Azure/OpenAI/PlatformFactory.php b/src/platform/src/Bridge/Azure/OpenAI/PlatformFactory.php index fd4af948..9949803e 100644 --- a/src/platform/src/Bridge/Azure/OpenAI/PlatformFactory.php +++ b/src/platform/src/Bridge/Azure/OpenAI/PlatformFactory.php @@ -31,6 +31,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); @@ -40,7 +41,7 @@ public static function create( return new Platform( [$GPTResponseFactory, $embeddingsResponseFactory, $whisperResponseFactory], [new ResponseConverter(), new Embeddings\ResponseConverter(), new \Symfony\AI\Platform\Bridge\OpenAI\Whisper\ResponseConverter()], - Contract::create(new AudioNormalizer()), + $contract ?? Contract::create(new AudioNormalizer()), ); } } diff --git a/src/platform/src/Bridge/Bedrock/PlatformFactory.php b/src/platform/src/Bridge/Bedrock/PlatformFactory.php index bd5d8784..c4d7d252 100644 --- a/src/platform/src/Bridge/Bedrock/PlatformFactory.php +++ b/src/platform/src/Bridge/Bedrock/PlatformFactory.php @@ -15,6 +15,7 @@ use Symfony\AI\Platform\Bridge\Bedrock\Anthropic\ClaudeHandler; use Symfony\AI\Platform\Bridge\Bedrock\Meta\LlamaModelClient; use Symfony\AI\Platform\Bridge\Bedrock\Nova\NovaHandler; +use Symfony\AI\Platform\Contract; /** * @author Björn Altmann @@ -23,11 +24,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/src/Bridge/Google/Contract/GoogleContract.php b/src/platform/src/Bridge/Google/Contract/GoogleContract.php new file mode 100644 index 00000000..298908a2 --- /dev/null +++ b/src/platform/src/Bridge/Google/Contract/GoogleContract.php @@ -0,0 +1,33 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\AI\Platform\Bridge\Google\Contract; + +use Symfony\AI\Platform\Contract; +use Symfony\Component\Serializer\Normalizer\NormalizerInterface; + +/** + * @author Denis Zunke + */ +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/src/Bridge/Google/PlatformFactory.php b/src/platform/src/Bridge/Google/PlatformFactory.php index 56729767..b0496afe 100644 --- a/src/platform/src/Bridge/Google/PlatformFactory.php +++ b/src/platform/src/Bridge/Google/PlatformFactory.php @@ -11,11 +11,7 @@ namespace Symfony\AI\Platform\Bridge\Google; -use Symfony\AI\Platform\Bridge\Google\Contract\AssistantMessageNormalizer; -use Symfony\AI\Platform\Bridge\Google\Contract\MessageBagNormalizer; -use Symfony\AI\Platform\Bridge\Google\Contract\ToolCallMessageNormalizer; -use Symfony\AI\Platform\Bridge\Google\Contract\ToolNormalizer; -use Symfony\AI\Platform\Bridge\Google\Contract\UserMessageNormalizer; +use Symfony\AI\Platform\Bridge\Google\Contract\GoogleContract; use Symfony\AI\Platform\Bridge\Google\Embeddings\ModelClient; use Symfony\AI\Platform\Contract; use Symfony\AI\Platform\Platform; @@ -31,17 +27,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/src/Bridge/HuggingFace/PlatformFactory.php b/src/platform/src/Bridge/HuggingFace/PlatformFactory.php index f25c51c1..75850141 100644 --- a/src/platform/src/Bridge/HuggingFace/PlatformFactory.php +++ b/src/platform/src/Bridge/HuggingFace/PlatformFactory.php @@ -28,13 +28,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/src/Bridge/Mistral/PlatformFactory.php b/src/platform/src/Bridge/Mistral/PlatformFactory.php index 0b5ccabb..3f8f7890 100644 --- a/src/platform/src/Bridge/Mistral/PlatformFactory.php +++ b/src/platform/src/Bridge/Mistral/PlatformFactory.php @@ -30,13 +30,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/src/Bridge/Ollama/PlatformFactory.php b/src/platform/src/Bridge/Ollama/PlatformFactory.php index fdde43e3..7ef4fd63 100644 --- a/src/platform/src/Bridge/Ollama/PlatformFactory.php +++ b/src/platform/src/Bridge/Ollama/PlatformFactory.php @@ -11,6 +11,7 @@ namespace Symfony\AI\Platform\Bridge\Ollama; +use Symfony\AI\Platform\Contract; use Symfony\AI\Platform\Platform; use Symfony\Component\HttpClient\EventSourceHttpClient; use Symfony\Contracts\HttpClient\HttpClientInterface; @@ -23,10 +24,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/src/Bridge/OpenAI/PlatformFactory.php b/src/platform/src/Bridge/OpenAI/PlatformFactory.php index 1cea3e09..2de87d58 100644 --- a/src/platform/src/Bridge/OpenAI/PlatformFactory.php +++ b/src/platform/src/Bridge/OpenAI/PlatformFactory.php @@ -33,6 +33,7 @@ public static function create( #[\SensitiveParameter] string $apiKey, ?HttpClientInterface $httpClient = null, + ?Contract $contract = null, ): Platform { $httpClient = $httpClient instanceof EventSourceHttpClient ? $httpClient : new EventSourceHttpClient($httpClient); @@ -51,7 +52,7 @@ public static function create( $dallEModelClient, new WhisperResponseConverter(), ], - Contract::create(new AudioNormalizer()), + $contract ?? Contract::create(new AudioNormalizer()), ); } } diff --git a/src/platform/src/Bridge/OpenRouter/PlatformFactory.php b/src/platform/src/Bridge/OpenRouter/PlatformFactory.php index 15b53da2..4f42a03f 100644 --- a/src/platform/src/Bridge/OpenRouter/PlatformFactory.php +++ b/src/platform/src/Bridge/OpenRouter/PlatformFactory.php @@ -28,14 +28,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/src/Bridge/Replicate/PlatformFactory.php b/src/platform/src/Bridge/Replicate/PlatformFactory.php index 51e9d7a8..49701e34 100644 --- a/src/platform/src/Bridge/Replicate/PlatformFactory.php +++ b/src/platform/src/Bridge/Replicate/PlatformFactory.php @@ -27,11 +27,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/src/Bridge/Voyage/PlatformFactory.php b/src/platform/src/Bridge/Voyage/PlatformFactory.php index 8497a9c0..537fe797 100644 --- a/src/platform/src/Bridge/Voyage/PlatformFactory.php +++ b/src/platform/src/Bridge/Voyage/PlatformFactory.php @@ -11,6 +11,7 @@ namespace Symfony\AI\Platform\Bridge\Voyage; +use Symfony\AI\Platform\Contract; use Symfony\AI\Platform\Platform; use Symfony\Component\HttpClient\EventSourceHttpClient; use Symfony\Contracts\HttpClient\HttpClientInterface; @@ -24,10 +25,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/src/Contract.php b/src/platform/src/Contract.php index e18f5efc..ed2b8003 100644 --- a/src/platform/src/Contract.php +++ b/src/platform/src/Contract.php @@ -30,12 +30,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, ) { } @@ -70,7 +70,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]); } @@ -80,7 +80,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,