Skip to content

Commit c9e2ad9

Browse files
authored
feat: open contract for more flexible normalizer configuration (#372)
Could fix part of #371 - the question for flexibility. Why? Messages are given on platform calls and are given with the `MessageInterface`. So it is very flexible what could be given on call. Normalizers are only configured once, internally. So one side is highly flexible, the other side not. Giving the contract an interface allows creating own contracts with whatever backed technology one wants. So more flexible. Additionally it allows to add a contract that has an amount of normalizers one wants. For making it easier i thought about having model contract sets. I did it for anthropic, google and the platform itself. So a custom contract could look like ```php use PhpLlm\LlmChain\Platform\Bridge\OpenAI\PlatformFactory; use PhpLlm\LlmChain\Platform\Contract; $platform = PlatformFactory::create( 'my-api-key', contract: Contract::create(new MyOwnMessageBagNormalizer(), new MyOwnMessageThingyNormalizer()), ); ``` or, totally flexible: ```php use PhpLlm\LlmChain\Platform\Bridge\OpenAI\PlatformFactory; $platform = PlatformFactory::create( 'my-api-key', contract: new MyVeryOwnContractUtilizingJsonStreamer(), ); ``` Maybe this could be a way for more flexibility with the combination of non configurable normalizer and "unknown" message input?
1 parent 9428d8b commit c9e2ad9

File tree

15 files changed

+102
-49
lines changed

15 files changed

+102
-49
lines changed
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace PhpLlm\LlmChain\Platform\Bridge\Anthropic\Contract;
6+
7+
use PhpLlm\LlmChain\Platform\Contract;
8+
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
9+
10+
/**
11+
* @author Denis Zunke <[email protected]>
12+
*/
13+
final readonly class AnthropicContract extends Contract
14+
{
15+
public static function create(NormalizerInterface ...$normalizer): Contract
16+
{
17+
return parent::create(
18+
new AssistantMessageNormalizer(),
19+
new DocumentNormalizer(),
20+
new DocumentUrlNormalizer(),
21+
new ImageNormalizer(),
22+
new ImageUrlNormalizer(),
23+
new MessageBagNormalizer(),
24+
new ToolCallMessageNormalizer(),
25+
new ToolNormalizer(),
26+
...$normalizer,
27+
);
28+
}
29+
}

src/Platform/Bridge/Anthropic/PlatformFactory.php

Lines changed: 3 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,7 @@
44

55
namespace PhpLlm\LlmChain\Platform\Bridge\Anthropic;
66

7-
use PhpLlm\LlmChain\Platform\Bridge\Anthropic\Contract\AssistantMessageNormalizer;
8-
use PhpLlm\LlmChain\Platform\Bridge\Anthropic\Contract\DocumentNormalizer;
9-
use PhpLlm\LlmChain\Platform\Bridge\Anthropic\Contract\DocumentUrlNormalizer;
10-
use PhpLlm\LlmChain\Platform\Bridge\Anthropic\Contract\ImageNormalizer;
11-
use PhpLlm\LlmChain\Platform\Bridge\Anthropic\Contract\ImageUrlNormalizer;
12-
use PhpLlm\LlmChain\Platform\Bridge\Anthropic\Contract\MessageBagNormalizer;
13-
use PhpLlm\LlmChain\Platform\Bridge\Anthropic\Contract\ToolCallMessageNormalizer;
14-
use PhpLlm\LlmChain\Platform\Bridge\Anthropic\Contract\ToolNormalizer;
7+
use PhpLlm\LlmChain\Platform\Bridge\Anthropic\Contract\AnthropicContract;
158
use PhpLlm\LlmChain\Platform\Contract;
169
use PhpLlm\LlmChain\Platform\Platform;
1710
use Symfony\Component\HttpClient\EventSourceHttpClient;
@@ -27,22 +20,14 @@ public static function create(
2720
string $apiKey,
2821
string $version = '2023-06-01',
2922
?HttpClientInterface $httpClient = null,
23+
?Contract $contract = null,
3024
): Platform {
3125
$httpClient = $httpClient instanceof EventSourceHttpClient ? $httpClient : new EventSourceHttpClient($httpClient);
3226

3327
return new Platform(
3428
[new ModelClient($httpClient, $apiKey, $version)],
3529
[new ResponseConverter()],
36-
Contract::create(
37-
new AssistantMessageNormalizer(),
38-
new DocumentNormalizer(),
39-
new DocumentUrlNormalizer(),
40-
new ImageNormalizer(),
41-
new ImageUrlNormalizer(),
42-
new MessageBagNormalizer(),
43-
new ToolCallMessageNormalizer(),
44-
new ToolNormalizer(),
45-
)
30+
$contract ?? AnthropicContract::create(),
4631
);
4732
}
4833
}

