Skip to content

Commit

Permalink
Merge pull request #38 from deepseek-php/support-many-http-clients
Browse files Browse the repository at this point in the history
support many http clients (Guzzle & Symfony)
  • Loading branch information
omaralalwi authored Feb 22, 2025
2 parents dea86c5 + 5e7a482 commit dfc28dc
Show file tree
Hide file tree
Showing 7 changed files with 124 additions and 112 deletions.
37 changes: 27 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
- [🚀 Quick Start](#-quick-start)
- [Basic Usage](#basic-usage)
- [Advanced Configuration](#advanced-configuration)
- [Use with Symfony HttpClient](#use-with-symfony-httpclient)
- [Get Models List](#get-models-list)
- [Framework Integration](#-framework-integration)
- [🆕 Migration Guide](#-migration-guide)
Expand All @@ -37,12 +38,13 @@

## ✨ Features

- **Seamless API Integration**: PHP-first interface for DeepSeek's AI capabilities
- **Fluent Builder Pattern**: Chainable methods for intuitive request building
- **Enterprise Ready**: PSR-18 compliant HTTP client integration
- **Model Flexibility**: Support for multiple DeepSeek models (Coder, Chat, etc.)
- **Streaming Ready**: Built-in support for real-time response handling
- **Framework Friendly**: Laravel & Symfony packages available
- **Seamless API Integration**: PHP-first interface for DeepSeek's AI capabilities.
- **Fluent Builder Pattern**: Chainable methods for intuitive request building.
- **Enterprise Ready**: PSR-18 compliant HTTP client integration.
- **Model Flexibility**: Support for multiple DeepSeek models (Coder, Chat, etc.).
- **Streaming Ready**: Built-in support for real-time response handling.
- **Many Http Clients**: easy to use `Guzzle http client` (default) , or `symfony http client`.
- **Framework Friendly**: Laravel & Symfony packages available.

---

Expand Down Expand Up @@ -85,15 +87,32 @@ echo $response;
use DeepSeek\DeepSeekClient;
use DeepSeek\Enums\Models;

$response = DeepSeekClient::build('your-api-key')
->withBaseUrl('https://api.deepseek.com/v2')
$client = DeepSeekClient::build(apiKey:'your-api-key', baseUrl:'https://api.deepseek.com/v3', timeout:30, clientType:'guzzle');

$response = $client
->withModel(Models::CODER->value)
->withStream()
->withTemperature(1.2)
->run();

echo 'API Response:'.$response;
```

### Use with Symfony HttpClient
the package already built with `symfony Http client`, if you need to use package with `symfony` Http Client , it is easy to achieve that, just pass `clientType:'symfony'` with `build` function.

ex with symfony:

```php
// with defaults baseUrl and timeout
$client = DeepSeekClient::build('your-api-key', clientType:'symfony')
// with customization
$client = DeepSeekClient::build(apiKey:'your-api-key', baseUrl:'https://api.deepseek.com/v3', timeout:30, 'symfony');

$client->query('Explain quantum computing in simple terms')
->run();
```

### Get Models List

```php
Expand All @@ -110,8 +129,6 @@ echo $response; // {"object":"list","data":[{"id":"deepseek-chat","object":"mode

### [Laravel Deepseek Package](https://github.com/deepseek-php/deepseek-laravel)

### [Symfony Deepseek Package](https://github.com/deepseek-php/deepseek-symfony)

---

## 🚧 Migration Guide
Expand Down
12 changes: 9 additions & 3 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,15 @@
"sdk",
"api",
"php",
"symfony",
"client",
"llm",
"nlp",
"openai",
"qwen",
"symfony-deepseek",
"deepseek-symfony",
"symfony-http-client",
"symfony-client",
"machine-learning",
"php-sdk",
"ai-sdk",
Expand Down Expand Up @@ -45,15 +49,17 @@
"role": "creator"
}
],
"version": "2.0.1",
"version": "2.0.2",
"require": {
"php": "^8.1.0",
"nyholm/psr7": "^1.8",
"php-http/discovery": "^1.20.0",
"php-http/multipart-stream-builder": "^1.4.2",
"psr/http-client": "^1.0.3",
"psr/http-client-implementation": "^1.0.1",
"psr/http-factory-implementation": "*",
"psr/http-message": "^1.1.0|^2.0.0"
"psr/http-message": "^1.1.0|^2.0.0",
"symfony/http-client": "^7.2"
},
"require-dev": {
"guzzlehttp/guzzle": "^7.9.2",
Expand Down
7 changes: 4 additions & 3 deletions src/Contracts/Factories/ApiFactoryContract.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use DeepSeek\Factories\ApiFactory;
use GuzzleHttp\Client;
use Psr\Http\Client\ClientInterface;

interface ApiFactoryContract
{
Expand Down Expand Up @@ -39,9 +40,9 @@ public function setKey(string $apiKey): ApiFactory;
public function setTimeout(?int $timeout = null): ApiFactory;

/**
* Build and return the Guzzle Client instance.
* Build and return http Client instance.
*
* @return Client
* @return ClientInterface
*/
public function run(): Client;
public function run(?string $clientType = null): ClientInterface;
}
7 changes: 5 additions & 2 deletions src/DeepSeekClient.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use DeepSeek\Contracts\ClientContract;
use DeepSeek\Contracts\Models\ResultContract;
use DeepSeek\Enums\Requests\ClientTypes;
use DeepSeek\Enums\Requests\EndpointSuffixes;
use DeepSeek\Resources\Resource;
use Psr\Http\Client\ClientInterface;
Expand Down Expand Up @@ -94,13 +95,15 @@ public function run(): string
* @param int|null $timeout The timeout duration for requests in seconds (optional).
* @return self A new instance of the DeepSeekClient.
*/
public static function build(string $apiKey, ?string $baseUrl = null, ?int $timeout = null): self
public static function build(string $apiKey, ?string $baseUrl = null, ?int $timeout = null, ?string $clientType = null): self
{
$clientType = $clientType ?? ClientTypes::GUZZLE->value;

$httpClient = ApiFactory::build()
->setBaseUri($baseUrl)
->setTimeout($timeout)
->setKey($apiKey)
->run();
->run($clientType);

return new self($httpClient);
}
Expand Down
10 changes: 10 additions & 0 deletions src/Enums/Requests/ClientTypes.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

namespace DeepSeek\Enums\Requests;

enum ClientTypes: string
{
case GUZZLE = 'guzzle';
case SYMFONY = 'symfony';

}
98 changes: 39 additions & 59 deletions src/Factories/ApiFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,103 +4,83 @@

use DeepSeek\Contracts\Factories\ApiFactoryContract;
use DeepSeek\Enums\Configs\DefaultConfigs;
use DeepSeek\Enums\Requests\ClientTypes;
use DeepSeek\Enums\Requests\HeaderFlags;
use GuzzleHttp\Client;
use Psr\Http\Client\ClientInterface;
use Symfony\Component\HttpClient\HttpClient;
use Symfony\Component\HttpClient\Psr18Client;
use RuntimeException;
use InvalidArgumentException;

final class ApiFactory implements ApiFactoryContract
{
/**
* The API key for authentication.
*
* @var string
*/
protected string $apiKey;

/**
* The base URL for the API.
*
* @var string
*/
protected string $baseUrl;

/**
* The timeout value for the API request in seconds.
*
* @var int
*/
protected int $timeout;
protected array $clientConfig;

/**
* Returns an instance of the ApiFactory.
*
* This is a static factory method that creates a new instance of the class.
*
* @return self A new instance of the ApiFactory.
*/
public static function build(): self
{
return new self;
return new self();
}

/**
* Set the base URL for the API.
*
* If no URL is provided, the default base URL from the configuration is used.
*
* @param string|null $baseUrl The base URL to set (optional).
* @return self The instance of the self for method chaining.
*/
public function setBaseUri(?string $baseUrl = null): self
{
$this->baseUrl = $baseUrl ? trim($baseUrl) : DefaultConfigs::BASE_URL->value;
return $this;
}

/**
* Set the API key for authentication.
*
* @param string $apiKey The API key to set.
* @return self The instance of the self for method chaining.
*/
public function setKey(string $apiKey): self
{
$this->apiKey = trim($apiKey);
return $this;
}

/**
* Set the timeout for the API request.
*
* If no timeout is provided, the default timeout value from the configuration is used.
*
* @param int|null $timeout The timeout value in seconds (optional).
* @return self The instance of the self for method chaining.
*/
public function setTimeout(?int $timeout = null): self
{
$this->timeout = $timeout ?: (int)DefaultConfigs::TIMEOUT->value;
return $this;
}

/**
* Build and return the Guzzle Client instance.
*
* This method creates and configures a new Guzzle HTTP client instance
* using the provided base URL, timeout, and headers.
*
* @return Client A Guzzle client instance configured for the API.
*/
public function run(): Client
public function initialize(): self
{
$clientConfig = [
if (!isset($this->baseUrl)) {
$this->setBaseUri();
}

if (!isset($this->apiKey)) {
throw new RuntimeException('API key must be set using setKey() before initialization.');
}

if (!isset($this->timeout)) {
$this->setTimeout();
}

$this->clientConfig = [
HeaderFlags::BASE_URL->value => $this->baseUrl,
HeaderFlags::TIMEOUT->value => $this->timeout,
HeaderFlags::HEADERS->value => [
HeaderFlags::AUTHORIZATION->value => 'Bearer ' . $this->apiKey,
HeaderFlags::CONTENT_TYPE->value => "application/json",
HeaderFlags::CONTENT_TYPE->value => 'application/json',
],
];

return new Client($clientConfig);
return $this;
}

public function run(?string $clientType = null): ClientInterface
{
$clientType = $clientType ?? ClientTypes::GUZZLE->value;

if (!isset($this->clientConfig)) {
$this->initialize();
}

return match (strtolower($clientType)) {
ClientTypes::GUZZLE->value => new Client($this->clientConfig),
ClientTypes::SYMFONY->value => new Psr18Client(HttpClient::create($this->clientConfig)),
default => throw new InvalidArgumentException("Unsupported client type: {$clientType}")
};
}
}
Loading

0 comments on commit dfc28dc

Please sign in to comment.