Skip to content

Commit 41faaa6

Browse files
TomLisankiechr-hertel
authored andcommitted
[Platform][Anthropic] Allow beta feature flags to be passed into platform invocations
1 parent 8cb4528 commit 41faaa6

File tree

3 files changed

+130
-4
lines changed

3 files changed

+130
-4
lines changed

src/platform/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,5 +58,6 @@ CHANGELOG
5858
* Add response promises for async operations
5959
* Add InMemoryPlatform and InMemoryRawResult for testing Platform without external Providers calls
6060
* Add tool calling support for Ollama platform
61+
* Allow beta feature flags to be passed into Anthropic model options
6162

6263

src/platform/src/Bridge/Anthropic/ModelClient.php

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,15 +39,26 @@ public function supports(Model $model): bool
3939

4040
public function request(Model $model, array|string $payload, array $options = []): RawHttpResult
4141
{
42+
$headers = [
43+
'x-api-key' => $this->apiKey,
44+
'anthropic-version' => $this->version,
45+
];
46+
4247
if (isset($options['tools'])) {
4348
$options['tool_choice'] = ['type' => 'auto'];
4449
}
4550

51+
if (
52+
isset($options['beta_features'])
53+
&& \is_array($options['beta_features'])
54+
&& !empty($options['beta_features'])
55+
) {
56+
$headers['anthropic-beta'] = implode(',', $options['beta_features']);
57+
unset($options['beta_features']);
58+
}
59+
4660
return new RawHttpResult($this->httpClient->request('POST', 'https://api.anthropic.com/v1/messages', [
47-
'headers' => [
48-
'x-api-key' => $this->apiKey,
49-
'anthropic-version' => $this->version,
50-
],
61+
'headers' => $headers,
5162
'json' => array_merge($options, $payload),
5263
]));
5364
}
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\AI\Platform\Bridge\Anthropic\Tests;
13+
14+
use PHPUnit\Framework\Attributes\CoversClass;
15+
use PHPUnit\Framework\TestCase;
16+
use Symfony\AI\Platform\Bridge\Anthropic\Claude;
17+
use Symfony\AI\Platform\Bridge\Anthropic\ModelClient;
18+
use Symfony\Component\HttpClient\MockHttpClient;
19+
use Symfony\Component\HttpClient\Response\JsonMockResponse;
20+
21+
#[CoversClass(ModelClient::class)]
22+
class ModelClientTest extends TestCase
23+
{
24+
private MockHttpClient $httpClient;
25+
private ModelClient $modelClient;
26+
private Claude $model;
27+
28+
protected function setUp(): void
29+
{
30+
$this->model = new Claude();
31+
}
32+
33+
public function testAnthropicBetaHeaderIsSetWithSingleBetaFeature()
34+
{
35+
$this->httpClient = new MockHttpClient(function ($method, $url, $options) {
36+
$this->assertEquals('POST', $method);
37+
$this->assertEquals('https://api.anthropic.com/v1/messages', $url);
38+
39+
$headers = $this->parseHeaders($options['headers']);
40+
41+
$this->assertArrayHasKey('anthropic-beta', $headers);
42+
$this->assertEquals('feature-1', $headers['anthropic-beta']);
43+
44+
return new JsonMockResponse('{"success": true}');
45+
});
46+
47+
$this->modelClient = new ModelClient($this->httpClient, 'test-api-key');
48+
49+
$options = ['beta_features' => ['feature-1']];
50+
$this->modelClient->request($this->model, ['message' => 'test'], $options);
51+
}
52+
53+
public function testAnthropicBetaHeaderIsSetWithMultipleBetaFeatures()
54+
{
55+
$this->httpClient = new MockHttpClient(function ($method, $url, $options) {
56+
$headers = $this->parseHeaders($options['headers']);
57+
58+
$this->assertArrayHasKey('anthropic-beta', $headers);
59+
$this->assertEquals('feature-1,feature-2,feature-3', $headers['anthropic-beta']);
60+
61+
return new JsonMockResponse('{"success": true}');
62+
});
63+
64+
$this->modelClient = new ModelClient($this->httpClient, 'test-api-key', '2023-06-01');
65+
66+
$options = ['beta_features' => ['feature-1', 'feature-2', 'feature-3']];
67+
$this->modelClient->request($this->model, ['message' => 'test'], $options);
68+
}
69+
70+
public function testAnthropicBetaHeaderIsNotSetWhenBetaFeaturesIsEmpty()
71+
{
72+
$this->httpClient = new MockHttpClient(function ($method, $url, $options) {
73+
$headers = $this->parseHeaders($options['headers']);
74+
75+
$this->assertArrayNotHasKey('anthropic-beta', $headers);
76+
77+
return new JsonMockResponse('{"success": true}');
78+
});
79+
80+
$this->modelClient = new ModelClient($this->httpClient, 'test-api-key', '2023-06-01');
81+
82+
$options = ['beta_features' => []];
83+
$this->modelClient->request($this->model, ['message' => 'test'], $options);
84+
}
85+
86+
public function testAnthropicBetaHeaderIsNotSetWhenBetaFeaturesIsNotProvided()
87+
{
88+
$this->httpClient = new MockHttpClient(function ($method, $url, $options) {
89+
$headers = $this->parseHeaders($options['headers']);
90+
91+
$this->assertArrayNotHasKey('anthropic-beta', $headers);
92+
93+
return new JsonMockResponse('{"success": true}');
94+
});
95+
96+
$this->modelClient = new ModelClient($this->httpClient, 'test-api-key', '2023-06-01');
97+
98+
$options = ['some_other_option' => 'value'];
99+
$this->modelClient->request($this->model, ['message' => 'test'], $options);
100+
}
101+
102+
private function parseHeaders(array $headers): array
103+
{
104+
$parsed = [];
105+
foreach ($headers as $header) {
106+
if (str_contains($header, ':')) {
107+
[$key, $value] = explode(':', $header, 2);
108+
$parsed[trim($key)] = trim($value);
109+
}
110+
}
111+
112+
return $parsed;
113+
}
114+
}

0 commit comments

Comments
 (0)