src/Platform/Bridge/Azure/Meta/PlatformFactory.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
namespace PhpLlm\LlmChain\Platform\Bridge\Azure\Meta;
66

7+
use PhpLlm\LlmChain\Platform\Contract;
78
use PhpLlm\LlmChain\Platform\Platform;
89
use Symfony\Component\HttpClient\HttpClient;
910
use Symfony\Contracts\HttpClient\HttpClientInterface;
@@ -18,9 +19,10 @@ public static function create(
1819
#[\SensitiveParameter]
1920
string $apiKey,
2021
?HttpClientInterface $httpClient = null,
22+
?Contract $contract = null,
2123
): Platform {
2224
$modelClient = new LlamaHandler($httpClient ?? HttpClient::create(), $baseUrl, $apiKey);
2325

24-
return new Platform([$modelClient], [$modelClient]);
26+
return new Platform([$modelClient], [$modelClient], $contract);
2527
}
2628
}

src/Platform/Bridge/Azure/OpenAI/PlatformFactory.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ public static function create(
2424
#[\SensitiveParameter]
2525
string $apiKey,
2626
?HttpClientInterface $httpClient = null,
27+
?Contract $contract = null,
2728
): Platform {
2829
$httpClient = $httpClient instanceof EventSourceHttpClient ? $httpClient : new EventSourceHttpClient($httpClient);
2930
$embeddingsResponseFactory = new EmbeddingsModelClient($httpClient, $baseUrl, $deployment, $apiVersion, $apiKey);
@@ -33,7 +34,7 @@ public static function create(
3334
return new Platform(
3435
[$GPTResponseFactory, $embeddingsResponseFactory, $whisperResponseFactory],
3536
[new ResponseConverter(), new Embeddings\ResponseConverter(), new \PhpLlm\LlmChain\Platform\Bridge\OpenAI\Whisper\ResponseConverter()],
36-
Contract::create(new AudioNormalizer()),
37+
$contract ?? Contract::create(new AudioNormalizer()),
3738
);
3839
}
3940
}

src/Platform/Bridge/Bedrock/PlatformFactory.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
use PhpLlm\LlmChain\Platform\Bridge\Bedrock\Anthropic\ClaudeHandler;
99
use PhpLlm\LlmChain\Platform\Bridge\Bedrock\Meta\LlamaModelClient;
1010
use PhpLlm\LlmChain\Platform\Bridge\Bedrock\Nova\NovaHandler;
11+
use PhpLlm\LlmChain\Platform\Contract;
1112

1213
/**
1314
* @author Björn Altmann
@@ -16,11 +17,12 @@
1617
{
1718
public static function create(
1819
BedrockRuntimeClient $bedrockRuntimeClient = new BedrockRuntimeClient(),
20+
?Contract $contract = null,
1921
): Platform {
2022
$modelClient[] = new ClaudeHandler($bedrockRuntimeClient);
2123
$modelClient[] = new NovaHandler($bedrockRuntimeClient);
2224
$modelClient[] = new LlamaModelClient($bedrockRuntimeClient);
2325

24-
return new Platform($modelClient);
26+
return new Platform($modelClient, $contract);
2527
}
2628
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace PhpLlm\LlmChain\Platform\Bridge\Google\Contract;
6+
7+
use PhpLlm\LlmChain\Platform\Contract;
8+
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
9+
10+
/**
11+
* @author Denis Zunke <[email protected]>
12+
*/
13+
final readonly class GoogleContract extends Contract
14+
{
15+
public static function create(NormalizerInterface ...$normalizer): Contract
16+
{
17+
return parent::create(
18+
new AssistantMessageNormalizer(),
19+
new MessageBagNormalizer(),
20+
new ToolNormalizer(),
21+
new ToolCallMessageNormalizer(),
22+
new UserMessageNormalizer(),
23+
...$normalizer,
24+
);
25+
}
26+
}

src/Platform/Bridge/Google/PlatformFactory.php

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,7 @@
44

55
namespace PhpLlm\LlmChain\Platform\Bridge\Google;
66

