Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions docs/reference/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,39 @@ Custom handler for streaming requests. The handler receives a PSR-7 `RequestInte

You only need this for HTTP clients other than Guzzle or Symfony. Both of those are detected automatically and handled without any extra configuration.

## Beta features

Anthropic's beta features are opt-in via the `anthropic-beta` header. This SDK lets you enable them per request by passing a `betas` array alongside your normal parameters:

```php
$response = $client->messages()->create([
'model' => 'claude-opus-4-6',
'max_tokens' => 1024,
'messages' => [
['role' => 'user', 'content' => 'Hello!'],
],
'betas' => [
'interleaved-thinking-2025-05-14',
'extended-cache-ttl-2025-04-11',
],
]);
```

The SDK pulls `betas` out of the parameters before serialization, so nothing leaks into the JSON body or query string. It becomes a comma-separated `anthropic-beta` header on that one request only.

If some beta is enabled for every request in your app, set it globally on the factory and skip the per-call array:

```php
$client = Anthropic::factory()
->withApiKey('your-api-key')
->withHttpHeader('anthropic-beta', 'interleaved-thinking-2025-05-14')
->make();
```

Global and per-request combine. If you set `anthropic-beta: interleaved-thinking-2025-05-14` globally and pass `betas: ['files-api-2025-04-14']` on one call, that call sends both, de-duplicated.

Some resources require a specific beta header to work at all. The SDK auto-injects those for you, so you never have to type the version string for the resource you're already using. You only pass `betas` when you want to add additional ones on top.

## HTTP client setup

### Guzzle
Expand Down
125 changes: 91 additions & 34 deletions src/ValueObjects/Transporter/Payload.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,14 @@ final class Payload
* Creates a new Request value object.
*
* @param array<string, mixed> $parameters
* @param list<string> $betas
*/
private function __construct(
private readonly ContentType $contentType,
private readonly Method $method,
private readonly ResourceUri $uri,
private readonly array $parameters = [],
private readonly array $betas = [],
) {
// ..
}
Expand All @@ -39,11 +41,9 @@ private function __construct(
*/
public static function list(string $resource, array $parameters = []): self
{
$contentType = ContentType::JSON;
$method = Method::GET;
$uri = ResourceUri::list($resource);
[$parameters, $betas] = self::splitBetas($parameters);

return new self($contentType, $method, $uri, $parameters);
return new self(ContentType::JSON, Method::GET, ResourceUri::list($resource), $parameters, $betas);
}

/**
Expand All @@ -53,11 +53,9 @@ public static function list(string $resource, array $parameters = []): self
*/
public static function retrieve(string $resource, string $id, string $suffix = '', array $parameters = []): self
{
$contentType = ContentType::JSON;
$method = Method::GET;
$uri = ResourceUri::retrieve($resource, $id, $suffix);
[$parameters, $betas] = self::splitBetas($parameters);

return new self($contentType, $method, $uri, $parameters);
return new self(ContentType::JSON, Method::GET, ResourceUri::retrieve($resource, $id, $suffix), $parameters, $betas);
}

/**
Expand All @@ -67,23 +65,17 @@ public static function retrieve(string $resource, string $id, string $suffix = '
*/
public static function modify(string $resource, string $id, array $parameters = []): self
{
$contentType = ContentType::JSON;
$method = Method::POST;
$uri = ResourceUri::modify($resource, $id);
[$parameters, $betas] = self::splitBetas($parameters);

return new self($contentType, $method, $uri, $parameters);
return new self(ContentType::JSON, Method::POST, ResourceUri::modify($resource, $id), $parameters, $betas);
}

/**
* Creates a new Payload value object from the given parameters.
*/
public static function retrieveContent(string $resource, string $id): self
{
$contentType = ContentType::JSON;
$method = Method::GET;
$uri = ResourceUri::retrieveContent($resource, $id);

return new self($contentType, $method, $uri);
return new self(ContentType::JSON, Method::GET, ResourceUri::retrieveContent($resource, $id));
}

/**
Expand All @@ -93,11 +85,9 @@ public static function retrieveContent(string $resource, string $id): self
*/
public static function create(string $resource, array $parameters): self
{
$contentType = ContentType::JSON;
$method = Method::POST;
$uri = ResourceUri::create($resource);
[$parameters, $betas] = self::splitBetas($parameters);

return new self($contentType, $method, $uri, $parameters);
return new self(ContentType::JSON, Method::POST, ResourceUri::create($resource), $parameters, $betas);
}

/**
Expand All @@ -107,35 +97,43 @@ public static function create(string $resource, array $parameters): self
*/
public static function upload(string $resource, array $parameters): self
{
$contentType = ContentType::MULTIPART;
$method = Method::POST;
$uri = ResourceUri::upload($resource);
[$parameters, $betas] = self::splitBetas($parameters);

return new self($contentType, $method, $uri, $parameters);
return new self(ContentType::MULTIPART, Method::POST, ResourceUri::upload($resource), $parameters, $betas);
}

/**
* Creates a new Payload value object from the given parameters.
*/
public static function cancel(string $resource, string $id): self
{
$contentType = ContentType::JSON;
$method = Method::POST;
$uri = ResourceUri::cancel($resource, $id);

return new self($contentType, $method, $uri);
return new self(ContentType::JSON, Method::POST, ResourceUri::cancel($resource, $id));
}

/**
* Creates a new Payload value object from the given parameters.
*/
public static function delete(string $resource, string $id): self
{
$contentType = ContentType::JSON;
$method = Method::DELETE;
$uri = ResourceUri::delete($resource, $id);
return new self(ContentType::JSON, Method::DELETE, ResourceUri::delete($resource, $id));
}

return new self($contentType, $method, $uri);
/**
* Returns a new Payload with the given betas merged into this one.
*
* Resources use this to auto-inject the beta header they need (e.g. `files-api-2025-04-14`) while keeping any betas the user already passed via the `betas` parameter.
*
* @param list<string> $betas
*/
public function withBetas(array $betas): self
{
return new self(
$this->contentType,
$this->method,
$this->uri,
$this->parameters,
self::dedupeBetas([...$this->betas, ...$betas]),
);
}

/**
Expand Down Expand Up @@ -201,6 +199,65 @@ public function toRequest(BaseUri $baseUri, Headers $headers, QueryParams $query
$request = $request->withHeader($name, $value);
}

if ($this->betas !== []) {
$request = $request->withHeader('anthropic-beta', self::mergeBetaHeader($request->getHeaderLine('anthropic-beta'), $this->betas));
}

return $request;
}

/**
* Splits out the `betas` parameter from the request body, turning it into a per-request header.
*
* @param array<string, mixed> $parameters
* @return array{0: array<string, mixed>, 1: list<string>}
*/
private static function splitBetas(array $parameters): array
{
if (! isset($parameters['betas']) || ! is_array($parameters['betas'])) {
return [$parameters, []];
}

/** @var array<array-key, mixed> $rawBetas */
$rawBetas = $parameters['betas'];
unset($parameters['betas']);

$betas = [];
foreach ($rawBetas as $beta) {
if (is_string($beta) && trim($beta) !== '') {
$betas[] = trim($beta);
}
}

return [$parameters, self::dedupeBetas($betas)];
}

/**
* Merges a comma-separated beta header string with additional betas, de-duplicating in order of appearance.
*
* @param list<string> $betas
*/
private static function mergeBetaHeader(string $existing, array $betas): string
{
$existingList = [];
if ($existing !== '') {
foreach (explode(',', $existing) as $item) {
$trimmed = trim($item);
if ($trimmed !== '') {
$existingList[] = $trimmed;
}
}
}

return implode(',', self::dedupeBetas([...$existingList, ...$betas]));
}

/**
* @param list<string> $betas
* @return list<string>
*/
private static function dedupeBetas(array $betas): array
{
return array_values(array_unique($betas));
}
}
Loading