<?php
namespace Anthropic;
use Anthropic\Transporters\HttpTransporter;
use Anthropic\ValueObjects\ApiKey;
use Anthropic\ValueObjects\Transporter\BaseUri;
use Anthropic\ValueObjects\Transporter\Headers;
use Anthropic\ValueObjects\Transporter\QueryParams;
use Closure;
use Exception;
use GuzzleHttp\Client as GuzzleClient;
use Http\Discovery\Psr18ClientDiscovery;
use Psr\Http\Client\ClientInterface;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;
use Symfony\Component\HttpClient\Psr18Client;
final class Factory
{
/**
* The API key for the requests.
*/
private ?string $apiKey = null;
/**
* The HTTP client for the requests.
*/
private ?ClientInterface $httpClient = null;
/**
* The base URI for the requests.
*/
private ?string $baseUri = null;
/**
* The HTTP headers for the requests.
*
* @var array<string, string>
*/
private array $headers = [];
/**
* The query parameters for the requests.
*
* @var array<string, string|int>
*/
private array $queryParams = [];
private ?Closure $streamHandler = null;
/**
* Sets the API key for the requests.
*/
public function withApiKey(string $apiKey): self
{
$this->apiKey = trim($apiKey);
return $this;
}
/**
* Sets the HTTP client for the requests.
* If no client is provided the factory will try to find one using PSR-18 HTTP Client Discovery.
*/
public function withHttpClient(ClientInterface $client): self
{
$this->httpClient = $client;
return $this;
}
/**
* Sets the stream handler for the requests. Not required when using Guzzle.
*/
public function withStreamHandler(Closure $streamHandler): self
{
$this->streamHandler = $streamHandler;
return $this;
}
/**
* Sets the base URI for the requests.
* If no URI is provided the factory will use the default Anthropic API URI.
*/
public function withBaseUri(string $baseUri): self
{
$this->baseUri = $baseUri;
return $this;
}
/**
* Adds a custom HTTP header to the requests.
*/
public function withHttpHeader(string $name, string $value): self
{
$this->headers[$name] = $value;
return $this;
}
/**
* Adds a custom query parameter to the request url.
*/
public function withQueryParam(string $name, string $value): self
{
$this->queryParams[$name] = $value;
return $this;
}
/**
* Creates a new Anthropic Client.
*/
public function make(): Client
{
$headers = Headers::create();
if ($this->apiKey !== null) {
$headers = Headers::withAuthorization(ApiKey::from($this->apiKey));
}
foreach ($this->headers as $name => $value) {
$headers = $headers->withCustomHeader($name, $value);
}
$baseUri = BaseUri::from($this->baseUri ?: 'api.anthropic.com/v1');
$queryParams = QueryParams::create();
foreach ($this->queryParams as $name => $value) {
$queryParams = $queryParams->withParam($name, $value);
}
$client = $this->httpClient ??= Psr18ClientDiscovery::find();
$sendAsync = $this->makeStreamHandler($client);
$transporter = new HttpTransporter($client, $baseUri, $headers, $queryParams, $sendAsync);
return new Client($transporter);
}
/**
* Creates a new stream handler for "stream" requests.
*/
private function makeStreamHandler(ClientInterface $client): Closure
{
if (! is_null($this->streamHandler)) {
return $this->streamHandler;
}
if ($client instanceof GuzzleClient) {
return fn (RequestInterface $request): ResponseInterface => $client->send($request, ['stream' => true]);
}
if ($client instanceof Psr18Client) { // @phpstan-ignore-line
return fn (RequestInterface $request): ResponseInterface => $client->sendRequest($request); // @phpstan-ignore-line
}
return function (RequestInterface $_): never {
throw new Exception('To use stream requests you must provide an stream handler closure via the Anthropic factory.');
};
}
}