7-
use PhpLlm\LlmChain\Platform\Bridge\Google\Contract\AssistantMessageNormalizer;
8-
use PhpLlm\LlmChain\Platform\Bridge\Google\Contract\MessageBagNormalizer;
9-
use PhpLlm\LlmChain\Platform\Bridge\Google\Contract\ToolCallMessageNormalizer;
10-
use PhpLlm\LlmChain\Platform\Bridge\Google\Contract\ToolNormalizer;
11-
use PhpLlm\LlmChain\Platform\Bridge\Google\Contract\UserMessageNormalizer;
7+
use PhpLlm\LlmChain\Platform\Bridge\Google\Contract\GoogleContract;
128
use PhpLlm\LlmChain\Platform\Bridge\Google\Embeddings\ModelClient;
139
use PhpLlm\LlmChain\Platform\Contract;
1410
use PhpLlm\LlmChain\Platform\Platform;
@@ -24,17 +20,16 @@ public static function create(
2420
#[\SensitiveParameter]
2521
string $apiKey,
2622
?HttpClientInterface $httpClient = null,
23+
?Contract $contract = null,
2724
): Platform {
2825
$httpClient = $httpClient instanceof EventSourceHttpClient ? $httpClient : new EventSourceHttpClient($httpClient);
2926
$responseHandler = new ModelHandler($httpClient, $apiKey);
3027
$embeddings = new ModelClient($httpClient, $apiKey);
3128

32-
return new Platform([$responseHandler, $embeddings], [$responseHandler, $embeddings], Contract::create(
33-
new AssistantMessageNormalizer(),
34-
new MessageBagNormalizer(),
35-
new ToolNormalizer(),
36-
new ToolCallMessageNormalizer(),
37-
new UserMessageNormalizer(),
38-
));
29+
return new Platform(
30+
[$responseHandler, $embeddings],
31+
[$responseHandler, $embeddings],
32+
$contract ?? GoogleContract::create(),
33+
);
3934
}
4035
}

src/Platform/Bridge/HuggingFace/PlatformFactory.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,14 @@ public static function create(
2121
string $apiKey,
2222
string $provider = Provider::HF_INFERENCE,
2323
?HttpClientInterface $httpClient = null,
24+
?Contract $contract = null,
2425
): Platform {
2526
$httpClient = $httpClient instanceof EventSourceHttpClient ? $httpClient : new EventSourceHttpClient($httpClient);
2627

2728
return new Platform(
2829
[new ModelClient($httpClient, $provider, $apiKey)],
2930
[new ResponseConverter()],
30-
Contract::create(
31+
$contract ?? Contract::create(
3132
new FileNormalizer(),
3233
new MessageBagNormalizer(),
3334
),

src/Platform/Bridge/Mistral/PlatformFactory.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,14 @@ public static function create(
2323
#[\SensitiveParameter]
2424
string $apiKey,
2525
?HttpClientInterface $httpClient = null,
26+
?Contract $contract = null,
2627
): Platform {
2728
$httpClient = $httpClient instanceof EventSourceHttpClient ? $httpClient : new EventSourceHttpClient($httpClient);
2829

2930
return new Platform(
3031
[new EmbeddingsModelClient($httpClient, $apiKey), new MistralModelClient($httpClient, $apiKey)],
3132
[new EmbeddingsResponseConverter(), new MistralResponseConverter()],
32-
Contract::create(new ToolNormalizer()),
33+
$contract ?? Contract::create(new ToolNormalizer()),
3334
);
3435
}
3536
}

src/Platform/Bridge/Ollama/PlatformFactory.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
namespace PhpLlm\LlmChain\Platform\Bridge\Ollama;
66

7+
use PhpLlm\LlmChain\Platform\Contract;
78
use PhpLlm\LlmChain\Platform\Platform;
89
use Symfony\Component\HttpClient\EventSourceHttpClient;
910
use Symfony\Contracts\HttpClient\HttpClientInterface;
@@ -16,10 +17,11 @@ final class PlatformFactory
1617
public static function create(
1718
string $hostUrl = 'http://localhost:11434',
1819
?HttpClientInterface $httpClient = null,
20+
?Contract $contract = null,
1921
): Platform {
2022
$httpClient = $httpClient instanceof EventSourceHttpClient ? $httpClient : new EventSourceHttpClient($httpClient);
2123
$handler = new LlamaModelHandler($httpClient, $hostUrl);
2224

23-
return new Platform([$handler], [$handler]);
25+
return new Platform([$handler], [$handler], $contract);
2426
}
2527
}

src/Platform/Bridge/OpenAI/PlatformFactory.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ public static function create(
2626
#[\SensitiveParameter]
2727
string $apiKey,
2828
?HttpClientInterface $httpClient = null,
29+
?Contract $contract = null,
2930
): Platform {
3031
$httpClient = $httpClient instanceof EventSourceHttpClient ? $httpClient : new EventSourceHttpClient($httpClient);
3132

@@ -44,7 +45,7 @@ public static function create(
4445
$dallEModelClient,
4546
new WhisperResponseConverter(),
4647
],
47-
Contract::create(new AudioNormalizer()),
48+
$contract ?? Contract::create(new AudioNormalizer()),
4849
);
4950
}
5051
}

src/Platform/Bridge/OpenRouter/PlatformFactory.php

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,19 @@ public static function create(
2121
#[\SensitiveParameter]
2222
string $apiKey,
2323
?HttpClientInterface $httpClient = null,
24+
?Contract $contract = null,
2425
): Platform {
2526
$httpClient = $httpClient instanceof EventSourceHttpClient ? $httpClient : new EventSourceHttpClient($httpClient);
2627
$handler = new Client($httpClient, $apiKey);
2728

28-
return new Platform([$handler], [$handler], Contract::create(
29-
new AssistantMessageNormalizer(),
30-
new MessageBagNormalizer(),
31-
new UserMessageNormalizer(),
32-
));
29+
return new Platform(
30+
[$handler],
31+
[$handler],
32+
$contract ?? Contract::create(
33+
new AssistantMessageNormalizer(),
34+
new MessageBagNormalizer(),
35+
new UserMessageNormalizer(),
36+
),
37+
);
3338
}
3439
}

src/Platform/Bridge/Replicate/PlatformFactory.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,12 @@ public static function create(
2020
#[\SensitiveParameter]
2121
string $apiKey,
2222
?HttpClientInterface $httpClient = null,
23+
?Contract $contract = null,
2324
): Platform {
2425
return new Platform(
2526
[new LlamaModelClient(new Client($httpClient ?? HttpClient::create(), new Clock(), $apiKey))],
2627
[new LlamaResponseConverter()],
27-
Contract::create(new LlamaMessageBagNormalizer()),
28+
$contract ?? Contract::create(new LlamaMessageBagNormalizer()),
2829
);
2930
}
3031
}

src/Platform/Bridge/Voyage/PlatformFactory.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
namespace PhpLlm\LlmChain\Platform\Bridge\Voyage;
66

7+
use PhpLlm\LlmChain\Platform\Contract;
78
use PhpLlm\LlmChain\Platform\Platform;
89
use Symfony\Component\HttpClient\EventSourceHttpClient;
910
use Symfony\Contracts\HttpClient\HttpClientInterface;
@@ -17,10 +18,11 @@ public static function create(
1718
#[\SensitiveParameter]
1819
string $apiKey,
1920
?HttpClientInterface $httpClient = null,
21+
?Contract $contract = null,
2022
): Platform {
2123
$httpClient = $httpClient instanceof EventSourceHttpClient ? $httpClient : new EventSourceHttpClient($httpClient);
2224
$handler = new ModelHandler($httpClient, $apiKey);
2325

24-
return new Platform([$handler], [$handler]);
26+
return new Platform([$handler], [$handler], $contract);
2527
}
2628
}

src/Platform/Contract.php

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,12 @@
2323
/**
2424
* @author Christopher Hertel <[email protected]>
2525
*/
26-
final readonly class Contract
26+
readonly class Contract
2727
{
2828
public const CONTEXT_MODEL = 'model';
2929

30-
public function __construct(
31-
private NormalizerInterface $normalizer,
30+
final public function __construct(
31+
protected NormalizerInterface $normalizer,
3232
) {
3333
}
3434

@@ -63,7 +63,7 @@ public static function create(NormalizerInterface ...$normalizer): self
6363
*
6464
* @return array<string, mixed>|string
6565
*/
66-
public function createRequestPayload(Model $model, object|array|string $input): string|array
66+
final public function createRequestPayload(Model $model, object|array|string $input): string|array
6767
{
6868
return $this->normalizer->normalize($input, context: [self::CONTEXT_MODEL => $model]);
6969
}
@@ -73,7 +73,7 @@ public function createRequestPayload(Model $model, object|array|string $input):
7373
*
7474
* @return array<string, mixed>
7575
*/
76-
public function createToolOption(array $tools, Model $model): array
76+
final public function createToolOption(array $tools, Model $model): array
7777
{
7878
return $this->normalizer->normalize($tools, context: [self::CONTEXT_MODEL => $model, AbstractObjectNormalizer::PRESERVE_EMPTY_OBJECTS => true]);
7979
}

0 commit comments

Comments
 (0)